From 6cb5da56b36854415e8c13b9a8825025c33637e7 Mon Sep 17 00:00:00 2001 From: Benjamin Admin Date: Mon, 11 May 2026 07:50:03 +0200 Subject: [PATCH] =?UTF-8?q?feat(frontend):=20persistent=20gap=20projects?= =?UTF-8?q?=20=E2=80=94=20list,=20create,=20re-analyze?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Project list view with saved projects - Create + analyze in one flow (saves to DB) - Re-open saved projects for re-analysis - 3 views: projects list → wizard → dashboard Co-Authored-By: Claude Opus 4.6 (1M context) --- .../app/sdk/gap-analysis/page.tsx | 162 ++++++++++++++++-- 1 file changed, 143 insertions(+), 19 deletions(-) diff --git a/admin-compliance/app/sdk/gap-analysis/page.tsx b/admin-compliance/app/sdk/gap-analysis/page.tsx index 075eb5e..4ef8d9c 100644 --- a/admin-compliance/app/sdk/gap-analysis/page.tsx +++ b/admin-compliance/app/sdk/gap-analysis/page.tsx @@ -1,9 +1,17 @@ 'use client' -import React, { useState } from 'react' +import React, { useState, useEffect, useCallback } from 'react' import { ProductWizard } from './_components/ProductWizard' import { GapDashboard } from './_components/GapDashboard' +interface GapProject { + id: string + name: string + description: string + product_type: string + created_at: string +} + interface GapReport { profile_id: string profile_name: string @@ -39,23 +47,80 @@ interface GapReport { }> } +type View = 'projects' | 'wizard' | 'dashboard' + +const PRODUCT_TYPE_LABELS: Record = { + iot: 'IoT', software: 'Software', saas: 'SaaS', hardware: 'Hardware', + machinery: 'Maschine', medical_device: 'Medizin', exchange: 'Fintech', other: 'Sonstiges', +} + export default function GapAnalysisPage() { + const [view, setView] = useState('projects') + const [projects, setProjects] = useState([]) const [report, setReport] = useState(null) const [loading, setLoading] = useState(false) const [error, setError] = useState('') - const handleAnalyze = async (profile: Record) => { + const loadProjects = useCallback(async () => { + try { + const res = await fetch('/api/sdk/v1/gap/projects', { + headers: { 'X-Tenant-ID': '00000000-0000-0000-0000-000000000001' }, + }) + if (res.ok) { + const data = await res.json() + setProjects(data.projects || []) + } + } catch { /* ignore */ } + }, []) + + useEffect(() => { loadProjects() }, [loadProjects]) + + const handleCreateAndAnalyze = async (profile: Record) => { setLoading(true) setError('') try { - const res = await fetch('/api/sdk/v1/gap/analyze', { + // Save project + const createRes = await fetch('/api/sdk/v1/gap/projects', { method: 'POST', - headers: { 'Content-Type': 'application/json' }, + headers: { + 'Content-Type': 'application/json', + 'X-Tenant-ID': '00000000-0000-0000-0000-000000000001', + }, body: JSON.stringify(profile), }) + if (!createRes.ok) throw new Error('Projekt konnte nicht gespeichert werden') + const created = await createRes.json() + const projectId = created.project?.id + + // Run analysis + const analyzeRes = await fetch(`/api/sdk/v1/gap/projects/${projectId}/analyze`, { + method: 'POST', + headers: { 'X-Tenant-ID': '00000000-0000-0000-0000-000000000001' }, + }) + if (!analyzeRes.ok) throw new Error(await analyzeRes.text()) + const data = await analyzeRes.json() + setReport(data) + setView('dashboard') + loadProjects() + } catch (e) { + setError(e instanceof Error ? e.message : 'Analyse fehlgeschlagen') + } finally { + setLoading(false) + } + } + + const handleOpenProject = async (projectId: string) => { + setLoading(true) + setError('') + try { + const res = await fetch(`/api/sdk/v1/gap/projects/${projectId}/analyze`, { + method: 'POST', + headers: { 'X-Tenant-ID': '00000000-0000-0000-0000-000000000001' }, + }) if (!res.ok) throw new Error(await res.text()) const data = await res.json() setReport(data) + setView('dashboard') } catch (e) { setError(e instanceof Error ? e.message : 'Analyse fehlgeschlagen') } finally { @@ -66,29 +131,88 @@ export default function GapAnalysisPage() { return (
-
-

- Regulatory Gap-Analyse -

-

- Beschreiben Sie Ihr Produkt und erhalten Sie eine priorisierte - Liste der Compliance-Anforderungen. -

+
+
+

+ Regulatory Gap-Analyse +

+

+ Produkt beschreiben, Regulierungen erkennen, Prioritaeten setzen. +

+
+ {view !== 'projects' && ( + + )}
{error && (

{error}

+
)} - {!report ? ( - - ) : ( - setReport(null)} - /> + {view === 'projects' && ( +
+ {/* New project button */} + + + {/* Project list */} + {projects.length > 0 && ( +
+

Gespeicherte Projekte

+ {projects.map(p => ( + + ))} +
+ )} + + {projects.length === 0 && ( +

+ Noch keine Projekte. Starten Sie Ihre erste Gap-Analyse. +

+ )} +
+ )} + + {view === 'wizard' && ( + + )} + + {view === 'dashboard' && report && ( + { setView('projects'); setReport(null) }} /> )}