Protótipo visual das 14 telas do projeto Claude Design
(af466763-6cc2-4746-9103-6226934b80a5), portado para Next.js.
Eventos, perfis, cronograma, cardápio e bar vivem no Supabase (Postgres + Auth),
com RLS em todas as tabelas. Os módulos ainda não migrados (presentes, galeria,
mural, amigo secreto) continuam lendo fixtures em src/lib/fixtures/
até que cada um ganhe seu próprio change.
Pré-requisitos: Node 20+, Docker Desktop rodando e make.
make devUm comando faz tudo: instala dependências, sobe o Supabase local, gera o
.env.local a partir das chaves do próprio CLI, aplica as migrations pendentes
e abre a aplicação em http://localhost:3000.
É seguro repetir — make dev não apaga dados. Na primeira execução ele baixa as
imagens dos contêineres, o que leva alguns minutos.
Abra /rotas para um índice navegável de todas as telas.
| Comando | O que faz |
|---|---|
make dev |
Sobe tudo e roda a aplicação |
make setup |
Primeira execução: instala, sobe o banco e carrega o seed |
make reset |
Recria o banco — migrations + seed. Apaga os dados locais |
make seed |
Regenera supabase/seed.sql dos mocks e carrega |
make types |
Regenera src/lib/database.types.ts a partir do schema |
make test |
Testes de política de RLS e de sessão |
make verify |
Lint + tipos + build + testes (o que o CI roda) |
make studio / make mail |
Abre o Studio / a caixa de e-mail local |
make stop |
Derruba os contêineres, preservando os dados |
make clean |
Derruba e apaga os dados do banco local |
make help |
Lista os alvos |
Cada alvo é um atalho para o script npm equivalente (npm run db:reset,
npm test, …), então quem não tem make pode seguir pelos scripts do
package.json — só precisará montar o .env.local na mão, copiando de
.env.example as chaves que npm run db:start imprime.
Sem Docker, aponte o .env.local para um projeto Supabase de staging pessoal —
o código é o mesmo, só mudam as variáveis.
| Serviço | URL |
|---|---|
| API (Supabase) | http://127.0.0.1:55421 |
| Postgres | postgresql://postgres:postgres@127.0.0.1:55422/postgres |
| Studio | http://127.0.0.1:55423 |
| Caixa de e-mail | http://127.0.0.1:55424 |
Os links mágicos enviados em desenvolvimento não saem da máquina: abra a
caixa de e-mail local em 55424 para clicar no link.
As portas saem da faixa
554xxem vez da543xxpadrão do Supabase CLI para não conflitar com outro projeto Supabase rodando na mesma máquina e para escapar da faixa que o Windows/WinNAT reserva dinamicamente (544xxcaiu nela). Elas vivem emsupabase/config.toml.
| Comando | O que faz |
|---|---|
npm run db:start / db:stop |
Sobe / derruba o ambiente local |
npm run db:reset |
Recria o banco: migrations em ordem + seed |
npm run db:migrate |
Aplica as migrations pendentes |
npm run db:seed |
Regenera supabase/seed.sql dos mocks e carrega |
npm run db:types |
Regenera src/lib/database.types.ts do schema |
npm run db:diff |
Falha se o schema divergir das migrations |
Next.js 16 (App Router) · React 19 · TypeScript · Tailwind CSS v4 · Supabase
| Rota | Tela |
|---|---|
/ |
Landing Page |
/e/[slug] |
Página do Evento |
/e/[slug]/rsvp |
Fluxo RSVP (3 passos) |
/e/[slug]/presentes |
Lista de Presentes → checkout → sucesso |
/e/[slug]/galeria |
Galeria e Mural (com estado vazio) |
/e/[slug]/amigo-secreto |
Amigo Secreto — Cadastro |
/e/[slug]/amigo-secreto/revelacao |
Amigo Secreto — Revelação |
| Rota | Tela |
|---|---|
/onboarding |
"Sua página está sendo preparada" (informativa) |
/painel |
Painel do Cliente |
/painel/convidados |
Gestão de Convidados |
/painel/editor |
Editor da Página |
/sem-acesso |
Conta autenticada sem provisionamento e sem evento |
| Rota | Tela |
|---|---|
/admin |
Painel Admin |
/admin/clientes |
Contas de cliente — cadastrar (convite) e listar |
/admin/paginas |
Criar página de evento e atribuir o anfitrião |
Slugs de exemplo carregados pelo seed: marina-e-thiago, 15-anos-sofia,
aniversario-do-bento, convencao-nexus-2026.
/painel, /painel/*, /onboarding e /admin/* exigem sessão.
Entre em /entrar com um dos e-mails do seed — admin@exemplo.local para o
painel administrativo, marina-e-thiago@exemplo.local para o de cliente — e
abra o link mágico na caixa de e-mail local (http://127.0.0.1:55424).
A criação de evento não é self-service. A Equipe Villa Glam (admin):
- cadastra a conta do cliente em
/admin/clientes(nome + e-mail) — o perfil nasce já provisionado, sem e-mail nenhum ainda; - cria a página do evento em
/admin/paginas, escolhendo a conta anfitriã e o tipo de evento (casamento, debutante, corporativo, aniversário, formatura, confraternização ou outros) — só agora sai o e-mail de acesso ao painel, com o endereço da página e o e-mail de login; - o cliente abre o link de acesso, cai em
/painele passa a editar o conteúdo, o cronograma, a capa e a moderação da galeria.
Se esse e-mail falhar ou o cliente perdê-lo, o admin reenvia pela coluna
Acesso em /admin/clientes. O envio real depende de um provedor
configurado — ver docs/email-resend.md.
Um e-mail que autentica sem ter sido provisionado e sem ser anfitrião de nenhum
evento cai em /sem-acesso — a sessão existe, mas nenhum painel se abre.
A galeria pode exigir aprovação prévia: com a política previa, a foto
enviada por um convidado nasce pendente e só aparece depois que o anfitrião
aprova. O mural não tem aprovação prévia — todo recado (com ou sem foto)
aparece na hora; o anfitrião pode ocultar ou remover depois de publicado.
| Método | Rota | |
|---|---|---|
| GET | /api/eventos |
do banco, escopado por RLS |
| POST | /api/eventos |
cria a página — só admin, anfitrião no corpo |
| GET · POST | /api/admin/clientes |
lista / cadastra (convite) contas de cliente — só admin |
| GET | /api/eventos/[slug] |
do banco, 404 real |
| GET · POST | /api/eventos/[slug]/convidados |
evento real, convidados de fixture |
| GET | /api/eventos/[slug]/presentes |
evento real, presentes de fixture |
| GET | /api/auth/callback |
troca o link mágico por sessão |
| POST | /api/auth/sair |
encerra a sessão |
POST /api/eventos/[slug]/convidados ainda devolve persisted: false — o RSVP
chega no change add-guests-rsvp-backend.
As páginas de servidor não passam por estas rotas: chamam src/lib/db/
direto. As rotas existem para consumo externo e para Client Components.
src/
proxy.ts Renova a sessão (o antigo middleware)
app/
page.tsx Landing
entrar/ Login por link mágico
rotas/ Índice de todas as telas
onboarding/ Tela informativa "página em preparação"
sem-acesso/ Conta autenticada sem acesso a painel
painel/ layout + visão geral, convidados, editor
admin/ Guarda o papel de admin + clientes, paginas
e/[slug]/ Páginas públicas do evento
api/ Route handlers (eventos, sessão)
components/
ui.tsx Button, Input, Sheet, Badge, Toggle, Stepper...
ImageSlot.tsx Placeholder de foto (equivale ao <image-slot>)
GuestShell.tsx Moldura das telas do convidado
PanelNav.tsx Countdown.tsx SairButton.tsx
lib/
env.ts env.public.ts Variáveis validadas (servidor / navegador)
supabase/ Clients: server, client, admin (service-role)
auth/ Guardas de rota e validação de redirect
db/ Acesso a dados — único lugar com snake_case
validation/ Schemas Zod das rotas de escrita
fixtures/ O que ainda é mock (ver fixtures/README.md)
database.types.ts Gerado do schema por `npm run db:types`
mock-data.ts Fonte do seed — não importar em páginas
types.ts themes.ts format.ts slug.ts use-now.ts
supabase/
migrations/ Única fonte de verdade do schema
seed.sql Gerado a partir de mock-data.ts
scripts/db/ start, reset, types, diff, seed
tests/ Políticas de RLS e fluxo de sessão
Tokens em src/app/globals.css (--paper, --ink, --accent, …), expostos ao
Tailwind via @theme inline — daí vêm bg-paper, text-ink-soft, bg-accent.
O tema de cor é por evento: themeVars(event.themeKey) sobrescreve --accent
numa subárvore, então a mesma página muda de cor conforme o evento. Quatro temas
em src/lib/themes.ts: terracota, oliva, marinho e dourado.
Tipografia: Playfair Display (títulos), Inter (texto), Cormorant Garamond (usada pelo tema "Romântico" do editor).
Três projetos Supabase, um único código. O que muda entre eles é só o conjunto de variáveis de ambiente.
| Ambiente | Projeto | Origem das variáveis |
|---|---|---|
| local | Supabase CLI em Docker | .env.local, a partir de .env.example |
| staging | projeto na nuvem | variáveis do provedor de deploy |
| produção | projeto na nuvem | variáveis do provedor de deploy |
Nenhum arquivo .env é versionado além de .env.example. Toda variável é
validada em tempo de import: src/lib/env.public.ts (navegador) e
src/lib/env.ts (servidor, server-only). Falta uma variável, a aplicação não
sobe — e o erro diz qual.
SUPABASE_SERVICE_ROLE_KEY ignora a RLS e é lida somente por
src/lib/supabase/admin.ts. Qualquer import novo desse módulo pede revisão.
- A mudança de schema nasce como migration local:
npx supabase migration new <nome>, ounpx supabase db diff -f <nome>para capturar o que foi feito no Studio local. npm run db:resetprova que a migration aplica em banco vazio, em ordem.- Staging:
npx supabase link --project-ref <ref-staging>enpx supabase db push. npm run db:diff -- --linkedconfirma que não sobrou divergência. O pipeline roda esse comando e interrompe o deploy se ele falhar.- Produção: mesmo par de comandos, com o ref de produção.
Nenhuma alteração de schema é feita pelo painel do Supabase. Migrations versionadas são a única fonte de verdade — RLS, funções e triggers incluídos. O que for alterado direto no painel de staging ou produção não é válido, será apontado por
npm run db:diffe sobrescrito na promoção seguinte.
O pipeline em .github/workflows/ci.yml já roda lint, tipos, build e os testes
de política a cada push, e bloqueia o merge quando o schema remoto diverge das
migrations. Para o passo de divergência funcionar, faltam credenciais que só
quem tem a conta do Supabase pode criar:
- Criar os projetos de staging e produção no Supabase.
- Registrar como secrets do repositório:
SUPABASE_ACCESS_TOKEN,SUPABASE_STAGING_PROJECT_REFeSUPABASE_STAGING_DB_PASSWORD. - No provedor de deploy, definir por ambiente as seis variáveis do
.env.example, comAPP_ENV=stagingeAPP_ENV=production. - No painel de cada projeto Supabase, em Authentication → URL
Configuration, apontar Site URL para o
NEXT_PUBLIC_SITE_URLdaquele ambiente e adicionar<NEXT_PUBLIC_SITE_URL>/api/auth/callbackàs Redirect URLs. Sem isso o link mágico volta para o lugar errado. - Aplicar as migrations em staging a partir do zero
(
npx supabase link --project-ref <ref>enpx supabase db push) e confirmar comnpm run db:diff -- --linked.
Os refs de projeto não entram no repositório — são configuração de ambiente, como todo o resto.
npm run verify # lint + tipos + build + testesOs testes de política (tests/rls.test.mjs) falam com o banco local
autenticados como cliente A, cliente B e admin, e afirmam que leitura e escrita
cruzadas entre clientes são negadas. Exigem
npm run db:start. tests/auth.test.mjs percorre o link mágico de ponta a
ponta e é pulado se a aplicação não estiver rodando.
Os tipos em src/lib/types.ts são o contrato das respostas de API. O banco usa
snake_case; a conversão para camelCase acontece só em src/lib/db/.