Files
simone b7203c3da2 docs(12-05): complete offer editor detail page plan
- Add 12-05-SUMMARY.md documenting OfferEditorClient + edit route
2026-06-15 10:26:24 +02:00

7.1 KiB

phase, plan, subsystem, tags, requires, provides, affects, tech-stack, key-files, key-decisions, patterns-established, requirements-completed, duration, completed
phase plan subsystem tags requires provides affects tech-stack key-files key-decisions patterns-established requirements-completed duration completed
12-offer-composition-drag-drop-csv-import 05 ui
next.js
react
tailwind
server-actions
drizzle
phase provides
12-offer-composition-drag-drop-csv-import (Plan 03) getOfferEditorData/getOfferFieldOptions query layer + saveOfferEditor/toggleOfferArchived/renameOfferOption/addOfferTag/removeOfferTag server actions
/admin/offers/[id]/edit route: full offer editor page (server component + client component)
OfferEditorClient — editable name, Categoria/Ticket/Tipo/Obiettivo tags, services x tier (A/B/C) checkbox matrix with live totals, independent Prezzo Pubblico, Promessa di Trasformazione block, Salva/Annulla/Archivia actions
12-04-offer-list-page
added patterns
Tier padding to exactly 3 (A/B/C) via padTiers() so the matrix always renders 3 columns even for offers with <3 persisted tiers
Client-side useMemo for category-filtered services + live per-tier Totale Servizi (OFFER-11), zero network calls on checkbox toggle
Batched save: all macro/tier/tag edits held in local state, persisted in one saveOfferEditor() call on 'Salva Offerta' (no per-keystroke server calls for tags)
created modified
src/app/admin/offers/[id]/edit/page.tsx
src/components/admin/offers/OfferEditorClient.tsx
EditableCell.onSave is synchronous (value: string) => void in this codebase (not Promise-returning as the plan's interface sketch suggested) — all macro-field onSave handlers call setMacro synchronously, consistent with the actual component signature
renameOfferOption calls (Categoria/Ticket/Tipo/Obiettivo rename) are fired via startTransition with optimistic local-state updates to options/tags, matching ServiceTable's existing rename pattern from Phase 11
OFFER-11
OFFER-15
OFFER-16
OFFER-17
18min 2026-06-15

Phase 12 Plan 05: Offer Editor Detail Page Summary

Full-page Offer Editor at /admin/offers/[id]/edit: 4-dimension tags, category-filtered A/B/C services checkbox matrix with live "Totale Servizi", independent "Prezzo Pubblico" per tier, 5-field transformation promise, and Salva/Annulla/Archivia actions.

Performance

  • Duration: 18 min
  • Started: 2026-06-15T08:07:00Z (approx)
  • Completed: 2026-06-15T08:25:22Z
  • Tasks: 2 completed
  • Files modified: 2 (both new)

Accomplishments

  • Created src/app/admin/offers/[id]/edit/page.tsx: async server component using Next.js 16's Promise<{ id: string }> params pattern, fetches getOfferEditorData(id) + getOfferFieldOptions() via Promise.all, calls notFound() for unknown ids, delegates to OfferEditorClient
  • Created src/components/admin/offers/OfferEditorClient.tsx: client component covering the full UI-SPEC section 2 contract:
    • Header with "← Indietro" link, "Modifica Offerta" heading, editable offer name via EditableCell
    • "Tag" section: Categoria/Ticket via OptionSelect (single-select, with rename), Tipo/Obiettivo via OptionMultiSelect (multi-select, creatable on the fly via "+ Crea Tipo"/"+ Crea Obiettivo")
    • "Servizi Inclusi" matrix: services filtered by macro.category (live-recomputed via useMemo when Categoria changes), one checkbox column per tier (A/B/C), "Totale Servizi" row recalculated instantly client-side on every toggle (OFFER-11), independent "Prezzo Pubblico" numeric input per tier (OFFER-16, D-5)
    • "Promessa di Trasformazione" block: 5 EditableCell fields (Aiuto/A ottenere/In/Senza/Grazie a) mapped to cliente_ideale/risultato/tempo/pain/metodo (OFFER-17)
    • "Salva Offerta" (disabled with tooltip unless at least one tier has an assigned service) → saveOfferEditor → redirect to /admin/offers; "Annulla" → plain link back; "Archivia" (visible only when not already archived) → confirm dialog → toggleOfferArchived(id, true) → redirect
  • All literal Italian copy strings from the UI-SPEC copywriting contract table implemented verbatim

Task Commits

Each task was committed atomically:

  1. Task 1: Create editor route + page.tsx server component - 8e0e4b9 (feat)
  2. Task 2: Build OfferEditorClient — tags, services matrix with live totals, promise block, actions - 8c5c918 (feat)

Plan metadata: (this commit, follows)

Files Created/Modified

  • src/app/admin/offers/[id]/edit/page.tsx - New server component: fetches editor data + field options, 404s on missing offer, renders OfferEditorClient
  • src/components/admin/offers/OfferEditorClient.tsx - New client component: the entire offer editor UI and its Salva/Annulla/Archivia/rename interactions

Decisions Made

  • EditableCell.onSave in this codebase is (value: string) => void (synchronous), not (value: string) => void | Promise<void> as the plan's interface sketch suggested — implemented all macro-field handlers as synchronous setMacro updates, matching the real signature (verified via npx tsc --noEmit)
  • Tier padding: padTiers() always produces exactly 3 entries (A, B, C), filling missing tiers with empty placeholders (id: "", assignedServiceIds: [], servicesTotal: "0") so the matrix always renders 3 columns regardless of how many tiers exist in the DB
  • Tag rename (Categoria/Ticket/Tipo/Obiettivo) follows the existing ServiceTable/renameServiceOption optimistic-update pattern: local state updates immediately, renameOfferOption server action fires via startTransition

Deviations from Plan

None - plan executed exactly as written. The only adjustment was using the actual (synchronous) EditableCell.onSave signature instead of the plan's sketch signature, which is a same-behavior implementation detail (Rule 1 — code correctness), not a scope change.

Issues Encountered

None.

User Setup Required

None - no external service configuration required. This plan is pure UI (server + client components) consuming the already-deployed Plan 03 query layer and server actions against the live production schema (migration 0008).

Next Phase Readiness

  • /admin/offers/[id]/edit is fully functional and type-checks/lints clean across the whole project (npx tsc --noEmit and npx eslint both pass with zero errors)
  • Plan 04's list page (/admin/offers + OfferListClient.tsx, built in parallel in a separate worktree) can link directly to /admin/offers/[id]/edit — no shared files were touched, no merge conflicts expected
  • All four target requirements (OFFER-11, OFFER-15, OFFER-16, OFFER-17) are implemented end-to-end: UI -> saveOfferEditor/toggleOfferArchived/renameOfferOption (Plan 03) -> offer_macros/offer_micros/offer_tier_services/tags (Plan 01/02, live on prod)

Phase: 12-offer-composition-drag-drop-csv-import Completed: 2026-06-15

Self-Check: PASSED

All created files verified present on disk:

  • FOUND: src/app/admin/offers/[id]/edit/page.tsx
  • FOUND: src/components/admin/offers/OfferEditorClient.tsx
  • FOUND: .planning/phases/12-offer-composition-drag-drop-csv-import/12-05-SUMMARY.md

All task commits verified present in git log: