feat: implementar ABM completo de comisiones, carreras y asignaturas con proteccion de integridad y corregir edificios y campus virtual
This commit is contained in:
@@ -652,20 +652,13 @@ const handleCareersList = async (req, res) => {
|
||||
console.warn('Careers list API notice:', apiErr.message);
|
||||
}
|
||||
|
||||
if (careers.length === 0) {
|
||||
careers = [
|
||||
{ id: 1, name: 'Licenciatura en Sistemas', code: 'LIC-SIS', description: 'Formación en ingeniería de software y sistemas.' },
|
||||
{ id: 2, name: 'Tecnicatura en Desarrollo de Software', code: 'TEC-DS', description: 'Desarrollo web y aplicaciones.' }
|
||||
];
|
||||
stats[1] = { subjects_count: 8, commissions_count: 12 };
|
||||
stats[2] = { subjects_count: 6, commissions_count: 8 };
|
||||
}
|
||||
|
||||
res.render('admin/careers/list', {
|
||||
title: 'Gestión de Carreras - Edu-Space Admin',
|
||||
careers,
|
||||
stats,
|
||||
search,
|
||||
msg: req.query.msg || '',
|
||||
error: req.query.error || '',
|
||||
can_edit: true
|
||||
});
|
||||
} catch (err) {
|
||||
@@ -678,10 +671,12 @@ const handleCareerAdd = async (req, res) => {
|
||||
const { name, code, description } = req.body;
|
||||
try {
|
||||
await req.apiClient.post('/careers', { name, code, description });
|
||||
res.redirect('/admin/careers_list?msg=created');
|
||||
} catch (e) {
|
||||
console.warn('Career add API notice:', e.response?.data || e.message);
|
||||
const errCode = e.response?.status === 409 ? 'conflict' : 'create';
|
||||
res.redirect(`/admin/careers_list?error=${errCode}`);
|
||||
}
|
||||
res.redirect('/admin/careers_list');
|
||||
};
|
||||
|
||||
const handleCareerEdit = async (req, res) => {
|
||||
@@ -689,20 +684,24 @@ const handleCareerEdit = async (req, res) => {
|
||||
const { name, code, description } = req.body;
|
||||
try {
|
||||
await req.apiClient.put(`/careers/${id}`, { name, code, description });
|
||||
res.redirect('/admin/careers_list?msg=updated');
|
||||
} catch (e) {
|
||||
console.warn('Career edit API notice:', e.response?.data || e.message);
|
||||
const errCode = e.response?.status === 409 ? 'conflict' : 'edit';
|
||||
res.redirect(`/admin/careers_list?error=${errCode}`);
|
||||
}
|
||||
res.redirect('/admin/careers_list');
|
||||
};
|
||||
|
||||
const handleCareerDelete = async (req, res) => {
|
||||
const id = req.params.id || req.body.id;
|
||||
try {
|
||||
await req.apiClient.delete(`/careers/${id}`);
|
||||
res.redirect('/admin/careers_list?msg=deleted');
|
||||
} catch (e) {
|
||||
console.warn('Career delete API notice:', e.response?.data || e.message);
|
||||
const errCode = e.response?.status === 409 ? 'has_subjects' : 'delete';
|
||||
res.redirect(`/admin/careers_list?error=${errCode}`);
|
||||
}
|
||||
res.redirect('/admin/careers_list');
|
||||
};
|
||||
|
||||
router.get(['/careers_list', '/careers', '/career_list'], handleCareersList);
|
||||
@@ -767,10 +766,12 @@ const handleSubjectAdd = async (req, res) => {
|
||||
description,
|
||||
is_active: is_active === 'on' || is_active === 'true' || is_active === true
|
||||
});
|
||||
res.redirect('/admin/subjects_list?msg=created');
|
||||
} catch (e) {
|
||||
console.warn('Subject add API notice:', e.response?.data || e.message);
|
||||
const errCode = e.response?.status === 409 ? 'conflict' : 'create';
|
||||
res.redirect(`/admin/subjects_list?error=${errCode}`);
|
||||
}
|
||||
res.redirect('/admin/subjects_list');
|
||||
};
|
||||
|
||||
const handleSubjectEdit = async (req, res) => {
|
||||
@@ -786,20 +787,23 @@ const handleSubjectEdit = async (req, res) => {
|
||||
description,
|
||||
is_active: is_active === 'on' || is_active === 'true' || is_active === true
|
||||
});
|
||||
res.redirect('/admin/subjects_list?msg=updated');
|
||||
} catch (e) {
|
||||
console.warn('Subject edit API notice:', e.response?.data || e.message);
|
||||
const errCode = e.response?.status === 409 ? 'conflict' : 'edit';
|
||||
res.redirect(`/admin/subjects_list?error=${errCode}`);
|
||||
}
|
||||
res.redirect('/admin/subjects_list');
|
||||
};
|
||||
|
||||
const handleSubjectToggle = async (req, res) => {
|
||||
const id = req.params.id || req.body.id;
|
||||
try {
|
||||
await req.apiClient.post(`/subjects/${id}/toggle`);
|
||||
res.redirect('/admin/subjects_list?msg=toggled');
|
||||
} catch (e) {
|
||||
console.warn('Subject toggle API notice:', e.message);
|
||||
res.redirect('/admin/subjects_list?error=toggle');
|
||||
}
|
||||
res.redirect('/admin/subjects_list');
|
||||
};
|
||||
|
||||
const handleSubjectDelete = async (req, res) => {
|
||||
@@ -888,6 +892,9 @@ const handleCommissionsList = async (req, res) => {
|
||||
shift_filter: shift,
|
||||
active_filter: active,
|
||||
created,
|
||||
msg: req.query.msg || (created ? 'created' : ''),
|
||||
error: req.query.error || '',
|
||||
can_edit: true,
|
||||
pagination: { pages: 1, page: 1, total: commissions.length, has_prev: false, has_next: false }
|
||||
});
|
||||
} catch (err) {
|
||||
@@ -1019,31 +1026,60 @@ const handleCommissionAdd = async (req, res) => {
|
||||
active: true
|
||||
};
|
||||
await req.apiClient.post('/commissions', payload);
|
||||
res.redirect('/admin/commissions_list?created=1');
|
||||
res.redirect('/admin/commissions_list?msg=created');
|
||||
} catch (e) {
|
||||
console.warn('Commission add API notice:', e.response?.data || e.message);
|
||||
res.redirect('/admin/commissions_list');
|
||||
res.redirect('/admin/commissions_list?error=create');
|
||||
}
|
||||
};
|
||||
|
||||
const handleCommissionEdit = async (req, res) => {
|
||||
const id = req.params.id || req.body.id;
|
||||
try {
|
||||
await req.apiClient.put(`/commissions/${id}`, req.body);
|
||||
const { subject_id, code, semester, year, shift, teacher_id, max_students, schedule, virtual_link, academic_term_id, active } = req.body;
|
||||
const payload = {
|
||||
code: (code || '').trim(),
|
||||
semester: semester || '1C',
|
||||
year: year ? parseInt(year, 10) : 2026,
|
||||
shift: shift || 'Mañana',
|
||||
teacher_id: teacher_id ? parseInt(teacher_id, 10) : null,
|
||||
max_students: max_students ? parseInt(max_students, 10) : 40,
|
||||
schedule: (schedule || '').trim(),
|
||||
virtual_link: (virtual_link || '').trim()
|
||||
};
|
||||
if (subject_id) payload.subject_id = parseInt(subject_id, 10);
|
||||
if (academic_term_id) payload.academic_term_id = parseInt(academic_term_id, 10);
|
||||
if (active !== undefined) payload.active = (active === 'on' || active === 'true' || active === true);
|
||||
|
||||
await req.apiClient.put(`/commissions/${id}`, payload);
|
||||
res.redirect('/admin/commissions_list?msg=updated');
|
||||
} catch (e) {
|
||||
console.warn('Commission edit API notice:', e.response?.data || e.message);
|
||||
res.redirect('/admin/commissions_list?error=edit');
|
||||
}
|
||||
res.redirect('/admin/commissions_list');
|
||||
};
|
||||
|
||||
const handleCommissionToggle = async (req, res) => {
|
||||
const id = req.params.id || req.body.id;
|
||||
try {
|
||||
await req.apiClient.post(`/commissions/${id}/toggle`);
|
||||
res.redirect('/admin/commissions_list?msg=toggled');
|
||||
} catch (e) {
|
||||
console.warn('Commission toggle API notice:', e.message);
|
||||
res.redirect('/admin/commissions_list?error=toggle');
|
||||
}
|
||||
};
|
||||
|
||||
const handleCommissionDelete = async (req, res) => {
|
||||
const id = req.params.id || req.body.id;
|
||||
try {
|
||||
const delRes = await req.apiClient.delete(`/commissions/${id}`);
|
||||
const action = delRes.data?.action || 'deleted';
|
||||
res.redirect(`/admin/commissions_list?msg=${action}`);
|
||||
} catch (e) {
|
||||
console.warn('Commission delete API notice:', e.response?.data || e.message);
|
||||
res.redirect('/admin/commissions_list?error=delete');
|
||||
}
|
||||
res.redirect('/admin/commissions_list');
|
||||
};
|
||||
|
||||
const handleCommissionAssignTeacher = async (req, res) => {
|
||||
@@ -1132,6 +1168,7 @@ router.get(['/commission_detail', '/commission_detail/:id', '/commissions/:id'],
|
||||
router.post(['/commission_add', '/commissions/add'], handleCommissionAdd);
|
||||
router.post(['/commission_edit', '/commission_edit/:id'], handleCommissionEdit);
|
||||
router.post(['/commission_toggle', '/commission_toggle/:id'], handleCommissionToggle);
|
||||
router.post(['/commission_delete', '/commission_delete/:id', '/commissions/:id/delete'], handleCommissionDelete);
|
||||
router.post(['/commission_assign_teacher', '/commission_assign_teacher/:id'], handleCommissionAssignTeacher);
|
||||
router.post(['/commission_add_teacher', '/commissions/:id/teachers', '/commissions/teachers/add'], handleCommissionAddTeacher);
|
||||
router.post(['/commission_remove_teacher', '/commissions/:id/teachers/delete', '/commissions/teachers/remove'], handleCommissionRemoveTeacher);
|
||||
|
||||
@@ -5,112 +5,120 @@ const { createFormMock } = require('../utils/formHelper');
|
||||
|
||||
router.use(requireAuth);
|
||||
|
||||
// Mock storage of buildings to support CRUD in memory/BFF layer
|
||||
let buildingsData = [
|
||||
// Fallback data si la API estuviera inaccesible (con capacidad física real)
|
||||
const defaultBuildings = [
|
||||
{
|
||||
id: 1,
|
||||
code: 'CENTRAL',
|
||||
name: 'Edificio Central',
|
||||
is_active: true,
|
||||
address: 'Av. Paseo Colón 255, CABA',
|
||||
description: 'Sede académica principal, aulas de grado, laboratorios y auditorio.',
|
||||
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,
|
||||
total_capacity: 1250
|
||||
physical_capacity: 1250,
|
||||
total_capacity: 1250,
|
||||
capacity_display: '1250'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
code: 'VIRTUAL',
|
||||
name: 'Campus Virtual',
|
||||
is_active: true,
|
||||
address: 'campus.unicaba.edu.ar',
|
||||
is_virtual: true,
|
||||
address: 'campus.unicaba.edu.ar / MS Teams & Moodle',
|
||||
description: 'Entorno de aprendizaje digital, aulas sincrónicas y videoconferencias.',
|
||||
floors: 1,
|
||||
floors: 0,
|
||||
classrooms_count: 2,
|
||||
total_capacity: 9999
|
||||
physical_capacity: 0,
|
||||
total_capacity: 0,
|
||||
capacity_display: 'Ilimitada (Virtual)'
|
||||
}
|
||||
];
|
||||
|
||||
// Helper to compute live totals
|
||||
const getLiveStats = async (apiClient) => {
|
||||
try {
|
||||
const response = await apiClient.get('/classrooms');
|
||||
const classrooms = (response.data && response.data.classrooms) || [];
|
||||
const physical = classrooms.filter(c => !c.is_virtual && (!c.code || !c.code.includes('VIRTUAL')));
|
||||
const virtual = classrooms.filter(c => c.is_virtual || (c.code && c.code.includes('VIRTUAL')));
|
||||
|
||||
const physCap = physical.reduce((acc, c) => acc + (c.capacity || 0), 0);
|
||||
|
||||
if (buildingsData[0]) {
|
||||
buildingsData[0].classrooms_count = physical.length;
|
||||
buildingsData[0].total_capacity = physCap;
|
||||
}
|
||||
if (buildingsData[1]) {
|
||||
buildingsData[1].classrooms_count = virtual.length;
|
||||
}
|
||||
|
||||
const totalRooms = classrooms.length;
|
||||
const totalCap = physCap + (buildingsData[1]?.total_capacity || 9999);
|
||||
|
||||
return { totalRooms, totalCap };
|
||||
} catch (e) {
|
||||
return { totalRooms: 31, totalCap: 11249 };
|
||||
}
|
||||
};
|
||||
|
||||
// 1. List Buildings
|
||||
const handleListBuildings = async (req, res) => {
|
||||
try {
|
||||
const { search = '', status = '' } = req.query;
|
||||
const stats = await getLiveStats(req.apiClient);
|
||||
let buildings = [];
|
||||
let stats = {
|
||||
total_buildings: 2,
|
||||
total_classrooms: 31,
|
||||
physical_classrooms_count: 29,
|
||||
total_physical_capacity: 1250,
|
||||
total_floors: 4
|
||||
};
|
||||
|
||||
let filtered = [...buildingsData];
|
||||
|
||||
if (search) {
|
||||
const s = search.toLowerCase();
|
||||
filtered = filtered.filter(b =>
|
||||
(b.name && b.name.toLowerCase().includes(s)) ||
|
||||
(b.code && b.code.toLowerCase().includes(s)) ||
|
||||
(b.address && b.address.toLowerCase().includes(s))
|
||||
);
|
||||
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];
|
||||
}
|
||||
|
||||
if (status === 'active') {
|
||||
filtered = filtered.filter(b => b.is_active);
|
||||
} else if (status === 'inactive') {
|
||||
filtered = filtered.filter(b => !b.is_active);
|
||||
// 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: filtered,
|
||||
total_classrooms: stats.totalRooms,
|
||||
total_capacity: stats.totalCap,
|
||||
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 (Offline)',
|
||||
buildings: buildingsData,
|
||||
title: 'Gestión de Edificios - Edu-Space Admin',
|
||||
buildings: defaultBuildings,
|
||||
total_classrooms: 31,
|
||||
total_capacity: 11249,
|
||||
total_capacity: 1250,
|
||||
total_physical_capacity: 1250,
|
||||
search: '',
|
||||
status_filter: ''
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
router.get('/', handleListBuildings);
|
||||
router.get('/list', handleListBuildings);
|
||||
router.get('/list_buildings', handleListBuildings);
|
||||
router.get(['/', '/list', '/list_buildings'], handleListBuildings);
|
||||
|
||||
// 2. Add Building
|
||||
// 2. Add Building (GET & POST)
|
||||
router.get(['/add', '/add_building'], (req, res) => {
|
||||
const form = createFormMock({
|
||||
is_active: true,
|
||||
floors: 3
|
||||
name: '',
|
||||
code: '',
|
||||
address: '',
|
||||
floors: 1,
|
||||
description: '',
|
||||
is_active: true
|
||||
});
|
||||
res.render('buildings/form', {
|
||||
title: 'Nuevo Edificio - Edu-Space Admin',
|
||||
@@ -119,50 +127,139 @@ router.get(['/add', '/add_building'], (req, res) => {
|
||||
});
|
||||
});
|
||||
|
||||
router.post(['/add', '/add_building'], (req, res) => {
|
||||
const { name, code, address, floors, description, is_active } = req.body;
|
||||
const newBuilding = {
|
||||
id: buildingsData.length + 1,
|
||||
name: name || `Edificio ${buildingsData.length + 1}`,
|
||||
code: code || `EDIF-${buildingsData.length + 1}`,
|
||||
address: address || '',
|
||||
floors: parseInt(floors, 10) || 1,
|
||||
description: description || '',
|
||||
is_active: is_active === 'true' || is_active === 'on' || is_active === true,
|
||||
classrooms_count: 0,
|
||||
total_capacity: 0
|
||||
};
|
||||
buildingsData.push(newBuilding);
|
||||
res.redirect('/buildings/list_buildings');
|
||||
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 / Edit / Toggle Building
|
||||
router.get(['/view/:id', '/view_building'], (req, res) => {
|
||||
// 3. View Building
|
||||
router.get(['/view/:id', '/view_building', '/:id'], async (req, res) => {
|
||||
const id = parseInt(req.params.id || req.query.id, 10) || 1;
|
||||
const building = buildingsData.find(b => b.id === id) || buildingsData[0];
|
||||
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: []
|
||||
classrooms
|
||||
});
|
||||
});
|
||||
|
||||
router.get(['/edit/:id', '/edit_building'], (req, res) => {
|
||||
// 4. Edit Building (GET)
|
||||
const handleEditBuildingGet = async (req, res) => {
|
||||
const id = parseInt(req.params.id || req.query.id, 10) || 1;
|
||||
const building = buildingsData.find(b => b.id === id) || buildingsData[0];
|
||||
const form = createFormMock(building);
|
||||
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');
|
||||
});
|
||||
|
||||
router.post(['/toggle/:id', '/toggle_building'], (req, res) => {
|
||||
const id = parseInt(req.params.id || req.query.id, 10) || 1;
|
||||
const building = buildingsData.find(b => b.id === id);
|
||||
if (building) {
|
||||
building.is_active = !building.is_active;
|
||||
// 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');
|
||||
});
|
||||
|
||||
@@ -633,6 +633,7 @@ const handleMyGrades = async (req, res) => {
|
||||
|
||||
router.get(['/gradebook', '/gradebook/:id', '/commissions/:id/gradebook'], handleGradebook);
|
||||
router.get(['/my_grades', '/my-grades', '/mis_calificaciones', '/mis-notas'], handleMyGrades);
|
||||
router.get(['/commissions_view', '/commissions'], (req, res) => res.redirect('/admin/commissions_list'));
|
||||
|
||||
module.exports = router;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user