diff --git a/app/api/accounts/wipe-all/route.ts b/app/api/accounts/wipe-all/route.ts new file mode 100644 index 0000000..334796a --- /dev/null +++ b/app/api/accounts/wipe-all/route.ts @@ -0,0 +1,39 @@ +import { NextResponse } from 'next/server'; +import { getClients } from '@/lib/clients'; +import { wipeAccountData } from '@/lib/db'; + +/** + * POST /api/accounts/wipe-all — the bulk form of /api/accounts/[id]/wipe: + * drop every account's cached rows on every firm and rebuild them all from + * fresh Tradovate reports. One report per account, so this takes a while on + * big rosters; firms run in parallel, accounts within a firm sequentially + * (that's fetchDaysTraded's own pacing against the flaky report server). + * + * Same destructive caveat as the single wipe: cached rows older than the + * report's 28-day window cannot be refetched. The UI confirms first. + */ +export async function POST() { + const clients = [...getClients().values()]; + + let wiped = 0; + let failedAccounts = 0; + + await Promise.all(clients.map(async (client) => { + for (const account of client.accountList) { + wipeAccountData(account.id); + delete client.dailyPnL[account.id]; + delete client.fullDailyPnL[account.id]; + delete client.daysTraded[account.id]; + delete client.priorProfit[account.id]; + delete client.lastFundDates[account.id]; + delete client.fundTransactions[account.id]; + delete client.lastFetchErrors[account.name]; + delete client.lastFetchRaw[account.name]; + wiped++; + } + const result = await client.fetchDaysTraded(); + failedAccounts += result.failedAccounts; + })); + + return NextResponse.json({ ok: true, wiped, failedAccounts }); +} diff --git a/app/page.tsx b/app/page.tsx index 1bd7ad5..2aa4e4d 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -244,6 +244,72 @@ function CredentialForm({ firm, onSaved }: { firm: FirmConfig; onSaved: () => vo ); } +/** Bulk form of the detail page's wipe: drops every account's cached rows on + * every firm and refetches them all. One report per account, so the busy state + * can run for minutes on a big roster. Same 28-day-window caveat, so the + * confirm step spells it out. */ +function WipeAllButton() { + const [confirming, setConfirming] = useState(false); + const [busy, setBusy] = useState(false); + const [result, setResult] = useState(''); + + async function wipeAll() { + setBusy(true); + setResult(''); + try { + const res = await fetch('/api/accounts/wipe-all', { method: 'POST' }); + const body = await res.json().catch(() => ({})); + if (!res.ok) throw new Error(body.error ?? 'Wipe failed'); + setResult(body.failedAccounts > 0 + ? `Rebuilt ${body.wiped}, ${body.failedAccounts} refetch failure(s)` + : `Rebuilt ${body.wiped} account(s)`); + // No refresh hook needed — the dashboard polls /api/state every 5s. + setConfirming(false); + } catch (err) { + setResult(err instanceof Error ? err.message : 'Wipe failed'); + } finally { + setBusy(false); + } + } + + if (confirming) { + return ( +
+ + Wipe every account's cached data and refetch? Days older than the 28-day report window are lost for good. + + + +
+ ); + } + + return ( +
+ {result && {result}} + +
+ ); +} + function AccountRow({ account, firm, hideDead, privacy, onAssigned }: { account: AccountState; firm: FirmConfig; @@ -623,6 +689,7 @@ export default function Home() {

AutoTrader

+ {deadCount > 0 && (