website (17 pages + 3 components): - multiplayer/wizard, middleware/wizard+test-wizard, communication - builds/wizard, staff-search, voice, sbom/wizard - foerderantrag, mail/tasks, tools/communication, sbom - compliance/evidence, uni-crawler, brandbook (already done) - CollectionsTab, IngestionTab, RiskHeatmap backend-lehrer (5 files): - letters_api (641 → 2), certificates_api (636 → 2) - alerts_agent/db/models (636 → 3) - llm_gateway/communication_service (614 → 2) - game/database already done in prior batch klausur-service (2 files): - hybrid_vocab_extractor (664 → 2) - klausur-service/frontend: api.ts (620 → 3), EHUploadWizard (591 → 2) voice-service (3 files): - bqas/rag_judge (618 → 3), runner (529 → 2) - enhanced_task_orchestrator (519 → 2) studio-v2 (6 files): - korrektur/[klausurId] (578 → 4), fairness (569 → 2) - AlertsWizard (552 → 2), OnboardingWizard (513 → 2) - korrektur/api.ts (506 → 3), geo-lernwelt (501 → 2) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
76 lines
2.7 KiB
TypeScript
76 lines
2.7 KiB
TypeScript
import { WizardStep, EDUCATION_CONTENT } from './types'
|
|
|
|
export 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-primary-100 text-primary-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>
|
|
)
|
|
}
|
|
|
|
export function EducationCard({ stepId }: { stepId: string }) {
|
|
const content = EDUCATION_CONTENT[stepId]
|
|
if (!content) return null
|
|
|
|
return (
|
|
<div className="bg-primary-50 border border-primary-200 rounded-lg p-6 mb-6">
|
|
<h3 className="text-lg font-semibold text-primary-800 mb-4 flex items-center">
|
|
<span className="mr-2">📖</span>
|
|
{content.title}
|
|
</h3>
|
|
<div className="space-y-2 text-primary-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-primary-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-primary-200">
|
|
<p className="text-sm font-semibold text-primary-700 mb-2">💡 Tipps:</p>
|
|
{content.tips.map((tip, index) => (
|
|
<p key={index} className="text-sm text-primary-700 ml-4">• {tip}</p>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|