klausur-service (11 files): - cv_gutter_repair, ocr_pipeline_regression, upload_api - ocr_pipeline_sessions, smart_spell, nru_worksheet_generator - ocr_pipeline_overlays, mail/aggregator, zeugnis_api - cv_syllable_detect, self_rag backend-lehrer (17 files): - classroom_engine/suggestions, generators/quiz_generator - worksheets_api, llm_gateway/comparison, state_engine_api - classroom/models (→ 4 submodules), services/file_processor - alerts_agent/api/wizard+digests+routes, content_generators/pdf - classroom/routes/sessions, llm_gateway/inference - classroom_engine/analytics, auth/keycloak_auth - alerts_agent/processing/rule_engine, ai_processor/print_versions agent-core (5 files): - brain/memory_store, brain/knowledge_graph, brain/context_manager - orchestrator/supervisor, sessions/session_manager admin-lehrer (5 components): - GridOverlay, StepGridReview, DevOpsPipelineSidebar - DataFlowDiagram, sbom/wizard/page website (2 files): - DependencyMap, lehrer/abitur-archiv Other: nibis_ingestion, grid_detection_service, export-doclayout-onnx Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
164 lines
5.2 KiB
TypeScript
164 lines
5.2 KiB
TypeScript
'use client'
|
|
|
|
/**
|
|
* SBOM (Software Bill of Materials) - Lern-Wizard
|
|
*
|
|
* Interaktiver Wizard zum Verstehen der SBOM.
|
|
*/
|
|
|
|
import { useState } from 'react'
|
|
import Link from 'next/link'
|
|
import { STEPS, type WizardStep } from './_components/wizard-content'
|
|
import { WizardStepper, EducationCard, CategoryDemo } from './_components/WizardWidgets'
|
|
|
|
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>
|
|
)
|
|
}
|