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:
+65
-5
@@ -218,6 +218,7 @@
|
||||
$('cal-status').textContent = open ? 'Abierta' : 'Cerrada';
|
||||
$('cal-status').className = `badge ${open ? 'open' : 'closed'}`;
|
||||
showPanelView('calendar');
|
||||
closeNameEditor();
|
||||
renderLimitsEditor();
|
||||
rerender();
|
||||
window.scrollTo(0, 0);
|
||||
@@ -225,6 +226,42 @@
|
||||
|
||||
$('cal-back').addEventListener('click', () => loadRounds());
|
||||
|
||||
// ----- editar el nombre de la ronda -----
|
||||
|
||||
function closeNameEditor() {
|
||||
$('cal-name-editor').classList.add('hidden');
|
||||
$('cal-name-edit').classList.remove('hidden');
|
||||
}
|
||||
|
||||
$('cal-name-edit').addEventListener('click', () => {
|
||||
$('cal-name-input').value = cal.round.name;
|
||||
$('cal-name-editor').classList.remove('hidden');
|
||||
$('cal-name-edit').classList.add('hidden');
|
||||
$('cal-name-input').focus();
|
||||
$('cal-name-input').select();
|
||||
});
|
||||
|
||||
$('cal-name-cancel').addEventListener('click', closeNameEditor);
|
||||
|
||||
$('cal-name-save').addEventListener('click', async () => {
|
||||
const name = $('cal-name-input').value.trim();
|
||||
if (!name) { toast('Escribe un nombre', true); return; }
|
||||
try {
|
||||
const data = await fetchJSON(`/api/admin/rounds/${cal.round.id}/name`, { method: 'PUT', body: { name } });
|
||||
cal.round.name = data.name;
|
||||
$('cal-title').textContent = `${data.name} ${cal.round.year}`;
|
||||
toast('✓ Nombre actualizado');
|
||||
closeNameEditor();
|
||||
} catch (ex) {
|
||||
toast(ex.message, true);
|
||||
}
|
||||
});
|
||||
|
||||
$('cal-name-input').addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Enter') { e.preventDefault(); $('cal-name-save').click(); }
|
||||
if (e.key === 'Escape') { e.preventDefault(); closeNameEditor(); }
|
||||
});
|
||||
|
||||
// ----- editor de límites -----
|
||||
|
||||
function renderLimitsEditor() {
|
||||
@@ -256,6 +293,26 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ----- bloquear / desbloquear días -----
|
||||
|
||||
// Alterna el bloqueo de un día para toda la ronda (envía la lista completa).
|
||||
async function toggleBlocked(date) {
|
||||
const current = new Set(cal.blocked || []);
|
||||
const willBlock = !current.has(date);
|
||||
if (willBlock) current.add(date); else current.delete(date);
|
||||
try {
|
||||
const data = await fetchJSON(`/api/admin/rounds/${cal.round.id}/blocked`, {
|
||||
method: 'PUT',
|
||||
body: { dates: [...current] },
|
||||
});
|
||||
cal.blocked = data.blocked;
|
||||
toast(willBlock ? '🔒 Día bloqueado para todo el equipo' : 'Día desbloqueado');
|
||||
rerender();
|
||||
} catch (ex) {
|
||||
toast(ex.message, true);
|
||||
}
|
||||
}
|
||||
|
||||
// ----- datos derivados -----
|
||||
|
||||
// Ocupación por fecha y cargo: {date: {role: {approved, pending, rejected}}}.
|
||||
@@ -345,14 +402,15 @@
|
||||
|
||||
function renderGrid() {
|
||||
const occ = occupancy();
|
||||
const blockedSet = new Set(cal.blocked || []);
|
||||
const worker = workerById(selectedWorkerId);
|
||||
const myReq = worker ? new Map(worker.requests.map((r) => [r.date, r.status])) : null;
|
||||
|
||||
const hint = document.createElement('p');
|
||||
hint.className = 'hint cal-mode-hint';
|
||||
hint.innerHTML = worker
|
||||
? `Editando a <b>${esc(worker.name)}</b> · ${esc(cal.roles[worker.role] || worker.role)}. Toca un día para aprobarlo, rechazarlo, dejarlo pendiente o quitarlo.`
|
||||
: 'Vista de ocupación de todo el equipo. Elige un empleado en la izquierda para aprobar o editar sus días.';
|
||||
? `Editando a <b>${esc(worker.name)}</b> · ${esc(cal.roles[worker.role] || worker.role)}. Toca un día para aprobarlo, rechazarlo, dejarlo pendiente o quitarlo. Puedes asignar días aunque estén <b>completos o bloqueados</b>.`
|
||||
: 'Vista de ocupación de todo el equipo. Toca un día para <b>bloquearlo o desbloquearlo</b> para todo el equipo (🔒 no elegible por nadie). Elige un empleado en la izquierda para aprobar o editar sus días.';
|
||||
|
||||
const grid = $('cal-grid');
|
||||
grid.innerHTML = '';
|
||||
@@ -388,6 +446,7 @@
|
||||
if (date === focusedDate) cls += ' focused';
|
||||
btn.className = cls;
|
||||
btn.innerHTML = `<span class="n">${d}</span>`;
|
||||
if (blockedSet.has(date)) btn.classList.add('is-blocked');
|
||||
const others = held(occ[date]?.[worker.role]) - (status && status !== 'rejected' ? 1 : 0);
|
||||
const lim = cal.limits[worker.role];
|
||||
if (others > 0) {
|
||||
@@ -398,9 +457,9 @@
|
||||
}
|
||||
btn.addEventListener('click', () => focusDay(date));
|
||||
} else {
|
||||
// Modo ocupación: chips por cargo con su carga.
|
||||
btn.className = 'cday cday--info';
|
||||
btn.disabled = true;
|
||||
// Modo ocupación: chips por cargo con su carga. Tocar bloquea/desbloquea
|
||||
// el día para todo el equipo.
|
||||
btn.className = 'cday' + (blockedSet.has(date) ? ' cday--blocked' : '');
|
||||
btn.innerHTML = `<span class="n">${d}</span>`;
|
||||
const cell = occ[date];
|
||||
if (cell) {
|
||||
@@ -418,6 +477,7 @@
|
||||
}
|
||||
if (chips.children.length) btn.appendChild(chips);
|
||||
}
|
||||
btn.addEventListener('click', () => toggleBlocked(date));
|
||||
}
|
||||
days.appendChild(btn);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user