Files
cannamanage/docs/sprint-5/cannamanage-sprint5-plan-review.md
Patrick Plate f42c166329 feat(sprint-5): Phase 2 — React Query API client layer
- @tanstack/react-query with QueryClientProvider in providers/index.tsx
- Typed api-client.ts fetch wrapper with ApiError class + apiDownload
- Service modules: members, distributions, stock, reports, dashboard, portal, staff
- Offline banner component (onlineManager subscription)
- API error boundary with retry button
- Loading skeleton components (card, table, chart, form, dashboard)
- i18n for error/loading states (de/en)
2026-06-12 19:59:41 +02:00

289 lines
22 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# CannaManage Sprint 5 Plan — Multi-Persona Review Panel
**Date:** 2026-06-12
**Reviewed Document:** `docs/sprint-5/cannamanage-sprint5-plan.md` (v2, ~840 lines)
**Review Method:** 6-persona stakeholder simulation, scoring on 4 dimensions (0100%)
**Iteration:** 1
---
## v2 Decisions Incorporated
The Sprint 5 plan v2 incorporates all Q&A decisions from the planning session:
1.**@tanstack/react-query** — caching, refetch, optimistic updates (Q1)
2.**Per-component loading** — independent card/table loading, no full-page blocking (Q2)
3.**Stale-while-revalidate + "Offline" banner** — graceful degradation (Q3)
4.**Full CRUD staff management** — list, invite, edit perms, revoke (Q4)
5.**Dual seed data strategy** — SQL for dev/test + API-driven for system E2E (Q5)
6.**Next.js 15.2.8 → 15.5.18 upgrade** — addresses 8+ Snyk CVEs in Phase 1 (Bonus)
---
## 1. 👤 Club Member (End User)
*"I'm a regular member of a cannabis social club. I want to see my quota, pick up my cannabis, and check my history — now with real data instead of fake numbers."*
### Findings
| # | Type | Observation |
|---|------|-------------|
| 1 | ✅ Positive | **Real-time quota data** — my portal dashboard now shows actual usage from the database, not mock numbers. I can trust the "28g remaining" display because it's coming from the real backend with server-authoritative calculations. |
| 2 | ✅ Positive | **Stale-while-revalidate** — if my phone briefly loses signal in the club, I still see my last-known quota instead of a blank page. The "Offline" banner is unobtrusive. |
| 3 | ✅ Positive | **Per-component loading** — when I open the portal, I see my quota radial load first (fast), then history loads separately. No frozen full-page spinner. |
| 4 | ✅ Positive | **Distribution history is live** — I can see a distribution appear in my history immediately after the staff records it. No manual refresh needed. |
| 5 | ⚠️ Minor | **No push notification for distributions** — I still won't get notified when a distribution is recorded against my account. I have to check manually. (Acknowledged as Sprint 6+ scope.) |
| 6 | ✅ Positive | **Portal auth is explicitly planned** — Phase 5 specifically calls out wiring the portal login flow with session-based auth. My separate login experience is preserved. |
### Scores
| Dimension | Score | Rationale |
|-----------|-------|-----------|
| Precision | 92% | Portal integration is explicitly scoped in Phase 5 with specific endpoint mappings (`/portal/dashboard`, `/portal/history`). Loading patterns and offline handling are precisely defined. |
| Correctness | 94% | React Query's stale-while-revalidate behavior correctly serves cached data during network issues. Quota calculations remain server-authoritative. Session-based portal auth is appropriate for member-facing UI. |
| Usability | 88% | Per-component loading and offline resilience significantly improve my experience. Only gap: no proactive notifications when distributions are recorded or quotas reset. |
| Usefulness | 90% | The transition from mock to real data is exactly what I need — my portal becomes trustworthy and useful for tracking my actual consumption. |
**Composite Score: 91%**
### Remaining Gaps (minor, Sprint 6+)
- Push/email notifications for distributions and quota resets
- PWA manifest for mobile home-screen shortcut
---
## 2. 🏢 Club Owner / Vorstand (Business Owner)
*"I run the Anbauvereinigung. I need staff management, real reports for the Behörde, and confidence that the system works end-to-end."*
### Findings
| # | Type | Observation |
|---|------|-------------|
| 1 | ✅ Positive | **Full CRUD staff management** — Phase 6 covers list, invite, edit permissions, revoke. All 8 granular permissions are specified. This is exactly what I need to delegate work to my team without giving everyone full access. |
| 2 | ✅ Positive | **Real report downloads** — Phase 5 wires PDF/CSV report generation from actual database data. Monthly reports for the Behörde will contain real distribution records, not mock data. |
| 3 | ✅ Positive | **System test harness** — Phase 7 proves the full stack works end-to-end with a deterministic test flow. This gives me confidence that deployments won't break critical workflows. |
| 4 | ✅ Positive | **Docker Compose full stack** — I can run the entire system locally with one command for demos to my Vorstand or to show the Behörde during audits. |
| 5 | ✅ Positive | **Seed data for dev/test** — the dual strategy (SQL + API-driven) means I can quickly spin up a realistic environment for training new staff. |
| 6 | ⚠️ Minor | **Staff activity log deferred** — I can't yet see *what* actions my staff performed (who recorded which distribution). Deferred to Sprint 6. |
| 7 | ⚠️ Minor | **Club settings UI still pending** — email whitelist, prevention officer limits, and other club-level configuration isn't in Sprint 5 scope. |
| 8 | ✅ Positive | **Permission chips are color-coded** — the plan specifies visual badges per permission, making it easy to scan who has what access at a glance. |
### Scores
| Dimension | Score | Rationale |
|-----------|-------|-----------|
| Precision | 93% | Staff management is thoroughly specified: 8 named permissions, invite flow, pending status handling, revoke confirmation. API endpoints are concrete. |
| Correctness | 91% | Permission model matches the existing `StaffPermission` enum in the backend. RBAC enforcement (403 for unauthorized) is explicitly tested. Report generation uses established PDF/CSV generation code. |
| Usability | 85% | Full CRUD staff management covers my daily needs. Missing activity log means I can't audit staff behavior yet. Club settings require direct backend config. |
| Usefulness | 92% | This sprint delivers the #1 missing piece: staff management. Combined with real reports and system tests, I can now operate the club with confidence. |
**Composite Score: 90%**
### Remaining Gaps (Sprint 6)
- Staff activity log (who did what, when)
- Club settings UI (email whitelist, prevention officer limits)
---
## 3. 💻 Developer (Technical Implementer)
*"I'm the one building this. Is the integration architecture sound? Are dependencies clear? Is the testing strategy robust?"*
### Findings
| # | Type | Observation |
|---|------|-------------|
| 1 | ✅ Positive | **React Query architecture is well-designed** — two-layer client (server-side `apiServer()` + client-side `apiFetch()`) with Next.js rewrite proxy keeps auth tokens server-side. Clean separation of concerns. |
| 2 | ✅ Positive | **Service hooks pattern is consistent** — every domain gets the same structure: `useQuery` for reads, `useMutation` with `invalidateQueries` for writes. Easy to replicate across 7 service files. |
| 3 | ✅ Positive | **Next.js upgrade in Phase 1** — doing the security upgrade *before* integration work prevents debugging whether issues come from the upgrade or new code. Smart sequencing. |
| 4 | ✅ Positive | **Docker Compose is production-like** — multi-stage Maven build, PostgreSQL health checks, proper service dependencies. Build cache optimization mentioned for developer experience. |
| 5 | ✅ Positive | **Error handling is comprehensive** — API error → status-specific German toast message mapping is precisely defined (401/403/409/500). Offline detection returns "Verbindungsfehler" toast. |
| 6 | ✅ Positive | **Dual test strategy preserved** — mock E2E tests (<30s) for rapid iteration, system tests (minutes) for full-stack confidence. Both have clear value and separate configs. |
| 7 | ⚠️ Minor | **No API versioning strategy discussed** — if backend DTOs change, there's no contract test or OpenAPI schema validation to catch frontend/backend drift. Currently relies on manual verification during Phase 3. |
| 8 | ✅ Positive | **Risk assessment is realistic** — acknowledges Docker build slowness, CORS container issues, Playwright startup flakiness, and provides concrete mitigations for each. |
| 9 | ✅ Positive | **Seed data with `ON CONFLICT DO NOTHING`** — idempotent SQL ensures repeatable migrations don't fail on re-runs. Correct Flyway pattern. |
| 10 | ⚠️ Minor | **Optimistic update complexity** — Phase 4 mentions optimistic updates for distributions, but doesn't detail rollback behavior when the backend rejects (e.g., quota exceeded after optimistic decrement). React Query's `onMutate`/`onError` rollback pattern should be specified. |
### Scores
| Dimension | Score | Rationale |
|-----------|-------|-----------|
| Precision | 91% | Code examples for every pattern (React Query, CORS, Docker, error handling, seeds). File paths are explicit. Only missing: optimistic update rollback detail. |
| Correctness | 93% | Architecture decisions are technically sound. React Query + Next.js rewrite proxy is the established pattern. Docker multi-stage build is correct. Flyway repeatable migration is appropriate for seed data. |
| Usability | 88% | Developer experience is considered: DevTools in dev, skeleton components pre-built, one-command Docker startup. Phase sequencing is logical. Slightly high effort estimate (10.5 days single worker) may compress. |
| Usefulness | 92% | This plan is immediately actionable. Every phase has acceptance criteria checkboxes. Directory structure shows exact file placement. Minimal ambiguity in implementation order. |
**Composite Score: 91%**
### Remaining Gaps (nice-to-have)
- OpenAPI contract tests or schema validation between frontend types and backend DTOs
- Explicit optimistic update rollback pattern documentation
---
## 4. 🛡️ Compliance Officer (CanKG / BfArM Regulatory)
*"I ensure the system meets CanKG regulatory requirements. Distribution records must be immutable, quotas enforced, and audit trails available."*
### Findings
| # | Type | Observation |
|---|------|-------------|
| 1 | ✅ Positive | **Quota enforcement is server-authoritative** — the plan explicitly routes quota checks through the backend (`GET /compliance/quota/{memberId}`), not client-side calculations. The 409 response for exceeded limits prevents frontend manipulation. |
| 2 | ✅ Positive | **Under-21 differentiation preserved** — Phase 4 acceptance criteria explicitly states "Under-21 members see 30g monthly limit enforced." The reduced quota is backend-enforced. |
| 3 | ✅ Positive | **System test validates compliance flow** — the test harness (Phase 7) includes a distribution → quota update verification step, proving the enforcement pipeline works end-to-end. |
| 4 | ✅ Positive | **Real PDF reports for Behörde** — the monthly report is generated from actual database records via the established `PdfReportGenerator`. No more mock data in regulatory documents. |
| 5 | ✅ Positive | **Staff permission model controls access** — only users with `RECORD_DISTRIBUTIONS` permission can create distributions. `MANAGE_COMPLIANCE` controls compliance dashboard access. Least-privilege principle applied. |
| 6 | ⚠️ Minor | **No mention of distribution record immutability in Sprint 5** — Sprint 4 established audit trail and 🔒 indicators. Sprint 5 should confirm these survive the integration (i.e., the real backend enforces immutability, not just the frontend). |
| 7 | ✅ Positive | **Seed data includes realistic compliance scenarios** — 5 members with varying ages (including one born 2005 = under-21), 3 batches with different strains. Good for testing age-based quota differentiation. |
### Scores
| Dimension | Score | Rationale |
|-----------|-------|-----------|
| Precision | 88% | Quota enforcement endpoints and error codes are precisely specified. Under-21 handling is explicit. Minor gap: immutability guarantee from backend not re-confirmed for Sprint 5. |
| Correctness | 92% | Server-authoritative quota checks with 409 response is the correct enforcement pattern. Staff permissions align with CanKG operational requirements. Age calculation from `date_of_birth` in seed data is testable. |
| Usability | 86% | Compliance officers benefit from real reports and system tests proving the workflow. Missing: no dedicated compliance dashboard enhancements in Sprint 5 (existing dashboard from Sprint 3 carries forward). |
| Usefulness | 90% | The transition to real data makes compliance reporting meaningful. System tests provide regulatory confidence. Staff permissions enable proper access control documentation for Behörde audits. |
**Composite Score: 89%**
### Remaining Gaps (Sprint 6)
- Explicit immutability confirmation at the API level (backend `@PreAuthorize` + DB trigger or soft-delete pattern)
- Monthly report auto-sealing (cryptographic timestamp)
- Compliance dashboard enhancements (violation alerts, trend charts)
---
## 5. 🔒 Security Auditor
*"I review the system for security vulnerabilities. Authentication, authorization, data protection, and secure communication are my focus."*
### Findings
| # | Type | Observation |
|---|------|-------------|
| 1 | ✅ Positive | **Next.js upgrade 15.2.8 → 15.5.18** — proactively addresses 8+ known CVEs including SSRF, authentication bypass, and resource exhaustion. Doing this in Phase 1 before integration work is the correct priority. |
| 2 | ✅ Positive | **JWT stays server-side** — the proxy architecture (`/api/backend/*` rewrite) means JWT tokens never touch the browser. Client-side fetches use `credentials: "include"` with session cookies only. Reduced token theft surface. |
| 3 | ✅ Positive | **CORS configuration is restrictive** — only `localhost:3000` and `frontend:3000` (Docker) are whitelisted. `allowCredentials: true` with explicit origins (not `*`). `maxAge: 3600L` limits preflight cache. |
| 4 | ✅ Positive | **Permission-based authorization** — 8 granular permissions with enforcement at API level (403 for insufficient permissions). Only ADMIN + MANAGE_STAFF holders can modify staff. Defense in depth. |
| 5 | ✅ Positive | **Error messages don't leak internals** — the `useApiErrorHandler` maps status codes to generic German messages. No stack traces, no internal paths, no SQL errors exposed to the client. |
| 6 | ⚠️ Minor | **Seed data contains plaintext password placeholders**`$2a$10$...bcrypt...` in the SQL is obviously a placeholder, but the plan should note that real bcrypt hashes must be generated during implementation. Password `admin123` is acceptable for test-seed profile only. |
| 7 | ✅ Positive | **Docker secrets in environment variables**`JWT_SECRET` and `POSTGRES_PASSWORD` are in docker-compose with clear "change-in-prod" suffixes. The plan acknowledges these are dev-only values. |
| 8 | ⚠️ Minor | **No rate limiting mentioned** — the API endpoints (especially `/auth/login`, `/staff/invite`) should have rate limiting to prevent brute-force attacks. Not Sprint 5 scope but worth noting. |
| 9 | ✅ Positive | **Portal uses session auth (not JWT)** — member portal correctly uses a separate auth mechanism (session cookies + CSRF) appropriate for public-facing user interfaces. |
| 10 | ✅ Positive | **System test validates auth flows** — the test harness logs in as both admin and member, confirming both auth paths work correctly and are isolated from each other. |
### Scores
| Dimension | Score | Rationale |
|-----------|-------|-----------|
| Precision | 90% | CORS config is exact (origins, methods, headers, maxAge). Auth architecture is clearly layered (server-side JWT, client-side session). Permission model is enumerated. |
| Correctness | 92% | Next.js upgrade addresses real CVEs. Server-side token handling is the security best practice. CORS with explicit origins + credentials is correctly restrictive. bcrypt for passwords is appropriate. |
| Usability | 87% | Security measures don't impede development (CORS allows localhost, DevTools in dev mode, test-seed profile for easy setup). Good security/DX balance. |
| Usefulness | 91% | Proactive CVE remediation, server-side token architecture, and granular permissions provide a solid security foundation. Rate limiting and 2FA are reasonable Sprint 6+ deferrals. |
**Composite Score: 90%**
### Remaining Gaps (Sprint 6+)
- Rate limiting on auth endpoints
- 2FA (TOTP) for admin accounts
- CSRF token handling for portal session auth (not explicitly mentioned)
- Content Security Policy headers (carried forward from Sprint 4)
---
## 6. 🎨 UX Designer
*"I focus on user experience, interaction design, accessibility, and visual consistency across the application."*
### Findings
| # | Type | Observation |
|---|------|-------------|
| 1 | ✅ Positive | **Per-component skeleton loaders** — the plan includes specific skeleton components (`skeleton-card.tsx`, `skeleton-table.tsx`) that show shimmer during loading. This is better than a full-page spinner — users see progressive content appear. |
| 2 | ✅ Positive | **Offline banner is non-blocking** — "Stale-while-revalidate + banner" means users can still interact with cached data while the banner communicates status. No modal or blocking overlay. |
| 3 | ✅ Positive | **Error states have retry actions**`error-state.tsx` component includes a retry button. Users aren't stuck — they can attempt recovery without navigating away. |
| 4 | ✅ Positive | **Permission chips are color-coded** — staff permissions displayed as visual badges make the permission grid scannable at a glance. Pattern is consistent with existing UI components (shadcn Badge). |
| 5 | ✅ Positive | **i18n for staff management** — Phase 6 explicitly includes adding German + English strings. Language consistency maintained across the new feature. |
| 6 | ⚠️ Minor | **No loading animation specification** — skeleton components are mentioned but no design tokens for shimmer timing, color, or animation duration. Should match existing shadcn skeleton defaults. |
| 7 | ⚠️ Minor | **Optimistic update UX not detailed** — when a distribution is recorded optimistically, what does the user see? Immediate list update with a subtle "saving..." indicator? Or just instant appearance? The undo pattern for failed optimistic updates needs visual design. |
| 8 | ✅ Positive | **Toast messages are in German** — error toasts use natural German ("Sitzung abgelaufen", "Kontingent überschritten", "Verbindungsfehler"). Consistent with the i18n-first approach from Sprint 4. |
| 9 | ✅ Positive | **Staff invite dialog with checkboxes** — the permission editor uses checkbox grid (8 permissions), which is the appropriate control for multi-select binary options. Familiar pattern. |
| 10 | ⚠️ Minor | **No empty state illustrations** — empty states get messages but no visual illustrations. Sprint 4 defined empty state messages; Sprint 5 should carry them forward with real API responses. |
### Scores
| Dimension | Score | Rationale |
|-----------|-------|-----------|
| Precision | 87% | Loading patterns (skeletons, error state, retry) are specified as components. Toast messages have exact copy. Minor gap: no animation timing or optimistic update visual feedback details. |
| Correctness | 90% | Per-component loading is the UX best practice for data-heavy dashboards. Stale-while-revalidate preserves user context during connectivity issues. Checkbox grid for permissions is appropriate. |
| Usability | 86% | Progressive loading, offline resilience, and German error messages create a polished experience. Gaps: optimistic update visual feedback and empty state illustrations not specified. |
| Usefulness | 88% | The UX improvements make the transition from mock to real data transparent to users. Loading and error states prevent confusion. Staff management UI follows established patterns. |
**Composite Score: 88%**
### Remaining Gaps (nice-to-have)
- Optimistic update visual feedback pattern (saving indicator, rollback animation)
- Empty state illustrations (consistent with shadcn/ui style)
- Loading animation design tokens (shimmer timing, colors)
- Accessibility audit for new staff management components (focus management in dialogs)
---
## Summary & Composite Scores
| # | Persona | Composite Score | Top Concern |
|---|---------|-----------------|-------------|
| 1 | 👤 Club Member | **91%** | No push notifications for distributions |
| 2 | 🏢 Club Owner | **90%** | Staff activity log deferred |
| 3 | 💻 Developer | **91%** | Optimistic update rollback pattern unspecified |
| 4 | 🛡️ Compliance Officer | **89%** | Immutability re-confirmation at API level |
| 5 | 🔒 Security Auditor | **90%** | Rate limiting on auth endpoints |
| 6 | 🎨 UX Designer | **88%** | Optimistic update visual feedback unspecified |
### Overall Score: **90%** ✅
**Verdict: APPROVED — exceeds 85% threshold on first pass.**
---
## Dimension Averages
| Dimension | Average | Min | Max |
|-----------|---------|-----|-----|
| **Precision** | 90% | 87% (UX) | 93% (Owner) |
| **Correctness** | 92% | 90% (UX) | 94% (Member) |
| **Usability** | 87% | 85% (Owner) | 88% (Dev/Member) |
| **Usefulness** | 91% | 88% (UX) | 92% (Dev/Owner) |
---
## Consolidated Improvement Suggestions (Non-Blocking)
These are recommendations for implementation-time refinement, not plan revisions:
| # | Category | Suggestion | Priority |
|---|----------|------------|----------|
| 1 | Technical | Add explicit optimistic update rollback pattern (React Query `onMutate`/`onError`) | Medium |
| 2 | Security | Add rate limiting to `/auth/login` and `/staff/invite` endpoints | Medium |
| 3 | Compliance | Re-confirm distribution immutability at API level (not just UI lock icons) | Medium |
| 4 | UX | Define visual feedback for optimistic updates (saving indicator, error rollback) | Low |
| 5 | Technical | Consider OpenAPI schema validation for frontend/backend DTO contract | Low |
| 6 | UX | Carry forward empty state illustrations from Sprint 4 design system | Low |
| 7 | Security | Document CSRF token handling for portal session auth | Low |
---
## Comparison with Sprint 4 Review
| Metric | Sprint 4 (Iteration 1) | Sprint 4 (Iteration 2) | Sprint 5 (This Review) |
|--------|------------------------|------------------------|------------------------|
| Overall Score | 78% | 88% | **90%** |
| Iterations Needed | 2 | — | **1** ✅ |
| Blocking Gaps | 8 | 0 | **0** |
| Non-Blocking Gaps | 5 | 3 | **7** (minor) |
Sprint 5 plan achieves a higher first-pass score than Sprint 4's final iteration, demonstrating that learnings from the Sprint 4 review process were incorporated into the planning methodology. All non-blocking gaps are implementation-time refinements rather than plan-level deficiencies.