feat: Data Management tab — purge endpoints for all major data stores

Backend — new DELETE /purge-all endpoints:
  - /api/logs/purge-all          → truncate system_logs (immediate, no 30d wait)
  - /api/reports/purge-all       → cycle_reports + ai_reports
  - /api/portfolio/purge-all     → portfolio + trade_entry_prices
  - /api/analytics/purge-all     → pattern_score_history, regime_clusters,
                                    pattern_embeddings, cycle_runs,
                                    macro_regime_history, geo_alert_history
  - /api/var/purge-all           → var_snapshots + pnl_snapshots
  - /api/pattern-lab/purge-all   → backtest_lab_runs

Frontend — Config.tsx: new 'Data Management' tab
  - PurgeButton component with inline double-confirm (click Purge → confirm)
  - Shows table names affected + row count deleted
  - 6 purge actions: Logs, AI Reports, Portfolio, Analytics, VaR, Pattern Lab

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
OpenSquared
2026-06-22 18:25:01 +02:00
parent 303ecc2a3a
commit 66f6607568
7 changed files with 221 additions and 3 deletions

View File

@@ -1,7 +1,7 @@
import { useState, useEffect } from 'react'
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import { useSources, useUpdateSources, useUpdateApiKeys, useConfig, useAiStatus, useAnalysisConfig, useSaveAnalysisConfig, useCycleStatus, useUpdateCycleConfig, useTriggerCycle, useRiskProfiles, useUpsertProfile, useDeleteProfile, useExitDefaults, useSaveExitDefaults, useOptionsGate, useSaveOptionsGate, useTechIndicatorsConfig, useSaveTechIndicatorsConfig } from '../hooks/useApi'
import { Settings, Key, Globe, CheckCircle, XCircle, AlertCircle, Save, Eye, EyeOff, Brain, SlidersHorizontal, RefreshCw, Zap, Plus, Trash2, Pencil, X, Lock, Gauge, DollarSign, TrendingUp, ShieldAlert } from 'lucide-react'
import { Settings, Key, Globe, CheckCircle, XCircle, AlertCircle, Save, Eye, EyeOff, Brain, SlidersHorizontal, RefreshCw, Zap, Plus, Trash2, Pencil, X, Lock, Gauge, DollarSign, TrendingUp, ShieldAlert, DatabaseBackup } from 'lucide-react'
import clsx from 'clsx'
const API = ''
@@ -353,7 +353,7 @@ export default function Config() {
const [fredKey, setFredKey] = useState('')
const [showKeys, setShowKeys] = useState(false)
const [savedMsg, setSavedMsg] = useState('')
const [configTab, setConfigTab] = useState<'ia' | 'cycle' | 'sources' | 'options' | 'profiles'>('ia')
const [configTab, setConfigTab] = useState<'ia' | 'cycle' | 'sources' | 'options' | 'profiles' | 'data'>('ia')
// IV gate local state
const [ivGateEnabled, setIvGateEnabled] = useState(true)
@@ -517,6 +517,7 @@ export default function Config() {
{ key: 'sources', label: 'Sources', icon: Globe },
{ key: 'options', label: 'Options — Settings', icon: TrendingUp },
{ key: 'profiles', label: 'Risk Profiles', icon: SlidersHorizontal },
{ key: 'data', label: 'Data Management', icon: DatabaseBackup },
] as const
return (
@@ -1500,6 +1501,145 @@ export default function Config() {
{/* ── Profils de risque ── */}
{configTab === 'profiles' && <RiskProfilesCard />}
{/* ── Data Management ── */}
{configTab === 'data' && <DataManagementCard />}
</div>
)
}
// ── Data Management card ───────────────────────────────────────────────────────
function PurgeButton({ label, description, tables, endpoint, color = 'red' }: {
label: string
description: string
tables: string[]
endpoint: string
color?: 'red' | 'orange'
}) {
const qc = useQueryClient()
const [confirm, setConfirm] = useState(false)
const [loading, setLoading] = useState(false)
const [done, setDone] = useState<string | null>(null)
const handlePurge = async () => {
setLoading(true)
try {
const res = await fetch(endpoint, { method: 'DELETE' })
const data = await res.json()
setDone(`Deleted ${data.deleted ?? '?'} rows`)
setConfirm(false)
qc.invalidateQueries()
} catch (e: any) {
setDone(`Error: ${e?.message}`)
} finally {
setLoading(false)
}
}
const borderCol = color === 'red' ? 'border-red-800/40' : 'border-orange-800/40'
const bgCol = color === 'red' ? 'bg-red-900/5' : 'bg-orange-900/5'
const btnCol = color === 'red'
? 'bg-red-700 hover:bg-red-600 disabled:opacity-50'
: 'bg-orange-700 hover:bg-orange-600 disabled:opacity-50'
return (
<div className={`border ${borderCol} ${bgCol} rounded-lg p-4`}>
<div className="flex items-start justify-between gap-3">
<div className="flex-1">
<div className="text-sm font-semibold text-slate-200 mb-0.5">{label}</div>
<div className="text-xs text-slate-500 mb-1.5">{description}</div>
<div className="flex flex-wrap gap-1">
{tables.map(t => (
<span key={t} className="text-[10px] font-mono bg-slate-800 text-slate-400 px-1.5 py-0.5 rounded">{t}</span>
))}
</div>
</div>
<div className="flex-shrink-0 flex flex-col items-end gap-1.5">
{done && <span className="text-[10px] text-emerald-400">{done}</span>}
{!confirm ? (
<button onClick={() => { setConfirm(true); setDone(null) }}
className={`text-xs px-3 py-1.5 rounded text-white font-semibold ${btnCol}`}>
<Trash2 className="w-3 h-3 inline mr-1" />Purge
</button>
) : (
<div className="flex items-center gap-2">
<span className="text-xs text-red-400 font-semibold">Are you sure?</span>
<button onClick={handlePurge} disabled={loading}
className={`text-xs px-3 py-1.5 rounded text-white font-semibold ${btnCol}`}>
{loading ? 'Deleting…' : 'Yes, delete all'}
</button>
<button onClick={() => setConfirm(false)} className="text-xs text-slate-500 hover:text-slate-300">Cancel</button>
</div>
)}
</div>
</div>
</div>
)
}
function DataManagementCard() {
return (
<div className="space-y-4">
<div className="flex items-center gap-2 mb-2">
<DatabaseBackup className="w-4 h-4 text-red-400" />
<h2 className="text-base font-bold text-white">Data Management</h2>
<span className="text-xs text-slate-500 ml-2">Irreversible operations all deletes are permanent</span>
</div>
<PurgeButton
label="System Logs"
description="Clear all system logs immediately (normally kept 30 days)."
tables={['system_logs']}
endpoint="/api/logs/purge-all"
color="orange"
/>
<PurgeButton
label="AI Reports (Rapport IA)"
description="Delete all generated cycle reports and trade assessments."
tables={['cycle_reports', 'ai_reports']}
endpoint="/api/reports/purge-all"
color="orange"
/>
<PurgeButton
label="Portfolio — All Positions"
description="Delete every position (open + closed) and all trade entry price history. Cannot be undone."
tables={['portfolio', 'trade_entry_prices']}
endpoint="/api/portfolio/purge-all"
color="red"
/>
<PurgeButton
label="Analytics History"
description="Purge all pattern score history, regime clusters, embeddings, cycle run logs, and macro/geo alert history."
tables={['pattern_score_history', 'regime_clusters', 'pattern_embeddings', 'cycle_runs', 'macro_regime_history', 'geo_alert_history']}
endpoint="/api/analytics/purge-all"
color="red"
/>
<PurgeButton
label="VaR & PnL Snapshots"
description="Delete all Value-at-Risk snapshots and daily PnL snapshots."
tables={['var_snapshots', 'pnl_snapshots']}
endpoint="/api/var/purge-all"
color="orange"
/>
<PurgeButton
label="Pattern Lab Runs"
description="Delete all Pattern Lab backtest runs (event presets + instrument scans)."
tables={['backtest_lab_runs']}
endpoint="/api/pattern-lab/purge-all"
color="orange"
/>
<div className="mt-2 p-3 border border-slate-700/40 rounded-lg bg-dark-700/30">
<p className="text-xs text-slate-500">
<span className="font-semibold text-slate-400">Note:</span> Custom patterns, risk profiles, config, API keys, and the Knowledge Base are managed from their respective sections and are not affected by the purges above.
</p>
</div>
</div>
)
}