refactor: Implemented basic app structure

This commit is contained in:
2026-04-10 19:12:26 -04:00
parent a46c97c88a
commit 652ac1e2af
14 changed files with 2142 additions and 49 deletions

View File

@@ -1,125 +0,0 @@
# 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 `useToast``useIonToast` to avoid conflict with PrimeVue's auto-imported `useToast``app/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

View File

@@ -0,0 +1,103 @@
# Session Handoff: Local Dev Setup & Ionic Module Migration
**Date:** 2026-03-26
**Session Duration:** ~1.5 hours
**Session Focus:** Initialize local Supabase DB, get app loading in browser, fix Ionic/Nuxt router integration using the official @nuxtjs/ionic module
**Context Usage at Handoff:** Medium-high
## What Was Accomplished
1. **Node upgraded to 22** — added `engines: { node: ">=22" }` to `package.json`, created `.nvmrc` with value `22`
2. **Local Supabase initialized** — schema applied via migration; DB running at `postgresql://postgres:postgres@127.0.0.1:54322/postgres`
3. **`.env` switched to local Supabase** — URL `http://127.0.0.1:54321`, publishable key `sb_publishable_ACJWlzQHlZjBrEguHvfOxg_3BJgxAaH`
4. **Schema migration created**`supabase/migrations/20260325000000_initial_schema.sql` (reordered from `schema.sql`: all tables first, then RLS policies, then trigger — original order caused `members` not found error during `boats` policy creation)
5. **`yarn dev` running** — app responds HTTP 200 at `http://localhost:3000`
6. **Migrated to `@nuxtjs/ionic`** — replaced manual IonicVue plugin + navManager/viewStacks shims (250 lines) with the official Nuxt Ionic module
## Exact State of Work in Progress
- App loads and serves HTTP 200; browser rendering not fully verified (IonRouterOutlet may still need testing)
- No pages beyond skeleton `index.vue`, `login.vue`, `auth/callback.vue` — all page content is TODO
- Auth flow not implemented (`login.vue` is still a skeleton)
## Decisions Made This Session
- USE `@nuxtjs/ionic` module INSTEAD OF manual `@ionic/vue` plugin BECAUSE it is the idiomatic Nuxt+Ionic integration; provides `@ionic/vue-router` correctly (which supplies `navManager` and `viewStacks` that `IonRouterOutlet` requires) — STATUS: confirmed
- USE local Supabase via CLI INSTEAD OF remote project for development BECAUSE user decision — STATUS: confirmed
- DOCKER_HOST must be set to `unix:///run/user/1000/podman/podman.sock` for all `npx supabase` commands on this machine — STATUS: confirmed
- USE `supabase db reset` to apply schema changes locally — STATUS: confirmed
- `mode: 'md'` moved from plugin to `ionic.config` in `nuxt.config.ts` — STATUS: confirmed
## Key Numbers Generated or Discovered This Session
- Local Supabase API port: 54321
- Local Supabase DB port: 54322
- Local Supabase Studio port: 54323
- Local Supabase Mailpit port: 54324
- Migration file timestamp: 20260325000000
- 6 tables in schema (unchanged from prior session)
- `@nuxtjs/ionic` version installed: 1.0.2
## Files Created or Modified
| File Path | Action | Description |
|-----------|--------|-------------|
| `.nvmrc` | Created | Pins Node to version 22 |
| `package.json` | Modified | Added `engines: { node: ">=22" }`, added `@nuxtjs/ionic`, `@ionic/vue-router` |
| `supabase/migrations/20260325000000_initial_schema.sql` | Created | Initial schema migration (reordered from schema.sql) |
| `.env` | Modified | Switched from remote Supabase to local (URL + publishable key) |
| `nuxt.config.ts` | Modified | Added `@nuxtjs/ionic` module, `ionic: { css: { utilities: true }, config: { mode: 'md' } }`, removed manual Ionic CSS imports and CDN link, removed vite optimizeDeps for @ionic/vue |
| `app/plugins/ionic.client.ts` | Deleted | Replaced by @nuxtjs/ionic module |
## What the NEXT Session Should Do
1. **First**: Verify app renders correctly in browser (check for Vue/Ionic console errors, confirm IonRouterOutlet works)
2. **Then**: Implement `app/pages/login.vue` — OTP flow: email input → `sendOtp()` → token input → `verifyOtp()` → redirect to `/`
3. **Then**: Implement `app/pages/auth/callback.vue` — handle magic link redirect (Supabase sets session from URL hash)
4. **Then**: Test auth flow end-to-end: send OTP via Mailpit (http://127.0.0.1:54324), verify, confirm member row created in `members` table
5. **Then**: Implement `app/pages/index.vue` — home page shell showing boat list or welcome state
6. **Then**: Run `npx supabase gen types typescript --local > app/types/supabase.ts` to replace placeholder types and remove `as any` casts in stores
## Open Questions Requiring User Input
- [ ] Should `login.vue` support magic link (email link) in addition to OTP, or OTP-only? — impacts login page UI
- [ ] Scheduling refactor design still deferred — should it be designed before or after auth + boat pages?
## Assumptions That Need Validation
- ASSUMED: `@nuxtjs/ionic` module correctly wires `@ionic/vue-router` so `IonRouterOutlet` has `navManager` and `viewStacks` — validate by loading app in browser and checking for no inject warnings
- ASSUMED: `handle_new_user()` trigger fires on magic link / OTP first sign-in — validate by signing in a test user and checking `members` table
- ASSUMED: `useIonRouter()` composable (from `@nuxtjs/ionic` auto-imports) should be used instead of `useRouter()` — validate against module docs; update `app/stores/auth.ts` if needed
## What NOT to Re-Read
- `docs/archive/handoffs/handoff-2026-03-25-initial-setup.md` — superseded
- `docs/archive/handoffs/handoff-2026-03-25-project-scaffold.md` — superseded
## Files to Load Next Session
- `app/pages/login.vue` — primary implementation target
- `app/stores/auth.ts` — has `sendOtp()`, `verifyOtp()`, `sendMagicLink()` — needed for login page
- `app/pages/auth/callback.vue` — second implementation target
- `app/types/supabase.ts` — after regenerating with `--local` flag
## Dev Environment Reference
```
# Start local Supabase (already running, but if needed):
DOCKER_HOST=unix:///run/user/1000/podman/podman.sock npx supabase start
# Check status / get keys:
DOCKER_HOST=unix:///run/user/1000/podman/podman.sock npx supabase status
# Apply schema changes:
DOCKER_HOST=unix:///run/user/1000/podman/podman.sock npx supabase db reset
# Start app:
yarn dev
# Mailpit (view OTP emails):
http://127.0.0.1:54324
# Supabase Studio:
http://127.0.0.1:54323
```