Moduli online con Google Sheets
Moduli online che salvano le risposte direttamente su Google Sheets
Incasso mensile: 6725 USD (fonte indicata nella scheda)
Cosa fa
Un creatore di moduli online che collega le risposte a Google Sheets in automatico. È attivo da 10 anni.
Come guadagna
Incasso annuo dichiarato di circa 80.700 $ nell'annuncio di vendita su Microns, che verifica i dati. Il nome del progetto non è pubblico.
Rendilo tuo
- Lo stesso collegamento verso gli strumenti che usano le piccole aziende italiane: WhatsApp, gestionali, email.
- Prova che un'idea semplice fatta bene dura anni.
Le frasi da scrivere all'AI
1. Base del sito e grafica
Crea un'app web in italiano chiamata "Modulo a Foglio": crea moduli online le cui risposte finiscono da sole, riga per riga, in un foglio Google Sheets del cliente. Usa React, Vite, TypeScript e Tailwind CSS.
Pagine da creare:
1. Pagina iniziale con l'animazione "modulo compilato → riga nel foglio" e prezzi
2. Registrazione e accesso
3. Creatore del modulo con i campi da trascinare
4. Collegamento al foglio Google: istruzioni passo passo
5. Pagina pubblica del modulo /f/:slug
Stile: pratico e chiaro: sfondo bianco, verde Sheets #188038, testo #202124, carattere "Inter", campi del modulo grandi e ben distanziati. 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:
- "moduli": titolo, slug, campi in JSON, id del foglio Google, nome della scheda, collegati all'utente
- "risposte": copia di ogni risposta, con stato dell'invio al foglio (inviata, in errore)
Regole di sicurezza obbligatorie:
- i moduli pubblicati sono visibili a tutti, ma solo il proprietario li modifica
- le risposte le scrive solo la Edge Function
- la chiave dell'account di servizio Google sta solo nei segreti della Edge Function
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. Collegamento a Google Sheets
Il collegamento usa un account di servizio Google: un indirizzo email speciale del sito. L'utente apre il suo foglio, lo condivide con quell'indirizzo come "Editor" e incolla il link del foglio nel sito. Mostra questi passaggi con immagini.
Quando qualcuno invia il modulo, la Edge Function "invia-a-foglio" (nei Dettagli tecnici) controlla i campi, salva una copia nella tabella "risposte" e aggiunge una riga al foglio con data, ora e tutte le risposte nell'ordine dei campi. Alla prima risposta scrive anche la riga di intestazione.
Se Google non risponde, la risposta resta salvata come "in errore" e viene ritentata ogni ora: nessuna risposta va persa.
4. Creatore del modulo e risposte
Crea il creatore del modulo: campi da aggiungere (testo, email, telefono, numero, data, scelta singola, scelta multipla, testo lungo), obbligatori o no, riordinabili trascinando, con anteprima dal vivo.
Pagina risposte con l'elenco, lo stato dell'invio al foglio e il pulsante "Reinvia". Email al proprietario per ogni nuova risposta, se attivata. Codice da incorporare il modulo in un altro sito.
5. Pagamenti con Stripe
Aggiungi un abbonamento a 7 € al mese. Gratis l'utente ha: un modulo e 100 risposte al mese. Con il piano Pro sblocca: moduli e risposte illimitati, caricamento di file, email a ogni risposta e nessun marchio.
A pagamento riuscito il webhook porta l'utente al piano Pro; se l'abbonamento viene disdetto o non rinnovato lo riporta al piano gratuito.
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. Foglio: togli la condivisione del foglio, invia una risposta e controlla che resti salvata e venga reinviata dopo aver rimesso la condivisione.
Correggi quello che non va e scrivimi cosa hai cambiato.
Dettagli tecnici
Aggiungere una riga a Google Sheets
La Edge Function si autentica con l'account di servizio Google (una firma con chiave privata) e aggiunge una riga in fondo al foglio. La chiave privata sta solo nei segreti.
// Parte della Edge Function "invia-a-foglio"
import { SignJWT, importPKCS8 } from "npm:jose";
async function tokenGoogle(): Promise<string> {
const email = Deno.env.get("GOOGLE_SERVICE_EMAIL")!;
const chiave = await importPKCS8(Deno.env.get("GOOGLE_PRIVATE_KEY")!, "RS256");
const firma = await new SignJWT({ scope: "https://www.googleapis.com/auth/spreadsheets" })
.setProtectedHeader({ alg: "RS256" })
.setIssuer(email)
.setAudience("https://oauth2.googleapis.com/token")
.setIssuedAt()
.setExpirationTime("1h")
.sign(chiave);
const r = await fetch("https://oauth2.googleapis.com/token", {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
assertion: firma,
}),
});
if (!r.ok) throw new Error("Accesso a Google non riuscito");
return (await r.json()).access_token;
}
export async function aggiungiRiga(idFoglio: string, scheda: string, valori: string[]) {
const url = "https://sheets.googleapis.com/v4/spreadsheets/" + idFoglio +
"/values/" + encodeURIComponent(scheda + "!A1") + ":append?valueInputOption=USER_ENTERED";
const r = await fetch(url, {
method: "POST",
headers: { Authorization: "Bearer " + (await tokenGoogle()), "Content-Type": "application/json" },
body: JSON.stringify({ values: [[new Date().toLocaleString("it-IT"), ...valori]] }),
});
if (!r.ok) throw new Error("Google Sheets ha risposto " + r.status);
}Tabelle e nuovi tentativi
I moduli, le risposte con lo stato dell'invio, e il controllo orario che rimanda quelle in errore. Richiede pg_cron e pg_net.
create table moduli (
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,
titolo text not null,
campi jsonb not null default '[]',
id_foglio text,
scheda text not null default 'Risposte',
pubblicato boolean not null default false
);
alter table moduli enable row level security;
create policy "moduli pubblicati" on moduli for select using (pubblicato or auth.uid() = utente_id);
create policy "gestisco i miei moduli" on moduli
for all using (auth.uid() = utente_id) with check (auth.uid() = utente_id);
create table risposte (
id uuid primary key default gen_random_uuid(),
modulo_id uuid not null references moduli(id) on delete cascade,
dati jsonb not null,
stato text not null default 'da_inviare' check (stato in ('da_inviare', 'inviata', 'errore')),
creato_il timestamptz not null default now()
);
alter table risposte enable row level security;
create policy "risposte dei miei moduli" on risposte for select using (exists (
select 1 from moduli m where m.id = modulo_id and m.utente_id = auth.uid()));
-- Ogni ora ritenta le risposte non arrivate al foglio
select cron.schedule('ritenta-fogli', '0 * * * *', $
select net.http_post(
url := 'https://TUO-PROGETTO.supabase.co/functions/v1/ritenta-fogli',
headers := jsonb_build_object('Authorization', 'Bearer TUA-CHIAVE-CRON')
);
$);
Pezzi comuni a tutti i progetti
Glossario
- JSONB
- Un tipo di colonna che contiene dati strutturati flessibili, come un piccolo documento.
- Cron
- Un'operazione programmata che parte da sola a orari fissi, per esempio ogni lunedì mattina.
- Account di servizio
- Un "utente robot" di Google con il suo indirizzo email: si condivide il foglio con lui e il sito può scriverci.