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

22 KiB
Raw Permalink Blame History

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 variablesJWT_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 actionserror-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.