Quellcode durchsuchen

documentação frontend

leonardo vor 2 Monaten
Ursprung
Commit
248e41d28f
5 geänderte Dateien mit 456 neuen und 6 gelöschten Zeilen
  1. 88 0
      docs/architecture.md
  2. 130 0
      docs/components.md
  3. 191 0
      docs/composables.md
  4. 37 0
      docs/setup.md
  5. 10 6
      docs/style.md

+ 88 - 0
docs/architecture.md

@@ -0,0 +1,88 @@
+# 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, BaseInput, BaseBadge
+│   └── 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
+    ├── auth/         LoginView
+    ├── configuracoes/
+    ├── conversas/    ConversasHistoricoView, ConversasPesquisarView
+    ├── pagina-inicial/
+    └── usuarios/
+```
+
+## 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 |
+| `/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`
+
+## 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 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
+
+Consulte [composables.md](composables.md) para detalhes de cada um.
+
+## 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):
+
+```
+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
+```
+
+> 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`.
+
+## 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
+```
+
+## 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.

+ 130 - 0
docs/components.md

@@ -0,0 +1,130 @@
+# Design System — Componentes Base
+
+Componentes em `src/components/base/`. São a base de toda a UI — sempre prefira esses componentes a elementos HTML nativos.
+
+---
+
+## BaseButton
+
+**Arquivo:** `src/components/base/BaseButton.vue`
+
+Botão com suporte a variantes, tamanhos e estado de carregamento.
+
+### Props
+
+| Prop | Tipo | Padrão | Valores aceitos |
+|---|---|---|---|
+| `variant` | `String` | `"primary"` | `"primary"` · `"secondary"` · `"ghost"` · `"danger"` |
+| `size` | `String` | `"md"` | `"sm"` · `"md"` · `"lg"` |
+| `type` | `String` | `"button"` | `"button"` · `"submit"` · `"reset"` |
+| `disabled` | `Boolean` | `false` | — |
+| `loading` | `Boolean` | `false` | Exibe spinner e bloqueia cliques |
+
+### Variantes
+
+| Variante | Uso |
+|---|---|
+| `primary` | Ação principal da tela (submit, confirmar) |
+| `secondary` | Ação secundária, menos destaque |
+| `ghost` | Ação discreta, sem borda ou fundo visível |
+| `danger` | Ações destrutivas (excluir, revogar) |
+
+### Exemplos
+
+```vue
+<BaseButton>Salvar</BaseButton>
+
+<BaseButton variant="secondary" size="sm">Cancelar</BaseButton>
+
+<BaseButton variant="danger" :loading="salvando" @click="excluir">
+  Excluir
+</BaseButton>
+
+<BaseButton type="submit" :disabled="!formValido">Enviar</BaseButton>
+```
+
+---
+
+## BaseInput
+
+**Arquivo:** `src/components/base/BaseInput.vue`
+
+Campo de texto com suporte a label, hint e mensagem de erro. Compatível com `v-model`.
+
+### Props
+
+| Prop | Tipo | Padrão | Descrição |
+|---|---|---|---|
+| `modelValue` | `String` | `""` | Valor do campo (use com `v-model`) |
+| `label` | `String` | `""` | Rótulo exibido acima do input |
+| `hint` | `String` | `""` | Texto de ajuda abaixo do campo |
+| `error` | `String` | `""` | Mensagem de erro (substitui hint quando preenchida) |
+| `id` | `String` | `""` | ID do input; gerado automaticamente se omitido |
+
+Aceita todos os atributos HTML nativos de `<input>` via `v-bind="$attrs"` (ex: `placeholder`, `type`, `autocomplete`).
+
+### Exemplos
+
+```vue
+<BaseInput v-model="nome" label="Nome completo" placeholder="Ex: João Silva" />
+
+<BaseInput
+  v-model="email"
+  label="E-mail"
+  type="email"
+  hint="Usado para login no sistema"
+  :error="erros.email"
+/>
+
+<BaseInput v-model="senha" label="Senha" type="password" />
+```
+
+---
+
+## BaseBadge
+
+**Arquivo:** `src/components/base/BaseBadge.vue`
+
+Chip/etiqueta para exibir status, categorias ou contadores.
+
+### Props
+
+| Prop | Tipo | Padrão | Valores aceitos |
+|---|---|---|---|
+| `variant` | `String` | `"default"` | `"default"` · `"success"` · `"danger"` · `"info"` · `"warning"` · `"violet"` |
+
+### Variantes
+
+| Variante | Cor | Uso sugerido |
+|---|---|---|
+| `default` | Cinza | Status genérico, neutro |
+| `success` | Verde | Ativo, aprovado, concluído |
+| `danger` | Vermelho | Inativo, erro, bloqueado |
+| `info` | Azul | Informação, em andamento |
+| `warning` | Âmbar | Alerta, pendente |
+| `violet` | Violeta | Destaque especial, admin |
+
+### Exemplos
+
+```vue
+<BaseBadge variant="success">Ativo</BaseBadge>
+
+<BaseBadge variant="danger">Inativo</BaseBadge>
+
+<BaseBadge variant="violet">Admin</BaseBadge>
+
+<BaseBadge>Padrão</BaseBadge>
+```
+
+---
+
+## Skeletons
+
+**Pasta:** `src/components/skeletons/`
+
+| Componente | Uso |
+|---|---|
+| `SkeletonConversation.vue` | Placeholder de item de conversa na sidebar |
+| `SkeletonDocumentCard.vue` | Placeholder de card de documento na listagem |
+
+Exibidos durante o carregamento assíncrono, substituídos pelo conteúdo real quando os dados chegam.

+ 191 - 0
docs/composables.md

@@ -0,0 +1,191 @@
+# Composables
+
+Lógica reutilizável em `src/composables/`. `useAuth` e `useChat` são singletons — o estado é compartilhado entre todos os componentes que os importam.
+
+---
+
+## useAuth
+
+**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.
+
+### O que expõe
+
+| Nome | Tipo | Descrição |
+|---|---|---|
+| `session` | `Readonly<Ref>` | Sessão completa (usuario, accessToken, refreshToken, rememberMe) |
+| `user` | `ComputedRef` | Objeto do usuário logado (`null` se não autenticado) |
+| `isAuthenticated` | `ComputedRef<Boolean>` | `true` se há usuário com `Id` na sessão |
+| `login(params)` | `Function` | Faz login; params: `{ login, senha, rememberMe? }` |
+| `logout()` | `Function` | Revoga o token e limpa a sessão |
+
+### Comportamento
+
+- A sessão é salva em `localStorage` (se `rememberMe`) ou `sessionStorage`
+- O `accessToken` é injetado automaticamente no cliente HTTP via `setAccessToken()`
+- O refresh proativo agenda a renovação 60s antes do accessToken expirar
+- Se o refresh falhar, o usuário é redirecionado para `/login` automaticamente
+
+### Exemplo
+
+```js
+import { useAuth } from "@/composables/useAuth.js";
+
+const { user, isAuthenticated, login, logout } = useAuth();
+
+// Login
+await login({ login: "usuario@empresa.com", senha: "123456", rememberMe: true });
+
+// Verificar nível
+if (String(user.value?.Nivel) === "3") { /* admin */ }
+
+// Logout
+await logout();
+```
+
+---
+
+## useChat
+
+**Arquivo:** `src/composables/useChat.js`
+**Padrão:** Singleton explícito
+
+Gerencia todo o estado do chat: lista de conversas, mensagens ativas, envio de mensagens via stream SSE.
+
+### O que expõe
+
+| Nome | Tipo | Descrição |
+|---|---|---|
+| `conversations` | `Ref<Array>` | Lista de conversas do usuário |
+| `activeConversationId` | `Ref<String\|null>` | ID da conversa ativa |
+| `messages` | `Ref<Array>` | Mensagens da conversa ativa |
+| `loading` | `Ref<Boolean>` | `true` enquanto uma resposta está sendo gerada |
+| `error` | `Ref<String>` | Mensagem de erro do último envio |
+| `conversationsLoading` | `Ref<Boolean>` | `true` enquanto carrega a lista de conversas |
+| `conversationsError` | `Ref<String>` | Erro ao carregar conversas |
+| `conversationsHasMore` | `Ref<Boolean>` | Há mais conversas para paginar |
+| `send(content)` | `Function` | Envia mensagem; cria conversa se não houver ativa |
+| `newConversation()` | `Function` | Limpa mensagens e define nova conversa (sem ID) |
+| `setActiveConversation(id)` | `Function` | Carrega mensagens de uma conversa existente |
+| `renameConversation(id, title)` | `Function` | Renomeia uma conversa |
+| `deleteConversation(id)` | `Function` | Remove uma conversa e suas mensagens |
+| `exportConversation(id)` | `Function` | Faz download da conversa em Markdown |
+| `searchConversations(query)` | `Function` | Filtra conversas pelo título (client-side) |
+| `loadConversationsList()` | `Function` | Recarrega a lista do início |
+| `loadMoreConversations()` | `Function` | Carrega próxima página de conversas |
+| `clearError()` | `Function` | Limpa `error` |
+| `cancelCurrentStream()` | `Function` | Cancela o stream SSE em andamento |
+
+### Exemplo
+
+```js
+import { useChat } from "@/composables/useChat.js";
+
+const { messages, loading, send, newConversation, setActiveConversation } = useChat();
+
+// Enviar mensagem
+await send("Qual é a política de férias da empresa?");
+
+// Abrir conversa existente
+await setActiveConversation(42);
+
+// Nova conversa
+newConversation();
+```
+
+---
+
+## useSearch
+
+**Arquivo:** `src/composables/useSearch.js`
+**Padrão:** Instância por componente
+
+Busca semântica nos documentos da base de conhecimento.
+
+### O que expõe
+
+| Nome | Tipo | Descrição |
+|---|---|---|
+| `results` | `Ref<Array>` | Resultados da busca (`[{ content, source, score }]`) |
+| `loading` | `Ref<Boolean>` | `true` durante a busca |
+| `error` | `Ref<String>` | Mensagem de erro |
+| `run(query)` | `Function` | Executa a busca com a query informada |
+
+### Exemplo
+
+```js
+import { useSearch } from "@/composables/useSearch.js";
+
+const { results, loading, error, run } = useSearch();
+
+await run("política de benefícios");
+// results.value → [{ content: "...", source: "rh/beneficios.pdf", score: 0.92 }]
+```
+
+---
+
+## useToast
+
+**Arquivo:** `src/composables/useToast.js`
+**Padrão:** Instância por componente (wrapper de `vue-toastification`)
+
+Exibe notificações toast no canto da tela.
+
+### O que expõe
+
+| Método | Descrição |
+|---|---|
+| `sucesso(msg)` | Toast verde de sucesso |
+| `erro(msg)` | Toast vermelho de erro |
+| `info(msg)` | Toast azul informativo |
+| `aviso(msg)` | Toast âmbar de aviso |
+
+### Exemplo
+
+```js
+import { useToast } from "@/composables/useToast.js";
+
+const toast = useToast();
+
+toast.sucesso("Documento enviado com sucesso!");
+toast.erro("Não foi possível excluir o documento.");
+toast.info("Processando o arquivo...");
+toast.aviso("A sessão expira em 5 minutos.");
+```
+
+---
+
+## useUsers
+
+**Arquivo:** `src/composables/useUsers.js`
+**Padrão:** Estado compartilhado via refs no escopo do módulo
+
+Gerencia a lista de usuários do sistema (apenas para administradores).
+
+### O que expõe
+
+| Nome | Tipo | Descrição |
+|---|---|---|
+| `users` | `Ref<Array>` | Lista de usuários carregados |
+| `loading` | `Ref<Boolean>` | `true` durante carregamento |
+| `error` | `Ref<String>` | Erro ao carregar |
+| `loadUsers()` | `Function` | Busca a lista de usuários da API |
+| `createUser(data)` | `Function` | Cria usuário; atualiza `users` automaticamente |
+| `updateUser(id, data)` | `Function` | Atualiza usuário; atualiza `users` automaticamente |
+| `toggleStatus(id)` | `Function` | Ativa/desativa usuário; atualiza `users` automaticamente |
+
+### Exemplo
+
+```js
+import { useUsers } from "@/composables/useUsers.js";
+
+const { users, loading, loadUsers, createUser, toggleStatus } = useUsers();
+
+await loadUsers();
+
+await createUser({ nome: "Maria", login: "maria", senha: "abc123", nivel: "1" });
+
+await toggleStatus(5);
+```

+ 37 - 0
docs/setup.md

@@ -0,0 +1,37 @@
+# Setup — Frontend
+
+## Pré-requisitos
+
+- Node.js 20+
+- Backend rodando (ver [backend/docs/setup.md](../../backend/docs/setup.md))
+
+## Instalação
+
+```bash
+cd frontend
+npm install
+```
+
+## Variáveis de ambiente
+
+Crie o arquivo `frontend/.env` (ou `.env.local` para sobrescrever localmente):
+
+| Variável | Descrição | Exemplo |
+|---|---|---|
+| `VITE_API_URL` | URL base da API do backend | `http://localhost:3001` |
+
+## Rodando localmente
+
+```bash
+npm run dev
+```
+
+O frontend estará disponível em `http://localhost:5173`.
+
+## Comandos disponíveis
+
+| Comando | Descrição |
+|---|---|
+| `npm run dev` | Servidor de desenvolvimento com HMR |
+| `npm run build` | Build de produção (saída em `dist/`) |
+| `npm run preview` | Pré-visualizar o build de produção localmente |

+ 10 - 6
docs/style.md

@@ -30,7 +30,7 @@
 
 ## Botão primário (submit)
 
-Usar o componente `Button` do PrimeVue com estilo default — ele já suporta dark mode via tema do projeto, sem necessidade de overrides.
+Usar o componente `BaseButton` (variante `primary`) do design system — ele já trata light/dark via variáveis de cor do projeto, sem necessidade de overrides. Ver [components.md](components.md).
 
 ## Padrão de uso
 
@@ -55,10 +55,14 @@ Todo `gray-*` que for texto, borda ou hover deve ganhar a contraparte `dark:` co
 - `text-gray-900`/`dark:text-gray-100` — texto principal
 - `text-gray-500`/`dark:text-gray-400` — texto secundário
 - `text-gray-400`/`dark:text-gray-500` — texto terciário
-- Use o componente `Message` com `severity="error"` do PrimeVue para mensagens de erro nos formulários
+- Use a prop `error` do `BaseInput` para exibir mensagens de erro nos formulários.
 
-## Componentes PrimeVue
+## Design system
 
-- `Button`: usar componente sem overrides de cor — segue o tema do projeto.
-- `InputText`, `Password`, `Message`: seguem o tema via variáveis CSS do PrimeVue, já suportam dark mode.
-- Não aplicar `bg-gray-900!` ou outras overrides manuais diretamente nos componentes PrimeVue.
+A UI é construída sobre o design system próprio (`src/components/base/`) + Tailwind CSS. **Não há PrimeVue no projeto.**
+
+- `BaseButton`: variantes `primary`/`secondary`/`ghost`/`danger`, já tratam light/dark.
+- `BaseInput`: campo com `label`, `hint` e `error` integrados.
+- `BaseBadge`: variantes `default`/`success`/`danger`/`info`/`warning`/`violet`.
+
+Detalhes de props e exemplos em [components.md](components.md). Para cores que não vêm dos componentes base, aplicar Tailwind com o prefixo `dark:` segundo as tabelas acima.