fix(tracker): budget display and payment progress fixes

This commit is contained in:
null 2026-06-07 17:03:29 -05:00
parent 71e783a799
commit 955fb96aec
3 changed files with 29 additions and 29 deletions

View File

@ -25,12 +25,12 @@ export function PaymentProgress({ row, threshold, onOpen, onMarkFullAmount, comp
onClick={onOpen}
className={cn(
'w-full rounded-md text-left transition-colors hover:bg-accent/60 focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50',
compact ? 'p-2' : 'px-2 py-1.5',
compact ? 'p-2' : 'px-2 py-1',
)}
title="View payment history"
>
<div className="flex items-center justify-between gap-2 text-xs">
<span className={cn('tracker-number text-[13px] font-semibold', summary.paid > 0 ? 'text-emerald-300' : 'text-muted-foreground/85')}>
<span className={cn('tracker-number text-xs font-semibold', summary.paid > 0 ? 'text-emerald-300' : 'text-muted-foreground/85')}>
{amountLabel}
</span>
{summary.count > 1 && (

View File

@ -15,13 +15,13 @@ function SortableHead({ sortKey, activeSortKey, sortDir, onSort, children, class
return (
<TableHead
aria-sort={active ? (sortDir === TRACKER_SORT_ASC ? 'ascending' : 'descending') : 'none'}
className={cn('py-2.5', className)}
className={cn('h-11 py-2.5', className)}
>
<button
type="button"
onClick={() => onSort?.(sortKey)}
className={cn(
'group inline-flex h-8 items-center gap-1.5 rounded-md px-2 text-xs font-medium uppercase tracking-wider transition-all',
'group inline-flex h-8 items-center gap-1.5 rounded-md px-2 text-[11px] font-semibold uppercase tracking-[0.08em] transition-all',
'hover:bg-accent hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/60',
active ? 'bg-accent/70 text-foreground shadow-sm' : 'text-muted-foreground'
)}
@ -217,15 +217,15 @@ export function TrackerBucket({ label, rows, year, month, refresh, onEditBill, l
<TableRow className="border-border/80 bg-background/30 hover:bg-background/30">
<SortableHead sortKey="name" activeSortKey={sortKey} sortDir={sortDir} onSort={onSort} className="w-[18%]">Bill</SortableHead>
<SortableHead sortKey="due" activeSortKey={sortKey} sortDir={sortDir} onSort={onSort} className="w-[10%]">Due</SortableHead>
<SortableHead sortKey="expected" activeSortKey={sortKey} sortDir={sortDir} onSort={onSort} className="w-[10%] text-right">Expected</SortableHead>
<SortableHead sortKey="previous" activeSortKey={sortKey} sortDir={sortDir} onSort={onSort} className="w-[10%] text-right">Last Month</SortableHead>
<SortableHead sortKey="expected" activeSortKey={sortKey} sortDir={sortDir} onSort={onSort} className="w-[10%] text-center">Expected</SortableHead>
<SortableHead sortKey="previous" activeSortKey={sortKey} sortDir={sortDir} onSort={onSort} className="w-[10%] text-center">Last Month</SortableHead>
<SortableHead sortKey="paid" activeSortKey={sortKey} sortDir={sortDir} onSort={onSort} className="w-[10%] text-center">Paid</SortableHead>
<SortableHead sortKey="paid_date" activeSortKey={sortKey} sortDir={sortDir} onSort={onSort} className="w-[10%]">Paid Date</SortableHead>
<SortableHead sortKey="status" activeSortKey={sortKey} sortDir={sortDir} onSort={onSort} className="w-[9%] text-center">Status</SortableHead>
<TableHead className="w-[10%] py-2.5 text-center text-xs font-medium uppercase tracking-wider text-muted-foreground">
<TableHead className="h-11 w-[10%] py-2.5 text-center text-[11px] font-semibold uppercase tracking-[0.08em] text-muted-foreground">
Action
</TableHead>
<TableHead className="w-[23%] py-2.5 text-xs font-medium uppercase tracking-wider text-muted-foreground border-l border-border/80 pl-4">
<TableHead className="h-11 w-[23%] py-2.5 text-[11px] font-semibold uppercase tracking-[0.08em] text-muted-foreground border-l border-border/80 pl-4">
Notes
</TableHead>
</TableRow>
@ -241,8 +241,8 @@ export function TrackerBucket({ label, rows, year, month, refresh, onEditBill, l
</div>
</TableCell>
<TableCell className="w-[10%] py-3"><div className="h-3 w-20 rounded-md bg-muted" /></TableCell>
<TableCell className="w-[10%] py-3 text-right"><div className="h-3 w-20 ml-auto rounded-md bg-muted" /></TableCell>
<TableCell className="w-[10%] py-3 text-right"><div className="h-3 w-20 ml-auto rounded-md bg-muted" /></TableCell>
<TableCell className="w-[10%] py-3 text-center"><div className="mx-auto h-3 w-20 rounded-md bg-muted" /></TableCell>
<TableCell className="w-[10%] py-3 text-center"><div className="mx-auto h-3 w-20 rounded-md bg-muted" /></TableCell>
<TableCell className="w-[10%] py-3"><div className="mx-auto h-7 w-24 rounded-md bg-muted" /></TableCell>
<TableCell className="w-[10%] py-3"><div className="h-7 w-24 rounded-md bg-muted" /></TableCell>
<TableCell className="w-[9%] py-3"><div className="mx-auto h-5 w-20 rounded-md bg-muted" /></TableCell>

View File

@ -305,12 +305,12 @@ export function TrackerRow({ row, year, month, refresh, index, onEditBill, moveC
style={{ animationDelay: `${index * 40}ms` }}
>
{/* Bill name + category + monthly notes (if set) */}
<TableCell className="w-[18%] py-3">
<TableCell className="w-[18%] py-2.5">
<div className="flex min-w-0 items-center gap-2.5">
<div className="flex shrink-0 items-center gap-0.5">
<GripVertical
className={cn(
'h-4 w-4 text-muted-foreground/55',
'h-3.5 w-3.5 text-muted-foreground/55',
moveControls?.enabled && 'cursor-grab group-active:cursor-grabbing',
!moveControls?.enabled && 'opacity-30',
)}
@ -347,7 +347,7 @@ export function TrackerRow({ row, year, month, refresh, index, onEditBill, moveC
target="_blank"
rel="noreferrer"
className={cn(
'min-w-0 truncate text-base font-bold leading-tight text-foreground transition-colors',
'min-w-0 truncate text-sm font-semibold leading-tight text-foreground transition-colors',
'hover:underline decoration-muted-foreground/50 underline-offset-2',
isSkipped && 'line-through',
)}
@ -355,7 +355,7 @@ export function TrackerRow({ row, year, month, refresh, index, onEditBill, moveC
{row.name}
</a>
) : (
<span className={cn('min-w-0 truncate text-base font-bold leading-tight text-foreground', isSkipped && 'line-through')}>
<span className={cn('min-w-0 truncate text-sm font-semibold leading-tight text-foreground', isSkipped && 'line-through')}>
{row.name}
</span>
)}
@ -427,7 +427,7 @@ export function TrackerRow({ row, year, month, refresh, index, onEditBill, moveC
</TooltipProvider>
<Button
size="icon" variant="ghost"
className="h-7 w-7 shrink-0 opacity-100 transition-opacity text-muted-foreground hover:text-foreground hover:bg-accent lg:opacity-0 lg:group-hover:opacity-100"
className="h-6 w-6 shrink-0 opacity-100 transition-opacity text-muted-foreground hover:text-foreground hover:bg-accent lg:opacity-0 lg:group-hover:opacity-100"
title="Edit bill"
onClick={() => onEditBill?.(row)}
>
@ -435,7 +435,7 @@ export function TrackerRow({ row, year, month, refresh, index, onEditBill, moveC
</Button>
</div>
{row.category_name && (
<p className="mt-1 truncate text-[11px] font-medium leading-none text-muted-foreground/75">{row.category_name}</p>
<p className="mt-0.5 truncate text-[11px] font-medium leading-none text-muted-foreground/75">{row.category_name}</p>
)}
{/* Monthly notes shown inline under the bill name */}
{row.monthly_notes && (
@ -449,7 +449,7 @@ export function TrackerRow({ row, year, month, refresh, index, onEditBill, moveC
</TableCell>
{/* Due */}
<TableCell className="tracker-number w-[10%] py-3 text-[13px] font-medium text-foreground/75">
<TableCell className="tracker-number w-[10%] py-2.5 text-[13px] font-medium text-foreground/75">
{editingDue ? (
<input
type="number"
@ -478,7 +478,7 @@ export function TrackerRow({ row, year, month, refresh, index, onEditBill, moveC
</TableCell>
{/* Expected / Actual — shows actual_amount in amber when it overrides the template */}
<TableCell className="tracker-number w-[10%] py-3 text-right text-[13px] font-semibold">
<TableCell className="tracker-number w-[10%] py-2.5 text-center text-[13px] font-semibold">
{editingExpected ? (
<input
type="number"
@ -491,23 +491,23 @@ export function TrackerRow({ row, year, month, refresh, index, onEditBill, moveC
if (e.key === 'Enter') e.currentTarget.blur();
if (e.key === 'Escape') { setEditingExpected(false); }
}}
className="tracker-number w-24 rounded border border-border bg-transparent px-1 py-0.5 text-right text-sm font-semibold text-foreground outline-none focus:ring-[2px] focus:ring-ring/50"
className="tracker-number mx-auto w-24 rounded border border-border bg-transparent px-1 py-0.5 text-center text-sm font-semibold text-foreground outline-none focus:ring-[2px] focus:ring-ring/50"
/>
) : effectiveActual != null ? (
<button
type="button"
onClick={() => { setExpectedDraft(String(effectiveActual)); setEditingExpected(true); }}
className="rounded px-1 py-0.5 text-amber-300 transition-colors hover:bg-accent"
className="mx-auto rounded px-1 py-0.5 text-amber-300 transition-colors hover:bg-accent"
title={`Monthly override — click to edit. Template default: ${fmt(row.expected_amount)}`}
>
{fmt(effectiveActual)}
</button>
) : (
<div className="flex flex-col items-end gap-0.5">
<div className="mx-auto flex w-[68px] flex-col items-center gap-0.5">
<button
type="button"
onClick={() => { setExpectedDraft(String(row.expected_amount)); setEditingExpected(true); }}
className="rounded px-1 py-0.5 text-foreground/85 transition-colors hover:bg-accent hover:text-foreground"
className="rounded px-1 py-0.5 text-center text-foreground/85 transition-colors hover:bg-accent hover:text-foreground"
title="Click to edit expected amount"
>
{fmt(row.expected_amount)}
@ -522,7 +522,7 @@ export function TrackerRow({ row, year, month, refresh, index, onEditBill, moveC
const min = Math.min(...vals);
const max = Math.max(...vals);
const range = max - min || 1;
const W = 44, H = 16;
const W = 40, H = 12;
const pts = vals.map((v, i) => {
const x = (i / (vals.length - 1)) * W;
const y = H - ((v - min) / range) * H;
@ -550,12 +550,12 @@ export function TrackerRow({ row, year, month, refresh, index, onEditBill, moveC
</TableCell>
{/* Previous month paid */}
<TableCell className="tracker-number w-[10%] py-3 text-right text-[13px] font-medium text-muted-foreground/80">
<TableCell className="tracker-number w-[10%] py-2.5 text-center text-[13px] font-medium text-muted-foreground/80">
{row.previous_month_paid > 0 ? fmt(row.previous_month_paid) : '—'}
</TableCell>
{/* Amount paid — mismatch now compares against threshold */}
<TableCell className="w-[10%] py-3 text-center">
<TableCell className="w-[10%] py-2.5 text-center">
<PaymentProgress
row={row}
threshold={threshold}
@ -566,7 +566,7 @@ export function TrackerRow({ row, year, month, refresh, index, onEditBill, moveC
</TableCell>
{/* Paid date */}
<TableCell className="w-[10%] py-3 text-[13px] text-foreground/75">
<TableCell className="w-[10%] py-2.5 text-[13px] text-foreground/75">
<button
type="button"
onClick={() => setPaymentLedgerOpen(true)}
@ -579,7 +579,7 @@ export function TrackerRow({ row, year, month, refresh, index, onEditBill, moveC
</TableCell>
{/* Status — uses effectiveStatus (accounts for skipped + threshold) */}
<TableCell className="w-[9%] py-3">
<TableCell className="w-[9%] py-2.5">
<div className="flex flex-col items-center gap-1">
<StatusBadge
status={effectiveStatus}
@ -602,7 +602,7 @@ export function TrackerRow({ row, year, month, refresh, index, onEditBill, moveC
</TableCell>
{/* Actions */}
<TableCell className="w-[10%] py-3 text-center">
<TableCell className="w-[10%] py-2.5 text-center">
<div className="flex items-center justify-center gap-1">
{showUpdateNudge ? (
<div className="flex items-center gap-1 animate-in fade-in slide-in-from-right-1 duration-200">
@ -658,7 +658,7 @@ export function TrackerRow({ row, year, month, refresh, index, onEditBill, moveC
</TableCell>
{/* Notes cell (monthly state notes) */}
<TableCell className="w-[23%] py-3 border-l border-border pl-4">
<TableCell className="w-[23%] py-2.5 border-l border-border pl-4">
<NotesCell row={{ ...row, year, month }} refresh={refresh} />
</TableCell>
</TableRow>