Files
Papo/frontend/src/pages/Tasks.tsx
Pepe44DEV 2ff54970e2
All checks were successful
Deploy Discord Bot / deploy (push) Successful in -1m11s
SonarQube / sonar (push) Successful in -3s
add setup wizard, lockdown, team tasks, embed builder; fix admin dashboard
Four new independent features: /setup (guided onboarding wizard for
module/channel/automod-strength config), /lockdown (raid protection with
per-channel permission snapshot/restore and join-kick), /task (staff task
tracker with status buttons + dashboard view), /embed create (modal-driven
embed builder with optional link button). Also fixes the Admin dashboard,
which always showed "-" because loadAdminData() double-wrapped the overview
response and discarded activity data, plus mismatched field names against
adminService's actual shape (guildCount/activeGuilds24/uptimeMs).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-03 01:02:39 +02:00

81 lines
3.9 KiB
TypeScript

import { Card, CardContent, CardHeader, Input, TextArea, Button, Chip, TextField, Label } from '@heroui/react';
import { ListChecks, Trash2, Plus } from 'lucide-react';
import { useApp } from '../context/AppContext';
import { SectionCard } from '../components/shared/SectionCard';
import { formatDate } from '../utils/formatters';
const STATUS_LABELS: Record<string, string> = { open: 'Offen', in_progress: 'In Bearbeitung', done: 'Erledigt' };
const STATUS_COLORS: Record<string, 'warning' | 'accent' | 'success'> = { open: 'warning', in_progress: 'accent', done: 'success' };
export function Tasks() {
const { tasks, taskDraft, setTaskDraft, createTask, updateTaskStatus, deleteTask } = useApp();
return (
<SectionCard title="Team-Aufgaben" subtitle="Aufgaben für dein Team erstellen und Status pflegen.">
<div className="grid gap-5 xl:grid-cols-[1fr_360px]">
<div>
<h3 className="mb-3 text-base font-semibold">Aufgaben ({tasks.length})</h3>
<div className="space-y-3">
{tasks.length ? tasks.map((t) => (
<Card key={t.id}>
<CardContent className="flex flex-col gap-3 p-4">
<div className="flex items-center justify-between gap-2">
<div className="min-w-0">
<div className="font-semibold truncate">{t.title}</div>
<div className="text-xs text-muted">von {t.createdByTag} · {formatDate(t.createdAt)}</div>
</div>
<Button isIconOnly size="sm" variant="danger-soft" onPress={() => deleteTask(t.id)}>
<Trash2 size={14} />
</Button>
</div>
{t.description && <p className="text-sm text-muted">{t.description}</p>}
<div className="flex flex-wrap items-center gap-2">
<select
className="rounded-xl px-3 py-2 text-sm"
value={t.status}
onChange={(e) => updateTaskStatus(t.id, e.target.value)}
>
<option value="open">Offen</option>
<option value="in_progress">In Bearbeitung</option>
<option value="done">Erledigt</option>
</select>
<Chip size="sm" variant="soft" color={STATUS_COLORS[t.status] || 'warning'}>
{STATUS_LABELS[t.status] || t.status}
</Chip>
{t.assigneeId && <span className="text-xs text-muted">Zugewiesen an User-ID {t.assigneeId}</span>}
</div>
</CardContent>
</Card>
)) : (
<div className="flex flex-col items-center gap-2 py-8 text-center text-sm text-muted">
<ListChecks size={24} />
Keine Aufgaben
</div>
)}
</div>
</div>
<Card>
<CardHeader className="px-5 pt-5 pb-0">
<h3 className="text-base font-semibold">Neue Aufgabe</h3>
</CardHeader>
<CardContent className="flex flex-col gap-4 p-5">
<TextField>
<Label>Titel</Label>
<Input value={taskDraft.title} onChange={(e) => setTaskDraft((s) => ({ ...s, title: e.target.value }))} />
</TextField>
<TextField>
<Label>Beschreibung</Label>
<TextArea rows={4} value={taskDraft.description} onChange={(e) => setTaskDraft((s) => ({ ...s, description: e.target.value }))} />
</TextField>
<p className="text-xs text-muted">Eine zuständige Person lässt sich aktuell nur über <code>/task create</code> in Discord zuweisen.</p>
<Button variant="primary" onPress={createTask} isDisabled={!taskDraft.title.trim()}>
<Plus size={16} /> Aufgabe erstellen
</Button>
</CardContent>
</Card>
</div>
</SectionCard>
);
}