Actualizar asignados.html
This commit is contained in:
296
asignados.html
296
asignados.html
@@ -40,7 +40,7 @@
|
|||||||
</head>
|
</head>
|
||||||
<body class="text-slate-800 font-sans antialiased h-screen flex flex-col overflow-hidden relative">
|
<body class="text-slate-800 font-sans antialiased h-screen flex flex-col overflow-hidden relative">
|
||||||
|
|
||||||
<header class="bg-white px-5 pt-8 pb-4 shadow-sm z-20 shrink-0 border-b border-slate-100 flex justify-between items-center">
|
<header class="bg-white px-5 pt-8 pb-4 shadow-sm z-20 shrink-0 border-b border-slate-100 flex justify-between items-center relative">
|
||||||
<div>
|
<div>
|
||||||
<p class="text-[10px] font-black text-primary-dynamic uppercase tracking-widest mb-0.5">Por Agendar</p>
|
<p class="text-[10px] font-black text-primary-dynamic uppercase tracking-widest mb-0.5">Por Agendar</p>
|
||||||
<h1 class="text-2xl font-black tracking-tight text-slate-800 leading-none">Sin Cita</h1>
|
<h1 class="text-2xl font-black tracking-tight text-slate-800 leading-none">Sin Cita</h1>
|
||||||
@@ -53,10 +53,29 @@
|
|||||||
<main class="flex-1 overflow-y-auto no-scrollbar p-5 relative z-10" id="mainArea">
|
<main class="flex-1 overflow-y-auto no-scrollbar p-5 relative z-10" id="mainArea">
|
||||||
<div id="loader" class="text-center py-10 opacity-50">
|
<div id="loader" class="text-center py-10 opacity-50">
|
||||||
<i data-lucide="loader-2" class="w-8 h-8 animate-spin mx-auto text-primary-dynamic mb-2"></i>
|
<i data-lucide="loader-2" class="w-8 h-8 animate-spin mx-auto text-primary-dynamic mb-2"></i>
|
||||||
<p class="text-xs font-bold uppercase tracking-widest">Buscando pendientes...</p>
|
<p class="text-xs font-bold uppercase tracking-widest mt-2 text-slate-400">Sincronizando...</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="servicesList" class="space-y-4 pb-24 hidden fade-in">
|
<div id="contentWrapper" class="hidden pb-24 fade-in">
|
||||||
|
|
||||||
|
<div id="requestsSection" class="mb-6 hidden">
|
||||||
|
<h2 class="text-xs font-black text-slate-400 uppercase tracking-widest mb-3 flex items-center gap-2">
|
||||||
|
<span class="relative flex h-2.5 w-2.5">
|
||||||
|
<span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-blue-400 opacity-75"></span>
|
||||||
|
<span class="relative inline-flex rounded-full h-2.5 w-2.5 bg-blue-500"></span>
|
||||||
|
</span>
|
||||||
|
Citas Solicitadas
|
||||||
|
</h2>
|
||||||
|
<div id="requestsList" class="space-y-4">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="noDateSection">
|
||||||
|
<h2 class="text-xs font-black text-slate-400 uppercase tracking-widest mb-3">Pendientes de Fecha</h2>
|
||||||
|
<div id="servicesList" class="space-y-4">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
@@ -85,7 +104,7 @@
|
|||||||
<span id="detRef" class="text-[10px] font-black text-slate-400 uppercase tracking-widest"></span>
|
<span id="detRef" class="text-[10px] font-black text-slate-400 uppercase tracking-widest"></span>
|
||||||
<h3 class="font-black text-xl text-slate-800 uppercase leading-none" id="detName"></h3>
|
<h3 class="font-black text-xl text-slate-800 uppercase leading-none" id="detName"></h3>
|
||||||
</div>
|
</div>
|
||||||
<button onclick="closeModal()" class="w-8 h-8 bg-slate-100 rounded-full flex items-center justify-center text-slate-500 hover:text-red-500"><i data-lucide="x" class="w-4 h-4"></i></button>
|
<button onclick="closeModal('actionModal', 'modalContent')" class="w-8 h-8 bg-slate-100 rounded-full flex items-center justify-center text-slate-500 hover:text-red-500"><i data-lucide="x" class="w-4 h-4"></i></button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="overflow-y-auto no-scrollbar flex-1 pb-4">
|
<div class="overflow-y-auto no-scrollbar flex-1 pb-4">
|
||||||
@@ -111,7 +130,6 @@
|
|||||||
<hr class="border-slate-100 mb-6">
|
<hr class="border-slate-100 mb-6">
|
||||||
|
|
||||||
<div class="bg-white border border-slate-200 rounded-[1.5rem] p-5 shadow-sm space-y-5">
|
<div class="bg-white border border-slate-200 rounded-[1.5rem] p-5 shadow-sm space-y-5">
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<div class="flex justify-between items-center mb-2">
|
<div class="flex justify-between items-center mb-2">
|
||||||
<p class="text-[10px] font-black text-primary-dynamic uppercase tracking-widest flex items-center gap-1.5"><i data-lucide="clock" class="w-4 h-4"></i> Duración Estimada</p>
|
<p class="text-[10px] font-black text-primary-dynamic uppercase tracking-widest flex items-center gap-1.5"><i data-lucide="clock" class="w-4 h-4"></i> Duración Estimada</p>
|
||||||
@@ -133,21 +151,17 @@
|
|||||||
|
|
||||||
<div>
|
<div>
|
||||||
<p class="text-[10px] font-black text-primary-dynamic uppercase tracking-widest mb-2 flex items-center gap-1.5"><i data-lucide="calendar" class="w-4 h-4"></i> Seleccionar Día</p>
|
<p class="text-[10px] font-black text-primary-dynamic uppercase tracking-widest mb-2 flex items-center gap-1.5"><i data-lucide="calendar" class="w-4 h-4"></i> Seleccionar Día</p>
|
||||||
<div id="dayCarousel" class="flex overflow-x-auto gap-2 pb-2 no-scrollbar">
|
<div id="dayCarousel" class="flex overflow-x-auto gap-2 pb-2 no-scrollbar"></div>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<p class="text-[10px] font-black text-primary-dynamic uppercase tracking-widest mb-2 flex items-center gap-1.5"><i data-lucide="watch" class="w-4 h-4"></i> Huecos Disponibles</p>
|
<p class="text-[10px] font-black text-primary-dynamic uppercase tracking-widest mb-2 flex items-center gap-1.5"><i data-lucide="watch" class="w-4 h-4"></i> Huecos Disponibles</p>
|
||||||
<div id="timeGrid" class="grid grid-cols-4 gap-2">
|
<div id="timeGrid" class="grid grid-cols-4 gap-2"></div>
|
||||||
</div>
|
|
||||||
<p id="noSlotsMsg" class="hidden text-xs text-rose-500 font-bold bg-rose-50 p-3 rounded-xl border border-rose-100 text-center mt-2">
|
<p id="noSlotsMsg" class="hidden text-xs text-rose-500 font-bold bg-rose-50 p-3 rounded-xl border border-rose-100 text-center mt-2">
|
||||||
No hay huecos libres para esta duración.
|
No hay huecos libres para esta duración.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pt-4 shrink-0 bg-white border-t border-slate-100">
|
<div class="pt-4 shrink-0 bg-white border-t border-slate-100">
|
||||||
@@ -155,7 +169,53 @@
|
|||||||
<i data-lucide="calendar-check" class="w-5 h-5"></i> Confirmar Cita
|
<i data-lucide="calendar-check" class="w-5 h-5"></i> Confirmar Cita
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="approveModal" class="fixed inset-0 bg-slate-900/60 z-[110] hidden flex-col justify-end transition-opacity duration-300 opacity-0">
|
||||||
|
<div id="approveModalContent" class="bg-white rounded-t-[2rem] p-6 pb-12 transform translate-y-full transition-transform duration-300 shadow-2xl">
|
||||||
|
<div class="flex justify-between items-center mb-6">
|
||||||
|
<div>
|
||||||
|
<span id="appRef" class="text-[10px] font-black text-slate-400 uppercase tracking-widest"></span>
|
||||||
|
<h3 class="font-black text-xl text-slate-800 uppercase leading-none" id="appName"></h3>
|
||||||
|
</div>
|
||||||
|
<button onclick="closeModal('approveModal', 'approveModalContent')" class="w-8 h-8 bg-slate-100 rounded-full flex items-center justify-center text-slate-500 hover:text-red-500"><i data-lucide="x" class="w-4 h-4"></i></button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bg-blue-50 border border-blue-100 p-5 rounded-2xl mb-6 shadow-inner text-center">
|
||||||
|
<p class="text-[10px] font-black text-blue-600 uppercase tracking-widest mb-1">Fecha Solicitada por el Cliente</p>
|
||||||
|
<h4 class="text-2xl font-black text-slate-800" id="appDate">--/--/----</h4>
|
||||||
|
<p class="text-sm font-bold text-slate-600 mt-1" id="appTime">--:--</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<input type="hidden" id="appId">
|
||||||
|
|
||||||
|
<div class="mb-8">
|
||||||
|
<p class="text-[10px] font-black text-slate-500 uppercase tracking-widest mb-2 ml-1">¿Cuánto tiempo vas a tardar?</p>
|
||||||
|
<div class="relative">
|
||||||
|
<select id="appDurationInput" class="w-full bg-slate-50 border border-slate-200 p-4 rounded-xl text-sm font-black text-slate-700 outline-none focus:ring-2 focus:ring-blue-500 appearance-none pr-10">
|
||||||
|
<option value="15">15 Minutos (Muy rápido)</option>
|
||||||
|
<option value="30">30 Minutos</option>
|
||||||
|
<option value="45">45 Minutos</option>
|
||||||
|
<option value="60" selected>1 Hora (Estándar)</option>
|
||||||
|
<option value="90">1 Hora y Media</option>
|
||||||
|
<option value="120">2 Horas</option>
|
||||||
|
<option value="180">3 Horas</option>
|
||||||
|
<option value="240">4 Horas (Media jornada)</option>
|
||||||
|
</select>
|
||||||
|
<i data-lucide="chevron-down" class="w-5 h-5 absolute right-4 top-1/2 -translate-y-1/2 text-slate-400 pointer-events-none"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-2 gap-3">
|
||||||
|
<button onclick="rejectRequest()" id="btnReject" class="bg-white border-2 border-rose-200 text-rose-600 font-black py-4 rounded-xl hover:bg-rose-50 flex items-center justify-center gap-2 transition-all text-xs uppercase tracking-widest">
|
||||||
|
<i data-lucide="x" class="w-4 h-4"></i> Rechazar
|
||||||
|
</button>
|
||||||
|
<button onclick="approveRequest()" id="btnApprove" class="bg-blue-600 text-white font-black py-4 rounded-xl shadow-lg shadow-blue-500/30 hover:bg-blue-700 flex items-center justify-center gap-2 transition-all text-xs uppercase tracking-widest">
|
||||||
|
<i data-lucide="check" class="w-4 h-4"></i> Aceptar
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<p class="text-center text-[9px] font-bold text-slate-400 mt-4">Al aceptar, se enviará un WhatsApp al cliente confirmando la cita automáticamente.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -170,7 +230,8 @@
|
|||||||
: 'https://integrarepara-api.integrarepara.es';
|
: 'https://integrarepara-api.integrarepara.es';
|
||||||
|
|
||||||
let localServices = []; // Solo los "Sin Cita"
|
let localServices = []; // Solo los "Sin Cita"
|
||||||
let globalAllServices = []; // TODOS los servicios para calcular solapamientos
|
let globalAllServices = []; // TODOS para solapamientos
|
||||||
|
let pendingRequests = []; // Solicitudes del portal
|
||||||
let systemStatuses = [];
|
let systemStatuses = [];
|
||||||
let bizHours = { m_start: '09:00', m_end: '14:00', a_start: '16:00', a_end: '19:00' };
|
let bizHours = { m_start: '09:00', m_end: '14:00', a_start: '16:00', a_end: '19:00' };
|
||||||
|
|
||||||
@@ -188,11 +249,8 @@
|
|||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
|
|
||||||
if(data.ok && data.config && data.config.portal_settings) {
|
if(data.ok && data.config && data.config.portal_settings) {
|
||||||
// Extraemos horario comercial
|
|
||||||
const ps = data.config.portal_settings;
|
const ps = data.config.portal_settings;
|
||||||
if(ps.m_start) bizHours = { m_start: ps.m_start, m_end: ps.m_end, a_start: ps.a_start, a_end: ps.a_end };
|
if(ps.m_start) bizHours = { m_start: ps.m_start, m_end: ps.m_end, a_start: ps.a_start, a_end: ps.a_end };
|
||||||
|
|
||||||
// Extraemos colores
|
|
||||||
if(ps.app_settings) {
|
if(ps.app_settings) {
|
||||||
theme = ps.app_settings;
|
theme = ps.app_settings;
|
||||||
localStorage.setItem('app_theme', JSON.stringify(theme));
|
localStorage.setItem('app_theme', JSON.stringify(theme));
|
||||||
@@ -207,7 +265,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", async () => {
|
document.addEventListener("DOMContentLoaded", async () => {
|
||||||
if (!localStorage.getItem("token") || localStorage.getItem("role") !== 'operario') {
|
if (!localStorage.getItem("token") || (localStorage.getItem("role") !== 'operario' && localStorage.getItem("role") !== 'operario_cerrado')) {
|
||||||
window.location.href = "index.html"; return;
|
window.location.href = "index.html"; return;
|
||||||
}
|
}
|
||||||
await applyTheme();
|
await applyTheme();
|
||||||
@@ -225,23 +283,32 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function refreshData() {
|
async function refreshData() {
|
||||||
document.getElementById('servicesList').classList.add('hidden');
|
document.getElementById('contentWrapper').classList.add('hidden');
|
||||||
document.getElementById('loader').classList.remove('hidden');
|
document.getElementById('loader').classList.remove('hidden');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`${API_URL}/services/active`, {
|
const headers = { "Authorization": `Bearer ${localStorage.getItem("token")}` };
|
||||||
headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` }
|
|
||||||
});
|
|
||||||
const data = await res.json();
|
|
||||||
|
|
||||||
if (data.ok) {
|
// 1. Cargar servicios activos del operario
|
||||||
globalAllServices = data.services; // Guardamos TODOS para chequear la agenda real
|
const resActivos = await fetch(`${API_URL}/services/active`, { headers });
|
||||||
|
const dataActivos = await resActivos.json();
|
||||||
|
|
||||||
// Filtramos los SIN CITA
|
// 2. Cargar peticiones de cita pendientes de este operario
|
||||||
localServices = data.services.filter(s => {
|
const resReqs = await fetch(`${API_URL}/agenda/requests`, { headers });
|
||||||
|
const dataReqs = await resReqs.json();
|
||||||
|
|
||||||
|
if (dataActivos.ok && dataReqs.ok) {
|
||||||
|
globalAllServices = dataActivos.services;
|
||||||
|
pendingRequests = dataReqs.requests;
|
||||||
|
|
||||||
|
// Filtrar la lista normal de "Sin Cita"
|
||||||
|
localServices = globalAllServices.filter(s => {
|
||||||
if (s.provider === 'SYSTEM_BLOCK') return false;
|
if (s.provider === 'SYSTEM_BLOCK') return false;
|
||||||
const raw = s.raw_data || {};
|
const raw = s.raw_data || {};
|
||||||
|
// Si ya tiene fecha o si tiene una solicitud pendiente, no lo mostramos en el listado base
|
||||||
if (raw.scheduled_date && raw.scheduled_date.trim() !== "") return false;
|
if (raw.scheduled_date && raw.scheduled_date.trim() !== "") return false;
|
||||||
|
if (raw.appointment_status === 'pending') return false;
|
||||||
|
|
||||||
if (raw.status_operativo) {
|
if (raw.status_operativo) {
|
||||||
const st = systemStatuses.find(x => String(x.id) === String(raw.status_operativo));
|
const st = systemStatuses.find(x => String(x.id) === String(raw.status_operativo));
|
||||||
if (st && st.is_final) return false;
|
if (st && st.is_final) return false;
|
||||||
@@ -249,22 +316,151 @@
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
renderRequests();
|
||||||
renderServices();
|
renderServices();
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert("Error de conexión");
|
alert("Error de conexión");
|
||||||
} finally {
|
} finally {
|
||||||
document.getElementById('loader').classList.add('hidden');
|
document.getElementById('loader').classList.add('hidden');
|
||||||
document.getElementById('servicesList').classList.remove('hidden');
|
document.getElementById('contentWrapper').classList.remove('hidden');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// RENDERIZADO DE SOLICITUDES PENDIENTES
|
||||||
|
// ==========================================
|
||||||
|
function renderRequests() {
|
||||||
|
const reqSection = document.getElementById('requestsSection');
|
||||||
|
const reqList = document.getElementById('requestsList');
|
||||||
|
reqList.innerHTML = '';
|
||||||
|
|
||||||
|
if (pendingRequests.length === 0) {
|
||||||
|
reqSection.classList.add('hidden');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
reqSection.classList.remove('hidden');
|
||||||
|
|
||||||
|
pendingRequests.forEach(req => {
|
||||||
|
const raw = req.raw_data || {};
|
||||||
|
const name = raw["Nombre Cliente"] || raw["CLIENTE"] || "Cliente";
|
||||||
|
const rDate = formatDate(raw.requested_date);
|
||||||
|
const rTime = addOneHour(raw.requested_time); // Calculamos el tramo para mostrarlo
|
||||||
|
|
||||||
|
reqList.innerHTML += `
|
||||||
|
<div class="bg-blue-50 border border-blue-200 p-4 rounded-3xl flex justify-between items-center shadow-sm relative overflow-hidden">
|
||||||
|
<div class="absolute right-0 top-0 w-16 h-16 bg-blue-100 rounded-bl-full opacity-50 z-0"></div>
|
||||||
|
<div class="relative z-10 flex-1">
|
||||||
|
<p class="text-[9px] font-black text-blue-500 uppercase tracking-widest mb-0.5">Cita Solicitada</p>
|
||||||
|
<h3 class="font-black text-slate-800 text-sm leading-tight truncate">${name}</h3>
|
||||||
|
<p class="text-[10px] font-bold text-slate-600 mt-1">${rDate} | ${raw.requested_time} - ${rTime}</p>
|
||||||
|
</div>
|
||||||
|
<button onclick="openApproveModal(${req.id})" class="bg-blue-600 text-white w-10 h-10 rounded-full flex items-center justify-center shadow-lg active:scale-95 transition-transform shrink-0 ml-3 relative z-10">
|
||||||
|
<i data-lucide="chevron-right" class="w-5 h-5"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
});
|
||||||
|
lucide.createIcons();
|
||||||
|
}
|
||||||
|
|
||||||
|
function openApproveModal(id) {
|
||||||
|
const req = pendingRequests.find(r => r.id === id);
|
||||||
|
if(!req) return;
|
||||||
|
const raw = req.raw_data || {};
|
||||||
|
|
||||||
|
document.getElementById('appId').value = id;
|
||||||
|
document.getElementById('appRef').innerText = `Exp. #${req.service_ref || "S/R"}`;
|
||||||
|
document.getElementById('appName').innerText = raw["Nombre Cliente"] || "Cliente";
|
||||||
|
|
||||||
|
document.getElementById('appDate').innerText = formatDate(raw.requested_date);
|
||||||
|
document.getElementById('appTime').innerText = `Llegada aprox: ${raw.requested_time} - ${addOneHour(raw.requested_time)}`;
|
||||||
|
|
||||||
|
// Reseteamos el selector de duración a 1 hora por defecto
|
||||||
|
document.getElementById('appDurationInput').value = "60";
|
||||||
|
|
||||||
|
const modal = document.getElementById('approveModal');
|
||||||
|
const content = document.getElementById('approveModalContent');
|
||||||
|
modal.classList.remove('hidden');
|
||||||
|
void modal.offsetWidth;
|
||||||
|
modal.classList.remove('opacity-0');
|
||||||
|
content.classList.remove('translate-y-full');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function approveRequest() {
|
||||||
|
const id = document.getElementById('appId').value;
|
||||||
|
const duration = document.getElementById('appDurationInput').value;
|
||||||
|
|
||||||
|
const btn = document.getElementById('btnApprove');
|
||||||
|
const originalContent = btn.innerHTML;
|
||||||
|
btn.innerHTML = `<i data-lucide="loader-2" class="w-4 h-4 animate-spin"></i>`;
|
||||||
|
btn.disabled = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await fetch(`${API_URL}/agenda/requests/${id}/approve`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${localStorage.getItem("token")}` },
|
||||||
|
body: JSON.stringify({ duration: duration })
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res.ok) {
|
||||||
|
showToast("Cita Aceptada");
|
||||||
|
closeModal('approveModal', 'approveModalContent');
|
||||||
|
refreshData();
|
||||||
|
} else {
|
||||||
|
alert("Error al confirmar.");
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
alert("Error de conexión");
|
||||||
|
} finally {
|
||||||
|
btn.innerHTML = originalContent;
|
||||||
|
btn.disabled = false;
|
||||||
|
lucide.createIcons();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function rejectRequest() {
|
||||||
|
if(!confirm("¿Seguro que quieres rechazar este horario? El cliente recibirá un mensaje para elegir otro.")) return;
|
||||||
|
|
||||||
|
const id = document.getElementById('appId').value;
|
||||||
|
const btn = document.getElementById('btnReject');
|
||||||
|
const originalContent = btn.innerHTML;
|
||||||
|
btn.innerHTML = `<i data-lucide="loader-2" class="w-4 h-4 animate-spin"></i>`;
|
||||||
|
btn.disabled = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await fetch(`${API_URL}/agenda/requests/${id}/reject`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${localStorage.getItem("token")}` }
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res.ok) {
|
||||||
|
showToast("Cita Rechazada");
|
||||||
|
closeModal('approveModal', 'approveModalContent');
|
||||||
|
refreshData();
|
||||||
|
} else {
|
||||||
|
alert("Error al rechazar.");
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
alert("Error de conexión");
|
||||||
|
} finally {
|
||||||
|
btn.innerHTML = originalContent;
|
||||||
|
btn.disabled = false;
|
||||||
|
lucide.createIcons();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// RENDERIZADO DE LISTADO NORMAL (SIN CITA)
|
||||||
|
// ==========================================
|
||||||
function renderServices() {
|
function renderServices() {
|
||||||
const container = document.getElementById('servicesList');
|
const container = document.getElementById('servicesList');
|
||||||
|
const noDateSec = document.getElementById('noDateSection');
|
||||||
|
|
||||||
if (localServices.length === 0) {
|
if (localServices.length === 0) {
|
||||||
container.innerHTML = `
|
container.innerHTML = `
|
||||||
<div class="text-center py-12 bg-white rounded-3xl border border-slate-100 shadow-sm mt-4">
|
<div class="text-center py-12 bg-white rounded-3xl border border-slate-100 shadow-sm mt-2">
|
||||||
<div class="w-16 h-16 bg-emerald-50 text-emerald-500 rounded-full flex items-center justify-center mx-auto mb-4">
|
<div class="w-16 h-16 bg-emerald-50 text-emerald-500 rounded-full flex items-center justify-center mx-auto mb-4">
|
||||||
<i data-lucide="check-check" class="w-8 h-8"></i>
|
<i data-lucide="check-check" class="w-8 h-8"></i>
|
||||||
</div>
|
</div>
|
||||||
@@ -272,9 +468,19 @@
|
|||||||
<p class="text-xs text-slate-400 font-medium mt-1">No tienes avisos pendientes de agendar.</p>
|
<p class="text-xs text-slate-400 font-medium mt-1">No tienes avisos pendientes de agendar.</p>
|
||||||
</div>`;
|
</div>`;
|
||||||
lucide.createIcons();
|
lucide.createIcons();
|
||||||
|
|
||||||
|
// Si tampoco hay peticiones, ocultamos el título "Pendientes de fecha"
|
||||||
|
if (pendingRequests.length === 0) {
|
||||||
|
noDateSec.querySelector('h2').classList.add('hidden');
|
||||||
|
} else {
|
||||||
|
noDateSec.classList.add('hidden');
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
noDateSec.classList.remove('hidden');
|
||||||
|
noDateSec.querySelector('h2').classList.remove('hidden');
|
||||||
|
|
||||||
container.innerHTML = localServices.map(s => {
|
container.innerHTML = localServices.map(s => {
|
||||||
const raw = s.raw_data || {};
|
const raw = s.raw_data || {};
|
||||||
const name = raw["Nombre Cliente"] || raw["CLIENTE"] || "Asegurado";
|
const name = raw["Nombre Cliente"] || raw["CLIENTE"] || "Asegurado";
|
||||||
@@ -328,6 +534,26 @@
|
|||||||
return `${h}:${min}`;
|
return `${h}:${min}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function addOneHour(timeStr) {
|
||||||
|
if(!timeStr) return "";
|
||||||
|
let [h, m] = timeStr.split(':').map(Number);
|
||||||
|
let totalMins = h * 60 + m + 60;
|
||||||
|
let newH = Math.floor(totalMins / 60);
|
||||||
|
let newM = totalMins % 60;
|
||||||
|
return `${String(newH).padStart(2,'0')}:${String(newM).padStart(2,'0')}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatDate(dateStr) {
|
||||||
|
if (!dateStr) return "";
|
||||||
|
try {
|
||||||
|
const parts = dateStr.split('-');
|
||||||
|
if(parts.length !== 3) return dateStr;
|
||||||
|
const d = new Date(parts[0], parts[1]-1, parts[2]);
|
||||||
|
const opciones = { weekday: 'long', day: 'numeric', month: 'short' };
|
||||||
|
return d.toLocaleDateString('es-ES', opciones);
|
||||||
|
} catch(e) { return dateStr; }
|
||||||
|
}
|
||||||
|
|
||||||
function buildDayCarousel() {
|
function buildDayCarousel() {
|
||||||
const container = document.getElementById('dayCarousel');
|
const container = document.getElementById('dayCarousel');
|
||||||
container.innerHTML = "";
|
container.innerHTML = "";
|
||||||
@@ -419,8 +645,8 @@
|
|||||||
let startMins = timeToMins(startStr);
|
let startMins = timeToMins(startStr);
|
||||||
let endMins = timeToMins(endStr);
|
let endMins = timeToMins(endStr);
|
||||||
|
|
||||||
// Saltos de 15 minutos
|
// Saltos de 30 minutos (igual que en buscar.html)
|
||||||
for (let m = startMins; m + duration <= endMins; m += 15) {
|
for (let m = startMins; m + duration <= endMins; m += 30) {
|
||||||
if (!isOverlapping(m, m + duration, occupied)) {
|
if (!isOverlapping(m, m + duration, occupied)) {
|
||||||
const timeStr = minsToTime(m);
|
const timeStr = minsToTime(m);
|
||||||
const isSelected = timeStr === pickerSelectedTime;
|
const isSelected = timeStr === pickerSelectedTime;
|
||||||
@@ -447,7 +673,7 @@
|
|||||||
grid.classList.remove('hidden');
|
grid.classList.remove('hidden');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Si la hora que estaba seleccionada ya no sale (porque ampliaron la duración), la borramos
|
// Si la hora que estaba seleccionada ya no sale, la borramos
|
||||||
if (pickerSelectedTime && !grid.innerHTML.includes(`'${pickerSelectedTime}'`)) {
|
if (pickerSelectedTime && !grid.innerHTML.includes(`'${pickerSelectedTime}'`)) {
|
||||||
pickerSelectedTime = "";
|
pickerSelectedTime = "";
|
||||||
}
|
}
|
||||||
@@ -456,7 +682,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ==========================================
|
// ==========================================
|
||||||
// APERTURA DE MODAL Y GUARDADO
|
// APERTURA DE MODAL Y GUARDADO (MANUAL)
|
||||||
// ==========================================
|
// ==========================================
|
||||||
function openActionModal(id) {
|
function openActionModal(id) {
|
||||||
const s = localServices.find(x => x.id === id);
|
const s = localServices.find(x => x.id === id);
|
||||||
@@ -487,9 +713,9 @@
|
|||||||
content.classList.remove('translate-y-full');
|
content.classList.remove('translate-y-full');
|
||||||
}
|
}
|
||||||
|
|
||||||
function closeModal() {
|
function closeModal(modalId, contentId) {
|
||||||
const modal = document.getElementById('actionModal');
|
const modal = document.getElementById(modalId);
|
||||||
const content = document.getElementById('modalContent');
|
const content = document.getElementById(contentId);
|
||||||
|
|
||||||
modal.classList.add('opacity-0');
|
modal.classList.add('opacity-0');
|
||||||
content.classList.add('translate-y-full');
|
content.classList.add('translate-y-full');
|
||||||
@@ -537,7 +763,7 @@
|
|||||||
|
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
showToast("Cita Agendada en Calendario");
|
showToast("Cita Agendada en Calendario");
|
||||||
closeModal();
|
closeModal('actionModal', 'modalContent');
|
||||||
refreshData();
|
refreshData();
|
||||||
} else {
|
} else {
|
||||||
alert("Error al guardar la cita.");
|
alert("Error al guardar la cita.");
|
||||||
|
|||||||
Reference in New Issue
Block a user