fix(edge-fn): replace getClaims with adminClient.auth.getUser(token)
fix(edge-fn): use user.id instead of claims.sub; fixes 500s and false cert_required fix(migrations): drop broad reservations SELECT policy; add reservation_slots view with security_invoker=false fix(tests): correct weekSlot() keys from start/end to start_time/end_time fix(tests): spread overlap test slots across separate ISO weeks fix(tests): update e2e assertion to match actual authenticated home text fix(app): hide IonMenu before user is authenticated feat(dx): add test:all script running unit, integration, and e2e in sequence docs(claude-md): document SELinux fix, Edge Function auth pattern, security_invoker behaviour
This commit is contained in:
135
docs/archive/handoffs/handoff-2026-04-20-reservations.md
Normal file
135
docs/archive/handoffs/handoff-2026-04-20-reservations.md
Normal file
@@ -0,0 +1,135 @@
|
||||
# Session Handoff: Boat Reservation System
|
||||
**Date:** 2026-04-20
|
||||
**Session Duration:** ~3 hours
|
||||
**Session Focus:** Implement full boat reservation system: member booking flow, admin slot/template management, configurable booking rules via Edge Function + DB, and integration tests
|
||||
**Context Usage at Handoff:** High
|
||||
|
||||
## What Was Accomplished
|
||||
|
||||
1. **DB migration 1: Overlap constraint + cert trigger** → `supabase/migrations/20260420115333_reservations_overlap_and_cert_check.sql`
|
||||
- `btree_gist` extension enabled
|
||||
- `EXCLUDE USING gist (boat_id WITH =, tstzrange(start_time, end_time, '[)') WITH &&)` on `reservations`
|
||||
- `enforce_reservation_cert_check()` trigger (BEFORE INSERT) — raises exception if `member.certifications` does not contain all `boat.required_certs`
|
||||
- `member_has_cert_for_boat(uuid, uuid)` helper function
|
||||
|
||||
2. **DB migration 2: Booking rules + RBAC** → `supabase/migrations/20260420132336_booking_rules_and_rbac.sql`
|
||||
- `booking_config` table (key/value, admin-editable) — RLS: authenticated read, admin write
|
||||
- `holidays` table (date, name) — RLS same as above
|
||||
- `is_weekend_or_holiday(date)` DB helper function
|
||||
- Dropped `"Users can create own reservations"` RLS policy — direct INSERT blocked for `authenticated` role
|
||||
- `reservation_slots` view (security_invoker=true) — exposes only `id, boat_id, start_time, end_time, status`
|
||||
- Default config seeded: max_sessions_per_week=2, max_weekend_sessions_per_period=1, weekend_period_weeks=2, open_session_advance_hours=24
|
||||
- 2026 Ontario sailing-season holidays seeded (Victoria Day through Thanksgiving)
|
||||
|
||||
3. **Edge Function: create-reservation** → `supabase/functions/create-reservation/index.ts`
|
||||
- POST endpoint enforcing: cert check, boat availability, weekly pre-booking limit, weekend/holiday limit, open-session window bypass
|
||||
- Reads all rule parameters from `booking_config` at runtime (no redeployment needed for config changes)
|
||||
- Uses service_role key for insert (bypasses RLS); caller authenticated via JWT in Authorization header
|
||||
- DB overlap exclusion constraint + cert trigger remain as final safety net
|
||||
- Error response shape: `{ error: { code: string, message: string } }` with specific codes: `cert_required`, `slot_taken`, `booking_limit_weekly`, `booking_limit_weekend`, `boat_unavailable`
|
||||
|
||||
4. **Member reservation creation page** → `app/pages/reservations/create.vue`
|
||||
- Step 1: 14-day horizontal date strip → available slots grouped by boat (reads `intervals` + `reservation_slots` view)
|
||||
- Boats the member is not certified for shown with warning and slots disabled
|
||||
- Step 2: Confirm slot → reason (select) + comment (textarea) → calls Edge Function
|
||||
- Error handling maps all Edge Function error codes to user-friendly messages
|
||||
|
||||
5. **Admin: Manage Slots page** → `app/pages/admin/intervals.vue`
|
||||
- 14-day date strip + per-boat accordion showing intervals
|
||||
- Inline boat out-of-service toggle (IonToggle → updates `boats.booking_available`)
|
||||
- Apply template dropdown per boat (creates intervals from `interval_templates.time_tuples`)
|
||||
- Add Slot modal (boat select + time range inputs)
|
||||
- Delete slot (trash button per row)
|
||||
|
||||
6. **Admin: Interval Templates page** → `app/pages/admin/templates.vue`
|
||||
- Full CRUD: create/edit (modal with dynamic time-tuple list) / delete (IonAlert confirm)
|
||||
- Time tuples displayed as chips showing HH:MM–HH:MM
|
||||
|
||||
7. **Admin: Booking Rules config page** → `app/pages/admin/config.vue`
|
||||
- Editable numeric fields for all 4 config keys
|
||||
- Holidays list with add/delete
|
||||
- Save button with dirty tracking
|
||||
|
||||
8. **Nav wired up** → `app/app.vue`
|
||||
- Boatswain+Admin: "Manage Slots" → `/admin/intervals`, "Templates" → `/admin/templates`
|
||||
- Admin only: "Booking Rules" → `/admin/config`
|
||||
- New icons added: `layersOutline`, `settingsOutline`
|
||||
|
||||
9. **Types updated** → `app/types/supabase.ts`
|
||||
- Added `booking_config`, `holidays` to `Database.public.Tables`
|
||||
- Added `reservation_slots` to `Database.public.Views`
|
||||
|
||||
10. **Integration tests** → `tests/integration/booking-constraints.test.ts`
|
||||
- 6 describe blocks: overlap constraint, certification check, out-of-service, weekly limit, weekend limit, open-session window bypass, RBAC visibility
|
||||
- Each creates isolated test users/boats, cleans up in afterAll
|
||||
- Runs against local Supabase + Edge Functions via `yarn test:integration`
|
||||
- Requires: `SUPABASE_SERVICE_ROLE_KEY`, `SUPABASE_KEY` env vars; Edge Functions served with `npx supabase functions serve`
|
||||
|
||||
## Exact State of Work in Progress
|
||||
|
||||
- Edge Function not yet tested end-to-end in browser (requires `npx supabase functions serve`)
|
||||
- Integration tests written but not yet run — need `supabase functions serve` running
|
||||
- Adjacent-session double-booking rule (Rule 6 from guidelines) NOT YET IMPLEMENTED — marked OPEN below
|
||||
|
||||
## Decisions Made This Session
|
||||
|
||||
- **Option C hybrid architecture** BECAUSE: rule logic in TypeScript (maintainable, no SQL migrations for rule changes), DB constraints for integrity invariants (overlap exclusion, cert check) that must be atomic and bypass-proof — STATUS: confirmed
|
||||
- **Direct INSERT locked for authenticated role** BECAUSE: members must go through Edge Function to enforce booking rules; admins retain direct access via "Admins can manage all reservations" policy — STATUS: confirmed
|
||||
- **`reservation_slots` view** BECAUSE: column-level RBAC — members can see that a slot is taken (boat/time/status) without seeing user_id, reason, comment, member_ids of other members' bookings — STATUS: confirmed
|
||||
- **`useSupabaseClient() as any`** in all new admin pages BECAUSE: `useSupabaseClient<Database>()` generic does not propagate through mutations in this Nuxt/@nuxtjs/supabase v1.5 setup; existing codebase uses cast pattern — STATUS: confirmed workaround
|
||||
- **Open-session bookings counted separately** BECAUSE: Rule 7 says "pre-book 2 sessions per week" — bookings made within `open_session_advance_hours` of start time bypass the weekly and weekend limits; pre-booking count inferred from `(start_time - created_at) > advance_hours` — STATUS: confirmed
|
||||
|
||||
## Key Numbers Generated or Discovered This Session
|
||||
|
||||
- Default max_sessions_per_week: **2**
|
||||
- Default max_weekend_sessions_per_period: **1**
|
||||
- Default weekend_period_weeks: **2**
|
||||
- Default open_session_advance_hours: **24**
|
||||
- Period epoch (fixed): **2026-01-05** (Monday of ISO week 2, 2026)
|
||||
- Holidays seeded: **5** (Victoria Day, Canada Day, Civic Holiday, Labour Day, Thanksgiving)
|
||||
- Overlap constraint type: `tstzrange(start_time, end_time, '[)')` — half-open interval (end exclusive)
|
||||
- Integration test describe blocks: **6**
|
||||
- Migration files created this session: **2**
|
||||
|
||||
## Files Created or Modified
|
||||
|
||||
| File Path | Action | Description |
|
||||
|-----------|--------|-------------|
|
||||
| `supabase/migrations/20260420115333_reservations_overlap_and_cert_check.sql` | Created | Overlap exclusion constraint, cert check trigger, member_has_cert_for_boat() |
|
||||
| `supabase/migrations/20260420132336_booking_rules_and_rbac.sql` | Created | booking_config, holidays tables, is_weekend_or_holiday(), reservation_slots view, drop direct INSERT policy |
|
||||
| `supabase/functions/create-reservation/index.ts` | Created | Edge Function — all booking rule enforcement in TypeScript |
|
||||
| `app/pages/reservations/create.vue` | Created | Member reservation creation: date strip → slot picker → confirm form → Edge Function call |
|
||||
| `app/pages/admin/intervals.vue` | Created | Admin interval management: date strip, per-boat slot list, service toggle, template apply |
|
||||
| `app/pages/admin/templates.vue` | Created | Admin interval template CRUD |
|
||||
| `app/pages/admin/config.vue` | Created | Admin booking rules config: numeric params + holidays management |
|
||||
| `app/app.vue` | Modified | Added nav links for /admin/intervals, /admin/templates, /admin/config; added layersOutline, settingsOutline icons |
|
||||
| `app/types/supabase.ts` | Modified | Added booking_config, holidays tables; reservation_slots view |
|
||||
| `tests/integration/booking-constraints.test.ts` | Created | Integration tests for all booking constraints |
|
||||
|
||||
## What the NEXT Session Should Do
|
||||
|
||||
1. **Run integration tests**: `npx supabase functions serve` in one terminal, then `yarn test:integration` — expect all 6 describe blocks to pass; fix any failures before proceeding
|
||||
2. **Implement adjacent-session rule (Rule 6)**: A member may book a second adjoining session for the same boat on the day of sail only if no other member has booked it. Add check in Edge Function: if `(start_time - now) <= open_session_advance_hours AND booking is adjacent to caller's existing booking for same boat AND no other user has the adjacent slot` → allow double-booking. Otherwise, a member booking an adjacent session is subject to normal rules.
|
||||
3. **Build admin reservations view**: `/admin/reservations` — list all upcoming reservations with boat/time/member/status; ability to confirm, cancel, or modify
|
||||
4. **Test the full member booking flow in browser** — run `yarn dev`, create test intervals via admin, attempt to book as member, verify Edge Function error messages surface correctly
|
||||
|
||||
## Open Questions Requiring User Input
|
||||
|
||||
- [ ] **Adjacent-session double-booking (Rule 6)** — exact logic needed: does "day of sail" mean within `open_session_advance_hours`? Does the system automatically allow it if the adjacent slot is free, or does the member explicitly opt in? Impacts Edge Function logic
|
||||
- [ ] **`useSupabaseClient` typing** — should we investigate properly typing the client (e.g., by configuring `supabase.types` in nuxt.config.ts to point at `types/supabase.ts`)? Currently all new pages use `as any`. Impacts type safety
|
||||
- [ ] **Crew requirement (Rule 8)** — "must have at least one experienced crew member certified as helmsperson on board." Can this be enforced in the app? Members would need to name their crew at booking time. Currently not implemented. Impacts reservation form UX
|
||||
- [ ] **PCOC / Basic Cruising cert codes** — what strings go in `boats.required_certs` and `members.certifications`? (e.g., `'pcoc'`, `'cya-basic-cruising'`?) Impacts cert check behavior
|
||||
|
||||
## Assumptions That Need Validation
|
||||
|
||||
- ASSUMED: `npx supabase functions serve` is available in local dev and tests will reach `http://localhost:54321/functions/v1/create-reservation` — validate by running tests
|
||||
- ASSUMED: `tstzrange(start_time, end_time, '[)')` half-open intervals match the club's intent (booking a morning slot 09:00–12:30 does not block a 12:30 afternoon slot) — validate with Patrick
|
||||
- ASSUMED: ISO week (Monday–Sunday) is the correct "week" definition for the 2/week limit — validate with club guidelines
|
||||
- ASSUMED: 2-week period epoch of 2026-01-05 aligns correctly with the club's alternating weekend schedule — validate with program administrator
|
||||
|
||||
## Files to Load Next Session
|
||||
|
||||
- `supabase/functions/create-reservation/index.ts` — to debug test failures or extend with adjacent-session rule
|
||||
- `tests/integration/booking-constraints.test.ts` — to run and review test results
|
||||
- `app/pages/reservations/create.vue` — to test browser flow and fix any UX issues
|
||||
- `app/types/supabase.ts` — if adding new tables or fixing type issues
|
||||
Reference in New Issue
Block a user