Python (6 files in klausur-service): - rbac.py (1,132 → 4), admin_api.py (1,012 → 4) - routes/eh.py (1,111 → 4), ocr_pipeline_geometry.py (1,105 → 5) Python (2 files in backend-lehrer): - unit_api.py (1,226 → 6), game_api.py (1,129 → 5) Website (6 page files): - 4x klausur-korrektur pages (1,249-1,328 LOC each) → shared components in website/components/klausur-korrektur/ (17 shared files) - companion (1,057 → 10), magic-help (1,017 → 8) All re-export barrels preserve backward compatibility. Zero import errors verified. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
77 lines
3.0 KiB
TypeScript
77 lines
3.0 KiB
TypeScript
'use client'
|
|
|
|
import type { Feature } from './types'
|
|
import { statusColors, priorityColors } from './types'
|
|
import { roadmapPhases } from './data'
|
|
|
|
interface FeaturesTabProps {
|
|
features: Feature[]
|
|
selectedPhase: string | null
|
|
setSelectedPhase: (phase: string | null) => void
|
|
updateFeatureStatus: (id: string, status: Feature['status']) => void
|
|
}
|
|
|
|
export default function FeaturesTab({ features, selectedPhase, setSelectedPhase, updateFeatureStatus }: FeaturesTabProps) {
|
|
return (
|
|
<div>
|
|
{/* Phase Filter */}
|
|
<div className="flex gap-2 mb-4 flex-wrap">
|
|
<button
|
|
onClick={() => setSelectedPhase(null)}
|
|
className={`px-3 py-1.5 rounded-lg text-sm font-medium transition-colors ${
|
|
!selectedPhase ? 'bg-primary-600 text-white' : 'bg-slate-100 text-slate-600 hover:bg-slate-200'
|
|
}`}
|
|
>
|
|
Alle
|
|
</button>
|
|
{roadmapPhases.map(phase => (
|
|
<button
|
|
key={phase.id}
|
|
onClick={() => setSelectedPhase(phase.id)}
|
|
className={`px-3 py-1.5 rounded-lg text-sm font-medium transition-colors ${
|
|
selectedPhase === phase.id ? 'bg-primary-600 text-white' : 'bg-slate-100 text-slate-600 hover:bg-slate-200'
|
|
}`}
|
|
>
|
|
{phase.name.replace('Phase ', 'P')}
|
|
</button>
|
|
))}
|
|
</div>
|
|
|
|
{/* Features List */}
|
|
<div className="space-y-2">
|
|
{features
|
|
.filter(f => !selectedPhase || f.phase === selectedPhase)
|
|
.map(feature => (
|
|
<div key={feature.id} className="flex items-center gap-4 p-3 bg-slate-50 rounded-lg">
|
|
<span className={`px-2 py-0.5 rounded text-xs font-medium ${priorityColors[feature.priority]}`}>
|
|
{feature.priority}
|
|
</span>
|
|
<div className="flex-1 min-w-0">
|
|
<div className="font-medium text-slate-900 truncate">{feature.title}</div>
|
|
<div className="text-xs text-slate-500 truncate">{feature.description}</div>
|
|
</div>
|
|
<span className={`px-2 py-1 rounded text-xs font-medium whitespace-nowrap ${
|
|
feature.effort === 'small' ? 'bg-green-100 text-green-700' :
|
|
feature.effort === 'medium' ? 'bg-amber-100 text-amber-700' :
|
|
feature.effort === 'large' ? 'bg-orange-100 text-orange-700' :
|
|
'bg-red-100 text-red-700'
|
|
}`}>
|
|
{feature.effort}
|
|
</span>
|
|
<select
|
|
value={feature.status}
|
|
onChange={(e) => updateFeatureStatus(feature.id, e.target.value as Feature['status'])}
|
|
className={`px-2 py-1 rounded text-xs font-medium border-0 cursor-pointer ${statusColors[feature.status]}`}
|
|
>
|
|
<option value="done">Fertig</option>
|
|
<option value="in_progress">In Arbeit</option>
|
|
<option value="todo">Todo</option>
|
|
<option value="backlog">Backlog</option>
|
|
</select>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|