diff --git a/.playwright-mcp/page-2026-04-08T15-57-28-475Z.yml b/.playwright-mcp/page-2026-04-08T15-57-28-475Z.yml new file mode 100644 index 0000000..699f54c --- /dev/null +++ b/.playwright-mcp/page-2026-04-08T15-57-28-475Z.yml @@ -0,0 +1,26 @@ +- generic [ref=e2]: + - region "Notifications (F8)": + - list + - generic [ref=e5]: + - generic [ref=e6]: + - img "Arcadia" [ref=e7] + - heading "Arcádia Suite" [level=1] [ref=e8] + - generic [ref=e9]: + - tablist [ref=e10]: + - tab "Login" [selected] [ref=e11] + - tab "Criar Conta" [ref=e12] + - tabpanel "Login" [ref=e13]: + - generic [ref=e14]: + - generic [ref=e15]: + - generic [ref=e16]: Bem-vindo de volta + - generic [ref=e17]: Entre com suas credenciais para acessar o sistema + - generic [ref=e19]: + - generic [ref=e20]: + - text: Usuário + - textbox "Usuário" [ref=e21]: + - /placeholder: Digite seu usuário + - generic [ref=e22]: + - text: Senha + - textbox "Senha" [ref=e23]: + - /placeholder: Digite sua senha + - button "Entrar" [ref=e24] \ No newline at end of file diff --git a/.playwright-mcp/page-2026-04-08T15-57-41-246Z.png b/.playwright-mcp/page-2026-04-08T15-57-41-246Z.png new file mode 100644 index 0000000..563c83d Binary files /dev/null and b/.playwright-mcp/page-2026-04-08T15-57-41-246Z.png differ diff --git a/.playwright-mcp/page-2026-04-08T15-58-19-679Z.yml b/.playwright-mcp/page-2026-04-08T15-58-19-679Z.yml new file mode 100644 index 0000000..aa7674b --- /dev/null +++ b/.playwright-mcp/page-2026-04-08T15-58-19-679Z.yml @@ -0,0 +1,34 @@ +- generic [ref=e2]: + - region "Notifications (F8)": + - list [ref=e26]: + - status [ref=e27]: + - generic [ref=e28]: + - generic [ref=e29]: Login failed + - generic [ref=e30]: Invalid username or password + - button [ref=e31]: + - img [ref=e32] + - generic [ref=e5]: + - generic [ref=e6]: + - img "Arcadia" [ref=e7] + - heading "Arcádia Suite" [level=1] [ref=e8] + - generic [ref=e9]: + - tablist [ref=e10]: + - tab "Login" [selected] [ref=e11] + - tab "Criar Conta" [ref=e12] + - tabpanel "Login" [ref=e13]: + - generic [ref=e14]: + - generic [ref=e15]: + - generic [ref=e16]: Bem-vindo de volta + - generic [ref=e17]: Entre com suas credenciais para acessar o sistema + - generic [ref=e19]: + - generic [ref=e20]: + - text: Usuário + - textbox "Usuário" [ref=e21]: + - /placeholder: Digite seu usuário + - text: admin + - generic [ref=e22]: + - text: Senha + - textbox "Senha" [ref=e23]: + - /placeholder: Digite sua senha + - text: admin + - button "Entrar" [ref=e24] \ No newline at end of file diff --git a/.playwright-mcp/page-2026-04-08T15-58-26-658Z.png b/.playwright-mcp/page-2026-04-08T15-58-26-658Z.png new file mode 100644 index 0000000..f73d198 Binary files /dev/null and b/.playwright-mcp/page-2026-04-08T15-58-26-658Z.png differ diff --git a/.playwright-mcp/page-2026-04-08T15-58-26-731Z.yml b/.playwright-mcp/page-2026-04-08T15-58-26-731Z.yml new file mode 100644 index 0000000..96c6a88 --- /dev/null +++ b/.playwright-mcp/page-2026-04-08T15-58-26-731Z.yml @@ -0,0 +1,28 @@ +- generic [ref=e2]: + - region "Notifications (F8)": + - list + - generic [ref=e5]: + - generic [ref=e6]: + - img "Arcadia" [ref=e7] + - heading "Arcádia Suite" [level=1] [ref=e8] + - generic [ref=e9]: + - tablist [ref=e10]: + - tab "Login" [selected] [ref=e11] + - tab "Criar Conta" [ref=e12] + - tabpanel "Login" [ref=e13]: + - generic [ref=e14]: + - generic [ref=e15]: + - generic [ref=e16]: Bem-vindo de volta + - generic [ref=e17]: Entre com suas credenciais para acessar o sistema + - generic [ref=e19]: + - generic [ref=e20]: + - text: Usuário + - textbox "Usuário" [ref=e21]: + - /placeholder: Digite seu usuário + - text: admin + - generic [ref=e22]: + - text: Senha + - textbox "Senha" [ref=e23]: + - /placeholder: Digite sua senha + - text: admin + - button "Entrar" [ref=e24] \ No newline at end of file diff --git a/.playwright-mcp/page-2026-04-08T15-58-47-519Z.yml b/.playwright-mcp/page-2026-04-08T15-58-47-519Z.yml new file mode 100644 index 0000000..8c8dee1 --- /dev/null +++ b/.playwright-mcp/page-2026-04-08T15-58-47-519Z.yml @@ -0,0 +1,30 @@ +- generic [ref=e2]: + - region "Notifications (F8)": + - list + - generic [ref=e5]: + - generic [ref=e6]: + - img "Arcadia" [ref=e7] + - heading "Arcádia Suite" [level=1] [ref=e8] + - generic [ref=e9]: + - tablist [ref=e10]: + - tab "Login" [selected] [ref=e11] + - tab "Criar Conta" [ref=e12] + - tabpanel "Login" [ref=e13]: + - generic [ref=e14]: + - generic [ref=e15]: + - generic [ref=e16]: Bem-vindo de volta + - generic [ref=e17]: Entre com suas credenciais para acessar o sistema + - generic [ref=e19]: + - generic [ref=e20]: + - text: Usuário + - textbox "Usuário" [ref=e21]: + - /placeholder: Digite seu usuário + - text: admin + - generic [ref=e22]: + - text: Senha + - textbox "Senha" [ref=e23]: + - /placeholder: Digite sua senha + - text: admin + - button "Entrando..." [disabled]: + - img + - text: Entrando... \ No newline at end of file diff --git a/.playwright-mcp/page-2026-04-08T15-58-53-177Z.yml b/.playwright-mcp/page-2026-04-08T15-58-53-177Z.yml new file mode 100644 index 0000000..96c6a88 --- /dev/null +++ b/.playwright-mcp/page-2026-04-08T15-58-53-177Z.yml @@ -0,0 +1,28 @@ +- generic [ref=e2]: + - region "Notifications (F8)": + - list + - generic [ref=e5]: + - generic [ref=e6]: + - img "Arcadia" [ref=e7] + - heading "Arcádia Suite" [level=1] [ref=e8] + - generic [ref=e9]: + - tablist [ref=e10]: + - tab "Login" [selected] [ref=e11] + - tab "Criar Conta" [ref=e12] + - tabpanel "Login" [ref=e13]: + - generic [ref=e14]: + - generic [ref=e15]: + - generic [ref=e16]: Bem-vindo de volta + - generic [ref=e17]: Entre com suas credenciais para acessar o sistema + - generic [ref=e19]: + - generic [ref=e20]: + - text: Usuário + - textbox "Usuário" [ref=e21]: + - /placeholder: Digite seu usuário + - text: admin + - generic [ref=e22]: + - text: Senha + - textbox "Senha" [ref=e23]: + - /placeholder: Digite sua senha + - text: admin + - button "Entrar" [ref=e24] \ No newline at end of file diff --git a/.playwright-mcp/page-2026-04-08T15-58-58-942Z.png b/.playwright-mcp/page-2026-04-08T15-58-58-942Z.png new file mode 100644 index 0000000..f73d198 Binary files /dev/null and b/.playwright-mcp/page-2026-04-08T15-58-58-942Z.png differ diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..98d0181 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,203 @@ +# 🤖 Referência Rápida para Agentes - Arcádia Suite + +> Guia de referência para desenvolvimento autônomo no projeto Arcádia Suite + +--- + +## 📁 Documentação Técnica por Componente + +### Navegação (Navbar) +- **Especificação Completa:** [`docs/SPEC_NAVBAR.md`](docs/SPEC_NAVBAR.md) +- **Arquivo Fonte:** `client/src/components/Browser/BrowserFrame.tsx` +- **Hook de Tracking:** `client/src/hooks/use-navigation-tracking.ts` +- **Error Boundary:** `client/src/components/ErrorBoundary.tsx` (tratamento de erros lazy loading) + +### Banco de Dados (Schema) +- **Arquivo Principal:** `shared/schema.ts` (7.000+ linhas) +- **Tabelas Principais:** + - `users`, `profiles`, `roles` - Autenticação e RBAC + - `tenants`, `tenantUsers` - Multi-tenancy + - `workspacePages`, `pageBlocks` - Produtividade (estilo Notion) + - `conversations`, `messages` - Chat/Agente + - `whatsappSessions`, `whatsappMessages` - WhatsApp + - `crmPartners`, `crmContracts` - CRM + - `fiscalNcms`, `fiscalGruposTributacao` - Fiscal + - `retailStores`, `posSales`, `mobileDevices` - Retail + - `manusRuns`, `manusSteps` - Agente Manus + - `biDatasets`, `biCharts`, `biDashboards` - BI + +### Backend (API Routes) +- **Registro de Rotas:** `server/routes.ts` +- **Autenticação:** `server/auth.ts` +- **Agente Manus:** `server/manus/service.ts` + `server/manus/tools.ts` +- **WhatsApp:** `server/whatsapp/routes.ts` + `server/whatsapp/service.ts` +- **CRM:** `server/crm/routes.ts` +- **Fiscal:** `server/fisco/routes.ts` +- **Retail:** `server/retail/routes.ts` +- **Blackboard (Agentes):** `server/blackboard/routes.ts` + +### Frontend (Páginas) +- **Router:** `client/src/App.tsx` (inclui ErrorBoundary + Suspense) +- **Cockpit (Home):** `client/src/pages/Cockpit.tsx` +- **Agent:** `client/src/pages/Agent.tsx` +- **DevCenter:** `client/src/pages/DevCenter.tsx` +- **Crm:** `client/src/pages/Crm.tsx` +- **Fisco:** `client/src/pages/Fisco.tsx` +- **Retail:** `client/src/pages/ArcadiaRetail.tsx` +- **WhatsApp:** `client/src/pages/WhatsApp.tsx` +- **ErrorBoundary:** `client/src/components/ErrorBoundary.tsx` + +--- + +## 🏗️ Arquitetura do Sistema + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ CAMADA DE APRESENTAÇÃO │ +│ React 18 + TypeScript + Tailwind CSS + shadcn/ui │ +│ 66 páginas / Interface tipo browser com abas │ +└─────────────────────────────────────────────────────────────────┘ + ▼ +┌─────────────────────────────────────────────────────────────────┐ +│ CAMADA DE ORQUESTRAÇÃO │ +│ Express.js + Socket.IO + Manus Agent │ +│ API REST + WebSocket em tempo real │ +└─────────────────────────────────────────────────────────────────┘ + ▼ +┌─────────────────────────────────────────────────────────────────┐ +│ CAMADA DE INTELIGÊNCIA │ +│ FastAPI (Contábil 8003, BI 8004, Automação 8005) │ +│ OpenAI GPT-4o + LiteLLM Gateway + Ollama │ +└─────────────────────────────────────────────────────────────────┘ + ▼ +┌─────────────────────────────────────────────────────────────────┐ +│ CAMADA DE DADOS │ +│ PostgreSQL 16 + Drizzle ORM + pgvector │ +│ Session Store + Multi-tenant │ +└─────────────────────────────────────────────────────────────────┘ +``` + +--- + +## 🛠️ Stack Tecnológico + +| Camada | Tecnologia | +|--------|------------| +| **Frontend** | React 18, TypeScript, Tailwind CSS, shadcn/ui | +| **Backend** | Express.js, Socket.IO, Passport.js | +| **Banco** | PostgreSQL 16, Drizzle ORM, pgvector | +| **Python** | FastAPI (microserviços nas portas 8001-8005) | +| **IA** | OpenAI GPT-4o, LiteLLM, Ollama | +| **Deploy** | Docker Compose, Coolify, PM2 | + +--- + +## 🔧 Comandos Úteis + +```bash +# Desenvolvimento +npm run dev # Inicia servidor de desenvolvimento +npm run dev:client # Apenas frontend (porta 5000) + +# Banco de dados +npm run db:push # Aplica migrations Drizzle +npm run db:apply-rls # Aplica políticas RLS + +# Build +npm run build # Build para produção +npm run start # Inicia em produção + +# Docker +docker compose up -d # Sobe todos os serviços +docker compose --profile ai up # Sobe com IA (Ollama + LiteLLM) +``` + +--- + +## 📋 Convenções de Código + +### Nomenclatura +- **Componentes:** PascalCase (ex: `BrowserFrame.tsx`) +- **Hooks:** camelCase com prefixo `use` (ex: `useAuth.tsx`) +- **Utilitários:** camelCase (ex: `navigation-tracking.ts`) +- **Rotas API:** kebab-case (ex: `/api/whatsapp/connect`) + +### Estrutura de Pastas +``` +client/src/ + components/ # Componentes reutilizáveis + pages/ # Páginas da aplicação + hooks/ # Custom hooks + lib/ # Utilitários e configurações + +server/ + [modulo]/ # Um diretório por módulo + routes.ts # Rotas da API + service.ts # Lógica de negócio + storage.ts # Acesso a dados +``` + +--- + +## 🧪 Test IDs + +Sempre adicione `data-testid` em elementos interativos: + +```tsx + +``` + +--- + +## 📚 Documentação Adicional + +- **Documentação Técnica Completa:** [`DOCUMENTATION.md`](DOCUMENTATION.md) +- **Mapa do Sistema:** [`MAPA_SISTEMA_ARCADIA.md`](MAPA_SISTEMA_ARCADIA.md) +- **Contexto para Claude:** [`CLAUDE.md`](CLAUDE.md) +- **README:** [`README.md`](README.md) + +--- + +## 🆘 Precisa de Ajuda? + +1. Consulte a especificação técnica específica em `docs/` +2. Verifique exemplos similares no código existente +3. Siga os padrões já estabelecidos no projeto + +--- + +*Última atualização: 2026-04-08* + +--- + +## 🐛 Tratamento de Erros - Lazy Loading + +### Problema Resolvido: Loading Infinito na Navegação + +**Sintoma:** Ao clicar em itens da navbar, se a página tivesse erro (import quebrado, sintaxe inválida), o loading ficava infinito. + +**Causa:** `React.lazy()` falha silenciosamente e `Suspense` não tem como capturar o erro. + +**Solução:** + +1. **ErrorBoundary** (`client/src/components/ErrorBoundary.tsx`): + - Captura erros de renderização, incluindo lazy load + - Mostra UI de fallback com botão de retry + +2. **LoadingFallback com Timeout** (`client/src/App.tsx`): + - 3s: mensagem "Isso está demorando..." + - 10s: alerta amarelo com botão recarregar + +3. **Integração:** +```tsx + + }> + {/* rotas */} + + +``` diff --git a/client/src/App.tsx b/client/src/App.tsx index b1b4d76..ec334a1 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -1,5 +1,5 @@ import { Switch, Route, useLocation } from "wouter"; -import { lazy, Suspense, useEffect } from "react"; +import { lazy, Suspense, useEffect, useState } from "react"; import { queryClient } from "./lib/queryClient"; import { QueryClientProvider } from "@tanstack/react-query"; import { Toaster } from "@/components/ui/toaster"; @@ -10,6 +10,7 @@ import { ProtectedRoute } from "@/lib/protected-route"; import { CommandPalette } from "@/components/CommandPalette"; import { KnowledgeCollectorInit } from "@/components/KnowledgeCollectorInit"; import { OpenClawWidget } from "@/components/openclaw/OpenClawWidget"; +import { ErrorBoundary } from "@/components/ErrorBoundary"; import NotFound from "@/pages/not-found"; import AuthPage from "@/pages/auth-page"; @@ -72,13 +73,56 @@ const XosSupervisor = lazy(() => import("@/pages/XosSupervisor")); const XosReports = lazy(() => import("@/pages/XosReports")); const XosProtocols = lazy(() => import("@/pages/XosProtocols")); - function LoadingFallback() { + const [showSlowLoading, setShowSlowLoading] = useState(false); + const [showError, setShowError] = useState(false); + + useEffect(() => { + // Show "slow loading" message after 3 seconds + const slowTimer = setTimeout(() => setShowSlowLoading(true), 3000); + // Show error hint after 10 seconds + const errorTimer = setTimeout(() => setShowError(true), 10000); + + return () => { + clearTimeout(slowTimer); + clearTimeout(errorTimer); + }; + }, []); + return ( -
+

Carregando...

+ + {showSlowLoading && ( +

+ Isso está demorando mais que o esperado... +

+ )} + + {showError && ( +
+

+ A página pode ter um erro. Tente recarregar ou volte para a página anterior. +

+ +
+ )}
@@ -87,72 +131,74 @@ function LoadingFallback() { function Router() { return ( - }> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - { const [, nav] = useLocation(); useEffect(() => nav("/soe"), []); return null; }} /> - - - - - - - - - - - - - - - - + + }> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { const [, nav] = useLocation(); useEffect(() => nav("/soe"), []); return null; }} /> + + + + + + + + + + + + + + + + - - - - - - - - - - + + + + + + + + + + + ); } diff --git a/client/src/components/ErrorBoundary.tsx b/client/src/components/ErrorBoundary.tsx new file mode 100644 index 0000000..8736ec5 --- /dev/null +++ b/client/src/components/ErrorBoundary.tsx @@ -0,0 +1,90 @@ +import { Component, ReactNode } from "react"; +import { AlertCircle, RefreshCw } from "lucide-react"; +import { Button } from "@/components/ui/button"; +import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card"; + +interface Props { + children: ReactNode; + fallback?: ReactNode; +} + +interface State { + hasError: boolean; + error?: Error; +} + +export class ErrorBoundary extends Component { + constructor(props: Props) { + super(props); + this.state = { hasError: false }; + } + + static getDerivedStateFromError(error: Error): State { + return { hasError: true, error }; + } + + componentDidCatch(error: Error, errorInfo: React.ErrorInfo) { + console.error("[ErrorBoundary] Erro capturado:", error); + console.error("[ErrorBoundary] Info:", errorInfo); + } + + handleRetry = () => { + this.setState({ hasError: false, error: undefined }); + window.location.reload(); + }; + + handleGoHome = () => { + this.setState({ hasError: false, error: undefined }); + window.location.href = "/"; + }; + + render() { + if (this.state.hasError) { + // Fallback customizado ou padrão + if (this.props.fallback) { + return this.props.fallback; + } + + return ( +
+ + +
+ +
+ Ops! Algo deu errado + + Não foi possível carregar esta página. O módulo pode estar incompleto ou com erro. + +
+ + {this.state.error && ( +
+ {this.state.error.message} +
+ )} + +
+ + +
+
+
+
+ ); + } + + return this.props.children; + } +} diff --git a/docs/SPEC_NAVBAR.md b/docs/SPEC_NAVBAR.md new file mode 100644 index 0000000..1acfaf8 --- /dev/null +++ b/docs/SPEC_NAVBAR.md @@ -0,0 +1,485 @@ +# Especificação Técnica - Navbar do Arcádia Suite + +> **Versão:** 1.0 +> **Data:** 2026-04-08 +> **Arquivo Fonte:** `client/src/components/Browser/BrowserFrame.tsx` +> **Status:** Implementado e em produção + +--- + +## 1. Visão Geral + +A navbar do Arcádia Suite é uma barra de navegação compacta, horizontal e fixa no topo da tela. Ela implementa um padrão de "navegação por bookmarks" onde cada item representa um módulo principal do sistema. + +### 1.1 Objetivos +- Acesso rápido aos módulos principais +- Navegação intuitiva estilo "browser" +- Rastreamento de uso para analytics +- Suporte a múltiplos perfis de usuário + +### 1.2 Princípios de Design +- **Minimalista:** Apenas ícone + texto curto +- **Escalável:** Scroll horizontal permite muitos itens +- **Responsiva:** Adapta-se a diferentes telas +- **Consistente:** Cores e ícones padronizados + +--- + +## 2. Estrutura do Componente + +``` +BrowserFrame (Container) +└── CompactNavigationBar (Navbar principal) + ├── Área de Bookmarks (scroll horizontal) + │ └── BookmarkItem[] (itens de navegação) + └── Área do Usuário (fixa à direita) + └── UserDropdown (menu do usuário) +``` + +### 2.1 Hierarquia de Componentes + +```tsx +// BrowserFrame.tsx +interface BrowserFrameProps { + children: React.ReactNode; +} + +function CompactNavigationBar() { + // Hook de navegação + const [location, setLocation] = useLocation(); // wouter + const { user, logoutMutation } = useAuth(); + const { trackPageView } = useNavigationTracking(); + + // Handler de navegação + const navigateTo = (path: string, pageName: string) => { + trackPageView(pageName, path); + setLocation(path); + }; + + return ( +
+ {/* Bookmarks */} + {/* User Menu */} +
+ ); +} + +export function BrowserFrame({ children }: BrowserFrameProps) { + return ( +
+ +
{children}
+
+ ); +} +``` + +--- + +## 3. Especificação Visual + +### 3.1 Dimensões e Layout + +| Propriedade | Valor | Tailwind | +|-------------|-------|----------| +| Altura | 40px | `h-10` | +| Padding horizontal | 12px | `px-3` | +| Gap entre itens | 8px | `gap-2` | +| Tamanho do ícone | 16x16px | `w-4 h-4` | +| Tamanho do ícone interno | 10x10px | `w-2.5 h-2.5` | +| Padding do bookmark | 6px 8px | `px-2 py-1.5` | +| Border radius | 4px | `rounded` | + +### 3.2 Cores e Estilos + +``` +Fundo: bg-background (varia com tema claro/escuro) +Borda inferior: border-border +Texto: text-muted-foreground +Hover: hover:bg-muted +Sombra: shadow-xs +``` + +### 3.3 Estados dos Bookmarks + +| Estado | Estilo | +|--------|--------| +| **Default** | `text-muted-foreground` | +| **Hover** | `hover:bg-muted` + cursor pointer | +| **Ativo** | Não há estado visual de "ativo" (intencional) | + +--- + +## 4. Itens de Navegação (Bookmarks) + +### 4.1 Lista Completa de Itens + +| Ordem | Nome | Rota | Ícone | Cor do Gradient | Visibilidade | +|-------|------|------|-------|-----------------|--------------| +| 1 | Administração | `/admin` | Settings | slate-700 → slate-900 | Apenas admin | +| 2 | Início | `/` | Arcádia Icon | - (imagem) | Todos | +| 3 | Agent | `/agent` | Bot | primary → blue-600 | Todos | +| 4 | Inbox | `/xos/inbox` | MessageCircle | #00a884 → #25D366 | Todos | +| 5 | Automações | `/automations` | Zap | #c89b3c → #d4a94a | Todos | +| 6 | Insights | `/insights` | LayoutDashboard | #1f334d → #2d4a6f | Todos | +| 7 | Compass | `/compass` | Compass | #c89b3c → #1f334d | Todos | +| 8 | Produção | `/production` | Users | indigo-500 → indigo-700 | Todos | +| 9 | Suporte | `/support` | Ticket | rose-500 → rose-700 | Todos | +| 10 | ERP | `/erp` | Package | blue-600 → blue-800 | Todos | +| 11 | Retail | `/retail` | Store | cyan-500 → blue-600 | Todos | +| 12 | Plus | `/plus` | Layers | purple-500 → purple-700 | Todos | +| 13 | Fisco | `/fisco` | Receipt | emerald-600 → emerald-800 | Todos | +| 14 | Engenharia | `/engineering` | Compass | teal-500 → green-700 | Todos | + +### 4.2 Estrutura de um Bookmark + +```tsx +
navigateTo("/rota", "Nome")} + data-testid="bookmark-nome" +> + {/* Container do ícone com gradiente */} +
+ +
+ {/* Texto (oculto em mobile) */} + Nome +
+``` + +--- + +## 5. Menu do Usuário (Dropdown) + +### 5.1 Estrutura + +``` +[Avatar + Nome] ▼ + │ + ├── Informações do Usuário + │ ├── Nome completo + │ ├── @username + │ └── Badge "Administrador" (se aplicável) + │ + ├── Meu Perfil + │ + ├── Plataforma (apenas admin) + │ └── Centro de Desenvolvimento + │ + └── Sair (texto vermelho) +``` + +### 5.2 Componentes + +- **Trigger:** Botão com avatar circular + nome +- **Avatar:** Inicial do nome em círculo colorido (5x5) +- **Dropdown:** `DropdownMenu` do shadcn/ui +- **Largura:** 224px (`w-56`) + +--- + +## 6. Comportamento e Interações + +### 6.1 Fluxo de Navegação + +``` +┌─────────────────┐ +│ Usuário clica │ +│ em bookmark │ +└────────┬────────┘ + │ + ▼ +┌─────────────────┐ +│ trackPageView │ ← Registra no backend via POST /api/learning/navigation +│ (pageName, path)│ +└────────┬────────┘ + │ + ▼ +┌─────────────────┐ +│ setLocation │ ← wouter atualiza URL +│ (path) │ +└────────┬────────┘ + │ + ▼ +┌─────────────────┐ +│ React.lazy() │ ← Code splitting carrega chunk +│ import() │ +└────────┬────────┘ + │ + ▼ +┌─────────────────┐ +│ │ ← Mostra LoadingFallback +│ fallback │ +└────────┬────────┘ + │ + ▼ +┌─────────────────┐ +│ Página carrega │ ← Componente renderizado +│ e renderiza │ +└─────────────────┘ +``` + +### 6.2 Rastreamento (Analytics) + +```typescript +// use-navigation-tracking.ts +const trackPageView = (pageName: string, route?: string) => { + track({ + module: pageName, // Ex: "Agent", "Fisco" + action: "page_view", + metadata: { + route, // Ex: "/agent", "/fisco" + timestamp: "2026-04-08T15:30:00Z" + }, + }); +}; +``` + +**Debounce:** 2000ms para evitar spam de eventos + +### 6.3 Responsividade + +| Breakpoint | Comportamento | +|------------|---------------| +| **Mobile** (< 768px) | Texto dos bookmarks oculto (`hidden md:inline`), apenas ícones | +| **Desktop** (≥ 768px) | Texto visível ao lado do ícone | + +### 6.4 Scroll Horizontal + +- A área de bookmarks tem `overflow-x-auto` +- Scrollbar oculta (`scrollbar-hide`) +- Itens não quebram linha (`flex-shrink-0`) + +--- + +## 7. Dependências + +### 7.1 Bibliotecas + +```json +{ + "wouter": "^3.3.5", // Router + "lucide-react": "^0.545.0", // Ícones + "@radix-ui/react-dropdown-menu": "^2.1.16" // Dropdown +} +``` + +### 7.2 Hooks Customizados + +| Hook | Arquivo | Propósito | +|------|---------|-----------| +| `useLocation` | `wouter` | Navegação de rotas | +| `useAuth` | `@/hooks/use-auth` | Dados do usuário e logout | +| `useNavigationTracking` | `@/hooks/use-navigation-tracking` | Analytics | + +### 7.3 Componentes UI + +- `Button` (shadcn/ui) +- `DropdownMenu`, `DropdownMenuContent`, `DropdownMenuItem`, etc. (shadcn/ui) + +--- + +## 8. Test IDs (data-testid) + +Usados para testes automatizados: + +``` +bookmark-admin +bookmark-home +bookmark-agent +bookmark-xos-inbox +bookmark-automations +bookmark-insights +bookmark-compass +bookmark-production +bookmark-support +bookmark-erp +bookmark-retail +bookmark-plus +bookmark-fisco +bookmark-engineering +button-user-menu +button-logout +menu-development +``` + +--- + +## 9. Como Modificar + +### 9.1 Adicionar Novo Bookmark + +```tsx +// Em CompactNavigationBar, adicionar antes do
da área de bookmarks: + +
navigateTo("/nova-rota", "Nome")} + data-testid="bookmark-novo" +> +
+ +
+ Novo +
+``` + +### 9.2 Alterar Ordem + +Reorganizar os elementos `
` na ordem desejada dentro da área de bookmarks. + +### 9.3 Condicional por Permissão + +```tsx +{user?.role === "admin" && ( +
...
+)} + +// Ou por módulo permitido: +{(user as any)?.allowedModules?.includes("modulo") && ( +
...
+)} +``` + +--- + +## 10. Integração com Sistema + +### 10.1 Uso nas Páginas + +Todas as páginas que usam a navbar devem ser envolvidas por `BrowserFrame`: + +```tsx +// Exemplo: client/src/pages/Agent.tsx +import { BrowserFrame } from "@/components/Browser/BrowserFrame"; + +export default function Agent() { + return ( + +
+ {/* Conteúdo da página */} +
+
+ ); +} +``` + +### 10.2 Páginas que NÃO usam BrowserFrame + +- `AuthPage` (tela de login) +- `NotFound` (página 404) + +--- + +## 11. Considerações Técnicas + +### 11.1 Performance +- Code splitting via `React.lazy()` em todas as páginas +- Suspense com fallback de loading +- Debounce no rastreamento (2s) + +### 11.2 Acessibilidade +- `cursor-pointer` em todos os bookmarks +- Estrutura semântica do dropdown +- Contraste adequado nos ícones (branco sobre gradiente) + +### 11.3 Limitações Conhecidas +- Não há indicador visual de "página ativa" (design intencional) +- Scroll horizontal pode esconder itens em telas muito pequenas +- Não suporta drag-and-drop para reordenar + +### 11.4 Tratamento de Erros (Resolvido) + +**Problema:** Se uma página carregada via `React.lazy()` tivesse erro (import quebrado, sintaxe inválida), o `Suspense` ficava preso no estado de loading infinito. + +**Solução Implementada:** + +1. **ErrorBoundary** (`client/src/components/ErrorBoundary.tsx`): + - Captura erros em componentes filhos, incluindo falhas no lazy loading + - Exibe UI de fallback com detalhes do erro + - Botão "Tentar Novamente" para recarregar + +2. **LoadingFallback Aprimorado** (`client/src/App.tsx`): + - Após 3s: mostra mensagem "Isso está demorando mais que o esperado..." + - Após 10s: mostra alerta amarelo com botão de recarregar + +3. **Integração no Router**: +```tsx +function Router() { + return ( + {/* ← Captura erros */} + }> + + + {/* ... outras rotas ... */} + + + + ); +} +``` + +**Fluxo de Erro:** +``` +Usuário clica → lazy() falha → Suspense não resolve → ErrorBoundary captura + ↓ + LoadingFallback mostra + aviso após 3s/10s + ↓ + ErrorBoundary renderiza + UI de erro com retry +``` + +--- + +## 12. Diagrama de Sequência + +``` +┌────────┐ ┌─────────────┐ ┌──────────┐ ┌─────────┐ ┌──────────┐ +│ Usuário│ │ Navbar │ │ wouter │ │ React │ │ Backend │ +└───┬────┘ └──────┬──────┘ └────┬─────┘ └────┬────┘ └────┬─────┘ + │ │ │ │ │ + │──clica─────────▶│ │ │ │ + │ │ │ │ │ + │ │──trackPageView────────────────────│──────────────▶│ + │ │ │ │ │ + │ │──setLocation───▶│ │ │ + │ │ │ │ │ + │ │ │──atualiza URL──│ │ + │ │ │ │ │ + │ │ │◀─URL changed───│ │ + │ │ │ │ │ + │ │ │──lazy import──▶│ │ + │ │ │ │ │ + │ │◀────────────────│──chunk loaded──│ │ + │ │ │ │ │ + │ │──renderiza nova página────────────▶│ │ + │ │ │ │ │ + │◀────────────────│ │ │ │ + │ │ │ │ │ +``` + +--- + +## 13. Changelog + +| Versão | Data | Alterações | +|--------|------|------------| +| 1.0 | 2026-04-08 | Especificação inicial criada | +| 1.1 | 2026-04-08 | Adicionado tratamento de erros (ErrorBoundary + LoadingFallback com timeout) | + +--- + +## 14. Referências + +- **Arquivo Fonte:** `client/src/components/Browser/BrowserFrame.tsx` +- **Hook de Tracking:** `client/src/hooks/use-navigation-tracking.ts` +- **Rotas:** `client/src/App.tsx` +- **Autenticação:** `client/src/hooks/use-auth.tsx` +- **UI Components:** `client/src/components/ui/` + +--- + +**Nota para Agentes:** +> Ao modificar a navbar, mantenha a consistência visual com os bookmarks existentes. Sempre adicione `data-testid` para testes e atualize esta especificação se houver mudanças significativas na estrutura ou comportamento.