import { useCallback, useEffect, useMemo, useState, type ReactElement } from 'react'; import { AccountRecord } from '../../../server/vault.js'; import { deleteAccount, fetchAccounts, updateStatus } from '../../lib/api.js'; import { SOURCE_LABELS } from './source-types.js'; type SourceType = 'poalim' | 'discount' | 'isracard' | 'amex' | 'cal' | 'max'; type AccountStatus = 'accepted' | 'ignored' | 'pending'; const STATUS_COLORS: Record = { accepted: '#2a7a2a', ignored: '#999', pending: '#b85c00', }; function StatusBadge({ status }: { status: AccountStatus }): ReactElement { return ( {status} ); } type AccountRowProps = { account: AccountRecord; onStatusChange(id: string, status: 'accepted' | 'ignored'): void; onDelete(id: string): void; }; function AccountRow({ account, onStatusChange, onDelete }: AccountRowProps): ReactElement { const label = account.branchNumber ? `${account.accountNumber} / branch ${account.branchNumber}` : account.accountNumber; return (
  • {label}
    {account.status === 'pending' && (

    Visit the Accounter client to set up this account.

    )}
  • ); } export function AccountsTab(): ReactElement { const [accounts, setAccounts] = useState<(AccountRecord & { nickname?: string })[]>([]); const [error, setError] = useState(null); const [pendingOnly, setPendingOnly] = useState(false); const load = useCallback(async () => { try { setAccounts(await fetchAccounts()); } catch { setError('Failed to load accounts'); } }, []); useEffect(() => { void load(); }, [load]); async function handleStatusChange(id: string, status: 'accepted' | 'ignored') { try { setAccounts(await updateStatus(id, status)); } catch { setError('Failed to update account'); } } async function handleDelete(id: string) { try { setAccounts(await deleteAccount(id)); } catch { setError('Failed to delete account'); } } const grouped = useMemo( () => accounts.reduce>((acc, a) => { const key = `${a.sourceType}:${a.nickname ?? a.sourceId}`; acc[key] ||= []; acc[key].push(a); return acc; }, {}), [accounts], ); const pending = useMemo(() => accounts.filter(a => a.status === 'pending'), [accounts]); const sections = useMemo(() => { if (pendingOnly) { return [{ key: 'pending', label: 'Pending accounts', accounts: pending }]; } return Object.entries(grouped).map(([key, accs]) => { const [sourceType, sourceId] = key.split(':') as [SourceType, string]; return { key, label: `${SOURCE_LABELS[sourceType]} (${sourceId})`, accounts: accs, }; }); }, [pendingOnly, pending, grouped]); return (

    Account Records

    {pending.length > 0 && ( )}
    {error && (

    {error}

    )} {accounts.length === 0 && (

    No account records discovered yet. Run a scrape to populate this list.

    )} {sections.map(section => (

    {section.label}

      {section.accounts.map(a => ( void handleStatusChange(id, status)} onDelete={id => void handleDelete(id)} /> ))}
    ))}
    ); }