From a127dd971b519252eb55b9587be83337c1657e2f Mon Sep 17 00:00:00 2001 From: Benjamin Admin Date: Tue, 12 May 2026 11:37:06 +0200 Subject: [PATCH] fix(compliance-check): resume polling after navigation away Save active check_id to localStorage so polling resumes when the user navigates away via sidebar and comes back. Same pattern as scan tab. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../agent/_components/ComplianceCheckTab.tsx | 46 ++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/admin-compliance/app/sdk/agent/_components/ComplianceCheckTab.tsx b/admin-compliance/app/sdk/agent/_components/ComplianceCheckTab.tsx index aeb3204..1fe3d13 100644 --- a/admin-compliance/app/sdk/agent/_components/ComplianceCheckTab.tsx +++ b/admin-compliance/app/sdk/agent/_components/ComplianceCheckTab.tsx @@ -29,6 +29,7 @@ type DocsState = Record const STORAGE_KEY_STATE = 'compliance-check-state' const STORAGE_KEY_RESULTS = 'compliance-check-results' const STORAGE_KEY_HISTORY = 'compliance-check-history' +const STORAGE_KEY_CHECK_ID = 'compliance-check-active-id' function emptyDocState(): DocState { return { url: '', text: '', loading: false, error: null } @@ -77,6 +78,9 @@ export function ComplianceCheckTab() { try { const s = localStorage.getItem(STORAGE_KEY_RESULTS); return s ? JSON.parse(s) : null } catch { return null } }) const [error, setError] = useState(null) + const [activeCheckId, setActiveCheckId] = useState(() => + typeof window !== 'undefined' ? localStorage.getItem(STORAGE_KEY_CHECK_ID) || '' : '' + ) const [history, setHistory] = useState(() => { if (typeof window === 'undefined') return [] try { return JSON.parse(localStorage.getItem(STORAGE_KEY_HISTORY) || '[]') } catch { return [] } @@ -91,6 +95,39 @@ export function ComplianceCheckTab() { try { localStorage.setItem(STORAGE_KEY_STATE, JSON.stringify(toSave)) } catch { /* quota */ } }, [docs]) + // Resume polling if check was in progress when navigating away + React.useEffect(() => { + if (!activeCheckId || results) return + let cancelled = false + setLoading(true) + setProgress('Pruefung laeuft noch...') + const poll = async () => { + while (!cancelled) { + await new Promise(r => setTimeout(r, 3000)) + try { + const res = await fetch(`/api/sdk/v1/agent/compliance-check?check_id=${activeCheckId}`) + if (!res.ok) continue + const data = await res.json() + if (data.progress) setProgress(data.progress) + if (data.status === 'completed' && data.result) { + setResults(data.result); setProgress(''); setLoading(false) + localStorage.setItem(STORAGE_KEY_RESULTS, JSON.stringify(data.result)) + localStorage.removeItem(STORAGE_KEY_CHECK_ID); setActiveCheckId('') + return + } + if (data.status === 'failed' || data.status === 'not_found') { + if (data.status === 'failed') setError(data.error || 'Pruefung fehlgeschlagen') + setProgress(''); setLoading(false) + localStorage.removeItem(STORAGE_KEY_CHECK_ID); setActiveCheckId('') + return + } + } catch { /* retry */ } + } + } + poll() + return () => { cancelled = true } + }, []) // eslint-disable-line react-hooks/exhaustive-deps + const updateDoc = useCallback((docType: DocTypeId, patch: Partial) => { setDocs(prev => ({ ...prev, [docType]: { ...prev[docType], ...patch } })) }, []) @@ -162,6 +199,8 @@ export function ComplianceCheckTab() { if (!startRes.ok) throw new Error(`Pruefung konnte nicht gestartet werden: ${startRes.status}`) const { check_id } = await startRes.json() if (!check_id) throw new Error('Keine Check-ID erhalten') + setActiveCheckId(check_id) + localStorage.setItem(STORAGE_KEY_CHECK_ID, check_id) // Poll for results let attempts = 0 @@ -175,6 +214,7 @@ export function ComplianceCheckTab() { setResults(pollData.result) setProgress('') localStorage.setItem(STORAGE_KEY_RESULTS, JSON.stringify(pollData.result)) + localStorage.removeItem(STORAGE_KEY_CHECK_ID); setActiveCheckId('') const resultKey = `compliance-check-result-${Date.now()}` try { localStorage.setItem(resultKey, JSON.stringify(pollData.result)) } catch { /* quota */ } @@ -190,11 +230,15 @@ export function ComplianceCheckTab() { break } if (pollData.status === 'failed') { + localStorage.removeItem(STORAGE_KEY_CHECK_ID); setActiveCheckId('') throw new Error(pollData.error || 'Pruefung fehlgeschlagen') } attempts++ } - if (attempts >= 120) throw new Error('Zeitlimit ueberschritten') + if (attempts >= 120) { + localStorage.removeItem(STORAGE_KEY_CHECK_ID); setActiveCheckId('') + throw new Error('Zeitlimit ueberschritten') + } } catch (e) { setError(e instanceof Error ? e.message : 'Unbekannter Fehler') setProgress('')