← Tutte le idee Strumenti online

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.