fortuno-app é o frontend SPA da plataforma Fortuno — uma plataforma online de sorteios (loterias privadas) que cobre todo o fluxo público (home, listagem de sorteios, página institucional, antifraude), o fluxo de compra com PIX (seleção/aleatório + QR Code + polling de status), a área autenticada do usuário (dashboard, meus números, meus pontos, meus sorteios) e o wizard administrativo de 8 etapas para criar/editar uma loteria. Construído em React 18 + TypeScript 5 + Vite 6, com Tailwind CSS para estilização e nauth-react para 100% do fluxo de autenticação.
Este repositório contém apenas o frontend. A API REST do Fortuno fica em um repositório separado (c:\repos\Fortuno\Fortuno) e a coleção Bruno com os contratos vive em c:\repos\Fortuno\Fortuno\bruno. O serviço de autenticação é o NAuth, consumido como pacote (nauth-react).
A arquitetura segue a skill /react-architecture do projeto: para cada entidade de domínio existe um quinteto canônico Types → Service → Context → Hook → Provider, com Services/apiHelpers.ts como única porta de saída HTTP, garantindo o header obrigatório X-Tenant-Id: fortuno em 100% das chamadas (SC-004).
- 🎯 Catálogo público de sorteios — home, listagem com paginação, página de detalhe com hero, regras, política de privacidade e prêmios.
- 🛒 Checkout com PIX — seleção manual ou aleatória de números, geração de QR Code, polling de status com backoff exponencial e simulador π de pagamento (mock).
- 👤 Área autenticada — dashboard, meus números (vouchers), meus pontos (saldo + extrato), meus sorteios (histórico) e gestão de conta via componentes
nauth-react. - 🧙 Wizard administrativo de 8 etapas — criação/edição de loteria (dados básicos, formato, descrições, imagens, combos, sorteios, prêmios, ativação), com persistência de progresso em
sessionStorage. - 🔐 Autenticação via NAuth — login, cadastro, esqueci-senha, alteração de senha e edição de perfil 100% em componentes da lib (sem telas custom — SC-007).
- 🌐 Multi-tenant por header —
X-Tenant-Id: fortunoinjetado em todas as requisições pela camadaapiHelpers. - 🧱 Provider chain de 9 entidades — Commission, Referral, Ticket, RaffleAward, Raffle, LotteryImage, LotteryCombo, Lottery, Checkout — todas seguindo o mesmo padrão de loading/error/toast.
- 🎨 Identidade visual canônica — paleta
fortuno.*(verde profundo, dourado intenso/suave, off-white) emtokens.css, fontes Inter + Playfair Display, dezenas de keyframes/animations sob medida. - 📝 Markdown rico — render de regras, política de privacidade e descrições editoriais via
react-markdown+remark-gfm, com geração de PDF cliente viajspdf. - 🌍 i18n pt-BR — namespace único
fortunoemi18next, pronto para extensão. - 🔔 Feedback uniforme —
sonnerpara toasts (sucesso/erro) comrichColorsno canto superior direito.
- React 18.3 — UI declarativa, Suspense, StrictMode
- TypeScript 5.6 (strict,
noUnusedLocals,noUnusedParameters,forceConsistentCasingInFileNames) — tipagem estática rígida - Vite 6 — bundler/dev server (porta 5173, alias
@/→src/) - React Router 6 — roteamento SPA com
BrowserRouter+ProtectedRoute
nauth-react^0.2.7 —NAuthProvider,useAuth, telas de login/cadastro/senha/perfil. Storage key canônica:fortuno:nauth.
- Tailwind CSS 3.4 +
tailwindcss-animate(exigido pornauth-react) - Tokens canônicos em
src/styles/tokens.css— coresfortuno.*, sombrasnoir-*/gold-*, gradientes e ~30 animações sob medida - Fontes — Inter (sans), Playfair Display (display)
- i18next 25 + react-i18next 15 — locale
pt-BR(única ativa), namespacefortuno
- Fetch API nativa (única forma permitida em novos services — Princípio II)
sonner— toastsreact-markdown+remark-gfm— render de Markdown@uiw/react-md-editor— editor de Markdown no wizard adminkeen-slider— carousel de sorteios e imagensqrcode.react— fallback de QR Code (o principal vem do backend comobrCodeBase64)jspdf— geração client-side de PDF de regras/políticalucide-react— ícones
- Vitest 2 — unit tests com
globals: true, ambientejsdom @testing-library/react+@testing-library/jest-dom+@testing-library/user-event— testes de componentes- MSW 2 — mocks de rede em testes
- Coverage — provider
v8
- GitHub Actions —
Version and Tag(GitVersion 5.x) →Create Release(gh release com release notes auto-geradas) - GitVersion —
mode: ContinuousDelivery, branches main/develop/feature/release/hotfix, prefixo de tagv
- Speckit (
.specify/) —constitution.mdv1.0.0 + templates (plan/spec/tasks) + scripts PowerShell - Skills locais (
.claude/skills/) —react-architecture,react-modal,react-alert,add-react-i18n,frontend-design,mermaid-chart,doc-manager,readme-generator
fortuno-app/
├── src/
│ ├── Services/ # uppercase S — HTTP clients (Princípio III)
│ │ ├── apiHelpers.ts # SINGLE SOURCE OF TRUTH — getHeaders, safeFetch, handleResponse, apiUrl
│ │ ├── lotteryService.ts # 1 classe por entidade
│ │ ├── lotteryComboService.ts
│ │ ├── lotteryImageService.ts
│ │ ├── raffleService.ts
│ │ ├── raffleAwardService.ts
│ │ ├── ticketService.ts
│ │ ├── referralService.ts
│ │ ├── commissionService.ts
│ │ ├── pdfService.ts # geração de PDF client-side
│ │ └── __tests__/ # testes unitários do apiHelpers
│ ├── Contexts/ # uppercase C — Provider + Context por entidade
│ │ ├── LotteryContext.tsx
│ │ ├── LotteryComboContext.tsx
│ │ ├── LotteryImageContext.tsx
│ │ ├── RaffleContext.tsx
│ │ ├── RaffleAwardContext.tsx
│ │ ├── TicketContext.tsx
│ │ ├── ReferralContext.tsx
│ │ ├── CommissionContext.tsx
│ │ └── CheckoutContext.tsx
│ ├── hooks/ # lowercase — useLottery, useTicket, useCheckout, useTenantHeader, ...
│ ├── types/ # lowercase — interfaces Info/InsertInfo/UpdateInfo + enums
│ ├── pages/
│ │ ├── public/ # HomePage, LotteryListPage, LotteryDetailPage, AboutPage, ContactPage
│ │ ├── auth/ # Login/Register/ForgotPassword/ResetPassword/ChangePassword/ProfileEdit
│ │ ├── dashboard/ # DashboardPage, MyNumbersPage, MyPointsPage, MyLotteriesPage
│ │ ├── checkout/ # CheckoutPage
│ │ └── admin/ # LotteryWizardPage + wizard-steps/Step1..Step8
│ ├── components/
│ │ ├── common/ # Modal, ConfirmModal, Pagination, LoadingSpinner, CountdownTimer, ...
│ │ ├── layout/ # AuthenticatedShell, Footer, UserMenu
│ │ ├── route-guards/ # ProtectedRoute (consome useAuth)
│ │ ├── lottery/ # LotteryHero, PrizesGrid, ComboPackCard, Receipt, modals/...
│ │ ├── checkout/ # CheckoutStepper, ChooseNumberModal, PixStep, SuccessStep, ...
│ │ ├── dashboard/ # AvatarFrame, LinkedStatCard, MyLotteriesPreview, LotteryRow, ...
│ │ ├── tickets/ # TicketCardPremium, TicketDetailModal, TicketsGrid, ...
│ │ ├── home/ # StatsBand, HeroFeaturedLottery, LotteryCarouselPremium, ...
│ │ ├── about/, contact/, points/, wizard/
│ ├── styles/
│ │ ├── tokens.css # variáveis CSS canônicas (paleta fortuno.*)
│ │ ├── index.css # entrada global
│ │ └── *.css # por-página (dashboard, lottery-detail, checkout, ...)
│ ├── i18n/
│ │ ├── index.ts # init i18next, ns 'fortuno'
│ │ └── locales/pt/fortuno.json
│ ├── utils/ # currency, datetime, getInitials, lotteryIcon
│ ├── App.tsx # <Routes> — rotas auth ficam fora do AuthenticatedShell
│ ├── main.tsx # provider chain + NAuthProvider + BrowserRouter + Toaster
│ └── test-setup.ts # setup vitest + jest-dom
├── specs/
│ └── 001-fortuno-frontend/ # spec.md, plan.md, research.md, data-model.md, contracts/, quickstart.md
├── .specify/
│ ├── memory/constitution.md # v1.0.0 — princípios I–VI
│ ├── templates/ # plan/spec/tasks/checklist/constitution
│ └── scripts/powershell/ # setup-plan, create-new-feature, check-prerequisites, ...
├── .claude/
│ ├── commands/ # speckit.specify, .plan, .tasks, .implement, .analyze, ...
│ └── skills/ # react-architecture, react-modal, react-alert, mermaid-chart, ...
├── .github/workflows/ # version-tag.yml, create-release.yml
├── docs/ # documentação adicional + diagramas
├── public/ # assets estáticos servidos pelo Vite
├── tailwind.config.js # extends fortuno.* + animations sob medida
├── postcss.config.js
├── vite.config.ts # alias @/ → src/, porta 5173
├── vitest.config.ts # globals, jsdom, setup-files, coverage v8
├── tsconfig.json # strict, paths @/*, types vite/client + vitest/globals
├── GitVersion.yml # ContinuousDelivery, prefix v
├── .env.production.example # template de variáveis de ambiente
├── MOCKS.md # registro vivo de mocks aguardando endpoints reais
├── CLAUDE.md # guia para Claude Code (arquitetura + comandos)
├── LICENSE # MIT
└── README.md # este arquivo
| Projeto | Tipo | Localização | Descrição |
|---|---|---|---|
| fortuno-app | SPA (este) | c:\repos\Fortuno\fortuno-app |
Frontend React consumindo a API Fortuno |
| Fortuno (backend) | REST API | c:\repos\Fortuno\Fortuno |
API .NET com domínio de loterias, tickets, pagamentos |
| Bruno collection | API tests | c:\repos\Fortuno\Fortuno\bruno |
Coleção Bruno (HTTP client) com os contratos canônicos |
nauth-react |
NPM package | ^0.2.7 |
Componentes e provider de autenticação NAuth |
Browser (usuário)
│
▼
fortuno-app (SPA — React + Vite)
├──▶ nauth-react ──▶ NAuth API (X-Tenant-Id: VITE_NAUTH_TENANT, Bearer)
└──▶ apiHelpers.ts ──▶ Fortuno API (X-Tenant-Id: fortuno, Bearer NAuth)
O diagrama abaixo ilustra a arquitetura de alto nível do fortuno-app — um SPA React que consome dois backends distintos (NAuth para auth, Fortuno para domínio) com apiHelpers.ts como porta única de saída HTTP:
Componentes principais:
- React Router 6 +
ProtectedRoutecontrolam a navegação. Rotas protegidas ficam aninhadas emAuthenticatedShell; rotas de auth (/login,/cadastro,/esqueci-senha,/recuperar-senha) ficam fora do shell com fundo dark dedicado. - Provider chain (
main.tsx) — 9 providers de domínio (Commission → Referral → Ticket → RaffleAward → Raffle → LotteryImage → LotteryCombo → Lottery → Checkout) aninhados dentro doNAuthProvider. Estado é local a cada provider — sem state manager global. Services/apiHelpers.ts— funçãogetHeaders()injetaX-Tenant-Id: fortunoem 100% das chamadas (SC-004) e adicionaAuthorization: Bearer <token>lendo o token NAuth delocalStorage["fortuno:nauth"]. WrappersafeFetchconverte erros de rede emApiError(status=0)com mensagem PT-BR.nauth-reactgerencia sessão, exibe telas de auth e persiste o token emlocalStorage(chavefortuno:nauth).- Tailwind + tokens — paleta
fortuno.*definida emsrc/styles/tokens.csse estendida emtailwind.config.js.
📄 Source: o Mermaid editável fica em
docs/system-design.mmd.
| Documento | Descrição |
|---|---|
| FRONTEND_STORE_TRANSPARENT_MIGRATION | Plano de migração frontend para a remoção de storeId exposto pela API de Lotteries (mudança breaking pendente). |
| FRONTEND_TICKET_NUMBER_FORMAT_MIGRATION | Plano de migração do formato unificado de número de ticket e faixas por NumberType (3 mudanças breaking nos contratos de Tickets/Lotteries). |
| CLAUDE.md | Guia operacional para o Claude Code — comandos, arquitetura, convenções constitucionais, env vars. |
| MOCKS.md | Registro vivo de mocks aguardando endpoints reais no backend. |
| Constituição do projeto | Princípios I–VI (skill obrigatória, stack fixa, casing inviolável, convenções, autenticação, env vars). |
| Spec da feature ativa | spec.md, plan.md, research.md, data-model.md, contracts/api-endpoints.md, quickstart.md. |
Antes de subir o dev server, configure as variáveis de ambiente:
cp .env.production.example .env# Base da API REST do Fortuno (sem barra final é tolerado — apiHelpers normaliza)
VITE_API_URL=https://api.fortuno.example.com
# Tenant ID injetado em X-Tenant-Id em 100% das chamadas Fortuno (SC-004)
VITE_FORTUNO_TENANT_ID=fortuno
# Base da API NAuth (autenticação)
VITE_NAUTH_API_URL=https://nauth.example.com
# Tenant do NAuth (config separada do tenant Fortuno acima)
VITE_NAUTH_TENANT=your_nauth_tenant_id_here
# Basename do BrowserRouter (use "/" para deploy na raiz)
VITE_SITE_BASENAME=/
# Links sociais e contato exibidos no Footer / contato
VITE_WHATSAPP_URL=https://wa.me/55XXXXXXXXXXX
VITE_INSTAGRAM_URL=https://instagram.com/fortuno
VITE_CONTACT_EMAIL=contato@fortuno.example.com- O arquivo
.env(e.env.production) está no.gitignore— nunca comite credenciais reais. - Apenas
.env.production.exampledeve ir para o controle de versão. - Toda env var exposta ao bundle DEVE ter prefixo
VITE_(Princípio VI da constituição). Outros prefixos viramundefinedem runtime.
- Node.js 20+ (compatível com Vite 6)
- npm 10+ (ou pnpm/yarn —
package.jsonnão declara packageManager) - API Fortuno rodando localmente ou disponível via HTTPS (repositório
c:\repos\Fortuno\Fortuno) - NAuth API acessível (URL definida em
VITE_NAUTH_API_URL)
🚫 Docker não é suportado neste repositório. A constituição (Princípio II) proíbe a criação de
docker-compose.ymlou execução dedocker/docker composeno ambiente local.
git clone https://github.com/emaginebr/fortuno-app.git
cd fortuno-app
npm installcp .env.production.example .env
# edite .env com as URLs reais da API Fortuno e NAuthnpm run dev
# Vite sobe em http://localhost:5173npm run build # tsc -b && vite build → gera dist/
npm run preview # serve dist/ localmente para smoke testStack: Vitest 2 + Testing Library + jsdom (config em vitest.config.ts, setup em src/test-setup.ts).
| Ação | Comando |
|---|---|
Watch (default do npm test) |
npm test |
| One-shot (CI) | npx vitest run |
| UI interativa do vitest | npm run test:ui |
| Cobertura (provider v8) | npm run test:coverage |
| Arquivo único | npx vitest run src/Services/__tests__/apiHelpers.test.ts |
| Filtro por nome do teste | npx vitest run -t "injeta X-Tenant-Id" |
| Typecheck (sem rodar testes) | npm run typecheck |
src/
├── Services/__tests__/ # testes unitários da camada HTTP (apiHelpers, getHeaders, safeFetch)
└── components/<area>/__tests__/ # testes de componente (ex.: ComboSelector)
vitest.config.ts exclui src/pages/auth/** da cobertura (lá só há composições de componentes do nauth-react).
- Header obrigatório
X-Tenant-Id: fortunoem 100% das chamadas (SC-004) — fail-fast se alguém usarfetchdireto. - Token NAuth lido de
localStorage["fortuno:nauth"](com fallbacks de chave) e enviado comoAuthorization: Bearer <token>. 401 →UnauthenticatedError→ redirect para/loginviaProtectedRoute. safeFetchisola erros de rede (CORS, DNS, timeout) emApiError(status=0)para evitarTypeError: Failed to fetchvazando como exceção genérica.
- Sem secrets no bundle. Apenas variáveis prefixadas
VITE_chegam ao client (Princípio VI). - Connection strings, chaves privadas, SECRET_KEY ficam server-side. O frontend só vê URLs públicas e tenant IDs.
- 100% via
nauth-react— proibido criar telas custom de auth (SC-007). - Sem armazenamento em cookies — token apenas em
localStorage, sob a chave canônicafortuno:nauth.
Dois workflows em .github/workflows/:
Triggers:
- Push em
main - Disparo manual (
workflow_dispatch)
Steps:
- Checkout com
fetch-depth: 0(necessário para GitVersion). - Instala GitVersion 5.x.
- Calcula a versão a partir de
GitVersion.yml(modeContinuousDelivery, branches main/develop/feature/release/hotfix). - Cria e empurra a tag
v<NuGetVersionV2>se ela ainda não existir.
Convenções de bump por commit message:
major:/breaking:→ bump MAJORfeat:/feature:/minor:→ bump MINORfix:/patch:→ bump PATCH+semver: none|skip→ não incrementa
Trigger: conclusão bem-sucedida do workflow Version and Tag.
Comportamento:
- Compara a tag mais recente com a anterior.
- Se a versão MINOR ou MAJOR mudou (ou se é a primeira tag), cria um GitHub Release com
gh release create … --generate-notes. - Se for apenas patch, pula a criação do release.
Pull requests são bem-vindas. Antes de abrir, confira o checklist constitucional do projeto.
- Fork do repositório.
- Crie uma branch a partir de
main(git checkout -b feature/nome-da-featureou001-feature-slugpara alinhar com o speckit). - Implemente seguindo a stack e convenções abaixo.
- Rode
npm run typecheckenpm testantes do commit. - Use mensagens convencionais (
feat:,fix:,refactor:,docs:,chore:) — o GitVersion depende disso para incrementar a versão. - Abra PR contra
main.
- Princípio I (não-negociável): novas entidades de domínio são scaffoldadas via skill
/react-architecture. Não reimplementar Types/Service/Context/Hook manualmente. - Princípio III (inviolável): casing literal de diretórios —
Contexts/,Services/(uppercase) ehooks/,types/(lowercase). Imports devem bater exato (Linux/CI quebra; Windows mascara). - Princípio IV:
interface(nãotypepara objetos), arrow functions,constpor padrão, PascalCase / camelCase / UPPER_CASE. - Princípio VI: env vars frontend usam prefixo
VITE_e são lidas viaimport.meta.env.VITE_*. NuncaREACT_APP_. - Stack fixa: sem Redux/Zustand/MobX (Context API é o padrão); novos services HTTP usam Fetch API; Axios fica apenas em código legado.
- HTTP via
apiHelpers: nunca chamefetchdireto a partir de um service — usesafeFetch+handleResponsepara garantir tenant header e tratamento padronizado de erros. - Mocks: crie um mock APENAS quando detectar endpoint faltando durante a implementação; marque com
// MOCK: aguarda endpoint <path>e registre emMOCKS.md.
Documento completo: .specify/memory/constitution.md.
Desenvolvido por Emagine.
Este projeto é licenciado sob a MIT License — veja o arquivo LICENSE para detalhes.
Copyright (c) 2026 Emagine.
- Construído com React, TypeScript e Vite.
- Estilo via Tailwind CSS +
tailwindcss-animate. - Autenticação ponta-a-ponta via
nauth-react. - Toasts com
sonner. - Markdown via
react-markdown+remark-gfm. - Versionamento automatizado com GitVersion.
- Workflow de spec-first com Speckit.
- Issues: GitHub Issues
- Pull Requests: GitHub PRs
- Releases: GitHub Releases
⭐ Se este projeto te ajudou, considere dar uma estrela!
