From 180ab517e65c931fae27b90c5d2160ee0f0bc57c Mon Sep 17 00:00:00 2001 From: lorentz Date: Sun, 3 May 2026 19:52:02 -0400 Subject: [PATCH] feat(05-01): create FinanceRow component - 2-line stacked row for invoice and payment lists (D-06) - Exports FinanceRow + FinanceRowProps per UI-SPEC contract - Supports amountTone destructive/positive/default variants - No priority stripe, no interactive handlers (read-only per D-22) - Phase comment block per Phase 3/4 convention --- components/mobile/FinanceRow.tsx | 43 ++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 components/mobile/FinanceRow.tsx diff --git a/components/mobile/FinanceRow.tsx b/components/mobile/FinanceRow.tsx new file mode 100644 index 0000000..b1aaaef --- /dev/null +++ b/components/mobile/FinanceRow.tsx @@ -0,0 +1,43 @@ +'use client'; + +/* FinanceRow โ€” phase 05 (FIN-02). + * Purpose: 2-line stacked row used by Open Invoices and Recent Payments lists. + * Line 1: primary label (left, text-sm font-semibold truncate) + amount (right, text-sm font-semibold). + * Line 2: secondary metadata (left, text-xs text-muted-foreground) + optional date (right, text-xs text-muted-foreground). + * Pure presentational โ€” parent owns the data. + * Props: see FinanceRowProps. */ + +import { cn } from '@/lib/utils'; + +export interface FinanceRowProps { + /** Primary label on line 1 (e.g., customer name) */ + primary: string; + /** Amount string already formatted (e.g., "$1,234") โ€” caller passes fmt$(value) */ + amount: string; + /** Secondary metadata on line 2 (e.g., "#1234 ยท Due May 1" or just a date) */ + secondary?: React.ReactNode; + /** Optional right-side date on line 2 (used by payment rows; invoice rows put date in `secondary`) */ + rightSecondary?: React.ReactNode; + /** When true, amount renders in destructive color (overdue invoices). */ + amountTone?: 'default' | 'destructive' | 'positive'; +} + +export function FinanceRow({ primary, amount, secondary, rightSecondary, amountTone = 'default' }: FinanceRowProps) { + const amountClass = cn( + 'text-sm font-semibold shrink-0', + amountTone === 'destructive' && 'text-destructive', + amountTone === 'positive' && 'text-emerald-600' + ); + return ( +
+
+

{primary}

+ {secondary &&

{secondary}

} +
+
+

{amount}

+ {rightSecondary &&

{rightSecondary}

} +
+
+ ); +}