refactor: Admin-Layout komplett entfernt — SDK als einziges Layout
All checks were successful
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-ai-compliance (push) Successful in 32s
CI / test-python-backend-compliance (push) Successful in 31s
CI / test-python-document-crawler (push) Successful in 21s
CI / test-python-dsms-gateway (push) Successful in 19s

Kaputtes (admin) Layout geloescht (Role-Selection, 404-Sidebar, localhost-Dashboard).
SDK-Flow nach /sdk/sdk-flow verschoben. Route-Gruppe (sdk) aufgeloest.
Root-Seite redirected auf /sdk. ~25 ungenutzte Dateien/Verzeichnisse entfernt.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-03-04 11:43:00 +01:00
parent 7e5047290c
commit 215b95adfa
136 changed files with 8 additions and 8162 deletions

View File

@@ -0,0 +1,15 @@
'use client'
import { TOMGeneratorProvider } from '@/lib/sdk/tom-generator/context'
import { useSDK } from '@/lib/sdk'
export default function TOMLayout({ children }: { children: React.ReactNode }) {
const { state } = useSDK()
const tenantId = state?.tenantId || 'default'
return (
<TOMGeneratorProvider tenantId={tenantId}>
{children}
</TOMGeneratorProvider>
)
}

View File

@@ -0,0 +1,356 @@
'use client'
import React, { useState, useCallback, useMemo } from 'react'
import { useRouter } from 'next/navigation'
import { useSDK } from '@/lib/sdk'
import { StepHeader, STEP_EXPLANATIONS } from '@/components/sdk/StepHeader'
import { useTOMGenerator } from '@/lib/sdk/tom-generator/context'
import { DerivedTOM } from '@/lib/sdk/tom-generator/types'
import { TOMOverviewTab, TOMEditorTab, TOMGapExportTab } from '@/components/sdk/tom-dashboard'
// =============================================================================
// TYPES
// =============================================================================
type Tab = 'uebersicht' | 'editor' | 'generator' | 'gap-export'
interface TabDefinition {
key: Tab
label: string
}
const TABS: TabDefinition[] = [
{ key: 'uebersicht', label: 'Uebersicht' },
{ key: 'editor', label: 'Detail-Editor' },
{ key: 'generator', label: 'Generator' },
{ key: 'gap-export', label: 'Gap-Analyse & Export' },
]
// =============================================================================
// PAGE COMPONENT
// =============================================================================
export default function TOMPage() {
const router = useRouter()
const sdk = useSDK()
const { state, dispatch, bulkUpdateTOMs, runGapAnalysis } = useTOMGenerator()
// ---------------------------------------------------------------------------
// Local state
// ---------------------------------------------------------------------------
const [tab, setTab] = useState<Tab>('uebersicht')
const [selectedTOMId, setSelectedTOMId] = useState<string | null>(null)
// ---------------------------------------------------------------------------
// Computed / memoised values
// ---------------------------------------------------------------------------
const tomCount = useMemo(() => {
if (!state?.derivedTOMs) return 0
return Array.isArray(state.derivedTOMs)
? state.derivedTOMs.length
: Object.keys(state.derivedTOMs).length
}, [state?.derivedTOMs])
const lastModifiedFormatted = useMemo(() => {
if (!state?.metadata?.lastModified) return null
try {
const date = new Date(state.metadata.lastModified)
return date.toLocaleDateString('de-DE', {
day: '2-digit',
month: '2-digit',
year: 'numeric',
hour: '2-digit',
minute: '2-digit',
})
} catch {
return null
}
}, [state?.metadata?.lastModified])
// ---------------------------------------------------------------------------
// Handlers
// ---------------------------------------------------------------------------
const handleSelectTOM = useCallback((tomId: string) => {
setSelectedTOMId(tomId)
setTab('editor')
}, [])
const handleUpdateTOM = useCallback(
(tomId: string, updates: Partial<DerivedTOM>) => {
dispatch({
type: 'UPDATE_DERIVED_TOM',
payload: { id: tomId, data: updates },
})
},
[dispatch],
)
const handleStartGenerator = useCallback(() => {
router.push('/sdk/tom-generator')
}, [router])
const handleBackToOverview = useCallback(() => {
setSelectedTOMId(null)
setTab('uebersicht')
}, [])
const handleRunGapAnalysis = useCallback(() => {
if (typeof runGapAnalysis === 'function') {
runGapAnalysis()
}
}, [runGapAnalysis])
const handleTabChange = useCallback((newTab: Tab) => {
setTab(newTab)
}, [])
// ---------------------------------------------------------------------------
// Render helpers
// ---------------------------------------------------------------------------
const renderTabBar = () => (
<div className="flex flex-wrap gap-2">
{TABS.map((t) => {
const isActive = tab === t.key
return (
<button
key={t.key}
onClick={() => handleTabChange(t.key)}
className={`
rounded-lg px-4 py-2 text-sm font-medium transition-colors
${
isActive
? 'bg-purple-600 text-white shadow-sm'
: 'bg-gray-100 text-gray-600 hover:bg-gray-200'
}
`}
>
{t.label}
</button>
)
})}
</div>
)
// ---------------------------------------------------------------------------
// Tab 1 Uebersicht
// ---------------------------------------------------------------------------
const renderUebersicht = () => (
<TOMOverviewTab
state={state}
onSelectTOM={handleSelectTOM}
onStartGenerator={handleStartGenerator}
/>
)
// ---------------------------------------------------------------------------
// Tab 2 Detail-Editor
// ---------------------------------------------------------------------------
const renderEditor = () => (
<TOMEditorTab
state={state}
selectedTOMId={selectedTOMId}
onUpdateTOM={handleUpdateTOM}
onBack={handleBackToOverview}
/>
)
// ---------------------------------------------------------------------------
// Tab 3 Generator
// ---------------------------------------------------------------------------
const renderGenerator = () => (
<div className="space-y-6">
{/* Info card */}
<div className="bg-white rounded-xl border border-gray-200 p-6">
<div className="flex items-start gap-4">
{/* Icon */}
<div className="flex-shrink-0 w-12 h-12 rounded-lg bg-purple-100 flex items-center justify-center">
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-6 w-6 text-purple-600"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth={2}
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z"
/>
</svg>
</div>
<div className="flex-1">
<h3 className="text-lg font-semibold text-gray-900 mb-2">
TOM Generator &ndash; 6-Schritte-Assistent
</h3>
<p className="text-gray-600 leading-relaxed mb-4">
Der TOM Generator fuehrt Sie in 6 Schritten durch die systematische
Ableitung Ihrer technischen und organisatorischen Massnahmen. Sie
beantworten gezielte Fragen zu Ihrem Unternehmen, Ihrer
IT-Infrastruktur und Ihren Verarbeitungstaetigkeiten. Daraus werden
passende TOMs automatisch abgeleitet und priorisiert.
</p>
<div className="bg-purple-50 rounded-lg p-4 mb-4">
<h4 className="text-sm font-semibold text-purple-800 mb-2">
Die 6 Schritte im Ueberblick:
</h4>
<ol className="list-decimal list-inside text-sm text-purple-700 space-y-1">
<li>Unternehmenskontext erfassen</li>
<li>IT-Infrastruktur beschreiben</li>
<li>Verarbeitungstaetigkeiten zuordnen</li>
<li>Risikobewertung durchfuehren</li>
<li>TOM-Ableitung und Priorisierung</li>
<li>Ergebnis pruefen und uebernehmen</li>
</ol>
</div>
<button
onClick={handleStartGenerator}
className="bg-purple-600 text-white hover:bg-purple-700 rounded-lg px-6 py-3 text-sm font-medium transition-colors shadow-sm"
>
TOM Generator starten
</button>
</div>
</div>
</div>
{/* Quick stats only rendered when derivedTOMs exist */}
{tomCount > 0 && (
<div className="bg-white rounded-xl border border-gray-200 p-6">
<h3 className="text-base font-semibold text-gray-900 mb-4">
Aktueller Stand
</h3>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
{/* TOM count */}
<div className="bg-gray-50 rounded-lg p-4">
<p className="text-sm text-gray-500 mb-1">Abgeleitete TOMs</p>
<p className="text-2xl font-bold text-gray-900">{tomCount}</p>
</div>
{/* Last generated date */}
{lastModifiedFormatted && (
<div className="bg-gray-50 rounded-lg p-4">
<p className="text-sm text-gray-500 mb-1">Zuletzt generiert</p>
<p className="text-lg font-semibold text-gray-900">
{lastModifiedFormatted}
</p>
</div>
)}
{/* Status */}
<div className="bg-gray-50 rounded-lg p-4">
<p className="text-sm text-gray-500 mb-1">Status</p>
<div className="flex items-center gap-2">
<span className="w-2.5 h-2.5 rounded-full bg-green-500" />
<p className="text-sm font-medium text-gray-900">
TOMs vorhanden
</p>
</div>
</div>
</div>
<div className="mt-4 pt-4 border-t border-gray-100">
<p className="text-xs text-gray-400">
Sie koennen den Generator jederzeit erneut ausfuehren, um Ihre
TOMs zu aktualisieren oder zu erweitern.
</p>
</div>
</div>
)}
{/* Empty state when no TOMs exist yet */}
{tomCount === 0 && (
<div className="bg-white rounded-xl border border-dashed border-gray-300 p-8 text-center">
<div className="mx-auto w-16 h-16 rounded-full bg-gray-100 flex items-center justify-center mb-4">
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-8 w-8 text-gray-400"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth={1.5}
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M12 6v6m0 0v6m0-6h6m-6 0H6"
/>
</svg>
</div>
<h4 className="text-base font-medium text-gray-700 mb-1">
Noch keine TOMs vorhanden
</h4>
<p className="text-sm text-gray-500 mb-4">
Starten Sie den Generator, um Ihre ersten technischen und
organisatorischen Massnahmen abzuleiten.
</p>
<button
onClick={handleStartGenerator}
className="bg-purple-600 text-white hover:bg-purple-700 rounded-lg px-4 py-2 text-sm font-medium transition-colors"
>
Jetzt starten
</button>
</div>
)}
</div>
)
// ---------------------------------------------------------------------------
// Tab 4 Gap-Analyse & Export
// ---------------------------------------------------------------------------
const renderGapExport = () => (
<TOMGapExportTab
state={state}
onRunGapAnalysis={handleRunGapAnalysis}
/>
)
// ---------------------------------------------------------------------------
// Tab content router
// ---------------------------------------------------------------------------
const renderActiveTab = () => {
switch (tab) {
case 'uebersicht':
return renderUebersicht()
case 'editor':
return renderEditor()
case 'generator':
return renderGenerator()
case 'gap-export':
return renderGapExport()
default:
return renderUebersicht()
}
}
// ---------------------------------------------------------------------------
// Main render
// ---------------------------------------------------------------------------
return (
<div className="space-y-6">
{/* Step header */}
<StepHeader stepId="tom" {...STEP_EXPLANATIONS['tom']} />
{/* Tab bar */}
<div className="bg-white rounded-xl border border-gray-200 p-3">
{renderTabBar()}
</div>
{/* Active tab content */}
<div>{renderActiveTab()}</div>
</div>
)
}