diff --git a/admin-compliance/app/sdk/industry-templates/_components/DetailContent.tsx b/admin-compliance/app/sdk/industry-templates/_components/DetailContent.tsx new file mode 100644 index 0000000..4b50705 --- /dev/null +++ b/admin-compliance/app/sdk/industry-templates/_components/DetailContent.tsx @@ -0,0 +1,98 @@ +import type { IndustryTemplate, DetailTab } from '../_types' +import { DETAIL_TABS } from '../_constants' +import { VVTTab } from './VVTTab' +import { TOMTab } from './TOMTab' +import { RiskTab } from './RiskTab' + +export function DetailContent({ + detail, + activeTab, + onTabChange, + applying, + onApply, +}: { + detail: IndustryTemplate + activeTab: DetailTab + onTabChange: (tab: DetailTab) => void + applying: boolean + onApply: () => void +}) { + return ( +
+
+ {DETAIL_TABS.map((tab) => { + const isActive = activeTab === tab.key + let count = 0 + if (tab.key === 'vvt') count = detail.vvt_templates?.length || 0 + if (tab.key === 'tom') count = detail.tom_recommendations?.length || 0 + if (tab.key === 'risks') count = detail.risk_scenarios?.length || 0 + + return ( + + ) + })} +
+ +
+ {activeTab === 'vvt' && } + {activeTab === 'tom' && } + {activeTab === 'risks' && } +
+ +
+
+

+ Importiert alle Vorlagen, Empfehlungen und Szenarien in Ihr System. +

+ +
+
+
+ ) +} diff --git a/admin-compliance/app/sdk/industry-templates/_components/DetailHeader.tsx b/admin-compliance/app/sdk/industry-templates/_components/DetailHeader.tsx new file mode 100644 index 0000000..7233e10 --- /dev/null +++ b/admin-compliance/app/sdk/industry-templates/_components/DetailHeader.tsx @@ -0,0 +1,72 @@ +import type { IndustryTemplate } from '../_types' + +export function DetailHeader({ + detail, + onBack, +}: { + detail: IndustryTemplate + onBack: () => void +}) { + return ( +
+ + +
+
+ {detail.icon} +
+
+

{detail.name}

+

{detail.description}

+
+
+ + {detail.regulations && detail.regulations.length > 0 && ( +
+

+ Relevante Regulierungen +

+
+ {detail.regulations.map((reg) => ( + + {reg} + + ))} +
+
+ )} + +
+
+

+ {detail.vvt_templates?.length || 0} +

+

VVT-Vorlagen

+
+
+

+ {detail.tom_recommendations?.length || 0} +

+

TOM-Empfehlungen

+
+
+

+ {detail.risk_scenarios?.length || 0} +

+

Risiko-Szenarien

+
+
+
+ ) +} diff --git a/admin-compliance/app/sdk/industry-templates/_components/EmptyState.tsx b/admin-compliance/app/sdk/industry-templates/_components/EmptyState.tsx new file mode 100644 index 0000000..9dbe61a --- /dev/null +++ b/admin-compliance/app/sdk/industry-templates/_components/EmptyState.tsx @@ -0,0 +1,20 @@ +export function EmptyState({ onReload }: { onReload: () => void }) { + return ( +
+
+ {'\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. +

+ +
+ ) +} diff --git a/admin-compliance/app/sdk/industry-templates/_components/ErrorPanel.tsx b/admin-compliance/app/sdk/industry-templates/_components/ErrorPanel.tsx new file mode 100644 index 0000000..6626786 --- /dev/null +++ b/admin-compliance/app/sdk/industry-templates/_components/ErrorPanel.tsx @@ -0,0 +1,19 @@ +export function ErrorPanel({ message, onRetry }: { message: string; onRetry: () => void }) { + return ( +
+ + + +
+

Fehler

+

{message}

+
+ +
+ ) +} diff --git a/admin-compliance/app/sdk/industry-templates/_components/IndustryGrid.tsx b/admin-compliance/app/sdk/industry-templates/_components/IndustryGrid.tsx new file mode 100644 index 0000000..17c9034 --- /dev/null +++ b/admin-compliance/app/sdk/industry-templates/_components/IndustryGrid.tsx @@ -0,0 +1,49 @@ +import type { IndustrySummary } from '../_types' + +export function IndustryGrid({ + industries, + onSelect, +}: { + industries: IndustrySummary[] + onSelect: (slug: string) => void +}) { + return ( +
+ {industries.map((industry) => ( + + ))} +
+ ) +} diff --git a/admin-compliance/app/sdk/industry-templates/_components/PageHeader.tsx b/admin-compliance/app/sdk/industry-templates/_components/PageHeader.tsx new file mode 100644 index 0000000..6005f7c --- /dev/null +++ b/admin-compliance/app/sdk/industry-templates/_components/PageHeader.tsx @@ -0,0 +1,17 @@ +export function PageHeader() { + return ( +
+
+
+ {'\uD83C\uDFED'} +
+
+

Branchenspezifische Module

+

+ Vorkonfigurierte Compliance-Pakete nach Branche +

+
+
+
+ ) +} diff --git a/admin-compliance/app/sdk/industry-templates/_components/RiskTab.tsx b/admin-compliance/app/sdk/industry-templates/_components/RiskTab.tsx new file mode 100644 index 0000000..21fd129 --- /dev/null +++ b/admin-compliance/app/sdk/industry-templates/_components/RiskTab.tsx @@ -0,0 +1,64 @@ +import type { RiskScenario } from '../_types' +import { LIKELIHOOD_COLORS, IMPACT_COLORS, PRIORITY_LABELS } from '../_constants' + +export function RiskTab({ scenarios }: { scenarios: RiskScenario[] }) { + 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}

+
+
+ + + Wahrsch.: {likelihoodLabel} + +
+ | +
+ + + Auswirkung: {impactLabel} + +
+
+
+ +

{risk.description}

+ +
+
+ + + +
+

Massnahme

+

{risk.mitigation}

+
+
+
+
+ ) + })} +
+ ) +} diff --git a/admin-compliance/app/sdk/industry-templates/_components/Skeletons.tsx b/admin-compliance/app/sdk/industry-templates/_components/Skeletons.tsx new file mode 100644 index 0000000..a70ac63 --- /dev/null +++ b/admin-compliance/app/sdk/industry-templates/_components/Skeletons.tsx @@ -0,0 +1,53 @@ +export function GridSkeleton() { + return ( +
+ {[1, 2, 3, 4].map((i) => ( +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ))} +
+ ) +} + +export function DetailSkeleton() { + return ( +
+
+
+
+
+
+
+
+
+
+ {[1, 2, 3].map((i) => ( +
+ ))} +
+
+
+
+ {[1, 2, 3].map((i) => ( +
+ ))} +
+ {[1, 2, 3].map((i) => ( +
+ ))} +
+
+ ) +} diff --git a/admin-compliance/app/sdk/industry-templates/_components/TOMTab.tsx b/admin-compliance/app/sdk/industry-templates/_components/TOMTab.tsx new file mode 100644 index 0000000..fca97ba --- /dev/null +++ b/admin-compliance/app/sdk/industry-templates/_components/TOMTab.tsx @@ -0,0 +1,62 @@ +import type { TOMRecommendation } from '../_types' +import { PRIORITY_COLORS, PRIORITY_LABELS, TOM_CATEGORY_ICONS } from '../_constants' + +export function TOMTab({ recommendations }: { recommendations: TOMRecommendation[] }) { + if (recommendations.length === 0) { + return ( +
+

Keine TOM-Empfehlungen verfuegbar

+

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

+
+ ) + } + + 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} + +
+
+ ) + })} +
+
+ ) + })} +
+ ) +} diff --git a/admin-compliance/app/sdk/industry-templates/_components/Toast.tsx b/admin-compliance/app/sdk/industry-templates/_components/Toast.tsx new file mode 100644 index 0000000..aabb000 --- /dev/null +++ b/admin-compliance/app/sdk/industry-templates/_components/Toast.tsx @@ -0,0 +1,20 @@ +export function Toast({ message, onDismiss }: { message: string; onDismiss: () => void }) { + return ( +
+
+ + + +

{message}

+ +
+
+ ) +} diff --git a/admin-compliance/app/sdk/industry-templates/_components/VVTTab.tsx b/admin-compliance/app/sdk/industry-templates/_components/VVTTab.tsx new file mode 100644 index 0000000..d3546f0 --- /dev/null +++ b/admin-compliance/app/sdk/industry-templates/_components/VVTTab.tsx @@ -0,0 +1,75 @@ +import type { VVTTemplate } from '../_types' + +export function VVTTab({ templates }: { templates: VVTTemplate[] }) { + 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} + +
+ +
+
+
+

Rechtsgrundlage

+

{vvt.legal_basis}

+
+ +
+

Aufbewahrungsfrist

+

{vvt.retention_period}

+
+ +
+

Datenkategorien

+
+ {vvt.data_categories.map((cat) => ( + + {cat} + + ))} +
+
+ +
+

Betroffene

+
+ {vvt.data_subjects.map((sub) => ( + + {sub} + + ))} +
+
+
+
+
+ ))} +
+ ) +} diff --git a/admin-compliance/app/sdk/industry-templates/_constants.ts b/admin-compliance/app/sdk/industry-templates/_constants.ts new file mode 100644 index 0000000..73ee7cb --- /dev/null +++ b/admin-compliance/app/sdk/industry-templates/_constants.ts @@ -0,0 +1,55 @@ +import type { DetailTab } from './_types' + +export const DETAIL_TABS: { key: DetailTab; label: string }[] = [ + { key: 'vvt', label: 'VVT-Vorlagen' }, + { key: 'tom', label: 'TOM-Empfehlungen' }, + { key: 'risks', label: 'Risiko-Szenarien' }, +] + +export 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' }, +} + +export const PRIORITY_LABELS: Record = { + critical: 'Kritisch', + high: 'Hoch', + medium: 'Mittel', + low: 'Niedrig', +} + +export const LIKELIHOOD_COLORS: Record = { + low: 'bg-green-500', + medium: 'bg-yellow-500', + high: 'bg-orange-500', +} + +export const IMPACT_COLORS: Record = { + low: 'bg-green-500', + medium: 'bg-yellow-500', + high: 'bg-orange-500', + critical: 'bg-red-600', +} + +export 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', +} diff --git a/admin-compliance/app/sdk/industry-templates/_types.ts b/admin-compliance/app/sdk/industry-templates/_types.ts new file mode 100644 index 0000000..76be3f6 --- /dev/null +++ b/admin-compliance/app/sdk/industry-templates/_types.ts @@ -0,0 +1,45 @@ +export interface IndustrySummary { + slug: string + name: string + description: string + icon: string + regulation_count: number + template_count: number +} + +export interface IndustryTemplate { + slug: string + name: string + description: string + icon: string + regulations: string[] + vvt_templates: VVTTemplate[] + tom_recommendations: TOMRecommendation[] + risk_scenarios: RiskScenario[] +} + +export interface VVTTemplate { + name: string + purpose: string + legal_basis: string + data_categories: string[] + data_subjects: string[] + retention_period: string +} + +export interface TOMRecommendation { + category: string + name: string + description: string + priority: string +} + +export interface RiskScenario { + name: string + description: string + likelihood: string + impact: string + mitigation: string +} + +export type DetailTab = 'vvt' | 'tom' | 'risks' diff --git a/admin-compliance/app/sdk/industry-templates/page.tsx b/admin-compliance/app/sdk/industry-templates/page.tsx index 91ca9c8..02b58dd 100644 --- a/admin-compliance/app/sdk/industry-templates/page.tsx +++ b/admin-compliance/app/sdk/industry-templates/page.tsx @@ -10,181 +10,17 @@ */ 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 -// ============================================================================= +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() { - // --------------------------------------------------------------------------- - // State - // --------------------------------------------------------------------------- const [industries, setIndustries] = useState([]) const [selectedDetail, setSelectedDetail] = useState(null) const [selectedSlug, setSelectedSlug] = useState(null) @@ -196,10 +32,6 @@ export default function IndustryTemplatesPage() { const [applying, setApplying] = useState(false) const [toastMessage, setToastMessage] = useState(null) - // --------------------------------------------------------------------------- - // Data fetching - // --------------------------------------------------------------------------- - const loadIndustries = useCallback(async () => { setLoading(true) setError(null) @@ -237,7 +69,6 @@ export default function IndustryTemplatesPage() { 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 || [] @@ -264,10 +95,6 @@ export default function IndustryTemplatesPage() { loadIndustries() }, [loadIndustries]) - // --------------------------------------------------------------------------- - // Handlers - // --------------------------------------------------------------------------- - const handleBackToGrid = useCallback(() => { setSelectedSlug(null) setSelectedDetail(null) @@ -278,7 +105,6 @@ export default function IndustryTemplatesPage() { 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. ` + @@ -293,536 +119,14 @@ export default function IndustryTemplatesPage() { } }, [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)} + {error && } - {/* Main Content */} {loading ? ( selectedSlug ? : ) : selectedSlug ? ( - // Detail View
{detailLoading ? ( @@ -857,23 +158,30 @@ export default function IndustryTemplatesPage() { Zurueck zur Uebersicht - {renderError(detailError, () => loadDetail(selectedSlug))} + loadDetail(selectedSlug)} /> - ) : ( + ) : selectedDetail ? ( <> - {renderDetailHeader()} - {renderDetailContent()} + + - )} + ) : null}
) : industries.length === 0 && !error ? ( - renderEmptyState() + ) : ( - renderGrid() + )} - {/* Toast notification */} - {renderToast()} + {toastMessage && ( + setToastMessage(null)} /> + )}
) }