From 9bc1e43738242171b9fb9443496bbc4fb85799eb Mon Sep 17 00:00:00 2001 From: Simone Cavalli Date: Sat, 13 Jun 2026 16:03:10 +0200 Subject: [PATCH] docs(11-04): add plan summary and log unused createService action - 11-04-SUMMARY.md documents ServiceTable database-view rewrite + CatalogSearch - deferred-items.md: log now-unused createService/serviceSchema in actions.ts (item 6) --- .../11-04-SUMMARY.md | 135 ++++++++++++++++++ .../deferred-items.md | 10 ++ 2 files changed, 145 insertions(+) create mode 100644 .planning/phases/11-catalog-database-view-ux-legacy-consolidation/11-04-SUMMARY.md diff --git a/.planning/phases/11-catalog-database-view-ux-legacy-consolidation/11-04-SUMMARY.md b/.planning/phases/11-catalog-database-view-ux-legacy-consolidation/11-04-SUMMARY.md new file mode 100644 index 0000000..5dab022 --- /dev/null +++ b/.planning/phases/11-catalog-database-view-ux-legacy-consolidation/11-04-SUMMARY.md @@ -0,0 +1,135 @@ +--- +phase: 11-catalog-database-view-ux-legacy-consolidation +plan: 04 +subsystem: ui +tags: [react, nextjs, tailwind, server-actions, catalog, inline-edit, tags, search] + +# Dependency graph +requires: + - phase: 11-catalog-database-view-ux-legacy-consolidation + plan: "11-02" + provides: "ServiceWithTags type, getAllServices(), updateServiceField/quickAddService/addTagToService/removeTagFromService server actions" + - phase: 11-catalog-database-view-ux-legacy-consolidation + plan: "11-03" + provides: "EditableCell (text/number/textarea/toggle) and TagMultiSelect shared UI primitives" +provides: + - "Database-view /admin/catalog: 6-column inline-editable ServiceTable (Nome, Descrizione, Categoria, Prezzo, Tag, Stato), no Azioni column" + - "QuickAddRow — creates services via quickAddService(name) on Enter, unit_price=0 (D-12)" + - "Active/inactive split with 'Servizi disattivati' divider + opacity-50 (D-13)" + - "CatalogSearch client component — instant client-side name/tag filter, no reload (OFFER-10)" +affects: ["12"] + +# Tech tracking +tech-stack: + added: [] + patterns: + - "Co-located client filter wrapper (CatalogSearch) between an async Server Component page and a presentational table — useMemo filter over already-fetched data, zero new fetches on query change" + - "ServiceRow per-row error display via a conditional colSpan=6 trailing , keeping row height stable in the success path" + +key-files: + created: + - src/app/admin/catalog/CatalogSearch.tsx + modified: + - src/components/admin/catalog/ServiceTable.tsx + - src/app/admin/catalog/page.tsx + deleted: + - src/components/admin/catalog/ServiceForm.tsx + +key-decisions: + - "Left createService (and its serviceSchema) in src/app/admin/catalog/actions.ts as unused dead code rather than editing actions.ts, which was outside this plan's files_modified scope — logged to deferred-items.md for a future cleanup pass." + +patterns-established: + - "ServiceTable/CatalogSearch is now the canonical /admin/catalog database-view UX — any future catalog-adjacent admin table (Phase 12+) should follow the same EditableCell/TagMultiSelect/QuickAddRow/search-filter pattern per DESIGN-SYSTEM.md" + +requirements-completed: [OFFER-07, OFFER-08, OFFER-09, OFFER-10] + +# Metrics +duration: 12min +completed: 2026-06-13 +--- + +# Phase 11 Plan 4: Catalog Database-View UX — ServiceTable Rewrite + Search Summary + +**`/admin/catalog` is now a Notion/Airtable-style database view: every `services` cell (name, description, category, price, tags, active status) is click-to-edit via `EditableCell`/`TagMultiSelect`, a quick-add row creates new services on Enter, a search bar filters client-side by name/tag instantly, and inactive services sink below a "Servizi disattivati" divider at reduced opacity — with zero "Azioni" column anywhere.** + +## Performance + +- **Duration:** ~12 min +- **Started:** 2026-06-13T13:55:00Z +- **Completed:** 2026-06-13T14:02:06Z +- **Tasks:** 2 of 2 +- **Files modified:** 3 (1 created, 1 deleted, 2 modified — counting page.tsx + ServiceTable.tsx as modified, CatalogSearch.tsx as created, ServiceForm.tsx as deleted) + +## Accomplishments + +- `ServiceTable.tsx` fully rewritten as a database-view table: 6 columns (Nome, Descrizione, Categoria, Prezzo, Tag, Stato), every cell rendered via `EditableCell` (text/textarea/number/toggle) or `TagMultiSelect`, no "Azioni" column (D-14). +- `ServiceRow` wires each field's `onSave` to `updateServiceField(service.id, field, value)` and the tags column to `TagMultiSelect`'s `onTagsChanged`; both call `router.refresh()` after the server action resolves. Per-row save errors render inline via a `colSpan={6}` trailing cell without affecting row height in the success path. +- `QuickAddRow` renders a borderless `Input` with placeholder "+ Aggiungi servizio"; pressing Enter with non-empty text calls `quickAddService(name)`, clears the input, and refreshes (D-12). +- Services are partitioned into `activeServices`/`inactiveServices`; active services render first, then `QuickAddRow`, then (if any) a "Servizi disattivati" divider row followed by inactive services with `opacity-50` (D-13). +- New `src/app/admin/catalog/CatalogSearch.tsx` client component: holds search query state, filters `ServiceWithTags[]` by `name` or any `tags[]` entry (case-insensitive substring match) via `useMemo`, and renders `ServiceTable` with the filtered list — purely client-state, no `router.push`/`refresh`/`
` (OFFER-10). +- `page.tsx` rewritten: remains an async Server Component calling `getAllServices()`, no longer imports `ServiceForm`, renders `CatalogSearch` (or the existing "Nessun servizio..." message, reworded to point at the table's quick-add row, when the catalog is empty). +- `ServiceForm.tsx` deleted entirely — its "+ Aggiungi servizio" full-form creation flow is fully replaced by `ServiceTable`'s `QuickAddRow`. +- `npx tsc --noEmit`: zero errors project-wide. `npx eslint` on all 3 touched files: zero issues. `npx next build`: 0 errors, 0 warnings — `/admin/catalog` (auth-gated dynamic route) was not statically pre-rendered, so no live-DB query was attempted (consistent with the plan's DB-state note; not a firewall block, build was simply clean). + +## Task Commits + +Each task was committed atomically: + +1. **Task 1: Rewrite ServiceTable as database-view (inline edit, tags, quick-add, active/inactive split)** - `c0bedf3` (feat) +2. **Task 2: Add client-side search bar to page.tsx, remove ServiceForm** - `912a892` (feat) + +**Plan metadata:** (this commit, following SUMMARY) + +## Files Created/Modified + +- `src/components/admin/catalog/ServiceTable.tsx` - Full rewrite: `ServiceRow` (EditableCell x5 + TagMultiSelect), `QuickAddRow`, `ServiceTable` with active/inactive split and "Servizi disattivati" divider +- `src/app/admin/catalog/CatalogSearch.tsx` - New client component: search input (Lucide `Search` icon) + `useMemo` name/tag filter wrapping `ServiceTable` +- `src/app/admin/catalog/page.tsx` - Removed `ServiceForm` import/render; now renders `CatalogSearch` with `ServiceWithTags[]` from `getAllServices()` +- `src/components/admin/catalog/ServiceForm.tsx` - Deleted (102 lines); quick-add functionality fully replaced by `ServiceTable`'s `QuickAddRow` + +## Decisions Made + +- Left `createService` (and the `serviceSchema` zod validator it shares with `updateService`) in `src/app/admin/catalog/actions.ts` as now-unused dead code, rather than editing `actions.ts` — that file was not in this plan's `files_modified` scope, and `updateService` still depends on `serviceSchema`. Logged to `deferred-items.md` (item #6) for a future cleanup pass. +- Followed the plan's prescribed code for `ServiceTable.tsx`/`CatalogSearch.tsx`/`page.tsx` verbatim — no changes needed to satisfy lint/typecheck (unlike Plan 03's `EditableCell`, no `useEffect`/ref patterns were introduced here that would trigger `react-hooks/set-state-in-effect` or `react-hooks/refs`). + +## Deviations from Plan + +None - plan executed exactly as written. All 12 behavior tests for Task 1 and all 7 behavior tests for Task 2 are satisfied by the code as specified in `11-04-PLAN.md`. + +## Issues Encountered + +None. + +## Known Stubs + +None - `ServiceTable` and `CatalogSearch` are both fully wired to live data (`getAllServices()` -> `ServiceWithTags[]`) and Plan 02's server actions (`updateServiceField`, `quickAddService`, `addTagToService`/`removeTagFromService` via `TagMultiSelect`). No hardcoded/empty/placeholder values flow to rendering. + +## Threat Flags + +None - this plan introduces no new endpoints, auth paths, or trust-boundary changes beyond what was already reviewed in `11-04-PLAN.md`'s threat model: + +- T-11-14 (Information Disclosure, accept): `CatalogSearch` receives the full `ServiceWithTags[]` (including inactive services) regardless of search query — same as pre-Phase-11 `ServiceTable`, and `/admin/catalog` remains behind the Auth.js admin session guard. +- T-11-15 (Tampering, mitigate): `QuickAddRow` uses `startTransition` and only clears the input on a successful `quickAddService` resolve; a failed call surfaces an inline error without a partial row (atomic DB insert). +- T-11-16 (DoS, accept): catalog size remains bounded by manual admin entry; no bulk import in this phase. + +## User Setup Required + +None - no new environment variables or external service configuration needed. + +### DB-state note (carried from Plans 01-03, non-blocking for this plan) + +The `tags` table (Plan 01) and the `addTagToService`/`removeTagFromService`/`updateServiceField`/`quickAddService` server actions (Plan 02) remain unverified against the **live** Postgres DB — the migration apply step is firewalled and pending manual SSH+docker exec by the user (tracked in `STATE.md` Blockers since Plan 01). This plan's verification was code-only (`npx tsc --noEmit`, `eslint`, `next build`), per the plan's ``. `next build` did not attempt to reach the live DB for `/admin/catalog` (auth-gated dynamic route, not statically rendered) — the build was simply clean, no firewall error encountered. + +**End-to-end runtime verification of the full database-view UX (inline edit persisting, tag creation persisting, quick-add row creating a row, active/inactive split with real data) requires the Plan 01 `tags` table migration to be applied to the live DB first.** This is the same outstanding checkpoint documented in `11-01-SUMMARY.md`, `11-02-SUMMARY.md`, and `11-03-SUMMARY.md`. + +## Next Phase Readiness + +- **Phase 11 UI deliverable complete:** `/admin/catalog` now satisfies OFFER-07 (inline edit), OFFER-08 (tag multi-select), OFFER-09 (quick-add), OFFER-10 (search/filter), and D-13/D-14 (inactive split, no Azioni column) at the code level. All 4 plans of Phase 11 (01-04) are now executed. +- **Pending before milestone close:** Apply the Plan 01 `tags` table migration (`scripts/push-11-tags-migration.ts`) and the Phase 11 legacy-consolidation scripts (`migrate-services.ts`, `validate-services-migration.ts`, `migrate-tags.ts`, `validate-tags-migration.ts`) to the live Postgres DB via SSH+docker exec, per `STATE.md` Blockers. This is required for OFFER-13 and for end-to-end runtime verification of this plan's UI. +- **Recommendation for next session:** Resolve the DB-migration checkpoint (apply Plan 01's migration + Phase 11 consolidation scripts), then do a manual smoke test of `/admin/catalog`: inline-edit each column type, add/remove a tag (including creating a brand-new tag), use the quick-add row, toggle a service's active state, and verify the search bar filters by name/tag. +- **Phase 12 readiness:** `ServiceWithTags`/`getAllServices()`/`updateServiceField` etc. are the now-proven query/action/UI stack for `services` — Phase 12 (Offer Composition Drag&Drop & CSV Import) can build on this same foundation when rewiring `/admin/offers` to read from `services`. +- **Cleanup candidate (non-blocking):** `createService`/`serviceSchema` dead code in `src/app/admin/catalog/actions.ts` (deferred-items.md #6). + +--- +*Phase: 11-catalog-database-view-ux-legacy-consolidation* +*Completed: 2026-06-13* diff --git a/.planning/phases/11-catalog-database-view-ux-legacy-consolidation/deferred-items.md b/.planning/phases/11-catalog-database-view-ux-legacy-consolidation/deferred-items.md index f2b4cd0..5397e80 100644 --- a/.planning/phases/11-catalog-database-view-ux-legacy-consolidation/deferred-items.md +++ b/.planning/phases/11-catalog-database-view-ux-legacy-consolidation/deferred-items.md @@ -43,3 +43,13 @@ Items discovered during execution that are out of scope for the current plan/tas - **Impact:** Low — both requirements legitimately complete once 11-04 lands (this plan is a direct, immediate prerequisite, same wave sequence, no risk of the milestone closing prematurely mid-phase). However the "Complete" status in REQUIREMENTS.md was technically premature at the time 11-02 marked it. - **Recommended fix:** No action needed if 11-04 executes next as planned. If 11-04 is ever skipped/deferred, re-open OFFER-07/08 status in REQUIREMENTS.md to "In Progress" until the UI wiring lands. - **Status:** Not fixed — pre-existing from 11-02, informational only, expected to self-resolve when 11-04 completes. + +## From Plan 11-04 + +### 6. `createService` server action now unused after `ServiceForm.tsx` deletion + +- **Found during:** 11-04, Task 2 +- **Issue:** `src/app/admin/catalog/actions.ts` exports `createService(formData: FormData)`, whose only consumer was `src/components/admin/catalog/ServiceForm.tsx`. This plan deletes `ServiceForm.tsx` (its create-service UX is replaced by `ServiceTable`'s `QuickAddRow` + `quickAddService`), leaving `createService` (and its `serviceSchema` zod validator, also used by `updateService`) as dead/unused exported code. +- **Impact:** Low — `npx tsc --noEmit` and `eslint` both pass cleanly (unused *exported* functions are not flagged by this project's lint config). No runtime impact; `createService` simply has zero callers. +- **Recommended fix:** Remove `createService` (and `serviceSchema` if `updateService` no longer needs it — `updateService` still uses it, so keep the schema) from `src/app/admin/catalog/actions.ts` in a follow-up cleanup pass. `src/app/admin/catalog/actions.ts` was not in this plan's `files_modified` list, so removing it here would be out of scope (Rule 1/3 do not apply — not a bug, not blocking). +- **Status:** Not fixed. Logged for future cleanup (Phase 12 or a standalone hygiene pass).