- Исправлено выпадающее меню профиля (hover-баг с margin-top) - Исправлена авторизация: правильные пути к API (api/auth.php) - Исправлены ссылки на админку (admin/index.php вместо admin_panel.php) - Исправлены пути API корзины в catalog.php и checkout.php - Добавлена форма добавления/редактирования товаров в админке - Исправлены кнопки +/- в корзине (улучшена обработка AJAX) - Исправлена регистрация: правильные пути и обработка boolean в PostgreSQL - Добавлена миграция для назначения прав админа пользователю admin@mail.ru - Удален тестовый блок 'Быстрый вход' для неавторизованных пользователей - Улучшена обработка ошибок во всех API-эндпоинтах
346 lines
13 KiB
JavaScript
346 lines
13 KiB
JavaScript
// script.js
|
||
|
||
$(document).ready(function() {
|
||
// Инициализация корзины
|
||
let cart = {
|
||
items: [
|
||
{ id: 1, name: 'Кресло OPPORTUNITY', price: 16999, quantity: 1 },
|
||
{ id: 2, name: 'Кресло GOLDEN', price: 19999, quantity: 1 },
|
||
{ id: 3, name: 'Светильник POLET', price: 7999, quantity: 1 }
|
||
],
|
||
delivery: 2000,
|
||
discount: 0
|
||
};
|
||
|
||
// Функция обновления общей суммы
|
||
function updateTotal() {
|
||
let productsTotal = 0;
|
||
let totalCount = 0;
|
||
|
||
// Пересчитываем товары
|
||
$('.products__item').each(function() {
|
||
const $item = $(this);
|
||
const price = parseInt($item.data('price'));
|
||
const quantity = parseInt($item.find('.products__qty-value').text());
|
||
|
||
productsTotal += price * quantity;
|
||
totalCount += quantity;
|
||
});
|
||
|
||
// Обновляем отображение
|
||
$('.products-total').text(productsTotal + ' ₽');
|
||
$('.summary-count').text(totalCount);
|
||
$('.total-count').text(totalCount + ' шт.');
|
||
$('.cart-count').text(totalCount);
|
||
|
||
// Обновляем итоговую сумму
|
||
const finalTotal = productsTotal + cart.delivery - cart.discount;
|
||
$('.final-total').text(finalTotal + ' ₽');
|
||
}
|
||
|
||
// Функция валидации email
|
||
function validateEmail(email) {
|
||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||
return emailRegex.test(email);
|
||
}
|
||
|
||
// Функция валидации имени (ФИО)
|
||
function validateFullName(name) {
|
||
// Проверяем, что имя содержит только буквы, пробелы, дефисы и апострофы
|
||
const nameRegex = /^[a-zA-Zа-яА-ЯёЁ\s\-']+$/;
|
||
|
||
// Проверяем, что имя состоит минимум из 2 слов
|
||
const words = name.trim().split(/\s+/);
|
||
|
||
return nameRegex.test(name) && words.length >= 2;
|
||
}
|
||
|
||
// Функция валидации телефона
|
||
function validatePhone(phone) {
|
||
// Российский формат телефона: +7XXXXXXXXXX
|
||
const phoneRegex = /^\+7\d{10}$/;
|
||
return phoneRegex.test(phone);
|
||
}
|
||
|
||
// Функция отображения сообщения
|
||
function showMessage(messageId, duration = 5000) {
|
||
// Скрываем все сообщения
|
||
$('.message').hide();
|
||
|
||
// Показываем нужное сообщение
|
||
$(messageId).fadeIn(300);
|
||
|
||
// Автоматически скрываем через указанное время
|
||
if (duration > 0) {
|
||
setTimeout(() => {
|
||
$(messageId).fadeOut(300);
|
||
}, duration);
|
||
}
|
||
}
|
||
|
||
// Функция показа ошибки приватности
|
||
function showPrivacyError(show) {
|
||
if (show) {
|
||
$('#privacy-error').show();
|
||
} else {
|
||
$('#privacy-error').hide();
|
||
}
|
||
}
|
||
|
||
// Функция отображения ошибки конкретного поля
|
||
function showFieldError(fieldId, message) {
|
||
// Убираем старые ошибки
|
||
$(fieldId).removeClass('error-input');
|
||
$(fieldId + '-error').remove();
|
||
|
||
if (message) {
|
||
$(fieldId).addClass('error-input');
|
||
$(fieldId).after('<div class="field-error" id="' + fieldId.replace('#', '') + '-error">' + message + '</div>');
|
||
}
|
||
}
|
||
|
||
// Валидация поля при потере фокуса с указанием конкретной ошибки
|
||
$('#fullname').on('blur', function() {
|
||
const value = $(this).val().trim();
|
||
if (value) {
|
||
if (!validateFullName(value)) {
|
||
showFieldError('#fullname', 'ФИО должно содержать только буквы и состоять минимум из 2 слов');
|
||
} else {
|
||
showFieldError('#fullname', '');
|
||
}
|
||
} else {
|
||
showFieldError('#fullname', '');
|
||
}
|
||
});
|
||
|
||
$('#email').on('blur', function() {
|
||
const value = $(this).val().trim();
|
||
if (value) {
|
||
if (!validateEmail(value)) {
|
||
showFieldError('#email', 'Введите корректный email адрес (например: example@mail.ru)');
|
||
} else {
|
||
showFieldError('#email', '');
|
||
}
|
||
} else {
|
||
showFieldError('#email', '');
|
||
}
|
||
});
|
||
|
||
$('#phone').on('blur', function() {
|
||
const value = $(this).val().trim();
|
||
if (value) {
|
||
if (!validatePhone(value)) {
|
||
showFieldError('#phone', 'Введите номер в формате +7XXXXXXXXXX (10 цифр после +7)');
|
||
} else {
|
||
showFieldError('#phone', '');
|
||
}
|
||
} else {
|
||
showFieldError('#phone', '');
|
||
}
|
||
});
|
||
|
||
// Валидация обязательных полей
|
||
$('#region').on('blur', function() {
|
||
const value = $(this).val().trim();
|
||
if (!value) {
|
||
showFieldError('#region', 'Укажите регион доставки');
|
||
} else {
|
||
showFieldError('#region', '');
|
||
}
|
||
});
|
||
|
||
$('#address').on('blur', function() {
|
||
const value = $(this).val().trim();
|
||
if (!value) {
|
||
showFieldError('#address', 'Укажите адрес доставки (улица, дом, квартира)');
|
||
} else {
|
||
showFieldError('#address', '');
|
||
}
|
||
});
|
||
|
||
// Очистка ошибки при начале ввода
|
||
$('.form__input').on('input', function() {
|
||
const fieldId = '#' + $(this).attr('id');
|
||
showFieldError(fieldId, '');
|
||
});
|
||
|
||
// Обработчик увеличения количества
|
||
$('.products__qty-btn.plus').click(function() {
|
||
const $qtyValue = $(this).siblings('.products__qty-value');
|
||
let quantity = parseInt($qtyValue.text());
|
||
$qtyValue.text(quantity + 1);
|
||
updateTotal();
|
||
});
|
||
|
||
// Обработчик уменьшения количества
|
||
$('.products__qty-btn.minus').click(function() {
|
||
const $qtyValue = $(this).siblings('.products__qty-value');
|
||
let quantity = parseInt($qtyValue.text());
|
||
if (quantity > 1) {
|
||
$qtyValue.text(quantity - 1);
|
||
updateTotal();
|
||
}
|
||
});
|
||
|
||
// Обработчик удаления товара
|
||
$('.remove-from-cart').click(function() {
|
||
const $productItem = $(this).closest('.products__item');
|
||
$productItem.fadeOut(300, function() {
|
||
$(this).remove();
|
||
updateTotal();
|
||
|
||
// Показываем сообщение, если корзина пуста
|
||
if ($('.products__item').length === 0) {
|
||
$('.products__list').html('<div class="empty-cart">Корзина пуста</div>');
|
||
}
|
||
});
|
||
});
|
||
|
||
// Обработчик применения промокода
|
||
$('.promo__btn').click(function() {
|
||
const promoCode = $('.promo__input').val().toUpperCase();
|
||
|
||
if (promoCode === 'SALE10') {
|
||
cart.discount = Math.round(parseInt($('.products-total').text()) * 0.1);
|
||
$('.discount-total').text(cart.discount + ' ₽');
|
||
showMessage('#form-error', 3000);
|
||
$('#form-error').text('Промокод применен! Скидка 10%').removeClass('error').addClass('success');
|
||
} else if (promoCode === 'FREE') {
|
||
cart.delivery = 0;
|
||
$('.delivery-price').text('0 ₽');
|
||
showMessage('#form-error', 3000);
|
||
$('#form-error').text('Промокод применен! Бесплатная доставка').removeClass('error').addClass('success');
|
||
} else if (promoCode) {
|
||
showMessage('#form-error', 3000);
|
||
$('#form-error').text('Промокод недействителен').removeClass('success').addClass('error');
|
||
}
|
||
|
||
updateTotal();
|
||
});
|
||
|
||
// Обработчик выбора доставки
|
||
$('input[name="delivery"]').change(function() {
|
||
if ($(this).val() === 'pickup') {
|
||
cart.delivery = 0;
|
||
$('.delivery-price').text('0 ₽');
|
||
} else {
|
||
cart.delivery = 2000;
|
||
$('.delivery-price').text('2000 ₽');
|
||
}
|
||
updateTotal();
|
||
});
|
||
|
||
// Функция проверки всех полей формы
|
||
function validateForm() {
|
||
let isValid = true;
|
||
let errorMessages = [];
|
||
|
||
// Очищаем все старые ошибки
|
||
$('.field-error').remove();
|
||
$('.form__input').removeClass('error-input');
|
||
|
||
// Проверка обязательных полей
|
||
const requiredFields = [
|
||
{
|
||
id: '#fullname',
|
||
value: $('#fullname').val().trim(),
|
||
validator: validateFullName,
|
||
required: true,
|
||
message: 'ФИО должно содержать только буквы и состоять минимум из 2 слов'
|
||
},
|
||
{
|
||
id: '#phone',
|
||
value: $('#phone').val().trim(),
|
||
validator: validatePhone,
|
||
required: true,
|
||
message: 'Введите корректный номер телефона в формате +7XXXXXXXXXX'
|
||
},
|
||
{
|
||
id: '#email',
|
||
value: $('#email').val().trim(),
|
||
validator: validateEmail,
|
||
required: true,
|
||
message: 'Введите корректный email адрес'
|
||
},
|
||
{
|
||
id: '#region',
|
||
value: $('#region').val().trim(),
|
||
validator: (val) => val.length > 0,
|
||
required: true,
|
||
message: 'Поле "Регион" обязательно для заполнения'
|
||
},
|
||
{
|
||
id: '#address',
|
||
value: $('#address').val().trim(),
|
||
validator: (val) => val.length > 0,
|
||
required: true,
|
||
message: 'Поле "Адрес" обязательно для заполнения'
|
||
}
|
||
];
|
||
|
||
// Проверяем каждое поле
|
||
requiredFields.forEach(field => {
|
||
if (field.required && (!field.value || !field.validator(field.value))) {
|
||
isValid = false;
|
||
errorMessages.push(field.message);
|
||
showFieldError(field.id, field.message);
|
||
}
|
||
});
|
||
|
||
// Проверка согласия на обработку данных
|
||
if (!$('#privacy-checkbox').is(':checked')) {
|
||
isValid = false;
|
||
showPrivacyError(true);
|
||
errorMessages.push('Необходимо согласие на обработку персональных данных');
|
||
} else {
|
||
showPrivacyError(false);
|
||
}
|
||
|
||
// Показываем общее сообщение, если есть ошибки
|
||
if (!isValid && errorMessages.length > 0) {
|
||
showMessage('#form-error', 5000);
|
||
$('#form-error').text('Исправьте следующие ошибки: ' + errorMessages.join('; ')).removeClass('success').addClass('error');
|
||
|
||
// Прокручиваем к первой ошибке
|
||
$('html, body').animate({
|
||
scrollTop: $('.error-input').first().offset().top - 100
|
||
}, 500);
|
||
}
|
||
|
||
return isValid;
|
||
}
|
||
|
||
// Обработчик оформления заказа
|
||
$('#submit-order').click(function() {
|
||
// Проверка валидации всех полей
|
||
if (!validateForm()) {
|
||
return;
|
||
}
|
||
|
||
// Симуляция отправки заказа
|
||
$(this).prop('disabled', true).text('ОБРАБОТКА...');
|
||
|
||
setTimeout(() => {
|
||
showMessage('#order-success', 5000);
|
||
$(this).prop('disabled', false).text('ОФОРМИТЬ ЗАКАЗ');
|
||
|
||
// Здесь можно добавить редирект на страницу благодарности
|
||
// window.location.href = 'спасибо.html';
|
||
}, 2000);
|
||
});
|
||
|
||
// Маска для телефона
|
||
$('#phone').on('input', function() {
|
||
let phone = $(this).val().replace(/\D/g, '');
|
||
if (phone.length > 0) {
|
||
if (phone[0] !== '7' && phone[0] !== '8') {
|
||
phone = '7' + phone;
|
||
}
|
||
phone = '+7' + phone.substring(1, 11);
|
||
$(this).val(phone);
|
||
}
|
||
});
|
||
|
||
// Инициализация при загрузке
|
||
updateTotal();
|
||
}); |