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>
This commit is contained in:
212
.planning/phases/03-organization-layer/03-02-PLAN.md
Normal file
212
.planning/phases/03-organization-layer/03-02-PLAN.md
Normal file
@@ -0,0 +1,212 @@
|
||||
---
|
||||
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>
|
||||
Reference in New Issue
Block a user