feat(timeline): add pulse animation for in-flight drag mutations
Allocation bars that have active optimistic overrides (post-drag, awaiting server confirmation) now pulse subtly via animate-pulse. The pending set is derived from the existing optimisticAllocations map keys, requiring no additional state. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,80 +1,102 @@
|
||||
# Plan: Scenario Regression Depth + Housekeeping
|
||||
# Plan: Navigation Route Smoke Test (Ticket #50)
|
||||
|
||||
Stand: 2026-04-02
|
||||
|
||||
---
|
||||
|
||||
# Workstream E: Scenario Regression Depth
|
||||
**Stand: 2026-04-03**
|
||||
|
||||
## Anforderungsanalyse
|
||||
|
||||
Vier Szenario-Module haben **keine direkten Unit-Tests**:
|
||||
**Untersuchungsergebnis:** Die in #50 gemeldeten 404s sind kein echter Code-Defekt.
|
||||
- `analytics/skills` → HTTP 200 (in Docker-Logs bestätigt)
|
||||
- `reports/chargeability` → HTTP 200 (in Docker-Logs bestätigt)
|
||||
- `analytics/insights` → Seite existiert, wurde schlicht noch nicht kompiliert (Next.js dev kompiliert Routen on-demand beim ersten Besuch)
|
||||
- Alle 35 Nav-Hrefs wurden gegen existierende `page.tsx`-Dateien geprüft: **keine toten Links**
|
||||
|
||||
| Datei | Inhalt | Lücke |
|
||||
|-------|--------|-------|
|
||||
| `scenario-shared.ts` | Pure helpers: `roundToTenths`, `getScenarioAvailability`, `collectScenarioSkillSet`, `calculateScenarioEntryHours` | Keine Tests |
|
||||
| `scenario-baseline.ts` | `readProjectScenarioBaseline` — lädt Assignments/Demands, berechnet Kosten/Stunden | Keine Tests |
|
||||
| `scenario-apply.ts` | `applyProjectScenario` — CANCELLED/update/create Branches, appliedCount | Keine Tests |
|
||||
| `scenario-simulation.ts` | `simulateProjectScenario` — Baseline vs. Szenario-Delta, Warnungen, Skill-Coverage | Keine Tests |
|
||||
**Was gebaut wird:** Ein Playwright-Smoke-Test, der bei jedem Lauf alle sichtbaren Sidebar-Destinations (navSections + adminNavEntries) für einen eingeloggten Admin-User überprüft. Verhindert, dass künftige Regressions (nav-Link zu nicht-existierender Route) erst von Usern entdeckt werden.
|
||||
|
||||
Bestehende Coverage: `scenario-router.test.ts` (Auth-Guards), `scenario-procedure-support.test.ts` (Delegation), `assistant-tools-scenarios.test.ts` (1 Integration-Test) — alles Delegation, keine Business-Logik.
|
||||
|
||||
## Betroffene Pakete & Dateien
|
||||
|
||||
| Paket | Datei | Art |
|
||||
|-------|-------|-----|
|
||||
| `packages/api` | `src/__tests__/scenario-shared.test.ts` | create |
|
||||
| `packages/api` | `src/__tests__/scenario-apply.test.ts` | create |
|
||||
| `packages/api` | `src/__tests__/scenario-baseline.test.ts` | create |
|
||||
| `packages/api` | `src/__tests__/scenario-simulation.test.ts` | create |
|
||||
|
||||
## Task-Liste
|
||||
|
||||
- [x] **E-1a:** `scenario-shared.test.ts` — Pure-Helper-Tests (keine Mocks außer resource-capacity)
|
||||
- `roundToTenths`: 0.15 → 0.2, 0.34 → 0.3, ganzer Integer bleibt
|
||||
- `getScenarioAvailability`: null → DEFAULT_AVAILABILITY; valides Objekt wird durchgereicht
|
||||
- `collectScenarioSkillSet`: null → leeres Set; leeres Array → leeres Set; doppelte Skills dedupliziert; lowercase-Normalisierung; leere Strings gefiltert; Mixed case dedupliziert
|
||||
- `calculateScenarioEntryHours`: ohne resourceId → delegiert an `calculateAllocation`; mit resourceId → delegiert an `calculateEffectiveBookedHours`
|
||||
- Mock: `@capakraken/engine/allocation` und `../lib/resource-capacity.js`
|
||||
|
||||
- [x] **E-1b:** `scenario-apply.test.ts` — CRUD-Branch-Tests
|
||||
- NOT_FOUND wenn `project.findUnique` null zurückgibt
|
||||
- `remove: true` + `assignmentId` → `assignment.update` mit `status: "CANCELLED"`, appliedCount = 0 (cancel trifft `continue` vor `created.push`)
|
||||
- `assignmentId` ohne remove → `assignment.update` mit neuen Daten, appliedCount = 1
|
||||
- kein `assignmentId`, kein `resourceId` → Zeile wird übersprungen, appliedCount = 0
|
||||
- kein `assignmentId`, hat `resourceId` → `assignment.create` mit korrektem `dailyCostCents`, appliedCount = 1
|
||||
- Mehrere Changes → korrekter appliedCount summiert
|
||||
|
||||
- [x] **E-1c:** `scenario-baseline.test.ts` — Baseline-Lade-Tests
|
||||
- NOT_FOUND wenn Projekt nicht gefunden
|
||||
- Leeres Projekt (keine Assignments, keine Demands) → `totalCostCents: 0`, `totalHours: 0`, `assignments: []`, `demands: []`
|
||||
- Assignment mit bekanntem `lcrCents` und `hoursPerDay` → `costCents` korrekt berechnet
|
||||
- CANCELLED Assignments werden herausgefiltert (kommen nicht in `baselineAllocations`)
|
||||
- Demands werden korrekt gemappt (kein `costCents`, hat `headcount`, `roleName` aus roleEntity)
|
||||
- `totalCostCents` ist Summe aller Assignment-`costCents`
|
||||
|
||||
- [x] **E-1d:** `scenario-simulation.test.ts` — Simulations-Logik-Tests
|
||||
- NOT_FOUND wenn Projekt nicht gefunden
|
||||
- remove-Change → Assignment fehlt im Scenario-headcount (`delta.headcount < 0`)
|
||||
- Neues Assignment hinzufügen → `delta.headcount > 0`
|
||||
- Budget-Warnung wenn `scenarioCostCents > budgetCents` → warnings enthält "exceeds budget"
|
||||
- Skill-Coverage: Szenario mit mehr Skills → `delta.skillCoveragePct > 100`
|
||||
- Szenario ohne Änderungen aber mit bestehenden Assignments → `delta.costCents = 0`, `delta.hours = 0`
|
||||
|
||||
## Abhängigkeiten
|
||||
|
||||
- E-1a und E-1b können **parallel** geschrieben werden (separate Dateien)
|
||||
- E-1c und E-1d können **parallel** geschrieben werden
|
||||
- Keine Abhängigkeiten zwischen allen vier
|
||||
|
||||
## Akzeptanzkriterien
|
||||
|
||||
- [x] `pnpm test:unit` läuft grün
|
||||
- [x] Alle 4 neuen Test-Dateien existieren mit ≥ 4 Tests jeweils (4 Dateien, 31 Tests, alle grün)
|
||||
**Betroffenes Paket:** `apps/web` (nur Testinfrastruktur, kein Produktionscode)
|
||||
|
||||
---
|
||||
|
||||
# Workstream F: .env.example + Docs Housekeeping
|
||||
## Betroffene Pakete & Dateien
|
||||
|
||||
- [x] **F-1:** `.env.example` committen (commit 1ec56aa — erweitert auf ~85 Zeilen mit vollständiger Dokumentation)
|
||||
- [x] **F-2:** `plan.md` nach Abschluss mit erledigten Tasks aktualisieren
|
||||
| Paket | Dateien | Art der Änderung |
|
||||
|-------|---------|-----------------|
|
||||
| `apps/web` | `e2e/dev-system/nav-smoke.spec.ts` | create |
|
||||
| `apps/web` | `e2e/navigation.spec.ts` | edit — 9 bisher ungetestete Routen hinzufügen |
|
||||
|
||||
---
|
||||
|
||||
## Task-Liste
|
||||
|
||||
- [ ] **Task 1: `e2e/dev-system/nav-smoke.spec.ts` erstellen**
|
||||
|
||||
Neues Spec in der `dev-system`-Suite (läuft gegen Live-Dev-Server auf Port 3100).
|
||||
Nutzt gespeicherten Auth-State (`storageState: .auth/admin.json`) — kein manueller
|
||||
Login nötig, vermeidet Rate-Limiter. Admin-User hat Zugriff auf alle Routen.
|
||||
|
||||
Alle Hrefs als eigenständige Konstante definieren — **NICHT AppShell importieren**
|
||||
(würde Next.js-Infrastruktur in die Test-Runtime einziehen).
|
||||
|
||||
Pro Route: `page.goto(href, { waitUntil: "commit" })` + zwei Assertions:
|
||||
1. `response.status() !== 404`
|
||||
2. `"This page could not be found"` ist nicht sichtbar (max 15 s warten)
|
||||
|
||||
Timeout für die Seite auf `60_000 ms` setzen wegen JIT-Compile-Verzögerung bei
|
||||
noch nicht kompilierten Routen (z.B. `analytics/insights` beim ersten Besuch).
|
||||
|
||||
**Hrefs (Stand AppShell.tsx 2026-04-03):**
|
||||
- Planning: `/dashboard`, `/timeline`, `/allocations`, `/staffing`, `/notifications`
|
||||
- Estimating: `/estimates`, `/admin/rate-cards`, `/admin/effort-rules`, `/admin/experience-multipliers`
|
||||
- Resources: `/resources`, `/projects`, `/roles`
|
||||
- Analytics: `/analytics/skills`, `/reports/chargeability`, `/reports/builder`, `/analytics/computation-graph`, `/analytics/insights`
|
||||
- Time Off: `/vacations/my`, `/vacations`
|
||||
- Account: `/account/security`
|
||||
- Admin: `/admin/blueprints`, `/admin/clients`, `/admin/countries`, `/admin/org-units`, `/admin/utilization-categories`, `/admin/management-levels`, `/admin/imports`, `/admin/calculation-rules`, `/admin/vacations`, `/admin/users`, `/admin/system-roles`, `/admin/settings`, `/admin/notifications`, `/admin/webhooks`, `/admin/activity-log`
|
||||
|
||||
→ Datei: `apps/web/e2e/dev-system/nav-smoke.spec.ts`
|
||||
|
||||
- [ ] **Task 2: `e2e/navigation.spec.ts` erweitern**
|
||||
|
||||
Die bestehende `navigation.spec.ts` im Standard-Test-Suite prüft nur 5 Routen via
|
||||
Sidebar-Click. Einen **separaten Test-Block** `"all nav routes resolve (no 404)"`
|
||||
hinzufügen, der die restlichen regulären Routen (ohne Admin-Routen, da Role-Gate
|
||||
im isolierten Test-Server-Seed unbekannt) per `page.goto` prüft.
|
||||
|
||||
Neue Routen zusätzlich zum bestehenden 5er-Set:
|
||||
`/estimates`, `/roles`, `/analytics/skills`, `/reports/chargeability`,
|
||||
`/reports/builder`, `/analytics/computation-graph`, `/analytics/insights`,
|
||||
`/vacations/my`, `/vacations`, `/account/security`
|
||||
|
||||
Der bestehende Click-Test bleibt unverändert.
|
||||
|
||||
→ Datei: `apps/web/e2e/navigation.spec.ts`
|
||||
|
||||
---
|
||||
|
||||
## Abhängigkeiten
|
||||
|
||||
- Task 1 und Task 2 sind **vollständig unabhängig** — können parallel implementiert werden.
|
||||
- Task 1 setzt gespeicherte Auth-State-Dateien voraus (`e2e/dev-system/.auth/admin.json`).
|
||||
Diese existieren bereits (von vorherigen Runs). Falls nicht: `playwright test --config
|
||||
playwright.dev.config.ts` ohne Spec-Filter — global-setup läuft zuerst.
|
||||
|
||||
---
|
||||
|
||||
## Akzeptanzkriterien
|
||||
|
||||
- [ ] `pnpm --filter @capakraken/web exec playwright test --config playwright.dev.config.ts e2e/dev-system/nav-smoke.spec.ts` → alle 35 Tests grün gegen Live-Dev-Server auf Port 3100
|
||||
- [ ] `pnpm --filter @capakraken/web exec playwright test e2e/navigation.spec.ts` → grün im Standard-Test-Suite
|
||||
- [ ] `pnpm test:unit` → unverändert grün (kein Produktionscode geändert)
|
||||
- [ ] `pnpm --filter @capakraken/web exec tsc --noEmit` → keine neuen Errors
|
||||
- [ ] Gitea #50 mit Analyseergebnis kommentieren und schließen
|
||||
|
||||
---
|
||||
|
||||
## Risiken & offene Fragen
|
||||
|
||||
1. **Slow first-compile:** `analytics/insights` wurde im Dev-Server noch nie kompiliert. Der erste Goto kann 10–15 s dauern (JIT + Batch-Query). Test-Timeout auf `60_000 ms` setzen.
|
||||
|
||||
2. **`/vacations/my` für Admin ohne Resource:** Admin-User `admin@planarchy.dev` hat möglicherweise kein verlinktes Resource-Record. Die Seite rendert dann ein Amber-Warning-Banner — kein 404. Smoke-Test besteht trotzdem, da nur HTTP-Status und "page not found"-Text geprüft werden.
|
||||
|
||||
3. **Role-Gates in Standard-Suite (Task 2):** Der Test-Server-Seed-User `admin@capakraken.dev` muss systemRole ADMIN haben damit Role-gated Routen erreichbar sind. Prüfen via `test-server.mjs` bevor Implementierung.
|
||||
|
||||
4. **Credentials nicht mischen:** `navigation.spec.ts` (Standard-Suite) → `admin@capakraken.dev`. `nav-smoke.spec.ts` (dev-system) → `admin@planarchy.dev` via storageState. Nie kreuzen.
|
||||
|
||||
Reference in New Issue
Block a user