11 KiB
11 KiB
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 headerX-Tenant-Slug - Branding: armazenado no DB (
tenant_config), injetado via/api/tenant/configcomo 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_idna 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
- Criar tabela
tenants—id, slug, name, domain, active, created_at - Criar tabela
tenant_config—tenant_id, primary_color, secondary_color, logo_url, favicon_url, contact_email, contact_phone, app_url, asaas_key - Adicionar coluna
tenant_idemusers - Adicionar coluna
tenant_idemraffles - Adicionar coluna
tenant_idemorders - Adicionar coluna
tenant_idemtickets - Adicionar coluna
tenant_idempromotions - Adicionar coluna
tenant_idemwhatsapp_logs - Criar tabelas do clube já com
tenant_id:benefit_clubs,club_plans,club_members,club_dependents,club_payments,club_partners - Inserir tenant padrão
alemao - Popular
tenant_id = 1em todos os registros existentes - Criar índices em
tenant_idem todas as tabelas
Middleware de tenant
- Criar
middleware/tenant.js— detecta por subdomínio e headerX-Tenant-Slug; anexareq.tenant - Fallback: hostname sem tenant → responde com
req.tenantdo slugalemao(ambiente local) - Criar
middleware/tenantAdmin.js— verifica que admin pertence ao tenant correto
Isolamento de dados
- Atualizar models
User,Raffle,Order,Ticket,Promotionpara filtrar portenant_id - Atualizar
analyticsControllercom filtro de tenant - Atualizar
whatsappServicecomtenant_idnos logs
API pública
GET /api/tenant/config— retorna branding do tenant detectado (público)
Fase 9B — Autenticação do Cliente: Email + Senha
Backend
- Adicionar colunas
email VARCHAR(255),password_hash VARCHAR(255)emusers - Atualizar
POST /api/register— aceitarnome, email, cpf, telefone, password - Atualizar
POST /api/login— email+senha; fallback CPF+telefone para legados - Criar
POST /api/auth/forgot-password— token temporário, envia WhatsApp - Criar
POST /api/auth/reset-password— valida token, atualiza hash - Criar tabela
password_reset_tokens—user_id, token, expires_at, used - Atualizar JWT payload — incluir
tenant_id verifyUserToken— validartenant_iddo token
Migração de legados
- Login CPF+telefone continua funcionando se
password_hash IS NULL 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
- Criar
models/BenefitClub.js— CRUD clubes e planos - Criar
models/ClubMember.js— findByCpf, create, updateStatus, listByClub - Criar
models/ClubPayment.js— create, findByMember, listOverdue - Criar
models/ClubPartner.js— CRUD parceiros por clube
Asaas — Assinaturas recorrentes
- Criar
services/clubAsaasService.js createCustomer(cpf, nome, email, telefone)→asaas_customer_idcreateSubscription(customerId, planPrice, description)→asaas_subscription_id+ primeiro PIXcancelSubscription(subscriptionId)suspendSubscription(subscriptionId)listPayments(subscriptionId)→ histórico- Webhook
POST /api/webhooks/asaas/club—PAYMENT_RECEIVED,PAYMENT_OVERDUE,PAYMENT_DELETED
API pública
GET /api/clubs— clubes ativos do tenant com planosGET /api/clubs/:slug— detalhe + parceiros + benefíciosPOST /api/clubs/:slug/subscribe— adesão: cria membro + assinatura Asaas + retorna PIXGET /api/clubs/card/:cpf— carteirinha digital (dados não-sensíveis, público)POST /api/clubs/member/cancel— cancelamento pelo cliente
Fase 10B — Clube Odonto
Backend
GET /api/clubs/odonto/partners— clínicas/dentistas parceirosGET /api/clubs/odonto/validate/:cpf— validação pública para recepções de clínicas- Worker: membros com pagamento +5 dias em atraso →
status = suspended - Worker: reativar após pagamento de mensalidade em atraso
WhatsApp — Templates
club_welcome— boas-vindas + link carteirinhaclub_payment_reminder— lembrete 3 dias antes do vencimentoclub_payment_confirmed— confirmação + validadeclub_payment_overdue— inadimplência + link pagarclub_suspended— plano suspensoclub_canceled— confirmação de cancelamento- Adicionar
club_payment_confirmedeclub_suspendedemCRITICAL_TYPES - Criar
services/clubNotificationService.js
Admin (admin-app)
- Criar
ClubOdonto.tsxcom KPIs: membros ativos, MRR, inadimplentes, cancelamentos - Lista de membros com status, plano, próxima cobrança, botão detalhes
- Lista de inadimplentes com "Reenviar cobrança WhatsApp"
- Modal membro: histórico pagamentos, dependentes, ações suspender/cancelar/reativar
- CRUD parceiros (clínicas)
- Gráfico evolução de membros (Recharts AreaChart)
- Registrar rota
/clubs/odontonoApp.tsx
Fase 10C — Clube Médico
Backend
- Criar clube
medicocom planos Individual e Família GET /api/clubs/medico/partners— parceiros por especialidadeGET /api/clubs/medico/validate/:cpf- Template WhatsApp
club_medico_partner_new— novo parceiro na rede
Admin (admin-app)
- Criar
ClubMedico.tsx(componentes reutilizados deClubOdonto.tsx) - Filtro de parceiros por especialidade (clínico geral, pediatria, ginecologia, etc.)
- Registrar rota
/clubs/mediconoApp.tsx
Fase 10F — Relatórios do Clube (admin-app)
- Criar
ClubReports.tsx - MRR (Monthly Recurring Revenue) — gráfico de linha por mês
- Churn rate mensal (cancelamentos / total início do mês)
- Inadimplência — % membros com pagamento em atraso
- Crescimento — novos vs cancelamentos por mês
- Distribuição de planos — Individual vs Família por clube (PieChart)
- Export CSV: membros ativos filtrado por clube/plano/status
- Registrar rota
/clubs/reportsnoApp.tsx
Fase 9E — Super Admin (SPA React)
Backend
- Criar tabela
super_admins—id, email, password_hash, name, created_at - Criar
middleware/superAuth.js— JWT secret separado POST /superadmin/api/loginGET /superadmin/api/tenantsPOST /superadmin/api/tenantsPUT /superadmin/api/tenants/:idDELETE /superadmin/api/tenants/:id— soft deleteGET /superadmin/api/tenants/:id/stats— receita, usuários, tickets, membros clubePOST /superadmin/api/tenants/:id/admins- Servir
superadmin-appemGET /superadmin*
superadmin-app (React + Tailwind)
- Setup: Vite + React 18 + TS + Tailwind, output →
public/superadmin/ LoginPage— email + senhaDashboard— totais globais por empresaTenantsPage— tabela empresas, status, receita, membros clubeTenantDetailPage— stats + formulário branding (logo, cores, domínio)BrandingEditor— preview live das cores/logoTenantAdminsPage— cria/lista admins por empresa
Fase 9F — Admin React Multi-tenant
tenant_idno JWT do admin (cloud_token)- Header do admin-app exibe nome e logo do tenant
- Settings: aba "Identidade Visual" — edita
tenant_config - Settings:
APP_URLeasaas_keypor 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
- Criar
customer-app/— Vite + React 18 + TS + Tailwind - Build output →
sistema-nuvem/public/app/ - Adicionar
lucide-react,axios,swiper(para hero slider) - Hook
useTenant()— carrega/api/tenant/config, aplica CSS variables - Componente
<TenantProvider>wrappa o app
Páginas
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édicoLoginPage— email+senha, tabs entrar/cadastrar, link "esqueci minha senha"RafflePage— grid de números multi-seleção, contador de selecionados + valor, modal checkout 3 etapas (resumo → login/cadastro inline → PIX)DashboardPage— meus tickets, opt-in WhatsApp, carteirinha clubeCarteirinhaPage—/carteirinha/:tokenpública, exibe<MemberCard>sem loginClubSubscribePage— modal de adesão ao clube (escolha plano → dados → PIX)BlockedPage— substituibloqueado.html
Remoção do Bootstrap
- Remover
bootstrap.min.cssebootstrap-icons.min.cssdepublic/css/ - 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) |