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:
558
admin-v2/app/(admin)/infrastructure/sbom/wizard/page.tsx
Normal file
558
admin-v2/app/(admin)/infrastructure/sbom/wizard/page.tsx
Normal file
@@ -0,0 +1,558 @@
|
||||
'use client'
|
||||
|
||||
/**
|
||||
* SBOM (Software Bill of Materials) - Lern-Wizard
|
||||
*
|
||||
* Migriert von /admin/sbom/wizard (website) nach /infrastructure/sbom/wizard (admin-v2)
|
||||
*
|
||||
* Interaktiver Wizard zum Verstehen der SBOM:
|
||||
* - Was ist eine SBOM?
|
||||
* - Warum ist sie wichtig?
|
||||
* - Kategorien erklaert
|
||||
* - Breakpilot Drive (Unity/C#/Game) Komponenten
|
||||
* - Lizenzen und Compliance
|
||||
*/
|
||||
|
||||
import { useState } from 'react'
|
||||
import Link from 'next/link'
|
||||
|
||||
// ==============================================
|
||||
// Types
|
||||
// ==============================================
|
||||
|
||||
type StepStatus = 'pending' | 'active' | 'completed'
|
||||
|
||||
interface WizardStep {
|
||||
id: string
|
||||
name: string
|
||||
icon: string
|
||||
status: StepStatus
|
||||
}
|
||||
|
||||
// ==============================================
|
||||
// Constants
|
||||
// ==============================================
|
||||
|
||||
const STEPS: WizardStep[] = [
|
||||
{ id: 'welcome', name: 'Willkommen', icon: '📋', status: 'pending' },
|
||||
{ id: 'what-is-sbom', name: 'Was ist SBOM?', icon: '❓', status: 'pending' },
|
||||
{ id: 'why-important', name: 'Warum wichtig?', icon: '⚠️', status: 'pending' },
|
||||
{ id: 'categories', name: 'Kategorien', icon: '📁', status: 'pending' },
|
||||
{ id: 'infrastructure', name: 'Infrastruktur', icon: '🏗️', status: 'pending' },
|
||||
{ id: 'unity-game', name: 'Unity & Game', icon: '🎮', status: 'pending' },
|
||||
{ id: 'licenses', name: 'Lizenzen', icon: '📜', status: 'pending' },
|
||||
{ id: 'summary', name: 'Zusammenfassung', icon: '✅', status: 'pending' },
|
||||
]
|
||||
|
||||
const EDUCATION_CONTENT: Record<string, { title: string; content: string[]; tips?: string[] }> = {
|
||||
'welcome': {
|
||||
title: 'Willkommen zum SBOM-Wizard!',
|
||||
content: [
|
||||
'Eine **Software Bill of Materials (SBOM)** ist wie ein Zutaten-Etikett fuer Software.',
|
||||
'Sie listet alle Komponenten auf, aus denen eine Anwendung besteht:',
|
||||
'• Open-Source-Bibliotheken',
|
||||
'• Frameworks und Engines',
|
||||
'• Infrastruktur-Dienste',
|
||||
'• Entwicklungs-Tools',
|
||||
'In diesem Wizard lernst du, warum SBOMs wichtig sind und welche Komponenten BreakPilot verwendet - inklusive der neuen **Breakpilot Drive** (Unity) Komponenten.',
|
||||
],
|
||||
tips: [
|
||||
'SBOMs sind seit 2021 fuer US-Regierungsauftraege Pflicht',
|
||||
'Die EU plant aehnliche Vorschriften im Cyber Resilience Act',
|
||||
],
|
||||
},
|
||||
'what-is-sbom': {
|
||||
title: 'Was ist eine SBOM?',
|
||||
content: [
|
||||
'**SBOM = Software Bill of Materials**',
|
||||
'Eine SBOM ist eine vollstaendige Liste aller Software-Komponenten:',
|
||||
'**Enthaltene Informationen:**',
|
||||
'• Name der Komponente',
|
||||
'• Version',
|
||||
'• Lizenz (MIT, Apache, GPL, etc.)',
|
||||
'• Herkunft (Source URL)',
|
||||
'• Typ (Library, Service, Tool)',
|
||||
'**Formate:**',
|
||||
'• SPDX (Linux Foundation Standard)',
|
||||
'• CycloneDX (OWASP Standard)',
|
||||
'• SWID Tags (ISO Standard)',
|
||||
'BreakPilot verwendet eine eigene Darstellung im Admin-Panel, die alle relevanten Infos zeigt.',
|
||||
],
|
||||
tips: [
|
||||
'Eine SBOM ist wie ein Beipackzettel fuer Medikamente',
|
||||
'Sie ermoeglicht schnelle Reaktion bei Sicherheitsluecken',
|
||||
],
|
||||
},
|
||||
'why-important': {
|
||||
title: 'Warum sind SBOMs wichtig?',
|
||||
content: [
|
||||
'**1. Sicherheit (Security)**',
|
||||
'Wenn eine Sicherheitsluecke in einer Bibliothek entdeckt wird (z.B. Log4j), kannst du sofort pruefen ob du betroffen bist.',
|
||||
'**2. Compliance (Lizenz-Einhaltung)**',
|
||||
'Verschiedene Lizenzen haben verschiedene Anforderungen:',
|
||||
'• MIT: Fast keine Einschraenkungen',
|
||||
'• GPL: Copyleft - abgeleitete Werke muessen auch GPL sein',
|
||||
'• Proprietary: Kommerzielle Nutzung eingeschraenkt',
|
||||
'**3. Supply Chain Security**',
|
||||
'Moderne Software besteht aus hunderten Abhaengigkeiten. Eine SBOM macht diese Kette transparent.',
|
||||
'**4. Regulatorische Anforderungen**',
|
||||
'US Executive Order 14028 verlangt SBOMs fuer Regierungssoftware.',
|
||||
],
|
||||
tips: [
|
||||
'Log4Shell (2021) betraf Millionen von Systemen',
|
||||
'Mit SBOM: Betroffenheit in Minuten geprueft',
|
||||
],
|
||||
},
|
||||
'categories': {
|
||||
title: 'SBOM-Kategorien in BreakPilot',
|
||||
content: [
|
||||
'Die BreakPilot SBOM ist in Kategorien unterteilt:',
|
||||
'**infrastructure** (Blau)',
|
||||
'→ Kern-Infrastruktur: PostgreSQL, Valkey, Keycloak, Docker',
|
||||
'**security-tools** (Rot)',
|
||||
'→ Sicherheits-Tools: Trivy, Gitleaks, Semgrep',
|
||||
'**python** (Gelb)',
|
||||
'→ Python-Backend: FastAPI, Pydantic, httpx',
|
||||
'**go** (Cyan)',
|
||||
'→ Go-Services: Gin, GORM, JWT',
|
||||
'**nodejs** (Gruen)',
|
||||
'→ Frontend: Next.js, React, Tailwind',
|
||||
'**unity** (Amber) ← NEU!',
|
||||
'→ Game Engine: Unity 6, URP, TextMeshPro',
|
||||
'**csharp** (Fuchsia) ← NEU!',
|
||||
'→ C#/.NET: .NET Standard, UnityWebRequest',
|
||||
'**game** (Rose) ← NEU!',
|
||||
'→ Breakpilot Drive Service',
|
||||
],
|
||||
tips: [
|
||||
'Klicke auf eine Kategorie um zu filtern',
|
||||
'Die neuen Unity/Game-Kategorien wurden fuer Breakpilot Drive hinzugefuegt',
|
||||
],
|
||||
},
|
||||
'infrastructure': {
|
||||
title: 'Infrastruktur-Komponenten',
|
||||
content: [
|
||||
'BreakPilot basiert auf robuster Infrastruktur:',
|
||||
'**Datenbanken:**',
|
||||
'• PostgreSQL 16 - Relationale Datenbank',
|
||||
'• Valkey 8 - In-Memory Cache (Redis-Fork)',
|
||||
'• ChromaDB - Vector Store fuer RAG',
|
||||
'**Auth & Security:**',
|
||||
'• Keycloak 23 - Identity & Access Management',
|
||||
'• HashiCorp Vault - Secrets Management',
|
||||
'**Container & Orchestrierung:**',
|
||||
'• Docker - Container Runtime',
|
||||
'• Traefik - Reverse Proxy',
|
||||
'**Kommunikation:**',
|
||||
'• Matrix Synapse - Chat/Messaging',
|
||||
'• Jitsi Meet - Video-Konferenzen',
|
||||
],
|
||||
tips: [
|
||||
'Alle Services laufen in Docker-Containern',
|
||||
'Ports sind in docker-compose.yml definiert',
|
||||
],
|
||||
},
|
||||
'unity-game': {
|
||||
title: 'Unity & Breakpilot Drive',
|
||||
content: [
|
||||
'**Neu hinzugefuegt fuer Breakpilot Drive:**',
|
||||
'**Unity Engine (6000.0)**',
|
||||
'→ Die Game Engine fuer das Lernspiel',
|
||||
'→ Lizenz: Unity EULA (kostenlos bis 100k Revenue)',
|
||||
'**Universal Render Pipeline (17.x)**',
|
||||
'→ Optimierte Grafik-Pipeline fuer WebGL',
|
||||
'→ Lizenz: Unity Companion License',
|
||||
'**TextMeshPro (3.2)**',
|
||||
'→ Fortgeschrittenes Text-Rendering',
|
||||
'**Unity Mathematics (1.3)**',
|
||||
'→ SIMD-optimierte Mathe-Bibliothek',
|
||||
'**Newtonsoft.Json (3.2)**',
|
||||
'→ JSON-Serialisierung fuer API-Kommunikation',
|
||||
'**C# Abhaengigkeiten:**',
|
||||
'• .NET Standard 2.1',
|
||||
'• UnityWebRequest (HTTP Client)',
|
||||
'• System.Text.Json',
|
||||
],
|
||||
tips: [
|
||||
'Unity 6 ist die neueste LTS-Version',
|
||||
'WebGL-Builds sind ~30-50 MB gross',
|
||||
],
|
||||
},
|
||||
'licenses': {
|
||||
title: 'Lizenz-Compliance',
|
||||
content: [
|
||||
'**Lizenz-Typen in BreakPilot:**',
|
||||
'**Permissive (Unkompliziert):**',
|
||||
'• MIT - Die meisten JS/Python Libs',
|
||||
'• Apache 2.0 - FastAPI, Keycloak',
|
||||
'• BSD - PostgreSQL',
|
||||
'**Copyleft (Vorsicht bei Aenderungen):**',
|
||||
'• GPL - Wenige Komponenten',
|
||||
'• AGPL - Jitsi (Server-Side OK)',
|
||||
'**Proprietary:**',
|
||||
'• Unity EULA - Kostenlos bis 100k Revenue',
|
||||
'• Unity Companion - Packages an Engine gebunden',
|
||||
'**Wichtig:**',
|
||||
'Alle verwendeten Lizenzen sind mit kommerziellem Einsatz kompatibel. Bei Fragen: Rechtsabteilung konsultieren.',
|
||||
],
|
||||
tips: [
|
||||
'MIT und Apache 2.0 sind am unproblematischsten',
|
||||
'AGPL erfordert Source-Code-Freigabe bei Modifikation',
|
||||
],
|
||||
},
|
||||
'summary': {
|
||||
title: 'Zusammenfassung',
|
||||
content: [
|
||||
'Du hast die SBOM von BreakPilot kennengelernt:',
|
||||
'✅ Was eine SBOM ist und warum sie wichtig ist',
|
||||
'✅ Die verschiedenen Kategorien (8 Stueck)',
|
||||
'✅ Infrastruktur-Komponenten',
|
||||
'✅ Die neuen Unity/Game-Komponenten fuer Breakpilot Drive',
|
||||
'✅ Lizenz-Typen und Compliance',
|
||||
'**Im SBOM-Dashboard kannst du:**',
|
||||
'• Nach Kategorie filtern',
|
||||
'• Nach Namen suchen',
|
||||
'• Lizenzen pruefen',
|
||||
'• Komponenten-Details ansehen',
|
||||
'**180+ Komponenten** sind dokumentiert und nachverfolgbar.',
|
||||
],
|
||||
tips: [
|
||||
'Pruefe regelmaessig auf veraltete Komponenten',
|
||||
'Bei neuen Abhaengigkeiten: SBOM aktualisieren',
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
// ==============================================
|
||||
// Components
|
||||
// ==============================================
|
||||
|
||||
function WizardStepper({
|
||||
steps,
|
||||
currentStep,
|
||||
onStepClick
|
||||
}: {
|
||||
steps: WizardStep[]
|
||||
currentStep: number
|
||||
onStepClick: (index: number) => void
|
||||
}) {
|
||||
return (
|
||||
<div className="flex items-center justify-between mb-8 overflow-x-auto pb-4">
|
||||
{steps.map((step, index) => (
|
||||
<div key={step.id} className="flex items-center">
|
||||
<button
|
||||
onClick={() => onStepClick(index)}
|
||||
className={`flex flex-col items-center min-w-[80px] p-2 rounded-lg transition-colors ${
|
||||
index === currentStep
|
||||
? 'bg-orange-100 text-orange-700'
|
||||
: step.status === 'completed'
|
||||
? 'bg-green-100 text-green-700 cursor-pointer hover:bg-green-200'
|
||||
: 'text-slate-400 hover:bg-slate-100'
|
||||
}`}
|
||||
>
|
||||
<span className="text-2xl mb-1">{step.icon}</span>
|
||||
<span className="text-xs font-medium text-center">{step.name}</span>
|
||||
{step.status === 'completed' && <span className="text-xs text-green-600">✓</span>}
|
||||
</button>
|
||||
{index < steps.length - 1 && (
|
||||
<div className={`h-0.5 w-8 mx-1 ${
|
||||
index < currentStep ? 'bg-green-400' : 'bg-slate-200'
|
||||
}`} />
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function EducationCard({ stepId }: { stepId: string }) {
|
||||
const content = EDUCATION_CONTENT[stepId]
|
||||
if (!content) return null
|
||||
|
||||
return (
|
||||
<div className="bg-orange-50 border border-orange-200 rounded-lg p-6 mb-6">
|
||||
<h3 className="text-lg font-semibold text-orange-800 mb-4 flex items-center">
|
||||
<span className="mr-2">📖</span>
|
||||
{content.title}
|
||||
</h3>
|
||||
<div className="space-y-2 text-orange-900">
|
||||
{content.content.map((line, index) => (
|
||||
<p
|
||||
key={index}
|
||||
className={`${line.startsWith('•') ? 'ml-4' : ''} ${line.startsWith('**') ? 'font-semibold mt-3' : ''}`}
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: line
|
||||
.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
|
||||
.replace(/→/g, '<span class="text-orange-600">→</span>')
|
||||
.replace(/← NEU!/g, '<span class="bg-amber-200 text-amber-800 px-1 rounded text-sm font-bold">← NEU!</span>')
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
{content.tips && content.tips.length > 0 && (
|
||||
<div className="mt-4 pt-4 border-t border-orange-200">
|
||||
<p className="text-sm font-semibold text-orange-700 mb-2">💡 Tipps:</p>
|
||||
{content.tips.map((tip, index) => (
|
||||
<p key={index} className="text-sm text-orange-700 ml-4">• {tip}</p>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function CategoryDemo({ stepId }: { stepId: string }) {
|
||||
if (stepId === 'categories') {
|
||||
const categories = [
|
||||
{ name: 'infrastructure', color: 'blue', count: 45 },
|
||||
{ name: 'security-tools', color: 'red', count: 12 },
|
||||
{ name: 'python', color: 'yellow', count: 35 },
|
||||
{ name: 'go', color: 'cyan', count: 18 },
|
||||
{ name: 'nodejs', color: 'green', count: 55 },
|
||||
{ name: 'unity', color: 'amber', count: 7, isNew: true },
|
||||
{ name: 'csharp', color: 'fuchsia', count: 3, isNew: true },
|
||||
{ name: 'game', color: 'rose', count: 1, isNew: true },
|
||||
]
|
||||
|
||||
return (
|
||||
<div className="bg-white border border-slate-200 rounded-lg p-4 mb-4">
|
||||
<h4 className="font-semibold text-slate-800 mb-3">Live-Vorschau: Kategorien</h4>
|
||||
<div className="grid grid-cols-4 gap-2">
|
||||
{categories.map((cat) => (
|
||||
<div
|
||||
key={cat.name}
|
||||
className={`bg-${cat.color}-100 text-${cat.color}-800 px-3 py-2 rounded-lg text-center text-sm relative`}
|
||||
>
|
||||
<p className="font-medium">{cat.name}</p>
|
||||
<p className="text-xs opacity-70">{cat.count} Komponenten</p>
|
||||
{cat.isNew && (
|
||||
<span className="absolute -top-1 -right-1 bg-amber-500 text-white text-xs px-1 rounded">NEU</span>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (stepId === 'unity-game') {
|
||||
const unityComponents = [
|
||||
{ name: 'Unity Engine', version: '6000.0', license: 'Unity EULA' },
|
||||
{ name: 'URP', version: '17.x', license: 'Unity Companion' },
|
||||
{ name: 'TextMeshPro', version: '3.2', license: 'Unity Companion' },
|
||||
{ name: 'Mathematics', version: '1.3', license: 'Unity Companion' },
|
||||
{ name: 'Newtonsoft.Json', version: '3.2', license: 'MIT' },
|
||||
]
|
||||
|
||||
return (
|
||||
<div className="bg-white border border-slate-200 rounded-lg p-4 mb-4">
|
||||
<h4 className="font-semibold text-slate-800 mb-3">Unity Packages (Breakpilot Drive)</h4>
|
||||
<div className="space-y-2">
|
||||
{unityComponents.map((comp) => (
|
||||
<div key={comp.name} className="flex items-center justify-between py-2 border-b border-slate-100 last:border-0">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="bg-amber-100 text-amber-800 text-xs px-2 py-0.5 rounded">unity</span>
|
||||
<span className="font-medium">{comp.name}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-4 text-sm text-slate-600">
|
||||
<span>{comp.version}</span>
|
||||
<span className="bg-slate-100 px-2 py-0.5 rounded text-xs">{comp.license}</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (stepId === 'licenses') {
|
||||
const licenses = [
|
||||
{ name: 'MIT', count: 85, color: 'green', risk: 'Niedrig' },
|
||||
{ name: 'Apache 2.0', count: 45, color: 'green', risk: 'Niedrig' },
|
||||
{ name: 'BSD', count: 12, color: 'green', risk: 'Niedrig' },
|
||||
{ name: 'Unity EULA', count: 1, color: 'yellow', risk: 'Mittel' },
|
||||
{ name: 'Unity Companion', count: 6, color: 'yellow', risk: 'Mittel' },
|
||||
{ name: 'AGPL', count: 2, color: 'orange', risk: 'Hoch' },
|
||||
]
|
||||
|
||||
return (
|
||||
<div className="bg-white border border-slate-200 rounded-lg p-4 mb-4">
|
||||
<h4 className="font-semibold text-slate-800 mb-3">Lizenz-Uebersicht</h4>
|
||||
<div className="space-y-2">
|
||||
{licenses.map((lic) => (
|
||||
<div key={lic.name} className="flex items-center justify-between py-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="font-medium">{lic.name}</span>
|
||||
<span className="text-sm text-slate-500">({lic.count})</span>
|
||||
</div>
|
||||
<span className={`text-xs px-2 py-1 rounded ${
|
||||
lic.risk === 'Niedrig' ? 'bg-green-100 text-green-700' :
|
||||
lic.risk === 'Mittel' ? 'bg-yellow-100 text-yellow-700' :
|
||||
'bg-orange-100 text-orange-700'
|
||||
}`}>
|
||||
Risiko: {lic.risk}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
// ==============================================
|
||||
// Main Component
|
||||
// ==============================================
|
||||
|
||||
export default function SBOMWizardPage() {
|
||||
const [currentStep, setCurrentStep] = useState(0)
|
||||
const [steps, setSteps] = useState<WizardStep[]>(STEPS)
|
||||
|
||||
const currentStepData = steps[currentStep]
|
||||
const isWelcome = currentStepData?.id === 'welcome'
|
||||
const isSummary = currentStepData?.id === 'summary'
|
||||
|
||||
const goToNext = () => {
|
||||
if (currentStep < steps.length - 1) {
|
||||
setSteps(prev => prev.map((step, idx) =>
|
||||
idx === currentStep && step.status === 'pending'
|
||||
? { ...step, status: 'completed' }
|
||||
: step
|
||||
))
|
||||
setCurrentStep(prev => prev + 1)
|
||||
}
|
||||
}
|
||||
|
||||
const goToPrev = () => {
|
||||
if (currentStep > 0) {
|
||||
setCurrentStep(prev => prev - 1)
|
||||
}
|
||||
}
|
||||
|
||||
const handleStepClick = (index: number) => {
|
||||
setCurrentStep(index)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-slate-100 py-8">
|
||||
<div className="max-w-4xl mx-auto px-4">
|
||||
{/* Header */}
|
||||
<div className="bg-white rounded-lg shadow-lg p-6 mb-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-slate-800">📋 SBOM Lern-Wizard</h1>
|
||||
<p className="text-slate-600 mt-1">
|
||||
Software Bill of Materials verstehen
|
||||
</p>
|
||||
</div>
|
||||
<Link
|
||||
href="/infrastructure/sbom"
|
||||
className="text-orange-600 hover:text-orange-800 text-sm"
|
||||
>
|
||||
← Zurueck zur SBOM
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Stepper */}
|
||||
<div className="bg-white rounded-lg shadow-lg p-6 mb-6">
|
||||
<WizardStepper
|
||||
steps={steps}
|
||||
currentStep={currentStep}
|
||||
onStepClick={handleStepClick}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="bg-white rounded-lg shadow-lg p-6">
|
||||
{/* Step Header */}
|
||||
<div className="flex items-center mb-6">
|
||||
<span className="text-4xl mr-4">{currentStepData?.icon}</span>
|
||||
<div>
|
||||
<h2 className="text-xl font-bold text-slate-800">
|
||||
Schritt {currentStep + 1}: {currentStepData?.name}
|
||||
</h2>
|
||||
<p className="text-slate-500 text-sm">
|
||||
{currentStep + 1} von {steps.length}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Education Card */}
|
||||
<EducationCard stepId={currentStepData?.id || ''} />
|
||||
|
||||
{/* Category Demo */}
|
||||
<CategoryDemo stepId={currentStepData?.id || ''} />
|
||||
|
||||
{/* Welcome Start Button */}
|
||||
{isWelcome && (
|
||||
<div className="text-center py-8">
|
||||
<button
|
||||
onClick={goToNext}
|
||||
className="bg-orange-600 text-white px-8 py-3 rounded-lg font-medium hover:bg-orange-700 transition-colors"
|
||||
>
|
||||
🚀 Lern-Tour starten
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Summary Actions */}
|
||||
{isSummary && (
|
||||
<div className="text-center py-6 space-y-4">
|
||||
<div className="flex justify-center gap-4">
|
||||
<Link
|
||||
href="/infrastructure/sbom"
|
||||
className="px-6 py-3 bg-orange-600 text-white rounded-lg font-medium hover:bg-orange-700 transition-colors"
|
||||
>
|
||||
📋 Zur SBOM
|
||||
</Link>
|
||||
<button
|
||||
onClick={() => {
|
||||
setCurrentStep(0)
|
||||
setSteps(STEPS.map(s => ({ ...s, status: 'pending' })))
|
||||
}}
|
||||
className="px-6 py-3 bg-slate-200 text-slate-700 rounded-lg font-medium hover:bg-slate-300 transition-colors"
|
||||
>
|
||||
🔄 Wizard neu starten
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Navigation */}
|
||||
{!isWelcome && (
|
||||
<div className="flex justify-between mt-8 pt-6 border-t">
|
||||
<button
|
||||
onClick={goToPrev}
|
||||
disabled={currentStep === 0}
|
||||
className={`px-6 py-2 rounded-lg transition-colors ${
|
||||
currentStep === 0
|
||||
? 'bg-slate-200 text-slate-400 cursor-not-allowed'
|
||||
: 'bg-slate-200 text-slate-700 hover:bg-slate-300'
|
||||
}`}
|
||||
>
|
||||
← Zurueck
|
||||
</button>
|
||||
|
||||
{!isSummary && (
|
||||
<button
|
||||
onClick={goToNext}
|
||||
className="bg-orange-600 text-white px-6 py-2 rounded-lg hover:bg-orange-700 transition-colors"
|
||||
>
|
||||
Weiter →
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Footer Info */}
|
||||
<div className="text-center text-slate-500 text-sm mt-6">
|
||||
BreakPilot SBOM - 180+ Komponenten dokumentiert
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user