fix: SimpleFIN recommendation card stays vertical in narrow sidebar
Title and amount in header, badges/reasons below, action buttons in a clean row at the bottom.
This commit is contained in:
parent
c3c0ab3542
commit
da6a93804b
|
|
@ -195,64 +195,66 @@ function BillPickerDialog({ open, onClose, recommendation, bills, onConfirm, bus
|
|||
function RecommendationCard({ recommendation, categoryId, onAccept, onDecline, onMatch, busy }) {
|
||||
return (
|
||||
<div className="rounded-lg border border-primary/20 bg-primary/[0.05] p-4">
|
||||
<div className="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
|
||||
<div className="min-w-0">
|
||||
<div className="flex min-w-0 flex-wrap items-center gap-2">
|
||||
<p className="min-w-0 max-w-full truncate text-sm font-semibold text-foreground">{recommendation.name}</p>
|
||||
<Badge variant="outline" className="border-primary/25 bg-primary/10 text-primary">
|
||||
{recommendation.confidence}% match
|
||||
</Badge>
|
||||
<div className="space-y-3">
|
||||
<div className="flex min-w-0 items-start justify-between gap-3">
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="truncate text-sm font-semibold text-foreground">{recommendation.name}</p>
|
||||
<p className="mt-1 text-xs font-medium text-muted-foreground">
|
||||
{TYPE_LABELS[recommendation.subscription_type] || 'Other'} · {recommendation.occurrence_count} charges · last seen {fmtDate(recommendation.last_seen_date)}
|
||||
</p>
|
||||
</div>
|
||||
<p className="mt-1 text-xs font-medium text-muted-foreground">
|
||||
{TYPE_LABELS[recommendation.subscription_type] || 'Other'} · {recommendation.occurrence_count} charges · last seen {fmtDate(recommendation.last_seen_date)}
|
||||
</p>
|
||||
<div className="mt-3 flex flex-wrap gap-2">
|
||||
{recommendation.reasons?.map(reason => (
|
||||
<span key={reason} className="rounded-md border border-border/60 bg-background/60 px-2 py-1 text-[11px] font-medium text-muted-foreground">
|
||||
{reason}
|
||||
</span>
|
||||
))}
|
||||
<div className="shrink-0 text-right">
|
||||
<p className="tracker-number text-base font-semibold text-foreground">{fmt(recommendation.expected_amount)}</p>
|
||||
<p className="tracker-number text-xs font-semibold text-emerald-600 dark:text-emerald-300">
|
||||
{fmt(recommendation.monthly_equivalent)} / mo
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="shrink-0 text-left sm:text-right">
|
||||
<p className="tracker-number text-base font-semibold text-foreground">{fmt(recommendation.expected_amount)}</p>
|
||||
<p className="tracker-number text-xs font-semibold text-emerald-600 dark:text-emerald-300">
|
||||
{fmt(recommendation.monthly_equivalent)} / mo
|
||||
</p>
|
||||
<div className="mt-3 grid grid-cols-1 gap-2 min-[380px]:grid-cols-3 sm:flex sm:flex-wrap sm:justify-end">
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
className="gap-1.5 text-muted-foreground hover:text-destructive"
|
||||
disabled={busy}
|
||||
onClick={() => onDecline(recommendation)}
|
||||
>
|
||||
{busy ? <Loader2 className="h-3.5 w-3.5 animate-spin" /> : <X className="h-3.5 w-3.5" />}
|
||||
Decline
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
variant="outline"
|
||||
className="gap-1.5"
|
||||
disabled={busy}
|
||||
onClick={() => onMatch(recommendation)}
|
||||
>
|
||||
<Link2 className="h-3.5 w-3.5" />
|
||||
Link to bill
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
className="gap-2"
|
||||
disabled={busy}
|
||||
onClick={() => onAccept({ ...recommendation, category_id: categoryId })}
|
||||
>
|
||||
{busy ? <Loader2 className="h-3.5 w-3.5 animate-spin" /> : <CheckCircle2 className="h-3.5 w-3.5" />}
|
||||
Track
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="flex min-w-0 flex-wrap items-center gap-2">
|
||||
<Badge variant="outline" className="border-primary/25 bg-primary/10 text-primary">
|
||||
{recommendation.confidence}% match
|
||||
</Badge>
|
||||
{recommendation.reasons?.map(reason => (
|
||||
<span key={reason} className="max-w-full rounded-md border border-border/60 bg-background/60 px-2 py-1 text-[11px] font-medium text-muted-foreground">
|
||||
{reason}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 gap-2 min-[380px]:grid-cols-3">
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
className="gap-1.5 text-muted-foreground hover:text-destructive"
|
||||
disabled={busy}
|
||||
onClick={() => onDecline(recommendation)}
|
||||
>
|
||||
{busy ? <Loader2 className="h-3.5 w-3.5 animate-spin" /> : <X className="h-3.5 w-3.5" />}
|
||||
Decline
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
variant="outline"
|
||||
className="gap-1.5"
|
||||
disabled={busy}
|
||||
onClick={() => onMatch(recommendation)}
|
||||
>
|
||||
<Link2 className="h-3.5 w-3.5" />
|
||||
Link to bill
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
className="gap-2"
|
||||
disabled={busy}
|
||||
onClick={() => onAccept({ ...recommendation, category_id: categoryId })}
|
||||
>
|
||||
{busy ? <Loader2 className="h-3.5 w-3.5 animate-spin" /> : <CheckCircle2 className="h-3.5 w-3.5" />}
|
||||
Track
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "bill-tracker",
|
||||
"version": "0.33.7",
|
||||
"version": "0.33.7.1",
|
||||
"description": "Monthly bill tracking system",
|
||||
"main": "server.js",
|
||||
"scripts": {
|
||||
|
|
|
|||
Loading…
Reference in New Issue