- LeadsViewToggle: client wrapper with useState<list|kanban>, useMemo filtered
- Search input (left) + pill toggle Lista/Kanban (right) on single row
- Filters leads by name/email/company/status/tags across both views
- Search state preserved when switching between Lista and Kanban
- LeadsSearch.tsx: replaced with re-export of LeadsViewToggle (backward compat)
- Import 58 offer membership tags for all 55 services (entity_type="services")
via scripts/import-service-offer-tags.ts (already run on prod)
- ServiceTable: remove Categoria column, rename Tag → Offerta; QuickAddRow
no longer has a category field (offer tags set post-creation)
- offer-queries: getOfferEditorData fetches offerTags per service (join on
tags WHERE entity_type="services") and exposes them in OfferEditorData
- OfferEditorClient: filter chips above "Servizi Inclusi" — Tutti / Entry
Offer / Signature Offer / Retainer Offer, derived from actual tag data
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
scripts/reset-and-import-services.ts — deletes 3 legacy test services,
imports 55 services from Notion "DB Offerta: Attività SC" CSV export
(Signature Offer / Entry Offer / Retainer Offer, with fase metadata).
scripts/import-services-notion.ts — standalone idempotent version.
Run via SSH tunnel with DATABASE_URL pointed at 127.0.0.1:54321.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
offer_macros.category ("Entry Offer") and services.category are different
taxonomies — filtering one by the other always returns empty. Remove the
client-side category filter; all active services are always shown.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Bug: remove server-side category pre-filter from getOfferEditorData;
services now loaded unfiltered, client-side filter handles display →
changing category no longer wipes the service list
- Fix tierTotals to use full service map (not filtered subset) so totals
stay correct when category is edited mid-session
- Add Ripristina button for archived offers (calls toggleOfferArchived false)
- Remove canSave gate: always allow saving; button is "Salva Bozza"
(secondary style, stays on page) when no services assigned, "Salva
Offerta" (primary, redirects to list) when at least one tier has services
- Show "Bozza salvata." inline feedback after draft save
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Category filter chips ("Tutti" + dynamic categoryOptions), active/inactive
styling per UI-SPEC (#1A463C / #e5e7eb)
- "Mostra offerte archiviate" toggle (default off), filters via useMemo
- Responsive card grid (1/2/3 cols) linking to /admin/offers/[id]/edit,
with category badge and "Archiviata" indicator
- "+ Nuova Offerta" inline form calling createOfferMacro via useTransition
+ router.refresh()
- Empty state with "Nessuna offerta" / "Inizia creando la tua prima offerta"
- Add src/app/admin/offers/[id]/edit/page.tsx as async server component
- Fetches getOfferEditorData(id) + getOfferFieldOptions() via Promise.all
- Calls notFound() when offer id does not exist
- Delegates rendering to OfferEditorClient
- getOfferListCards() for Plan 04 list page (category + archive status)
- getOfferEditorData(macroId) returns macro fields, A/B/C tiers with
assignedServiceIds + computed servicesTotal, category-filtered
availableServices, and tipoTags/obiettivoTags
- getOfferFieldOptions() for categoria/ticket/tipo/obiettivo select pools
- scripts/verify-12-03-queries.ts documents the 5 spec'd test cases
(typecheck-only, no test runner configured, prod DB not mutated)
- Hand-write 0008_offer_tier_schema.sql: additive ALTER TABLE ADD COLUMN IF NOT EXISTS for offer_macros/offer_micros, guarded DO-block CHECK constraint on tier_letter, CREATE TABLE IF NOT EXISTS offer_tier_services + index
- Append journal entry for 0008_offer_tier_schema following the 0006 entry shape
- Add scripts/push-12-offer-tier-schema.ts: idempotent push script with PRODUCTION DEPLOY NOTE — BLOCKING step for Plan 02 (SSH+docker exec) before Wave 3 query layer
assignQuoteToLead's generate_new check was unreachable since
SendQuoteModal always passed false. Removes the field from both
the server schema and the modal's local schema/defaultValues/call site.
- New LeadsSearch.tsx: client-side instant filter across name/email/
company/status/tags (mirrors CatalogSearch.tsx)
- page.tsx: fetch via getLeadsWithTags()/getLeadFieldOptions(), render
LeadsSearch + CreateLeadModal, revalidate=0
- [id]/page.tsx: fetch getLeadsWithTags()/getLeadFieldOptions(), find
lead by id, pass tags/tagOptions to LeadDetail
- LeadDetail.tsx: add OptionMultiSelect for lead tags in the "Profilo"
card, wired to addLeadTag/removeLeadTag/renameLeadTag with error display
Replace shadcn Table-based read-only LeadTable with an Attio-style raw
table database view (1:1 structural port of ServiceTable.tsx):
- EditableCell for name/email/phone/company/next_action, persisted via
updateLeadField
- Local StatusCell: closed click-to-open dropdown over the 6 fixed
LEAD_STAGES values, preserving semantic STAGE_COLOR badges (won=green,
lost=red, etc.) without modifying shared option-select.tsx
- OptionMultiSelect for lead tags, wired to addLeadTag/removeLeadTag/
renameLeadTag
- Per-row error display and "Nessun lead trovato" empty state