Acepto las condiciones de reserva y la política de privacidad
Condiciones de reserva
Reserva y puntualidad Mantendremos tu mesa reservada durante 15 minutos de cortesía. Si prevés llegar más tarde, te agradeceremos que nos lo comuniques. En caso contrario, no podremos garantizar la disponibilidad de la reserva.
Cancelaciones Las cancelaciones realizadas con menos de 24 horas de antelación no serán reembolsables en ningún caso.
Edad mínima El acceso está permitido únicamente a mayores de 14 años.
Mascotas Las mascotas son bienvenidas en nuestra terraza, siempre que permanezcan limpias, tranquilas, atadas y bajo la supervisión de su responsable, evitando en todo momento molestias al resto de los clientes. Nos reservamos el derecho de asignar la mesa que consideremos más adecuada. Durante la temporada de invierno, te rogamos que nos consultes antes de realizar la reserva.
Código de vestimenta Agradecemos una vestimenta smart casual: elegante, cómoda e informal.
Convivencia Te rogamos mantener un ambiente tranquilo, evitando alzar la voz y mostrando respeto tanto hacia los demás clientes como hacia nuestro equipo. Está permitido fumar únicamente en la zona habilitada para ello.
Restricciones alimentarias Si tienes alguna alergia, intolerancia o restricción alimentaria, te agradeceremos que nos lo comuniques antes de tu llegada. Una vez iniciado el servicio, no podemos garantizar que sea posible realizar modificaciones en el menú.
Con el fin de preservar el ambiente y el bienestar de todos los presentes, nos reservamos el derecho de solicitar el abandono del establecimiento en caso de comportamientos que alteren la convivencia o el normal desarrollo del servicio.
'; }).join('');
var offset = (new Date(flavorCalYear, flavorCalMonth, 1).getDay() + 6) % 7;
var dim = new Date(flavorCalYear, flavorCalMonth + 1, 0).getDate();
for (var i = 0; i < offset; i++) h += '';
var minStr = formatDateLocal(flavorMinDate);
var maxStr = formatDateLocal(flavorMaxDate);
for (var d = 1; d <= dim; d++) {
var date = new Date(flavorCalYear, flavorCalMonth, d);
var dateStr = formatDateLocal(date);
var isSel = flavorData.date === dateStr;
var isToday = date.toDateString() === today.toDateString();
var isPast = dateStr < minStr || dateStr > maxStr;
var isClosed = !isPast && flavorOpenDays.indexOf(date.getDay()) === -1;
var isBlocked = !isPast && !isClosed && flavorBlockedDates[dateStr];
var cls = 'flavor-cal-day';
if (isPast) cls += ' past';
else if (isClosed || isBlocked) cls += ' closed';
else if (isSel) cls += ' on';
else if (isToday) cls += ' today';
if (isPast || isClosed || isBlocked) {
h += '
' + d + '
';
} else {
h += '';
}
}
grid.innerHTML = h;
grid.querySelectorAll('.flavor-cal-day[data-date]').forEach(function(b) {
b.addEventListener('click', function() {
flavorData.date = b.dataset.date;
flavorData.time = null;
flavorRenderCalendar();
flavorLoadTimeSlots(flavorData.date);
});
});
}
document.getElementById('flavorCalPrev').addEventListener('click', function() {
var now = new Date();
if (flavorCalMonth <= now.getMonth() && flavorCalYear <= now.getFullYear()) return;
flavorCalMonth--;
if (flavorCalMonth < 0) { flavorCalMonth = 11; flavorCalYear--; }
flavorRenderCalendar();
});
document.getElementById('flavorCalNext').addEventListener('click', function() {
flavorCalMonth++;
if (flavorCalMonth > 11) { flavorCalMonth = 0; flavorCalYear++; }
flavorRenderCalendar();
});
// =====================
// TIME SLOTS
// =====================
function flavorLoadTimeSlots(dateStr) {
var container = document.getElementById('flavorSlots');
var guests = flavorData.guests || 2;
container.innerHTML = '
' + flavorI18n.loadingTimes + '
';
fetch(flavorRestUrl + 'restaurants/' + flavorRestaurantId + '/available-slots?date=' + dateStr + '&guests=' + guests)
.then(function(r) { return r.json(); })
.then(function(data) {
if (data.success && data.data && data.data.length > 0) {
var html = '';
data.data.forEach(function(slot) {
var timeShort = slot.time.substring(0, 5);
if (slot.available) {
var cls = 'flavor-slot' + (flavorData.time === timeShort ? ' on' : '');
html += '';
} else {
var reasonText = flavorI18n.notAvailable;
if (slot.reason === 'capacity') reasonText = flavorI18n.capacityFull;
else if (slot.reason === 'max_reservations') reasonText = flavorI18n.maxReservations;
else if (slot.reason === 'min_advance') reasonText = flavorI18n.minAdvance;
html += '';
}
});
container.innerHTML = html;
container.querySelectorAll('.flavor-slot:not(.busy)').forEach(function(b) {
b.addEventListener('click', function() {
flavorData.time = b.dataset.t;
container.querySelectorAll('.flavor-slot').forEach(function(s) { s.classList.remove('on'); });
b.classList.add('on');
flavorCheckStep1();
});
});
} else {
container.innerHTML = '
';
// Disparar snippet de tracking "reserva creada" (Meta Pixel, GA, etc. según lo que haya configurado el restaurante).
flavorRunTrackingSnippet('flavor-tracking-reservation');
// Si el depósito ya se ha cobrado en el mismo flujo (Stripe Elements dentro del form), el evento de pago
// también corresponde ahora; el retorno de Stripe con ?payment=success cubre el otro caso (Checkout externo).
if (depositAmount > 0) {
flavorRunTrackingSnippet('flavor-tracking-payment');
}
}
function flavorShowError(message) {
flavorContainer.innerHTML =
'
' +
'
\u2717
' +
'
' + flavorI18n.error + '
' +
'
' + message + '
' +
'' +
'
';
}
// =====================
// TOAST
// =====================
function flavorToast(message, type) {
type = type || 'warning';
var existing = document.querySelector('.flavor-toast-modal');
var existingOverlay = document.querySelector('.flavor-toast-overlay');
if (existing) existing.remove();
if (existingOverlay) existingOverlay.remove();
var overlay = document.createElement('div');
overlay.className = 'flavor-toast-overlay';
document.body.appendChild(overlay);
var toast = document.createElement('div');
toast.className = 'flavor-toast-modal';
toast.innerHTML = '
' + message + '
';
document.body.appendChild(toast);
function closeToast() {
toast.classList.remove('show');
overlay.classList.remove('show');
setTimeout(function() { toast.remove(); overlay.remove(); }, 250);
}
overlay.onclick = closeToast;
setTimeout(function() { overlay.classList.add('show'); toast.classList.add('show'); }, 10);
setTimeout(closeToast, 2500);
}
// =====================
// BLOCKED DATES & INIT
// =====================
fetch(flavorRestUrl + 'restaurants/' + flavorRestaurantId + '/blocked-dates')
.then(function(r) { return r.json(); })
.then(function(data) {
if (data.success && data.data) {
flavorBlockedDates = data.data;
flavorRenderCalendar();
flavorSelectFirstAvailableDate();
}
})
.catch(function() { flavorSelectFirstAvailableDate(); });
function flavorSelectFirstAvailableDate() {
var today = new Date(); today.setHours(0,0,0,0);
for (var i = 0; i <= flavorMaxAdvanceDays; i++) {
var check = new Date(today);
check.setDate(today.getDate() + i);
var dateStr = formatDateLocal(check);
if (flavorOpenDays.indexOf(check.getDay()) === -1) continue;
if (flavorBlockedDates[dateStr]) continue;
if (i === 0) { var hoursLeft = 24 - new Date().getHours(); if (hoursLeft < flavorMinAdvanceHours) continue; }
flavorSelectDate(dateStr);
return;
}
}
function flavorSelectDate(dateStr) {
flavorData.date = dateStr;
flavorData.time = null;
flavorRenderCalendar();
flavorLoadTimeSlots(dateStr);
}
// =====================
// GESTION DE RESERVAS
// =====================
var flavorCurrentReservation = null;
function flavorShowManage() {
document.querySelectorAll('.flavor-panel').forEach(function(s) { s.style.display = 'none'; });
document.querySelector('.flavor-stepper').style.display = 'none';
document.querySelector('.flavor-manage-view').style.display = 'block';
document.querySelector('.flavor-manage-search').style.display = 'block';
document.querySelector('.flavor-manage-result').style.display = 'none';
document.querySelector('.flavor-manage-modify').style.display = 'none';
}
function flavorHideManage() {
document.querySelector('.flavor-manage-view').style.display = 'none';
document.querySelectorAll('.flavor-panel').forEach(function(s) { s.style.display = 'none'; });
document.getElementById('flavorPanel1').style.display = 'block';
document.getElementById('flavorPanel1').classList.add('active');
document.querySelector('.flavor-stepper').style.display = 'flex';
flavorStep = 1;
flavorUpdateStepper();
flavorCurrentReservation = null;
}
function flavorManageTab(tab) {
document.querySelectorAll('.flavor-manage-tab').forEach(function(t) { t.classList.remove('active'); });
document.querySelectorAll('.flavor-manage-tab-content').forEach(function(c) { c.classList.remove('active'); });
document.querySelector('.flavor-manage-tab-content[data-tab="' + tab + '"]').classList.add('active');
event.target.classList.add('active');
}
function flavorSearchByCode() {
var code = document.getElementById('flavor-manage-code').value.trim();
if (!code) { flavorToast(flavorI18n.enterCode); return; }
flavorSearchReservation({ code: code });
}
function flavorSearchByData() {
var firstName = document.getElementById('flavor-manage-firstname').value.trim();
var lastName = document.getElementById('flavor-manage-lastname').value.trim();
var phone = document.getElementById('flavor-manage-phone').value.trim();
var date = document.getElementById('flavor-manage-date').value;
var time = document.getElementById('flavor-manage-time').value;
if (!firstName) { flavorToast(flavorI18n.enterName); return; }
if (!lastName) { flavorToast(flavorI18n.enterLastName); return; }
if (!phone) { flavorToast(flavorI18n.enterPhone); return; }
if (!date) { flavorToast(flavorI18n.enterDate); return; }
if (!time) { flavorToast(flavorI18n.enterTime); return; }
flavorSearchReservation({ first_name: firstName, last_name: lastName, phone: phone, date: date, time: time + ':00' });
}
function flavorSearchReservation(params) {
document.querySelector('.flavor-loader').style.display = 'flex';
var url = flavorRestUrl + 'reservations/search?restaurant_id=' + flavorRestaurantId;
if (params.code) { url += '&code=' + encodeURIComponent(params.code); }
else {
url += '&first_name=' + encodeURIComponent(params.first_name);
url += '&last_name=' + encodeURIComponent(params.last_name);
url += '&phone=' + encodeURIComponent(params.phone);
url += '&date=' + encodeURIComponent(params.date);
url += '&time=' + encodeURIComponent(params.time);
}
fetch(url)
.then(function(r) { return r.json(); })
.then(function(data) {
document.querySelector('.flavor-loader').style.display = 'none';
if (data.success && data.data) { flavorCurrentReservation = data.data; flavorShowReservationResult(data.data); }
else { flavorToast(data.message || flavorI18n.reservationNotFound, 'error'); }
})
.catch(function() { document.querySelector('.flavor-loader').style.display = 'none'; flavorToast(flavorI18n.searchError, 'error'); });
}
function flavorShowReservationResult(res) {
document.querySelector('.flavor-manage-search').style.display = 'none';
document.querySelector('.flavor-manage-result').style.display = 'block';
document.querySelector('.flavor-manage-modify').style.display = 'none';
var dateParts = res.reservation_date.split('-');
var dateStr = dateParts[2] + '/' + dateParts[1] + '/' + dateParts[0];
var statusText = flavorI18n.confirmed, statusClass = 'confirmed';
if (res.status === 'pending') { statusText = flavorI18n.pending; statusClass = 'pending'; }
if (res.status === 'cancelled') { statusText = flavorI18n.cancelled; statusClass = 'cancelled'; }
document.querySelector('.flavor-reservation-code').textContent = res.reservation_code;
document.querySelector('.flavor-reservation-status').textContent = statusText;
document.querySelector('.flavor-reservation-status').className = 'flavor-reservation-status ' + statusClass;
document.querySelector('.flavor-reservation-date').textContent = dateStr;
document.querySelector('.flavor-reservation-time').textContent = res.reservation_time.substring(0, 5);
document.querySelector('.flavor-reservation-guests').textContent = res.guests + ' ' + flavorI18n.people;
document.querySelector('.flavor-reservation-name').textContent = res.first_name + ' ' + res.last_name;
document.querySelector('.flavor-reservation-email').textContent = res.email || '';
document.querySelector('.flavor-reservation-phone').textContent = res.phone || '';
var allergiesRow = document.querySelector('.flavor-reservation-allergies');
if (res.allergies && res.allergies.trim()) { allergiesRow.style.display = 'flex'; document.querySelector('.flavor-reservation-allergies-text').textContent = res.allergies; } else { allergiesRow.style.display = 'none'; }
var dietaryRow = document.querySelector('.flavor-reservation-dietary');
if (res.dietary_restrictions && res.dietary_restrictions.trim()) { dietaryRow.style.display = 'flex'; document.querySelector('.flavor-reservation-dietary-text').textContent = res.dietary_restrictions; } else { dietaryRow.style.display = 'none'; }
var notesRow = document.querySelector('.flavor-reservation-notes');
if (res.notes && res.notes.trim()) { notesRow.style.display = 'flex'; document.querySelector('.flavor-reservation-notes-text').textContent = res.notes; } else { notesRow.style.display = 'none'; }
var menuSection = document.querySelector('.flavor-reservation-menu');
var hasMenu = (res.menu_starters && res.menu_starters.trim()) || (res.menu_mains && res.menu_mains.trim()) || (res.menu_desserts && res.menu_desserts.trim());
if (hasMenu) {
menuSection.style.display = 'block';
var st = document.querySelector('.flavor-reservation-starters'); if (res.menu_starters && res.menu_starters.trim()) { st.style.display = 'block'; document.querySelector('.flavor-reservation-starters-text').textContent = res.menu_starters; } else { st.style.display = 'none'; }
var mn = document.querySelector('.flavor-reservation-mains'); if (res.menu_mains && res.menu_mains.trim()) { mn.style.display = 'block'; document.querySelector('.flavor-reservation-mains-text').textContent = res.menu_mains; } else { mn.style.display = 'none'; }
var ds = document.querySelector('.flavor-reservation-desserts'); if (res.menu_desserts && res.menu_desserts.trim()) { ds.style.display = 'block'; document.querySelector('.flavor-reservation-desserts-text').textContent = res.menu_desserts; } else { ds.style.display = 'none'; }
} else { menuSection.style.display = 'none'; }
var actionsDiv = document.querySelector('.flavor-manage-actions');
if (res.status === 'cancelled') { actionsDiv.style.display = 'none'; } else { actionsDiv.style.display = 'flex'; }
}
function flavorShowModify() {
document.querySelector('.flavor-manage-result').style.display = 'none';
document.querySelector('.flavor-manage-modify').style.display = 'block';
var res = flavorCurrentReservation;
var select = document.getElementById('flavor-modify-guests');
select.innerHTML = '';
for (var i = flavorMinGuests; i <= Math.min(flavorMaxGuests, 12); i++) {
var opt = document.createElement('option');
opt.value = i; opt.textContent = i + ' ' + (i === 1 ? flavorI18n.person : flavorI18n.people);
if (i === parseInt(res.guests)) opt.selected = true;
select.appendChild(opt);
}
document.getElementById('flavor-modify-date').value = res.reservation_date;
document.getElementById('flavor-modify-first-name').value = res.first_name || '';
document.getElementById('flavor-modify-last-name').value = res.last_name || '';
document.getElementById('flavor-modify-email').value = res.email || '';
document.getElementById('flavor-modify-phone').value = res.phone || '';
document.getElementById('flavor-modify-allergies').value = res.allergies || '';
document.getElementById('flavor-modify-dietary').value = res.dietary_restrictions || '';
document.getElementById('flavor-modify-notes').value = res.notes || '';
document.getElementById('flavor-modify-shared-table').checked = parseInt(res.shared_table) === 1;
document.getElementById('flavor-modify-starters').value = res.menu_starters || '';
document.getElementById('flavor-modify-mains').value = res.menu_mains || '';
document.getElementById('flavor-modify-desserts').value = res.menu_desserts || '';
flavorLoadModifyTimeSlots(res.reservation_date);
}
function flavorLoadModifyTimeSlots(date) {
var select = document.getElementById('flavor-modify-time');
select.innerHTML = '';
var guests = document.getElementById('flavor-modify-guests').value;
fetch(flavorRestUrl + 'restaurants/' + flavorRestaurantId + '/available-slots?date=' + date + '&guests=' + guests)
.then(function(r) { return r.json(); })
.then(function(data) {
select.innerHTML = '';
if (data.success && data.data && data.data.length > 0) {
data.data.forEach(function(slot) {
if (slot.available) { var opt = document.createElement('option'); opt.value = slot.time.substring(0, 5); opt.textContent = slot.time.substring(0, 5); select.appendChild(opt); }
});
var currentTime = flavorCurrentReservation.reservation_time;
var hasCurrentTime = false;
for (var i = 0; i < select.options.length; i++) { if (select.options[i].value === currentTime) { hasCurrentTime = true; break; } }
if (!hasCurrentTime && date === flavorCurrentReservation.reservation_date) {
var opt = document.createElement('option'); opt.value = currentTime; opt.textContent = currentTime.substring(0, 5) + ' ' + flavorI18n.current; opt.selected = true; select.insertBefore(opt, select.firstChild);
}
} else { select.innerHTML = ''; }
})
.catch(function() { select.innerHTML = ''; });
}
document.getElementById('flavor-modify-date').addEventListener('change', function() { flavorLoadModifyTimeSlots(this.value); });
function flavorBackToResult() {
document.querySelector('.flavor-manage-modify').style.display = 'none';
document.querySelector('.flavor-manage-result').style.display = 'block';
}
function flavorSaveModification() {
if (!flavorCurrentReservation) return;
var newTime = document.getElementById('flavor-modify-time').value;
if (!newTime) { flavorToast(flavorI18n.selectTimeFirst); return; }
document.querySelector('.flavor-loader').style.display = 'flex';
fetch(flavorRestUrl + 'reservations/' + flavorCurrentReservation.id + '/modify', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
guests: parseInt(document.getElementById('flavor-modify-guests').value),
reservation_date: document.getElementById('flavor-modify-date').value,
reservation_time: newTime,
first_name: document.getElementById('flavor-modify-first-name').value,
last_name: document.getElementById('flavor-modify-last-name').value,
email: document.getElementById('flavor-modify-email').value,
phone: document.getElementById('flavor-modify-phone').value,
allergies: document.getElementById('flavor-modify-allergies').value,
dietary_restrictions: document.getElementById('flavor-modify-dietary').value,
notes: document.getElementById('flavor-modify-notes').value,
shared_table: document.getElementById('flavor-modify-shared-table').checked ? 1 : 0,
menu_starters: document.getElementById('flavor-modify-starters').value,
menu_mains: document.getElementById('flavor-modify-mains').value,
menu_desserts: document.getElementById('flavor-modify-desserts').value
})
})
.then(function(r) { return r.json(); })
.then(function(data) {
document.querySelector('.flavor-loader').style.display = 'none';
if (data.success) { flavorCurrentReservation = data.data; flavorShowReservationResult(data.data); flavorToast(flavorI18n.modifySuccess, 'success'); }
else { flavorToast(data.message || flavorI18n.modifyError, 'error'); }
})
.catch(function() { document.querySelector('.flavor-loader').style.display = 'none'; flavorToast(flavorI18n.modifyError, 'error'); });
}
function flavorCancelReservation() {
if (!flavorCurrentReservation) return;
if (!confirm(flavorI18n.cancelConfirm)) return;
document.querySelector('.flavor-loader').style.display = 'flex';
fetch(flavorRestUrl + 'reservations/' + flavorCurrentReservation.id + '/cancel-public', { method: 'PUT', headers: { 'Content-Type': 'application/json' } })
.then(function(r) { return r.json(); })
.then(function(data) {
document.querySelector('.flavor-loader').style.display = 'none';
if (data.success) { flavorCurrentReservation.status = 'cancelled'; flavorShowReservationResult(flavorCurrentReservation); flavorToast(flavorI18n.cancelSuccess, 'success'); }
else { flavorToast(data.message || flavorI18n.cancelError, 'error'); }
})
.catch(function() { document.querySelector('.flavor-loader').style.display = 'none'; flavorToast(flavorI18n.cancelError, 'error'); });
}
// == Init ==
flavorRenderCalendar();
flavorCheckStep1();
// Detect manage_code in URL
(function() {
var urlParams = new URLSearchParams(window.location.search);
var manageCode = urlParams.get('manage_code');
if (manageCode || window.location.hash === '#manage') {
flavorShowManage();
setTimeout(function() { var el = document.getElementById('manage'); if (el) { window.scrollTo({ top: window.pageYOffset + el.getBoundingClientRect().top - 20, behavior: 'smooth' }); } }, 500);
if (manageCode) { document.getElementById('flavor-manage-code').value = manageCode; setTimeout(function() { flavorSearchReservation({ code: manageCode }); }, 300); }
}
})();
// Detectar retorno de Stripe Checkout externo con ?payment=success (link de pago enviado por email).
// Dispara el snippet de tracking de pago del depósito. Se ejecuta una sola vez y limpiamos el
// parámetro de la URL para que un F5 no vuelva a disparar el evento.
(function() {
try {
var params = new URLSearchParams(window.location.search);
if (params.get('payment') === 'success') {
flavorRunTrackingSnippet('flavor-tracking-payment');
// Limpiar los parámetros payment=success&code=... de la URL sin recargar
if (window.history && window.history.replaceState) {
params.delete('payment');
params.delete('code');
var qs = params.toString();
var cleanUrl = window.location.pathname + (qs ? '?' + qs : '') + window.location.hash;
window.history.replaceState({}, document.title, cleanUrl);
}
}
} catch (e) {}
})();