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 (
+ onTabChange(tab.key)}
+ className={`flex-1 px-4 py-3 text-sm font-medium transition-colors relative ${
+ isActive
+ ? 'text-emerald-700 bg-white border-b-2 border-emerald-500'
+ : 'text-slate-500 hover:text-slate-700 hover:bg-slate-100'
+ }`}
+ >
+ {tab.label}
+ {count > 0 && (
+
+ {count}
+
+ )}
+
+ )
+ })}
+
+
+
+ {activeTab === 'vvt' && }
+ {activeTab === 'tom' && }
+ {activeTab === 'risks' && }
+
+
+
+
+
+ Importiert alle Vorlagen, Empfehlungen und Szenarien in Ihr System.
+
+
+ {applying ? (
+ <>
+
+
+
+
+ Wird angewendet...
+ >
+ ) : (
+ <>
+
+
+
+ Branchenpaket anwenden
+ >
+ )}
+
+
+
+
+ )
+}
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 (
+
+
+
+
+
+ Zurueck zur Uebersicht
+
+
+
+
+ {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.
+
+
+ Erneut laden
+
+
+ )
+}
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 (
+
+
+
+
+
+
+ Erneut versuchen
+
+
+ )
+}
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) => (
+
onSelect(industry.slug)}
+ className="bg-white rounded-xl border border-slate-200 p-6 text-left hover:border-emerald-300 hover:shadow-md transition-all duration-200 group"
+ >
+
+
+ {industry.icon}
+
+
+
+ {industry.name}
+
+
+ {industry.description}
+
+
+
+
+
+
+
+
+ {industry.regulation_count} Regulierungen
+
+
+
+
+
+ {industry.template_count} Vorlagen
+
+
+
+ ))}
+
+ )
+}
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) => (
-
-
-
-
-
-
- Erneut versuchen
-
-
- )
-
- // ---------------------------------------------------------------------------
- // Render: Industry Grid
- // ---------------------------------------------------------------------------
-
- const renderGrid = () => (
-
- {industries.map((industry) => (
-
loadDetail(industry.slug)}
- className="bg-white rounded-xl border border-slate-200 p-6 text-left hover:border-emerald-300 hover:shadow-md transition-all duration-200 group"
- >
-
-
- {industry.icon}
-
-
-
- {industry.name}
-
-
- {industry.description}
-
-
-
-
-
-
-
-
- {industry.regulation_count} Regulierungen
-
-
-
-
-
- {industry.template_count} Vorlagen
-
-
-
- ))}
-
- )
-
- // ---------------------------------------------------------------------------
- // Render: Detail View - Header
- // ---------------------------------------------------------------------------
-
- const renderDetailHeader = () => {
- if (!selectedDetail) return null
- return (
-
-
-
-
-
- Zurueck zur Uebersicht
-
-
-
-
- {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 (
- setActiveTab(tab.key)}
- className={`flex-1 px-4 py-3 text-sm font-medium transition-colors relative ${
- isActive
- ? 'text-emerald-700 bg-white border-b-2 border-emerald-500'
- : 'text-slate-500 hover:text-slate-700 hover:bg-slate-100'
- }`}
- >
- {tab.label}
- {count > 0 && (
-
- {count}
-
- )}
-
- )
- })}
-
-
- {/* Tab Content */}
-
- {activeTab === 'vvt' && renderVVTTab()}
- {activeTab === 'tom' && renderTOMTab()}
- {activeTab === 'risks' && renderRiskTab()}
-
-
- {/* Apply Button */}
-
-
-
- Importiert alle Vorlagen, Empfehlungen und Szenarien in Ihr System.
-
-
- {applying ? (
- <>
-
-
-
-
- Wird angewendet...
- >
- ) : (
- <>
-
-
-
- Branchenpaket anwenden
- >
- )}
-
-
-
-
- )
- }
-
- // ---------------------------------------------------------------------------
- // Render: Toast
- // ---------------------------------------------------------------------------
-
- const renderToast = () => {
- if (!toastMessage) return null
- return (
-
-
-
-
-
-
{toastMessage}
-
setToastMessage(null)}
- className="text-slate-400 hover:text-white flex-shrink-0 ml-2"
- >
-
-
-
-
-
-
- )
- }
-
- // ---------------------------------------------------------------------------
- // 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.
-
-
- Erneut laden
-
-
- )
-
- // ---------------------------------------------------------------------------
- // 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)} />
+ )}
)
}