Descrizione
Implementare un flusso di prenotazione per corsi/eventi dove l'utente non si registra: inserisce nome, email (obbligatoria), telefono (opzionale) e opt‑in newsletter; la conferma avviene solo via link inviato tramite il sistema email di Supabase. Se il corso è pieno, l'utente entra in una waitlist e viene promosso solo dopo aver accettato via email.
Motivazione
Ridurre friction (nessuna registrazione) mantenendo sicurezza e validità email tramite double opt‑in.
Gestire correttamente overflow con waitlist e promozione controllata, evitando overbooking anche sotto carico concorrente.
Requisiti funzionali
Form pubblico per prenotazione (nome, email, telefono, opt‑in newsletter).
Invio email di conferma via Supabase (token one‑time); il booking rimane pending fino al click.
Al click:
se posti liberi → confirmed;
se posti esauriti → waitlist + position.
Quando si libera un posto, la persona in testa riceve email di promozione; deve accettare via link entro la finestra (default 48h) per diventare confirmed. Se non accetta, si propone al prossimo.
L’utente può cancellare la propria prenotazione fino a 72 ore prima dell’inizio.
Tutte le assegnazioni posti/promozioni/cancellazioni devono essere atomicamente gestite in PL/pgSQL RPC per evitare overbooking.
Salvare solo hash dei token in DB (es. sha256).
Acceptance criteria
Un utente non autenticato può inviare la richiesta e ricevere la mail di conferma (Supabase).
La prenotazione resta pending fino al click sul link; solo dopo la conferma cambia stato.
Non si superano mai i max_attendees definiti per evento/curso (anche sotto richieste concorrenti).
La promozione dalla waitlist richiede esplicita accettazione via email.
La cancellazione via link è possibile fino a 72 ore prima dell’inizio.
Admin può visualizzare, cancellare, reinviare mail e forzare promozioni.
DB / key fields (sintesi)
Nuova tabella: bookings
id uuid PK
event_id / course_id (FK)
contact_name, contact_email, contact_phone
newsletter boolean
status enum: pending|confirmed|waitlist|cancelled
position int (per waitlist)
token_hash, token_expires (confirm)
promotion_token_hash, promotion_expires (promozione)
created_at, confirmed_at, cancelled_at
Salvare solo gli hash dei token; token in chiaro inviati via mail.
API / RPC proposti
Edge Function pubblica: POST /bookings/create → salva pending, genera token, invia mail di conferma via Supabase.
Edge Function pubblica: GET /bookings/confirm?token=... → verifica token e chiama rpc_confirm_booking(p_token).
Edge Function pubblica: GET /bookings/accept_promotion?token=... → verifica promotion token e chiama rpc_accept_promotion(p_token).
Edge Function pubblica: GET /bookings/cancel?token=... → annulla (controllo 72h) e chiama rpc_cancel_booking(p_id_or_token).
Scheduled Function: bookings.cleanup → rimuove pending scaduti e gestisce timeout promozioni.
RPC/Postgres (transazionali): rpc_confirm_booking, rpc_accept_promotion, rpc_cancel_booking — tutte con FOR UPDATE / advisory lock per serializzare modifiche e conteggi.
Concorrency / sicurezza
Le RPC devono lockare le righe evento/course (SELECT ... FOR UPDATE) e la riga bookings inside the same transaction per evitare race conditions.
Rate‑limit e controllo abusi (IP/email) lato Edge Function.
Captcha opzionale per prevenire bot.
Backoffice (admin)
Pagina BookingsAdmin con filtri per evento/corso/status, ricerca email/nome, azioni: reinvia email, cancella, promuovi manualmente, esporta CSV.
Realtime updates: integrare con canali Realtime già presenti.
Files di riferimento
Client Supabase: supabase.ts:1-11
Layer API esistente: api.ts:1-100
Data loader / Realtime: useDataLoader.tsx:1-300
Migrazioni / schema di base: script.sql:1-120
Admin esistenti: EventsAdmin.tsx:1-200, CoursesAdmin.tsx:1-164
Pagine pubbliche: Courses.tsx:1-116, Events.tsx:1-78
Task (checklist per issue / PR)
Aggiungere migration SQL per bookings (tabella + indici).
Implementare RPC PL/pgSQL: rpc_confirm_booking, rpc_accept_promotion, rpc_cancel_booking.
Creare Edge Functions: create, confirm, accept_promotion, cancel, cleanup (usa API email di Supabase).
Implementare form pubblico in CourseDetail / EventCard e UX di conferma.
Implementare BookingsAdmin con azioni (reinvia, cancella, promuovi).
Aggiungere tests: unit per RPC, E2E per flussi concorrenti (simulazione conferme concorrenti e promozioni).
Documentazione: OpenSpec / openspec/changes + README con flow e configurazioni (token TTL, finestra promozione).
Definition of Done
Migrazione in repo + script caricato in supabase e testato localmente.
RPC e Edge Functions implementati e verificati su staging Supabase.
Form pubblico e admin funzionanti con test automatici che coprono: confirm, waitlist, promozione, cancellazione e concurrency.
Documentazione e checklist completate.
Descrizione
Implementare un flusso di prenotazione per corsi/eventi dove l'utente non si registra: inserisce nome, email (obbligatoria), telefono (opzionale) e opt‑in newsletter; la conferma avviene solo via link inviato tramite il sistema email di Supabase. Se il corso è pieno, l'utente entra in una waitlist e viene promosso solo dopo aver accettato via email.
Motivazione
Ridurre friction (nessuna registrazione) mantenendo sicurezza e validità email tramite double opt‑in.
Gestire correttamente overflow con waitlist e promozione controllata, evitando overbooking anche sotto carico concorrente.
Requisiti funzionali
Form pubblico per prenotazione (nome, email, telefono, opt‑in newsletter).
Invio email di conferma via Supabase (token one‑time); il booking rimane pending fino al click.
Al click:
se posti liberi → confirmed;
se posti esauriti → waitlist + position.
Quando si libera un posto, la persona in testa riceve email di promozione; deve accettare via link entro la finestra (default 48h) per diventare confirmed. Se non accetta, si propone al prossimo.
L’utente può cancellare la propria prenotazione fino a 72 ore prima dell’inizio.
Tutte le assegnazioni posti/promozioni/cancellazioni devono essere atomicamente gestite in PL/pgSQL RPC per evitare overbooking.
Salvare solo hash dei token in DB (es. sha256).
Acceptance criteria
Un utente non autenticato può inviare la richiesta e ricevere la mail di conferma (Supabase).
La prenotazione resta pending fino al click sul link; solo dopo la conferma cambia stato.
Non si superano mai i max_attendees definiti per evento/curso (anche sotto richieste concorrenti).
La promozione dalla waitlist richiede esplicita accettazione via email.
La cancellazione via link è possibile fino a 72 ore prima dell’inizio.
Admin può visualizzare, cancellare, reinviare mail e forzare promozioni.
DB / key fields (sintesi)
Nuova tabella: bookings
id uuid PK
event_id / course_id (FK)
contact_name, contact_email, contact_phone
newsletter boolean
status enum: pending|confirmed|waitlist|cancelled
position int (per waitlist)
token_hash, token_expires (confirm)
promotion_token_hash, promotion_expires (promozione)
created_at, confirmed_at, cancelled_at
Salvare solo gli hash dei token; token in chiaro inviati via mail.
API / RPC proposti
Edge Function pubblica: POST /bookings/create → salva pending, genera token, invia mail di conferma via Supabase.
Edge Function pubblica: GET /bookings/confirm?token=... → verifica token e chiama rpc_confirm_booking(p_token).
Edge Function pubblica: GET /bookings/accept_promotion?token=... → verifica promotion token e chiama rpc_accept_promotion(p_token).
Edge Function pubblica: GET /bookings/cancel?token=... → annulla (controllo 72h) e chiama rpc_cancel_booking(p_id_or_token).
Scheduled Function: bookings.cleanup → rimuove pending scaduti e gestisce timeout promozioni.
RPC/Postgres (transazionali): rpc_confirm_booking, rpc_accept_promotion, rpc_cancel_booking — tutte con FOR UPDATE / advisory lock per serializzare modifiche e conteggi.
Concorrency / sicurezza
Le RPC devono lockare le righe evento/course (SELECT ... FOR UPDATE) e la riga bookings inside the same transaction per evitare race conditions.
Rate‑limit e controllo abusi (IP/email) lato Edge Function.
Captcha opzionale per prevenire bot.
Backoffice (admin)
Pagina BookingsAdmin con filtri per evento/corso/status, ricerca email/nome, azioni: reinvia email, cancella, promuovi manualmente, esporta CSV.
Realtime updates: integrare con canali Realtime già presenti.
Files di riferimento
Client Supabase: supabase.ts:1-11
Layer API esistente: api.ts:1-100
Data loader / Realtime: useDataLoader.tsx:1-300
Migrazioni / schema di base: script.sql:1-120
Admin esistenti: EventsAdmin.tsx:1-200, CoursesAdmin.tsx:1-164
Pagine pubbliche: Courses.tsx:1-116, Events.tsx:1-78
Task (checklist per issue / PR)
Aggiungere migration SQL per bookings (tabella + indici).
Implementare RPC PL/pgSQL: rpc_confirm_booking, rpc_accept_promotion, rpc_cancel_booking.
Creare Edge Functions: create, confirm, accept_promotion, cancel, cleanup (usa API email di Supabase).
Implementare form pubblico in CourseDetail / EventCard e UX di conferma.
Implementare BookingsAdmin con azioni (reinvia, cancella, promuovi).
Aggiungere tests: unit per RPC, E2E per flussi concorrenti (simulazione conferme concorrenti e promozioni).
Documentazione: OpenSpec / openspec/changes + README con flow e configurazioni (token TTL, finestra promozione).
Definition of Done
Migrazione in repo + script caricato in supabase e testato localmente.
RPC e Edge Functions implementati e verificati su staging Supabase.
Form pubblico e admin funzionanti con test automatici che coprono: confirm, waitlist, promozione, cancellazione e concurrency.
Documentazione e checklist completate.