App de check-in do ministério TocaPlay da Igreja Aponte. Cadastro persistente de pré-adolescentes, check-in por culto (manhã/tarde), gestão de saída, formação automática de pequenos grupos, presença de voluntários, configuração de horários e relatórios de crescimento. Multi-unidade (Boa Viagem, Casa Forte, Recife Antigo), isolado por unidade no nível do banco.
- Next.js (App Router, TypeScript) na Vercel
- Supabase — Postgres + Auth + RLS (isolamento por unidade) + Realtime
- Tailwind v4, Recharts, react-hook-form/zod
- Identidade visual "voxel night" (estética Minecraft)
Pré-requisitos: Node 20+, Docker (para o Supabase local).
npm install
cp .env.example .env.local # já preenchido com as chaves locais padrão
npm run db:start # sobe o Supabase local (aplica migrations + seed)
# se as chaves impressas diferirem, atualize .env.local
npm run create-admin -- admin@aponte.local suaSenha123 "Seu Nome" # 1º admin geral
npm run dev # http://localhost:3000Comandos úteis:
| Comando | O que faz |
|---|---|
npm run db:reset |
Recria o banco local aplicando migrations + seed |
npm run gen:types |
Regenera src/types/database.ts a partir do banco local |
npm run test:grouping |
Testa o algoritmo de pequenos grupos |
npm run verify:rls |
Verifica o isolamento por unidade (RLS) com sessões reais |
npm run verify:concurrency |
Verifica uso simultâneo (check-in duplicado, IDs, estoque, isolamento) |
npm run typecheck / npm run lint |
Checagens estáticas |
src/types/database.tsé escrito à mão (o app depende dos aliasesSex,Unit,Teen…). Ao criar uma migration nova, atualize esse arquivo à mão — não rodesupabase gen typespor cima (ele apaga os aliases).
- global_admin — vê todas as unidades. Crie o primeiro com
create-admin. - unit_admin — gerencia a própria unidade (cadastros, horários, usuários).
- volunteer — faz check-in e forma grupos na própria unidade.
Novos usuários são criados dentro do app em Config → Usuários (usa a service role no servidor; a chave nunca vai ao cliente).
- Crie um projeto em supabase.com.
supabase link --project-ref <ref>esupabase db push(aplica as migrations).- Auth → Hooks: ative o Custom Access Token apontando para
public.custom_access_token_hook(as migrations já criam a função e as permissões). - Auth → URL Configuration: adicione a URL da Vercel em Site URL e Redirect URLs.
- Rode o
create-adminapontando para a URL/chaves do projeto (via.envtemporário).
- Importe o repositório.
- Variáveis de ambiente:
NEXT_PUBLIC_SUPABASE_URL,NEXT_PUBLIC_SUPABASE_ANON_KEYSUPABASE_SERVICE_ROLE_KEY(marque como sensitive, só servidor)NEXT_PUBLIC_SITE_URL= URL de produção
- Deploy.
- Isolamento por unidade é imposto por RLS lendo
unit_id/user_roledas claims do JWT (injetadas pelo access token hook a partir deprofiles). Nunca confie emunit_idvindo do cliente — as server actions derivam decurrent_unit(). - FKs compostas
(unit_id, …)impedem, no nível do banco, referências cruzadas entre unidades. - A service role só é usada no servidor (
src/lib/supabase/admin.ts, marcadoserver-only) para provisionar usuários. - Ao mudar a unidade/papel de um usuário, o novo claim vale a partir do próximo login (peça para a pessoa sair e entrar de novo).
O app é feito para ser usado ao mesmo tempo nas 3 igrejas. As garantias são de banco, não de aplicação:
- Check-in sem duplicata:
unique (session_id, teen_id)— dois check-ins simultâneos do mesmo pré-adolescente resultam em um só (o outro recebe erro amigável). - IDs sem buraco: o contador
next_teen_display_idusa lock de linha, então cadastros simultâneos na mesma unidade geram IDs sequenciais e únicos. - Estoque sem perda de escrita: movimentos passam por
record_stock_movement(quantity = quantity + deltaatômico), então recepções simultâneas somam corretamente. - Isolamento: RLS + FKs compostas
(unit_id, id)impedem que uma unidade veja ou altere dados de outra, mesmo sob carga paralela. - As telas de check-in, grupos e voluntários assinam o Realtime, então aparelhos diferentes veem as mudanças ao vivo.
Rode npm run verify:concurrency para checar os quatro cenários de uma vez.