268 lines
9.4 KiB
JavaScript
268 lines
9.4 KiB
JavaScript
const express = require('express');
|
|
const router = express.Router();
|
|
const { requireAuth } = require('../middlewares/authMiddleware');
|
|
const { createFormMock } = require('../utils/formHelper');
|
|
|
|
router.use(requireAuth);
|
|
|
|
// Fallback data si la API estuviera inaccesible (con capacidad física real)
|
|
const defaultBuildings = [
|
|
{
|
|
id: 1,
|
|
code: 'CENTRAL',
|
|
name: 'Edificio Central',
|
|
is_active: true,
|
|
is_virtual: false,
|
|
address: 'Tte. Gral. Juan Domingo Perón 802, CABA',
|
|
description: 'Sede central institucional de UniCABA. Aulas de grado, auditorio y laboratorios.',
|
|
floors: 4,
|
|
classrooms_count: 29,
|
|
physical_capacity: 1250,
|
|
total_capacity: 1250,
|
|
capacity_display: '1250'
|
|
},
|
|
{
|
|
id: 2,
|
|
code: 'VIRTUAL',
|
|
name: 'Campus Virtual',
|
|
is_active: true,
|
|
is_virtual: true,
|
|
address: 'campus.unicaba.edu.ar / MS Teams & Moodle',
|
|
description: 'Entorno de aprendizaje digital, aulas sincrónicas y videoconferencias.',
|
|
floors: 0,
|
|
classrooms_count: 2,
|
|
physical_capacity: 0,
|
|
total_capacity: 0,
|
|
capacity_display: 'Ilimitada (Virtual)'
|
|
}
|
|
];
|
|
|
|
// 1. List Buildings
|
|
const handleListBuildings = async (req, res) => {
|
|
try {
|
|
const { search = '', status = '' } = req.query;
|
|
let buildings = [];
|
|
let stats = {
|
|
total_buildings: 2,
|
|
total_classrooms: 31,
|
|
physical_classrooms_count: 29,
|
|
total_physical_capacity: 1250,
|
|
total_floors: 4
|
|
};
|
|
|
|
try {
|
|
const apiRes = await req.apiClient.get('/buildings', { params: { search, status } });
|
|
if (apiRes.data && apiRes.data.buildings) {
|
|
buildings = apiRes.data.buildings;
|
|
if (apiRes.data.stats) {
|
|
stats = apiRes.data.stats;
|
|
}
|
|
}
|
|
} catch (apiErr) {
|
|
console.warn('API /buildings notice, using fallback:', apiErr.message);
|
|
buildings = [...defaultBuildings];
|
|
}
|
|
|
|
// Si por alguna razón la lista vino vacía de la API y no había filtros de búsqueda, usar default
|
|
if (buildings.length === 0 && !search && !status) {
|
|
buildings = [...defaultBuildings];
|
|
}
|
|
|
|
// Asegurar campos calculados consistentes para cada edificio
|
|
buildings = buildings.map(b => {
|
|
const is_virtual = Boolean(b.is_virtual || (b.code && b.code.toUpperCase() === 'VIRTUAL') || (b.name && b.name.toUpperCase().includes('VIRTUAL')));
|
|
return {
|
|
...b,
|
|
is_virtual,
|
|
capacity_display: is_virtual ? 'Ilimitada (Virtual)' : String(b.physical_capacity !== undefined ? b.physical_capacity : (b.total_capacity || 0)),
|
|
total_capacity: is_virtual ? 0 : (b.physical_capacity !== undefined ? b.physical_capacity : (b.total_capacity || 0)),
|
|
floors_display: is_virtual ? 'Digital' : String(b.floors ?? 1)
|
|
};
|
|
});
|
|
|
|
// Capacidad física total: suma exclusiva de sedes presenciales (nunca suma 9999)
|
|
const total_physical_capacity = stats.total_physical_capacity || buildings
|
|
.filter(b => !b.is_virtual)
|
|
.reduce((acc, b) => acc + (b.physical_capacity || b.total_capacity || 0), 0);
|
|
|
|
res.render('buildings/list', {
|
|
title: 'Gestión de Edificios - Edu-Space Admin',
|
|
buildings,
|
|
total_classrooms: stats.total_classrooms || buildings.reduce((a, b) => a + (b.classrooms_count || 0), 0),
|
|
total_capacity: total_physical_capacity,
|
|
total_physical_capacity,
|
|
search,
|
|
status_filter: status
|
|
});
|
|
} catch (error) {
|
|
console.error('Error rendering buildings list:', error.message);
|
|
res.render('buildings/list', {
|
|
title: 'Gestión de Edificios - Edu-Space Admin',
|
|
buildings: defaultBuildings,
|
|
total_classrooms: 31,
|
|
total_capacity: 1250,
|
|
total_physical_capacity: 1250,
|
|
search: '',
|
|
status_filter: ''
|
|
});
|
|
}
|
|
};
|
|
|
|
router.get(['/', '/list', '/list_buildings'], handleListBuildings);
|
|
|
|
// 2. Add Building (GET & POST)
|
|
router.get(['/add', '/add_building'], (req, res) => {
|
|
const form = createFormMock({
|
|
name: '',
|
|
code: '',
|
|
address: '',
|
|
floors: 1,
|
|
description: '',
|
|
is_active: true
|
|
});
|
|
res.render('buildings/form', {
|
|
title: 'Nuevo Edificio - Edu-Space Admin',
|
|
building: null,
|
|
form
|
|
});
|
|
});
|
|
|
|
router.post(['/add', '/add_building'], async (req, res) => {
|
|
try {
|
|
const { name, code, address, floors, description, is_active } = req.body;
|
|
const payload = {
|
|
name: (name || '').trim(),
|
|
code: (code || '').trim().toUpperCase() || null,
|
|
address: (address || '').trim() || null,
|
|
floors: parseInt(floors, 10) || 1,
|
|
description: (description || '').trim() || null,
|
|
is_active: is_active === 'true' || is_active === 'on' || is_active === true || is_active === '1'
|
|
};
|
|
await req.apiClient.post('/buildings', payload);
|
|
res.redirect('/buildings/list_buildings?created=1');
|
|
} catch (err) {
|
|
console.warn('Error creating building:', err.response?.data || err.message);
|
|
res.redirect('/buildings/list_buildings?error=create');
|
|
}
|
|
});
|
|
|
|
// 3. View Building
|
|
router.get(['/view/:id', '/view_building', '/:id'], async (req, res) => {
|
|
const id = parseInt(req.params.id || req.query.id, 10) || 1;
|
|
let building = null;
|
|
let classrooms = [];
|
|
|
|
try {
|
|
const apiRes = await req.apiClient.get(`/buildings/${id}`);
|
|
if (apiRes.data && apiRes.data.building) {
|
|
building = apiRes.data.building;
|
|
classrooms = apiRes.data.classrooms || [];
|
|
}
|
|
} catch (e) {
|
|
building = defaultBuildings.find(b => b.id === id) || defaultBuildings[0];
|
|
}
|
|
|
|
if (!building) {
|
|
return res.redirect('/buildings/list_buildings');
|
|
}
|
|
|
|
const is_virtual = Boolean(building.is_virtual || (building.code && building.code.toUpperCase() === 'VIRTUAL') || (building.name && building.name.toUpperCase().includes('VIRTUAL')));
|
|
building = {
|
|
...building,
|
|
is_virtual,
|
|
capacity_display: is_virtual ? 'Ilimitada (Virtual)' : String(building.physical_capacity || building.total_capacity || 0),
|
|
floors_display: is_virtual ? 'Digital' : String(building.floors ?? 1)
|
|
};
|
|
|
|
res.render('buildings/view', {
|
|
title: `Edificio ${building.name} - Edu-Space Admin`,
|
|
building,
|
|
classrooms
|
|
});
|
|
});
|
|
|
|
// 4. Edit Building (GET)
|
|
const handleEditBuildingGet = async (req, res) => {
|
|
const id = parseInt(req.params.id || req.query.id, 10) || 1;
|
|
let building = null;
|
|
|
|
try {
|
|
const apiRes = await req.apiClient.get(`/buildings/${id}`);
|
|
if (apiRes.data && apiRes.data.building) {
|
|
building = apiRes.data.building;
|
|
}
|
|
} catch (e) {
|
|
building = defaultBuildings.find(b => b.id === id) || defaultBuildings[0];
|
|
}
|
|
|
|
if (!building) {
|
|
return res.redirect('/buildings/list_buildings');
|
|
}
|
|
|
|
const form = createFormMock({
|
|
name: building.name,
|
|
code: building.code,
|
|
address: building.address,
|
|
floors: building.floors,
|
|
description: building.description,
|
|
is_active: building.is_active
|
|
});
|
|
|
|
res.render('buildings/form', {
|
|
title: `Editar Edificio - ${building.name}`,
|
|
building,
|
|
form
|
|
});
|
|
};
|
|
|
|
router.get(['/edit/:id', '/:id/edit', '/edit_building'], handleEditBuildingGet);
|
|
|
|
// 5. Edit Building (POST)
|
|
const handleEditBuildingPost = async (req, res) => {
|
|
const id = parseInt(req.params.id || req.query.id || req.body.id, 10) || 1;
|
|
const { name, code, address, floors, description, is_active } = req.body;
|
|
|
|
try {
|
|
const payload = {
|
|
name: (name || '').trim(),
|
|
code: (code || '').trim().toUpperCase() || null,
|
|
address: (address || '').trim() || null,
|
|
floors: parseInt(floors, 10) || 0,
|
|
description: (description || '').trim() || null,
|
|
is_active: is_active === 'true' || is_active === 'on' || is_active === true || is_active === '1'
|
|
};
|
|
|
|
await req.apiClient.put(`/buildings/${id}`, payload);
|
|
res.redirect('/buildings/list_buildings?updated=1');
|
|
} catch (err) {
|
|
console.warn('Error updating building:', err.response?.data || err.message);
|
|
res.redirect('/buildings/list_buildings?error=update');
|
|
}
|
|
};
|
|
|
|
router.post(['/edit/:id', '/:id/edit', '/edit_building'], handleEditBuildingPost);
|
|
|
|
// 6. Toggle Building
|
|
router.post(['/toggle/:id', '/:id/toggle', '/toggle_building'], async (req, res) => {
|
|
const id = parseInt(req.params.id || req.query.id || req.body.id, 10) || 1;
|
|
try {
|
|
await req.apiClient.post(`/buildings/${id}/toggle`);
|
|
} catch (e) {
|
|
console.warn('Error toggling building:', e.message);
|
|
}
|
|
res.redirect('/buildings/list_buildings');
|
|
});
|
|
|
|
// 7. Delete Building
|
|
router.post(['/delete/:id', '/:id/delete', '/delete_building'], async (req, res) => {
|
|
const id = parseInt(req.params.id || req.query.id || req.body.id, 10);
|
|
try {
|
|
await req.apiClient.delete(`/buildings/${id}`);
|
|
} catch (e) {
|
|
console.warn('Error deleting building:', e.message);
|
|
}
|
|
res.redirect('/buildings/list_buildings');
|
|
});
|
|
|
|
module.exports = router;
|