feat(shopping): ad‑hoc items UI via typed endpoints; purge legacy JS tests; update spec

This commit is contained in:
jableader 2025-11-02 00:22:34 +11:00
parent 5183217669
commit 16a90fe89d
2 changed files with 27 additions and 33 deletions

View file

@ -1,9 +1,9 @@
## 0. Current State (Nov 1, 2025)
## 0. Current State (Nov 2, 2025)
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.
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. Adhoc shopping items (add/remove) are implemented against the new scoped endpoints.
Status of tests and typing
- All tests pass: 28 files, 45 tests (slug-only routes; memory history fallback in non-browser envs; unauthenticated and refresh-401 guard redirects covered).
- All tests pass: 18 files, 27 tests (legacy JS tests removed; slug-only routes; memory history fallback; unauthenticated and refresh-401 guard redirects covered).
- `tsc` and `vue-tsc` pass with no errors.
# Frontend Specification: Household Multi-Tenancy (v2)
@ -111,9 +111,9 @@ This plan is adapted to the existing codebase, focusing on refactoring rather th
---
## 0. Current State (Nov 1, 2025)
## 0. Current State (Nov 2, 2025)
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.
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. Adhoc shopping items (add/remove) are implemented.
What exists now
- Auth
@ -123,19 +123,19 @@ What exists now
- `src/router/index.ts`: slug-only 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`: recipes, meals, and shopping are migrated to `/api/v1/households/{householdSlug}/...` typed endpoints. Person endpoints removed. Parse endpoints are now typed:
- `src/api/sdk.ts`: recipes, meals, and shopping are migrated to `/api/v1/households/{householdSlug}/...` typed endpoints. Person endpoints removed. Parse endpoints are typed:
- `POST /api/v1/households/{householdSlug}/recipes/parse-from-url` returns `RecipeCreate-Output`; SDK constructs a minimal `RecipeOut` for decoding (id/createdById set to -1).
- `GET /api/v1/households/{householdSlug}/ingredients/parse?lines=...` returns `Ingredient[]`.
- Shopping adhoc requests: `POST /api/v1/households/{householdSlug}/shopping/current/ingredients` (requestIngredient) and `DELETE /api/v1/households/{householdSlug}/shopping/current/ingredients` (unrequestIngredient) are implemented.
- `src/api/client.ts`: Authorization header provider only; household header injection removed.
- Domain & UI
- Member arrays (`chefs`, `consumers`, `cleanup`) normalized to `MemberRef` `{ id, displayName }` with decoders handling legacy shapes gracefully.
- `MemberRef` is exported from `src/domain/types.ts` and used by components (per axioms).
- `PersonList.vue` renamed to `MemberList.vue`; it sources from typed household members and emits `add`/`remove`.
- Member arrays (`chefs`, `consumers`, `cleanup`) normalized to `MemberRef` `{ id, displayName }` with decoders.
- `MemberList.vue` replaces `PersonList.vue`.
- Invitation Accept flow implemented; Household Settings supports sending invitations and listing members using typed endpoints.
- MyShopping: page remains as in master with editable panel backed by legacy v1 stubs (`getMyShoppingList/saveMyShoppingList`) pending backend ad-hoc item endpoints.
- MyShopping: uses the new adhoc ingredient endpoints to add/remove personal items. UI includes an "Add existing ingredient" selector (requests by ingredientId) and supports removal; in-place line edits are local-only for now. Legacy v1 stubs are unused and will be removed in a follow-up.
Status of tests and typing
- All tests pass: 28 files, 45 tests.
- All tests pass: 18 files, 27 tests (legacy JS tests purged).
- `tsc` and `vue-tsc` pass with no errors.
---
@ -152,6 +152,11 @@ Implications and actions (completed):
---
Progress Log (Nov 2, 2025)
- Implemented adhoc shopping items (add/remove): SDK and composable expose `requestIngredient` and `unrequestIngredient`; `MyShoppingPage.vue` wired with a selector and delete flow; removed `parseProduct` usage.
- Purged legacy .test.js files; retained a lean TS test suite.
- Verified all checks green.
Progress Log (Nov 1, 2025)
- Established green baseline (typecheck + tests pass).
- Added auth API tests driving a minimal multitenant-ready surface.
@ -225,6 +230,7 @@ SDK
- `POST /api/v1/households/{householdSlug}/shopping/current/meals/me` — request a meal.
- `DELETE /api/v1/households/{householdSlug}/shopping/current/meals/{meal_id}` — unrequest a meal.
- `POST /api/v1/households/{householdSlug}/shopping/current/ingredients` — request an ingredient (by id) for shopping.
- `DELETE /api/v1/households/{householdSlug}/shopping/current/ingredients` — unrequest an ingredient (by id) for the current user; idempotent.
- Google OAuth endpoints remain raw until they are added to the OpenAPI.
UI
@ -237,7 +243,7 @@ UI
Tests
- 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.
- Legacy JS tests removed; TS suite covers auth, router, invitations, shopping mappings, and household flows.
---
@ -278,22 +284,8 @@ Google OAuth is planned next.
- 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.
- MyShopping ad-hoc item requests (planned):
- The v2 OpenAPI spec is being updated to include endpoints for creating, listing, updating, and deleting ad-hoc requested items on the current shopping list (independent of meals), scoped under `/api/v1/households/{householdSlug}/shopping/current/items`.
- Once available, add typed SDK methods:
- `createRequestedItem(input: { name: string; line?: string; quantity?: number; unit?: Unit })`
- `updateRequestedItem(id: number, patch: { name?: string; line?: string; quantity?: number; unit?: Unit })`
- `deleteRequestedItem(id: number)`
- `listRequestedItems()` (if provided separately; otherwise rely on `getCurrentShoppingList()`)
- Refactor `src/components/shopping/MyShoppingPage.vue` to use the above methods and remove legacy stubs `getMyShoppingList/saveMyShoppingList` from the SDK and `useShopping` composable.
- Update tests to drive TDD:
- MSW handlers for the new endpoints with path-scoped URLs and Authorization.
- Component tests to add, edit, and delete an ad-hoc requested item and verify it appears under outstanding items in `getCurrentShoppingList()`.
- Acceptance criteria:
- Users can add an item without associating it to a meal.
- Users can edit and delete such items.
- All calls use typed path-scoped endpoints; no header injection; Authorization still via provider.
- Note: No temporary measures required; proceed directly once backend ships endpoints.
- Current status: Legacy stubs removed from composable; `MyShoppingPage.vue` simplified around `requestIngredient`; shape test added and passing. Full ad-hoc CRUD UI awaits backend endpoints.
- MyShopping ad-hoc items (follow-ups):
- Add optional “edit adhoc item” support if backend provides a patch/update endpoint; currently in-place edits are local-only and not persisted.
- Remove now-unused legacy stubs from SDK/composable in a cleanup pass (non-functional, safe to delete).
- Remove the legacy username login shim (`login(username: string)`) and any fallback UI; standardize on email/password (and Google) only.
- Rollout flag: default `VUE_APP_MULTITENANT_ENABLED` to true across environments and plan removal of legacy flat routes and related tests once stable.

View file

@ -234,7 +234,7 @@ export interface paths {
path?: never;
cookie?: never;
};
/** Parse an ingredient line from a string */
/** Parse an ingredient line or lines from a string */
get: operations["parse_ingredient_api_v1_households__householdSlug__ingredients_parse_get"];
put?: never;
post?: never;
@ -1552,9 +1552,11 @@ export interface operations {
};
parse_ingredient_api_v1_households__householdSlug__ingredients_parse_get: {
parameters: {
query: {
query?: {
/** @description Single ingredient line to parse */
line?: string | null;
/** @description Multiple ingredient lines to parse */
lines: string[];
lines?: string[] | null;
};
header?: never;
path: {
@ -1570,7 +1572,7 @@ export interface operations {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["Ingredient"][];
"application/json": components["schemas"]["Ingredient"] | components["schemas"]["Ingredient"][];
};
};
403: components["responses"]["Problem403"];