Исправление багов авторизации, корзины и админки

- Исправлено выпадающее меню профиля (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-эндпоинтах
This commit is contained in:
kirill.khorkov
2025-12-16 02:58:44 +03:00
parent 3f257120fa
commit 29b9aaac50
388 changed files with 15312 additions and 3220 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

BIN
public/assets/img/1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

BIN
public/assets/img/100.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

BIN
public/assets/img/11.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 KiB

BIN
public/assets/img/111.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
public/assets/img/11_1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 KiB

BIN
public/assets/img/1_1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
public/assets/img/1_2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

BIN
public/assets/img/1_2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

BIN
public/assets/img/2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

BIN
public/assets/img/22.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

BIN
public/assets/img/25.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
public/assets/img/2_2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

BIN
public/assets/img/2_2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

BIN
public/assets/img/3.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

BIN
public/assets/img/3_3.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

BIN
public/assets/img/3_3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 414 KiB

BIN
public/assets/img/4.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

BIN
public/assets/img/44.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
public/assets/img/444 Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 KiB

BIN
public/assets/img/444.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
public/assets/img/444.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 291 KiB

BIN
public/assets/img/4_1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

BIN
public/assets/img/5.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

BIN
public/assets/img/5_5.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
public/assets/img/5_5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 KiB

BIN
public/assets/img/6.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

BIN
public/assets/img/6_6.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

BIN
public/assets/img/6_6.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 372 KiB

BIN
public/assets/img/7.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

BIN
public/assets/img/77.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 265 KiB

BIN
public/assets/img/777.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

BIN
public/assets/img/777.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 555 KiB

BIN
public/assets/img/7_7.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

BIN
public/assets/img/7_7.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 479 KiB

BIN
public/assets/img/8.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

BIN
public/assets/img/88.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 KiB

BIN
public/assets/img/888.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

BIN
public/assets/img/888.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
public/assets/img/8_8.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 430 KiB

BIN
public/assets/img/9.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 KiB

BIN
public/assets/img/99.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

BIN
public/assets/img/99.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

BIN
public/assets/img/99_1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

BIN
public/assets/img/99_2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

BIN
public/assets/img/99_3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 497 KiB

BIN
public/assets/img/9_9.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
public/assets/img/9_9.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 759 KiB

BIN
public/assets/img/black.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 654 KiB

BIN
public/assets/img/brown.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 739 KiB

BIN
public/assets/img/chair.PNG Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

BIN
public/assets/img/gray.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

BIN
public/assets/img/gray1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

BIN
public/assets/img/gray2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 655 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 228 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 294 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 306 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

View File

@@ -0,0 +1,62 @@
.error-message {
color: #ff0000;
font-size: 12px;
margin-top: 5px;
display: none;
}
.form__input.error {
border-color: #ff0000;
}
.form__group {
position: relative;
margin-bottom: 15px;
}
/* Стили для сообщений внизу страницы */
.page-messages {
position: fixed;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
z-index: 1000;
width: 90%;
max-width: 500px;
}
.message {
padding: 15px;
margin: 10px 0;
border-radius: 5px;
text-align: center;
font-weight: bold;
display: none;
}
.message.error {
background-color: #ffebee;
color: #c62828;
border: 1px solid #ffcdd2;
}
.message.success {
background-color: #e8f5e9;
color: #453227;
border: 1px solid #c8e6c9;
}
.message.warning {
background-color: #fff3e0;
color: #ef6c00;
border: 1px solid #ffe0b2;
}
.privacy-error {
color: #ff0000;
font-size: 12px;
margin-top: 5px;
display: none;
text-align: center;
}

View File

@@ -0,0 +1,346 @@
// 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();
});

384
public/assets/js/profile.js Normal file
View File

@@ -0,0 +1,384 @@
$(document).ready(function() {
// Функции для отображения сообщений
function showMessage(type, text) {
const messageId = type + 'Message';
const $message = $('#' + messageId);
$message.text(text).fadeIn(300);
setTimeout(() => {
$message.fadeOut(300);
}, 5000);
}
// Проверка, является ли email администратора
function isAdminEmail(email) {
const adminEmails = ['admin@aeterna.ru', 'administrator@aeterna.ru', 'aeterna@mail.ru'];
return adminEmails.includes(email.toLowerCase());
}
// Валидация ФИО (без цифр)
function validateFIO(fio) {
const words = fio.trim().split(/\s+/);
// Проверяем, что минимум 2 слова и нет цифр
const hasNoDigits = !/\d/.test(fio);
return words.length >= 2 && words.every(word => word.length >= 2) && hasNoDigits;
}
// Валидация города
function validateCity(city) {
return city.trim().length >= 2 && /^[а-яА-ЯёЁ\s-]+$/.test(city);
}
// Валидация email
function validateEmail(email) {
const localPart = email.split('@')[0];
// Проверка на кириллические символы перед @
if (/[а-яА-ЯёЁ]/.test(localPart)) {
showError('email', 'В тексте перед знаком "@" не должно быть кириллических символов');
return false;
}
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) {
showError('email', 'Введите корректный email адрес');
return false;
}
hideError('email');
return true;
}
// Валидация телефона
function validatePhone(phone) {
const phoneRegex = /^(\+7|8)[\s-]?\(?\d{3}\)?[\s-]?\d{3}[\s-]?\d{2}[\s-]?\d{2}$/;
return phoneRegex.test(phone.replace(/\s/g, ''));
}
// Валидация пароля
function validatePassword(password) {
return password.length >= 6;
}
// Показать/скрыть ошибку
function showError(fieldId, message) {
$('#' + fieldId).addClass('error');
$('#' + fieldId + '-error').text(message).show();
}
function hideError(fieldId) {
$('#' + fieldId).removeClass('error');
$('#' + fieldId + '-error').hide();
}
// Валидация формы
function validateForm() {
let isValid = true;
// Валидация ФИО
const fio = $('#fio').val();
if (!validateFIO(fio)) {
if (/\d/.test(fio)) {
showError('fio', 'ФИО не должно содержать цифры');
} else {
showError('fio', 'ФИО должно содержать минимум 2 слова (каждое от 2 символов)');
}
isValid = false;
} else {
hideError('fio');
}
// Валидация города
const city = $('#city').val();
if (!validateCity(city)) {
showError('city', 'Укажите корректное название города (только русские буквы)');
isValid = false;
} else {
hideError('city');
}
// Валидация email
const email = $('#email').val();
if (!validateEmail(email)) {
showError('email', 'Введите корректный email адрес');
isValid = false;
} else {
hideError('email');
}
// Валидация телефона
const phone = $('#phone').val();
if (!validatePhone(phone)) {
showError('phone', 'Введите номер в формате: +7(912)999-12-23');
isValid = false;
} else {
hideError('phone');
}
// Валидация пароля
const password = $('#password').val();
if (!validatePassword(password)) {
showError('password', 'Пароль должен содержать минимум 6 символов');
isValid = false;
} else {
hideError('password');
}
// Проверка совпадения паролей
const confirmPassword = $('#confirm-password').val();
if (password !== confirmPassword) {
showError('confirm-password', 'Пароли не совпадают');
isValid = false;
} else {
hideError('confirm-password');
}
// Проверка согласия с условиями
if (!$('#privacy').is(':checked')) {
$('#privacy-error').show();
isValid = false;
} else {
$('#privacy-error').hide();
}
return isValid;
}
// Реальная валидация при вводе
$('input').on('blur', function() {
const fieldId = $(this).attr('id');
const value = $(this).val();
switch(fieldId) {
case 'fio':
if (!validateFIO(value)) {
if (/\d/.test(value)) {
showError(fieldId, 'ФИО не должно содержать цифры');
} else {
showError(fieldId, 'ФИО должно содержать минимум 2 слова');
}
} else {
hideError(fieldId);
}
break;
case 'city':
if (!validateCity(value)) {
showError(fieldId, 'Укажите корректное название города');
} else {
hideError(fieldId);
}
break;
case 'email':
if (!validateEmail(value)) {
showError(fieldId, 'Введите корректный email адрес');
} else {
hideError(fieldId);
}
break;
case 'phone':
if (!validatePhone(value)) {
showError(fieldId, 'Введите номер в формате: +7(912)999-12-23');
} else {
hideError(fieldId);
}
break;
case 'password':
if (!validatePassword(value)) {
showError(fieldId, 'Пароль должен содержать минимум 6 символов');
} else {
hideError(fieldId);
}
break;
case 'confirm-password':
const password = $('#password').val();
if (value !== password) {
showError(fieldId, 'Пароли не совпадают');
} else {
hideError(fieldId);
}
break;
}
});
// Обработка отправки формы
$('#registrationForm').on('submit', function(e) {
e.preventDefault();
if (validateForm()) {
const email = $('#email').val();
const isAdmin = isAdminEmail(email);
// Сохраняем данные пользователя в localStorage
const userData = {
email: email,
fio: $('#fio').val(),
phone: $('#phone').val(),
isAdmin: isAdmin,
registered: new Date().toISOString()
};
localStorage.setItem('userData', JSON.stringify(userData));
localStorage.setItem('isLoggedIn', 'true');
localStorage.setItem('isAdmin', isAdmin.toString());
// Эмуляция успешной регистрации
showMessage('success', 'Регистрация прошла успешно! ' +
(isAdmin ? 'Вы зарегистрированы как администратор.' : 'Добро пожаловать в AETERNA!'));
setTimeout(() => {
// Перенаправление на главную страницу
window.location.href = 'cite_mebel.php';
}, 2000);
} else {
showMessage('error', 'Пожалуйста, исправьте ошибки в форме');
}
});
// Плавная прокрутка к якорям
$('a[href^="#"]').on('click', function(event) {
var target = $(this.getAttribute('href'));
if (target.length) {
event.preventDefault();
$('html, body').stop().animate({
scrollTop: target.offset().top
}, 1000);
}
});
// Переключение между регистрацией и входом
$('.login-btn').on('click', function() {
showMessage('warning', 'Переход к форме входа...');
setTimeout(() => {
window.location.href = 'вход.php';
}, 1000);
});
// Обработка ссылки "Сменить пароль"
$('.password-link').on('click', function(e) {
e.preventDefault();
showMessage('warning', 'Функция смены пароля будет доступна после регистрации');
});
// Маска для телефона
$('#phone').on('input', function() {
let value = $(this).val().replace(/\D/g, '');
if (value.startsWith('7') || value.startsWith('8')) {
value = value.substring(1);
}
if (value.length > 0) {
value = '+7(' + value;
if (value.length > 6) value = value.substring(0, 6) + ')' + value.substring(6);
if (value.length > 10) value = value.substring(0, 10) + '-' + value.substring(10);
if (value.length > 13) value = value.substring(0, 13) + '-' + value.substring(13);
if (value.length > 16) value = value.substring(0, 16);
}
$(this).val(value);
});
// Запрет ввода цифр в поле ФИО
$('#fio').on('input', function() {
let value = $(this).val();
// Удаляем цифры из значения
value = value.replace(/\d/g, '');
$(this).val(value);
});
});
// Для входа (файл вход.html)
$(document).ready(function() {
// Проверяем, если пользователь уже вошел
if (localStorage.getItem('isLoggedIn') === 'true') {
const userData = JSON.parse(localStorage.getItem('userData') || '{}');
if (userData.email) {
$('#login-email').val(userData.email);
}
}
// Функция проверки пароля администратора
function checkAdminPassword(email, password) {
// Административные аккаунты
const adminAccounts = {
'admin@aeterna.ru': 'admin123',
'administrator@aeterna.ru': 'admin123',
'aeterna@mail.ru': 'admin123'
};
return adminAccounts[email.toLowerCase()] === password;
}
// Валидация формы входа
$('#loginForm').on('submit', function(e) {
e.preventDefault();
let isValid = true;
const email = $('#login-email').val();
const password = $('#login-password').val();
// Валидация email
if (!isValidEmail(email)) {
$('#email-error').show();
isValid = false;
} else {
$('#email-error').hide();
}
// Валидация пароля
if (password.length < 6) {
$('#password-error').show();
isValid = false;
} else {
$('#password-error').hide();
}
if (isValid) {
// Здесь обычно отправка данных на сервер
showMessage('success', 'Вы успешно вошли в систему!');
// Перенаправление на главную страницу через 1.5 секунды
setTimeout(function() {
window.location.href = 'cite_mebel.php';
}, 1500);
}
});
// Функция показа сообщений
function showMessage(type, text) {
const messageId = type + 'Message';
const $message = $('#' + messageId);
$message.text(text).show();
setTimeout(function() {
$message.fadeOut();
}, 3000);
}
// Скрываем сообщения об ошибках при фокусе на полях
$('input').on('focus', function() {
$(this).next('.error-message').hide();
});
// Обработка чекбокса "Запомнить меня"
$('#remember').on('change', function() {
if ($(this).is(':checked')) {
const email = $('#login-email').val();
if (email) {
localStorage.setItem('rememberedEmail', email);
}
} else {
localStorage.removeItem('rememberedEmail');
}
});
// Автозаполнение email, если пользователь выбрал "Запомнить меня"
const rememberedEmail = localStorage.getItem('rememberedEmail');
if (rememberedEmail) {
$('#login-email').val(rememberedEmail);
$('#remember').prop('checked', true);
}
// Обработка ссылки "Забыли пароль?"
$('.forgot-password').on('click', function(e) {
e.preventDefault();
showMessage('info', 'Для восстановления пароля обратитесь к администратору');
});
});

View File

@@ -0,0 +1,142 @@
.error-message {
color: #ff0000;
font-size: 12px;
margin-top: 5px;
display: none;
}
.form__input.error {
border-color: #ff0000;
}
.form__group {
position: relative;
margin-bottom: 15px;
}
/* Стили для сообщений внизу страницы */
.page-messages {
position: fixed;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
z-index: 1000;
width: 90%;
max-width: 500px;
}
.message {
padding: 15px;
margin: 10px 0;
border-radius: 5px;
text-align: center;
font-weight: bold;
display: none;
}
.message.error {
background-color: #ffebee;
color: #c62828;
border: 1px solid #ffcdd2;
}
.message.success {
background-color: #e8f5e9;
color: #453227;
border: 1px solid #c8e6c9;
}
.message.warning {
background-color: #fff3e0;
color: #ef6c00;
border: 1px solid #ffe0b2;
}
.privacy-error {
color: #ff0000;
font-size: 12px;
margin-top: 5px;
display: none;
text-align: center;
}
/* Дополнительные стили для формы регистрации */
.input-group {
position: relative;
margin-bottom: 20px;
}
.profile-form input.error {
border-color: #ff0000;
background-color: #fff5f5;
}
.privacy-checkbox {
margin: 20px 0;
text-align: center;
}
.privacy-checkbox label {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
cursor: pointer;
font-size: 14px;
}
.privacy-checkbox input[type="checkbox"] {
margin: 0;
}
/* Исправление отступов для страницы регистрации */
.profile-page-main {
padding: 40px 0;
min-height: calc(100vh - 200px);
}
/* Убедимся, что контейнер не перекрывает шапку и футер */
.profile-container {
margin: 0 auto;
position: relative;
z-index: 1;
}
.input-hint {
font-size: 12px;
color: #666;
margin-top: 5px;
}
/* Стили для страницы входа */
.form-options {
display: flex;
justify-content: space-between;
align-items: center;
margin: 20px 0;
}
.remember-me {
display: flex;
align-items: center;
gap: 8px;
font-size: 14px;
color: #453227;
}
.remember-me input[type="checkbox"] {
width: 16px;
height: 16px;
cursor: pointer;
}
.forgot-password {
font-size: 14px;
color: #453227;
text-decoration: underline;
}
.forgot-password:hover {
color: #617365;
text-decoration: none;
}

View File

@@ -0,0 +1,86 @@
// ===================================
// === ПЕРЕМЕННЫЕ И МИКСИНЫ AETERNA ===
// ===================================
@color-primary: #617365;
@color-secondary: #D1D1D1;
@color-accent: #453227;
@color-text-dark: #333;
@color-text-light: #fff;
@color-button: @color-accent;
@color-beige: #A2A09A;
@font-logo: 'Anek Kannada', sans-serif;
@font-main: 'Anonymous Pro', monospace;
@font-heading: 'Playfair Display', serif;
@shadow-light: 0 5px 15px rgba(0, 0, 0, 0.2);
@shadow-dark: 2px 2px 4px rgba(0, 0, 0, 0.3);
.flex-center(@gap: 0) {
display: flex;
align-items: center;
justify-content: center;
gap: @gap;
}
.flex-between() {
display: flex;
align-items: center;
justify-content: space-between;
}
.flex-column() {
display: flex;
flex-direction: column;
}
.icon-base(@size: 18px, @hover-scale: 1.1) {
cursor: pointer;
transition: all 0.3s ease;
font-size: @size;
&:hover {
transform: scale(@hover-scale);
}
}
.image-overlay() {
position: absolute;
inset: 0;
.flex-center(15px);
flex-direction: column;
text-align: center;
background-color: rgba(0, 0, 0, 0.4);
padding: 20px;
color: @color-text-light;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.7);
transition: all 0.3s ease;
}
.menu-base() {
position: absolute;
top: 100%;
left: 0;
width: 250px;
background: white;
border-radius: 8px;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
padding: 15px;
z-index: 1000;
margin-top: 5px;
display: none;
}
.input-base() {
width: 100%;
padding: 12px 15px;
border: 1px solid #ccc;
background-color: #fff;
font-family: @font-main;
font-size: 14px;
outline: none;
transition: border-color 0.3s ease;
&:focus {
border-color: @color-primary;
}
}

File diff suppressed because it is too large Load Diff