← Tutte le idee Casa e famiglia

TREK

Organizzare un viaggio insieme, in tempo reale, con mappa e budget

Cosa fa

Un pianificatore di viaggi da installare sul proprio server: programma giorno per giorno, mappa interattiva, budget condiviso e modifiche in tempo reale tra più persone.

Come guadagna

Progetto open source su GitHub con oltre 14.000 stelle, che non dichiara incassi. Ha licenza AGPL: puoi partire dal suo codice, ma se offri la tua versione online devi pubblicare anche il tuo codice modificato. Idee nostre per guadagnare: versione online pronta da usare in abbonamento, affiliazioni con hotel ed esperienze.

Rendilo tuo

  • Per i viaggi di gruppo italiani: gite di classe, addii al celibato, viaggi tra amici con le spese divise.
  • Guadagna con le affiliazioni di hotel e attività consigliate.

Le frasi da scrivere all'AI

1. Base del sito e grafica

Crea un'app web in italiano chiamata "Si Parte": pianifica i viaggi di gruppo: programma giorno per giorno, mappa delle tappe, idee e voti, e le spese condivise con il calcolo di chi deve dare quanto a chi, modificato insieme in tempo reale. Usa React, Vite, TypeScript e Tailwind CSS.

Pagine da creare:
1. Pagina iniziale con un esempio di viaggio e il prezzo
2. Il viaggio: giorni con le attività, mappa delle tappe
3. Idee: proposte dei partecipanti con voti
4. Spese: chi ha pagato cosa, per chi, e i conti finali
5. Partecipanti e inviti

Stile: da viaggio allegro: sfondo bianco, blu mare #0284C7, giallo sole #FACC15, corallo #FB7185, un colore per partecipante, carattere "Nunito", mappa grande. Nessuna immagine generica di stock.

Tutto deve funzionare perfettamente da telefono. Tutti i testi in italiano, chiari e senza parole tecniche.

2. Database, account e sicurezza

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

Crea queste tabelle:
- "viaggi" e "partecipanti": il viaggio e chi ne fa parte
- "attivita": giorno, ora, titolo, luogo con coordinate, note
- "idee" e "voti"
- "spese": chi ha pagato, importo, per chi (tutti o alcuni), categoria

Regole di sicurezza obbligatorie:
- vede e modifica il viaggio solo chi ne fa parte
- si entra con un link d'invito
- le modifiche si vedono subito da tutti

Usa esattamente il codice SQL che trovi nei "Dettagli tecnici" di questa scheda: eseguilo nel SQL Editor di Supabase prima di continuare, poi collega le pagine alle tabelle.

3. Programma e mappa insieme

Il programma è diviso per giorni; le attività si aggiungono con ora, luogo (cercato sulla mappa con OpenStreetMap) e note, e si spostano trascinandole. La mappa mostra le tappe di ogni giorno collegate da una linea. Tutti vedono le modifiche degli altri in tempo reale (Supabase Realtime).

Le idee ("cena di pesce a Positano?") si votano; quelle più votate si trascinano nel programma.

Funziona anche in viaggio senza connessione per consultare il programma.

4. Spese e conti finali

Ogni spesa ha chi ha pagato, l'importo e per chi (tutti o solo alcuni). Il sito calcola il saldo di ciascuno e il numero minimo di bonifici per pareggiare i conti (codice nei Dettagli tecnici): invece di 12 scambi tra 5 amici, ne bastano 4.

Riepilogo delle spese per categoria e per persona, ed esportazione in Excel.

5. Pagamenti con Stripe

Aggiungi un pagamento singolo di 4,99 € per viaggio. Il pagamento sblocca per quel viaggio i partecipanti illimitati (gratis fino a 4), la mappa con le tappe, le spese condivise e l'uso offline.

A pagamento riuscito il webhook imposta "completo" a true nella tabella "viaggi", sulla riga del viaggio indicata in client_reference_id.

Per la sessione di pagamento e il webhook segui la guida "Pagamenti con Stripe" di Fattibile.

6. Controlli specifici di questo progetto

Oltre ai controlli della guida "Controllo finale", verifica in particolare:
1. Conti: con 5 persone e 10 spese miste, controlla che dopo i bonifici suggeriti tutti i saldi siano a zero.
Correggi quello che non va e scrivimi cosa hai cambiato.

Dettagli tecnici

Il minimo di bonifici per pareggiare i conti

Calcola il saldo di ciascuno (pagato meno dovuto) e fa pagare chi deve di più a chi deve ricevere di più, finché tutti sono a zero.

type Spesa = { pagatoDa: string; centesimi: number; per: string[] };

export function conti(spese: Spesa[], persone: string[]) {
  const saldo = new Map(persone.map((p) => [p, 0]));
  for (const s of spese) {
    saldo.set(s.pagatoDa, saldo.get(s.pagatoDa)! + s.centesimi);
    const quota = Math.floor(s.centesimi / s.per.length);
    let resto = s.centesimi - quota * s.per.length;            // centesimi che avanzano
    for (const p of s.per) saldo.set(p, saldo.get(p)! - quota - (resto-- > 0 ? 1 : 0));
  }

  const creditori = [...saldo].filter(([, v]) => v > 0).sort((a, b) => b[1] - a[1]);
  const debitori = [...saldo].filter(([, v]) => v < 0).sort((a, b) => a[1] - b[1]);
  const bonifici: { da: string; a: string; centesimi: number }[] = [];

  let i = 0, j = 0;
  while (i < debitori.length && j < creditori.length) {
    const importo = Math.min(-debitori[i][1], creditori[j][1]);
    bonifici.push({ da: debitori[i][0], a: creditori[j][0], centesimi: importo });
    debitori[i][1] += importo;
    creditori[j][1] -= importo;
    if (debitori[i][1] === 0) i++;
    if (creditori[j][1] === 0) j++;
  }
  return bonifici;
}

Viaggio condiviso tra i partecipanti

Solo chi fa parte del viaggio vede e modifica attività e spese; si entra con il codice del link d'invito.

create table viaggi (
  id uuid primary key default gen_random_uuid(),
  nome text not null,
  dal date not null, al date not null,
  codice_invito uuid not null default gen_random_uuid(),
  completo boolean not null default false
);

create table partecipanti (
  viaggio_id uuid not null references viaggi(id) on delete cascade,
  utente_id uuid not null references auth.users(id) on delete cascade,
  primary key (viaggio_id, utente_id)
);

create function nel_viaggio(v uuid) returns boolean
language sql stable security definer set search_path = public as $
  select exists (select 1 from partecipanti where viaggio_id = v and utente_id = auth.uid());
$;

create table spese (
  id uuid primary key default gen_random_uuid(),
  viaggio_id uuid not null references viaggi(id) on delete cascade,
  pagato_da uuid not null references auth.users(id),
  centesimi int not null check (centesimi > 0),
  per uuid[] not null,
  descrizione text not null
);

alter table viaggi enable row level security;
alter table spese enable row level security;
create policy "il mio viaggio" on viaggi for select using (nel_viaggio(id));
create policy "spese del viaggio" on spese for all using (nel_viaggio(viaggio_id)) with check (nel_viaggio(viaggio_id));

-- Entrare con il link d'invito (massimo 4 persone se il viaggio non è completo)
create function entra_nel_viaggio(codice uuid) returns uuid
language plpgsql security definer set search_path = public as $
declare v viaggi;
begin
  select * into v from viaggi where codice_invito = codice;
  if v.id is null then raise exception 'Invito non valido'; end if;
  if not v.completo and (select count(*) from partecipanti where viaggio_id = v.id) >= 4 then
    raise exception 'La versione gratuita ammette 4 partecipanti';
  end if;
  insert into partecipanti values (v.id, auth.uid()) on conflict do nothing;
  return v.id;
end $;

Pezzi comuni a tutti i progetti

Glossario

Realtime
Una funzione di Supabase che aggiorna la pagina di tutti quando qualcuno cambia un dato.