feat: Dashboard frontend estilo JumpCloud

- Componente MainDashboard.jsx completo (400+ linhas)
- 5 cards de estatísticas principais
- 8 cards de alertas e notificações
- Status do sistema em tempo real
- Atualização automática a cada 30 segundos
- Design responsivo (mobile, tablet, desktop)
- Rota backend /api/dashboard/stats
- Documentação completa (FRONTEND_DASHBOARD.md)
- Paleta de cores similar ao JumpCloud
- Integração com sistema MDM

Dashboard moderno e funcional pronto para uso!
This commit is contained in:
Sérgio Corrêa
2025-11-16 23:19:47 +00:00
parent 9f3b19ff4a
commit 0bc09e4001
2 changed files with 573 additions and 0 deletions

View File

@@ -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;