diff --git a/admin-compliance/app/sdk/control-library/page.tsx b/admin-compliance/app/sdk/control-library/page.tsx index 0e7bb77..d658547 100644 --- a/admin-compliance/app/sdk/control-library/page.tsx +++ b/admin-compliance/app/sdk/control-library/page.tsx @@ -101,6 +101,10 @@ export default function ControlLibraryPage() { similarity_score: number; match_rank: number; match_method: string }>>([]) + // Abort controllers for cancelling stale requests + const metaAbortRef = useRef(null) + const controlsAbortRef = useRef(null) + // Debounce search const searchTimer = useRef | null>(null) useEffect(() => { @@ -135,17 +139,25 @@ export default function ControlLibraryPage() { } catch { /* ignore */ } }, []) - // Load faceted metadata (reloads when filters change) + // Load faceted metadata (reloads when filters change, cancels stale requests) const loadMeta = useCallback(async () => { + if (metaAbortRef.current) metaAbortRef.current.abort() + const controller = new AbortController() + metaAbortRef.current = controller try { const qs = buildParams() - const res = await fetch(`${BACKEND_URL}?endpoint=controls-meta${qs ? `&${qs}` : ''}`) - if (res.ok) setMeta(await res.json()) - } catch { /* ignore */ } + const res = await fetch(`${BACKEND_URL}?endpoint=controls-meta${qs ? `&${qs}` : ''}`, { signal: controller.signal }) + if (res.ok && !controller.signal.aborted) setMeta(await res.json()) + } catch (e) { + if (e instanceof DOMException && e.name === 'AbortError') return + } }, [buildParams]) - // Load controls page + // Load controls page (cancels stale requests) const loadControls = useCallback(async () => { + if (controlsAbortRef.current) controlsAbortRef.current.abort() + const controller = new AbortController() + controlsAbortRef.current = controller try { setLoading(true) @@ -164,19 +176,22 @@ export default function ControlLibraryPage() { const countQs = buildParams() const [ctrlRes, countRes] = await Promise.all([ - fetch(`${BACKEND_URL}?endpoint=controls&${qs}`), - fetch(`${BACKEND_URL}?endpoint=controls-count&${countQs}`), + fetch(`${BACKEND_URL}?endpoint=controls&${qs}`, { signal: controller.signal }), + fetch(`${BACKEND_URL}?endpoint=controls-count&${countQs}`, { signal: controller.signal }), ]) - if (ctrlRes.ok) setControls(await ctrlRes.json()) - if (countRes.ok) { - const data = await countRes.json() - setTotalCount(data.total || 0) + if (!controller.signal.aborted) { + if (ctrlRes.ok) setControls(await ctrlRes.json()) + if (countRes.ok) { + const data = await countRes.json() + setTotalCount(data.total || 0) + } } } catch (err) { + if (err instanceof DOMException && err.name === 'AbortError') return setError(err instanceof Error ? err.message : 'Fehler beim Laden') } finally { - setLoading(false) + if (!controller.signal.aborted) setLoading(false) } }, [buildParams, sortBy, currentPage]) @@ -701,6 +716,7 @@ export default function ControlLibraryPage() { {Object.entries(VERIFICATION_METHODS).map(([k, v]) => ( ))} + {meta?.verification_method_counts?.['__none__'] ? : null}