From 8e4015545989a4e7564357c579425bf5c0b86420 Mon Sep 17 00:00:00 2001 From: Benjamin Admin Date: Tue, 5 May 2026 10:47:39 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20Scan=20state=20persists=20across=20navi?= =?UTF-8?q?gation=20=E2=80=94=20resume=20polling=20on=20return?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - URL, mode, tab, scan result persisted in localStorage - Active scan_id stored — polling resumes when returning to page - Scan results survive navigation to other SDK modules - 'Scan laeuft noch...' shown when returning to in-progress scan - Cleans up localStorage when scan completes or fails Co-Authored-By: Claude Opus 4.6 (1M context) --- admin-compliance/app/sdk/agent/page.tsx | 69 +++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 4 deletions(-) diff --git a/admin-compliance/app/sdk/agent/page.tsx b/admin-compliance/app/sdk/agent/page.tsx index f400763..89774b1 100644 --- a/admin-compliance/app/sdk/agent/page.tsx +++ b/admin-compliance/app/sdk/agent/page.tsx @@ -21,15 +21,71 @@ const TABS: { id: AnalysisTab; label: string; desc: string }[] = [ ] export default function AgentPage() { - const [url, setUrl] = useState('') - const [mode, setMode] = useState('post_launch') - const [tab, setTab] = useState('quick') + // Restore state from localStorage on mount + const [url, setUrl] = useState(() => typeof window !== 'undefined' ? localStorage.getItem('agent-scan-url') || '' : '') + const [mode, setMode] = useState(() => (typeof window !== 'undefined' ? localStorage.getItem('agent-scan-mode') as AnalysisMode : null) || 'post_launch') + const [tab, setTab] = useState(() => (typeof window !== 'undefined' ? localStorage.getItem('agent-scan-tab') as AnalysisTab : null) || 'quick') const [scanLoading, setScanLoading] = useState(false) const [scanError, setScanError] = useState(null) - const [scanData, setScanData] = useState(null) + const [scanData, setScanData] = useState(() => { + if (typeof window === 'undefined') return null + try { const s = localStorage.getItem('agent-scan-result'); return s ? JSON.parse(s) : null } catch { return null } + }) const [scanProgress, setScanProgress] = useState('') + const [activeScanId, setActiveScanId] = useState(() => typeof window !== 'undefined' ? localStorage.getItem('agent-scan-id') || '' : '') const { analyze, answerFollowUp, loading, error, result, history } = useAgentAnalysis() + // Persist state to localStorage + React.useEffect(() => { localStorage.setItem('agent-scan-url', url) }, [url]) + React.useEffect(() => { localStorage.setItem('agent-scan-mode', mode) }, [mode]) + React.useEffect(() => { localStorage.setItem('agent-scan-tab', tab) }, [tab]) + React.useEffect(() => { if (scanData?.services) localStorage.setItem('agent-scan-result', JSON.stringify(scanData)) }, [scanData]) + + // Resume polling if scan was in progress when page was left + React.useEffect(() => { + if (!activeScanId || scanData?.services) return + let cancelled = false + setScanLoading(true) + setScanProgress('Scan laeuft noch...') + const poll = async () => { + while (!cancelled) { + await new Promise(r => setTimeout(r, 5000)) + try { + const res = await fetch(`/api/sdk/v1/agent/scan?scan_id=${activeScanId}`) + if (!res.ok) continue + const data = await res.json() + if (data.progress) setScanProgress(data.progress) + if (data.status === 'completed' && data.result) { + setScanData(data.result) + setScanProgress('') + setScanLoading(false) + localStorage.setItem('agent-scan-result', JSON.stringify(data.result)) + localStorage.removeItem('agent-scan-id') + setActiveScanId('') + return + } + if (data.status === 'failed') { + setScanError(data.error || 'Scan fehlgeschlagen') + setScanProgress('') + setScanLoading(false) + localStorage.removeItem('agent-scan-id') + setActiveScanId('') + return + } + if (data.status === 'not_found') { + setScanProgress('') + setScanLoading(false) + localStorage.removeItem('agent-scan-id') + setActiveScanId('') + return + } + } catch { /* retry */ } + } + } + poll() + return () => { cancelled = true } + }, []) // eslint-disable-line react-hooks/exhaustive-deps + const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() if (!url.trim()) return @@ -51,6 +107,8 @@ export default function AgentPage() { if (!startRes.ok) throw new Error(`Scan konnte nicht gestartet werden: ${startRes.status}`) const { scan_id } = await startRes.json() if (!scan_id) throw new Error('Keine Scan-ID erhalten') + setActiveScanId(scan_id) + localStorage.setItem('agent-scan-id', scan_id) // Step 2: Poll for results let attempts = 0 @@ -68,6 +126,9 @@ export default function AgentPage() { if (pollData.status === 'completed' && pollData.result) { setScanData(pollData.result) setScanProgress('') + localStorage.setItem('agent-scan-result', JSON.stringify(pollData.result)) + localStorage.removeItem('agent-scan-id') + setActiveScanId('') break } if (pollData.status === 'failed') {