- 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!
206 lines
8.2 KiB
JavaScript
206 lines
8.2 KiB
JavaScript
const express = require('express');
|
|
const router = express.Router();
|
|
const { query } = require('../config/database');
|
|
const { authenticateToken } = require('../middleware/auth');
|
|
|
|
// GET /api/dashboard - Estatísticas do dashboard
|
|
router.get('/', authenticateToken, async (req, res) => {
|
|
try {
|
|
const company_id = req.user.company_id;
|
|
console.log('GET /api/dashboard - Company ID:', company_id);
|
|
|
|
// Estatísticas de dispositivos
|
|
const devicesStats = await query(
|
|
`SELECT
|
|
COUNT(*) as total,
|
|
COUNT(*) FILTER (WHERE is_active = true) as active,
|
|
COUNT(*) FILTER (WHERE is_active = false) as inactive,
|
|
COUNT(*) FILTER (WHERE user_id IS NOT NULL) as assigned
|
|
FROM devices
|
|
WHERE company_id = $1`,
|
|
[company_id]
|
|
);
|
|
|
|
// Estatísticas de usuários
|
|
const usersStats = await query(
|
|
`SELECT
|
|
COUNT(*) as total,
|
|
COUNT(*) FILTER (WHERE is_active = true) as active,
|
|
COUNT(*) FILTER (WHERE is_active = false) as inactive
|
|
FROM users
|
|
WHERE company_id = $1`,
|
|
[company_id]
|
|
);
|
|
|
|
// Estatísticas de atividades (últimas 24 horas)
|
|
const activitiesStats = await query(
|
|
`SELECT
|
|
COUNT(*) as total_24h,
|
|
COUNT(*) FILTER (WHERE activity_type = 'idle') as idle_count,
|
|
COUNT(*) FILTER (WHERE activity_type = 'active') as active_count
|
|
FROM activities
|
|
WHERE company_id = $1
|
|
AND created_at >= NOW() - INTERVAL '24 hours'`,
|
|
[company_id]
|
|
);
|
|
|
|
// Estatísticas de chaves de ativação
|
|
const keysStats = await query(
|
|
`SELECT
|
|
COUNT(*) as total,
|
|
COUNT(*) FILTER (WHERE is_used = true) as used,
|
|
COUNT(*) FILTER (WHERE is_used = false) as available
|
|
FROM activation_keys
|
|
WHERE company_id = $1`,
|
|
[company_id]
|
|
);
|
|
|
|
// Dispositivos recentes (últimos 5)
|
|
const recentDevices = await query(
|
|
`SELECT d.*, u.name as user_name, u.email as user_email
|
|
FROM devices d
|
|
LEFT JOIN users u ON d.user_id = u.id
|
|
WHERE d.company_id = $1
|
|
ORDER BY d.created_at DESC
|
|
LIMIT 5`,
|
|
[company_id]
|
|
);
|
|
|
|
// Atividades recentes (últimas 10)
|
|
const recentActivities = await query(
|
|
`SELECT a.*, d.device_name, u.name as user_name
|
|
FROM activities a
|
|
LEFT JOIN devices d ON a.device_id = d.device_id
|
|
LEFT JOIN users u ON d.user_id = u.id
|
|
WHERE a.company_id = $1
|
|
ORDER BY a.created_at DESC
|
|
LIMIT 10`,
|
|
[company_id]
|
|
);
|
|
|
|
// Converter valores string para números
|
|
const devicesData = devicesStats.rows[0];
|
|
const usersData = usersStats.rows[0];
|
|
const activitiesData = activitiesStats.rows[0];
|
|
const keysData = keysStats.rows[0];
|
|
|
|
const response = {
|
|
success: true,
|
|
stats: {
|
|
devices: {
|
|
total: parseInt(devicesData.total) || 0,
|
|
active: parseInt(devicesData.active) || 0,
|
|
inactive: parseInt(devicesData.inactive) || 0,
|
|
assigned: parseInt(devicesData.assigned) || 0
|
|
},
|
|
users: {
|
|
total: parseInt(usersData.total) || 0,
|
|
active: parseInt(usersData.active) || 0,
|
|
inactive: parseInt(usersData.inactive) || 0
|
|
},
|
|
activities: {
|
|
total_24h: parseInt(activitiesData.total_24h) || 0,
|
|
idle_count: parseInt(activitiesData.idle_count) || 0,
|
|
active_count: parseInt(activitiesData.active_count) || 0
|
|
},
|
|
keys: {
|
|
total: parseInt(keysData.total) || 0,
|
|
used: parseInt(keysData.used) || 0,
|
|
available: parseInt(keysData.available) || 0
|
|
}
|
|
},
|
|
recent: {
|
|
devices: recentDevices.rows,
|
|
activities: recentActivities.rows
|
|
}
|
|
};
|
|
|
|
console.log('GET /api/dashboard - Resposta:', JSON.stringify(response, null, 2));
|
|
|
|
res.json(response);
|
|
} catch (error) {
|
|
console.error('Erro ao obter dados do dashboard:', error);
|
|
console.error('Erro detalhado:', error.message);
|
|
console.error('Stack:', error.stack);
|
|
res.status(500).json({ error: 'Erro ao obter dados do dashboard', details: error.message });
|
|
}
|
|
});
|
|
|
|
// 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;
|
|
|