7.8 KiB
7.8 KiB
Session Handoff: Historical Booking Constraint + Offline Cache + Booking Draft
Date: 2026-04-21 Session Duration: ~2 hours Session Focus: Enforced historical booking rule, fixed Edge Function error surfacing, implemented offline cache system with Realtime updates, and fixed slot-click navigation from schedule to create page using a booking draft composable. Context Usage at Handoff: ~75%
What Was Accomplished
- Historical booking constraint — tests written and Edge Function enforced →
tests/integration/booking-constraints.test.ts,supabase/functions/create-reservation/index.ts - Error message fix — "Can not book a reservation in the past." is the user-facing message for
historical_booking_not_allowed(422) - Fixed
functions-jsv2 error body parsing —response.dataisnullon non-2xx; body must be read fromresponse.error.context.json()→app/pages/reservations/create.vue - Historical booking uses
end_time— a booking is historical only when itsend_time < now()(notstart_time) - Offline cache system — localStorage, 24h TTL, Realtime-updated →
app/composables/useAppCache.ts,app/composables/useOfflineStatus.ts - Booking draft composable — module-level ref for cross-page state (replaces broken query-param deep-link) →
app/composables/useBookingDraft.ts - Offline indicator — fixed chip in top-right corner in
app/app.vue; Realtime channelapp-cache-syncpatchesslots:,intervals:, andboatscache on DB changes - Schedule page cache integration →
app/pages/schedule.vue:fetchBoatsandfetchSchedulewrite to cache on success, read from cache when offline - Create page booking draft →
app/pages/reservations/create.vue:onIonViewWillEnterreads draft (no async fetch needed), jumps to step 2 with pre-filled boat + slot; also cache-awareloadSlotsfor offline step 1 - CLAUDE.md caching rule documented — "Every table/view read from Supabase must be written to
useAppCacheon success and read from it when offline" — includes pattern, key conventions, Realtime guidance
Exact State of Work in Progress
- Booking draft navigation: complete — clicking a slot in schedule sets draft and routes to
/reservations/create; create page reads draft inonIonViewWillEnterand goes directly to step 2 - Offline cache: complete for
boats,intervals,slots(schedule + create pages); NOT yet applied to other pages (/boat,/reference,/profile, admin pages) - Realtime: subscribed to
reservations,intervals,boatsinapp.vue; if new tables are cached in future, add subscriptions toapp-cache-syncchannel
Decisions Made This Session
- Historical =
end_time < now()BECAUSE a session that started in the past but hasn't ended yet is still bookable — STATUS: confirmed - Query params abandoned for slot navigation BECAUSE
+in ISO timestamps (+00:00) URL-encodes to%2B, which can be decoded as a space by some parsers; async boat fetch adds an auth-timing race;onIonViewWillEnterroute-timing is unreliable — CHOSE module-level reactiveref(booking draft) instead — STATUS: confirmed functions-jsv2 error body is inerror.context, notdata—dataisnullon non-2xx;error.contextis the rawResponse— applies to all error code handling increate.vue— STATUS: confirmed- Cache key for schedule data = ISO week Monday (
cache.weekKey(utcIso)→weekMonday(date)) so desktop (week view) and mobile (day view) share the same cache entries — STATUS: confirmed - Offline = read-only — booking submission is not attempted offline (Edge Function call fails naturally); no explicit offline guard on submit — ASSUMED acceptable
Key Numbers Generated or Discovered This Session
functions-jsversion in use:2.100.0(confirmed fromnode_modules)- Cache TTL: 24 hours (ms:
86_400_000) - Realtime channel name:
app-cache-sync - Error code for historical booking:
historical_booking_not_allowed(HTTP 422) - localStorage key prefix:
cache:(e.g.,cache:boats,cache:slots:2026-04-20,cache:intervals:2026-04-20)
Files Created or Modified
| File Path | Action | Description |
|---|---|---|
app/composables/useOfflineStatus.ts |
Created | Module-level isOnline ref; wires online/offline browser events |
app/composables/useAppCache.ts |
Created | localStorage cache; get (TTL-enforced), peek (stale-ok), set, invalidate, invalidatePrefix, weekKey |
app/composables/useBookingDraft.ts |
Created | Module-level BookingDraft ref; set(boat, startTime, endTime) / take() |
app/app.vue |
Modified | Offline chip (fixed top-right, warning colour); Realtime app-cache-sync channel for reservations, intervals, boats |
app/pages/schedule.vue |
Modified | fetchBoats/fetchSchedule cache-aware; bookSlot sets draft instead of query params |
app/pages/reservations/create.vue |
Modified | onIonViewWillEnter consumes draft (jumps to step 2); loadSlots cache-aware; functions-js v2 error body fix; historical_booking_not_allowed in codeMessages |
supabase/functions/create-reservation/index.ts |
Modified | Historical guard: endDate < now() → admin-only (422 historical_booking_not_allowed); admin skips cert check + booking-limit checks |
tests/integration/booking-constraints.test.ts |
Modified | Added describe('historical booking constraint'): member rejected (422), admin allowed (201), skipper rejected (422) |
CLAUDE.md |
Modified | Added "Offline Cache" section under Architecture with pattern, key conventions, Realtime guidance, and booking-draft pattern |
What the NEXT Session Should Do
- Verify slot-click flow end to end: open schedule page, click an available slot, confirm create page opens at step 2 with the correct boat name and time pre-filled, submit a reservation
- Apply cache pattern to remaining pages:
/boat(boats list),/reference(reference_docs),/profile(member record), admin pages as needed — follow the pattern documented inCLAUDE.md - Decide on the DatePicker replacement (OPEN from previous session):
<DatePicker inline>onapp/pages/index.vue:59is the only PrimeVue usage — options are remove the calendar card, keep PrimeVue, or use native<input type="date"> - Build
app/pages/admin/reservations.vue— admin view of all bookings (file exists, likely scaffolded) - Wire cancel-reservation — OPEN: is a cancel Edge Function planned?
Open Questions Requiring User Input
app/pages/index.vue:59— what replaces<DatePicker inline>? Remove card, keep PrimeVue, or native input? Impacts whether PrimeVue stays in the stack- Is a cancel-reservation Edge Function planned? Impacts backend scope before April 30
- Should offline submission attempts show an explicit "You are offline — cannot book" message rather than a generic network error?
Assumptions That Need Validation
- ASSUMED:
app/pages/admin/reservations.vueis scaffolded but incomplete — verify by reading the file - ASSUMED: offline booking submission failing silently (network error toast) is acceptable — validate with Patrick
- ASSUMED:
onIonViewWillEnterfires before the user can interact with the page — if there is a visible flash of step 1 before the draft is consumed, addstep.value = 2synchronously before theawaitin the handler
Files to Load Next Session
docs/summaries/handoff-2026-04-21-historical-booking-offline-cache.md— this filedocs/summaries/00-project-brief.md— project contextapp/pages/reservations/create.vue— if continuing booking flow workapp/pages/admin/reservations.vue— if building admin bookings viewapp/pages/index.vue— if resolving the DatePicker question