Landing page super minimalista para Allons. Negro, logo + un solo CTA → captura email → guarda en Supabase con atribución por QR.
- Next.js 15 (App Router)
- React 19
- Tailwind 4
- Supabase (tabla
waitlist)
cd allons-waitlist
pnpm install
cp .env.example .env.local # llena las 3 variables
pnpm dev.env.local:
NEXT_PUBLIC_SUPABASE_URL=https://YOUR_PROJECT.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=sb_publishable_...
SUPABASE_SERVICE_ROLE_KEY=eyJ...
El
SUPABASE_SERVICE_ROLE_KEYsolo se usa server-side en/api/waitlist. Nunca lo expongas con prefijoNEXT_PUBLIC_.
Abre tu proyecto → SQL Editor → pega y corre db/schema.sql.
Crea:
public.waitlistcon columnasemail,source,referer,user_agent,ip,created_at- Índice único en
lower(email)(un email solo se registra una vez) - View
waitlist_by_sourcepara ver el conteo por QR/ubicación - RLS encendido — solo el
service_role(el server) puede leer/escribir
Vercel:
vercel link
vercel env add NEXT_PUBLIC_SUPABASE_URL
vercel env add NEXT_PUBLIC_SUPABASE_ANON_KEY
vercel env add SUPABASE_SERVICE_ROLE_KEY
vercel --prodLa landing lee el query param ?src=. El valor se guarda en la columna source.
URL → https://allonsapp.com/?src=la20
Insert → { email: "...", source: "la20" }
La generación de QRs se gestiona desde allons-admin, no desde este repo.
Usa la vista /waitlist-qr del panel admin o el script pnpm run qr en
allons-admin.
- Solo
a-z,0-9,-,_. Máx. 40 chars. - El backend valida con regex; cualquier slug "raro" se guarda como
null(= directo). - Mantén el slug igual entre reprints de la misma ubicación.
En SQL Editor de Supabase:
-- Resumen agregado
select * from waitlist_by_source;
-- Detalle de la última semana, por ubicación
select source, email, created_at
from waitlist
where created_at > now() - interval '7 days'
order by created_at desc;
-- Conversión por día y por QR
select
date_trunc('day', created_at) as day,
coalesce(source, '(direct)') as source,
count(*) as signups
from waitlist
group by 1, 2
order by 1 desc, 3 desc;.
├── db/schema.sql # corre esto en Supabase
├── public/ # logo Allons
└── src/
├── app/
│ ├── api/waitlist/route.ts # POST endpoint (server-side, service_role)
│ ├── page.tsx # landing
│ ├── layout.tsx
│ └── globals.css
├── components/
│ ├── AllonsLogo.tsx
│ └── WaitlistForm.tsx # CTA → email → submit
└── lib/
└── supabase-server.ts # admin client