/* ============================================================ RESERVAS — disponibilidad por franja horaria Combina cupo automático (máx. personas/hora) + bloqueos manuales del administrador (día completo o franja puntual). Todo se guarda en este dispositivo (localStorage). ============================================================ */ const { HOURS: RES_HOURS } = window; const RES_CAP_KEY = "do_res_capacity"; const RES_LIST_KEY = "do_reservations"; const RES_BLOCKS_KEY = "do_res_blocks"; const DEFAULT_CAPACITY = 15; // personas por franja horaria function loadCapacity() { const n = parseInt(localStorage.getItem(RES_CAP_KEY), 10); return Number.isFinite(n) && n > 0 ? n : DEFAULT_CAPACITY; } function saveCapacity(n) { localStorage.setItem(RES_CAP_KEY, String(n)); } function loadReservations() { try { return JSON.parse(localStorage.getItem(RES_LIST_KEY) || "[]"); } catch (e) { return []; } } function saveReservations(list) { localStorage.setItem(RES_LIST_KEY, JSON.stringify(list)); } function loadBlocks() { try { const b = JSON.parse(localStorage.getItem(RES_BLOCKS_KEY) || "null"); if (b && typeof b === "object") return { days: b.days || [], slots: b.slots || [] }; } catch (e) {} return { days: [], slots: [] }; } function saveBlocks(b) { localStorage.setItem(RES_BLOCKS_KEY, JSON.stringify(b)); } // Franjas horarias del día (cada hora, dentro del horario de atención) function getHourSlots() { const start = Math.ceil(RES_HOURS.open); const end = Math.floor(RES_HOURS.close); const out = []; for (let h = start; h < end; h++) { const period = h >= 12 ? "pm" : "am"; const h12 = h > 12 ? h - 12 : h; out.push({ value: String(h).padStart(2, "0") + ":00", label: h12 + ":00 " + period }); } return out; } function bookedCount(reservations, date, time) { return reservations .filter(r => r.date === date && r.time === time && r.status !== "cancelada") .reduce((s, r) => s + (r.people || 0), 0); } function slotStatus(date, time, reservations, blocks, capacity) { const dayBlocked = blocks.days.includes(date); const slotBlocked = blocks.slots.includes(date + "|" + time); const booked = bookedCount(reservations, date, time); const full = booked >= capacity; return { available: !dayBlocked && !slotBlocked && !full, dayBlocked, slotBlocked, full, booked, capacity }; } /* ---- Selector de franja horaria (para el cliente) ---- */ function SlotPicker({ date, time, onPick }) { const [, force] = useState(0); useEffect(() => { const onStorage = () => force(x => x + 1); window.addEventListener("storage", onStorage); return () => window.removeEventListener("storage", onStorage); }, []); if (!date) return
Elige primero una fecha para ver las horas disponibles.
; const reservations = loadReservations(); const blocks = loadBlocks(); const capacity = loadCapacity(); const slots = getHourSlots(); const dayFull = blocks.days.includes(date); return (No hay disponibilidad este día. Elige otra fecha.
} {!dayFull && slots.map(s => { const st = slotStatus(date, s.value, reservations, blocks, capacity); return ( ); })}Aún no hay reservas registradas desde la carta.
} {upcoming.map(r => (