diff --git a/ init_db.php b/ init_db.php
new file mode 100644
index 0000000..901a438
--- /dev/null
+++ b/ init_db.php
@@ -0,0 +1,122 @@
+getConnection();
+
+// Создаем таблицы, если они не существуют
+$tables = [
+ 'users' => "
+ CREATE TABLE IF NOT EXISTS users (
+ user_id SERIAL PRIMARY KEY,
+ email VARCHAR(255) UNIQUE NOT NULL,
+ password_hash VARCHAR(255) NOT NULL,
+ full_name VARCHAR(100) NOT NULL,
+ phone VARCHAR(20),
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ is_active BOOLEAN DEFAULT TRUE,
+ is_admin BOOLEAN DEFAULT FALSE
+ )
+ ",
+
+ 'categories' => "
+ CREATE TABLE IF NOT EXISTS categories (
+ category_id SERIAL PRIMARY KEY,
+ name VARCHAR(100) NOT NULL,
+ slug VARCHAR(100) UNIQUE NOT NULL,
+ parent_id INTEGER REFERENCES categories(category_id),
+ description TEXT,
+ sort_order INTEGER DEFAULT 0,
+ is_active BOOLEAN DEFAULT TRUE
+ )
+ ",
+
+ 'products' => "
+ CREATE TABLE IF NOT EXISTS products (
+ product_id SERIAL PRIMARY KEY,
+ category_id INTEGER REFERENCES categories(category_id),
+ name VARCHAR(200) NOT NULL,
+ slug VARCHAR(200) UNIQUE NOT NULL,
+ description TEXT,
+ price DECIMAL(10, 2) NOT NULL,
+ old_price DECIMAL(10, 2),
+ sku VARCHAR(50) UNIQUE,
+ stock_quantity INTEGER DEFAULT 0,
+ is_available BOOLEAN DEFAULT TRUE,
+ is_featured BOOLEAN DEFAULT FALSE,
+ rating DECIMAL(3, 2) DEFAULT 0,
+ review_count INTEGER DEFAULT 0,
+ image_url VARCHAR(500),
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+ )
+ "
+];
+
+foreach ($tables_to_create as $table_name => $sql) {
+ try {
+ $db->exec($sql);
+ echo "Таблица '$table_name' создана/проверена ";
+ } catch (PDOException $e) {
+ echo "Ошибка создания таблицы '$table_name': " . $e->getMessage() . " ";
+ }
+}
+
+// Добавляем тестовые данные
+// Проверяем, есть ли уже категории
+$check_categories = $db->query("SELECT COUNT(*) FROM categories")->fetchColumn();
+
+if ($check_categories == 0) {
+ // Добавляем категории
+ $categories = [
+ ['Мягкая мебель', 'myagkaya-mebel', NULL, 'Диваны, кресла, пуфы'],
+ ['Диваны', 'divany', 1, 'Прямые и угловые диваны'],
+ ['Кресла', 'kresla', 1, 'Кресла для гостиной и офиса'],
+ ['Спальня', 'spalnya', NULL, 'Кровати, тумбы, комоды'],
+ ['Кровати', 'krovati', 4, 'Односпальные и двуспальные кровати']
+ ];
+
+ foreach ($categories as $category) {
+ $stmt = $db->prepare("INSERT INTO categories (name, slug, parent_id, description) VALUES (?, ?, ?, ?)");
+ $stmt->execute($category);
+ }
+ echo "Добавлены категории ";
+}
+
+// Проверяем, есть ли уже товары
+$check_products = $db->query("SELECT COUNT(*) FROM products")->fetchColumn();
+
+if ($check_products == 0) {
+ // Добавляем товары
+ $products = [
+ [2, 'Диван VELVET', 'divan-velvet', 'Прямой диван с тканевой обивкой', 45999, 54999, 'DIV-VEL-001', 10],
+ [2, 'Диван MODERN', 'divan-modern', 'Угловой диван с кожаной обивкой', 78999, 89999, 'DIV-MOD-002', 5],
+ [3, 'Кресло OPPORTUNITY', 'kreslo-opportunity', 'Кресло с деревянными ножками', 16999, 19999, 'KRES-OPP-001', 15],
+ [3, 'Кресло GOLDEN', 'kreslo-golden', 'Золотистое кресло для гостиной', 19999, 23999, 'KRES-GOL-002', 8],
+ [5, 'Кровать CLASSIC', 'krovat-classic', 'Двуспальная кровать из массива дуба', 64999, 74999, 'KROV-CLA-001', 3]
+ ];
+
+ foreach ($products as $product) {
+ $stmt = $db->prepare("INSERT INTO products (category_id, name, slug, description, price, old_price, sku, stock_quantity)
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
+ $stmt->execute($product);
+ }
+ echo "Добавлены товары ";
+}
+
+// Проверяем, есть ли администратор
+$check_admin = $db->prepare("SELECT COUNT(*) FROM users WHERE email = ?");
+$check_admin->execute(['admin@aeterna.ru']);
+
+if ($check_admin->fetchColumn() == 0) {
+ // Добавляем администратора (пароль: admin123)
+ $admin_password = password_hash('admin123', PASSWORD_DEFAULT);
+ $stmt = $db->prepare("INSERT INTO users (email, password_hash, full_name, phone, is_admin)
+ VALUES (?, ?, ?, ?, ?)");
+ $stmt->execute(['admin@aeterna.ru', $admin_password, 'Администратор AETERNA', '+79129991223', true]);
+ echo "Добавлен администратор (email: admin@aeterna.ru, пароль: admin123) ";
+}
+
+echo "
База данных успешно инициализирована! ";
+echo "Перейти в каталог ";
+?>
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..9c84dff
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,49 @@
+# IDE и редакторы
+.idea/
+.vscode/
+*.swp
+*.swo
+*~
+.DS_Store
+
+# Зависимости
+/vendor/
+/node_modules/
+
+# Логи
+*.log
+logs/
+
+# Загруженные файлы пользователей
+/uploads/products/*
+!/uploads/products/.gitkeep
+
+# Конфигурационные файлы с секретами (если есть)
+.env
+.env.local
+.env.*.local
+
+# Кэш
+/cache/
+*.cache
+
+# Временные файлы
+/tmp/
+*.tmp
+
+# Скомпилированные CSS
+*.css.map
+
+# База данных SQLite (если используется локально)
+*.db
+*.sqlite
+*.sqlite3
+
+# Файлы резервных копий
+*.bak
+*.backup
+
+# PHP debug/profiling
+.phpunit.result.cache
+phpunit.xml
+
diff --git a/add_to_cart.php b/add_to_cart.php
new file mode 100644
index 0000000..4ff29d8
--- /dev/null
+++ b/add_to_cart.php
@@ -0,0 +1,116 @@
+ false, 'message' => 'Требуется авторизация']);
+ exit();
+}
+
+if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_POST['product_id'])) {
+ $product_id = intval($_POST['product_id']);
+ $quantity = intval($_POST['quantity'] ?? 1);
+ $user_id = $_SESSION['user_id'] ?? 0;
+
+ if ($user_id == 0) {
+ echo json_encode(['success' => false, 'message' => 'Пользователь не найден']);
+ exit();
+ }
+
+ $db = Database::getInstance()->getConnection();
+
+ try {
+ // Проверяем наличие товара на складе
+ $checkStock = $db->prepare("
+ SELECT stock_quantity, name, price
+ FROM products
+ WHERE product_id = ? AND is_available = TRUE
+ ");
+ $checkStock->execute([$product_id]);
+ $product = $checkStock->fetch();
+
+ if (!$product) {
+ echo json_encode(['success' => false, 'message' => 'Товар не найден']);
+ exit();
+ }
+
+ if ($product['stock_quantity'] < $quantity) {
+ echo json_encode(['success' => false, 'message' => 'Недостаточно товара на складе']);
+ exit();
+ }
+
+ // Проверяем, есть ли товар уже в корзине пользователя
+ $checkCart = $db->prepare("
+ SELECT cart_id, quantity
+ FROM cart
+ WHERE user_id = ? AND product_id = ?
+ ");
+ $checkCart->execute([$user_id, $product_id]);
+ $cartItem = $checkCart->fetch();
+
+ if ($cartItem) {
+ // Обновляем количество
+ $newQuantity = $cartItem['quantity'] + $quantity;
+
+ // Проверяем общее количество
+ if ($newQuantity > $product['stock_quantity']) {
+ echo json_encode(['success' => false, 'message' => 'Превышено доступное количество']);
+ exit();
+ }
+
+ $updateStmt = $db->prepare("
+ UPDATE cart
+ SET quantity = ?, updated_at = CURRENT_TIMESTAMP
+ WHERE cart_id = ?
+ ");
+ $updateStmt->execute([$newQuantity, $cartItem['cart_id']]);
+ } else {
+ // Добавляем новый товар
+ $insertStmt = $db->prepare("
+ INSERT INTO cart (user_id, product_id, quantity)
+ VALUES (?, ?, ?)
+ ");
+ $insertStmt->execute([$user_id, $product_id, $quantity]);
+ }
+
+ // Обновляем сессию
+ if (!isset($_SESSION['cart'])) {
+ $_SESSION['cart'] = [];
+ }
+
+ if (isset($_SESSION['cart'][$product_id])) {
+ $_SESSION['cart'][$product_id]['quantity'] += $quantity;
+ } else {
+ $_SESSION['cart'][$product_id] = [
+ 'quantity' => $quantity,
+ 'name' => $product['name'],
+ 'price' => $product['price'],
+ 'added_at' => time()
+ ];
+ }
+
+ // Получаем общее количество товаров в корзине
+ $cartCountStmt = $db->prepare("
+ SELECT SUM(quantity) as total
+ FROM cart
+ WHERE user_id = ?
+ ");
+ $cartCountStmt->execute([$user_id]);
+ $cart_count = $cartCountStmt->fetchColumn() ?: 0;
+
+ echo json_encode([
+ 'success' => true,
+ 'cart_count' => $cart_count,
+ 'message' => 'Товар добавлен в корзину'
+ ]);
+
+ } catch (PDOException $e) {
+ echo json_encode([
+ 'success' => false,
+ 'message' => 'Ошибка базы данных: ' . $e->getMessage()
+ ]);
+ }
+} else {
+ echo json_encode(['success' => false, 'message' => 'Неверный запрос']);
+}
+?>
\ No newline at end of file
diff --git a/admin_actions.php b/admin_actions.php
new file mode 100644
index 0000000..ea9563b
--- /dev/null
+++ b/admin_actions.php
@@ -0,0 +1,170 @@
+getConnection();
+$action = $_GET['action'] ?? '';
+
+try {
+ switch ($action) {
+ case 'delete_product':
+ if (isset($_GET['id'])) {
+ $productId = intval($_GET['id']);
+ // Делаем товар недоступным
+ $stmt = $db->prepare("
+ UPDATE products
+ SET is_available = FALSE, stock_quantity = 0, updated_at = CURRENT_TIMESTAMP
+ WHERE product_id = ?
+ ");
+ $stmt->execute([$productId]);
+ header('Location: admin_panel.php?action=products&message=Товар помечен как недоступный');
+ exit();
+ }
+ break;
+
+ case 'restore_product':
+ if (isset($_GET['id'])) {
+ $productId = intval($_GET['id']);
+ // Восстанавливаем товар
+ $stmt = $db->prepare("
+ UPDATE products
+ SET is_available = TRUE, stock_quantity = 10, updated_at = CURRENT_TIMESTAMP
+ WHERE product_id = ?
+ ");
+ $stmt->execute([$productId]);
+ header('Location: admin_panel.php?action=products&message=Товар восстановлен');
+ exit();
+ }
+ break;
+
+ case 'delete_category':
+ if (isset($_GET['id'])) {
+ $categoryId = intval($_GET['id']);
+
+ try {
+ // 1. Проверяем, есть ли товары в этой категории
+ $checkProducts = $db->prepare("SELECT COUNT(*) FROM products WHERE category_id = ?");
+ $checkProducts->execute([$categoryId]);
+ $productCount = $checkProducts->fetchColumn();
+
+ if ($productCount > 0) {
+ // Если есть товары, делаем категорию неактивной
+ $stmt = $db->prepare("UPDATE categories SET is_active = FALSE WHERE category_id = ?");
+ $stmt->execute([$categoryId]);
+ header('Location: admin_panel.php?action=categories&message=Категория скрыта (содержит товары)');
+ exit();
+ }
+
+ // 2. Проверяем, есть ли дочерние категории
+ $checkChildren = $db->prepare("SELECT COUNT(*) FROM categories WHERE parent_id = ?");
+ $checkChildren->execute([$categoryId]);
+ $childCount = $checkChildren->fetchColumn();
+
+ if ($childCount > 0) {
+ // Вариант A: Делаем категорию неактивной
+ $stmt = $db->prepare("UPDATE categories SET is_active = FALSE WHERE category_id = ?");
+ $stmt->execute([$categoryId]);
+ header('Location: admin_panel.php?action=categories&message=Категория скрыта (имеет дочерние категории)');
+ exit();
+
+ // Вариант B: Удаляем вместе с дочерними (раскомментируйте если нужно)
+ /*
+ // Сначала удаляем дочерние категории
+ $stmt = $db->prepare("DELETE FROM categories WHERE parent_id = ?");
+ $stmt->execute([$categoryId]);
+
+ // Затем удаляем саму категорию
+ $stmt = $db->prepare("DELETE FROM categories WHERE category_id = ?");
+ $stmt->execute([$categoryId]);
+
+ header('Location: admin_panel.php?action=categories&message=Категория и её дочерние категории удалены');
+ exit();
+ */
+ }
+
+ // 3. Если нет товаров и дочерних категорий, удаляем
+ $stmt = $db->prepare("DELETE FROM categories WHERE category_id = ?");
+ $stmt->execute([$categoryId]);
+
+ header('Location: admin_panel.php?action=categories&message=Категория удалена');
+ exit();
+
+ } catch (PDOException $e) {
+ header('Location: admin_panel.php?action=categories&error=' . urlencode($e->getMessage()));
+ exit();
+ }
+ }
+ break;
+
+ case 'delete_category_force':
+ // Принудительное удаление с дочерними категориями
+ if (isset($_GET['id'])) {
+ $categoryId = intval($_GET['id']);
+
+ try {
+ // Сначала перемещаем товары в другую категорию (например, в первую)
+ $firstCategory = $db->query("SELECT category_id FROM categories WHERE category_id != ? LIMIT 1")->fetchColumn();
+
+ if ($firstCategory) {
+ $moveProducts = $db->prepare("UPDATE products SET category_id = ? WHERE category_id = ?");
+ $moveProducts->execute([$firstCategory, $categoryId]);
+ }
+
+ // Обнуляем parent_id у дочерних категорий
+ $stmt = $db->prepare("UPDATE categories SET parent_id = NULL WHERE parent_id = ?");
+ $stmt->execute([$categoryId]);
+
+ // Удаляем категорию
+ $stmt = $db->prepare("DELETE FROM categories WHERE category_id = ?");
+ $stmt->execute([$categoryId]);
+
+ header('Location: admin_panel.php?action=categories&message=Категория удалена. Товары перемещены.');
+ exit();
+
+ } catch (PDOException $e) {
+ header('Location: admin_panel.php?action=categories&error=' . urlencode($e->getMessage()));
+ exit();
+ }
+ }
+ break;
+
+ case 'toggle_user':
+ if (isset($_GET['id'])) {
+ $userId = intval($_GET['id']);
+ $stmt = $db->prepare("
+ UPDATE users
+ SET is_active = NOT is_active, updated_at = CURRENT_TIMESTAMP
+ WHERE user_id = ?
+ ");
+ $stmt->execute([$userId]);
+ header('Location: admin_panel.php?action=users&message=Статус пользователя изменен');
+ exit();
+ }
+ break;
+
+ case 'make_admin':
+ if (isset($_GET['id'])) {
+ $userId = intval($_GET['id']);
+ $stmt = $db->prepare("UPDATE users SET is_admin = TRUE WHERE user_id = ?");
+ $stmt->execute([$userId]);
+ header('Location: admin_panel.php?action=users&message=Пользователь назначен администратором');
+ exit();
+ }
+ break;
+ }
+} catch (PDOException $e) {
+ header('Location: admin_panel.php?error=' . urlencode($e->getMessage()));
+ exit();
+}
+
+// Если действие не распознано
+header('Location: admin_panel.php');
+exit();
+?>
\ No newline at end of file
diff --git a/admin_panel.php b/admin_panel.php
new file mode 100644
index 0000000..f8a4252
--- /dev/null
+++ b/admin_panel.php
@@ -0,0 +1,772 @@
+Сначала добавьте категории!';
+}
+
+// Проверка прав администратора
+if (!isset($_SESSION['isAdmin']) || $_SESSION['isAdmin'] !== true) {
+ echo "";
+ exit();
+}
+
+$db = Database::getInstance()->getConnection();
+
+// Обработка действий
+$action = $_GET['action'] ?? 'dashboard';
+$message = $_GET['message'] ?? '';
+$error = $_GET['error'] ?? '';
+
+// Обработка POST запросов - ДОБАВЛЕНО ПРОСТОЕ И РАБОТАЮЩЕЕ!
+if ($_SERVER['REQUEST_METHOD'] === 'POST') {
+ $post_action = $_POST['action'] ?? '';
+
+ try {
+ if ($post_action === 'add_category') {
+ $name = trim($_POST['name'] ?? '');
+ $slug = strtolower(preg_replace('/[^a-z0-9]+/i', '-', $name));
+ $parent_id = !empty($_POST['parent_id']) ? (int)$_POST['parent_id'] : NULL;
+ $description = trim($_POST['description'] ?? '');
+ $sort_order = (int)($_POST['sort_order'] ?? 0);
+ $is_active = isset($_POST['is_active']) ? 1 : 0;
+
+ if (empty($name)) {
+ throw new Exception('Название категории обязательно');
+ }
+
+ $stmt = $db->prepare("
+ INSERT INTO categories (name, slug, parent_id, description, sort_order, is_active)
+ VALUES (?, ?, ?, ?, ?, ?)
+ ");
+
+ $result = $stmt->execute([$name, $slug, $parent_id, $description, $sort_order, $is_active]);
+
+ if ($result) {
+ header('Location: admin_panel.php?action=categories&message=Категория+успешно+добавлена');
+ exit();
+ }
+ }
+
+ // ИСПРАВЬТЕ БЛОК edit_category или добавьте его если его нет:
+ if ($post_action === 'edit_category' && isset($_POST['category_id'])) {
+ $category_id = (int)$_POST['category_id'];
+ $name = trim($_POST['name'] ?? '');
+ $parent_id = !empty($_POST['parent_id']) ? (int)$_POST['parent_id'] : NULL;
+ $description = trim($_POST['description'] ?? '');
+ $sort_order = (int)($_POST['sort_order'] ?? 0);
+ $is_active = isset($_POST['is_active']) ? 1 : 0;
+
+ if (empty($name)) {
+ throw new Exception('Название категории обязательно');
+ }
+
+ $slug = strtolower(preg_replace('/[^a-z0-9]+/i', '-', $name));
+
+ $stmt = $db->prepare("
+ UPDATE categories SET
+ name = ?,
+ slug = ?,
+ parent_id = ?,
+ description = ?,
+ sort_order = ?,
+ is_active = ?,
+ updated_at = CURRENT_TIMESTAMP
+ WHERE category_id = ?
+ ");
+
+ $stmt->execute([$name, $slug, $parent_id, $description, $sort_order, $is_active, $category_id]);
+
+ header('Location: admin_panel.php?action=categories&message=Категория+обновлена');
+ exit();
+ }
+
+ if ($post_action === 'add_product') {
+ $name = trim($_POST['name'] ?? '');
+ $slug = strtolower(preg_replace('/[^a-z0-9]+/i', '-', $name));
+ $category_id = (int)($_POST['category_id'] ?? 0);
+ $description = trim($_POST['description'] ?? '');
+ $price = (float)($_POST['price'] ?? 0);
+ $old_price = !empty($_POST['old_price']) ? (float)$_POST['old_price'] : NULL;
+ $sku = trim($_POST['sku'] ?? '');
+ $stock_quantity = (int)($_POST['stock_quantity'] ?? 0);
+ $is_available = isset($_POST['is_available']) ? 1 : 0;
+ $is_featured = isset($_POST['is_featured']) ? 1 : 0;
+ $image_url = trim($_POST['image_url'] ?? '');
+ $color = trim($_POST['color'] ?? '');
+ $material = trim($_POST['material'] ?? '');
+ $card_size = trim($_POST['card_size'] ?? 'small');
+
+
+ // ВАЖНО: Проверяем category_id
+ if ($category_id <= 0) {
+ $_SESSION['error'] = 'Выберите корректную категорию';
+ header('Location: admin_panel.php?action=add_product');
+ exit();
+ }
+
+ // Проверяем существование категории
+ $check_category = $db->prepare("SELECT COUNT(*) FROM categories WHERE category_id = ?");
+ $check_category->execute([$category_id]);
+ if ($check_category->fetchColumn() == 0) {
+ $_SESSION['error'] = 'Выбранная категория не существует';
+ header('Location: admin_panel.php?action=add_product');
+ exit();
+ }
+
+ if (empty($name)) throw new Exception('Название товара обязательно');
+ if ($price <= 0) throw new Exception('Цена должна быть больше 0');
+
+ // Генерируем SKU если пустой
+ if (empty($sku)) {
+ $sku = 'PROD-' . strtoupper(substr(preg_replace('/[^a-z0-9]/i', '', $name), 0, 6)) . '-' . rand(100, 999);
+ }
+
+ $stmt = $db->prepare("
+ INSERT INTO products (
+ category_id, name, slug, description, price, old_price,
+ sku, stock_quantity, is_available, is_featured, image_url,
+ color, material, card_size
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
+ ");
+
+ $result = $stmt->execute([
+ $category_id, $name, $slug, $description, $price, $old_price,
+ $sku, $stock_quantity, $is_available, $is_featured, $image_url,
+ $color, $material, $card_size
+ ]);
+
+ if ($result) {
+ $_SESSION['message'] = 'Товар успешно добавлен';
+ header('Location: admin_panel.php?action=products');
+ exit();
+ }
+ }
+
+ // ИСПРАВЛЕННЫЙ КОД для edit_product в admin_panel.php:
+ if ($post_action === 'edit_product' && isset($_POST['product_id'])) {
+ $product_id = (int)$_POST['product_id'];
+ $name = trim($_POST['name'] ?? '');
+ $category_id = (int)($_POST['category_id'] ?? 1); // ПО УМОЛЧАНИЮ 1, чтобы избежать 0
+ $description = trim($_POST['description'] ?? '');
+ $price = (float)($_POST['price'] ?? 0);
+ $old_price = !empty($_POST['old_price']) ? (float)$_POST['old_price'] : NULL;
+ $stock_quantity = (int)($_POST['stock_quantity'] ?? 0);
+ $is_available = isset($_POST['is_available']) ? 1 : 0;
+ $image_url = trim($_POST['image_url'] ?? '');
+ $color = trim($_POST['color'] ?? '');
+ $material = trim($_POST['material'] ?? '');
+
+ // ВАЖНО: Проверяем category_id
+ if ($category_id <= 0) {
+ // Если category_id = 0, устанавливаем первую доступную категорию
+ $firstCat = $db->query("SELECT category_id FROM categories LIMIT 1")->fetchColumn();
+ $category_id = $firstCat ?: 1;
+ }
+
+ $stmt = $db->prepare("
+ UPDATE products SET
+ name = ?,
+ category_id = ?,
+ description = ?,
+ price = ?,
+ old_price = ?,
+ stock_quantity = ?,
+ is_available = ?,
+ image_url = ?,
+ color = ?,
+ material = ?,
+ updated_at = CURRENT_TIMESTAMP
+ WHERE product_id = ?
+ ");
+
+ $stmt->execute([
+ $name, $category_id, $description, $price, $old_price,
+ $stock_quantity, $is_available, $image_url, $color, $material, $product_id
+ ]);
+
+ header('Location: admin_panel.php?action=products&message=Товар+обновлен');
+ exit();
+ }
+
+ if ($post_action === 'delete_category' && isset($_POST['category_id'])) {
+ $categoryId = intval($_POST['category_id']);
+
+ // 1. Проверяем, есть ли товары в этой категории
+ $checkProducts = $db->prepare("SELECT COUNT(*) FROM products WHERE category_id = ?");
+ $checkProducts->execute([$categoryId]);
+ $productCount = $checkProducts->fetchColumn();
+
+ // 2. Проверяем, есть ли дочерние категории
+ $checkChildren = $db->prepare("SELECT COUNT(*) FROM categories WHERE parent_id = ?");
+ $checkChildren->execute([$categoryId]);
+ $childCount = $checkChildren->fetchColumn();
+
+ if ($productCount > 0) {
+ // Если есть товары, делаем категорию неактивной вместо удаления
+ $stmt = $db->prepare("UPDATE categories SET is_active = FALSE WHERE category_id = ?");
+ $stmt->execute([$categoryId]);
+
+ header('Location: admin_panel.php?action=categories&message=Категория+скрыта+(содержит+товары)');
+ exit();
+ } elseif ($childCount > 0) {
+ // Если есть дочерние категории, делаем неактивной
+ $stmt = $db->prepare("UPDATE categories SET is_active = FALSE WHERE category_id = ?");
+ $stmt->execute([$categoryId]);
+
+ header('Location: admin_panel.php?action=categories&message=Категория+скрыта+(имеет+дочерние+категории)');
+ exit();
+ } else {
+ // Если нет товаров и дочерних категорий, удаляем
+ $stmt = $db->prepare("DELETE FROM categories WHERE category_id = ?");
+ $stmt->execute([$categoryId]);
+
+ header('Location: admin_panel.php?action=categories&message=Категория+удалена');
+ exit();
+ }
+ }
+ } catch (PDOException $e) {
+ header('Location: admin_panel.php?action=' . $action . '&error=' . urlencode('Ошибка БД: ' . $e->getMessage()));
+ exit();
+ } catch (Exception $e) {
+ header('Location: admin_panel.php?action=' . $action . '&error=' . urlencode($e->getMessage()));
+ exit();
+ }
+ }
+
+
+// Получение данных для отображения
+try {
+ // Статистика
+ $stats = [
+ 'total_products' => $db->query("SELECT COUNT(*) FROM products")->fetchColumn(),
+ 'active_products' => $db->query("SELECT COUNT(*) FROM products WHERE is_available = TRUE")->fetchColumn(),
+ 'total_orders' => $db->query("SELECT COUNT(*) FROM orders")->fetchColumn(),
+ 'total_users' => $db->query("SELECT COUNT(*) FROM users")->fetchColumn(),
+ 'revenue' => $db->query("SELECT COALESCE(SUM(final_amount), 0) FROM orders WHERE status = 'completed'")->fetchColumn()
+ ];
+
+ // Получаем все категории
+ $allCategories = $db->query("SELECT * FROM categories WHERE is_active = TRUE ORDER BY name")->fetchAll();
+
+ // Получаем родительские категории
+ $parentCategories = $db->query("SELECT * FROM categories WHERE parent_id IS NULL AND is_active = TRUE ORDER BY name")->fetchAll();
+
+ switch ($action) {
+ case 'products':
+ $showAll = isset($_GET['show_all']) && $_GET['show_all'] == '1';
+ $sql = $showAll
+ ? "SELECT p.*, c.name as category_name FROM products p LEFT JOIN categories c ON p.category_id = c.category_id ORDER BY p.created_at DESC"
+ : "SELECT p.*, c.name as category_name FROM products p LEFT JOIN categories c ON p.category_id = c.category_id WHERE p.is_available = TRUE ORDER BY p.created_at DESC";
+ $data = $db->query($sql)->fetchAll();
+ break;
+
+ case 'categories':
+ $data = $db->query("
+ SELECT c1.*, c2.name as parent_name,
+ (SELECT COUNT(*) FROM products p WHERE p.category_id = c1.category_id) as product_count
+ FROM categories c1
+ LEFT JOIN categories c2 ON c1.parent_id = c2.category_id
+ ORDER BY c1.sort_order, c1.name
+ ")->fetchAll();
+ break;
+
+ case 'orders':
+ $data = $db->query("
+ SELECT o.*, u.email as user_email
+ FROM orders o
+ LEFT JOIN users u ON o.user_id = u.user_id
+ ORDER BY o.created_at DESC
+ LIMIT 50
+ ")->fetchAll();
+ break;
+
+ case 'users':
+ $data = $db->query("SELECT * FROM users ORDER BY created_at DESC LIMIT 50")->fetchAll();
+ break;
+
+ case 'add_product':
+ case 'edit_product':
+ if ($action === 'edit_product' && isset($_GET['id'])) {
+ $productId = (int)$_GET['id'];
+ $stmt = $db->prepare("SELECT * FROM products WHERE product_id = ?");
+ $stmt->execute([$productId]);
+ $edit_data = $stmt->fetch();
+ }
+ break;
+
+ case 'add_category':
+ case 'edit_category':
+ if ($action === 'edit_category' && isset($_GET['id'])) {
+ $categoryId = (int)$_GET['id'];
+ $stmt = $db->prepare("SELECT * FROM categories WHERE category_id = ?");
+ $stmt->execute([$categoryId]);
+ $edit_data = $stmt->fetch();
+ }
+ break;
+
+ }
+
+} catch (PDOException $e) {
+ $error = "Ошибка базы данных: " . $e->getMessage();
+}
+?>
+
+
+
+
+
+ AETERNA - Админ-панель
+
+
+
+
+
+
+
+
+
+
+
+ = htmlspecialchars(urldecode($message)) ?>
+
+
+
+
+
+ = htmlspecialchars(urldecode($error)) ?>
+
+
+
+
+
+
Статистика
+
+
+
= $stats['total_products'] ?>
+
Всего товаров
+
+
+
= $stats['active_products'] ?>
+
Активных товаров
+
+
+
= $stats['total_orders'] ?>
+
Заказов
+
+
+
= $stats['total_users'] ?>
+
Пользователей
+
+
+
+
+
+
+
+
+
Управление товарами
+
+
+
+
+
+
+ ID
+ Название
+ Категория
+ Цена
+ На складе
+ Статус
+ Действия
+
+
+
+
+
+ = $product['product_id'] ?>
+ = htmlspecialchars($product['name']) ?>
+ = htmlspecialchars($product['category_name'] ?? 'Без категории') ?>
+ = number_format($product['price'], 0, '', ' ') ?> ₽
+ = $product['stock_quantity'] ?>
+
+ 0): ?>
+ ✓ Доступен
+
+ ✗ Недоступен
+
+ ⚠ Нет на складе
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ID
+ Название
+ Slug
+ Родительская
+ Товаров
+ Действия
+
+
+
+
+
+ = $category['category_id'] ?>
+ = htmlspecialchars($category['name']) ?>
+ = htmlspecialchars($category['slug']) ?>
+ = htmlspecialchars($category['parent_name'] ?? '—') ?>
+ = $category['product_count'] ?>
+
+
+
+ Редактировать
+
+
+
+ 0 ? 'disabled' : '' ?>>
+ Удалить
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Заказы
+
+
+
+ № заказа
+ Клиент
+ Сумма
+ Статус
+ Дата
+ Действия
+
+
+
+
+
+ = htmlspecialchars($order['order_number']) ?>
+ = htmlspecialchars($order['customer_name']) ?>
+ = number_format($order['final_amount'], 0, '', ' ') ?> ₽
+ = htmlspecialchars($order['status']) ?>
+ = date('d.m.Y H:i', strtotime($order['created_at'])) ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
Пользователи
+
+
+
+ ID
+ Email
+ ФИО
+ Дата регистрации
+ Статус
+
+
+
+
+
+ = $user['user_id'] ?>
+ = htmlspecialchars($user['email']) ?>
+ = htmlspecialchars($user['full_name']) ?>
+ = date('d.m.Y', strtotime($user['created_at'])) ?>
+
+
+ ✓ Активен
+
+ ✗ Неактивен
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/catalog.php b/catalog.php
new file mode 100644
index 0000000..f731d5f
--- /dev/null
+++ b/catalog.php
@@ -0,0 +1,1382 @@
+getConnection();
+
+// Получаем параметры фильтрации
+$category_id = $_GET['category'] ?? 0;
+$search = $_GET['search'] ?? '';
+$min_price = $_GET['min_price'] ?? 0;
+$max_price = $_GET['max_price'] ?? 1000000;
+$colors = isset($_GET['colors']) ? (array)$_GET['colors'] : [];
+$materials = isset($_GET['materials']) ? (array)$_GET['materials'] : [];
+$show_all = isset($_GET['show_all']) && $_GET['show_all'] == '1';
+
+// Проверяем уведомления
+$success_message = $_GET['success'] ?? '';
+$error_message = $_GET['error'] ?? '';
+
+try {
+ // Получаем информацию о пользователе
+ $user_id = $_SESSION['user_id'] ?? 0;
+ $userStmt = $db->prepare("SELECT * FROM users WHERE user_id = ?");
+ $userStmt->execute([$user_id]);
+ $user = $userStmt->fetch();
+
+ $isAdmin = isset($_SESSION['isAdmin']) && $_SESSION['isAdmin'] === true;
+ $userEmail = $_SESSION['user_email'] ?? '';
+ $fullName = $_SESSION['full_name'] ?? $userEmail;
+ $loginTime = $_SESSION['login_time'] ?? time();
+
+ // Получаем категории (ИСПРАВЛЕНО: убрано дублирование fetchAll)
+ try {
+ $categoriesStmt = $db->prepare("
+ SELECT * FROM categories
+ WHERE is_active = TRUE
+ ORDER BY sort_order, name
+ ");
+ $categoriesStmt->execute();
+ $categories = $categoriesStmt->fetchAll();
+ } catch (PDOException $e) {
+ $categories = [];
+ error_log("Ошибка получения категорий: " . $e->getMessage());
+ }
+
+ // Получаем подкатегории для текущей категории (если выбрана)
+ $subcategories = [];
+ if ($category_id > 0) {
+ $subStmt = $db->prepare("
+ SELECT * FROM subcategories
+ WHERE category_id = ? AND is_active = TRUE
+ ORDER BY sort_order, name
+ ");
+ $subStmt->execute([$category_id]);
+ $subcategories = $subStmt->fetchAll();
+ }
+
+ // Проверяем наличие столбцов color и material в таблице products
+ $checkColumns = $db->query("
+ SELECT column_name
+ FROM information_schema.columns
+ WHERE table_name = 'products'
+ AND column_name IN ('color', 'material')
+ ");
+ $existingColumns = $checkColumns->fetchAll(PDO::FETCH_COLUMN);
+
+ $hasColorColumn = in_array('color', $existingColumns);
+ $hasMaterialColumn = in_array('material', $existingColumns);
+
+ // Получаем доступные цвета из базы данных (если столбец существует)
+ $availableColors = [];
+ if ($hasColorColumn) {
+ $colorsStmt = $db->query("
+ SELECT DISTINCT color FROM products
+ WHERE color IS NOT NULL AND color != '' AND is_available = TRUE
+ ORDER BY color
+ ");
+ $availableColors = $colorsStmt->fetchAll(PDO::FETCH_COLUMN);
+ }
+
+ // Получаем доступные материалы из базы данных (если столбец существует)
+ $availableMaterials = [];
+ if ($hasMaterialColumn) {
+ $materialsStmt = $db->query("
+ SELECT DISTINCT material FROM products
+ WHERE material IS NOT NULL AND material != '' AND is_available = TRUE
+ ORDER BY material
+ ");
+ $availableMaterials = $materialsStmt->fetchAll(PDO::FETCH_COLUMN);
+ }
+
+ // Получаем ВСЕ товары ИЗ БАЗЫ ДАННЫХ с фильтрами
+ $sql = "SELECT p.*, c.name as category_name
+ FROM products p
+ LEFT JOIN categories c ON p.category_id = c.category_id
+ WHERE 1=1";
+
+ $params = [];
+ $hasFilters = false;
+
+ // Фильтрация по доступности (если не админ и не показываем все)
+ if (!$show_all && !$isAdmin) {
+ $sql .= " AND p.is_available = TRUE";
+ }
+
+ // Фильтрация по категории
+ if ($category_id > 0) {
+ $sql .= " AND p.category_id = ?";
+ $params[] = $category_id;
+ $hasFilters = true;
+ }
+
+ // Фильтрация по цене
+ if ($min_price > 0 || $max_price < 1000000) {
+ $sql .= " AND p.price BETWEEN ? AND ?";
+ $params[] = $min_price;
+ $params[] = $max_price;
+ $hasFilters = true;
+ }
+
+ // Фильтрация по цвету
+ if ($hasColorColumn && !empty($colors)) {
+ $placeholders = implode(',', array_fill(0, count($colors), '?'));
+ $sql .= " AND p.color IN ($placeholders)";
+ $params = array_merge($params, $colors);
+ $hasFilters = true;
+ }
+
+ // Фильтрация по материалу
+ if ($hasMaterialColumn && !empty($materials)) {
+ $placeholders = implode(',', array_fill(0, count($materials), '?'));
+ $sql .= " AND p.material IN ($placeholders)";
+ $params = array_merge($params, $materials);
+ $hasFilters = true;
+ }
+
+ // Поиск
+ if (!empty($search)) {
+ $sql .= " AND (p.name LIKE ? OR p.description LIKE ?)";
+ $params[] = "%$search%";
+ $params[] = "%$search%";
+ $hasFilters = true;
+ }
+
+ // Получаем товары из базы данных с фильтрами
+ $sql = "SELECT p.*, c.name as category_name
+ FROM products p
+ LEFT JOIN categories c ON p.category_id = c.category_id
+ WHERE 1=1";
+
+ $params = [];
+
+ // Фильтрация по доступности
+ if (!$show_all && !$isAdmin) {
+ $sql .= " AND p.is_available = TRUE";
+ }
+
+ // Фильтрация по категории
+ if ($category_id > 0) {
+ $sql .= " AND p.category_id = ?";
+ $params[] = $category_id;
+ }
+
+ // Фильтрация по цене
+ if ($min_price > 0 || $max_price < 1000000) {
+ $sql .= " AND p.price BETWEEN ? AND ?";
+ $params[] = $min_price;
+ $params[] = $max_price;
+ }
+
+ // Поиск
+ if (!empty($search)) {
+ $sql .= " AND (p.name LIKE ? OR p.description LIKE ?)";
+ $params[] = "%$search%";
+ $params[] = "%$search%";
+ }
+
+ $sql .= " ORDER BY p.product_id ASC LIMIT 9"; // Ограничиваем 9 товаров
+
+ $stmt = $db->prepare($sql);
+ $stmt->execute($params);
+ $filteredProducts = $stmt->fetchAll();
+
+ // Оригинальные размеры для первых 9 товаров (ID 1-9)
+ $originalSizes = [
+ 1 => 'small', // Светильник MINNIGHT
+ 2 => 'large', // Диван MODERN (Кровать MODER)
+ 3 => 'tall align-right', // Торшер MARCIA
+ 4 => 'wide', // Светильник POLET
+ 5 => 'small1', // Стол NORD
+ 6 => 'wide2', // Диван ROYALTY
+ 7 => 'wide3', // Кресло MINIMAL
+ 8 => 'wide2_1', // Стол LONKI
+ 9 => 'full-width' // Диван HEMMINS
+ ];
+
+ // Классы для изображений
+ $imgClasses = ['small1', 'wide2', 'wide3', 'wide2_1'];
+
+} catch (PDOException $e) {
+ die("Ошибка базы данных: " . $e->getMessage());
+}
+?>
+
+
+
+
+
+ AETERNA - Каталог
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'Категория успешно добавлена!',
+ 'category_updated' => 'Категория успешно обновлена!',
+ 'category_deleted' => 'Категория успешно удалена!',
+ 'product_added' => 'Товар успешно добавлен!',
+ 'product_updated' => 'Товар успешно обновлен!',
+ 'product_deleted' => 'Товар успешно удален!'
+ ];
+ echo $messages[$success_message] ?? 'Операция выполнена успешно!';
+ ?>
+
+
+
+
+
+ Ошибка: = htmlspecialchars($error_message) ?>
+
+
+
+
+
+
+
+ Панель управления каталогом
+
+ Вы вошли как администратор
+
+
+
+
+
+
+
+
+
+ Добро пожаловать, = htmlspecialchars($fullName) ?> !
+
+
+ Администратор
+
+
+
+
+ Показаны все товары
+
+
+
+
+
+
+
+
+
+
+ Каталог мебели
+
+ ( товаров)
+
+
+
+
+
+ Результаты поиска по запросу: "= htmlspecialchars($search) ?> "
+
+ Очистить поиск
+
+
+
+
+
+
+
+
+ Показаны все товары, включая недоступные. Недоступные товары отмечены серым цветом.
+
+
+
+
+
+
+
+ ['name' => 'Светильник MINNIGHT', 'price' => 7999, 'image' => 'img2/1_2.png', 'size' => 'small'],
+ 2 => ['name' => 'Кровать MODER', 'price' => 45999, 'image' => 'img2/3_3.png', 'size' => 'large'],
+ 3 => ['name' => 'Торшер MARCIA', 'price' => 11999, 'image' => 'img2/2_2.png', 'size' => 'tall align-right'],
+ 4 => ['name' => 'Светильник POLET', 'price' => 5499, 'image' => 'img2/4.jpg', 'size' => 'wide'],
+ 5 => ['name' => 'Стол NORD', 'price' => 23999, 'image' => 'img2/5_5.png', 'size' => 'small1'],
+ 6 => ['name' => 'Диван ROYALTY', 'price' => 78999, 'image' => 'img2/6_6.png', 'size' => 'wide2'],
+ 7 => ['name' => 'Кресло MINIMAL', 'price' => 29999, 'image' => 'img2/7_7.png', 'size' => 'wide3'],
+ 8 => ['name' => 'Стол LONKI', 'price' => 34999, 'image' => 'img2/8_8.png', 'size' => 'wide2_1'],
+ 9 => ['name' => 'Диван HEMMINS', 'price' => 89999, 'image' => 'img2/9_9.png', 'size' => 'full-width']
+ ];
+
+ // Если в базе данных товаров нет, показываем оригинальные
+ if (empty($filteredProducts)) {
+ foreach ($originalProducts as $id => $product): ?>
+
+
+
+
+
+
= $product['name'] ?>
+
= number_format($product['price'], 0, '', ' ') ?> ₽
+
+
+
+
+
+
+
+
+
+
+
+
= htmlspecialchars($product['name']) ?>
+
= number_format($product['price'], 0, '', ' ') ?> ₽
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/catalog_admin.php b/catalog_admin.php
new file mode 100644
index 0000000..524f499
--- /dev/null
+++ b/catalog_admin.php
@@ -0,0 +1,924 @@
+getConnection();
+
+ // ОБРАБОТКА POST ЗАПРОСОВ ДЛЯ КАТЕГОРИЙ
+ if ($_SERVER['REQUEST_METHOD'] === 'POST' && $is_admin) {
+ $form_action = $_POST['action'] ?? '';
+
+ if ($form_action === 'add_category' || $form_action === 'edit_category') {
+ $name = trim($_POST['name'] ?? '');
+ $parent_id = !empty($_POST['parent_id']) ? (int)$_POST['parent_id'] : null;
+ $description = trim($_POST['description'] ?? '');
+ $sort_order = (int)($_POST['sort_order'] ?? 0);
+ $is_active = isset($_POST['is_active']) ? 1 : 0;
+ $category_id_post = (int)($_POST['category_id'] ?? 0);
+
+ // Валидация
+ if (empty($name)) {
+ $_SESSION['error'] = 'Название категории обязательно';
+ header('Location: catalog_admin.php?action=' . $form_action . ($category_id_post ? '&id=' . $category_id_post : ''));
+ exit();
+ }
+
+ // Создаем slug
+ $slug = strtolower(preg_replace('/[^a-z0-9]+/i', '-', $name));
+ $slug = preg_replace('/^-+|-+$/', '', $slug); // Убираем дефисы по краям
+
+ if ($form_action === 'add_category') {
+ // Проверяем существование slug
+ $check = $db->prepare("SELECT COUNT(*) FROM categories WHERE slug = ?");
+ $check->execute([$slug]);
+ if ($check->fetchColumn() > 0) {
+ $slug = $slug . '-' . time(); // Добавляем timestamp для уникальности
+ }
+
+ $sql = "INSERT INTO categories (name, slug, parent_id, description, sort_order, is_active)
+ VALUES (?, ?, ?, ?, ?, ?)";
+ $stmt = $db->prepare($sql);
+ $stmt->execute([$name, $slug, $parent_id, $description, $sort_order, $is_active]);
+
+ $_SESSION['message'] = 'Категория успешно добавлена';
+ header('Location: catalog_admin.php?action=categories');
+ exit();
+
+ } elseif ($form_action === 'edit_category' && $category_id_post > 0) {
+ // Проверяем существование slug для других категорий
+ $check = $db->prepare("SELECT COUNT(*) FROM categories WHERE slug = ? AND category_id != ?");
+ $check->execute([$slug, $category_id_post]);
+ if ($check->fetchColumn() > 0) {
+ $slug = $slug . '-' . $category_id_post;
+ }
+
+ $sql = "UPDATE categories SET
+ name = ?, slug = ?, parent_id = ?, description = ?,
+ sort_order = ?, is_active = ?, updated_at = CURRENT_TIMESTAMP
+ WHERE category_id = ?";
+ $stmt = $db->prepare($sql);
+ $stmt->execute([$name, $slug, $parent_id, $description, $sort_order, $is_active, $category_id_post]);
+
+ $_SESSION['message'] = 'Категория успешно обновлена';
+ header('Location: catalog_admin.php?action=categories');
+ exit();
+ }
+ }
+
+ // Удаление категории
+ if ($form_action === 'delete_category') {
+ $category_id_del = (int)($_POST['category_id'] ?? 0);
+
+ if ($category_id_del > 0) {
+ // Проверяем наличие активных товаров
+ $check_products = $db->prepare("
+ SELECT COUNT(*) as product_count
+ FROM products
+ WHERE category_id = ? AND is_available = TRUE
+ ");
+ $check_products->execute([$category_id_del]);
+ $active_products = $check_products->fetchColumn();
+
+ if ($active_products > 0) {
+ $_SESSION['error'] = 'Невозможно удалить категорию с активными товарами';
+ header('Location: catalog_admin.php?action=categories');
+ exit();
+ }
+
+ // Проверяем дочерние категории
+ $check_children = $db->prepare("
+ SELECT COUNT(*) as child_count
+ FROM categories
+ WHERE parent_id = ? AND is_active = TRUE
+ ");
+ $check_children->execute([$category_id_del]);
+ $active_children = $check_children->fetchColumn();
+
+ if ($active_children > 0) {
+ $_SESSION['error'] = 'Невозможно удалить категорию с активными дочерними категориями';
+ header('Location: catalog_admin.php?action=categories');
+ exit();
+ }
+
+ // Удаляем категорию
+ $stmt = $db->prepare("DELETE FROM categories WHERE category_id = ?");
+ $stmt->execute([$category_id_del]);
+
+ $_SESSION['message'] = 'Категория успешно удалена';
+ header('Location: catalog_admin.php?action=categories');
+ exit();
+ }
+ }
+ }
+
+ // Получаем категории для отображения
+ $categories_stmt = $db->query("
+ SELECT c1.*, c2.name as parent_name,
+ (SELECT COUNT(*) FROM products WHERE category_id = c1.category_id) as product_count
+ FROM categories c1
+ LEFT JOIN categories c2 ON c1.parent_id = c2.category_id
+ ORDER BY c1.sort_order, c1.name
+ ");
+ $categories = $categories_stmt->fetchAll();
+
+ // Для редактирования категории
+ if ($action === 'edit_category' && $product_id > 0) {
+ $cat_stmt = $db->prepare("SELECT * FROM categories WHERE category_id = ?");
+ $cat_stmt->execute([$product_id]);
+ $current_category = $cat_stmt->fetch();
+
+ if (!$current_category) {
+ $_SESSION['error'] = 'Категория не найдена';
+ header('Location: catalog_admin.php?action=categories');
+ exit();
+ }
+ }
+
+ // Получаем товары
+ $sql = "SELECT p.*, c.name as category_name FROM products p
+ LEFT JOIN categories c ON p.category_id = c.category_id
+ WHERE p.is_available = TRUE";
+
+ $params = [];
+ if ($category_id && is_numeric($category_id)) {
+ $sql .= " AND p.category_id = ?";
+ $params[] = $category_id;
+ }
+
+ $sql .= " ORDER BY p.product_id DESC";
+
+ if ($params) {
+ $stmt = $db->prepare($sql);
+ $stmt->execute($params);
+ } else {
+ $stmt = $db->query($sql);
+ }
+
+ $products = $stmt->fetchAll();
+
+ // Сообщения из сессии
+ $message = $_SESSION['message'] ?? '';
+ $error = $_SESSION['error'] ?? '';
+
+ // Очищаем сообщения после использования
+ unset($_SESSION['message']);
+ unset($_SESSION['error']);
+
+} catch (PDOException $e) {
+ $error = "Ошибка подключения к базе данных: " . $e->getMessage();
+}
+?>
+
+
+
+
+
+
+ AETERNA - Каталог
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ = htmlspecialchars($message) ?>
+
+
+
+
+
+ = htmlspecialchars($error) ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ID
+ Название
+ Родительская
+ Товаров
+ Порядок
+ Статус
+ Действия
+
+
+
+
+
+ = $cat['category_id'] ?>
+
+ = htmlspecialchars($cat['name']) ?>
+ = htmlspecialchars($cat['slug']) ?>
+
+ = htmlspecialchars($cat['parent_name'] ?? '—') ?>
+
+ = $cat['product_count'] ?> товаров
+
+ = $cat['sort_order'] ?>
+
+
+ Активна
+
+ Неактивна
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $product['price']): ?>
+
+ -= round(($product['old_price'] - $product['price']) / $product['old_price'] * 100) ?>%
+
+
+
+
+
+
+
+
= htmlspecialchars($product['name']) ?>
+
+ = htmlspecialchars(mb_substr($product['description'], 0, 100)) ?>...
+
+
+ $product['price']): ?>
+
+ = number_format($product['old_price'], 0, '', ' ') ?> ₽
+
+
+ = number_format($product['price'], 0, '', ' ') ?> ₽
+
+
+
+ В наличии: = $product['stock_quantity'] ?> шт.
+
+
+
+ В корзину
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+getConnection();
+$action = $_POST['action'] ?? '';
+
+if ($_SERVER['REQUEST_METHOD'] === 'POST') {
+ try {
+ switch ($action) {
+ case 'add_category':
+ $name = $_POST['name'] ?? '';
+ $parent_id = $_POST['parent_id'] ?: null;
+ $description = $_POST['description'] ?? null;
+ $sort_order = $_POST['sort_order'] ?? 0;
+ $is_active = isset($_POST['is_active']) ? 1 : 0;
+
+ // Создаем slug из названия
+ $slug = strtolower(preg_replace('/[^a-z0-9]+/i', '-', $name));
+
+ $stmt = $db->prepare("
+ INSERT INTO categories (name, slug, parent_id, description, sort_order, is_active)
+ VALUES (?, ?, ?, ?, ?, ?)
+ ");
+
+ $stmt->execute([$name, $slug, $parent_id, $description, $sort_order, $is_active]);
+
+ header('Location: catalog_admin.php?action=categories&message=Категория успешно добавлена');
+ exit();
+
+ case 'edit_category':
+ $category_id = $_POST['category_id'] ?? 0;
+ $name = $_POST['name'] ?? '';
+ $parent_id = $_POST['parent_id'] ?: null;
+ $description = $_POST['description'] ?? null;
+ $sort_order = $_POST['sort_order'] ?? 0;
+ $is_active = isset($_POST['is_active']) ? 1 : 0;
+
+ // Создаем slug из названия
+ $slug = strtolower(preg_replace('/[^a-z0-9]+/i', '-', $name));
+
+ $stmt = $db->prepare("
+ UPDATE categories SET
+ name = ?,
+ slug = ?,
+ parent_id = ?,
+ description = ?,
+ sort_order = ?,
+ is_active = ?
+ WHERE category_id = ?
+ ");
+
+ $stmt->execute([$name, $slug, $parent_id, $description, $sort_order, $is_active, $category_id]);
+
+ header('Location: catalog_admin.php?action=categories&message=Категория успешно обновлена');
+ exit();
+
+ case 'delete_category':
+ $category_id = $_POST['category_id'] ?? 0;
+
+ // Проверяем, есть ли активные товары в этой категории
+ $checkStmt = $db->prepare("
+ SELECT COUNT(*)
+ FROM products
+ WHERE category_id = ? AND is_available = TRUE
+ ");
+ $checkStmt->execute([$category_id]);
+ $active_products = $checkStmt->fetchColumn();
+
+ if ($active_products > 0) {
+ header('Location: catalog_admin.php?action=categories&error=Невозможно удалить категорию с активными товарами');
+ exit();
+ }
+
+ // Проверяем дочерние категории
+ $checkChildStmt = $db->prepare("
+ SELECT COUNT(*)
+ FROM categories
+ WHERE parent_id = ? AND is_active = TRUE
+ ");
+ $checkChildStmt->execute([$category_id]);
+ $active_children = $checkChildStmt->fetchColumn();
+
+ if ($active_children > 0) {
+ header('Location: catalog_admin.php?action=categories&error=Невозможно удалить категорию с активными дочерними категориями');
+ exit();
+ }
+
+ // Удаляем категорию
+ $stmt = $db->prepare("DELETE FROM categories WHERE category_id = ?");
+ $stmt->execute([$category_id]);
+
+ header('Location: catalog_admin.php?action=categories&message=Категория успешно удалена');
+ exit();
+ }
+
+ } catch (PDOException $e) {
+ header('Location: catalog_admin.php?action=categories&error=' . urlencode('Ошибка базы данных: ' . $e->getMessage()));
+ exit();
+ }
+}
+
+header('Location: catalog_admin.php');
+exit();
+?>
\ No newline at end of file
diff --git a/check_admin.php b/check_admin.php
new file mode 100644
index 0000000..e64c585
--- /dev/null
+++ b/check_admin.php
@@ -0,0 +1,17 @@
+
\ No newline at end of file
diff --git a/check_auth_status.php b/check_auth_status.php
new file mode 100644
index 0000000..5ddf663
--- /dev/null
+++ b/check_auth_status.php
@@ -0,0 +1,21 @@
+ isset($_SESSION['isLoggedIn']) && $_SESSION['isLoggedIn'] === true
+];
+
+if ($response['loggedIn']) {
+ $response['user'] = [
+ 'user_id' => $_SESSION['user_id'] ?? 0,
+ 'email' => $_SESSION['user_email'] ?? '',
+ 'full_name' => $_SESSION['full_name'] ?? '',
+ 'is_admin' => $_SESSION['isAdmin'] ?? false,
+ 'login_time' => $_SESSION['login_time'] ?? time()
+ ];
+}
+
+header('Content-Type: application/json');
+echo json_encode($response);
+?>
\ No newline at end of file
diff --git a/check_categories_table.php b/check_categories_table.php
new file mode 100644
index 0000000..6fa72ca
--- /dev/null
+++ b/check_categories_table.php
@@ -0,0 +1,51 @@
+getConnection();
+
+echo "Проверка категорий в базе данных ";
+
+try {
+ $stmt = $db->query("SELECT category_id, name, slug, parent_id FROM categories ORDER BY category_id");
+ $categories = $stmt->fetchAll();
+
+ if (empty($categories)) {
+ echo "Категорий нет! Нужно сначала добавить категории.
";
+
+ // Добавим тестовые категории
+ $insert_sql = "
+ INSERT INTO categories (name, slug, parent_id, description) VALUES
+ ('Мягкая мебель', 'myagkaya-mebel', NULL, 'Диваны, кресла, пуфы'),
+ ('Диваны', 'divany', 1, 'Прямые и угловые диваны'),
+ ('Кресла', 'kresla', 1, 'Кресла для гостиной и офиса'),
+ ('Спальня', 'spalnya', NULL, 'Кровати, тумбы, комоды'),
+ ('Кровати', 'krovati', 4, 'Односпальные и двуспальные кровати')
+ RETURNING category_id
+ ";
+
+ $db->exec($insert_sql);
+ echo "Добавлены тестовые категории
";
+
+ // Снова проверим
+ $stmt = $db->query("SELECT category_id, name, slug, parent_id FROM categories ORDER BY category_id");
+ $categories = $stmt->fetchAll();
+ }
+
+ echo "";
+ echo "ID Название Slug Родитель ";
+
+ foreach ($categories as $category) {
+ echo "";
+ echo "" . $category['category_id'] . " ";
+ echo "" . htmlspecialchars($category['name']) . " ";
+ echo "" . htmlspecialchars($category['slug']) . " ";
+ echo "" . ($category['parent_id'] ?: '-') . " ";
+ echo " ";
+ }
+
+ echo "
";
+
+} catch (PDOException $e) {
+ echo "Ошибка: " . $e->getMessage();
+}
+?>
\ No newline at end of file
diff --git a/cite_mebel.php b/cite_mebel.php
new file mode 100644
index 0000000..ad53c38
--- /dev/null
+++ b/cite_mebel.php
@@ -0,0 +1,762 @@
+
+
+
+
+
+ AETERNA - Мебель и Интерьер
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
ДОБАВЬТЕ ИЗЫСКАННОСТИ В СВОЙ ИНТЕРЬЕР
+
Мы создаем мебель, которая сочетает в себе безупречный дизайн, натуральные материалы, продуманный функционал, чтобы ваш день начинался и заканчивался с комфортом.
+
+
+
ПЕРЕЙТИ В КАТАЛОГ
+
+
ПЕРЕЙТИ В КАТАЛОГ
+
+
+
+
+
+
+
+
+
+
+
+
О НАС
+
Компания AETERNA - российский производитель качественной корпусной и мягкой мебели для дома и офиса. С 2015 года мы успешно реализуем проекты любой сложности, сочетая современные технологии, проверенные материалы и классическое мастерство.
+
+
+
+
+
+
+
Наша сеть включает 30+ российских фабрик, отобранных по строгим стандартам качества. Мы сотрудничаем исключительно с лидерами рынка, чья продукция доказала свое превосходство временем.
+
+
+
+
+
+
+
+
+
+
+
+
ГОТОВОЕ РЕШЕНИЕ ДЛЯ ВАШЕЙ ГОСТИНОЙ
+
УСПЕЙТЕ ЗАКАЗАТЬ СЕЙЧАС
+
+
Подробнее
+
+
+
+
+
+
ГОТОВОЕ РЕШЕНИЕ ДЛЯ ВАШЕЙ СПАЛЬНИ
+
УСПЕЙТЕ ЗАКАЗАТЬ СЕЙЧАС
+
+
Подробнее
+
+
+
+
+
+
+
+
+
+
+
+
30 000+
+
Довольных покупателей
+
+
+
4500+
+
Реализованных заказов
+
+
+
+
+
+
+
+
ОТВЕТЫ НА ВОПРОСЫ
+
+
+
1
+
+
Сколько времени занимает доставка?
+
Доставка готовых позиций занимает 1-3 дня. Мебель на заказ изготавливается от 14 до 45 рабочих дней, в зависимости от сложности. Точные сроки озвучит ваш менеджер при оформлении заказа.
+
+
+
+
2
+
+
Нужно ли вносить предоплату?
+
Да, для запуска заказа в производство необходима предоплата в размере 50-70% от стоимости, в зависимости от изделия. Оставшаяся сумма оплачивается при доставке и приемке мебели.
+
+
+
+
3
+
+
Предоставляется ли рассрочка или кредит?
+
Да, мы сотрудничаем с несколькими банками и предлагаем рассрочку на 6 или 12 месяцев без первоначального взноса, а также кредит на более длительный срок. Все условия уточняйте у вашего менеджера.
+
+
+
+
4
+
+
Что делать, если мебель пришла с дефектом?
+
В этом случае необходимо в течение 7 дней со дня доставки сообщить нам о проблеме, прислать фото/видео дефекта. Мы оперативно решим вопрос о бесплатной замене или ремонте изделия.
+
+
+
+
Задать вопрос
+
+
+
+
+
+
+
+
+
+
+
+
+ Быстрый вход
+
+
+
+ Войти как Администратор
+
+
+ Войти как Пользователь
+
+
+
+ Для полного функционала перейдите на страницу входа
+
+
+
+
+
+
+
+
+
+
+
+
Быстрый вход:
+
+ Войти как Админ
+
+
+ Войти как Пользователь
+
+
+
+
+
\ No newline at end of file
diff --git a/config/check_auth.js b/config/check_auth.js
new file mode 100644
index 0000000..e67bbe1
--- /dev/null
+++ b/config/check_auth.js
@@ -0,0 +1,114 @@
+// check_auth.js
+$(document).ready(function() {
+ // Проверка авторизации при загрузке страницы
+ checkAuthStatus();
+
+ // Обработка формы входа
+ $('#loginForm').on('submit', function(e) {
+ e.preventDefault();
+
+ const email = $('#login-email').val();
+ const password = $('#login-password').val();
+ const remember = $('#remember').is(':checked');
+
+ $.ajax({
+ url: 'login_handler.php',
+ method: 'POST',
+ data: {
+ email: email,
+ password: password
+ },
+ success: function(response) {
+ try {
+ const result = JSON.parse(response);
+ if (result.success) {
+ // Сохраняем в localStorage если выбрано "Запомнить меня"
+ if (remember) {
+ localStorage.setItem('rememberedEmail', email);
+ } else {
+ localStorage.removeItem('rememberedEmail');
+ }
+
+ // Перенаправляем
+ window.location.href = result.redirect || 'catalog.php';
+ } else {
+ showMessage('error', result.message || 'Ошибка авторизации');
+ }
+ } catch(e) {
+ showMessage('error', 'Ошибка обработки ответа');
+ }
+ },
+ error: function() {
+ showMessage('error', 'Ошибка сервера');
+ }
+ });
+ });
+
+ // Проверка статуса авторизации
+ function checkAuthStatus() {
+ $.ajax({
+ url: 'check_auth_status.php',
+ method: 'GET',
+ success: function(response) {
+ try {
+ const result = JSON.parse(response);
+ if (result.loggedIn) {
+ updateUserProfile(result.user);
+ }
+ } catch(e) {
+ console.error('Ошибка проверки авторизации', e);
+ }
+ }
+ });
+ }
+
+ // Обновление профиля пользователя
+ function updateUserProfile(user) {
+ // Обновляем шапку, если есть элементы для профиля
+ if ($('#userEmail').length) {
+ $('#userEmail').text(user.email);
+ }
+ if ($('#userName').length) {
+ $('#userName').text(user.full_name);
+ }
+ }
+
+ // Показать сообщение
+ function showMessage(type, text) {
+ const $message = $('#' + type + 'Message');
+ if ($message.length) {
+ $message.text(text).fadeIn();
+ setTimeout(() => $message.fadeOut(), 5000);
+ } else {
+ alert(text);
+ }
+ }
+
+ // Проверка авторизации для ссылок
+ function checkAuth(redirectUrl) {
+ $.ajax({
+ url: 'check_auth_status.php',
+ method: 'GET',
+ success: function(response) {
+ try {
+ const result = JSON.parse(response);
+ if (result.loggedIn) {
+ window.location.href = redirectUrl;
+ } else {
+ // Показываем модальное окно или перенаправляем на вход
+ showLoginModal(redirectUrl);
+ }
+ } catch(e) {
+ showLoginModal(redirectUrl);
+ }
+ }
+ });
+ return false;
+ }
+
+ // Показать модальное окно входа
+ function showLoginModal(redirectUrl) {
+ // Можно реализовать модальное окно или перенаправить на страницу входа
+ window.location.href = 'вход.php?redirect=' + encodeURIComponent(redirectUrl);
+ }
+});
\ No newline at end of file
diff --git a/config/database.php b/config/database.php
new file mode 100644
index 0000000..d7e632b
--- /dev/null
+++ b/config/database.php
@@ -0,0 +1,32 @@
+connection = new PDO(
+ "pgsql:host=localhost;dbname=aeterna_db;",
+ "postgres",
+ "1234"
+ );
+ $this->connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+ $this->connection->exec("SET NAMES 'utf8'");
+ } catch(PDOException $e) {
+ die("Ошибка подключения: " . $e->getMessage());
+ }
+ }
+
+ public static function getInstance() {
+ if (self::$instance == null) {
+ self::$instance = new Database();
+ }
+ return self::$instance;
+ }
+
+ public function getConnection() {
+ return $this->connection;
+ }
+}
+?>
\ No newline at end of file
diff --git a/debug_db.php b/debug_db.php
new file mode 100644
index 0000000..32ca002
--- /dev/null
+++ b/debug_db.php
@@ -0,0 +1,55 @@
+getConnection();
+
+echo "Проверка базы данных: ";
+
+// Проверка таблиц
+$tables = ['users', 'categories', 'products', 'orders', 'order_items', 'cart'];
+foreach ($tables as $table) {
+ try {
+ $result = $db->query("SELECT COUNT(*) FROM $table")->fetchColumn();
+ echo "✅ Таблица '$table': $result записей ";
+ } catch (Exception $e) {
+ echo "❌ Таблица '$table': НЕ СУЩЕСТВУЕТ ";
+ }
+}
+
+echo "Содержимое таблиц: ";
+
+// Показать категории
+echo "Категории: ";
+try {
+ $categories = $db->query("SELECT * FROM categories")->fetchAll();
+ if (empty($categories)) {
+ echo "Категорий нет! ";
+ } else {
+ echo "ID Название Slug Родитель ";
+ foreach ($categories as $cat) {
+ echo "{$cat['category_id']} {$cat['name']} {$cat['slug']} {$cat['parent_id']} ";
+ }
+ echo "
";
+ }
+} catch (Exception $e) {
+ echo "Ошибка: " . $e->getMessage();
+}
+
+// Показать товары
+echo "Товары: ";
+try {
+ $products = $db->query("SELECT * FROM products")->fetchAll();
+ if (empty($products)) {
+ echo "Товаров нет! ";
+ } else {
+ echo "ID Название Цена Категория Статус ";
+ foreach ($products as $product) {
+ echo "{$product['product_id']} {$product['name']} {$product['price']} {$product['category_id']} " . ($product['is_available'] ? 'Активен' : 'Неактивен') . " ";
+ }
+ echo "
";
+ }
+} catch (Exception $e) {
+ echo "Ошибка: " . $e->getMessage();
+}
+?>
\ No newline at end of file
diff --git a/fix_categories.php b/fix_categories.php
new file mode 100644
index 0000000..62503c1
--- /dev/null
+++ b/fix_categories.php
@@ -0,0 +1,69 @@
+getConnection();
+
+echo "Исправление проблем с категориями ";
+
+try {
+ // 1. Удаляем категорию с ID=0 если она есть
+ $db->exec("DELETE FROM categories WHERE category_id = 0");
+
+ // 2. Проверяем, есть ли категории
+ $catCount = $db->query("SELECT COUNT(*) FROM categories")->fetchColumn();
+
+ if ($catCount == 0) {
+ echo "Добавляем основные категории...
";
+ $db->exec("
+ INSERT INTO categories (name, slug, description, is_active) VALUES
+ ('Диваны', 'divany', 'Мягкая мебель для гостиной', TRUE),
+ ('Кресла', 'kresla', 'Кресла для гостиной и офиса', TRUE),
+ ('Кровати', 'krovati', 'Мебель для спальни', TRUE),
+ ('Столы', 'stoly', 'Обеденные и рабочие столы', TRUE),
+ ('Стулья', 'stulya', 'Стулья для кухни и офиса', TRUE)
+ ");
+ echo "✓ Категории добавлены
";
+ }
+
+ // 3. Исправляем товары с category_id = 0 или NULL
+ $badProducts = $db->query("
+ SELECT COUNT(*) FROM products
+ WHERE category_id IS NULL OR category_id = 0 OR
+ category_id NOT IN (SELECT category_id FROM categories)
+ ")->fetchColumn();
+
+ if ($badProducts > 0) {
+ echo "Исправляем товары с некорректными категориями ($badProducts шт)...
";
+
+ // Получаем первую категорию
+ $firstCat = $db->query("SELECT category_id FROM categories LIMIT 1")->fetchColumn();
+
+ if ($firstCat) {
+ $db->exec("
+ UPDATE products
+ SET category_id = $firstCat
+ WHERE category_id IS NULL OR category_id = 0 OR
+ category_id NOT IN (SELECT category_id FROM categories)
+ ");
+ echo "✓ Товары исправлены (category_id установлен в $firstCat)
";
+ }
+ }
+
+ // 4. Показываем текущее состояние
+ echo "Текущие категории: ";
+ $cats = $db->query("SELECT category_id, name FROM categories ORDER BY category_id")->fetchAll();
+
+ echo "";
+ echo "ID Название ";
+ foreach ($cats as $cat) {
+ echo "{$cat['category_id']} {$cat['name']} ";
+ }
+ echo "
";
+
+ echo "✓ База данных исправлена!
";
+
+} catch (PDOException $e) {
+ echo "Ошибка: " . $e->getMessage() . "
";
+}
+?>
\ No newline at end of file
diff --git a/fix_database.php b/fix_database.php
new file mode 100644
index 0000000..9edc526
--- /dev/null
+++ b/fix_database.php
@@ -0,0 +1,89 @@
+getConnection();
+
+echo "Исправление проблем с базой данных ";
+
+try {
+ // 1. Проверяем есть ли категории
+ $stmt = $db->query("SELECT COUNT(*) FROM categories");
+ $cat_count = $stmt->fetchColumn();
+
+ if ($cat_count == 0) {
+ echo "Добавляем тестовые категории...
";
+ $db->exec("
+ INSERT INTO categories (name, slug, description) VALUES
+ ('Диваны', 'divany', 'Мягкая мебель для гостиной'),
+ ('Кресла', 'kresla', 'Кресла для гостиной и офиса'),
+ ('Кровати', 'krovati', 'Мебель для спальни')
+ ");
+ echo "✓ Категории добавлены
";
+ }
+
+ // 2. Проверяем товары с некорректными category_id
+ $stmt = $db->query("
+ SELECT COUNT(*) as bad_count
+ FROM products
+ WHERE category_id IS NULL OR category_id NOT IN (SELECT category_id FROM categories)
+ ");
+ $bad_count = $stmt->fetchColumn();
+
+ if ($bad_count > 0) {
+ echo "Исправляем товары с некорректными категориями ($bad_count шт)...
";
+
+ // Устанавливаем первую доступную категорию
+ $stmt = $db->query("SELECT category_id FROM categories LIMIT 1");
+ $first_cat = $stmt->fetchColumn();
+
+ if ($first_cat) {
+ $db->exec("
+ UPDATE products
+ SET category_id = $first_cat
+ WHERE category_id IS NULL OR category_id NOT IN (SELECT category_id FROM categories)
+ ");
+ echo "✓ Товары исправлены (установлена категория ID: $first_cat)
";
+ }
+ }
+
+ // 3. Показываем текущее состояние
+ echo "Текущее состояние: ";
+
+ // Категории
+ $stmt = $db->query("SELECT category_id, name FROM categories ORDER BY category_id");
+ echo "Категории:
";
+ while ($row = $stmt->fetch()) {
+ echo "ID: {$row['category_id']} - {$row['name']} ";
+ }
+ echo " ";
+
+ // Товары
+ $stmt = $db->query("
+ SELECT p.product_id, p.name, p.category_id, c.name as cat_name
+ FROM products p
+ LEFT JOIN categories c ON p.category_id = c.category_id
+ ORDER BY p.product_id
+ ");
+
+ echo "Товары:
";
+ echo "";
+ echo "ID Название Категория ID Категория ";
+
+ while ($row = $stmt->fetch()) {
+ echo "";
+ echo "{$row['product_id']} ";
+ echo "" . htmlspecialchars($row['name']) . " ";
+ echo "" . ($row['category_id'] ?: 'NULL') . " ";
+ echo "" . ($row['cat_name'] ?: 'Без категории') . " ";
+ echo " ";
+ }
+
+ echo "
";
+
+ echo "✓ База данных исправлена!
";
+
+} catch (PDOException $e) {
+ echo "Ошибка: " . $e->getMessage() . "
";
+}
+?>
\ No newline at end of file
diff --git a/get_cart.php b/get_cart.php
new file mode 100644
index 0000000..dbef740
--- /dev/null
+++ b/get_cart.php
@@ -0,0 +1,62 @@
+ false, 'message' => 'Требуется авторизация']);
+ exit();
+}
+
+$user_id = $_SESSION['user_id'] ?? 0;
+
+if ($user_id == 0) {
+ echo json_encode(['success' => false, 'message' => 'Пользователь не найден']);
+ exit();
+}
+
+$db = Database::getInstance()->getConnection();
+
+try {
+ // Получаем корзину из БД
+ $stmt = $db->prepare("
+ SELECT
+ c.cart_id,
+ c.product_id,
+ c.quantity,
+ p.name,
+ p.price,
+ p.image_url,
+ p.stock_quantity
+ FROM cart c
+ JOIN products p ON c.product_id = p.product_id
+ WHERE c.user_id = ? AND p.is_available = TRUE
+ ORDER BY c.created_at DESC
+ ");
+ $stmt->execute([$user_id]);
+ $cart_items = $stmt->fetchAll();
+
+ // Обновляем сессию
+ $_SESSION['cart'] = [];
+ foreach ($cart_items as $item) {
+ $_SESSION['cart'][$item['product_id']] = [
+ 'quantity' => $item['quantity'],
+ 'name' => $item['name'],
+ 'price' => $item['price'],
+ 'added_at' => time()
+ ];
+ }
+
+ echo json_encode([
+ 'success' => true,
+ 'cart_items' => $cart_items,
+ 'total_items' => count($cart_items)
+ ]);
+
+} catch (PDOException $e) {
+ echo json_encode([
+ 'success' => false,
+ 'message' => 'Ошибка базы данных: ' . $e->getMessage()
+ ]);
+}
+?>
\ No newline at end of file
diff --git a/get_cart_count.php b/get_cart_count.php
new file mode 100644
index 0000000..39cdc01
--- /dev/null
+++ b/get_cart_count.php
@@ -0,0 +1,22 @@
+ false, 'cart_count' => 0]);
+ exit();
+}
+
+$user_id = $_SESSION['user_id'] ?? 0;
+$db = Database::getInstance()->getConnection();
+
+try {
+ $stmt = $db->prepare("SELECT SUM(quantity) as total FROM cart WHERE user_id = ?");
+ $stmt->execute([$user_id]);
+ $cart_count = $stmt->fetchColumn() ?: 0;
+
+ echo json_encode(['success' => true, 'cart_count' => $cart_count]);
+} catch (PDOException $e) {
+ echo json_encode(['success' => false, 'cart_count' => 0]);
+}
\ No newline at end of file
diff --git a/get_product.php b/get_product.php
new file mode 100644
index 0000000..e801cd7
--- /dev/null
+++ b/get_product.php
@@ -0,0 +1,33 @@
+ false, 'message' => 'Доступ запрещен']);
+ exit();
+}
+
+if (!isset($_GET['id'])) {
+ echo json_encode(['success' => false, 'message' => 'ID не указан']);
+ exit();
+}
+
+try {
+ $db = Database::getInstance()->getConnection();
+ $product_id = $_GET['id'];
+
+ $stmt = $db->prepare("SELECT * FROM products WHERE product_id = ?");
+ $stmt->execute([$product_id]);
+ $product = $stmt->fetch();
+
+ if ($product) {
+ echo json_encode($product);
+ } else {
+ echo json_encode(['success' => false, 'message' => 'Товар не найден']);
+ }
+
+} catch (PDOException $e) {
+ echo json_encode(['success' => false, 'message' => 'Ошибка базы данных: ' . $e->getMessage()]);
+}
+?>
\ No newline at end of file
diff --git a/header_common.php b/header_common.php
new file mode 100644
index 0000000..9b197d9
--- /dev/null
+++ b/header_common.php
@@ -0,0 +1,142 @@
+
+
+
\ No newline at end of file
diff --git a/img/1.jpg b/img/1.jpg
new file mode 100644
index 0000000..1df7668
Binary files /dev/null and b/img/1.jpg differ
diff --git a/img/1_1.jpg b/img/1_1.jpg
new file mode 100644
index 0000000..3f9e1a1
Binary files /dev/null and b/img/1_1.jpg differ
diff --git a/img/2.jpg b/img/2.jpg
new file mode 100644
index 0000000..417df4a
Binary files /dev/null and b/img/2.jpg differ
diff --git a/img/2_2.jpg b/img/2_2.jpg
new file mode 100644
index 0000000..1f9b783
Binary files /dev/null and b/img/2_2.jpg differ
diff --git a/img/3.jpg b/img/3.jpg
new file mode 100644
index 0000000..134dd4e
Binary files /dev/null and b/img/3.jpg differ
diff --git a/img/3_3.jpg b/img/3_3.jpg
new file mode 100644
index 0000000..7e56605
Binary files /dev/null and b/img/3_3.jpg differ
diff --git a/img/4.jpg b/img/4.jpg
new file mode 100644
index 0000000..c462942
Binary files /dev/null and b/img/4.jpg differ
diff --git a/img/5.jpg b/img/5.jpg
new file mode 100644
index 0000000..456cec1
Binary files /dev/null and b/img/5.jpg differ
diff --git a/img/5_5.jpg b/img/5_5.jpg
new file mode 100644
index 0000000..9ced54f
Binary files /dev/null and b/img/5_5.jpg differ
diff --git a/img/6.jpg b/img/6.jpg
new file mode 100644
index 0000000..f94188c
Binary files /dev/null and b/img/6.jpg differ
diff --git a/img/6_6.jpg b/img/6_6.jpg
new file mode 100644
index 0000000..0aa5c4b
Binary files /dev/null and b/img/6_6.jpg differ
diff --git a/img/7.jpg b/img/7.jpg
new file mode 100644
index 0000000..8ba2fe5
Binary files /dev/null and b/img/7.jpg differ
diff --git a/img/7_7.jpg b/img/7_7.jpg
new file mode 100644
index 0000000..5ee1524
Binary files /dev/null and b/img/7_7.jpg differ
diff --git a/img/8.jpg b/img/8.jpg
new file mode 100644
index 0000000..e869ff9
Binary files /dev/null and b/img/8.jpg differ
diff --git a/img/9.jpg b/img/9.jpg
new file mode 100644
index 0000000..967e002
Binary files /dev/null and b/img/9.jpg differ
diff --git a/img/9_9.jpg b/img/9_9.jpg
new file mode 100644
index 0000000..a77f2ae
Binary files /dev/null and b/img/9_9.jpg differ
diff --git a/img/chair.PNG b/img/chair.PNG
new file mode 100644
index 0000000..12af9db
Binary files /dev/null and b/img/chair.PNG differ
diff --git a/img/диван.jpg b/img/диван.jpg
new file mode 100644
index 0000000..578d1a3
Binary files /dev/null and b/img/диван.jpg differ
diff --git a/img/диван_1.jpg b/img/диван_1.jpg
new file mode 100644
index 0000000..3618723
Binary files /dev/null and b/img/диван_1.jpg differ
diff --git a/img/кресло.jpg b/img/кресло.jpg
new file mode 100644
index 0000000..6d1c12b
Binary files /dev/null and b/img/кресло.jpg differ
diff --git a/img/кресло_1.jpg b/img/кресло_1.jpg
new file mode 100644
index 0000000..e59b3b1
Binary files /dev/null and b/img/кресло_1.jpg differ
diff --git a/img/слайдер_1.jpg b/img/слайдер_1.jpg
new file mode 100644
index 0000000..255440e
Binary files /dev/null and b/img/слайдер_1.jpg differ
diff --git a/img/слайдер_2.jpg b/img/слайдер_2.jpg
new file mode 100644
index 0000000..9cb6403
Binary files /dev/null and b/img/слайдер_2.jpg differ
diff --git a/img/слайдер_3.jpg b/img/слайдер_3.jpg
new file mode 100644
index 0000000..dc65d0b
Binary files /dev/null and b/img/слайдер_3.jpg differ
diff --git a/img/слайдер_4.jpg b/img/слайдер_4.jpg
new file mode 100644
index 0000000..f6d8d38
Binary files /dev/null and b/img/слайдер_4.jpg differ
diff --git a/img/слайдер_5.jpg b/img/слайдер_5.jpg
new file mode 100644
index 0000000..a9cfbb6
Binary files /dev/null and b/img/слайдер_5.jpg differ
diff --git a/img/слайдер_6.jpg b/img/слайдер_6.jpg
new file mode 100644
index 0000000..0dc28a0
Binary files /dev/null and b/img/слайдер_6.jpg differ
diff --git a/img/спальня.jpg b/img/спальня.jpg
new file mode 100644
index 0000000..f403324
Binary files /dev/null and b/img/спальня.jpg differ
diff --git a/img/стили_оформления.css b/img/стили_оформления.css
new file mode 100644
index 0000000..581822b
--- /dev/null
+++ b/img/стили_оформления.css
@@ -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;
+}
diff --git a/img2/1 — копия.jpg b/img2/1 — копия.jpg
new file mode 100644
index 0000000..68992d6
Binary files /dev/null and b/img2/1 — копия.jpg differ
diff --git a/img2/1.jpg b/img2/1.jpg
new file mode 100644
index 0000000..1df7668
Binary files /dev/null and b/img2/1.jpg differ
diff --git a/img2/100.jpg b/img2/100.jpg
new file mode 100644
index 0000000..646480d
Binary files /dev/null and b/img2/100.jpg differ
diff --git a/img2/11.jpg b/img2/11.jpg
new file mode 100644
index 0000000..3a8270e
Binary files /dev/null and b/img2/11.jpg differ
diff --git a/img2/111.jpg b/img2/111.jpg
new file mode 100644
index 0000000..33dc453
Binary files /dev/null and b/img2/111.jpg differ
diff --git a/img2/11_1.png b/img2/11_1.png
new file mode 100644
index 0000000..4735774
Binary files /dev/null and b/img2/11_1.png differ
diff --git a/img2/1_2.jpg b/img2/1_2.jpg
new file mode 100644
index 0000000..2d7a54a
Binary files /dev/null and b/img2/1_2.jpg differ
diff --git a/img2/1_2.png b/img2/1_2.png
new file mode 100644
index 0000000..9a8a6c0
Binary files /dev/null and b/img2/1_2.png differ
diff --git a/img2/22.jpg b/img2/22.jpg
new file mode 100644
index 0000000..f59b320
Binary files /dev/null and b/img2/22.jpg differ
diff --git a/img2/25.jpg b/img2/25.jpg
new file mode 100644
index 0000000..03eddcf
Binary files /dev/null and b/img2/25.jpg differ
diff --git a/img2/2_2.jpg b/img2/2_2.jpg
new file mode 100644
index 0000000..76ba9ba
Binary files /dev/null and b/img2/2_2.jpg differ
diff --git a/img2/2_2.png b/img2/2_2.png
new file mode 100644
index 0000000..8fa6e8c
Binary files /dev/null and b/img2/2_2.png differ
diff --git a/img2/3.jpg b/img2/3.jpg
new file mode 100644
index 0000000..973449d
Binary files /dev/null and b/img2/3.jpg differ
diff --git a/img2/3_3.png b/img2/3_3.png
new file mode 100644
index 0000000..ba16e91
Binary files /dev/null and b/img2/3_3.png differ
diff --git a/img2/4.jpg b/img2/4.jpg
new file mode 100644
index 0000000..da2945c
Binary files /dev/null and b/img2/4.jpg differ
diff --git a/img2/44.jpg b/img2/44.jpg
new file mode 100644
index 0000000..e38c994
Binary files /dev/null and b/img2/44.jpg differ
diff --git a/img2/444 b/img2/444
new file mode 100644
index 0000000..dc6e3fa
Binary files /dev/null and b/img2/444 differ
diff --git a/img2/444 (1).png b/img2/444 (1).png
new file mode 100644
index 0000000..ddf7e92
Binary files /dev/null and b/img2/444 (1).png differ
diff --git a/img2/444.jpg b/img2/444.jpg
new file mode 100644
index 0000000..dc6e3fa
Binary files /dev/null and b/img2/444.jpg differ
diff --git a/img2/444.png b/img2/444.png
new file mode 100644
index 0000000..cd53804
Binary files /dev/null and b/img2/444.png differ
diff --git a/img2/4_1.jpg b/img2/4_1.jpg
new file mode 100644
index 0000000..69f2236
Binary files /dev/null and b/img2/4_1.jpg differ
diff --git a/img2/5.jpg b/img2/5.jpg
new file mode 100644
index 0000000..456cec1
Binary files /dev/null and b/img2/5.jpg differ
diff --git a/img2/5_5.png b/img2/5_5.png
new file mode 100644
index 0000000..369b63b
Binary files /dev/null and b/img2/5_5.png differ
diff --git a/img2/6.jpg b/img2/6.jpg
new file mode 100644
index 0000000..550e17d
Binary files /dev/null and b/img2/6.jpg differ
diff --git a/img2/6_6.png b/img2/6_6.png
new file mode 100644
index 0000000..af949f9
Binary files /dev/null and b/img2/6_6.png differ
diff --git a/img2/7.jpg b/img2/7.jpg
new file mode 100644
index 0000000..17150c7
Binary files /dev/null and b/img2/7.jpg differ
diff --git a/img2/77.jpg b/img2/77.jpg
new file mode 100644
index 0000000..d9bf304
Binary files /dev/null and b/img2/77.jpg differ
diff --git a/img2/777 (1).png b/img2/777 (1).png
new file mode 100644
index 0000000..6b01d3f
Binary files /dev/null and b/img2/777 (1).png differ
diff --git a/img2/777.jpg b/img2/777.jpg
new file mode 100644
index 0000000..908d164
Binary files /dev/null and b/img2/777.jpg differ
diff --git a/img2/777.png b/img2/777.png
new file mode 100644
index 0000000..41d57c8
Binary files /dev/null and b/img2/777.png differ
diff --git a/img2/7_7.png b/img2/7_7.png
new file mode 100644
index 0000000..d50782e
Binary files /dev/null and b/img2/7_7.png differ
diff --git a/img2/8.jpg b/img2/8.jpg
new file mode 100644
index 0000000..8b41c63
Binary files /dev/null and b/img2/8.jpg differ
diff --git a/img2/88.jpg b/img2/88.jpg
new file mode 100644
index 0000000..713eeec
Binary files /dev/null and b/img2/88.jpg differ
diff --git a/img2/888 (1).png b/img2/888 (1).png
new file mode 100644
index 0000000..127e80d
Binary files /dev/null and b/img2/888 (1).png differ
diff --git a/img2/888.jpg b/img2/888.jpg
new file mode 100644
index 0000000..e8f33f7
Binary files /dev/null and b/img2/888.jpg differ
diff --git a/img2/888.png b/img2/888.png
new file mode 100644
index 0000000..35e3403
Binary files /dev/null and b/img2/888.png differ
diff --git a/img2/8_8.png b/img2/8_8.png
new file mode 100644
index 0000000..9f86b2c
Binary files /dev/null and b/img2/8_8.png differ
diff --git a/img2/9.jpg b/img2/9.jpg
new file mode 100644
index 0000000..0b772e3
Binary files /dev/null and b/img2/9.jpg differ
diff --git a/img2/99.jpg b/img2/99.jpg
new file mode 100644
index 0000000..ed309ba
Binary files /dev/null and b/img2/99.jpg differ
diff --git a/img2/99.png b/img2/99.png
new file mode 100644
index 0000000..8fd29b2
Binary files /dev/null and b/img2/99.png differ
diff --git a/img2/99_1.jpg b/img2/99_1.jpg
new file mode 100644
index 0000000..0247358
Binary files /dev/null and b/img2/99_1.jpg differ
diff --git a/img2/99_2.jpg b/img2/99_2.jpg
new file mode 100644
index 0000000..468fd51
Binary files /dev/null and b/img2/99_2.jpg differ
diff --git a/img2/99_3.png b/img2/99_3.png
new file mode 100644
index 0000000..ae2cd8e
Binary files /dev/null and b/img2/99_3.png differ
diff --git a/img2/9_9.jpg b/img2/9_9.jpg
new file mode 100644
index 0000000..a77f2ae
Binary files /dev/null and b/img2/9_9.jpg differ
diff --git a/img2/9_9.png b/img2/9_9.png
new file mode 100644
index 0000000..733a453
Binary files /dev/null and b/img2/9_9.png differ
diff --git a/img2/black.png b/img2/black.png
new file mode 100644
index 0000000..a10af51
Binary files /dev/null and b/img2/black.png differ
diff --git a/img2/black1.png b/img2/black1.png
new file mode 100644
index 0000000..f529bd3
Binary files /dev/null and b/img2/black1.png differ
diff --git a/img2/black2.png b/img2/black2.png
new file mode 100644
index 0000000..75758a2
Binary files /dev/null and b/img2/black2.png differ
diff --git a/img2/brown.png b/img2/brown.png
new file mode 100644
index 0000000..9ef4db4
Binary files /dev/null and b/img2/brown.png differ
diff --git a/img2/brown1.png b/img2/brown1.png
new file mode 100644
index 0000000..0b178f1
Binary files /dev/null and b/img2/brown1.png differ
diff --git a/img2/brown2.png b/img2/brown2.png
new file mode 100644
index 0000000..346b22f
Binary files /dev/null and b/img2/brown2.png differ
diff --git a/img2/chair.PNG b/img2/chair.PNG
new file mode 100644
index 0000000..12af9db
Binary files /dev/null and b/img2/chair.PNG differ
diff --git a/img2/gray.png b/img2/gray.png
new file mode 100644
index 0000000..55ab5bb
Binary files /dev/null and b/img2/gray.png differ
diff --git a/img2/gray1.png b/img2/gray1.png
new file mode 100644
index 0000000..3137e25
Binary files /dev/null and b/img2/gray1.png differ
diff --git a/img2/gray2.png b/img2/gray2.png
new file mode 100644
index 0000000..95b8781
Binary files /dev/null and b/img2/gray2.png differ
diff --git a/img2/диван.jpg b/img2/диван.jpg
new file mode 100644
index 0000000..578d1a3
Binary files /dev/null and b/img2/диван.jpg differ
diff --git a/img2/диван_1.jpg b/img2/диван_1.jpg
new file mode 100644
index 0000000..3618723
Binary files /dev/null and b/img2/диван_1.jpg differ
diff --git a/img2/кресло.jpg b/img2/кресло.jpg
new file mode 100644
index 0000000..6d1c12b
Binary files /dev/null and b/img2/кресло.jpg differ
diff --git a/img2/кресло_1.jpg b/img2/кресло_1.jpg
new file mode 100644
index 0000000..e59b3b1
Binary files /dev/null and b/img2/кресло_1.jpg differ
diff --git a/img2/слайдер_1.jpg b/img2/слайдер_1.jpg
new file mode 100644
index 0000000..255440e
Binary files /dev/null and b/img2/слайдер_1.jpg differ
diff --git a/img2/слайдер_2.jpg b/img2/слайдер_2.jpg
new file mode 100644
index 0000000..9cb6403
Binary files /dev/null and b/img2/слайдер_2.jpg differ
diff --git a/img2/слайдер_3.jpg b/img2/слайдер_3.jpg
new file mode 100644
index 0000000..dc65d0b
Binary files /dev/null and b/img2/слайдер_3.jpg differ
diff --git a/img2/слайдер_4.jpg b/img2/слайдер_4.jpg
new file mode 100644
index 0000000..f6d8d38
Binary files /dev/null and b/img2/слайдер_4.jpg differ
diff --git a/img2/слайдер_5.jpg b/img2/слайдер_5.jpg
new file mode 100644
index 0000000..a9cfbb6
Binary files /dev/null and b/img2/слайдер_5.jpg differ
diff --git a/img2/слайдер_6.jpg b/img2/слайдер_6.jpg
new file mode 100644
index 0000000..0dc28a0
Binary files /dev/null and b/img2/слайдер_6.jpg differ
diff --git a/img2/спальня.jpg b/img2/спальня.jpg
new file mode 100644
index 0000000..f403324
Binary files /dev/null and b/img2/спальня.jpg differ
diff --git a/login.php b/login.php
new file mode 100644
index 0000000..9023dc1
--- /dev/null
+++ b/login.php
@@ -0,0 +1,48 @@
+getConnection();
+
+ try {
+ // Проверяем пользователя
+ $stmt = $db->prepare("
+ SELECT user_id, email, password_hash, full_name
+ FROM users
+ WHERE email = ? AND is_active = TRUE
+ ");
+ $stmt->execute([$email]);
+ $user = $stmt->fetch();
+
+ if ($user && password_verify($password, $user['password_hash'])) {
+ // Сохраняем в сессию
+ $_SESSION['user_id'] = $user['user_id'];
+ $_SESSION['user_email'] = $user['email'];
+ $_SESSION['full_name'] = $user['full_name'];
+ $_SESSION['isLoggedIn'] = true;
+ $_SESSION['login_time'] = time();
+
+ // Обновляем время последнего входа
+ $update_stmt = $db->prepare("
+ UPDATE users
+ SET updated_at = CURRENT_TIMESTAMP
+ WHERE user_id = ?
+ ");
+ $update_stmt->execute([$user['user_id']]);
+
+ header('Location: catalog.php');
+ exit();
+ } else {
+ header('Location: вход.php?error=invalid_credentials');
+ exit();
+ }
+ } catch (PDOException $e) {
+ header('Location: вход.php?error=db_error');
+ exit();
+ }
+}
+?>
\ No newline at end of file
diff --git a/login_handler.php b/login_handler.php
new file mode 100644
index 0000000..3f63de6
--- /dev/null
+++ b/login_handler.php
@@ -0,0 +1,66 @@
+ false, 'message' => 'Заполните все поля']);
+ exit();
+ }
+
+ $db = Database::getInstance()->getConnection();
+
+ try {
+ // Проверяем пользователя в базе данных
+ $stmt = $db->prepare("
+ SELECT user_id, email, password_hash, full_name, phone, city, is_admin, is_active
+ FROM users
+ WHERE email = ?
+ ");
+ $stmt->execute([$email]);
+ $user = $stmt->fetch();
+
+ if (!$user) {
+ echo json_encode(['success' => false, 'message' => 'Пользователь не найден']);
+ exit();
+ }
+
+ if (!$user['is_active']) {
+ echo json_encode(['success' => false, 'message' => 'Аккаунт заблокирован']);
+ exit();
+ }
+
+ // Проверяем пароль
+ if (!password_verify($password, $user['password_hash'])) {
+ echo json_encode(['success' => false, 'message' => 'Неверный пароль']);
+ exit();
+ }
+
+ // Сохраняем в сессию
+ $_SESSION['user_id'] = $user['user_id'];
+ $_SESSION['user_email'] = $user['email'];
+ $_SESSION['full_name'] = $user['full_name'];
+ $_SESSION['user_phone'] = $user['phone'] ?? '';
+ $_SESSION['user_city'] = $user['city'] ?? '';
+ $_SESSION['isLoggedIn'] = true;
+ $_SESSION['isAdmin'] = (bool)$user['is_admin'];
+ $_SESSION['login_time'] = time();
+
+ // Обновляем время последнего входа
+ $updateStmt = $db->prepare("UPDATE users SET last_login = CURRENT_TIMESTAMP WHERE user_id = ?");
+ $updateStmt->execute([$user['user_id']]);
+
+ echo json_encode(['success' => true, 'redirect' => 'catalog.php']);
+
+ } catch (PDOException $e) {
+ echo json_encode(['success' => false, 'message' => 'Ошибка базы данных']);
+ }
+
+} else {
+ echo json_encode(['success' => false, 'message' => 'Неверный запрос']);
+}
+?>
\ No newline at end of file
diff --git a/logout.php b/logout.php
new file mode 100644
index 0000000..3b5d01e
--- /dev/null
+++ b/logout.php
@@ -0,0 +1,22 @@
+
\ No newline at end of file
diff --git a/mixins.less b/mixins.less
new file mode 100644
index 0000000..934de15
--- /dev/null
+++ b/mixins.less
@@ -0,0 +1,85 @@
+// ===================================
+// === ПЕРЕМЕННЫЕ И МИКСИНЫ 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;
+ }
+}
\ No newline at end of file
diff --git a/print_order.php b/print_order.php
new file mode 100644
index 0000000..9e92cf9
--- /dev/null
+++ b/print_order.php
@@ -0,0 +1,208 @@
+getConnection();
+
+try {
+ $orderStmt = $db->prepare("
+ SELECT o.*, u.email, u.full_name
+ FROM orders o
+ LEFT JOIN users u ON o.user_id = u.user_id
+ WHERE o.order_id = ?
+ ");
+ $orderStmt->execute([$orderId]);
+ $order = $orderStmt->fetch();
+
+ if (!$order) {
+ die('Заказ не найден');
+ }
+
+ $itemsStmt = $db->prepare("
+ SELECT * FROM order_items
+ WHERE order_id = ?
+ ");
+ $itemsStmt->execute([$orderId]);
+ $order_items = $itemsStmt->fetchAll();
+
+} catch (PDOException $e) {
+ die('Ошибка базы данных: ' . $e->getMessage());
+}
+?>
+
+
+
+
+
+ Печать заказа #= $order['order_number'] ?>
+
+
+
+
+ Печать
+
+
+
+
+
+
Заказ #= htmlspecialchars($order['order_number']) ?>
+
Дата: = date('d.m.Y H:i', strtotime($order['created_at'])) ?>
+
Статус: = $order['status'] ?>
+
+
+
+
+
Информация о клиенте
+
ФИО: = htmlspecialchars($order['customer_name']) ?>
+
Email: = htmlspecialchars($order['customer_email']) ?>
+
Телефон: = htmlspecialchars($order['customer_phone']) ?>
+
+
+
+
Информация о доставке
+
Адрес: = nl2br(htmlspecialchars($order['delivery_address'])) ?>
+
Способ доставки: = $order['delivery_method'] == 'courier' ? 'Курьер' : 'Самовывоз' ?>
+
Способ оплаты: = $order['payment_method'] == 'card' ? 'Карта' : 'Наличные' ?>
+
+
+
+ Состав заказа
+
+
+
+ №
+ Товар
+ Кол-во
+ Цена
+ Сумма
+
+
+
+
+
+
+ = $counter++ ?>
+ = htmlspecialchars($item['product_name']) ?>
+ = $item['quantity'] ?>
+ = number_format($item['unit_price'], 0, '', ' ') ?> ₽
+ = number_format($item['total_price'], 0, '', ' ') ?> ₽
+
+
+
+
+
+ Сумма товаров:
+ = number_format($order['total_amount'], 0, '', ' ') ?> ₽
+
+ 0): ?>
+
+ Скидка:
+ -= number_format($order['discount_amount'], 0, '', ' ') ?> ₽
+
+
+ 0): ?>
+
+ Доставка:
+ = number_format($order['delivery_cost'], 0, '', ' ') ?> ₽
+
+
+
+ Итого к оплате:
+ = number_format($order['final_amount'], 0, '', ' ') ?> ₽
+
+
+
+
+
+
+
Примечания
+
= nl2br(htmlspecialchars($order['notes'])) ?>
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/process_order.php b/process_order.php
new file mode 100644
index 0000000..e7f5805
--- /dev/null
+++ b/process_order.php
@@ -0,0 +1,134 @@
+getConnection();
+
+ try {
+ $db->beginTransaction();
+
+ // Получаем данные из формы
+ $customer_name = $_POST['full_name'] ?? '';
+ $customer_email = $_POST['email'] ?? '';
+ $customer_phone = $_POST['phone'] ?? '';
+ $delivery_address = $_POST['address'] ?? '';
+ $region = $_POST['region'] ?? '';
+ $payment_method = $_POST['payment'] ?? 'card';
+ $delivery_method = $_POST['delivery'] ?? 'courier';
+ $notes = $_POST['notes'] ?? '';
+ $discount_amount = floatval($_POST['discount'] ?? 0);
+ $delivery_cost = floatval($_POST['delivery_price'] ?? 2000);
+
+ // Генерируем номер заказа
+ $order_number = 'ORD-' . date('Ymd-His') . '-' . rand(1000, 9999);
+
+ // Получаем корзину пользователя
+ $cartStmt = $db->prepare("
+ SELECT
+ c.product_id,
+ c.quantity,
+ p.name,
+ p.price,
+ p.stock_quantity
+ FROM cart c
+ JOIN products p ON c.product_id = p.product_id
+ WHERE c.user_id = ?
+ ");
+ $cartStmt->execute([$user_id]);
+ $cart_items = $cartStmt->fetchAll();
+
+ if (empty($cart_items)) {
+ throw new Exception('Корзина пуста');
+ }
+
+ // Рассчитываем итоги
+ $total_amount = 0;
+ foreach ($cart_items as $item) {
+ $total_amount += $item['price'] * $item['quantity'];
+ }
+
+ $final_amount = $total_amount - $discount_amount + $delivery_cost;
+
+ // Создаем заказ
+ $orderStmt = $db->prepare("
+ INSERT INTO orders (
+ user_id, order_number, total_amount, discount_amount,
+ delivery_cost, final_amount, status, payment_method,
+ delivery_method, delivery_address, customer_name,
+ customer_email, customer_phone, notes
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
+ RETURNING order_id
+ ");
+
+ $orderStmt->execute([
+ $user_id, $order_number, $total_amount, $discount_amount,
+ $delivery_cost, $final_amount, 'pending', $payment_method,
+ $delivery_method, $delivery_address, $customer_name,
+ $customer_email, $customer_phone, $notes
+ ]);
+
+ $order_id = $orderStmt->fetchColumn();
+
+ // Добавляем товары в заказ и обновляем остатки
+ foreach ($cart_items as $item) {
+ // Добавляем в order_items
+ $itemStmt = $db->prepare("
+ INSERT INTO order_items (
+ order_id, product_id, product_name,
+ quantity, unit_price, total_price
+ ) VALUES (?, ?, ?, ?, ?, ?)
+ ");
+
+ $item_total = $item['price'] * $item['quantity'];
+ $itemStmt->execute([
+ $order_id, $item['product_id'], $item['name'],
+ $item['quantity'], $item['price'], $item_total
+ ]);
+
+ // Обновляем остатки на складе
+ $updateStmt = $db->prepare("
+ UPDATE products
+ SET stock_quantity = stock_quantity - ?,
+ updated_at = CURRENT_TIMESTAMP
+ WHERE product_id = ?
+ ");
+ $updateStmt->execute([$item['quantity'], $item['product_id']]);
+ }
+
+ // Очищаем корзину
+ $clearCartStmt = $db->prepare("DELETE FROM cart WHERE user_id = ?");
+ $clearCartStmt->execute([$user_id]);
+
+ // Очищаем сессию
+ unset($_SESSION['cart']);
+
+ $db->commit();
+
+ // Перенаправляем на страницу успеха
+ header('Location: order_success.php?id=' . $order_id);
+ exit();
+
+ } catch (Exception $e) {
+ $db->rollBack();
+ header('Location: оформление_заказа.php?error=' . urlencode($e->getMessage()));
+ exit();
+ }
+} else {
+ header('Location: оформление_заказа.php');
+ exit();
+}
+?>
\ No newline at end of file
diff --git a/product_modern.php b/product_modern.php
new file mode 100644
index 0000000..ebc63d9
--- /dev/null
+++ b/product_modern.php
@@ -0,0 +1,350 @@
+getConnection();
+
+// Получаем информацию о товаре "Диван MODERN" (ID 2 из базы данных)
+try {
+ $productStmt = $db->prepare("
+ SELECT p.*, c.name as category_name
+ FROM products p
+ LEFT JOIN categories c ON p.category_id = c.category_id
+ WHERE p.product_id = 2 AND p.is_available = TRUE
+ ");
+ $productStmt->execute();
+ $product = $productStmt->fetch();
+
+ if (!$product) {
+ header('Location: catalog.php?error=product_not_found');
+ exit();
+ }
+
+ // Получаем похожие товары
+ $similarStmt = $db->prepare("
+ SELECT * FROM products
+ WHERE category_id = ? AND product_id != ? AND is_available = TRUE
+ ORDER BY RANDOM()
+ LIMIT 3
+ ");
+ $similarStmt->execute([$product['category_id'], $product['product_id']]);
+ $similarProducts = $similarStmt->fetchAll();
+
+} catch (PDOException $e) {
+ die("Ошибка базы данных: " . $e->getMessage());
+}
+?>
+
+
+
+
+
+ AETERNA - = htmlspecialchars($product['name']) ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Диван MODERN
+
+
+
+ ★
+ ★
+ ★
+ ★
+ ★
+
+
4.5
+
(24 отзыва)
+
+
+
+
+ = number_format($product['price'], 0, '', ' ') ?> ₽
+
+ $product['price']): ?>
+
+ = number_format($product['old_price'], 0, '', ' ') ?> ₽
+
+
+ -= round(($product['old_price'] - $product['price']) / $product['old_price'] * 100) ?>%
+
+
+
+
+
+
+ Артикул:
+ = $product['sku'] ?? 'DIV-MOD-001' ?>
+
+
+ Категория:
+ Диваны
+
+
+ Цвет:
+ Серый, Бежевый, Темно-синий
+
+
+ Материал:
+ Экокожа, Дерево
+
+
+ Размеры (Ш×Г×В):
+ 220 × 95 × 85 см
+
+
+
+
+ 0): ?>
+ В наличии: = $product['stock_quantity'] ?> шт.
+
+ Нет в наличии
+
+
+
+
+ Угловой диван MODERN – сочетание современного дизайна и бескомпромиссного комфорта.
+ Каркас из массива дерева обеспечивает долговечность, а наполнитель из высокоэластичного
+ пенополиуретана гарантирует оптимальную поддержку спины. Диван оснащен механизмом
+ трансформации «еврокнижка», что позволяет использовать его как спальное место.
+
+ Особенности:
+ • Прочная конструкция из массива дерева
+ • Механизм трансформации «еврокнижка»
+ • Наполнитель: пенополиуретан + периотек
+ • Съемные чехлы для легкой чистки
+ • Встроенные подлокотники с полками
+
+
+ 0): ?>
+
+
+ -
+
+ +
+
+
+
+
+ В корзину
+
+
+ Купить сейчас
+
+
+
+
+
+
+ Уведомить о поступлении
+
+
+
+
+
+
+
+
+ Похожие товары
+
+
+
+
+
+
+
+
= htmlspecialchars($similar['name']) ?>
+
+ = htmlspecialchars(mb_substr($similar['description'] ?? '', 0, 80)) ?>...
+
+
+ = number_format($similar['price'], 0, '', ' ') ?> ₽
+
+
+ Подробнее
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/product_page.php b/product_page.php
new file mode 100644
index 0000000..44afd8d
--- /dev/null
+++ b/product_page.php
@@ -0,0 +1,492 @@
+getConnection();
+
+try {
+ // Получаем информацию о товаре
+ $productStmt = $db->prepare("
+ SELECT
+ p.*,
+ c.name as category_name,
+ c.slug as category_slug
+ FROM products p
+ LEFT JOIN categories c ON p.category_id = c.category_id
+ WHERE p.product_id = ? AND p.is_available = TRUE
+ ");
+ $productStmt->execute([$product_id]);
+ $product = $productStmt->fetch();
+
+ if (!$product) {
+ header('Location: catalog.php?error=product_not_found');
+ exit();
+ }
+
+ // Получаем похожие товары
+ $similarStmt = $db->prepare("
+ SELECT * FROM products
+ WHERE category_id = ?
+ AND product_id != ?
+ AND is_available = TRUE
+ ORDER BY RANDOM()
+ LIMIT 3
+ ");
+ $similarStmt->execute([$product['category_id'], $product_id]);
+ $similarProducts = $similarStmt->fetchAll();
+
+ // Получаем отзывы (если есть отдельная таблица reviews)
+ $reviewsStmt = $db->prepare("
+ SELECT rating, comment, created_at
+ FROM reviews
+ WHERE product_id = ?
+ ORDER BY created_at DESC
+ LIMIT 5
+ ");
+ $reviewsStmt->execute([$product_id]);
+ $reviews = $reviewsStmt->fetchAll();
+
+} catch (PDOException $e) {
+ die("Ошибка базы данных: " . $e->getMessage());
+}
+
+// HTML код страницы товара
+?>
+
+
+
+
+
+ AETERNA - = htmlspecialchars($product['name']) ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
= htmlspecialchars($product['name']) ?>
+
+
+
+ = 0.5;
+
+ for ($i = 1; $i <= 5; $i++) {
+ if ($i <= $fullStars) {
+ echo '★ ';
+ } elseif ($i == $fullStars + 1 && $hasHalfStar) {
+ echo '★ ';
+ } else {
+ echo '☆ ';
+ }
+ }
+ ?>
+
+
= number_format($rating, 1) ?>
+
(= $product['review_count'] ?> отзывов)
+
+
+
+
+ = number_format($product['price'], 0, '', ' ') ?> ₽
+
+ $product['price']): ?>
+
+ = number_format($product['old_price'], 0, '', ' ') ?> ₽
+
+
+ -= round(($product['old_price'] - $product['price']) / $product['old_price'] * 100) ?>%
+
+
+
+
+
+ 10) {
+ echo ' В наличии';
+ } elseif ($product['stock_quantity'] > 0) {
+ echo ' Осталось мало: ' . $product['stock_quantity'] . ' шт.';
+ } else {
+ echo ' Нет в наличии';
+ }
+ ?>
+
+
+
+
+ Артикул:
+ = $product['sku'] ?? 'N/A' ?>
+
+
+ Категория:
+ = htmlspecialchars($product['category_name'] ?? 'Без категории') ?>
+
+
+ На складе:
+ = $product['stock_quantity'] ?> шт.
+
+
+
+
+ = nl2br(htmlspecialchars($product['description'] ?? 'Описание отсутствует')) ?>
+
+
+ 0): ?>
+
+
+ -
+
+ +
+
+
+
+
+ В корзину
+
+
+ Купить сейчас
+
+
+
+
+
+
+ Уведомить о поступлении
+
+
+
+
+
+
+
+
+
+
+
+
+ Похожие товары
+
+
+
+
+
+
+
+
= htmlspecialchars($similar['name']) ?>
+
+ = number_format($similar['price'], 0, '', ' ') ?> ₽
+
+
+ Подробнее
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/register.php b/register.php
new file mode 100644
index 0000000..a5d967b
--- /dev/null
+++ b/register.php
@@ -0,0 +1,87 @@
+getConnection();
+
+ try {
+ // Проверяем существование email
+ $check_stmt = $db->prepare("SELECT user_id FROM users WHERE email = ?");
+ $check_stmt->execute([$email]);
+
+ if ($check_stmt->fetch()) {
+ $errors[] = 'Пользователь с таким email уже существует';
+ } else {
+ // Хэшируем пароль
+ $password_hash = password_hash($password, PASSWORD_DEFAULT);
+
+ // Создаем пользователя
+ $stmt = $db->prepare("
+ INSERT INTO users (email, password_hash, full_name, phone, is_active)
+ VALUES (?, ?, ?, ?, TRUE)
+ RETURNING user_id
+ ");
+
+ $stmt->execute([$email, $password_hash, $full_name, $phone]);
+ $user_id = $stmt->fetchColumn();
+
+ if ($user_id) {
+ // Автоматически входим
+ $_SESSION['user_id'] = $user_id;
+ $_SESSION['user_email'] = $email;
+ $_SESSION['full_name'] = $full_name;
+ $_SESSION['isLoggedIn'] = true;
+ $_SESSION['isAdmin'] = false; // Обычный пользователь
+
+ // Успешная регистрация
+ $_SESSION['registration_success'] = true;
+ header('Location: catalog.php');
+ exit();
+ } else {
+ $errors[] = 'Ошибка при создании пользователя';
+ }
+ }
+ } catch (PDOException $e) {
+ error_log("Ошибка регистрации: " . $e->getMessage());
+ $errors[] = 'Ошибка базы данных. Попробуйте позже.';
+ }
+ }
+
+ // Если есть ошибки, сохраняем их
+ if (!empty($errors)) {
+ $_SESSION['registration_errors'] = $errors;
+ $_SESSION['old_input'] = [
+ 'email' => $email,
+ 'full_name' => $full_name,
+ 'phone' => $phone
+ ];
+ header('Location: профиль.php');
+ exit();
+ }
+} else {
+ header('Location: профиль.php');
+ exit();
+}
+?>
\ No newline at end of file
diff --git a/register_handler.php b/register_handler.php
new file mode 100644
index 0000000..76ce3b9
--- /dev/null
+++ b/register_handler.php
@@ -0,0 +1,182 @@
+ $full_name,
+ 'city' => $city,
+ 'email' => $email,
+ 'phone' => $phone
+ ];
+ header('Location: профиль.php');
+ exit();
+ }
+
+ // Подключаемся к базе данных
+ $db = Database::getInstance()->getConnection();
+
+ try {
+ // Проверяем, существует ли пользователь с таким email
+ $checkStmt = $db->prepare("SELECT user_id FROM users WHERE email = ?");
+ $checkStmt->execute([$email]);
+
+ if ($checkStmt->fetch()) {
+ $_SESSION['registration_errors'] = ['Пользователь с таким email уже существует'];
+ $_SESSION['old_data'] = [
+ 'fio' => $full_name,
+ 'city' => $city,
+ 'email' => $email,
+ 'phone' => $phone
+ ];
+ header('Location: профиль.php');
+ exit();
+ }
+
+ // Хэшируем пароль
+ $password_hash = password_hash($password, PASSWORD_DEFAULT);
+
+ // Определяем, является ли пользователь администратором
+ $is_admin = false;
+ $admin_emails = ['admin@aeterna.ru', 'administrator@aeterna.ru', 'aeterna@mail.ru'];
+ if (in_array(strtolower($email), $admin_emails)) {
+ $is_admin = true;
+ }
+
+ // РАЗНЫЕ ВАРИАНТЫ ДЛЯ ТЕСТИРОВАНИЯ - РАСКОММЕНТИРУЙТЕ НУЖНЫЙ
+
+ // Вариант 1: С явным преобразованием boolean (самый надежный)
+ $stmt = $db->prepare("
+ INSERT INTO users (email, password_hash, full_name, phone, city, is_admin)
+ VALUES (?, ?, ?, ?, ?, ?)
+ RETURNING user_id
+ ");
+
+ // Преобразуем boolean в integer для PostgreSQL
+ $stmt->execute([
+ $email,
+ $password_hash,
+ $full_name,
+ $phone,
+ $city,
+ $is_admin ? 1 : 0 // Преобразуем в integer (1 или 0)
+ ]);
+
+ // Вариант 2: С использованием CAST в SQL (альтернатива)
+ /*
+ $stmt = $db->prepare("
+ INSERT INTO users (email, password_hash, full_name, phone, city, is_admin)
+ VALUES (?, ?, ?, ?, ?, CAST(? AS boolean))
+ RETURNING user_id
+ ");
+
+ $stmt->execute([
+ $email,
+ $password_hash,
+ $full_name,
+ $phone,
+ $city,
+ $is_admin ? 'true' : 'false' // Строковые значения true/false
+ ]);
+ */
+
+ $user_id = $stmt->fetchColumn();
+
+ if ($user_id) {
+ // Автоматически авторизуем пользователя
+ $_SESSION['user_id'] = $user_id;
+ $_SESSION['user_email'] = $email;
+ $_SESSION['full_name'] = $full_name;
+ $_SESSION['user_phone'] = $phone;
+ $_SESSION['user_city'] = $city;
+ $_SESSION['isLoggedIn'] = true;
+ $_SESSION['isAdmin'] = $is_admin;
+ $_SESSION['login_time'] = time();
+
+ // Обновляем время последнего входа
+ $updateStmt = $db->prepare("UPDATE users SET last_login = CURRENT_TIMESTAMP WHERE user_id = ?");
+ $updateStmt->execute([$user_id]);
+
+ // Перенаправляем на главную или каталог
+ $_SESSION['registration_success'] = 'Регистрация прошла успешно! ' .
+ ($is_admin ? 'Вы зарегистрированы как администратор.' : 'Добро пожаловать в AETERNA!');
+
+ header('Location: catalog.php');
+ exit();
+ } else {
+ throw new Exception('Ошибка при создании пользователя');
+ }
+
+ } catch (PDOException $e) {
+ // Логируем полную ошибку для отладки
+ error_log("Registration DB Error: " . $e->getMessage());
+ error_log("SQL State: " . $e->getCode());
+ error_log("Email: " . $email);
+
+ $_SESSION['registration_errors'] = ['Ошибка базы данных: ' . $e->getMessage()];
+ $_SESSION['old_data'] = [
+ 'fio' => $full_name,
+ 'city' => $city,
+ 'email' => $email,
+ 'phone' => $phone
+ ];
+ header('Location: профиль.php');
+ exit();
+ } catch (Exception $e) {
+ error_log("Registration Error: " . $e->getMessage());
+
+ $_SESSION['registration_errors'] = [$e->getMessage()];
+ header('Location: профиль.php');
+ exit();
+ }
+
+} else {
+ // Если запрос не POST, перенаправляем на форму регистрации
+ header('Location: профиль.php');
+ exit();
+}
+?>
\ No newline at end of file
diff --git a/style_for_cite.less b/style_for_cite.less
new file mode 100644
index 0000000..3a52d85
--- /dev/null
+++ b/style_for_cite.less
@@ -0,0 +1,3178 @@
+@import "mixins.less";
+@import "стили_оформления.less";
+// =======================
+// === БАЗОВЫЕ СТИЛИ ===
+// =======================
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+
+html, body {
+ height: 100%;
+}
+
+html {
+ scroll-behavior: smooth;
+}
+
+body {
+ font-family: @font-main;
+ background-color: @color-secondary;
+ color: @color-text-dark;
+ line-height: 1.6;
+ display: flex;
+ flex-direction: column;
+ min-height: 100vh;
+}
+
+.container {
+ max-width: 1210px;
+ margin: 0 auto;
+ padding: 0 20px;
+}
+
+ul {
+ list-style: none;
+}
+
+a {
+ text-decoration: none;
+ color: inherit;
+ transition: all 0.3s ease;
+}
+
+h1, h2, h3, h4, h5, h6 {
+ font-family: @font-heading;
+ margin: 0;
+}
+
+p, li, span {
+ font-family: @font-main;
+}
+
+// =======================
+// === КОМПОНЕНТЫ ===
+// =======================
+
+.logo, .footer-logo {
+ font: bold 32px/1 @font-logo;
+ letter-spacing: 2px;
+ text-shadow: @shadow-dark;
+ flex-shrink: 0;
+}
+
+.btn {
+ padding: 12px 30px;
+ border: none;
+ cursor: pointer;
+ font-size: 14px;
+ text-transform: uppercase;
+ transition: all 0.3s ease;
+ font-family: @font-main;
+
+ &.primary-btn {
+ background-color: @color-button;
+ color: @color-text-light;
+
+ &:hover {
+ background-color: lighten(@color-button, 10%);
+ transform: translateY(-2px);
+ box-shadow: @shadow-light;
+ }
+ }
+}
+
+.number-circle {
+ .flex-center();
+ width: 28px;
+ height: 28px;
+ border-radius: 50%;
+ background-color: @color-button;
+ color: @color-text-light;
+ font-size: 16px;
+ font-weight: bold;
+ flex-shrink: 0;
+}
+
+.breadcrumbs {
+ font-size: 14px;
+ margin-bottom: 20px;
+ color: #666;
+
+ a {
+ color: #666;
+ opacity: 0.7;
+ &:hover { opacity: 1; }
+ }
+
+ .current-page {
+ font-weight: bold;
+ color: @color-text-dark;
+ }
+}
+
+// =======================
+// === ШАПКА САЙТА ===
+// =======================
+.header {
+ background-color: @color-secondary;
+ border-bottom: 1px solid rgba(0, 0, 0, 0.05);
+ z-index: 1000;
+
+ &__top, &__bottom {
+ padding: 15px 0;
+ .container {
+ .flex-between();
+ gap: 20px;
+ }
+ }
+
+ &__bottom {
+ padding: 10px 0;
+ border-top: 1px solid rgba(0, 0, 0, 0.05);
+
+ .catalog-link.active-catalog {
+ background-color: rgba(0, 0, 0, 0.08);
+ pointer-events: none;
+ }
+ }
+
+ .search-catalog {
+ .flex-center();
+ border: 2px solid @color-text-dark;
+ background-color: #fff;
+ max-width: 600px;
+ width: 100%;
+ margin: 0 auto;
+ overflow: hidden;
+
+ .catalog-dropdown {
+ position: relative;
+ background-color: #f8f8f8;
+ padding: 10px 15px 10px 25px;
+ font-size: 18px;
+ cursor: pointer;
+ border-right: 1px solid @color-text-dark;
+ .flex-center(10px);
+ width: 200px;
+ flex-shrink: 0;
+
+ &__menu {
+ .menu-base();
+ li {
+ padding: 8px 0;
+ cursor: pointer;
+ transition: color 0.3s;
+ border-bottom: 1px solid #f0f0f0;
+ &:last-child { border-bottom: none; }
+ &:hover { color: @color-accent; }
+ }
+ }
+ &:hover &__menu { display: block; }
+ }
+
+ .search-box {
+ .flex-center();
+ padding: 0 15px;
+ flex-grow: 1;
+ position: relative;
+ font-size: 15px;
+
+ input {
+ border: none;
+ padding: 10px 30px 10px 0;
+ outline: none;
+ font-size: 16px;
+ width: 100%;
+ text-align: left;
+ }
+
+ .search-icon {
+ font-size: 20px;
+ width: 24px;
+ height: 24px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ }
+ }}
+
+ &__icons--top {
+ .flex-center(15px);
+ flex-shrink: 0;
+ .icon { .icon-base(); font-size: 20px;}
+ }
+
+ .nav-list {
+ .flex-center(30px);
+ font-size: 18px;
+ a {
+ text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.2);
+ &:hover { text-shadow: @shadow-dark; }
+ &.active {
+ border-bottom: 2px solid @color-button;
+ padding-bottom: 5px;
+ text-shadow: @shadow-dark;
+ }
+ &[href="#footer"] {
+ cursor: pointer;
+ }
+ }
+ }
+
+ .catalog-link {
+ .flex-center(10px);
+ border-radius: 4px;
+ white-space: nowrap;
+ font-size: 18px;
+ padding: 10px 18px;
+ &:hover { background-color: rgba(0, 0, 0, 0.05); }
+ }
+
+ .header-phone {
+ font-weight: bold;
+ color: @color-button;
+ flex-shrink: 0;
+ }
+}
+
+// =======================
+// === ОСНОВНЫЕ СЕКЦИИ ===
+// =======================
+.hero {
+ padding: 15px 0;
+
+ &__content {
+ .flex-center(50px);
+ min-height: 60vh;
+ align-items: center;
+ }
+
+ &__image-block {
+ position: relative;
+ flex: 0 0 40%;
+ max-width: 600px;
+ height: 600px;
+ .flex-center();
+
+ .hero__circle {
+ position: absolute;
+ width: 450px;
+ height: 450px;
+ background-color: @color-primary;
+ border-radius: 50%;
+ z-index: 1;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ }
+
+ .hero__img {
+ position: relative;
+ width: 100%;
+ height: 100%;
+ object-fit: contain;
+ z-index: 2;
+ }
+ }
+
+ &__text-block {
+ flex: 0 0 60%;
+ padding-left: 50px;
+
+ h1 {
+ font-size: 42px;
+ font-weight: normal;
+ margin-bottom: 25px;
+ line-height: 1.3;
+ }
+
+ .hero__usp-text {
+ position: relative;
+ padding-left: 50px;
+ margin-bottom: 35px;
+ line-height: 1.7;
+ .flex-center();
+ justify-content: flex-start;
+ min-height: 40px;
+ font-size: 16px;
+
+ &::before {
+ content: "✓";
+ position: absolute;
+ left: 0;
+ top: 50%;
+ transform: translateY(-50%);
+ width: 32px;
+ height: 32px;
+ border: 2px solid @color-button;
+ background-color: transparent;
+ color: @color-button;
+ border-radius: 50%;
+ .flex-center();
+ font-size: 16px;
+ font-weight: bold;
+ }
+ }
+
+ .btn.primary-btn {
+ margin: 25px 0 0 50px;
+ padding: 14px 35px;
+ font-size: 15px;
+ }
+ }
+}
+
+.advantages {
+ padding: 30px 0 40px;
+
+ &__header {
+ display: flex;
+ align-items: center;
+ gap: 50px;
+ margin-bottom: 40px;
+ h2 {
+ font-size: 32px;
+ font-weight: normal;
+ flex: 0 0 30%;
+ }
+ }
+
+ &__items {
+ flex: 0 0 70%;
+ display: flex;
+ gap: 30px;
+ }
+
+ .advantage-item {
+ flex: 1;
+ text-align: left;
+ position: relative;
+ padding-top: 30px;
+
+ &__number {
+ .number-circle();
+ position: absolute;
+ top: 0;
+ left: 0;
+ }
+
+ h4 {
+ font-weight: 600;
+ margin-bottom: 10px;
+ }
+ }
+}
+
+.promo-images {
+ display: flex;
+ gap: 20px;
+ margin-top: 50px;
+
+ .promo-image-col {
+ position: relative;
+ overflow: hidden;
+ border-radius: 8px;
+ flex: 1;
+ transition: all 0.3s ease;
+
+ &:hover {
+ transform: translateY(-5px);
+ box-shadow: @shadow-light;
+ .image-overlay-text { background-color: rgba(0, 0, 0, 0.6); }
+ img { transform: scale(1.05); }
+ .image-overlay-text h4,
+ .image-overlay-text .overlay-link { transform: translateY(0); }
+ }
+
+ img {
+ width: 100%;
+ height: 350px;
+ object-fit: cover;
+ display: block;
+ transition: transform 0.5s ease;
+ }
+
+ .image-overlay-text {
+ .image-overlay();
+ h4 {
+ font-size: 24px;
+ text-transform: uppercase;
+ line-height: 1.2;
+ margin-bottom: 15px;
+ transform: translateY(20px);
+ transition: transform 0.3s ease;
+ }
+ }
+
+ .overlay-link {
+ display: inline-block;
+ text-transform: uppercase;
+ font-weight: bold;
+ border-radius: 3px;
+ margin-top: 15px;
+ padding: 10px 25px;
+ background-color: @color-button;
+ color: @color-text-light;
+ font-size: 12px;
+ transform: translateY(20px);
+ transition: all 0.3s ease;
+
+ &:hover {
+ background-color: lighten(@color-button, 10%);
+ transform: translateY(-2px);
+ box-shadow: @shadow-light;
+ }
+ }
+ }
+}
+
+.about {
+ padding: 40px 0 80px;
+
+ &__content {
+ display: flex;
+ align-items: flex-start;
+ gap: 50px;
+ }
+
+ &__column {
+ display: flex;
+ flex-direction: column;
+ gap: 20px;
+ &--left { flex: 0 0 40%; margin-bottom: 30px; }
+ &--right {
+ flex: 0 0 60%;
+ .about__caption {
+ padding-right: 50px;
+ }
+ }
+ }
+
+ &__text-block {
+ margin-bottom: 30px;
+ h2 { margin-bottom: 15px; }
+ }
+
+ &__img {
+ width: 93%;
+ object-fit: cover;
+ display: block;
+ &--small { height: 300px; }
+ &--large { height: 450px; }
+ }
+
+ .text-justified {
+ text-align: justify;
+ color: #555;
+ }
+}
+
+.solutions {
+ padding: 0;
+ background-color: @color-secondary;
+
+ &-slider {
+ position: relative;
+ width: 100%;
+ max-width: 1200px;
+ margin: 40px auto;
+ border-radius: 8px;
+ overflow: hidden;
+
+ &__slides {
+ display: flex;
+ width: 200%;
+ height: 100%;
+ animation: slideLeftRight 10s infinite ease-in-out;
+ }
+
+ &__slide {
+ width: 50%;
+ flex-shrink: 0;
+ position: relative;
+ overflow: hidden;
+ transition: transform 0.5s ease, box-shadow 0.5s ease;
+
+ &:hover {
+ transform: scale(1.02);
+ box-shadow: 0 10px 25px rgba(0,0,0,0.3);
+ .solution-img {
+ transform: scale(1.05);
+ filter: brightness(0.8);
+ }
+ .solution-text-overlay {
+ opacity: 1;
+ transform: translateY(-5px);
+ }
+ .solution-image-link {
+ transform: translateX(-50%) translateY(-6px);
+ background-color: rgba(255,255,255,0.9);
+ color: @color-text-dark;
+ }
+ }
+ }
+
+ .solution-img {
+ width: 100%;
+ height: auto;
+ object-fit: cover;
+ display: block;
+ }
+
+ .solution-text-overlay {
+ position: absolute;
+ top: 15%;
+ left: 8%;
+ color: #493131;
+ text-shadow: 2px 2px 4px rgba(0,0,0,0.6);
+ z-index: 2;
+ opacity: 0.9;
+ transition: opacity 0.5s ease, transform 0.5s ease;
+ h2 {
+ font-size: 35px;
+ text-transform: uppercase;
+ margin-bottom: 10px;
+ }
+ p {
+ font-size: 25px;
+ text-transform: uppercase;
+ }
+ }
+
+ .solution-image-link {
+ position: absolute;
+ bottom: 40px;
+ left: 50%;
+ transform: translateX(-50%);
+ padding: 12px 30px;
+ border: 2px solid @color-text-light;
+ color: #493131;
+ text-transform: uppercase;
+ font-size: 16px;
+ font-weight: bold;
+ background: transparent;
+ transition: 0.4s ease;
+ z-index: 2;
+ &:hover {
+ background: @color-text-light;
+ color: @color-text-dark;
+ transform: translateX(-50%) translateY(-2px);
+ }
+ }
+ }
+}
+
+@keyframes slideLeftRight {
+ 0%, 40% { transform: translateX(0); }
+ 50%, 90% { transform: translateX(-50%); }
+ 100% { transform: translateX(0); }
+}
+
+.stats {
+ padding: 0;
+ margin-top: 20px;
+
+ .container {
+ display: flex;
+ justify-content: flex-end;
+ }
+
+ &__items {
+ display: flex;
+ gap: 20px;
+ .stat-item {
+ text-align: left;
+ .stat-number {
+ font-size: 36px;
+ font-weight: bold;
+ color: @color-text-dark;
+ margin-bottom: 5px;
+ }
+ .stat-label { color: @color-text-dark; }
+ }
+ }
+}
+
+.faq {
+ padding: 50px 0;
+
+ h2 {
+ text-align: left;
+ font-size: 32px;
+ font-weight: normal;
+ margin-bottom: 40px;
+ }
+
+ &__items {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 40px 60px;
+ margin-bottom: 40px;
+ }
+
+ .faq-item {
+ flex: 0 0 calc(50% - 30px);
+ .flex-center(15px);
+ align-items: flex-start;
+ &__content h4 {
+ font-weight: 600;
+ margin-bottom: 10px;
+ }
+ }
+
+ .btn.primary-btn {
+ display: block;
+ width: 100%;
+ margin: 20px auto 80px;
+ }
+}
+
+// =======================
+// === СТИЛИ КАТАЛОГА ===
+// =======================
+.catalog-main {
+ padding: 30px 0 60px;
+ background-color: lighten(@color-secondary, 5%);
+}
+
+.catalog-wrapper {
+ display: flex;
+ gap: 20px;
+}
+
+.catalog-sidebar {
+ flex: 0 0 250px;
+ background-color: #fff;
+ padding: 20px;
+ box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05);
+ height: fit-content;
+}
+
+.filter-group {
+ margin-bottom: 30px;
+}
+
+.filter-title {
+ font-size: 16px;
+ font-weight: bold;
+ margin-bottom: 15px;
+ text-transform: uppercase;
+}
+
+.filter-list li {
+ padding: 5px 0;
+ font-size: 16px;
+ a {
+ color: #555;
+ transition: color 0.2s;
+ &:hover { color: @color-accent; }
+ &.active-category {
+ font-weight: bold;
+ color: @color-primary;
+ }
+ }
+}
+
+.price-range {
+ display: flex;
+ flex-direction: column;
+ gap: 15px;
+ width: 100%;
+
+ .range-slider {
+ width: 100%;
+
+ input[type="range"] {
+ -webkit-appearance: none;
+ appearance: none;
+ width: 100%;
+ height: 5px;
+ background: @color-primary;
+ border-radius: 5px;
+ outline: none;
+ margin: 0;
+
+ &::-webkit-slider-thumb {
+ -webkit-appearance: none;
+ appearance: none;
+ width: 20px;
+ height: 20px;
+ background: @color-accent;
+ border: 2px solid #fff;
+ border-radius: 50%;
+ cursor: pointer;
+ box-shadow: 0 2px 4px rgba(0,0,0,0.2);
+ transition: all 0.3s ease;
+
+ &:hover {
+ transform: scale(1.1);
+ background: lighten(@color-accent, 10%);
+ }
+ }
+
+ &::-moz-range-thumb {
+ width: 20px;
+ height: 20px;
+ background: @color-accent;
+ border: 2px solid #fff;
+ border-radius: 50%;
+ cursor: pointer;
+ box-shadow: 0 2px 4px rgba(0,0,0,0.2);
+ transition: all 0.3s ease;
+
+ &:hover {
+ transform: scale(1.1);
+ background: lighten(@color-accent, 10%);
+ }
+ }
+ }
+ }
+
+ .price-display {
+ font-size: 14px;
+ font-weight: bold;
+ text-align: center;
+ color: @color-text-dark;
+ padding: 10px;
+ background: #f8f8f8;
+ border-radius: 4px;
+ }
+}
+
+.filter-options {
+ list-style: none;
+ li {
+ display: flex;
+ align-items: center;
+ padding: 4px 0;
+ font-size: 14px;
+ }
+ label {
+ margin-left: 10px;
+ cursor: pointer;
+ color: #555;
+ }
+ input[type="checkbox"] {
+ width: 15px;
+ height: 15px;
+ cursor: pointer;
+ accent-color: @color-primary;
+ &:checked + label {
+ font-weight: bold;
+ color: @color-primary;
+ }
+ }
+}
+
+.filter-apply-btn {
+ width: 100%;
+ margin-top: 20px;
+}
+
+.catalog-products {
+ flex-grow: 1;
+}
+
+.products-container {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 20px;
+}
+
+.product-card {
+ background-color: #fff;
+ box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05);
+ display: flex;
+ flex-direction: column;
+ position: relative;
+ transition: transform 0.3s ease;
+ box-sizing: border-box;
+
+ &:hover {
+ transform: translateY(-5px);
+ box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
+ .product-img { transform: scale(1.05); }
+ }
+}
+
+.product-image-container {
+ position: relative;
+ overflow: hidden;
+ margin-bottom: 0;
+ padding: 0;
+ height: 250px;
+ .flex-center();
+}
+
+.product-img {
+ width: 100%;
+ height: 100%;
+ object-fit: contain;
+ display: block;
+ transition: transform 0.3s ease;
+ margin: 0;
+}
+
+.product-img1 {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ display: block;
+ transition: transform 0.3s ease;
+ margin: 0;
+}
+
+.product-discount {
+ position: absolute;
+ top: 10px;
+ right: 10px;
+ background-color: @color-button;
+ color: @color-text-light;
+ padding: 3px 8px;
+ font-size: 12px;
+ font-weight: bold;
+ z-index: 10;
+}
+
+.product-wishlist-icon {
+ position: absolute;
+ top: 10px;
+ left: 10px;
+ color: #333;
+ font-size: 18px;
+ cursor: pointer;
+ transition: color 0.3s ease;
+ z-index: 10;
+ &:hover { color: @color-accent; }
+}
+
+.product-name {
+ font-size: 16px;
+ font-weight: bold;
+ margin-bottom: 5px;
+}
+
+.product-details {
+ font-size: 13px;
+ color: #777;
+ margin-bottom: 10px;
+ flex-grow: 1;
+}
+
+.product-price {
+ font-size: 18px;
+ font-weight: bold;
+ color: @color-button;
+}
+
+.product-card.small { flex: 0 0 300px; max-width: 300px; height: 200px; }
+.product-card.small1 { flex: 0 0 320px; max-width: 320px; height: 250px;width: 320px; }
+.product-card.large { flex: 0 0 580px; max-width: 580px; height: 380px; }
+.product-card.wide { flex: 0 0 240px; max-width: 240px; height: 250px; }
+.product-card.wide1 { flex: 0 0 350px; max-width: 350px; height: 250px; }
+.product-card.wide2 { flex: 0 0 560px; max-width: 560px; height: 260px; }
+.product-card.wide2_1 { flex: 0 0 560px; max-width: 560px; height: 260px; margin: -280px 0 0; }
+.product-card.wide3 {
+ flex: 0 0 320px; max-width: 320px; height: 540px;
+ .product-image-container { height: 580px; }
+}
+.product-card.wide4 {
+ flex: 0 0 545px; max-width: 545px; margin: -270px 0 0; height: 250px;
+ .product-image-container { padding: 0; justify-content: flex-start; }
+ .product-img { margin-left: 0; align-self: flex-start; object-position: left center; }
+}
+.product-card.tall { flex: 0 0 300px; max-width: 300px; margin: -180px 0 0; height: 430px; }
+.product-card.full-width { flex: 0 0 100%; margin: -20px 0 0; max-width: 900px; height: 300px;}
+
+.product-card.full-width {
+ flex: 0 0 100%;
+ max-width: 100%;
+ height: 300px;
+
+ .product-image-container {
+ height: 100%;
+ padding: 0;
+ margin: 0;
+
+ .product-img1 {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ margin: 0;
+ padding: 0;
+ }
+ }
+}
+
+.product-card.tall .product-image-container,
+.product-card.large .product-image-container { height: 430px; }
+
+// =======================
+// === СТРАНИЦА ТОВАРА ===
+// =======================
+.product__section {
+ display: flex;
+ gap: 0;
+ margin: 30px 0;
+}
+
+.product__gallery, .product__info {
+ flex: 1;
+}
+
+.product__main-image {
+ margin-bottom: 15px;
+}
+
+.product__image {
+ width: 500px;
+ height: 300px;
+ border-radius: 4px;
+}
+
+.product__thumbnails {
+ display: flex;
+ gap: 10px;
+}
+
+.product__thumbnail {
+ border: none;
+ background: none;
+ cursor: pointer;
+ padding: 0;
+}
+
+.product__thumbnail img {
+ width: 245px;
+ height: 150px;
+ object-fit: cover;
+ border-radius: 4px;
+}
+
+.product__title {
+ font-size: 30px;
+ margin-bottom: 35px;
+}
+
+.product__rating {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ margin-bottom: 30px;
+}
+
+.product__color-selector {
+ display: flex;
+ gap: 10px;
+ margin-bottom: 40px;
+}
+
+.product__color-option {
+ width: 45px;
+ height: 45px;
+ border-radius: 50%;
+ border: 2px solid transparent;
+ cursor: pointer;
+ transition: transform 0.3s ease;
+
+ &:hover{
+ transform: translateY(-2px);
+ }
+}
+
+.product__color-option.active {
+ border-color: @color-primary;
+}
+
+.product__description {
+ margin-bottom: 65px;
+ line-height: 1.5;
+}
+
+.product__details-link {
+ display: inline-block;
+ margin-bottom: 20px;
+ color: @color-primary;
+ font-weight: bold;
+}
+
+.product__purchase {
+ display: flex;
+ justify-content: space-between;
+ margin-bottom: 35px;
+}
+
+.product__price {
+ font-size: 24px;
+ font-weight: bold;
+}
+
+.product__quantity {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+}
+
+.product__qty-btn {
+ width: 30px;
+ height: 30px;
+ background: @color-button;
+ color: @color-text-light;
+ border: none;
+ border-radius: 50%;
+ cursor: pointer;
+ font-weight: bold;
+ transition: all 0.3s ease;
+
+ &:hover {
+ background: lighten(@color-button, 10%);
+ transform: scale(1.1);
+ }
+}
+
+.product__qty-value {
+ font-weight: bold;
+ min-width: 30px;
+ text-align: center;
+}
+
+.product__actions {
+ display: flex;
+ gap: 15px;
+}
+
+.product__btn {
+ flex: 1;
+ padding: 12px 20px;
+ border: none;
+ border-radius: 4px;
+ font-weight: bold;
+ cursor: pointer;
+ transition: all 0.3s ease;
+
+ &:hover {
+ transform: translateY(-2px);
+ box-shadow: @shadow-light;
+ }
+}
+
+.product__btn.primary {
+ background: @color-button;
+ color: @color-text-light;
+
+ &:hover {
+ background: lighten(@color-button, 10%);
+ }
+}
+
+.product__btn.secondary {
+ background: transparent;
+ border: 1px solid @color-button;
+ color: @color-button;
+
+ &:hover {
+ background: @color-button;
+ color: @color-text-light;
+ }
+}
+
+.similar {
+ margin: 60px 0;
+}
+
+.similar__title {
+ margin-bottom: 30px;
+ font-size: 28px;
+ font-weight: bold;
+}
+
+.similar__grid {
+ display: flex;
+ gap: 25px;
+ flex-wrap: wrap;
+ justify-content: space-between;
+}
+
+.similar__card {
+ flex: 0 0 calc(33.333% - 17px);
+ min-width: 320px;
+ background: @color-secondary;
+ border-radius: 12px;
+ overflow: hidden;
+ transition: all 0.3s ease;
+ box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
+
+ &:hover {
+ transform: translateY(-8px);
+ box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
+ }
+}
+
+.similar__card-image {
+ height: 300px;
+ overflow: hidden;
+ background: white;
+
+ img {
+ width: 100%;
+ height: 100%;
+ object-fit: contain;
+ transition: transform 0.3s ease;
+
+ &:hover {
+ transform: scale(1.05);
+ }
+ }
+}
+
+.similar__card-content {
+ padding: 25px;
+}
+
+.similar__card-title {
+ font-weight: bold;
+ margin-bottom: 10px;
+ font-size: 20px;
+ color: @color-text-dark;
+}
+
+.similar__card-description {
+ font-size: 15px;
+ margin-bottom: 15px;
+ color: #666;
+ line-height: 1.5;
+}
+
+.similar__card-price {
+ font-weight: bold;
+ font-size: 22px;
+ color: @color-button;
+}
+
+@media (max-width: 1024px) {
+ .similar {
+ &__card {
+ flex: 0 0 calc(50% - 13px);
+ min-width: 280px;
+ }
+ }
+}
+
+@media (max-width: 768px) {
+ .similar {
+ &__grid {
+ justify-content: center;
+ }
+
+ &__card {
+ flex: 0 0 100%;
+ max-width: 400px;
+ }
+ }
+}
+
+// =======================
+// === КОРЗИНА И ЗАКАЗ ===
+// =======================
+.main__content {
+ display: flex;
+ gap: 40px;
+ margin: 30px 0;
+
+ .products {
+ flex: 1;
+ }
+
+ .order {
+ flex: 0 0 65%;
+ padding: 40px;
+
+ &__header {
+ .flex-between();
+ margin-bottom: 20px;
+ }
+
+ &__title {
+ font-family: @font-logo;
+ font-size: 28px;
+ color: @color-text-dark;
+ margin: 0;
+ }
+
+ &__total {
+ font-weight: bold;
+ color: @color-text-dark;
+ }
+
+ &__section {
+ margin-bottom: 25px;
+ }
+
+ &__section-title {
+ font-family: @font-logo;
+ margin-bottom: 15px;
+ font-size: 18px;
+ color: @color-text-dark;
+ }
+ }
+}
+
+.products {
+ &__title {
+ font-family: @font-logo;
+ margin-bottom: 20px;
+ font-size: 24px;
+ color: @color-text-dark;
+ }
+
+ &__list {
+ .flex-column();
+ gap: 20px;
+ }
+
+ &__item {
+ background-color: @color-secondary;
+ border-radius: 8px;
+ padding: 20px;
+ display: flex;
+ gap: 15px;
+ border: 1px solid @color-secondary;
+ transition: transform 0.3s ease;
+ align-items: flex-start;
+ position: relative;
+
+ &:hover {
+ transform: translateY(-2px);
+ }
+ }
+
+ &__image {
+ width: 300px;
+ height: 200px;
+ border-radius: 4px;
+ flex-shrink: 0;
+ display: flex;
+ align-items: center;
+ justify-content: flex-start;
+ }
+
+ .product-img {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ display: block;
+ transition: transform 0.3s ease;
+ margin: 0;
+ }
+
+ &__details {
+ flex: 1;
+ .flex-column();
+ justify-content: space-between;
+ align-items: flex-start;
+ min-height: 200px;
+ }
+
+ &__name {
+ font-weight: bold;
+ margin-bottom: 5px;
+ color: @color-accent;
+ font-size: 18px;
+ font-family: @font-main;
+ }
+
+ &__price {
+ font-weight: bold;
+ font-size: 18px;
+ margin-bottom: 15px;
+ color: @color-text-dark;
+ }
+
+ &__controls {
+ display: flex;
+ align-items: center;
+ gap: 15px;
+ margin-top: auto;
+ width: 100%;
+ justify-content: space-between;
+ }
+
+ &__quantity {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ }
+
+ &__qty-btn {
+ width: 30px;
+ height: 30px;
+ background-color: @color-text-dark;
+ color: @color-text-light;
+ border: none;
+ border-radius: 50%;
+ cursor: pointer;
+ .flex-center();
+ font-family: @font-main;
+ font-weight: bold;
+ transition: all 0.3s ease;
+
+ &:hover {
+ transform: scale(1.1);
+ }
+ }
+
+ &__qty-value {
+ font-weight: bold;
+ min-width: 30px;
+ text-align: center;
+ font-size: 16px;
+ }
+
+ &__cart-icon {
+ background-color: transparent;
+ color: @color-text-dark;
+ width: 40px;
+ height: 40px;
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+ transition: all 0.3s ease;
+ border: 2px solid @color-text-dark;
+ margin-left: 20px;
+
+ &:hover {
+ transform: scale(1.1);
+ }
+
+ i { font-size: 18px; }
+ }
+}
+
+.form {
+ &__group { margin-bottom: 15px; }
+ &__label {
+ display: block;
+ margin-bottom: 5px;
+ font-weight: bold;
+ color: #000000;
+ }
+ &__input {
+ width: 100%;
+ padding: 14px 16px;
+ border: 2px solid #ccc;
+ font-family: @font-main;
+ font-size: 15px;
+ transition: border-color 0.3s ease;
+
+ &:focus {
+ border-color: @color-primary;
+ }
+
+ &:hover {
+ border-color: darken(#ccc, 10%);
+ }
+ &::placeholder {
+ font-style: italic;
+ color: #999;
+ }
+ }
+ &__row {
+ display: flex;
+ gap: 20px;
+ justify-content: space-between;
+ }
+ &__input--half {
+ width: 100%;
+ }
+ &__radio-group {
+ display: flex;
+ justify-content: space-between;
+ margin-bottom: 20px;
+ margin-top: 20px;
+ }
+ &__radio-label {
+ display: flex;
+ align-items: center;
+ cursor: pointer;
+ color: @color-text-dark;
+ position: relative;
+ padding-left: 30px;
+ flex: 1;
+
+ &:hover {
+ .form__custom-radio {
+ border-color: lighten(@color-accent, 10%);
+ }
+ }
+ }
+ &__radio-input {
+ position: absolute;
+ opacity: 0;
+ cursor: pointer;
+ }
+ &__custom-radio {
+ position: absolute;
+ left: 0;
+ height: 20px;
+ width: 20px;
+ background-color: @color-secondary;
+ border: 2px solid @color-accent;
+ border-radius: 50%;
+ transition: border-color 0.3s ease;
+ }
+ &__radio-input:checked ~ &__custom-radio {
+ background-color: @color-accent;
+
+ &:after {
+ content: "";
+ position: absolute;
+ display: block;
+ top: 4px;
+ left: 4px;
+ width: 8px;
+ height: 8px;
+ border-radius: 50%;
+ background: white;
+ }
+ }
+}
+
+.divider {
+ height: 1px;
+ background-color: #999;
+ margin: 20px 0;
+}
+
+.promo {
+ display: flex;
+ margin-bottom: 20px;
+
+ &__input {
+ flex: 1;
+ padding: 10px;
+ border: 1px solid #000;
+ background-color: @color-secondary;
+ font-family: @font-main;
+ height: auto;
+ min-height: 48px;
+
+ &:hover {
+ border-color: @color-primary;
+ }
+
+ &::placeholder {
+ font-style: italic;
+ color: #999;
+ }
+ }
+
+ &__btn {
+ background-color: @color-accent;
+ color: @color-secondary;
+ border: none;
+ padding: 10px 60px;
+ cursor: pointer;
+ font-family: @font-main;
+ font-size: 18px;
+ transition: all 0.3s ease;
+
+ &:hover {
+ background-color: lighten(@color-accent, 10%);
+ transform: translateY(-2px);
+ }
+ }
+}
+
+.summary {
+ margin-bottom: 20px;
+
+ &__item {
+ display: flex;
+ justify-content: space-between;
+ margin-bottom: 10px;
+ color: @color-text-dark;
+
+ &.total {
+ font-weight: bold;
+ font-size: 18px;
+ padding-top: 10px;
+ margin-top: 10px;
+ }
+ }
+}
+
+.order-btn {
+ width: 100%;
+ background-color: @color-accent;
+ color: @color-secondary;
+ border: none;
+ padding: 15px;
+ border-radius: 4px;
+ font-size: 18px;
+ cursor: pointer;
+ margin-bottom: 10px;
+ font-family: @font-main;
+ transition: all 0.3s ease;
+
+ &:hover {
+ background-color: lighten(@color-accent, 10%);
+ transform: translateY(-2px);
+ }
+}
+
+.privacy {
+ display: flex;
+ gap: 8px;
+ font-size: 16px;
+ color: #666;
+ margin-bottom: 20px;
+
+ input[type="checkbox"] {
+ width: 18px;
+ height: 18px;
+ cursor: pointer;
+ }
+}
+
+.services {
+ display: flex;
+ flex-direction: row;
+ justify-content: center;
+ flex-wrap: wrap;
+ gap: 24px;
+ margin-bottom: 24px;
+
+ &__title {
+ font-family: @font-logo;
+ margin-bottom: 10px;
+ font-size: 18px;
+ color: @color-text-dark;
+ display: block;
+ width: 100%;
+ }
+
+ &__item {
+ display: flex;
+ justify-content: space-between;
+ margin-bottom: 8px;
+ color: @color-text-dark;
+ width: 100%;
+ }
+}
+
+.cart-icon {
+ position: relative;
+}
+
+.cart-count {
+ position: absolute;
+ top: -8px;
+ right: -8px;
+ background: @color-accent;
+ color: @color-text-light;
+ border-radius: 50%;
+ width: 18px;
+ height: 18px;
+ font-size: 12px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.form__input.error {
+ border-color: #ff4444;
+ box-shadow: 0 0 5px rgba(255, 68, 68, 0.3);
+}
+
+.empty-cart {
+ text-align: center;
+ padding: 40px;
+ color: #666;
+ font-size: 18px;
+}
+
+// =======================
+// === АВТОРИЗАЦИЯ ===
+// =======================
+.profile-page-main {
+ .flex-center();
+ min-height: 80vh;
+ padding: 40px 0;
+ background-color: lighten(@color-secondary, 5%);
+ z-index: 1;
+
+ .profile-container {
+ display: flex;
+ width: 100%;
+ max-width: 1000px;
+ min-height: 600px;
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
+ background-color: @color-text-light;
+ }
+
+ .profile-left-col {
+ flex: 0 0 35%;
+ background-color: @color-primary;
+ color: @color-text-light;
+ display: flex;
+ justify-content: flex-start;
+ align-items: flex-start;
+ padding: 40px;
+ .logo {
+ font-size: 32px;
+ font-weight: normal;
+ text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.4);
+ color: @color-text-light;
+ }
+ }
+
+ .profile-right-col {
+ flex: 0 0 65%;
+ .flex-center();
+ padding: 40px;
+ .profile-form-block {
+ width: 100%;
+ max-width: 400px;
+ h2 {
+ font-size: 28px;
+ font-weight: normal;
+ margin-bottom: 40px;
+ text-align: left;
+ color: @color-text-dark;
+ }
+ }
+ }
+
+ .profile-form {
+ .input-group {
+ margin-bottom: 20px;
+ label {
+ display: block;
+ font-size: 12px;
+ font-weight: bold;
+ color: @color-text-dark;
+ margin-bottom: 5px;
+ text-transform: uppercase;
+ }
+ }
+
+ input[type="text"],
+ input[type="email"],
+ input[type="tel"] {
+ .input-base();
+ }
+
+ .password-link {
+ display: block;
+ text-align: left;
+ font-size: 13px;
+ color: @color-text-dark;
+ text-decoration: underline;
+ margin: 10px 0 20px;
+ &:hover {
+ color: @color-accent;
+ text-decoration: none;
+ }
+ }
+
+ .save-btn {
+ padding: 15px 30px;
+ border: none;
+ cursor: pointer;
+ font-size: 15px;
+ text-transform: uppercase;
+ transition: all 0.3s ease;
+ font-family: @font-main;
+ width: 100%;
+ margin-top: 20px;
+ background-color: @color-primary;
+ color: @color-text-light;
+
+ &:hover {
+ background-color: lighten(@color-primary, 10%);
+ transform: translateY(-2px);
+ box-shadow: @shadow-light;
+ }
+ }
+
+ .auth-actions {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-top: 25px;
+ padding-top: 20px;
+ border-top: 1px solid #eee;
+
+ .auth-text {
+ font-size: 13px;
+ color: @color-text-dark;
+ }
+
+ .login-btn {
+ background-color: transparent;
+ color: @color-accent;
+ border: 1px solid @color-accent;
+ padding: 10px 25px;
+ font-size: 13px;
+ cursor: pointer;
+ transition: all 0.3s ease;
+
+ &:hover {
+ background-color: @color-primary;
+ color: @color-text-light;
+ }
+ }
+ }
+ }
+}
+
+// =======================
+// === СЕКЦИЯ УСЛУГ ===
+// =======================
+.services-section {
+ padding: 60px 0;
+ background-color: @color-secondary;
+}
+
+.services__wrapper {
+ display: flex;
+ flex-direction: column;
+ gap: 30px;
+}
+
+.services__top-row {
+ display: flex;
+ gap: 30px;
+ justify-content: center;
+
+ @media (max-width: 768px) {
+ flex-direction: column;
+ align-items: center;
+ }
+}
+
+.service-card {
+ border-radius: 8px;
+ padding: 40px;
+ min-height: 200px;
+ text-align: center;
+ transition: all 0.3s ease;
+
+ &:hover {
+ transform: translateY(-5px);
+ box-shadow: @shadow-light;
+ }
+
+ &--green {
+ background: @color-primary;
+ color: @color-text-light;
+ flex: 1;
+ max-width: 450px;
+ }
+
+ &--beige {
+ background: @color-beige;
+ color: @color-text-light;
+ width: 100%;
+ max-width: 930px;
+ margin: 0 auto;
+ }
+
+ &__title {
+ font-family: @font-logo;
+ font-size: 24px;
+ font-weight: bold;
+ margin-bottom: 15px;
+ text-transform: uppercase;
+ }
+
+ &__text {
+ font-family: @font-main;
+ font-size: 16px;
+ line-height: 1.6;
+ margin: 0;
+ }
+}
+
+// =======================
+// === ФУТЕР ===
+// =======================
+.footer {
+ background-color: @color-primary;
+ color: black;
+ padding: 40px 0 10px;
+ position: relative;
+ z-index: 1000;
+
+ &::before {
+ content: '';
+ display: block;
+ position: absolute;
+ top: -80px;
+ left: 0;
+ width: 100%;
+ height: 1px;
+ visibility: hidden;
+ }
+
+ &__content {
+ display: flex;
+ gap: 20px;
+ padding-bottom: 30px;
+ border-bottom: 1px solid rgba(255, 255, 255, 0.2);
+ }
+
+ &__col {
+ flex: 1;
+ &--logo { flex: 1.5; }
+ h5 {
+ margin-bottom: 15px;
+ font-size: 14px;
+ text-transform: uppercase;
+ }
+ ul li {
+ margin-bottom: 8px;
+ a:hover { text-decoration: underline; }
+ }
+ .social-icons,
+ .payment-icons {
+ .flex-center(15px);
+ justify-content: flex-start;
+ margin-top: 10px;
+ }
+ .social-icons .icon {
+ .icon-base(20px, 1.1);
+ color: black;
+ &:hover { color: @color-accent; }
+ }
+ .payment-icons .pay-icon {
+ .icon-base(24px, 1.05);
+ color: black;
+ }
+ }
+
+ .copyright {
+ text-align: center;
+ font-size: 12px;
+ padding-top: 20px;
+ color: rgba(255, 255, 255, 0.6);
+ }
+}
+
+// =======================
+// === ДОСТАВКА ===
+// =======================
+.delivery-content {
+ max-width: 1200px;
+ margin: 0 auto;
+ padding: 40px 20px;
+}
+
+.delivery-content h1 {
+ font-family: @font-logo;
+ font-size: 42px;
+ text-align: center;
+ margin-bottom: 50px;
+ color: #453227;
+}
+
+.delivery-section {
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: center;
+ gap: 30px;
+ margin-bottom: 60px;
+}
+
+.delivery-card {
+ background: white;
+ padding: 30px;
+ border-radius: 12px;
+ box-shadow: 0 5px 15px rgba(0,0,0,0.1);
+ text-align: center;
+ transition: transform 0.3s ease;
+ flex: 1;
+ min-width: 350px;
+ max-width: 400px;
+}
+
+.delivery-card:hover {
+ transform: translateY(-5px);
+}
+
+.delivery-icon {
+ font-size: 48px;
+ color: #617365;
+ margin-bottom: 20px;
+}
+
+.delivery-card h3 {
+ font-family: @font-logo;
+ font-size: 24px;
+ margin-bottom: 20px;
+ color: #453227;
+}
+
+.delivery-details {
+ text-align: left;
+}
+
+.detail-item {
+ display: flex;
+ justify-content: space-between;
+ margin-bottom: 12px;
+ padding-bottom: 12px;
+ border-bottom: 1px solid #f0f0f0;
+}
+
+.detail-label {
+ font-weight: bold;
+ color: #333;
+}
+
+.detail-value {
+ color: #617365;
+ text-align: right;
+}
+
+// =======================
+// === ГАРАНТИЯ ===
+// =======================
+.warranty-content {
+ max-width: 1200px;
+ margin: 0 auto;
+ padding: 40px 20px;
+}
+
+.warranty-content h1 {
+ font-family: 'Anek Kannada', sans-serif;
+ font-size: 42px;
+ text-align: center;
+ margin-bottom: 50px;
+ color: #453227;
+}
+
+.warranty-overview {
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: center;
+ gap: 25px;
+ margin-bottom: 60px;
+}
+
+.warranty-card {
+ background: white;
+ padding: 30px;
+ border-radius: 12px;
+ text-align: center;
+ box-shadow: 0 5px 15px rgba(0,0,0,0.1);
+ transition: transform 0.3s ease;
+ flex: 1;
+ min-width: 250px;
+ max-width: 280px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+}
+
+.warranty-card:hover {
+ transform: translateY(-5px);
+}
+
+.warranty-icon {
+ font-size: 48px;
+ color: #617365;
+ margin-bottom: 20px;
+}
+
+.warranty-card h3 {
+ font-family: 'Anek Kannada', sans-serif;
+ font-size: 20px;
+ margin-bottom: 15px;
+ color: #453227;
+}
+
+.warranty-period {
+ font-size: 24px;
+ font-weight: bold;
+ color: #617365;
+ margin-top: auto;
+}
+
+.coverage-section {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 40px;
+ margin-bottom: 60px;
+}
+
+.coverage-covered,
+.coverage-not-covered {
+ flex: 1;
+ min-width: 300px;
+}
+
+.coverage-section h2 {
+ font-family: 'Anek Kannada', sans-serif;
+ font-size: 24px;
+ margin-bottom: 25px;
+ color: #453227;
+}
+
+.coverage-list {
+ display: flex;
+ flex-direction: column;
+ gap: 20px;
+}
+
+.coverage-item {
+ display: flex;
+ align-items: flex-start;
+ gap: 15px;
+ padding: 20px;
+ border-radius: 8px;
+ background: white;
+ box-shadow: 0 3px 10px rgba(0,0,0,0.1);
+}
+
+.coverage-item.covered i {
+ color: #28a745;
+ font-size: 20px;
+ margin-top: 2px;
+ flex-shrink: 0;
+}
+
+.coverage-item.not-covered i {
+ color: #dc3545;
+ font-size: 20px;
+ margin-top: 2px;
+ flex-shrink: 0;
+}
+
+.coverage-text {
+ flex: 1;
+}
+
+.coverage-item h4 {
+ font-family: 'Anek Kannada', sans-serif;
+ font-size: 16px;
+ margin-bottom: 5px;
+ color: #333;
+}
+
+.card {
+ min-height: 250px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: @color-text-light;
+ text-align: center;
+
+ &--green {
+ background: @color-primary;
+ flex: 0 1 450px;
+ max-width: 450px;
+ }
+
+ &--beige {
+ background: @color-beige;
+ color: @color-text-dark;
+ flex: 0 1 925px;
+ max-width: 925px;
+ }
+}
+
+.design-section {
+ display: flex;
+ justify-content: center;
+ margin-bottom: 40px;
+ .card { width: 100%; }
+}
+
+// =======================
+// === АДАПТИВНОСТЬ ===
+// =======================
+@media (max-width: 1240px) {
+ .catalog-wrapper { gap: 20px; }
+ .catalog-sidebar { flex: 0 0 200px; }
+ .products-container {
+ gap: 15px;
+ display: flex;
+ flex-wrap: wrap;
+ }
+
+ .product-card.small1 {
+ margin-top: 100px;
+ }
+
+ .product-card.small,
+ .product-card.small1,
+ .product-card.large,
+ .product-card.wide,
+ .product-card.wide1,
+ .product-card.wide2,
+ .product-card.wide2_1,
+ .product-card.wide4 {
+ flex: 0 0 calc(33.333% - 10px);
+ max-width: calc(33.333% - 10px);
+ height: 180px;
+ margin: 0;
+
+ .product-image-container {
+ height: 180px;
+ }
+ }
+
+ .product-card.wide3 {
+ flex: 0 0 calc(25% - 10px);
+ max-width: calc(25% - 10px);
+ height: 300px;
+ margin: 0;
+
+ .product-image-container {
+ height: 350px;
+ }
+ }
+
+ .product-card.tall {
+ flex: 0 0 calc(25% - 10px);
+ max-width: calc(25% - 10px);
+ height: 300px;
+ margin: 0;
+
+ .product-image-container {
+ height: 300px;
+ }
+ }
+
+ .product-card.full-width {
+ flex: 0 0 100%;
+ max-width: 100%;
+ height: 300px;
+ margin: 0;
+
+ .product-image-container {
+ height: 300px;
+ }
+ }
+
+ .product-card.small { order: 1; }
+ .product-card.large { order: 2; }
+ .product-card.wide { order: 3; }
+
+ .product-card.small1 { order: 11; }
+ .product-card.wide2 { order: 12; }
+ .product-card.wide2_1 { order: 13; }
+
+ .product-card.wide3 { order: 21; }
+ .product-card.tall { order: 22; }
+
+ .product-card.wide3 { order: 31; }
+
+ .product-card.full-width { order: 41; flex-basis: 100%; }
+
+ .main__content {
+ gap: 20px;
+ .products {
+ flex: 0 0 35%;
+ .products__image {
+ width: 250px;
+ height: 180px;
+ }
+ }
+ .order {
+ flex: 0 0 60%;
+ padding: 30px;
+
+ .order__title {
+ font-size: 24px;
+ }
+
+ .order__section-title {
+ font-size: 16px;
+ }
+ }
+ }
+
+ .solutions-slider {
+ &__slide {
+ .solution-text-overlay {
+ top: 10%;
+ left: 5%;
+ h2 {
+ font-size: 26px;
+ margin-bottom: 5px;
+ line-height: 1.2;
+ }
+ p {
+ font-size: 18px;
+ line-height: 1.2;
+ }
+ }
+ .solution-image-link {
+ bottom: 70px;
+ padding: 10px 25px;
+ font-size: 14px;
+ }
+ }
+ }
+
+ .product__image {
+ width: 350px;
+ height: 250px;
+ }
+
+ .product__thumbnail img {
+ width: 170px;
+ height: 120px;
+ }
+}
+
+@media (max-width: 1024px) {
+ .main__content {
+ gap: 25px;
+ .products {
+ flex: 0 0 30%;
+ .products__image {
+ width: 200px;
+ height: 150px;
+ }
+
+ .products__name {
+ font-size: 16px;
+ }
+
+ .products__price {
+ font-size: 16px;
+ }
+ }
+ .order {
+ flex: 0 0 60%;
+ padding: 25px;
+
+ .order__title {
+ font-size: 22px;
+ }
+
+ .form__input {
+ padding: 12px 14px;
+ font-size: 14px;
+ }
+
+ .promo__btn {
+ padding: 10px 40px;
+ font-size: 16px;
+ }
+ }
+ }
+}
+
+@media (max-width: 768px) {
+ .container { padding: 0 15px; }
+
+ .delivery-section {
+ flex-direction: column;
+ align-items: center;
+ }
+
+ .delivery-card {
+ min-width: 100%;
+ max-width: 100%;
+ }
+
+ .delivery-content h1 {
+ font-size: 32px;
+ }
+
+ .warranty-overview {
+ flex-direction: column;
+ align-items: center;
+ }
+
+ .warranty-card {
+ max-width: 100%;
+ width: 100%;
+ }
+
+ .coverage-section {
+ flex-direction: column;
+ }
+
+ .warranty-content h1 {
+ font-size: 32px;
+ }
+
+ .header__top .container,
+ .header__bottom .container,
+ .hero__content,
+ .advantages__header,
+ .about__content,
+ .advantages__items,
+ .promo-images,
+ .stats__items,
+ .faq__items,
+ .catalog-wrapper,
+ .main__content {
+ flex-direction: column;
+ gap: 30px;
+ }
+
+ .search-catalog {
+ order: 3;
+ width: 100%;
+ max-width: 100%;
+ }
+
+ .nav-list {
+ flex-wrap: wrap;
+ justify-content: center;
+ gap: 15px;
+ }
+
+ .hero {
+ &__image-block {
+ flex: none;
+ max-width: 400px;
+ height: 400px;
+ }
+ &__circle {
+ width: 380px;
+ height: 380px;
+ }
+ &__text-block {
+ flex: none;
+ padding-left: 0;
+ text-align: center;
+ h1 { font-size: 32px; }
+ .hero__usp-text {
+ padding-left: 0;
+ justify-content: center;
+ &::before { display: none; }
+ }
+ .btn.primary-btn { margin-left: 0; }
+ }
+ }
+
+ .advantages__header h2,
+ .faq h2 { font-size: 28px; }
+
+ .faq-item,
+ .stat-item {
+ flex: none;
+ .flex-center();
+ text-align: center;
+ }
+
+ .stats .container { justify-content: center; }
+ .catalog-dropdown__menu { width: 200px; }
+
+ .catalog-sidebar { width: 100%; flex: none; }
+ .products-container { gap: 15px; }
+
+ .product-card.small,
+ .product-card.small1,
+ .product-card.large,
+ .product-card.wide,
+ .product-card.wide1,
+ .product-card.wide2,
+ .product-card.wide2_1,
+ .product-card.wide3,
+ .product-card.wide4,
+ .product-card.tall,
+ .product-card.full-width {
+ flex: 0 0 100%;
+ max-width: 100%;
+ height: 250px;
+ margin: 0;
+
+ .product-image-container {
+ height: 200px;
+ }
+ }
+
+ .main__content {
+ flex-direction: column;
+ gap: 20px;
+
+ .products,
+ .order {
+ flex: 0 0 100%;
+ width: 100%;
+ }
+
+ .products {
+ .products__item {
+ flex-direction: column;
+ text-align: center;
+ gap: 15px;
+ }
+
+ .products__image {
+ width: 100%;
+ height: 200px;
+ justify-content: center;
+ }
+
+ .products__details {
+ min-height: auto;
+ align-items: center;
+ }
+
+ .products__controls {
+ justify-content: center;
+ margin-top: 15px;
+ }
+
+ .products__cart-icon {
+ margin-left: 0;
+ }
+ }
+
+ .order {
+ padding: 20px;
+
+ .order__title {
+ font-size: 20px;
+ text-align: center;
+ }
+
+ .order__total {
+ text-align: center;
+ }
+
+ .form__radio-group {
+ flex-direction: column;
+ gap: 15px;
+ }
+
+ .form__radio-label {
+ flex: none;
+ justify-content: flex-start;
+ }
+
+ .promo {
+ flex-direction: column;
+ gap: 10px;
+
+ &__btn {
+ width: 100%;
+ padding: 12px;
+ }
+ }
+
+ .order-btn {
+ padding: 12px;
+ font-size: 16px;
+ }
+
+ .services {
+ flex-direction: column;
+ align-items: center;
+ }
+ }
+ }
+
+ .product-image-container { height: 200px; }
+ .product-card.tall .product-image-container,
+ .product-card.large .product-image-container { height: 250px; }
+
+ .profile-page-main {
+ .profile-container {
+ flex-direction: column;
+ min-height: auto;
+ max-width: 100%;
+ box-shadow: none;
+ }
+ .profile-left-col {
+ flex: none;
+ width: 100%;
+ height: 100px;
+ .flex-center();
+ padding: 0;
+ }
+ .profile-right-col {
+ flex: none;
+ width: 100%;
+ padding: 30px 20px;
+ }
+ .profile-form-block { max-width: 100%; }
+ }
+
+ .form__row { flex-direction: column; }
+ .form__input--half { flex: 0 0 100%; max-width: 100%; }
+ .services { flex-direction: column; align-items: center; }
+
+ .services-section {
+ padding: 40px 0;
+ }
+
+ .service-card {
+ padding: 30px 20px;
+ min-height: 180px;
+
+ &--green,
+ &--beige {
+ max-width: 100%;
+ }
+
+ &__title {
+ font-size: 20px;
+ }
+
+ &__text {
+ font-size: 14px;
+ }
+ }
+ .solutions-slider {
+ margin: 20px auto;
+ &__slide {
+ .solution-text-overlay {
+ top: 8%;
+ left: 4%;
+ h2 {
+ font-size: 20px;
+ margin-bottom: 3px;
+ line-height: 1.1;
+ }
+ p {
+ font-size: 15px;
+ line-height: 1.1;
+ }
+ }
+ .solution-image-link {
+ bottom: 90px;
+ padding: 8px 20px;
+ font-size: 13px;
+ }
+ }
+ }
+}
+
+// Стили для ошибок полей
+.error-input {
+ border-color: #ff4444 !important;
+ box-shadow: 0 0 0 1px #ff4444;
+}
+
+.field-error {
+ color: #ff4444;
+ font-size: 12px;
+ margin-top: 5px;
+ margin-bottom: 10px;
+}
+
+// Стили для сообщений
+.message {
+ padding: 15px;
+ margin: 20px 0;
+ border-radius: 5px;
+ display: none;
+}
+
+.message.error {
+ background-color: #ffebee;
+ color: #c62828;
+ border: 1px solid #ffcdd2;
+}
+
+.message.success {
+ background-color: #e8f5e9;
+ color: #2e7d32;
+ border: 1px solid #c8e6c9;
+}
+
+// Добавьте в конец файла
+.access-denied {
+ text-align: center;
+ padding: 80px 20px;
+ background: white;
+ border-radius: 10px;
+ box-shadow: 0 5px 15px rgba(0,0,0,0.1);
+ margin: 50px 0;
+
+ h2 {
+ color: #dc3545;
+ margin-bottom: 30px;
+ font-size: 28px;
+ }
+
+ p {
+ color: #666;
+ margin-bottom: 40px;
+ font-size: 18px;
+ line-height: 1.6;
+ }
+
+ .btn {
+ margin: 5px;
+ min-width: 200px;
+ }
+}
+// =======================
+// === ПРОФИЛЬ ПОЛЬЗОВАТЕЛЯ ===
+// =======================
+
+.user-profile-dropdown {
+ position: relative;
+ display: inline-block;
+
+ &__toggle {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ cursor: pointer;
+ padding: 8px 12px;
+ border-radius: 4px;
+ transition: all 0.3s ease;
+
+ &:hover {
+ background-color: rgba(0, 0, 0, 0.05);
+ }
+
+ .user-avatar {
+ width: 32px;
+ height: 32px;
+ border-radius: 50%;
+ background-color: @color-primary;
+ color: @color-text-light;
+ .flex-center();
+ font-weight: bold;
+ }
+
+ .user-info {
+ display: flex;
+ flex-direction: column;
+
+ .user-email {
+ font-size: 12px;
+ color: #666;
+ max-width: 150px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+
+ .user-status {
+ font-size: 10px;
+ padding: 2px 6px;
+ border-radius: 10px;
+ text-transform: uppercase;
+
+ &.admin {
+ background-color: #617365;
+ color: white;
+ }
+
+ &.user {
+ background-color: #28a745;
+ color: white;
+ }
+ }
+ }
+ }
+
+ &__menu {
+ .menu-base();
+ width: 220px;
+ top: 100%;
+ right: 0;
+
+ .user-details {
+ padding: 15px;
+ border-bottom: 1px solid #eee;
+
+ .user-name {
+ font-weight: bold;
+ margin-bottom: 5px;
+ }
+
+ .user-registered {
+ font-size: 11px;
+ color: #999;
+ }
+ }
+
+ ul {
+ padding: 10px 0;
+
+ li {
+ padding: 8px 15px;
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ transition: background-color 0.3s ease;
+
+ &:hover {
+ background-color: #f5f5f5;
+ }
+
+ &.logout {
+ color: #dc3545;
+ border-top: 1px solid #eee;
+ margin-top: 5px;
+ padding-top: 12px;
+
+ &:hover {
+ background-color: #ffe6e6;
+ }
+ }
+ }
+ }
+ }
+
+ &:hover &__menu {
+ display: block;
+ }
+}
+
+// =======================
+// === КАРТОЧКА ТОВАРА ===
+// =======================
+
+.product-image-container {
+ position: relative;
+ overflow: hidden;
+ margin-bottom: 0;
+ padding: 0;
+ height: 250px;
+ .flex-center();
+
+ &:hover {
+ .product-overlay-info {
+ opacity: 1;
+ transform: translateY(0);
+ }
+ }
+}
+
+.product-overlay-info {
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ background: linear-gradient(to top, rgba(0,0,0,0.8), transparent);
+ color: white;
+ padding: 15px;
+ opacity: 0;
+ transform: translateY(10px);
+ transition: all 0.3s ease;
+
+ .product-overlay-name {
+ font-weight: bold;
+ font-size: 14px;
+ margin-bottom: 5px;
+ }
+
+ .product-overlay-price {
+ font-size: 16px;
+ font-weight: bold;
+
+ .old-price {
+ text-decoration: line-through;
+ font-size: 12px;
+ color: #ccc;
+ margin-right: 5px;
+ }
+
+ .current-price {
+ color: #ffd700;
+ }
+ }
+
+ .product-overlay-category {
+ font-size: 11px;
+ opacity: 0.8;
+ margin-top: 3px;
+ }
+
+ .product-overlay-stock {
+ font-size: 11px;
+ margin-top: 5px;
+
+ &.out-of-stock {
+ color: #ff6b6b;
+ }
+
+ i {
+ margin-right: 5px;
+ }
+ }
+}
+
+.product-card-details {
+ padding: 15px;
+ background: white;
+ flex-grow: 1;
+ display: flex;
+ flex-direction: column;
+
+ .product-card-name {
+ font-weight: bold;
+ font-size: 16px;
+ margin-bottom: 8px;
+ color: @color-text-dark;
+ }
+
+ .product-card-description {
+ font-size: 13px;
+ color: #777;
+ margin-bottom: 10px;
+ flex-grow: 1;
+ }
+
+ .product-card-attributes {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 8px;
+ margin-bottom: 10px;
+
+ .attribute {
+ font-size: 11px;
+ background: #f5f5f5;
+ padding: 3px 8px;
+ border-radius: 12px;
+ color: #666;
+
+ i {
+ margin-right: 3px;
+ }
+ }
+ }
+
+ .product-card-price {
+ margin-bottom: 10px;
+
+ .old-price {
+ text-decoration: line-through;
+ font-size: 14px;
+ color: #999;
+ margin-right: 8px;
+ }
+
+ .current-price {
+ font-size: 18px;
+ font-weight: bold;
+ color: @color-button;
+ }
+ }
+
+ .add-to-cart-btn {
+ width: 100%;
+ padding: 8px;
+ font-size: 14px;
+ }
+
+ .admin-actions {
+ display: flex;
+ gap: 5px;
+ margin-top: 10px;
+
+ .admin-btn {
+ flex: 1;
+ font-size: 12px;
+ padding: 6px;
+
+ &.delete-btn {
+ background: #dc3545;
+
+ &:hover {
+ background: #c82333;
+ }
+ }
+ }
+ }
+}
+
+// =======================
+// === ПРОФИЛЬ ПОЛЬЗОВАТЕЛЯ ===
+// =======================
+
+.user-profile-dropdown {
+ position: relative;
+ display: inline-block;
+
+ .user-profile-toggle {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ cursor: pointer;
+ padding: 8px 12px;
+ border-radius: 4px;
+ transition: all 0.3s ease;
+
+ &:hover {
+ background-color: rgba(0, 0, 0, 0.05);
+
+ .dropdown-arrow {
+ transform: rotate(180deg);
+ }
+ }
+
+ .user-avatar {
+ width: 36px;
+ height: 36px;
+ border-radius: 50%;
+ background: linear-gradient(135deg, #617365 0%, #453227 100%);
+ color: @color-text-light;
+ .flex-center();
+ font-weight: bold;
+ font-size: 16px;
+ box-shadow: 0 2px 5px rgba(0,0,0,0.2);
+ }
+
+ .user-info {
+ display: flex;
+ flex-direction: column;
+
+ .user-email {
+ font-size: 12px;
+ color: #666;
+ max-width: 120px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+
+ .user-status {
+ font-size: 10px;
+ padding: 2px 8px;
+ border-radius: 12px;
+ text-transform: uppercase;
+ font-weight: bold;
+ text-align: center;
+ margin-top: 2px;
+
+ &.admin {
+ background-color: #617365;
+ color: white;
+ border: 1px solid #617365;
+ }
+
+ &.user {
+ background-color: #28a745;
+ color: white;
+ border: 1px solid #28a745;
+ }
+ }
+ }
+
+ .dropdown-arrow {
+ font-size: 10px;
+ color: #666;
+ transition: transform 0.3s ease;
+ }
+ }
+
+ .user-profile-menu {
+ .menu-base();
+ width: 280px;
+ top: 100%;
+ right: 0;
+ margin-top: 10px;
+ padding: 0;
+ overflow: hidden;
+
+ .user-profile-header {
+ padding: 20px;
+ background: linear-gradient(135deg, #617365 0%, #453227 100%);
+ color: white;
+
+ .user-profile-name {
+ font-weight: bold;
+ margin-bottom: 8px;
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ font-size: 16px;
+ }
+
+ .user-profile-details {
+ small {
+ display: block;
+ opacity: 0.8;
+ margin-bottom: 5px;
+ font-size: 11px;
+
+ i {
+ margin-right: 5px;
+ width: 14px;
+ text-align: center;
+ }
+ }
+ }
+ }
+
+ .user-profile-links {
+ list-style: none;
+ padding: 10px 0;
+
+ li {
+ border-bottom: 1px solid #f0f0f0;
+
+ &:last-child {
+ border-bottom: none;
+ }
+
+ a {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ padding: 12px 20px;
+ color: #333;
+ transition: all 0.3s ease;
+
+ &:hover {
+ background-color: #f8f9fa;
+ color: @color-primary;
+ text-decoration: none;
+
+ i {
+ transform: scale(1.1);
+ }
+ }
+
+ i {
+ width: 20px;
+ text-align: center;
+ font-size: 14px;
+ color: #617365;
+ transition: transform 0.3s ease;
+ }
+
+ span {
+ flex-grow: 1;
+ }
+ }
+ }
+
+ .logout-item {
+ border-top: 2px solid #f0f0f0;
+ margin-top: 5px;
+
+ a {
+ color: #dc3545;
+
+ &:hover {
+ background-color: #ffe6e6;
+ color: #c82333;
+
+ i {
+ color: #dc3545;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ &:hover .user-profile-menu {
+ display: block;
+ animation: fadeIn 0.3s ease;
+ }
+}
+
+@keyframes fadeIn {
+ from {
+ opacity: 0;
+ transform: translateY(-10px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+// Для мобильных устройств
+@media (max-width: 768px) {
+ .user-profile-dropdown {
+ .user-profile-toggle {
+ .user-info {
+ display: none;
+ }
+
+ .dropdown-arrow {
+ display: none;
+ }
+ }
+
+ .user-profile-menu {
+ width: 250px;
+ right: -50px;
+ }
+ }
+}
+// Добавьте в конец файла
+.unavailable-product {
+ position: relative;
+ opacity: 0.6;
+ filter: grayscale(0.7);
+
+ &::before {
+ content: "ТОВАР ЗАКОНЧИЛСЯ";
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ background: rgba(0, 0, 0, 0.85);
+ color: white;
+ padding: 15px 25px;
+ border-radius: 5px;
+ font-weight: bold;
+ font-size: 16px;
+ text-align: center;
+ z-index: 100;
+ white-space: nowrap;
+ pointer-events: none;
+ box-shadow: 0 4px 12px rgba(0,0,0,0.3);
+ }
+
+ .product-name-overlay {
+ .name, .price {
+ color: #999 !important;
+ text-shadow: none !important;
+ }
+ }
+
+ .add-to-cart-btn {
+ display: none !important;
+ }
+
+ &:hover {
+ transform: none !important;
+ cursor: not-allowed;
+ }
+}
+
+.out-of-stock-badge {
+ position: absolute;
+ top: 10px;
+ left: 10px;
+ background: #6c757d;
+ color: white;
+ padding: 5px 10px;
+ border-radius: 4px;
+ font-size: 12px;
+ font-weight: bold;
+ z-index: 10;
+}
+
+// Для админ-таблицы
+.admin-table tr.unavailable {
+ background-color: #f8f9fa !important;
+ opacity: 0.7;
+
+ td {
+ color: #999;
+ }
+}
+
+.status-unavailable {
+ background-color: #6c757d !important;
+ color: white !important;
+}
\ No newline at end of file
diff --git a/test_add_simple.php b/test_add_simple.php
new file mode 100644
index 0000000..b68e64d
--- /dev/null
+++ b/test_add_simple.php
@@ -0,0 +1,131 @@
+getConnection();
+
+// Обработка формы
+if ($_SERVER['REQUEST_METHOD'] === 'POST') {
+ $action = $_POST['action'] ?? '';
+
+ if ($action === 'add_category') {
+ $name = $_POST['name'] ?? '';
+ $slug = preg_replace('/[^a-z0-9]/i', '-', strtolower($name));
+
+ try {
+ $stmt = $db->prepare("INSERT INTO categories (name, slug) VALUES (?, ?)");
+ $result = $stmt->execute([$name, $slug]);
+
+ if ($result) {
+ $message = "✅ Категория '$name' успешно добавлена!";
+ $message_class = "success";
+ } else {
+ $message = "❌ Ошибка при добавлении категории";
+ $message_class = "error";
+ }
+ } catch (PDOException $e) {
+ $message = "❌ Ошибка БД: " . $e->getMessage();
+ $message_class = "error";
+ }
+ }
+
+ if ($action === 'add_product') {
+ $name = $_POST['product_name'] ?? '';
+ $price = floatval($_POST['price'] ?? 0);
+ $category_id = intval($_POST['category_id'] ?? 1);
+ $slug = preg_replace('/[^a-z0-9]/i', '-', strtolower($name));
+
+ try {
+ $stmt = $db->prepare("INSERT INTO products (category_id, name, slug, price, stock_quantity, is_available) VALUES (?, ?, ?, ?, ?, ?)");
+ $result = $stmt->execute([$category_id, $name, $slug, $price, 10, true]);
+
+ if ($result) {
+ $message = "✅ Товар '$name' успешно добавлен!";
+ $message_class = "success";
+ } else {
+ $message = "❌ Ошибка при добавлении товара";
+ $message_class = "error";
+ }
+ } catch (PDOException $e) {
+ $message = "❌ Ошибка БД: " . $e->getMessage();
+ $message_class = "error";
+ }
+ }
+}
+
+// Получить категории для выпадающего списка
+$categories = [];
+try {
+ $categories = $db->query("SELECT * FROM categories")->fetchAll();
+} catch (Exception $e) {
+ $categories_error = "Ошибка получения категорий: " . $e->getMessage();
+}
+
+?>
+
+
+
+
+ Тестовое добавление
+
+
+
+ Тестовое добавление
+
+
+ = $message ?>
+
+
+
+ Добавить категорию
+
+
+ Добавить категорию
+
+
+
+ Добавить товар
+
+
+
+
+
+
+
+ = htmlspecialchars($cat['name']) ?>
+
+
+ Нет категорий (используется ID 1)
+
+
+
+ Добавить товар
+
+
+
+
+ Существующие категории (= count($categories) ?>):
+
+
+ ID: = $cat['category_id'] ?> - = htmlspecialchars($cat['name']) ?>
+
+
+
+
+ = $categories_error ?>
+
+
+
\ No newline at end of file
diff --git a/update_cart.php b/update_cart.php
new file mode 100644
index 0000000..8ab9f4a
--- /dev/null
+++ b/update_cart.php
@@ -0,0 +1,31 @@
+ false, 'message' => 'Требуется авторизация']);
+ exit();
+}
+
+if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_POST['product_id'])) {
+ $product_id = intval($_POST['product_id']);
+ $quantity = intval($_POST['quantity'] ?? 1);
+ $user_id = $_SESSION['user_id'] ?? 0;
+
+ $db = Database::getInstance()->getConnection();
+
+ try {
+ $stmt = $db->prepare("
+ UPDATE cart
+ SET quantity = ?, updated_at = CURRENT_TIMESTAMP
+ WHERE user_id = ? AND product_id = ?
+ ");
+ $stmt->execute([$quantity, $user_id, $product_id]);
+
+ echo json_encode(['success' => true]);
+
+ } catch (PDOException $e) {
+ echo json_encode(['success' => false, 'message' => 'Ошибка базы данных']);
+ }
+}
+?>
\ No newline at end of file
diff --git a/uploads/products/.gitkeep b/uploads/products/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/Гарантия.php b/Гарантия.php
new file mode 100644
index 0000000..4730164
--- /dev/null
+++ b/Гарантия.php
@@ -0,0 +1,206 @@
+
+
+
+
+
+ AETERNA - Гарантия
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
ГАРАНТИЙНЫЕ ОБЯЗАТЕЛЬСТВА
+
+
+
+
+
+
+
Мягкая мебель
+
18 месяцев
+
+
+
+
+
+
+
Корпусная мебель
+
24 месяца
+
+
+
+
+
+
+
Элементы освещения
+
12 месяцев
+
+
+
+
+
+
+
Фурнитура и механизмы
+
36 месяцев
+
+
+
+
+
+
Что покрывается гарантией
+
+
+
+
+
Производственные дефекты
+
Трещины, сколы, брак материалов
+
+
+
+
+
+
Неисправности механизмов
+
Трансформации, выдвижные системы
+
+
+
+
+
+
Проблемы с фурнитурой
+
Ручки, петли, направляющие
+
+
+
+
+
+
Дефекты покрытия
+
Отслоение шпона, краски, ламинации
+
+
+
+
+
+
+
Что не покрывается гарантией
+
+
+
+
+
Механические повреждения
+
Царапины, вмятины от неправильной эксплуатации
+
+
+
+
+
+
Следы износа
+
Естественное старение материалов
+
+
+
+
+
+
Неправильная сборка
+
Последствия самостоятельного ремонта
+
+
+
+
+
+
Внешние воздействия
+
Повреждения от жидкостей, солнечных лучей
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Доставка.php b/Доставка.php
new file mode 100644
index 0000000..ba659c5
--- /dev/null
+++ b/Доставка.php
@@ -0,0 +1,174 @@
+
+
+
+
+
+ AETERNA - Доставка и оплата
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
ДОСТАВКА И ОПЛАТА
+
+
+
+
+
+
+
Курьерская доставка
+
+
+ Бесплатная доставка:
+ при заказе от 30 000 ₽
+
+
+ В пределах МКАД:
+ 1 500 ₽
+
+
+ За МКАД:
+ 1 500 ₽ + 50 ₽/км
+
+
+ Время доставки:
+ с 9:00 до 21:00
+
+
+
+
+
+
+
+
+
Самовывоз из шоурума
+
+
+ Адрес:
+ г. Москва, ул. Дизайнерская, 15
+
+
+ Стоимость:
+ Бесплатно
+
+
+ Время получения:
+ в течение 2 часов после подтверждения
+
+
+ Парковка:
+ Бесплатная для клиентов
+
+
+
+
+
+
+
+
+
Доставка по России
+
+
+ Стоимость:
+ рассчитывается индивидуально
+
+
+ Сроки:
+ от 3 до 14 дней
+
+
+ Транспортные компании:
+ СДЭК, Boxberry, Деловые Линии
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/вход.php b/вход.php
new file mode 100644
index 0000000..bb358b8
--- /dev/null
+++ b/вход.php
@@ -0,0 +1,305 @@
+
+
+
+
+
+ AETERNA - Вход
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+// В файле вход.html добавьте:
+
+
+
+
diff --git a/оформление_заказа.php b/оформление_заказа.php
new file mode 100644
index 0000000..b3fe155
--- /dev/null
+++ b/оформление_заказа.php
@@ -0,0 +1,484 @@
+getConnection();
+
+// Получаем корзину пользователя
+$cart_items = [];
+$total_amount = 0;
+$total_quantity = 0;
+
+try {
+ $stmt = $db->prepare("
+ SELECT
+ c.cart_id,
+ c.product_id,
+ c.quantity,
+ p.name,
+ p.price,
+ p.image_url,
+ p.stock_quantity
+ FROM cart c
+ JOIN products p ON c.product_id = p.product_id
+ WHERE c.user_id = ? AND p.is_available = TRUE
+ ORDER BY c.created_at DESC
+ ");
+ $stmt->execute([$user_id]);
+ $cart_items = $stmt->fetchAll();
+
+ // Рассчитываем общую сумму
+ foreach ($cart_items as $item) {
+ $total_amount += $item['price'] * $item['quantity'];
+ $total_quantity += $item['quantity'];
+ }
+
+} catch (PDOException $e) {
+ $error = "Ошибка загрузки корзины: " . $e->getMessage();
+}
+?>
+
+
+
+
+
+ AETERNA - Оформление заказа
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Товары в корзине
+
+
+
+
+
Ваша корзина пуста
+
+
+
+
+
+
+
+
+
+
+
= htmlspecialchars($item['name']) ?>
+
= number_format($item['price'], 0, '', ' ') ?> ₽
+
+
+ -
+ = $item['quantity'] ?>
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ПРИМЕНИТЬ
+
+
+
+
+ Товары, = $total_quantity ?> шт.
+ = number_format($total_amount, 0, '', ' ') ?> ₽
+
+
+ Скидка
+ 0 ₽
+
+
+
+ Доставка
+ 2000 ₽
+
+
+
+ ИТОГО:
+ = number_format($total_amount + 2000, 0, '', ' ') ?> ₽
+
+
+
+ ОФОРМИТЬ ЗАКАЗ
+
+
+
+ Даю согласие на обработку персональных данных
+
+
+ Необходимо согласие на обработку персональных данных
+
+
+
+
УСЛУГИ
+
+ Доставка
+ 2000 ₽
+
+
+ Сборка
+ 1000 ₽
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/оформление_заказа_jquery.js b/оформление_заказа_jquery.js
new file mode 100644
index 0000000..74e8305
--- /dev/null
+++ b/оформление_заказа_jquery.js
@@ -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('' + message + '
');
+ }
+ }
+
+ // Валидация поля при потере фокуса с указанием конкретной ошибки
+ $('#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('Корзина пуста
');
+ }
+ });
+ });
+
+ // Обработчик применения промокода
+ $('.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();
+});
\ No newline at end of file
diff --git a/профиль.php b/профиль.php
new file mode 100644
index 0000000..ba3aab2
--- /dev/null
+++ b/профиль.php
@@ -0,0 +1,778 @@
+getConnection();
+
+ try {
+ // Проверяем, существует ли пользователь
+ $checkStmt = $db->prepare("SELECT user_id FROM users WHERE email = ?");
+ $checkStmt->execute([$email]);
+
+ if (!$checkStmt->fetch()) {
+ // Создаем пользователя
+ $password_hash = password_hash($password, PASSWORD_DEFAULT);
+ $is_admin = ($_GET['quick_register'] == 'admin');
+
+ $stmt = $db->prepare("
+ INSERT INTO users (email, password_hash, full_name, phone, city, is_admin)
+ VALUES (?, ?, ?, ?, ?, ?)
+ RETURNING user_id
+ ");
+
+ $stmt->execute([$email, $password_hash, $full_name, $phone, $city, $is_admin]);
+ $user_id = $stmt->fetchColumn();
+ } else {
+ // Получаем существующего пользователя
+ $stmt = $db->prepare("SELECT user_id, is_admin FROM users WHERE email = ?");
+ $stmt->execute([$email]);
+ $user = $stmt->fetch();
+ $user_id = $user['user_id'];
+ $is_admin = $user['is_admin'];
+ }
+
+ // Авторизуем
+ $_SESSION['user_id'] = $user_id;
+ $_SESSION['user_email'] = $email;
+ $_SESSION['full_name'] = $full_name;
+ $_SESSION['user_phone'] = $phone;
+ $_SESSION['user_city'] = $city;
+ $_SESSION['isLoggedIn'] = true;
+ $_SESSION['isAdmin'] = $is_admin;
+ $_SESSION['login_time'] = time();
+
+ header('Location: catalog.php');
+ exit();
+
+ } catch (Exception $e) {
+ $registration_errors[] = 'Ошибка быстрой регистрации: ' . $e->getMessage();
+ }
+}
+?>
+
+
+
+
+
+ AETERNA - Регистрация
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Ошибки регистрации:
+
+
+ = htmlspecialchars($error) ?>
+
+
+
+
+
+
+
+ = htmlspecialchars($registration_success) ?>
+
+
+
+
+ Для доступа к каталогу и оформления заказов необходимо зарегистрироваться
+
+
+
+
+
AETERNA
+
+
Присоединяйтесь к нам
+
+ Создайте аккаунт чтобы получить доступ ко всем функциям:
+
+
+ Доступ к каталогу товаров
+ Добавление товаров в корзину
+ Оформление заказов
+ История покупок
+ Специальные предложения
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Quick Admin
+
+
+ Quick User
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/профиль_jquery.js b/профиль_jquery.js
new file mode 100644
index 0000000..4aa7ff1
--- /dev/null
+++ b/профиль_jquery.js
@@ -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', 'Для восстановления пароля обратитесь к администратору');
+ });
+});
\ No newline at end of file
diff --git a/стили_оформления.less b/стили_оформления.less
new file mode 100644
index 0000000..fb07c54
--- /dev/null
+++ b/стили_оформления.less
@@ -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;
+}
\ No newline at end of file
diff --git a/страница_товара.php b/страница_товара.php
new file mode 100644
index 0000000..21058b5
--- /dev/null
+++ b/страница_товара.php
@@ -0,0 +1,237 @@
+
+
+
+
+
+ AETERNA - Стул Ender
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Стул ENDER
+
+
+ ★
+ 9/10
+ 54 отзывов
+
+
+
+
+
+
+
+
+
+
+ Стулья с закрытой обивкой созданы в стилистике практичности и комфортабельности,
+ придает помещению, эффектно выделяющимися средствами.
+
+
+
+
+
16 999 ₽
+
+ −
+ 1
+ +
+
+
+
+
+ В КОРЗИНУ
+ КУПИТЬ
+
+
+
+
+
+ Похожие
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/услуги.php b/услуги.php
new file mode 100644
index 0000000..9c05bc6
--- /dev/null
+++ b/услуги.php
@@ -0,0 +1,113 @@
+
+
+
+
+
+
+ AETERNA - Услуги
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
ДОСТАВКА
+
Стоимость доставки зависит от таких факторов, как: вес, адрес, удаленность от города, дата
+
+
+
СБОРКА
+
Стоимость сборки рассчитывается индивидуально, так как на цену влияет несколько факторов
+
+
+
+
+
ДИЗАЙН‑ПРОЕКТ
+
Предоставляем услугу по составлению дизайн‑проекта. Учитываем индивидуальные пожелания каждого клиента. Работаем с интерьерами различной сложности.
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file