fix(admin-v2): Restore complete admin-v2 application
The admin-v2 application was incomplete in the repository. This commit restores all missing components: - Admin pages (76 pages): dashboard, ai, compliance, dsgvo, education, infrastructure, communication, development, onboarding, rbac - SDK pages (45 pages): tom, dsfa, vvt, loeschfristen, einwilligungen, vendor-compliance, tom-generator, dsr, and more - Developer portal (25 pages): API docs, SDK guides, frameworks - All components, lib files, hooks, and types - Updated package.json with all dependencies The issue was caused by incomplete initial repository state - the full admin-v2 codebase existed in backend/admin-v2 and docs-src/admin-v2 but was never fully synced to the main admin-v2 directory. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
129
admin-v2/app/(sdk)/sdk/tom-generator/architecture/page.tsx
Normal file
129
admin-v2/app/(sdk)/sdk/tom-generator/architecture/page.tsx
Normal file
@@ -0,0 +1,129 @@
|
||||
'use client'
|
||||
|
||||
import React from 'react'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { useTOMGenerator } from '@/lib/sdk/tom-generator'
|
||||
import { ArchitectureStep } from '@/components/sdk/tom-generator/steps/ArchitectureStep'
|
||||
import { TOM_GENERATOR_STEPS } from '@/lib/sdk/tom-generator/types'
|
||||
|
||||
/**
|
||||
* Step 3: Architecture & Hosting
|
||||
*
|
||||
* Collects infrastructure information including:
|
||||
* - Hosting model (On-Premise/Cloud/Hybrid)
|
||||
* - Location (DE/EU/Third country)
|
||||
* - Cloud providers with certifications
|
||||
* - Multi-tenancy
|
||||
* - Encryption (at rest / in transit)
|
||||
*/
|
||||
export default function ArchitecturePage() {
|
||||
const router = useRouter()
|
||||
const { state, goToNextStep, goToPreviousStep } = useTOMGenerator()
|
||||
|
||||
const currentStepIndex = TOM_GENERATOR_STEPS.findIndex((s) => s.id === 'architecture-hosting')
|
||||
const prevStep = TOM_GENERATOR_STEPS[currentStepIndex - 1]
|
||||
const nextStep = TOM_GENERATOR_STEPS[currentStepIndex + 1]
|
||||
|
||||
const handleNext = () => {
|
||||
goToNextStep()
|
||||
if (nextStep) {
|
||||
router.push(nextStep.url)
|
||||
}
|
||||
}
|
||||
|
||||
const handleBack = () => {
|
||||
goToPreviousStep()
|
||||
if (prevStep) {
|
||||
router.push(prevStep.url)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="max-w-4xl mx-auto px-4 py-8">
|
||||
{/* Step Header */}
|
||||
<div className="mb-8">
|
||||
<div className="flex items-center gap-2 text-sm text-gray-500 mb-2">
|
||||
<span>Schritt 3 von 6</span>
|
||||
<span className="text-gray-300">|</span>
|
||||
<span>Architektur & Hosting</span>
|
||||
</div>
|
||||
<h1 className="text-2xl font-bold text-gray-900">
|
||||
IT-Architektur & Hosting
|
||||
</h1>
|
||||
<p className="mt-2 text-gray-600">
|
||||
Beschreiben Sie Ihre technische Infrastruktur. Die Hosting-Umgebung
|
||||
beeinflusst wesentlich die erforderlichen Sicherheitsmassnahmen.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Progress Indicator */}
|
||||
<div className="mb-8">
|
||||
<div className="flex items-center gap-2">
|
||||
{TOM_GENERATOR_STEPS.map((step, index) => {
|
||||
const stepState = state.steps.find((s) => s.id === step.id)
|
||||
const isCompleted = stepState?.completed
|
||||
const isCurrent = state.currentStep === step.id
|
||||
|
||||
return (
|
||||
<React.Fragment key={step.id}>
|
||||
<button
|
||||
onClick={() => router.push(step.url)}
|
||||
className={`w-8 h-8 rounded-full flex items-center justify-center text-sm font-medium transition-all ${
|
||||
isCompleted
|
||||
? 'bg-green-500 text-white'
|
||||
: isCurrent
|
||||
? 'bg-blue-500 text-white'
|
||||
: 'bg-gray-200 text-gray-500 hover:bg-gray-300'
|
||||
}`}
|
||||
title={step.name}
|
||||
>
|
||||
{isCompleted ? (
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
|
||||
</svg>
|
||||
) : (
|
||||
index + 1
|
||||
)}
|
||||
</button>
|
||||
{index < TOM_GENERATOR_STEPS.length - 1 && (
|
||||
<div
|
||||
className={`flex-1 h-1 rounded ${
|
||||
isCompleted ? 'bg-green-500' : 'bg-gray-200'
|
||||
}`}
|
||||
/>
|
||||
)}
|
||||
</React.Fragment>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Step Content */}
|
||||
<div className="bg-white rounded-xl border border-gray-200 p-6">
|
||||
<ArchitectureStep />
|
||||
</div>
|
||||
|
||||
{/* Navigation */}
|
||||
<div className="flex justify-between mt-6">
|
||||
<button
|
||||
onClick={handleBack}
|
||||
className="px-4 py-2 text-gray-600 hover:text-gray-900 transition-colors flex items-center gap-2"
|
||||
>
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
|
||||
</svg>
|
||||
Zurueck
|
||||
</button>
|
||||
<button
|
||||
onClick={handleNext}
|
||||
className="px-6 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors flex items-center gap-2"
|
||||
>
|
||||
Weiter
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
128
admin-v2/app/(sdk)/sdk/tom-generator/data/page.tsx
Normal file
128
admin-v2/app/(sdk)/sdk/tom-generator/data/page.tsx
Normal file
@@ -0,0 +1,128 @@
|
||||
'use client'
|
||||
|
||||
import React from 'react'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { useTOMGenerator } from '@/lib/sdk/tom-generator'
|
||||
import { DataCategoriesStep } from '@/components/sdk/tom-generator/steps/DataCategoriesStep'
|
||||
import { TOM_GENERATOR_STEPS } from '@/lib/sdk/tom-generator/types'
|
||||
|
||||
/**
|
||||
* Step 2: Data Categories
|
||||
*
|
||||
* Collects data processing information including:
|
||||
* - Data categories (with warnings for special categories)
|
||||
* - Data subjects (with warnings for minors)
|
||||
* - Data volume
|
||||
* - Third country transfers
|
||||
*/
|
||||
export default function DataPage() {
|
||||
const router = useRouter()
|
||||
const { state, goToNextStep, goToPreviousStep } = useTOMGenerator()
|
||||
|
||||
const currentStepIndex = TOM_GENERATOR_STEPS.findIndex((s) => s.id === 'data-categories')
|
||||
const prevStep = TOM_GENERATOR_STEPS[currentStepIndex - 1]
|
||||
const nextStep = TOM_GENERATOR_STEPS[currentStepIndex + 1]
|
||||
|
||||
const handleNext = () => {
|
||||
goToNextStep()
|
||||
if (nextStep) {
|
||||
router.push(nextStep.url)
|
||||
}
|
||||
}
|
||||
|
||||
const handleBack = () => {
|
||||
goToPreviousStep()
|
||||
if (prevStep) {
|
||||
router.push(prevStep.url)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="max-w-4xl mx-auto px-4 py-8">
|
||||
{/* Step Header */}
|
||||
<div className="mb-8">
|
||||
<div className="flex items-center gap-2 text-sm text-gray-500 mb-2">
|
||||
<span>Schritt 2 von 6</span>
|
||||
<span className="text-gray-300">|</span>
|
||||
<span>Datenkategorien</span>
|
||||
</div>
|
||||
<h1 className="text-2xl font-bold text-gray-900">
|
||||
Datenkategorien & Betroffene
|
||||
</h1>
|
||||
<p className="mt-2 text-gray-600">
|
||||
Welche Arten von personenbezogenen Daten verarbeiten Sie?
|
||||
Die Sensitivitaet der Daten bestimmt die erforderlichen Schutzmassnahmen.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Progress Indicator */}
|
||||
<div className="mb-8">
|
||||
<div className="flex items-center gap-2">
|
||||
{TOM_GENERATOR_STEPS.map((step, index) => {
|
||||
const stepState = state.steps.find((s) => s.id === step.id)
|
||||
const isCompleted = stepState?.completed
|
||||
const isCurrent = state.currentStep === step.id
|
||||
|
||||
return (
|
||||
<React.Fragment key={step.id}>
|
||||
<button
|
||||
onClick={() => router.push(step.url)}
|
||||
className={`w-8 h-8 rounded-full flex items-center justify-center text-sm font-medium transition-all ${
|
||||
isCompleted
|
||||
? 'bg-green-500 text-white'
|
||||
: isCurrent
|
||||
? 'bg-blue-500 text-white'
|
||||
: 'bg-gray-200 text-gray-500 hover:bg-gray-300'
|
||||
}`}
|
||||
title={step.name}
|
||||
>
|
||||
{isCompleted ? (
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
|
||||
</svg>
|
||||
) : (
|
||||
index + 1
|
||||
)}
|
||||
</button>
|
||||
{index < TOM_GENERATOR_STEPS.length - 1 && (
|
||||
<div
|
||||
className={`flex-1 h-1 rounded ${
|
||||
isCompleted ? 'bg-green-500' : 'bg-gray-200'
|
||||
}`}
|
||||
/>
|
||||
)}
|
||||
</React.Fragment>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Step Content */}
|
||||
<div className="bg-white rounded-xl border border-gray-200 p-6">
|
||||
<DataCategoriesStep />
|
||||
</div>
|
||||
|
||||
{/* Navigation */}
|
||||
<div className="flex justify-between mt-6">
|
||||
<button
|
||||
onClick={handleBack}
|
||||
className="px-4 py-2 text-gray-600 hover:text-gray-900 transition-colors flex items-center gap-2"
|
||||
>
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
|
||||
</svg>
|
||||
Zurueck
|
||||
</button>
|
||||
<button
|
||||
onClick={handleNext}
|
||||
className="px-6 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors flex items-center gap-2"
|
||||
>
|
||||
Weiter
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
30
admin-v2/app/(sdk)/sdk/tom-generator/layout.tsx
Normal file
30
admin-v2/app/(sdk)/sdk/tom-generator/layout.tsx
Normal file
@@ -0,0 +1,30 @@
|
||||
'use client'
|
||||
|
||||
import React from 'react'
|
||||
import { TOMGeneratorProvider } from '@/lib/sdk/tom-generator'
|
||||
|
||||
/**
|
||||
* TOM Generator Layout
|
||||
*
|
||||
* Wraps all TOM Generator pages with the TOMGeneratorProvider
|
||||
* to share state across all wizard steps.
|
||||
*
|
||||
* Note: In production, tenantId would come from authentication/session.
|
||||
* For development, we use a default demo tenant ID.
|
||||
*/
|
||||
export default function TOMGeneratorLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
// TODO: In production, get tenantId from authentication context
|
||||
const tenantId = 'demo-tenant'
|
||||
|
||||
return (
|
||||
<TOMGeneratorProvider tenantId={tenantId}>
|
||||
<div className="min-h-screen bg-gray-50">
|
||||
{children}
|
||||
</div>
|
||||
</TOMGeneratorProvider>
|
||||
)
|
||||
}
|
||||
215
admin-v2/app/(sdk)/sdk/tom-generator/page.tsx
Normal file
215
admin-v2/app/(sdk)/sdk/tom-generator/page.tsx
Normal file
@@ -0,0 +1,215 @@
|
||||
'use client'
|
||||
|
||||
import React from 'react'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { useTOMGenerator } from '@/lib/sdk/tom-generator'
|
||||
import { TOM_GENERATOR_STEPS } from '@/lib/sdk/tom-generator/types'
|
||||
|
||||
/**
|
||||
* TOM Generator Landing Page
|
||||
*
|
||||
* Shows overview of the wizard and allows starting or resuming.
|
||||
*/
|
||||
export default function TOMGeneratorPage() {
|
||||
const router = useRouter()
|
||||
const { state, resetState } = useTOMGenerator()
|
||||
|
||||
// Calculate progress
|
||||
const completedSteps = state.steps.filter((s) => s.completed).length
|
||||
const totalSteps = state.steps.length
|
||||
const progressPercent = totalSteps > 0 ? Math.round((completedSteps / totalSteps) * 100) : 0
|
||||
|
||||
// Determine the current step URL
|
||||
const currentStepConfig = TOM_GENERATOR_STEPS.find((s) => s.id === state.currentStep)
|
||||
const currentStepUrl = currentStepConfig?.url || '/sdk/tom-generator/scope'
|
||||
|
||||
const handleStartNew = () => {
|
||||
resetState()
|
||||
router.push('/sdk/tom-generator/scope')
|
||||
}
|
||||
|
||||
const handleResume = () => {
|
||||
router.push(currentStepUrl)
|
||||
}
|
||||
|
||||
const hasProgress = completedSteps > 0
|
||||
|
||||
return (
|
||||
<div className="max-w-4xl mx-auto px-4 py-8">
|
||||
{/* Header */}
|
||||
<div className="mb-8">
|
||||
<h1 className="text-3xl font-bold text-gray-900">TOM Generator</h1>
|
||||
<p className="mt-2 text-gray-600">
|
||||
Leiten Sie Ihre Technischen und Organisatorischen Massnahmen (TOMs) nach Art. 32 DSGVO
|
||||
systematisch ab und dokumentieren Sie diese.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Progress Card */}
|
||||
{hasProgress && (
|
||||
<div className="bg-white rounded-xl border border-gray-200 p-6 mb-8">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<h2 className="text-lg font-semibold text-gray-900">Ihr Fortschritt</h2>
|
||||
<span className="text-sm text-gray-500">
|
||||
{completedSteps} von {totalSteps} Schritten abgeschlossen
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Progress Bar */}
|
||||
<div className="h-3 bg-gray-100 rounded-full overflow-hidden mb-4">
|
||||
<div
|
||||
className="h-full bg-gradient-to-r from-blue-500 to-purple-500 transition-all duration-500"
|
||||
style={{ width: `${progressPercent}%` }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Steps Overview */}
|
||||
<div className="grid grid-cols-2 md:grid-cols-3 gap-3">
|
||||
{TOM_GENERATOR_STEPS.map((step, index) => {
|
||||
const stepState = state.steps.find((s) => s.id === step.id)
|
||||
const isCompleted = stepState?.completed
|
||||
const isCurrent = state.currentStep === step.id
|
||||
|
||||
return (
|
||||
<button
|
||||
key={step.id}
|
||||
onClick={() => router.push(step.url)}
|
||||
className={`flex items-center gap-3 p-3 rounded-lg border transition-all ${
|
||||
isCompleted
|
||||
? 'bg-green-50 border-green-200 text-green-700'
|
||||
: isCurrent
|
||||
? 'bg-blue-50 border-blue-200 text-blue-700'
|
||||
: 'bg-gray-50 border-gray-200 text-gray-500 hover:bg-gray-100'
|
||||
}`}
|
||||
>
|
||||
<div
|
||||
className={`w-8 h-8 rounded-full flex items-center justify-center text-sm font-medium ${
|
||||
isCompleted
|
||||
? 'bg-green-500 text-white'
|
||||
: isCurrent
|
||||
? 'bg-blue-500 text-white'
|
||||
: 'bg-gray-200 text-gray-500'
|
||||
}`}
|
||||
>
|
||||
{isCompleted ? (
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
|
||||
</svg>
|
||||
) : (
|
||||
index + 1
|
||||
)}
|
||||
</div>
|
||||
<span className="text-sm font-medium">{step.name}</span>
|
||||
</button>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
|
||||
{/* Actions */}
|
||||
<div className="flex gap-3 mt-6">
|
||||
<button
|
||||
onClick={handleResume}
|
||||
className="flex-1 px-4 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors font-medium"
|
||||
>
|
||||
Fortfahren
|
||||
</button>
|
||||
<button
|
||||
onClick={handleStartNew}
|
||||
className="px-4 py-3 border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50 transition-colors"
|
||||
>
|
||||
Neu starten
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Introduction Card */}
|
||||
<div className="bg-white rounded-xl border border-gray-200 p-6 mb-8">
|
||||
<h2 className="text-lg font-semibold text-gray-900 mb-4">Was ist der TOM Generator?</h2>
|
||||
<p className="text-gray-600 mb-4">
|
||||
Der TOM Generator fuehrt Sie in 6 Schritten durch die Erstellung Ihrer DSGVO-konformen
|
||||
Technischen und Organisatorischen Massnahmen:
|
||||
</p>
|
||||
|
||||
<div className="space-y-3">
|
||||
{TOM_GENERATOR_STEPS.map((step, index) => (
|
||||
<div key={step.id} className="flex items-start gap-3">
|
||||
<div className="w-6 h-6 rounded-full bg-blue-100 text-blue-600 flex items-center justify-center text-sm font-medium flex-shrink-0">
|
||||
{index + 1}
|
||||
</div>
|
||||
<div>
|
||||
<div className="font-medium text-gray-900">{step.name}</div>
|
||||
<div className="text-sm text-gray-500">{step.description.de}</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{!hasProgress && (
|
||||
<button
|
||||
onClick={handleStartNew}
|
||||
className="w-full mt-6 px-4 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors font-medium"
|
||||
>
|
||||
Jetzt starten
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Features */}
|
||||
<div className="grid md:grid-cols-3 gap-4">
|
||||
<div className="bg-white rounded-xl border border-gray-200 p-6">
|
||||
<div className="w-10 h-10 rounded-lg bg-purple-100 flex items-center justify-center mb-4">
|
||||
<svg className="w-5 h-5 text-purple-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" />
|
||||
</svg>
|
||||
</div>
|
||||
<h3 className="font-semibold text-gray-900 mb-2">60+ Kontrollen</h3>
|
||||
<p className="text-sm text-gray-500">
|
||||
Vordefinierte Kontrollen mit Mapping zu DSGVO, ISO 27001 und BSI-Grundschutz
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-xl border border-gray-200 p-6">
|
||||
<div className="w-10 h-10 rounded-lg bg-green-100 flex items-center justify-center mb-4">
|
||||
<svg className="w-5 h-5 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
|
||||
</svg>
|
||||
</div>
|
||||
<h3 className="font-semibold text-gray-900 mb-2">Lueckenanalyse</h3>
|
||||
<p className="text-sm text-gray-500">
|
||||
Automatische Identifikation fehlender Massnahmen mit Handlungsempfehlungen
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-xl border border-gray-200 p-6">
|
||||
<div className="w-10 h-10 rounded-lg bg-blue-100 flex items-center justify-center mb-4">
|
||||
<svg className="w-5 h-5 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
||||
</svg>
|
||||
</div>
|
||||
<h3 className="font-semibold text-gray-900 mb-2">Export</h3>
|
||||
<p className="text-sm text-gray-500">
|
||||
Generieren Sie Ihre TOM-Dokumentation als Word, PDF oder strukturiertes JSON
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Legal Note */}
|
||||
<div className="mt-8 p-4 bg-blue-50 rounded-lg border border-blue-200">
|
||||
<div className="flex gap-3">
|
||||
<svg className="w-5 h-5 text-blue-600 flex-shrink-0 mt-0.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
<div>
|
||||
<div className="font-medium text-blue-900">Hinweis zur Rechtsgrundlage</div>
|
||||
<p className="text-sm text-blue-700 mt-1">
|
||||
Die generierten TOMs basieren auf Art. 32 DSGVO. Die Auswahl der konkreten Massnahmen
|
||||
sollte immer unter Beruecksichtigung des Stands der Technik, der Implementierungskosten
|
||||
und der Art, des Umfangs und der Zwecke der Verarbeitung erfolgen.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
147
admin-v2/app/(sdk)/sdk/tom-generator/review/page.tsx
Normal file
147
admin-v2/app/(sdk)/sdk/tom-generator/review/page.tsx
Normal file
@@ -0,0 +1,147 @@
|
||||
'use client'
|
||||
|
||||
import React from 'react'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { useTOMGenerator } from '@/lib/sdk/tom-generator'
|
||||
import { ReviewExportStep } from '@/components/sdk/tom-generator/steps/ReviewExportStep'
|
||||
import { TOM_GENERATOR_STEPS } from '@/lib/sdk/tom-generator/types'
|
||||
|
||||
/**
|
||||
* Step 6: Review & Export
|
||||
*
|
||||
* Final step including:
|
||||
* - Summary of all steps
|
||||
* - Derived TOMs table
|
||||
* - Gap analysis visualization
|
||||
* - Evidence Vault overview
|
||||
* - Export (Word/PDF/JSON/ZIP)
|
||||
*/
|
||||
export default function ReviewPage() {
|
||||
const router = useRouter()
|
||||
const { state, goToPreviousStep } = useTOMGenerator()
|
||||
|
||||
const currentStepIndex = TOM_GENERATOR_STEPS.findIndex((s) => s.id === 'review-export')
|
||||
const prevStep = TOM_GENERATOR_STEPS[currentStepIndex - 1]
|
||||
|
||||
const handleBack = () => {
|
||||
goToPreviousStep()
|
||||
if (prevStep) {
|
||||
router.push(prevStep.url)
|
||||
}
|
||||
}
|
||||
|
||||
// Check if all previous steps are completed
|
||||
const allPreviousStepsCompleted = TOM_GENERATOR_STEPS
|
||||
.slice(0, currentStepIndex)
|
||||
.every((step) => {
|
||||
const stepState = state.steps.find((s) => s.id === step.id)
|
||||
return stepState?.completed
|
||||
})
|
||||
|
||||
return (
|
||||
<div className="max-w-5xl mx-auto px-4 py-8">
|
||||
{/* Step Header */}
|
||||
<div className="mb-8">
|
||||
<div className="flex items-center gap-2 text-sm text-gray-500 mb-2">
|
||||
<span>Schritt 6 von 6</span>
|
||||
<span className="text-gray-300">|</span>
|
||||
<span>Review & Export</span>
|
||||
</div>
|
||||
<h1 className="text-2xl font-bold text-gray-900">
|
||||
Zusammenfassung & Export
|
||||
</h1>
|
||||
<p className="mt-2 text-gray-600">
|
||||
Pruefen Sie Ihre abgeleiteten TOMs, analysieren Sie Luecken und exportieren
|
||||
Sie Ihre Dokumentation in verschiedenen Formaten.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Progress Indicator */}
|
||||
<div className="mb-8">
|
||||
<div className="flex items-center gap-2">
|
||||
{TOM_GENERATOR_STEPS.map((step, index) => {
|
||||
const stepState = state.steps.find((s) => s.id === step.id)
|
||||
const isCompleted = stepState?.completed
|
||||
const isCurrent = state.currentStep === step.id
|
||||
|
||||
return (
|
||||
<React.Fragment key={step.id}>
|
||||
<button
|
||||
onClick={() => router.push(step.url)}
|
||||
className={`w-8 h-8 rounded-full flex items-center justify-center text-sm font-medium transition-all ${
|
||||
isCompleted
|
||||
? 'bg-green-500 text-white'
|
||||
: isCurrent
|
||||
? 'bg-blue-500 text-white'
|
||||
: 'bg-gray-200 text-gray-500 hover:bg-gray-300'
|
||||
}`}
|
||||
title={step.name}
|
||||
>
|
||||
{isCompleted ? (
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
|
||||
</svg>
|
||||
) : (
|
||||
index + 1
|
||||
)}
|
||||
</button>
|
||||
{index < TOM_GENERATOR_STEPS.length - 1 && (
|
||||
<div
|
||||
className={`flex-1 h-1 rounded ${
|
||||
isCompleted ? 'bg-green-500' : 'bg-gray-200'
|
||||
}`}
|
||||
/>
|
||||
)}
|
||||
</React.Fragment>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Warning if previous steps not completed */}
|
||||
{!allPreviousStepsCompleted && (
|
||||
<div className="mb-6 p-4 bg-yellow-50 rounded-lg border border-yellow-200">
|
||||
<div className="flex gap-3">
|
||||
<svg className="w-5 h-5 text-yellow-600 flex-shrink-0 mt-0.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
|
||||
</svg>
|
||||
<div>
|
||||
<div className="font-medium text-yellow-900">Nicht alle Schritte abgeschlossen</div>
|
||||
<p className="text-sm text-yellow-700 mt-1">
|
||||
Bitte schliessen Sie alle vorherigen Schritte ab, um eine vollstaendige TOM-Dokumentation
|
||||
zu generieren. Fehlende Informationen fuehren zu unvollstaendigen Empfehlungen.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Step Content */}
|
||||
<div className="bg-white rounded-xl border border-gray-200 p-6">
|
||||
<ReviewExportStep />
|
||||
</div>
|
||||
|
||||
{/* Navigation */}
|
||||
<div className="flex justify-between mt-6">
|
||||
<button
|
||||
onClick={handleBack}
|
||||
className="px-4 py-2 text-gray-600 hover:text-gray-900 transition-colors flex items-center gap-2"
|
||||
>
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
|
||||
</svg>
|
||||
Zurueck
|
||||
</button>
|
||||
<button
|
||||
onClick={() => router.push('/sdk/tom-generator')}
|
||||
className="px-6 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 transition-colors flex items-center gap-2"
|
||||
>
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
|
||||
</svg>
|
||||
Abschliessen
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
146
admin-v2/app/(sdk)/sdk/tom-generator/risk/page.tsx
Normal file
146
admin-v2/app/(sdk)/sdk/tom-generator/risk/page.tsx
Normal file
@@ -0,0 +1,146 @@
|
||||
'use client'
|
||||
|
||||
import React from 'react'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { useTOMGenerator } from '@/lib/sdk/tom-generator'
|
||||
import { RiskProtectionStep } from '@/components/sdk/tom-generator/steps/RiskProtectionStep'
|
||||
import { TOM_GENERATOR_STEPS } from '@/lib/sdk/tom-generator/types'
|
||||
|
||||
/**
|
||||
* Step 5: Risk & Protection Level
|
||||
*
|
||||
* Assesses risk and protection requirements including:
|
||||
* - CIA Assessment (Confidentiality/Integrity/Availability, 1-5 each)
|
||||
* - Calculated protection level
|
||||
* - Special risks
|
||||
* - Regulatory requirements
|
||||
* - DSFA indicator (automatic)
|
||||
*/
|
||||
export default function RiskPage() {
|
||||
const router = useRouter()
|
||||
const { state, goToNextStep, goToPreviousStep } = useTOMGenerator()
|
||||
|
||||
const currentStepIndex = TOM_GENERATOR_STEPS.findIndex((s) => s.id === 'risk-protection')
|
||||
const prevStep = TOM_GENERATOR_STEPS[currentStepIndex - 1]
|
||||
const nextStep = TOM_GENERATOR_STEPS[currentStepIndex + 1]
|
||||
|
||||
const handleNext = () => {
|
||||
goToNextStep()
|
||||
if (nextStep) {
|
||||
router.push(nextStep.url)
|
||||
}
|
||||
}
|
||||
|
||||
const handleBack = () => {
|
||||
goToPreviousStep()
|
||||
if (prevStep) {
|
||||
router.push(prevStep.url)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="max-w-4xl mx-auto px-4 py-8">
|
||||
{/* Step Header */}
|
||||
<div className="mb-8">
|
||||
<div className="flex items-center gap-2 text-sm text-gray-500 mb-2">
|
||||
<span>Schritt 5 von 6</span>
|
||||
<span className="text-gray-300">|</span>
|
||||
<span>Risiko & Schutzbedarf</span>
|
||||
</div>
|
||||
<h1 className="text-2xl font-bold text-gray-900">
|
||||
Risikobewertung & Schutzbedarf
|
||||
</h1>
|
||||
<p className="mt-2 text-gray-600">
|
||||
Bewerten Sie die Schutzziele (CIA) fuer Ihre Datenverarbeitung.
|
||||
Der ermittelte Schutzbedarf bestimmt die Intensitaet der erforderlichen Massnahmen.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Progress Indicator */}
|
||||
<div className="mb-8">
|
||||
<div className="flex items-center gap-2">
|
||||
{TOM_GENERATOR_STEPS.map((step, index) => {
|
||||
const stepState = state.steps.find((s) => s.id === step.id)
|
||||
const isCompleted = stepState?.completed
|
||||
const isCurrent = state.currentStep === step.id
|
||||
|
||||
return (
|
||||
<React.Fragment key={step.id}>
|
||||
<button
|
||||
onClick={() => router.push(step.url)}
|
||||
className={`w-8 h-8 rounded-full flex items-center justify-center text-sm font-medium transition-all ${
|
||||
isCompleted
|
||||
? 'bg-green-500 text-white'
|
||||
: isCurrent
|
||||
? 'bg-blue-500 text-white'
|
||||
: 'bg-gray-200 text-gray-500 hover:bg-gray-300'
|
||||
}`}
|
||||
title={step.name}
|
||||
>
|
||||
{isCompleted ? (
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
|
||||
</svg>
|
||||
) : (
|
||||
index + 1
|
||||
)}
|
||||
</button>
|
||||
{index < TOM_GENERATOR_STEPS.length - 1 && (
|
||||
<div
|
||||
className={`flex-1 h-1 rounded ${
|
||||
isCompleted ? 'bg-green-500' : 'bg-gray-200'
|
||||
}`}
|
||||
/>
|
||||
)}
|
||||
</React.Fragment>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* CIA Info Box */}
|
||||
<div className="mb-6 p-4 bg-blue-50 rounded-lg border border-blue-200">
|
||||
<div className="flex gap-3">
|
||||
<svg className="w-5 h-5 text-blue-600 flex-shrink-0 mt-0.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
<div>
|
||||
<div className="font-medium text-blue-900">CIA-Triade</div>
|
||||
<p className="text-sm text-blue-700 mt-1">
|
||||
<strong>Vertraulichkeit</strong>: Schutz vor unbefugtem Zugriff |
|
||||
<strong> Integritaet</strong>: Schutz vor unbefugter Aenderung |
|
||||
<strong> Verfuegbarkeit</strong>: Sicherstellung des Zugriffs
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Step Content */}
|
||||
<div className="bg-white rounded-xl border border-gray-200 p-6">
|
||||
<RiskProtectionStep />
|
||||
</div>
|
||||
|
||||
{/* Navigation */}
|
||||
<div className="flex justify-between mt-6">
|
||||
<button
|
||||
onClick={handleBack}
|
||||
className="px-4 py-2 text-gray-600 hover:text-gray-900 transition-colors flex items-center gap-2"
|
||||
>
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
|
||||
</svg>
|
||||
Zurueck
|
||||
</button>
|
||||
<button
|
||||
onClick={handleNext}
|
||||
className="px-6 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors flex items-center gap-2"
|
||||
>
|
||||
Weiter
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
115
admin-v2/app/(sdk)/sdk/tom-generator/scope/page.tsx
Normal file
115
admin-v2/app/(sdk)/sdk/tom-generator/scope/page.tsx
Normal file
@@ -0,0 +1,115 @@
|
||||
'use client'
|
||||
|
||||
import React from 'react'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { useTOMGenerator } from '@/lib/sdk/tom-generator'
|
||||
import { ScopeRolesStep } from '@/components/sdk/tom-generator/steps/ScopeRolesStep'
|
||||
import { TOM_GENERATOR_STEPS } from '@/lib/sdk/tom-generator/types'
|
||||
|
||||
/**
|
||||
* Step 1: Scope & Roles
|
||||
*
|
||||
* Collects company profile information including:
|
||||
* - Company name, industry, size
|
||||
* - GDPR role (Controller/Processor/Joint Controller)
|
||||
* - Products/Services
|
||||
* - DPO and IT Security contacts
|
||||
*/
|
||||
export default function ScopePage() {
|
||||
const router = useRouter()
|
||||
const { state, goToNextStep } = useTOMGenerator()
|
||||
|
||||
const currentStepIndex = TOM_GENERATOR_STEPS.findIndex((s) => s.id === 'scope-roles')
|
||||
const nextStep = TOM_GENERATOR_STEPS[currentStepIndex + 1]
|
||||
|
||||
// Handle step completion
|
||||
const handleStepComplete = () => {
|
||||
goToNextStep()
|
||||
if (nextStep) {
|
||||
router.push(nextStep.url)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="max-w-4xl mx-auto px-4 py-8">
|
||||
{/* Step Header */}
|
||||
<div className="mb-8">
|
||||
<div className="flex items-center gap-2 text-sm text-gray-500 mb-2">
|
||||
<span>Schritt 1 von 6</span>
|
||||
<span className="text-gray-300">|</span>
|
||||
<span>Scope & Rollen</span>
|
||||
</div>
|
||||
<h1 className="text-2xl font-bold text-gray-900">
|
||||
Unternehmens- und Rollendefinition
|
||||
</h1>
|
||||
<p className="mt-2 text-gray-600">
|
||||
Definieren Sie Ihr Unternehmen und Ihre Rolle in der Datenverarbeitung.
|
||||
Diese Informationen bestimmen, welche TOMs fuer Sie relevant sind.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Progress Indicator */}
|
||||
<div className="mb-8">
|
||||
<div className="flex items-center gap-2">
|
||||
{TOM_GENERATOR_STEPS.map((step, index) => {
|
||||
const stepState = state.steps.find((s) => s.id === step.id)
|
||||
const isCompleted = stepState?.completed
|
||||
const isCurrent = state.currentStep === step.id
|
||||
|
||||
return (
|
||||
<React.Fragment key={step.id}>
|
||||
<button
|
||||
onClick={() => router.push(step.url)}
|
||||
className={`w-8 h-8 rounded-full flex items-center justify-center text-sm font-medium transition-all ${
|
||||
isCompleted
|
||||
? 'bg-green-500 text-white'
|
||||
: isCurrent
|
||||
? 'bg-blue-500 text-white'
|
||||
: 'bg-gray-200 text-gray-500 hover:bg-gray-300'
|
||||
}`}
|
||||
title={step.name}
|
||||
>
|
||||
{isCompleted ? (
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
|
||||
</svg>
|
||||
) : (
|
||||
index + 1
|
||||
)}
|
||||
</button>
|
||||
{index < TOM_GENERATOR_STEPS.length - 1 && (
|
||||
<div
|
||||
className={`flex-1 h-1 rounded ${
|
||||
isCompleted ? 'bg-green-500' : 'bg-gray-200'
|
||||
}`}
|
||||
/>
|
||||
)}
|
||||
</React.Fragment>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Step Content */}
|
||||
<div className="bg-white rounded-xl border border-gray-200 p-6">
|
||||
<ScopeRolesStep />
|
||||
</div>
|
||||
|
||||
{/* Navigation */}
|
||||
<div className="flex justify-between mt-6">
|
||||
<button
|
||||
onClick={() => router.push('/sdk/tom-generator')}
|
||||
className="px-4 py-2 text-gray-600 hover:text-gray-900 transition-colors"
|
||||
>
|
||||
Zurueck zur Uebersicht
|
||||
</button>
|
||||
<button
|
||||
onClick={handleStepComplete}
|
||||
className="px-6 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
|
||||
>
|
||||
Weiter
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
129
admin-v2/app/(sdk)/sdk/tom-generator/security/page.tsx
Normal file
129
admin-v2/app/(sdk)/sdk/tom-generator/security/page.tsx
Normal file
@@ -0,0 +1,129 @@
|
||||
'use client'
|
||||
|
||||
import React from 'react'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { useTOMGenerator } from '@/lib/sdk/tom-generator'
|
||||
import { SecurityProfileStep } from '@/components/sdk/tom-generator/steps/SecurityProfileStep'
|
||||
import { TOM_GENERATOR_STEPS } from '@/lib/sdk/tom-generator/types'
|
||||
|
||||
/**
|
||||
* Step 4: Security Profile
|
||||
*
|
||||
* Collects security measures including:
|
||||
* - Authentication (Password, MFA, SSO)
|
||||
* - IAM/PAM
|
||||
* - Logging & Retention
|
||||
* - Backup & DR
|
||||
* - Vulnerability Management, Pentests, Training
|
||||
*/
|
||||
export default function SecurityPage() {
|
||||
const router = useRouter()
|
||||
const { state, goToNextStep, goToPreviousStep } = useTOMGenerator()
|
||||
|
||||
const currentStepIndex = TOM_GENERATOR_STEPS.findIndex((s) => s.id === 'security-profile')
|
||||
const prevStep = TOM_GENERATOR_STEPS[currentStepIndex - 1]
|
||||
const nextStep = TOM_GENERATOR_STEPS[currentStepIndex + 1]
|
||||
|
||||
const handleNext = () => {
|
||||
goToNextStep()
|
||||
if (nextStep) {
|
||||
router.push(nextStep.url)
|
||||
}
|
||||
}
|
||||
|
||||
const handleBack = () => {
|
||||
goToPreviousStep()
|
||||
if (prevStep) {
|
||||
router.push(prevStep.url)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="max-w-4xl mx-auto px-4 py-8">
|
||||
{/* Step Header */}
|
||||
<div className="mb-8">
|
||||
<div className="flex items-center gap-2 text-sm text-gray-500 mb-2">
|
||||
<span>Schritt 4 von 6</span>
|
||||
<span className="text-gray-300">|</span>
|
||||
<span>Security-Profil</span>
|
||||
</div>
|
||||
<h1 className="text-2xl font-bold text-gray-900">
|
||||
Sicherheitsmassnahmen
|
||||
</h1>
|
||||
<p className="mt-2 text-gray-600">
|
||||
Erfassen Sie Ihre bestehenden Sicherheitsmassnahmen. Diese Informationen
|
||||
helfen bei der Identifikation von Luecken und Verbesserungspotenzialen.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Progress Indicator */}
|
||||
<div className="mb-8">
|
||||
<div className="flex items-center gap-2">
|
||||
{TOM_GENERATOR_STEPS.map((step, index) => {
|
||||
const stepState = state.steps.find((s) => s.id === step.id)
|
||||
const isCompleted = stepState?.completed
|
||||
const isCurrent = state.currentStep === step.id
|
||||
|
||||
return (
|
||||
<React.Fragment key={step.id}>
|
||||
<button
|
||||
onClick={() => router.push(step.url)}
|
||||
className={`w-8 h-8 rounded-full flex items-center justify-center text-sm font-medium transition-all ${
|
||||
isCompleted
|
||||
? 'bg-green-500 text-white'
|
||||
: isCurrent
|
||||
? 'bg-blue-500 text-white'
|
||||
: 'bg-gray-200 text-gray-500 hover:bg-gray-300'
|
||||
}`}
|
||||
title={step.name}
|
||||
>
|
||||
{isCompleted ? (
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
|
||||
</svg>
|
||||
) : (
|
||||
index + 1
|
||||
)}
|
||||
</button>
|
||||
{index < TOM_GENERATOR_STEPS.length - 1 && (
|
||||
<div
|
||||
className={`flex-1 h-1 rounded ${
|
||||
isCompleted ? 'bg-green-500' : 'bg-gray-200'
|
||||
}`}
|
||||
/>
|
||||
)}
|
||||
</React.Fragment>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Step Content */}
|
||||
<div className="bg-white rounded-xl border border-gray-200 p-6">
|
||||
<SecurityProfileStep />
|
||||
</div>
|
||||
|
||||
{/* Navigation */}
|
||||
<div className="flex justify-between mt-6">
|
||||
<button
|
||||
onClick={handleBack}
|
||||
className="px-4 py-2 text-gray-600 hover:text-gray-900 transition-colors flex items-center gap-2"
|
||||
>
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
|
||||
</svg>
|
||||
Zurueck
|
||||
</button>
|
||||
<button
|
||||
onClick={handleNext}
|
||||
className="px-6 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors flex items-center gap-2"
|
||||
>
|
||||
Weiter
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user