diff --git a/.planning/phases/12-offer-composition-drag-drop-csv-import/12-04-SUMMARY.md b/.planning/phases/12-offer-composition-drag-drop-csv-import/12-04-SUMMARY.md new file mode 100644 index 0000000..1440bed --- /dev/null +++ b/.planning/phases/12-offer-composition-drag-drop-csv-import/12-04-SUMMARY.md @@ -0,0 +1,121 @@ +--- +phase: 12-offer-composition-drag-drop-csv-import +plan: 04 +subsystem: ui +tags: [next.js, react, tailwind, shadcn] + +# Dependency graph +requires: + - phase: 12-offer-composition-drag-drop-csv-import (Plan 03) + provides: getOfferListCards(), getOfferFieldOptions(), createOfferMacro server action +provides: + - "/admin/offers list page rewritten as category-filterable, archive-aware offer card grid (UI-SPEC section 1)" + - "OfferListClient component: category filter chips, archive toggle, '+ Nuova Offerta' inline create form, card grid linking to /admin/offers/[id]/edit" +affects: [12-05-offer-editor-page] + +# Tech tracking +tech-stack: + added: [] + patterns: + - "Thin server-component list page (Promise.all fetch -> client component), mirroring src/app/admin/catalog/page.tsx" + - "useTransition + router.refresh() for FormData server-action create flow without returning the new row's id" + +key-files: + created: + - src/components/admin/offers/OfferListClient.tsx + modified: + - src/app/admin/offers/page.tsx + +key-decisions: + - "Legacy macro/micro/offer_services CRUD UI removed from /admin/offers entirely per D-1 and UI-SPEC section 1; underlying actions.ts exports (createMacro, createMicro, createOfferService, deleteMacro, deleteMicro, updateMicroOfferServices) left untouched and unreferenced — zero-risk dead exports, confirmed via grep no other usages exist" + - "Card grid links to /admin/offers/${id}/edit (Plan 05's route) — link target correct even though Plan 05 may not have landed yet at verification time" + +patterns-established: + - "'+ Nuova Offerta' inline create-form pattern: toggle visibility, startTransition(createOfferMacro(formData)), then router.refresh() — admin clicks the new card to open the editor (no auto-navigation, no id returned from the server action)" + +requirements-completed: [OFFER-18] + +# Metrics +duration: 10min +completed: 2026-06-15 +--- + +# Phase 12 Plan 04: Offer List Page Redesign Summary + +**Rewrote `/admin/offers` from a legacy flat macro/micro/offer_services CRUD page into a category-filterable, archive-aware offer card grid with a "+ Nuova Offerta" inline creation flow, per UI-SPEC section 1.** + +## Performance + +- **Duration:** 10 min +- **Started:** 2026-06-15T08:15:00Z (approx) +- **Completed:** 2026-06-15T08:25:30Z +- **Tasks:** 2 completed +- **Files modified:** 2 (1 rewritten, 1 created) + +## Accomplishments + +- `src/app/admin/offers/page.tsx` rewritten as a thin server component: `Promise.all([getOfferListCards(), getOfferFieldOptions()])` feeding `OfferListClient`, matching the `catalog/page.tsx` pattern (239 lines of legacy macro/micro/offer_services CRUD forms removed) +- New `src/components/admin/offers/OfferListClient.tsx`: + - Category filter chip row ("Tutti" + dynamic `categoryOptions`), active state `#1A463C` border/text, inactive `#e5e7eb`/`#71717a` + - "Mostra offerte archiviate" checkbox toggle (default off), filters via `useMemo` on `activeCategory` + `showArchived` + - Responsive card grid (1/2/3 columns) — each card is a `Link` to `/admin/offers/${card.id}/edit`, showing `internal_name`, `description` (line-clamp-2), category `Badge`, and red "Archiviata" text for archived offers + - Empty state: "Nessuna offerta" / "Inizia creando la tua prima offerta" + "+ Nuova Offerta" CTA + - "+ Nuova Offerta" header CTA toggles an inline form (`Input name="internal_name"` + "Crea" button); on submit calls `createOfferMacro` via `startTransition`, then `router.refresh()` — new card appears in the grid + - All literal copy strings verbatim from UI-SPEC copywriting contract table (Italian) + +## Task Commits + +Each task was committed atomically: + +1. **Task 1: Rewrite /admin/offers page.tsx as the new list page entry point** - `68dc1b6` (feat) +2. **Task 2: Build OfferListClient — category filter, archive toggle, card grid, create CTA** - `7df4b9c` (feat) + +**Plan metadata:** (this commit, follows) + +## Files Created/Modified + +- `src/app/admin/offers/page.tsx` - Thin server component: fetches `getOfferListCards()` + `getOfferFieldOptions()`, renders `OfferListClient` +- `src/components/admin/offers/OfferListClient.tsx` - Client component: category filter chips, archive toggle, responsive card grid, "+ Nuova Offerta" inline create form using `createOfferMacro` + +## Decisions Made + +- Removed all legacy macro/micro/offer_services CRUD UI from `/admin/offers` per D-1 and UI-SPEC section 1; confirmed via `grep -rn` that no other file in `src/app`/`src/components` references `createMacro`, `createMicro`, `createOfferService`, `deleteMacro`, `deleteMicro`, `toggleOfferServiceActive`, `getCatalogWithMicros`, `getAllOfferServices`, or `ServiceCheckboxList` outside the old page — these remain exported from `actions.ts`/`offer-queries.ts` as zero-risk dead code (per plan's explicit instruction not to remove them) +- Used shadcn `Button`, `Badge`, and `Input` components for CTA, category badge, and create-form text input respectively, matching the project's existing component library usage in `CatalogSearch.tsx` + +## Deviations from Plan + +None - plan executed exactly as written. Both tasks implemented the documented component structure, state shape, filter logic, and copy strings verbatim. + +## Issues Encountered + +None. + +## User Setup Required + +None - no external service configuration required. Pure UI change consuming Plan 03's existing query/action exports. + +## Next Phase Readiness + +- `/admin/offers` now renders the new card-grid list per UI-SPEC section 1; OFFER-18 satisfied (category filter + archive toggle, default hides archived) +- Each card links to `/admin/offers/${id}/edit` — Plan 05 builds this route in a parallel worktree; link target is correct regardless of Plan 05's landing order +- `npx tsc --noEmit` passes with zero errors across the full project; `npx eslint` clean for both modified/created files +- No legacy macro/micro/offer_services CRUD routes remain; underlying actions/queries left as unreferenced exports for future cleanup (out of this plan's scope) + +--- + +*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/app/admin/offers/page.tsx +- FOUND: src/components/admin/offers/OfferListClient.tsx +- FOUND: .planning/phases/12-offer-composition-drag-drop-csv-import/12-04-SUMMARY.md + +All task commits verified present in git log: + +- FOUND: 68dc1b6 (Task 1) +- FOUND: 7df4b9c (Task 2) +- FOUND: ca37126 (docs: plan summary) diff --git a/src/app/admin/offers/page.tsx b/src/app/admin/offers/page.tsx index b7a0919..61d8008 100644 --- a/src/app/admin/offers/page.tsx +++ b/src/app/admin/offers/page.tsx @@ -1,238 +1,17 @@ -import { getCatalogWithMicros, getAllOfferServices } from "@/lib/offer-queries"; -import { - createMacro, - createMicro, - createOfferService, - deleteMacro, - deleteMicro, - toggleOfferServiceActive, -} from "./actions"; -import { ServiceCheckboxList } from "@/components/admin/offers/ServiceCheckboxList"; +import { getOfferListCards, getOfferFieldOptions } from "@/lib/offer-queries"; +import { OfferListClient } from "@/components/admin/offers/OfferListClient"; export const revalidate = 0; export default async function OffersPage() { - const [catalog, allServices] = await Promise.all([ - getCatalogWithMicros(), - getAllOfferServices(), + const [cards, options] = await Promise.all([ + getOfferListCards(), + getOfferFieldOptions(), ]); return ( -
{macro.internal_name}
-Pubblico: {macro.public_name}
- {macro.transformation_promise && ( -{macro.transformation_promise}
- )} -Micro-Offerte
- - {macro.micros.map((micro) => ( -{micro.internal_name}
-- Pubblico: {micro.public_name} ·{" "} - {micro.duration_months} {micro.duration_months === 1 ? "mese" : "mesi"} -
- {micro.transformation_promise && ( -{micro.transformation_promise}
- )} -- Prezzo cumulativo: €{parseFloat(micro.cumulative_price).toFixed(2)} -
-Servizi inclusi:
-Nessuna macro-offerta ancora. Creane una sopra.
- )} -- I servizi qui sono diversi dal catalogo preventivi — hanno una descrizione della - trasformazione e vengono raggruppati nelle micro-offerte. -
- - {/* List services */} -{svc.name}
- {svc.transformation_description && ( -{svc.transformation_description}
- )} -Nessun servizio ancora.
- )} -Inizia creando la tua prima offerta
+{card.description}
+ )} +