feat(frontend): RemitDrawer component with CAS adjustments + parties + claim payments
This commit is contained in:
@@ -0,0 +1,148 @@
|
||||
import { fmt } from "@/lib/format";
|
||||
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="text-[11px] font-medium uppercase tracking-[0.14em] text-[color:var(--m-ink-tertiary)]">
|
||||
{label}
|
||||
</span>
|
||||
<span
|
||||
className={
|
||||
mono
|
||||
? "font-mono text-base tabular-nums text-[color:var(--m-ink-primary)]"
|
||||
: "text-sm text-[color:var(--m-ink-primary)]"
|
||||
}
|
||||
style={mono ? { fontFamily: "var(--m-font-mono)" } : undefined}
|
||||
>
|
||||
{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="text-[10.5px] font-semibold uppercase tracking-[0.18em] 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)]/15 bg-[color:var(--m-surface)] p-4"
|
||||
>
|
||||
{/* Headline totals — big mono digits, side by side */}
|
||||
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
|
||||
<div
|
||||
data-testid="summary-paid"
|
||||
className="flex flex-col gap-1 rounded-md bg-[color:var(--m-surface)]/40 px-3 py-2"
|
||||
>
|
||||
<span className="text-[10px] font-medium uppercase tracking-[0.14em] text-[color:var(--m-ink-tertiary)]">
|
||||
Paid amount
|
||||
</span>
|
||||
<span
|
||||
className="font-mono text-2xl font-semibold tabular-nums text-[color:var(--m-ink-primary)]"
|
||||
style={{ fontFamily: "var(--m-font-mono)" }}
|
||||
>
|
||||
{fmt.usdPrecise(remit.paidAmount)}
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
data-testid="summary-adjustment"
|
||||
className="flex flex-col gap-1 rounded-md bg-[color:var(--m-surface)]/40 px-3 py-2"
|
||||
>
|
||||
<span className="text-[10px] font-medium uppercase tracking-[0.14em] text-[color:var(--m-ink-tertiary)]">
|
||||
Adjustment amount
|
||||
</span>
|
||||
<span
|
||||
className={
|
||||
hasAdjustment
|
||||
? "font-mono text-2xl font-semibold tabular-nums text-[color:var(--m-ink-primary)]"
|
||||
: "font-mono text-2xl tabular-nums text-[color:var(--m-ink-tertiary)]"
|
||||
}
|
||||
style={{ fontFamily: "var(--m-font-mono)" }}
|
||||
>
|
||||
{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)]/10">
|
||||
<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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user