Compare commits
10 Commits
3f257120fa
...
fix-auth-c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
07289608e5 | ||
|
|
f4f57bd153 | ||
|
|
fe288b3caf | ||
|
|
6a866dffe3 | ||
|
|
a7282f7363 | ||
|
|
8a93cf8657 | ||
|
|
474fe41d41 | ||
|
|
f597029525 | ||
|
|
b87450c12b | ||
|
|
29b9aaac50 |
122
init_db.php
@@ -1,122 +0,0 @@
|
||||
<?php
|
||||
require_once 'config/database.php';
|
||||
|
||||
$db = Database::getInstance()->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' создана/проверена<br>";
|
||||
} catch (PDOException $e) {
|
||||
echo "Ошибка создания таблицы '$table_name': " . $e->getMessage() . "<br>";
|
||||
}
|
||||
}
|
||||
|
||||
// Добавляем тестовые данные
|
||||
// Проверяем, есть ли уже категории
|
||||
$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 "Добавлены категории<br>";
|
||||
}
|
||||
|
||||
// Проверяем, есть ли уже товары
|
||||
$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 "Добавлены товары<br>";
|
||||
}
|
||||
|
||||
// Проверяем, есть ли администратор
|
||||
$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)<br>";
|
||||
}
|
||||
|
||||
echo "<h3>База данных успешно инициализирована!</h3>";
|
||||
echo "<a href='catalog.php'>Перейти в каталог</a>";
|
||||
?>
|
||||
8
.dockerignore
Normal file
@@ -0,0 +1,8 @@
|
||||
.git
|
||||
.gitignore
|
||||
.DS_Store
|
||||
node_modules
|
||||
*.log
|
||||
.env
|
||||
.env.local
|
||||
|
||||
10
Dockerfile
Normal file
@@ -0,0 +1,10 @@
|
||||
FROM php:8.2-apache
|
||||
|
||||
RUN apt-get update && apt-get install -y libpq-dev \
|
||||
&& docker-php-ext-install pdo pdo_pgsql \
|
||||
&& apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN a2enmod rewrite headers alias
|
||||
|
||||
WORKDIR /var/www/html
|
||||
|
||||
@@ -1,170 +0,0 @@
|
||||
<?php
|
||||
// admin_actions.php
|
||||
session_start();
|
||||
require_once 'config/database.php';
|
||||
|
||||
// Проверка прав администратора
|
||||
if (!isset($_SESSION['isAdmin']) || $_SESSION['isAdmin'] !== true) {
|
||||
header('Location: вход.php?error=admin_only');
|
||||
exit();
|
||||
}
|
||||
|
||||
$db = Database::getInstance()->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();
|
||||
?>
|
||||
@@ -1,924 +0,0 @@
|
||||
<?php
|
||||
// catalog_admin.php - единый файл каталога с админ-панелью
|
||||
session_start();
|
||||
|
||||
// Подключение к базе данных
|
||||
require_once 'config/database.php';
|
||||
|
||||
// Проверка прав администратора
|
||||
$is_admin = isset($_SESSION['isAdmin']) && $_SESSION['isAdmin'] === true;
|
||||
$action = $_GET['action'] ?? '';
|
||||
$product_id = $_GET['id'] ?? 0;
|
||||
$category_id = $_GET['category'] ?? '';
|
||||
|
||||
// Если не админ, перенаправляем
|
||||
if (!$is_admin) {
|
||||
header('Location: вход.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
try {
|
||||
$db = Database::getInstance()->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();
|
||||
}
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>AETERNA - Каталог</title>
|
||||
<link rel="stylesheet/less" type="text/css" href="style_for_cite.less">
|
||||
<script src="https://cdn.jsdelivr.net/npm/less"></script>
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
|
||||
<style>
|
||||
.admin-panel {
|
||||
background: #f8f9fa;
|
||||
padding: 20px;
|
||||
margin: 20px 0;
|
||||
border-radius: 8px;
|
||||
border-left: 4px solid #617365;
|
||||
}
|
||||
|
||||
.admin-btn {
|
||||
background: #617365;
|
||||
color: white;
|
||||
padding: 8px 15px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
margin: 5px;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.admin-btn:hover {
|
||||
background: #453227;
|
||||
}
|
||||
|
||||
.admin-form {
|
||||
background: white;
|
||||
padding: 30px;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 5px 15px rgba(0,0,0,0.1);
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.form-group input, .form-group textarea, .form-group select {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
font-family: "Anonymous Pro", monospace;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.form-group textarea {
|
||||
min-height: 100px;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
.form-group input[type="checkbox"] {
|
||||
width: auto;
|
||||
display: inline-block;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.alert {
|
||||
padding: 15px;
|
||||
margin-bottom: 20px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.alert-success {
|
||||
background: #d4edda;
|
||||
color: #155724;
|
||||
border: 1px solid #c3e6cb;
|
||||
}
|
||||
|
||||
.alert-danger {
|
||||
background: #f8d7da;
|
||||
color: #721c24;
|
||||
border: 1px solid #f5c6cb;
|
||||
}
|
||||
|
||||
.badge {
|
||||
padding: 3px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 11px;
|
||||
font-weight: bold;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.badge-warning {
|
||||
background: #ffc107;
|
||||
color: #212529;
|
||||
}
|
||||
|
||||
.badge-info {
|
||||
background: #17a2b8;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.badge-success {
|
||||
background: #28a745;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.badge-danger {
|
||||
background: #dc3545;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: 8px 15px;
|
||||
border-radius: 4px;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.btn-sm {
|
||||
padding: 5px 10px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.btn-warning {
|
||||
background: #ffc107;
|
||||
color: #212529;
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
background: #dc3545;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: #6c757d;
|
||||
color: white;
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.btn-success {
|
||||
background: #28a745;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.admin-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.admin-table th,
|
||||
.admin-table td {
|
||||
padding: 12px 15px;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.admin-table th {
|
||||
background: #f8f9fa;
|
||||
font-weight: bold;
|
||||
color: #453227;
|
||||
}
|
||||
|
||||
.admin-table tr:hover {
|
||||
background: #f8f9fa;
|
||||
}
|
||||
|
||||
.catalog-dropdown {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.catalog-dropdown__menu {
|
||||
display: none;
|
||||
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;
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.catalog-dropdown:hover .catalog-dropdown__menu {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.catalog-dropdown__menu ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.catalog-dropdown__menu li {
|
||||
padding: 8px 0;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.catalog-dropdown__menu li:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.catalog-dropdown__menu a {
|
||||
color: #333;
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.catalog-dropdown__menu a:hover {
|
||||
color: #453227;
|
||||
padding-left: 5px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header class="header">
|
||||
<div class="header__top">
|
||||
<div class="container header__top-content">
|
||||
<div class="logo">AETERNA</div>
|
||||
<div class="search-catalog">
|
||||
<div class="catalog-dropdown">
|
||||
Все категории <span>▼</span>
|
||||
<div class="catalog-dropdown__menu">
|
||||
<ul>
|
||||
<li><a href="catalog_admin.php">Все товары</a></li>
|
||||
<?php foreach ($categories as $cat): ?>
|
||||
<li><a href="catalog_admin.php?category=<?= $cat['category_id'] ?>">
|
||||
<?= htmlspecialchars($cat['name']) ?>
|
||||
</a></li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="search-box">
|
||||
<input type="text" placeholder="Поиск товаров">
|
||||
<span class="search-icon"><i class="fas fa-search"></i></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="header__icons--top">
|
||||
<a href="оформление_заказа.php" class="icon cart-icon">
|
||||
<i class="fas fa-shopping-cart"></i>
|
||||
<span class="cart-count">0</span>
|
||||
</a>
|
||||
<?php if ($is_admin): ?>
|
||||
<div class="user-profile-dropdown">
|
||||
<div class="user-profile-toggle">
|
||||
<div class="user-avatar">
|
||||
<?= strtoupper(substr($_SESSION['user_email'] ?? 'A', 0, 1)) ?>
|
||||
</div>
|
||||
<div class="user-info">
|
||||
<div class="user-email"><?= htmlspecialchars($_SESSION['user_email'] ?? '') ?></div>
|
||||
<div class="user-status admin">Админ</div>
|
||||
</div>
|
||||
<i class="fas fa-chevron-down dropdown-arrow"></i>
|
||||
</div>
|
||||
<div class="user-profile-menu">
|
||||
<div class="user-profile-header">
|
||||
<div class="user-profile-name">
|
||||
<i class="fas fa-user-shield"></i>
|
||||
<?= htmlspecialchars($_SESSION['full_name'] ?? $_SESSION['user_email']) ?>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="user-profile-links">
|
||||
<li><a href="logout.php" class="logout-link">
|
||||
<i class="fas fa-sign-out-alt"></i>
|
||||
<span>Выйти</span>
|
||||
</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="header__bottom">
|
||||
<div class="container header__bottom-content">
|
||||
<div class="catalog-menu">
|
||||
<a href="catalog_admin.php" class="catalog-link active-catalog">
|
||||
<div class="catalog-icon">
|
||||
<span class="line"></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"></span>
|
||||
</div>
|
||||
<span class="catalog-lines">☰</span>
|
||||
Каталог
|
||||
</a>
|
||||
</div>
|
||||
<nav class="nav">
|
||||
<ul class="nav-list">
|
||||
<li><a href="cite_mebel.php">Главная</a></li>
|
||||
<li><a href="услуги.php">Услуги</a></li>
|
||||
<li><a href="Доставка.php">Доставка и оплата</a></li>
|
||||
<li><a href="Гарантия.php">Гарантия</a></li>
|
||||
<li><a href="#footer">Контакты</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
<div class="header-phone">+7(912)999-12-23</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="catalog-main">
|
||||
<div class="container">
|
||||
<div class="breadcrumbs">
|
||||
<a href="cite_mebel.php">Главная</a> • <span class="current-page">Каталог</span>
|
||||
</div>
|
||||
|
||||
<?php if ($is_admin): ?>
|
||||
<!-- Панель администратора -->
|
||||
<div class="admin-panel">
|
||||
<h3>Панель управления каталогом</h3>
|
||||
<a href="?action=add_product" class="admin-btn">
|
||||
<i class="fas fa-plus"></i> Добавить товар
|
||||
</a>
|
||||
<a href="?action=add_category" class="admin-btn">
|
||||
<i class="fas fa-folder-plus"></i> Добавить категорию
|
||||
</a>
|
||||
<a href="?action=categories" class="admin-btn" style="background: #ffc107; color: #212529;">
|
||||
<i class="fas fa-tags"></i> Управление категориями
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<?php if ($message): ?>
|
||||
<div class="alert alert-success">
|
||||
<i class="fas fa-check-circle"></i> <?= htmlspecialchars($message) ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($error): ?>
|
||||
<div class="alert alert-danger">
|
||||
<i class="fas fa-exclamation-circle"></i> <?= htmlspecialchars($error) ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($action === 'add_category' || $action === 'edit_category'): ?>
|
||||
<!-- Форма добавления/редактирования категории -->
|
||||
<div class="admin-form" id="categoryForm">
|
||||
<h3><?= ($action === 'add_category') ? 'Добавление категории' : 'Редактирование категории' ?></h3>
|
||||
|
||||
<?php if (isset($error) && !empty($error)): ?>
|
||||
<div class="alert alert-danger">
|
||||
<i class="fas fa-exclamation-circle"></i> <?= htmlspecialchars($error) ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form method="POST" action="catalog_admin.php" id="categoryFormElement">
|
||||
<input type="hidden" name="action" value="<?= $action ?>">
|
||||
<?php if ($action === 'edit_category' && isset($current_category)): ?>
|
||||
<input type="hidden" name="category_id" value="<?= $current_category['category_id'] ?>">
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="category_name">Название категории: *</label>
|
||||
<input type="text" name="name" id="category_name" required
|
||||
value="<?= htmlspecialchars($current_category['name'] ?? '') ?>"
|
||||
placeholder="Введите название категории">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="parent_category">Родительская категория:</label>
|
||||
<select name="parent_id" id="parent_category">
|
||||
<option value="">Без родительской категории</option>
|
||||
<?php foreach ($categories as $cat): ?>
|
||||
<?php if (!isset($current_category['category_id']) || $cat['category_id'] != $current_category['category_id']): ?>
|
||||
<option value="<?= $cat['category_id'] ?>"
|
||||
<?= (isset($current_category['parent_id']) && $current_category['parent_id'] == $cat['category_id']) ? 'selected' : '' ?>>
|
||||
<?= htmlspecialchars($cat['name']) ?>
|
||||
</option>
|
||||
<?php endif; ?>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="category_description">Описание:</label>
|
||||
<textarea name="description" id="category_description" rows="3"
|
||||
placeholder="Описание категории (необязательно)"><?= htmlspecialchars($current_category['description'] ?? '') ?></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="sort_order">Порядок сортировки:</label>
|
||||
<input type="number" name="sort_order" id="sort_order"
|
||||
value="<?= $current_category['sort_order'] ?? 0 ?>"
|
||||
min="0" max="100">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>
|
||||
<input type="checkbox" name="is_active" value="1"
|
||||
<?= (!isset($current_category['is_active']) || $current_category['is_active']) ? 'checked' : '' ?>>
|
||||
Активна
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="admin-btn">
|
||||
<i class="fas fa-save"></i> Сохранить
|
||||
</button>
|
||||
<a href="catalog_admin.php?action=categories" class="admin-btn" style="background: #6c757d;">
|
||||
<i class="fas fa-times"></i> Отмена
|
||||
</a>
|
||||
</form>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="catalog-wrapper">
|
||||
<aside class="catalog-sidebar">
|
||||
<div class="filter-group">
|
||||
<h4 class="filter-title">КАТЕГОРИИ</h4>
|
||||
<ul class="filter-list">
|
||||
<li><a href="catalog_admin.php" class="<?= (!$category_id) ? 'active-category' : '' ?>">Все товары</a></li>
|
||||
<?php foreach ($categories as $cat): ?>
|
||||
<li>
|
||||
<a href="catalog_admin.php?category=<?= $cat['category_id'] ?>"
|
||||
class="<?= ($category_id == $cat['category_id']) ? 'active-category' : '' ?>">
|
||||
<?= htmlspecialchars($cat['name']) ?>
|
||||
</a>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="filter-group">
|
||||
<h4 class="filter-title">ЦЕНА</h4>
|
||||
<div class="price-range">
|
||||
<div class="range-slider">
|
||||
<input type="range" min="1000" max="100000" value="50000" step="1000">
|
||||
</div>
|
||||
<div class="price-display">До 50 000 ₽</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="btn primary-btn filter-apply-btn">ПРИМЕНИТЬ</button>
|
||||
</aside>
|
||||
|
||||
<section class="catalog-products">
|
||||
<?php if ($action === 'categories'): ?>
|
||||
<!-- Раздел управления категориями -->
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">
|
||||
<h2>Управление категориями</h2>
|
||||
<a href="?action=add_category" class="btn btn-success">
|
||||
<i class="fas fa-plus"></i> Добавить категорию
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<table class="admin-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Название</th>
|
||||
<th>Родительская</th>
|
||||
<th>Товаров</th>
|
||||
<th>Порядок</th>
|
||||
<th>Статус</th>
|
||||
<th>Действия</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($categories as $cat): ?>
|
||||
<tr>
|
||||
<td><?= $cat['category_id'] ?></td>
|
||||
<td>
|
||||
<strong><?= htmlspecialchars($cat['name']) ?></strong>
|
||||
<br><small style="color: #666;"><?= htmlspecialchars($cat['slug']) ?></small>
|
||||
</td>
|
||||
<td><?= htmlspecialchars($cat['parent_name'] ?? '—') ?></td>
|
||||
<td>
|
||||
<span class="badge badge-info"><?= $cat['product_count'] ?> товаров</span>
|
||||
</td>
|
||||
<td><?= $cat['sort_order'] ?></td>
|
||||
<td>
|
||||
<?php if ($cat['is_active']): ?>
|
||||
<span class="badge badge-success">Активна</span>
|
||||
<?php else: ?>
|
||||
<span class="badge badge-warning">Неактивна</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td>
|
||||
<a href="?action=edit_category&id=<?= $cat['category_id'] ?>"
|
||||
class="btn btn-sm btn-warning" title="Редактировать">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
|
||||
<form method="POST" action="catalog_admin.php" style="display: inline-block;"
|
||||
onsubmit="return confirm('Вы уверены, что хотите удалить категорию?');">
|
||||
<input type="hidden" name="action" value="delete_category">
|
||||
<input type="hidden" name="category_id" value="<?= $cat['category_id'] ?>">
|
||||
<?php if ($cat['product_count'] == 0): ?>
|
||||
<button type="submit" class="btn btn-sm btn-danger" title="Удалить">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
<?php else: ?>
|
||||
<button type="button" class="btn btn-sm btn-secondary"
|
||||
title="Нельзя удалить категорию с товарами" disabled>
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<?php else: ?>
|
||||
<!-- Основной каталог товаров -->
|
||||
<div class="products-container">
|
||||
<?php foreach ($products as $product): ?>
|
||||
<div class="product-card <?= getCardSizeClass($product) ?>">
|
||||
<?php if ($is_admin): ?>
|
||||
<div class="admin-actions">
|
||||
<a href="?action=edit&id=<?= $product['product_id'] ?>"
|
||||
class="admin-btn" style="background: #28a745;">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
<a href="?action=delete&id=<?= $product['product_id'] ?>"
|
||||
class="admin-btn" style="background: #dc3545;"
|
||||
onclick="return confirm('Сделать товар недоступным?')">
|
||||
<i class="fas fa-trash"></i>
|
||||
</a>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="product-image-container">
|
||||
<img src="<?= htmlspecialchars($product['image_url'] ?: 'img1/default.jpg') ?>"
|
||||
alt="<?= htmlspecialchars($product['name']) ?>"
|
||||
class="product-img">
|
||||
|
||||
<?php if ($product['old_price'] && $product['old_price'] > $product['price']): ?>
|
||||
<span class="product-discount">
|
||||
-<?= round(($product['old_price'] - $product['price']) / $product['old_price'] * 100) ?>%
|
||||
</span>
|
||||
<?php endif; ?>
|
||||
|
||||
<i class="fas fa-shopping-cart product-wishlist-icon"
|
||||
onclick="addToCart(<?= $product['product_id'] ?>)"></i>
|
||||
</div>
|
||||
|
||||
<div class="product-info" style="padding: 15px;">
|
||||
<div class="product-name"><?= htmlspecialchars($product['name']) ?></div>
|
||||
<div class="product-details">
|
||||
<?= htmlspecialchars(mb_substr($product['description'], 0, 100)) ?>...
|
||||
</div>
|
||||
<div class="product-price" style="margin-top: 10px;">
|
||||
<?php if ($product['old_price'] && $product['old_price'] > $product['price']): ?>
|
||||
<span style="text-decoration: line-through; color: #999; font-size: 14px;">
|
||||
<?= number_format($product['old_price'], 0, '', ' ') ?> ₽
|
||||
</span><br>
|
||||
<?php endif; ?>
|
||||
<?= number_format($product['price'], 0, '', ' ') ?> ₽
|
||||
</div>
|
||||
|
||||
<div class="product-stock" style="font-size: 12px; color: #28a745; margin-top: 5px;">
|
||||
В наличии: <?= $product['stock_quantity'] ?> шт.
|
||||
</div>
|
||||
|
||||
<button onclick="addToCart(<?= $product['product_id'] ?>)"
|
||||
class="btn btn-primary" style="width: 100%; margin-top: 10px;">
|
||||
<i class="fas fa-shopping-cart"></i> В корзину
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer class="footer" id="footer">
|
||||
<div class="container footer__content">
|
||||
<div class="footer__col footer--logo">
|
||||
<div class="logo">AETERNA</div>
|
||||
</div>
|
||||
<div class="footer__col">
|
||||
<h5>ПОКУПАТЕЛЮ</h5>
|
||||
<ul>
|
||||
<li><a href="catalog_admin.php">Каталог</a></li>
|
||||
<li><a href="услуги.php">Услуги</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer__col">
|
||||
<h5>ПОМОЩЬ</h5>
|
||||
<ul>
|
||||
<li><a href="Доставка.php">Доставка и оплата</a></li>
|
||||
<li><a href="Гарантия.php">Гарантия и возврат</a></li>
|
||||
<li><a href="cite_mebel.php#faq">Ответы на вопросы</a></li>
|
||||
<li><a href="#footer">Контакты</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer__col">
|
||||
<h5>КОНТАКТЫ</h5>
|
||||
<p>aeterna@mail.ru</p>
|
||||
<p>+7(912)999-12-23</p>
|
||||
<div class="social-icons">
|
||||
<span class="icon"><i class="fab fa-telegram"></i></span>
|
||||
<span class="icon"><i class="fab fa-instagram"></i></span>
|
||||
<span class="icon"><i class="fab fa-vk"></i></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer__col">
|
||||
<h5>ПРИНИМАЕМ К ОПЛАТЕ</h5>
|
||||
<div class="payment-icons">
|
||||
<span class="pay-icon"><i class="fab fa-cc-visa"></i></span>
|
||||
<span class="pay-icon"><i class="fab fa-cc-mastercard"></i></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="copyright">
|
||||
<p>© 2025 AETERNA. Все права защищены.</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
// Функция для показа формы добавления товара
|
||||
function showAddForm() {
|
||||
window.location.href = 'catalog_admin.php?action=add';
|
||||
}
|
||||
|
||||
// Функция для показа формы добавления категории
|
||||
function showAddCategoryForm() {
|
||||
window.location.href = 'catalog_admin.php?action=add_category';
|
||||
}
|
||||
|
||||
// Функция для редактирования категории
|
||||
function editCategory(categoryId) {
|
||||
window.location.href = 'catalog_admin.php?action=edit_category&id=' + categoryId;
|
||||
}
|
||||
|
||||
// Функция для скрытия формы
|
||||
function hideForm() {
|
||||
window.location.href = 'catalog_admin.php?action=categories';
|
||||
}
|
||||
|
||||
// Функция добавления в корзину
|
||||
function addToCart(productId) {
|
||||
$.ajax({
|
||||
url: "cart_handler.php",
|
||||
method: "POST",
|
||||
data: { action: "add", product_id: productId, quantity: 1 },
|
||||
success: function(response) {
|
||||
try {
|
||||
var result = JSON.parse(response);
|
||||
if (result.success) {
|
||||
alert("Товар добавлен в корзину!");
|
||||
// Обновляем счетчик корзины
|
||||
if ($(".cart-count").length) {
|
||||
var current = parseInt($(".cart-count").text()) || 0;
|
||||
$(".cart-count").text(current + 1);
|
||||
}
|
||||
} else {
|
||||
alert("Ошибка: " + result.message);
|
||||
}
|
||||
} catch(e) {
|
||||
alert("Товар добавлен в корзину!");
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
alert("Товар добавлен в корзину!");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Обработка формы категории с подтверждением
|
||||
$(document).ready(function() {
|
||||
$('#categoryFormElement').on('submit', function(e) {
|
||||
const action = $(this).find('input[name="action"]').val();
|
||||
const categoryName = $(this).find('input[name="name"]').val();
|
||||
|
||||
if (!categoryName.trim()) {
|
||||
e.preventDefault();
|
||||
alert('Пожалуйста, введите название категории');
|
||||
return false;
|
||||
}
|
||||
|
||||
const message = action === 'add_category'
|
||||
? 'Добавить новую категорию "' + categoryName + '"?'
|
||||
: 'Сохранить изменения в категории?';
|
||||
|
||||
if (!confirm(message)) {
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// Подтверждение удаления категории
|
||||
$('form[action="catalog_admin.php"]').on('submit', function(e) {
|
||||
const action = $(this).find('input[name="action"]').val();
|
||||
if (action === 'delete_category') {
|
||||
const categoryId = $(this).find('input[name="category_id"]').val();
|
||||
if (!confirm('Вы уверены, что хотите удалить эту категорию?')) {
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
<?php if ($is_admin): ?>
|
||||
$('.product-card').hover(
|
||||
function() {
|
||||
$(this).find('.admin-actions').show();
|
||||
},
|
||||
function() {
|
||||
$(this).find('.admin-actions').hide();
|
||||
}
|
||||
);
|
||||
<?php endif; ?>
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
<?php
|
||||
// Вспомогательная функция для определения размера карточки
|
||||
function getCardSizeClass($product) {
|
||||
$sizes = ['small', 'large', 'wide', 'tall', 'small1', 'wide2', 'wide3', 'wide2_1', 'full-width'];
|
||||
$index = $product['product_id'] % count($sizes);
|
||||
return $sizes[$index];
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
<?php
|
||||
// catalog_admin_action.php
|
||||
session_start();
|
||||
require_once 'config/database.php';
|
||||
|
||||
if (!isset($_SESSION['is_admin']) || $_SESSION['is_admin'] !== true) {
|
||||
header('Location: вход.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
$db = Database::getInstance()->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();
|
||||
?>
|
||||
@@ -1,17 +0,0 @@
|
||||
<?php
|
||||
// check_admin.php
|
||||
session_start();
|
||||
|
||||
function checkAdmin() {
|
||||
if (!isset($_SESSION['isAdmin']) || $_SESSION['isAdmin'] !== true) {
|
||||
header('Location: вход.php?error=admin_required');
|
||||
exit();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Возвращает true если пользователь администратор
|
||||
function isAdmin() {
|
||||
return isset($_SESSION['isAdmin']) && $_SESSION['isAdmin'] === true;
|
||||
}
|
||||
?>
|
||||
@@ -1,21 +0,0 @@
|
||||
<?php
|
||||
// check_auth_status.php
|
||||
session_start();
|
||||
|
||||
$response = [
|
||||
'loggedIn' => 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);
|
||||
?>
|
||||
@@ -1,51 +0,0 @@
|
||||
<?php
|
||||
require_once 'config/database.php';
|
||||
|
||||
$db = Database::getInstance()->getConnection();
|
||||
|
||||
echo "<h2>Проверка категорий в базе данных</h2>";
|
||||
|
||||
try {
|
||||
$stmt = $db->query("SELECT category_id, name, slug, parent_id FROM categories ORDER BY category_id");
|
||||
$categories = $stmt->fetchAll();
|
||||
|
||||
if (empty($categories)) {
|
||||
echo "<p style='color: red;'>Категорий нет! Нужно сначала добавить категории.</p>";
|
||||
|
||||
// Добавим тестовые категории
|
||||
$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 "<p style='color: green;'>Добавлены тестовые категории</p>";
|
||||
|
||||
// Снова проверим
|
||||
$stmt = $db->query("SELECT category_id, name, slug, parent_id FROM categories ORDER BY category_id");
|
||||
$categories = $stmt->fetchAll();
|
||||
}
|
||||
|
||||
echo "<table border='1' cellpadding='5'>";
|
||||
echo "<tr><th>ID</th><th>Название</th><th>Slug</th><th>Родитель</th></tr>";
|
||||
|
||||
foreach ($categories as $category) {
|
||||
echo "<tr>";
|
||||
echo "<td>" . $category['category_id'] . "</td>";
|
||||
echo "<td>" . htmlspecialchars($category['name']) . "</td>";
|
||||
echo "<td>" . htmlspecialchars($category['slug']) . "</td>";
|
||||
echo "<td>" . ($category['parent_id'] ?: '-') . "</td>";
|
||||
echo "</tr>";
|
||||
}
|
||||
|
||||
echo "</table>";
|
||||
|
||||
} catch (PDOException $e) {
|
||||
echo "Ошибка: " . $e->getMessage();
|
||||
}
|
||||
?>
|
||||
@@ -1,114 +0,0 @@
|
||||
// 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);
|
||||
}
|
||||
});
|
||||
55
debug_db.php
@@ -1,55 +0,0 @@
|
||||
<?php
|
||||
// debug_db.php
|
||||
require_once 'config/database.php';
|
||||
|
||||
$db = Database::getInstance()->getConnection();
|
||||
|
||||
echo "<h2>Проверка базы данных:</h2>";
|
||||
|
||||
// Проверка таблиц
|
||||
$tables = ['users', 'categories', 'products', 'orders', 'order_items', 'cart'];
|
||||
foreach ($tables as $table) {
|
||||
try {
|
||||
$result = $db->query("SELECT COUNT(*) FROM $table")->fetchColumn();
|
||||
echo "✅ Таблица '$table': $result записей<br>";
|
||||
} catch (Exception $e) {
|
||||
echo "❌ Таблица '$table': НЕ СУЩЕСТВУЕТ<br>";
|
||||
}
|
||||
}
|
||||
|
||||
echo "<h2>Содержимое таблиц:</h2>";
|
||||
|
||||
// Показать категории
|
||||
echo "<h3>Категории:</h3>";
|
||||
try {
|
||||
$categories = $db->query("SELECT * FROM categories")->fetchAll();
|
||||
if (empty($categories)) {
|
||||
echo "Категорий нет!<br>";
|
||||
} else {
|
||||
echo "<table border='1'><tr><th>ID</th><th>Название</th><th>Slug</th><th>Родитель</th></tr>";
|
||||
foreach ($categories as $cat) {
|
||||
echo "<tr><td>{$cat['category_id']}</td><td>{$cat['name']}</td><td>{$cat['slug']}</td><td>{$cat['parent_id']}</td></tr>";
|
||||
}
|
||||
echo "</table>";
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
echo "Ошибка: " . $e->getMessage();
|
||||
}
|
||||
|
||||
// Показать товары
|
||||
echo "<h3>Товары:</h3>";
|
||||
try {
|
||||
$products = $db->query("SELECT * FROM products")->fetchAll();
|
||||
if (empty($products)) {
|
||||
echo "Товаров нет!<br>";
|
||||
} else {
|
||||
echo "<table border='1'><tr><th>ID</th><th>Название</th><th>Цена</th><th>Категория</th><th>Статус</th></tr>";
|
||||
foreach ($products as $product) {
|
||||
echo "<tr><td>{$product['product_id']}</td><td>{$product['name']}</td><td>{$product['price']}</td><td>{$product['category_id']}</td><td>" . ($product['is_available'] ? 'Активен' : 'Неактивен') . "</td></tr>";
|
||||
}
|
||||
echo "</table>";
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
echo "Ошибка: " . $e->getMessage();
|
||||
}
|
||||
?>
|
||||
27
docker-compose.yml
Normal file
@@ -0,0 +1,27 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
apache:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
container_name: cite_practica_apache
|
||||
ports:
|
||||
- "80:80"
|
||||
volumes:
|
||||
- ./public:/var/www/html/cite_practica:rw
|
||||
- ./docker/apache/vhosts.conf:/etc/apache2/sites-available/000-default.conf:ro
|
||||
environment:
|
||||
- APACHE_DOCUMENT_ROOT=/var/www/html/cite_practica
|
||||
command: >
|
||||
bash -c "
|
||||
echo '127.0.0.1 admin' >> /etc/hosts &&
|
||||
apache2-foreground
|
||||
"
|
||||
networks:
|
||||
- cite_practica_network
|
||||
restart: unless-stopped
|
||||
|
||||
networks:
|
||||
cite_practica_network:
|
||||
driver: bridge
|
||||
12
docker/apache/entrypoint.sh
Executable file
@@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
a2enmod rewrite
|
||||
a2enmod headers
|
||||
|
||||
echo "127.0.0.1 admin" >> /etc/hosts
|
||||
|
||||
a2ensite 000-default
|
||||
|
||||
exec apache2-foreground
|
||||
|
||||
26
docker/apache/vhosts.conf
Normal file
@@ -0,0 +1,26 @@
|
||||
<VirtualHost *:80>
|
||||
ServerName admin
|
||||
ServerAlias localhost
|
||||
DocumentRoot /var/www/html
|
||||
Alias /cite_practica /var/www/html/cite_practica
|
||||
|
||||
<Directory /var/www/html>
|
||||
Options Indexes FollowSymLinks
|
||||
AllowOverride All
|
||||
Require all granted
|
||||
</Directory>
|
||||
|
||||
<Directory /var/www/html/cite_practica>
|
||||
Options Indexes FollowSymLinks
|
||||
AllowOverride All
|
||||
Require all granted
|
||||
DirectoryIndex cite_mebel.php index.php index.html
|
||||
</Directory>
|
||||
|
||||
<FilesMatch \.php$>
|
||||
SetHandler application/x-httpd-php
|
||||
</FilesMatch>
|
||||
|
||||
ErrorLog ${APACHE_LOG_DIR}/cite_practica_error.log
|
||||
CustomLog ${APACHE_LOG_DIR}/cite_practica_access.log combined
|
||||
</VirtualHost>
|
||||
@@ -1,69 +0,0 @@
|
||||
<?php
|
||||
// fix_categories.php
|
||||
require_once 'config/database.php';
|
||||
|
||||
$db = Database::getInstance()->getConnection();
|
||||
|
||||
echo "<h2>Исправление проблем с категориями</h2>";
|
||||
|
||||
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 "<p>Добавляем основные категории...</p>";
|
||||
$db->exec("
|
||||
INSERT INTO categories (name, slug, description, is_active) VALUES
|
||||
('Диваны', 'divany', 'Мягкая мебель для гостиной', TRUE),
|
||||
('Кресла', 'kresla', 'Кресла для гостиной и офиса', TRUE),
|
||||
('Кровати', 'krovati', 'Мебель для спальни', TRUE),
|
||||
('Столы', 'stoly', 'Обеденные и рабочие столы', TRUE),
|
||||
('Стулья', 'stulya', 'Стулья для кухни и офиса', TRUE)
|
||||
");
|
||||
echo "<p style='color: green;'>✓ Категории добавлены</p>";
|
||||
}
|
||||
|
||||
// 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 "<p>Исправляем товары с некорректными категориями ($badProducts шт)...</p>";
|
||||
|
||||
// Получаем первую категорию
|
||||
$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 "<p style='color: green;'>✓ Товары исправлены (category_id установлен в $firstCat)</p>";
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Показываем текущее состояние
|
||||
echo "<h3>Текущие категории:</h3>";
|
||||
$cats = $db->query("SELECT category_id, name FROM categories ORDER BY category_id")->fetchAll();
|
||||
|
||||
echo "<table border='1' cellpadding='5'>";
|
||||
echo "<tr><th>ID</th><th>Название</th></tr>";
|
||||
foreach ($cats as $cat) {
|
||||
echo "<tr><td>{$cat['category_id']}</td><td>{$cat['name']}</td></tr>";
|
||||
}
|
||||
echo "</table>";
|
||||
|
||||
echo "<p style='color: green; margin-top: 20px;'>✓ База данных исправлена!</p>";
|
||||
|
||||
} catch (PDOException $e) {
|
||||
echo "<p style='color: red;'>Ошибка: " . $e->getMessage() . "</p>";
|
||||
}
|
||||
?>
|
||||
@@ -1,89 +0,0 @@
|
||||
<?php
|
||||
// fix_database.php
|
||||
require_once 'config/database.php';
|
||||
|
||||
$db = Database::getInstance()->getConnection();
|
||||
|
||||
echo "<h2>Исправление проблем с базой данных</h2>";
|
||||
|
||||
try {
|
||||
// 1. Проверяем есть ли категории
|
||||
$stmt = $db->query("SELECT COUNT(*) FROM categories");
|
||||
$cat_count = $stmt->fetchColumn();
|
||||
|
||||
if ($cat_count == 0) {
|
||||
echo "<p>Добавляем тестовые категории...</p>";
|
||||
$db->exec("
|
||||
INSERT INTO categories (name, slug, description) VALUES
|
||||
('Диваны', 'divany', 'Мягкая мебель для гостиной'),
|
||||
('Кресла', 'kresla', 'Кресла для гостиной и офиса'),
|
||||
('Кровати', 'krovati', 'Мебель для спальни')
|
||||
");
|
||||
echo "<p style='color: green;'>✓ Категории добавлены</p>";
|
||||
}
|
||||
|
||||
// 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 "<p>Исправляем товары с некорректными категориями ($bad_count шт)...</p>";
|
||||
|
||||
// Устанавливаем первую доступную категорию
|
||||
$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 "<p style='color: green;'>✓ Товары исправлены (установлена категория ID: $first_cat)</p>";
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Показываем текущее состояние
|
||||
echo "<h3>Текущее состояние:</h3>";
|
||||
|
||||
// Категории
|
||||
$stmt = $db->query("SELECT category_id, name FROM categories ORDER BY category_id");
|
||||
echo "<p><strong>Категории:</strong></p><ul>";
|
||||
while ($row = $stmt->fetch()) {
|
||||
echo "<li>ID: {$row['category_id']} - {$row['name']}</li>";
|
||||
}
|
||||
echo "</ul>";
|
||||
|
||||
// Товары
|
||||
$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 "<p><strong>Товары:</strong></p>";
|
||||
echo "<table border='1' cellpadding='5'>";
|
||||
echo "<tr><th>ID</th><th>Название</th><th>Категория ID</th><th>Категория</th></tr>";
|
||||
|
||||
while ($row = $stmt->fetch()) {
|
||||
echo "<tr>";
|
||||
echo "<td>{$row['product_id']}</td>";
|
||||
echo "<td>" . htmlspecialchars($row['name']) . "</td>";
|
||||
echo "<td>" . ($row['category_id'] ?: 'NULL') . "</td>";
|
||||
echo "<td>" . ($row['cat_name'] ?: 'Без категории') . "</td>";
|
||||
echo "</tr>";
|
||||
}
|
||||
|
||||
echo "</table>";
|
||||
|
||||
echo "<p style='color: green; margin-top: 20px;'>✓ База данных исправлена!</p>";
|
||||
|
||||
} catch (PDOException $e) {
|
||||
echo "<p style='color: red;'>Ошибка: " . $e->getMessage() . "</p>";
|
||||
}
|
||||
?>
|
||||
@@ -1,142 +0,0 @@
|
||||
<?php
|
||||
if (session_status() == PHP_SESSION_NONE) {
|
||||
session_start();
|
||||
}
|
||||
$isLoggedIn = isset($_SESSION['isLoggedIn']) && $_SESSION['isLoggedIn'] === true;
|
||||
$isAdmin = isset($_SESSION['isAdmin']) && $_SESSION['isAdmin'] === true;
|
||||
$userEmail = $_SESSION['user_email'] ?? '';
|
||||
$fullName = $_SESSION['full_name'] ?? $userEmail;
|
||||
?>
|
||||
<!-- Стандартный header для всех страниц -->
|
||||
<header class="header">
|
||||
<div class="header__top">
|
||||
<div class="container header__top-content">
|
||||
<div class="logo">AETERNA</div>
|
||||
|
||||
<div class="search-catalog">
|
||||
<div class="catalog-dropdown">
|
||||
Все категории <span>▼</span>
|
||||
<div class="catalog-dropdown__menu">
|
||||
<ul>
|
||||
<li><a href="catalog.php">Все товары</a></li>
|
||||
<li><a href="catalog.php?category=1">Мягкая мебель</a></li>
|
||||
<li><a href="catalog.php?category=2">Диваны</a></li>
|
||||
<li><a href="catalog.php?category=3">Кресла</a></li>
|
||||
<li><a href="catalog.php?category=4">Спальня</a></li>
|
||||
<li><a href="catalog.php?category=5">Кровати</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="search-box">
|
||||
<input type="text" placeholder="Поиск товаров" id="searchInput">
|
||||
<span class="search-icon"><i class="fas fa-search"></i></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="header__icons--top">
|
||||
<?php if ($isLoggedIn): ?>
|
||||
<!-- Иконка корзины -->
|
||||
<a href="оформление_заказа.php" class="icon cart-icon">
|
||||
<i class="fas fa-shopping-cart"></i>
|
||||
<span class="cart-count">0</span>
|
||||
</a>
|
||||
|
||||
<!-- Блок профиля -->
|
||||
<div class="user-profile-dropdown">
|
||||
<div class="user-profile-toggle">
|
||||
<div class="user-avatar">
|
||||
<?= !empty($userEmail) ? strtoupper(substr($userEmail, 0, 1)) : 'U' ?>
|
||||
</div>
|
||||
<div class="user-info">
|
||||
<div class="user-email"><?= htmlspecialchars($userEmail) ?></div>
|
||||
<div class="user-status <?= $isAdmin ? 'admin' : 'user' ?>">
|
||||
<?= $isAdmin ? 'Админ' : 'Пользователь' ?>
|
||||
</div>
|
||||
</div>
|
||||
<i class="fas fa-chevron-down dropdown-arrow"></i>
|
||||
</div>
|
||||
|
||||
<div class="user-profile-menu">
|
||||
<div class="user-profile-header">
|
||||
<div class="user-profile-name">
|
||||
<i class="fas fa-user"></i> <?= htmlspecialchars($fullName) ?>
|
||||
</div>
|
||||
<div class="user-profile-details">
|
||||
<small><i class="far fa-envelope"></i> <?= htmlspecialchars($userEmail) ?></small>
|
||||
<?php if (isset($_SESSION['login_time'])): ?>
|
||||
<small><i class="far fa-clock"></i> Вошел: <?= date('d.m.Y H:i', $_SESSION['login_time']) ?></small>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul class="user-profile-links">
|
||||
<li>
|
||||
<a href="профиль.php">
|
||||
<i class="fas fa-user-cog"></i>
|
||||
<span>Настройки профиля</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="оформление_заказа.php">
|
||||
<i class="fas fa-shopping-bag"></i>
|
||||
<span>Мои заказы</span>
|
||||
</a>
|
||||
</li>
|
||||
<?php if ($isAdmin): ?>
|
||||
<li>
|
||||
<a href="admin_panel.php">
|
||||
<i class="fas fa-user-shield"></i>
|
||||
<span>Панель администратора</span>
|
||||
</a>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
<li class="logout-item">
|
||||
<a href="logout.php" class="logout-link">
|
||||
<i class="fas fa-sign-out-alt"></i>
|
||||
<span>Выйти из аккаунта</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php else: ?>
|
||||
<!-- Если не авторизован -->
|
||||
<a href="вход.php" class="icon">
|
||||
<i class="far fa-user"></i>
|
||||
</a>
|
||||
<a href="вход.php" style="font-size: 12px; color: #666; margin-left: 5px;">
|
||||
Войти
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="header__bottom">
|
||||
<div class="container header__bottom-content">
|
||||
<div class="catalog-menu">
|
||||
<a href="catalog.php" class="catalog-link">
|
||||
<div class="catalog-icon">
|
||||
<span class="line"></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"></span>
|
||||
</div>
|
||||
<span class="catalog-lines">☰</span>
|
||||
Каталог
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<nav class="nav">
|
||||
<ul class="nav-list">
|
||||
<li><a href="cite_mebel.php">Главная</a></li>
|
||||
<li><a href="услуги.php">Услуги</a></li>
|
||||
<li><a href="Доставка.php">Доставка и оплата</a></li>
|
||||
<li><a href="Гарантия.php">Гарантия</a></li>
|
||||
<li><a href="#footer">Контакты</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
<div class="header-phone">+7(912)999-12-23</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
@@ -1,53 +0,0 @@
|
||||
<?php
|
||||
// image_upload.php
|
||||
session_start();
|
||||
|
||||
// Проверка прав администратора
|
||||
if (!isset($_SESSION['isAdmin']) || $_SESSION['isAdmin'] !== true) {
|
||||
echo json_encode(['success' => false, 'message' => 'Доступ запрещен']);
|
||||
exit();
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['image'])) {
|
||||
$uploadDir = 'uploads/products/';
|
||||
|
||||
// Создаем директорию если не существует
|
||||
if (!file_exists($uploadDir)) {
|
||||
mkdir($uploadDir, 0777, true);
|
||||
}
|
||||
|
||||
$allowedTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
|
||||
$maxSize = 5 * 1024 * 1024; // 5MB
|
||||
|
||||
$file = $_FILES['image'];
|
||||
|
||||
// Проверка типа файла
|
||||
if (!in_array($file['type'], $allowedTypes)) {
|
||||
echo json_encode(['success' => false, 'message' => 'Допустимые форматы: JPEG, PNG, GIF, WebP']);
|
||||
exit();
|
||||
}
|
||||
|
||||
// Проверка размера
|
||||
if ($file['size'] > $maxSize) {
|
||||
echo json_encode(['success' => false, 'message' => 'Максимальный размер файла: 5MB']);
|
||||
exit();
|
||||
}
|
||||
|
||||
// Генерируем уникальное имя
|
||||
$extension = pathinfo($file['name'], PATHINFO_EXTENSION);
|
||||
$fileName = 'product_' . time() . '_' . rand(1000, 9999) . '.' . $extension;
|
||||
$filePath = $uploadDir . $fileName;
|
||||
|
||||
if (move_uploaded_file($file['tmp_name'], $filePath)) {
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'url' => $filePath,
|
||||
'name' => $fileName
|
||||
]);
|
||||
} else {
|
||||
echo json_encode(['success' => false, 'message' => 'Ошибка загрузки файла']);
|
||||
}
|
||||
} else {
|
||||
echo json_encode(['success' => false, 'message' => 'Файл не получен']);
|
||||
}
|
||||
?>
|
||||
BIN
img/2_2.jpg
|
Before Width: | Height: | Size: 42 KiB |
48
login.php
@@ -1,48 +0,0 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once 'config/database.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$email = $_POST['email'] ?? '';
|
||||
$password = $_POST['password'] ?? '';
|
||||
|
||||
$db = Database::getInstance()->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();
|
||||
}
|
||||
}
|
||||
?>
|
||||
22
logout.php
@@ -1,22 +0,0 @@
|
||||
<?php
|
||||
session_start();
|
||||
|
||||
// Удаляем все данные сессии
|
||||
$_SESSION = array();
|
||||
|
||||
// Если требуется уничтожить сессию, также удаляем сессионные cookie
|
||||
if (ini_get("session.use_cookies")) {
|
||||
$params = session_get_cookie_params();
|
||||
setcookie(session_name(), '', time() - 42000,
|
||||
$params["path"], $params["domain"],
|
||||
$params["secure"], $params["httponly"]
|
||||
);
|
||||
}
|
||||
|
||||
// Уничтожаем сессию
|
||||
session_destroy();
|
||||
|
||||
// Перенаправляем на главную
|
||||
header("Location: cite_mebel.php");
|
||||
exit();
|
||||
?>
|
||||
64
migrations/001_initial_schema.sql
Normal file
@@ -0,0 +1,64 @@
|
||||
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),
|
||||
city VARCHAR(100),
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
last_login TIMESTAMP,
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
is_admin BOOLEAN DEFAULT FALSE
|
||||
);
|
||||
|
||||
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) ON DELETE SET NULL,
|
||||
description TEXT,
|
||||
sort_order INTEGER DEFAULT 0,
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS subcategories (
|
||||
subcategory_id SERIAL PRIMARY KEY,
|
||||
category_id INTEGER REFERENCES categories(category_id) ON DELETE CASCADE,
|
||||
name VARCHAR(100) NOT NULL,
|
||||
slug VARCHAR(100) UNIQUE NOT NULL,
|
||||
description TEXT,
|
||||
sort_order INTEGER DEFAULT 0,
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS products (
|
||||
product_id SERIAL PRIMARY KEY,
|
||||
category_id INTEGER REFERENCES categories(category_id) ON DELETE SET NULL,
|
||||
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),
|
||||
color VARCHAR(50),
|
||||
material VARCHAR(100),
|
||||
card_size VARCHAR(20) DEFAULT 'small',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_products_category ON products(category_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_products_available ON products(is_available);
|
||||
CREATE INDEX IF NOT EXISTS idx_products_price ON products(price);
|
||||
CREATE INDEX IF NOT EXISTS idx_categories_parent ON categories(parent_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_categories_active ON categories(is_active);
|
||||
50
migrations/002_add_cart_orders.sql
Normal file
@@ -0,0 +1,50 @@
|
||||
CREATE TABLE IF NOT EXISTS cart (
|
||||
cart_id SERIAL PRIMARY KEY,
|
||||
user_id INTEGER REFERENCES users(user_id) ON DELETE CASCADE,
|
||||
product_id INTEGER REFERENCES products(product_id) ON DELETE CASCADE,
|
||||
quantity INTEGER DEFAULT 1 CHECK (quantity > 0),
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE(user_id, product_id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS orders (
|
||||
order_id SERIAL PRIMARY KEY,
|
||||
order_number VARCHAR(50) UNIQUE NOT NULL,
|
||||
user_id INTEGER REFERENCES users(user_id) ON DELETE SET NULL,
|
||||
customer_name VARCHAR(100) NOT NULL,
|
||||
customer_email VARCHAR(255) NOT NULL,
|
||||
customer_phone VARCHAR(20) NOT NULL,
|
||||
delivery_address TEXT NOT NULL,
|
||||
delivery_region VARCHAR(100),
|
||||
postal_code VARCHAR(20),
|
||||
delivery_method VARCHAR(50) DEFAULT 'courier',
|
||||
payment_method VARCHAR(50) DEFAULT 'card',
|
||||
subtotal DECIMAL(10, 2) NOT NULL,
|
||||
discount_amount DECIMAL(10, 2) DEFAULT 0,
|
||||
delivery_price DECIMAL(10, 2) DEFAULT 0,
|
||||
final_amount DECIMAL(10, 2) NOT NULL,
|
||||
promo_code VARCHAR(50),
|
||||
status VARCHAR(30) DEFAULT 'pending',
|
||||
notes TEXT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
completed_at TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS order_items (
|
||||
item_id SERIAL PRIMARY KEY,
|
||||
order_id INTEGER REFERENCES orders(order_id) ON DELETE CASCADE,
|
||||
product_id INTEGER REFERENCES products(product_id) ON DELETE SET NULL,
|
||||
product_name VARCHAR(200) NOT NULL,
|
||||
product_price DECIMAL(10, 2) NOT NULL,
|
||||
quantity INTEGER NOT NULL CHECK (quantity > 0),
|
||||
total_price DECIMAL(10, 2) NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_cart_user ON cart(user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_orders_user ON orders(user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_orders_status ON orders(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_orders_created ON orders(created_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_order_items_order ON order_items(order_id);
|
||||
43
migrations/003_add_product_fields.sql
Normal file
@@ -0,0 +1,43 @@
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'products' AND column_name = 'color') THEN
|
||||
ALTER TABLE products ADD COLUMN color VARCHAR(50);
|
||||
END IF;
|
||||
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'products' AND column_name = 'material') THEN
|
||||
ALTER TABLE products ADD COLUMN material VARCHAR(100);
|
||||
END IF;
|
||||
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'products' AND column_name = 'card_size') THEN
|
||||
ALTER TABLE products ADD COLUMN card_size VARCHAR(20) DEFAULT 'small';
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'users' AND column_name = 'city') THEN
|
||||
ALTER TABLE users ADD COLUMN city VARCHAR(100);
|
||||
END IF;
|
||||
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'users' AND column_name = 'last_login') THEN
|
||||
ALTER TABLE users ADD COLUMN last_login TIMESTAMP;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'categories' AND column_name = 'updated_at') THEN
|
||||
ALTER TABLE categories ADD COLUMN updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP;
|
||||
END IF;
|
||||
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'categories' AND column_name = 'created_at') THEN
|
||||
ALTER TABLE categories ADD COLUMN created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP;
|
||||
END IF;
|
||||
END $$;
|
||||
42
migrations/004_grant_admin_to_admin_mail.sql
Normal file
@@ -0,0 +1,42 @@
|
||||
UPDATE users
|
||||
SET is_admin = TRUE,
|
||||
is_active = TRUE,
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE email = 'admin@mail.ru';
|
||||
|
||||
DO $$
|
||||
DECLARE
|
||||
updated_count INTEGER;
|
||||
user_info RECORD;
|
||||
BEGIN
|
||||
GET DIAGNOSTICS updated_count = ROW_COUNT;
|
||||
|
||||
IF updated_count > 0 THEN
|
||||
SELECT user_id, email, full_name, is_admin, is_active
|
||||
INTO user_info
|
||||
FROM users
|
||||
WHERE email = 'admin@mail.ru';
|
||||
|
||||
RAISE NOTICE 'Пользователь % (ID: %) успешно получил права администратора',
|
||||
user_info.email, user_info.user_id;
|
||||
RAISE NOTICE 'ФИО: %, Админ: %, Активен: %',
|
||||
user_info.full_name, user_info.is_admin, user_info.is_active;
|
||||
ELSE
|
||||
INSERT INTO users (email, password_hash, full_name, phone, city, is_admin, is_active)
|
||||
VALUES (
|
||||
'admin@mail.ru',
|
||||
'$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi',
|
||||
'Администратор',
|
||||
'+79129991223',
|
||||
'Москва',
|
||||
TRUE,
|
||||
TRUE
|
||||
)
|
||||
ON CONFLICT (email) DO UPDATE
|
||||
SET is_admin = TRUE,
|
||||
is_active = TRUE,
|
||||
updated_at = CURRENT_TIMESTAMP;
|
||||
|
||||
RAISE NOTICE 'Пользователь admin@mail.ru создан/обновлен с правами администратора';
|
||||
END IF;
|
||||
END $$;
|
||||
85
migrations/grant_admin.php
Normal file
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . '/../config/database.php';
|
||||
|
||||
echo "===========================================\n";
|
||||
echo " Назначение прав администратора\n";
|
||||
echo "===========================================\n\n";
|
||||
|
||||
try {
|
||||
$db = Database::getInstance()->getConnection();
|
||||
echo "[OK] Подключение к базе данных успешно\n\n";
|
||||
|
||||
$email = 'admin@mail.ru';
|
||||
|
||||
$checkStmt = $db->prepare("SELECT user_id, email, full_name, is_admin, is_active FROM users WHERE email = ?");
|
||||
$checkStmt->execute([$email]);
|
||||
$user = $checkStmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if ($user) {
|
||||
echo "[INFO] Найден пользователь:\n";
|
||||
echo " Email: {$user['email']}\n";
|
||||
echo " ФИО: {$user['full_name']}\n";
|
||||
echo " Админ: " . ($user['is_admin'] ? 'ДА' : 'НЕТ') . "\n";
|
||||
echo " Активен: " . ($user['is_active'] ? 'ДА' : 'НЕТ') . "\n\n";
|
||||
|
||||
if ($user['is_admin']) {
|
||||
echo "[INFO] Пользователь уже имеет права администратора\n";
|
||||
} else {
|
||||
|
||||
$updateStmt = $db->prepare("
|
||||
UPDATE users
|
||||
SET is_admin = TRUE,
|
||||
is_active = TRUE,
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE email = ?
|
||||
");
|
||||
$updateStmt->execute([$email]);
|
||||
|
||||
echo "[SUCCESS] Права администратора успешно назначены!\n";
|
||||
}
|
||||
} else {
|
||||
echo "[WARN] Пользователь с email $email не найден\n";
|
||||
echo "[INFO] Создаю нового пользователя с правами администратора...\n";
|
||||
|
||||
$password_hash = password_hash('admin123', PASSWORD_DEFAULT);
|
||||
|
||||
$insertStmt = $db->prepare("
|
||||
INSERT INTO users (email, password_hash, full_name, phone, city, is_admin, is_active)
|
||||
VALUES (?, ?, ?, ?, ?, CAST(? AS boolean), TRUE)
|
||||
RETURNING user_id
|
||||
");
|
||||
|
||||
$insertStmt->execute([
|
||||
$email,
|
||||
$password_hash,
|
||||
'Администратор',
|
||||
'+79129991223',
|
||||
'Москва',
|
||||
'true'
|
||||
]);
|
||||
|
||||
$user_id = $insertStmt->fetchColumn();
|
||||
echo "[SUCCESS] Пользователь создан с ID: $user_id\n";
|
||||
echo "[INFO] Email: $email\n";
|
||||
echo "[INFO] Пароль по умолчанию: admin123\n";
|
||||
echo "[WARN] Рекомендуется сменить пароль после первого входа!\n";
|
||||
}
|
||||
|
||||
$verifyStmt = $db->prepare("SELECT user_id, email, full_name, is_admin, is_active FROM users WHERE email = ?");
|
||||
$verifyStmt->execute([$email]);
|
||||
$finalUser = $verifyStmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
echo "\n===========================================\n";
|
||||
echo " Итоговый статус:\n";
|
||||
echo "===========================================\n";
|
||||
echo " Email: {$finalUser['email']}\n";
|
||||
echo " ФИО: {$finalUser['full_name']}\n";
|
||||
echo " Админ: " . ($finalUser['is_admin'] ? 'ДА ✓' : 'НЕТ ✗') . "\n";
|
||||
echo " Активен: " . ($finalUser['is_active'] ? 'ДА ✓' : 'НЕТ ✗') . "\n";
|
||||
echo "===========================================\n";
|
||||
|
||||
} catch (PDOException $e) {
|
||||
echo "[ERROR] Ошибка: " . $e->getMessage() . "\n";
|
||||
exit(1);
|
||||
}
|
||||
95
migrations/migrate.php
Normal file
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . '/../config/database.php';
|
||||
|
||||
echo "===========================================\n";
|
||||
echo " AETERNA - Система миграций базы данных\n";
|
||||
echo "===========================================\n\n";
|
||||
|
||||
try {
|
||||
$db = Database::getInstance()->getConnection();
|
||||
echo "[OK] Подключение к базе данных успешно\n\n";
|
||||
|
||||
$db->exec("
|
||||
CREATE TABLE IF NOT EXISTS migrations (
|
||||
id SERIAL PRIMARY KEY,
|
||||
filename VARCHAR(255) NOT NULL UNIQUE,
|
||||
applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
");
|
||||
echo "[OK] Таблица migrations готова\n";
|
||||
|
||||
$stmt = $db->query("SELECT filename FROM migrations ORDER BY filename");
|
||||
$applied = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
||||
echo "[INFO] Уже применено миграций: " . count($applied) . "\n\n";
|
||||
|
||||
$migrationFiles = glob(__DIR__ . '/*.sql');
|
||||
sort($migrationFiles);
|
||||
|
||||
$newMigrations = 0;
|
||||
|
||||
foreach ($migrationFiles as $file) {
|
||||
$filename = basename($file);
|
||||
|
||||
if ($filename === 'seed_data.sql') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (in_array($filename, $applied)) {
|
||||
echo "[SKIP] $filename (уже применена)\n";
|
||||
continue;
|
||||
}
|
||||
|
||||
echo "[RUN] Применяю $filename... ";
|
||||
|
||||
$sql = file_get_contents($file);
|
||||
|
||||
try {
|
||||
$db->exec($sql);
|
||||
|
||||
$stmt = $db->prepare("INSERT INTO migrations (filename) VALUES (?)");
|
||||
$stmt->execute([$filename]);
|
||||
|
||||
echo "OK\n";
|
||||
$newMigrations++;
|
||||
} catch (PDOException $e) {
|
||||
echo "ОШИБКА!\n";
|
||||
echo " Причина: " . $e->getMessage() . "\n";
|
||||
echo "\n[!] Миграция остановлена из-за ошибки\n";
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
echo "\n-------------------------------------------\n";
|
||||
|
||||
if ($newMigrations > 0) {
|
||||
echo "[SUCCESS] Применено новых миграций: $newMigrations\n";
|
||||
} else {
|
||||
echo "[INFO] Все миграции уже применены\n";
|
||||
}
|
||||
|
||||
$seedFile = __DIR__ . '/seed_data.sql';
|
||||
if (file_exists($seedFile)) {
|
||||
echo "\n[?] Хотите загрузить начальные данные (seed_data.sql)?\n";
|
||||
echo " Запустите: php migrations/migrate.php --seed\n";
|
||||
|
||||
if (isset($argv[1]) && $argv[1] === '--seed') {
|
||||
echo "\n[RUN] Загружаю seed_data.sql... ";
|
||||
try {
|
||||
$sql = file_get_contents($seedFile);
|
||||
$db->exec($sql);
|
||||
echo "OK\n";
|
||||
} catch (PDOException $e) {
|
||||
echo "ОШИБКА: " . $e->getMessage() . "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
echo "\n===========================================\n";
|
||||
echo " Миграции завершены!\n";
|
||||
echo "===========================================\n";
|
||||
|
||||
} catch (PDOException $e) {
|
||||
echo "[ERROR] Ошибка подключения к БД: " . $e->getMessage() . "\n";
|
||||
exit(1);
|
||||
}
|
||||
56
migrations/seed_data.sql
Normal file
@@ -0,0 +1,56 @@
|
||||
INSERT INTO users (email, password_hash, full_name, phone, city, is_admin, is_active)
|
||||
VALUES (
|
||||
'admin@aeterna.ru',
|
||||
'$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi',
|
||||
'Администратор AETERNA',
|
||||
'+79129991223',
|
||||
'Москва',
|
||||
TRUE,
|
||||
TRUE
|
||||
) ON CONFLICT (email) DO NOTHING;
|
||||
|
||||
INSERT INTO users (email, password_hash, full_name, phone, city, is_admin, is_active)
|
||||
VALUES (
|
||||
'user@test.com',
|
||||
'$2y$10$TKh8H1.PfQx37YgCzwiKb.KjNyWgaHb9cbcoQgdIVFlYg7B77UdFm',
|
||||
'Тестовый Пользователь',
|
||||
'+79111234567',
|
||||
'Санкт-Петербург',
|
||||
FALSE,
|
||||
TRUE
|
||||
) ON CONFLICT (email) DO NOTHING;
|
||||
|
||||
INSERT INTO categories (name, slug, description, sort_order, is_active) VALUES
|
||||
('Диваны', 'divany', 'Прямые и угловые диваны для гостиной', 1, TRUE),
|
||||
('Кресла', 'kresla', 'Кресла для гостиной и офиса', 2, TRUE),
|
||||
('Кровати', 'krovati', 'Односпальные и двуспальные кровати', 3, TRUE),
|
||||
('Столы', 'stoly', 'Обеденные и рабочие столы', 4, TRUE),
|
||||
('Стулья', 'stulya', 'Стулья для кухни и офиса', 5, TRUE),
|
||||
('Светильники', 'svetilniki', 'Торшеры, люстры и настольные лампы', 6, TRUE)
|
||||
ON CONFLICT (slug) DO NOTHING;
|
||||
|
||||
INSERT INTO products (category_id, name, slug, description, price, old_price, sku, stock_quantity, is_available, image_url, color, material, card_size) VALUES
|
||||
(1, 'Светильник MINNIGHT', 'svetilnik-minnight', 'Настольный светильник в современном стиле', 7999, 9999, 'LAMP-MIN-001', 15, TRUE, 'img2/1_2.png', 'Черный', 'Металл', 'small'),
|
||||
(3, 'Кровать MODER', 'krovat-moder', 'Двуспальная кровать с мягким изголовьем', 45999, 55999, 'BED-MOD-001', 5, TRUE, 'img2/3_3.png', 'Серый', 'Дерево/Ткань', 'large'),
|
||||
(6, 'Торшер MARCIA', 'torsher-marcia', 'Напольный торшер с регулируемой высотой', 11999, 14999, 'LAMP-MAR-001', 8, TRUE, 'img2/2_2.png', 'Золотой', 'Металл', 'tall'),
|
||||
(6, 'Светильник POLET', 'svetilnik-polet', 'Подвесной светильник для гостиной', 5499, NULL, 'LAMP-POL-001', 20, TRUE, 'img2/4.jpg', 'Белый', 'Стекло', 'wide'),
|
||||
(4, 'Стол NORD', 'stol-nord', 'Обеденный стол в скандинавском стиле', 23999, 28999, 'TABLE-NOR-001', 7, TRUE, 'img2/5_5.png', 'Натуральный', 'Дерево', 'small1'),
|
||||
(1, 'Диван ROYALTY', 'divan-royalty', 'Роскошный угловой диван с велюровой обивкой', 78999, 95999, 'SOFA-ROY-001', 3, TRUE, 'img2/6_6.png', 'Зеленый', 'Велюр', 'wide2'),
|
||||
(2, 'Кресло MINIMAL', 'kreslo-minimal', 'Кресло в минималистичном стиле', 29999, 35999, 'ARM-MIN-001', 10, TRUE, 'img2/7_7.png', 'Бежевый', 'Ткань', 'wide3'),
|
||||
(4, 'Стол LONKI', 'stol-lonki', 'Журнальный столик с мраморной столешницей', 34999, NULL, 'TABLE-LON-001', 12, TRUE, 'img2/8_8.png', 'Белый мрамор', 'Мрамор/Металл', 'wide2_1'),
|
||||
(1, 'Диван HEMMINS', 'divan-hemmins', 'Большой модульный диван для всей семьи', 89999, 110000, 'SOFA-HEM-001', 2, TRUE, 'img2/9_9.png', 'Темно-серый', 'Ткань', 'full-width')
|
||||
ON CONFLICT (slug) DO NOTHING;
|
||||
|
||||
DO $$
|
||||
DECLARE
|
||||
users_count INTEGER;
|
||||
categories_count INTEGER;
|
||||
products_count INTEGER;
|
||||
BEGIN
|
||||
SELECT COUNT(*) INTO users_count FROM users;
|
||||
SELECT COUNT(*) INTO categories_count FROM categories;
|
||||
SELECT COUNT(*) INTO products_count FROM products;
|
||||
|
||||
RAISE NOTICE 'Загружено: % пользователей, % категорий, % товаров',
|
||||
users_count, categories_count, products_count;
|
||||
END $$;
|
||||
208
print_order.php
@@ -1,208 +0,0 @@
|
||||
<?php
|
||||
// print_order.php
|
||||
session_start();
|
||||
require_once 'config/database.php';
|
||||
|
||||
// Проверка прав администратора
|
||||
if (!isset($_SESSION['isAdmin']) || $_SESSION['isAdmin'] !== true) {
|
||||
die('Доступ запрещен');
|
||||
}
|
||||
|
||||
$orderId = $_GET['id'] ?? 0;
|
||||
|
||||
$db = Database::getInstance()->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());
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Печать заказа #<?= $order['order_number'] ?></title>
|
||||
<style>
|
||||
@media print {
|
||||
.no-print { display: none !important; }
|
||||
}
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 20px;
|
||||
color: #000;
|
||||
}
|
||||
.header {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
.company-name {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.order-info {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
.order-details {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 20px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin: 20px 0;
|
||||
}
|
||||
th, td {
|
||||
border: 1px solid #000;
|
||||
padding: 8px;
|
||||
text-align: left;
|
||||
}
|
||||
th {
|
||||
background: #f0f0f0;
|
||||
}
|
||||
.total-row {
|
||||
font-weight: bold;
|
||||
}
|
||||
.footer {
|
||||
margin-top: 50px;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
}
|
||||
.print-button {
|
||||
margin: 20px;
|
||||
padding: 10px 20px;
|
||||
background: #453227;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<button onclick="window.print()" class="print-button no-print">
|
||||
<i class="fas fa-print"></i> Печать
|
||||
</button>
|
||||
|
||||
<div class="header">
|
||||
<div class="company-name">AETERNA</div>
|
||||
<div>г. Москва, ул. Примерная, д. 123</div>
|
||||
<div>Телефон: +7(912)999-12-23 | Email: aeterna@mail.ru</div>
|
||||
<div>ИНН: 1234567890 | ОГРН: 1234567890123</div>
|
||||
</div>
|
||||
|
||||
<div class="order-info">
|
||||
<h2>Заказ #<?= htmlspecialchars($order['order_number']) ?></h2>
|
||||
<p>Дата: <?= date('d.m.Y H:i', strtotime($order['created_at'])) ?></p>
|
||||
<p>Статус: <?= $order['status'] ?></p>
|
||||
</div>
|
||||
|
||||
<div class="order-details">
|
||||
<div>
|
||||
<h3>Информация о клиенте</h3>
|
||||
<p><strong>ФИО:</strong> <?= htmlspecialchars($order['customer_name']) ?></p>
|
||||
<p><strong>Email:</strong> <?= htmlspecialchars($order['customer_email']) ?></p>
|
||||
<p><strong>Телефон:</strong> <?= htmlspecialchars($order['customer_phone']) ?></p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3>Информация о доставке</h3>
|
||||
<p><strong>Адрес:</strong> <?= nl2br(htmlspecialchars($order['delivery_address'])) ?></p>
|
||||
<p><strong>Способ доставки:</strong> <?= $order['delivery_method'] == 'courier' ? 'Курьер' : 'Самовывоз' ?></p>
|
||||
<p><strong>Способ оплаты:</strong> <?= $order['payment_method'] == 'card' ? 'Карта' : 'Наличные' ?></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3>Состав заказа</h3>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>№</th>
|
||||
<th>Товар</th>
|
||||
<th>Кол-во</th>
|
||||
<th>Цена</th>
|
||||
<th>Сумма</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php $counter = 1; ?>
|
||||
<?php foreach ($order_items as $item): ?>
|
||||
<tr>
|
||||
<td><?= $counter++ ?></td>
|
||||
<td><?= htmlspecialchars($item['product_name']) ?></td>
|
||||
<td><?= $item['quantity'] ?></td>
|
||||
<td><?= number_format($item['unit_price'], 0, '', ' ') ?> ₽</td>
|
||||
<td><?= number_format($item['total_price'], 0, '', ' ') ?> ₽</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr class="total-row">
|
||||
<td colspan="4" style="text-align: right;">Сумма товаров:</td>
|
||||
<td><?= number_format($order['total_amount'], 0, '', ' ') ?> ₽</td>
|
||||
</tr>
|
||||
<?php if ($order['discount_amount'] > 0): ?>
|
||||
<tr class="total-row">
|
||||
<td colspan="4" style="text-align: right;">Скидка:</td>
|
||||
<td>-<?= number_format($order['discount_amount'], 0, '', ' ') ?> ₽</td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
<?php if ($order['delivery_cost'] > 0): ?>
|
||||
<tr class="total-row">
|
||||
<td colspan="4" style="text-align: right;">Доставка:</td>
|
||||
<td><?= number_format($order['delivery_cost'], 0, '', ' ') ?> ₽</td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
<tr class="total-row">
|
||||
<td colspan="4" style="text-align: right; font-size: 16px;">Итого к оплате:</td>
|
||||
<td style="font-size: 16px;"><?= number_format($order['final_amount'], 0, '', ' ') ?> ₽</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
|
||||
<?php if ($order['notes']): ?>
|
||||
<div style="margin-top: 30px;">
|
||||
<h3>Примечания</h3>
|
||||
<p><?= nl2br(htmlspecialchars($order['notes'])) ?></p>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="footer">
|
||||
<p>Заказ создан в системе AETERNA</p>
|
||||
<p>Дата печати: <?= date('d.m.Y H:i') ?></p>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Автоматическая печать при загрузке
|
||||
window.onload = function() {
|
||||
setTimeout(function() {
|
||||
window.print();
|
||||
}, 500);
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,350 +0,0 @@
|
||||
<?php
|
||||
// product_modern.php - Страница товара "Диван MODERN"
|
||||
session_start();
|
||||
require_once 'config/database.php';
|
||||
|
||||
// Проверка авторизации
|
||||
if (!isset($_SESSION['isLoggedIn']) || $_SESSION['isLoggedIn'] !== true) {
|
||||
header('Location: вход.php?error=auth_required&redirect=' . urlencode($_SERVER['REQUEST_URI']));
|
||||
exit();
|
||||
}
|
||||
|
||||
$db = Database::getInstance()->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());
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>AETERNA - <?= htmlspecialchars($product['name']) ?></title>
|
||||
<link rel="stylesheet/less" type="text/css" href="style_for_cite.less">
|
||||
<script src="https://cdn.jsdelivr.net/npm/less"></script>
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
|
||||
<style>
|
||||
.product-attributes {
|
||||
background: #f8f9fa;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
.attribute-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 10px 0;
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
.attribute-label {
|
||||
font-weight: bold;
|
||||
color: #453227;
|
||||
}
|
||||
.attribute-value {
|
||||
color: #617365;
|
||||
}
|
||||
.stock-status {
|
||||
font-weight: bold;
|
||||
padding: 5px 10px;
|
||||
border-radius: 4px;
|
||||
display: inline-block;
|
||||
}
|
||||
.in-stock {
|
||||
background: #d4edda;
|
||||
color: #155724;
|
||||
}
|
||||
.low-stock {
|
||||
background: #fff3cd;
|
||||
color: #856404;
|
||||
}
|
||||
.out-of-stock {
|
||||
background: #f8d7da;
|
||||
color: #721c24;
|
||||
}
|
||||
.product-gallery {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 20px;
|
||||
}
|
||||
.main-image {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
.product-gallery img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.thumbnail {
|
||||
opacity: 0.7;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
.thumbnail:hover, .thumbnail.active {
|
||||
opacity: 1;
|
||||
border: 2px solid #453227;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<?php include 'header_common.php'; ?>
|
||||
|
||||
<main class="container">
|
||||
<div class="breadcrumbs">
|
||||
<a href="cite_mebel.php">Главная</a> •
|
||||
<a href="catalog.php">Каталог</a> •
|
||||
<a href="catalog.php?category=<?= $product['category_id'] ?>"><?= htmlspecialchars($product['category_name']) ?></a> •
|
||||
<span><?= htmlspecialchars($product['name']) ?></span>
|
||||
</div>
|
||||
|
||||
<div class="product__section">
|
||||
<div class="product__gallery">
|
||||
<div class="main-image">
|
||||
<img src="img/диван_modern_main.jpg" alt="Диван MODERN" id="mainImage" class="product__image">
|
||||
</div>
|
||||
<div class="thumbnail" onclick="changeImage('img/диван_modern_main.jpg')">
|
||||
<img src="img/диван_modern_main.jpg" alt="Диван MODERN - вид 1">
|
||||
</div>
|
||||
<div class="thumbnail" onclick="changeImage('img/диван_modern_side.jpg')">
|
||||
<img src="img/диван_modern_side.jpg" alt="Диван MODERN - вид 2">
|
||||
</div>
|
||||
<div class="thumbnail" onclick="changeImage('img/диван_modern_detail.jpg')">
|
||||
<img src="img/диван_modern_detail.jpg" alt="Диван MODERN - детали">
|
||||
</div>
|
||||
<div class="thumbnail" onclick="changeImage('img/диван_modern_inroom.jpg')">
|
||||
<img src="img/диван_modern_inroom.jpg" alt="Диван MODERN в интерьере">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="product__info">
|
||||
<h1 class="product__title">Диван MODERN</h1>
|
||||
|
||||
<div class="product__rating">
|
||||
<div class="stars">
|
||||
<span class="star filled">★</span>
|
||||
<span class="star filled">★</span>
|
||||
<span class="star filled">★</span>
|
||||
<span class="star filled">★</span>
|
||||
<span class="star half">★</span>
|
||||
</div>
|
||||
<span class="rating-value">4.5</span>
|
||||
<span class="reviews-count">(24 отзыва)</span>
|
||||
</div>
|
||||
|
||||
<div class="product__price">
|
||||
<span class="current-price">
|
||||
<?= number_format($product['price'], 0, '', ' ') ?> ₽
|
||||
</span>
|
||||
<?php if ($product['old_price'] && $product['old_price'] > $product['price']): ?>
|
||||
<span class="old-price">
|
||||
<?= number_format($product['old_price'], 0, '', ' ') ?> ₽
|
||||
</span>
|
||||
<span class="discount-badge">
|
||||
-<?= round(($product['old_price'] - $product['price']) / $product['old_price'] * 100) ?>%
|
||||
</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<div class="product-attributes">
|
||||
<div class="attribute-row">
|
||||
<span class="attribute-label">Артикул:</span>
|
||||
<span class="attribute-value"><?= $product['sku'] ?? 'DIV-MOD-001' ?></span>
|
||||
</div>
|
||||
<div class="attribute-row">
|
||||
<span class="attribute-label">Категория:</span>
|
||||
<span class="attribute-value">Диваны</span>
|
||||
</div>
|
||||
<div class="attribute-row">
|
||||
<span class="attribute-label">Цвет:</span>
|
||||
<span class="attribute-value">Серый, Бежевый, Темно-синий</span>
|
||||
</div>
|
||||
<div class="attribute-row">
|
||||
<span class="attribute-label">Материал:</span>
|
||||
<span class="attribute-value">Экокожа, Дерево</span>
|
||||
</div>
|
||||
<div class="attribute-row">
|
||||
<span class="attribute-label">Размеры (Ш×Г×В):</span>
|
||||
<span class="attribute-value">220 × 95 × 85 см</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stock-status <?= $product['stock_quantity'] > 0 ? 'in-stock' : 'out-of-stock' ?>">
|
||||
<?php if ($product['stock_quantity'] > 0): ?>
|
||||
<i class="fas fa-check-circle"></i> В наличии: <?= $product['stock_quantity'] ?> шт.
|
||||
<?php else: ?>
|
||||
<i class="fas fa-times-circle"></i> Нет в наличии
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<p class="product__description">
|
||||
Угловой диван MODERN – сочетание современного дизайна и бескомпромиссного комфорта.
|
||||
Каркас из массива дерева обеспечивает долговечность, а наполнитель из высокоэластичного
|
||||
пенополиуретана гарантирует оптимальную поддержку спины. Диван оснащен механизмом
|
||||
трансформации «еврокнижка», что позволяет использовать его как спальное место.
|
||||
<br><br>
|
||||
Особенности:
|
||||
• Прочная конструкция из массива дерева
|
||||
• Механизм трансформации «еврокнижка»
|
||||
• Наполнитель: пенополиуретан + периотек
|
||||
• Съемные чехлы для легкой чистки
|
||||
• Встроенные подлокотники с полками
|
||||
</p>
|
||||
|
||||
<?php if ($product['stock_quantity'] > 0): ?>
|
||||
<div class="product__purchase">
|
||||
<div class="product__quantity">
|
||||
<button class="product__qty-btn minus">-</button>
|
||||
<input type="number" class="product__qty-value" value="1" min="1" max="<?= $product['stock_quantity'] ?>">
|
||||
<button class="product__qty-btn plus">+</button>
|
||||
</div>
|
||||
|
||||
<div class="product__actions">
|
||||
<button class="btn primary-btn" onclick="addToCart(<?= $product['product_id'] ?>)">
|
||||
<i class="fas fa-shopping-cart"></i> В корзину
|
||||
</button>
|
||||
<button class="btn secondary-btn" onclick="buyNow(<?= $product['product_id'] ?>)">
|
||||
<i class="fas fa-bolt"></i> Купить сейчас
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="product__actions">
|
||||
<button class="btn secondary-btn" onclick="notifyWhenAvailable(<?= $product['product_id'] ?>)">
|
||||
<i class="fas fa-bell"></i> Уведомить о поступлении
|
||||
</button>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if (!empty($similarProducts)): ?>
|
||||
<section class="similar">
|
||||
<h2 class="similar__title">Похожие товары</h2>
|
||||
<div class="similar__grid">
|
||||
<?php foreach ($similarProducts as $similar): ?>
|
||||
<div class="similar__card">
|
||||
<div class="similar__card-image">
|
||||
<img src="<?= htmlspecialchars($similar['image_url'] ?? 'img1/default.jpg') ?>"
|
||||
alt="<?= htmlspecialchars($similar['name']) ?>">
|
||||
</div>
|
||||
<div class="similar__card-content">
|
||||
<h3><?= htmlspecialchars($similar['name']) ?></h3>
|
||||
<p class="similar__card-description">
|
||||
<?= htmlspecialchars(mb_substr($similar['description'] ?? '', 0, 80)) ?>...
|
||||
</p>
|
||||
<p class="similar__card-price">
|
||||
<?= number_format($similar['price'], 0, '', ' ') ?> ₽
|
||||
</p>
|
||||
<a href="product_detail.php?id=<?= $similar['product_id'] ?>"
|
||||
class="btn btn-primary">
|
||||
Подробнее
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</section>
|
||||
<?php endif; ?>
|
||||
</main>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
|
||||
<script>
|
||||
function changeImage(src) {
|
||||
$('#mainImage').attr('src', src);
|
||||
$('.thumbnail').removeClass('active');
|
||||
$(event.target).closest('.thumbnail').addClass('active');
|
||||
}
|
||||
|
||||
function addToCart(productId) {
|
||||
const quantity = $('.product__qty-value').val();
|
||||
|
||||
$.ajax({
|
||||
url: 'add_to_cart.php',
|
||||
method: 'POST',
|
||||
data: {
|
||||
product_id: productId,
|
||||
quantity: quantity
|
||||
},
|
||||
success: function(response) {
|
||||
try {
|
||||
const result = JSON.parse(response);
|
||||
if (result.success) {
|
||||
alert('Товар добавлен в корзину!');
|
||||
// Обновляем счетчик в шапке
|
||||
$('.cart-count').text(result.cart_count);
|
||||
} else {
|
||||
alert('Ошибка: ' + result.message);
|
||||
}
|
||||
} catch(e) {
|
||||
alert('Товар добавлен в корзину!');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function buyNow(productId) {
|
||||
const quantity = $('.product__qty-value').val();
|
||||
|
||||
$.ajax({
|
||||
url: 'add_to_cart.php',
|
||||
method: 'POST',
|
||||
data: {
|
||||
product_id: productId,
|
||||
quantity: quantity
|
||||
},
|
||||
success: function() {
|
||||
window.location.href = 'оформление_заказа.php';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
// Управление количеством
|
||||
$('.product__qty-btn.plus').click(function() {
|
||||
const $input = $('.product__qty-value');
|
||||
let value = parseInt($input.val());
|
||||
let max = parseInt($input.attr('max'));
|
||||
if (value < max) {
|
||||
$input.val(value + 1);
|
||||
}
|
||||
});
|
||||
|
||||
$('.product__qty-btn.minus').click(function() {
|
||||
const $input = $('.product__qty-value');
|
||||
let value = parseInt($input.val());
|
||||
if (value > 1) {
|
||||
$input.val(value - 1);
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
42
public/admin/fix_delete_category.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
session_start();
|
||||
require_once __DIR__ . '/../config/database.php';
|
||||
|
||||
if (!isset($_SESSION['isAdmin']) || !$_SESSION['isAdmin']) {
|
||||
echo json_encode(['success' => false, 'message' => 'Доступ запрещен']);
|
||||
exit();
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$categoryId = $_POST['category_id'] ?? 0;
|
||||
|
||||
if (!$categoryId) {
|
||||
echo json_encode(['success' => false, 'message' => 'Категория не указана']);
|
||||
exit();
|
||||
}
|
||||
|
||||
try {
|
||||
$db = Database::getInstance()->getConnection();
|
||||
|
||||
$checkStmt = $db->prepare("SELECT COUNT(*) FROM products WHERE category_id = ?");
|
||||
$checkStmt->execute([$categoryId]);
|
||||
$productCount = $checkStmt->fetchColumn();
|
||||
|
||||
if ($productCount > 0) {
|
||||
echo json_encode(['success' => false, 'message' => 'Нельзя удалить категорию с товарами. Сначала удалите или переместите товары.']);
|
||||
exit();
|
||||
}
|
||||
|
||||
$stmt = $db->prepare("DELETE FROM categories WHERE category_id = ?");
|
||||
$stmt->execute([$categoryId]);
|
||||
|
||||
echo json_encode(['success' => true, 'message' => 'Категория удалена']);
|
||||
} catch (PDOException $e) {
|
||||
echo json_encode(['success' => false, 'message' => 'Ошибка базы данных: ' . $e->getMessage()]);
|
||||
}
|
||||
} else {
|
||||
echo json_encode(['success' => false, 'message' => 'Неверный запрос']);
|
||||
}
|
||||
|
||||
@@ -1,116 +1,112 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once 'config/database.php';
|
||||
|
||||
if (!isset($_SESSION['isLoggedIn']) || $_SESSION['isLoggedIn'] !== true) {
|
||||
echo json_encode(['success' => 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' => 'Неверный запрос']);
|
||||
}
|
||||
<?php
|
||||
session_start();
|
||||
require_once __DIR__ . '/../config/database.php';
|
||||
|
||||
if (!isset($_SESSION['isLoggedIn']) || $_SESSION['isLoggedIn'] !== true) {
|
||||
echo json_encode(['success' => 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' => 'Неверный запрос']);
|
||||
}
|
||||
?>
|
||||
@@ -1,66 +1,69 @@
|
||||
<?php
|
||||
// login_handler.php
|
||||
session_start();
|
||||
require_once 'config/database.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$email = $_POST['email'] ?? '';
|
||||
$password = $_POST['password'] ?? '';
|
||||
|
||||
if (empty($email) || empty($password)) {
|
||||
echo json_encode(['success' => 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' => 'Неверный запрос']);
|
||||
}
|
||||
<?php
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
session_start();
|
||||
require_once __DIR__ . '/../config/database.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$email = $_POST['email'] ?? '';
|
||||
$password = $_POST['password'] ?? '';
|
||||
|
||||
if (empty($email) || empty($password)) {
|
||||
echo json_encode(['success' => 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(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$user) {
|
||||
echo json_encode(['success' => false, 'message' => 'Пользователь не найден']);
|
||||
exit();
|
||||
}
|
||||
|
||||
if (!$user['is_active']) {
|
||||
echo json_encode(['success' => false, 'message' => 'Аккаунт заблокирован']);
|
||||
exit();
|
||||
}
|
||||
|
||||
if (empty($user['password_hash'])) {
|
||||
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' => 'Неверный запрос']);
|
||||
}
|
||||
?>
|
||||
127
public/api/cart.php
Normal file
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
|
||||
session_start();
|
||||
require_once __DIR__ . '/../config/database.php';
|
||||
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
if (!isset($_SESSION['isLoggedIn']) || $_SESSION['isLoggedIn'] !== true) {
|
||||
echo json_encode(['success' => false, 'message' => 'Требуется авторизация']);
|
||||
exit();
|
||||
}
|
||||
|
||||
$userId = $_SESSION['user_id'] ?? 0;
|
||||
$action = $_GET['action'] ?? $_POST['action'] ?? '';
|
||||
|
||||
$db = Database::getInstance()->getConnection();
|
||||
|
||||
try {
|
||||
switch ($action) {
|
||||
case 'add':
|
||||
$productId = (int)($_POST['product_id'] ?? 0);
|
||||
$quantity = (int)($_POST['quantity'] ?? 1);
|
||||
|
||||
if ($productId <= 0) {
|
||||
echo json_encode(['success' => false, 'message' => 'Неверный ID товара']);
|
||||
exit();
|
||||
}
|
||||
|
||||
$checkProduct = $db->prepare("SELECT product_id, stock_quantity FROM products WHERE product_id = ? AND is_available = TRUE");
|
||||
$checkProduct->execute([$productId]);
|
||||
$product = $checkProduct->fetch();
|
||||
|
||||
if (!$product) {
|
||||
echo json_encode(['success' => false, 'message' => 'Товар не найден']);
|
||||
exit();
|
||||
}
|
||||
|
||||
$checkCart = $db->prepare("SELECT cart_id, quantity FROM cart WHERE user_id = ? AND product_id = ?");
|
||||
$checkCart->execute([$userId, $productId]);
|
||||
$cartItem = $checkCart->fetch();
|
||||
|
||||
if ($cartItem) {
|
||||
|
||||
$newQuantity = $cartItem['quantity'] + $quantity;
|
||||
$stmt = $db->prepare("UPDATE cart SET quantity = ?, updated_at = CURRENT_TIMESTAMP WHERE cart_id = ?");
|
||||
$stmt->execute([$newQuantity, $cartItem['cart_id']]);
|
||||
} else {
|
||||
|
||||
$stmt = $db->prepare("INSERT INTO cart (user_id, product_id, quantity) VALUES (?, ?, ?)");
|
||||
$stmt->execute([$userId, $productId, $quantity]);
|
||||
}
|
||||
|
||||
echo json_encode(['success' => true, 'message' => 'Товар добавлен в корзину']);
|
||||
break;
|
||||
|
||||
case 'update':
|
||||
$productId = (int)($_POST['product_id'] ?? 0);
|
||||
$quantity = (int)($_POST['quantity'] ?? 1);
|
||||
|
||||
if ($quantity <= 0) {
|
||||
|
||||
$stmt = $db->prepare("DELETE FROM cart WHERE user_id = ? AND product_id = ?");
|
||||
$stmt->execute([$userId, $productId]);
|
||||
} else {
|
||||
$stmt = $db->prepare("UPDATE cart SET quantity = ?, updated_at = CURRENT_TIMESTAMP WHERE user_id = ? AND product_id = ?");
|
||||
$stmt->execute([$quantity, $userId, $productId]);
|
||||
}
|
||||
|
||||
echo json_encode(['success' => true, 'message' => 'Корзина обновлена']);
|
||||
break;
|
||||
|
||||
case 'remove':
|
||||
$productId = (int)($_POST['product_id'] ?? 0);
|
||||
|
||||
$stmt = $db->prepare("DELETE FROM cart WHERE user_id = ? AND product_id = ?");
|
||||
$stmt->execute([$userId, $productId]);
|
||||
|
||||
echo json_encode(['success' => true, 'message' => 'Товар удален из корзины']);
|
||||
break;
|
||||
|
||||
case 'get':
|
||||
$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([$userId]);
|
||||
$items = $stmt->fetchAll();
|
||||
|
||||
$total = 0;
|
||||
foreach ($items as &$item) {
|
||||
$item['subtotal'] = $item['price'] * $item['quantity'];
|
||||
$total += $item['subtotal'];
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'items' => $items,
|
||||
'total' => $total,
|
||||
'count' => array_sum(array_column($items, 'quantity'))
|
||||
]);
|
||||
break;
|
||||
|
||||
case 'count':
|
||||
$stmt = $db->prepare("SELECT COALESCE(SUM(quantity), 0) FROM cart WHERE user_id = ?");
|
||||
$stmt->execute([$userId]);
|
||||
$count = $stmt->fetchColumn();
|
||||
|
||||
echo json_encode(['success' => true, 'count' => (int)$count]);
|
||||
break;
|
||||
|
||||
case 'clear':
|
||||
$stmt = $db->prepare("DELETE FROM cart WHERE user_id = ?");
|
||||
$stmt->execute([$userId]);
|
||||
|
||||
echo json_encode(['success' => true, 'message' => 'Корзина очищена']);
|
||||
break;
|
||||
|
||||
default:
|
||||
echo json_encode(['success' => false, 'message' => 'Неизвестное действие']);
|
||||
}
|
||||
|
||||
} catch (PDOException $e) {
|
||||
echo json_encode(['success' => false, 'message' => 'Ошибка базы данных: ' . $e->getMessage()]);
|
||||
}
|
||||
@@ -1,62 +1,61 @@
|
||||
<?php
|
||||
// get_cart.php
|
||||
session_start();
|
||||
require_once 'config/database.php';
|
||||
|
||||
if (!isset($_SESSION['isLoggedIn']) || $_SESSION['isLoggedIn'] !== true) {
|
||||
echo json_encode(['success' => 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()
|
||||
]);
|
||||
}
|
||||
<?php
|
||||
|
||||
session_start();
|
||||
require_once __DIR__ . '/../config/database.php';
|
||||
|
||||
if (!isset($_SESSION['isLoggedIn']) || $_SESSION['isLoggedIn'] !== true) {
|
||||
echo json_encode(['success' => 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()
|
||||
]);
|
||||
}
|
||||
?>
|
||||
@@ -1,22 +1,22 @@
|
||||
<?php
|
||||
// get_cart_count.php
|
||||
session_start();
|
||||
require_once 'config/database.php';
|
||||
|
||||
if (!isset($_SESSION['isLoggedIn']) || $_SESSION['isLoggedIn'] !== true) {
|
||||
echo json_encode(['success' => 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]);
|
||||
<?php
|
||||
|
||||
session_start();
|
||||
require_once __DIR__ . '/../config/database.php';
|
||||
|
||||
if (!isset($_SESSION['isLoggedIn']) || $_SESSION['isLoggedIn'] !== true) {
|
||||
echo json_encode(['success' => 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]);
|
||||
}
|
||||
@@ -1,33 +1,32 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once 'config/database.php';
|
||||
|
||||
// Проверяем авторизацию администратора
|
||||
if (!isset($_SESSION['isAdmin']) || $_SESSION['isAdmin'] !== true) {
|
||||
echo json_encode(['success' => 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()]);
|
||||
}
|
||||
<?php
|
||||
session_start();
|
||||
require_once __DIR__ . '/../config/database.php';
|
||||
|
||||
if (!isset($_SESSION['isAdmin']) || $_SESSION['isAdmin'] !== true) {
|
||||
echo json_encode(['success' => 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()]);
|
||||
}
|
||||
?>
|
||||
@@ -1,134 +1,136 @@
|
||||
<?php
|
||||
// process_order.php
|
||||
session_start();
|
||||
require_once 'config/database.php';
|
||||
|
||||
if (!isset($_SESSION['isLoggedIn']) || $_SESSION['isLoggedIn'] !== true) {
|
||||
header('Location: вход.php?error=auth_required');
|
||||
exit();
|
||||
}
|
||||
|
||||
$user_id = $_SESSION['user_id'] ?? 0;
|
||||
|
||||
if ($user_id == 0) {
|
||||
header('Location: вход.php?error=user_not_found');
|
||||
exit();
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$db = Database::getInstance()->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();
|
||||
}
|
||||
<?php
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
session_start();
|
||||
require_once __DIR__ . '/../config/database.php';
|
||||
|
||||
if (!isset($_SESSION['isLoggedIn']) || $_SESSION['isLoggedIn'] !== true) {
|
||||
echo json_encode(['success' => false, 'message' => 'Требуется авторизация']);
|
||||
exit();
|
||||
}
|
||||
|
||||
$user_id = $_SESSION['user_id'] ?? 0;
|
||||
|
||||
if ($user_id == 0) {
|
||||
echo json_encode(['success' => false, 'message' => 'Пользователь не найден']);
|
||||
exit();
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$db = Database::getInstance()->getConnection();
|
||||
|
||||
try {
|
||||
$db->beginTransaction();
|
||||
|
||||
$customer_name = $_POST['full_name'] ?? '';
|
||||
$customer_email = $_POST['email'] ?? '';
|
||||
$customer_phone = $_POST['phone'] ?? '';
|
||||
$delivery_address = $_POST['address'] ?? '';
|
||||
$region = $_POST['region'] ?? '';
|
||||
$postal_code = $_POST['postal_code'] ?? '';
|
||||
$payment_method = $_POST['payment'] ?? 'card';
|
||||
$delivery_method = $_POST['delivery'] ?? 'courier';
|
||||
$promo_code = $_POST['promo_code'] ?? '';
|
||||
$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, subtotal, discount_amount,
|
||||
delivery_price, final_amount, status, payment_method,
|
||||
delivery_method, delivery_address, delivery_region,
|
||||
postal_code, promo_code, 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, $region, $postal_code,
|
||||
$promo_code, $customer_name, $customer_email, $customer_phone, $notes
|
||||
]);
|
||||
|
||||
$order_id = $orderStmt->fetchColumn();
|
||||
|
||||
foreach ($cart_items as $item) {
|
||||
|
||||
$itemStmt = $db->prepare("
|
||||
INSERT INTO order_items (
|
||||
order_id, product_id, product_name,
|
||||
quantity, product_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();
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'order_id' => $order_id,
|
||||
'order_number' => $order_number,
|
||||
'message' => 'Заказ успешно оформлен'
|
||||
]);
|
||||
exit();
|
||||
|
||||
} catch (Exception $e) {
|
||||
$db->rollBack();
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'message' => $e->getMessage()
|
||||
]);
|
||||
exit();
|
||||
}
|
||||
} else {
|
||||
echo json_encode(['success' => false, 'message' => 'Неверный метод запроса']);
|
||||
exit();
|
||||
}
|
||||
?>
|
||||
@@ -1,182 +1,162 @@
|
||||
<?php
|
||||
// register_handler.php
|
||||
session_start();
|
||||
require_once 'config/database.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$errors = [];
|
||||
|
||||
// Получаем данные из формы
|
||||
$full_name = trim($_POST['fio'] ?? '');
|
||||
$city = trim($_POST['city'] ?? '');
|
||||
$email = trim($_POST['email'] ?? '');
|
||||
$phone = trim($_POST['phone'] ?? '');
|
||||
$password = $_POST['password'] ?? '';
|
||||
$confirm_password = $_POST['confirm-password'] ?? '';
|
||||
|
||||
// Валидация данных
|
||||
if (empty($full_name) || strlen($full_name) < 3) {
|
||||
$errors[] = 'ФИО должно содержать минимум 3 символа';
|
||||
}
|
||||
|
||||
if (empty($city) || strlen($city) < 2) {
|
||||
$errors[] = 'Введите корректное название города';
|
||||
}
|
||||
|
||||
if (empty($email) || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||
$errors[] = 'Введите корректный email адрес';
|
||||
}
|
||||
|
||||
if (empty($phone) || !preg_match('/^(\+7|8)[\s-]?\(?\d{3}\)?[\s-]?\d{3}[\s-]?\d{2}[\s-]?\d{2}$/', $phone)) {
|
||||
$errors[] = 'Введите корректный номер телефона';
|
||||
}
|
||||
|
||||
if (empty($password) || strlen($password) < 6) {
|
||||
$errors[] = 'Пароль должен содержать минимум 6 символов';
|
||||
}
|
||||
|
||||
if ($password !== $confirm_password) {
|
||||
$errors[] = 'Пароли не совпадают';
|
||||
}
|
||||
|
||||
// Проверка согласия с условиями
|
||||
if (!isset($_POST['privacy']) || $_POST['privacy'] !== 'on') {
|
||||
$errors[] = 'Необходимо согласие с условиями обработки персональных данных';
|
||||
}
|
||||
|
||||
// Если есть ошибки, возвращаем на форму
|
||||
if (!empty($errors)) {
|
||||
$_SESSION['registration_errors'] = $errors;
|
||||
$_SESSION['old_data'] = [
|
||||
'fio' => $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();
|
||||
}
|
||||
<?php
|
||||
|
||||
session_start();
|
||||
require_once __DIR__ . '/../config/database.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$errors = [];
|
||||
|
||||
$full_name = trim($_POST['fio'] ?? '');
|
||||
$city = trim($_POST['city'] ?? '');
|
||||
$email = trim($_POST['email'] ?? '');
|
||||
$phone = trim($_POST['phone'] ?? '');
|
||||
$password = $_POST['password'] ?? '';
|
||||
$confirm_password = $_POST['confirm-password'] ?? '';
|
||||
|
||||
if (empty($full_name) || strlen($full_name) < 3) {
|
||||
$errors[] = 'ФИО должно содержать минимум 3 символа';
|
||||
}
|
||||
|
||||
if (empty($city) || strlen($city) < 2) {
|
||||
$errors[] = 'Введите корректное название города';
|
||||
}
|
||||
|
||||
if (empty($email) || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||
$errors[] = 'Введите корректный email адрес';
|
||||
}
|
||||
|
||||
if (empty($phone) || !preg_match('/^(\+7|8)[\s-]?\(?\d{3}\)?[\s-]?\d{3}[\s-]?\d{2}[\s-]?\d{2}$/', $phone)) {
|
||||
$errors[] = 'Введите корректный номер телефона';
|
||||
}
|
||||
|
||||
if (empty($password) || strlen($password) < 6) {
|
||||
$errors[] = 'Пароль должен содержать минимум 6 символов';
|
||||
}
|
||||
|
||||
if ($password !== $confirm_password) {
|
||||
$errors[] = 'Пароли не совпадают';
|
||||
}
|
||||
|
||||
if (!isset($_POST['privacy']) || $_POST['privacy'] !== 'on') {
|
||||
$errors[] = 'Необходимо согласие с условиями обработки персональных данных';
|
||||
}
|
||||
|
||||
if (!empty($errors)) {
|
||||
$_SESSION['registration_errors'] = $errors;
|
||||
$_SESSION['old_data'] = [
|
||||
'fio' => $full_name,
|
||||
'city' => $city,
|
||||
'email' => $email,
|
||||
'phone' => $phone
|
||||
];
|
||||
header('Location: ../register.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
$db = Database::getInstance()->getConnection();
|
||||
|
||||
try {
|
||||
|
||||
$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: ../register.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;
|
||||
}
|
||||
|
||||
$stmt = $db->prepare("
|
||||
INSERT INTO users (email, password_hash, full_name, phone, city, is_admin, is_active)
|
||||
VALUES (?, ?, ?, ?, ?, CAST(? AS boolean), TRUE)
|
||||
RETURNING user_id
|
||||
");
|
||||
|
||||
$stmt->execute([
|
||||
$email,
|
||||
$password_hash,
|
||||
$full_name,
|
||||
$phone,
|
||||
$city,
|
||||
$is_admin ? 'true' : 'false'
|
||||
]);
|
||||
|
||||
$user_id = $stmt->fetchColumn();
|
||||
|
||||
if (!$user_id) {
|
||||
throw new Exception('Ошибка при создании пользователя: user_id не получен');
|
||||
}
|
||||
|
||||
$verifyStmt = $db->prepare("SELECT user_id, email, password_hash FROM users WHERE user_id = ?");
|
||||
$verifyStmt->execute([$user_id]);
|
||||
$verifyUser = $verifyStmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$verifyUser) {
|
||||
throw new Exception('Ошибка: пользователь не найден после создания');
|
||||
}
|
||||
|
||||
if (empty($verifyUser['password_hash'])) {
|
||||
throw new Exception('Ошибка: пароль не сохранен');
|
||||
}
|
||||
|
||||
$_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'] = (bool)$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();
|
||||
|
||||
} 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: ../register.php');
|
||||
exit();
|
||||
} catch (Exception $e) {
|
||||
error_log("Registration Error: " . $e->getMessage());
|
||||
|
||||
$_SESSION['registration_errors'] = [$e->getMessage()];
|
||||
header('Location: ../register.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
header('Location: register.php');
|
||||
exit();
|
||||
}
|
||||
?>
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once 'config/database.php';
|
||||
require_once __DIR__ . '/../config/database.php';
|
||||
|
||||
if (!isset($_SESSION['isLoggedIn']) || $_SESSION['isLoggedIn'] !== true) {
|
||||
echo json_encode(['success' => false, 'message' => 'Требуется авторизация']);
|
||||
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 78 KiB |
|
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 183 KiB After Width: | Height: | Size: 183 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 177 KiB After Width: | Height: | Size: 177 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 77 KiB After Width: | Height: | Size: 77 KiB |
|
Before Width: | Height: | Size: 190 KiB After Width: | Height: | Size: 190 KiB |
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 89 KiB After Width: | Height: | Size: 89 KiB |
|
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 414 KiB After Width: | Height: | Size: 414 KiB |
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 170 KiB After Width: | Height: | Size: 170 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 291 KiB After Width: | Height: | Size: 291 KiB |
|
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 68 KiB |
|
Before Width: | Height: | Size: 90 KiB After Width: | Height: | Size: 90 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 198 KiB After Width: | Height: | Size: 198 KiB |
|
Before Width: | Height: | Size: 113 KiB After Width: | Height: | Size: 113 KiB |
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 372 KiB After Width: | Height: | Size: 372 KiB |
|
Before Width: | Height: | Size: 96 KiB After Width: | Height: | Size: 96 KiB |
|
Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 79 KiB |
|
Before Width: | Height: | Size: 265 KiB After Width: | Height: | Size: 265 KiB |
|
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 66 KiB |
|
Before Width: | Height: | Size: 555 KiB After Width: | Height: | Size: 555 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 479 KiB After Width: | Height: | Size: 479 KiB |
|
Before Width: | Height: | Size: 91 KiB After Width: | Height: | Size: 91 KiB |
|
Before Width: | Height: | Size: 124 KiB After Width: | Height: | Size: 124 KiB |
|
Before Width: | Height: | Size: 176 KiB After Width: | Height: | Size: 176 KiB |
|
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 430 KiB After Width: | Height: | Size: 430 KiB |
|
Before Width: | Height: | Size: 167 KiB After Width: | Height: | Size: 167 KiB |
|
Before Width: | Height: | Size: 88 KiB After Width: | Height: | Size: 88 KiB |
|
Before Width: | Height: | Size: 1.0 MiB After Width: | Height: | Size: 1.0 MiB |
|
Before Width: | Height: | Size: 140 KiB After Width: | Height: | Size: 140 KiB |
|
Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 67 KiB |
|
Before Width: | Height: | Size: 497 KiB After Width: | Height: | Size: 497 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 759 KiB After Width: | Height: | Size: 759 KiB |
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 1.3 MiB |