Bloqueo de días, override del admin y edición del nombre de ronda
- Admin puede bloquear/desbloquear días (rounds.blocked) desde la vista de ocupación; los trabajadores no pueden elegirlos (🔒). - El admin puede asignar cualquier día aunque supere el límite por día/cargo o esté bloqueado (override explícito en requests/set). - Editar el nombre de la ronda con edición inline en el calendario (PUT /api/admin/rounds/:id/name). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -56,6 +56,8 @@ const hasColumn = (table, col) =>
|
||||
if (!hasColumn('rounds', 'roles')) db.exec('ALTER TABLE rounds ADD COLUMN roles TEXT');
|
||||
// Límite de solicitudes por día y cargo (JSON {clave: máximo}).
|
||||
if (!hasColumn('rounds', 'limits')) db.exec('ALTER TABLE rounds ADD COLUMN limits TEXT');
|
||||
// Días bloqueados por el admin para toda la ronda (JSON array de fechas).
|
||||
if (!hasColumn('rounds', 'blocked')) db.exec('ALTER TABLE rounds ADD COLUMN blocked TEXT');
|
||||
// Estado de aprobación de cada solicitud: pending | approved | rejected.
|
||||
if (!hasColumn('requests', 'status')) {
|
||||
db.exec("ALTER TABLE requests ADD COLUMN status TEXT NOT NULL DEFAULT 'pending'");
|
||||
@@ -107,6 +109,19 @@ function roundLimits(round) {
|
||||
return out;
|
||||
}
|
||||
|
||||
// Días bloqueados por el admin (no elegibles por ningún trabajador). Devuelve
|
||||
// una lista de fechas válidas del año de la ronda, sin duplicados y ordenadas.
|
||||
function roundBlocked(round) {
|
||||
if (!round.blocked) return [];
|
||||
try {
|
||||
const parsed = JSON.parse(round.blocked);
|
||||
if (Array.isArray(parsed)) {
|
||||
return [...new Set(parsed.map(String).filter((d) => isValidDate(d, round.year)))].sort();
|
||||
}
|
||||
} catch { /* sin bloqueos */ }
|
||||
return [];
|
||||
}
|
||||
|
||||
// Convierte una etiqueta ("Camarero/a") en una clave estable ("camarero_a").
|
||||
const slugify = (s) =>
|
||||
String(s)
|
||||
@@ -248,6 +263,7 @@ app.get('/api/round/:token', (req, res) => {
|
||||
round: { name: round.name, year: round.year, status: round.status },
|
||||
roles: roundRoles(round),
|
||||
limits: roundLimits(round),
|
||||
blocked: roundBlocked(round),
|
||||
today: todayISO(),
|
||||
me: worker
|
||||
? { name: worker.name, role: worker.role, requests: workerRequests(worker.id) }
|
||||
@@ -309,6 +325,14 @@ app.put('/api/round/:token/requests', (req, res) => {
|
||||
return res.status(400).json({ error: 'No puedes elegir días pasados ni el día de hoy.' });
|
||||
}
|
||||
|
||||
// Los días nuevos no pueden estar bloqueados por la administración.
|
||||
const blocked = new Set(roundBlocked(round));
|
||||
if (added.some((d) => blocked.has(d))) {
|
||||
return res.status(409).json({
|
||||
error: 'Algún día que elegiste está bloqueado por la administración. Recarga la página.',
|
||||
});
|
||||
}
|
||||
|
||||
// Los días nuevos no pueden superar el límite de tu cargo (cuentan las
|
||||
// solicitudes de los demás que no estén rechazadas).
|
||||
const limit = roundLimits(round)[worker.role];
|
||||
@@ -422,6 +446,30 @@ app.put('/api/admin/rounds/:id/limits', requireAdmin, (req, res) => {
|
||||
res.json({ ok: true, limits });
|
||||
});
|
||||
|
||||
// Editar el nombre de una ronda (igual que se editan los límites).
|
||||
app.put('/api/admin/rounds/:id/name', requireAdmin, (req, res) => {
|
||||
const name = String(req.body.name ?? '').trim();
|
||||
if (!name || name.length > 80) return res.status(400).json({ error: 'Indica el nombre del local' });
|
||||
const info = db.prepare('UPDATE rounds SET name = ? WHERE id = ?').run(name, req.params.id);
|
||||
if (!info.changes) return res.status(404).json({ error: 'Ronda no encontrada' });
|
||||
res.json({ ok: true, name });
|
||||
});
|
||||
|
||||
// Bloquear / desbloquear días de una ronda. Los días bloqueados no son elegibles
|
||||
// por ningún trabajador, pero el admin sí puede asignarlos manualmente. Recibe la
|
||||
// lista completa de días bloqueados y la guarda (reemplazo total).
|
||||
app.put('/api/admin/rounds/:id/blocked', requireAdmin, (req, res) => {
|
||||
const round = db.prepare('SELECT * FROM rounds WHERE id = ?').get(req.params.id);
|
||||
if (!round) return res.status(404).json({ error: 'Ronda no encontrada' });
|
||||
const dates = req.body.dates;
|
||||
if (!Array.isArray(dates) || dates.length > 366) {
|
||||
return res.status(400).json({ error: 'Días no válidos' });
|
||||
}
|
||||
const blocked = [...new Set(dates.map(String))].filter((d) => isValidDate(d, round.year)).sort();
|
||||
db.prepare('UPDATE rounds SET blocked = ? WHERE id = ?').run(JSON.stringify(blocked), round.id);
|
||||
res.json({ ok: true, blocked });
|
||||
});
|
||||
|
||||
// Vista de calendario del administrador: empleados con sus solicitudes (y estado),
|
||||
// más los cargos y límites de la ronda.
|
||||
app.get('/api/admin/rounds/:id/calendar', requireAdmin, (req, res) => {
|
||||
@@ -435,12 +483,15 @@ app.get('/api/admin/rounds/:id/calendar', requireAdmin, (req, res) => {
|
||||
round: { id: round.id, name: round.name, year: round.year, status: round.status },
|
||||
roles: roundRoles(round),
|
||||
limits: roundLimits(round),
|
||||
blocked: roundBlocked(round),
|
||||
today: todayISO(),
|
||||
workers,
|
||||
});
|
||||
});
|
||||
|
||||
// Aprobar / rechazar / volver a pendiente / añadir / quitar días de un empleado.
|
||||
// El admin manda: no se aplica el límite por día/cargo ni el bloqueo de días,
|
||||
// así que puede asignar cualquier día aunque esté completo o bloqueado.
|
||||
app.post('/api/admin/rounds/:id/requests/set', requireAdmin, (req, res) => {
|
||||
const round = db.prepare('SELECT * FROM rounds WHERE id = ?').get(req.params.id);
|
||||
if (!round) return res.status(404).json({ error: 'Ronda no encontrada' });
|
||||
|
||||
Reference in New Issue
Block a user