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,101 @@
# Session Handoff: Build Fixes & Dev Environment
**Date:** 2026-03-15
**Session Duration:** ~2 hours
**Session Focus:** Resolve all TypeScript/ESLint build errors from dependency updates; fix dev server startup
**Context Usage at Handoff:** Medium
## What Was Accomplished
1. Fixed 30 TypeScript errors (14 files) → build now passes with 0 errors
2. Fixed 12 ESLint problems (6 errors, 6 warnings) → 0 remaining
3. Fixed `quasar dev` startup error (`FlatESLint is not a constructor`) → downgraded ESLint v10→v9
4. Fixed missing Appwrite env vars in `.env.local` → app connects to backend on dev
## Exact State of Work in Progress
- **Build**: CLEAN — `yarn quasar build` exits 0, no TS or ESLint errors
- **Dev server**: FUNCTIONAL — `quasar dev` starts without errors; ESLint inline checking via `vite-plugin-checker` is restored
- **Runtime**: UNTESTED this session — app has not been manually tested against the dev Appwrite backend
## Decisions Made This Session
- **`as unknown as Type` for all Appwrite store casts** — CONFIRMED: Appwrite v23 made `DefaultDocument` strict; it no longer overlaps domain types, so the double-cast is required. Applied to: `boat.ts`, `interval.ts`, `intervalTemplate.ts`, `reservation.ts`, `task.ts`
- **ESLint downgraded v10.0.3 → v9.39.4** — CONFIRMED: `vite-plugin-checker` v0.12.0 calls `FlatESLint` which was merged back into `ESLint` in v10; v9 preserves the API. Also downgraded `@eslint/js` (v10→v9) and `eslint-plugin-vue` (v10→v9)
- **`getWeekdaySkips` removed** — CONFIRMED: removed from `@quasar/quasar-ui-qcalendar` API; `createDayList` now takes `weekdays` array directly as 4th param (previously took `weekdaySkips` computed value)
- **`subtasks` removed from TaskCardComponent template** — ASSUMED SAFE: `Task` type has no `subtasks` field; template refs were dead code. See open question.
- **`no-debugger: 'off'`** — CONFIRMED: hardcoded because `process` is not available in ESLint globals when linting `.js` files (config file context)
- **`.env.local` variable names corrected** — CONFIRMED: file had `VITE_APPWRITE_ENDPOINT` / `VITE_APPWRITE_PROJECT`; `appwrite.ts` reads `VITE_APPWRITE_API_ENDPOINT` / `VITE_APPWRITE_API_PROJECT`
## Key Numbers Generated or Discovered This Session
- TypeScript errors at session start: 30 (across 14 files)
- ESLint problems at session start: 12 (6 errors, 6 warnings)
- TypeScript errors at session end: 0
- ESLint problems at session end: 0
- ESLint: v10.0.3 → v9.39.4
- `@eslint/js`: v10 → v9
- `eslint-plugin-vue`: v10 → v9
- `register-service-worker`: newly added (was missing from package.json)
## Conditional Logic Established
- IF Appwrite SDK returns `DefaultDocument` THEN cast via `as unknown as DomainType` BECAUSE v23 `DefaultDocument` is strict and no longer assignable to domain types that extend `Partial<Models.Document>`
- IF `vite-plugin-checker` is v0.12.x THEN ESLint must be v9.x BECAUSE v0.12.x uses `FlatESLint` constructor removed in ESLint v10
- IF `createDayList` is called from qcalendar THEN pass `weekdays` array as 4th arg directly BECAUSE `getWeekdaySkips` was removed from the qcalendar public API
- IF `.env.local` is updated THEN variable names must match `import.meta.env.VITE_APPWRITE_API_ENDPOINT` / `VITE_APPWRITE_API_PROJECT` as read in `src/boot/appwrite.ts`
## Files Created or Modified
| File Path | Action | Description |
|-----------|--------|-------------|
| `src/stores/boat.ts` | Modified | `as unknown as Boat[]` |
| `src/stores/interval.ts` | Modified | `as unknown as Interval` (3 places) |
| `src/stores/intervalTemplate.ts` | Modified | Map callback cast + `as unknown as IntervalTemplate` (3 places); `timeTuple` cast |
| `src/stores/reservation.ts` | Modified | `as unknown as Reservation` (5 places) |
| `src/stores/task.ts` | Modified | `as unknown as Task[]`, `TaskTag[]`, `SkillTag[]`, `Task` (5 places) |
| `src/stores/sampledata/schedule.ts` | Modified | `id``$id`, `blocks``timeTuples`, removed `reservationDate` |
| `src/components/boat/BoatPreviewComponent.vue` | Modified | `boat.id``boat.$id` |
| `src/components/scheduling/boat/BoatScheduleTableComponent.vue` | Modified | `block.id``block.$id`; `NodeJS.Timeout``ReturnType<typeof setInterval>`; ternary→if/else |
| `src/components/scheduling/boat/CalendarHeaderComponent.vue` | Modified | Removed `getWeekdaySkips` import+computed; `createDayList` now passes `weekdays` directly |
| `src/components/task/TaskCardComponent.vue` | Modified | Removed `defineProps` explicit import; removed `subtasks` template refs |
| `src/components/task/TaskListComponent.vue` | Modified | `task.id``task.$id` |
| `src/components/task/TaskTableComponent.vue` | Modified | Removed `defineProps` from explicit import |
| `src/components/ResourceScheduleViewerComponent.vue` | Modified | Removed `|| undefined`; `catch { }`; removed stale eslint-disable comments |
| `src/pages/LoginPage.vue` | Modified | `catch { }` |
| `src/pages/schedule/ManageCalendar.vue` | Modified | `block.id``block.$id` |
| `src/boot/appwrite.ts` | Modified | Removed stale `console.log(API_ENDPOINT)` |
| `eslint.config.js` | Modified | `no-debugger` hardcoded to `'off'` |
| `quasar.config.ts` | Modified | ESLint checker restored (had been temporarily removed) |
| `package.json` / `yarn.lock` | Modified | ESLint v10→v9; `@eslint/js` v10→v9; `eslint-plugin-vue` v10→v9; added `register-service-worker` |
| `.env.local` | Modified | Variable names corrected: `VITE_APPWRITE_ENDPOINT``VITE_APPWRITE_API_ENDPOINT`, `VITE_APPWRITE_PROJECT``VITE_APPWRITE_API_PROJECT`; endpoint URL updated to include `/v1` |
| `docs/summaries/handoff-2026-03-15-build-fixes.md` | Created | This file |
| `docs/archive/handoffs/handoff-2026-03-15-dependency-updates.md` | Archived | Superseded by this handoff |
## What the NEXT Session Should Do
1. **First**: Run `quasar dev` and manually test the login flow against the dev Appwrite backend to validate v23 API calls work at runtime
2. **Validate**: Boat listing, reservation creation/cancellation, interval loading — confirm no runtime errors from the v23 positional-param deprecations
3. **Commit**: Stage all modified files and commit as `"fix: Resolve build errors from dependency updates"` (single clean commit covering all TS/ESLint/qcalendar/env fixes)
4. **Optional**: Migrate Appwrite calls from deprecated positional-param style to object-param style (affects all stores — low priority, they still work)
5. **Optional**: Add `subtasks?: Task[]` to `Task` interface in `src/stores/task.ts` if that feature is planned
## Open Questions Requiring User Input
- [ ] `task.subtasks` removed from `TaskCardComponent` template — should `subtasks?: Task[]` be added to the `Task` interface for future use, or is subtask support not planned?
- [ ] Appwrite v23 deprecated positional-param overloads (hints in every store call). Migrate now or leave for later?
## Assumptions That Need Validation
- ASSUMED: Appwrite v23 positional-param API calls behave identically at runtime to v14 — validate by doing a full login + reservation flow against the dev backend
- ASSUMED: `subtasks` in `TaskCardComponent` was dead/future code — no user confirmed this
## What NOT to Re-Read
- `docs/archive/handoffs/handoff-2026-03-15-dependency-updates.md` — archived; superseded by this file
- `docs/archive/handoffs/handoff-2026-03-15-auth-magic-link.md` — archived; auth work complete
## Files to Load Next Session
- `src/stores/task.ts` — if adding `subtasks` to Task interface
- `src/boot/appwrite.ts` — if migrating to Appwrite v23 object-param style
- Any store file (`boat.ts`, `interval.ts`, `reservation.ts`, etc.) — if migrating Appwrite calls

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