255 lines
12 KiB
JavaScript
255 lines
12 KiB
JavaScript
const express = require('express');
|
|
const router = express.Router();
|
|
const { requireAuth } = require('../middlewares/authMiddleware');
|
|
|
|
router.use(requireAuth);
|
|
|
|
router.get('/', async (req, res) => {
|
|
const actual_role = (req.user?.role || 'admin').toLowerCase();
|
|
const roleParam = (req.query.role || req.query.view_as || '').toLowerCase().trim();
|
|
const role = (['admin', 'bedelia', 'docente', 'alumno'].includes(roleParam) ? roleParam : actual_role) || 'admin';
|
|
|
|
let apiData = {};
|
|
let backendStats = {};
|
|
let audit_logs = [];
|
|
let monthly_data = [];
|
|
|
|
try {
|
|
const response = await req.apiClient.get('/dashboard/stats');
|
|
apiData = response.data || {};
|
|
backendStats = apiData.stats || {};
|
|
audit_logs = apiData.audit_logs || [];
|
|
monthly_data = apiData.monthly_data || [];
|
|
} catch (error) {
|
|
console.warn('Dashboard stats API notice:', error.message);
|
|
}
|
|
|
|
if (!monthly_data || monthly_data.length === 0) {
|
|
monthly_data = [
|
|
{ month: 'Ene', reservations: 120 }, { month: 'Feb', reservations: 180 }, { month: 'Mar', reservations: 250 },
|
|
{ month: 'Abr', reservations: 220 }, { month: 'May', reservations: 300 }, { month: 'Jun', reservations: 310 },
|
|
{ month: 'Jul', reservations: 200 }, { month: 'Ago', reservations: 350 }, { month: 'Sep', reservations: 420 },
|
|
{ month: 'Oct', reservations: 480 }, { month: 'Nov', reservations: 520 }, { month: 'Dic', reservations: 210 }
|
|
];
|
|
}
|
|
|
|
const total_classrooms = backendStats.total_classrooms ?? 31;
|
|
const total_subjects = backendStats.active_subjects ?? backendStats.total_subjects ?? 12;
|
|
const total_users = backendStats.total_active_users ?? backendStats.total_users ?? 8;
|
|
const today_reservations = backendStats.today_reservations ?? 19;
|
|
const pending_count = backendStats.pending_count ?? 3;
|
|
const physical_classrooms = backendStats.physical_classrooms ?? Math.max(1, total_classrooms - 4);
|
|
const virtual_classrooms_count = backendStats.virtual_classrooms ?? 4;
|
|
const occupancy_rate = backendStats.occupancy_rate ?? 68;
|
|
|
|
const assigned_classrooms = backendStats.assigned_classrooms ?? 26;
|
|
const assigned_classrooms_pct = backendStats.assigned_classrooms_pct ?? 96.3;
|
|
const unassigned_classrooms = backendStats.unassigned_classrooms ?? 1;
|
|
const unassigned_classrooms_pct = backendStats.unassigned_classrooms_pct ?? 3.7;
|
|
|
|
const assigned_today_classrooms = backendStats.assigned_today_classrooms ?? 0;
|
|
const assigned_today_pct = backendStats.assigned_today_pct ?? 0;
|
|
const unassigned_today_classrooms = backendStats.unassigned_today_classrooms ?? physical_classrooms;
|
|
const unassigned_today_pct = backendStats.unassigned_today_pct ?? 100;
|
|
|
|
const apiPending = Array.isArray(apiData.pending_reservations) && apiData.pending_reservations.length > 0
|
|
? apiData.pending_reservations
|
|
: null;
|
|
|
|
const apiTodaySchedule = Array.isArray(apiData.today_schedule_list) && apiData.today_schedule_list.length > 0
|
|
? apiData.today_schedule_list
|
|
: null;
|
|
|
|
const apiUpcoming = Array.isArray(apiData.upcoming_reservations) && apiData.upcoming_reservations.length > 0
|
|
? apiData.upcoming_reservations
|
|
: null;
|
|
|
|
const apiMilestones = Array.isArray(apiData.milestones) && apiData.milestones.length > 0
|
|
? apiData.milestones
|
|
: null;
|
|
|
|
// Consolidated and role-specific stats
|
|
const stats = {
|
|
total_classrooms,
|
|
total_subjects,
|
|
total_users,
|
|
today_reservations,
|
|
pending_count: apiPending ? apiPending.length : pending_count,
|
|
physical_classrooms,
|
|
virtual_classrooms: virtual_classrooms_count,
|
|
occupancy_rate,
|
|
assigned_classrooms,
|
|
assigned_classrooms_pct,
|
|
unassigned_classrooms,
|
|
unassigned_classrooms_pct,
|
|
assigned_today_classrooms,
|
|
assigned_today_pct,
|
|
unassigned_today_classrooms,
|
|
unassigned_today_pct,
|
|
my_today_count: apiTodaySchedule ? Math.min(3, apiTodaySchedule.length) : 2,
|
|
my_total_reservations: 8,
|
|
virtual_rooms_count: virtual_classrooms_count,
|
|
today_classes_count: apiTodaySchedule ? Math.min(4, apiTodaySchedule.length) : 2,
|
|
milestones_count: apiMilestones ? apiMilestones.length : 3
|
|
};
|
|
|
|
let role_data = {};
|
|
|
|
if (role === 'admin') {
|
|
role_data = {
|
|
recent_audits: audit_logs.length > 0 ? audit_logs : [
|
|
{ created_at: '2026-09-18 21:15', action_type: 'UPDATE_PERMISSION', user_email: 'admin@edu-space.com', ip_address: '127.0.0.1' },
|
|
{ created_at: '2026-09-18 20:30', action_type: 'CREATE_COMMISSION', user_email: 'admin@edu-space.com', ip_address: '127.0.0.1' },
|
|
{ created_at: '2026-09-18 19:45', action_type: 'LOGIN_SUCCESS', user_email: 'admin@edu-space.com', ip_address: '127.0.0.1' }
|
|
]
|
|
};
|
|
} else if (role === 'bedelia') {
|
|
role_data = {
|
|
pending_reservations: apiPending || [
|
|
{
|
|
id: 101,
|
|
start_time: '08:00',
|
|
end_time: '12:00',
|
|
classroom: { code: 'Aula 101' },
|
|
subject_name: 'Arquitectura de Computadoras',
|
|
user: { name: 'Prof. Gabriel Martínez' }
|
|
},
|
|
{
|
|
id: 102,
|
|
start_time: '14:00',
|
|
end_time: '18:00',
|
|
classroom: { code: 'Lab Informática 2' },
|
|
subject_name: 'Bases de Datos II',
|
|
user: { name: 'Dra. Laura Blanco' }
|
|
},
|
|
{
|
|
id: 103,
|
|
start_time: '18:30',
|
|
end_time: '22:00',
|
|
classroom: { code: 'Aula Magna' },
|
|
subject_name: 'Seminario de Ciberseguridad',
|
|
user: { name: 'Lic. Fernando Gómez' }
|
|
}
|
|
],
|
|
today_schedule_list: apiTodaySchedule || [
|
|
{ start_time: '08:00', end_time: '12:00', classroom: { code: 'Aula 102' }, subject_name: 'Algoritmos y Estructuras de Datos', user: { name: 'Prof. Ana Vega' } },
|
|
{ start_time: '08:30', end_time: '12:30', classroom: { code: 'Aula 204' }, subject_name: 'Matemática Discreta', user: { name: 'Prof. Carlos López' } },
|
|
{ start_time: '14:00', end_time: '18:00', classroom: { code: 'Lab Redes' }, subject_name: 'Redes y Telecomunicaciones', user: { name: 'Ing. Marcos Ruiz' } }
|
|
]
|
|
};
|
|
} else if (role === 'docente') {
|
|
const myClasses = apiTodaySchedule ? apiTodaySchedule.slice(0, 3) : null;
|
|
role_data = {
|
|
my_today_classes: myClasses || [
|
|
{
|
|
id: 201,
|
|
start_time: '08:30',
|
|
end_time: '12:00',
|
|
subject_name: 'Algoritmos y Estructuras de Datos',
|
|
subject_code: 'AED-102',
|
|
commission: { code: 'COM-A' },
|
|
classroom: { is_virtual: false, location_display: 'Piso 1 · Aula 104' },
|
|
virtual_link: ''
|
|
},
|
|
{
|
|
id: 202,
|
|
start_time: '18:30',
|
|
end_time: '22:00',
|
|
subject_name: 'Programación Concurrente y Distribuida',
|
|
subject_code: 'PCD-301',
|
|
commission: { code: 'COM-D' },
|
|
classroom: { is_virtual: true, location_display: 'Campus Virtual' },
|
|
virtual_link: 'https://meet.google.com/edu-space-pcd'
|
|
}
|
|
],
|
|
virtual_classrooms: [
|
|
{ code: 'Sala Virtual 1', link: 'https://meet.google.com/edu-space-v1' },
|
|
{ code: 'Sala Virtual 2', link: 'https://meet.google.com/edu-space-v2' }
|
|
]
|
|
};
|
|
} else if (role === 'alumno') {
|
|
const studentClasses = apiTodaySchedule ? apiTodaySchedule.slice(0, 4) : null;
|
|
role_data = {
|
|
today_classes: studentClasses || [
|
|
{
|
|
start_time: '08:00',
|
|
end_time: '12:00',
|
|
subject_name: 'Bases de Datos Relacionales',
|
|
subject_code: 'BDR-201',
|
|
commission: { code: 'COM-1' },
|
|
classroom: { is_virtual: false, location_display: 'Piso 2 · Laboratorio 3' },
|
|
virtual_link: ''
|
|
},
|
|
{
|
|
start_time: '18:30',
|
|
end_time: '22:30',
|
|
subject_name: 'Redes y Telecomunicaciones',
|
|
subject_code: 'RYT-302',
|
|
commission: { code: 'COM-2' },
|
|
classroom: { is_virtual: true, location_display: 'Campus Virtual (Google Meet)' },
|
|
virtual_link: 'https://meet.google.com/edu-space-demo'
|
|
}
|
|
],
|
|
upcoming_milestones: apiMilestones ? apiMilestones.map(m => ({
|
|
...m,
|
|
milestone_type: m.type_obj
|
|
})) : [
|
|
{
|
|
title: 'Primer Examen Parcial',
|
|
materia_codigo: 'BDR-201',
|
|
subject_name: 'Bases de Datos Relacionales',
|
|
date: '25/09/2026',
|
|
type_obj: { name: 'Examen Parcial', color: '#dc3545' },
|
|
milestone_type: { name: 'Examen Parcial', color: '#dc3545' },
|
|
notes: 'Traer DNI y calculadora',
|
|
classroom: 'Piso 2 · Aula 204'
|
|
},
|
|
{
|
|
title: 'Entrega TP Grupal Nº 2',
|
|
materia_codigo: 'AED-102',
|
|
subject_name: 'Algoritmos y Estructuras de Datos',
|
|
date: '30/09/2026',
|
|
type_obj: { name: 'Trabajo Práctico', color: '#198754' },
|
|
milestone_type: { name: 'Trabajo Práctico', color: '#198754' },
|
|
notes: 'Subir código al repositorio',
|
|
classroom: 'Campus Virtual',
|
|
link: 'https://campus.edu-space.com/tps'
|
|
},
|
|
{
|
|
title: 'Recuperatorio Parcial 1',
|
|
materia_codigo: 'RYT-302',
|
|
subject_name: 'Redes y Telecomunicaciones',
|
|
date: '08/10/2026',
|
|
type_obj: { name: 'Recuperatorio', color: '#fd7e14' },
|
|
milestone_type: { name: 'Recuperatorio', color: '#fd7e14' },
|
|
notes: 'Piso 2 · Aula 204',
|
|
classroom: 'Piso 2 · Aula 204'
|
|
}
|
|
]
|
|
};
|
|
}
|
|
|
|
const upcoming_reservations = apiUpcoming || [
|
|
{ start_time: '08:00', end_time: '12:00', date_display: '22/09', subject_name: 'Matemática Discreta', subject_code: 'MAT-101', shift: 'Mañana', commission: { code: 'COM-1' }, status: 'CONFIRMED', classroom: { code: 'Aula 101' }, user: { name: 'Prof. Carlos López' } },
|
|
{ start_time: '14:00', end_time: '18:00', date_display: '23/09', subject_name: 'Sistemas Operativos', subject_code: 'SO-202', shift: 'Tarde', commission: { code: 'COM-2' }, status: 'CONFIRMED', classroom: { code: 'Lab 2' }, user: { name: 'Dra. Ana Vega' } },
|
|
{ start_time: '18:30', end_time: '22:30', date_display: '24/09', subject_name: 'Ingeniería de Software', subject_code: 'IS-301', shift: 'Noche', commission: { code: 'COM-3' }, status: 'PENDING', classroom: { code: 'Aula Magna' }, user: { name: 'Ing. Marcos Ruiz' } }
|
|
];
|
|
|
|
res.render('dashboard', {
|
|
title: role === 'bedelia' ? 'Panel Operativo de Bedelía - Edu-Space Admin' :
|
|
role === 'docente' ? 'Panel Docente - Edu-Space Admin' :
|
|
role === 'alumno' ? 'Mi Portal de Cursada - Edu-Space Admin' :
|
|
'Panel de Administración - Edu-Space Admin',
|
|
user: req.user,
|
|
role,
|
|
actual_role,
|
|
stats,
|
|
role_data,
|
|
monthly_data,
|
|
audit_logs,
|
|
upcoming_reservations
|
|
});
|
|
});
|
|
|
|
module.exports = router;
|