feat: project colors, timeline filters, sidebar fix, GitLooper agent, and misc improvements

- Fix sidebar double-highlight on /vacations/my (Gitea #6): add isNavItemActive() helper
- Add project color picker (schema + API + modal + timeline rendering)
- Add ProjectCombobox/ResourceCombobox to timeline toolbar
- Show PENDING vacations on timeline with dashed/dimmed style
- Add "show demand projects" preference with localStorage persistence
- Add ProjectAssignmentsTable with total hours/cost columns
- Extend vacation API to accept status arrays
- Add GitLooper formal YAML agent configuration
- Extend user admin with permission overrides UI
- Add delete-assignment use case tests
- Add status-styles.ts shared badge constants
- Centralize formatMoney/formatCents in format.ts

Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
2026-03-17 10:22:52 +01:00
parent b0e55786c3
commit eb283147d1
34 changed files with 1545 additions and 255 deletions
+49 -70
View File
@@ -1,98 +1,77 @@
# Review-Report — 2026-03-15
# Review-Report — 2026-03-15 (3D Computation Graph)
## Ergebnis: ✅ Bestanden
Alle Quality Gates bestanden. Keine kritischen Probleme gefunden. Zwei Minor-Issues behoben waehrend des Reviews.
Alle Quality Gates bestanden. Keine kritischen Probleme. Zwei Minor-Empfehlungen.
## Quality Gates
| Gate | Status | Details |
|------|--------|---------|
| Engine Tests | ✅ | 254/254 bestanden (17 Dateien) |
| Staffing Tests | ✅ | 37/37 bestanden (3 Dateien) |
| API Tests | ✅ | 209/209 bestanden (21 Dateien) |
| Application Tests | ✅ | 67/67 bestanden (15 Dateien) |
| **Gesamt** | **✅** | **567/567 Tests bestanden** |
| TypeScript (web) | ✅ | 0 Fehler |
| TypeScript (api) | ✅ | 0 Fehler |
| Paketabhaengigkeiten | ✅ | Keine Zyklen |
| Engine Tests | ✅ | 283/283 (19 files) |
| Staffing Tests | ✅ | 37/37 (3 files) |
| API Tests | ✅ | 209/209 (21 files) |
| Application Tests | ✅ | 67/67 (15 files) |
| TypeScript (web) | ✅ | 0 errors (excl. BlueprintFieldEditor TS2589) |
| TypeScript (api) | ✅ | 0 errors |
## Architektur-Checkliste
## Code-Review-Checkliste
- [x] Keine zirkulaeren Abhaengigkeiten — engine, staffing, ui sind sauber isoliert
- [x] `engine` und `staffing` haben keine DB-Imports
- [x] Alle 24 tRPC-Router in `packages/api/src/router/index.ts` registriert
- [x] SSE-Events: 10 Emitter fuer Allocation, Project, Budget, Vacation, Role, Notification
- [x] SSE-Debouncing (50ms) aktiv in event-bus.ts
### Architektur
- [x] Keine zirkulaeren Abhaengigkeiten — `api → engine/shared/db` (erlaubt)
- [x] `engine` und `staffing` unveraendert, keine DB-Imports
- [x] Neuer Router `computationGraph` in `index.ts` registriert
- [x] Keine SSE-Events noetig (read-only Feature, keine Mutations)
## TypeScript & Typsicherheit
### TypeScript & Typsicherheit
- [x] `any`-Types nur an `react-force-graph-3d`-Grenzen mit `eslint-disable` Kommentar (6 Stellen)
- [x] Prisma-Enums gecastet: `pa.status as unknown as string` + `as Parameters<typeof computeBudgetStatus>[2]`
- [x] JSONB-Feld gecastet: `commercialTerms as { contingencyPercent?: number; ... } | null`
- [x] `scheduleRules as SpainScheduleRule | null` korrekt
- [x] `exactOptionalPropertyTypes` beachtet: `...(formula ? { formula } : {})` Pattern
- [x] Keine unkommentierten `any`-Types — alle Vorkommnisse haben `// eslint-disable-next-line` oder sind intentionale Casts
- [x] Prisma-Enums an Client-Grenzen mit `as unknown as SharedType` gecastet
- [x] JSONB-Felder korrekt gecastet
- [x] Nullable FKs mit optional chaining behandelt
- [x] `exactOptionalPropertyTypes` Pattern eingehalten (Spread statt `{ key: undefined }`)
### Datenbank & Prisma
- [x] Keine Schema-Aenderungen — rein lesende Queries
- [x] Geldbetraege in Integer-Cents: `lcrCents`, `dailyCostCents`, `budgetCents` etc.
- [x] Kein Seed noetig (kein neues Modell)
## Datenbank & Prisma
### UI & Komponenten
- [x] `"use client"` Direktive gesetzt
- [x] Three.js via `dynamic(() => import(...), { ssr: false })` — kein SSR-Problem
- [x] Neue Seite in AppShell-Navigation ergaenzt ("Computation Graph" unter Analytics)
- [x] Opake Hintergruende: `bg-zinc-50`, `bg-zinc-900/95` (95% ist akzeptabel fuer Tooltip)
- [x] Geldbetraege als Integer-Cents
- [x] Kein unsicheres Raw-SQL in App-Routern — einziges `$executeRaw` in resource.ts nutzt tagged template literals (parameterisiert)
- [x] Composite Indexes fuer Assignment, DemandRequirement, Vacation vorhanden
### Sicherheit
- [x] Beide Procedures nutzen `controllerProcedure` (ADMIN + MANAGER + CONTROLLER)
- [x] Keine Raw-Queries — nur Prisma `findMany`/`findUniqueOrThrow`
- [x] Keine sensiblen Daten im Response — nur berechnete Werte und Formeln
## Sicherheit
## Gefundene Probleme
- [x] `protectedProcedure` erfordert jetzt `session.user` UND `dbUser` (gehaertet)
- [x] Vacation create/cancel: Ownership-Check fuer USER-Rolle
- [x] `user.list` auf `adminProcedure` eingeschraenkt
- [x] `entitlement.getBalance`: Ownership-Check fuer USER-Rolle
- [x] Allocation listView/list/listDemands/listAssignments: Anonymisierung angewendet
- [x] Security Headers in next.config.ts (X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy)
- [x] Keine Passwoerter/Secrets in tRPC-Responses — `settings.ts` gibt nur `hasApiKey`/`hasSmtpPassword` Booleans zurueck
- [x] Alle Mutations hinter `managerProcedure` oder `adminProcedure`
- [x] Kein `publicProcedure` in Routern
### Kritisch
Keine.
## UI & Komponenten
### Minor
- [x] AppShell: Kollabierbare Navigationsgruppen (Estimating, ACN-Orga)
- [x] Tailwind-Opacity nur in 5er-Schritten (CSS-Fix fuer `/92`, `/88`, `/94`)
- [x] `trpc.role.list` korrekt als Array behandelt
1. **Duplizierte Types**`GraphNode`, `GraphLink`, `Domain` sind sowohl in `packages/api/.../computation-graph.ts` als auch `apps/web/.../domain-colors.ts` definiert. Funktioniert (tRPC inferiert die Typen), aber bei Aenderungen muss man beide Stellen anpassen. Empfehlung: Types nach `@planarchy/shared` verschieben wenn sie stabil sind.
## Waehrend des Reviews behoben
2. **`project.list` Query** — Der Client castet das Ergebnis via `(projectData as any)?.projects ?? (projectData as any)`. Das deutet auf Unsicherheit ueber das Return-Format hin. Sollte nach dem Merge geprueft werden, ob `.projects` oder direkt das Array zurueckkommt.
### Minor: assignment-bookings Test-Sync
- **Datei:** `packages/application/src/__tests__/assignment-bookings.test.ts`
- **Problem:** `clientId: true` wurde zum `project` Select in `list-assignment-bookings.ts` hinzugefuegt, aber 3 Test-Assertions nicht aktualisiert (pre-existing)
- **Fix:** `clientId: true` in alle 3 Test-Select-Assertions eingefuegt → 67/67 Tests bestanden
### Empfehlungen
## Offene Items (nicht-blockierend)
1. **Bundle Size Monitoring**`three` und `react-force-graph-3d` fuegen ~700KB (gzipped) hinzu. Dank `dynamic import` + `{ ssr: false }` trifft das nur die Computation Graph Seite. Trotzdem: bei der naechsten Bundle-Analyse verifizieren.
Diese stammen aus dem Security-Audit und sind im Backlog (`docs/security-audit-2026-03-15.md`):
2. **E2E-Test** — Aktuell kein Test fuer die neue Seite. Ein Playwright-Smoke-Test (`navigate to /analytics/computation-graph, expect canvas element`) waere sinnvoll.
1. **SSE Event-Filterung** — Events werden global an alle authentifizierten User gebroadcastet, ohne Rollen-/Projekt-Scoping
2. **Rate Limiting** — Kein Rate Limiter auf Auth, Admin-Tests, oder API-Endpunkten
3. **Passwort-Policy** — Nur `min(8)` ohne Komplexitaetsanforderungen
4. **xlsx Parser** — Bekannte Advisories (Prototype Pollution, ReDoS) in `xlsx@0.18.5`
5. **JWT maxAge** — Auth.js Default 30 Tage ohne explizite Konfiguration
6. **Next.js Middleware** — Kein `middleware.ts` fuer serverseitige Route-Protection
## Empfehlungen
1. SSE-Event-Filterung priorisieren — groesstes verbleibendes Datenschutz-Risiko
2. Rate Limiting mit `rate-limiter-flexible` + Redis einbauen (Auth zuerst)
3. `xlsx` durch `exceljs` oder `SheetJS Pro` ersetzen fuer untrusted Parsing
4. `middleware.ts` fuer `/(app)/` Routen einfuegen
3. **Link Formula Labels** — Plan sah Three.js Text-Sprites auf Kanten vor (E3). Die Formeln sind in den Link-Daten vorhanden (`formula` Feld), werden aber aktuell nur bei Hover (indirekt ueber Node-Tooltip) sichtbar. Kann als Follow-up ergaenzt werden.
## Learnings-Vorschlag fuer LEARNINGS.md
```
### Security: protectedProcedure muss dbUser pruefen (2026-03-15)
`protectedProcedure` pruefte nur `session.user`, nicht ob der DB-User noch existiert.
Geloest: `dbUser`-Check in die Middleware eingefuegt. Stale Sessions werden jetzt abgelehnt.
Konsequenz: Alle Test-Caller muessen ein gueltiges `dbUser`-Objekt mitgeben.
```markdown
### react-force-graph-3d in Next.js 15
### Security: IDOR-Checks bei Self-Service-Endpunkten (2026-03-15)
Vacation create/cancel und Entitlement getBalance hatten keine Ownership-Checks.
USER-Rolle konnte Aktionen fuer beliebige Ressourcen ausfuehren.
Pattern: Bei `protectedProcedure`-Endpunkten die eine `resourceId` akzeptieren,
immer `resource.userId === ctx.dbUser.id` pruefen fuer nicht-privilegierte Rollen.
- Muss als `dynamic(() => import("react-force-graph-3d"), { ssr: false })` geladen werden
- React 19 Kompatibilitaet: funktioniert, aber TypeScript-Generics sind loose — `as any` Cast + eslint-disable noetig bei Callbacks (`onNodeClick`, `nodeThreeObject`, `linkColor`)
- Node-Rendering via Canvas-basierte Three.js Sprites (nicht HTML-Overlays) — performanter bei 50+ Knoten
- `warmupTicks={50}` + `cooldownTicks={0}` verhindert die Kraft-Simulation und nutzt stattdessen die fixen `fx/fy/fz` Positionen
```