Files
Portal/cita.html
2026-02-21 16:04:45 +00:00

216 lines
11 KiB
HTML

<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Agendar Cita - IntegraRepara</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://unpkg.com/lucide@latest"></script>
<style>
body { background-color: #f8fafc; }
.fade-in { animation: fadeIn 0.4s ease-out forwards; }
@keyframes fadeIn { from { opacity: 0; transform: translateY(15px); } to { opacity: 1; transform: translateY(0); } }
.slot-btn.selected { background-color: #2563eb; color: white; border-color: #2563eb; }
</style>
</head>
<body class="text-slate-800 font-sans antialiased min-h-screen flex flex-col items-center">
<div id="loader" class="fixed inset-0 bg-slate-50 z-50 flex flex-col items-center justify-center">
<div class="w-12 h-12 border-4 border-blue-200 border-t-blue-600 rounded-full animate-spin mb-4"></div>
<p class="text-sm font-bold text-slate-500 animate-pulse">Buscando rutas de tu técnico...</p>
</div>
<main id="mainContent" class="hidden w-full max-w-md mx-auto min-h-screen flex flex-col relative pb-10">
<header class="bg-white p-6 pt-10 border-b border-slate-200 sticky top-0 z-10 shrink-0">
<button onclick="window.history.back()" class="text-slate-400 hover:text-slate-800 mb-4 transition-colors">
<i data-lucide="arrow-left" class="w-6 h-6"></i>
</button>
<h1 class="text-2xl font-black tracking-tight leading-none text-slate-800">¿Cuándo te viene mejor?</h1>
<p class="text-sm text-slate-500 font-medium mt-2">Selecciona tu preferencia y te mostraremos los horarios en los que el técnico estará por tu zona.</p>
</header>
<div class="p-6 flex-1 flex flex-col gap-6 fade-in">
<div id="step1">
<div class="grid grid-cols-2 gap-4">
<button onclick="selectPreference('morning')" id="btnMorning" class="bg-white border-2 border-slate-200 p-6 rounded-3xl flex flex-col items-center gap-3 hover:border-blue-400 hover:bg-blue-50 transition-all text-slate-600">
<i data-lucide="sun" class="w-10 h-10 text-amber-500"></i>
<span class="font-black uppercase text-sm">Mañanas</span>
</button>
<button onclick="selectPreference('afternoon')" id="btnAfternoon" class="bg-white border-2 border-slate-200 p-6 rounded-3xl flex flex-col items-center gap-3 hover:border-blue-400 hover:bg-blue-50 transition-all text-slate-600">
<i data-lucide="sunset" class="w-10 h-10 text-orange-500"></i>
<span class="font-black uppercase text-sm">Tardes</span>
</button>
</div>
</div>
<div id="step2" class="hidden space-y-6 fade-in">
<h3 class="font-black text-slate-800 uppercase tracking-widest text-xs">1. Elige un día disponible</h3>
<select id="daySelect" onchange="renderSlots()" class="w-full bg-white border-2 border-slate-200 p-4 rounded-2xl font-bold text-slate-700 outline-none focus:border-blue-500">
<option value="">Selecciona un día...</option>
</select>
<div id="hoursContainer" class="hidden space-y-4 fade-in">
<h3 class="font-black text-slate-800 uppercase tracking-widest text-xs">2. Elige la hora aproximada</h3>
<div id="slotsGrid" class="grid grid-cols-3 gap-3">
</div>
</div>
</div>
<div class="mt-auto pt-6">
<button id="btnConfirm" onclick="confirmBooking()" class="w-full bg-blue-600 text-white font-black py-4 rounded-2xl shadow-lg opacity-50 pointer-events-none transition-all flex justify-center items-center gap-2 uppercase tracking-widest text-xs">
Confirmar Cita <i data-lucide="check-circle" class="w-4 h-4"></i>
</button>
</div>
</div>
</main>
<div id="successScreen" class="hidden w-full max-w-md mx-auto p-6 flex flex-col items-center justify-center min-h-screen text-center fade-in bg-blue-600 text-white">
<div class="w-24 h-24 bg-white text-blue-600 rounded-full flex items-center justify-center mb-6 shadow-2xl">
<i data-lucide="clock" class="w-12 h-12"></i>
</div>
<h2 class="text-3xl font-black mb-2">Solicitud Enviada</h2>
<p class="font-medium opacity-90 mb-8">Estamos revisando la ruta de tu técnico. Recibirás un mensaje en breve confirmando tu cita.</p>
<button onclick="window.location.href = window.location.href.replace('cita.html', 'portal.html')" class="bg-slate-900 text-white font-black py-3 px-8 rounded-xl shadow-lg uppercase text-xs tracking-widest">
Volver al Resumen
</button>
</div>
<script>
const API_URL = window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1'
? 'http://localhost:3000' : 'https://integrarepara-api.integrarepara.es';
let agendaData = [];
let currentPreference = null;
let selectedDate = null;
let selectedTime = null;
document.addEventListener("DOMContentLoaded", async () => {
lucide.createIcons();
const urlParams = new URLSearchParams(window.location.search);
const token = urlParams.get('token');
const serviceId = urlParams.get('service');
if (!token || !serviceId) {
alert("Enlace inválido"); return;
}
try {
// Pedir la disponibilidad inteligente al servidor
const res = await fetch(`${API_URL}/public/portal/${token}/slots?serviceId=${serviceId}`);
const data = await res.json();
if (data.ok) {
agendaData = data.days;
document.getElementById('loader').classList.add('hidden');
document.getElementById('mainContent').classList.remove('hidden');
} else {
alert(data.error || "No se han podido cargar los horarios");
}
} catch (e) { alert("Error de conexión"); }
});
function selectPreference(pref) {
currentPreference = pref;
selectedTime = null;
updateConfirmButton();
// UI feedback
document.getElementById('btnMorning').classList.remove('border-blue-500', 'bg-blue-50');
document.getElementById('btnAfternoon').classList.remove('border-blue-500', 'bg-blue-50');
if (pref === 'morning') document.getElementById('btnMorning').classList.add('border-blue-500', 'bg-blue-50');
if (pref === 'afternoon') document.getElementById('btnAfternoon').classList.add('border-blue-500', 'bg-blue-50');
document.getElementById('step2').classList.remove('hidden');
// Llenar el selector de días solo con los días que tengan huecos en esa preferencia
const select = document.getElementById('daySelect');
select.innerHTML = '<option value="">Selecciona un día...</option>';
agendaData.forEach(day => {
const hasSlots = pref === 'morning' ? day.morning.length > 0 : day.afternoon.length > 0;
if (hasSlots) {
select.innerHTML += `<option value="${day.date}" class="uppercase">${day.displayDate}</option>`;
}
});
document.getElementById('hoursContainer').classList.add('hidden');
}
function renderSlots() {
selectedDate = document.getElementById('daySelect').value;
selectedTime = null;
updateConfirmButton();
if (!selectedDate) {
document.getElementById('hoursContainer').classList.add('hidden');
return;
}
const dayObj = agendaData.find(d => d.date === selectedDate);
const slots = currentPreference === 'morning' ? dayObj.morning : dayObj.afternoon;
const grid = document.getElementById('slotsGrid');
grid.innerHTML = slots.map(time => `
<button onclick="selectTime('${time}', this)" class="slot-btn bg-white border border-slate-200 py-3 rounded-xl font-black text-slate-600 shadow-sm transition-all hover:bg-slate-50">
${time}
</button>
`).join('');
document.getElementById('hoursContainer').classList.remove('hidden');
}
function selectTime(time, btnEl) {
selectedTime = time;
document.querySelectorAll('.slot-btn').forEach(b => b.classList.remove('selected'));
btnEl.classList.add('selected');
updateConfirmButton();
}
function updateConfirmButton() {
const btn = document.getElementById('btnConfirm');
if (selectedDate && selectedTime) {
btn.classList.remove('opacity-50', 'pointer-events-none');
} else {
btn.classList.add('opacity-50', 'pointer-events-none');
}
}
async function confirmBooking() {
const urlParams = new URLSearchParams(window.location.search);
const token = urlParams.get('token');
const serviceId = urlParams.get('service');
const btn = document.getElementById('btnConfirm');
btn.innerHTML = '<i data-lucide="loader-2" class="w-4 h-4 animate-spin"></i> Guardando...';
lucide.createIcons();
try {
const res = await fetch(`${API_URL}/public/portal/${token}/book`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ serviceId, date: selectedDate, time: selectedTime })
});
const data = await res.json();
if (data.ok) {
document.getElementById('mainContent').classList.add('hidden');
document.getElementById('successScreen').classList.remove('hidden');
document.getElementById('successScreen').classList.add('flex');
} else {
alert("Error al confirmar la cita. Es posible que el hueco ya se haya ocupado.");
btn.innerHTML = 'Confirmar Cita <i data-lucide="check-circle" class="w-4 h-4"></i>';
lucide.createIcons();
}
} catch(e) {
alert("Error de conexión");
btn.innerHTML = 'Confirmar Cita <i data-lucide="check-circle" class="w-4 h-4"></i>';
lucide.createIcons();
}
}
</script>
</body>
</html>