# Arquitetura — Frontend ## Estrutura de pastas ``` frontend/src/ ├── api/ Funções de chamada HTTP (uma por domínio) ├── components/ Componentes Vue reutilizáveis │ ├── base/ Design system: BaseButton, BaseBadge, BaseModal │ └── skeletons/ Skeletons de carregamento ├── composables/ Estado e lógica reutilizável (useAuth, useChat, etc.) ├── docs/ Documentação do frontend ├── layout/ Layouts de página (LayoutSistema.vue) ├── router/ Configuração do Vue Router ├── styles/ CSS global e variáveis de tema ├── utils/ Utilitários genéricos └── views/ Páginas da aplicação ├── atendimentos/ AtendimentoView, AvaliacoesView, GoldenSetView, NaoResolvidosView, RankingAtendentesView ├── auth/ LoginView ├── configuracoes/ ├── conversas/ ConversasHistoricoView, ConversasPesquisarView, ConversasWhatsappView ├── documentos/ DocumentosView, CarregarDocumentoView ├── pagina-inicial/ └── usuarios/ ``` Design system e componentes reutilizáveis em [components.md](components.md). ## Roteamento O Vue Router está configurado em `src/router/index.js`. Todas as rotas autenticadas usam o meta `requiresAuth: true`, e a rota de login usa `guestOnly: true`. | Rota | Nome | Componente | Acesso | |---|---|---|---| | `/login` | `login` | `LoginView` | Apenas não autenticados | | `/` | `home` | `PaginaInicialView` | Autenticados | | `/conversas/historico` | `conversas-historico` | `ConversasHistoricoView` | Autenticados | | `/conversas/pesquisar` | `conversas-pesquisar` | `ConversasPesquisarView` | Autenticados | | `/conversas-whatsapp` | `conversas-whatsapp` | `ConversasWhatsappView` | Autenticados | | `/configuracoes` | `configuracoes` | `ConfiguracoesView` | Autenticados | | `/usuarios` | `usuarios` | `UsuariosView` | Admin (`nivelMinimo`) | | `/atendimentos/avaliacoes` | `avaliacoes` | `AvaliacoesView` | Admin (`nivelMinimo`) | | `/atendimentos/nao-resolvidos` | `atendimentos-nao-resolvidos` | `NaoResolvidosView` | Admin (`nivelMinimo`) | | `/atendimentos/ranking-atendentes` | `ranking-atendentes` | `RankingAtendentesView` | Admin (`nivelMinimo`) | | `/atendimentos/:id` | `atendimento` | `AtendimentoView` | Admin (`nivelMinimo`) | | `/atendimentos/golden-set` | `golden-set` | `GoldenSetView` | Admin (`nivelMinimo`) | | `/documentos` | `documentos` | `DocumentosView` | Admin (`nivelMinimo`) | | `/documentos/carregar` | `documentos-carregar` | `CarregarDocumentoView` | Admin (`nivelMinimo`) | Todas as rotas usam lazy loading (`() => import(...)`) — nenhuma view é importada estaticamente no router. **Guard de rota** (`router.beforeEach`): - Usuário não autenticado tentando rota `requiresAuth` → redirecionado para `/login` (com `?redirect=` de volta) - Usuário autenticado tentando rota `guestOnly` (`/login`) → redirecionado para `/` - Usuário sem o `nivelMinimo` exigido pela rota → redirecionado para `/configuracoes` - Falha ao carregar um chunk lazy (deploy novo com o usuário na aba antiga) → tenta um reload automático da página uma vez; se falhar de novo, mostra um banner (`chunkLoadError`) em vez de loop ## Layout do sistema `src/layout/LayoutSistema.vue` é o layout padrão de todas as páginas autenticadas. Ele contém: - Barra de navegação lateral com lista de conversas - Área principal de conteúdo (slot padrão) - Integração com `useChat` para carregar e exibir as conversas na sidebar ## Estado da aplicação O frontend **não usa Pinia**. O estado é gerenciado por composables (`src/composables/`), a maioria com padrão singleton: - `useAuth` — singleton via closure (`session` é um `ref` no escopo do módulo) - `useChat` — singleton explícito (variável `singleton` no escopo do módulo) - `useListaAvaliacoes` — instância por componente; estado compartilhado por `AvaliacoesView` e `NaoResolvidosView` (paginação, busca com debounce, sincronização com a query string) - `useConversas`, `useConversaEdicao`, `useWhatsappConexao`, `useDocumentos` — estado do chat de atendente (WhatsApp) e da tela de documentos, instância por componente - `useSearch`, `useToast`, `useUsers`, `useMenuUsuario`, `useSidebar`, `useTheme`, `useRouteLoading`, `useValorDebounced`, `useAsyncState` — instâncias por componente ou utilitários sem estado próprio compartilhado Consulte [composables.md](composables.md) para detalhes de cada um (documenta os principais; nem todos os listados acima têm entrada própria lá). ## Camada de API Os arquivos em `src/api/` encapsulam as chamadas HTTP, um por domínio. Todos usam o `client.js` (que faz `fetch` com injeção de token e refresh automático). Referência de endpoints em [backend/docs/api.md](../../backend/docs/api.md): ``` src/api/ ├── client.js Cliente HTTP com interceptor de auth e refresh automático (apiFetch, fetchAutenticado) ├── auth.js loginRequest, logoutRequest, refreshTokenRequest, solicitarResetSenha ├── chat.js sendChatStream, ingestDocuments, ingestFile, ingestUrl, │ search, listDocuments, deleteDocumentsBySource ├── chatConversas.js CRUD de conversas do chat: listConversations, createConversation, │ getConversationMessages, updateConversationTitle, deleteConversation, exportConversation ├── atendimentos.js sincronização, avaliação, golden set, ranking de atendentes e relatórios PDF ├── conversas.js chat de atendente via WhatsApp: listarConversas, responderConversa, gerarSugestaoResposta, ... ├── whatsappConexao.js statusWhatsapp └── users.js listUsers, createUser, updateUser, toggleUserStatus, reset de senha ``` > Chat, busca, ingest e documentos vivem em `chat.js` — não há `documents.js`/`ingest.js` separados. `conversations.js` não existe: o CRUD de conversas do chat é `chatConversas.js`, para não colidir com `conversas.js` (chat de atendente via WhatsApp, domínio diferente). ## Fluxo de autenticação no frontend ``` 1. Ao montar o app, useAuth lê o token salvo em localStorage ou sessionStorage 2. O token é injetado no client.js via setAccessToken() 3. O wrapper de `fetch` em `client.js` captura respostas 401 e tenta refresh automático 4. Se o refresh falha, setAuthErrorCallback() é chamado → sessão limpa → redirect /login 5. O refresh proativo (scheduleProactiveRefresh) renova o token 60s antes de expirar ``` Detalhes do composable em [composables.md](composables.md#useauth); fluxo e níveis de acesso do lado backend em [backend/docs/auth.md](../../backend/docs/auth.md). ## Tema (light/dark) O tema é controlado pela classe `.dark` no elemento ``. O componente `BotaoAlterarTema.vue` alterna entre os dois modos. Consulte [style.md](style.md) para o sistema de cores.