Hero phase dots, name hierarchy, lighter page title, cleaner sections
This commit is contained in:
parent
6148db7902
commit
db5d3cb34d
@ -5,11 +5,11 @@
|
|||||||
// table below as support. DV: full-page replace with hero card + collapsible
|
// table below as support. DV: full-page replace with hero card + collapsible
|
||||||
// section groups.
|
// section groups.
|
||||||
|
|
||||||
import { useEffect, useMemo, useState } from "react";
|
import React, { useEffect, useMemo, useState } from "react";
|
||||||
import { useNavigate, useParams } from "react-router-dom";
|
import { useNavigate, useParams } from "react-router-dom";
|
||||||
import {
|
import {
|
||||||
ChevronLeft, ChevronRight, Search, ArrowUp, ArrowDown, X, RefreshCw,
|
ChevronLeft, ChevronRight, Search, ArrowUp, ArrowDown, X, RefreshCw,
|
||||||
CheckCircle2, LogOut, Sparkles, Clock, Car, ClipboardList, PhoneOutgoing,
|
CheckCircle2, LogOut, Sparkles, Car, ClipboardList, PhoneOutgoing,
|
||||||
CalendarCheck2, MessageSquareHeart, Trophy, ArrowLeft, ChevronDown, ChevronUp,
|
CalendarCheck2, MessageSquareHeart, Trophy, ArrowLeft, ChevronDown, ChevronUp,
|
||||||
Plus,
|
Plus,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
@ -66,6 +66,44 @@ const STATE_TONE: Record<string, { bg: string; fg: string; dot: string }> = {
|
|||||||
};
|
};
|
||||||
function tone(name: string) { return STATE_TONE[name] || { bg: "#F5F5F4", fg: "#57534E", dot: "#A8A29E" }; }
|
function tone(name: string) { return STATE_TONE[name] || { bg: "#F5F5F4", fg: "#57534E", dot: "#A8A29E" }; }
|
||||||
|
|
||||||
|
const PHASES = ["Captured", "Scheduling", "Test drive", "Feedback"] as const;
|
||||||
|
function phaseIndexFor(state: string | null | undefined): number {
|
||||||
|
if (!state) return 0;
|
||||||
|
if (state === STATE_IDS.AWAITING_FEEDBACK_CALL || state === STATE_IDS.FEEDBACK_CALL_FAILED) return 3;
|
||||||
|
if (state === STATE_IDS.FEEDBACK_COLLECTED || state === STATE_IDS.END_STATE) return 3;
|
||||||
|
if (state === STATE_IDS.SCHEDULED) return 2;
|
||||||
|
if (state === STATE_IDS.AWAITING_SCHEDULER_CALL || state === STATE_IDS.SCHEDULING_CALL_FAILED) return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
function PhaseDots({ state }: { state: string | null | undefined }) {
|
||||||
|
const current = phaseIndexFor(state);
|
||||||
|
return (
|
||||||
|
<div className="mt-4 flex items-center gap-2.5 flex-wrap">
|
||||||
|
{PHASES.map((label, i) => {
|
||||||
|
const reached = i <= current;
|
||||||
|
return (
|
||||||
|
<React.Fragment key={label}>
|
||||||
|
<div className="flex items-center gap-1.5">
|
||||||
|
<span
|
||||||
|
className="w-2.5 h-2.5 rounded-full shrink-0"
|
||||||
|
style={
|
||||||
|
reached
|
||||||
|
? { background: "#e31837" }
|
||||||
|
: { background: "transparent", border: "2px solid rgba(255,255,255,0.4)" }
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<span className={`text-[11px] font-semibold uppercase tracking-[0.08em] ${reached ? "text-white" : "text-white/50"}`}>
|
||||||
|
{label}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{i < PHASES.length - 1 && <span className="w-5 h-px" style={{ background: "rgba(255,255,255,0.2)" }} />}
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function iconFor(label: string) {
|
function iconFor(label: string) {
|
||||||
const k = label.toLowerCase();
|
const k = label.toLowerCase();
|
||||||
if (k.includes("lead")) return <ClipboardList size={15} />;
|
if (k.includes("lead")) return <ClipboardList size={15} />;
|
||||||
@ -123,7 +161,7 @@ export default function VariantB() {
|
|||||||
<div className={EYEBROW_CLS} style={EYEBROW_STYLE}>
|
<div className={EYEBROW_CLS} style={EYEBROW_STYLE}>
|
||||||
Test-drive lifecycle
|
Test-drive lifecycle
|
||||||
</div>
|
</div>
|
||||||
<h1 className="text-[40px] font-bold tracking-tight leading-[1.05] mt-2" style={{ color: INK }}>
|
<h1 className="text-[24px] font-semibold tracking-tight leading-tight mt-1.5" style={{ color: INK }}>
|
||||||
{activeNav?.label ?? "Dashboard"}
|
{activeNav?.label ?? "Dashboard"}
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
@ -615,44 +653,42 @@ function DetailPage({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
{/* Breadcrumb — TM style: red 12px font-weight 700 */}
|
{/* Hero — full-bleed navy band. Back button at the top, then the lead block.
|
||||||
<div className="flex items-center gap-2 text-[12px] font-bold leading-6 uppercase tracking-[-0.02em]">
|
The -mx-10 pulls it out of main's px-10 so it spans the content area
|
||||||
<button onClick={onBack} className="inline-flex items-center gap-1.5 transition-opacity hover:opacity-70" style={{ color: ACCENT }}>
|
edge-to-edge. */}
|
||||||
<ArrowLeft size={13} />{breadcrumbLabel}
|
|
||||||
</button>
|
|
||||||
<span className="text-stone-300">/</span>
|
|
||||||
<span style={{ color: INK }}>Lead detail</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Hero — editorial TM band. Big red number bug (instance id), name + meta on
|
|
||||||
the left, state badge as a solid white tablet on the right (fills the
|
|
||||||
previously-empty space and carries the strongest signal: status). */}
|
|
||||||
<div
|
<div
|
||||||
className="rounded-none px-7 py-6 text-white relative overflow-hidden"
|
className="rounded-none -mx-10 -mt-10 px-10 pt-7 pb-7 text-white relative overflow-hidden"
|
||||||
style={{
|
style={{ background: TM_NAVY }}
|
||||||
background: `linear-gradient(120deg, ${ACCENT} 0%, ${ACCENT_DARK} 100%)`,
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<span className="absolute top-0 left-0 bottom-0 w-1.5" style={{ background: TM_NAVY }} />
|
<span className="absolute top-0 left-0 bottom-0 w-1.5" style={{ background: ACCENT }} />
|
||||||
{/* Diagonal navy slice on the far right — TM editorial accent */}
|
{/* Diagonal red slice on the far right — mirrors techmahindra.com footer */}
|
||||||
<span
|
<span
|
||||||
className="absolute top-0 right-0 bottom-0 w-40 pointer-events-none opacity-40"
|
className="absolute top-0 right-0 bottom-0 w-56 pointer-events-none opacity-50"
|
||||||
style={{
|
style={{
|
||||||
background: `linear-gradient(115deg, transparent 0%, transparent 55%, ${TM_NAVY} 55%, ${TM_NAVY} 58%, transparent 58%, transparent 72%, ${TM_NAVY} 72%, ${TM_NAVY} 74%, transparent 74%)`,
|
background: `linear-gradient(115deg, transparent 0%, transparent 55%, ${ACCENT} 55%, ${ACCENT} 58%, transparent 58%, transparent 72%, ${ACCENT} 72%, ${ACCENT} 74%, transparent 74%)`,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="relative flex items-stretch justify-between gap-8 flex-wrap">
|
{/* Back link — sits at the hero top, white on navy */}
|
||||||
<div className="flex items-stretch gap-5 min-w-0 flex-1">
|
<div className="relative mb-5 flex items-center gap-2 text-[11px] font-semibold uppercase tracking-[0.08em] leading-none">
|
||||||
{/* Big number bug — instance ID as editorial signature */}
|
<button
|
||||||
<div className="flex flex-col justify-center pr-5 border-r border-white/15">
|
onClick={onBack}
|
||||||
<div className="text-[10px] font-semibold uppercase tracking-[0.18em] text-white/60 mb-1">Lead ID</div>
|
className="inline-flex items-center gap-1.5 text-white/75 hover:text-white transition-colors"
|
||||||
<div className="text-[28px] font-bold leading-none tabular-nums">#{instanceId}</div>
|
>
|
||||||
|
<ArrowLeft size={13} className="shrink-0" />
|
||||||
|
<span>{breadcrumbLabel}</span>
|
||||||
|
</button>
|
||||||
|
<span className="text-white/30">/</span>
|
||||||
|
<span className="text-white/55">Lead detail</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="min-w-0 flex flex-col justify-center">
|
<div className="relative flex items-stretch justify-between gap-8 flex-wrap">
|
||||||
<div className="text-[11px] font-semibold uppercase tracking-[0.15em] mb-2" style={{ color: "#ffd9de" }}>Lead</div>
|
<div className="min-w-0 flex-1 flex flex-col justify-center">
|
||||||
<h2 className="text-[30px] font-bold leading-none tracking-tight truncate">{heroValue || `#${instanceId}`}</h2>
|
<div className="text-[11px] font-semibold uppercase tracking-[0.15em] mb-2" style={{ color: ACCENT }}>Lead</div>
|
||||||
|
<div className="flex items-baseline gap-3 min-w-0 flex-wrap">
|
||||||
|
<h2 className="text-[36px] font-bold leading-none tracking-tight truncate">{heroValue || `#${instanceId}`}</h2>
|
||||||
|
<span className="text-[20px] font-semibold leading-none tabular-nums text-white/60">#{instanceId}</span>
|
||||||
|
</div>
|
||||||
<div className="mt-3 flex items-center gap-3 text-[12.5px] text-white/80 flex-wrap">
|
<div className="mt-3 flex items-center gap-3 text-[12.5px] text-white/80 flex-wrap">
|
||||||
{model && <span><span className="text-white/55">Model</span> <span className="font-semibold text-white">{model}</span></span>}
|
{model && <span><span className="text-white/55">Model</span> <span className="font-semibold text-white">{model}</span></span>}
|
||||||
{dv.data["created_at"] && (
|
{dv.data["created_at"] && (
|
||||||
@ -662,7 +698,7 @@ function DetailPage({
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<PhaseDots state={instanceState} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Right column — state badge stacked over action buttons. Fills the dead space. */}
|
{/* Right column — state badge stacked over action buttons. Fills the dead space. */}
|
||||||
@ -708,26 +744,48 @@ function DetailPage({
|
|||||||
|
|
||||||
{/* Stack of independent section cards — each its own border, no shared chrome. */}
|
{/* Stack of independent section cards — each its own border, no shared chrome. */}
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
{Object.entries(groups).filter(([, fs]) => fs.length > 0).map(([key, fs]) => (
|
{Object.entries(groups).filter(([, fs]) => fs.length > 0).map(([key, fs], i) => (
|
||||||
<SectionCard key={key} sectionKey={key} fields={fs} data={dv.data!} />
|
<SectionCard key={key} index={i} sectionKey={key} fields={fs} data={dv.data!} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{audit.length > 0 && (
|
{audit.length > 0 && (
|
||||||
<div className="bg-white rounded-none p-6" style={{ border: CARD_BORDER }}>
|
<div className="bg-white rounded-none p-6" style={{ border: CARD_BORDER }}>
|
||||||
<div className="flex items-center gap-2 mb-4">
|
<div className="flex items-center gap-3 mb-5">
|
||||||
<Clock size={14} className="text-[#e31837]" />
|
<span className="text-[11px] font-bold tabular-nums px-1.5 py-0.5 text-white" style={{ background: ACCENT, letterSpacing: "0.04em" }}>LOG</span>
|
||||||
<div className={EYEBROW_CLS} style={EYEBROW_STYLE}>Audit</div>
|
<div className="text-[17px] font-bold tracking-tight leading-none" style={{ color: INK }}>Activity timeline</div>
|
||||||
|
<div className="text-[11.5px] font-medium tabular-nums text-stone-400">{audit.length} {audit.length === 1 ? "event" : "events"}</div>
|
||||||
|
</div>
|
||||||
|
<ol className="relative">
|
||||||
|
{/* Vertical rail down the timeline */}
|
||||||
|
<span className="absolute left-[5px] top-2 bottom-2 w-px" style={{ background: TM_BORDER }} />
|
||||||
|
{audit.map((e, i) => {
|
||||||
|
const meta = ACTIVITY_LABELS[e.activity_id];
|
||||||
|
const label = meta?.label ?? "Workflow step";
|
||||||
|
const tone = meta?.tone ?? "system";
|
||||||
|
const isLast = i === audit.length - 1;
|
||||||
|
const dotColor = isLast ? ACCENT : tone === "ai" ? TM_NAVY : tone === "user" ? "#57534e" : "#a8a29e";
|
||||||
|
const actor = tone === "ai" ? "AI · Maya" : (e.user_roles?.[0] ?? "System");
|
||||||
|
return (
|
||||||
|
<li key={e.id} className="relative pl-7 pb-4 last:pb-0 text-[13px]">
|
||||||
|
<span
|
||||||
|
className="absolute left-0 top-1 w-[11px] h-[11px] rounded-full"
|
||||||
|
style={{ background: dotColor, boxShadow: `0 0 0 3px white` }}
|
||||||
|
/>
|
||||||
|
<div className="flex items-baseline justify-between gap-4">
|
||||||
|
<div className="min-w-0">
|
||||||
|
<div className="font-semibold leading-tight" style={{ color: INK }}>{label}</div>
|
||||||
|
<div className="text-[11.5px] uppercase tracking-[0.04em] text-stone-400 mt-1">
|
||||||
|
{actor}{e.execution_state && e.execution_state !== "completed" ? ` · ${e.execution_state}` : ""}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span className="text-stone-400 tabular-nums shrink-0 text-[12px]">
|
||||||
|
{new Date(e.created_at).toLocaleString("en-IN", { day: "2-digit", month: "short", hour: "2-digit", minute: "2-digit" })}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-[18px] font-bold leading-tight -mt-3 mb-4" style={{ color: INK }}>Activity timeline</div>
|
|
||||||
<ol className="space-y-3">
|
|
||||||
{audit.map((e, i) => (
|
|
||||||
<li key={e.id} className="flex items-start gap-3 text-[13px]">
|
|
||||||
<span className={`mt-1 w-2 h-2 rounded-full shrink-0 ${i === audit.length - 1 ? "" : ""}`} style={{ background: i === audit.length - 1 ? ACCENT : "#D6D3D1" }} />
|
|
||||||
<span className="text-stone-700 flex-1">{e.activity_id.slice(0, 8)}…</span>
|
|
||||||
<span className="text-stone-400 tabular-nums shrink-0">{new Date(e.created_at).toLocaleString("en-IN", { day: "2-digit", month: "short", hour: "2-digit", minute: "2-digit" })}</span>
|
|
||||||
</li>
|
</li>
|
||||||
))}
|
);
|
||||||
|
})}
|
||||||
</ol>
|
</ol>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@ -736,6 +794,19 @@ function DetailPage({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Section titles — single strong word, no subtitle (cleaner editorial feel).
|
// Section titles — single strong word, no subtitle (cleaner editorial feel).
|
||||||
|
// Human labels for activity UUIDs that appear in the audit log. Anything not
|
||||||
|
// in this map falls back to "Workflow step".
|
||||||
|
const ACTIVITY_LABELS: Record<string, { label: string; tone: "user" | "ai" | "system" }> = {
|
||||||
|
[ACTIVITY_IDS.INIT_ACTIVITY]: { label: "Lead captured", tone: "user" },
|
||||||
|
[ACTIVITY_IDS.MARK_TEST_DRIVE_DONE]: { label: "Test drive marked done", tone: "user" },
|
||||||
|
[ACTIVITY_IDS.AI_CONFIRM_SCHEDULING_SUCCESS]: { label: "Scheduling call succeeded", tone: "ai" },
|
||||||
|
[ACTIVITY_IDS.AI_CONFIRM_SCHEDULING_FAILED]: { label: "Scheduling call failed", tone: "ai" },
|
||||||
|
[ACTIVITY_IDS.RETRY_SCHEDULING_CALL]: { label: "Retried scheduling call", tone: "user" },
|
||||||
|
[ACTIVITY_IDS.AI_CONFIRM_FEEDBACK_COLLECTED]: { label: "Feedback collected", tone: "ai" },
|
||||||
|
[ACTIVITY_IDS.AI_CONFIRM_FEEDBACK_FAILED]: { label: "Feedback call failed", tone: "ai" },
|
||||||
|
[ACTIVITY_IDS.RETRY_FEEDBACK_CALL]: { label: "Retried feedback call", tone: "user" },
|
||||||
|
};
|
||||||
|
|
||||||
const SECTION_TITLE: Record<string, string> = {
|
const SECTION_TITLE: Record<string, string> = {
|
||||||
customer: "Customer",
|
customer: "Customer",
|
||||||
vehicle: "Vehicle",
|
vehicle: "Vehicle",
|
||||||
@ -755,8 +826,9 @@ const SECTION_EMPTY: Record<string, string> = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function SectionCard({
|
function SectionCard({
|
||||||
sectionKey, fields, data,
|
index, sectionKey, fields, data,
|
||||||
}: {
|
}: {
|
||||||
|
index: number;
|
||||||
sectionKey: string;
|
sectionKey: string;
|
||||||
fields: any[];
|
fields: any[];
|
||||||
data: Record<string, unknown>;
|
data: Record<string, unknown>;
|
||||||
@ -769,18 +841,44 @@ function SectionCard({
|
|||||||
const isEmpty = populated === 0;
|
const isEmpty = populated === 0;
|
||||||
const [open, setOpen] = useState(!isEmpty);
|
const [open, setOpen] = useState(!isEmpty);
|
||||||
|
|
||||||
if (isEmpty) {
|
// White header with a small red number tag — editorial, no navy weight.
|
||||||
return (
|
const numberTag = (
|
||||||
<div className="bg-white rounded-none px-6 py-5" style={{ border: CARD_BORDER }}>
|
<span
|
||||||
<div className="flex items-baseline gap-3">
|
className="text-[11px] font-bold tabular-nums px-1.5 py-0.5"
|
||||||
<div className="text-[18px] font-bold tracking-tight leading-none" style={{ color: INK }}>{title}</div>
|
style={{ background: ACCENT, color: "white", letterSpacing: "0.04em" }}
|
||||||
<div className="text-[11.5px] font-medium tabular-nums">
|
>
|
||||||
<span className="text-stone-400" style={{ fontWeight: 600 }}>0</span>
|
{String(index + 1).padStart(2, "0")}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
|
||||||
|
const headerRow = (interactive: boolean) => (
|
||||||
|
<div
|
||||||
|
className={`flex items-center justify-between gap-4 px-6 py-4 ${interactive ? "cursor-pointer group" : ""}`}
|
||||||
|
style={{ borderBottom: interactive && open ? `1px solid ${TM_BORDER}` : "none" }}
|
||||||
|
onClick={interactive ? () => setOpen((o) => !o) : undefined}
|
||||||
|
>
|
||||||
|
<div className="min-w-0 flex items-center gap-3">
|
||||||
|
{numberTag}
|
||||||
|
<div className="text-[17px] font-bold tracking-tight leading-none" style={{ color: INK }}>{title}</div>
|
||||||
|
<div className="text-[11.5px] font-medium tabular-nums ml-1">
|
||||||
|
<span style={{ color: populated > 0 ? ACCENT : "#a8a29e", fontWeight: 700 }}>{populated}</span>
|
||||||
<span className="text-stone-300"> / </span>
|
<span className="text-stone-300"> / </span>
|
||||||
<span className="text-stone-400">{fields.length}</span>
|
<span className="text-stone-400">{fields.length}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-2 text-[13px] text-stone-400 leading-relaxed">
|
{interactive && (
|
||||||
|
<span className="shrink-0 text-stone-400 group-hover:text-stone-700 transition-colors">
|
||||||
|
{open ? <ChevronUp size={16} /> : <ChevronDown size={16} />}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isEmpty) {
|
||||||
|
return (
|
||||||
|
<div className="bg-white rounded-none overflow-hidden" style={{ border: CARD_BORDER }}>
|
||||||
|
{headerRow(false)}
|
||||||
|
<div className="px-6 pb-5 text-[13px] text-stone-400 leading-relaxed" style={{ marginTop: -8 }}>
|
||||||
{SECTION_EMPTY[sectionKey] ?? "Nothing recorded here yet."}
|
{SECTION_EMPTY[sectionKey] ?? "Nothing recorded here yet."}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -788,30 +886,12 @@ function SectionCard({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-white rounded-none" style={{ border: CARD_BORDER }}>
|
<div className="bg-white rounded-none overflow-hidden" style={{ border: CARD_BORDER }}>
|
||||||
<button
|
{headerRow(true)}
|
||||||
onClick={() => setOpen((o) => !o)}
|
|
||||||
className="w-full flex items-center justify-between text-left gap-4 group px-6 py-4"
|
|
||||||
style={{ borderBottom: open ? `1px solid ${TM_BORDER}` : "none" }}
|
|
||||||
>
|
|
||||||
<div className="min-w-0 flex items-baseline gap-3">
|
|
||||||
<div className="text-[18px] font-bold tracking-tight leading-none" style={{ color: INK }}>
|
|
||||||
{title}
|
|
||||||
</div>
|
|
||||||
<div className="text-[11.5px] font-medium tabular-nums">
|
|
||||||
<span style={{ color: populated > 0 ? ACCENT : "#a8a29e", fontWeight: 600 }}>{populated}</span>
|
|
||||||
<span className="text-stone-300"> / </span>
|
|
||||||
<span className="text-stone-400">{fields.length}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<span className="shrink-0 text-stone-400 group-hover:text-stone-600 transition-colors">
|
|
||||||
{open ? <ChevronUp size={16} /> : <ChevronDown size={16} />}
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
{open && (
|
{open && (
|
||||||
<dl className="px-6 py-2">
|
<dl className="px-6 py-2">
|
||||||
{fields.map((f, i) => {
|
{fields.map((f) => {
|
||||||
const raw = data[f.field_key];
|
const raw = data[f.field_key];
|
||||||
const empty = raw == null || raw === "" || raw === "—";
|
const empty = raw == null || raw === "" || raw === "—";
|
||||||
const text = empty ? "—" : fmtCellText(raw, f.data_type, f.field_key);
|
const text = empty ? "—" : fmtCellText(raw, f.data_type, f.field_key);
|
||||||
@ -819,8 +899,7 @@ function SectionCard({
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={f.field_key}
|
key={f.field_key}
|
||||||
className="flex items-baseline gap-6 py-2.5"
|
className="flex items-baseline gap-6 py-2"
|
||||||
style={{ borderTop: i === 0 ? "none" : `1px solid #f1f1f4` }}
|
|
||||||
>
|
>
|
||||||
<dt className="w-48 shrink-0 text-[11px] font-semibold uppercase tracking-[0.03em] text-stone-500">
|
<dt className="w-48 shrink-0 text-[11px] font-semibold uppercase tracking-[0.03em] text-stone-500">
|
||||||
{fmtLabel(f.output_label)}
|
{fmtLabel(f.output_label)}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user