be932c1930
- 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)
145 lines
7.0 KiB
Markdown
145 lines
7.0 KiB
Markdown
# 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 /documents`
|
|
- `uploadDocument(clubId, title, category, accessLevel, description, file)` → `POST /documents/upload` (multipart)
|
|
- `downloadDocument(id)` → `GET /documents/{id}/download` → Blob
|
|
- `deleteDocument(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**
|