fix(tracker): payment progress tracking fixes

This commit is contained in:
null 2026-06-07 16:44:40 -05:00
parent 13e41aec74
commit 34fcbb0d92
3 changed files with 23 additions and 20 deletions

View File

@ -1,7 +1,7 @@
import { cn, fmt } from '@/lib/utils'; import { cn, fmt } from '@/lib/utils';
import { paymentSummary } from '@/lib/trackerUtils'; import { paymentSummary } from '@/lib/trackerUtils';
export function PaymentProgress({ row, threshold, onOpen, onMarkFullAmount, compact = false }) { export function PaymentProgress({ row, threshold, onOpen, onMarkFullAmount, compact = false, className }) {
const summary = paymentSummary(row, threshold); const summary = paymentSummary(row, threshold);
const barTone = summary.remaining === 0 const barTone = summary.remaining === 0
? 'bg-emerald-500' ? 'bg-emerald-500'
@ -19,7 +19,7 @@ export function PaymentProgress({ row, threshold, onOpen, onMarkFullAmount, comp
const showQuickFix = onMarkFullAmount && summary.partial && summary.paid > 0; const showQuickFix = onMarkFullAmount && summary.partial && summary.paid > 0;
return ( return (
<div> <div className={cn('w-full', className)}>
<button <button
type="button" type="button"
onClick={onOpen} onClick={onOpen}

View File

@ -219,10 +219,12 @@ export function TrackerBucket({ label, rows, year, month, refresh, onEditBill, l
<SortableHead sortKey="due" activeSortKey={sortKey} sortDir={sortDir} onSort={onSort} className="w-[10%]">Due</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="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="previous" activeSortKey={sortKey} sortDir={sortDir} onSort={onSort} className="w-[10%] text-right">Last Month</SortableHead>
<SortableHead sortKey="paid" activeSortKey={sortKey} sortDir={sortDir} onSort={onSort} className="w-[10%] text-right">Paid</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="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%]">Status</SortableHead> <SortableHead sortKey="status" activeSortKey={sortKey} sortDir={sortDir} onSort={onSort} className="w-[9%] text-center">Status</SortableHead>
<TableHead className="w-[10%] py-2.5" /> <TableHead className="w-[10%] py-2.5 text-center text-xs font-medium uppercase tracking-wider 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="w-[23%] py-2.5 text-xs font-medium uppercase tracking-wider text-muted-foreground border-l border-border/80 pl-4">
Notes Notes
</TableHead> </TableHead>
@ -241,11 +243,11 @@ export function TrackerBucket({ label, rows, year, month, refresh, onEditBill, l
<TableCell className="w-[10%] py-3"><div className="h-3 w-20 rounded-md bg-muted" /></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-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"><div className="h-7 w-24 ml-auto 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-[10%] py-3"><div className="h-7 w-24 rounded-md bg-muted" /></TableCell>
<TableCell className="w-[9%] py-3"><div className="h-5 w-20 rounded-md bg-muted" /></TableCell> <TableCell className="w-[9%] py-3"><div className="mx-auto h-5 w-20 rounded-md bg-muted" /></TableCell>
<TableCell className="w-[10%] py-3 text-right"> <TableCell className="w-[10%] py-3 text-center">
<div className="flex items-center justify-end gap-1"> <div className="flex items-center justify-center gap-1">
<div className="h-7 w-20 rounded-md bg-muted" /> <div className="h-7 w-20 rounded-md bg-muted" />
<div className="h-7 w-7 rounded-md bg-muted" /> <div className="h-7 w-7 rounded-md bg-muted" />
</div> </div>

View File

@ -306,7 +306,7 @@ export function TrackerRow({ row, year, month, refresh, index, onEditBill, moveC
> >
{/* Bill name + category + monthly notes (if set) */} {/* Bill name + category + monthly notes (if set) */}
<TableCell className="w-[18%] py-3"> <TableCell className="w-[18%] py-3">
<div className="flex items-center gap-2.5"> <div className="flex min-w-0 items-center gap-2.5">
<div className="flex shrink-0 items-center gap-0.5"> <div className="flex shrink-0 items-center gap-0.5">
<GripVertical <GripVertical
className={cn( className={cn(
@ -339,15 +339,15 @@ export function TrackerRow({ row, year, month, refresh, index, onEditBill, moveC
</button> </button>
</div> </div>
</div> </div>
<div> <div className="min-w-0">
<div className="flex items-center gap-1"> <div className="flex min-w-0 items-center gap-1.5">
{row.website ? ( {row.website ? (
<a <a
href={row.website} href={row.website}
target="_blank" target="_blank"
rel="noreferrer" rel="noreferrer"
className={cn( className={cn(
'text-[15px] font-semibold leading-tight text-foreground transition-colors', 'min-w-0 truncate text-base font-bold leading-tight text-foreground transition-colors',
'hover:underline decoration-muted-foreground/50 underline-offset-2', 'hover:underline decoration-muted-foreground/50 underline-offset-2',
isSkipped && 'line-through', isSkipped && 'line-through',
)} )}
@ -355,7 +355,7 @@ export function TrackerRow({ row, year, month, refresh, index, onEditBill, moveC
{row.name} {row.name}
</a> </a>
) : ( ) : (
<span className={cn('text-[15px] font-semibold leading-tight text-foreground', isSkipped && 'line-through')}> <span className={cn('min-w-0 truncate text-base font-bold leading-tight text-foreground', isSkipped && 'line-through')}>
{row.name} {row.name}
</span> </span>
)} )}
@ -427,7 +427,7 @@ export function TrackerRow({ row, year, month, refresh, index, onEditBill, moveC
</TooltipProvider> </TooltipProvider>
<Button <Button
size="icon" variant="ghost" size="icon" variant="ghost"
className="h-7 w-7 opacity-100 transition-opacity text-muted-foreground hover:text-foreground hover:bg-accent lg:opacity-0 lg:group-hover:opacity-100" 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"
title="Edit bill" title="Edit bill"
onClick={() => onEditBill?.(row)} onClick={() => onEditBill?.(row)}
> >
@ -435,7 +435,7 @@ export function TrackerRow({ row, year, month, refresh, index, onEditBill, moveC
</Button> </Button>
</div> </div>
{row.category_name && ( {row.category_name && (
<p className="mt-0.5 text-xs text-muted-foreground/85">{row.category_name}</p> <p className="mt-1 truncate text-[11px] font-medium leading-none text-muted-foreground/75">{row.category_name}</p>
)} )}
{/* Monthly notes shown inline under the bill name */} {/* Monthly notes shown inline under the bill name */}
{row.monthly_notes && ( {row.monthly_notes && (
@ -555,10 +555,11 @@ export function TrackerRow({ row, year, month, refresh, index, onEditBill, moveC
</TableCell> </TableCell>
{/* Amount paid — mismatch now compares against threshold */} {/* Amount paid — mismatch now compares against threshold */}
<TableCell className="w-[10%] py-3 text-right"> <TableCell className="w-[10%] py-3 text-center">
<PaymentProgress <PaymentProgress
row={row} row={row}
threshold={threshold} threshold={threshold}
className="mx-auto max-w-[7.5rem]"
onOpen={() => setPaymentLedgerOpen(true)} onOpen={() => setPaymentLedgerOpen(true)}
onMarkFullAmount={!isSkipped ? handleMarkFullAmount : undefined} onMarkFullAmount={!isSkipped ? handleMarkFullAmount : undefined}
/> />
@ -579,7 +580,7 @@ export function TrackerRow({ row, year, month, refresh, index, onEditBill, moveC
{/* Status — uses effectiveStatus (accounts for skipped + threshold) */} {/* Status — uses effectiveStatus (accounts for skipped + threshold) */}
<TableCell className="w-[9%] py-3"> <TableCell className="w-[9%] py-3">
<div className="flex flex-col items-start gap-1"> <div className="flex flex-col items-center gap-1">
<StatusBadge <StatusBadge
status={effectiveStatus} status={effectiveStatus}
clickable clickable
@ -601,8 +602,8 @@ export function TrackerRow({ row, year, month, refresh, index, onEditBill, moveC
</TableCell> </TableCell>
{/* Actions */} {/* Actions */}
<TableCell className="w-[10%] py-3 text-right"> <TableCell className="w-[10%] py-3 text-center">
<div className="flex items-center justify-end gap-1"> <div className="flex items-center justify-center gap-1">
{showUpdateNudge ? ( {showUpdateNudge ? (
<div className="flex items-center gap-1 animate-in fade-in slide-in-from-right-1 duration-200"> <div className="flex items-center gap-1 animate-in fade-in slide-in-from-right-1 duration-200">
<span className="text-[10px] text-muted-foreground">Update default?</span> <span className="text-[10px] text-muted-foreground">Update default?</span>