// frontend\src\components\ui\TaskList.tsx 'use client' export type TaskItem = { id: string status: 'idle' | 'running' | 'done' | 'error' | 'cancelled' title: string text?: string done?: number total?: number err?: string cancellable?: boolean // zeigt X nur wenn true fading?: boolean // für sanftes Ausblenden } type Props = { tasks: TaskItem[] onCancel?: (id: string) => void } function pct(done?: number, total?: number) { const d = Number(done ?? 0) const t = Number(total ?? 0) if (!t || t <= 0) return 0 return Math.max(0, Math.min(100, Math.round((d / t) * 100))) } export default function TaskList({ tasks, onCancel }: Props) { const visible = (tasks || []).filter((t) => t.status !== 'idle') return (
Hintergrundaufgaben
Laufende Hintergrundaufgaben (z.B. Assets/Previews).
{visible.length === 0 ? (
Keine laufenden Aufgaben.
) : null} {visible.map((t) => { const p = pct(t.done, t.total) const isRunning = t.status === 'running' const hasProgress = isRunning && (t.total ?? 0) > 0 const title = (t.title ?? '').trim() const suffix = (t.text ?? '').trim() return (
{/* Left icon: Cancel X (running+cancellable) OR green check (done) */}
{isRunning && t.cancellable && onCancel ? ( ) : t.status === 'done' ? ( ) : ( )}
{/* One-line row: title (optional) + status + progress */}
{title || 'Aufgabe'} {suffix ? ( {' · '}{suffix} ) : null}
{/* Status badge (running bewusst ohne Badge) */} {t.status === 'done' ? ( fertig ) : t.status === 'cancelled' ? ( abgebrochen ) : t.status === 'error' ? ( fehler ) : null} {/* Spacer */}
{/* Progress compact on same line */} {hasProgress ? (
{t.done ?? 0}/{t.total ?? 0} {p}% {/* Progressbar: auf Mobile ausblenden, ab sm anzeigen */}
) : null}
{/* Error text optional (unter der Zeile) */} {t.status === 'error' && t.err ? (
{t.err}
) : null}
) })}
) }