--- phase: 03-organization-layer plan: "02" subsystem: ui tags: [react, fastapi, pydantic, tanstack-query, typescript, swipe-file, topic-overrides] # Dependency graph requires: - phase: 03-01 provides: SwipeService CRUD + pagina SwipeFile con hook useSwipeItems pronti provides: - topic_overrides in CalendarRequest (backend Pydantic schema) - GenerationPipeline applica topic override prima della chiamata LLM - useMarkSwipeUsed hook in hooks.ts - Griglia 13 slot con picker inline Swipe File nel form Genera Calendario - CalendarRequest.topic_overrides in types.ts affects: - 04-enrichment - vps-lab-deploy # Tech tracking tech-stack: added: [] patterns: - "topic_overrides dict[int, str]: slot_index -> topic passato end-to-end frontend -> backend -> pipeline" - "Picker inline posizionato con absolute + z-20 sotto ogni slot card" - "fire-and-forget mutation per mark-used (non blocca UI)" key-files: created: [] modified: - backend/schemas/calendar.py - backend/services/generation_pipeline.py - frontend/src/types.ts - frontend/src/api/hooks.ts - frontend/src/pages/GenerateCalendar.tsx key-decisions: - "topic_overrides come dict[int, str]: chiave = slot_index (0-12), valore = topic string" - "Override controllato nel _run_generation PRIMA del check slot.topic e PRIMA della chiamata LLM" - "mark-used chiamato fire-and-forget (mutate senza await) per non bloccare la UI" - "Picker inline con absolute positioning: si apre sotto il bottone dello slot, chiuso cliccando X o selezionando" - "showSwipePicker toggle: click sullo stesso slot chiude il picker (prev === slotIndex ? null : slotIndex)" patterns-established: - "Override pattern: dizionario slot_index -> valore, controllato prima della generazione automatica" - "Picker inline con z-20: visibile sopra altri elementi del form, no modal overlay" # Metrics duration: 3min completed: 2026-03-09 --- # Phase 3 Plan 02: Swipe-to-Calendar Integration Summary **topic_overrides in CalendarRequest + picker Swipe File inline per 13 slot nel form Genera Calendario con mark-used automatico** ## Performance - **Duration:** ~3 min - **Started:** 2026-03-08T23:30:30Z - **Completed:** 2026-03-08T23:33:16Z - **Tasks:** 2 - **Files modified:** 5 ## Accomplishments 1. **Backend schema** — `topic_overrides: Optional[dict[int, str]]` aggiunto a `CalendarRequest` in Pydantic; campo passa validazione e arriva alla pipeline 2. **Pipeline wiring** — `GenerationPipeline._run_generation` controlla override per slot_index prima di chiamare LLM; log informativo quando applicato; slot senza override non modificati 3. **Frontend types + hook** — `CalendarRequest.topic_overrides` in types.ts; hook `useMarkSwipeUsed` aggiunto in hooks.ts (POST /swipe/{id}/mark-used con invalidateQueries) 4. **UI griglia 13 slot** — Form Genera Calendario con sezione "Topic Override" opzionale: griglia 3 colonne su desktop, 1 su mobile; ogni slot mostra bottone "Da Swipe File" o topic assegnato con X per rimuovere 5. **Picker inline** — Apre lista SwipeItems con nicchia badge e badge Usato; selezione assegna topic + chiama mark-used; slot con override ha bordo amber-500/30 ## Task Commits Ogni task committato atomicamente: 1. **Task 1: Backend — topic_overrides in CalendarRequest + pipeline wiring** - `67769dd` (feat) 2. **Task 2: Frontend — Picker Swipe File nel form Genera Calendario + mark used** - `f449d94` (feat) **Plan metadata:** (vedi commit docs sotto) ## Files Created/Modified - `backend/schemas/calendar.py` - Aggiunto campo `topic_overrides: Optional[dict[int, str]]` - `backend/services/generation_pipeline.py` - Override applicato nel `_run_generation` prima della chiamata LLM - `frontend/src/types.ts` - `CalendarRequest.topic_overrides?: Record | null` - `frontend/src/api/hooks.ts` - Hook `useMarkSwipeUsed` aggiunto nella sezione Swipe File - `frontend/src/pages/GenerateCalendar.tsx` - Sezione Topic Override con griglia slot e picker inline ## Decisions Made - **dict[int, str] per topic_overrides**: chiave numerica = indice slot (0-12), piu' diretto di lista; JSON serializza come `{"0": "topic"}` ma Pydantic converte automaticamente string key → int - **Override check prima di slot.topic e prima di LLM**: ordine di priorita' — override utente > topic gia' presente > generazione LLM - **fire-and-forget per mark-used**: `markUsed.mutate(item.id)` senza await per non bloccare la UI; il cache invalidation avviene in background - **Picker inline con absolute positioning**: evita modal overlay pesante; z-20 garantisce visibilita'; si chiude automaticamente dopo selezione o cliccando X ## Deviations from Plan None — piano eseguito esattamente come scritto. ## Issues Encountered None. ## User Setup Required None — nessuna configurazione esterna richiesta. ## Next Phase Readiness - Phase 3 (Organization Layer) completa: SwipeService CRUD + pagina SwipeFile + integrazione nel form Genera Calendario - Pronto per `vps-lab-deploy` per deployare end-to-end su VPS e testare il flusso completo - Phase 4 (Enrichment — Unsplash) puo' iniziare indipendentemente --- *Phase: 03-organization-layer* *Completed: 2026-03-09*