diff --git a/FRONTEND_DASHBOARD.md b/FRONTEND_DASHBOARD.md new file mode 100644 index 0000000..afba751 --- /dev/null +++ b/FRONTEND_DASHBOARD.md @@ -0,0 +1,498 @@ +# 🎨 Dashboard Frontend - NoIdle (estilo JumpCloud) + +## πŸ“‹ VisΓ£o Geral + +Dashboard moderno e funcional para o NoIdle, inspirado no design do JumpCloud, com estatΓ­sticas em tempo real, alertas e notificaΓ§Γ΅es. + +--- + +## ✨ Componentes Criados + +### 1. **MainDashboard.jsx** (400 linhas) + +Componente principal do dashboard com: + +**EstatΓ­sticas Principais (Cards grandes):** +- πŸ‘₯ UsuΓ‘rios +- πŸ‘₯ Times +- πŸ’» Dispositivos +- πŸ›‘οΈ PolΓ­ticas MDM +- πŸ“Š Atividades Hoje + +**Cards de Alertas (8 cards):** +- ⚠️ Dispositivos Offline (7+ dias) +- ℹ️ Sem Atividade (24h) +- ❌ PolΓ­ticas Falhadas (24h) +- βœ… Comandos Pendentes +- πŸ”„ Precisam AtualizaΓ§Γ£o +- πŸ’Ύ EspaΓ§o em Disco Baixo +- 🟒 Online Agora +- πŸ‘€ UsuΓ‘rios Inativos (30d) + +**Status do Sistema:** +- API Backend +- Banco de Dados +- Sistema MDM +- Última AtualizaΓ§Γ£o + +**NotificaΓ§Γ΅es:** +- Dispositivos +- Atividade Recente + +--- + +## 🎨 Preview do Design + +``` +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ Bem-vindo, Admin! πŸ‘‹ [Atualizar] [ConfiguraΓ§Γ΅es] β”‚ +β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ +β”‚ β”‚ +β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”β”‚ +β”‚ β”‚ 77 β”‚ β”‚ 14 β”‚ β”‚ 143 β”‚ β”‚ 8 β”‚ β”‚ 1.2K β”‚β”‚ +β”‚ β”‚ UsuΓ‘rios β”‚ β”‚ Times β”‚ β”‚Disposit. β”‚ β”‚PolΓ­ticas β”‚ β”‚Ativid. β”‚β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜β”‚ +β”‚ β”‚ +β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ β”‚ [●] 0 β”‚ β”‚ [βœ“] 0 β”‚ β”‚ [!] 0 β”‚ β”‚ [i] 0 β”‚ β”‚ +β”‚ β”‚ Offline β”‚ β”‚Sem Ativ. β”‚ β”‚PolΓ­ticas β”‚ β”‚Comandos β”‚ β”‚ +β”‚ β”‚ 7+ dias β”‚ β”‚ 24h β”‚ β”‚ Falhadas β”‚ β”‚Pendentes β”‚ β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β”‚ β”‚ +β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ β”‚ [↑] 0 β”‚ β”‚ [!] 0 β”‚ β”‚ [βœ“] 45 β”‚ β”‚ [i] 0 β”‚ β”‚ +β”‚ β”‚Precisam β”‚ β”‚ Disco β”‚ β”‚ Online β”‚ β”‚Inativos β”‚ β”‚ +β”‚ β”‚Atualiz. β”‚ β”‚ Baixo β”‚ β”‚ Agora β”‚ β”‚ 30d β”‚ β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β”‚ β”‚ +β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ β”‚ Status do Sistema [βœ“] OK β”‚ β”‚ +β”‚ β”‚ βœ… Todos os serviΓ§os estΓ£o operacionais β”‚ β”‚ +β”‚ β”‚ β€’ API Backend: Online β”‚ β”‚ +β”‚ β”‚ β€’ Banco de Dados: Conectado β”‚ β”‚ +β”‚ β”‚ β€’ Sistema MDM: Ativo β”‚ β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β”‚ β”‚ +β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ β”‚ NotificaΓ§Γ΅es β”‚ β”‚ Atividade Recente β”‚ β”‚ +β”‚ β”‚ β€’ Offline 7+ dias: 12 β”‚ β”‚ β€’ Dispositivos online: 45 β”‚ β”‚ +β”‚ β”‚ β€’ Sem atividade: 8 β”‚ β”‚ β€’ PolΓ­ticas exec. hoje: - β”‚ β”‚ +β”‚ β”‚ β€’ Precisam update: 5 β”‚ β”‚ β€’ Comandos pendentes: 3 β”‚ β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +``` + +--- + +## πŸš€ ImplementaΓ§Γ£o + +### 1. Instalar DependΓͺncias + +```bash +cd /var/www/pointcontrol/frontend +npm install @mui/material @emotion/react @emotion/styled @mui/icons-material +``` + +### 2. Criar Estrutura de Pastas + +```bash +mkdir -p components/Dashboard +mkdir -p pages/dashboard +``` + +### 3. Copiar Componente + +Arquivo jΓ‘ criado: +- `frontend/components/Dashboard/MainDashboard.jsx` + +### 4. Criar PΓ‘gina Next.js + +```javascript +// pages/dashboard/index.js +import MainDashboard from '../../components/Dashboard/MainDashboard'; +import Layout from '../../components/Layout'; + +export default function DashboardPage() { + return ( + + + + ); +} +``` + +### 5. Atualizar NavegaΓ§Γ£o + +No menu lateral, adicionar link para `/dashboard`. + +--- + +## πŸ“Š API Endpoint + +### GET /api/dashboard/stats + +**Resposta:** +```json +{ + "success": true, + "stats": { + "users": 77, + "teams": 14, + "devices": 143, + "policies": 8, + "activities_today": 1234, + "devices_online": 45, + "devices_offline": 98, + "pending_commands": 3 + }, + "alerts": { + "devices_offline_7days": 12, + "devices_no_activity_24h": 8, + "failed_policies_24h": 2, + "devices_need_update": 5, + "low_disk_space": 1, + "inactive_users": 3 + } +} +``` + +--- + +## 🎨 CustomizaΓ§Γ£o de Cores + +### Paleta de Cores Usada: + +```javascript +const colors = { + primary: '#2563eb', // Azul - UsuΓ‘rios + secondary: '#7c3aed', // Roxo - Times + success: '#059669', // Verde - Dispositivos + danger: '#dc2626', // Vermelho - PolΓ­ticas + warning: '#f59e0b', // Laranja - Avisos + info: '#3b82f6', // Azul claro - InformaΓ§Γ΅es + orange: '#ea580c', // Laranja escuro - Atividades + gray: '#6b7280' // Cinza - Inativos +}; +``` + +### Customizar no Theme: + +```javascript +// theme.js +import { createTheme } from '@mui/material/styles'; + +const theme = createTheme({ + palette: { + primary: { + main: '#2563eb', + }, + secondary: { + main: '#7c3aed', + }, + success: { + main: '#10b981', + }, + error: { + main: '#dc2626', + }, + warning: { + main: '#f59e0b', + }, + info: { + main: '#3b82f6', + }, + }, + typography: { + fontFamily: '"Inter", "Roboto", "Helvetica", "Arial", sans-serif', + h4: { + fontWeight: 700, + }, + h6: { + fontWeight: 600, + }, + }, + shape: { + borderRadius: 8, + }, +}); + +export default theme; +``` + +--- + +## πŸ”„ AtualizaΓ§Γ£o em Tempo Real + +O componente atualiza automaticamente a cada 30 segundos: + +```javascript +useEffect(() => { + fetchDashboardData(); + const interval = setInterval(fetchDashboardData, 30000); + return () => clearInterval(interval); +}, []); +``` + +Para desabilitar: +```javascript +// Remover o setInterval +``` + +Para mudar frequΓͺncia: +```javascript +const interval = setInterval(fetchDashboardData, 60000); // 1 minuto +``` + +--- + +## πŸ“± Responsividade + +### Breakpoints Material-UI: + +```javascript +// xs: 0-600px (mobile) +// sm: 600-960px (tablet) +// md: 960-1280px (laptop) +// lg: 1280-1920px (desktop) +// xl: 1920px+ (large screens) +``` + +### Grid Responsivo: + +```javascript + // 5 cards em desktop, 2 em tablet, 1 em mobile + + + + // 4 cards em desktop, 2 em tablet, 1 em mobile + + +``` + +--- + +## πŸ§ͺ Testes + +### Testar Componente: + +```javascript +// __tests__/MainDashboard.test.js +import { render, screen, waitFor } from '@testing-library/react'; +import MainDashboard from '../components/Dashboard/MainDashboard'; + +global.fetch = jest.fn(() => + Promise.resolve({ + json: () => Promise.resolve({ + success: true, + stats: { + users: 77, + teams: 14, + devices: 143, + // ... + }, + alerts: { + devices_offline_7days: 12, + // ... + } + }) + }) +); + +test('renders dashboard with stats', async () => { + render(); + + await waitFor(() => { + expect(screen.getByText('77')).toBeInTheDocument(); + expect(screen.getByText('UsuΓ‘rios')).toBeInTheDocument(); + }); +}); +``` + +--- + +## 🎯 Funcionalidades Futuras + +### 1. GrΓ‘ficos e Charts +```javascript +import { LineChart, BarChart } from 'recharts'; + +// GrΓ‘fico de atividades dos ΓΊltimos 7 dias +// GrΓ‘fico de polΓ­ticas executadas +// GrΓ‘fico de dispositivos online vs offline +``` + +### 2. Filtros AvanΓ§ados +```javascript +// Filtrar por perΓ­odo (hoje, semana, mΓͺs) +// Filtrar por time +// Filtrar por status +``` + +### 3. AΓ§Γ΅es RΓ‘pidas +```javascript +// Executar polΓ­tica em dispositivos selecionados +// Enviar notificaΓ§Γ£o para usuΓ‘rios +// Gerar relatΓ³rio +``` + +### 4. NotificaΓ§Γ΅es em Tempo Real +```javascript +// WebSocket para notificaΓ§Γ΅es push +// Toast notifications +// Badge de contador +``` + +--- + +## πŸ“ Checklist de ImplementaΓ§Γ£o + +### Backend: +- [x] Criar rota `/api/dashboard/stats` +- [x] Queries para estatΓ­sticas +- [x] Queries para alertas +- [ ] Testar endpoint com Postman + +### Frontend: +- [x] Criar componente `MainDashboard.jsx` +- [ ] Criar pΓ‘gina `pages/dashboard/index.js` +- [ ] Adicionar ao menu de navegaΓ§Γ£o +- [ ] Configurar tema Material-UI +- [ ] Testar responsividade +- [ ] Adicionar loading states +- [ ] Adicionar error handling + +### IntegraΓ§Γ΅es: +- [ ] Conectar com sistema MDM +- [ ] Conectar com polΓ­ticas +- [ ] Conectar com dispositivos +- [ ] Conectar com usuΓ‘rios + +--- + +## πŸš€ Deploy + +### 1. Build Frontend: +```bash +cd /var/www/pointcontrol/frontend +npm run build +``` + +### 2. Start Production: +```bash +npm start +# ou +pm2 start npm --name "noidle-frontend" -- start +``` + +### 3. Configurar Proxy Nginx: +```nginx +location / { + proxy_pass http://localhost:3000; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host $host; + proxy_cache_bypass $http_upgrade; +} +``` + +--- + +## πŸ“– DocumentaΓ§Γ£o de Componentes + +### StatCard + +```javascript + +``` + +**Props:** +- `title` (string): TΓ­tulo do card +- `value` (number): Valor numΓ©rico +- `icon` (Component): Ícone Material-UI +- `color` (string): Cor do Γ­cone (hex) +- `link` (string): URL para navegaΓ§Γ£o + +### AlertCard + +```javascript + +``` + +**Props:** +- `title` (string): TΓ­tulo do alerta +- `value` (number): Valor numΓ©rico +- `icon` (Component): Ícone Material-UI +- `color` (string): Cor da barra lateral +- `link` (string): URL para ver detalhes + +--- + +## 🎨 Screenshots Esperados + +### Desktop (1920x1080): +- 5 cards de estatΓ­sticas principais em linha +- 4 cards de alertas por linha (2 linhas = 8 cards) +- 2 cards de notificaΓ§Γ΅es lado a lado + +### Tablet (768x1024): +- 2 cards de estatΓ­sticas por linha +- 2 cards de alertas por linha +- 2 cards de notificaΓ§Γ΅es empilhados + +### Mobile (375x667): +- 1 card por linha (empilhados) +- Scroll vertical + +--- + +## βœ… Status + +| Componente | Status | Progresso | +|------------|--------|-----------| +| MainDashboard.jsx | βœ… Criado | 100% | +| API /dashboard/stats | βœ… Criada | 100% | +| PΓ‘gina Next.js | ⏳ Pendente | 0% | +| Tema Material-UI | ⏳ Pendente | 0% | +| Testes | ⏳ Pendente | 0% | +| Deploy | ⏳ Pendente | 0% | + +--- + +## πŸ†˜ Troubleshooting + +### Erro: "Module not found: @mui/material" +```bash +npm install @mui/material @emotion/react @emotion/styled +``` + +### Erro: "fetch is not defined" (SSR) +```javascript +// Usar SWR ou adicionar polyfill +import 'isomorphic-fetch'; +``` + +### Cards nΓ£o responsivos +```javascript +// Verificar breakpoints do Grid + +``` + +--- + +**Dashboard Frontend Implementado e Pronto! πŸŽ‰** + +**PrΓ³ximo passo:** Criar a pΓ‘gina Next.js e integrar no menu. + diff --git a/backend/routes/dashboard.js b/backend/routes/dashboard.js index b684594..59f852d 100644 --- a/backend/routes/dashboard.js +++ b/backend/routes/dashboard.js @@ -126,5 +126,80 @@ router.get('/', authenticateToken, async (req, res) => { } }); +// GET /api/dashboard/stats - EstatΓ­sticas expandidas para o novo dashboard +router.get('/stats', async (req, res) => { + try { + // EstatΓ­sticas principais + const mainStats = await query(` + SELECT + (SELECT COUNT(*) FROM users) as users, + (SELECT COUNT(*) FROM teams) as teams, + (SELECT COUNT(*) FROM devices) as devices, + (SELECT COUNT(*) FROM policies WHERE enabled = true) as policies, + (SELECT COUNT(*) FROM activities WHERE DATE(created_at) = CURRENT_DATE) as activities_today, + (SELECT COUNT(*) FROM devices WHERE status = 'online') as devices_online, + (SELECT COUNT(*) FROM devices WHERE status = 'offline') as devices_offline, + (SELECT COUNT(*) FROM policy_commands WHERE status IN ('pending', 'sent')) as pending_commands + `); + + // Alertas e notificaΓ§Γ΅es + const alerts = await query(` + SELECT + (SELECT COUNT(*) FROM devices + WHERE last_seen < NOW() - INTERVAL '7 days' OR last_seen IS NULL) as devices_offline_7days, + + (SELECT COUNT(*) FROM devices d + LEFT JOIN activities a ON d.device_id = a.device_id + WHERE a.created_at IS NULL OR a.created_at < NOW() - INTERVAL '24 hours') as devices_no_activity_24h, + + (SELECT COUNT(*) FROM policy_executions + WHERE status = 'failed' + AND executed_at > NOW() - INTERVAL '24 hours') as failed_policies_24h, + + (SELECT COUNT(*) FROM devices + WHERE device_info->>'needs_update' = 'true') as devices_need_update, + + (SELECT COUNT(*) FROM devices + WHERE (device_info->>'disk_free_gb')::int < 10) as low_disk_space, + + (SELECT COUNT(*) FROM users + WHERE last_login < NOW() - INTERVAL '30 days' OR last_login IS NULL) as inactive_users + `); + + const stats = mainStats.rows[0]; + const alertsData = alerts.rows[0]; + + res.json({ + success: true, + stats: { + users: parseInt(stats.users) || 0, + teams: parseInt(stats.teams) || 0, + devices: parseInt(stats.devices) || 0, + policies: parseInt(stats.policies) || 0, + activities_today: parseInt(stats.activities_today) || 0, + devices_online: parseInt(stats.devices_online) || 0, + devices_offline: parseInt(stats.devices_offline) || 0, + pending_commands: parseInt(stats.pending_commands) || 0 + }, + alerts: { + devices_offline_7days: parseInt(alertsData.devices_offline_7days) || 0, + devices_no_activity_24h: parseInt(alertsData.devices_no_activity_24h) || 0, + failed_policies_24h: parseInt(alertsData.failed_policies_24h) || 0, + devices_need_update: parseInt(alertsData.devices_need_update) || 0, + low_disk_space: parseInt(alertsData.low_disk_space) || 0, + inactive_users: parseInt(alertsData.inactive_users) || 0 + } + }); + + } catch (error) { + console.error('Erro ao obter estatΓ­sticas do dashboard:', error); + res.status(500).json({ + success: false, + error: 'Erro ao obter estatΓ­sticas do dashboard', + details: error.message + }); + } +}); + module.exports = router;