Files
mercado.clube67.com/docs/superAdmin.md
T

245 lines
11 KiB
Markdown

# Super Admin, Multi-tenant & Clube de Benefícios — Plano de Implementação
## Visão Geral
Transformar o sistema em uma plataforma multi-tenant SaaS. Cada empresa tem identidade visual, domínio, usuários e rifas isolados. Um Super Admin gerencia todas as empresas. As páginas públicas migram de Bootstrap/HTML para React + Tailwind em uma única entrega que inclui o Clube de Benefícios (Odonto + Médico).
---
## Decisões de Arquitetura
- **Detecção de tenant**: subdomínio (`alemao.app.com`, `ariane.app.com`) + fallback header `X-Tenant-Slug`
- **Branding**: armazenado no DB (`tenant_config`), injetado via `/api/tenant/config` como CSS variables
- **Auth do cliente**: CPF+telefone → **email + senha** (bcrypt). Legados mantêm fallback temporário.
- **Bootstrap removido**: zero reescrita em Bootstrap — próxima versão das páginas públicas já é React
- **customer-app**: SPA React unificada com landing, login, sorteio, clube e dashboard do cliente
- **Super Admin**: SPA React separada em `/superadmin`
- **tenant_id**: todas as tabelas (existentes e novas do clube) recebem `tenant_id` na mesma migration
---
## Sequência de Execução
```
9A → 9B → 10A → 10B → 10C
│ └─► 10F (relatórios, após 10B+10C)
├─► 9E (super admin, paralelo com 10A+10B+10C)
└─► 9F (admin multi-tenant, paralelo com 10A+10B+10C)
9D + 9C + 10D + 10E ← FUSÃO: customer-app React unificado
(após 9B + 10B + 10C estarem com backend pronto)
```
> 10D (slides Bootstrap) REMOVIDA — Bootstrap não recebe mais desenvolvimento.
> 9C (multi-ticket) e 10E (carteirinha) são implementados dentro do customer-app React.
---
## Fase 9A — Fundação Multi-tenant (Backend) ← ATUAL
### Banco de dados
- [x] Criar tabela `tenants``id, slug, name, domain, active, created_at`
- [x] Criar tabela `tenant_config``tenant_id, primary_color, secondary_color, logo_url, favicon_url, contact_email, contact_phone, app_url, asaas_key`
- [x] Adicionar coluna `tenant_id` em `users`
- [x] Adicionar coluna `tenant_id` em `raffles`
- [x] Adicionar coluna `tenant_id` em `orders`
- [x] Adicionar coluna `tenant_id` em `tickets`
- [x] Adicionar coluna `tenant_id` em `promotions`
- [x] Adicionar coluna `tenant_id` em `whatsapp_logs`
- [x] Criar tabelas do clube já com `tenant_id`: `benefit_clubs`, `club_plans`, `club_members`, `club_dependents`, `club_payments`, `club_partners`
- [x] Inserir tenant padrão `alemao`
- [x] Popular `tenant_id = 1` em todos os registros existentes
- [x] Criar índices em `tenant_id` em todas as tabelas
### Middleware de tenant
- [x] Criar `middleware/tenant.js` — detecta por subdomínio e header `X-Tenant-Slug`; anexa `req.tenant`
- [x] Fallback: hostname sem tenant → responde com `req.tenant` do slug `alemao` (ambiente local)
- [x] Criar `middleware/tenantAdmin.js` — verifica que admin pertence ao tenant correto
### Isolamento de dados
- [x] Atualizar models `User`, `Raffle`, `Order`, `Ticket`, `Promotion` para filtrar por `tenant_id`
- [x] Atualizar `analyticsController` com filtro de tenant
- [x] Atualizar `whatsappService` com `tenant_id` nos logs
### API pública
- [x] `GET /api/tenant/config` — retorna branding do tenant detectado (público)
---
## Fase 9B — Autenticação do Cliente: Email + Senha
### Backend
- [x] Adicionar colunas `email VARCHAR(255)`, `password_hash VARCHAR(255)` em `users`
- [x] Atualizar `POST /api/register` — aceitar `nome, email, cpf, telefone, password`
- [x] Atualizar `POST /api/login` — email+senha; fallback CPF+telefone para legados
- [x] Criar `POST /api/auth/forgot-password` — token temporário, envia WhatsApp
- [x] Criar `POST /api/auth/reset-password` — valida token, atualiza hash
- [x] Criar tabela `password_reset_tokens``user_id, token, expires_at, used`
- [x] Atualizar JWT payload — incluir `tenant_id`
- [x] `verifyUserToken` — validar `tenant_id` do token
### Migração de legados
- [x] Login CPF+telefone continua funcionando se `password_hash IS NULL`
- [x] `POST /api/auth/migrate` — legado informa CPF+telefone+novo email+senha
---
## Fase 10A — Clube de Benefícios: Backend
> Tabelas já criadas na 9A com `tenant_id`. Esta fase implementa a lógica de negócio.
### Models
- [x] Criar `models/BenefitClub.js` — CRUD clubes e planos
- [x] Criar `models/ClubMember.js` — findByCpf, create, updateStatus, listByClub
- [x] Criar `models/ClubPayment.js` — create, findByMember, listOverdue
- [x] Criar `models/ClubPartner.js` — CRUD parceiros por clube
### Asaas — Assinaturas recorrentes
- [x] Criar `services/clubAsaasService.js`
- [x] `createCustomer(cpf, nome, email, telefone)``asaas_customer_id`
- [x] `createSubscription(customerId, planPrice, description)``asaas_subscription_id` + primeiro PIX
- [x] `cancelSubscription(subscriptionId)`
- [x] `suspendSubscription(subscriptionId)`
- [x] `listPayments(subscriptionId)` → histórico
- [x] Webhook `POST /api/webhooks/asaas/club``PAYMENT_RECEIVED`, `PAYMENT_OVERDUE`, `PAYMENT_DELETED`
### API pública
- [x] `GET /api/clubs` — clubes ativos do tenant com planos
- [x] `GET /api/clubs/:slug` — detalhe + parceiros + benefícios
- [x] `POST /api/clubs/:slug/subscribe` — adesão: cria membro + assinatura Asaas + retorna PIX
- [x] `GET /api/clubs/card/:cpf` — carteirinha digital (dados não-sensíveis, público)
- [x] `POST /api/clubs/member/cancel` — cancelamento pelo cliente
---
## Fase 10B — Clube Odonto
### Backend
- [x] `GET /api/clubs/odonto/partners` — clínicas/dentistas parceiros
- [x] `GET /api/clubs/odonto/validate/:cpf` — validação pública para recepções de clínicas
- [x] Worker: membros com pagamento +5 dias em atraso → `status = suspended`
- [x] Worker: reativar após pagamento de mensalidade em atraso
### WhatsApp — Templates
- [x] `club_welcome` — boas-vindas + link carteirinha
- [x] `club_payment_reminder` — lembrete 3 dias antes do vencimento
- [x] `club_payment_confirmed` — confirmação + validade
- [x] `club_payment_overdue` — inadimplência + link pagar
- [x] `club_suspended` — plano suspenso
- [x] `club_canceled` — confirmação de cancelamento
- [x] Adicionar `club_payment_confirmed` e `club_suspended` em `CRITICAL_TYPES`
- [x] Criar `services/clubNotificationService.js`
### Admin (admin-app)
- [x] Criar `ClubOdonto.tsx` com KPIs: membros ativos, MRR, inadimplentes, cancelamentos
- [x] Lista de membros com status, plano, próxima cobrança, botão detalhes
- [x] Lista de inadimplentes com "Reenviar cobrança WhatsApp"
- [x] Modal membro: histórico pagamentos, dependentes, ações suspender/cancelar/reativar
- [x] CRUD parceiros (clínicas)
- [x] Gráfico evolução de membros (Recharts AreaChart)
- [x] Registrar rota `/clubs/odonto` no `App.tsx`
---
## Fase 10C — Clube Médico
### Backend
- [x] Criar clube `medico` com planos Individual e Família
- [x] `GET /api/clubs/medico/partners` — parceiros por especialidade
- [x] `GET /api/clubs/medico/validate/:cpf`
- [x] Template WhatsApp `club_medico_partner_new` — novo parceiro na rede
### Admin (admin-app)
- [x] Criar `ClubMedico.tsx` (componentes reutilizados de `ClubOdonto.tsx`)
- [x] Filtro de parceiros por especialidade (clínico geral, pediatria, ginecologia, etc.)
- [x] Registrar rota `/clubs/medico` no `App.tsx`
---
## Fase 10F — Relatórios do Clube (admin-app)
- [x] Criar `ClubReports.tsx`
- [x] MRR (Monthly Recurring Revenue) — gráfico de linha por mês
- [x] Churn rate mensal (cancelamentos / total início do mês)
- [x] Inadimplência — % membros com pagamento em atraso
- [x] Crescimento — novos vs cancelamentos por mês
- [x] Distribuição de planos — Individual vs Família por clube (PieChart)
- [x] Export CSV: membros ativos filtrado por clube/plano/status
- [x] Registrar rota `/clubs/reports` no `App.tsx`
---
## Fase 9E — Super Admin (SPA React)
### Backend
- [x] Criar tabela `super_admins``id, email, password_hash, name, created_at`
- [x] Criar `middleware/superAuth.js` — JWT secret separado
- [x] `POST /superadmin/api/login`
- [x] `GET /superadmin/api/tenants`
- [x] `POST /superadmin/api/tenants`
- [x] `PUT /superadmin/api/tenants/:id`
- [x] `DELETE /superadmin/api/tenants/:id` — soft delete
- [x] `GET /superadmin/api/tenants/:id/stats` — receita, usuários, tickets, membros clube
- [x] `POST /superadmin/api/tenants/:id/admins`
- [x] Servir `superadmin-app` em `GET /superadmin*`
### superadmin-app (React + Tailwind)
- [x] Setup: Vite + React 18 + TS + Tailwind, output → `public/superadmin/`
- [x] `LoginPage` — email + senha
- [x] `Dashboard` — totais globais por empresa
- [x] `TenantsPage` — tabela empresas, status, receita, membros clube
- [x] `TenantDetailPage` — stats + formulário branding (logo, cores, domínio)
- [x] `BrandingEditor` — preview live das cores/logo
- [x] `TenantAdminsPage` — cria/lista admins por empresa
---
## Fase 9F — Admin React Multi-tenant
- [x] `tenant_id` no JWT do admin (`cloud_token`)
- [x] Header do admin-app exibe nome e logo do tenant
- [x] Settings: aba "Identidade Visual" — edita `tenant_config`
- [x] Settings: `APP_URL` e `asaas_key` por tenant (não mais variáveis globais de ambiente)
---
## Fase 9D+9C+10D+10E — customer-app React (Entrega Unificada)
> Uma única SPA React substitui todas as páginas Bootstrap e entrega todas as funcionalidades do cliente de uma vez.
### Setup
- [x] Criar `customer-app/` — Vite + React 18 + TS + Tailwind
- [x] Build output → `sistema-nuvem/public/app/`
- [x] Adicionar `lucide-react`, `axios`, `swiper` (para hero slider)
- [x] Hook `useTenant()` — carrega `/api/tenant/config`, aplica CSS variables
- [x] Componente `<TenantProvider>` wrappa o app
### Páginas
- [x] `LandingPage` — hero slider 3 slides (promoções, odonto, médico), seção promoções, seção sorteio, seção clube odonto, seção clube médico
- [x] `LoginPage` — email+senha, tabs entrar/cadastrar, link "esqueci minha senha"
- [x] `RafflePage` — grid de números multi-seleção, contador de selecionados + valor, modal checkout 3 etapas (resumo → login/cadastro inline → PIX)
- [x] `DashboardPage` — meus tickets, opt-in WhatsApp, carteirinha clube
- [x] `CarteirinhaPage``/carteirinha/:token` pública, exibe `<MemberCard>` sem login
- [x] `ClubSubscribePage` — modal de adesão ao clube (escolha plano → dados → PIX)
- [x] `BlockedPage` — substitui `bloqueado.html`
### Remoção do Bootstrap
- [x] Remover `bootstrap.min.css` e `bootstrap-icons.min.css` de `public/css/`
- [x] Remover todos os HTML de `views/landing/` (rotas redirecionadas para `/app/*`)
---
## Stack Final
| Camada | Tecnologia |
|--------|-----------|
| Backend | Node.js + Express |
| Admin | React 18 + Tailwind + Recharts (existente) |
| Customer app | React 18 + Tailwind (novo — substitui Bootstrap) |
| Super Admin | React 18 + Tailwind (novo) |
| CSS | Tailwind — zero Bootstrap |
| Ícones | Lucide React — zero Bootstrap Icons |
| DB | PostgreSQL + tabelas tenant + clube |
| Pagamentos recorrentes | Asaas Subscriptions API |
| Notificações | WhatsApp via BullMQ (existente + novos templates) |