leonardo há 2 dias atrás
pai
commit
5558350fd6
4 ficheiros alterados com 80 adições e 21 exclusões
  1. 42 19
      docs/architecture.md
  2. 2 2
      docs/composables.md
  3. 28 0
      src/styles/tailwind.css
  4. 8 0
      src/utils/markdownRenderer.js

+ 42 - 19
docs/architecture.md

@@ -15,13 +15,17 @@ frontend/src/
 ├── styles/           CSS global e variáveis de tema
 ├── utils/            Utilitários genéricos
 └── views/            Páginas da aplicação
-    ├── auth/         LoginView
+    ├── atendimentos/     AtendimentoView, AvaliacoesView, GoldenSetView, NaoResolvidosView, RankingAtendentesView
+    ├── auth/             LoginView
     ├── configuracoes/
-    ├── conversas/    ConversasHistoricoView, ConversasPesquisarView
+    ├── 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`.
@@ -32,13 +36,24 @@ O Vue Router está configurado em `src/router/index.js`. Todas as rotas autentic
 | `/` | `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 (nivel "3") |
-
-**Guard de rota:**
-- Usuário não autenticado tentando rota protegida → redirecionado para `/login`
-- Usuário autenticado tentando `/login` → redirecionado para `/`
-- Usuário de nível "1" tentando `/usuarios` → redirecionado para `/configuracoes`
+| `/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
 
@@ -49,29 +64,35 @@ O Vue Router está configurado em `src/router/index.js`. Todas as rotas autentic
 
 ## Estado da aplicação
 
-O frontend **não usa Pinia**. O estado é gerenciado por composables com padrão singleton:
+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)
-- `useSearch`, `useToast`, `useUsers` — instâncias por componente
+- `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.
+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. Todos usam o `client.js` (que faz `fetch` com injeção de token e refresh automático):
+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
-├── auth.js           loginRequest, logoutRequest, refreshTokenRequest
-├── chat.js           sendChat, sendChatStream, search, ingestDocuments,
-│                     ingestFile, ingestUrl, listDocuments, deleteDocumentsBySource
-├── conversations.js  listConversations, createConversation, ...
-└── users.js          listUsers, createUser, updateUser, toggleUserStatus
+├── 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
 ```
 
-> As funções de chat, busca, ingest e documentos vivem todas em `chat.js` — não há arquivos `documents.js` ou `ingest.js` separados. A função de ingest de texto é `ingestDocuments`.
+> 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
 
@@ -83,6 +104,8 @@ src/api/
 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 `<html>`. O componente `BotaoAlterarTema.vue` alterna entre os dois modos. Consulte [style.md](style.md) para o sistema de cores.

+ 2 - 2
docs/composables.md

@@ -1,6 +1,6 @@
 # Composables
 
-Lógica reutilizável em `src/composables/`. `useAuth` e `useChat` são singletons — o estado é compartilhado entre todos os componentes que os importam.
+Lógica reutilizável em `src/composables/`. `useAuth` e `useChat` são singletons — o estado é compartilhado entre todos os componentes que os importam. Visão geral de onde cada composable entra na aplicação em [architecture.md](architecture.md).
 
 ---
 
@@ -9,7 +9,7 @@ Lógica reutilizável em `src/composables/`. `useAuth` e `useChat` são singleto
 **Arquivo:** `src/composables/useAuth.js`
 **Padrão:** Singleton (estado no escopo do módulo)
 
-Gerencia a sessão do usuário: login, logout, persistência de token e refresh proativo.
+Gerencia a sessão do usuário: login, logout, persistência de token e refresh proativo. Reflete os níveis de acesso definidos em [backend/docs/auth.md](../../backend/docs/auth.md).
 
 ### O que expõe
 

+ 28 - 0
src/styles/tailwind.css

@@ -281,6 +281,34 @@ body {
     margin: 0.8em 0;
   }
 
+  .prose-content .table-wrapper {
+    margin: 0.5em 0 0.7em;
+    overflow-x: auto;
+    border: 1px solid var(--border);
+    border-radius: 10px;
+  }
+  .prose-content table {
+    width: 100%;
+    border-collapse: collapse;
+    font-size: 0.85em;
+    white-space: nowrap;
+  }
+  .prose-content th,
+  .prose-content td {
+    padding: 6px 10px;
+    text-align: left;
+    border-bottom: 1px solid var(--border);
+  }
+  .prose-content thead th {
+    background: var(--muted);
+    color: var(--muted-foreground);
+    font-weight: 650;
+  }
+  .prose-content tbody tr:last-child th,
+  .prose-content tbody tr:last-child td {
+    border-bottom: none;
+  }
+
   
   .prose-content code:not(pre code) {
     background: rgba(91, 140, 255, 0.1);

+ 8 - 0
src/utils/markdownRenderer.js

@@ -89,6 +89,14 @@ renderer.code = ({ text, lang }) => {
   </div>`;
 };
 
+// `this` precisa ser o renderer real (com `this.parser` já plugado pelo marked.use)
+// pra tablecell/tablerow conseguirem parsear o conteúdo inline das células —
+// chamar um `new Renderer()` solto quebra com "Cannot read properties of undefined
+// (reading 'parseInline')".
+renderer.table = function (token) {
+  return `<div class="table-wrapper">${Renderer.prototype.table.call(this, token)}</div>`;
+};
+
 marked.use({
   renderer,
   breaks: true,