feat: Enhance reservation functionality

This commit is contained in:
2026-04-22 10:23:22 -04:00
parent 7f1e82acc2
commit 534d66c774
25 changed files with 1236 additions and 91 deletions

View File

@@ -31,6 +31,26 @@ You work with Patrick, a Solutions Architect, on the OYS Borrow a Boat app (oysq
- `app/middleware/auth.ts` — global auth guard via useSupabaseUser()
- Auth pages use `definePageMeta({ layout: false })` (effectively a no-op with IonRouterOutlet, but documents intent)
### Ionic + Nuxt Watch-outs
Source: https://ionic.nuxtjs.org/get-started/watch-outs
**Never use Nuxt navigation components**`<NuxtPage>`, `<NuxtLayout>`, `<NuxtLink>` are not integrated with this module. Use `<IonRouterOutlet>` and `useIonRouter()` / `router-link` prop on Ionic components instead.
**Every page must have `<IonPage>` as its root element** — required for Ionic page transitions and stack navigation.
**Route params: always import `useRoute` from `vue-router` directly** — Nuxt's auto-imported `useRoute()` always returns `params: {}` when used with IonRouterOutlet. This is a known bug with no upstream fix yet.
```typescript
import { useRoute, useRouter } from 'vue-router' // NOT Nuxt auto-import
```
**`onMounted` / `onBeforeMount` are unreliable** — IonRouterOutlet preserves DOM elements rather than unmounting them, so mounted hooks may not fire when expected. Use Ionic lifecycle hooks (`onIonViewWillEnter`) instead. See Data Fetching Pattern below.
**`useHead()` requires workarounds** — not compatible out of the box with IonRouterOutlet's component persistence.
**`<keep-alive>`, `<transition>`, `<router-view>` do not work as expected** — IonRouterOutlet manages its own lifecycle; don't layer Vue's built-in equivalents on top of it.
**No SSR** — this app targets mobile/PWA; SSR is disabled (`ssr: false` in nuxt.config). Do not add SSR-dependent patterns.
### Auth
- Supabase Auth — magic link + OTP only (no password auth)
- `useSupabaseUser()` composable (from @nuxtjs/supabase)
@@ -55,6 +75,33 @@ You work with Patrick, a Solutions Architect, on the OYS Borrow a Boat app (oysq
- Ionicons only (`ionicons/icons`) — no PrimeIcons
- Always import individual icon names from `ionicons/icons` (tree-shakeable)
### Data Fetching Pattern
Every page that reads from Supabase **must** use this pattern. Do NOT use `onMounted` for data fetches.
**Why**: `onMounted` (and `watch(user, { immediate: true })`) fire during component setup — before `IonRouterOutlet` has populated `route.params` and before `@nuxtjs/supabase` has restored the session into the Supabase JS client. The fetch runs unauthenticated, RLS returns 0 rows, and the page shows empty/error state.
```typescript
// Route params: always computed, never destructured at setup time
const id = computed(() => route.params.id as string)
const user = useSupabaseUser()
async function load() {
if (!user.value) return // guard: session not ready
if (!id.value) return // guard: param pages only
// ... fetch data
}
// Fires when session becomes available (handles direct URL load / page refresh)
watch([user, id], ([u, rid]) => { if (u && rid) load() }, { immediate: true })
// Fires on every Ionic page activation (handles IonRouterOutlet page cache)
onIonViewWillEnter(() => { if (user.value) load() })
```
- For pages **without** a route param: omit `id` from the watch array, keep `watch(user, ...)`.
- Reactive filter deps (e.g. `selectedDate`, `filterDateFrom`) use a separate `watch(dep, () => { if (user.value) fetch() })` — not `watch(dep, fetch)`, so they don't fire before the session is ready.
- `onMounted` is still valid for **non-auth DOM setup** (e.g., `window.addEventListener`, breakpoint detection).
### Offline Cache
- **Rule**: Every table/view read from Supabase must be written to `useAppCache` on success and read from it when offline.
- **Composables**: `useAppCache` (localStorage, 24h TTL), `useOfflineStatus` (reactive `isOnline`)