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.