главная сырой слайдер форма запроса цен

This commit is contained in:
Alisa
2026-06-06 16:59:36 +03:00
parent 0b6b1f5d5e
commit 65d182b4af
3 changed files with 706 additions and 15 deletions
+329 -3
View File
@@ -9,6 +9,8 @@ declare(strict_types=1);
/** @var array<int, array{title: string, url?: string}> $breadcrumbs */
/** @var bool $showBackButton */
$megaCategories = [];
$currentPath = parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH) ?: '/';
$isCartPage = $currentPath === '/cart';
$brandLogoSvg = <<<'SVG'
<svg class="brand-logo-svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 650 220" role="img" aria-labelledby="brand-logo-title brand-logo-desc">
<title id="brand-logo-title">Рыбсток</title>
@@ -122,6 +124,7 @@ try {
<?php require base_path('views/partials/mega-category-menu.php'); ?>
<?php if (!$isCartPage): ?>
<section class="price-mode-bar" aria-label="Система оплаты при получении">
<div class="price-mode-inner">
<span>Выберите систему оплаты при получении:</span>
@@ -130,11 +133,15 @@ try {
<span>Наличными</span>
<b>Выгодно от 7000р.</b>
</button>
<button type="button" class="price-mode-button" data-payment-mode="qr">QR-код</button>
<button type="button" class="price-mode-button price-mode-button-qr" data-payment-mode="qr">
<span>QR-код</span>
<b>Без комиссии даже с кредитки</b>
</button>
<button type="button" class="price-mode-button" data-payment-mode="invoice">Расчетный счет</button>
</div>
</div>
</section>
<?php endif; ?>
<header class="site-utility">
<?php if ($showBackButton): ?>
@@ -261,10 +268,17 @@ try {
const storageKeys = {
paymentMode: 'rybstock_payment_mode',
cartTotal: 'rybstock_cart_total',
cartItems: 'rybstock_cart_items'
cartItems: 'rybstock_cart_items',
deliveryType: 'rybstock_delivery_type',
deliveryDistance: 'rybstock_delivery_distance',
deliveryLift: 'rybstock_delivery_lift',
deliveryAddress: 'rybstock_delivery_address'
};
const getPaymentMode = () => localStorage.getItem(storageKeys.paymentMode) || 'cash';
const getDeliveryType = () => localStorage.getItem(storageKeys.deliveryType) || '';
const getDeliveryDistance = () => Math.max(0, Number(localStorage.getItem(storageKeys.deliveryDistance) || 0));
const getDeliveryLift = () => localStorage.getItem(storageKeys.deliveryLift) === '1';
const getCartTotal = () => Number(localStorage.getItem(storageKeys.cartTotal) || 0);
const getCartItems = () => {
try {
@@ -294,7 +308,7 @@ try {
const total = getCartTotal();
if (payment === 'invoice') {
return { multiplier: 1.08, fixedDiscount: 0, label: 'Оплата по расчетному счету: +8%' };
return { multiplier: 1.08, fixedDiscount: 0, label: 'Расчетный счет: счет день в день' };
}
if (payment === 'qr') {
@@ -323,6 +337,115 @@ try {
return Math.max(0, subtotal - Number(mode.fixedDiscount || 0));
};
const deliveryLabels = {
pickup: 'самовывоз',
inside_mkad: 'доставку внутри МКАД',
outside_mkad: 'доставку за МКАД'
};
const deliveryMinimums = {
pickup: 1,
inside_mkad: 2000,
outside_mkad: 5000
};
const deliveryMinimumMessage = (type, amount) => {
const minimum = deliveryMinimums[type] || 0;
const missing = Math.max(0, minimum - amount);
if (missing <= 0) {
return '';
}
return `Уважаемый клиент, Вы выбрали ${deliveryLabels[type] || 'получение'}, минимальная сумма - ${formatRub(minimum)}, добавьте пожалуйста товары на сумму ${formatRub(missing)}.`;
};
const deliveryCalculation = (items = getCartItems()) => {
const type = getDeliveryType();
const amount = cartCurrentTotal(items);
const lift = getDeliveryLift() && type !== 'pickup' ? 390 : 0;
const distance = getDeliveryDistance();
const minimum = deliveryMinimums[type] || 0;
const minimumOk = type ? amount >= minimum : false;
const minimumMessage = type ? deliveryMinimumMessage(type, amount) : '';
if (!type) {
return {
selected: false,
minimumOk: false,
minimumMessage: '',
price: 0,
lift,
total: 0,
label: 'Выберите получение',
note: 'Выберите самовывоз, доставку внутри МКАД или доставку за МКАД.'
};
}
if (type === 'pickup') {
return {
selected: true,
minimumOk,
minimumMessage,
price: 0,
lift: 0,
total: 0,
label: 'Самовывоз бесплатно',
note: 'Самовывоз осуществляется после оформления заказа на сайте и подтверждения заказа по телефону.'
};
}
if (type === 'inside_mkad') {
const free = amount >= 5000;
const price = free ? 0 : 599;
return {
selected: true,
minimumOk,
minimumMessage,
price,
lift,
total: price + lift,
label: free ? 'Бесплатно внутри МКАД' : 'Доставка внутри МКАД',
note: free
? 'Заказ от 5000 руб. доставим внутри МКАД бесплатно до подъезда. При заказе от 2000 до 5000 руб. доставка внутри МКАД стоит 599 руб.'
: 'Доставка внутри МКАД доступна от 2000 руб. При заказе от 2000 до 5000 руб. доставка стоит 599 руб., от 5000 руб. - бесплатно до подъезда.'
};
}
if (type === 'outside_mkad' && distance <= 0) {
return {
selected: true,
minimumOk,
minimumMessage,
price: 0,
lift,
total: lift,
label: 'Доставка за МКАД',
note: 'Доставка за МКАД доступна от 5000 руб. До 10 км от МКАД бесплатная доставка от 10000 руб.; дальше минимальная сумма для бесплатной доставки считается как 1000 руб. за каждый км от МКАД. Если заказ не проходит на бесплатную доставку, расчет: 199 руб. + 60 руб. за км.'
};
}
const freeThreshold = distance <= 10 ? 10000 : distance * 1000;
const free = amount >= freeThreshold;
const price = free ? 0 : Math.floor(199 + distance * 60);
return {
selected: true,
minimumOk,
minimumMessage,
price,
lift,
total: price + lift,
label: free ? 'Бесплатно за МКАД' : 'Доставка за МКАД',
note: distance > 0
? (free
? `Заказ проходит на бесплатную доставку: расстояние ${distance} км от МКАД.`
: `Расчет доставки: 199 руб. + ${distance} км × 60 руб. Минимум для бесплатной доставки: ${formatRub(freeThreshold)}.`)
: 'Доставка за МКАД доступна от 5000 руб. До 10 км от МКАД бесплатная доставка от 10000 руб.; дальше минимальная сумма для бесплатной доставки считается как 1000 руб. за каждый км от МКАД. Если заказ не проходит на бесплатную доставку, расчет: 199 руб. + 60 руб. за км.'
};
};
const tierValues = (base) => ({
base: base,
cash20: base * 0.95,
@@ -412,10 +535,20 @@ try {
const modeNode = cartPage.querySelector('[data-cart-current-mode]');
const savingRow = cartPage.querySelector('[data-cart-saving-row]');
const savingNode = cartPage.querySelector('[data-cart-saving-total]');
const deliveryTotalNode = cartPage.querySelector('[data-cart-delivery-total]');
const deliveryNoteNode = cartPage.querySelector('[data-cart-delivery-note]');
const minimumWarningNode = cartPage.querySelector('[data-cart-minimum-warning]');
const grandTotalNode = cartPage.querySelector('[data-cart-grand-total]');
const outsideDistanceField = cartPage.querySelector('[data-outside-distance-field]');
const liftField = cartPage.querySelector('[data-lift-field]');
const addressInput = cartPage.querySelector('[data-cart-address]');
const distanceInput = cartPage.querySelector('[data-cart-distance]');
const liftInput = cartPage.querySelector('[data-cart-lift]');
const items = getCartItems();
const baseTotal = cartBaseTotal(items);
const currentTotal = cartCurrentTotal(items);
const savingTotal = Math.max(0, baseTotal - currentTotal);
const delivery = deliveryCalculation(items);
if (emptyNode) {
emptyNode.hidden = items.length !== 0;
@@ -465,6 +598,47 @@ try {
if (savingNode) {
savingNode.textContent = formatRub(savingTotal);
}
cartPage.querySelectorAll('[data-cart-delivery-type]').forEach((input) => {
input.checked = input.value === getDeliveryType();
});
if (outsideDistanceField) {
outsideDistanceField.hidden = getDeliveryType() !== 'outside_mkad';
}
if (liftField) {
liftField.hidden = !['inside_mkad', 'outside_mkad'].includes(getDeliveryType());
}
if (addressInput && document.activeElement !== addressInput) {
addressInput.value = localStorage.getItem(storageKeys.deliveryAddress) || '';
}
if (distanceInput && document.activeElement !== distanceInput) {
distanceInput.value = getDeliveryDistance() > 0 ? String(getDeliveryDistance()) : '';
}
if (liftInput) {
liftInput.checked = getDeliveryLift();
}
if (deliveryTotalNode) {
deliveryTotalNode.textContent = delivery.selected ? formatRub(delivery.total) : delivery.label;
}
if (deliveryNoteNode) {
deliveryNoteNode.textContent = delivery.note;
}
if (minimumWarningNode) {
minimumWarningNode.hidden = !delivery.minimumMessage;
minimumWarningNode.textContent = delivery.minimumMessage || '';
}
if (grandTotalNode) {
grandTotalNode.textContent = formatRub(currentTotal + delivery.total);
}
};
const updateModeButtons = () => {
@@ -547,6 +721,158 @@ try {
window.RybStockPricing.setPaymentMode(modeButton.dataset.paymentMode || 'cash');
});
document.addEventListener('click', (event) => {
const deliveryLabel = event.target.closest('.cart-delivery-options label');
if (deliveryLabel) {
const input = deliveryLabel.querySelector('[data-cart-delivery-type]');
if (input && !input.checked) {
event.preventDefault();
input.checked = true;
input.dispatchEvent(new Event('change', { bubbles: true }));
}
}
});
document.addEventListener('click', (event) => {
const liftLabel = event.target.closest('.cart-lift-label');
if (!liftLabel || event.target.closest('[data-cart-lift]')) {
return;
}
const input = liftLabel.querySelector('[data-cart-lift]');
if (input) {
event.preventDefault();
input.checked = !input.checked;
input.dispatchEvent(new Event('change', { bubbles: true }));
}
});
document.addEventListener('change', (event) => {
const deliveryType = event.target.closest('[data-cart-delivery-type]');
if (deliveryType) {
localStorage.setItem(storageKeys.deliveryType, deliveryType.value);
localStorage.setItem(storageKeys.deliveryDistance, '0');
if (deliveryType.value === 'pickup') {
localStorage.setItem(storageKeys.deliveryLift, '0');
}
updateAllPrices();
return;
}
const liftInput = event.target.closest('[data-cart-lift]');
if (liftInput) {
localStorage.setItem(storageKeys.deliveryLift, liftInput.checked ? '1' : '0');
updateAllPrices();
}
});
document.addEventListener('input', (event) => {
const distanceInput = event.target.closest('[data-cart-distance]');
if (distanceInput) {
localStorage.setItem(storageKeys.deliveryDistance, String(Math.max(0, Number(distanceInput.value || 0))));
updateAllPrices();
return;
}
const addressInput = event.target.closest('[data-cart-address]');
if (addressInput) {
localStorage.setItem(storageKeys.deliveryAddress, addressInput.value);
localStorage.setItem(storageKeys.deliveryDistance, '0');
updateAllPrices();
}
});
document.addEventListener('submit', (event) => {
const form = event.target.closest('[data-cart-order-form]');
if (!form) {
return;
}
event.preventDefault();
const cartPage = form.closest('[data-cart-page]');
const successNode = cartPage?.querySelector('[data-cart-order-success]');
const items = getCartItems();
const delivery = deliveryCalculation(items);
const name = form.querySelector('[data-order-name]')?.value.trim() || '';
const phone = form.querySelector('[data-order-phone]')?.value.trim() || '';
const email = form.querySelector('[data-order-email]')?.value.trim() || '';
const comment = form.querySelector('[data-order-comment]')?.value.trim() || '';
const finalSumNeeded = form.querySelector('[data-order-final-sum-needed]')?.checked || false;
const invoiceRequisites = form.closest('[data-cart-page]')?.querySelector('[data-order-invoice-requisites]')?.value.trim() || '';
const invoiceFiles = [...(form.closest('[data-cart-page]')?.querySelector('[data-order-invoice-file]')?.files || [])].map((file) => file.name);
if (items.length === 0) {
window.alert('Добавьте товары в корзину.');
return;
}
if (!getDeliveryType()) {
window.alert('Выберите самовывоз, доставку внутри МКАД или доставку за МКАД.');
return;
}
if (!delivery.minimumOk) {
window.alert(delivery.minimumMessage);
return;
}
if (!name || !phone) {
window.alert('Укажите имя и телефон.');
return;
}
const orderNumber = 'RS-' + new Date().toISOString().replace(/\D/g, '').slice(0, 14);
const productsTotal = cartCurrentTotal(items);
const grandTotal = productsTotal + delivery.total;
const order = {
orderNumber,
createdAt: new Date().toISOString(),
name,
phone,
email,
comment,
finalSumNeeded,
invoiceRequisites,
invoiceFiles,
paymentMode: getPaymentMode(),
deliveryType: getDeliveryType(),
address: localStorage.getItem(storageKeys.deliveryAddress) || '',
distance: getDeliveryDistance(),
lift: getDeliveryLift(),
items,
productsTotal,
deliveryTotal: delivery.total,
grandTotal
};
localStorage.setItem('rybstock_last_order', JSON.stringify(order));
if (successNode) {
successNode.hidden = false;
successNode.innerHTML = `
<strong>Спасибо за Ваш заказ. Заказ ${escapeHtml(orderNumber)} принят в обработку.</strong>
<p>Спасибо, что оформили заказ на сайте Рыбсток. Мы благодарим Вас за доверие.</p>
<p>Доставка и самовывоз товара происходят только после подтверждения заказа по телефону. Сумма к оплате при получении: <b>${formatRub(grandTotal)}</b>.</p>
<p>Если заказ оформлен в будний день до 14.00, мы перезвоним Вам сегодня. Если заказ оформлен после 14.00, мы свяжемся на следующий рабочий день с 10.00 до 14.00.</p>
<p>Суббота и воскресенье - выходные. Заказы, оформленные в пятницу после 14.00, автоматически переносятся на понедельник.</p>
<p>Если у Вас срочный вопрос, свяжитесь с нами по телефону <a href="tel:+79777363363">+7-977-736-33-63</a>.</p>
`;
successNode.scrollIntoView({ behavior: 'smooth', block: 'center' });
}
});
document.addEventListener('click', (event) => {
const quantityButton = event.target.closest('[data-quantity-minus], [data-quantity-plus]');