diff --git a/.planning/phases/12-offer-composition-drag-drop-csv-import/12-03-SUMMARY.md b/.planning/phases/12-offer-composition-drag-drop-csv-import/12-03-SUMMARY.md new file mode 100644 index 0000000..48caece --- /dev/null +++ b/.planning/phases/12-offer-composition-drag-drop-csv-import/12-03-SUMMARY.md @@ -0,0 +1,129 @@ +--- +phase: 12-offer-composition-drag-drop-csv-import +plan: 03 +subsystem: api +tags: [drizzle, postgres, zod, server-actions, next.js] + +# Dependency graph +requires: + - phase: 12-offer-composition-drag-drop-csv-import (Plan 01) + provides: Additive offer_macros/offer_micros columns (category, ticket, is_archived, transformation-promise fields, tier_letter, public_price) and offer_tier_services junction table, applied to prod via Plan 02 +provides: + - getOfferEditorData(macroId) — full editor data shape (macro fields, A/B/C tiers with assignedServiceIds + computed servicesTotal, category-filtered availableServices, tipoTags/obiettivoTags) + - getOfferListCards() — list-page cards with category + archive status + - getOfferFieldOptions() — categoria/ticket/tipo/obiettivo select pools + - saveOfferEditor(macroId, payload) — single-call persistence of macro scalars, per-tier upsert + offer_tier_services replace, and Tipo/Obiettivo tag replace + - toggleOfferArchived, addOfferTag, removeOfferTag, renameOfferOption, createOfferMacro server actions +affects: [12-04-offer-list-page, 12-05-offer-editor-page] + +# Tech tracking +tech-stack: + added: [] + patterns: + - "Polymorphic tags table reused for offer_macros Tipo/Obiettivo dimensions via entity_type = 'offer_macros.tipo' | 'offer_macros.obiettivo', mirroring the Phase 11 services/leads D-06 pattern" + - "Computed servicesTotal via coalesce(sum(unit_price::numeric), 0) joined through offer_tier_services -> services, mirroring getCatalogWithMicros's cumulMap pattern" + - "Delete-then-reinsert for many-to-many replacement (offer_tier_services per tier, Tipo/Obiettivo tags per macro), mirroring updateMicroOfferServices" + - "Zod enum tier_letter validation (A/B/C) as defense-in-depth alongside the Plan 01 DB CHECK constraint" + +key-files: + created: + - scripts/verify-12-03-queries.ts + - scripts/verify-12-03-actions.ts + modified: + - src/lib/offer-queries.ts + - src/app/admin/offers/actions.ts + +key-decisions: + - "No test runner configured (no scripts.test in package.json) — wrote typecheck-only verification scripts documenting the 14 spec'd test cases (5 query + 9 action), per plan's explicit fallback; not executed against prod DB" + - "saveOfferEditor uses sequential awaits (no transaction wrapper) per macro update -> tier upserts -> tag replace, matching the existing per-statement pattern in this codebase (no interactive tx helper elsewhere)" + +patterns-established: + - "OFFER_TIPO_ENTITY/OFFER_OBIETTIVO_ENTITY constants ('offer_macros.tipo'/'offer_macros.obiettivo') as the canonical entity_type values for offer-level tag dimensions — reuse in Plans 04/05 UI" + +requirements-completed: [OFFER-11, OFFER-15, OFFER-16, OFFER-17, OFFER-18] + +# Metrics +duration: 12min +completed: 2026-06-15 +--- + +# Phase 12 Plan 03: Offer Editor Query Layer & Server Actions Summary + +**Query layer (`getOfferEditorData`, `getOfferListCards`, `getOfferFieldOptions`) and server actions (`saveOfferEditor` + archive/tag/rename/create actions) giving Plans 04/05 a complete, type-safe, admin-only data contract for the offer editor.** + +## Performance + +- **Duration:** 12 min +- **Started:** 2026-06-15T08:15:00Z (approx) +- **Completed:** 2026-06-15T08:17:07Z +- **Tasks:** 2 completed +- **Files modified:** 4 (2 source, 2 new verification scripts) + +## Accomplishments + +- Added `getOfferListCards()` to `src/lib/offer-queries.ts`: returns `{ id, internal_name, description, category, is_archived }` per `offer_macros` row, ordered by `sort_order`, including archived offers (client-side filter per UI-SPEC) +- Added `getOfferEditorData(macroId)`: returns `{ macro, tiers, availableServices, tipoTags, obiettivoTags }` — tiers ordered A→B→C via SQL `CASE` expression, each with `assignedServiceIds` and a computed `servicesTotal` (`coalesce(sum(unit_price::numeric), 0)` joined through `offer_tier_services`), available services pre-filtered by `services.category === macro.category` (D-4, best-effort when category unset) +- Added `getOfferFieldOptions()`: `{ categoria, ticket, tipo, obiettivo }` distinct-value pools, mirroring `getCatalogFieldOptions`'s null-filter + alphabetical sort pattern +- Added `saveOfferEditor(macroId, payload)` to `src/app/admin/offers/actions.ts`: Zod-validates the full editor payload (`tier_letter` restricted to `["A","B","C"]`), updates `offer_macros` scalars, upserts each tier (update if `id` present, insert + `.returning({id})` if not), delete-then-reinserts `offer_tier_services` per tier, and delete-then-reinserts Tipo/Obiettivo `tags` +- Added `toggleOfferArchived`, `addOfferTag`/`removeOfferTag` (tipo/obiettivo dimensions), `renameOfferOption` (categoria/ticket/tipo/obiettivo), and `createOfferMacro` (Plan 04 "+ Nuova Offerta" target, defaults `public_name` to `internal_name`) +- All new actions call `requireAdmin()` first; existing exports in both files left untouched + +## Task Commits + +Each task was committed atomically: + +1. **Task 1: Query layer — getOfferEditorData, getOfferListCards, getOfferFieldOptions** - `0d742f2` (feat) +2. **Task 2: Server actions — saveOfferEditor, toggleOfferArchived, offer tag CRUD, createOfferMacro** - `a372c61` (feat) + +**Plan metadata:** (this commit, follows) + +## Files Created/Modified + +- `src/lib/offer-queries.ts` - Added `getOfferListCards`, `getOfferEditorData`, `getOfferFieldOptions` + `OfferListCard`/`OfferTierData`/`OfferEditorData`/`OfferFieldOptions` types; imports `offer_tier_services`, `services`, `tags`, `and` from drizzle-orm +- `src/app/admin/offers/actions.ts` - Added `saveOfferEditor`, `toggleOfferArchived`, `addOfferTag`, `removeOfferTag`, `renameOfferOption`, `createOfferMacro` + `SaveOfferEditorPayload`/`tierSchema`/`saveOfferEditorSchema` types; imports `offer_tier_services`, `tags`, `and`, `inArray` +- `scripts/verify-12-03-queries.ts` - Typecheck-only documentation of the 5 query-layer test cases (Tests 1-5 from Task 1's ``) +- `scripts/verify-12-03-actions.ts` - Typecheck-only documentation of the 9 server-action test cases (Tests 1-9 from Task 2's ``) + +## Decisions Made + +- No test runner configured in this repo (`package.json` has no `scripts.test`), and `.env.local`'s `DATABASE_URL` points at production per project memory — followed the plan's explicit fallback and wrote `scripts/verify-12-03-queries.ts` / `scripts/verify-12-03-actions.ts` as typecheck-only documentation of all 14 spec'd test cases, not executed against the live DB +- `saveOfferEditor` uses sequential `await`s (macro update → per-tier upsert/replace → tag replace) with no transaction wrapper, matching the existing per-statement pattern used by `updateMicroOfferServices` and the rest of this codebase + +## Deviations from Plan + +None - plan executed exactly as written. Both tasks implemented the documented function signatures, return shapes, and validation rules verbatim; existing exports in `offer-queries.ts` and `actions.ts` were not modified. + +## Issues Encountered + +None. + +## User Setup Required + +None - no external service configuration required. This plan is pure source-code (query/action functions); migration 0008 was already live on production (verified per Plan 02), and no data-mutating scripts were run. + +## Next Phase Readiness + +- Plan 04 (offer list page) can call `getOfferListCards()` and `createOfferMacro` for the "+ Nuova Offerta" button +- Plan 05 (offer editor page) can call `getOfferEditorData(macroId)` to populate the full editor (macro fields, tier matrix with live totals, Tipo/Obiettivo tags, category-filtered service catalog) and persist all of it via a single `saveOfferEditor(macroId, payload)` call; archive toggling via `toggleOfferArchived`; tag/option CRUD via `addOfferTag`/`removeOfferTag`/`renameOfferOption` +- `npx tsc --noEmit` passes with zero errors across the full project +- No legacy `offer_micro_services`/`offer_services`/existing-export code paths were touched + +--- + +*Phase: 12-offer-composition-drag-drop-csv-import* +*Completed: 2026-06-15* + +## Self-Check: PASSED + +All created/modified files verified present on disk: + +- FOUND: src/lib/offer-queries.ts +- FOUND: src/app/admin/offers/actions.ts +- FOUND: scripts/verify-12-03-queries.ts +- FOUND: scripts/verify-12-03-actions.ts +- FOUND: .planning/phases/12-offer-composition-drag-drop-csv-import/12-03-SUMMARY.md + +All task commits verified present in git log: + +- FOUND: 0d742f2 (Task 1) +- FOUND: a372c61 (Task 2)