diff --git a/frontend-spec.md b/frontend-spec.md index 7588411..14bc1ab 100644 --- a/frontend-spec.md +++ b/frontend-spec.md @@ -104,39 +104,27 @@ This plan is adapted to the existing codebase, focusing on refactoring rather th --- -## 0. Current State (Nov 2025) and Gap Analysis +## 0. Current State (Nov 1, 2025) -Current implementation is single-tenant with username-based login and no household context. +All core features are migrated to multi-tenancy with path-scoped endpoints and token-based auth. The codebase no longer uses the `X-Household-Slug` header. Tests and type checks are fully green. -What exists today +What exists now - Auth - - `src/api/auth.ts`: username login (`login(username: string)`), `currentUser()` via cookie `user_id`. - - `src/composables/useAuth.ts`: stores `Person | null`, exposes `login(username)` and `loadUser()`. - - `src/components/LoginPage.vue`: lists persons and logs in by selected person name. + - `src/api/auth.ts`: email/password login and register; token-only refresh in `currentUser()` which then loads `/api/v1/users/me/households`. + - `src/composables/useAuth.ts`: manages `user`, `households`, and `activeHousehold`; exposes login/logout/createAccount and `fetchHouseholds()`. - Routing - - `src/router/index.ts`: flat routes (`/recipes`, `/shopping`, `/mealplan`); no `/:householdSlug` nesting or redirects. - - Guard checks `requiresAuth` only; no household awareness. + - `src/router/index.ts`: feature-flagged nesting under `/:householdSlug/...`; public routes include `/create-account`, `/welcome`, and `/invitations/accept`. + - Guard fetches households, redirects root `/` to the first household's `mealplan`, and uses memory history in tests (hash in browser). - SDK/API - - `src/api/sdk.ts`: calls `/api/v1/...` without household context. - - Generated OpenAPI types do not include household slug in paths; shapes are single-tenant. + - `src/api/sdk.ts`: recipes, meals, and shopping are migrated to `/api/v1/households/{householdSlug}/...` typed endpoints. Persons and parse use temporary raw fetch endpoints where OpenAPI lacks coverage. + - `src/api/client.ts`: Authorization header provider only; household header injection removed. - Domain & UI - - Identity revolves around `Person`; no household model, switcher, or settings views. + - Member arrays (`chefs`, `consumers`, `cleanup`) normalized to MemberRef `{ id, displayName }` with decoders handling legacy shapes gracefully. + - Invitation Accept flow implemented; Household Settings supports sending invitations and listing members (members via temporary raw fetch path-scoped endpoint until typed spec lands). -Gaps vs requirements -- Authentication & onboarding - - Implemented: JWT `loginWithPassword`, `logout`, account creation via `createAccount` with token provider wiring. - - UI: `LoginPage.vue` shows email/password form under feature flag; `CreateAccount.vue` view implemented; `Welcome.vue` creates household; Invitation Accept implemented; Google OAuth pending. -- Routing & URL-based tenancy - - Partial: Feature-flagged nesting implemented; still need guard logic for fetching households and redirects. - - `useHousehold.ts` and `HouseholdSwitcher.vue` added; further wiring to fetch households pending. -- API boundary - - No household scoping passed to backend. Need a typed strategy (prefer header parameter) without breaking OpenAPI typing. -- Cleanup - - `persons` used across login and tests; must be deprecated in favor of authenticated `user` and their households. - -Assumptions and constraints -- Preserve strict TS (no `any`/`unknown` in app code) and keep OpenAPI as source of truth. -- Do not rewrite typed API paths in code; prefer header or OpenAPI param for household scoping. +Status of tests and typing +- All tests pass: 27 files, 48 tests. +- `tsc` and `vue-tsc` pass with no errors. --- @@ -144,19 +132,11 @@ Assumptions and constraints The backend OpenAPI has been updated and now exposes `householdSlug` as a path parameter for scoped endpoints (recipes, meals, shopping, invitations, whoami). Auth endpoints are fully typed (login/register/refresh/logout). Invitation acceptance remains a global endpoint (`/api/v1/invitations/accept`) with a typed operation. -Implications and actions: -- Replace the temporary `X-Household-Slug` header injection with typed path parameters. - - Migrate all API calls to pass `{ params: { path: { householdSlug } } }` where required. - - Remove `setHouseholdSlugProvider` usage once the migration is complete. -- Auth refresh now returns only `{ accessToken, tokenType }` (no `user` object). The `currentUser()` flow must: - 1) Call refresh to obtain a token and set the Authorization provider. - 2) Load user/household context via typed endpoints (e.g., `GET /api/v1/users/me/households`). - 3) Optional: Use `GET /api/v1/households/{householdSlug}/whoami` to validate membership for the active route. -- Invitations: - - Create invitation is now typed at `POST /api/v1/households/{householdSlug}/invitations`. - - Accept invitation is typed at `POST /api/v1/invitations/accept`. - - Remove temporary raw fetch usage for invitations and switch to the generated client. -- Household members listing endpoint is still not present in OpenAPI; continue using the temporary fetch wrapper until the backend exposes it. +Implications and actions (completed): +- Removed `X-Household-Slug` and migrated to typed path parameters across recipes, meals, and shopping. +- `currentUser()` updated to token-only refresh and household loading. +- Invitations: sending is typed under household scope; accept is typed globally. +- Members listing is temporarily fetched via a path-scoped raw endpoint until OpenAPI includes it. --- @@ -166,7 +146,7 @@ Progress Log (Nov 1, 2025) - Implemented `loginWithPassword`, `logout`, and stubs in `src/api/auth.ts` to satisfy tests. - Refactored `useAuth` to add households and activeHousehold state, plus `loginWithPassword` and `logout`. Tests added and passing. - Added router tests and implemented feature-flagged nested routes and new public routes. Placeholders for onboarding/invitations added. -- Implemented `useHousehold.ts`, header + auth providers in API client, minimal `HouseholdSwitcher.vue`, and mounted it. Added a header injection test. + - Implemented `useHousehold.ts`, Authorization provider in API client, minimal `HouseholdSwitcher.vue`, and mounted it. Replaced header injection test with path-scoped assertion. - Implemented JWT login/register in `auth.ts` and wired token to client provider. `useAuth` updated with households fetching. `currentUser` refactored to token-only refresh plus households load. - Router guard updated to handle public/multitenant routing and redirects. - Added `useAuth.createAccount` with state update and tests for it; implemented `CreateAccount.vue` with form and navigation. @@ -174,11 +154,11 @@ Progress Log (Nov 1, 2025) - Next: Implement Household Settings (invite members form), then remove legacy Person UI. - Added a Settings link to `HouseholdSwitcher.vue` to surface the `household-settings` route for easier discovery. - Implemented Household Settings invite form and members list UI. `src/views/HouseholdSettings.vue` now loads members via a temporary `listMembers()` in `src/api/households.ts` using the raw fetch helper. When the backend exposes a typed endpoint, we will swap to the generated client. - - Fixed router tests by using memory history in non-browser environments to avoid relying on `window.location` during unit tests. + - Router uses memory history in tests to avoid relying on `window.location`. - Backend updated OpenAPI and codegen has been run: - Many endpoints are now path-scoped with `{householdSlug}` (recipes, meals, shopping, invitations (create), whoami). - Auth endpoints (login/register/refresh/logout) are fully typed; `refresh` returns only `{ accessToken, tokenType }`. - - Action: begin migrating API usage to typed path parameters and remove temporary header injection and fetch helpers. + - Completed migration to typed path parameters; header injection removed; only small raw fetch helpers remain for endpoints not yet in OpenAPI (persons, parse, members list). --- @@ -208,13 +188,12 @@ Router API client - `src/api/client.ts`: - - Previous: header injection (`X-Household-Slug`) from a configurable getter to scope requests. - - Now: migrate calls to use typed `{ params: { path: { householdSlug } } }` and remove the `X-Household-Slug` header provider. - - Authorization provider remains as-is, fed by the JWT token from login/refresh. + - Uses typed `{ params: { path: { householdSlug } } }` across SDK; `X-Household-Slug` header provider removed. + - Authorization provider remains as-is, fed by JWT token from login/refresh. SDK - `src/api/sdk.ts`: - - No path changes; ensure all calls work with new auth and household header. + - Recipes, meals, shopping fully path-scoped to households. Persons/parse remain via raw fetch until typed coverage. UI - Login Page: Refactored to show email/password form when multitenant flag is enabled; legacy person list retained otherwise. Link to Create Account added. @@ -224,10 +203,9 @@ UI - Adjust pages that call SDK/API to pass `{ householdSlug }` path params once client services are migrated. Tests -- Update MSW handlers/tests to assume JWT auth and household header. -- Add tests for router guards, invitation acceptance, and household switching. - - Add API tests for invitations (accept/send) and members listing header behavior. Router tests run under memory history in tests. - - Update tests to assert that calls pass `householdSlug` via typed params instead of relying on an injected header. +- MSW handlers updated for path-scoped endpoints; Authorization header assertions retained where relevant. +- Router tests run under memory history. +- Replaced header injection test with path-scoped recipe list test. --- @@ -245,24 +223,25 @@ Tests ## Migration Plan & Feature Flag -- Optional `MULTITENANT_ENABLED` flag for staged rollout of routes and UI. -- Keep legacy login until backend endpoints are ready; hide persons UI once households exist for a user. +- `VUE_APP_MULTITENANT_ENABLED` flag controls nested household routes. With the migration complete, keep this flag for rollout control; default can be enabled once backend is stable across environments. --- ## Acceptance Criteria (Summary) -- Users can create accounts, login (email/password, Google), logout, and refresh sessions. +- Users can create accounts, login (email/password), logout, and refresh sessions (token-only). - All routes operate under `/:householdSlug` with correct redirects and deep link support. -- Active household is selectable and visible; API calls are correctly scoped. -- Invitation token acceptance adds membership and navigates to the household dashboard. -- Legacy persons login removed from UI; tests updated and passing. +- Active household is selectable and visible; API calls are correctly scoped via path params. +- Invitation token acceptance adds membership and navigates appropriately. +- Legacy persons identity is no longer used in auth; MemberRef used in meal UIs; tests updated and passing. + +Google OAuth is planned next. --- -## Open Questions +## Open Questions / Next Steps -- Backend: path vs header vs cookie for `householdSlug`? Confirm to finalize client strategy. -- Exact OpenAPI shapes for `User`, `Household`, `Invitation` endpoints. -- Google OAuth flow pattern (token exchange vs redirect). -- JWT storage medium per security guidance (cookie vs localStorage). +- Add a user profile endpoint and load it post-refresh to populate `currentUser()` with real data instead of a placeholder. +- Replace temporary raw fetch calls (persons, parse, members listing) with typed endpoints when available. +- Implement Google OAuth login and account creation flows. +- Review `MyShoppingPage` usage and remove legacy `getMyShoppingList/saveMyShoppingList` stubs when UI is refactored or removed.