← Tutte le idee Casa e famiglia

Aisle

Il sito del tuo matrimonio, con conferme di presenza e lista invitati

Incasso mensile: 2100 USD (fonte indicata nella scheda)

Cosa fa

Permette agli sposi di creare il sito del matrimonio, raccogliere le conferme di presenza online, gestire la lista degli invitati e organizzare un matrimonio all'estero.

Come guadagna

Ha entrate ricorrenti ogni mese, verificate su TrustMRR. Prezzi e piani esatti sono indicati sul sito del progetto.

Rendilo tuo

  • Per i matrimoni italiani: tableau dei tavoli, lista nozze, indicazioni per chiesa e ristorante.
  • Lo stesso per altre feste: battesimi, comunioni, compleanni dei 18 anni.

Le frasi da scrivere all'AI

1. Base del sito e grafica

Crea un'app web in italiano chiamata "Il Nostro Sì" per creare il sito del proprio matrimonio. Usa React, Vite, TypeScript e Tailwind CSS.

Pagine da creare:
1. Pagina iniziale di presentazione: titolo grande "Il sito del vostro matrimonio, pronto in 10 minuti", tre vantaggi (conferme degli invitati online, tutte le informazioni in un link, niente più messaggi da rincorrere), tre esempi di siti, prezzi e un pulsante "Crea il vostro sito".
2. Pagina di registrazione e accesso.
3. Pannello degli sposi (per ora vuoto, con il titolo "Il vostro matrimonio").
4. Pagina pubblica del matrimonio all'indirizzo /m/:slug (per ora con dati di esempio).

Stile: elegante e romantico ma moderno. Sfondo color avorio #FBF8F3, testo grigio scuro #2B2B2B, colore principale verde salvia #8A9A7B, dettagli color oro tenue #C8A96A. Titoli con il carattere "Cormorant Garamond", testi con "Inter". Molto spazio bianco, angoli arrotondati, nessuna immagine generica di stock.

Tutto deve funzionare perfettamente da telefono, perché la maggior parte degli invitati aprirà il link da WhatsApp. Tutti i testi in italiano.

2. Database, account e sicurezza

Collega il progetto a Supabase e attiva la registrazione con email e password.

Crea due tabelle:
- "matrimoni": un matrimonio per coppia, con indirizzo della pagina (slug), nomi degli sposi, data, luogo della cerimonia, luogo del ricevimento, la loro storia, foto di copertina e un campo "pagato" che parte da falso.
- "risposte": le conferme degli invitati, collegate al matrimonio, con nome, se partecipa, numero di persone (da 0 a 10), scelta del menù (carne, pesce, vegetariano, bambini), allergie e un messaggio per gli sposi.

Regole di sicurezza obbligatorie:
- la pagina di un matrimonio è visibile a chiunque abbia il link;
- solo gli sposi possono creare e modificare il loro matrimonio;
- gli invitati possono inviare una risposta, ma non possono leggere le risposte degli altri;
- solo gli sposi leggono le risposte del loro matrimonio;
- il campo "pagato" non può essere cambiato dagli sposi: lo cambia solo il sistema dei pagamenti.

Usa esattamente il codice SQL che trovi nei "Dettagli tecnici" di questa scheda (tabelle, regole di sicurezza e blocco del campo pagato): eseguilo in Supabase prima di continuare.

3. La pagina del matrimonio e le conferme degli invitati

Completa la pagina pubblica del matrimonio all'indirizzo /m/:slug, leggendo i dati dalla tabella "matrimoni".

In alto: foto di copertina a tutta larghezza, i nomi degli sposi in grande e la data scritta per esteso (es. "Sabato 14 giugno 2027"), con un conto alla rovescia dei giorni mancanti.

Sezioni sotto, in quest'ordine: la nostra storia; la cerimonia (luogo, orario, pulsante "Apri in Google Maps"); il ricevimento (luogo, orario, pulsante mappa); informazioni utili (parcheggio, dove dormire, lista nozze, se compilate).

In fondo, il modulo "Confermate la vostra presenza": nome e cognome, "Ci sarai?" con due pulsanti grandi Sì / No; se sì, numero di persone, menù per ogni persona e allergie; un messaggio facoltativo per gli sposi. Al momento dell'invio salva la risposta nella tabella "risposte" e mostra un ringraziamento con un piccolo effetto di coriandoli.

Se il matrimonio non esiste mostra una pagina gentile "Matrimonio non trovato". Se il matrimonio non è ancora pagato, mostra la pagina ma con il modulo delle conferme disattivato e la scritta "Le conferme apriranno presto".

4. Il pannello degli sposi

Completa il pannello degli sposi, visibile solo dopo l'accesso.

Se gli sposi non hanno ancora un matrimonio, mostra un modulo guidato in 3 passaggi: (1) nomi e data, (2) luoghi e orari di cerimonia e ricevimento, (3) la vostra storia e la foto di copertina, caricata su Supabase Storage. L'indirizzo della pagina (slug) si crea da solo dai nomi, ad esempio "giulia-e-marco", e si può modificare; se è già usato, aggiungi un numero.

Se il matrimonio esiste già, mostra:
- un riquadro con il link della pagina, un pulsante "Copia link" e un pulsante "Condividi su WhatsApp" con un messaggio già pronto per gli invitati;
- quattro numeri grandi: risposte ricevute, persone confermate, persone che non vengono, persone con allergie;
- il conteggio dei menù (carne, pesce, vegetariano, bambini), da passare al ristorante;
- l'elenco di tutte le risposte con ricerca per nome e filtro "vengono / non vengono";
- un pulsante "Scarica l'elenco per il ristorante" che scarica un file CSV apribile con Excel;
- un pulsante "Modifica i dati del matrimonio".

5. Pagamento con Stripe

Aggiungi il pagamento una tantum di 49 € con Stripe Checkout per attivare le conferme degli invitati.

Nel pannello degli sposi, se il matrimonio non è pagato, mostra un riquadro "Attiva le conferme degli invitati – 49 €" con cosa si ottiene: conferme illimitate, elenco per il ristorante, sito senza pubblicità per sempre.

Crea una Edge Function di Supabase che crea la sessione di pagamento di Stripe Checkout, passando l'id del matrimonio come "client_reference_id", e reindirizza gli sposi alla pagina di pagamento. Dopo il pagamento riportali al pannello con il messaggio "Pagamento ricevuto, le conferme sono attive".

Il campo "pagato" deve diventare vero solo tramite il webhook di Stripe, mai dal browser: crea una seconda Edge Function "stripe-webhook" uguale a quella nei "Dettagli tecnici" di questa scheda. Le chiavi segrete di Stripe e la service role key di Supabase vanno solo nei segreti delle Edge Function, mai nel codice del sito.

6. Controllo finale prima di pubblicare

Controlla tutta l'app prima della pubblicazione e correggi quello che non va:
1. Sicurezza: prova con un secondo account a leggere le risposte di un matrimonio non tuo e a modificare il campo "pagato" dal browser: entrambe le cose devono essere bloccate. Verifica che nel codice del sito non compaiano chiavi segrete.
2. Telefono: apri la pagina di un matrimonio e il modulo delle conferme su uno schermo largo 375 pixel: niente testo tagliato, pulsanti grandi e facili da toccare.
3. Errori: se manca la connessione o un campo obbligatorio, mostra un messaggio chiaro in italiano, mai una pagina bianca.
4. Condivisione: aggiungi le anteprime per WhatsApp e Facebook (titolo con i nomi degli sposi, data e foto di copertina) alla pagina del matrimonio.
5. Privacy: aggiungi una pagina privacy che spiega che i dati degli invitati sono visibili solo agli sposi e vengono cancellati 6 mesi dopo il matrimonio, e un avviso sotto il modulo delle conferme.
Alla fine scrivimi l'elenco di cosa hai controllato e cosa hai corretto.

Dettagli tecnici

Tabelle del database

Crea le due tabelle: i matrimoni e le risposte degli invitati. Si incolla nel SQL Editor di Supabase.

-- Un matrimonio per coppia
create table matrimoni (
  id uuid primary key default gen_random_uuid(),
  utente_id uuid not null references auth.users(id) on delete cascade,
  slug text not null unique,              -- es. "giulia-e-marco"
  nomi text not null,                     -- es. "Giulia & Marco"
  data_matrimonio date not null,
  cerimonia text not null default '',
  ricevimento text not null default '',
  storia text not null default '',
  foto_url text,
  pagato boolean not null default false,
  creato_il timestamptz not null default now()
);

-- Le conferme degli invitati
create table risposte (
  id uuid primary key default gen_random_uuid(),
  matrimonio_id uuid not null references matrimoni(id) on delete cascade,
  nome text not null,
  partecipa boolean not null,
  persone int not null default 1 check (persone between 0 and 10),
  menu text check (menu in ('carne', 'pesce', 'vegetariano', 'bambini')),
  allergie text not null default '',
  messaggio text not null default '',
  creato_il timestamptz not null default now()
);

Regole di sicurezza (RLS)

Decide chi può vedere e modificare cosa: la pagina è pubblica, le risposte le leggono solo gli sposi, e il campo "pagato" lo cambia solo il sistema dei pagamenti.

alter table matrimoni enable row level security;
alter table risposte enable row level security;

-- La pagina del matrimonio è pubblica: chi ha il link la vede
create policy "pagina pubblica" on matrimoni
  for select using (true);

-- Solo gli sposi creano il loro matrimonio, e non possono crearlo già "pagato"
create policy "sposi creano" on matrimoni
  for insert with check (auth.uid() = utente_id and pagato = false);

create policy "sposi modificano" on matrimoni
  for update using (auth.uid() = utente_id) with check (auth.uid() = utente_id);

-- Gli invitati inviano la risposta ma non leggono quelle degli altri
create policy "invitati rispondono" on risposte
  for insert with check (true);

create policy "sposi leggono le risposte" on risposte
  for select using (
    exists (select 1 from matrimoni m
            where m.id = risposte.matrimonio_id and m.utente_id = auth.uid())
  );

-- Il campo "pagato" lo cambia solo il webhook di Stripe (service role)
create function blocca_pagato() returns trigger language plpgsql as $
begin
  if new.pagato is distinct from old.pagato
     and coalesce(auth.role(), '') <> 'service_role' then
    raise exception 'Il campo pagato lo cambia solo il sistema dei pagamenti';
  end if;
  return new;
end $;

create trigger matrimoni_pagato before update on matrimoni
  for each row execute function blocca_pagato();

Webhook di Stripe

Il programmino che Stripe chiama quando un pagamento va a buon fine: controlla che la chiamata venga davvero da Stripe e segna il matrimonio come pagato. Va pubblicato con l'opzione --no-verify-jwt, perché Stripe non ha un account Supabase.

// supabase/functions/stripe-webhook/index.ts
import Stripe from "npm:stripe";
import { createClient } from "npm:@supabase/supabase-js";

const stripe = new Stripe(Deno.env.get("STRIPE_SECRET_KEY")!, {
  httpClient: Stripe.createFetchHttpClient(),
});
const crittografia = Stripe.createSubtleCryptoProvider(); // necessario su Deno
const supabase = createClient(
  Deno.env.get("SUPABASE_URL")!,
  Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!, // segreta: solo qui
);

Deno.serve(async (req) => {
  const firma = req.headers.get("stripe-signature") ?? "";
  const corpo = await req.text();

  let evento: Stripe.Event;
  try {
    // Senza questo controllo chiunque potrebbe fingersi Stripe
    evento = await stripe.webhooks.constructEventAsync(
      corpo, firma, Deno.env.get("STRIPE_WEBHOOK_SECRET")!, undefined, crittografia,
    );
  } catch {
    return new Response("Firma non valida", { status: 400 });
  }

  if (evento.type === "checkout.session.completed") {
    const sessione = evento.data.object as Stripe.Checkout.Session;
    if (sessione.client_reference_id) {
      await supabase.from("matrimoni")
        .update({ pagato: true })
        .eq("id", sessione.client_reference_id);
    }
  }
  return new Response("ok");
});

Riepilogo per il ristorante

Una sola interrogazione che conta persone e menù: è il numero che gli sposi devono dare al ristorante.

select
  count(*) filter (where partecipa)                         as conferme,
  coalesce(sum(persone) filter (where partecipa), 0)        as persone_totali,
  count(*) filter (where partecipa and menu = 'carne')       as menu_carne,
  count(*) filter (where partecipa and menu = 'pesce')       as menu_pesce,
  count(*) filter (where partecipa and menu = 'vegetariano') as menu_vegetariano,
  count(*) filter (where partecipa and menu = 'bambini')     as menu_bambini,
  count(*) filter (where partecipa and allergie <> '')       as con_allergie
from risposte
where matrimonio_id = (select id from matrimoni where slug = 'giulia-e-marco');

Pezzi comuni a tutti i progetti

Glossario

RSVP
La conferma di presenza degli invitati (dal francese "répondez s'il vous plaît").
Slug
La parte finale e leggibile dell'indirizzo, es. /m/giulia-e-marco.
RLS
Row Level Security: le regole del database che decidono chi può leggere o modificare ogni riga.
Edge Function
Un piccolo programma che gira sui server di Supabase, dove si possono usare chiavi segrete.
Webhook
Un indirizzo che un altro servizio (qui Stripe) chiama per avvisarti che è successo qualcosa.
Service role
La chiave "padrona" di Supabase che ignora le regole di sicurezza: va solo nelle Edge Function, mai nel sito.