Blog

The 11 best restaurants in Pla de Mallorca

Pla de Mallorca is one of the regions of the island where you will find a large number of bars and restaurants where you can taste rustic dishes from Mallorcan and Mediterranean cuisine. Many of these restaurants, rebuilt in old wineries (cellers mallorquins) or in traditional houses and possessions, you can try delicious dishes and wines, but also signature and creative cuisine.

 

The restaurants of Pla de Mallorca with delicious dishes and wines.

  1. Restaurante Treurer Olive Grove.
  2. Celler Sa Torre de Santa Eugenia.
  3. Brut Restaurant, Llubi.
  4. Restaurant S’Hostal d’Algaida.
  5. Sa Casa Mallorquina, Algaida.
  6. Celler Bar Randa.
  7. Santuari de Cura Restaurant.
  8. Sa Cuina De N’Aina, Sencelles.
  9. Es Celler de Petra.
  10. Antic Celler Son Toreo, Sineu.
  11. Celler Can Font, Sineu.

1. Treurer Olive Grove.

Nestled in an incomparable setting, the Treurer Olive Grove Restaurant stands as a true viewpoint overlooking the mountains of Cura and Randa. Located in a historic estate, surrounded by an extensive young olive grove, Treurer offers a unique dining experience that combines the beauty of the Mallorcan landscape with the culinary richness of the region.

A dream setting

The setting of the restaurant is simply spectacular. The historic estate, guarded by young olive trees, creates an atmosphere of peace and tranquility that blends perfectly with the culinary experience. Every visit to Treurer is a delight for the senses, where nature and gastronomy intertwine harmoniously.

Passion and creativity in the kitchen

Chef José Cortés is passionate about Mediterranean gastronomy and a fervent advocate of local and seasonal products. Trained at prestigious culinary schools and with vast experience in renowned restaurants, Cortés brings unparalleled creativity and passion to each dish.

A menu that celebrates the flavors of Mallorca

Treurer Restaurant ‘s menu is inspired by Mediterranean cuisine, with an emphasis on fresh, seasonal ingredients. Main courses include “Cannelloni of sea bass, Majorcan shrimp and its own bisque” and “Black pork tenderloin with tumbet”. Vegetarian options, such as the “Eggplant trunk stuffed with seasonal vegetables, with ramallet tomato and herbs from the garden”, are also a delight for the palate.

Appetizers and starters, such as the “Croquettes of camaiot, mushrooms and fennel” and the “Treurer salad with fresh and seasonal products”, highlight the best local products. Desserts, including traditional Mallorcan options with a modern twist, such as “Pudin de ensaimada with crumble and meringue milk ice cream”, are the perfect finishing touch to a memorable meal.

Themed gastronomic experiences at the Treurer Restaurant

Treurer Restaurant not only offers exquisite cuisine, but also stands out for its themed dining experiences that elevate each visit to a unique and memorable level. These experiences are designed to immerse diners in a culinary journey that goes beyond the simple meal, combining flavors, ambiance and culture on every occasion.

2. Celler Sa Torre de Santa Eugenia.

In the municipality of Santa Eugenia, in an old manor house in the Pla de Mallorca, you will find Sa Torre, a family farmhouse that offers nature and tranquillity, but also a Celler converted into a haute cuisine restaurant. In this winery, where the traditional decoration has been preserved, they have a tasting menu that changes weekly, thus demonstrating the chefs’ capacity for innovation.

In addition to enjoying an incredible atmosphere in which you’ll breathe the Majorcan past of the area and enjoying an exceptional menu, at Celler Sa Torre you’ll be able to choose one of the Spanish or Majorcan wines from their private collection, among which there is one of their own harvest, the Natiu finca Sa Torre.

3. Brut Restaurant, Llubi.

This restaurant offers a luxurious tasting experience. With capacity for twelve people, Brut is characterized by a contrasting cuisine based on small producers where local, fresh and seasonal products are essential. Plus, their dishes pair perfectly with craft beer or kombucha, or a select variety of wines.

The fusion of traditional and contemporary techniques characterizes the preparations made in this restaurant where. On the other hand, you will be able to enjoy a cosy and private atmosphere, ideal for very special occasions.

4. Restaurant S’Hostal d’Algaida.

If what you want is to try Majorcan cuisine specialties such as sopes, tumbet or pa amb oli, this is one of the best restaurants in Pla de Mallorca that will offer you these dishes. This cosy restaurant with antique decoration has been offering its services since 1961. Since then they have become known for elaborations such as fried Majorcan, snails and porcella (pork). In addition, it has a wide variety of typical desserts such as ensaïmada, and delicious homemade ice cream.

5. Sa Casa Mallorquina, Algaida.

Also in the municipality of Algaida, Sa Casa Mallorquina is an incredible place to enjoy excellent Italian food made with Majorcan products. As they explain, the spirit of their cuisine is a balance between flavors and colours, a perfect chromatic combination and, indeed, their dishes are worth admiring as well as tasting.

This restaurant has multiple spaces where to enjoy its delicious dishes – two patios, one inside and one outside, a terrace and an interior room in which a beautiful fireplace stands out. But in addition, in its underground cellar delicious wines are discovered, both Majorcan and Italian.

6. Celler Bar Randa.

This winery, well known by the locals, is the perfect place for those who want to try typical Majorcan food at reasonable prices. The Celler Bar Randa is located at the beginning of the road that leads to Santuari de Cura. Its aesthetics as a stone tavern, its many chairs, its multiple tools, pots and ancient utensils, together with its cosy fireplace give this place a rustic touch both inside and on its terrace.

7. Santuari de Cura Restaurant.

On of the best restaurants in Pla de Mallorca

Located on Puig de Randa, the Santuari de Cura restaurant was established in the old library and dining room of the convent. From this place, diners can not only taste a menu based on Majorcan cuisine, with exquisite preparations and local and fresh products, but they can also observe the formidable views of the bay of Palma and the villages of Pla de Mallorca.

Since this restaurant can be visited after a long walk and ascent to Puig de Randa, it also offers a cafeteria service throughout the day that many hikers take advantage of to recover energy while observing the spectacular views, or while resting at the Sanctuary square by the shade of a tree.

8. Sa Cuina De N’Aina, Sencelles.

This family restaurant built in a typical Majorcan house, preserving the traditional rustic appearance of es Pla, offers Mediterranean cuisine with typical dishes from the island, as well as a large selection of national Majorcan wines and delicious homemade desserts and ice creams. Sa Cuina De N’Aina has three lounges and an ideal terrace for the hottest days.

9. Es Celler de Petra.

Es Celler de Petra is characterized by the use of local products and special care and dedication in the preparation of its dishes. Brut rice, soups, aubergines and snails are the gastronomic specialties of this traditional Majorcan cuisine restaurant. Their desserts, based on ancient recipes, are also spectacular.

This restaurant, built in the cellars of a Majorcan country house in Petra, stands out for its high ceilings and its traditional and rustic decoration, perfect for enjoying delicious dishes and blending with local diners and their endearing hubbub.

10. Antic Celler Son Toreo, Sineu.

The oldest Majorcan cellar in Sineu, with a construction that dates back to shortly after the first millennium AD. according to archaeologists, it is a spectacular cellar with high vaults and crushed stone walls. Renovated to house a tapas bar, it offers traditional dishes such as pa amb oli with typical Balearic products (Mahon cheese, sobrasada, camaiot, butifarró, bacon…) that, together with quality Majorcan wines and other grilled meats make up a tasty menu that won’t leave anyone indifferent.

11. Celler Can Font, Sineu.

In the cellar of a building dating back to the 12th century, a restaurant has been adapted where you can taste typical Majorcan cuisine, surrounded by old winebags and rustic furniture. One of the star dishes of this restaurant is Sineu’s frit which, together with tripe or tongue with capers, form a spectacular triad. In the kitchens of Celler Can Font you can only see fresh local products that give rise to traditional and rustic dishes, but equally delicious.

Susbcribe to our newsletter

Reservar mesa ▲

Reserva tu mesa en Qanat

Selecciona fecha, hora y número de comensales

1
Book your table
2
Pay and confirm
Book your table
Guests
Date
Time

Select a date to see available times

Your details
I prefer shared table
Confirm your reservation
Guests
—
Date
—
Time
—
First name
—
Email
—
Phone
—
Shared table
—
Notes
—
Card guarantee
A deposit is required to confirm your reservation
0.00 €
Refundable if you cancel at least 24 hours in advance
Card details
I accept the booking conditions and the privacy policy
Booking conditions

Reservation and punctuality 
We will hold your reserved table for 15 minutes as a courtesy. If you expect to arrive later, we would appreciate it if you could inform us. Otherwise, we cannot guarantee the availability of the reservation.

Cancellations
Cancellations made with less than 24 hours' notice will not be refundable under any circumstances.

Minimum age
Access is permitted only to those aged 14 and over.

Pets
Pets are welcome on our terrace, provided they remain clean, calm, on a lead and under the supervision of their owner, avoiding at all times any disturbance to other guests. We reserve the right to assign the table we consider most appropriate. During the winter season, please consult us before making your reservation.

Dress code
We appreciate smart casual attire: elegant, comfortable and informal.

Conduct
We ask you to maintain a calm atmosphere, avoiding raising your voice and showing respect both towards other guests and towards our team. Smoking is permitted only in the designated area.

Dietary restrictions
If you have any allergies, intolerances or dietary restrictions, we would appreciate it if you could inform us before your arrival. Once service has begun, we cannot guarantee that it will be possible to make modifications to the menu.

In order to preserve the atmosphere and wellbeing of all those present, we reserve the right to request that you leave the establishment in the event of behaviour that disrupts the peaceful environment or the normal running of service.

Processing...

Manage reservation

← Back to booking

Menu selection

Starters
Main courses
Desserts

Modify reservation

Menu selection

'; }).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 = '

' + flavorI18n.noTimesAvailable + '

'; } }) .catch(function() { container.innerHTML = '

' + flavorI18n.errorLoadingTimes + '

'; }); } // ===================== // CHIPS (Guests) // ===================== document.getElementById('flavorChips').addEventListener('click', function(e) { var c = e.target.closest('.flavor-chip'); if (!c) return; flavorData.guests = parseInt(c.dataset.val); document.querySelectorAll('.flavor-chip').forEach(function(x) { x.classList.remove('on'); }); c.classList.add('on'); if (flavorData.date) { flavorData.time = null; flavorLoadTimeSlots(flavorData.date); } flavorCheckStep1(); }); // ===================== // SHARED TABLE TOGGLE // ===================== var sharedToggle = document.getElementById('flavorSharedToggle'); if (sharedToggle) { sharedToggle.addEventListener('click', function() { flavorData.shared = !flavorData.shared; sharedToggle.classList.toggle('on', flavorData.shared); }); } // ===================== // VALIDATIONS // ===================== function flavorCheckStep1() { var hasBooking = !!(flavorData.date && flavorData.time); if (flavorTotalSteps === 2) { // In 2-step mode, step 1 also contains data fields var hasData = ['flavor-first-name','flavor-last-name','flavor-email','flavor-phone'].every(function(id) { return document.getElementById(id).value.trim() !== ''; }); document.getElementById('flavorBtn1Next').disabled = !(hasBooking && hasData); } else { document.getElementById('flavorBtn1Next').disabled = !hasBooking; } } function flavorCheckStep2() { var ok = ['flavor-first-name','flavor-last-name','flavor-email','flavor-phone'].every(function(id) { return document.getElementById(id).value.trim() !== ''; }); var btn2 = document.getElementById('flavorBtn2Next'); if (btn2) btn2.disabled = !ok; } function flavorCheckStep3() { var chk = document.getElementById('flavor-accept-terms'); // El checkbox solo existe si procede; si no está, el botón queda disponible document.getElementById('flavorBtn3Confirm').disabled = chk ? !chk.checked : false; } ['flavor-first-name','flavor-last-name','flavor-email','flavor-phone'].forEach(function(id) { document.getElementById(id).addEventListener('input', function() { flavorCheckStep2(); if (flavorTotalSteps === 2) flavorCheckStep1(); }); }); (function(){ var chk = document.getElementById('flavor-accept-terms'); if (chk) chk.addEventListener('change', flavorCheckStep3); })(); function flavorValidateStep2() { var fn = document.getElementById('flavor-first-name').value.trim(); var ln = document.getElementById('flavor-last-name').value.trim(); var em = document.getElementById('flavor-email').value.trim(); var ph = document.getElementById('flavor-phone').value.trim(); if (!fn) { flavorToast(flavorI18n.nameRequired); return false; } if (!ln) { flavorToast(flavorI18n.lastNameRequired); return false; } if (!em) { flavorToast(flavorI18n.emailRequired); return false; } if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(em)) { flavorToast(flavorI18n.emailInvalid); return false; } if (!ph) { flavorToast(flavorI18n.phoneRequired); return false; } return true; } // ===================== // SUMMARY // ===================== function flavorBuildSummary() { document.getElementById('sumGuests').textContent = flavorData.guests + ' ' + (flavorData.guests === 1 ? flavorI18n.person : flavorI18n.people); if (flavorData.date) { var parts = flavorData.date.split('-'); var d = new Date(parseInt(parts[0]), parseInt(parts[1]) - 1, parseInt(parts[2])); var dn = flavorI18n.dayNames[d.getDay()].toLowerCase(); document.getElementById('sumDate').textContent = dn + ', ' + parseInt(parts[2]) + ' de ' + flavorI18n.months[parseInt(parts[1]) - 1].toLowerCase() + ' ' + parts[0]; } document.getElementById('sumTime').textContent = flavorData.time ? flavorData.time + 'h' : '\u2014'; var fn = document.getElementById('flavor-first-name').value.trim(); var ln = document.getElementById('flavor-last-name').value.trim(); document.getElementById('sumName').textContent = (fn + ' ' + ln).trim() || '\u2014'; document.getElementById('sumEmail').textContent = document.getElementById('flavor-email').value.trim() || '\u2014'; var pre = document.getElementById('flavor-prefix').value; var ph = document.getElementById('flavor-phone').value.trim(); document.getElementById('sumPhone').textContent = ph ? pre + ph : '\u2014'; document.getElementById('sumShared').textContent = flavorData.shared ? flavorI18n.yes : flavorI18n.no; document.getElementById('sumNotes').textContent = document.getElementById('flavor-notes').value.trim() || '\u2014'; var depositAmount = flavorCalculateDeposit(); var depositBox = document.getElementById('flavorDepositBox'); var cardBox = document.getElementById('flavorCardInputBox'); if (depositAmount > 0 && flavorStripeKey) { depositBox.style.display = 'block'; cardBox.style.display = 'block'; document.getElementById('flavorDepAmount').textContent = depositAmount.toFixed(2) + ' \u20AC'; var perPerson = flavorDepositPerPerson > 0 ? (flavorData.guests + ' \u00D7 ' + flavorDepositPerPerson.toFixed(2) + ' \u20AC ' + flavorI18n.perPerson) : ''; document.getElementById('flavorDepSub').textContent = perPerson; document.getElementById('flavor-submit-text').style.display = 'none'; document.getElementById('flavor-submit-text-pay').style.display = 'inline'; } else { depositBox.style.display = 'none'; cardBox.style.display = 'none'; document.getElementById('flavor-submit-text').style.display = 'inline'; document.getElementById('flavor-submit-text-pay').style.display = 'none'; } flavorGuaranteeRender(); } // ===================== // NAVIGATION BUTTONS // ===================== document.getElementById('flavorBtn1Next').addEventListener('click', function() { if (!flavorData.guests) { flavorToast(flavorI18n.selectGuests); return; } if (!flavorData.date) { flavorToast(flavorI18n.selectDate); return; } if (!flavorData.time) { flavorToast(flavorI18n.selectTime); return; } if (flavorTotalSteps === 2 && !flavorValidateStep2()) return; flavorGoStep(2); }); var btn2Back = document.getElementById('flavorBtn2Back'); if (btn2Back) btn2Back.addEventListener('click', function() { flavorGoStep(1); }); var btn2Next = document.getElementById('flavorBtn2Next'); if (btn2Next) btn2Next.addEventListener('click', function() { if (!flavorValidateStep2()) return; flavorGoStep(3); }); document.getElementById('flavorBtn3Back').addEventListener('click', function() { flavorGoStep(flavorTotalSteps === 2 ? 1 : 2); }); document.getElementById('flavorBtn3Confirm').addEventListener('click', function() { flavorSubmit(); }); // Stepper click document.querySelectorAll('.flavor-step-ind').forEach(function(el) { el.addEventListener('click', function() { var target = parseInt(el.dataset.step); if (target < flavorStep) flavorGoStep(target); }); }); // ===================== // DEPOSIT & STRIPE // ===================== function flavorCalculateDeposit() { if (!flavorRequireDeposit) return 0; return flavorDepositPerReservation + (flavorDepositPerPerson * (flavorData.guests || 0)); } function flavorGuaranteeTotal() { if (!flavorGuarantee || flavorRequireDeposit || !flavorStripeKey) return 0; var g = flavorData.guests || 0; return g >= flavorGuaranteeMin ? flavorGuaranteePP * g : 0; } function flavorMoney(v) { var s = v.toFixed(2); return (flavorGI18n.lang === 'en' ? s : s.replace('.', ',')) + ' \u20AC'; } function flavorGuaranteeRender() { var box = document.getElementById('flavorGuaranteeBox'); if (!box) return; var total = flavorGuaranteeTotal(); box.parentNode.classList.toggle('has-guarantee', total > 0); if (total <= 0) { box.style.display = 'none'; return; } var tpl = flavorGuaranteeHours > 0 ? flavorGI18n.terms : flavorGI18n.termsNoHours; document.getElementById('flavorGTerms').textContent = tpl.replace('{hours}', flavorGuaranteeHours).replace('{pp}', flavorMoney(flavorGuaranteePP)).replace('{total}', flavorMoney(total)); box.style.display = 'block'; document.getElementById('flavorCardInputBox').style.display = 'block'; } function flavorSubmitWithGuarantee() { var acc = document.getElementById('flavor-guarantee-accept'); if (acc && !acc.checked) { flavorToast(flavorGI18n.acceptRequired); return; } if (!flavorStripe || !flavorCardElement) { flavorToast(flavorGI18n.error); return; } var submitBtn = document.getElementById('flavorBtn3Confirm'); var oldHtml = submitBtn.innerHTML; submitBtn.disabled = true; submitBtn.innerHTML = '' + flavorGI18n.processing + ''; var postData = flavorGetPostData(); var customerName = (postData.first_name + ' ' + postData.last_name).trim(); var fail = function(msg) { submitBtn.disabled = false; submitBtn.innerHTML = oldHtml; var e = document.getElementById('flavor-card-errors'); if (e) e.textContent = msg || flavorGI18n.error; flavorToast(msg || flavorGI18n.error); }; fetch(flavorRestUrl + 'guarantee/setup-intent', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ shortcode_key: flavorShortcodeKey, guests: flavorData.guests, name: customerName, email: postData.email }) }) .then(function(r) { return r.json(); }) .then(function(d) { if (!d || !d.success) throw new Error((d && d.message) || flavorGI18n.error); return flavorStripe.confirmCardSetup(d.client_secret, { payment_method: { card: flavorCardElement, billing_details: { name: customerName, email: postData.email } } }); }) .then(function(res) { if (res.error) throw new Error(res.error.message); postData.guarantee_setup_intent = res.setupIntent.id; postData.guarantee_accepted = 1; document.querySelector('.flavor-loader').style.display = 'flex'; return fetch(flavorRestUrl + 'reservations', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(postData) }); }) .then(function(r) { return r.json(); }) .then(function(r) { document.querySelector('.flavor-loader').style.display = 'none'; if (!r.success) throw new Error(r.message || flavorI18n.errorCreate); submitBtn.disabled = false; submitBtn.innerHTML = oldHtml; flavorShowSuccess(r.reservation.reservation || r.reservation); }) .catch(function(err) { document.querySelector('.flavor-loader').style.display = 'none'; fail(err && err.message); }); } function flavorInitStripe() { var depositAmount = flavorCalculateDeposit(); if ((depositAmount > 0 || flavorGuaranteeTotal() > 0) && flavorStripeKey && !flavorStripe) { flavorStripe = Stripe(flavorStripeKey); var elements = flavorStripe.elements({locale: "en"}); flavorCardElement = elements.create('card', { style: { base: { fontSize: '14px', color: '#1a1a1a', fontFamily: 'Inter, -apple-system, sans-serif', '::placeholder': { color: '#c0c0c0' } }, invalid: { color: '#dc2626' } } }); flavorCardElement.mount('#flavor-card-element'); flavorCardElement.on('change', function(event) { flavorCardComplete = !!event.complete; flavorCardError = event.error ? event.error.message : ''; document.getElementById('flavor-card-errors').textContent = flavorCardError; }); } } // ===================== // SUBMIT // ===================== function flavorSubmit() { var _chk = document.getElementById('flavor-accept-terms'); if (_chk && !_chk.checked) { flavorToast(flavorI18n.acceptTerms); return; } // In 2-step mode, validate step2 fields before submit if (flavorTotalSteps === 2 && !flavorValidateStep2()) return; if (flavorGuaranteeTotal() > 0) { flavorSubmitWithGuarantee(); return; } var depositAmount = flavorCalculateDeposit(); if (depositAmount > 0 && flavorStripeKey && flavorCardElement) { flavorSubmitWithPayment(); } else { flavorSubmitWithoutPayment(); } } function flavorGetPostData() { var prefix = document.getElementById('flavor-prefix').value; var phone = document.getElementById('flavor-phone').value.trim(); return { restaurant_id: flavorRestaurantId, shortcode_key: flavorShortcodeKey, guests: flavorData.guests, reservation_date: flavorData.date, reservation_time: flavorData.time, first_name: document.getElementById('flavor-first-name').value, last_name: document.getElementById('flavor-last-name').value, email: document.getElementById('flavor-email').value, phone: prefix + phone, allergies: document.getElementById('flavor-allergies').value, dietary_restrictions: '', notes: document.getElementById('flavor-notes').value, shared_table: flavorData.shared ? 1 : 0, // Idioma del formulario: decide el idioma de los emails de la reserva y del cliente lang: "en" }; } function flavorSubmitWithoutPayment() { document.querySelector('.flavor-loader').style.display = 'flex'; fetch(flavorRestUrl + 'reservations', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(flavorGetPostData()) }) .then(function(r) { return r.json(); }) .then(function(r) { document.querySelector('.flavor-loader').style.display = 'none'; if (r.success) { flavorShowSuccess(r.reservation.reservation || r.reservation); } else { flavorShowError(r.message || flavorI18n.errorCreate); } }) .catch(function() { document.querySelector('.flavor-loader').style.display = 'none'; flavorShowError(flavorI18n.error); }); } // Tarjeta completa (lo dice Stripe Elements) y cobro pendiente reutilizable: cada intento fallido // (número incompleto, tarjeta rechazada…) ya no crea un pago nuevo «Incompleto» en Stripe. var flavorCardComplete = false, flavorCardError = '', flavorPendingIntent = null; function flavorSubmitWithPayment() { if (!flavorCardComplete) { var msg = flavorCardError || "Please complete your card details"; document.getElementById('flavor-card-errors').textContent = msg; flavorToast(msg); return; } var submitBtn = document.getElementById('flavorBtn3Confirm'); submitBtn.disabled = true; submitBtn.innerHTML = '' + flavorI18n.paymentProcessing + ''; var postData = flavorGetPostData(); var customerName = postData.first_name + ' ' + postData.last_name; var customerEmail = postData.email; var intentSig = [flavorRestaurantId, flavorData.guests, customerEmail].join('|'); var intentReq = (flavorPendingIntent && flavorPendingIntent.sig === intentSig) ? Promise.resolve({ success: true, data: { client_secret: flavorPendingIntent.secret } }) : fetch(flavorRestUrl + 'payment/create-intent', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ restaurant_id: flavorRestaurantId, guests: flavorData.guests, name: customerName, email: customerEmail }) }).then(function(resp) { return resp.json(); }); intentReq .then(function(paymentData) { if (!paymentData.success) throw new Error(paymentData.message || flavorI18n.errorPayment); flavorPendingIntent = { sig: intentSig, secret: paymentData.data.client_secret }; return flavorStripe.confirmCardPayment(paymentData.data.client_secret, { payment_method: { card: flavorCardElement, billing_details: { name: customerName, email: customerEmail } } }).then(function(result) { if (result.error) { // Ya cobrado (p. ej. doble clic): se sigue con la reserva con ese mismo pago if (result.error.payment_intent && result.error.payment_intent.status === 'succeeded') return result.error.payment_intent.id; throw new Error(result.error.message); } flavorPendingIntent = null; return result.paymentIntent.id; }); }) .then(function(paymentIntentId) { postData.payment_intent_id = paymentIntentId; postData.deposit_paid = 1; return fetch(flavorRestUrl + 'reservations', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(postData) }); }) .then(function(response) { return response.json(); }) .then(function(r) { if (!r.success) throw new Error(r.message || flavorI18n.errorCreate); submitBtn.disabled = false; flavorShowSuccess(r.reservation.reservation || r.reservation); }) .catch(function(error) { submitBtn.disabled = false; submitBtn.innerHTML = '' + flavorI18n.paymentProcessing + ''; document.getElementById('flavor-card-errors').textContent = error.message; flavorToast(error.message); }); } // ===================== // SUCCESS / ERROR // ===================== // Ejecuta un snippet de tracking pegado por el restaurante desde el backoffice. // El snippet se sirve como
'; }; var rows = row(flavorI18n.name, esc(postData.first_name) + ' ' + esc(postData.last_name)) + row(flavorI18n.email, esc(postData.email)) + row(flavorI18n.phone, esc(postData.phone)) + row(flavorI18n.sharedTable, flavorData.shared ? flavorI18n.yes : flavorI18n.no); if (postData.allergies && postData.allergies.trim()) rows += '
' + String(flavorI18n.allergies).replace(/:\s*$/, '') + '' + esc(postData.allergies) + '
'; if (postData.notes && postData.notes.trim()) rows += row(flavorI18n.notes, esc(postData.notes)); var depositAmount = flavorCalculateDeposit(); if (depositAmount > 0) rows += row(flavorI18n.depositPaid, depositAmount.toFixed(2) + ' €'); if (flavorGuaranteeTotal() > 0) rows += row(flavorGI18n.row, esc(flavorGI18n.saved)); // Calendario: .ics (Apple, Outlook, móvil) y Google Calendar. Duración orientativa de 2 h. var calHtml = ''; if (d && flavorData.time) { var hm = String(flavorData.time).split(':'), start = new Date(d.getTime()); start.setHours(+hm[0], +hm[1] || 0, 0, 0); var end = new Date(start.getTime() + 2 * 3600000); var f = function(x) { var p = function(n) { return (n < 10 ? '0' : '') + n; }; return x.getFullYear() + p(x.getMonth() + 1) + p(x.getDate()) + 'T' + p(x.getHours()) + p(x.getMinutes()) + '00'; }; var title = (flavorI18n.okAt || '%s').replace('%s', R.name || ''); var descr = flavorI18n.okCode + ': ' + code + ' · ' + flavorData.guests + ' ' + flavorI18n.people; var icsEsc = function(s) { return String(s || '').replace(/([,;\\])/g, '\\$1').replace(/\n/g, '\\n'); }; var ics = ['BEGIN:VCALENDAR', 'VERSION:2.0', 'PRODID:-//bookings.rest//ES', 'BEGIN:VEVENT', 'UID:' + code + '@bookings.rest', 'DTSTAMP:' + f(new Date()), 'DTSTART:' + f(start), 'DTEND:' + f(end), 'SUMMARY:' + icsEsc(title), 'LOCATION:' + icsEsc(R.address || ''), 'DESCRIPTION:' + icsEsc(descr), 'END:VEVENT', 'END:VCALENDAR'].join('\r\n'); var icsUrl = 'data:text/calendar;charset=utf-8,' + encodeURIComponent(ics); var gUrl = 'https://calendar.google.com/calendar/render?action=TEMPLATE&text=' + encodeURIComponent(title) + '&dates=' + f(start) + '/' + f(end) + '&details=' + encodeURIComponent(descr) + '&location=' + encodeURIComponent(R.address || ''); calHtml = '' + flavorI18n.okCalendar + '' + 'Google Calendar'; } var place = ''; if (R.name || R.address) { place = '

' + esc(R.name || '') + '' + (R.address ? '
' + esc(R.address) : '') + (R.maps ? ' · ' + flavorI18n.okDirections + '' : '') + '

'; } flavorContainer.innerHTML = '
' + '
' + '

' + flavorI18n.reservationConfirmed + '

' + place + '
' + '
' + flavorI18n.date.replace(/:$/, '') + '' + esc(dateStr) + '
' + '
' + flavorI18n.time.replace(/:$/, '') + '' + esc(flavorData.time || '') + '
' + '
' + flavorI18n.guests.replace(/:$/, '') + '' + esc(flavorData.guests) + '
' + '
' + '
' + flavorI18n.okCode + '' + esc(code) + '' + '
' + '
' + rows + '
' + '

' + (flavorI18n.okMail || '').replace('%s', '' + esc(postData.email) + '') + '

' + '
' + calHtml + '' + '
' + '
'; var cp = document.getElementById('flavorOkCopy'); if (cp) cp.addEventListener('click', function() { var done = function() { cp.textContent = flavorI18n.okCopied; setTimeout(function() { cp.textContent = flavorI18n.okCopy; }, 2000); }; if (navigator.clipboard && navigator.clipboard.writeText) navigator.clipboard.writeText(code).then(done, function() {}); }); try { flavorContainer.scrollIntoView({behavior: 'smooth', block: 'start'}); } catch (e) {} if (typeof flavorRunTrackingSnippet === 'function') { // 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, el evento de pago también corresponde ahora 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(); }); // Día pedido por URL (?date=AAAA-MM-DD, p. ej. desde el botón del email del menú del día). // Solo se usa si ese día se puede reservar; si no, se sigue con el primer día disponible. function flavorTryUrlDate() { var q; try { q = new URLSearchParams(window.location.search).get('date'); } catch (e) { return false; } if (!q || !/^\d{4}-\d{2}-\d{2}$/.test(q)) return false; var today = (typeof flavorTodayInTz === 'function') ? flavorTodayInTz() : new Date(); today.setHours(0, 0, 0, 0); var p = q.split('-'), d = new Date(+p[0], +p[1] - 1, +p[2]); var diff = Math.round((d - today) / 86400000); if (diff < 0 || diff > flavorMaxAdvanceDays) return false; if (flavorOpenDays.indexOf(d.getDay()) === -1 || flavorBlockedDates[q]) return false; if (diff === 0) { var hour = (typeof flavorGetTzParts === 'function') ? flavorGetTzParts(new Date()).hour : new Date().getHours(); if (24 - hour < flavorMinAdvanceHours) return false; } flavorCalMonth = d.getMonth(); flavorCalYear = d.getFullYear(); flavorSelectDate(q); return true; } function flavorSelectFirstAvailableDate() { if (flavorTryUrlDate()) return; 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; var flavorManagementToken = ''; 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; flavorManagementToken = data.management_token || ''; 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({ management_token: flavorManagementToken, 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; var _bl = String(window.BR_DIALOG_LANG || 'es'); BRDialog.confirm(flavorI18n.cancelConfirm, {danger: true, ok: ({es: 'Sí, cancelar', ca: 'Sí, cancel·lar', en: 'Yes, cancel', de: 'Ja, stornieren'})[_bl] || 'Sí, cancelar', cancel: ({es: 'No', ca: 'No', en: 'No', de: 'Nein'})[_bl] || 'No'}) .then(function(ok){ if (ok) flavorCancelReservationGo(); }); } function flavorCancelReservationGo() { document.querySelector('.flavor-loader').style.display = 'flex'; fetch(flavorRestUrl + 'reservations/' + flavorCurrentReservation.id + '/cancel-public', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ management_token: flavorManagementToken }) }) .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(); // Datos del cliente desde el botón del email del menú (?bk=código cifrado). El servidor lo canjea // por email, nombre y teléfono; solo se rellenan los campos vacíos. (function() { var k; try { k = new URLSearchParams(window.location.search).get('bk'); } catch (e) { return; } if (!k) return; fetch(flavorRestUrl + 'menu-prefill?k=' + encodeURIComponent(k) + '&r=' + encodeURIComponent(flavorRestaurantId)) .then(function(r) { return r.ok ? r.json() : null; }) .then(function(d) { if (!d || !d.success) return; function put(id, v) { var el = document.getElementById(id); if (!el || !v || el.value) return; el.value = v; el.dispatchEvent(new Event('input', { bubbles: true })); el.dispatchEvent(new Event('change', { bubbles: true })); } put('flavor-email', d.email); put('flavor-first-name', d.first_name); put('flavor-last-name', d.last_name); var ph = String(d.phone || '').replace(/\s+/g, ''); if (ph) { var sel = document.getElementById('flavor-prefix'); if (sel && ph.charAt(0) === '+') { for (var i = 0; i < sel.options.length; i++) { var pre = sel.options[i].value || sel.options[i].text; if (ph.indexOf(pre) === 0) { sel.value = pre; ph = ph.slice(pre.length); break; } } } put('flavor-phone', ph); } }) .catch(function() {}); })(); // 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) {} })();
Miguel Miralles

Miquel Miralles, Treurer's CEO, is part of the owner family, being already the second generation involved in the project. He has a background in economics and law, and has taken numerous courses related to foreign trade, olive agronomy and extra virgin olive oil production techniques. His training and experience of more than 15 years in the world of olive oil are her guarantee to lead this project.

Entradas recientes

  • Uncategorized

Discover the Top Hotels in Can Picafort – Mallorca

Explore the top hotels in Can Picafort, Mallorca. From luxury resorts to family-friendly stays, find…

2 weeks hace
  • Uncategorized
  • Uncategorized

Differences between extra virgin, virgin and refined olive oil and their uses

Discover the differences between extra virgin, virgin and refined olive oil: official EU classification, properties,…

4 weeks hace
  • Blog
  • Uncategorized
  • Turismo

Pollença: heritage, art and spirituality in the heart of Mallorca

Discover Pollença, cradle of art and spirituality in Mallorca: churches, the Calvary, artistic tradition and…

2 months hace
  • Blog
  • Blog
  • Gourmet
  • Gourmet

Winter Gastronomy in Mallorca: Seasonal Dishes and New-Harvest Olive Oil

Discover winter gastronomy in Mallorca: seasonal dishes, traditional recipes and the new-harvest olive oil that…

2 months hace
  • Blog
  • Gourmet

Olive oil and Mallorcan wine pairing: a dish-by-dish guide

Discover how to pair Mallorcan extra virgin olive oil with the island's best wines: DO…

2 months hace
  • Blog
  • Blog
  • Gourmet
  • Gourmet
  • Turismo
  • Turismo

Artisan Cheeses from Mallorca: Varieties, Production and Pairings

Discover artisan cheeses from Mallorca: history, varieties, visitable dairies and how to pair them with…

2 months hace