fbe9940a3f
Distill the UI into a single, recognizable voice: a precision instrument for one operator on one machine. Bloomberg-coded chrome, warm-paper detail surfaces, mono-heavy numerics, with one editorial serif accent for moments of weight. Design system - Three-font stack: Geist Sans (UI), Geist Mono (data), Instrument Serif (editorial display). No Inter, no system fonts. - Two surfaces: dark chrome (--background, --accent, --signal) and warm paper detail surfaces (--surface, --surface-ink*). - Inbox has its own Ticker Tape terminal palette (--tt-*). - Shared component classes: .eyebrow, .mono, .display, .surface, .surface-2, .row-hover, .nav-active, .kbd, .editorial, .hairline. - --m-* token aliases for the legacy drawer components so test- asserted class strings keep resolving to the same hue family. Drawers (Claim + Remit) - Editorial display face for totals (paid/adjustment amounts). - Color-coded money tiles: green-tinted paid card, amber-tinted adjustment card when non-zero, muted otherwise. - Tabs get accent-blue underline + CSS-driven active state. - Validation banners with proper background opacity + ring-around- dot success badge. - StateHistoryTimeline: dashed border, ring around dots, ↳ prefix for remit ids. - DiagnosesList, PartiesGrid, MatchedRemitCard, CasAdjustmentsPanel: refined typography, dashed dividers, italic descriptions, mono amounts, font-semibold totals, hover row tints. Inbox Ticker Tape - Custom RowCheckbox with sr-only input + amber accent. - Alternating row striping, hover tints, accent rail with inset shadow on selection. - Refined sparkline with glow at high values. - BulkBar: bottom-floating bar with amber count chip, larger shadow. - CandidateBreakdown: animated progress bars with amber gradient. - InboxHeader: Instrument Serif headline, mono day/date stamp, pulsing amber status dot. Dialogs & search - NewClaimDialog: editorial title, mono NPI/CPT/amount fields. - SearchBar: refined input row with mono, footer with pulsing loading indicator. - KeyboardCheatsheet: monogram icon chip, hover row states, refined eyebrow header. Primitives - StatusBadge / ClaimStateBadge: per-state dot indicators. - SelectItem: data-[highlighted]:outline tokens for keyboard a11y. - Table primitives: refined header treatment, hover/focus states. Tests + build - 354/354 tests passing across 59 files. - Vite build clean (53.84 kB CSS / 560.86 kB JS). - Eyebrow assertions updated to match the consolidated .eyebrow class (intact visual contract, abstracted class string). - Badge variant tokens updated to the polished bg-muted/80 / /0.14 opacity scale.
160 lines
5.0 KiB
TypeScript
160 lines
5.0 KiB
TypeScript
import { fmt } from "@/lib/format";
|
|
import { cn } from "@/lib/utils";
|
|
import type { RemitDetail } from "@/hooks/useRemitDetail";
|
|
|
|
type FinancialSummaryCardProps = {
|
|
remit: RemitDetail;
|
|
};
|
|
|
|
/**
|
|
* Single label/value row used inside the financial summary card.
|
|
* `mono` opts the value into the project's mono numeric treatment
|
|
* (font-mono + tabular-nums + slightly heavier weight) so currency
|
|
* figures line up vertically across rows.
|
|
*/
|
|
function SummaryRow({
|
|
testId,
|
|
label,
|
|
value,
|
|
mono = false,
|
|
}: {
|
|
testId: string;
|
|
label: string;
|
|
value: React.ReactNode;
|
|
mono?: boolean;
|
|
}) {
|
|
return (
|
|
<div
|
|
data-testid={testId}
|
|
className="flex items-baseline justify-between gap-3 py-2"
|
|
>
|
|
<span className="eyebrow text-[color:var(--m-ink-tertiary)]">
|
|
{label}
|
|
</span>
|
|
<span
|
|
className={
|
|
mono
|
|
? "mono text-sm tabular-nums text-[color:var(--m-ink-primary)]"
|
|
: "text-sm text-[color:var(--m-ink-primary)]"
|
|
}
|
|
>
|
|
{value}
|
|
</span>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Optional value renderer: returns `—` for null/undefined/empty so the
|
|
* card never prints "null" or "undefined" in the UI.
|
|
*/
|
|
function opt(v: string | number | null | undefined, format?: (s: string) => string): React.ReactNode {
|
|
if (v === null || v === undefined) return "—";
|
|
if (typeof v === "string" && v.trim() === "") return "—";
|
|
if (typeof v === "number" && !Number.isFinite(v)) return "—";
|
|
return format ? format(String(v)) : v;
|
|
}
|
|
|
|
/**
|
|
* Financial summary card for the remittance detail drawer.
|
|
*
|
|
* Surfaces the headline money figures (paid amount + adjustment amount)
|
|
* plus the supporting payment identifiers (method, date, check/trace
|
|
* number). Headline amounts are stacked at the top so the eye lands on
|
|
* the totals first, then scans the supporting rows below.
|
|
*
|
|
* All fields gracefully degrade to "—" when absent — the detail
|
|
* endpoint doesn't yet populate `paymentMethod`, `paymentDate`, or
|
|
* `checkNumber` for every remit, and we'd rather show an em-dash than
|
|
* "undefined".
|
|
*/
|
|
export function FinancialSummaryCard({ remit }: FinancialSummaryCardProps) {
|
|
const hasAdjustment = Math.abs(remit.adjustmentAmount) > 0;
|
|
|
|
return (
|
|
<section
|
|
className="flex flex-col gap-3 px-6 py-4"
|
|
data-testid="financial-summary"
|
|
>
|
|
<h3
|
|
data-testid="section-label"
|
|
className="eyebrow text-[color:var(--m-ink-tertiary)]"
|
|
>
|
|
Financial Summary
|
|
</h3>
|
|
|
|
<div
|
|
data-testid="financial-summary-inner"
|
|
className="flex flex-col gap-4 rounded-lg border border-[color:var(--m-border-heavy)]/30 bg-[color:var(--m-surface)]/60 p-4"
|
|
>
|
|
{/* Headline totals — big display digits, side by side */}
|
|
<div className="grid grid-cols-1 gap-3 sm:grid-cols-2">
|
|
<div
|
|
data-testid="summary-paid"
|
|
className="flex flex-col gap-1.5 rounded-md border border-[color:var(--m-success)]/30 bg-[hsl(var(--success)/0.08)] px-3 py-2.5"
|
|
>
|
|
<span className="eyebrow text-[color:var(--m-success)]">
|
|
Paid amount
|
|
</span>
|
|
<span
|
|
className="display text-2xl tabular-nums text-[color:var(--m-success)]"
|
|
>
|
|
{fmt.usdPrecise(remit.paidAmount)}
|
|
</span>
|
|
</div>
|
|
<div
|
|
data-testid="summary-adjustment"
|
|
className={cn(
|
|
"flex flex-col gap-1.5 rounded-md border px-3 py-2.5",
|
|
hasAdjustment
|
|
? "border-[color:var(--m-warning)]/40 bg-[hsl(var(--warning)/0.10)]"
|
|
: "border-[color:var(--m-border-heavy)]/20 bg-[color:var(--m-surface)]/40"
|
|
)}
|
|
>
|
|
<span
|
|
className={cn(
|
|
"eyebrow",
|
|
hasAdjustment
|
|
? "text-[color:var(--m-warning)]"
|
|
: "text-[color:var(--m-ink-tertiary)]"
|
|
)}
|
|
>
|
|
Adjustment amount
|
|
</span>
|
|
<span
|
|
className={cn(
|
|
"display text-2xl tabular-nums",
|
|
hasAdjustment
|
|
? "text-[color:var(--m-warning)]"
|
|
: "text-[color:var(--m-ink-tertiary)]"
|
|
)}
|
|
>
|
|
{hasAdjustment ? fmt.usdPrecise(remit.adjustmentAmount) : "—"}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Supporting rows: method, payment date, check / trace number */}
|
|
<div className="flex flex-col divide-y divide-[color:var(--m-border-heavy)]/15">
|
|
<SummaryRow
|
|
testId="summary-method"
|
|
label="Payment method"
|
|
value={opt(remit.paymentMethod)}
|
|
/>
|
|
<SummaryRow
|
|
testId="summary-payment-date"
|
|
label="Payment date"
|
|
value={opt(remit.paymentDate, fmt.date)}
|
|
/>
|
|
<SummaryRow
|
|
testId="summary-check-number"
|
|
label="Check / trace number"
|
|
value={opt(remit.checkNumber)}
|
|
mono
|
|
/>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|