Some checks failed
Build + Deploy / build-admin-compliance (push) Failing after 34s
Build + Deploy / build-developer-portal (push) Successful in 56s
Build + Deploy / build-tts (push) Successful in 1m8s
CI/CD / go-lint (push) Has been skipped
Build + Deploy / trigger-orca (push) Has been skipped
CI/CD / python-lint (push) Has been skipped
CI/CD / nodejs-lint (push) Has been skipped
CI/CD / test-go-ai-compliance (push) Successful in 38s
CI/CD / test-python-backend-compliance (push) Successful in 32s
Build + Deploy / build-backend-compliance (push) Successful in 7s
Build + Deploy / build-ai-sdk (push) Successful in 7s
Build + Deploy / build-document-crawler (push) Successful in 33s
Build + Deploy / build-dsms-gateway (push) Successful in 20s
CI/CD / test-python-dsms-gateway (push) Has been cancelled
CI/CD / validate-canonical-controls (push) Has been cancelled
CI/CD / test-python-document-crawler (push) Has been cancelled
All 8 components imported by app/sdk/training/page.tsx were missing. Docker build was failing with Module not found errors. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
105 lines
4.5 KiB
TypeScript
105 lines
4.5 KiB
TypeScript
'use client'
|
|
|
|
import type { TrainingAssignment } from '@/lib/sdk/training/types'
|
|
import { STATUS_LABELS, STATUS_COLORS } from '@/lib/sdk/training/types'
|
|
|
|
export default function AssignmentsTab({
|
|
assignments,
|
|
statusFilter,
|
|
onStatusFilterChange,
|
|
onAssignmentClick,
|
|
}: {
|
|
assignments: TrainingAssignment[]
|
|
statusFilter: string
|
|
onStatusFilterChange: (v: string) => void
|
|
onAssignmentClick: (assignment: TrainingAssignment) => void
|
|
}) {
|
|
return (
|
|
<div className="space-y-4">
|
|
<div className="flex items-center gap-3">
|
|
<select
|
|
value={statusFilter}
|
|
onChange={e => onStatusFilterChange(e.target.value)}
|
|
className="px-3 py-2 text-sm border rounded-lg bg-white"
|
|
>
|
|
<option value="">Alle Status</option>
|
|
{Object.entries(STATUS_LABELS).map(([k, v]) => (
|
|
<option key={k} value={k}>{v}</option>
|
|
))}
|
|
</select>
|
|
<span className="text-sm text-gray-500">{assignments.length} Zuweisungen</span>
|
|
</div>
|
|
|
|
{assignments.length === 0 ? (
|
|
<div className="text-center py-12 text-gray-500 text-sm">Keine Zuweisungen gefunden.</div>
|
|
) : (
|
|
<div className="bg-white border rounded-lg overflow-hidden">
|
|
<table className="w-full text-sm">
|
|
<thead className="bg-gray-50">
|
|
<tr>
|
|
<th className="px-4 py-3 text-left font-medium text-gray-600">Nutzer</th>
|
|
<th className="px-4 py-3 text-left font-medium text-gray-600">Modul</th>
|
|
<th className="px-4 py-3 text-left font-medium text-gray-600">Status</th>
|
|
<th className="px-4 py-3 text-left font-medium text-gray-600">Fortschritt</th>
|
|
<th className="px-4 py-3 text-left font-medium text-gray-600">Frist</th>
|
|
<th className="px-4 py-3 text-left font-medium text-gray-600">Quiz</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody className="divide-y">
|
|
{assignments.map(a => {
|
|
const colors = STATUS_COLORS[a.status]
|
|
const deadline = new Date(a.deadline)
|
|
const isOverdue = deadline < new Date() && a.status !== 'completed'
|
|
return (
|
|
<tr
|
|
key={a.id}
|
|
onClick={() => onAssignmentClick(a)}
|
|
className="hover:bg-gray-50 cursor-pointer"
|
|
>
|
|
<td className="px-4 py-3">
|
|
<div className="font-medium text-gray-900">{a.user_name}</div>
|
|
<div className="text-xs text-gray-500">{a.user_email}</div>
|
|
</td>
|
|
<td className="px-4 py-3">
|
|
<code className="text-xs bg-gray-100 px-1.5 py-0.5 rounded">{a.module_code ?? a.module_id.slice(0, 8)}</code>
|
|
{a.module_title && <div className="text-xs text-gray-500 mt-0.5">{a.module_title}</div>}
|
|
</td>
|
|
<td className="px-4 py-3">
|
|
<span className={`text-xs px-2 py-0.5 rounded-full ${colors.bg} ${colors.text}`}>
|
|
{STATUS_LABELS[a.status]}
|
|
</span>
|
|
</td>
|
|
<td className="px-4 py-3">
|
|
<div className="flex items-center gap-2">
|
|
<div className="w-20 h-1.5 bg-gray-200 rounded-full overflow-hidden">
|
|
<div
|
|
className="h-full bg-blue-500 rounded-full"
|
|
style={{ width: `${a.progress_percent}%` }}
|
|
/>
|
|
</div>
|
|
<span className="text-xs text-gray-600">{a.progress_percent}%</span>
|
|
</div>
|
|
</td>
|
|
<td className={`px-4 py-3 text-xs ${isOverdue ? 'text-red-600 font-medium' : 'text-gray-600'}`}>
|
|
{deadline.toLocaleDateString('de-DE')}
|
|
</td>
|
|
<td className="px-4 py-3 text-xs text-gray-600">
|
|
{a.quiz_score != null ? (
|
|
<span className={a.quiz_passed ? 'text-green-600' : 'text-red-600'}>
|
|
{Math.round(a.quiz_score)}% {a.quiz_passed ? '✓' : '✗'}
|
|
</span>
|
|
) : (
|
|
<span className="text-gray-400">—</span>
|
|
)}
|
|
</td>
|
|
</tr>
|
|
)
|
|
})}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|