[split-required] Split final batch of monoliths >1000 LOC
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>
This commit is contained in:
110
website/app/admin/companion/_components/FeedbackTab.tsx
Normal file
110
website/app/admin/companion/_components/FeedbackTab.tsx
Normal file
@@ -0,0 +1,110 @@
|
||||
'use client'
|
||||
|
||||
import type { Feature, TeacherFeedback } from './types'
|
||||
import { priorityColors, feedbackTypeIcons } from './types'
|
||||
|
||||
interface FeedbackTabProps {
|
||||
features: Feature[]
|
||||
filteredFeedback: TeacherFeedback[]
|
||||
feedbackFilter: string
|
||||
setFeedbackFilter: (filter: string) => void
|
||||
updateFeedbackStatus: (id: string, status: TeacherFeedback['status']) => void
|
||||
}
|
||||
|
||||
export default function FeedbackTab({
|
||||
features,
|
||||
filteredFeedback,
|
||||
feedbackFilter,
|
||||
setFeedbackFilter,
|
||||
updateFeedbackStatus,
|
||||
}: FeedbackTabProps) {
|
||||
return (
|
||||
<div>
|
||||
{/* Filter */}
|
||||
<div className="flex gap-2 mb-4 flex-wrap">
|
||||
{['all', 'new', 'bug', 'feature_request', 'improvement'].map(filter => (
|
||||
<button
|
||||
key={filter}
|
||||
onClick={() => setFeedbackFilter(filter)}
|
||||
className={`px-3 py-1.5 rounded-lg text-sm font-medium transition-colors ${
|
||||
feedbackFilter === filter ? 'bg-primary-600 text-white' : 'bg-slate-100 text-slate-600 hover:bg-slate-200'
|
||||
}`}
|
||||
>
|
||||
{filter === 'all' ? 'Alle' :
|
||||
filter === 'new' ? 'Neu' :
|
||||
filter === 'bug' ? 'Bugs' :
|
||||
filter === 'feature_request' ? 'Feature-Requests' : 'Verbesserungen'}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Feedback List */}
|
||||
<div className="space-y-3">
|
||||
{filteredFeedback.map(fb => (
|
||||
<div key={fb.id} className="border border-slate-200 rounded-xl p-4">
|
||||
<div className="flex items-start gap-3">
|
||||
<div className={`w-10 h-10 rounded-full flex items-center justify-center ${
|
||||
fb.type === 'bug' ? 'bg-red-100' :
|
||||
fb.type === 'feature_request' ? 'bg-blue-100' :
|
||||
fb.type === 'improvement' ? 'bg-amber-100' :
|
||||
fb.type === 'praise' ? 'bg-pink-100' : 'bg-purple-100'
|
||||
}`}>
|
||||
<svg className={`w-5 h-5 ${
|
||||
fb.type === 'bug' ? 'text-red-600' :
|
||||
fb.type === 'feature_request' ? 'text-blue-600' :
|
||||
fb.type === 'improvement' ? 'text-amber-600' :
|
||||
fb.type === 'praise' ? 'text-pink-600' : 'text-purple-600'
|
||||
}`} fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d={feedbackTypeIcons[fb.type]} />
|
||||
</svg>
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center gap-2 mb-1">
|
||||
<span className="font-semibold text-slate-900">{fb.title}</span>
|
||||
<span className={`px-1.5 py-0.5 rounded text-xs ${priorityColors[fb.priority]}`}>
|
||||
{fb.priority}
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-sm text-slate-600 mb-2">{fb.description}</p>
|
||||
<div className="flex items-center gap-4 text-xs text-slate-400">
|
||||
<span>{fb.teacher}</span>
|
||||
<span>{fb.date}</span>
|
||||
{fb.relatedFeature && (
|
||||
<span className="text-primary-600">→ {features.find(f => f.id === fb.relatedFeature)?.title}</span>
|
||||
)}
|
||||
</div>
|
||||
{fb.response && (
|
||||
<div className="mt-2 p-2 bg-green-50 rounded text-sm text-green-800">
|
||||
<strong>Antwort:</strong> {fb.response}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<select
|
||||
value={fb.status}
|
||||
onChange={(e) => updateFeedbackStatus(fb.id, e.target.value as TeacherFeedback['status'])}
|
||||
className={`px-2 py-1 rounded text-xs font-medium border-0 cursor-pointer ${
|
||||
fb.status === 'new' ? 'bg-red-100 text-red-700' :
|
||||
fb.status === 'acknowledged' ? 'bg-blue-100 text-blue-700' :
|
||||
fb.status === 'planned' ? 'bg-amber-100 text-amber-700' :
|
||||
fb.status === 'implemented' ? 'bg-green-100 text-green-700' :
|
||||
'bg-slate-100 text-slate-600'
|
||||
}`}
|
||||
>
|
||||
<option value="new">Neu</option>
|
||||
<option value="acknowledged">Gesehen</option>
|
||||
<option value="planned">Geplant</option>
|
||||
<option value="implemented">Umgesetzt</option>
|
||||
<option value="declined">Abgelehnt</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Add Feedback Button */}
|
||||
<button className="mt-4 w-full py-3 border-2 border-dashed border-slate-300 rounded-xl text-slate-500 hover:border-primary-400 hover:text-primary-600 transition-colors">
|
||||
+ Neues Feedback hinzufuegen
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user