feat: adicionar 5 novos painéis ao dashboard (MCPs, n8n, Paperclip, IA, Operações)
Expansão do dashboard de 3 para 8 páginas com dados reais do stack: - MCPs: monitorização de 33 MCPs no gateway com ping e estado online/offline - n8n: 14 workflows com último run, duração e falhas 24h - Paperclip: 16 agentes operacionais, routines e issues (PostgreSQL) - IA/Claude: visão das 3 camadas (189 skills, 72 agents, 39 MCPs, CARL) - Operações: tickets Desk CRM por departamento + cobertura PROCs 16 ficheiros novos (3042 linhas), 3 existentes editados. Nova dependência: pg (PostgreSQL client para Paperclip). Audit: 0 vulnerabilidades (npm audit fix aplicado). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,473 @@
|
||||
---
|
||||
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/<nome>/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=<authentik-bearer-token>
|
||||
|
||||
# n8n
|
||||
N8N_API_URL=https://automator.descomplicar.pt/api/v1
|
||||
N8N_API_KEY=<n8n-api-key>
|
||||
|
||||
# Paperclip PostgreSQL
|
||||
PAPERCLIP_DB_HOST=clip.descomplicar.pt
|
||||
PAPERCLIP_DB_PORT=54329
|
||||
PAPERCLIP_DB_NAME=paperclip
|
||||
PAPERCLIP_DB_USER=<user>
|
||||
PAPERCLIP_DB_PASS=<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)
|
||||
Reference in New Issue
Block a user