[split-required] Split remaining 500-680 LOC files (final batch)
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>
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
interface AssistantSidebarProps {
|
||||
isRTL: boolean
|
||||
t: (key: string) => string
|
||||
assistantHistory: { role: string; content: string }[]
|
||||
assistantMessage: string
|
||||
setAssistantMessage: (msg: string) => void
|
||||
onAskAssistant: () => void
|
||||
onClose: () => void
|
||||
}
|
||||
|
||||
export function AssistantSidebar({
|
||||
isRTL,
|
||||
t,
|
||||
assistantHistory,
|
||||
assistantMessage,
|
||||
setAssistantMessage,
|
||||
onAskAssistant,
|
||||
onClose,
|
||||
}: AssistantSidebarProps) {
|
||||
return (
|
||||
<div className={`fixed ${isRTL ? 'left-0' : 'right-0'} top-0 h-full w-96 bg-white border-${isRTL ? 'r' : 'l'} border-slate-200 shadow-xl z-30 flex flex-col`}>
|
||||
<div className={`p-4 border-b border-slate-200 flex items-center justify-between ${isRTL ? 'flex-row-reverse' : ''}`}>
|
||||
<div className={`flex items-center gap-2 ${isRTL ? 'flex-row-reverse' : ''}`}>
|
||||
<div className="w-8 h-8 rounded-lg bg-gradient-to-br from-blue-500 to-indigo-600 flex items-center justify-center">
|
||||
<svg className="w-4 h-4 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} 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>
|
||||
<h3 className="font-semibold text-slate-900">{t('fa_assistant_title')}</h3>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="p-2 rounded-lg hover:bg-slate-100 transition-colors"
|
||||
>
|
||||
<svg className="w-4 h-4 text-slate-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Chat History */}
|
||||
<div className="flex-1 overflow-y-auto p-4 space-y-4">
|
||||
{assistantHistory.length === 0 && (
|
||||
<div className="text-center py-8">
|
||||
<p className="text-slate-500 text-sm">
|
||||
{t('fa_assistant_placeholder')}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
{assistantHistory.map((msg, idx) => (
|
||||
<div
|
||||
key={idx}
|
||||
className={`flex ${msg.role === 'user' ? 'justify-end' : 'justify-start'}`}
|
||||
>
|
||||
<div
|
||||
className={`max-w-[85%] p-3 rounded-xl text-sm ${
|
||||
msg.role === 'user'
|
||||
? 'bg-blue-600 text-white'
|
||||
: 'bg-slate-100 text-slate-700'
|
||||
}`}
|
||||
>
|
||||
{msg.content}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Input */}
|
||||
<div className="p-4 border-t border-slate-200">
|
||||
<div className={`flex gap-2 ${isRTL ? 'flex-row-reverse' : ''}`}>
|
||||
<input
|
||||
type="text"
|
||||
value={assistantMessage}
|
||||
onChange={(e) => setAssistantMessage(e.target.value)}
|
||||
onKeyDown={(e) => e.key === 'Enter' && onAskAssistant()}
|
||||
placeholder={t('fa_assistant_placeholder')}
|
||||
className="flex-1 px-3 py-2 border border-slate-200 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
/>
|
||||
<button
|
||||
onClick={onAskAssistant}
|
||||
className="p-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
|
||||
>
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import React from 'react'
|
||||
|
||||
export const stepIcons: Record<string, React.ReactNode> = {
|
||||
'document-text': (
|
||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 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>
|
||||
),
|
||||
'academic-cap': (
|
||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 14l9-5-9-5-9 5 9 5z" />
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 14l6.16-3.422a12.083 12.083 0 01.665 6.479A11.952 11.952 0 0012 20.055a11.952 11.952 0 00-6.824-2.998 12.078 12.078 0 01.665-6.479L12 14z" />
|
||||
</svg>
|
||||
),
|
||||
'server': (
|
||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2" />
|
||||
</svg>
|
||||
),
|
||||
'document-report': (
|
||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 17v-2m3 2v-4m3 4v-6m2 10H7a2 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>
|
||||
),
|
||||
'currency-euro': (
|
||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M14.121 15.536c-1.171 1.952-3.07 1.952-4.242 0-1.172-1.953-1.172-5.119 0-7.072 1.171-1.952 3.07-1.952 4.242 0M8 10.5h4m-4 3h4m9-1.5a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
),
|
||||
'calculator': (
|
||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 7h6m0 10v-3m-3 3h.01M9 17h.01M9 14h.01M12 14h.01M15 11h.01M12 11h.01M9 11h.01M7 21h10a2 2 0 002-2V5a2 2 0 00-2-2H7a2 2 0 00-2 2v14a2 2 0 002 2z" />
|
||||
</svg>
|
||||
),
|
||||
'calendar': (
|
||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
|
||||
</svg>
|
||||
),
|
||||
'document-download': (
|
||||
<svg className="w-5 h-5" 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>
|
||||
),
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
export function Step1({ t }: { t: (key: string) => string }) {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<p className="text-slate-600">{t('fa_step1_desc')}</p>
|
||||
<div className="p-4 bg-blue-50 border border-blue-200 rounded-lg">
|
||||
<p className="text-sm text-blue-700">{t('fa_wizard_next')}</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function Step2({ t }: { t: (key: string) => string }) {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-slate-700 mb-2">{t('fa_step2_title')} *</label>
|
||||
<input type="text" className="w-full px-4 py-2 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500" />
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-slate-700 mb-2">{t('fa_step2_subtitle')}</label>
|
||||
<input type="number" className="w-full px-4 py-2 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500" />
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-slate-700 mb-2">{t('fa_step2_subtitle')}</label>
|
||||
<input type="number" className="w-full px-4 py-2 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function Step3({ t }: { t: (key: string) => string }) {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-slate-700 mb-2">{t('fa_step3_desc')}</label>
|
||||
<select className="w-full px-4 py-2 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
|
||||
<option>16 Mbit/s</option>
|
||||
<option>16-50 Mbit/s</option>
|
||||
<option>50-100 Mbit/s</option>
|
||||
<option>100-250 Mbit/s</option>
|
||||
<option>250+ Mbit/s</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function Step4({ t }: { t: (key: string) => string }) {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-slate-700 mb-2">{t('fa_step4_desc')} *</label>
|
||||
<textarea className="w-full px-4 py-2 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500" rows={3} />
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-slate-700 mb-2">{t('fa_step4_subtitle')} *</label>
|
||||
<textarea className="w-full px-4 py-2 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500" rows={4} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function Step5({ t }: { t: (key: string) => string }) {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<p className="text-slate-600">{t('fa_step5_desc')}</p>
|
||||
<div className="border border-slate-200 rounded-lg overflow-hidden">
|
||||
<table className="w-full text-sm">
|
||||
<thead className="bg-slate-50">
|
||||
<tr>
|
||||
<th className="px-4 py-2 text-left font-medium text-slate-700">{t('fa_step5_subtitle')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr className="border-t border-slate-200">
|
||||
<td className="px-4 py-2">
|
||||
<button className="text-blue-600 hover:text-blue-700 font-medium text-sm flex items-center gap-1">
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" />
|
||||
</svg>
|
||||
+
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function Step6({ t }: { t: (key: string) => string }) {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-slate-700 mb-2">{t('fa_step6_desc')}</label>
|
||||
<div className="flex items-center gap-4">
|
||||
<input type="range" min="50" max="100" defaultValue="90" className="flex-1" />
|
||||
<span className="text-lg font-semibold text-slate-900">90%</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="p-4 bg-slate-50 rounded-lg">
|
||||
<div className="text-sm text-slate-500">{t('fa_step6_subtitle')}</div>
|
||||
<div className="text-xl font-bold text-slate-900">0,00 EUR</div>
|
||||
</div>
|
||||
<div className="p-4 bg-blue-50 rounded-lg">
|
||||
<div className="text-sm text-blue-600">{t('fa_step6_title')}</div>
|
||||
<div className="text-xl font-bold text-blue-700">0,00 EUR</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function Step7({ t }: { t: (key: string) => string }) {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-slate-700 mb-2">{t('fa_step7_subtitle')} *</label>
|
||||
<input type="date" className="w-full px-4 py-2 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500" />
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-slate-700 mb-2">{t('fa_step7_subtitle')} *</label>
|
||||
<input type="date" className="w-full px-4 py-2 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function Step8({ t }: { t: (key: string) => string }) {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="p-4 bg-green-50 border border-green-200 rounded-lg">
|
||||
<h3 className="font-semibold text-green-800">{t('fa_step8_title')}</h3>
|
||||
<p className="text-sm text-green-700 mt-1">{t('fa_step8_desc')}</p>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-slate-700 mb-2">{t('fa_step8_subtitle')} *</label>
|
||||
<textarea className="w-full px-4 py-2 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500" rows={4} />
|
||||
</div>
|
||||
<div className="space-y-3">
|
||||
<label className="flex items-center gap-3">
|
||||
<input type="checkbox" className="w-4 h-4 rounded border-slate-300" />
|
||||
<span className="text-sm text-slate-700">{t('fa_info_text')}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
export interface WizardStep {
|
||||
number: number
|
||||
id: string
|
||||
titleKey: string
|
||||
subtitleKey: string
|
||||
descKey: string
|
||||
icon: string
|
||||
is_required: boolean
|
||||
is_completed: boolean
|
||||
}
|
||||
|
||||
export interface FormData {
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
export const DEFAULT_STEPS: WizardStep[] = [
|
||||
{ number: 1, id: 'foerderprogramm', titleKey: 'fa_step1_title', subtitleKey: 'fa_step1_subtitle', descKey: 'fa_step1_desc', icon: 'document-text', is_required: true, is_completed: false },
|
||||
{ number: 2, id: 'schulinformationen', titleKey: 'fa_step2_title', subtitleKey: 'fa_step2_subtitle', descKey: 'fa_step2_desc', icon: 'academic-cap', is_required: true, is_completed: false },
|
||||
{ number: 3, id: 'bestandsaufnahme', titleKey: 'fa_step3_title', subtitleKey: 'fa_step3_subtitle', descKey: 'fa_step3_desc', icon: 'server', is_required: true, is_completed: false },
|
||||
{ number: 4, id: 'projektbeschreibung', titleKey: 'fa_step4_title', subtitleKey: 'fa_step4_subtitle', descKey: 'fa_step4_desc', icon: 'document-report', is_required: true, is_completed: false },
|
||||
{ number: 5, id: 'investitionen', titleKey: 'fa_step5_title', subtitleKey: 'fa_step5_subtitle', descKey: 'fa_step5_desc', icon: 'currency-euro', is_required: true, is_completed: false },
|
||||
{ number: 6, id: 'finanzierungsplan', titleKey: 'fa_step6_title', subtitleKey: 'fa_step6_subtitle', descKey: 'fa_step6_desc', icon: 'calculator', is_required: true, is_completed: false },
|
||||
{ number: 7, id: 'zeitplan', titleKey: 'fa_step7_title', subtitleKey: 'fa_step7_subtitle', descKey: 'fa_step7_desc', icon: 'calendar', is_required: true, is_completed: false },
|
||||
{ number: 8, id: 'abschluss', titleKey: 'fa_step8_title', subtitleKey: 'fa_step8_subtitle', descKey: 'fa_step8_desc', icon: 'document-download', is_required: true, is_completed: false },
|
||||
]
|
||||
@@ -4,65 +4,10 @@ import { useState, useEffect } from 'react'
|
||||
import { useParams, useRouter } from 'next/navigation'
|
||||
import Link from 'next/link'
|
||||
import { useLanguage } from '@/lib/LanguageContext'
|
||||
|
||||
interface WizardStep {
|
||||
number: number
|
||||
id: string
|
||||
titleKey: string
|
||||
subtitleKey: string
|
||||
descKey: string
|
||||
icon: string
|
||||
is_required: boolean
|
||||
is_completed: boolean
|
||||
}
|
||||
|
||||
interface FormData {
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
const stepIcons: Record<string, React.ReactNode> = {
|
||||
'document-text': (
|
||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 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>
|
||||
),
|
||||
'academic-cap': (
|
||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 14l9-5-9-5-9 5 9 5z" />
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 14l6.16-3.422a12.083 12.083 0 01.665 6.479A11.952 11.952 0 0012 20.055a11.952 11.952 0 00-6.824-2.998 12.078 12.078 0 01.665-6.479L12 14z" />
|
||||
</svg>
|
||||
),
|
||||
'server': (
|
||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2" />
|
||||
</svg>
|
||||
),
|
||||
'document-report': (
|
||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 17v-2m3 2v-4m3 4v-6m2 10H7a2 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>
|
||||
),
|
||||
'currency-euro': (
|
||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M14.121 15.536c-1.171 1.952-3.07 1.952-4.242 0-1.172-1.953-1.172-5.119 0-7.072 1.171-1.952 3.07-1.952 4.242 0M8 10.5h4m-4 3h4m9-1.5a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
),
|
||||
'calculator': (
|
||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 7h6m0 10v-3m-3 3h.01M9 17h.01M9 14h.01M12 14h.01M15 11h.01M12 11h.01M9 11h.01M7 21h10a2 2 0 002-2V5a2 2 0 00-2-2H7a2 2 0 00-2 2v14a2 2 0 002 2z" />
|
||||
</svg>
|
||||
),
|
||||
'calendar': (
|
||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
|
||||
</svg>
|
||||
),
|
||||
'document-download': (
|
||||
<svg className="w-5 h-5" 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>
|
||||
),
|
||||
}
|
||||
import { WizardStep, FormData, DEFAULT_STEPS } from './_components/types'
|
||||
import { stepIcons } from './_components/StepIcons'
|
||||
import { Step1, Step2, Step3, Step4, Step5, Step6, Step7, Step8 } from './_components/WizardSteps'
|
||||
import { AssistantSidebar } from './_components/AssistantSidebar'
|
||||
|
||||
export default function FoerderantragWizardPage() {
|
||||
const params = useParams()
|
||||
@@ -70,19 +15,8 @@ export default function FoerderantragWizardPage() {
|
||||
const { t, isRTL } = useLanguage()
|
||||
const applicationId = params.applicationId as string
|
||||
|
||||
const defaultSteps: WizardStep[] = [
|
||||
{ number: 1, id: 'foerderprogramm', titleKey: 'fa_step1_title', subtitleKey: 'fa_step1_subtitle', descKey: 'fa_step1_desc', icon: 'document-text', is_required: true, is_completed: false },
|
||||
{ number: 2, id: 'schulinformationen', titleKey: 'fa_step2_title', subtitleKey: 'fa_step2_subtitle', descKey: 'fa_step2_desc', icon: 'academic-cap', is_required: true, is_completed: false },
|
||||
{ number: 3, id: 'bestandsaufnahme', titleKey: 'fa_step3_title', subtitleKey: 'fa_step3_subtitle', descKey: 'fa_step3_desc', icon: 'server', is_required: true, is_completed: false },
|
||||
{ number: 4, id: 'projektbeschreibung', titleKey: 'fa_step4_title', subtitleKey: 'fa_step4_subtitle', descKey: 'fa_step4_desc', icon: 'document-report', is_required: true, is_completed: false },
|
||||
{ number: 5, id: 'investitionen', titleKey: 'fa_step5_title', subtitleKey: 'fa_step5_subtitle', descKey: 'fa_step5_desc', icon: 'currency-euro', is_required: true, is_completed: false },
|
||||
{ number: 6, id: 'finanzierungsplan', titleKey: 'fa_step6_title', subtitleKey: 'fa_step6_subtitle', descKey: 'fa_step6_desc', icon: 'calculator', is_required: true, is_completed: false },
|
||||
{ number: 7, id: 'zeitplan', titleKey: 'fa_step7_title', subtitleKey: 'fa_step7_subtitle', descKey: 'fa_step7_desc', icon: 'calendar', is_required: true, is_completed: false },
|
||||
{ number: 8, id: 'abschluss', titleKey: 'fa_step8_title', subtitleKey: 'fa_step8_subtitle', descKey: 'fa_step8_desc', icon: 'document-download', is_required: true, is_completed: false },
|
||||
]
|
||||
|
||||
const [currentStep, setCurrentStep] = useState(1)
|
||||
const [steps, setSteps] = useState<WizardStep[]>(defaultSteps)
|
||||
const [steps, setSteps] = useState<WizardStep[]>(DEFAULT_STEPS)
|
||||
const [formData, setFormData] = useState<FormData>({})
|
||||
const [isSaving, setIsSaving] = useState(false)
|
||||
const [showAssistant, setShowAssistant] = useState(false)
|
||||
@@ -146,24 +80,15 @@ export default function FoerderantragWizardPage() {
|
||||
|
||||
const renderStepContent = () => {
|
||||
switch (currentStep) {
|
||||
case 1:
|
||||
return <Step1 t={t} />
|
||||
case 2:
|
||||
return <Step2 t={t} />
|
||||
case 3:
|
||||
return <Step3 t={t} />
|
||||
case 4:
|
||||
return <Step4 t={t} />
|
||||
case 5:
|
||||
return <Step5 t={t} />
|
||||
case 6:
|
||||
return <Step6 t={t} />
|
||||
case 7:
|
||||
return <Step7 t={t} />
|
||||
case 8:
|
||||
return <Step8 t={t} />
|
||||
default:
|
||||
return null
|
||||
case 1: return <Step1 t={t} />
|
||||
case 2: return <Step2 t={t} />
|
||||
case 3: return <Step3 t={t} />
|
||||
case 4: return <Step4 t={t} />
|
||||
case 5: return <Step5 t={t} />
|
||||
case 6: return <Step6 t={t} />
|
||||
case 7: return <Step7 t={t} />
|
||||
case 8: return <Step8 t={t} />
|
||||
default: return null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -306,235 +231,17 @@ export default function FoerderantragWizardPage() {
|
||||
|
||||
{/* Assistant Sidebar */}
|
||||
{showAssistant && (
|
||||
<div className={`fixed ${isRTL ? 'left-0' : 'right-0'} top-0 h-full w-96 bg-white border-${isRTL ? 'r' : 'l'} border-slate-200 shadow-xl z-30 flex flex-col`}>
|
||||
<div className={`p-4 border-b border-slate-200 flex items-center justify-between ${isRTL ? 'flex-row-reverse' : ''}`}>
|
||||
<div className={`flex items-center gap-2 ${isRTL ? 'flex-row-reverse' : ''}`}>
|
||||
<div className="w-8 h-8 rounded-lg bg-gradient-to-br from-blue-500 to-indigo-600 flex items-center justify-center">
|
||||
<svg className="w-4 h-4 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} 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>
|
||||
<h3 className="font-semibold text-slate-900">{t('fa_assistant_title')}</h3>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setShowAssistant(false)}
|
||||
className="p-2 rounded-lg hover:bg-slate-100 transition-colors"
|
||||
>
|
||||
<svg className="w-4 h-4 text-slate-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Chat History */}
|
||||
<div className="flex-1 overflow-y-auto p-4 space-y-4">
|
||||
{assistantHistory.length === 0 && (
|
||||
<div className="text-center py-8">
|
||||
<p className="text-slate-500 text-sm">
|
||||
{t('fa_assistant_placeholder')}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
{assistantHistory.map((msg, idx) => (
|
||||
<div
|
||||
key={idx}
|
||||
className={`flex ${msg.role === 'user' ? 'justify-end' : 'justify-start'}`}
|
||||
>
|
||||
<div
|
||||
className={`max-w-[85%] p-3 rounded-xl text-sm ${
|
||||
msg.role === 'user'
|
||||
? 'bg-blue-600 text-white'
|
||||
: 'bg-slate-100 text-slate-700'
|
||||
}`}
|
||||
>
|
||||
{msg.content}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Input */}
|
||||
<div className="p-4 border-t border-slate-200">
|
||||
<div className={`flex gap-2 ${isRTL ? 'flex-row-reverse' : ''}`}>
|
||||
<input
|
||||
type="text"
|
||||
value={assistantMessage}
|
||||
onChange={(e) => setAssistantMessage(e.target.value)}
|
||||
onKeyDown={(e) => e.key === 'Enter' && handleAskAssistant()}
|
||||
placeholder={t('fa_assistant_placeholder')}
|
||||
className="flex-1 px-3 py-2 border border-slate-200 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
/>
|
||||
<button
|
||||
onClick={handleAskAssistant}
|
||||
className="p-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
|
||||
>
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<AssistantSidebar
|
||||
isRTL={isRTL}
|
||||
t={t}
|
||||
assistantHistory={assistantHistory}
|
||||
assistantMessage={assistantMessage}
|
||||
setAssistantMessage={setAssistantMessage}
|
||||
onAskAssistant={handleAskAssistant}
|
||||
onClose={() => setShowAssistant(false)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// Step Components
|
||||
function Step1({ t }: { t: (key: string) => string }) {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<p className="text-slate-600">{t('fa_step1_desc')}</p>
|
||||
<div className="p-4 bg-blue-50 border border-blue-200 rounded-lg">
|
||||
<p className="text-sm text-blue-700">{t('fa_wizard_next')}</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function Step2({ t }: { t: (key: string) => string }) {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-slate-700 mb-2">{t('fa_step2_title')} *</label>
|
||||
<input type="text" className="w-full px-4 py-2 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500" />
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-slate-700 mb-2">{t('fa_step2_subtitle')}</label>
|
||||
<input type="number" className="w-full px-4 py-2 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500" />
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-slate-700 mb-2">{t('fa_step2_subtitle')}</label>
|
||||
<input type="number" className="w-full px-4 py-2 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function Step3({ t }: { t: (key: string) => string }) {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-slate-700 mb-2">{t('fa_step3_desc')}</label>
|
||||
<select className="w-full px-4 py-2 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
|
||||
<option>16 Mbit/s</option>
|
||||
<option>16-50 Mbit/s</option>
|
||||
<option>50-100 Mbit/s</option>
|
||||
<option>100-250 Mbit/s</option>
|
||||
<option>250+ Mbit/s</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function Step4({ t }: { t: (key: string) => string }) {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-slate-700 mb-2">{t('fa_step4_desc')} *</label>
|
||||
<textarea className="w-full px-4 py-2 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500" rows={3} />
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-slate-700 mb-2">{t('fa_step4_subtitle')} *</label>
|
||||
<textarea className="w-full px-4 py-2 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500" rows={4} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function Step5({ t }: { t: (key: string) => string }) {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<p className="text-slate-600">{t('fa_step5_desc')}</p>
|
||||
<div className="border border-slate-200 rounded-lg overflow-hidden">
|
||||
<table className="w-full text-sm">
|
||||
<thead className="bg-slate-50">
|
||||
<tr>
|
||||
<th className="px-4 py-2 text-left font-medium text-slate-700">{t('fa_step5_subtitle')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr className="border-t border-slate-200">
|
||||
<td className="px-4 py-2">
|
||||
<button className="text-blue-600 hover:text-blue-700 font-medium text-sm flex items-center gap-1">
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" />
|
||||
</svg>
|
||||
+
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function Step6({ t }: { t: (key: string) => string }) {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-slate-700 mb-2">{t('fa_step6_desc')}</label>
|
||||
<div className="flex items-center gap-4">
|
||||
<input type="range" min="50" max="100" defaultValue="90" className="flex-1" />
|
||||
<span className="text-lg font-semibold text-slate-900">90%</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="p-4 bg-slate-50 rounded-lg">
|
||||
<div className="text-sm text-slate-500">{t('fa_step6_subtitle')}</div>
|
||||
<div className="text-xl font-bold text-slate-900">0,00 EUR</div>
|
||||
</div>
|
||||
<div className="p-4 bg-blue-50 rounded-lg">
|
||||
<div className="text-sm text-blue-600">{t('fa_step6_title')}</div>
|
||||
<div className="text-xl font-bold text-blue-700">0,00 EUR</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function Step7({ t }: { t: (key: string) => string }) {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-slate-700 mb-2">{t('fa_step7_subtitle')} *</label>
|
||||
<input type="date" className="w-full px-4 py-2 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500" />
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-slate-700 mb-2">{t('fa_step7_subtitle')} *</label>
|
||||
<input type="date" className="w-full px-4 py-2 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function Step8({ t }: { t: (key: string) => string }) {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="p-4 bg-green-50 border border-green-200 rounded-lg">
|
||||
<h3 className="font-semibold text-green-800">{t('fa_step8_title')}</h3>
|
||||
<p className="text-sm text-green-700 mt-1">{t('fa_step8_desc')}</p>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-slate-700 mb-2">{t('fa_step8_subtitle')} *</label>
|
||||
<textarea className="w-full px-4 py-2 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500" rows={4} />
|
||||
</div>
|
||||
<div className="space-y-3">
|
||||
<label className="flex items-center gap-3">
|
||||
<input type="checkbox" className="w-4 h-4 rounded border-slate-300" />
|
||||
<span className="text-sm text-slate-700">{t('fa_info_text')}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user