Actualizar calendario.html

This commit is contained in:
2026-03-28 21:30:17 +00:00
parent ebc2fe19ca
commit 8e6471ac86

View File

@@ -48,6 +48,10 @@
/* Estilos para el chat */ /* Estilos para el chat */
.msg-me { background-color: #dcfce7; border-bottom-right-radius: 4px; border: 1px solid #bbf7d0; align-self: flex-end; } .msg-me { background-color: #dcfce7; border-bottom-right-radius: 4px; border: 1px solid #bbf7d0; align-self: flex-end; }
.msg-other { background-color: #f1f5f9; border-bottom-left-radius: 4px; border: 1px solid #e2e8f0; align-self: flex-start; } .msg-other { background-color: #f1f5f9; border-bottom-left-radius: 4px; border: 1px solid #e2e8f0; align-self: flex-start; }
/* Modal Nuevo Servicio */
.input-modern { @apply w-full bg-slate-50 border border-slate-200 px-4 py-3 rounded-xl text-sm font-semibold text-slate-700 outline-none transition-all focus:border-blue-500 focus:bg-white focus:ring-2 focus:ring-blue-100; }
.label-modern { @apply block text-[10px] font-black text-slate-500 uppercase tracking-widest mb-1.5 ml-1; }
</style> </style>
</head> </head>
<body class="text-slate-800 font-sans antialiased h-screen flex flex-col overflow-hidden relative text-left"> <body class="text-slate-800 font-sans antialiased h-screen flex flex-col overflow-hidden relative text-left">
@@ -64,6 +68,9 @@
<div id="topNotificationBadge" class="hidden bg-red-500 text-white text-[10px] font-black px-1.5 py-0.5 rounded-full animate-bounce shadow-sm border border-white">!</div> <div id="topNotificationBadge" class="hidden bg-red-500 text-white text-[10px] font-black px-1.5 py-0.5 rounded-full animate-bounce shadow-sm border border-white">!</div>
</div> </div>
</div> </div>
<button onclick="openNewServiceModal()" class="w-10 h-10 shrink-0 bg-primary-dynamic rounded-full flex items-center justify-center text-white shadow-md active:scale-90 transition-transform mr-1">
<i data-lucide="plus" class="w-5 h-5"></i>
</button>
<div class="flex gap-1 shrink-0"> <div class="flex gap-1 shrink-0">
<button onclick="changeWeek(-1)" class="w-10 h-10 bg-white rounded-full flex items-center justify-center text-slate-600 border border-slate-200 active:bg-slate-50 shadow-sm"><i data-lucide="chevron-left" class="w-5 h-5"></i></button> <button onclick="changeWeek(-1)" class="w-10 h-10 bg-white rounded-full flex items-center justify-center text-slate-600 border border-slate-200 active:bg-slate-50 shadow-sm"><i data-lucide="chevron-left" class="w-5 h-5"></i></button>
<button onclick="changeWeek(1)" class="w-10 h-10 bg-white rounded-full flex items-center justify-center text-slate-600 border border-slate-200 active:bg-slate-50 shadow-sm"><i data-lucide="chevron-right" class="w-5 h-5"></i></button> <button onclick="changeWeek(1)" class="w-10 h-10 bg-white rounded-full flex items-center justify-center text-slate-600 border border-slate-200 active:bg-slate-50 shadow-sm"><i data-lucide="chevron-right" class="w-5 h-5"></i></button>
@@ -282,6 +289,69 @@
</div> </div>
</div> </div>
<div id="newServiceModal" class="translate-y-full transition-transform duration-300 fixed inset-0 z-[150] flex flex-col bg-slate-50" style="display:none;">
<div class="pt-safe bg-white border-b border-slate-200 flex justify-between items-center p-4 shrink-0 shadow-sm">
<button type="button" onclick="closeNewServiceModal()" class="bg-slate-100 p-2.5 rounded-full text-slate-600 active:scale-90 transition-transform"><i data-lucide="x" class="w-6 h-6"></i></button>
<h2 class="font-black text-slate-800 text-sm uppercase tracking-widest text-center">Nuevo Aviso</h2>
<div class="w-10"></div>
</div>
<form onsubmit="saveNewServiceApp(event)" class="flex-1 overflow-y-auto p-5 space-y-5 no-scrollbar pb-32">
<div class="bg-white p-5 rounded-3xl shadow-sm border border-slate-200 space-y-4 text-left">
<p class="text-[10px] font-black text-slate-400 uppercase tracking-widest flex items-center gap-1.5"><i data-lucide="user" class="w-4 h-4 text-blue-500"></i> Datos Cliente</p>
<div>
<label class="label-modern">Teléfono</label>
<input type="tel" id="nsPhone" oninput="searchClientApp(this.value)" placeholder="Ej: 600123456" class="input-modern" required>
</div>
<div>
<label class="label-modern">Nombre</label>
<input type="text" id="nsName" placeholder="Nombre completo" class="input-modern" required>
</div>
<div>
<label class="label-modern">Dirección</label>
<input type="text" id="nsAddress" placeholder="Calle, número, piso..." class="input-modern" required>
</div>
</div>
<div class="bg-white p-5 rounded-3xl shadow-sm border border-slate-200 space-y-4 text-left">
<p class="text-[10px] font-black text-slate-400 uppercase tracking-widest flex items-center gap-1.5"><i data-lucide="calendar" class="w-4 h-4 text-emerald-500"></i> Cita y Avería</p>
<div class="grid grid-cols-2 gap-3">
<div>
<label class="label-modern">Día</label>
<input type="date" id="nsDate" class="input-modern" required>
</div>
<div>
<label class="label-modern">Hora</label>
<input type="time" id="nsTime" class="input-modern" required>
</div>
</div>
<div>
<label class="label-modern">Tiempo Estimado</label>
<div class="relative">
<select id="nsDuration" class="input-modern appearance-none pr-8 cursor-pointer">
<option value="15">15 min</option>
<option value="30">30 min</option>
<option value="45">45 min</option>
<option value="60" selected>1 h</option>
<option value="90">1.5 h</option>
<option value="120">2 h</option>
<option value="180">3 h</option>
<option value="240">4 h</option>
</select>
<i data-lucide="chevron-down" class="w-4 h-4 absolute right-3 top-1/2 -translate-y-1/2 text-slate-400 pointer-events-none"></i>
</div>
</div>
<div>
<label class="label-modern">Descripción del Trabajo</label>
<textarea id="nsDesc" rows="3" placeholder="Detalle de la avería..." class="input-modern resize-none" required></textarea>
</div>
</div>
<button type="submit" id="btnSaveNewApp" class="w-full bg-primary-dynamic text-white font-black py-4 rounded-2xl shadow-xl flex items-center justify-center gap-2 uppercase tracking-widest active:scale-95 transition-all text-xs">
<i data-lucide="check-circle" class="w-5 h-5"></i> Crear y Citar
</button>
</form>
</div>
<div id="toast" class="fixed top-8 left-1/2 -translate-x-1/2 bg-slate-900 text-white px-6 py-3 rounded-full shadow-2xl z-[200] font-bold text-xs uppercase tracking-widest flex items-center gap-2 transition-all duration-300 opacity-0 pointer-events-none transform -translate-y-10"> <div id="toast" class="fixed top-8 left-1/2 -translate-x-1/2 bg-slate-900 text-white px-6 py-3 rounded-full shadow-2xl z-[200] font-bold text-xs uppercase tracking-widest flex items-center gap-2 transition-all duration-300 opacity-0 pointer-events-none transform -translate-y-10">
<i data-lucide="check" class="w-4 h-4 text-emerald-400"></i> <i data-lucide="check" class="w-4 h-4 text-emerald-400"></i>
<span id="toastMsg">Guardado</span> <span id="toastMsg">Guardado</span>
@@ -1262,6 +1332,119 @@
setTimeout(() => { t.classList.add('opacity-0', 'pointer-events-none', '-translate-y-10'); t.classList.remove('translate-y-0'); }, 2000); setTimeout(() => { t.classList.add('opacity-0', 'pointer-events-none', '-translate-y-10'); t.classList.remove('translate-y-0'); }, 2000);
} }
// =====================================
// FUNCIONES DE ALTA RÁPIDA (OPERARIO)
// =====================================
function openNewServiceModal() {
document.getElementById('nsPhone').value = "";
document.getElementById('nsName').value = "";
document.getElementById('nsAddress').value = "";
document.getElementById('nsDesc').value = "";
document.getElementById('nsTime').value = "";
document.getElementById('nsDate').value = selectedDateStr;
const modal = document.getElementById('newServiceModal');
modal.style.display = 'flex';
setTimeout(() => modal.classList.remove('translate-y-full'), 10);
safeLoadIcons();
}
function closeNewServiceModal() {
const modal = document.getElementById('newServiceModal');
modal.classList.add('translate-y-full');
setTimeout(() => modal.style.display = 'none', 300);
}
async function searchClientApp(phone) {
if (phone.length < 9) {
document.getElementById('nsName').classList.remove('bg-emerald-50');
return;
}
try {
const res = await fetch(`${API_URL}/clients/search?phone=${phone}`, {
headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` }
});
const data = await res.json();
if (data.ok && data.client) {
const nameInput = document.getElementById('nsName');
if (!nameInput.value) {
nameInput.value = data.client.full_name;
nameInput.classList.add('bg-emerald-50');
}
if (!document.getElementById('nsAddress').value && data.client.addresses?.length > 0) {
document.getElementById('nsAddress').value = data.client.addresses[0];
}
}
} catch (e) {}
}
async function saveNewServiceApp(e) {
e.preventDefault();
const btn = document.getElementById('btnSaveNewApp');
const originalHTML = btn.innerHTML;
btn.innerHTML = `<i data-lucide="loader-2" class="w-5 h-5 animate-spin"></i> Procesando...`;
btn.disabled = true;
try {
const token = localStorage.getItem("token");
const payloadDecoded = JSON.parse(atob(token.split('.')[1]));
const myWorkerId = payloadDecoded.sub;
const createData = {
phone: document.getElementById('nsPhone').value,
name: document.getElementById('nsName').value,
address: document.getElementById('nsAddress').value,
description: document.getElementById('nsDesc').value,
guild_id: null,
assigned_to: myWorkerId,
duration_minutes: document.getElementById('nsDuration').value,
is_urgent: false,
is_company: false,
company_name: 'Particular',
company_ref: null,
mode: 'manual'
};
const resCreate = await fetch(`${API_URL}/services/manual-high`, {
method: 'POST',
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${token}` },
body: JSON.stringify(createData)
});
const dataCreate = await resCreate.json();
if (!dataCreate.ok || !dataCreate.id) throw new Error("No se pudo crear el servicio");
const citadoSt = systemStatuses.find(st => st.name.toLowerCase().includes('citado'));
const statusMapId = citadoSt ? String(citadoSt.id) : null;
const date = document.getElementById('nsDate').value;
const time = document.getElementById('nsTime').value;
const duration = document.getElementById('nsDuration').value;
await fetch(`${API_URL}/services/set-appointment/${dataCreate.id}`, {
method: 'PUT',
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${token}` },
body: JSON.stringify({
date: date,
time: time,
duration_minutes: duration,
status_operativo: statusMapId
})
});
closeNewServiceModal();
showToast("¡Aviso Creado y Citado!");
refreshData();
} catch (err) {
showToast("Error al guardar", true);
} finally {
btn.innerHTML = originalHTML;
btn.disabled = false;
safeLoadIcons();
}
}
function logout() { localStorage.clear(); window.location.href = "index.html"; } function logout() { localStorage.clear(); window.location.href = "index.html"; }
</script> </script>
</body> </body>