import React, { useState } from 'react'; import { TrendingUp, TrendingDown, ChevronDown } from 'lucide-react'; import { toast } from 'sonner'; import { api } from '@/api.js'; import { cn, fmt } from '@/lib/utils'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { Collapsible, CollapsibleTrigger, CollapsibleContent } from '@/components/ui/collapsible'; function DriftRow({ row, refresh }) { const [loading, setLoading] = useState(false); async function handleUpdate() { setLoading(true); const oldAmount = row.expected_amount; try { await api.updateBill(row.id, { expected_amount: row.recent_amount }); toast.success(`"${row.name}" updated to ${fmt(row.recent_amount)}`, { action: { label: 'Undo', onClick: async () => { try { await api.updateBill(row.id, { expected_amount: oldAmount }); toast.success('Reverted'); refresh(); } catch { toast.error('Failed to revert'); } }, }, }); refresh(); } catch { toast.error('Failed to update bill'); } finally { setLoading(false); } } async function handleDismiss() { setLoading(true); try { await api.snoozeBillDrift(row.id); toast.success('Hidden for 30 days'); refresh(); } catch { toast.error('Failed to dismiss'); } finally { setLoading(false); } } const isUp = row.direction === 'up'; const sign = isUp ? '+' : ''; return (
{/* Bill info */}
{row.name} {row.category_name && ( {row.category_name} )}
{isUp ? : } {sign}{row.drift_pct}% over {row.months_sampled} months
{/* Amount change */}
{fmt(row.expected_amount)} {fmt(row.recent_amount)} {sign}{row.drift_pct}%
{/* Actions */}
); } export default function DriftInsightPanel({ driftBills, refresh }) { const [isOpen, setIsOpen] = useState(true); if (!driftBills?.length) return null; const totalNetDelta = driftBills.reduce((sum, b) => sum + (b.recent_amount - b.expected_amount), 0); const sign = totalNetDelta >= 0 ? '+' : ''; const netColor = totalNetDelta >= 0 ? 'text-amber-500 dark:text-amber-400' : 'text-teal-500 dark:text-teal-400'; const hasIncrease = driftBills.some(b => b.direction === 'up'); return (
{/* Header */} {/* Bill rows */}
{driftBills.map(row => ( ))}
); }