Voltar ao início

SaaS · Em produção

Portal MX

A plataforma que tirou a gestão de um campeonato de motocross da planilha e do grupo de WhatsApp — inscrição, pagamento, documentação, resultado e ranking no mesmo lugar.

PapelDesenvolvedor solo
ClienteCampeonato Paulista de Motocross
Período2025 — 2026
Status No ar
Emblema do Campeonato Paulista de Motocross — Cross Sport

O problema

Um campeonato inteiro rodando em planilha

Organizar uma etapa de motocross envolve muito mais do que largar as motos. Cada piloto precisa se inscrever numa categoria, provar que tem licença e seguro em dia, pagar a taxa, receber um número, correr, e ter o resultado somado à pontuação da temporada. Multiplique isso por dezenas de pilotos, mais de uma dúzia de categorias e várias etapas.

Antes do Portal MX, esse fluxo vivia espalhado entre planilhas, mensagens de WhatsApp e comprovantes soltos. Os pontos de ruptura eram sempre os mesmos:

  • 01Inscrição por mensagem, com dados incompletos e retrabalho de digitação.
  • 02Documentos e comprovantes de pagamento perdidos na conversa.
  • 03Conferência manual de quem pagou, quem está regular e quem pode correr.
  • 04Ranking somado à mão depois de cada etapa — lento e sujeito a erro.
  • 05Piloto sem lugar nenhum para consultar a própria situação.

A solução

O que foi construído

O Portal MX é uma aplicação única com duas faces. A pública, onde o piloto se inscreve e o público acompanha o campeonato. E a administrativa, onde a organização conduz a operação da temporada.

Portal público

18 rotas abertas

  • /
  • /eventos
  • /eventos/:slug
  • /eventos/:slug/inscrever
  • /calendario
  • /rankings
  • /resultados
  • /noticias
  • /galeria
  • /pilotos
  • /pilotos/:id
  • /empresa
  • /sobre
  • /blog
  • /parceiros
  • /contato
  • /termos
  • /privacidade

Painel administrativo

14 módulos, acesso por papel

  • Dashboard
  • Eventos
  • Inscrições
  • Pilotos
  • Pagamentos
  • Categorias
  • Rankings
  • Calendário
  • Notícias
  • Galeria
  • Usuários
  • Logs
  • Analytics
  • Configurações

Arquitetura

Como as peças se encaixam

É uma SPA em React servida como estático, falando direto com o Supabase. Não existe backend próprio no meio do caminho: a autorização mora no banco, em Row Level Security, e o mesmo conjunto de regras vale para qualquer cliente que se conecte.

Vercel build estático + CDN NAVEGADOR SPA React 19 + Vite React Router · React Query Recharts · Zod · Tailwind SUPABASE Supabase Auth sessão + JWT Postgres 20 tabelas · 72 policies RLS 22 migrations versionadas Storage documentos · galeria toda leitura e escrita passa por RLS login consultas + JWT upload

Decisões

As escolhas que sustentam o sistema

Autorização

Row Level Security como fonte da verdade

Esconder um botão no front-end não é segurança. Como o navegador fala direto com o banco, toda regra de quem-vê-o-quê está escrita em policies de RLS no Postgres — 72 delas. Um piloto lê a própria inscrição porque a policy permite, não porque a tela não mostrou o resto.

Papéis

Quatro níveis de acesso, verificados no banco

superadmin, admin, organizer e pilot. O papel vem do perfil do usuário e é lido dentro das policies, então a mesma consulta devolve conjuntos diferentes conforme quem pergunta. As rotas do admin ainda são protegidas no cliente, mas isso é conveniência de navegação — não é a barreira.

Schema

Banco versionado em migrations

São 22 migrations numeradas, do schema base às permissões e ao fluxo de inscrição. O banco é reconstruível do zero em ordem, e cada mudança tem um arquivo com data e intenção — o que evita o clássico "funciona na minha máquina" quando o schema é editado direto no painel.

Dados no cliente

React Query no lugar de estado global

Quase todo o estado da aplicação é, na verdade, estado do servidor. Delegar cache, revalidação e loading ao React Query eliminou a necessidade de um store global e manteve as telas de listagem consistentes sem sincronização manual.

Formulários

Validação declarada uma vez

Inscrição de evento tem muitos campos condicionais. Com Zod descrevendo o formato e React Hook Form ligando ao formulário, a regra de validação fica em um lugar só e o erro aparece no campo certo, sem cadeia de ifs espalhada pelo componente.

Números

O tamanho do projeto

122arquivos TypeScript
20.4klinhas de TS/TSX
20tabelas no Postgres
72policies de RLS
22migrations versionadas
14módulos no admin

Números apurados diretamente no código-fonte do projeto. Não incluí métricas de uso (pilotos cadastrados, inscrições processadas) porque essas eu não consigo verificar aqui.

Aprendizados

O que eu levo daqui

  • Autorização perto do dado envelhece melhor. Cada tela nova herda as regras em vez de reimplementá-las.
  • Modelar o domínio antes da interface economizou reescrita. Categoria, etapa, inscrição e resultado são entidades com regras próprias — tratá-las como tal desde o começo evitou remendos depois.
  • Software para quem não é da área precisa perdoar erro. Boa parte do esforço foi em estado vazio, mensagem de erro clara e confirmação — não em funcionalidade nova.
  • Fazer sozinho ensina o custo real de cada decisão: quem escolhe a arquitetura é quem mantém, migra e depura ela às onze da noite.