Tasks 21-24 were completed and committed but not marked in plan:
- Task 21: c29cff3 (Login + Club Picker + Dashboard)
- Task 22: 7a2b79a (Docker Compose Full Stack)
- Task 23: 6124557 (Backend Dockerfiles)
- Task 24: 6124557 (Frontend Dockerfiles)
Updated plan to reflect actual completion state.
Implements Tasks 23 & 24: Backend and Frontend Dockerfiles
Backend Dockerfiles:
- Dockerfile.dev: Development with dotnet watch hot reload
- Base: sdk:10.0, installs dotnet-ef tool
- Layer caching: csproj files copied before source
- Entry: dotnet watch run with --no-restore
- Dockerfile: Production multi-stage build
- Build stage: sdk:10.0, restore + build + publish
- Runtime stage: aspnet:10.0-alpine (~110MB)
- Health check: /health/live endpoint
- Non-root: USER app (built-in)
Frontend Dockerfiles:
- Dockerfile.dev: Development with Bun hot reload
- Base: node:22-alpine, installs Bun globally
- Layer caching: package.json + bun.lock before source
- Command: bun run dev
- Dockerfile: Production standalone 3-stage build
- Deps stage: Install with --frozen-lockfile
- Build stage: bun run build → standalone output
- Runner stage: node:22-alpine with non-root nextjs user
- Copies: .next/standalone, .next/static, public
- Health check: Node.js HTTP GET to port 3000
- Entry: node server.js (~240MB)
All Dockerfiles use layer caching optimization and security best practices.
Note: Docker build verification skipped (Docker daemon not running).
Implements Task 19: Task List + Task Detail + Status Transitions UI
New components:
- useTasks hook: TanStack Query hooks (useTasks, useTask, useCreateTask, useUpdateTask)
- Task list page: shadcn Table with status filter, pagination, status badges
- Task detail page: Full task info with valid status transition buttons
- New task form: Create task with title, description, assigneeId, dueDate
Key features:
- Status transitions match backend logic: Open→Assigned→InProgress→Review→Done
- Review status allows back-transition to InProgress (only bidirectional)
- Only valid next states shown as buttons (VALID_TRANSITIONS map)
- Status badge colors: Open=gray, Assigned=blue, InProgress=yellow, Review=red, Done=green
- TanStack Query with automatic cache invalidation on mutations
- Next.js 15+ async params pattern (use() hook)
TDD:
- 3 task list tests (renders rows, status badges, new task button)
- 3 task detail tests (Open→Assigned, InProgress→Review, Review→Done+InProgress)
All tests pass (31/31). Build succeeds.
Implement Task 16: Club + Member API endpoints with MemberSyncService
Services:
- ClubService: GetMyClubsAsync (user's clubs), GetCurrentClubAsync (tenant club)
- MemberService: GetMembersAsync (list), GetMemberByIdAsync, GetCurrentMemberAsync
- MemberSyncService: Auto-creates Member records from JWT on first request
Middleware:
- MemberSyncMiddleware: Runs after auth, calls MemberSyncService
Endpoints:
- GET /api/clubs/me (list user's clubs)
- GET /api/clubs/current (current tenant's club)
- GET /api/members (list members, RLS filtered)
- GET /api/members/{id} (member detail)
- GET /api/members/me (current user's membership)
Tests: 14 integration tests (6 club + 8 member)
- Club filtering by user membership
- Multi-tenant isolation via RLS
- Member auto-sync on first request
- Cross-tenant access blocked
- Role-based authorization
Build: 0 errors, all tests compile
Pattern: TypedResults, RequireAuthorization policies, TDD approach
- Add middleware.ts for route protection (redirects unauthenticated users to /login)
- Add useActiveClub() hook for managing active club context (localStorage + session)
- Add apiClient() fetch wrapper with automatic Authorization + X-Tenant-Id headers
- Configure vitest with jsdom environment and global test setup
- Add comprehensive test coverage: 16/16 tests passing (hooks + API utility)
- Install test dependencies: vitest, @testing-library/react, @vitejs/plugin-react, happy-dom
Task 10 COMPLETE - all acceptance criteria met
- Use consolidated Finbuckle.MultiTenant namespace instead of separate imports
- Switch TenantProvider to use untyped IMultiTenantContextAccessor (Finbuckle 9.x pattern)
- Register TenantDbConnectionInterceptor and SaveChangesTenantInterceptor as singletons
- Add interceptors to DbContext configuration for RLS tenant context support
- Update evidence files for Task 7 and Task 8 verification
- Create SeedDataService in Infrastructure/Seed with idempotent seeding
- Seed 2 clubs: Sunrise Tennis Club, Valley Cycling Club
- Seed 7 member records (5 unique Keycloak test users)
- Seed 8 work items covering all status states
- Seed 5 shifts with date variety (past, today, future)
- Seed shift signups for realistic partial capacity
- Register SeedDataService in Program.cs with development-only guard
- Use deterministic GUID generation from club names
- Ensure all tenant IDs match for RLS compliance
- Track in learnings.md and evidence files for Task 22 QA
- Add docker-compose.yml (v3.9) with postgres:16-alpine and keycloak:26.1 services
- Configure PostgreSQL with separate workclub and keycloak databases
- Setup Keycloak with database backend, admin user, and realm import capability
- Create PostgreSQL init script to provision development databases and users
- Add placeholder realm-export.json for Keycloak realm configuration
- Configure healthchecks and app-network bridge for service discovery
- Document configuration and patterns in learnings.md