From ee31513dc9a86373893879da03b635adfed03928 Mon Sep 17 00:00:00 2001 From: null Date: Sat, 4 Jul 2026 19:32:39 -0500 Subject: [PATCH] refactor(ts): convert Drift + Overdue panels to TSX (B4) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DriftInsightPanel (new @/types DriftBill — its own row shape, money in dollars) and OverdueCommandCenter (TrackerRow[]). Note: TS flags `Date - Date` arithmetic → use .getTime(); the overdue/net-delta totals need asDollars at the fmt boundary. typecheck 0, build green. Co-Authored-By: Claude Opus 4.8 --- ...InsightPanel.jsx => DriftInsightPanel.tsx} | 10 +++--- ...andCenter.jsx => OverdueCommandCenter.tsx} | 34 ++++++++++++++----- client/types.ts | 13 +++++++ 3 files changed, 45 insertions(+), 12 deletions(-) rename client/components/tracker/{DriftInsightPanel.jsx => DriftInsightPanel.tsx} (95%) rename client/components/tracker/{OverdueCommandCenter.jsx => OverdueCommandCenter.tsx} (89%) diff --git a/client/components/tracker/DriftInsightPanel.jsx b/client/components/tracker/DriftInsightPanel.tsx similarity index 95% rename from client/components/tracker/DriftInsightPanel.jsx rename to client/components/tracker/DriftInsightPanel.tsx index 1d84bac..6ad6943 100644 --- a/client/components/tracker/DriftInsightPanel.jsx +++ b/client/components/tracker/DriftInsightPanel.tsx @@ -1,13 +1,15 @@ -import React, { useState } from 'react'; +import { useState } from 'react'; import { TrendingUp, TrendingDown, ChevronDown } from 'lucide-react'; import { toast } from 'sonner'; import { api } from '@/api'; import { cn, fmt } from '@/lib/utils'; +import { asDollars } from '@/lib/money'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { Collapsible, CollapsibleTrigger, CollapsibleContent } from '@/components/ui/collapsible'; +import type { DriftBill } from '@/types'; -function DriftRow({ row, refresh }) { +function DriftRow({ row, refresh }: { row: DriftBill; refresh: () => void }) { const [loading, setLoading] = useState(false); async function handleUpdate() { @@ -131,7 +133,7 @@ function DriftRow({ row, refresh }) { ); } -export default function DriftInsightPanel({ driftBills, refresh }) { +export default function DriftInsightPanel({ driftBills, refresh }: { driftBills?: DriftBill[]; refresh: () => void }) { const [isOpen, setIsOpen] = useState(true); if (!driftBills?.length) return null; @@ -159,7 +161,7 @@ export default function DriftInsightPanel({ driftBills, refresh }) { : `${driftBills.length} bills changed price`} - {sign}{fmt(Math.abs(totalNetDelta))}/mo net + {sign}{fmt(asDollars(Math.abs(totalNetDelta)))}/mo net void; + onRefresh: () => void; +} + +function OverdueRow({ row, year, month, onPayNow, onRefresh }: OverdueRowProps) { const [loading, setLoading] = useState(false); const threshold = row.actual_amount ?? row.expected_amount; @@ -51,7 +61,7 @@ function OverdueRow({ row, year, month, onPayNow, onRefresh }) { } } - async function handleSnooze(days) { + async function handleSnooze(days: number) { setLoading(true); try { await api.snoozeOverdue(row.id, { @@ -141,7 +151,15 @@ function OverdueRow({ row, year, month, onPayNow, onRefresh }) { ); } -export default function OverdueCommandCenter({ rows, year, month, refresh, onPayNow }) { +interface OverdueCommandCenterProps { + rows: TrackerRow[]; + year: number; + month: number; + refresh: () => void; + onPayNow: (row: TrackerRow) => void; +} + +export default function OverdueCommandCenter({ rows, year, month, refresh, onPayNow }: OverdueCommandCenterProps) { const todayStr = localDateString(); const [isOpen, setIsOpen] = useState(true); @@ -181,7 +199,7 @@ export default function OverdueCommandCenter({ rows, year, month, refresh, onPay {overdueRows.length > 0 && ( - {fmt(totalOverdue)} + {fmt(asDollars(totalOverdue))} )} diff --git a/client/types.ts b/client/types.ts index 2e63693..383ff3b 100644 --- a/client/types.ts +++ b/client/types.ts @@ -14,6 +14,19 @@ import type { Dollars } from '@/lib/money'; export type TrackerStatus = | 'paid' | 'autodraft' | 'upcoming' | 'due_soon' | 'late' | 'missed' | 'skipped'; +// A price-drift finding (driftService.getDriftReport) — a bill whose recent +// payments trend away from its expected amount. +export interface DriftBill { + id: number; + name: string; + category_name?: string | null; + expected_amount: Dollars; + recent_amount: Dollars; + direction: string; + drift_pct: number; + months_sampled: number; +} + // A suggested autopay payment (trackerService.applyAutopaySuggestions). export interface AutopaySuggestion { bill_id: number;