- sprint12-analysis.md (full page audit) - sprint12-plan.md (button fix plan) - sprint12-testplan.md (button fix test plan) - sprint12-phase2-integration-tests.md (v3, expert-approved) - sprint12-phase2-panel-review.md (3 review cycles, 95% confidence) - sprint12-code-review.md (approved with comments, blockers fixed)
7.0 KiB
Sprint 12 Analysis: "Golden Test Standard — Everything Present Must Work"
Datum: 18.06.2026
Autor: Patrick Plate / Lumen (Planner)
Status: v1
Prinzip: Jeder sichtbare Button muss funktionieren — sonst warum ist er da?
1. Audit-Zusammenfassung
| Seite | Status | P0 (broken) | P1 (UX) | P2 (polish) |
|---|---|---|---|---|
/documents |
❌ Kritisch | 3 | 2 | 0 |
/board |
❌ Kritisch | 3 | 0 | 0 |
/dashboard |
✅ OK | 0 | 0 | 0 |
/members |
✅ OK | 0 | 0 | 0 |
/distributions |
✅ OK | 0 | 0 | 0 |
/stock |
✅ OK | 0 | 0 | 0 |
/grow |
✅ OK | 0 | 0 | 0 |
/reports |
✅ OK | 0 | 0 | 0 |
/calendar |
✅ OK | 0 | 0 | 0 |
/forum |
✅ OK | 0 | 0 | 0 |
/info-board |
✅ OK | 0 | 0 | 0 |
/finance |
✅ OK | 0 | 0 | 0 |
/assemblies |
✅ OK | 0 | 0 | 0 |
/compliance |
✅ OK | 0 | 0 | 0 |
/audit-log |
✅ OK | 0 | 0 | 0 |
/settings/staff |
✅ OK | 0 | 0 | 0 |
/settings/billing |
⚠️ TBD | 0 | 0 | 0 |
/settings/privacy |
⚠️ TBD | 0 | 0 | 0 |
Gesamt: 6 P0-Defekte, 2 P1-Defekte — alle konzentriert auf 2 Seiten.
2. P0 Findings — Komplett defekte Buttons
2.1 Documents Page (documents/page.tsx)
| # | Element | Zeile | Problem | Service vorhanden? |
|---|---|---|---|---|
| P0-1 | Upload-Button im Dialog | 217 | onClick={() => setUploadOpen(false)} — schließt nur den Dialog, ruft nie uploadDocument() auf |
✅ Ja: uploadDocument() in services/documents.ts:31 |
| P0-2 | Download-Button pro Datei | 307-308 | <Button variant="ghost" size="icon"> — kein onClick handler |
✅ Ja: downloadDocument() in services/documents.ts:73 |
| P0-3 | Delete-Button pro Datei | 310-313 | <Button variant="ghost" size="icon"> — kein onClick handler |
✅ Ja: deleteDocument() in services/documents.ts:79 |
Root Cause: Die Documents-Seite wurde als statische Mock-Darstellung gebaut. Die Service-Schicht (services/documents.ts) existiert vollständig mit uploadDocument(), downloadDocument(), deleteDocument() und listDocuments() — aber keine dieser Funktionen wird von der Page aufgerufen. Die Seite nutzt nicht mal React Query (nur useState mit hartkodierten Mock-Daten).
2.2 Board Page (board/page.tsx)
| # | Element | Zeile | Problem | Service vorhanden? |
|---|---|---|---|---|
| P0-4 | "Position speichern" Button | 189-191 | onClick={() => setPositionDialogOpen(false)} — schließt nur Dialog, kein API-Call |
✅ Ja: createPosition() in services/board.ts |
| P0-5 | "Wahl bestätigen" Button | 244-248 | onClick={() => setElectDialogOpen(false)} — schließt nur Dialog, kein API-Call |
✅ Ja: electBoardMember() in services/board.ts |
| P0-6 | Mitglied absetzen (UserMinus) | 269-273 | <Button variant="ghost" size="icon"> — kein onClick handler |
✅ Ja: removeBoardMember() in services/board.ts |
Root Cause: Identisches Pattern wie Documents — die Seite zeigt Mock-Daten, die Dialoge sammeln Formulardaten, aber der Submit-Button schließt nur den Dialog ohne die Service-Schicht aufzurufen.
3. P1 Findings — UX-Issues (Documents speziell)
3.1 Documents: Fehlende visuelle Kategorie-Unterscheidung
Problem: Die getCategoryBadgeVariant() Funktion nutzt nur 4 generische Badge-Varianten (default, secondary, destructive, outline) für 6 Kategorien. Mehrere Kategorien teilen sich denselben Stil:
- VERTRAG und VERSICHERUNG → beide
outline(identisch) - PROTOKOLL und SONSTIGES → beide
secondary(identisch)
Lösung: Eigene Farben pro Kategorie + Icons:
- SATZUNG → Blau + BookOpen-Icon
- PROTOKOLL → Lila + FileText-Icon
- VERTRAG → Amber + FileSignature-Icon
- VERSICHERUNG → Cyan + Shield-Icon
- GENEHMIGUNG → Grün + CheckCircle-Icon
- SONSTIGES → Grau + File-Icon
3.2 Documents: Table-Layout ohne min-width
Problem: Die Table-Zellen haben keine min-w-* oder w-* Constraints. Bei langen Dateinamen/Titeln stretcht sich die Name-Spalte über die gesamte verfügbare Breite, während kurze Zellen (Size, Date) zusammengedrückt werden.
Lösung:
- Name-Spalte:
max-w-[300px] truncate - Access-Spalte:
w-[120px] - Size-Spalte:
w-[80px] - Date-Spalte:
w-[100px] - Actions-Spalte:
w-[80px]
4. Funktionale Seiten (kein Handlungsbedarf)
Die folgenden Seiten sind korrekt mit ihren Service-Layern verdrahtet:
| Seite | Pattern | Mutations/Actions |
|---|---|---|
| Members | React Query + TanStack Table | Edit navigiert zu /members/[id] ✅ |
| Distributions | useDistributionsQuery() + TanStack Table |
"New" verlinkt korrekt ✅ |
| Stock | useBatchesQuery() + useRecallBatchMutation() |
Recall mit AlertDialog ✅ |
| Reports | useMonthlyReportQuery() etc. |
Download-Buttons mit try/catch + Blob ✅ |
| Calendar | useEventsQuery() + useCreateEventMutation() + useCancelEventMutation() |
Create-Form + Cancel funktional ✅ |
| Forum | useCreateTopic() + useLockTopic() etc. |
Alle Moderation-Buttons verdrahtet ✅ |
| Info-Board | useCreatePostMutation() + useDeletePostMutation() etc. |
CRUD komplett ✅ |
| Finance | useRecordPaymentMutation() + useRecordExpenseMutation() |
PaymentForm/ExpenseForm mit onSubmit ✅ |
| Assemblies | createAssembly() + getAssemblies() |
Create-Dialog mit Service-Call ✅ |
| Compliance | getComplianceDashboard() + completeDeadline() |
Deadline-Buttons funktional ✅ |
| Audit-Log | useAuditLogQuery() + useExportAuditPdfMutation() |
Export mit Loading-State ✅ |
| Staff | useInviteStaffMutation() + useRevokeStaffMutation() |
Invite/Revoke/Permissions komplett ✅ |
5. Architektur-Analyse: Documents Page Refactoring
Die Documents-Seite braucht ein komplettes Refactoring von "static mock display" zu "React Query powered":
Ist-Zustand:
useState(mockDocuments) → static render → buttons do nothing
Soll-Zustand:
useQuery(['documents']) → dynamic data
useMutation(uploadDocument) → upload with progress
downloadDocument(id) → blob download + save-as
useMutation(deleteDocument) → confirm dialog + optimistic update
Vorhandene Service-Funktionen (alle bereits implementiert in services/documents.ts):
listDocuments(clubId, category?, accessLevel?)→GET /documentsuploadDocument(clubId, title, category, accessLevel, description, file)→POST /documents/upload(multipart)downloadDocument(id)→GET /documents/{id}/download→ BlobdeleteDocument(id, clubId)→DELETE /documents/{id}getStorageUsage(clubId)→GET /documents/usage
6. Empfehlung
Scope ist sehr fokussiert: Nur 2 Seiten brauchen Arbeit — Documents und Board. Alle anderen 16+ Seiten sind funktional korrekt verdrahtet.
Geschätzter Aufwand:
- Documents Page Refactoring: ~3h (React Query integration + download/delete handlers + UX fixes)
- Board Page Wiring: ~1.5h (3 handlers verdrahten + Confirmation-Dialogs)
- Gesamt: ~4.5h