chore: bump version to v0.28.4.4

This commit is contained in:
null 2026-05-28 20:14:00 -05:00
parent 3b44fe3cbc
commit 92cc667947
4 changed files with 41 additions and 33 deletions

View File

@ -108,7 +108,7 @@ function EditableCell({ row, field, threshold, defaultPaymentDate, refresh }) {
onChange={e => setValue(e.target.value)} onChange={e => setValue(e.target.value)}
onBlur={commit} onBlur={commit}
onKeyDown={onKeyDown} onKeyDown={onKeyDown}
className="h-7 w-28 text-right font-mono text-sm bg-background/80 border-border/60" className="tracker-number h-7 w-28 text-right text-sm font-medium bg-background/80 border-border/60"
/> />
); );
} }
@ -118,7 +118,7 @@ function EditableCell({ row, field, threshold, defaultPaymentDate, refresh }) {
onClick={startEdit} onClick={startEdit}
title={`Click to edit ${field === 'amount' ? 'payment amount' : 'paid date'}`} title={`Click to edit ${field === 'amount' ? 'payment amount' : 'paid date'}`}
className={cn( className={cn(
'cursor-pointer rounded-md px-1.5 py-0.5 text-sm font-mono', 'tracker-number cursor-pointer rounded-md px-1.5 py-0.5 text-sm font-semibold',
'transition-all duration-150 hover:bg-accent hover:ring-1 hover:ring-border', 'transition-all duration-150 hover:bg-accent hover:ring-1 hover:ring-border',
isEmpty && 'text-muted-foreground', isEmpty && 'text-muted-foreground',
mismatch && 'text-amber-500', mismatch && 'text-amber-500',
@ -185,7 +185,7 @@ export const MobileTrackerRow = React.memo(function MobileTrackerRow({ row, year
type="button" type="button"
onClick={() => onEditBill?.(row)} onClick={() => onEditBill?.(row)}
className={cn( className={cn(
'min-w-0 truncate text-left text-sm font-semibold leading-tight text-foreground', 'min-w-0 truncate text-left text-[15px] font-semibold leading-tight text-foreground',
'underline-offset-2 transition-colors hover:text-primary hover:underline', 'underline-offset-2 transition-colors hover:text-primary hover:underline',
isSkipped && 'line-through', isSkipped && 'line-through',
)} )}
@ -206,7 +206,7 @@ export const MobileTrackerRow = React.memo(function MobileTrackerRow({ row, year
<div className="grid grid-cols-2 gap-x-3 gap-y-2 text-xs text-muted-foreground sm:grid-cols-4"> <div className="grid grid-cols-2 gap-x-3 gap-y-2 text-xs text-muted-foreground sm:grid-cols-4">
<div> <div>
<p className="uppercase tracking-wide text-muted-foreground/60">Due</p> <p className="uppercase tracking-wide text-muted-foreground/60">Due</p>
<p className="mt-0.5 font-mono text-sm text-foreground">{fmtDate(row.due_date)}</p> <p className="tracker-number mt-0.5 text-sm font-medium text-foreground/90">{fmtDate(row.due_date)}</p>
</div> </div>
<div> <div>
<p className="uppercase tracking-wide text-muted-foreground/60">Category</p> <p className="uppercase tracking-wide text-muted-foreground/60">Category</p>
@ -214,13 +214,13 @@ export const MobileTrackerRow = React.memo(function MobileTrackerRow({ row, year
</div> </div>
<div> <div>
<p className="uppercase tracking-wide text-muted-foreground/60">Expected</p> <p className="uppercase tracking-wide text-muted-foreground/60">Expected</p>
<p className={cn('mt-0.5 font-mono text-sm', row.actual_amount != null ? 'text-amber-500' : 'text-foreground')}> <p className={cn('tracker-number mt-0.5 text-sm font-semibold', row.actual_amount != null ? 'text-amber-300' : 'text-foreground')}>
{fmt(threshold)} {fmt(threshold)}
</p> </p>
</div> </div>
<div> <div>
<p className="uppercase tracking-wide text-muted-foreground/60">Remaining</p> <p className="uppercase tracking-wide text-muted-foreground/60">Remaining</p>
<p className={cn('mt-0.5 font-mono text-sm', remaining > 0 ? 'text-foreground' : 'text-emerald-500')}> <p className={cn('tracker-number mt-0.5 text-sm font-semibold', remaining > 0 ? 'text-foreground' : 'text-emerald-300')}>
{fmt(remaining)} {fmt(remaining)}
</p> </p>
</div> </div>
@ -230,11 +230,11 @@ export const MobileTrackerRow = React.memo(function MobileTrackerRow({ row, year
<div className="grid grid-cols-2 gap-2 text-xs sm:flex sm:items-center"> <div className="grid grid-cols-2 gap-2 text-xs sm:flex sm:items-center">
<div className="rounded-md bg-muted/45 px-2 py-1.5"> <div className="rounded-md bg-muted/45 px-2 py-1.5">
<span className="text-muted-foreground">Paid </span> <span className="text-muted-foreground">Paid </span>
<span className="font-mono text-emerald-500">{row.total_paid > 0 ? fmt(row.total_paid) : '—'}</span> <span className="tracker-number font-semibold text-emerald-300">{row.total_paid > 0 ? fmt(row.total_paid) : '—'}</span>
</div> </div>
<div className="rounded-md bg-muted/45 px-2 py-1.5"> <div className="rounded-md bg-muted/45 px-2 py-1.5">
<span className="text-muted-foreground">Date </span> <span className="text-muted-foreground">Date </span>
<span className="font-mono text-foreground">{row.last_paid_date ? fmtDate(row.last_paid_date) : '—'}</span> <span className="tracker-number font-medium text-foreground">{row.last_paid_date ? fmtDate(row.last_paid_date) : '—'}</span>
</div> </div>
</div> </div>
@ -245,7 +245,7 @@ export const MobileTrackerRow = React.memo(function MobileTrackerRow({ row, year
ref={amountRef} ref={amountRef}
type="number" min="0" step="0.01" type="number" min="0" step="0.01"
defaultValue={threshold} defaultValue={threshold}
className="h-8 w-24 text-right font-mono text-sm bg-background/70 border-border/60" className="tracker-number h-8 w-24 text-right text-sm font-medium bg-background/70 border-border/60"
title="Payment amount" title="Payment amount"
aria-label={`${row.name} payment amount`} aria-label={`${row.name} payment amount`}
/> />

View File

@ -150,6 +150,14 @@
@apply surface overflow-hidden shadow-sm; @apply surface overflow-hidden shadow-sm;
} }
.tracker-number {
font-family: Inter, Roboto, ui-sans-serif, system-ui, sans-serif;
font-variant-numeric: tabular-nums lining-nums;
font-feature-settings: "tnum" 1, "lnum" 1;
-webkit-font-smoothing: auto;
text-rendering: optimizeLegibility;
}
/* Custom Scrollbar */ /* Custom Scrollbar */
.scrollbar-thin { .scrollbar-thin {
scrollbar-width: thin; scrollbar-width: thin;

View File

@ -477,7 +477,7 @@ function PaymentProgress({ row, threshold, onOpen, onMarkFullAmount, compact = f
title="View payment history" title="View payment history"
> >
<div className="flex items-center justify-between gap-2 text-xs"> <div className="flex items-center justify-between gap-2 text-xs">
<span className={cn('font-mono', summary.paid > 0 ? 'text-emerald-500' : 'text-muted-foreground')}> <span className={cn('tracker-number text-[13px] font-semibold', summary.paid > 0 ? 'text-emerald-300' : 'text-muted-foreground/85')}>
{amountLabel} {amountLabel}
</span> </span>
{summary.count > 1 && ( {summary.count > 1 && (
@ -1456,7 +1456,7 @@ function Row({ row, year, month, refresh, index, onEditBill }) {
target="_blank" target="_blank"
rel="noreferrer" rel="noreferrer"
className={cn( className={cn(
'font-medium text-sm leading-tight transition-colors', 'text-[15px] font-semibold 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',
)} )}
@ -1464,7 +1464,7 @@ function Row({ row, year, month, refresh, index, onEditBill }) {
{row.name} {row.name}
</a> </a>
) : ( ) : (
<span className={cn('font-medium text-sm leading-tight', isSkipped && 'line-through')}> <span className={cn('text-[15px] font-semibold leading-tight text-foreground', isSkipped && 'line-through')}>
{row.name} {row.name}
</span> </span>
)} )}
@ -1478,7 +1478,7 @@ function Row({ row, year, month, refresh, index, onEditBill }) {
</Button> </Button>
</div> </div>
{row.category_name && ( {row.category_name && (
<p className="text-[11px] text-muted-foreground mt-0.5">{row.category_name}</p> <p className="mt-0.5 text-xs text-muted-foreground/85">{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 && (
@ -1492,7 +1492,7 @@ function Row({ row, year, month, refresh, index, onEditBill }) {
</TableCell> </TableCell>
{/* Due */} {/* Due */}
<TableCell className="w-[10%] py-3 text-sm font-mono text-muted-foreground"> <TableCell className="tracker-number w-[10%] py-3 text-[13px] font-medium text-foreground/75">
{editingDue ? ( {editingDue ? (
<input <input
type="number" type="number"
@ -1505,7 +1505,7 @@ function Row({ row, year, month, refresh, index, onEditBill }) {
if (e.key === 'Enter') e.currentTarget.blur(); if (e.key === 'Enter') e.currentTarget.blur();
if (e.key === 'Escape') { setEditingDue(false); } if (e.key === 'Escape') { setEditingDue(false); }
}} }}
className="w-12 rounded border border-border bg-transparent px-1 py-0.5 text-sm font-mono text-foreground outline-none focus:ring-[2px] focus:ring-ring/50" className="tracker-number w-12 rounded border border-border bg-transparent px-1 py-0.5 text-sm font-medium text-foreground outline-none focus:ring-[2px] focus:ring-ring/50"
title="Day of month (131)" title="Day of month (131)"
/> />
) : ( ) : (
@ -1521,7 +1521,7 @@ function Row({ row, year, month, refresh, index, onEditBill }) {
</TableCell> </TableCell>
{/* Expected / Actual — shows actual_amount in amber when it overrides the template */} {/* Expected / Actual — shows actual_amount in amber when it overrides the template */}
<TableCell className="w-[10%] py-3 text-right font-mono text-sm"> <TableCell className="tracker-number w-[10%] py-3 text-right text-[13px] font-semibold">
{editingExpected ? ( {editingExpected ? (
<input <input
type="number" type="number"
@ -1534,13 +1534,13 @@ function Row({ row, year, month, refresh, index, onEditBill }) {
if (e.key === 'Enter') e.currentTarget.blur(); if (e.key === 'Enter') e.currentTarget.blur();
if (e.key === 'Escape') { setEditingExpected(false); } if (e.key === 'Escape') { setEditingExpected(false); }
}} }}
className="w-24 rounded border border-border bg-transparent px-1 py-0.5 text-right text-sm font-mono text-foreground outline-none focus:ring-[2px] focus:ring-ring/50" 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"
/> />
) : effectiveActual != null ? ( ) : effectiveActual != null ? (
<button <button
type="button" type="button"
onClick={() => { setExpectedDraft(String(effectiveActual)); setEditingExpected(true); }} onClick={() => { setExpectedDraft(String(effectiveActual)); setEditingExpected(true); }}
className="rounded px-1 py-0.5 text-amber-500 transition-colors hover:bg-accent" className="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)}`} title={`Monthly override — click to edit. Template default: ${fmt(row.expected_amount)}`}
> >
{fmt(effectiveActual)} {fmt(effectiveActual)}
@ -1550,7 +1550,7 @@ function Row({ row, year, month, refresh, index, onEditBill }) {
<button <button
type="button" type="button"
onClick={() => { setExpectedDraft(String(row.expected_amount)); setEditingExpected(true); }} onClick={() => { setExpectedDraft(String(row.expected_amount)); setEditingExpected(true); }}
className="rounded px-1 py-0.5 text-muted-foreground transition-colors hover:bg-accent hover:text-foreground" className="rounded px-1 py-0.5 text-foreground/85 transition-colors hover:bg-accent hover:text-foreground"
title="Click to edit expected amount" title="Click to edit expected amount"
> >
{fmt(row.expected_amount)} {fmt(row.expected_amount)}
@ -1571,7 +1571,7 @@ function Row({ row, year, month, refresh, index, onEditBill }) {
</TableCell> </TableCell>
{/* Previous month paid */} {/* Previous month paid */}
<TableCell className="w-[10%] py-3 text-right font-mono text-sm text-muted-foreground/70"> <TableCell className="tracker-number w-[10%] py-3 text-right text-[13px] font-medium text-muted-foreground/80">
{row.previous_month_paid > 0 ? fmt(row.previous_month_paid) : '—'} {row.previous_month_paid > 0 ? fmt(row.previous_month_paid) : '—'}
</TableCell> </TableCell>
@ -1586,11 +1586,11 @@ function Row({ row, year, month, refresh, index, onEditBill }) {
</TableCell> </TableCell>
{/* Paid date */} {/* Paid date */}
<TableCell className="w-[10%] py-3 text-sm text-muted-foreground"> <TableCell className="w-[10%] py-3 text-[13px] text-foreground/75">
<button <button
type="button" type="button"
onClick={() => setPaymentLedgerOpen(true)} onClick={() => setPaymentLedgerOpen(true)}
className="rounded-md px-1.5 py-0.5 font-mono transition-colors hover:bg-accent hover:text-foreground" className="tracker-number rounded-md px-1.5 py-0.5 font-medium transition-colors hover:bg-accent hover:text-foreground"
title="View payment history" title="View payment history"
> >
{row.last_paid_date ? fmtDate(row.last_paid_date) : '—'} {row.last_paid_date ? fmtDate(row.last_paid_date) : '—'}
@ -1650,7 +1650,7 @@ function Row({ row, year, month, refresh, index, onEditBill }) {
ref={amountRef} ref={amountRef}
type="number" min="0" step="0.01" type="number" min="0" step="0.01"
defaultValue={summary.remaining || threshold} defaultValue={summary.remaining || threshold}
className="h-7 w-20 text-right font-mono text-sm bg-background/50 border-border/50" className="tracker-number h-7 w-20 text-right text-sm font-medium bg-background/50 border-border/50"
title="Payment amount" title="Payment amount"
/> />
<Button <Button
@ -1855,7 +1855,7 @@ function MobileTrackerRow({ row, year, month, refresh, index, onEditBill }) {
target="_blank" target="_blank"
rel="noreferrer" rel="noreferrer"
className={cn( className={cn(
'min-w-0 truncate text-sm font-semibold leading-tight text-foreground', 'min-w-0 truncate text-[15px] font-semibold leading-tight text-foreground',
'underline-offset-2 transition-colors hover:text-primary hover:underline', 'underline-offset-2 transition-colors hover:text-primary hover:underline',
isSkipped && 'line-through', isSkipped && 'line-through',
)} )}
@ -1863,7 +1863,7 @@ function MobileTrackerRow({ row, year, month, refresh, index, onEditBill }) {
{row.name} {row.name}
</a> </a>
) : ( ) : (
<span className={cn('min-w-0 truncate text-sm font-semibold leading-tight text-foreground', isSkipped && 'line-through')}> <span className={cn('min-w-0 truncate text-[15px] font-semibold leading-tight text-foreground', isSkipped && 'line-through')}>
{row.name} {row.name}
</span> </span>
)} )}
@ -1888,7 +1888,7 @@ function MobileTrackerRow({ row, year, month, refresh, index, onEditBill }) {
<div className="grid grid-cols-2 gap-x-3 gap-y-2 text-xs text-muted-foreground sm:grid-cols-4"> <div className="grid grid-cols-2 gap-x-3 gap-y-2 text-xs text-muted-foreground sm:grid-cols-4">
<div> <div>
<p className="uppercase tracking-wide text-muted-foreground/60">Due</p> <p className="uppercase tracking-wide text-muted-foreground/60">Due</p>
<p className="mt-0.5 font-mono text-sm text-foreground">{fmtDate(row.due_date)}</p> <p className="tracker-number mt-0.5 text-sm font-medium text-foreground/90">{fmtDate(row.due_date)}</p>
</div> </div>
<div> <div>
<p className="uppercase tracking-wide text-muted-foreground/60">Category</p> <p className="uppercase tracking-wide text-muted-foreground/60">Category</p>
@ -1896,19 +1896,19 @@ function MobileTrackerRow({ row, year, month, refresh, index, onEditBill }) {
</div> </div>
<div> <div>
<p className="uppercase tracking-wide text-muted-foreground/60">Expected</p> <p className="uppercase tracking-wide text-muted-foreground/60">Expected</p>
<p className={cn('mt-0.5 font-mono text-sm', row.actual_amount != null ? 'text-amber-500' : 'text-foreground')}> <p className={cn('tracker-number mt-0.5 text-sm font-semibold', row.actual_amount != null ? 'text-amber-300' : 'text-foreground')}>
{fmt(threshold)} {fmt(threshold)}
</p> </p>
</div> </div>
<div> <div>
<p className="uppercase tracking-wide text-muted-foreground/60">Last Month</p> <p className="uppercase tracking-wide text-muted-foreground/60">Last Month</p>
<p className="mt-0.5 font-mono text-sm text-muted-foreground/70"> <p className="tracker-number mt-0.5 text-sm font-medium text-muted-foreground/80">
{fmt(row.previous_month_paid)} {fmt(row.previous_month_paid)}
</p> </p>
</div> </div>
<div> <div>
<p className="uppercase tracking-wide text-muted-foreground/60">Remaining</p> <p className="uppercase tracking-wide text-muted-foreground/60">Remaining</p>
<p className={cn('mt-0.5 font-mono text-sm', remaining > 0 ? 'text-foreground' : 'text-emerald-500')}> <p className={cn('tracker-number mt-0.5 text-sm font-semibold', remaining > 0 ? 'text-foreground' : 'text-emerald-300')}>
{fmt(remaining)} {fmt(remaining)}
</p> </p>
</div> </div>
@ -1922,14 +1922,14 @@ function MobileTrackerRow({ row, year, month, refresh, index, onEditBill }) {
<div className="grid grid-cols-2 gap-2 text-xs sm:flex sm:items-center"> <div className="grid grid-cols-2 gap-2 text-xs sm:flex sm:items-center">
<div className="rounded-md bg-muted/45 px-2 py-1.5"> <div className="rounded-md bg-muted/45 px-2 py-1.5">
<span className="text-muted-foreground">Paid </span> <span className="text-muted-foreground">Paid </span>
<span className="font-mono text-emerald-500">{row.total_paid > 0 ? fmt(row.total_paid) : '—'}</span> <span className="tracker-number font-semibold text-emerald-300">{row.total_paid > 0 ? fmt(row.total_paid) : '—'}</span>
</div> </div>
<div className="rounded-md bg-muted/45 px-2 py-1.5"> <div className="rounded-md bg-muted/45 px-2 py-1.5">
<span className="text-muted-foreground">Date </span> <span className="text-muted-foreground">Date </span>
<button <button
type="button" type="button"
onClick={() => setPaymentLedgerOpen(true)} onClick={() => setPaymentLedgerOpen(true)}
className="rounded font-mono text-foreground underline-offset-2 hover:underline" className="tracker-number rounded font-medium text-foreground underline-offset-2 hover:underline"
> >
{row.last_paid_date ? fmtDate(row.last_paid_date) : '—'} {row.last_paid_date ? fmtDate(row.last_paid_date) : '—'}
{summary.count > 1 && <span className="ml-1 text-[10px] text-muted-foreground">({summary.count})</span>} {summary.count > 1 && <span className="ml-1 text-[10px] text-muted-foreground">({summary.count})</span>}
@ -1953,7 +1953,7 @@ function MobileTrackerRow({ row, year, month, refresh, index, onEditBill }) {
ref={amountRef} ref={amountRef}
type="number" min="0" step="0.01" type="number" min="0" step="0.01"
defaultValue={summary.remaining || threshold} defaultValue={summary.remaining || threshold}
className="h-8 w-24 text-right font-mono text-sm bg-background/70 border-border/60" className="tracker-number h-8 w-24 text-right text-sm font-medium bg-background/70 border-border/60"
title="Payment amount" title="Payment amount"
aria-label={`${row.name} payment amount`} aria-label={`${row.name} payment amount`}
/> />

View File

@ -1,6 +1,6 @@
{ {
"name": "bill-tracker", "name": "bill-tracker",
"version": "0.28.4.3", "version": "0.28.4.4",
"description": "Monthly bill tracking system", "description": "Monthly bill tracking system",
"main": "server.js", "main": "server.js",
"scripts": { "scripts": {