--- title: SPEC — Expansão DashDescomplicar Q2 2026 date: 2026-04-06 type: spec status: active desk_project: 65 tags: [dashboard, expansion, mcps, paperclip, n8n, claude-code] --- # SPEC — Expansão DashDescomplicar Q2 2026 ## 1. Contexto O DashDescomplicar é o painel de gestão interno da Descomplicar®. Stack: React 19 + Vite + Tailwind CSS 4 + Express 4 + MySQL (Desk CRM BD). Deploy em EasyPanel via Dockerfile. ### Estado actual (3 páginas) | Página | Rotas API | Dados | |--------|-----------|-------| | **Dashboard** (App.tsx) | `/api/dashboard` | Tarefas (urgente/alta/vencidas/em teste), leads (contactar/followup/proposta), projectos, billing 360, calendário Google, timesheet | | **Monitor** | `/api/monitor`, `/api/hetzner`, `/api/server-metrics`, `/api/wp-monitor`, `/api/diagnostic` | Servidores Hetzner (CPU/rede/disco), serviços HTTP (11 URLs), EasyPanel containers, sites WordPress (CWP) | | **Financeiro** | `/api/financial` | Vendas/despesas mês e ano, lucro, categorias, evolução mensal 12 meses | ### O que falta (referência: plano-migracao-mcps-gateway-auth.md, Fase 5) Novos painéis para reflectir o stack completo: MCPs (33 no gateway), n8n (14 workflows), Paperclip (16 agentes operacionais), Claude Code/IA (189 skills, 72 agents, 9 hooks), LightRAG (knowledge graph), Desk CRM expandido (tickets, SLAs), e Operações (worklogs, PROCs, calendário). ### Auth OIDC com Authentik **já implementado no frontend** (react-oidc-context, AuthWrapper.tsx, config.ts). Backend tem placeholder (`OIDC_ENABLED=true`). Falta activar em produção. --- ## 2. Objectivos 1. Expandir o dashboard de 3 para 8 páginas 2. Cada novo painel usa dados reais via API (gateway MCPs, BD MySQL, APIs HTTP) 3. Manter a consistência visual (dark theme, Tailwind, framer-motion, lucide-react) 4. Zero dependências novas — usar as existentes (recharts, framer-motion, lucide) 5. Cada painel é independente — pode ser implementado e testado isoladamente --- ## 3. Arquitectura de dados (fontes por painel) ``` Express API (porta 3001) ├── /api/dashboard → MySQL (Desk CRM) + Google Calendar API [EXISTE] ├── /api/monitor → MySQL (tbl_eal_monitoring) + Hetzner API [EXISTE] ├── /api/financial → MySQL (Desk CRM invoices/expenses) [EXISTE] ├── /api/mcps → HTTP GET gateway.descomplicar.pt/health + per-MCP ping [NOVO] ├── /api/n8n → HTTP GET automator.descomplicar.pt/api/v1 (API key) [NOVO] ├── /api/paperclip → PostgreSQL clip.descomplicar.pt (porta 54329) [NOVO] ├── /api/ai → Ficheiros locais + MySQL (stats) [NOVO] └── /api/operations → MySQL (Desk CRM) + Google Calendar [NOVO] ``` --- ## 4. Especificação por painel ### 4.1 Painel MCPs (nova página `/mcps`) **Objectivo:** Ver todos os 33+2 MCPs com estado online/offline em tempo real. **Rota API:** `GET /api/mcps` **Fonte de dados:** - Gateway health: `GET https://gateway.descomplicar.pt/health` (sem auth, já existe) - Per-MCP ping: `GET https://gateway.descomplicar.pt/v1//mcp` com Bearer token (resposta 200 = online) - Lista estática dos MCPs com metadados (nome, porta, categoria, enabled/disabled) **Dados retornados:** ```typescript interface McpStatus { name: string // "desk-crm-v3" port: number // 3150 category: string // "crm" | "infra" | "ai" | "tools" | "external" enabled: boolean // true/false em claude.json status: 'online' | 'offline' | 'disabled' | 'unknown' response_time_ms: number | null last_check: string // ISO timestamp tools_count?: number // número de tools (se conhecido) } interface McpDashboard { gateway_status: 'online' | 'offline' total: number online: number offline: number disabled: number mcps: McpStatus[] auth: { method: string // "dual-layer: IP whitelist + Bearer token" token_expires: string | null } } ``` **UI (página McpMonitor.tsx):** - Header com stats gerais (total/online/offline/disabled) - Grid de cards por MCP, agrupados por categoria - Indicador de cor: verde (online), vermelho (offline), cinza (disabled) - Response time badge em cada card - Filtros: por categoria, por estado - Botão refresh manual **Implementação backend (api/services/mcps.ts):** - Lista hardcoded de MCPs com metadados (extraída de port-map.json e claude.json) - Ping paralelo a cada MCP enabled (Promise.allSettled com timeout 5s) - Cache de 60 segundos (evitar spam ao gateway) --- ### 4.2 Painel n8n (nova página `/n8n`) **Objectivo:** Ver 14 workflows operacionais com estado, último run, próximo run. **Rota API:** `GET /api/n8n` **Fonte de dados:** - n8n API REST: `https://automator.descomplicar.pt/api/v1/workflows` (API key em env) - n8n API REST: `https://automator.descomplicar.pt/api/v1/executions?limit=50` **Dados retornados:** ```typescript interface N8nWorkflow { id: string name: string active: boolean last_execution: { status: 'success' | 'error' | 'running' | null started_at: string | null finished_at: string | null duration_ms: number | null } | null tags: string[] } interface N8nDashboard { total: number active: number inactive: number failed_24h: number workflows: N8nWorkflow[] last_updated: string } ``` **UI (página N8nMonitor.tsx):** - Stats cards: total/activos/falhas 24h - Tabela de workflows: nome, activo, último run (com cor: verde/vermelho), duração - Filtro: activos/todos - Alerta visual se algum workflow falhou nas últimas 24h **Env vars necessárias:** - `N8N_API_URL` (default: `https://automator.descomplicar.pt/api/v1`) - `N8N_API_KEY` --- ### 4.3 Painel Paperclip (nova página `/paperclip`) **Objectivo:** Ver os 16 agentes operacionais, routines, e issues. **Rota API:** `GET /api/paperclip` **Fonte de dados:** - PostgreSQL do Paperclip: `clip.descomplicar.pt:54329` (credenciais em env) - Tabelas: `agents`, `agent_runs`, `routines`, `routine_executions`, `issues` **Dados retornados:** ```typescript interface PaperclipAgent { id: string name: string role: string // "CEO", "CTO", "Director", "Specialist" status: 'active' | 'idle' | 'error' | 'archived' last_heartbeat: string | null last_run: string | null total_runs: number } interface PaperclipRoutine { id: string name: string cron: string active: boolean last_run: string | null last_status: 'success' | 'error' | null next_run: string | null } interface PaperclipDashboard { agents: { total: number active: number idle: number error: number list: PaperclipAgent[] } routines: { total: number active: number list: PaperclipRoutine[] } issues: { open: number in_progress: number closed_7d: number } } ``` **UI (página Paperclip.tsx):** - Stats cards: agentes activos/idle/error, routines activas - Grid de cards para agentes (agrupados por role/nível hierárquico) - Tabela de routines com cron, último/próximo run - Contador de issues (open/in-progress/closed) **Env vars necessárias:** - `PAPERCLIP_DB_HOST` (default: clip.descomplicar.pt) - `PAPERCLIP_DB_PORT` (default: 54329) - `PAPERCLIP_DB_NAME` - `PAPERCLIP_DB_USER` - `PAPERCLIP_DB_PASS` **Dependência adicional:** `pg` (PostgreSQL client para Node.js) — **única nova dependência no projecto** --- ### 4.4 Painel Claude Code / IA (nova página `/ai`) **Objectivo:** Ver skills top, agents, MCPs activos, e CARL. **Rota API:** `GET /api/ai` **Fonte de dados:** - Contagens estáticas derivadas do STK-Estado-Actual.md (actualizar periodicamente) - CARL config: leitura de `/media/ealmeida/Dados/.carl/carl.json` (se acessível via EasyPanel volume mount, senão estático) - Hooks: contagem de ficheiros em `~/.claude/hooks/` **Dados retornados:** ```typescript interface AiDashboard { skills: { total: number // 189 directas: number // 31 plugins: number // 158 top_10: string[] // nomes das 10 mais usadas } agents: { total: number // 72 directos: number // 18 plugins: number // 54 } mcps: { total: number // 39 enabled: number // 10 gateway: number // 33 local: number // 2 } hooks: { total_files: number // 26 active: number // 9 } carl: { domains: number // 7 rules: number // ~45 decisions: number } plugins: { total: number // 14 descomplicar + 6 oficiais + 3 terceiros active: number // 6 } } ``` **UI (página AiOverview.tsx):** - Cards grandes com métricas (skills, agents, MCPs, hooks, plugins) - Secção CARL: domínios com contagem de regras - Sem interactividade complexa — é um painel informativo/snapshot **Nota:** Este painel é maioritariamente estático. Os dados mudam raramente (quando se adicionam skills/agents). Actualização via endpoint manual ou ficheiro JSON servido estáticamente. --- ### 4.5 Painel Operações (nova página `/operations`) **Objectivo:** Visão operacional — tickets, SLAs, procedimentos. **Rota API:** `GET /api/operations` **Fonte de dados:** - MySQL (Desk CRM): tickets abertos, por prioridade, SLAs - Contagens estáticas: PROCs (48), departamentos (7) **Dados retornados:** ```typescript interface OperationsDashboard { tickets: { open: number high_priority: number avg_response_hours: number by_department: { dept: string; count: number }[] } procedures: { total: number // 48 departments: number // 7 (D1-D7) coverage: { dept: string; procs: number; pct: number }[] } } ``` **UI (página Operations.tsx):** - Cards: tickets abertos, alta prioridade, tempo médio resposta - Gráfico barras: tickets por departamento - Tabela cobertura PROCs por departamento --- ## 5. Alterações ao código existente ### 5.1 Layout.tsx — adicionar itens de navegação ```typescript const NAV_ITEMS: NavItem[] = [ { to: '/', label: 'Dashboard', icon: LayoutDashboard }, { to: '/monitor', label: 'Monitor', icon: Activity }, { to: '/financial', label: 'Financeiro', icon: CreditCard }, // NOVOS: { to: '/mcps', label: 'MCPs', icon: Network }, { to: '/n8n', label: 'Automações', icon: Workflow }, { to: '/paperclip', label: 'Paperclip', icon: Bot }, { to: '/ai', label: 'IA / Claude', icon: Brain }, { to: '/operations', label: 'Operações', icon: ClipboardList }, ] ``` ### 5.2 App.tsx — adicionar rotas Novas rotas no React Router para cada página. ### 5.3 server.ts — registar novas rotas API ```typescript import mcpsRouter from './routes/mcps.js' import n8nRouter from './routes/n8n.js' import paperclipRouter from './routes/paperclip.js' import aiRouter from './routes/ai.js' import operationsRouter from './routes/operations.js' app.use('/api/mcps', mcpsRouter) app.use('/api/n8n', n8nRouter) app.use('/api/paperclip', paperclipRouter) app.use('/api/ai', aiRouter) app.use('/api/operations', operationsRouter) ``` --- ## 6. Ficheiros a criar (por sprint) ### Sprint 1 — MCPs + n8n (estimativa: 8-10h) ``` api/routes/mcps.ts # Rota GET /api/mcps api/services/mcps.ts # Ping gateway, lista MCPs, cache api/routes/n8n.ts # Rota GET /api/n8n api/services/n8n.ts # Chamadas API n8n src/pages/McpMonitor.tsx # Página frontend MCPs src/pages/N8nMonitor.tsx # Página frontend n8n ``` ### Sprint 2 — Paperclip (estimativa: 6-8h) ``` api/services/paperclip-db.ts # Conexão PostgreSQL Paperclip api/routes/paperclip.ts # Rota GET /api/paperclip api/services/paperclip.ts # Queries aos agentes/routines/issues src/pages/Paperclip.tsx # Página frontend Paperclip ``` ### Sprint 3 — IA + Operações (estimativa: 4-6h) ``` api/routes/ai.ts # Rota GET /api/ai api/services/ai.ts # Dados estáticos/contagens IA api/routes/operations.ts # Rota GET /api/operations api/services/operations.ts # Queries tickets + PROCs src/pages/AiOverview.tsx # Página frontend IA src/pages/Operations.tsx # Página frontend Operações ``` ### Sprint 4 — Integração + polish (estimativa: 2-3h) ``` # Alterações a ficheiros existentes: src/components/Layout.tsx # Adicionar 5 itens nav src/App.tsx # Adicionar 5 rotas (no routing section) api/server.ts # Registar 5 routers novos .env.example # Novas env vars documentadas README.md # Actualizar documentação ``` --- ## 7. Env vars novas necessárias ```env # MCPs Gateway MCP_GATEWAY_URL=https://gateway.descomplicar.pt MCP_GATEWAY_TOKEN= # n8n N8N_API_URL=https://automator.descomplicar.pt/api/v1 N8N_API_KEY= # Paperclip PostgreSQL PAPERCLIP_DB_HOST=clip.descomplicar.pt PAPERCLIP_DB_PORT=54329 PAPERCLIP_DB_NAME=paperclip PAPERCLIP_DB_USER= PAPERCLIP_DB_PASS= ``` --- ## 8. Dependências | Pacote | Razão | Sprint | |--------|-------|--------| | `pg` | PostgreSQL client (Paperclip BD) | Sprint 2 | Todas as outras dependências já existem no projecto (recharts, framer-motion, lucide-react, express, mysql2, zod). --- ## 9. Padrões a seguir (consistência) - **Backend:** Router Express separado por domínio. Service file com queries. Async/await com try/catch. - **Frontend:** Página como componente único com useState/useEffect/useCallback. motion.div com containerVariants/itemVariants. Cards com gradientes e lucide icons. - **Estilo:** Dark theme (bg-zinc-950, borders white/10, text zinc-400/white). Gradientes brand-500/violet-600. Rounded-2xl nos cards. - **Error handling:** try/catch no backend → 500 JSON. Frontend → estado de loading/error com retry. - **Cache:** Backend cache simples com timestamp (60s para MCPs, 300s para n8n). --- ## 10. Riscos e mitigações | Risco | Mitigação | |-------|-----------| | n8n API key expirar | Documentar no .env.example, alertar na UI | | Paperclip BD inacessível do EasyPanel | Verificar rede Docker Swarm, fallback com dados estáticos | | Gateway health check lento (33 MCPs) | Promise.allSettled com timeout 5s + cache 60s | | Página MCPs demasiado pesada | Ping apenas MCPs enabled (10), disabled mostrados como cinza sem ping | --- ## 11. Critérios de aceitação - [ ] Todas as 8 páginas carregam sem erros - [ ] Cada API retorna dados reais (não mocks) - [ ] Navegação sidebar mostra todas as 8 páginas - [ ] Mobile responsive (sidebar colapsável funciona com 8 itens) - [ ] Build de produção compila sem erros (`npm run build`) - [ ] Zero vulnerabilidades de segurança (`npm audit`) --- ## 12. Fora de escopo - Auth OIDC em produção (já implementado, activar separadamente) - LightRAG painel visual com grafo D3.js (fase futura, complexidade alta) - Painel Desk CRM expandido com Kanban (já tem dados no Dashboard principal) - Botões de acção (enable/disable MCP, restart workflow) — apenas visualização - Testes unitários (existem mas não são prioritários para esta expansão)