fix(tracker): payment progress tracking fixes
This commit is contained in:
parent
13e41aec74
commit
34fcbb0d92
|
|
@ -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}
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue