refactor: everything to nuxt.js

This commit is contained in:
2026-03-19 14:30:36 -04:00
parent 6e1f58cd8e
commit bb3042014e
159 changed files with 6786 additions and 11198 deletions

View File

@@ -0,0 +1,186 @@
# Handoff: Nuxt 3 Migration Plan
Date: 2026-03-18
Type: Architecture / Planning
## Decision
Migrate bab-app from Quasar 2 + Vue 3 PWA to **Nuxt 3 + Capacitor + FullCalendar**.
Retain Appwrite backend unchanged. Auth simplified to magic link / email OTP only.
## Current Stack (Source of Truth)
- Quasar 2 / Vue 3 / TypeScript / Pinia / Appwrite
- Build: PWA only (`quasar build -m pwa`)
- Appwrite project: `65ede55a213134f2b688` @ `https://appwrite.toal.ca/v1`
- Database: `bab_prod`
- Collections: boat, reservation, interval, intervalTemplate, task, taskTags, skillTags
- 9 Pinia stores, 21 components, 2 layouts, ~20 pages/routes
- Auth: email token + magic link + password (vue3-google-login present but UNUSED)
## Target Stack
- **Nuxt 3** (file-based routing, Nitro, Vite)
- **nuxt-quasar-ui** module — keeps all Quasar components during migration, de-risks UI rewrite
- **@fullcalendar/vue3** — replaces @quasar/quasar-ui-qcalendar
- **@capacitor/core** + plugins — camera, geolocation (native mobile future)
- **Pinia** — unchanged
- **Appwrite SDK** — unchanged, via Nuxt plugin
- **Auth**: magic link + email OTP only; remove vue3-google-login
## Key Architectural Decisions
- Use `nuxt-quasar-ui` to avoid a big-bang UI rewrite — keeps q-* components working
- File-based routing replaces routes.ts — directory structure maps 1:1 to current routes
- Boot file → Nuxt plugin (appwrite.ts becomes plugins/appwrite.ts)
- Router guards → Nuxt middleware (auth.ts global middleware)
- **PWA only initially** — Capacitor deferred until native camera/GPS features are needed
- FullCalendar resource-timeline view for multi-boat scheduling (replaces QCalendarResource)
- **FullCalendar non-commercial license confirmed** — key: `CC-Attribution-NonCommercial-NoDerivatives` (OYS is a registered Ontario not-for-profit #000082982)
- **Static output** — `nuxt generate`, served via nginx, consistent with current Ansible deploy
- **TestFlight / App Store deferred** — revisit when PWA hits UX or capability limits
## Migration Phases (see plan in this file)
### Phase 1 — Foundation (1-2 days) ✅ COMPLETE 2026-03-19
Scaffold new Nuxt 3 project alongside existing. Do NOT delete old project until Phase 6 complete.
New project: `/home/ptoal/Dev/mobile-projects/bab-app-nuxt/`
Tasks:
- `npx nuxi@latest init bab-app-nuxt`
- Add modules: `nuxt-quasar-ui`, `@pinia/nuxt`, `@vite-pwa/nuxt`
- Configure `nuxt.config.ts`: Quasar options, Vite aliases, env vars
- Migrate `.env.local` vars (VITE_ prefix → NUXT_PUBLIC_ for public vars)
- Capacitor: DEFERRED — PWA only until native features needed
### Phase 2 — Appwrite Plugin + Types (0.5 days)
- Copy `src/boot/appwrite.ts``plugins/appwrite.ts`
- Change export pattern to Nuxt plugin format (`defineNuxtPlugin`)
- Provide `$appwrite` via `useNuxtApp()`
- Copy all TypeScript type files verbatim
### Phase 3 — Auth (1 day)
- Migrate `stores/auth.ts``stores/auth.ts` (Pinia, near-verbatim)
- Remove all password auth methods (createEmailPasswordSession, etc.)
- Keep: `createMagicURLToken`, `updateMagicURLSession`, `createEmailToken`, `updatePhoneSession`
- Remove `vue3-google-login` from package.json
- Create `middleware/auth.global.ts` — replaces router/index.ts navigation guard
- Public routes: `/login`, `/signup`, `/pwreset`, `/terms-of-service`, `/privacy-policy`
- Magic link callback: `pages/auth/callback.vue` — handles `?userId=&secret=` params
### Phase 4 — Remaining Stores (0.5 days)
All stores are framework-agnostic Pinia — direct copy with minor import path updates:
- stores/boat.ts
- stores/reservation.ts
- stores/interval.ts
- stores/intervalTemplate.ts
- stores/task.ts
- stores/reference.ts
- stores/realtime.ts
- stores/memberProfile.ts
### Phase 5 — File-Based Routing (Page Scaffold) (0.5 days)
Create empty page files matching Nuxt convention. Route mapping:
| Old path | Nuxt file |
|---|---|
| `/` | `pages/index.vue` |
| `/boat` | `pages/boat.vue` |
| `/certification` | `pages/certification.vue` |
| `/profile` | `pages/profile.vue` |
| `/checklist` | `pages/checklist.vue` |
| `/reference` | `pages/reference/index.vue` |
| `/reference/:id/view` | `pages/reference/[id]/view.vue` |
| `/schedule` | `pages/schedule/index.vue` |
| `/schedule/book` | `pages/schedule/book.vue` |
| `/schedule/view` | `pages/schedule/view.vue` |
| `/schedule/list` | `pages/schedule/list.vue` |
| `/schedule/edit/:id` | `pages/schedule/edit/[id].vue` |
| `/schedule/manage` | `pages/schedule/manage.vue` |
| `/task` | PARKED — task feature not migrated (collections absent from bab_prod) |
| `/task/:id/edit` | PARKED |
| `/admin/user` | `pages/admin/user.vue` |
| `/admin/boat` | `pages/admin/boat.vue` |
| `/login` | `pages/login.vue` |
| `/signup` | `pages/signup.vue` |
| `/pwreset` | `pages/pwreset.vue` |
| `/terms-of-service` | `pages/terms-of-service.vue` |
| `/privacy-policy` | `pages/privacy-policy.vue` |
Admin route guard: `definePageMeta({ middleware: ['auth', 'admin'] })` in admin pages.
Schedule manage guard: `definePageMeta({ middleware: ['auth', 'schedule-admin'] })`.
### Phase 6 — Layouts + Global Components (1 day)
- `layouts/default.vue` — replaces MainLayout.vue (q-layout, q-header, LeftDrawer, BottomNav)
- `layouts/admin.vue` — replaces AdminLayout.vue
- Migrate components verbatim first; replace Quasar components later if needed:
- components/BottomNavComponent.vue
- components/LeftDrawer.vue (OPEN: was listed in agent findings but not in original glob — confirm file exists)
- components/ToolbarComponent.vue
### Phase 7 — Page Migration (3-5 days)
Migrate pages in this order (lowest to highest complexity):
1. Public pages: login, signup, pwreset, terms, privacy (simple forms)
2. Profile, Certification, Checklist, Boat (read-only/simple CRUD)
3. Reference pages
4. Admin pages (UserAdmin, BoatAdmin — q-table heavy)
5. Schedule pages (most complex — last)
6. Task pages — PARKED. Skip until bab_prod collections (task, taskTags, skillTags) are created.
Affected: stores/task.ts, components/task/*, pages/task/*, BottomNav task link, navlinks.ts task entry.
### Phase 8 — FullCalendar (2-3 days)
Replace QCalendar with FullCalendar Vue 3.
Packages:
```
@fullcalendar/vue3
@fullcalendar/core
@fullcalendar/resource-timeline # multi-boat resource view
@fullcalendar/daygrid # month/day grid
@fullcalendar/timegrid # week/day time grid
@fullcalendar/interaction # drag and drop
@fullcalendar/list # list view
```
Mapping:
- `ResourceScheduleViewerComponent.vue` → new `FullCalendarResourceView.vue`
- Resources = boats (one row per boat)
- Events = reservations + intervals
- DnD for admin interval management (replaces IntervalTemplateComponent drag behavior)
- `CalendarHeaderComponent.vue` — FullCalendar has built-in header toolbar, simplify or remove
- `BoatScheduleTableComponent.vue` — replace with FullCalendar list/grid view
FullCalendar license: open-source plugins are free; resource-timeline requires **FullCalendar Premium** license ($0 for open-source/non-commercial projects — confirm this applies, otherwise budget ~$200/yr).
OPEN: Confirm if OYS Borrow a Boat qualifies for FullCalendar open-source license.
### Phase 9 — Capacitor (1 day)
- `npx cap add ios` / `npx cap add android`
- Install plugins (not wired to features yet, just scaffold):
- `@capacitor/camera`
- `@capacitor/geolocation`
- Create `composables/useCamera.ts` and `composables/useGeolocation.ts`
- Detect Capacitor.isNativePlatform() and fall back to browser APIs on web
- Update `nuxt.config.ts` ssr: false (Capacitor requires SPA mode)
- capacitor.config.ts: `webDir: '.output/public'`
### Phase 10 — CI/CD (0.5 days)
Update `.gitea/workflows/build.yaml`:
- Replace `quasar build -m pwa` with `nuxt build`
- Output dir: `.output/public/` (static) or `.output/` (server)
- Ansible deploy: serve `.output/public` as static site (nginx), same as current
## Open Questions
- OPEN: Does OYS qualify for FullCalendar non-commercial license? (resource-timeline is premium)
- OPEN: Confirm `LeftDrawer.vue` exists in src/layouts/ or src/components/ — agent referenced it but not in initial glob
- OPEN: Is Capacitor native app publishing (App Store / Play Store) in scope, or just Capacitor for future native API access with PWA as primary delivery?
- ASSUMED: SSR is not needed — Appwrite client SDK runs browser-side; deploy as SPA/static
- ASSUMED: Nuxt deployed as static output (not Node server) — consistent with current nginx/Ansible deploy
## Effort Estimate
| Phase | Days |
|---|---|
| 1-4 (Foundation, Plugin, Auth, Stores) | 3 |
| 5-6 (Routing, Layouts) | 1.5 |
| 7 (Page migration) | 4 |
| 8 (FullCalendar) | 2.5 |
| 9 (Capacitor) | 1 |
| 10 (CI/CD) | 0.5 |
| **Total** | **~12.5 days** |

View File

@@ -0,0 +1,82 @@
# Session Handoff: Nuxt Migration — Phases 3 & 4 Complete
**Date:** 2026-03-19
**Session Focus:** Auth store, middleware, callback page, and remaining Pinia stores
**Context Usage at Handoff:** ~50%
## What Was Accomplished
1. Phase 3 — Auth complete
2. Phase 4 — All remaining stores complete (except `task.ts`, still parked)
## Exact State of Work in Progress
- **Migration plan**: 10-phase plan. Phases 14 marked complete. Phase 5 (Pages/Components) is next.
- **New project**: `/home/ptoal/Dev/mobile-projects/bab-app-nuxt/` — stores and middleware in place. No pages migrated yet (other than `auth/callback.vue`).
- **Old project**: `/home/ptoal/Dev/mobile-projects/bab-app/` — untouched. Do not delete until Phase 6.
## Decisions Made This Session
- **Magic link redirect URL**: changed from `/login` to `/auth/callback` — dedicated handler, cleaner separation. CONFIRMED.
- **realtime.ts generic type**: changed from `RealtimeResponseEvent<Interval>` (incorrect) to `RealtimeResponseEvent<unknown>` — original typed the callback fn parameter too narrowly since multiple channels use the same store. CONFIRMED.
- **Boat interface not re-exported from boat.ts**: `boat.ts` re-exports `Boat` from `~/utils/boat.types` via `export { type Boat }` to preserve import compatibility with components that do `import { Boat } from '~/stores/boat'`. CONFIRMED.
## Key Numbers
- **7 stores created**: `auth`, `boat`, `reservation`, `interval`, `intervalTemplate`, `reference`, `memberProfile`
- **2 middleware/pages created**: `middleware/auth.global.ts`, `pages/auth/callback.vue`
- **1 plugin updated**: `plugins/appwrite.client.ts`
- **Task stores**: still 0 — `task`, `taskTags`, `skillTags` collections not in `bab_prod`
## Files Created or Modified
| File Path | Action | Description |
|-----------|--------|-------------|
| `bab-app-nuxt/app/stores/auth.ts` | Created | Magic link + OTP only; `register()`/`login()` removed; boat/reservation init wired |
| `bab-app-nuxt/app/stores/boat.ts` | Created | Re-exports `Boat` from `~/utils/boat.types`; near-verbatim port |
| `bab-app-nuxt/app/stores/reservation.ts` | Created | Near-verbatim port; imports updated |
| `bab-app-nuxt/app/stores/interval.ts` | Created | Near-verbatim port; `Boat` imported from `~/utils/boat.types` |
| `bab-app-nuxt/app/stores/intervalTemplate.ts` | Created | Near-verbatim port |
| `bab-app-nuxt/app/stores/reference.ts` | Created | Verbatim port (static data, no Appwrite) |
| `bab-app-nuxt/app/stores/realtime.ts` | Created | Generic type corrected to `unknown` |
| `bab-app-nuxt/app/stores/memberProfile.ts` | Created | Verbatim port (static data, no Appwrite) |
| `bab-app-nuxt/app/middleware/auth.global.ts` | Created | Global Nuxt route guard; public via `to.meta.public`; roles via `to.meta.requiredRoles` |
| `bab-app-nuxt/app/pages/auth/callback.vue` | Created | Handles `?userId=&secret=` magic link params; redirects to `/` |
| `bab-app-nuxt/app/plugins/appwrite.client.ts` | Modified | `authStore.init()` wired in |
## What the NEXT Session Should Do
1. **Execute Phase 5 — Pages & Components**
- Identify pages in old `src/pages/` — migrate one at a time
- Key pages: `LoginPage.vue`, `IndexPage.vue`, `BoatPage.vue`, `ProfilePage.vue`, `CertificationPage.vue`, `ChecklistPage.vue`
- Schedule pages: `SchedulePageView.vue`, `ScheduleIndexPage.vue`, `BoatReservationPage.vue`, `BoatScheduleView.vue`, `ListReservationsPage.vue`, `ModifyBoatReservation.vue`, `ManageCalendar.vue`
- Admin pages: `UserAdminPage.vue`, `BoatAdminPage.vue`
- Static pages: `TermsOfServicePage.vue`, `PrivacyPolicyPage.vue`, `ErrorNotFound.vue`
- Add `definePageMeta({ public: true })` to: `login.vue`, `signup.vue`, `pwreset.vue`, `terms-of-service.vue`, `privacy-policy.vue`, `auth/callback.vue` (already done)
- Add `definePageMeta({ requiredRoles: ['Schedule Admins'] })` to `schedule/manage.vue`
- Add `definePageMeta({ requiredRoles: ['admin'] })` to all `admin/*.vue` pages
2. **Execute Phase 6 — Layout**
- Migrate `MainLayout.vue` and `AdminLayout.vue`
- Confirm `LeftDrawer.vue` exists in `src/components/` (ASSUMED — not yet confirmed)
## Open Questions
- [ ] **OPEN**: `task`/`taskTags`/`skillTags` collections — will they ever be created in `bab_prod`?
- [ ] **OPEN**: What pages are in `src/pages/` — exact list not yet read (read on demand per CLAUDE.md rule 6)
## Assumptions
- ASSUMED: `LeftDrawer.vue` exists in `src/components/`. Verify before Phase 6.
- ASSUMED: `nuxt generate` (static) is sufficient. Still unvalidated.
- ASSUMED: `LoginPage.vue` in old app handles both magic link and OTP login UI — new app needs `pages/login.vue` built from scratch with `definePageMeta({ public: true })`.
## What NOT to Re-Read
- All stores in `bab-app-nuxt/app/stores/` — just created, content known
- `src/stores/auth.ts`, `src/stores/boat.ts`, etc. — fully migrated; do not re-read
## Files to Load Next Session
- `docs/summaries/handoff-2026-03-19-nuxt-migration-phases-3-4.md` (this file)
- `src/pages/` — read one page at a time per processing protocol
- `src/layouts/MainLayout.vue` — needed for Phase 6 planning
- `bab-app-nuxt/app/stores/auth.ts` — if login page needs store shape reference

View File

@@ -0,0 +1,106 @@
# Session Handoff: Nuxt Migration — Phases 1 & 2 Complete
**Date:** 2026-03-19
**Session Duration:** ~2 hours
**Session Focus:** Framework selection, migration planning, and execution of Phases 1 & 2 of Quasar → Nuxt 3 migration
**Context Usage at Handoff:** ~60%
## What Was Accomplished
1. Framework analysis (React Native vs Flutter vs Next/Nuxt) → decision: Nuxt 3 + Capacitor (deferred) + FullCalendar
2. Migration plan created → `docs/summaries/handoff-2026-03-18-nuxt-migration-plan.md` (now the active plan reference)
3. Phase 1 — Foundation complete → `/home/ptoal/Dev/mobile-projects/bab-app-nuxt/`
4. Phase 2 — Appwrite plugin + types complete → `app/utils/`, `app/plugins/`
5. Integration tests written and passing (11/11) → `tests/appwrite-connection.test.ts`
6. Task feature parked → removed from AppwriteIds and tests
## Exact State of Work in Progress
- **Migration plan**: 10-phase plan in `docs/summaries/handoff-2026-03-18-nuxt-migration-plan.md`. Phases 12 marked complete. Phase 3 (Auth) is next.
- **New project**: `/home/ptoal/Dev/mobile-projects/bab-app-nuxt/` — scaffolded and running. Dev server confirmed clean on `localhost:3000`.
- **Old project**: `/home/ptoal/Dev/mobile-projects/bab-app/` — untouched, still operational. Do not delete until Phase 6 is complete.
## Decisions Made This Session
- **Nuxt 3 + nuxt-quasar-ui** — keeps all Quasar (`q-*`) components working; avoids big-bang UI rewrite. CONFIRMED.
- **FullCalendar non-commercial license** — key `CC-Attribution-NonCommercial-NoDerivatives` confirmed valid. OYS is Ontario not-for-profit #000082982. CONFIRMED.
- **PWA only initially** — Capacitor deferred until native camera/GPS features are actively needed. CONFIRMED.
- **Static output** — `nuxt generate``.output/public/` served via nginx. Consistent with existing Ansible deploy. CONFIRMED.
- **TestFlight/App Store deferred** — revisit when PWA hits UX or capability limits. CONFIRMED.
- **Auth: magic link + email OTP only** — password auth and Google (`vue3-google-login`) removed. CONFIRMED.
- **Task feature parked** — collections `task`, `taskTags`, `skillTags` absent from `bab_prod`. Not migrated until Appwrite collections are created. CONFIRMED.
## Key Numbers Generated or Discovered This Session
- **11/11 tests passing** — backend connection verified
- **4 collections confirmed reachable** — `boat`, `reservation`, `interval`, `intervalTemplate` (return 401 unauthenticated = exist and require auth)
- **3 collections absent** — `task`, `taskTags`, `skillTags` return 404 from `bab_prod`
- **Appwrite project ID**: `65ede55a213134f2b688`
- **Appwrite endpoint**: `https://appwrite.toal.ca/v1`
- **Database ID**: `bab_prod`
- **~12 days** total estimated migration effort (unchanged)
- **Nuxt version**: 4.4.2 (installed as `nuxt@^4.4.2`)
- **nuxt-quasar-ui version**: 3.0.1
- **fontIcons config** must be an array `['material-icons']`, not a string — bug if passed as string (iterates characters)
## Conditional Logic Established
- IF task feature is needed, THEN create collections `task`, `taskTags`, `skillTags` in Appwrite dashboard first, THEN migrate `stores/task.ts`, `components/task/*`, `pages/task/*`
- IF Capacitor is added later, THEN change `nuxt.config.ts` `webDir` to `.output/public` and run `npx cap add ios && npx cap add android`
- IF App Store publishing is pursued, THEN use TestFlight (iOS) + Play Store internal testing track, NOT full public store listing (3050 user club app)
## Files Created or Modified
| File Path | Action | Description |
|-----------|--------|-------------|
| `/home/ptoal/Dev/mobile-projects/bab-app-nuxt/` | Created | New Nuxt 4.4.2 project root |
| `bab-app-nuxt/nuxt.config.ts` | Created | ssr:false, nuxt-quasar-ui, @pinia/nuxt, @vite-pwa/nuxt, PWA manifest, runtimeConfig |
| `bab-app-nuxt/.env.local` | Created | `NUXT_PUBLIC_APPWRITE_ENDPOINT`, `NUXT_PUBLIC_APPWRITE_PROJECT_ID` |
| `bab-app-nuxt/public/icons/` | Created | Copied from old project — all PWA icon sizes |
| `bab-app-nuxt/app/utils/appwrite.ts` | Created | Appwrite client + services + AppwriteIds (task collections excluded) |
| `bab-app-nuxt/app/utils/boat.types.ts` | Created | `Boat` interface (extracted so schedule.ts can depend on it without the store) |
| `bab-app-nuxt/app/utils/schedule.types.ts` | Created | `Interval`, `Reservation`, `IntervalTemplate`, `TimeTuple` |
| `bab-app-nuxt/app/utils/misc.ts` | Created | `getNewId()`, `LoadingTypes` |
| `bab-app-nuxt/app/utils/schedule.ts` | Created | Schedule utilities; `date` from quasar kept; imports updated to `~/utils/*` |
| `bab-app-nuxt/app/plugins/appwrite.client.ts` | Created | Stub plugin; auth store init wired in Phase 3 |
| `bab-app-nuxt/vitest.config.ts` | Created | Vitest with `loadEnv` to pick up `.env.local` |
| `bab-app-nuxt/tests/appwrite-connection.test.ts` | Created | 11 integration tests verifying backend connectivity |
| `bab-app-nuxt/package.json` | Modified | Added `test` and `test:watch` scripts |
| `docs/summaries/handoff-2026-03-18-nuxt-migration-plan.md` | Modified | Added confirmed decisions, updated Phase 1 complete, task parked in Phase 7 |
## What the NEXT Session Should Do
1. **First**: Read `docs/summaries/handoff-2026-03-18-nuxt-migration-plan.md` for the full 10-phase plan and current state
2. **Then**: Execute **Phase 3 — Auth**
- Migrate `src/stores/auth.ts``bab-app-nuxt/app/stores/auth.ts`
- Strip password auth methods; keep magic link + email OTP only
- Remove `vue3-google-login` from `bab-app-nuxt/package.json`
- Create `app/middleware/auth.global.ts` (replaces old router navigation guard in `src/router/index.ts`)
- Create `app/pages/auth/callback.vue` — handles magic link redirect `?userId=&secret=` params
- Wire `authStore.init()` call into `app/plugins/appwrite.client.ts`
3. **Then**: Execute **Phase 4 — Remaining Stores** (all are near-verbatim Pinia ports)
- `stores/boat.ts` — import `Boat` from `~/utils/boat.types` instead of defining inline
- `stores/reservation.ts`, `interval.ts`, `intervalTemplate.ts`, `reference.ts`, `realtime.ts`, `memberProfile.ts`
- Skip `stores/task.ts` (parked)
## Open Questions Requiring User Input
- [ ] **Appwrite `task`/`taskTags`/`skillTags` collections** — will these ever be created in `bab_prod`? Determines whether task feature gets migrated at all, or is permanently dropped.
## Assumptions That Need Validation
- ASSUMED: `nuxt generate` (static) is sufficient — no SSR/server-side rendering needed. Validate: all Appwrite calls are client-side only (no server routes needed).
- ASSUMED: `LeftDrawer.vue` exists in `src/components/` (referenced in codebase exploration but not confirmed by direct file read). Verify before Phase 6.
## What NOT to Re-Read
- `src/boot/appwrite.ts` — fully migrated; summarized above
- `src/utils/misc.ts`, `src/utils/schedule.ts`, `src/stores/schedule.types.ts` — fully migrated; summarized above
- `templates/claude-templates.md` — not needed next session
## Files to Load Next Session
- `docs/summaries/handoff-2026-03-18-nuxt-migration-plan.md` — active migration plan with all phases
- `bab-app-nuxt/app/utils/appwrite.ts` — current AppwriteIds shape (needed for store migration)
- `bab-app-nuxt/app/plugins/appwrite.client.ts` — needs auth init wired in Phase 3
- `src/stores/auth.ts` — source for Phase 3 migration (read once, then discard)
- `src/router/index.ts` — source for middleware logic (read once, then discard)

View File

@@ -0,0 +1,112 @@
# Session Handoff: Nuxt Migration — Phases 35 Complete
**Date:** 2026-03-19
**Session Focus:** Auth, all stores, all pages, all layouts, all components
**Context at Handoff:** High — recommend fresh session for Phase 6+
## What Was Accomplished
1. Phase 3 — Auth (stores/auth.ts, middleware, callback page, plugin)
2. Phase 4 — All 7 non-task stores
3. Phase 5 — All pages, layouts, and non-task components
## Exact State of bab-app-nuxt
### Layouts
- `app/layouts/default.vue` — MainLayout port; reads `route.meta.title` for header title
- `app/layouts/admin.vue` — AdminLayout port; tabs updated to `/admin/user` and `/admin/boat`
- `app/app.vue` — Updated to `<NuxtLayout><NuxtPage /></NuxtLayout>`
### Pages (all created)
| Nuxt Path | Old Path | Notes |
|-----------|----------|-------|
| `pages/index.vue` | `IndexPage.vue` | Uses `useNavLinks()` composable |
| `pages/login.vue` | `LoginPage.vue` | `layout: false`; magic link `onMounted` removed (→ `auth/callback.vue`) |
| `pages/auth/callback.vue` | (new) | Handles `?userId=&secret=` from magic link |
| `pages/boat.vue` | `BoatPage.vue` | |
| `pages/profile.vue` | `ProfilePage.vue` | |
| `pages/certification.vue` | `CertificationPage.vue` | |
| `pages/checklist.vue` | `ChecklistPage.vue` | |
| `pages/schedule.vue` | `SchedulePageView.vue` | Parent wrapper; `<NuxtPage />` |
| `pages/schedule/index.vue` | `ScheduleIndexPage.vue` | |
| `pages/schedule/book.vue` | `BoatReservationPage.vue` | |
| `pages/schedule/view.vue` | `BoatScheduleView.vue` | |
| `pages/schedule/list.vue` | `ListReservationsPage.vue` | |
| `pages/schedule/edit/[id].vue` | `ModifyBoatReservation.vue` | |
| `pages/schedule/manage.vue` | `ManageCalendar.vue` | `requiredRoles: ['Schedule Admins']` |
| `pages/reference.vue` | `ReferencePage.vue` | Parent wrapper |
| `pages/reference/index.vue` | `ReferenceIndexPage.vue` | |
| `pages/reference/reference/[id]/view.vue` | `ReferenceItemPage.vue` | Double `reference` path preserved from old routes |
| `pages/admin/user.vue` | `UserAdminPage.vue` | `layout: 'admin'`, `requiredRoles: ['admin']` |
| `pages/admin/boat.vue` | `BoatAdminPage.vue` | `layout: 'admin'`, `requiredRoles: ['admin']` |
| `pages/signup.vue` | `SignupPage.vue` | **STUB** — password auth removed; shows "contact admin" |
| `pages/pwreset.vue` | `ResetPassword.vue` | **STUB** — password auth removed |
| `pages/terms-of-service.vue` | `TermsOfServicePage.vue` | `layout: false` |
| `pages/privacy-policy.vue` | `PrivacyPolicyPage.vue` | `layout: false` |
| `pages/[...slug].vue` | `ErrorNotFound.vue` | Catch-all 404 |
### Components (all created under `app/components/`)
- `boat/BoatComponent.vue` — stub
- `boat/BoatPickerComponent.vue`
- `boat/BoatPreviewComponent.vue`
- `CertificationComponent.vue`
- `ReferencePreviewComponent.vue`
- `BoatReservationComponent.vue`
- `ResourceScheduleViewerComponent.vue` — abandoned/retained for reference
- `scheduling/NavigationBar.vue`
- `scheduling/ReservationCardComponent.vue` — named route updated to `/schedule/edit/${id}`
- `scheduling/BoatSelection.vue` — empty stub
- `scheduling/IntervalTemplateComponent.vue`
- `scheduling/boat/BoatScheduleTableComponent.vue`
- `scheduling/boat/CalendarHeaderComponent.vue`
- `LeftDrawer.vue` — uses `useNavLinks()` composable; logout via `authStore.logout()`
- `BottomNavComponent.vue`
### Utils added
- `app/utils/navlinks.ts``useNavLinks()` composable (replaces module-level `enabledLinks`)
- `app/utils/version.ts``APP_VERSION = '0.0.0'`
- `app/assets/` — All assets copied from old project
### Skipped (task feature parked)
- `task/TaskEditPage.vue`, `task/TaskPage.vue`, `admin/TaskAdminPage.vue`
- `task/TaskEditComponent.vue`, `task/TaskListComponent.vue`, `task/TaskCardComponent.vue`, `task/TaskTableComponent.vue`
- `NewPasswordComponent.vue` (password auth removed)
## Key Decisions Made This Session
- **ToolbarComponent dropped** — layout `default.vue` handles title via `route.meta.title`. Pages use `definePageMeta({ title: '...' })` instead.
- **Public pages pattern** — `definePageMeta({ public: true, layout: false })` for all auth-less full-layout pages
- **Magic link redirect** — `/auth/callback` (not `/login`) — matches Phase 3 decision
- **signup.vue / pwreset.vue as stubs** — password auth removed; stubs show redirect message. CONFIRMED.
- **navlinks.ts → composable** — original had module-level `useAuthStore()` which would break in Nuxt; converted to `useNavLinks()` composable
- **Nuxt named routes not used** — all navigation uses explicit paths (`/schedule/edit/${id}` not `{ name: 'edit-reservation' }`) to avoid Nuxt route name collisions
## Open Questions
- [ ] **OPEN**: `task`/`taskTags`/`skillTags` collections — will they ever be created in `bab_prod`?
- [x] **RESOLVED**: `signup.vue` — admin-only invite is permanent. `signup.vue` stub is the final state; self-service registration will not be built.
## What NEXT Session Should Do
1. **Phase 6 — Build & Test**
- Run `yarn dev` in `bab-app-nuxt/` — verify dev server starts clean
- Navigate to `/login` — verify page renders
- Test login flow (magic link or OTP)
- Navigate through key pages post-login
- Fix any TypeScript or import errors that surface
2. **Phase 7 — nuxt.config.ts QCalendar registration**
- `@quasar/quasar-ui-qcalendar` is imported directly in components — verify this works in Nuxt (no plugin registration needed if imported directly)
- If calendar components don't render, add CSS import to `nuxt.config.ts`
3. **Phase 8 — Deploy** (if Phase 6 passes)
- `yarn generate` → verify `.output/public/` built correctly
- Update Ansible deploy playbook if needed (path may differ from old `dist/`)
## Files to Load Next Session
- `bab-app-nuxt/nuxt.config.ts` — check QCalendar CSS import, verify PWA config
- `bab-app-nuxt/package.json` — verify no missing deps
- Any error output from `yarn dev`
## What NOT to Re-Read
- All files in `bab-app-nuxt/app/` — just created; content known
- All files in `bab-app/src/` — fully migrated; do not re-read

View File

@@ -0,0 +1,82 @@
# Session Handoff: Nuxt Migration — Phase 6 Complete
**Date:** 2026-03-19
**Session Focus:** Build & TypeScript error fixes
**Context at Handoff:** Low — clean build, ready for Phase 7+
## What Was Accomplished
**Phase 6 — Build & Test**
1. Added missing dependency `@quasar/quasar-ui-qcalendar@4.1.2` to `bab-app-nuxt/package.json` (was in original app but omitted from nuxt package.json)
2. Fixed all TypeScript errors — 0 errors after fixes (verified via `nuxi typecheck`)
3. Dev server (`yarn dev`) starts clean — no errors, one benign PWA workbox warning
## TypeScript Fixes Applied
### verbatimModuleSyntax: split type-only imports (TS1484/TS1485)
| File | Fixed |
|------|-------|
| `app/stores/reservation.ts` | `ComputedRef` from vue, `Timestamp` from qcalendar |
| `app/stores/intervalTemplate.ts` | `Ref` from vue, `Models` from appwrite |
| `app/stores/interval.ts` | `Timestamp` from qcalendar |
| `app/components/ResourceScheduleViewerComponent.vue` | `TimestampOrNull`, `Timestamp` from qcalendar |
| `app/components/scheduling/boat/BoatScheduleTableComponent.vue` | `Timestamp` from qcalendar |
| `app/components/scheduling/boat/CalendarHeaderComponent.vue` | `Timestamp` from qcalendar |
| `app/pages/schedule/manage.vue` | `Timestamp` from qcalendar |
| `app/pages/schedule/view.vue` | `Timestamp` from qcalendar |
### Actual type errors fixed
| File | Error | Fix |
|------|-------|-----|
| `app/stores/auth.ts:85` | `string \| undefined` return | `?? 'Unknown'` |
| `app/utils/schedule.ts:8` | array index `string \| undefined` | non-null assertion `!` on `arr[i]` |
| `app/utils/schedule.ts:23` | `split()[1]` is `string \| undefined` | non-null assertion `!` |
| `app/utils/schedule.ts:33-34` | `arr[i-1]` possibly undefined | non-null assertion `!` |
| `app/components/BoatReservationComponent.vue:57` | `updateInterval` handler type mismatch with `defineModel` emit | widened to `Interval \| null \| undefined` |
| `app/components/scheduling/boat/BoatScheduleTableComponent.vue:79` | `boats.value[i].displayName` after guard | `?.displayName ?? ''` |
| `app/components/scheduling/boat/BoatScheduleTableComponent.vue:119` | `result[key].push()` after undefined check | non-null assertion `!` |
| `app/components/scheduling/boat/BoatScheduleTableComponent.vue:126` | `Record[key]` returns `T \| undefined` | `?? []` |
| `app/components/scheduling/boat/BoatScheduleTableComponent.vue:175` | `boats[i].name` in template | `?.name` |
## Known Non-Errors (ignored)
- **PWA workbox WARN**: `_nuxt/builds/**/*.json` pattern matches nothing in dev mode — expected, not present in dev SW dist
- **vue-router volar warning**: `Cannot find module '@vue/language-core'` during `nuxi typecheck` — npx version mismatch, does not affect build
- **Deprecated Appwrite API hints (TS6387)**: `databases.listDocuments`, `createDocument`, etc. show as deprecated — these are hints, not errors; the old API still works. OPEN: migrate to new Appwrite SDK v14+ API signatures in a future session.
## Current State
- `yarn dev` — clean build, no errors
- `nuxi typecheck` — 0 TS errors
- All pages, stores, layouts, components in place from Phases 15
- `@quasar/quasar-ui-qcalendar@4.1.2` installed
## What NEXT Session Should Do
1. **Phase 7 — QCalendar CSS / runtime verification**
- Start `yarn dev`, open browser to `http://localhost:3000`
- Navigate to `/login` — verify page renders
- Test login flow (OTP or magic link)
- Navigate to `/schedule/book` — verify QCalendarDay renders correctly
- If calendar has no styling, add to `nuxt.config.ts`:
```ts
css: ['@quasar/quasar-ui-qcalendar/dist/index.css']
```
- Navigate to `/admin/user` and `/admin/boat` — verify admin layout renders
2. **Phase 8 — Generate & Deploy**
- `yarn generate` → verify `.output/public/` built
- Update Ansible deploy playbook if dist path changed from old `dist/` to `.output/public/`
- Check `.gitea/workflows/build.yaml` — may need path update
## Open Questions
- [ ] **OPEN**: Appwrite SDK deprecated API calls — migrate to v14+ signatures? (TS6387 hints in all stores)
- [ ] **OPEN**: `task`/`taskTags`/`skillTags` collections — will they ever be created in `bab_prod`?
## Files to Load Next Session
- `bab-app-nuxt/nuxt.config.ts` — if CSS import needed
- `.gitea/workflows/build.yaml` — check output path for deploy
- Any browser console errors from `yarn dev`

View File

@@ -0,0 +1,93 @@
# Session Handoff: Nuxt Migration — Phase 7 Complete
**Date:** 2026-03-19
**Session Focus:** Runtime verification and fix
**Context at Handoff:** Low — all pages working in dev, ready for Phase 8
## What Was Accomplished
**Phase 7 — Runtime Verification & Fixes**
All pages now load correctly in dev. Eight issues found and fixed:
### Fix 1: QCalendar CSS not imported
- Added `css: ['@quasar/quasar-ui-qcalendar/dist/index.css']` to `nuxt.config.ts`
### Fix 2: Appwrite client initialized at module load time with wrong env var pattern
- `app/utils/appwrite.ts` used `import.meta.env.NUXT_PUBLIC_*` — not available in Nuxt browser context
- **Fix**: extracted `initAppwriteClient(endpoint, projectId)` function, called from plugin after `useRuntimeConfig()`
- `app/plugins/appwrite.client.ts` now calls `useRuntimeConfig()` and guards against empty config
### Fix 3: `.env.local` not loaded by Nuxt 4
- Nuxt 4 auto-loads `.env` but NOT `.env.local`
- **Fix**: copied `.env.local``.env` (both are gitignored via `.env.*`)
### Fix 4: `AddressbarColor` plugin missing
- Added `'AddressbarColor'` to Quasar plugins list in `nuxt.config.ts`
- Added `?.` guard on `q.addressbarColor?.set(...)` in `app/layouts/default.vue`
### Fix 5: Static image assets not resolved in Vite
- `src="~/assets/oysqn_logo.png"` in component props is not processed by Vite (unlike Webpack's `~` convention)
- **Fix**: copied `oysqn_logo.png`, `oysqn_logo_only.png`, `oys_lighthouse.jpg` to `public/`
- Updated 4 pages (`login.vue`, `index.vue`, `signup.vue`, `pwreset.vue`) to use `/oysqn_logo.png`
- CSS `url('~/assets/...')` in `<style>` blocks IS handled by Vite — left unchanged
### Fix 6: Quasar SCSS variables (`$primary` etc.) undefined
- Old Quasar/Webpack auto-imported variables; Vite does not
- **Fix**: added to `nuxt.config.ts`:
```ts
vite: {
css: {
preprocessorOptions: {
sass: {
additionalData: '@use "quasar/src/css/variables.sass" as *\n',
},
},
},
},
```
- Affects: `schedule/view.vue`, `LeftDrawer.vue`, `CalendarHeaderComponent.vue`, `BoatScheduleTableComponent.vue`
### Fix 7: `sass-embedded` missing
- Added `sass-embedded` as dev dependency
### Fix 8: PWA service worker caching stale HTML in dev
- Old SW was caching HTML with empty Appwrite config
- **Fix**: set `pwa.devOptions.enabled: false` in `nuxt.config.ts`
## Current State
- `yarn dev` — all pages load correctly
- Login, schedule, admin pages verified working
- Appwrite auth flow functional
- QCalendar renders with correct styling
## Files Changed This Session
- `bab-app-nuxt/nuxt.config.ts` — CSS import, Vite SASS config, AddressbarColor plugin, PWA dev disabled
- `bab-app-nuxt/app/utils/appwrite.ts` — lazy init via `initAppwriteClient()`
- `bab-app-nuxt/app/plugins/appwrite.client.ts` — uses `useRuntimeConfig()`, guards empty config
- `bab-app-nuxt/app/layouts/default.vue` — optional chaining on `route?.meta?.title` and `q.addressbarColor?.set()`
- `bab-app-nuxt/app/pages/login.vue`, `index.vue`, `signup.vue`, `pwreset.vue` — logo src path fixed
- `bab-app-nuxt/public/` — added `oysqn_logo.png`, `oysqn_logo_only.png`, `oys_lighthouse.jpg`
- `bab-app-nuxt/.env` — created from `.env.local` for Nuxt 4 dotenv loading
## What NEXT Session Should Do
### Phase 8 — Generate & Deploy
1. **Update `.gitea/workflows/build.yaml`** — current CI is for the old Quasar app:
- Remove `yarn dlx @quasar/cli ext invoke @quasar/qcalendar` step (Quasar CLI, not needed)
- Add `cd bab-app-nuxt` or set working-directory for install/build steps
- Change build command to `yarn generate`
- Change artifact path from `dist/` to `bab-app-nuxt/.output/public/`
- Env vars: CI uses `vars.ENV_FILE` → needs to write to `bab-app-nuxt/.env`
2. **Test `yarn generate`** in `bab-app-nuxt/` — verify `.output/public/` is produced
3. **Check Ansible deploy playbook** — update dist path if needed
## Open Questions
- [ ] **OPEN**: Appwrite SDK deprecated API calls — migrate to v14+ signatures? (TS6387 hints in all stores)
- [ ] **OPEN**: `task`/`taskTags`/`skillTags` collections — will they ever be created in `bab_prod`?
- [ ] **OPEN**: Should `.env.local` be removed now that `.env` is in place, to avoid confusion?

View File

@@ -0,0 +1,43 @@
# Session Handoff: Nuxt Migration — Phase 8 Complete
**Date:** 2026-03-19
**Session Focus:** Generate & Deploy pipeline update
**Context at Handoff:** Low — build pipeline updated, ready to push and test CI
## What Was Accomplished
**Phase 8 — Generate & Deploy**
`yarn generate` verified working — outputs 22 prerendered routes to `bab-app-nuxt/.output/public`.
### Changes Made
**`generate-version.cjs`**
- Now writes `APP_VERSION` to BOTH `src/version.ts` (old Quasar) AND `bab-app-nuxt/app/utils/version.ts` (Nuxt)
**`.releaserc.json`**
- `prepareCmd`: `npm run generate-version '${nextRelease.version}' && cd bab-app-nuxt && yarn install --immutable && yarn generate`
- `publishCmd`: tar from `bab-app-nuxt/.output/public` instead of `dist/pwa`
**`.gitea/workflows/build.yaml`**
- Removed: `yarn dlx @quasar/cli ext invoke @quasar/qcalendar` (Quasar CLI step, not needed for Nuxt)
- Changed: env file written to `bab-app-nuxt/.env` instead of `.env.local`
(Nuxt 4 auto-loads `.env`, not `.env.local`)
## Current State
- `yarn generate` in `bab-app-nuxt/``.output/public/`
- CI pipeline updated for Nuxt build
- App pages working in dev (Phase 7)
## What NEXT Session Should Do
1. **Push to `devel` or `alpha` branch** and verify CI pipeline succeeds in Gitea
2. **Verify Ansible deploy** picks up the new artifact correctly (same tar.gz format, just different content)
3. **Smoke test the deployed app** in the target environment
## Open Questions
- [ ] **OPEN**: Appwrite SDK deprecated API calls — migrate to v14+ signatures? (TS6387 hints in all stores)
- [ ] **OPEN**: `task`/`taskTags`/`skillTags` collections — will they ever be created in `bab_prod`?
- [ ] **OPEN**: Should `.env.local` in `bab-app-nuxt/` be removed to avoid confusion with `.env`?
- [ ] **OPEN**: Should `nuxt_test` branch be merged to `main` once CI passes, retiring the old Quasar app?