🏛️ Sistema de Credenciamento de Licitações

Documentação Técnica Completa - Frontend & Backend

📌 Sobre o Sistema
Plataforma completa para gestão de credenciamentos em licitações públicas, desenvolvida com React 18 (frontend) e NestJS (backend), utilizando MySQL como banco de dados.
Visão Geral
Frontend
Backend
API
Deploy

🎯 Visão Geral

💻 Frontend

Tecnologias:

  • React 18.2 + TypeScript
  • Vite 5.2
  • React Router DOM 6.22
  • Axios + JWT Auth
  • SweetAlert2

⚙️ Backend

Tecnologias:

  • NestJS 10.x + TypeScript
  • Prisma ORM 5.x
  • MySQL 8.x
  • JWT Authentication
  • Nodemailer

🗄️ Modelo de Dados Principal

Entidade Descrição Campos Principais
User Usuários do sistema email, password, name, role (ADMIN/USER)
Credenciamento Registro principal numeroCredenciamento, status, modalidade, 31 campos
CredenciamentoLog Auditoria de alterações campo, valorAnterior, valorNovo, usuário
Anexo Arquivos anexados nomeArquivo, caminhoArquivo, tamanho (max 10MB)
Orgao Órgãos públicos titulo, active
Portal Portais de licitação titulo, url, active
Tipo Tipos de credenciamento titulo, active
Fornecedor Fornecedores vencedores nome, cnpj, active

📊 Status do Credenciamento

17 Status Possíveis
ENVIAR_REDE          // Enviar para rede
HABILITADOS          // Já habilitados
ENVIAR_MKT           // Enviar para marketing
AGUARDANDO_HAB       // Aguardando habilitação
AGUARDANDO_ESCOLHA   // Aguardando escolha
RECURSO              // Em recurso
INABILITADA          // Inabilitado
NAO_VAMOS            // Não vamos participar
ESCOLHIDOS           // Fomos escolhidos
REVOGADO             // Licitação revogada
DESERTO              // Sem participantes
FRACASSADO           // Processo fracassado
SUSPENSO             // Processo suspenso
CANCELADO            // Processo cancelado
IMPUGNADO            // Processo impugnado
HOMOLOGADO           // Processo homologado
ADJUDICADO           // Processo adjudicado

💻 Frontend - React + TypeScript

📁 Estrutura de Páginas

Rota Componente Descrição
/login Login.tsx Autenticação com JWT
/dashboard Dashboard.tsx Cards estatísticos, gráficos e notificações
/acompanhamento Acompanhamento.tsx Notificações de prazos (3/5/10 dias)
/credenciamentos CredenciamentoList.tsx Lista com filtros avançados e export Excel
/credenciamentos/novo CredenciamentoForm.tsx Formulário com 4 abas
/usuarios UserList.tsx Gestão de usuários (ADMIN)
/orgaos OrgaoList.tsx CRUD de órgãos
/portais PortalList.tsx CRUD de portais
/tipos TipoList.tsx CRUD de tipos
/fornecedores FornecedorList.tsx CRUD de fornecedores

📝 Formulário de Credenciamento (4 Abas)

Aba 1 - Dados Principais (31 campos)

  • Número do Credenciamento
  • Órgão, Município, UF
  • Status, Modalidade
  • Portal, Tipo, Fornecedor
  • Valores (mensal, global, contrato)
  • Analista responsável

Aba 2 - Datas e Prazos

  • Data de Envio, MKT, Rede
  • Data Máxima para Enviar (alerta visual)
  • Data de Habilitação, Escolha

Alertas: Vermelho (<3 dias), Amarelo (<5 dias)

Aba 3 - Operacional

  • POC (Pontos de Contato)
  • SULTS
  • Andamento
  • Contatos
  • Pendências

Aba 4 - Histórico (Logs)

Tabela com auditoria completa:

  • Data/Hora da alteração
  • Usuário que alterou
  • Campo modificado
  • Valor anterior → Valor novo

🔐 Autenticação (AuthContext)

Fluxo de Autenticação
// 1. Login
const { login } = useAuth();
await login(email, password);

// 2. Token armazenado no localStorage
localStorage.setItem('token', token);

// 3. Interceptor Axios adiciona token automaticamente
api.interceptors.request.use((config) => {
  const token = localStorage.getItem('token');
  config.headers.Authorization = `Bearer ${token}`;
  return config;
});

// 4. PrivateRoute protege rotas
}>
  } />

📊 Export para Excel

Exportar Lista Filtrada
import * as XLSX from 'xlsx';

const exportToExcel = async () => {
  const data = await credenciamentoService.export(filters);
  const ws = XLSX.utils.json_to_sheet(data);
  const wb = XLSX.utils.book_new();
  XLSX.utils.book_append_sheet(wb, ws, 'Credenciamentos');
  XLSX.writeFile(wb, `credenciamentos_${new Date().toISOString()}.xlsx`);
};

⚙️ Backend - NestJS + Prisma

🏗️ Arquitetura de Módulos

Módulo Responsabilidade
AuthModule Autenticação JWT, registro, login
CredenciamentoModule CRUD principal, filtros, notificações, logs
UserModule Gestão de usuários (ADMIN)
AnexoModule Upload/download de arquivos (max 10MB)
EmailModule Envio de notificações via Nodemailer
CronModule Jobs agendados (notificações diárias 8h)
OrgaoModule CRUD de órgãos
PortalModule CRUD de portais
TipoModule CRUD de tipos
FornecedorModule CRUD de fornecedores

🔒 Sistema de Autenticação

Endpoints de Autenticação
POST /auth/register    # Criar novo usuário
POST /auth/login       # Autenticar e receber JWT
GET  /auth/profile     # Buscar perfil autenticado

Fluxo de Autenticação

  1. Cliente envia email + password
  2. Backend valida e gera JWT (validade: 7 dias)
  3. Token retornado ao cliente
  4. Cliente envia token em todas as requests: Authorization: Bearer <token>

📝 Sistema de Logs (Auditoria)

✅ Auditoria Automática

Toda alteração em credenciamentos gera log automático com:

  • Campo alterado
  • Valor anterior
  • Valor novo
  • Usuário que fez a alteração
  • Data e hora precisa

🔔 Sistema de Notificações (Cron Job)

⏰ Job Agendado: Diariamente às 8h

Regras de Notificação:
• 10 dias antes da dataMaximaEnviar: Notificação para ADMIN
• 5 dias antes: Notificação para ADMIN e USER
• 3 dias antes: Notificação urgente para todos

📧 Configuração de Email

Variáveis de Ambiente (.env)
EMAIL_HOST=smtp.gmail.com
EMAIL_PORT=587
EMAIL_USER=seu-email@gmail.com
EMAIL_PASS=sua-senha-app
EMAIL_FROM="Sistema Credenciamento <noreply@credenciamento.com>"

🗃️ Prisma Schema (Principais Entidades)

schema.prisma
model User {
  id        Int      @id @default(autoincrement())
  email     String   @unique
  password  String   // hash bcrypt
  name      String
  role      Role     @default(USER)
  active    Boolean  @default(true)
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}

model Credenciamento {
  id                    Int      @id @default(autoincrement())
  numeroCredenciamento  String?
  status                StatusCredenciamento
  modalidade            Modalidade
  orgaoId               Int
  portalId              Int?
  tipoId                Int?
  fornecedorId          Int?
  analistaId            Int
  // ... 31 campos totais
}

model CredenciamentoLog {
  id                Int      @id @default(autoincrement())
  credenciamentoId  Int
  campo             String
  valorAnterior     String?
  valorNovo         String?
  usuarioNome       String
  timestamp         DateTime @default(now())
}

📡 Endpoints da API

🔐 Autenticação

Método Endpoint Descrição
POST /auth/register Criar novo usuário
POST /auth/login Autenticar e receber JWT
GET /auth/profile Buscar perfil autenticado

📋 Credenciamentos

Método Endpoint Descrição Auth
GET /credenciamentos Listar com filtros e paginação JWT
GET /credenciamentos/notifications Notificações de prazos JWT
GET /credenciamentos/:id Buscar por ID (com logs) JWT
POST /credenciamentos Criar novo JWT
PATCH /credenciamentos/:id Atualizar (gera log automático) JWT
DELETE /credenciamentos/:id Deletar JWT + ADMIN

Filtros Disponíveis (Query Params)

Exemplo de Request com Filtros
GET /credenciamentos?status=HABILITADOS&analistaId=2&page=1&limit=10
Authorization: Bearer <token>
Parâmetro Tipo Descrição
status string Filtrar por status
analistaId number Filtrar por analista
orgaoId number Filtrar por órgão
portalId number Filtrar por portal
tipoId number Filtrar por tipo
dataInicio date Data de início (range)
dataFim date Data de fim (range)
municipio string Filtrar por município
uf string Filtrar por UF
page number Número da página (default: 1)
limit number Itens por página (default: 10)

Exemplo de Response

Response JSON
{
  "data": [
    {
      "id": 1,
      "numeroCredenciamento": "CRED-2024-001",
      "status": "HABILITADOS",
      "orgao": {
        "id": 1,
        "titulo": "Prefeitura Municipal"
      },
      "analista": {
        "id": 2,
        "name": "João Silva"
      },
      "dataLicitacao": "2024-10-20T00:00:00.000Z"
    }
  ],
  "meta": {
    "total": 45,
    "page": 1,
    "limit": 10,
    "totalPages": 5
  }
}

📎 Upload de Arquivos

Método Endpoint Descrição Limite
POST /anexos/credenciamento/:id Upload de arquivo 10MB
GET /anexos/credenciamento/:id Listar anexos -
GET /anexos/:id/download Download de arquivo -
DELETE /anexos/:id Deletar arquivo -
Exemplo de Upload
POST /anexos/credenciamento/1
Content-Type: multipart/form-data
Authorization: Bearer <token>

file: [arquivo.pdf]

👥 Usuários (ADMIN apenas)

Método Endpoint Descrição
GET /users Listar usuários
GET /users/:id Buscar por ID
POST /users Criar usuário
PATCH /users/:id Atualizar usuário
DELETE /users/:id Deletar usuário

🏢 Entidades Auxiliares (CRUD Completo)

Endpoints disponíveis para:

Todos seguem o padrão: GET, GET/:id, POST, PATCH/:id, DELETE/:id

🚀 Deploy e Configuração

📦 Variáveis de Ambiente

Backend (.env)
# Database
DATABASE_URL="mysql://user:pass@host:3306/db_name"

# JWT
JWT_SECRET="sua-chave-secreta-muito-segura"
JWT_EXPIRES_IN="7d"

# Email
EMAIL_HOST="smtp.gmail.com"
EMAIL_PORT=587
EMAIL_USER="email@gmail.com"
EMAIL_PASS="senha-app"
EMAIL_FROM="Sistema <noreply@sistema.com>"

# Upload
MAX_FILE_SIZE=10485760  # 10MB
Frontend (.env.production)
VITE_API_URL=https://api.seu-dominio.com

🔧 Build e Deploy - Backend

Comandos de Deploy
# 1. Gerar Prisma Client
npm run prisma:generate

# 2. Executar migrações
npm run prisma:migrate deploy

# 3. Build do NestJS
npm run build

# 4. Iniciar aplicação
npm run start:prod

💻 Build e Deploy - Frontend

Comandos de Deploy
# 1. Build para produção
npm run build

# 2. Preview local (opcional)
npm run preview

# 3. Deploy (exemplo Vercel)
vercel --prod

🔒 Segurança

⚠️ Checklist de Segurança

  • ✅ Senhas hasheadas com bcrypt (salt rounds: 10)
  • ✅ JWT com expiração configurável
  • ✅ Validação de inputs com class-validator
  • ✅ Guards para proteção de rotas
  • ✅ CORS configurado
  • ✅ Limite de upload (10MB)
  • ⚠️ Recomendado: Rate limiting (@nestjs/throttler)
  • ⚠️ Recomendado: Helmet para headers
  • ⚠️ Recomendado: SSL/TLS em produção

🗃️ Prisma - Comandos Úteis

Gerenciamento de Banco de Dados
# Criar migração
npm run prisma:migrate dev -- --name nome_da_migracao

# Aplicar migrações (produção)
npm run prisma:migrate deploy

# Abrir Prisma Studio (interface visual)
npm run prisma:studio

# Popular banco com dados iniciais
npm run prisma:seed

# Resetar banco (CUIDADO - apaga dados)
npx prisma migrate reset

📊 Monitoramento

✅ Prisma Studio

Interface visual para gerenciar banco de dados

npm run prisma:studio
# Acesse: http://localhost:5555

🐛 Troubleshooting

Erro de conexão com MySQL

# Verificar se MySQL está rodando
docker-compose ps

# Ver logs do MySQL
docker-compose logs mysql

# Reiniciar MySQL
docker-compose restart mysql

CORS Error no Frontend

Verifique se o backend está com CORS habilitado para a origem do frontend em main.ts:

app.enableCors({
  origin: ['http://localhost:3000', 'https://seu-dominio.com'],
  credentials: true,
});

401 Unauthorized

Token expirado ou inválido. Faça logout e login novamente.