Arquitetura do Frontend¶
O frontend do TepConfina e uma SPA (Single Page Application) construida com React 18 e Vite 5, utilizando TypeScript para tipagem estatica e Tailwind CSS 3 para estilizacao.
Stack Tecnologica¶
| Tecnologia | Finalidade |
|---|---|
| React 18 | Biblioteca de UI com concurrent features |
| Vite 5 | Build tool com HMR ultra-rapido |
| TypeScript | Tipagem estatica |
| Tailwind CSS 3 | Framework CSS utility-first (dark mode class based) |
| TanStack Query | Gerenciamento de estado do servidor |
| Zustand | Gerenciamento de estado local |
| React Hook Form | Gerenciamento de formularios (FormProvider para shared register/errors) |
| Zod | Validacao de schemas |
| React Router | createBrowserRouter + RouterProvider (data router API, suporta useBlocker) |
| Recharts 2.12 | Graficos e visualizacoes (CandleChart custom no Mercado) |
| Axios | Cliente HTTP (interceptors auth/refresh) |
| jsPDF | Geracao de PDF client-side (relatorios + lote detalhado) |
| react-helmet-async | SEO/meta tags (13 paginas) |
| VitePWA + Workbox | PWA com service worker autoUpdate |
| @microsoft/signalr | Long Polling para PrecoAtualizado/PrecoAlerta (API Gateway REST nao suporta WebSocket) |
Estrutura do Projeto¶
Estrutura flat — páginas no src/pages/ raiz, abas do detalhe do lote em src/pages/lote-tabs/.
src/
├── components/
│ ├── layout/ # AppLayout, Sidebar, Header
│ ├── ui/ # DataTable, Button, Modal, ConfirmDialog,
│ │ # SearchInput, Pagination, EmptyState,
│ │ # PageTitle, Breadcrumb, TableSkeleton, ErrorBoundary
│ ├── icons/ # Icones customizados (CowIcon)
│ ├── charts/ # LineChart, BarChart
│ ├── dashboard/ # HojeSummary, PrevisaoVendaCard
│ └── mercado/ # CandleChart, FuturesContractsCard, HedgeSimulator,
│ # HedgeDecisionCard, HedgeLadder, HedgeHistory,
│ # MapaPrecosBrasil, ReportarNegocioModal
│
├── pages/
│ ├── DashboardPage.tsx # Painel com KPIs + Hoje + Previsao Venda
│ ├── LotesPage.tsx
│ ├── LoteFormPage.tsx # Wizard 4 steps + edicao completa
│ ├── LoteDetalhePage.tsx # 13 abas (resumo, compras, pesagens, racao,
│ │ # med, sanitario, animais, previsao, suplementos,
│ │ # arrendamentos, conferencia visual, gastos, graficos)
│ ├── lote-tabs/ # Componentes de cada aba
│ ├── AnimaisPage.tsx # Lista global + Novo Animal + Cadastro em Lote
│ ├── PesagensPage.tsx
│ ├── RacoesPage.tsx
│ ├── RacaoFormPage.tsx
│ ├── ProdutoresPage.tsx
│ ├── ProdutorFormPage.tsx
│ ├── FinanceiroPage.tsx
│ ├── MercadoPage.tsx # BGI1! candles + override @ + simuladores
│ ├── MercadoColaborativoPage.tsx
│ ├── SimuladorPage.tsx
│ ├── ComparativoLotesPage.tsx
│ ├── EstoqueInsumosPage.tsx
│ ├── AlertasPage.tsx
│ ├── NotificacoesPage.tsx
│ ├── RelatoriosPage.tsx
│ ├── ImportacaoPage.tsx
│ ├── AuditoriaPage.tsx
│ ├── AjudaPage.tsx
│ ├── AssinaturaPage.tsx
│ ├── PlanosPage.tsx
│ ├── AgentePage.tsx # Chat com Agente IA
│ ├── ApiKeysPage.tsx
│ ├── UsuariosPage.tsx
│ ├── UsuarioFormPage.tsx
│ ├── TenantsPage.tsx
│ ├── CadastroPage.tsx # Signup
│ └── LoginPage.tsx
│
├── services/
│ ├── api.ts # Axios + interceptors auth/refresh
│ ├── authService.ts
│ ├── loteService.ts
│ ├── animalService.ts
│ ├── marketService.ts # /api/market/* (candles, quote, contracts)
│ ├── precoMercadoService.ts # /api/precos-mercado/* (legacy dashboard)
│ ├── hedgeService.ts
│ ├── ... # 25+ services
│
├── stores/
│ └── authStore.ts # Zustand: usuario, token, login/logout
│
├── hooks/
│ ├── useFeatureFlag.ts
│ ├── usePaginatedQuery.ts # Wrapper TanStack Query + paginacao
│ ├── useSignalR.ts # Long Polling para PrecoAtualizado/PrecoAlerta
│ ├── useUnsavedChanges.ts # Blocker + ConfirmDialog
│ └── usePrecoArrobaManual.ts # Override manual da @ (localStorage)
│
├── contexts/
│ └── FeatureFlagContext.tsx
│
├── lib/
│ ├── utils.ts # cn, formatCurrency, formatNumber, formatDate
│ ├── exportListPdf.ts # Util generica de PDF
│ ├── relatorioLotePdf.ts # PDF detalhado do lote
│ ├── calculateHedge.ts # Logica do simulador de hedge
│ ├── getHedgeDecision.ts # Engine de decisao (5 criterios)
│ └── helpContent.ts # Conteudo da pagina /ajuda
│
├── types/ # Tipos por dominio (lote.ts, animal.ts, ...)
├── test/ # Vitest unit tests (17 arquivos, 178 testes)
├── e2e/ # Playwright (4 specs, 42 testes contra producao)
├── router.tsx # createBrowserRouter + ProtectedRoute
├── App.tsx # RouterProvider + QueryClient + Providers
├── main.tsx
└── index.css
Gerenciamento de Estado¶
O frontend adota uma estrategia de dois stores para separar responsabilidades:
graph LR
subgraph "Estado Local (Zustand)"
AUTH[authStore<br/>usuario, token, login, logout]
end
subgraph "Estado do Servidor (TanStack Query)"
LOTES[useLotes<br/>listagem, cache, refetch]
ANIMAIS[useAnimais<br/>listagem, cache, refetch]
DASH[useDashboard<br/>KPIs, cache, refetch]
end
subgraph "Estado de Formulario (React Hook Form)"
FORM[useForm + zodResolver<br/>validacao, submit]
end
AUTH -->|token| LOTES
AUTH -->|token| ANIMAIS
AUTH -->|token| DASH | Tipo de Estado | Biblioteca | Uso |
|---|---|---|
| Autenticacao | Zustand | Usuario logado, tokens, permissoes |
| Dados do servidor | TanStack Query | Lotes, animais, pesagens, dashboards |
| Formularios | React Hook Form | Validacao, submissao, estados de campo |
| Feature flags | React Context | Ativacao/desativacao de funcionalidades |
Por que dois stores?
Zustand e ideal para estado sincrono e local (autenticacao, UI). TanStack Query gerencia automaticamente cache, revalidacao, loading states e erro para dados vindos da API, eliminando boilerplate de reducers e actions.
Integracao com API¶
A comunicacao com o backend e feita via Axios com interceptors configurados:
sequenceDiagram
participant C as Componente
participant A as Axios Instance
participant API as Backend API
participant S as AuthStore
C->>A: request GET /api/v1/lotes
A->>A: Interceptor: adiciona Bearer token
A->>API: GET /api/v1/lotes
API-->>A: 401 Unauthorized (token expirado)
A->>API: POST /api/v1/auth/refresh
API-->>A: 200 + novo token
A->>S: Atualiza token no store
A->>API: GET /api/v1/lotes (retry com novo token)
API-->>A: 200 + dados
A-->>C: Dados dos lotes Refresh Token
O interceptor de refresh token utiliza uma fila de requisicoes para evitar multiplas chamadas simultaneas de renovacao. Enquanto o refresh esta em andamento, novas requisicoes sao enfileiradas e liberadas apos a renovacao.
Roteamento¶
O roteamento usa createBrowserRouter + RouterProvider (data router API), permitindo useBlocker para confirmar saída em formulários com mudanças não salvas. Configurado em src/router.tsx.
/login → LoginPage (publica)
/cadastro → CadastroPage (publica)
/ → DashboardPage (protegida)
/lotes → LotesPage
/lotes/novo → LoteFormPage (wizard 4 steps)
/lotes/:id → LoteDetalhePage (13 abas)
/lotes/:id/editar → LoteFormPage
/animais → AnimaisPage (com Novo Animal + Cadastro em Lote)
/pesagens → PesagensPage
/racoes → RacoesPage
/racoes/novo → RacaoFormPage
/produtores → ProdutoresPage
/produtores/novo → ProdutorFormPage
/produtores/:id/editar → ProdutorFormPage
/financeiro → FinanceiroPage
/mercado → MercadoPage (BGI1! + override @ + simuladores)
/mercado-colaborativo → MercadoColaborativoPage
/simulador → SimuladorPage
/comparativo → ComparativoLotesPage
/estoque → EstoqueInsumosPage
/relatorios → RelatoriosPage
/importacao → ImportacaoPage
/auditoria → AuditoriaPage (Admin)
/agente → AgentePage (chat IA)
/alertas → AlertasPage
/notificacoes → NotificacoesPage
/ajuda → AjudaPage
/assinatura → AssinaturaPage
/planos → PlanosPage
/api-keys → ApiKeysPage
/usuarios → UsuariosPage (Admin)
/tenants → TenantsPage (Admin)
/perfil → editar usuário logado
Hooks de UX
useUnsavedChanges envolve formulários — bloqueia navegação e abre ConfirmDialog se houver mudanças não salvas. Funciona porque o data router suporta useBlocker.
Paleta de Cores¶
O Tailwind CSS esta configurado com cores customizadas alinhadas a identidade visual:
| Token | Hex | Uso |
|---|---|---|
primary | #008ED3 | Botoes principais, links, header |
secondary | #009945 | Indicadores positivos, sucesso |
accent | #26AAB2 | Destaques, badges, graficos |
neutral-800 | #1B3A5C | Textos, sidebar |
danger | #DC2626 | Erros, alertas criticos, delete |
warning | #F59E0B | Avisos, alertas moderados |
Componentes Principais¶
DataTable¶
Tabela generica com suporte a:
- Paginacao server-side
- Ordenacao por coluna
- Filtros por campo
- Acoes por linha (editar, excluir, visualizar)
- Estado de loading com skeleton
Dashboard¶
Painel principal com:
- Cards de KPIs (total de animais, lotes ativos, GMD medio, resultado financeiro)
- Grafico de evolucao de peso (Recharts LineChart)
- Grafico de custos por categoria (Recharts BarChart)
- Tabela de ultimos lotes com status
Feature Flags¶
// Uso do hook de feature flags
const { isEnabled } = useFeatureFlag();
if (isEnabled('modulo-financeiro')) {
return <FinanceiroPage />;
}
Feature Flags
O sistema de feature flags permite ativar/desativar modulos por tenant, facilitando o rollout gradual de funcionalidades e a customizacao por cliente.
Padrões cross-cutting¶
Dark mode¶
Tailwind configurado com darkMode: 'class'. Toggle no Header persiste em localStorage. Todas as páginas e componentes têm variantes dark:.
ErrorBoundary¶
Wrapper global em App.tsx captura exceções de render — mostra card full-screen com botão de reload e detalhes colapsáveis (apenas em dev). Evita tela branca em prod.
Null safety¶
Todas as páginas tratam dados da API com ?? 0, ?? [], optional chaining e Array.isArray() antes de iterar. Convenção forte do projeto para evitar quebra quando a API retorna campos null/undefined.
Português (textos com acento)¶
Toda label/placeholder/texto visível usa acentos corretos (Português BR). Variáveis e identificadores de código permanecem em ASCII (pesoMedio, não pesoMedío). Não alterar nomes de rotas/IDs.
Real-time via SignalR¶
Hub URL: VITE_API_URL + '/hubs/preco' configurado em useSignalR.ts. Transporte forçado em Long Polling (HttpTransportType.LongPolling) porque o API Gateway REST não suporta WebSocket.
Eventos ouvidos: - PrecoAtualizado — payload { precoArroba, variacao } — atualiza dashboard sem refetch - PrecoAlerta — payload { titulo, mensagem, precoArroba } — toast + notificação
PDF client-side¶
Util exportListPdf em src/lib/exportListPdf.ts aceita { title, columns, data } e gera PDF tabular. relatorioLotePdf.ts é específico para o relatório completo do lote (capa, KPIs, gráficos render).
SEO¶
react-helmet-async configurado no App.tsx. Cada página crítica (13 ao todo) define seu <PageTitle> que injeta <title> e meta description.
PWA¶
VitePWA com registerType: 'autoUpdate' e Workbox. Service worker faz cache-first para assets estáticos e network-first para /api/*. Manifesto inclui ícone TepConfina e modo standalone.
Acessibilidade¶
- ARIA labels em Sidebar, Header, e 7 tabelas principais
- Foco visível com
focus:ring-2 aria-pressedem botões toggle de filtroaria-labelem botões de ação por linha (excluir, editar)
Build e Desenvolvimento¶
| Comando | Descricao |
|---|---|
npm run dev | Servidor de desenvolvimento (porta 5173) |
npm run build | Build de producao otimizado |
npm run preview | Preview do build de producao |
npm run lint | Analise estatica com ESLint |
npm run test | Execucao de testes com Vitest |
Proxy de Desenvolvimento
O Vite esta configurado para fazer proxy de /api para localhost:5000, eliminando problemas de CORS durante o desenvolvimento local.
Paginas relacionadas: Visao Geral | Backend | Mobile