document.addEventListener('DOMContentLoaded', function() { var cartWidget = document.querySelector('.elementor-widget-woocommerce-menu-cart'); if (!cartWidget) return; cartWidget.classList.remove('elementor-menu-cart--shown'); var container = cartWidget.querySelector('.elementor-menu-cart__container'); var main = cartWidget.querySelector('.elementor-menu-cart__main'); if (container) container.setAttribute('aria-hidden', 'true'); if (main) main.setAttribute('aria-hidden', 'true'); });

Снеки

  • zEMGlDX-qrJClCJ-NbWOUkk_original

    Мідії фрі

    180,00 
    Золотисті мідії фрі з соковитою текстурою та пікантним спайсі-майонезом.
  • vTKkDCP-eUubDjO-fmQvMPM_original

    Сирні кульки

    120,00 
    Золотиста хрумка скоринка, що приховує ніжну тягучу серединку з насиченим сирним смаком. Кожен укус дарує той самий приємний хрум і теплу вершковість, від якої неможливо відірватися. Ідеальна закуска для справжніх поціновувачів сиру 120г
  • xJhjJbA-GDhkBgd-koQpemS_original

    Цибульні кільця

    95,00 
    Хрумкі кільця соковитої цибулі в золотистій паніровці, обсмажені до ідеальної хрусткої скоринки. Ароматна закуска з ніжкою серединкою та насиченим смаком, яка чудово смакує як самостійно, так і з улюбленими соусами 150г
  • olMbWrv-EauzRhC-GCVHPeX_original

    Курячі Стріпси

    125,00 
    Соковите м’ясо, загорнуте в золотисту ароматну скоринку. Хрустка паніровка підкреслює ніжність курки, створюючи ідеальний баланс текстур та насичений, по-справжньому домашній смак 130г
  • GlwhYCC-hmjgbmg-JplmDzP_original

    Діпи з куркою та сирним соусом

    145,00 
    Поєднання золотистої скоринки та ніжної серединки, доповнене м’якою куркою гриль і дрібно нарізаною обсмаженою цибулею. Тягучий Чеддер огортає кожен шматочок насиченим смаком, створюючи тепле, ароматне та особливо апетитне поєднання 200г
  • mCkFEdT-zDblhIC-KAXDdUU_original

    Картопляні діпи

    100,00 
    Золотисті хрусткі шматочки картоплі з м’якою та ніжною серединкою. Вони мають насичений смак, приємну текстуру та ідеально підходять як самостійна закуска або доповнення до улюблених соусів 140г
  • FeETGiZ-qkkvTFg-QoFwJdv_original

    Крила з соусом BUFFALO

    140,00 
    Хрустка золотиста скоринка, яка зберігає соковитість та аромат м’яса. Після обсмаження вони набувають ідеальної текстури, а подача з пікантним соусом Баффало на основі томатів та спецій додає яскравої гострої нотки. Страва, що дарує насичений смак та приємне пряне післясмакування 150г
  • WoTHpdx-xiRgwXk-nebeeiX_original

    Креветки в паніровці 5 шт

    174,00 
    Ніжне соковите м’ясо креветки, загорнуте в золотисту хрустку оболонку, яка зберігає усі її морські аромати. Кожен шматочок дарує приємну легкість, вишуканість та делікатну солонувату нотку, а хрумка паніровка додає ідеальний контраст текстур
(function($) { // Step-by-step configuration const stepConfig = [ { name: 'quantity', condition: () => true, render: renderQuantityStep, collect: collectQuantity }, { name: 'ingredients', condition: r => r.hidden_attributes?.length && !answers.skipIngredients, render: renderIngredientsChoice, collect: collectIngredients }, { name: 'sauceList', condition: (r,a) => canOffer.other() && a.otherCategory !== 21, render: body => renderProductList(() => `/wp-json/custom/v1/products/24`, 'Який соус бажаєте?', body), collect: collectProductSelection }, // выбор категории доп.товара { name: 'otherCategory', condition: () => true, render: renderOtherCategoriesChoice, collect: collectOtherCategory }, // выбор подтовара из выбранной категории (с подгрузкой его деталей) { name: 'otherProduct', condition: (r,a) => a.otherCategory, render: renderOtherProductStepWithFetch, collect: () => answers.otherProduct }, // количество и інгредієнти для выбранного подтовара { name: 'otherQuantity', condition: (r,a) => !!a.otherProduct, render: renderQuantityStep, collect: collectOtherQuantity }, { name: 'otherIngredients', condition: (r,a) => a.otherProduct && latestResponse.hidden_attributes?.length && !answers.otherSkipIngredients, render: renderIngredientsChoice, collect: collectOtherIngredients }, // соусы — сразу список { name: 'drinkCategory', condition: () => canOffer.drinks(), render: renderDrinkCategoryChoice, collect: collectDrinkCategory }, { name: 'drinkList', condition: (r,a) => canOffer.drinks() && a.drinkCategory && a.drinkCategory !== 'none', render: body => renderProductList( () => `/wp-json/custom/v1/products/${answers.drinkCategory}`, 'Який напій бажаєте?', body ), collect: collectProductSelection }, { name: 'dessertList', condition: () => canOffer.donut(), render: body => renderProductList(() => `/wp-json/custom/v1/products/23`, 'Який пончик з’їш сьогодні?', body), collect: collectProductSelection }, ]; let currentStep = 0; let firstResponse = null; let latestResponse = null; const CART_URL = '/cart/'; let productId = null; const canOffer = { other: () => !!(firstResponse?.custom_fields?.offer_other_categories), drinks: () => !!(firstResponse?.custom_fields?.offer_drinks), donut: () => !!(firstResponse?.custom_fields?.offer_donut), sauces: () => !!(firstResponse?.custom_fields?.offer_sauces), }; function escapeHtml(s=''){return String(s).replace(/[&"']/g, m=>({'&':'&','':'>','"':'"',"'":'''}[m]))} let productMeta = { name:'', image:'', link:'#', shortDesc:'', price:'' }; // основной товар let selectedMeta = { name:'', image:'', shortDesc:'', price:'' }; // выбранный подтовар const answers = { quantity: 1, otherQuantity: 1, // ingredients: {added:[], removed:[]} // otherIngredients: {added:[], removed:[]} }; function debounce(fn, wait){ let t; return function(...a){ clearTimeout(t); t=setTimeout(()=>fn.apply(this,a), wait); }; } function updateCartItemQty(itemKey, qty){ if (!window.HULI_AJAX || !HULI_AJAX.url || !HULI_AJAX.nonce) { console.error('[Cart] HULI_AJAX не инициализирован (проверь wp_head/wp_enqueue_scripts).'); const d = $.Deferred(); d.reject({ localError: 'HULI_AJAX_MISSING' }); return d.promise(); } qty = parseInt(qty, 10); if (isNaN(qty) || qty 0 ? intPart + (decPart ? decSep + decPart : '') : intPart; // если хочешь скрывать .00 — поставь opts.stripZeros = true if (opts.stripZeros && decPart && /^0+$/.test(decPart)) { number = intPart; } return `${prefix}${number}${suffix}`; } // Render and collect functions function renderQuantityStep($body, resp, answersObj) { // Определяем, какие метаданные использовать const meta = resp?.selectedMeta || productMeta; const title = meta.name || resp?.product_name || ''; const description = meta.shortDesc || resp?.product_short_description || ''; const price = meta.price ? `
${meta.price} ₴
` : (resp?.product_price ? `
${resp.product_price} ₴
` : ''); // Шапка с картинкой и описанием const headerHtml = `
${meta.image ? `
${escapeHtml(title)}
` : ``}
${title ? `

${escapeHtml(title)}

` : ``} ${description ? `
${description}
` : ``} ${price}
`; // Поле количества const qtyHtml = `
`; // Собираем контент $body.html(headerHtml + qtyHtml); // Кнопки и управление const $ctrl = $('
'); if (resp.hidden_attributes && resp.hidden_attributes.length > 0) { $ctrl.append(` `); $ctrl.find('#qty-change-ingredients').on('click', () => { // Для основного товара if (!resp.selectedMeta) { answers.skipIngredients = false; } else { // Для подтовара answers.otherSkipIngredients = false; } nextStep(); }); } // Кнопка «Детальніше» $ctrl.append(makeInlineDetailsBtn(meta.link || productMeta.link)); $body.append($ctrl); // Кнопка «Далі» в футере setFooterNext(() => { if (!resp.selectedMeta) { // Основной товар answers.skipIngredients = true; } else { // Подтовар answers.otherSkipIngredients = true; } nextStep(); }); } function renderCartSummary() { hideFooterAll(); const $body = $('#productModalBody'); $body.html(`
Оновлюємо кошик…
`); $.get('/wp-json/wc/store/cart', data => { const items = (data && data.items) || []; let list = ''; items.forEach(it => { const img = (it.images && it.images[0] && it.images[0].src) || ''; const name = it.name || 'Товар'; const qty = it.quantity || 1; const line = formatStoreAmount(it.totals?.line_total, it.totals, { stripZeros: true }); const key = it.key; // ключ позиции в корзине (нужен для апдейта) list += `
${img ? `${name}` : ''}
${name}
${line}
`; }); const totalsHtml = data?.totals?.total_price ? `
Разом: ${formatStoreAmount(data.totals.total_price, data.totals, { stripZeros: true })}
` : ''; $body.html(`

Ваше замовлення

${list || '
Кошик порожній.
'}
${totalsHtml}
Оформити замовлення
`); $('#modal-continue-shopping').on('click', () => $('#productModal').modal('hide')); // Делегирование событий для qty const applyChange = debounce(($row, newQty) => { const key = $row.data('key'); const $inputs = $row.find('.qty-input, .qty-minus, .qty-plus').prop('disabled', true); updateCartItemQty(key, newQty) .always(() => $inputs.prop('disabled', false)) .done(() => renderCartSummary()) .fail(() => renderCartSummary()); }, 300); $body.off('click.qtyMinus').on('click.qtyMinus', '.qty-minus', function(){ const $row = $(this).closest('.mini-cart-row'); const $in = $row.find('.qty-input'); const next = Math.max(0, (parseInt($in.val(),10)||0) - 1); $in.val(next); applyChange($row, next); }); $body.off('click.qtyPlus').on('click.qtyPlus', '.qty-plus', function(){ const $row = $(this).closest('.mini-cart-row'); const $in = $row.find('.qty-input'); const next = (parseInt($in.val(),10)||0) + 1; $in.val(next); applyChange($row, next); }); $body.off('input.qtyInput change.qtyInput').on('input.qtyInput change.qtyInput', '.qty-input', function(){ const $row = $(this).closest('.mini-cart-row'); const val = parseInt(this.value, 10); if (!Number.isNaN(val) && val >= 0) applyChange($row, val); }); }).fail(() => { $body.html(`
Не вдалося завантажити кошик.
Кошик
`); $('#modal-continue-shopping').on('click', () => $('#productModal').modal('hide')); }); } function renderDrinkCategoryChoice($body /*, resp, answers */) { hideFooterNextAndDetails(); // тут фиксированные id категорий напоїв const catIds = [26]; // Прелоадер $body.html(`
Завантажуємо категорії…
`); // Параллельно грузим метаданные (название + картинка) Promise.all(catIds.map(fetchCategoryMeta)).then(list => { let html = `

Оберіть категорію напоїв:

`; html += list.map(o => `
${o.image ? `${o.name}` : `
(без зображення)
`}

${o.name}

`).join(''); // html += ` //
//
// Нічого не бажаю //
// `; $body.html(html); setFooterNextAsNone(() => nextStep('none'), 'Далі'); $body.find('.drink-cat').on('click', e => { const id = $(e.currentTarget).data('id'); nextStep(id); // сохранит answers.drinkCategory в collect }); $body.find('#drink-none').on('click', () => nextStep('none')); }); } function fetchCategoryMeta(catId) { return $.get(`/wp-json/custom/v1/products/${catId}`) .then(data => { const name = data.category_name || `Категорія ${catId}`; const image = (data.category_image && data.category_image.trim()) ? data.category_image : (data.products && data.products.length ? data.products[0].image : ''); return { id: catId, name, image }; }) .catch(() => ({ id: catId, name: `Категорія ${catId}`, image: '' })); } function collectQuantity() { answers.quantity = parseInt($('#step-quantity').val(), 10) || 1; return answers.quantity; } function collectOtherQuantity() { answers.otherQuantity = parseInt($('#step-quantity').val(), 10) || 1; return answers.otherQuantity; } function renderIngredientsChoice($body, resp) { console.log('step: renderIngredientsChoice'); let addHtml = '', removeHtml = ''; (resp.hidden_attributes || []).forEach(attr => { const tpl = `
  • `; if (attr.value === '+') addHtml += tpl; else removeHtml += tpl; }); $body.html(`

    Додати інгредієнти:

      ${addHtml}

    Видалити інгредієнти:

      ${removeHtml}
    `); $body.find('#ingr-next').on('click', () => { const data = answers.otherProduct ? collectOtherIngredients() : collectIngredients(); nextStep(data); }); } function collectIngredients() { const added = [], removed = []; $('.add-ingredient:checked').each((_, el) => added.push(el.value)); $('.remove-ingredient:checked').each((_, el) => removed.push(el.value)); answers.ingredients = { added, removed }; return answers.ingredients; } function collectOtherIngredients() { const added = [], removed = []; $('.add-ingredient:checked').each((_, el) => added.push(el.value)); $('.remove-ingredient:checked').each((_, el) => removed.push(el.value)); answers.otherIngredients = { added, removed }; return answers.otherIngredients; } function renderOtherCategoriesChoice($body /*, resp */) { hideFooterNextAndDetails(); const arr = (firstResponse?.custom_fields?.other_categories_array || []); // если массив ID — подгружаем мета, если сразу объекты — используем их const isIds = arr.length && typeof arr[0] === 'number'; const renderFrom = isIds ? Promise.all(arr.map(fetchCategoryMeta)) : Promise.resolve(arr.map(c => ({ id: c.id, name: c.name, image: c.image }))); $body.html(`
    Завантажуємо категорії…
    `); renderFrom.then(list => { let html = '

    Що бажаєте додати?

    '; list.forEach(cat => { html += `
    ${cat.image ? `${cat.name}` : `
    (без зображення)
    `}

    ${cat.name}

    `; }); // html += ` //
    //

    Нічого не бажаю

    //
    //
    `; $body.html(html); setFooterNextAsNone(() => nextStep(null), 'Далі'); $body.find('.other-category').on('click', e => { const id = $(e.currentTarget).data('id'); nextStep(id === 'none' ? null : id); }); }); } function collectOtherCategory(val) { answers.otherCategory = val || null; return answers.otherCategory; } function collectDrinkCategory(selected) { answers.drinkCategory = selected; // 25 | 26 | 'none' return answers.drinkCategory; } function renderOtherProductStepWithFetch($body) { console.log('step: renderOtherProductStepWithFetch'); hideFooterNextAndDetails(); renderProductList( () => `/wp-json/custom/v1/products/${answers.otherCategory}`, 'Оберіть товар:', $body, (id, productFromList) => { if (!id) { answers.otherProduct = null; return nextStep(null); } answers.otherProduct = id; answers.otherSkipIngredients = true; // Запрашиваем детали выбранного подтовара $.get(`/wp-json/custom/v1/product-category/${id}`, resp => { // meta для отображения информации о подтоваре const item = resp?.products?.[0] || resp || {}; selectedMeta = { id: id, name: productFromList?.name || item.product_name || item.name || '', image: productFromList?.image || item.image || '', shortDesc: item.product_short_description || item.product_description || '', price: productFromList?.price || item.product_price || item.price || '' }; // Скрытые атрибуты (ингредиенты) const ha = resp?.hidden_attributes || item.hidden_attributes || []; // Теперь latestResponse содержит и данные товара, и атрибуты latestResponse = { hidden_attributes: ha, selectedMeta: selectedMeta }; // Проверяем, есть ли скрытые ингредиенты answers.otherSkipIngredients = !(Array.isArray(ha) && ha.length > 0); // Переходим на следующий шаг (otherQuantity) nextStep(id); }); } ); } function renderOtherProductStep($body) { hideFooterNextAndDetails(); renderProductList( () => `/wp-json/custom/v1/products/${answers.otherCategory}`, 'Оберіть товар:', $body ); } function renderYesNoQuestion(text, $body) { $body.html( `

    ${text}

    ` ); $body.find('.btn-choice').on('click', e => nextStep($(e.currentTarget).data('answer'))); } function collectYesNo(ans) { return ans; } function renderProductList(urlFn, title, $body, onSelect) { $.get(urlFn(), data => { hideFooterNextAndDetails(); const products = data.products || []; let html = `

    ${title}

    `; products.forEach((p, idx) => { html += `
    ${p.name}

    ${p.name}

    ${p.price} ₴

    `; }); // плитка "ничего не желаю" // html += ` //
    //

    Нічого не бажаю

    //
    `; html += `
    `; $body.html(html); setFooterNextAsNone(() => { if (onSelect) onSelect(null, null); else nextStep(null); }, 'Далі'); // Делегированный обработчик — работает независимо от момента вставки DOM $body.off('click', '.product-item').on('click', '.product-item', function() { const id = $(this).data('id'); if (id === 'none') { if (onSelect) onSelect(null, null); else nextStep(null); return; } const idx = $(this).data('idx'); const product = (typeof idx !== 'undefined') ? products[idx] : null; if (onSelect) onSelect(id, product); else nextStep(id); }); }); } function collectProductSelection(id) { return id || null; // 'none' -> null } // Finalize: sequentially add to cart function finalizeAddToCart() { hideFooterNextAndDetails(); // 1) Основний товар const mainData = { product_id: productId, quantity: answers.quantity || 1 }; if (answers.ingredients) { mainData.ingredients_added = answers.ingredients.added || []; mainData.ingredients_removed = answers.ingredients.removed || []; mainData.comment = `Додано інгредієнти: ${(answers.ingredients.added||[]).join(', ')}; Видалено інгредієнти: ${(answers.ingredients.removed||[]).join(', ')}`; } $.post('/?wc-ajax=add_to_cart', mainData) .done(() => { let seq = $.Deferred().resolve(); // 2) Підтовар (якщо є) if (answers.otherProduct) { seq = seq.then(() => { const otherData = { product_id: answers.otherProduct, quantity: answers.otherQuantity || 1 }; if (answers.otherIngredients) { otherData.ingredients_added = answers.otherIngredients.added || []; otherData.ingredients_removed = answers.otherIngredients.removed || []; otherData.comment = `Додано інгредієнти: ${(answers.otherIngredients.added||[]).join(', ')}; Видалено інгредієнти: ${(answers.otherIngredients.removed||[]).join(', ')}`; } return $.post('/?wc-ajax=add_to_cart', otherData); }); } // 3) Інші супутні (соуси/напої/десерти) ['sauceList','drinkList','dessertList'].forEach(key => { if (answers[key]) { seq = seq.then(() => $.post('/?wc-ajax=add_to_cart', { product_id: answers[key], quantity: 1 })); } }); // 4) Замість закриття модалки — показуємо фінальний крок з кошиком seq.always(() => renderCartSummary()); }) // Навіть якщо основний POST упав — все одно покажемо поточний стан кошика .fail(() => renderCartSummary()); } function setFooterNext(onNext) { const $next = $('#productModalNext'); $next.off('click').on('click', onNext).show(); $('#productModalLink').hide(); // футерный "Детальніше" прячем } /** * Переиспользуем кнопку «Далі» как «Нічого не бажаю» * @param {Function} onNone - что делать при "ничего не хочу" * @param {string} [label] - подпись кнопки (по умолчанию "Нічого не бажаю") */ function setFooterNextAsNone(onNone, label) { const $next = $('#productModalNext'); $next.text(label || 'Далі').off('click').on('click', onNone).show(); $('#productModalLink').hide(); } /** Полностью скрыть обе футерные кнопки (используй в финале/ошибках) */ function hideFooterAll() { $('#productModalNext').hide().off('click').text('Далі'); $('#productModalLink').hide(); } function processNext(answer) { if (answer !== undefined) { const prev = stepConfig[currentStep-1]; answers[prev.name] = answer; } while (currentStep = stepConfig.length) return finalizeAddToCart(); const step = stepConfig[currentStep++]; step.render($('#productModalBody'), latestResponse, answers); } function nextStep(arg) { processNext(arg); } function resetProductModal() { $('#productModalBody').empty(); } function setFooterNext(onNext) { // Показати «Далі» в футері і прив’язати дію const $next = $('#productModalNext'); $next.off('click').on('click', onNext).show(); // На кроках з «Далі» футерний «Детальніше» ховаємо, бо він тепер у тілі кроку $('#productModalLink').hide(); } function hideFooterNextAndDetails() { $('#productModalNext').hide().off('click'); $('#productModalLink').hide(); } /** * Маленький генератор «Детальніше» для ТІЛА кроку — ставимо там, де раніше була «Далі» * @param {string} href * @returns {jQuery} кнопка/лінк */ function makeInlineDetailsBtn(href, label) { const $btn = $(` ${label || 'Детальніше'} `); $btn.attr('href', href || '#'); return $btn; } // Entry point $(document).on('click', '#product_listing .item', function(e) { e.preventDefault(); resetProductModal(); productId = $(this).find('[data-product_id]').data('product_id'); // Парсим мету из карточки const $it = $(this); const $img = $it.find('img').first(); const imgSrc = $img.attr('src') || $img.attr('data-src') || $img.attr('data-lazy') || (($img.attr('srcset') || '').split(',')[0] || '').trim().split(' ')[0]; productMeta = { name: ($it.find('.title, .product-title').first().text() || $it.data('name') || '').trim(), image: imgSrc || $it.data('image') || '', link: $it.find('a').first().attr('href') || '#', shortDesc: '', price: '' }; if (productMeta.name) $('#productModalTitle').text(productMeta.name); $('#productModalLink').attr('href', productMeta.link); // Тянем детали основного товара — дополним shortDesc/price и отрендерим первый шаг $.get(`/wp-json/custom/v1/product-category/${productId}`, resp => { latestResponse = resp; firstResponse = resp; productMeta.shortDesc = resp.product_short_description || ''; productMeta.price = resp.product_price || resp.price || ''; currentStep = 0; Object.keys(answers).forEach(k => delete answers[k]); processNext(); }); $('#productModal').modal('show'); }); })(jQuery);
    x