'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' // ============================================================================= // TYPES // ============================================================================= interface IndustrySummary { slug: string name: string description: string icon: string regulation_count: number template_count: number } interface IndustryTemplate { slug: string name: string description: string icon: string regulations: string[] vvt_templates: VVTTemplate[] tom_recommendations: TOMRecommendation[] risk_scenarios: RiskScenario[] } interface VVTTemplate { name: string purpose: string legal_basis: string data_categories: string[] data_subjects: string[] retention_period: string } interface TOMRecommendation { category: string name: string description: string priority: string } interface RiskScenario { name: string description: string likelihood: string impact: string mitigation: string } // ============================================================================= // CONSTANTS // ============================================================================= type DetailTab = 'vvt' | 'tom' | 'risks' const DETAIL_TABS: { key: DetailTab; label: string }[] = [ { key: 'vvt', label: 'VVT-Vorlagen' }, { key: 'tom', label: 'TOM-Empfehlungen' }, { key: 'risks', label: 'Risiko-Szenarien' }, ] const PRIORITY_COLORS: Record = { critical: { bg: 'bg-red-50', text: 'text-red-700', border: 'border-red-200' }, high: { bg: 'bg-orange-50', text: 'text-orange-700', border: 'border-orange-200' }, medium: { bg: 'bg-yellow-50', text: 'text-yellow-700', border: 'border-yellow-200' }, low: { bg: 'bg-green-50', text: 'text-green-700', border: 'border-green-200' }, } const PRIORITY_LABELS: Record = { critical: 'Kritisch', high: 'Hoch', medium: 'Mittel', low: 'Niedrig', } const LIKELIHOOD_COLORS: Record = { low: 'bg-green-500', medium: 'bg-yellow-500', high: 'bg-orange-500', } const IMPACT_COLORS: Record = { low: 'bg-green-500', medium: 'bg-yellow-500', high: 'bg-orange-500', critical: 'bg-red-600', } const TOM_CATEGORY_ICONS: Record = { 'Zutrittskontrolle': '\uD83D\uDEAA', 'Zugangskontrolle': '\uD83D\uDD10', 'Zugriffskontrolle': '\uD83D\uDC65', 'Trennungskontrolle': '\uD83D\uDDC2\uFE0F', 'Pseudonymisierung': '\uD83C\uDFAD', 'Verschluesselung': '\uD83D\uDD12', 'Integritaet': '\u2705', 'Verfuegbarkeit': '\u2B06\uFE0F', 'Belastbarkeit': '\uD83D\uDEE1\uFE0F', 'Wiederherstellung': '\uD83D\uDD04', 'Datenschutz-Management': '\uD83D\uDCCB', 'Auftragsverarbeitung': '\uD83D\uDCDD', 'Incident Response': '\uD83D\uDEA8', 'Schulung': '\uD83C\uDF93', 'Netzwerksicherheit': '\uD83C\uDF10', 'Datensicherung': '\uD83D\uDCBE', 'Monitoring': '\uD83D\uDCCA', 'Physische Sicherheit': '\uD83C\uDFE2', } // ============================================================================= // SKELETON COMPONENTS // ============================================================================= function GridSkeleton() { return (
{[1, 2, 3, 4].map((i) => (
))}
) } function DetailSkeleton() { return (
{[1, 2, 3].map((i) => (
))}
{[1, 2, 3].map((i) => (
))}
{[1, 2, 3].map((i) => (
))}
) } // ============================================================================= // MAIN PAGE COMPONENT // ============================================================================= export default function IndustryTemplatesPage() { // --------------------------------------------------------------------------- // State // --------------------------------------------------------------------------- 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) // --------------------------------------------------------------------------- // Data fetching // --------------------------------------------------------------------------- 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() // Merge sub-resources if the detail endpoint did not include them 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]) // --------------------------------------------------------------------------- // Handlers // --------------------------------------------------------------------------- const handleBackToGrid = useCallback(() => { setSelectedSlug(null) setSelectedDetail(null) setDetailError(null) }, []) const handleApplyPackage = useCallback(async () => { if (!selectedDetail) return setApplying(true) try { // Placeholder: In production this would POST to an import endpoint 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]) // Auto-dismiss toast useEffect(() => { if (!toastMessage) return const timer = setTimeout(() => setToastMessage(null), 6000) return () => clearTimeout(timer) }, [toastMessage]) // --------------------------------------------------------------------------- // Render: Header // --------------------------------------------------------------------------- const renderHeader = () => (
{'\uD83C\uDFED'}

Branchenspezifische Module

Vorkonfigurierte Compliance-Pakete nach Branche

) // --------------------------------------------------------------------------- // Render: Error // --------------------------------------------------------------------------- const renderError = (message: string, onRetry: () => void) => (

Fehler

{message}

) // --------------------------------------------------------------------------- // Render: Industry Grid // --------------------------------------------------------------------------- const renderGrid = () => (
{industries.map((industry) => ( ))}
) // --------------------------------------------------------------------------- // Render: Detail View - Header // --------------------------------------------------------------------------- const renderDetailHeader = () => { if (!selectedDetail) return null return (
{selectedDetail.icon}

{selectedDetail.name}

{selectedDetail.description}

{/* Regulation Badges */} {selectedDetail.regulations && selectedDetail.regulations.length > 0 && (

Relevante Regulierungen

{selectedDetail.regulations.map((reg) => ( {reg} ))}
)} {/* Summary stats */}

{selectedDetail.vvt_templates?.length || 0}

VVT-Vorlagen

{selectedDetail.tom_recommendations?.length || 0}

TOM-Empfehlungen

{selectedDetail.risk_scenarios?.length || 0}

Risiko-Szenarien

) } // --------------------------------------------------------------------------- // Render: VVT Tab // --------------------------------------------------------------------------- const renderVVTTab = () => { const templates = selectedDetail?.vvt_templates || [] if (templates.length === 0) { return (

Keine VVT-Vorlagen verfuegbar

Fuer diese Branche wurden noch keine Verarbeitungsvorlagen definiert.

) } return (
{templates.map((vvt, idx) => (

{vvt.name}

{vvt.purpose}

{vvt.retention_period}
{/* Legal Basis */}

Rechtsgrundlage

{vvt.legal_basis}

{/* Retention Period (mobile only, since shown in badge on desktop) */}

Aufbewahrungsfrist

{vvt.retention_period}

{/* Data Categories */}

Datenkategorien

{vvt.data_categories.map((cat) => ( {cat} ))}
{/* Data Subjects */}

Betroffene

{vvt.data_subjects.map((sub) => ( {sub} ))}
))}
) } // --------------------------------------------------------------------------- // Render: TOM Tab // --------------------------------------------------------------------------- const renderTOMTab = () => { const recommendations = selectedDetail?.tom_recommendations || [] if (recommendations.length === 0) { return (

Keine TOM-Empfehlungen verfuegbar

Fuer diese Branche wurden noch keine technisch-organisatorischen Massnahmen definiert.

) } // Group by category const grouped: Record = {} recommendations.forEach((tom) => { if (!grouped[tom.category]) { grouped[tom.category] = [] } grouped[tom.category].push(tom) }) return (
{Object.entries(grouped).map(([category, items]) => { const icon = TOM_CATEGORY_ICONS[category] || '\uD83D\uDD27' return (
{icon}

{category}

({items.length})
{items.map((tom, idx) => { const prio = PRIORITY_COLORS[tom.priority] || PRIORITY_COLORS.medium const prioLabel = PRIORITY_LABELS[tom.priority] || tom.priority return (
{tom.name}

{tom.description}

{prioLabel}
) })}
) })}
) } // --------------------------------------------------------------------------- // Render: Risk Tab // --------------------------------------------------------------------------- const renderRiskTab = () => { const scenarios = selectedDetail?.risk_scenarios || [] if (scenarios.length === 0) { return (

Keine Risiko-Szenarien verfuegbar

Fuer diese Branche wurden noch keine Risiko-Szenarien definiert.

) } return (
{scenarios.map((risk, idx) => { const likelihoodColor = LIKELIHOOD_COLORS[risk.likelihood] || 'bg-slate-400' const impactColor = IMPACT_COLORS[risk.impact] || 'bg-slate-400' const likelihoodLabel = PRIORITY_LABELS[risk.likelihood] || risk.likelihood const impactLabel = PRIORITY_LABELS[risk.impact] || risk.impact return (

{risk.name}

{/* Likelihood badge */}
Wahrsch.: {likelihoodLabel}
| {/* Impact badge */}
Auswirkung: {impactLabel}

{risk.description}

{/* Mitigation */}

Massnahme

{risk.mitigation}

) })}
) } // --------------------------------------------------------------------------- // Render: Detail Tabs + Content // --------------------------------------------------------------------------- const renderDetailContent = () => { if (!selectedDetail) return null return (
{/* Tab Navigation */}
{DETAIL_TABS.map((tab) => { const isActive = activeTab === tab.key let count = 0 if (tab.key === 'vvt') count = selectedDetail.vvt_templates?.length || 0 if (tab.key === 'tom') count = selectedDetail.tom_recommendations?.length || 0 if (tab.key === 'risks') count = selectedDetail.risk_scenarios?.length || 0 return ( ) })}
{/* Tab Content */}
{activeTab === 'vvt' && renderVVTTab()} {activeTab === 'tom' && renderTOMTab()} {activeTab === 'risks' && renderRiskTab()}
{/* Apply Button */}

Importiert alle Vorlagen, Empfehlungen und Szenarien in Ihr System.

) } // --------------------------------------------------------------------------- // Render: Toast // --------------------------------------------------------------------------- const renderToast = () => { if (!toastMessage) return null return (

{toastMessage}

) } // --------------------------------------------------------------------------- // Render: Empty state // --------------------------------------------------------------------------- const renderEmptyState = () => (
{'\uD83C\uDFED'}

Keine Branchenvorlagen verfuegbar

Es sind derzeit keine branchenspezifischen Compliance-Pakete im System hinterlegt. Bitte kontaktieren Sie den Administrator oder versuchen Sie es spaeter erneut.

) // --------------------------------------------------------------------------- // Main Render // --------------------------------------------------------------------------- return (
{/* Inline keyframe for toast animation */} {renderHeader()} {/* Error state */} {error && renderError(error, loadIndustries)} {/* Main Content */} {loading ? ( selectedSlug ? : ) : selectedSlug ? ( // Detail View
{detailLoading ? ( ) : detailError ? ( <> {renderError(detailError, () => loadDetail(selectedSlug))} ) : ( <> {renderDetailHeader()} {renderDetailContent()} )}
) : industries.length === 0 && !error ? ( renderEmptyState() ) : ( renderGrid() )} {/* Toast notification */} {renderToast()}
) }