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:
+49
-70
@@ -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
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user