2026-05-09 13:03:36 -05:00
|
|
|
import React, { useMemo } from 'react';
|
2026-05-16 15:38:28 -05:00
|
|
|
import { Copy, History } from 'lucide-react';
|
2026-05-09 13:03:36 -05:00
|
|
|
import { Button } from '@/components/ui/button';
|
|
|
|
|
import { cn } from '@/lib/utils';
|
|
|
|
|
|
|
|
|
|
function hasHistoricalVisibility(bill) {
|
|
|
|
|
const visibility = bill.history_visibility;
|
|
|
|
|
return !!bill.has_history_ranges || (visibility && visibility !== 'default');
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-16 15:38:28 -05:00
|
|
|
export const MobileBillRow = React.memo(function MobileBillRow({ bill, onEdit, onToggle, onDelete, onHistory, onDuplicate }) {
|
2026-05-09 13:03:36 -05:00
|
|
|
const hasHistory = useMemo(() => hasHistoricalVisibility(bill), [bill]);
|
|
|
|
|
|
|
|
|
|
const statusClass = useMemo(() => {
|
|
|
|
|
return cn(
|
|
|
|
|
'rounded px-1.5 py-0.5 text-[10px] font-semibold uppercase tracking-wide',
|
|
|
|
|
bill.active
|
style: global readability/theme pass
- Sharpened font stack in index.css, removed softer Georgia digit font for UI text/money
- Tuned dark-mode tokens: clearer foreground, brighter muted text, stronger borders, defined cards
- Updated UI primitives: cards, buttons, inputs, selects, tables, badges
- Cleaned up bills rows, mobile bill rows, tracker dismiss, snowball icons, summary/category/health/analytics money values, import/export status icons
- Reduced fuzzy uppercase label spacing globally
2026-05-28 23:18:14 -05:00
|
|
|
? 'bg-emerald-500/20 text-emerald-300'
|
2026-05-09 13:03:36 -05:00
|
|
|
: 'bg-muted text-muted-foreground',
|
|
|
|
|
);
|
|
|
|
|
}, [bill.active]);
|
|
|
|
|
|
|
|
|
|
const autopayClass = useMemo(() => {
|
|
|
|
|
return cn(
|
style: global readability/theme pass
- Sharpened font stack in index.css, removed softer Georgia digit font for UI text/money
- Tuned dark-mode tokens: clearer foreground, brighter muted text, stronger borders, defined cards
- Updated UI primitives: cards, buttons, inputs, selects, tables, badges
- Cleaned up bills rows, mobile bill rows, tracker dismiss, snowball icons, summary/category/health/analytics money values, import/export status icons
- Reduced fuzzy uppercase label spacing globally
2026-05-28 23:18:14 -05:00
|
|
|
'rounded bg-emerald-500/20 px-1.5 py-0.5 text-[10px] font-semibold text-emerald-300',
|
2026-05-09 13:03:36 -05:00
|
|
|
!!bill.autopay_enabled ? 'opacity-100' : 'opacity-0',
|
|
|
|
|
);
|
|
|
|
|
}, [bill.autopay_enabled]);
|
|
|
|
|
|
|
|
|
|
const toggleBtnClass = useMemo(() => {
|
|
|
|
|
return cn(
|
|
|
|
|
'h-8 px-2.5 text-xs',
|
|
|
|
|
bill.active
|
|
|
|
|
? 'text-muted-foreground hover:text-destructive'
|
|
|
|
|
: 'text-emerald-500 hover:text-emerald-400',
|
|
|
|
|
);
|
|
|
|
|
}, [bill.active]);
|
|
|
|
|
|
|
|
|
|
return (
|
style: global readability/theme pass
- Sharpened font stack in index.css, removed softer Georgia digit font for UI text/money
- Tuned dark-mode tokens: clearer foreground, brighter muted text, stronger borders, defined cards
- Updated UI primitives: cards, buttons, inputs, selects, tables, badges
- Cleaned up bills rows, mobile bill rows, tracker dismiss, snowball icons, summary/category/health/analytics money values, import/export status icons
- Reduced fuzzy uppercase label spacing globally
2026-05-28 23:18:14 -05:00
|
|
|
<div className="rounded-xl border border-border/80 bg-card/90 p-3 shadow-sm shadow-black/10">
|
2026-05-09 13:03:36 -05:00
|
|
|
<div className="flex min-w-0 items-start justify-between gap-3">
|
|
|
|
|
<div className="min-w-0">
|
|
|
|
|
<div className="flex min-w-0 items-center gap-2">
|
|
|
|
|
<button
|
|
|
|
|
type="button"
|
|
|
|
|
className="min-w-0 truncate text-left text-sm font-semibold leading-tight text-foreground underline-offset-4 transition-colors hover:text-primary hover:underline focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50 rounded-sm"
|
|
|
|
|
onClick={() => onEdit?.(bill.id)}
|
|
|
|
|
title={`Edit ${bill.name}`}
|
|
|
|
|
>
|
|
|
|
|
{bill.name}
|
|
|
|
|
</button>
|
|
|
|
|
{hasHistory && (
|
|
|
|
|
<span
|
|
|
|
|
className="inline-flex h-5 w-5 shrink-0 items-center justify-center rounded-full border border-sky-500/25 bg-sky-500/10 text-sky-500"
|
|
|
|
|
title="Historical visibility configured"
|
|
|
|
|
aria-label="Historical visibility configured"
|
|
|
|
|
>
|
|
|
|
|
<History className="h-3 w-3" />
|
|
|
|
|
</span>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div className="mt-1 flex flex-wrap items-center gap-1.5">
|
|
|
|
|
<span className={statusClass}>
|
|
|
|
|
{bill.active ? 'Active' : 'Inactive'}
|
|
|
|
|
</span>
|
|
|
|
|
{bill.autopay_enabled && (
|
style: global readability/theme pass
- Sharpened font stack in index.css, removed softer Georgia digit font for UI text/money
- Tuned dark-mode tokens: clearer foreground, brighter muted text, stronger borders, defined cards
- Updated UI primitives: cards, buttons, inputs, selects, tables, badges
- Cleaned up bills rows, mobile bill rows, tracker dismiss, snowball icons, summary/category/health/analytics money values, import/export status icons
- Reduced fuzzy uppercase label spacing globally
2026-05-28 23:18:14 -05:00
|
|
|
<span className="rounded bg-emerald-500/20 px-1.5 py-0.5 text-[10px] font-semibold text-emerald-300">AP</span>
|
2026-05-09 13:03:36 -05:00
|
|
|
)}
|
|
|
|
|
{bill.has_2fa && (
|
style: global readability/theme pass
- Sharpened font stack in index.css, removed softer Georgia digit font for UI text/money
- Tuned dark-mode tokens: clearer foreground, brighter muted text, stronger borders, defined cards
- Updated UI primitives: cards, buttons, inputs, selects, tables, badges
- Cleaned up bills rows, mobile bill rows, tracker dismiss, snowball icons, summary/category/health/analytics money values, import/export status icons
- Reduced fuzzy uppercase label spacing globally
2026-05-28 23:18:14 -05:00
|
|
|
<span className="rounded bg-violet-500/20 px-1.5 py-0.5 text-[10px] font-semibold text-violet-300">2FA</span>
|
2026-05-09 13:03:36 -05:00
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
style: global readability/theme pass
- Sharpened font stack in index.css, removed softer Georgia digit font for UI text/money
- Tuned dark-mode tokens: clearer foreground, brighter muted text, stronger borders, defined cards
- Updated UI primitives: cards, buttons, inputs, selects, tables, badges
- Cleaned up bills rows, mobile bill rows, tracker dismiss, snowball icons, summary/category/health/analytics money values, import/export status icons
- Reduced fuzzy uppercase label spacing globally
2026-05-28 23:18:14 -05:00
|
|
|
<span className="tracker-number shrink-0 text-sm font-bold text-foreground">
|
2026-05-09 13:03:36 -05:00
|
|
|
${Number(bill.expected_amount).toFixed(2)}
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
|
style: global readability/theme pass
- Sharpened font stack in index.css, removed softer Georgia digit font for UI text/money
- Tuned dark-mode tokens: clearer foreground, brighter muted text, stronger borders, defined cards
- Updated UI primitives: cards, buttons, inputs, selects, tables, badges
- Cleaned up bills rows, mobile bill rows, tracker dismiss, snowball icons, summary/category/health/analytics money values, import/export status icons
- Reduced fuzzy uppercase label spacing globally
2026-05-28 23:18:14 -05:00
|
|
|
<div className="mt-3 grid grid-cols-3 gap-2 text-xs font-medium text-muted-foreground">
|
2026-05-09 13:03:36 -05:00
|
|
|
<div>
|
style: global readability/theme pass
- Sharpened font stack in index.css, removed softer Georgia digit font for UI text/money
- Tuned dark-mode tokens: clearer foreground, brighter muted text, stronger borders, defined cards
- Updated UI primitives: cards, buttons, inputs, selects, tables, badges
- Cleaned up bills rows, mobile bill rows, tracker dismiss, snowball icons, summary/category/health/analytics money values, import/export status icons
- Reduced fuzzy uppercase label spacing globally
2026-05-28 23:18:14 -05:00
|
|
|
<p className="uppercase tracking-normal text-muted-foreground">Due</p>
|
2026-05-09 13:03:36 -05:00
|
|
|
<p className="mt-0.5 text-sm text-foreground">Day {bill.due_day}</p>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="min-w-0">
|
style: global readability/theme pass
- Sharpened font stack in index.css, removed softer Georgia digit font for UI text/money
- Tuned dark-mode tokens: clearer foreground, brighter muted text, stronger borders, defined cards
- Updated UI primitives: cards, buttons, inputs, selects, tables, badges
- Cleaned up bills rows, mobile bill rows, tracker dismiss, snowball icons, summary/category/health/analytics money values, import/export status icons
- Reduced fuzzy uppercase label spacing globally
2026-05-28 23:18:14 -05:00
|
|
|
<p className="uppercase tracking-normal text-muted-foreground">Category</p>
|
2026-05-09 13:03:36 -05:00
|
|
|
<p className="mt-0.5 truncate text-sm text-foreground">{bill.category_name || '—'}</p>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
style: global readability/theme pass
- Sharpened font stack in index.css, removed softer Georgia digit font for UI text/money
- Tuned dark-mode tokens: clearer foreground, brighter muted text, stronger borders, defined cards
- Updated UI primitives: cards, buttons, inputs, selects, tables, badges
- Cleaned up bills rows, mobile bill rows, tracker dismiss, snowball icons, summary/category/health/analytics money values, import/export status icons
- Reduced fuzzy uppercase label spacing globally
2026-05-28 23:18:14 -05:00
|
|
|
<p className="uppercase tracking-normal text-muted-foreground">Cycle</p>
|
2026-05-09 13:03:36 -05:00
|
|
|
<p className="mt-0.5 text-sm capitalize text-foreground">{bill.billing_cycle || 'monthly'}</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div className="mt-3 flex flex-wrap items-center justify-end gap-1.5">
|
2026-05-16 15:38:28 -05:00
|
|
|
<Button
|
|
|
|
|
variant="ghost"
|
|
|
|
|
size="sm"
|
|
|
|
|
className="h-8 gap-1.5 px-2.5 text-xs text-muted-foreground hover:text-primary"
|
|
|
|
|
onClick={() => onDuplicate?.(bill)}
|
|
|
|
|
>
|
|
|
|
|
<Copy className="h-3.5 w-3.5" />
|
|
|
|
|
Duplicate
|
|
|
|
|
</Button>
|
2026-05-09 13:03:36 -05:00
|
|
|
<Button
|
|
|
|
|
variant="ghost"
|
|
|
|
|
size="sm"
|
|
|
|
|
className={toggleBtnClass}
|
|
|
|
|
onClick={() => onToggle?.(bill)}
|
|
|
|
|
>
|
|
|
|
|
{bill.active ? 'Deactivate' : 'Activate'}
|
|
|
|
|
</Button>
|
|
|
|
|
{!bill.active && (
|
|
|
|
|
<Button
|
|
|
|
|
variant="ghost"
|
|
|
|
|
size="sm"
|
|
|
|
|
className="h-8 px-2.5 text-xs text-sky-500 hover:text-sky-400 hover:bg-sky-500/10"
|
|
|
|
|
onClick={() => onHistory?.(bill)}
|
|
|
|
|
>
|
|
|
|
|
History
|
|
|
|
|
</Button>
|
|
|
|
|
)}
|
|
|
|
|
<Button
|
|
|
|
|
variant="ghost"
|
|
|
|
|
size="sm"
|
|
|
|
|
className="h-8 px-2.5 text-xs text-destructive hover:text-destructive hover:bg-destructive/10"
|
|
|
|
|
onClick={() => onDelete?.(bill)}
|
|
|
|
|
>
|
|
|
|
|
Delete
|
|
|
|
|
</Button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
MobileBillRow.displayName = 'MobileBillRow';
|