'use client' /** * Branchenspezifische Module (Phase 3.3) * * Industry-specific compliance template packages: * - Browse industry templates (grid view) * - View full detail with VVT, TOM, Risk tabs * - Apply template packages to current compliance setup */ import { useState, useEffect, useCallback } from 'react' import type { IndustrySummary, IndustryTemplate, DetailTab } from './_types' import { GridSkeleton, DetailSkeleton } from './_components/Skeletons' import { PageHeader } from './_components/PageHeader' import { ErrorPanel } from './_components/ErrorPanel' import { IndustryGrid } from './_components/IndustryGrid' import { DetailHeader } from './_components/DetailHeader' import { DetailContent } from './_components/DetailContent' import { Toast } from './_components/Toast' import { EmptyState } from './_components/EmptyState' export default function IndustryTemplatesPage() { const [industries, setIndustries] = useState([]) const [selectedDetail, setSelectedDetail] = useState(null) const [selectedSlug, setSelectedSlug] = useState(null) const [activeTab, setActiveTab] = useState('vvt') const [loading, setLoading] = useState(true) const [detailLoading, setDetailLoading] = useState(false) const [error, setError] = useState(null) const [detailError, setDetailError] = useState(null) const [applying, setApplying] = useState(false) const [toastMessage, setToastMessage] = useState(null) const loadIndustries = useCallback(async () => { setLoading(true) setError(null) try { const res = await fetch('/api/sdk/v1/industry/templates') if (!res.ok) { throw new Error(`HTTP ${res.status}: ${res.statusText}`) } const data = await res.json() setIndustries(Array.isArray(data) ? data : data.industries || data.templates || []) } catch (err) { console.error('Failed to load industries:', err) setError('Branchenvorlagen konnten nicht geladen werden. Bitte pruefen Sie die Verbindung zum Backend.') } finally { setLoading(false) } }, []) const loadDetail = useCallback(async (slug: string) => { setDetailLoading(true) setDetailError(null) setSelectedSlug(slug) setActiveTab('vvt') try { const [detailRes, vvtRes, tomRes, risksRes] = await Promise.all([ fetch(`/api/sdk/v1/industry/templates/${slug}`), fetch(`/api/sdk/v1/industry/templates/${slug}/vvt`), fetch(`/api/sdk/v1/industry/templates/${slug}/tom`), fetch(`/api/sdk/v1/industry/templates/${slug}/risks`), ]) if (!detailRes.ok) { throw new Error(`HTTP ${detailRes.status}: ${detailRes.statusText}`) } const detail: IndustryTemplate = await detailRes.json() if (vvtRes.ok) { const vvtData = await vvtRes.json() detail.vvt_templates = vvtData.vvt_templates || vvtData.templates || vvtData || [] } if (tomRes.ok) { const tomData = await tomRes.json() detail.tom_recommendations = tomData.tom_recommendations || tomData.recommendations || tomData || [] } if (risksRes.ok) { const risksData = await risksRes.json() detail.risk_scenarios = risksData.risk_scenarios || risksData.scenarios || risksData || [] } setSelectedDetail(detail) } catch (err) { console.error('Failed to load industry detail:', err) setDetailError('Details konnten nicht geladen werden. Bitte versuchen Sie es erneut.') } finally { setDetailLoading(false) } }, []) useEffect(() => { loadIndustries() }, [loadIndustries]) const handleBackToGrid = useCallback(() => { setSelectedSlug(null) setSelectedDetail(null) setDetailError(null) }, []) const handleApplyPackage = useCallback(async () => { if (!selectedDetail) return setApplying(true) try { await new Promise((resolve) => setTimeout(resolve, 1500)) setToastMessage( `Branchenpaket "${selectedDetail.name}" wurde erfolgreich angewendet. ` + `${selectedDetail.vvt_templates?.length || 0} VVT-Vorlagen, ` + `${selectedDetail.tom_recommendations?.length || 0} TOM-Empfehlungen und ` + `${selectedDetail.risk_scenarios?.length || 0} Risiko-Szenarien wurden importiert.` ) } catch { setToastMessage('Fehler beim Anwenden des Branchenpakets. Bitte versuchen Sie es erneut.') } finally { setApplying(false) } }, [selectedDetail]) useEffect(() => { if (!toastMessage) return const timer = setTimeout(() => setToastMessage(null), 6000) return () => clearTimeout(timer) }, [toastMessage]) return (
{error && } {loading ? ( selectedSlug ? : ) : selectedSlug ? (
{detailLoading ? ( ) : detailError ? ( <> loadDetail(selectedSlug)} /> ) : selectedDetail ? ( <> ) : null}
) : industries.length === 0 && !error ? ( ) : ( )} {toastMessage && ( setToastMessage(null)} /> )}
) }