Files
oysqn.app/docs/archive/handoffs/handoff-2026-03-25-initial-setup.md

9.4 KiB

Session Handoff: oysqn.app Initial Setup

Date: 2026-03-25 Session Duration: ~1 hour Session Focus: Create new project oysqn.app — scaffold, schema extraction, icon copy, role-based menu Context Usage at Handoff: Medium

What Was Accomplished

  1. Created project directory /home/ptoal/Dev/mobile-projects/oysqn.app/ and scaffolded full Nuxt 4 + Ionic Vue + PrimeVue + Supabase stack → all files listed in Files table below
  2. Installed dependencies via yarn install --ignore-engines (Node 20 compat issue with rollup-plugin-visualizer@7.0.1 requiring Node >=22) → yarn.lock created, .yarnrc set with --ignore-engines true
  3. Extracted schema from bab-app stores and translated Appwrite model → Supabase SQL → supabase/schema.sql
  4. Populated full TypeScript types → app/types/supabase.ts
  5. Rebuilt auth store with Supabase equivalents: magic link, OTP, hasRequiredRole(), fetchMember(), getUserNameById()app/stores/auth.ts
  6. Updated app/app.vue with role-based IonMenu (3 visibility tiers)
  7. Copied all icons and brand assets from bab-app → public/icons/, public/
  8. Renamed composable useToastuseIonToast to avoid conflict with PrimeVue's auto-imported useToastapp/composables/useIonToast.ts
  9. yarn typecheck passes with 0 errors

Exact State of Work in Progress

  • Supabase project NOT created — .env is empty placeholder; schema SQL exists but has not been run
  • app/stores/boat.ts uses as any cast for supabase client — placeholder until real types wired via supabase gen types
  • Auth store uses as any cast for same reason — will resolve once supabase gen types output replaces app/types/supabase.ts
  • No pages beyond skeleton index.vue, login.vue, auth/callback.vue — all page content is TODO
  • No app/stores/interval.ts, app/stores/intervalTemplate.ts, app/stores/reservation.ts, app/stores/reference.ts yet — to be built
  • Scheduling refactor deferred — new resource picker + booking flow design not started

Decisions Made This Session

  • USE IonRouterOutlet in app.vue instead of <NuxtPage> BECAUSE user decision — enables Ionic page transitions and lifecycle hooks — STATUS: confirmed
  • USE Supabase INSTEAD OF Appwrite BECAUSE user decision — new project rewrite — STATUS: confirmed
  • USE mode: 'md' in IonicVue plugin BECAUSE consistent cross-platform appearance for PWA — STATUS: confirmed
  • USE @nuxtjs/supabase module BECAUSE handles SSR-safe client creation and useSupabaseUser() composable automatically — STATUS: confirmed
  • NO @ionic/vue-router BECAUSE Nuxt manages the router; IonRouterOutlet from @ionic/vue works with standard Vue Router — STATUS: confirmed
  • USE magic link + OTP only (no password auth) BECAUSE carried forward from bab-app confirmed auth model — STATUS: confirmed
  • USE role column on members table INSTEAD OF Appwrite Teams BECAUSE Supabase native pattern; simpler RLS — STATUS: confirmed
  • USE handle_new_user() trigger BECAUSE auto-creates members row on first sign-in without extra client code — STATUS: provisional (not yet tested)
  • USE as any cast on useSupabaseClient() in stores FOR NOW BECAUSE @nuxtjs/supabase doesn't propagate generic Database type through PostgREST column-select type inference; will resolve after supabase gen types — STATUS: provisional
  • SAME IonMenu for all roles with conditional sections BECAUSE user decision — STATUS: confirmed
  • USE .yarnrc with --ignore-engines true BECAUSE Node 20 installed, rollup-plugin-visualizer requires Node >=22 — STATUS: confirmed

Key Numbers Generated or Discovered This Session

  • 6 tables in schema: boats, members, interval_templates, intervals, reservations, reference_docs
  • 3 menu visibility tiers: all-authenticated / isBoatswain (admin+boatswain) / isAdmin only
  • 0 TypeScript errors (yarn typecheck clean)
  • 31 icon/asset files copied from bab-app to public/icons/
  • 1 trigger in schema: handle_new_user() on auth.users insert

Conditional Logic Established

  • IF member.role is in ['admin', 'boatswain'] THEN isBoatswain is true → shows "Manage Schedule" menu item
  • IF member.role is 'admin' THEN isAdmin is true → shows "Users" + "Manage Boats" menu items
  • IF authStore.user changes to non-null THEN fetchMember() is called → populates member ref → menu items become visible
  • IF cert in members.certifications matches entry in boats.required_certs THEN user may book that boat — ASSUMED (not yet enforced in code, logic carried from bab-app)
  • IF interval.boat_id matches boats.id THEN interval appears in that boat's resource column in FullCalendar (scheduling refactor — deferred)

Files Created or Modified

File Path Action Description
package.json Created Nuxt 4 + @ionic/vue + primevue + @nuxtjs/supabase + @pinia/nuxt + @vite-pwa/nuxt
nuxt.config.ts Created Nuxt config: SSR=false, PrimeVue OYS theme (#027be3), PWA manifest, Ionic CSS
tsconfig.json Created Extends .nuxt/tsconfig.json
.gitignore Created Standard Nuxt gitignore
.env.example Created SUPABASE_URL + SUPABASE_KEY placeholders
.yarnrc Created --ignore-engines true for Node 20 compat
yarn.lock Created Resolved dependency lockfile
supabase/schema.sql Created Full Supabase schema: 6 tables, RLS policies, handle_new_user trigger
app/app.vue Created IonApp + IonMenu (role-based) + IonRouterOutlet; fetchMember on mount/user-watch
app/plugins/ionic.client.ts Created IonicVue plugin, mode: 'md'
app/middleware/auth.ts Created Global Supabase auth guard via useSupabaseUser()
app/stores/auth.ts Created Full auth store: role, isAdmin, isBoatswain, fetchMember, sendMagicLink, sendOtp, verifyOtp, getUserNameById, signOut, updateName
app/stores/boat.ts Created Skeleton boat store with placeholder types
app/types/supabase.ts Created Full Database type: 6 tables + MemberRole, ReservationStatus, Defect, TimeTuple
app/composables/useIonToast.ts Created Ionic toastController wrapper (renamed from useToast to avoid PrimeVue conflict)
app/pages/index.vue Created Skeleton home page (IonPage shell)
app/pages/login.vue Created Skeleton login page
app/pages/auth/callback.vue Created Supabase auth callback page
CLAUDE.md Created Project-specific instructions for new stack
docs/planning/personas.md Created Copied from bab-app
docs/context/archive-rules.md Created Copied from bab-app
docs/context/processing-protocol.md Created Copied from bab-app
docs/context/subagent-rules.md Created Copied from bab-app
templates/claude-templates.md Created Copied from bab-app
public/icons/ Created 31 icon/asset files copied from bab-app
public/favicon.ico Created Copied from bab-app
public/oysqn_logo.png Created Copied from bab-app
public/oysqn_logo_only.png Created Copied from bab-app
public/oys_lighthouse.jpg Created Copied from bab-app

What the NEXT Session Should Do

  1. First: Create Supabase project at supabase.com; copy URL + anon key to .env
  2. Then: Run schema: paste supabase/schema.sql into Supabase SQL editor and execute
  3. Then: Run npx supabase gen types typescript --project-id YOUR_ID > app/types/supabase.ts to replace placeholder types and remove as any casts
  4. Then: Implement app/pages/login.vue — OTP flow: email input → sendOtp() → token input → verifyOtp() → redirect to /
  5. Then: Implement app/pages/auth/callback.vue — handle magic link redirect (Supabase sets session from URL hash)
  6. Then: Run yarn dev and verify IonRouterOutlet renders, menu appears, auth redirect fires
  7. Then: Build remaining stores: reservation.ts, interval.ts, intervalTemplate.ts, reference.ts

Open Questions Requiring User Input

  • Supabase project ID — needed before types, schema, and .env can be configured
  • Scheduling refactor design — should new resource picker and booking flow be designed before or after auth + boat pages are implemented?
  • Should members.role ever be set to something other than 'member' automatically, or is role assignment always manual (admin sets it)?

Assumptions That Need Validation

  • ASSUMED: handle_new_user() trigger correctly fires on Supabase magic link / OTP first sign-in — validate by creating a test user and checking members table
  • ASSUMED: IonRouterOutlet in app.vue works without <NuxtLayout> wrapper in Nuxt 4 — validate at yarn dev
  • ASSUMED: useSupabaseUser() watcher in app.vue fires after OTP verification completes — validate in auth flow test
  • ASSUMED: cert matching logic (members.certifications vs boats.required_certs) carried forward as string array comparison — validate against domain rules in docs/planning/

What NOT to Re-Read

  • docs/archive/handoffs/handoff-2026-03-25-project-scaffold.md — superseded by this handoff
  • bab-app stores — schema extraction complete; source of truth is now supabase/schema.sql + app/types/supabase.ts

Files to Load Next Session

  • supabase/schema.sql — if modifying schema before running it
  • app/stores/auth.ts — when implementing login page
  • app/pages/login.vue — primary implementation target
  • app/types/supabase.ts — after regenerating from real Supabase project