Actualizar calendario.html
This commit is contained in:
183
calendario.html
183
calendario.html
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user