Actualizar agenda.html
This commit is contained in:
75
agenda.html
75
agenda.html
@@ -10,7 +10,6 @@
|
|||||||
.fade-in { animation: fadeIn 0.3s ease-in-out; }
|
.fade-in { animation: fadeIn 0.3s ease-in-out; }
|
||||||
.scroller::-webkit-scrollbar { width: 6px; }
|
.scroller::-webkit-scrollbar { width: 6px; }
|
||||||
.scroller::-webkit-scrollbar-thumb { background-color: #cbd5e1; border-radius: 4px; }
|
.scroller::-webkit-scrollbar-thumb { background-color: #cbd5e1; border-radius: 4px; }
|
||||||
/* Clases para tabs activas/inactivas */
|
|
||||||
.tab-active { color: #2563eb; border-bottom-width: 3px; border-color: #2563eb; font-weight: 900; }
|
.tab-active { color: #2563eb; border-bottom-width: 3px; border-color: #2563eb; font-weight: 900; }
|
||||||
.tab-inactive { color: #64748b; font-weight: 500; border-bottom-width: 3px; border-color: transparent; }
|
.tab-inactive { color: #64748b; font-weight: 500; border-bottom-width: 3px; border-color: transparent; }
|
||||||
.tab-inactive:hover { color: #334155; }
|
.tab-inactive:hover { color: #334155; }
|
||||||
@@ -52,18 +51,26 @@
|
|||||||
|
|
||||||
<div id="view-blocks" class="max-w-5xl mx-auto space-y-6 tab-view hidden fade-in">
|
<div id="view-blocks" class="max-w-5xl mx-auto space-y-6 tab-view hidden fade-in">
|
||||||
|
|
||||||
<div class="bg-white p-6 rounded-[2rem] border border-slate-200 shadow-sm flex flex-col md:flex-row gap-6 items-end">
|
<div class="bg-white p-6 rounded-[2rem] border border-slate-200 shadow-sm flex flex-col md:flex-row gap-6 items-start md:items-end">
|
||||||
<div class="flex-1 w-full space-y-4">
|
<div class="flex-1 w-full space-y-4">
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-xs font-black text-slate-500 uppercase tracking-widest mb-1.5">1. Selecciona Operario</label>
|
<label class="block text-xs font-black text-slate-500 uppercase tracking-widest mb-1.5">1. Operario</label>
|
||||||
<select id="blockWorker" class="w-full bg-slate-50 border border-slate-200 text-sm font-bold text-slate-700 p-3 rounded-xl outline-none focus:ring-2 focus:ring-blue-500">
|
<select id="blockWorker" class="w-full bg-slate-50 border border-slate-200 text-sm font-bold text-slate-700 p-3 rounded-xl outline-none focus:ring-2 focus:ring-blue-500">
|
||||||
<option value="">Cargando operarios...</option>
|
<option value="">Cargando operarios...</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="grid grid-cols-2 gap-4">
|
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-xs font-black text-slate-500 uppercase tracking-widest mb-1.5">2. Fecha</label>
|
<label class="block text-xs font-black text-slate-500 uppercase tracking-widest mb-1.5">2. Gremio (Opcional)</label>
|
||||||
|
<select id="blockGuild" class="w-full bg-slate-50 border border-slate-200 text-sm font-bold text-slate-700 p-3 rounded-xl outline-none focus:ring-2 focus:ring-blue-500">
|
||||||
|
<option value="">Bloqueo Total (Todos los gremios)</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
|
<div>
|
||||||
|
<label class="block text-xs font-black text-slate-500 uppercase tracking-widest mb-1.5">3. Fecha</label>
|
||||||
<input type="date" id="blockDate" class="w-full bg-slate-50 border border-slate-200 text-sm font-bold text-slate-700 p-3 rounded-xl outline-none focus:ring-2 focus:ring-blue-500">
|
<input type="date" id="blockDate" class="w-full bg-slate-50 border border-slate-200 text-sm font-bold text-slate-700 p-3 rounded-xl outline-none focus:ring-2 focus:ring-blue-500">
|
||||||
</div>
|
</div>
|
||||||
<div class="grid grid-cols-2 gap-2">
|
<div class="grid grid-cols-2 gap-2">
|
||||||
@@ -94,7 +101,7 @@
|
|||||||
<div class="flex-1 w-full space-y-4">
|
<div class="flex-1 w-full space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-xs font-black text-slate-500 uppercase tracking-widest mb-1.5">Motivo (Opcional)</label>
|
<label class="block text-xs font-black text-slate-500 uppercase tracking-widest mb-1.5">Motivo (Opcional)</label>
|
||||||
<input type="text" id="blockReason" placeholder="Ej: Médico, Asuntos propios, Vacaciones..." class="w-full bg-slate-50 border border-slate-200 text-sm font-bold text-slate-700 p-3 rounded-xl outline-none focus:ring-2 focus:ring-blue-500">
|
<input type="text" id="blockReason" placeholder="Ej: Médico, Curso, Saturación..." class="w-full bg-slate-50 border border-slate-200 text-sm font-bold text-slate-700 p-3 rounded-xl outline-none focus:ring-2 focus:ring-blue-500">
|
||||||
</div>
|
</div>
|
||||||
<button onclick="saveBlock()" class="w-full bg-slate-900 text-white font-black py-3 rounded-xl shadow-lg hover:bg-blue-600 transition-all uppercase tracking-widest text-xs flex items-center justify-center gap-2">
|
<button onclick="saveBlock()" class="w-full bg-slate-900 text-white font-black py-3 rounded-xl shadow-lg hover:bg-blue-600 transition-all uppercase tracking-widest text-xs flex items-center justify-center gap-2">
|
||||||
<i data-lucide="lock" class="w-4 h-4"></i> Generar Bloqueo
|
<i data-lucide="lock" class="w-4 h-4"></i> Generar Bloqueo
|
||||||
@@ -155,10 +162,8 @@
|
|||||||
<script>
|
<script>
|
||||||
document.addEventListener("DOMContentLoaded", () => {
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
if (!localStorage.getItem("token")) window.location.href = "index.html";
|
if (!localStorage.getItem("token")) window.location.href = "index.html";
|
||||||
// Pre-seleccionar pestaña 1
|
|
||||||
loadRequests();
|
loadRequests();
|
||||||
// Cargar datos para pestaña 2 en segundo plano
|
loadOperatorsAndGuilds();
|
||||||
loadOperators();
|
|
||||||
loadActiveBlocks();
|
loadActiveBlocks();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -239,11 +244,13 @@
|
|||||||
}).join('');
|
}).join('');
|
||||||
|
|
||||||
lucide.createIcons();
|
lucide.createIcons();
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
container.innerHTML = '<p class="text-center text-red-500 font-bold">Error de conexión</p>';
|
container.innerHTML = '<p class="text-center text-red-500 font-bold">Error de conexión</p>';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- FUNCIONES DEL MODAL DE APROBACIÓN ---
|
||||||
function openApproveModal(id, dateText) {
|
function openApproveModal(id, dateText) {
|
||||||
document.getElementById('aprvId').value = id;
|
document.getElementById('aprvId').value = id;
|
||||||
document.getElementById('aprvDateText').innerText = dateText;
|
document.getElementById('aprvDateText').innerText = dateText;
|
||||||
@@ -303,31 +310,39 @@
|
|||||||
} catch (e) { alert("Error de conexión"); }
|
} catch (e) { alert("Error de conexión"); }
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- LÓGICA DE BLOQUEOS DE AGENDA ---
|
// --- LÓGICA DE BLOQUEOS DE AGENDA Y GREMIOS ---
|
||||||
async function loadOperators() {
|
async function loadOperatorsAndGuilds() {
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`${API_URL}/operators`, {
|
// Cargar Operarios
|
||||||
headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` }
|
const resOp = await fetch(`${API_URL}/operators`, { headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } });
|
||||||
});
|
const dataOp = await resOp.json();
|
||||||
const data = await res.json();
|
if(dataOp.ok) {
|
||||||
if(data.ok) {
|
document.getElementById('blockWorker').innerHTML = '<option value="">Seleccione Operario...</option>' +
|
||||||
const sel = document.getElementById('blockWorker');
|
dataOp.operators.map(o => `<option value="${o.id}">${o.full_name}</option>`).join('');
|
||||||
sel.innerHTML = '<option value="">Seleccione Operario...</option>' +
|
}
|
||||||
data.operators.map(o => `<option value="${o.id}">${o.full_name}</option>`).join('');
|
|
||||||
|
// Cargar Gremios
|
||||||
|
const resG = await fetch(`${API_URL}/guilds`, { headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } });
|
||||||
|
const dataG = await resG.json();
|
||||||
|
if(dataG.ok) {
|
||||||
|
document.getElementById('blockGuild').innerHTML = '<option value="">Bloqueo Total (Todos los gremios)</option>' +
|
||||||
|
dataG.guilds.map(g => `<option value="${g.id}">${g.name}</option>`).join('');
|
||||||
}
|
}
|
||||||
} catch (e) { console.error(e); }
|
} catch (e) { console.error(e); }
|
||||||
}
|
}
|
||||||
|
|
||||||
async function saveBlock() {
|
async function saveBlock() {
|
||||||
const workerId = document.getElementById('blockWorker').value;
|
const workerId = document.getElementById('blockWorker').value;
|
||||||
|
const guildSelect = document.getElementById('blockGuild');
|
||||||
|
const guildId = guildSelect.value;
|
||||||
|
const guildName = guildId ? guildSelect.options[guildSelect.selectedIndex].text : null;
|
||||||
const date = document.getElementById('blockDate').value;
|
const date = document.getElementById('blockDate').value;
|
||||||
const timeStart = document.getElementById('blockTimeStart').value;
|
const timeStart = document.getElementById('blockTimeStart').value;
|
||||||
const timeEnd = document.getElementById('blockTimeEnd').value;
|
const timeEnd = document.getElementById('blockTimeEnd').value;
|
||||||
const reason = document.getElementById('blockReason').value || "Bloqueo Administrativo";
|
const reason = document.getElementById('blockReason').value || (guildId ? `Bloqueo de ${guildName}` : "Bloqueo Total");
|
||||||
|
|
||||||
if(!workerId || !date) return alert("Falta operario o fecha");
|
if(!workerId || !date) return alert("Falta operario o fecha");
|
||||||
|
|
||||||
// Calculamos duración en minutos
|
|
||||||
const startMins = parseInt(timeStart.split(':')[0]) * 60;
|
const startMins = parseInt(timeStart.split(':')[0]) * 60;
|
||||||
const endMins = parseInt(timeEnd.split(':')[0]) * 60;
|
const endMins = parseInt(timeEnd.split(':')[0]) * 60;
|
||||||
const duration = endMins - startMins;
|
const duration = endMins - startMins;
|
||||||
@@ -338,7 +353,7 @@
|
|||||||
const res = await fetch(`${API_URL}/agenda/blocks`, {
|
const res = await fetch(`${API_URL}/agenda/blocks`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${localStorage.getItem("token")}` },
|
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${localStorage.getItem("token")}` },
|
||||||
body: JSON.stringify({ worker_id: workerId, date: date, time: timeStart, duration: duration, reason: reason })
|
body: JSON.stringify({ worker_id: workerId, date: date, time: timeStart, duration: duration, reason: reason, guild_id: guildId, guild_name: guildName })
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
@@ -362,13 +377,21 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
container.innerHTML = data.blocks.map(b => {
|
container.innerHTML = data.blocks.map(b => {
|
||||||
|
// Etiqueta visual si es de un gremio concreto o total
|
||||||
|
const badge = b.guild_name
|
||||||
|
? `<span class="bg-indigo-100 text-indigo-700 px-2 py-0.5 rounded text-[9px] uppercase tracking-widest font-black">Solo ${b.guild_name}</span>`
|
||||||
|
: `<span class="bg-rose-100 text-rose-700 px-2 py-0.5 rounded text-[9px] uppercase tracking-widest font-black">Bloqueo Total</span>`;
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<div class="bg-white p-4 rounded-xl border border-rose-100 flex justify-between items-center shadow-sm">
|
<div class="bg-white p-4 rounded-xl border border-slate-200 flex justify-between items-center shadow-sm">
|
||||||
<div>
|
<div>
|
||||||
<p class="font-black text-slate-800 flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
|
<p class="font-black text-slate-800 flex items-center gap-1.5">
|
||||||
<i data-lucide="hard-hat" class="w-4 h-4 text-slate-400"></i> ${b.worker_name}
|
<i data-lucide="hard-hat" class="w-4 h-4 text-slate-400"></i> ${b.worker_name}
|
||||||
</p>
|
</p>
|
||||||
<p class="text-xs text-rose-600 font-bold mt-1">${b.date} | ${b.time} (${b.duration} min)</p>
|
${badge}
|
||||||
|
</div>
|
||||||
|
<p class="text-xs text-slate-600 font-bold mt-1.5">${b.date} | ${b.time} (${b.duration} min)</p>
|
||||||
<p class="text-[10px] text-slate-500 uppercase mt-0.5">${b.reason}</p>
|
<p class="text-[10px] text-slate-500 uppercase mt-0.5">${b.reason}</p>
|
||||||
</div>
|
</div>
|
||||||
<button onclick="deleteBlock(${b.id})" class="text-slate-300 hover:text-red-500 p-2 transition-colors">
|
<button onclick="deleteBlock(${b.id})" class="text-slate-300 hover:text-red-500 p-2 transition-colors">
|
||||||
|
|||||||
Reference in New Issue
Block a user