feat(data): "Erase my data" danger zone (Batch 5)
New services/userDataService.js eraseUserData() permanently wipes a user's
financial + imported data in one transaction (child → parent order for FK
safety): bills (+ cascading payments/monthly_bill_state/bill_history_ranges),
transactions/accounts/data_sources, categories/groups, templates, snowball,
spending rules/budgets, merchant rules, imports, and per-user hint tables. It
PRESERVES the account, sessions, 2FA/WebAuthn, login history and preferences —
this resets your data, not your account — then re-seeds default categories and
writes an audit row to import_history.
- POST /api/user/erase-data — rate-limited (demoDataLimiter), requires a
type-to-confirm token ("ERASE"), structured errors.
- UI: EraseDataSection danger-zone card (Export & backups pane) — red-accented,
"download a backup first" nudge, type-to-confirm AlertDialog, toasts; on
success DataPage reloads all state.
Tests: tests/eraseUserData.test.js — wipes user A only, preserves user B +
account + session, re-seeds categories, audited. Server 139 pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 15:21:07 -05:00
|
|
|
|
import { useState } from 'react';
|
|
|
|
|
|
import { toast } from 'sonner';
|
|
|
|
|
|
import { ShieldAlert, Loader2, Trash2 } from 'lucide-react';
|
|
|
|
|
|
import { api } from '@/api';
|
2026-07-04 21:33:25 -05:00
|
|
|
|
import { errMessage } from '@/lib/utils';
|
feat(data): "Erase my data" danger zone (Batch 5)
New services/userDataService.js eraseUserData() permanently wipes a user's
financial + imported data in one transaction (child → parent order for FK
safety): bills (+ cascading payments/monthly_bill_state/bill_history_ranges),
transactions/accounts/data_sources, categories/groups, templates, snowball,
spending rules/budgets, merchant rules, imports, and per-user hint tables. It
PRESERVES the account, sessions, 2FA/WebAuthn, login history and preferences —
this resets your data, not your account — then re-seeds default categories and
writes an audit row to import_history.
- POST /api/user/erase-data — rate-limited (demoDataLimiter), requires a
type-to-confirm token ("ERASE"), structured errors.
- UI: EraseDataSection danger-zone card (Export & backups pane) — red-accented,
"download a backup first" nudge, type-to-confirm AlertDialog, toasts; on
success DataPage reloads all state.
Tests: tests/eraseUserData.test.js — wipes user A only, preserves user B +
account + session, re-seeds categories, audited. Server 139 pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 15:21:07 -05:00
|
|
|
|
import { Button } from '@/components/ui/button';
|
|
|
|
|
|
import { Input } from '@/components/ui/input';
|
|
|
|
|
|
import {
|
|
|
|
|
|
AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent,
|
|
|
|
|
|
AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger,
|
|
|
|
|
|
} from '@/components/ui/alert-dialog';
|
2026-07-04 21:33:25 -05:00
|
|
|
|
import { SectionCard, type SectionCardProps } from './dataShared';
|
feat(data): "Erase my data" danger zone (Batch 5)
New services/userDataService.js eraseUserData() permanently wipes a user's
financial + imported data in one transaction (child → parent order for FK
safety): bills (+ cascading payments/monthly_bill_state/bill_history_ranges),
transactions/accounts/data_sources, categories/groups, templates, snowball,
spending rules/budgets, merchant rules, imports, and per-user hint tables. It
PRESERVES the account, sessions, 2FA/WebAuthn, login history and preferences —
this resets your data, not your account — then re-seeds default categories and
writes an audit row to import_history.
- POST /api/user/erase-data — rate-limited (demoDataLimiter), requires a
type-to-confirm token ("ERASE"), structured errors.
- UI: EraseDataSection danger-zone card (Export & backups pane) — red-accented,
"download a backup first" nudge, type-to-confirm AlertDialog, toasts; on
success DataPage reloads all state.
Tests: tests/eraseUserData.test.js — wipes user A only, preserves user B +
account + session, re-seeds categories, audited. Server 139 pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 15:21:07 -05:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Danger zone: permanently erase the user's financial data. Type-to-confirm, and
|
|
|
|
|
|
* makes clear that the account/login/2FA are preserved. Reuses POST
|
|
|
|
|
|
* /api/user/erase-data (transactional, audited, re-seeds default categories).
|
|
|
|
|
|
*/
|
2026-07-04 21:33:25 -05:00
|
|
|
|
export default function EraseDataSection({ onErased, cardProps = {} }: {
|
|
|
|
|
|
onErased?: () => void;
|
|
|
|
|
|
cardProps?: Partial<SectionCardProps>;
|
|
|
|
|
|
}) {
|
feat(data): "Erase my data" danger zone (Batch 5)
New services/userDataService.js eraseUserData() permanently wipes a user's
financial + imported data in one transaction (child → parent order for FK
safety): bills (+ cascading payments/monthly_bill_state/bill_history_ranges),
transactions/accounts/data_sources, categories/groups, templates, snowball,
spending rules/budgets, merchant rules, imports, and per-user hint tables. It
PRESERVES the account, sessions, 2FA/WebAuthn, login history and preferences —
this resets your data, not your account — then re-seeds default categories and
writes an audit row to import_history.
- POST /api/user/erase-data — rate-limited (demoDataLimiter), requires a
type-to-confirm token ("ERASE"), structured errors.
- UI: EraseDataSection danger-zone card (Export & backups pane) — red-accented,
"download a backup first" nudge, type-to-confirm AlertDialog, toasts; on
success DataPage reloads all state.
Tests: tests/eraseUserData.test.js — wipes user A only, preserves user B +
account + session, re-seeds categories, audited. Server 139 pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 15:21:07 -05:00
|
|
|
|
const [open, setOpen] = useState(false);
|
|
|
|
|
|
const [confirm, setConfirm] = useState('');
|
|
|
|
|
|
const [busy, setBusy] = useState(false);
|
|
|
|
|
|
const canErase = confirm.trim().toUpperCase() === 'ERASE';
|
|
|
|
|
|
|
|
|
|
|
|
async function handleErase() {
|
|
|
|
|
|
if (!canErase) return;
|
|
|
|
|
|
setBusy(true);
|
|
|
|
|
|
try {
|
2026-07-04 21:33:25 -05:00
|
|
|
|
const r = await api.eraseMyData(confirm.trim()) as { erased?: number };
|
|
|
|
|
|
const n = r.erased ?? 0;
|
|
|
|
|
|
toast.success(`Your data was erased — ${n} record${n === 1 ? '' : 's'} removed.`);
|
feat(data): "Erase my data" danger zone (Batch 5)
New services/userDataService.js eraseUserData() permanently wipes a user's
financial + imported data in one transaction (child → parent order for FK
safety): bills (+ cascading payments/monthly_bill_state/bill_history_ranges),
transactions/accounts/data_sources, categories/groups, templates, snowball,
spending rules/budgets, merchant rules, imports, and per-user hint tables. It
PRESERVES the account, sessions, 2FA/WebAuthn, login history and preferences —
this resets your data, not your account — then re-seeds default categories and
writes an audit row to import_history.
- POST /api/user/erase-data — rate-limited (demoDataLimiter), requires a
type-to-confirm token ("ERASE"), structured errors.
- UI: EraseDataSection danger-zone card (Export & backups pane) — red-accented,
"download a backup first" nudge, type-to-confirm AlertDialog, toasts; on
success DataPage reloads all state.
Tests: tests/eraseUserData.test.js — wipes user A only, preserves user B +
account + session, re-seeds categories, audited. Server 139 pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 15:21:07 -05:00
|
|
|
|
setOpen(false);
|
|
|
|
|
|
setConfirm('');
|
|
|
|
|
|
onErased?.();
|
|
|
|
|
|
} catch (err) {
|
2026-07-04 21:33:25 -05:00
|
|
|
|
toast.error(errMessage(err, 'Erase failed.'));
|
feat(data): "Erase my data" danger zone (Batch 5)
New services/userDataService.js eraseUserData() permanently wipes a user's
financial + imported data in one transaction (child → parent order for FK
safety): bills (+ cascading payments/monthly_bill_state/bill_history_ranges),
transactions/accounts/data_sources, categories/groups, templates, snowball,
spending rules/budgets, merchant rules, imports, and per-user hint tables. It
PRESERVES the account, sessions, 2FA/WebAuthn, login history and preferences —
this resets your data, not your account — then re-seeds default categories and
writes an audit row to import_history.
- POST /api/user/erase-data — rate-limited (demoDataLimiter), requires a
type-to-confirm token ("ERASE"), structured errors.
- UI: EraseDataSection danger-zone card (Export & backups pane) — red-accented,
"download a backup first" nudge, type-to-confirm AlertDialog, toasts; on
success DataPage reloads all state.
Tests: tests/eraseUserData.test.js — wipes user A only, preserves user B +
account + session, re-seeds categories, audited. Server 139 pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 15:21:07 -05:00
|
|
|
|
} finally {
|
|
|
|
|
|
setBusy(false);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
|
<SectionCard {...cardProps}>
|
|
|
|
|
|
<div className="space-y-4 px-6 py-5">
|
|
|
|
|
|
<div className="rounded-lg border border-rose-300/60 bg-rose-50 p-4 dark:border-rose-800/50 dark:bg-rose-950/20">
|
|
|
|
|
|
<div className="flex items-start gap-3">
|
|
|
|
|
|
<ShieldAlert className="mt-0.5 h-5 w-5 shrink-0 text-rose-600 dark:text-rose-400" />
|
|
|
|
|
|
<div className="min-w-0 text-sm">
|
|
|
|
|
|
<p className="font-medium text-rose-700 dark:text-rose-300">This permanently deletes your financial data.</p>
|
|
|
|
|
|
<p className="mt-1 text-muted-foreground">
|
|
|
|
|
|
Bills, payments, transactions, categories, bank connections, imports and rules will be erased and
|
|
|
|
|
|
can’t be recovered. Your <span className="font-medium text-foreground">account, login and 2FA are kept</span>.
|
|
|
|
|
|
Consider downloading a backup first.
|
|
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<AlertDialog open={open} onOpenChange={(v) => { setOpen(v); if (!v) setConfirm(''); }}>
|
|
|
|
|
|
<AlertDialogTrigger asChild>
|
|
|
|
|
|
<Button
|
|
|
|
|
|
variant="outline"
|
|
|
|
|
|
className="gap-2 border-rose-300 text-rose-600 hover:bg-rose-50 dark:border-rose-800/60 dark:text-rose-400 dark:hover:bg-rose-950/30"
|
|
|
|
|
|
>
|
|
|
|
|
|
<Trash2 className="h-4 w-4" /> Erase my data…
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
</AlertDialogTrigger>
|
|
|
|
|
|
<AlertDialogContent>
|
|
|
|
|
|
<AlertDialogHeader>
|
|
|
|
|
|
<AlertDialogTitle>Erase all your data?</AlertDialogTitle>
|
|
|
|
|
|
<AlertDialogDescription>
|
|
|
|
|
|
This permanently deletes your bills, payments, transactions, categories, bank connections and imports.
|
|
|
|
|
|
It cannot be undone. Type <span className="font-mono font-semibold text-foreground">ERASE</span> to confirm.
|
|
|
|
|
|
</AlertDialogDescription>
|
|
|
|
|
|
</AlertDialogHeader>
|
|
|
|
|
|
<Input
|
|
|
|
|
|
autoFocus
|
|
|
|
|
|
value={confirm}
|
|
|
|
|
|
onChange={(e) => setConfirm(e.target.value)}
|
|
|
|
|
|
placeholder="Type ERASE"
|
|
|
|
|
|
className="font-mono"
|
|
|
|
|
|
aria-label="Type ERASE to confirm"
|
|
|
|
|
|
/>
|
|
|
|
|
|
<AlertDialogFooter>
|
|
|
|
|
|
<AlertDialogCancel disabled={busy}>Cancel</AlertDialogCancel>
|
|
|
|
|
|
<AlertDialogAction
|
|
|
|
|
|
disabled={!canErase || busy}
|
|
|
|
|
|
onClick={(e) => { e.preventDefault(); handleErase(); }}
|
|
|
|
|
|
className="bg-rose-600 text-white hover:bg-rose-700"
|
|
|
|
|
|
>
|
|
|
|
|
|
{busy ? <><Loader2 className="mr-1.5 h-4 w-4 animate-spin" />Erasing…</> : 'Permanently erase'}
|
|
|
|
|
|
</AlertDialogAction>
|
|
|
|
|
|
</AlertDialogFooter>
|
|
|
|
|
|
</AlertDialogContent>
|
|
|
|
|
|
</AlertDialog>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</SectionCard>
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|