Actualizar agenda.html
This commit is contained in:
85
agenda.html
85
agenda.html
@@ -10,7 +10,6 @@
|
||||
.fade-in { animation: fadeIn 0.3s ease-in-out; }
|
||||
.scroller::-webkit-scrollbar { width: 6px; }
|
||||
.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-inactive { color: #64748b; font-weight: 500; border-bottom-width: 3px; border-color: transparent; }
|
||||
.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 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>
|
||||
<label class="block text-xs font-black text-slate-500 uppercase tracking-widest mb-1.5">1. Selecciona 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">
|
||||
<option value="">Cargando operarios...</option>
|
||||
</select>
|
||||
<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">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">
|
||||
<option value="">Cargando operarios...</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<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-2 gap-4">
|
||||
<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">2. Fecha</label>
|
||||
<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">
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
@@ -94,7 +101,7 @@
|
||||
<div class="flex-1 w-full space-y-4">
|
||||
<div>
|
||||
<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>
|
||||
<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
|
||||
@@ -155,10 +162,8 @@
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
if (!localStorage.getItem("token")) window.location.href = "index.html";
|
||||
// Pre-seleccionar pestaña 1
|
||||
loadRequests();
|
||||
// Cargar datos para pestaña 2 en segundo plano
|
||||
loadOperators();
|
||||
loadOperatorsAndGuilds();
|
||||
loadActiveBlocks();
|
||||
});
|
||||
|
||||
@@ -239,11 +244,13 @@
|
||||
}).join('');
|
||||
|
||||
lucide.createIcons();
|
||||
|
||||
} catch (e) {
|
||||
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) {
|
||||
document.getElementById('aprvId').value = id;
|
||||
document.getElementById('aprvDateText').innerText = dateText;
|
||||
@@ -303,31 +310,39 @@
|
||||
} catch (e) { alert("Error de conexión"); }
|
||||
}
|
||||
|
||||
// --- LÓGICA DE BLOQUEOS DE AGENDA ---
|
||||
async function loadOperators() {
|
||||
// --- LÓGICA DE BLOQUEOS DE AGENDA Y GREMIOS ---
|
||||
async function loadOperatorsAndGuilds() {
|
||||
try {
|
||||
const res = await fetch(`${API_URL}/operators`, {
|
||||
headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` }
|
||||
});
|
||||
const data = await res.json();
|
||||
if(data.ok) {
|
||||
const sel = document.getElementById('blockWorker');
|
||||
sel.innerHTML = '<option value="">Seleccione Operario...</option>' +
|
||||
data.operators.map(o => `<option value="${o.id}">${o.full_name}</option>`).join('');
|
||||
// Cargar Operarios
|
||||
const resOp = await fetch(`${API_URL}/operators`, { headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } });
|
||||
const dataOp = await resOp.json();
|
||||
if(dataOp.ok) {
|
||||
document.getElementById('blockWorker').innerHTML = '<option value="">Seleccione Operario...</option>' +
|
||||
dataOp.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); }
|
||||
}
|
||||
|
||||
async function saveBlock() {
|
||||
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 timeStart = document.getElementById('blockTimeStart').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");
|
||||
|
||||
// Calculamos duración en minutos
|
||||
const startMins = parseInt(timeStart.split(':')[0]) * 60;
|
||||
const endMins = parseInt(timeEnd.split(':')[0]) * 60;
|
||||
const duration = endMins - startMins;
|
||||
@@ -338,7 +353,7 @@
|
||||
const res = await fetch(`${API_URL}/agenda/blocks`, {
|
||||
method: 'POST',
|
||||
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) {
|
||||
@@ -362,13 +377,21 @@
|
||||
}
|
||||
|
||||
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 `
|
||||
<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>
|
||||
<p class="font-black text-slate-800 flex items-center gap-2">
|
||||
<i data-lucide="hard-hat" class="w-4 h-4 text-slate-400"></i> ${b.worker_name}
|
||||
</p>
|
||||
<p class="text-xs text-rose-600 font-bold mt-1">${b.date} | ${b.time} (${b.duration} min)</p>
|
||||
<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}
|
||||
</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>
|
||||
</div>
|
||||
<button onclick="deleteBlock(${b.id})" class="text-slate-300 hover:text-red-500 p-2 transition-colors">
|
||||
|
||||
Reference in New Issue
Block a user