import React, { useState, useEffect, useCallback } from 'react'; import { toast } from 'sonner'; import { ChevronDown, Trash2, ToggleLeft, ToggleRight, Settings2 } from 'lucide-react'; import { api } from '@/api'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; export default function BillRulesManager() { const [open, setOpen] = useState(false); const [rules, setRules] = useState([]); const [loading, setLoading] = useState(false); const load = useCallback(async () => { setLoading(true); try { const d = await api.allBillMerchantRules(); setRules(d.rules || []); } catch (err) { toast.error(err.message || 'Failed to load bill matching rules'); } finally { setLoading(false); } }, []); useEffect(() => { if (open) load(); }, [open, load]); const handleDelete = async (billId, ruleId, merchant) => { try { await api.deleteMerchantRule(billId, ruleId); setRules(prev => prev.filter(r => r.id !== ruleId)); toast.success(`Rule "${merchant}" removed`); } catch (err) { toast.error(err.message || 'Failed to delete rule'); } }; const handleToggleAutoLate = async (billId, ruleId, current) => { try { await api.toggleRuleAutoAttribute(billId, ruleId, !current); setRules(prev => prev.map(r => r.id === ruleId ? { ...r, auto_attribute_late: current ? 0 : 1 } : r )); } catch (err) { toast.error(err.message || 'Failed to update rule'); } }; // Group rules by bill const byBill = rules.reduce((acc, r) => { const key = r.bill_id; if (!acc[key]) acc[key] = { bill_name: r.bill_name, bill_id: r.bill_id, rules: [] }; acc[key].rules.push(r); return acc; }, {}); const groups = Object.values(byBill); return (
{open && (
{loading ? (

Loading…

) : groups.length === 0 ? (

No rules saved yet. Open a bill and add merchant matching rules to auto-match bank transactions.

) : (
{groups.map(group => (

{group.bill_name}

{group.rules.map(rule => (
{rule.merchant}
))}
))}
)}

Merchant patterns are matched with word-boundary rules. Toggle icon = auto-apply late month attribution.

)}
); }