Files
Michele 05988f4be5 docs(03): create phase plan
Phase 03: Organization Layer
- 2 plan(s) in 2 wave(s)
- Wave 1: SwipeService CRUD + API + UI (parallel-ready)
- Wave 2: Swipe-to-calendar integration (depends on 03-01)
- Ready for execution

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 23:21:41 +01:00

213 lines
9.1 KiB
Markdown

---
phase: 03-organization-layer
plan: 02
type: execute
wave: 2
depends_on: ["03-01"]
files_modified:
- frontend/src/pages/GenerateCalendar.tsx
- frontend/src/api/hooks.ts
autonomous: true
must_haves:
truths:
- "L'utente puo' aprire un picker dallo Swipe File nel form Genera Calendario e selezionare un topic come override per uno slot specifico"
- "Dopo la selezione, l'idea nello Swipe File viene marcata come 'usata' con badge visivo"
- "L'utente vede i topic override selezionati nel form prima di avviare la generazione"
- "I topic override selezionati vengono passati alla generazione bulk e applicati agli slot corrispondenti"
artifacts:
- path: "frontend/src/pages/GenerateCalendar.tsx"
provides: "Sezione override topic con picker da Swipe File integrato nel form"
contains: "swipe"
key_links:
- from: "frontend/src/pages/GenerateCalendar.tsx"
to: "/api/swipe"
via: "useSwipeItems hook + useMarkSwipeUsed"
pattern: "useSwipeItems|markUsed"
- from: "frontend/src/pages/GenerateCalendar.tsx"
to: "/api/generate/bulk"
via: "CalendarRequest with topic_overrides"
pattern: "topic_overrides"
---
<objective>
Integrazione Swipe File nel form Genera Calendario: meccanismo per selezionare topic dallo Swipe File come override per slot specifici prima della generazione batch.
Purpose: Permette all'utente di usare idee catturate nello Swipe File come topic pre-assegnati nel calendario, combinando l'ispirazione spontanea con la generazione strategica.
Output: Sezione "Topic Override" nel form Genera Calendario con picker modale da Swipe File, invio degli override alla generazione bulk.
</objective>
<execution_context>
@C:\Users\miche\.claude/get-shit-done/workflows/execute-plan.md
@C:\Users\miche\.claude/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/03-organization-layer/03-CONTEXT.md
@.planning/phases/03-organization-layer/03-01-SUMMARY.md
</context>
<tasks>
<task type="auto">
<name>Task 1: Backend — topic_overrides in CalendarRequest + pipeline wiring</name>
<files>
backend/schemas/calendar.py
backend/services/generation_pipeline.py
</files>
<action>
**1. Aggiorna `backend/schemas/calendar.py`** — Aggiungi campo `topic_overrides` a `CalendarRequest`:
```python
topic_overrides: Optional[dict[int, str]] = Field(
default=None,
description="Override topic per slot specifici. Chiave: indice slot (0-12), valore: topic. "
"Gli slot con override skipperanno la generazione topic LLM.",
)
```
Questo campo e' un dizionario `{slot_index: topic_string}`. Esempio: `{2: "3 errori che costano clienti", 7: "Case study dentista Milano"}`.
**2. Aggiorna `backend/services/generation_pipeline.py`** — Nel metodo che genera i topic per ogni slot (dentro `generate_bulk_async` o il metodo chiamato da esso), dopo aver creato i CalendarSlot:
- Se `request.topic_overrides` esiste e contiene l'indice dello slot corrente, usa il topic override invece di chiamare l'LLM per generare il topic.
- Applica il topic override al campo `slot.topic` dello slot corrispondente.
- La logica e': `if topic_overrides and slot.indice in topic_overrides: slot.topic = topic_overrides[slot.indice]` — skip la chiamata LLM per generare il topic per quello slot.
Assicurarsi che il campo topic venga applicato PRIMA della generazione del contenuto del carosello, perche' il topic viene passato al prompt LLM.
**Pattern**: Leggere attentamente `generation_pipeline.py` per capire dove i topic vengono generati/assegnati e inserire l'override nel punto giusto del flusso.
</action>
<verify>
- `python -c "from backend.schemas.calendar import CalendarRequest; r = CalendarRequest(obiettivo_campagna='Test obiettivo campagna', topic_overrides={0: 'Test topic'}); print(r.topic_overrides)"` stampa `{0: 'Test topic'}`
- `python -c "from backend.services.generation_pipeline import GenerationPipeline; print('OK')"` importa senza errori
</verify>
<done>
- CalendarRequest accetta `topic_overrides` opzionale (dict[int, str])
- GenerationPipeline applica topic override agli slot corrispondenti prima della generazione LLM
- Slot senza override continuano a funzionare come prima (topic generato dall'LLM)
</done>
</task>
<task type="auto">
<name>Task 2: Frontend — Picker Swipe File nel form Genera Calendario + mark used</name>
<files>
frontend/src/pages/GenerateCalendar.tsx
frontend/src/api/hooks.ts
frontend/src/types.ts
</files>
<action>
**1. Aggiorna `frontend/src/types.ts`** — Aggiungi `topic_overrides` a `CalendarRequest`:
```typescript
export interface CalendarRequest {
obiettivo_campagna: string
settimane?: number
nicchie?: string[] | null
frequenza_post?: number
data_inizio?: string | null
topic_overrides?: Record<number, string> | null // slot_index -> topic
}
```
**2. Aggiungi hook `useMarkSwipeUsed` in `frontend/src/api/hooks.ts`** (nella sezione Swipe File):
```typescript
export function useMarkSwipeUsed() {
const queryClient = useQueryClient()
return useMutation<SwipeItem, Error, string>({
mutationFn: (id) => apiPost<SwipeItem>(`/swipe/${id}/mark-used`),
onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['swipe'] }) },
})
}
```
**3. Modifica `frontend/src/pages/GenerateCalendar.tsx`** — Aggiungi sezione "Topic Override dallo Swipe File":
**Nuovo state:**
```typescript
const [topicOverrides, setTopicOverrides] = useState<Record<number, { topic: string; swipeId: string }>>({})
const [showSwipePicker, setShowSwipePicker] = useState<number | null>(null) // slot index attivo
```
**Sezione UI** — Aggiungere DOPO il campo "Tono di voce" e PRIMA del checkbox Nicchie:
- Titolo: "Topic Override (opzionale)" con sottotitolo "Seleziona topic dallo Swipe File per slot specifici"
- Griglia 13 slot (indici 0-12), ognuno con:
- Label: "Slot {N+1}" (1-based per utente)
- Se override assegnato: mostra topic troncato + bottone X per rimuovere override
- Se nessun override: bottone piccolo "Da Swipe File" (icona Lightbulb) per aprire il picker
- Il picker e' un pannello/dropdown che appare inline sotto lo slot cliccato:
- Mostra lista idee dallo Swipe File (via `useSwipeItems`)
- Ogni idea mostra: topic, nicchia badge, badge "usato" se gia' usata
- Click su un'idea: assegna il topic allo slot, chiude picker, chiama `useMarkSwipeUsed(swipeId)`
- Bottone "Chiudi" per chiudere senza selezionare
- Se Swipe File vuoto: messaggio "Nessuna idea nel tuo Swipe File" con link a `/swipe-file`
**Modifica handleSubmit** — Includi topic_overrides nella request:
```typescript
const req: CalendarRequest = {
obiettivo_campagna: obiettivo.trim(),
settimane,
frequenza_post: settings?.frequenza_post ?? 3,
nicchie: customNicchie ? settings?.nicchie_attive : undefined,
topic_overrides: Object.keys(topicOverrides).length > 0
? Object.fromEntries(
Object.entries(topicOverrides).map(([k, v]) => [parseInt(k), v.topic])
)
: undefined,
}
```
**Stile UI:**
- La griglia dei 13 slot usa un layout compatto: 3-4 colonne su desktop, 1 su mobile
- Ogni slot e' una mini-card stone-800 con bordo stone-700
- Slot con override: bordo amber-500/30, background amber-500/5
- Il picker inline usa la stessa palette stone/amber
- Animazione ingresso/uscita non necessaria — keep it simple
</action>
<verify>
- `cd frontend && npx tsc --noEmit` — nessun errore TypeScript
- `cd frontend && npm run build` — build pulita
- CalendarRequest in types.ts include `topic_overrides`
- hooks.ts contiene `useMarkSwipeUsed`
- GenerateCalendar.tsx importa `useSwipeItems` e `useMarkSwipeUsed`
</verify>
<done>
- Il form Genera Calendario mostra 13 slot con possibilita' di assegnare topic override dallo Swipe File
- Click "Da Swipe File" apre picker inline con lista idee
- Selezione di un'idea assegna il topic allo slot e marca l'idea come "usata"
- Override rimovibili con bottone X
- Gli override vengono passati come `topic_overrides` nella CalendarRequest alla generazione bulk
- TypeScript compila senza errori
</done>
</task>
</tasks>
<verification>
1. Frontend: `cd frontend && npx tsc --noEmit && npm run build` — build pulita
2. Backend: `python -c "from backend.schemas.calendar import CalendarRequest; print(CalendarRequest.model_fields.keys())"` include topic_overrides
3. CalendarRequest in types.ts include topic_overrides
4. GenerateCalendar.tsx ha griglia 13 slot con bottoni picker
5. Hook useMarkSwipeUsed presente in hooks.ts
6. La generazione bulk riceve e applica topic_overrides
</verification>
<success_criteria>
- L'utente puo' selezionare topic dallo Swipe File per slot specifici nel form Genera Calendario
- I topic selezionati vengono visualizzati nella griglia slot prima della generazione
- I topic override vengono inviati alla generazione bulk via CalendarRequest.topic_overrides
- L'idea Swipe File selezionata viene marcata come "usata" con badge visivo
- Gli override sono rimovibili (bottone X) prima di avviare la generazione
- Slot senza override continuano a generare topic automaticamente via LLM
</success_criteria>
<output>
After completion, create `.planning/phases/03-organization-layer/03-02-SUMMARY.md`
</output>