Fix LESS import error and refactor project structure
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>";
|
|
||||||
?>
|
|
||||||
63
.gitignore
vendored
@@ -1,49 +1,46 @@
|
|||||||
# IDE и редакторы
|
# Dependencies
|
||||||
|
/vendor/
|
||||||
|
/node_modules/
|
||||||
|
|
||||||
|
# IDE
|
||||||
.idea/
|
.idea/
|
||||||
.vscode/
|
.vscode/
|
||||||
*.swp
|
*.swp
|
||||||
*.swo
|
*.swo
|
||||||
*~
|
*~
|
||||||
|
|
||||||
|
# OS
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
# Зависимости
|
# Environment
|
||||||
/vendor/
|
|
||||||
/node_modules/
|
|
||||||
|
|
||||||
# Логи
|
|
||||||
*.log
|
|
||||||
logs/
|
|
||||||
|
|
||||||
# Загруженные файлы пользователей
|
|
||||||
/uploads/products/*
|
|
||||||
!/uploads/products/.gitkeep
|
|
||||||
|
|
||||||
# Конфигурационные файлы с секретами (если есть)
|
|
||||||
.env
|
.env
|
||||||
.env.local
|
.env.local
|
||||||
.env.*.local
|
.env.*.local
|
||||||
|
|
||||||
# Кэш
|
# Logs
|
||||||
|
*.log
|
||||||
|
/logs/
|
||||||
|
|
||||||
|
# Storage (uploads are gitignored, keep structure)
|
||||||
|
/storage/uploads/*
|
||||||
|
!/storage/uploads/.gitkeep
|
||||||
|
|
||||||
|
# Cache
|
||||||
/cache/
|
/cache/
|
||||||
*.cache
|
*.cache
|
||||||
|
|
||||||
# Временные файлы
|
# Compiled assets
|
||||||
|
/public/assets/css/*.css
|
||||||
|
!/public/assets/css/.gitkeep
|
||||||
|
|
||||||
|
# Docker volumes
|
||||||
|
/docker/data/
|
||||||
|
|
||||||
|
# Tests
|
||||||
|
/coverage/
|
||||||
|
.phpunit.result.cache
|
||||||
|
|
||||||
|
# Temporary files
|
||||||
/tmp/
|
/tmp/
|
||||||
*.tmp
|
*.tmp
|
||||||
|
|
||||||
# Скомпилированные CSS
|
|
||||||
*.css.map
|
|
||||||
|
|
||||||
# База данных SQLite (если используется локально)
|
|
||||||
*.db
|
|
||||||
*.sqlite
|
|
||||||
*.sqlite3
|
|
||||||
|
|
||||||
# Файлы резервных копий
|
|
||||||
*.bak
|
|
||||||
*.backup
|
|
||||||
|
|
||||||
# PHP debug/profiling
|
|
||||||
.phpunit.result.cache
|
|
||||||
phpunit.xml
|
|
||||||
|
|
||||||
|
|||||||
29
.htaccess
@@ -1,29 +0,0 @@
|
|||||||
# AETERNA MVC - Корневой .htaccess
|
|
||||||
# Перенаправляет все запросы в public/
|
|
||||||
|
|
||||||
<IfModule mod_rewrite.c>
|
|
||||||
RewriteEngine On
|
|
||||||
|
|
||||||
# Если запрос к assets (статика) - перенаправляем в public/assets
|
|
||||||
RewriteCond %{REQUEST_URI} ^/cite_practica/assets/
|
|
||||||
RewriteRule ^assets/(.*)$ public/assets/$1 [L]
|
|
||||||
|
|
||||||
# Если запрос к старым img директориям - оставляем как есть
|
|
||||||
RewriteCond %{REQUEST_URI} ^/cite_practica/img/
|
|
||||||
RewriteRule ^img/(.*)$ img/$1 [L]
|
|
||||||
|
|
||||||
RewriteCond %{REQUEST_URI} ^/cite_practica/img2/
|
|
||||||
RewriteRule ^img2/(.*)$ img2/$1 [L]
|
|
||||||
|
|
||||||
# Если это существующий файл (для обратной совместимости) - пропускаем
|
|
||||||
RewriteCond %{REQUEST_FILENAME} -f
|
|
||||||
RewriteRule ^ - [L]
|
|
||||||
|
|
||||||
# Все остальное - в public/index.php
|
|
||||||
RewriteCond %{REQUEST_FILENAME} !-d
|
|
||||||
RewriteRule ^(.*)$ public/index.php [QSA,L]
|
|
||||||
</IfModule>
|
|
||||||
|
|
||||||
# Отключаем просмотр директорий
|
|
||||||
Options -Indexes
|
|
||||||
|
|
||||||
19
Dockerfile
@@ -1,6 +1,5 @@
|
|||||||
FROM php:8.2-apache
|
FROM php:8.2-apache
|
||||||
|
|
||||||
# Установка расширений PHP
|
|
||||||
RUN apt-get update && apt-get install -y \
|
RUN apt-get update && apt-get install -y \
|
||||||
libpq-dev \
|
libpq-dev \
|
||||||
libzip-dev \
|
libzip-dev \
|
||||||
@@ -9,26 +8,22 @@ RUN apt-get update && apt-get install -y \
|
|||||||
&& apt-get clean \
|
&& apt-get clean \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
# Включаем mod_rewrite
|
RUN a2enmod rewrite headers expires
|
||||||
RUN a2enmod rewrite
|
|
||||||
|
|
||||||
# Копируем конфигурацию Apache
|
COPY docker/apache/vhosts.conf /etc/apache2/sites-available/000-default.conf
|
||||||
COPY docker/apache/vhosts.conf /etc/apache2/sites-available/vhosts.conf
|
|
||||||
COPY docker/apache/entrypoint.sh /usr/local/bin/entrypoint.sh
|
COPY docker/apache/entrypoint.sh /usr/local/bin/entrypoint.sh
|
||||||
RUN chmod +x /usr/local/bin/entrypoint.sh
|
RUN chmod +x /usr/local/bin/entrypoint.sh
|
||||||
|
|
||||||
# Рабочая директория
|
|
||||||
WORKDIR /var/www/html
|
WORKDIR /var/www/html
|
||||||
|
|
||||||
# Копируем приложение
|
|
||||||
COPY . /var/www/html/
|
COPY . /var/www/html/
|
||||||
|
|
||||||
# Устанавливаем права
|
RUN mkdir -p /var/www/html/storage/uploads \
|
||||||
RUN chown -R www-data:www-data /var/www/html
|
&& mkdir -p /var/www/html/public/assets/css
|
||||||
|
|
||||||
|
RUN chown -R www-data:www-data /var/www/html \
|
||||||
|
&& chmod -R 755 /var/www/html/storage
|
||||||
|
|
||||||
# Экспортируем порт
|
|
||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
|
|
||||||
# Точка входа
|
|
||||||
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
|
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
|
||||||
|
|
||||||
|
|||||||
171
README.md
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
# AETERNA - Интернет-магазин мебели
|
||||||
|
|
||||||
|
Современный интернет-магазин мебели на PHP с MVC архитектурой.
|
||||||
|
|
||||||
|
## Структура проекта
|
||||||
|
|
||||||
|
```
|
||||||
|
aeterna/
|
||||||
|
├── app/ # Приложение
|
||||||
|
│ ├── Controllers/ # Контроллеры
|
||||||
|
│ ├── Core/ # Ядро (App, Router, View, etc.)
|
||||||
|
│ ├── Models/ # Модели
|
||||||
|
│ └── Views/ # Шаблоны
|
||||||
|
├── config/ # Конфигурация
|
||||||
|
│ ├── app.php # Настройки приложения
|
||||||
|
│ ├── database.php # Настройки БД
|
||||||
|
│ └── routes.php # Маршруты
|
||||||
|
├── public/ # Публичная директория (DocumentRoot)
|
||||||
|
│ ├── index.php # Точка входа
|
||||||
|
│ ├── assets/ # Статические файлы
|
||||||
|
│ │ ├── css/
|
||||||
|
│ │ ├── js/
|
||||||
|
│ │ └── images/ # Изображения
|
||||||
|
│ └── .htaccess # Правила Apache
|
||||||
|
├── storage/ # Хранилище
|
||||||
|
│ └── uploads/ # Загруженные файлы
|
||||||
|
├── tests/ # Тесты
|
||||||
|
├── docker/ # Docker конфигурация
|
||||||
|
│ └── apache/
|
||||||
|
├── docker-compose.yml
|
||||||
|
├── Dockerfile
|
||||||
|
└── README.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## Требования
|
||||||
|
|
||||||
|
- PHP 8.2+
|
||||||
|
- PostgreSQL 14+
|
||||||
|
- Apache с mod_rewrite или Nginx
|
||||||
|
- Docker (опционально)
|
||||||
|
|
||||||
|
## Установка
|
||||||
|
|
||||||
|
### Вариант 1: Docker (рекомендуется)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Клонировать репозиторий
|
||||||
|
git clone <repository-url>
|
||||||
|
cd aeterna
|
||||||
|
|
||||||
|
# Запустить контейнеры
|
||||||
|
docker-compose up -d
|
||||||
|
|
||||||
|
# Приложение будет доступно по адресу:
|
||||||
|
# http://localhost:8080
|
||||||
|
```
|
||||||
|
|
||||||
|
### Вариант 2: Локальный сервер
|
||||||
|
|
||||||
|
#### Apache
|
||||||
|
|
||||||
|
1. Настройте DocumentRoot на директорию `public/`
|
||||||
|
|
||||||
|
```apache
|
||||||
|
<VirtualHost *:80>
|
||||||
|
ServerName aeterna.local
|
||||||
|
DocumentRoot /path/to/aeterna/public
|
||||||
|
|
||||||
|
<Directory /path/to/aeterna/public>
|
||||||
|
Options -Indexes +FollowSymLinks
|
||||||
|
AllowOverride All
|
||||||
|
Require all granted
|
||||||
|
</Directory>
|
||||||
|
</VirtualHost>
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Включите mod_rewrite:
|
||||||
|
```bash
|
||||||
|
sudo a2enmod rewrite
|
||||||
|
sudo systemctl restart apache2
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Nginx
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name aeterna.local;
|
||||||
|
root /path/to/aeterna/public;
|
||||||
|
index index.php;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ /index.php?$query_string;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~ \.php$ {
|
||||||
|
fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
|
||||||
|
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
|
||||||
|
include fastcgi_params;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~ /\.(?!well-known).* {
|
||||||
|
deny all;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Конфигурация
|
||||||
|
|
||||||
|
### База данных
|
||||||
|
|
||||||
|
Отредактируйте `config/database.php` или используйте переменные окружения:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export DB_HOST=localhost
|
||||||
|
export DB_PORT=5432
|
||||||
|
export DB_DATABASE=aeterna
|
||||||
|
export DB_USERNAME=user
|
||||||
|
export DB_PASSWORD=password
|
||||||
|
```
|
||||||
|
|
||||||
|
### Приложение
|
||||||
|
|
||||||
|
Отредактируйте `config/app.php`:
|
||||||
|
|
||||||
|
- `debug` - режим отладки (false для продакшена)
|
||||||
|
- `url` - URL приложения
|
||||||
|
- `admin_emails` - email адреса администраторов
|
||||||
|
|
||||||
|
## Функционал
|
||||||
|
|
||||||
|
### Для покупателей
|
||||||
|
- Каталог товаров с фильтрацией
|
||||||
|
- Корзина покупок
|
||||||
|
- Оформление заказов
|
||||||
|
- Регистрация и авторизация
|
||||||
|
|
||||||
|
### Для администраторов
|
||||||
|
- Управление товарами
|
||||||
|
- Управление категориями
|
||||||
|
- Управление заказами
|
||||||
|
- Управление пользователями
|
||||||
|
|
||||||
|
## Маршруты
|
||||||
|
|
||||||
|
| Метод | URL | Описание |
|
||||||
|
|-------|-----|----------|
|
||||||
|
| GET | `/` | Главная страница |
|
||||||
|
| GET | `/catalog` | Каталог товаров |
|
||||||
|
| GET | `/product/{id}` | Страница товара |
|
||||||
|
| GET | `/cart` | Корзина |
|
||||||
|
| GET | `/login` | Вход |
|
||||||
|
| GET | `/register` | Регистрация |
|
||||||
|
| GET | `/admin` | Админ-панель |
|
||||||
|
|
||||||
|
## Технологии
|
||||||
|
|
||||||
|
- **Backend**: PHP 8.2, MVC архитектура
|
||||||
|
- **Database**: PostgreSQL
|
||||||
|
- **Frontend**: HTML5, CSS3/LESS, JavaScript, jQuery
|
||||||
|
- **Сервер**: Apache/Nginx
|
||||||
|
- **Контейнеризация**: Docker
|
||||||
|
|
||||||
|
## Лицензия
|
||||||
|
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
## Автор
|
||||||
|
|
||||||
|
AETERNA Team
|
||||||
|
|
||||||
116
add_to_cart.php
@@ -1,116 +0,0 @@
|
|||||||
<?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' => 'Неверный запрос']);
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
@@ -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();
|
|
||||||
?>
|
|
||||||
772
admin_panel.php
@@ -1,772 +0,0 @@
|
|||||||
<?php
|
|
||||||
// admin_panel.php - ПОЛНОСТЬЮ ИСПРАВЛЕННАЯ ВЕРСИЯ
|
|
||||||
session_start();
|
|
||||||
require_once 'config/database.php';
|
|
||||||
|
|
||||||
// Включаем отладку ошибок
|
|
||||||
error_reporting(E_ALL);
|
|
||||||
ini_set('display_errors', 1);
|
|
||||||
|
|
||||||
if (empty($allCategories)) {
|
|
||||||
echo '<div class="alert alert-warning">Сначала добавьте категории!</div>';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Проверка прав администратора
|
|
||||||
if (!isset($_SESSION['isAdmin']) || $_SESSION['isAdmin'] !== true) {
|
|
||||||
echo "<script>alert('Требуется авторизация администратора'); window.location.href = 'вход.php';</script>";
|
|
||||||
exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
$db = Database::getInstance()->getConnection();
|
|
||||||
|
|
||||||
// Обработка действий
|
|
||||||
$action = $_GET['action'] ?? 'dashboard';
|
|
||||||
$message = $_GET['message'] ?? '';
|
|
||||||
$error = $_GET['error'] ?? '';
|
|
||||||
|
|
||||||
// Обработка POST запросов - ДОБАВЛЕНО ПРОСТОЕ И РАБОТАЮЩЕЕ!
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|
||||||
$post_action = $_POST['action'] ?? '';
|
|
||||||
|
|
||||||
try {
|
|
||||||
if ($post_action === 'add_category') {
|
|
||||||
$name = trim($_POST['name'] ?? '');
|
|
||||||
$slug = strtolower(preg_replace('/[^a-z0-9]+/i', '-', $name));
|
|
||||||
$parent_id = !empty($_POST['parent_id']) ? (int)$_POST['parent_id'] : NULL;
|
|
||||||
$description = trim($_POST['description'] ?? '');
|
|
||||||
$sort_order = (int)($_POST['sort_order'] ?? 0);
|
|
||||||
$is_active = isset($_POST['is_active']) ? 1 : 0;
|
|
||||||
|
|
||||||
if (empty($name)) {
|
|
||||||
throw new Exception('Название категории обязательно');
|
|
||||||
}
|
|
||||||
|
|
||||||
$stmt = $db->prepare("
|
|
||||||
INSERT INTO categories (name, slug, parent_id, description, sort_order, is_active)
|
|
||||||
VALUES (?, ?, ?, ?, ?, ?)
|
|
||||||
");
|
|
||||||
|
|
||||||
$result = $stmt->execute([$name, $slug, $parent_id, $description, $sort_order, $is_active]);
|
|
||||||
|
|
||||||
if ($result) {
|
|
||||||
header('Location: admin_panel.php?action=categories&message=Категория+успешно+добавлена');
|
|
||||||
exit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ИСПРАВЬТЕ БЛОК edit_category или добавьте его если его нет:
|
|
||||||
if ($post_action === 'edit_category' && isset($_POST['category_id'])) {
|
|
||||||
$category_id = (int)$_POST['category_id'];
|
|
||||||
$name = trim($_POST['name'] ?? '');
|
|
||||||
$parent_id = !empty($_POST['parent_id']) ? (int)$_POST['parent_id'] : NULL;
|
|
||||||
$description = trim($_POST['description'] ?? '');
|
|
||||||
$sort_order = (int)($_POST['sort_order'] ?? 0);
|
|
||||||
$is_active = isset($_POST['is_active']) ? 1 : 0;
|
|
||||||
|
|
||||||
if (empty($name)) {
|
|
||||||
throw new Exception('Название категории обязательно');
|
|
||||||
}
|
|
||||||
|
|
||||||
$slug = strtolower(preg_replace('/[^a-z0-9]+/i', '-', $name));
|
|
||||||
|
|
||||||
$stmt = $db->prepare("
|
|
||||||
UPDATE categories SET
|
|
||||||
name = ?,
|
|
||||||
slug = ?,
|
|
||||||
parent_id = ?,
|
|
||||||
description = ?,
|
|
||||||
sort_order = ?,
|
|
||||||
is_active = ?,
|
|
||||||
updated_at = CURRENT_TIMESTAMP
|
|
||||||
WHERE category_id = ?
|
|
||||||
");
|
|
||||||
|
|
||||||
$stmt->execute([$name, $slug, $parent_id, $description, $sort_order, $is_active, $category_id]);
|
|
||||||
|
|
||||||
header('Location: admin_panel.php?action=categories&message=Категория+обновлена');
|
|
||||||
exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($post_action === 'add_product') {
|
|
||||||
$name = trim($_POST['name'] ?? '');
|
|
||||||
$slug = strtolower(preg_replace('/[^a-z0-9]+/i', '-', $name));
|
|
||||||
$category_id = (int)($_POST['category_id'] ?? 0);
|
|
||||||
$description = trim($_POST['description'] ?? '');
|
|
||||||
$price = (float)($_POST['price'] ?? 0);
|
|
||||||
$old_price = !empty($_POST['old_price']) ? (float)$_POST['old_price'] : NULL;
|
|
||||||
$sku = trim($_POST['sku'] ?? '');
|
|
||||||
$stock_quantity = (int)($_POST['stock_quantity'] ?? 0);
|
|
||||||
$is_available = isset($_POST['is_available']) ? 1 : 0;
|
|
||||||
$is_featured = isset($_POST['is_featured']) ? 1 : 0;
|
|
||||||
$image_url = trim($_POST['image_url'] ?? '');
|
|
||||||
$color = trim($_POST['color'] ?? '');
|
|
||||||
$material = trim($_POST['material'] ?? '');
|
|
||||||
$card_size = trim($_POST['card_size'] ?? 'small');
|
|
||||||
|
|
||||||
|
|
||||||
// ВАЖНО: Проверяем category_id
|
|
||||||
if ($category_id <= 0) {
|
|
||||||
$_SESSION['error'] = 'Выберите корректную категорию';
|
|
||||||
header('Location: admin_panel.php?action=add_product');
|
|
||||||
exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Проверяем существование категории
|
|
||||||
$check_category = $db->prepare("SELECT COUNT(*) FROM categories WHERE category_id = ?");
|
|
||||||
$check_category->execute([$category_id]);
|
|
||||||
if ($check_category->fetchColumn() == 0) {
|
|
||||||
$_SESSION['error'] = 'Выбранная категория не существует';
|
|
||||||
header('Location: admin_panel.php?action=add_product');
|
|
||||||
exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (empty($name)) throw new Exception('Название товара обязательно');
|
|
||||||
if ($price <= 0) throw new Exception('Цена должна быть больше 0');
|
|
||||||
|
|
||||||
// Генерируем SKU если пустой
|
|
||||||
if (empty($sku)) {
|
|
||||||
$sku = 'PROD-' . strtoupper(substr(preg_replace('/[^a-z0-9]/i', '', $name), 0, 6)) . '-' . rand(100, 999);
|
|
||||||
}
|
|
||||||
|
|
||||||
$stmt = $db->prepare("
|
|
||||||
INSERT INTO products (
|
|
||||||
category_id, name, slug, description, price, old_price,
|
|
||||||
sku, stock_quantity, is_available, is_featured, image_url,
|
|
||||||
color, material, card_size
|
|
||||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
||||||
");
|
|
||||||
|
|
||||||
$result = $stmt->execute([
|
|
||||||
$category_id, $name, $slug, $description, $price, $old_price,
|
|
||||||
$sku, $stock_quantity, $is_available, $is_featured, $image_url,
|
|
||||||
$color, $material, $card_size
|
|
||||||
]);
|
|
||||||
|
|
||||||
if ($result) {
|
|
||||||
$_SESSION['message'] = 'Товар успешно добавлен';
|
|
||||||
header('Location: admin_panel.php?action=products');
|
|
||||||
exit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ИСПРАВЛЕННЫЙ КОД для edit_product в admin_panel.php:
|
|
||||||
if ($post_action === 'edit_product' && isset($_POST['product_id'])) {
|
|
||||||
$product_id = (int)$_POST['product_id'];
|
|
||||||
$name = trim($_POST['name'] ?? '');
|
|
||||||
$category_id = (int)($_POST['category_id'] ?? 1); // ПО УМОЛЧАНИЮ 1, чтобы избежать 0
|
|
||||||
$description = trim($_POST['description'] ?? '');
|
|
||||||
$price = (float)($_POST['price'] ?? 0);
|
|
||||||
$old_price = !empty($_POST['old_price']) ? (float)$_POST['old_price'] : NULL;
|
|
||||||
$stock_quantity = (int)($_POST['stock_quantity'] ?? 0);
|
|
||||||
$is_available = isset($_POST['is_available']) ? 1 : 0;
|
|
||||||
$image_url = trim($_POST['image_url'] ?? '');
|
|
||||||
$color = trim($_POST['color'] ?? '');
|
|
||||||
$material = trim($_POST['material'] ?? '');
|
|
||||||
|
|
||||||
// ВАЖНО: Проверяем category_id
|
|
||||||
if ($category_id <= 0) {
|
|
||||||
// Если category_id = 0, устанавливаем первую доступную категорию
|
|
||||||
$firstCat = $db->query("SELECT category_id FROM categories LIMIT 1")->fetchColumn();
|
|
||||||
$category_id = $firstCat ?: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
$stmt = $db->prepare("
|
|
||||||
UPDATE products SET
|
|
||||||
name = ?,
|
|
||||||
category_id = ?,
|
|
||||||
description = ?,
|
|
||||||
price = ?,
|
|
||||||
old_price = ?,
|
|
||||||
stock_quantity = ?,
|
|
||||||
is_available = ?,
|
|
||||||
image_url = ?,
|
|
||||||
color = ?,
|
|
||||||
material = ?,
|
|
||||||
updated_at = CURRENT_TIMESTAMP
|
|
||||||
WHERE product_id = ?
|
|
||||||
");
|
|
||||||
|
|
||||||
$stmt->execute([
|
|
||||||
$name, $category_id, $description, $price, $old_price,
|
|
||||||
$stock_quantity, $is_available, $image_url, $color, $material, $product_id
|
|
||||||
]);
|
|
||||||
|
|
||||||
header('Location: admin_panel.php?action=products&message=Товар+обновлен');
|
|
||||||
exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($post_action === 'delete_category' && isset($_POST['category_id'])) {
|
|
||||||
$categoryId = intval($_POST['category_id']);
|
|
||||||
|
|
||||||
// 1. Проверяем, есть ли товары в этой категории
|
|
||||||
$checkProducts = $db->prepare("SELECT COUNT(*) FROM products WHERE category_id = ?");
|
|
||||||
$checkProducts->execute([$categoryId]);
|
|
||||||
$productCount = $checkProducts->fetchColumn();
|
|
||||||
|
|
||||||
// 2. Проверяем, есть ли дочерние категории
|
|
||||||
$checkChildren = $db->prepare("SELECT COUNT(*) FROM categories WHERE parent_id = ?");
|
|
||||||
$checkChildren->execute([$categoryId]);
|
|
||||||
$childCount = $checkChildren->fetchColumn();
|
|
||||||
|
|
||||||
if ($productCount > 0) {
|
|
||||||
// Если есть товары, делаем категорию неактивной вместо удаления
|
|
||||||
$stmt = $db->prepare("UPDATE categories SET is_active = FALSE WHERE category_id = ?");
|
|
||||||
$stmt->execute([$categoryId]);
|
|
||||||
|
|
||||||
header('Location: admin_panel.php?action=categories&message=Категория+скрыта+(содержит+товары)');
|
|
||||||
exit();
|
|
||||||
} elseif ($childCount > 0) {
|
|
||||||
// Если есть дочерние категории, делаем неактивной
|
|
||||||
$stmt = $db->prepare("UPDATE categories SET is_active = FALSE WHERE category_id = ?");
|
|
||||||
$stmt->execute([$categoryId]);
|
|
||||||
|
|
||||||
header('Location: admin_panel.php?action=categories&message=Категория+скрыта+(имеет+дочерние+категории)');
|
|
||||||
exit();
|
|
||||||
} else {
|
|
||||||
// Если нет товаров и дочерних категорий, удаляем
|
|
||||||
$stmt = $db->prepare("DELETE FROM categories WHERE category_id = ?");
|
|
||||||
$stmt->execute([$categoryId]);
|
|
||||||
|
|
||||||
header('Location: admin_panel.php?action=categories&message=Категория+удалена');
|
|
||||||
exit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (PDOException $e) {
|
|
||||||
header('Location: admin_panel.php?action=' . $action . '&error=' . urlencode('Ошибка БД: ' . $e->getMessage()));
|
|
||||||
exit();
|
|
||||||
} catch (Exception $e) {
|
|
||||||
header('Location: admin_panel.php?action=' . $action . '&error=' . urlencode($e->getMessage()));
|
|
||||||
exit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Получение данных для отображения
|
|
||||||
try {
|
|
||||||
// Статистика
|
|
||||||
$stats = [
|
|
||||||
'total_products' => $db->query("SELECT COUNT(*) FROM products")->fetchColumn(),
|
|
||||||
'active_products' => $db->query("SELECT COUNT(*) FROM products WHERE is_available = TRUE")->fetchColumn(),
|
|
||||||
'total_orders' => $db->query("SELECT COUNT(*) FROM orders")->fetchColumn(),
|
|
||||||
'total_users' => $db->query("SELECT COUNT(*) FROM users")->fetchColumn(),
|
|
||||||
'revenue' => $db->query("SELECT COALESCE(SUM(final_amount), 0) FROM orders WHERE status = 'completed'")->fetchColumn()
|
|
||||||
];
|
|
||||||
|
|
||||||
// Получаем все категории
|
|
||||||
$allCategories = $db->query("SELECT * FROM categories WHERE is_active = TRUE ORDER BY name")->fetchAll();
|
|
||||||
|
|
||||||
// Получаем родительские категории
|
|
||||||
$parentCategories = $db->query("SELECT * FROM categories WHERE parent_id IS NULL AND is_active = TRUE ORDER BY name")->fetchAll();
|
|
||||||
|
|
||||||
switch ($action) {
|
|
||||||
case 'products':
|
|
||||||
$showAll = isset($_GET['show_all']) && $_GET['show_all'] == '1';
|
|
||||||
$sql = $showAll
|
|
||||||
? "SELECT p.*, c.name as category_name FROM products p LEFT JOIN categories c ON p.category_id = c.category_id ORDER BY p.created_at DESC"
|
|
||||||
: "SELECT p.*, c.name as category_name FROM products p LEFT JOIN categories c ON p.category_id = c.category_id WHERE p.is_available = TRUE ORDER BY p.created_at DESC";
|
|
||||||
$data = $db->query($sql)->fetchAll();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'categories':
|
|
||||||
$data = $db->query("
|
|
||||||
SELECT c1.*, c2.name as parent_name,
|
|
||||||
(SELECT COUNT(*) FROM products p WHERE p.category_id = c1.category_id) as product_count
|
|
||||||
FROM categories c1
|
|
||||||
LEFT JOIN categories c2 ON c1.parent_id = c2.category_id
|
|
||||||
ORDER BY c1.sort_order, c1.name
|
|
||||||
")->fetchAll();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'orders':
|
|
||||||
$data = $db->query("
|
|
||||||
SELECT o.*, u.email as user_email
|
|
||||||
FROM orders o
|
|
||||||
LEFT JOIN users u ON o.user_id = u.user_id
|
|
||||||
ORDER BY o.created_at DESC
|
|
||||||
LIMIT 50
|
|
||||||
")->fetchAll();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'users':
|
|
||||||
$data = $db->query("SELECT * FROM users ORDER BY created_at DESC LIMIT 50")->fetchAll();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'add_product':
|
|
||||||
case 'edit_product':
|
|
||||||
if ($action === 'edit_product' && isset($_GET['id'])) {
|
|
||||||
$productId = (int)$_GET['id'];
|
|
||||||
$stmt = $db->prepare("SELECT * FROM products WHERE product_id = ?");
|
|
||||||
$stmt->execute([$productId]);
|
|
||||||
$edit_data = $stmt->fetch();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'add_category':
|
|
||||||
case 'edit_category':
|
|
||||||
if ($action === 'edit_category' && isset($_GET['id'])) {
|
|
||||||
$categoryId = (int)$_GET['id'];
|
|
||||||
$stmt = $db->prepare("SELECT * FROM categories WHERE category_id = ?");
|
|
||||||
$stmt->execute([$categoryId]);
|
|
||||||
$edit_data = $stmt->fetch();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (PDOException $e) {
|
|
||||||
$error = "Ошибка базы данных: " . $e->getMessage();
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
<!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" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
|
|
||||||
<style>
|
|
||||||
body { font-family: Arial, sans-serif; margin: 0; padding: 0; background: #f5f5f5; }
|
|
||||||
.admin-header { background: #453227; color: white; padding: 20px; display: flex; justify-content: space-between; align-items: center; }
|
|
||||||
.admin-tabs { background: white; padding: 10px; border-bottom: 2px solid #453227; display: flex; gap: 10px; }
|
|
||||||
.admin-tab { padding: 10px 20px; border-radius: 5px; text-decoration: none; color: #333; }
|
|
||||||
.admin-tab:hover, .admin-tab.active { background: #453227; color: white; }
|
|
||||||
.admin-content { padding: 20px; }
|
|
||||||
.form-container { background: white; padding: 20px; border-radius: 5px; box-shadow: 0 2px 5px rgba(0,0,0,0.1); max-width: 800px; margin: 0 auto; }
|
|
||||||
.form-group { margin-bottom: 15px; }
|
|
||||||
.form-group label { display: block; margin-bottom: 5px; font-weight: bold; }
|
|
||||||
.form-control { width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; }
|
|
||||||
.btn { padding: 10px 20px; border: none; border-radius: 4px; cursor: pointer; text-decoration: none; display: inline-block; }
|
|
||||||
.btn-primary { background: #453227; color: white; }
|
|
||||||
.btn-success { background: #28a745; color: white; }
|
|
||||||
.btn-danger { background: #dc3545; color: white; }
|
|
||||||
.btn-warning { background: #ffc107; color: #333; }
|
|
||||||
.alert { padding: 15px; border-radius: 4px; margin-bottom: 20px; }
|
|
||||||
.alert-success { background: #d4edda; color: #155724; border: 1px solid #c3e6cb; }
|
|
||||||
.alert-danger { background: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; }
|
|
||||||
table { width: 100%; border-collapse: collapse; background: white; }
|
|
||||||
th, td { padding: 10px; border: 1px solid #ddd; text-align: left; }
|
|
||||||
th { background: #f8f9fa; }
|
|
||||||
.action-buttons { display: flex; gap: 5px; }
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="admin-header">
|
|
||||||
<h1><i class="fas fa-user-shield"></i> Админ-панель AETERNA</h1>
|
|
||||||
<div>
|
|
||||||
<span><?= htmlspecialchars($_SESSION['user_email'] ?? 'Администратор') ?></span>
|
|
||||||
<a href="catalog.php" class="btn btn-primary" style="margin-left: 10px;">В каталог</a>
|
|
||||||
<a href="logout.php" class="btn btn-danger" style="margin-left: 10px;">Выйти</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="admin-tabs">
|
|
||||||
<a href="?action=dashboard" class="admin-tab <?= $action == 'dashboard' ? 'active' : '' ?>">
|
|
||||||
<i class="fas fa-tachometer-alt"></i> Дашборд
|
|
||||||
</a>
|
|
||||||
<a href="?action=products" class="admin-tab <?= $action == 'products' ? 'active' : '' ?>">
|
|
||||||
<i class="fas fa-box"></i> Товары
|
|
||||||
</a>
|
|
||||||
<a href="?action=categories" class="admin-tab <?= $action == 'categories' ? 'active' : '' ?>">
|
|
||||||
<i class="fas fa-tags"></i> Категории
|
|
||||||
</a>
|
|
||||||
<a href="?action=orders" class="admin-tab <?= $action == 'orders' ? 'active' : '' ?>">
|
|
||||||
<i class="fas fa-shopping-cart"></i> Заказы
|
|
||||||
</a>
|
|
||||||
<a href="?action=users" class="admin-tab <?= $action == 'users' ? 'active' : '' ?>">
|
|
||||||
<i class="fas fa-users"></i> Пользователи
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="admin-content">
|
|
||||||
<?php if ($message): ?>
|
|
||||||
<div class="alert alert-success">
|
|
||||||
<i class="fas fa-check-circle"></i> <?= htmlspecialchars(urldecode($message)) ?>
|
|
||||||
</div>
|
|
||||||
<?php endif; ?>
|
|
||||||
|
|
||||||
<?php if ($error): ?>
|
|
||||||
<div class="alert alert-danger">
|
|
||||||
<i class="fas fa-exclamation-circle"></i> <?= htmlspecialchars(urldecode($error)) ?>
|
|
||||||
</div>
|
|
||||||
<?php endif; ?>
|
|
||||||
|
|
||||||
<?php if ($action == 'dashboard'): ?>
|
|
||||||
<!-- Дашборд -->
|
|
||||||
<h2>Статистика</h2>
|
|
||||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 20px; margin: 20px 0;">
|
|
||||||
<div style="background: white; padding: 20px; border-radius: 5px; text-align: center;">
|
|
||||||
<h3><?= $stats['total_products'] ?></h3>
|
|
||||||
<p>Всего товаров</p>
|
|
||||||
</div>
|
|
||||||
<div style="background: white; padding: 20px; border-radius: 5px; text-align: center;">
|
|
||||||
<h3><?= $stats['active_products'] ?></h3>
|
|
||||||
<p>Активных товаров</p>
|
|
||||||
</div>
|
|
||||||
<div style="background: white; padding: 20px; border-radius: 5px; text-align: center;">
|
|
||||||
<h3><?= $stats['total_orders'] ?></h3>
|
|
||||||
<p>Заказов</p>
|
|
||||||
</div>
|
|
||||||
<div style="background: white; padding: 20px; border-radius: 5px; text-align: center;">
|
|
||||||
<h3><?= $stats['total_users'] ?></h3>
|
|
||||||
<p>Пользователей</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style="text-align: center; margin: 40px 0;">
|
|
||||||
<a href="?action=add_product" class="btn btn-success" style="padding: 15px 30px; font-size: 16px;">
|
|
||||||
<i class="fas fa-plus"></i> Добавить новый товар
|
|
||||||
</a>
|
|
||||||
<a href="?action=add_category" class="btn btn-primary" style="padding: 15px 30px; font-size: 16px;">
|
|
||||||
<i class="fas fa-plus"></i> Добавить категорию
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<?php elseif ($action == 'products'): ?>
|
|
||||||
<!-- Товары -->
|
|
||||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">
|
|
||||||
<h2>Управление товарами</h2>
|
|
||||||
<div>
|
|
||||||
<a href="?action=add_product" class="btn btn-success">
|
|
||||||
<i class="fas fa-plus"></i> Добавить товар
|
|
||||||
</a>
|
|
||||||
<?php if (isset($_GET['show_all'])): ?>
|
|
||||||
<a href="?action=products" class="btn btn-primary">Только активные</a>
|
|
||||||
<?php else: ?>
|
|
||||||
<a href="?action=products&show_all=1" class="btn btn-primary">Показать все</a>
|
|
||||||
<?php endif; ?>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>ID</th>
|
|
||||||
<th>Название</th>
|
|
||||||
<th>Категория</th>
|
|
||||||
<th>Цена</th>
|
|
||||||
<th>На складе</th>
|
|
||||||
<th>Статус</th>
|
|
||||||
<th>Действия</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<?php foreach ($data as $product): ?>
|
|
||||||
<tr>
|
|
||||||
<td><?= $product['product_id'] ?></td>
|
|
||||||
<td><?= htmlspecialchars($product['name']) ?></td>
|
|
||||||
<td><?= htmlspecialchars($product['category_name'] ?? 'Без категории') ?></td>
|
|
||||||
<td><?= number_format($product['price'], 0, '', ' ') ?> ₽</td>
|
|
||||||
<td><?= $product['stock_quantity'] ?></td>
|
|
||||||
<td>
|
|
||||||
<?php if ($product['is_available'] && $product['stock_quantity'] > 0): ?>
|
|
||||||
<span style="color: green;">✓ Доступен</span>
|
|
||||||
<?php elseif (!$product['is_available']): ?>
|
|
||||||
<span style="color: red;">✗ Недоступен</span>
|
|
||||||
<?php else: ?>
|
|
||||||
<span style="color: orange;">⚠ Нет на складе</span>
|
|
||||||
<?php endif; ?>
|
|
||||||
</td>
|
|
||||||
<td class="action-buttons">
|
|
||||||
<a href="?action=edit_product&id=<?= $product['product_id'] ?>" class="btn btn-warning btn-sm">
|
|
||||||
<i class="fas fa-edit"></i>
|
|
||||||
</a>
|
|
||||||
<?php if ($product['is_available']): ?>
|
|
||||||
<form method="POST" style="display: inline;">
|
|
||||||
<input type="hidden" name="action" value="edit_product">
|
|
||||||
<input type="hidden" name="product_id" value="<?= $product['product_id'] ?>">
|
|
||||||
<input type="hidden" name="is_available" value="0">
|
|
||||||
<button type="submit" class="btn btn-danger btn-sm" onclick="return confirm('Сделать недоступным?')">
|
|
||||||
<i class="fas fa-times"></i>
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
<?php else: ?>
|
|
||||||
<form method="POST" style="display: inline;">
|
|
||||||
<input type="hidden" name="action" value="edit_product">
|
|
||||||
<input type="hidden" name="product_id" value="<?= $product['product_id'] ?>">
|
|
||||||
<input type="hidden" name="is_available" value="1">
|
|
||||||
<button type="submit" class="btn btn-success btn-sm" onclick="return confirm('Сделать доступным?')">
|
|
||||||
<i class="fas fa-check"></i>
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
<?php endif; ?>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<?php elseif ($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>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>ID</th>
|
|
||||||
<th>Название</th>
|
|
||||||
<th>Slug</th>
|
|
||||||
<th>Родительская</th>
|
|
||||||
<th>Товаров</th>
|
|
||||||
<th>Действия</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<?php foreach ($data as $category): ?>
|
|
||||||
<tr>
|
|
||||||
<td><?= $category['category_id'] ?></td>
|
|
||||||
<td><?= htmlspecialchars($category['name']) ?></td>
|
|
||||||
<td><?= htmlspecialchars($category['slug']) ?></td>
|
|
||||||
<td><?= htmlspecialchars($category['parent_name'] ?? '—') ?></td>
|
|
||||||
<td><?= $category['product_count'] ?></td>
|
|
||||||
<td class="action-buttons">
|
|
||||||
<!-- Кнопка редактирования -->
|
|
||||||
<a href="?action=edit_category&id=<?= $category['category_id'] ?>" class="btn btn-warning btn-sm">
|
|
||||||
<i class="fas fa-edit"></i> Редактировать
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<!-- Кнопка удаления с AJAX -->
|
|
||||||
<button type="button" class="btn btn-danger btn-sm delete-category-btn"
|
|
||||||
data-id="<?= $category['category_id'] ?>"
|
|
||||||
<?= $category['product_count'] > 0 ? 'disabled' : '' ?>>
|
|
||||||
<i class="fas fa-trash"></i> Удалить
|
|
||||||
</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<?php elseif (in_array($action, ['add_category', 'edit_category'])): ?>
|
|
||||||
<!-- Форма добавления/редактирования категории -->
|
|
||||||
<div class="form-container">
|
|
||||||
<h2><?= $action == 'add_category' ? 'Добавление категории' : 'Редактирование категории' ?></h2>
|
|
||||||
|
|
||||||
<form method="POST" action="fix_edit_category.php" id="categoryForm">
|
|
||||||
<input type="hidden" name="action" value="<?= $action == 'edit_category' ? 'edit_category' : 'add_category' ?>">
|
|
||||||
|
|
||||||
<?php if (isset($edit_data)): ?>
|
|
||||||
<input type="hidden" name="category_id" value="<?= $edit_data['category_id'] ?>">
|
|
||||||
<?php endif; ?>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label>Название категории *</label>
|
|
||||||
<input type="text" name="name" class="form-control"
|
|
||||||
value="<?= htmlspecialchars($edit_data['name'] ?? '') ?>" required>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label>Родительская категория</label>
|
|
||||||
<select name="parent_id" class="form-control">
|
|
||||||
<option value="">Без родительской категории</option>
|
|
||||||
<?php foreach ($parentCategories as $cat): ?>
|
|
||||||
<?php if (!isset($edit_data['category_id']) || $cat['category_id'] != $edit_data['category_id']): ?>
|
|
||||||
<option value="<?= $cat['category_id'] ?>"
|
|
||||||
<?= (isset($edit_data['parent_id']) && $edit_data['parent_id'] == $cat['category_id']) ? 'selected' : '' ?>>
|
|
||||||
<?= htmlspecialchars($cat['name']) ?>
|
|
||||||
</option>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label>Описание</label>
|
|
||||||
<textarea name="description" class="form-control" rows="3"><?= htmlspecialchars($edit_data['description'] ?? '') ?></textarea>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label>Порядок сортировки</label>
|
|
||||||
<input type="number" name="sort_order" class="form-control" min="0" max="100"
|
|
||||||
value="<?= $edit_data['sort_order'] ?? 0 ?>">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label>
|
|
||||||
<input type="checkbox" name="is_active" value="1"
|
|
||||||
<?= (!isset($edit_data['is_active']) || $edit_data['is_active']) ? 'checked' : '' ?>>
|
|
||||||
Активна
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button type="submit" class="btn btn-primary">
|
|
||||||
<?= $action == 'add_category' ? 'Добавить категорию' : 'Сохранить изменения' ?>
|
|
||||||
</button>
|
|
||||||
<a href="?action=categories" class="btn">Отмена</a>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<?php elseif (in_array($action, ['add_category', 'edit_category'])): ?>
|
|
||||||
<!-- Форма добавления/редактирования категории -->
|
|
||||||
<div class="form-container">
|
|
||||||
<h2><?= $action == 'add_category' ? 'Добавление категории' : 'Редактирование категории' ?></h2>
|
|
||||||
|
|
||||||
<form method="POST">
|
|
||||||
<input type="hidden" name="action" value="<?= $action == 'edit_category' ? 'edit_category' : 'add_category' ?>">
|
|
||||||
<?php if (isset($edit_data)): ?>
|
|
||||||
<input type="hidden" name="category_id" value="<?= $edit_data['category_id'] ?>">
|
|
||||||
<?php endif; ?>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label>Название категории *</label>
|
|
||||||
<input type="text" name="name" class="form-control"
|
|
||||||
value="<?= htmlspecialchars($edit_data['name'] ?? '') ?>" required>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label>Родительская категория</label>
|
|
||||||
<select name="parent_id" class="form-control">
|
|
||||||
<option value="">Без родительской категории</option>
|
|
||||||
<?php foreach ($parentCategories as $cat): ?>
|
|
||||||
<?php if (!isset($edit_data['category_id']) || $cat['category_id'] != $edit_data['category_id']): ?>
|
|
||||||
<option value="<?= $cat['category_id'] ?>"
|
|
||||||
<?= (isset($edit_data['parent_id']) && $edit_data['parent_id'] == $cat['category_id']) ? 'selected' : '' ?>>
|
|
||||||
<?= htmlspecialchars($cat['name']) ?>
|
|
||||||
</option>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label>Описание</label>
|
|
||||||
<textarea name="description" class="form-control" rows="3"><?= htmlspecialchars($edit_data['description'] ?? '') ?></textarea>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label>Порядок сортировки</label>
|
|
||||||
<input type="number" name="sort_order" class="form-control" min="0" max="100"
|
|
||||||
value="<?= $edit_data['sort_order'] ?? 0 ?>">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label>
|
|
||||||
<input type="checkbox" name="is_active" value="1"
|
|
||||||
<?= (!isset($edit_data['is_active']) || $edit_data['is_active']) ? 'checked' : '' ?>>
|
|
||||||
Активна
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button type="submit" class="btn btn-primary">
|
|
||||||
<?= $action == 'add_category' ? 'Добавить категорию' : 'Сохранить изменения' ?>
|
|
||||||
</button>
|
|
||||||
<a href="?action=categories" class="btn">Отмена</a>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<?php elseif ($action == 'orders'): ?>
|
|
||||||
<!-- Заказы -->
|
|
||||||
<h2>Заказы</h2>
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>№ заказа</th>
|
|
||||||
<th>Клиент</th>
|
|
||||||
<th>Сумма</th>
|
|
||||||
<th>Статус</th>
|
|
||||||
<th>Дата</th>
|
|
||||||
<th>Действия</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<?php foreach ($data as $order): ?>
|
|
||||||
<tr>
|
|
||||||
<td><?= htmlspecialchars($order['order_number']) ?></td>
|
|
||||||
<td><?= htmlspecialchars($order['customer_name']) ?></td>
|
|
||||||
<td><?= number_format($order['final_amount'], 0, '', ' ') ?> ₽</td>
|
|
||||||
<td><?= htmlspecialchars($order['status']) ?></td>
|
|
||||||
<td><?= date('d.m.Y H:i', strtotime($order['created_at'])) ?></td>
|
|
||||||
<td>
|
|
||||||
<a href="?action=order_details&id=<?= $order['order_id'] ?>" class="btn btn-primary btn-sm">
|
|
||||||
<i class="fas fa-eye"></i>
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<?php elseif ($action == 'users'): ?>
|
|
||||||
<!-- Пользователи -->
|
|
||||||
<h2>Пользователи</h2>
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>ID</th>
|
|
||||||
<th>Email</th>
|
|
||||||
<th>ФИО</th>
|
|
||||||
<th>Дата регистрации</th>
|
|
||||||
<th>Статус</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<?php foreach ($data as $user): ?>
|
|
||||||
<tr>
|
|
||||||
<td><?= $user['user_id'] ?></td>
|
|
||||||
<td><?= htmlspecialchars($user['email']) ?></td>
|
|
||||||
<td><?= htmlspecialchars($user['full_name']) ?></td>
|
|
||||||
<td><?= date('d.m.Y', strtotime($user['created_at'])) ?></td>
|
|
||||||
<td>
|
|
||||||
<?php if ($user['is_active']): ?>
|
|
||||||
<span style="color: green;">✓ Активен</span>
|
|
||||||
<?php else: ?>
|
|
||||||
<span style="color: red;">✗ Неактивен</span>
|
|
||||||
<?php endif; ?>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<?php endif; ?>
|
|
||||||
</div>
|
|
||||||
<script>
|
|
||||||
// Удаление категории через AJAX
|
|
||||||
$('.delete-category-btn').click(function() {
|
|
||||||
const categoryId = $(this).data('id');
|
|
||||||
const btn = $(this);
|
|
||||||
|
|
||||||
if (confirm('Удалить эту категорию?')) {
|
|
||||||
$.ajax({
|
|
||||||
url: 'fix_delete_category.php',
|
|
||||||
method: 'POST',
|
|
||||||
data: { category_id: categoryId },
|
|
||||||
success: function(response) {
|
|
||||||
const result = JSON.parse(response);
|
|
||||||
if (result.success) {
|
|
||||||
alert(result.message);
|
|
||||||
location.reload();
|
|
||||||
} else {
|
|
||||||
alert('Ошибка: ' + result.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Обработка формы категории
|
|
||||||
$('#categoryForm').submit(function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
$.ajax({
|
|
||||||
url: $(this).attr('action'),
|
|
||||||
method: 'POST',
|
|
||||||
data: $(this).serialize(),
|
|
||||||
success: function(response) {
|
|
||||||
const result = JSON.parse(response);
|
|
||||||
if (result.success) {
|
|
||||||
alert(result.message);
|
|
||||||
window.location.href = 'admin_panel.php?action=categories';
|
|
||||||
} else {
|
|
||||||
alert('Ошибка: ' + result.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -8,9 +8,6 @@ use App\Models\Category;
|
|||||||
use App\Models\Order;
|
use App\Models\Order;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
|
|
||||||
/**
|
|
||||||
* AdminController - контроллер админ-панели
|
|
||||||
*/
|
|
||||||
class AdminController extends Controller
|
class AdminController extends Controller
|
||||||
{
|
{
|
||||||
private Product $productModel;
|
private Product $productModel;
|
||||||
@@ -26,9 +23,6 @@ class AdminController extends Controller
|
|||||||
$this->userModel = new User();
|
$this->userModel = new User();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Дашборд
|
|
||||||
*/
|
|
||||||
public function dashboard(): void
|
public function dashboard(): void
|
||||||
{
|
{
|
||||||
$this->requireAdmin();
|
$this->requireAdmin();
|
||||||
@@ -47,11 +41,6 @@ class AdminController extends Controller
|
|||||||
], 'admin');
|
], 'admin');
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========== Товары ==========
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Список товаров
|
|
||||||
*/
|
|
||||||
public function products(): void
|
public function products(): void
|
||||||
{
|
{
|
||||||
$this->requireAdmin();
|
$this->requireAdmin();
|
||||||
@@ -68,9 +57,6 @@ class AdminController extends Controller
|
|||||||
], 'admin');
|
], 'admin');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Форма добавления товара
|
|
||||||
*/
|
|
||||||
public function addProduct(): void
|
public function addProduct(): void
|
||||||
{
|
{
|
||||||
$this->requireAdmin();
|
$this->requireAdmin();
|
||||||
@@ -85,9 +71,6 @@ class AdminController extends Controller
|
|||||||
], 'admin');
|
], 'admin');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Сохранение нового товара
|
|
||||||
*/
|
|
||||||
public function storeProduct(): void
|
public function storeProduct(): void
|
||||||
{
|
{
|
||||||
$this->requireAdmin();
|
$this->requireAdmin();
|
||||||
@@ -116,9 +99,6 @@ class AdminController extends Controller
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Форма редактирования товара
|
|
||||||
*/
|
|
||||||
public function editProduct(int $id): void
|
public function editProduct(int $id): void
|
||||||
{
|
{
|
||||||
$this->requireAdmin();
|
$this->requireAdmin();
|
||||||
@@ -140,9 +120,6 @@ class AdminController extends Controller
|
|||||||
], 'admin');
|
], 'admin');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Обновление товара
|
|
||||||
*/
|
|
||||||
public function updateProduct(int $id): void
|
public function updateProduct(int $id): void
|
||||||
{
|
{
|
||||||
$this->requireAdmin();
|
$this->requireAdmin();
|
||||||
@@ -168,9 +145,6 @@ class AdminController extends Controller
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Удаление товара (делаем недоступным)
|
|
||||||
*/
|
|
||||||
public function deleteProduct(int $id): void
|
public function deleteProduct(int $id): void
|
||||||
{
|
{
|
||||||
$this->requireAdmin();
|
$this->requireAdmin();
|
||||||
@@ -179,11 +153,6 @@ class AdminController extends Controller
|
|||||||
$this->redirect('/admin/products?message=' . urlencode('Товар скрыт'));
|
$this->redirect('/admin/products?message=' . urlencode('Товар скрыт'));
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========== Категории ==========
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Список категорий
|
|
||||||
*/
|
|
||||||
public function categories(): void
|
public function categories(): void
|
||||||
{
|
{
|
||||||
$this->requireAdmin();
|
$this->requireAdmin();
|
||||||
@@ -198,9 +167,6 @@ class AdminController extends Controller
|
|||||||
], 'admin');
|
], 'admin');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Форма добавления категории
|
|
||||||
*/
|
|
||||||
public function addCategory(): void
|
public function addCategory(): void
|
||||||
{
|
{
|
||||||
$this->requireAdmin();
|
$this->requireAdmin();
|
||||||
@@ -215,9 +181,6 @@ class AdminController extends Controller
|
|||||||
], 'admin');
|
], 'admin');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Сохранение категории
|
|
||||||
*/
|
|
||||||
public function storeCategory(): void
|
public function storeCategory(): void
|
||||||
{
|
{
|
||||||
$this->requireAdmin();
|
$this->requireAdmin();
|
||||||
@@ -238,9 +201,6 @@ class AdminController extends Controller
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Форма редактирования категории
|
|
||||||
*/
|
|
||||||
public function editCategory(int $id): void
|
public function editCategory(int $id): void
|
||||||
{
|
{
|
||||||
$this->requireAdmin();
|
$this->requireAdmin();
|
||||||
@@ -262,9 +222,6 @@ class AdminController extends Controller
|
|||||||
], 'admin');
|
], 'admin');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Обновление категории
|
|
||||||
*/
|
|
||||||
public function updateCategory(int $id): void
|
public function updateCategory(int $id): void
|
||||||
{
|
{
|
||||||
$this->requireAdmin();
|
$this->requireAdmin();
|
||||||
@@ -285,9 +242,6 @@ class AdminController extends Controller
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Удаление категории
|
|
||||||
*/
|
|
||||||
public function deleteCategory(int $id): void
|
public function deleteCategory(int $id): void
|
||||||
{
|
{
|
||||||
$this->requireAdmin();
|
$this->requireAdmin();
|
||||||
@@ -304,11 +258,6 @@ class AdminController extends Controller
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========== Заказы ==========
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Список заказов
|
|
||||||
*/
|
|
||||||
public function orders(): void
|
public function orders(): void
|
||||||
{
|
{
|
||||||
$this->requireAdmin();
|
$this->requireAdmin();
|
||||||
@@ -321,9 +270,6 @@ class AdminController extends Controller
|
|||||||
], 'admin');
|
], 'admin');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Детали заказа
|
|
||||||
*/
|
|
||||||
public function orderDetails(int $id): void
|
public function orderDetails(int $id): void
|
||||||
{
|
{
|
||||||
$this->requireAdmin();
|
$this->requireAdmin();
|
||||||
@@ -341,9 +287,6 @@ class AdminController extends Controller
|
|||||||
], 'admin');
|
], 'admin');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Обновление статуса заказа
|
|
||||||
*/
|
|
||||||
public function updateOrderStatus(int $id): void
|
public function updateOrderStatus(int $id): void
|
||||||
{
|
{
|
||||||
$this->requireAdmin();
|
$this->requireAdmin();
|
||||||
@@ -354,11 +297,6 @@ class AdminController extends Controller
|
|||||||
$this->redirect('/admin/orders/' . $id);
|
$this->redirect('/admin/orders/' . $id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========== Пользователи ==========
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Список пользователей
|
|
||||||
*/
|
|
||||||
public function users(): void
|
public function users(): void
|
||||||
{
|
{
|
||||||
$this->requireAdmin();
|
$this->requireAdmin();
|
||||||
@@ -371,4 +309,3 @@ class AdminController extends Controller
|
|||||||
], 'admin');
|
], 'admin');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,9 +5,6 @@ namespace App\Controllers;
|
|||||||
use App\Core\Controller;
|
use App\Core\Controller;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
|
|
||||||
/**
|
|
||||||
* AuthController - контроллер авторизации
|
|
||||||
*/
|
|
||||||
class AuthController extends Controller
|
class AuthController extends Controller
|
||||||
{
|
{
|
||||||
private User $userModel;
|
private User $userModel;
|
||||||
@@ -17,9 +14,6 @@ class AuthController extends Controller
|
|||||||
$this->userModel = new User();
|
$this->userModel = new User();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Форма входа
|
|
||||||
*/
|
|
||||||
public function loginForm(): void
|
public function loginForm(): void
|
||||||
{
|
{
|
||||||
if ($this->isAuthenticated()) {
|
if ($this->isAuthenticated()) {
|
||||||
@@ -35,9 +29,6 @@ class AuthController extends Controller
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Обработка входа
|
|
||||||
*/
|
|
||||||
public function login(): void
|
public function login(): void
|
||||||
{
|
{
|
||||||
$email = $this->getPost('email', '');
|
$email = $this->getPost('email', '');
|
||||||
@@ -62,7 +53,6 @@ class AuthController extends Controller
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Устанавливаем сессию
|
|
||||||
$this->setSession($user);
|
$this->setSession($user);
|
||||||
|
|
||||||
$this->json([
|
$this->json([
|
||||||
@@ -71,9 +61,6 @@ class AuthController extends Controller
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Форма регистрации
|
|
||||||
*/
|
|
||||||
public function registerForm(): void
|
public function registerForm(): void
|
||||||
{
|
{
|
||||||
if ($this->isAuthenticated()) {
|
if ($this->isAuthenticated()) {
|
||||||
@@ -86,15 +73,11 @@ class AuthController extends Controller
|
|||||||
'success' => $_SESSION['registration_success'] ?? null
|
'success' => $_SESSION['registration_success'] ?? null
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Очищаем flash данные
|
|
||||||
unset($_SESSION['registration_errors']);
|
unset($_SESSION['registration_errors']);
|
||||||
unset($_SESSION['old_data']);
|
unset($_SESSION['old_data']);
|
||||||
unset($_SESSION['registration_success']);
|
unset($_SESSION['registration_success']);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Обработка регистрации
|
|
||||||
*/
|
|
||||||
public function register(): void
|
public function register(): void
|
||||||
{
|
{
|
||||||
$errors = [];
|
$errors = [];
|
||||||
@@ -107,7 +90,6 @@ class AuthController extends Controller
|
|||||||
$confirmPassword = $this->getPost('confirm-password', '');
|
$confirmPassword = $this->getPost('confirm-password', '');
|
||||||
$privacy = $this->getPost('privacy');
|
$privacy = $this->getPost('privacy');
|
||||||
|
|
||||||
// Валидация
|
|
||||||
if (empty($fullName) || strlen($fullName) < 3) {
|
if (empty($fullName) || strlen($fullName) < 3) {
|
||||||
$errors[] = 'ФИО должно содержать минимум 3 символа';
|
$errors[] = 'ФИО должно содержать минимум 3 символа';
|
||||||
}
|
}
|
||||||
@@ -136,7 +118,6 @@ class AuthController extends Controller
|
|||||||
$errors[] = 'Необходимо согласие с условиями обработки персональных данных';
|
$errors[] = 'Необходимо согласие с условиями обработки персональных данных';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Проверяем существование email
|
|
||||||
if (empty($errors) && $this->userModel->emailExists($email)) {
|
if (empty($errors) && $this->userModel->emailExists($email)) {
|
||||||
$errors[] = 'Пользователь с таким email уже существует';
|
$errors[] = 'Пользователь с таким email уже существует';
|
||||||
}
|
}
|
||||||
@@ -153,7 +134,6 @@ class AuthController extends Controller
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Создаем пользователя
|
|
||||||
try {
|
try {
|
||||||
$userId = $this->userModel->register([
|
$userId = $this->userModel->register([
|
||||||
'email' => $email,
|
'email' => $email,
|
||||||
@@ -167,10 +147,7 @@ class AuthController extends Controller
|
|||||||
throw new \Exception('Ошибка при создании пользователя');
|
throw new \Exception('Ошибка при создании пользователя');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Получаем созданного пользователя
|
|
||||||
$user = $this->userModel->find($userId);
|
$user = $this->userModel->find($userId);
|
||||||
|
|
||||||
// Устанавливаем сессию
|
|
||||||
$this->setSession($user);
|
$this->setSession($user);
|
||||||
|
|
||||||
$_SESSION['registration_success'] = 'Регистрация прошла успешно!';
|
$_SESSION['registration_success'] = 'Регистрация прошла успешно!';
|
||||||
@@ -188,9 +165,6 @@ class AuthController extends Controller
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Выход из системы
|
|
||||||
*/
|
|
||||||
public function logout(): void
|
public function logout(): void
|
||||||
{
|
{
|
||||||
session_destroy();
|
session_destroy();
|
||||||
@@ -199,9 +173,6 @@ class AuthController extends Controller
|
|||||||
$this->redirect('/');
|
$this->redirect('/');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Установить сессию пользователя
|
|
||||||
*/
|
|
||||||
private function setSession(array $user): void
|
private function setSession(array $user): void
|
||||||
{
|
{
|
||||||
$_SESSION['user_id'] = $user['user_id'];
|
$_SESSION['user_id'] = $user['user_id'];
|
||||||
@@ -214,4 +185,3 @@ class AuthController extends Controller
|
|||||||
$_SESSION['login_time'] = time();
|
$_SESSION['login_time'] = time();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,9 +6,6 @@ use App\Core\Controller;
|
|||||||
use App\Models\Cart;
|
use App\Models\Cart;
|
||||||
use App\Models\Product;
|
use App\Models\Product;
|
||||||
|
|
||||||
/**
|
|
||||||
* CartController - контроллер корзины
|
|
||||||
*/
|
|
||||||
class CartController extends Controller
|
class CartController extends Controller
|
||||||
{
|
{
|
||||||
private Cart $cartModel;
|
private Cart $cartModel;
|
||||||
@@ -20,9 +17,6 @@ class CartController extends Controller
|
|||||||
$this->productModel = new Product();
|
$this->productModel = new Product();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Страница корзины
|
|
||||||
*/
|
|
||||||
public function index(): void
|
public function index(): void
|
||||||
{
|
{
|
||||||
$this->requireAuth();
|
$this->requireAuth();
|
||||||
@@ -39,9 +33,6 @@ class CartController extends Controller
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Добавить товар в корзину
|
|
||||||
*/
|
|
||||||
public function add(): void
|
public function add(): void
|
||||||
{
|
{
|
||||||
if (!$this->isAuthenticated()) {
|
if (!$this->isAuthenticated()) {
|
||||||
@@ -64,7 +55,6 @@ class CartController extends Controller
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Проверяем наличие товара
|
|
||||||
$product = $this->productModel->find($productId);
|
$product = $this->productModel->find($productId);
|
||||||
|
|
||||||
if (!$product || !$product['is_available']) {
|
if (!$product || !$product['is_available']) {
|
||||||
@@ -75,7 +65,6 @@ class CartController extends Controller
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Проверяем количество на складе
|
|
||||||
$cartItem = $this->cartModel->getItem($userId, $productId);
|
$cartItem = $this->cartModel->getItem($userId, $productId);
|
||||||
$currentQty = $cartItem ? $cartItem['quantity'] : 0;
|
$currentQty = $cartItem ? $cartItem['quantity'] : 0;
|
||||||
$newQty = $currentQty + $quantity;
|
$newQty = $currentQty + $quantity;
|
||||||
@@ -88,7 +77,6 @@ class CartController extends Controller
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Добавляем в корзину
|
|
||||||
$result = $this->cartModel->addItem($userId, $productId, $quantity);
|
$result = $this->cartModel->addItem($userId, $productId, $quantity);
|
||||||
|
|
||||||
if ($result) {
|
if ($result) {
|
||||||
@@ -106,9 +94,6 @@ class CartController extends Controller
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Обновить количество товара
|
|
||||||
*/
|
|
||||||
public function update(): void
|
public function update(): void
|
||||||
{
|
{
|
||||||
if (!$this->isAuthenticated()) {
|
if (!$this->isAuthenticated()) {
|
||||||
@@ -124,7 +109,6 @@ class CartController extends Controller
|
|||||||
$userId = $this->getCurrentUser()['id'];
|
$userId = $this->getCurrentUser()['id'];
|
||||||
|
|
||||||
if ($quantity <= 0) {
|
if ($quantity <= 0) {
|
||||||
// Если количество 0 или меньше - удаляем
|
|
||||||
$this->cartModel->removeItem($userId, $productId);
|
$this->cartModel->removeItem($userId, $productId);
|
||||||
$cartCount = $this->cartModel->getCount($userId);
|
$cartCount = $this->cartModel->getCount($userId);
|
||||||
$this->json([
|
$this->json([
|
||||||
@@ -134,7 +118,6 @@ class CartController extends Controller
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Проверяем наличие на складе
|
|
||||||
$product = $this->productModel->find($productId);
|
$product = $this->productModel->find($productId);
|
||||||
if (!$product || $quantity > $product['stock_quantity']) {
|
if (!$product || $quantity > $product['stock_quantity']) {
|
||||||
$this->json([
|
$this->json([
|
||||||
@@ -161,9 +144,6 @@ class CartController extends Controller
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Удалить товар из корзины
|
|
||||||
*/
|
|
||||||
public function remove(): void
|
public function remove(): void
|
||||||
{
|
{
|
||||||
if (!$this->isAuthenticated()) {
|
if (!$this->isAuthenticated()) {
|
||||||
@@ -193,9 +173,6 @@ class CartController extends Controller
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Получить количество товаров в корзине
|
|
||||||
*/
|
|
||||||
public function count(): void
|
public function count(): void
|
||||||
{
|
{
|
||||||
if (!$this->isAuthenticated()) {
|
if (!$this->isAuthenticated()) {
|
||||||
@@ -215,4 +192,3 @@ class CartController extends Controller
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,14 +4,8 @@ namespace App\Controllers;
|
|||||||
|
|
||||||
use App\Core\Controller;
|
use App\Core\Controller;
|
||||||
|
|
||||||
/**
|
|
||||||
* HomeController - контроллер главной страницы
|
|
||||||
*/
|
|
||||||
class HomeController extends Controller
|
class HomeController extends Controller
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* Главная страница
|
|
||||||
*/
|
|
||||||
public function index(): void
|
public function index(): void
|
||||||
{
|
{
|
||||||
$user = $this->getCurrentUser();
|
$user = $this->getCurrentUser();
|
||||||
@@ -23,4 +17,3 @@ class HomeController extends Controller
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,9 +6,6 @@ use App\Core\Controller;
|
|||||||
use App\Models\Order;
|
use App\Models\Order;
|
||||||
use App\Models\Cart;
|
use App\Models\Cart;
|
||||||
|
|
||||||
/**
|
|
||||||
* OrderController - контроллер заказов
|
|
||||||
*/
|
|
||||||
class OrderController extends Controller
|
class OrderController extends Controller
|
||||||
{
|
{
|
||||||
private Order $orderModel;
|
private Order $orderModel;
|
||||||
@@ -20,9 +17,6 @@ class OrderController extends Controller
|
|||||||
$this->cartModel = new Cart();
|
$this->cartModel = new Cart();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Страница оформления заказа (корзина)
|
|
||||||
*/
|
|
||||||
public function checkout(): void
|
public function checkout(): void
|
||||||
{
|
{
|
||||||
$this->requireAuth();
|
$this->requireAuth();
|
||||||
@@ -39,9 +33,6 @@ class OrderController extends Controller
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Создание заказа
|
|
||||||
*/
|
|
||||||
public function create(): void
|
public function create(): void
|
||||||
{
|
{
|
||||||
if (!$this->isAuthenticated()) {
|
if (!$this->isAuthenticated()) {
|
||||||
@@ -63,7 +54,6 @@ class OrderController extends Controller
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Получаем данные заказа
|
|
||||||
$orderData = [
|
$orderData = [
|
||||||
'customer_name' => $this->getPost('full_name', $user['full_name']),
|
'customer_name' => $this->getPost('full_name', $user['full_name']),
|
||||||
'customer_email' => $this->getPost('email', $user['email']),
|
'customer_email' => $this->getPost('email', $user['email']),
|
||||||
@@ -79,7 +69,6 @@ class OrderController extends Controller
|
|||||||
'notes' => $this->getPost('notes', '')
|
'notes' => $this->getPost('notes', '')
|
||||||
];
|
];
|
||||||
|
|
||||||
// Валидация
|
|
||||||
if (empty($orderData['customer_name'])) {
|
if (empty($orderData['customer_name'])) {
|
||||||
$this->json([
|
$this->json([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
@@ -122,4 +111,3 @@ class OrderController extends Controller
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,14 +4,8 @@ namespace App\Controllers;
|
|||||||
|
|
||||||
use App\Core\Controller;
|
use App\Core\Controller;
|
||||||
|
|
||||||
/**
|
|
||||||
* PageController - контроллер статических страниц
|
|
||||||
*/
|
|
||||||
class PageController extends Controller
|
class PageController extends Controller
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* Страница услуг
|
|
||||||
*/
|
|
||||||
public function services(): void
|
public function services(): void
|
||||||
{
|
{
|
||||||
$this->view('pages/services', [
|
$this->view('pages/services', [
|
||||||
@@ -20,9 +14,6 @@ class PageController extends Controller
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Страница доставки и оплаты
|
|
||||||
*/
|
|
||||||
public function delivery(): void
|
public function delivery(): void
|
||||||
{
|
{
|
||||||
$this->view('pages/delivery', [
|
$this->view('pages/delivery', [
|
||||||
@@ -31,9 +22,6 @@ class PageController extends Controller
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Страница гарантии
|
|
||||||
*/
|
|
||||||
public function warranty(): void
|
public function warranty(): void
|
||||||
{
|
{
|
||||||
$this->view('pages/warranty', [
|
$this->view('pages/warranty', [
|
||||||
@@ -42,4 +30,3 @@ class PageController extends Controller
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,9 +6,6 @@ use App\Core\Controller;
|
|||||||
use App\Models\Product;
|
use App\Models\Product;
|
||||||
use App\Models\Category;
|
use App\Models\Category;
|
||||||
|
|
||||||
/**
|
|
||||||
* ProductController - контроллер товаров и каталога
|
|
||||||
*/
|
|
||||||
class ProductController extends Controller
|
class ProductController extends Controller
|
||||||
{
|
{
|
||||||
private Product $productModel;
|
private Product $productModel;
|
||||||
@@ -20,9 +17,6 @@ class ProductController extends Controller
|
|||||||
$this->categoryModel = new Category();
|
$this->categoryModel = new Category();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Каталог товаров
|
|
||||||
*/
|
|
||||||
public function catalog(): void
|
public function catalog(): void
|
||||||
{
|
{
|
||||||
$this->requireAuth();
|
$this->requireAuth();
|
||||||
@@ -30,7 +24,6 @@ class ProductController extends Controller
|
|||||||
$user = $this->getCurrentUser();
|
$user = $this->getCurrentUser();
|
||||||
$isAdmin = $this->isAdmin();
|
$isAdmin = $this->isAdmin();
|
||||||
|
|
||||||
// Получаем параметры фильтрации
|
|
||||||
$filters = [
|
$filters = [
|
||||||
'category_id' => (int) $this->getQuery('category', 0),
|
'category_id' => (int) $this->getQuery('category', 0),
|
||||||
'search' => $this->getQuery('search', ''),
|
'search' => $this->getQuery('search', ''),
|
||||||
@@ -42,7 +35,6 @@ class ProductController extends Controller
|
|||||||
|
|
||||||
$showAll = $isAdmin && $this->getQuery('show_all') === '1';
|
$showAll = $isAdmin && $this->getQuery('show_all') === '1';
|
||||||
|
|
||||||
// Получаем данные
|
|
||||||
$categories = $this->categoryModel->getActive();
|
$categories = $this->categoryModel->getActive();
|
||||||
$products = $showAll
|
$products = $showAll
|
||||||
? $this->productModel->getAllForAdmin(true)
|
? $this->productModel->getAllForAdmin(true)
|
||||||
@@ -51,7 +43,6 @@ class ProductController extends Controller
|
|||||||
$availableColors = $this->productModel->getAvailableColors();
|
$availableColors = $this->productModel->getAvailableColors();
|
||||||
$availableMaterials = $this->productModel->getAvailableMaterials();
|
$availableMaterials = $this->productModel->getAvailableMaterials();
|
||||||
|
|
||||||
// Подкатегории для выбранной категории
|
|
||||||
$subcategories = [];
|
$subcategories = [];
|
||||||
if ($filters['category_id'] > 0) {
|
if ($filters['category_id'] > 0) {
|
||||||
$subcategories = $this->categoryModel->getChildren($filters['category_id']);
|
$subcategories = $this->categoryModel->getChildren($filters['category_id']);
|
||||||
@@ -72,9 +63,6 @@ class ProductController extends Controller
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Страница товара
|
|
||||||
*/
|
|
||||||
public function show(int $id): void
|
public function show(int $id): void
|
||||||
{
|
{
|
||||||
$this->requireAuth();
|
$this->requireAuth();
|
||||||
@@ -99,4 +87,3 @@ class ProductController extends Controller
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,66 +2,68 @@
|
|||||||
|
|
||||||
namespace App\Core;
|
namespace App\Core;
|
||||||
|
|
||||||
/**
|
|
||||||
* App - главный класс приложения
|
|
||||||
*/
|
|
||||||
class App
|
class App
|
||||||
{
|
{
|
||||||
private Router $router;
|
private Router $router;
|
||||||
private static ?App $instance = null;
|
private static ?App $instance = null;
|
||||||
|
private array $config = [];
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
self::$instance = $this;
|
self::$instance = $this;
|
||||||
|
|
||||||
// Регистрируем автозагрузчик сразу
|
|
||||||
$this->registerAutoloader();
|
$this->registerAutoloader();
|
||||||
|
|
||||||
$this->router = new Router();
|
$this->router = new Router();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Получить экземпляр приложения
|
|
||||||
*/
|
|
||||||
public static function getInstance(): ?self
|
public static function getInstance(): ?self
|
||||||
{
|
{
|
||||||
return self::$instance;
|
return self::$instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Получить роутер
|
|
||||||
*/
|
|
||||||
public function getRouter(): Router
|
public function getRouter(): Router
|
||||||
{
|
{
|
||||||
return $this->router;
|
return $this->router;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function getConfig(string $key = null)
|
||||||
* Инициализация приложения
|
{
|
||||||
*/
|
if ($key === null) {
|
||||||
|
return $this->config;
|
||||||
|
}
|
||||||
|
return $this->config[$key] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
public function init(): self
|
public function init(): self
|
||||||
{
|
{
|
||||||
// Запускаем сессию
|
$this->loadConfig();
|
||||||
|
date_default_timezone_set($this->config['timezone'] ?? 'Europe/Moscow');
|
||||||
|
|
||||||
if (session_status() === PHP_SESSION_NONE) {
|
if (session_status() === PHP_SESSION_NONE) {
|
||||||
session_start();
|
session_start();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Настраиваем обработку ошибок
|
|
||||||
$this->setupErrorHandling();
|
$this->setupErrorHandling();
|
||||||
|
|
||||||
// Загружаем маршруты
|
|
||||||
$this->loadRoutes();
|
$this->loadRoutes();
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private function loadConfig(): void
|
||||||
* Регистрация автозагрузчика классов
|
{
|
||||||
*/
|
$configPath = $this->getBasePath() . '/config/app.php';
|
||||||
|
if (file_exists($configPath)) {
|
||||||
|
$this->config = require $configPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getBasePath(): string
|
||||||
|
{
|
||||||
|
return defined('ROOT_PATH') ? ROOT_PATH : dirname(__DIR__, 2);
|
||||||
|
}
|
||||||
|
|
||||||
private function registerAutoloader(): void
|
private function registerAutoloader(): void
|
||||||
{
|
{
|
||||||
spl_autoload_register(function ($class) {
|
spl_autoload_register(function ($class) {
|
||||||
// Преобразуем namespace в путь к файлу
|
|
||||||
$prefix = 'App\\';
|
$prefix = 'App\\';
|
||||||
$baseDir = dirname(__DIR__) . '/';
|
$baseDir = dirname(__DIR__) . '/';
|
||||||
|
|
||||||
@@ -79,14 +81,9 @@ class App
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Настройка обработки ошибок
|
|
||||||
*/
|
|
||||||
private function setupErrorHandling(): void
|
private function setupErrorHandling(): void
|
||||||
{
|
{
|
||||||
$config = require dirname(__DIR__, 2) . '/config/app.php';
|
if ($this->config['debug'] ?? false) {
|
||||||
|
|
||||||
if ($config['debug'] ?? false) {
|
|
||||||
error_reporting(E_ALL);
|
error_reporting(E_ALL);
|
||||||
ini_set('display_errors', '1');
|
ini_set('display_errors', '1');
|
||||||
} else {
|
} else {
|
||||||
@@ -103,16 +100,11 @@ class App
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Обработка исключений
|
|
||||||
*/
|
|
||||||
private function handleException(\Throwable $e): void
|
private function handleException(\Throwable $e): void
|
||||||
{
|
{
|
||||||
$config = require dirname(__DIR__, 2) . '/config/app.php';
|
|
||||||
|
|
||||||
http_response_code(500);
|
http_response_code(500);
|
||||||
|
|
||||||
if ($config['debug'] ?? false) {
|
if ($this->config['debug'] ?? false) {
|
||||||
echo "<h1>Ошибка приложения</h1>";
|
echo "<h1>Ошибка приложения</h1>";
|
||||||
echo "<p><strong>Сообщение:</strong> " . htmlspecialchars($e->getMessage()) . "</p>";
|
echo "<p><strong>Сообщение:</strong> " . htmlspecialchars($e->getMessage()) . "</p>";
|
||||||
echo "<p><strong>Файл:</strong> " . htmlspecialchars($e->getFile()) . ":" . $e->getLine() . "</p>";
|
echo "<p><strong>Файл:</strong> " . htmlspecialchars($e->getFile()) . ":" . $e->getLine() . "</p>";
|
||||||
@@ -122,12 +114,9 @@ class App
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Загрузка маршрутов
|
|
||||||
*/
|
|
||||||
private function loadRoutes(): void
|
private function loadRoutes(): void
|
||||||
{
|
{
|
||||||
$routesFile = dirname(__DIR__, 2) . '/config/routes.php';
|
$routesFile = $this->getBasePath() . '/config/routes.php';
|
||||||
|
|
||||||
if (file_exists($routesFile)) {
|
if (file_exists($routesFile)) {
|
||||||
$router = $this->router;
|
$router = $this->router;
|
||||||
@@ -135,21 +124,17 @@ class App
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Запуск приложения
|
|
||||||
*/
|
|
||||||
public function run(): void
|
public function run(): void
|
||||||
{
|
{
|
||||||
$uri = $_SERVER['REQUEST_URI'] ?? '/';
|
$uri = $_SERVER['REQUEST_URI'] ?? '/';
|
||||||
$method = $_SERVER['REQUEST_METHOD'] ?? 'GET';
|
$method = $_SERVER['REQUEST_METHOD'] ?? 'GET';
|
||||||
|
|
||||||
// Удаляем базовый путь, если он есть
|
$basePath = $this->config['base_path'] ?? '';
|
||||||
$basePath = '/cite_practica';
|
|
||||||
if (strpos($uri, $basePath) === 0) {
|
if (!empty($basePath) && strpos($uri, $basePath) === 0) {
|
||||||
$uri = substr($uri, strlen($basePath));
|
$uri = substr($uri, strlen($basePath));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Если URI пустой, делаем его корневым
|
|
||||||
if (empty($uri) || $uri === false) {
|
if (empty($uri) || $uri === false) {
|
||||||
$uri = '/';
|
$uri = '/';
|
||||||
}
|
}
|
||||||
@@ -161,4 +146,3 @@ class App
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,35 +2,20 @@
|
|||||||
|
|
||||||
namespace App\Core;
|
namespace App\Core;
|
||||||
|
|
||||||
/**
|
|
||||||
* Controller - базовый класс контроллера
|
|
||||||
*/
|
|
||||||
abstract class Controller
|
abstract class Controller
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* Данные для передачи в представление
|
|
||||||
*/
|
|
||||||
protected array $data = [];
|
protected array $data = [];
|
||||||
|
|
||||||
/**
|
|
||||||
* Отрендерить представление
|
|
||||||
*/
|
|
||||||
protected function view(string $view, array $data = [], string $layout = 'main'): void
|
protected function view(string $view, array $data = [], string $layout = 'main'): void
|
||||||
{
|
{
|
||||||
echo View::render($view, $data, $layout);
|
echo View::render($view, $data, $layout);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Отрендерить представление без layout
|
|
||||||
*/
|
|
||||||
protected function viewPartial(string $view, array $data = []): void
|
protected function viewPartial(string $view, array $data = []): void
|
||||||
{
|
{
|
||||||
echo View::render($view, $data, null);
|
echo View::render($view, $data, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Вернуть JSON ответ
|
|
||||||
*/
|
|
||||||
protected function json(array $data, int $statusCode = 200): void
|
protected function json(array $data, int $statusCode = 200): void
|
||||||
{
|
{
|
||||||
http_response_code($statusCode);
|
http_response_code($statusCode);
|
||||||
@@ -39,18 +24,12 @@ abstract class Controller
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Редирект на другой URL
|
|
||||||
*/
|
|
||||||
protected function redirect(string $url): void
|
protected function redirect(string $url): void
|
||||||
{
|
{
|
||||||
header("Location: {$url}");
|
header("Location: {$url}");
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Получить текущего пользователя из сессии
|
|
||||||
*/
|
|
||||||
protected function getCurrentUser(): ?array
|
protected function getCurrentUser(): ?array
|
||||||
{
|
{
|
||||||
if (!isset($_SESSION['isLoggedIn']) || $_SESSION['isLoggedIn'] !== true) {
|
if (!isset($_SESSION['isLoggedIn']) || $_SESSION['isLoggedIn'] !== true) {
|
||||||
@@ -68,25 +47,16 @@ abstract class Controller
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Проверить, авторизован ли пользователь
|
|
||||||
*/
|
|
||||||
protected function isAuthenticated(): bool
|
protected function isAuthenticated(): bool
|
||||||
{
|
{
|
||||||
return isset($_SESSION['isLoggedIn']) && $_SESSION['isLoggedIn'] === true;
|
return isset($_SESSION['isLoggedIn']) && $_SESSION['isLoggedIn'] === true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Проверить, является ли пользователь администратором
|
|
||||||
*/
|
|
||||||
protected function isAdmin(): bool
|
protected function isAdmin(): bool
|
||||||
{
|
{
|
||||||
return $this->isAuthenticated() && isset($_SESSION['isAdmin']) && $_SESSION['isAdmin'] === true;
|
return $this->isAuthenticated() && isset($_SESSION['isAdmin']) && $_SESSION['isAdmin'] === true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Требовать авторизацию
|
|
||||||
*/
|
|
||||||
protected function requireAuth(): void
|
protected function requireAuth(): void
|
||||||
{
|
{
|
||||||
if (!$this->isAuthenticated()) {
|
if (!$this->isAuthenticated()) {
|
||||||
@@ -95,9 +65,6 @@ abstract class Controller
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Требовать права администратора
|
|
||||||
*/
|
|
||||||
protected function requireAdmin(): void
|
protected function requireAdmin(): void
|
||||||
{
|
{
|
||||||
if (!$this->isAdmin()) {
|
if (!$this->isAdmin()) {
|
||||||
@@ -105,9 +72,6 @@ abstract class Controller
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Получить POST данные
|
|
||||||
*/
|
|
||||||
protected function getPost(?string $key = null, $default = null)
|
protected function getPost(?string $key = null, $default = null)
|
||||||
{
|
{
|
||||||
if ($key === null) {
|
if ($key === null) {
|
||||||
@@ -116,9 +80,6 @@ abstract class Controller
|
|||||||
return $_POST[$key] ?? $default;
|
return $_POST[$key] ?? $default;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Получить GET данные
|
|
||||||
*/
|
|
||||||
protected function getQuery(?string $key = null, $default = null)
|
protected function getQuery(?string $key = null, $default = null)
|
||||||
{
|
{
|
||||||
if ($key === null) {
|
if ($key === null) {
|
||||||
@@ -127,17 +88,11 @@ abstract class Controller
|
|||||||
return $_GET[$key] ?? $default;
|
return $_GET[$key] ?? $default;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Установить flash-сообщение
|
|
||||||
*/
|
|
||||||
protected function setFlash(string $type, string $message): void
|
protected function setFlash(string $type, string $message): void
|
||||||
{
|
{
|
||||||
$_SESSION['flash'][$type] = $message;
|
$_SESSION['flash'][$type] = $message;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Получить flash-сообщение
|
|
||||||
*/
|
|
||||||
protected function getFlash(string $type): ?string
|
protected function getFlash(string $type): ?string
|
||||||
{
|
{
|
||||||
$message = $_SESSION['flash'][$type] ?? null;
|
$message = $_SESSION['flash'][$type] ?? null;
|
||||||
@@ -145,4 +100,3 @@ abstract class Controller
|
|||||||
return $message;
|
return $message;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,9 +2,6 @@
|
|||||||
|
|
||||||
namespace App\Core;
|
namespace App\Core;
|
||||||
|
|
||||||
/**
|
|
||||||
* Database - Singleton класс для подключения к PostgreSQL
|
|
||||||
*/
|
|
||||||
class Database
|
class Database
|
||||||
{
|
{
|
||||||
private static ?Database $instance = null;
|
private static ?Database $instance = null;
|
||||||
@@ -38,9 +35,6 @@ class Database
|
|||||||
return $this->connection;
|
return $this->connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Выполнить SELECT запрос
|
|
||||||
*/
|
|
||||||
public function query(string $sql, array $params = []): array
|
public function query(string $sql, array $params = []): array
|
||||||
{
|
{
|
||||||
$stmt = $this->connection->prepare($sql);
|
$stmt = $this->connection->prepare($sql);
|
||||||
@@ -48,9 +42,6 @@ class Database
|
|||||||
return $stmt->fetchAll();
|
return $stmt->fetchAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Выполнить SELECT запрос и получить одну запись
|
|
||||||
*/
|
|
||||||
public function queryOne(string $sql, array $params = []): ?array
|
public function queryOne(string $sql, array $params = []): ?array
|
||||||
{
|
{
|
||||||
$stmt = $this->connection->prepare($sql);
|
$stmt = $this->connection->prepare($sql);
|
||||||
@@ -59,52 +50,36 @@ class Database
|
|||||||
return $result ?: null;
|
return $result ?: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Выполнить INSERT/UPDATE/DELETE запрос
|
|
||||||
*/
|
|
||||||
public function execute(string $sql, array $params = []): bool
|
public function execute(string $sql, array $params = []): bool
|
||||||
{
|
{
|
||||||
$stmt = $this->connection->prepare($sql);
|
$stmt = $this->connection->prepare($sql);
|
||||||
return $stmt->execute($params);
|
return $stmt->execute($params);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Получить ID последней вставленной записи
|
|
||||||
*/
|
|
||||||
public function lastInsertId(): string
|
public function lastInsertId(): string
|
||||||
{
|
{
|
||||||
return $this->connection->lastInsertId();
|
return $this->connection->lastInsertId();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Начать транзакцию
|
|
||||||
*/
|
|
||||||
public function beginTransaction(): bool
|
public function beginTransaction(): bool
|
||||||
{
|
{
|
||||||
return $this->connection->beginTransaction();
|
return $this->connection->beginTransaction();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Подтвердить транзакцию
|
|
||||||
*/
|
|
||||||
public function commit(): bool
|
public function commit(): bool
|
||||||
{
|
{
|
||||||
return $this->connection->commit();
|
return $this->connection->commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Откатить транзакцию
|
|
||||||
*/
|
|
||||||
public function rollBack(): bool
|
public function rollBack(): bool
|
||||||
{
|
{
|
||||||
return $this->connection->rollBack();
|
return $this->connection->rollBack();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Запрещаем клонирование и десериализацию
|
|
||||||
private function __clone() {}
|
private function __clone() {}
|
||||||
|
|
||||||
public function __wakeup()
|
public function __wakeup()
|
||||||
{
|
{
|
||||||
throw new \Exception("Десериализация Singleton запрещена");
|
throw new \Exception("Десериализация Singleton запрещена");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,9 +2,6 @@
|
|||||||
|
|
||||||
namespace App\Core;
|
namespace App\Core;
|
||||||
|
|
||||||
/**
|
|
||||||
* Model - базовый класс модели
|
|
||||||
*/
|
|
||||||
abstract class Model
|
abstract class Model
|
||||||
{
|
{
|
||||||
protected Database $db;
|
protected Database $db;
|
||||||
@@ -16,18 +13,12 @@ abstract class Model
|
|||||||
$this->db = Database::getInstance();
|
$this->db = Database::getInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Найти запись по первичному ключу
|
|
||||||
*/
|
|
||||||
public function find(int $id): ?array
|
public function find(int $id): ?array
|
||||||
{
|
{
|
||||||
$sql = "SELECT * FROM {$this->table} WHERE {$this->primaryKey} = ?";
|
$sql = "SELECT * FROM {$this->table} WHERE {$this->primaryKey} = ?";
|
||||||
return $this->db->queryOne($sql, [$id]);
|
return $this->db->queryOne($sql, [$id]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Получить все записи
|
|
||||||
*/
|
|
||||||
public function all(?string $orderBy = null): array
|
public function all(?string $orderBy = null): array
|
||||||
{
|
{
|
||||||
$sql = "SELECT * FROM {$this->table}";
|
$sql = "SELECT * FROM {$this->table}";
|
||||||
@@ -37,9 +28,6 @@ abstract class Model
|
|||||||
return $this->db->query($sql);
|
return $this->db->query($sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Найти записи по условию
|
|
||||||
*/
|
|
||||||
public function where(array $conditions, ?string $orderBy = null): array
|
public function where(array $conditions, ?string $orderBy = null): array
|
||||||
{
|
{
|
||||||
$where = [];
|
$where = [];
|
||||||
@@ -59,9 +47,6 @@ abstract class Model
|
|||||||
return $this->db->query($sql, $params);
|
return $this->db->query($sql, $params);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Найти одну запись по условию
|
|
||||||
*/
|
|
||||||
public function findWhere(array $conditions): ?array
|
public function findWhere(array $conditions): ?array
|
||||||
{
|
{
|
||||||
$where = [];
|
$where = [];
|
||||||
@@ -76,9 +61,6 @@ abstract class Model
|
|||||||
return $this->db->queryOne($sql, $params);
|
return $this->db->queryOne($sql, $params);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Создать новую запись
|
|
||||||
*/
|
|
||||||
public function create(array $data): ?int
|
public function create(array $data): ?int
|
||||||
{
|
{
|
||||||
$columns = array_keys($data);
|
$columns = array_keys($data);
|
||||||
@@ -98,9 +80,6 @@ abstract class Model
|
|||||||
return (int) $stmt->fetchColumn();
|
return (int) $stmt->fetchColumn();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Обновить запись
|
|
||||||
*/
|
|
||||||
public function update(int $id, array $data): bool
|
public function update(int $id, array $data): bool
|
||||||
{
|
{
|
||||||
$set = [];
|
$set = [];
|
||||||
@@ -122,18 +101,12 @@ abstract class Model
|
|||||||
return $this->db->execute($sql, $params);
|
return $this->db->execute($sql, $params);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Удалить запись
|
|
||||||
*/
|
|
||||||
public function delete(int $id): bool
|
public function delete(int $id): bool
|
||||||
{
|
{
|
||||||
$sql = "DELETE FROM {$this->table} WHERE {$this->primaryKey} = ?";
|
$sql = "DELETE FROM {$this->table} WHERE {$this->primaryKey} = ?";
|
||||||
return $this->db->execute($sql, [$id]);
|
return $this->db->execute($sql, [$id]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Подсчитать количество записей
|
|
||||||
*/
|
|
||||||
public function count(array $conditions = []): int
|
public function count(array $conditions = []): int
|
||||||
{
|
{
|
||||||
$sql = "SELECT COUNT(*) FROM {$this->table}";
|
$sql = "SELECT COUNT(*) FROM {$this->table}";
|
||||||
@@ -153,28 +126,18 @@ abstract class Model
|
|||||||
return (int) $stmt->fetchColumn();
|
return (int) $stmt->fetchColumn();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Выполнить произвольный SQL запрос
|
|
||||||
*/
|
|
||||||
protected function query(string $sql, array $params = []): array
|
protected function query(string $sql, array $params = []): array
|
||||||
{
|
{
|
||||||
return $this->db->query($sql, $params);
|
return $this->db->query($sql, $params);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Выполнить произвольный SQL запрос и получить одну запись
|
|
||||||
*/
|
|
||||||
protected function queryOne(string $sql, array $params = []): ?array
|
protected function queryOne(string $sql, array $params = []): ?array
|
||||||
{
|
{
|
||||||
return $this->db->queryOne($sql, $params);
|
return $this->db->queryOne($sql, $params);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Выполнить произвольный SQL запрос (INSERT/UPDATE/DELETE)
|
|
||||||
*/
|
|
||||||
protected function execute(string $sql, array $params = []): bool
|
protected function execute(string $sql, array $params = []): bool
|
||||||
{
|
{
|
||||||
return $this->db->execute($sql, $params);
|
return $this->db->execute($sql, $params);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,17 +2,11 @@
|
|||||||
|
|
||||||
namespace App\Core;
|
namespace App\Core;
|
||||||
|
|
||||||
/**
|
|
||||||
* Router - маршрутизатор запросов
|
|
||||||
*/
|
|
||||||
class Router
|
class Router
|
||||||
{
|
{
|
||||||
private array $routes = [];
|
private array $routes = [];
|
||||||
private array $params = [];
|
private array $params = [];
|
||||||
|
|
||||||
/**
|
|
||||||
* Добавить маршрут
|
|
||||||
*/
|
|
||||||
public function add(string $method, string $route, string $controller, string $action): self
|
public function add(string $method, string $route, string $controller, string $action): self
|
||||||
{
|
{
|
||||||
$this->routes[] = [
|
$this->routes[] = [
|
||||||
@@ -24,25 +18,16 @@ class Router
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* GET маршрут
|
|
||||||
*/
|
|
||||||
public function get(string $route, string $controller, string $action): self
|
public function get(string $route, string $controller, string $action): self
|
||||||
{
|
{
|
||||||
return $this->add('GET', $route, $controller, $action);
|
return $this->add('GET', $route, $controller, $action);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* POST маршрут
|
|
||||||
*/
|
|
||||||
public function post(string $route, string $controller, string $action): self
|
public function post(string $route, string $controller, string $action): self
|
||||||
{
|
{
|
||||||
return $this->add('POST', $route, $controller, $action);
|
return $this->add('POST', $route, $controller, $action);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Найти маршрут по URL и методу
|
|
||||||
*/
|
|
||||||
public function match(string $url, string $method): ?array
|
public function match(string $url, string $method): ?array
|
||||||
{
|
{
|
||||||
$method = strtoupper($method);
|
$method = strtoupper($method);
|
||||||
@@ -57,7 +42,6 @@ class Router
|
|||||||
$pattern = $this->convertRouteToRegex($route['route']);
|
$pattern = $this->convertRouteToRegex($route['route']);
|
||||||
|
|
||||||
if (preg_match($pattern, $url, $matches)) {
|
if (preg_match($pattern, $url, $matches)) {
|
||||||
// Извлекаем параметры из URL
|
|
||||||
$this->params = $this->extractParams($route['route'], $matches);
|
$this->params = $this->extractParams($route['route'], $matches);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
@@ -71,27 +55,16 @@ class Router
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Преобразовать маршрут в регулярное выражение
|
|
||||||
*/
|
|
||||||
private function convertRouteToRegex(string $route): string
|
private function convertRouteToRegex(string $route): string
|
||||||
{
|
{
|
||||||
$route = trim($route, '/');
|
$route = trim($route, '/');
|
||||||
|
|
||||||
// Заменяем {param} на regex группу
|
|
||||||
$pattern = preg_replace('/\{([a-zA-Z_]+)\}/', '([^/]+)', $route);
|
$pattern = preg_replace('/\{([a-zA-Z_]+)\}/', '([^/]+)', $route);
|
||||||
|
|
||||||
return '#^' . $pattern . '$#';
|
return '#^' . $pattern . '$#';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Извлечь параметры из совпадений
|
|
||||||
*/
|
|
||||||
private function extractParams(string $route, array $matches): array
|
private function extractParams(string $route, array $matches): array
|
||||||
{
|
{
|
||||||
$params = [];
|
$params = [];
|
||||||
|
|
||||||
// Находим все {param} в маршруте
|
|
||||||
preg_match_all('/\{([a-zA-Z_]+)\}/', $route, $paramNames);
|
preg_match_all('/\{([a-zA-Z_]+)\}/', $route, $paramNames);
|
||||||
|
|
||||||
foreach ($paramNames[1] as $index => $name) {
|
foreach ($paramNames[1] as $index => $name) {
|
||||||
@@ -103,9 +76,6 @@ class Router
|
|||||||
return $params;
|
return $params;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Удалить query string из URL
|
|
||||||
*/
|
|
||||||
private function removeQueryString(string $url): string
|
private function removeQueryString(string $url): string
|
||||||
{
|
{
|
||||||
if ($pos = strpos($url, '?')) {
|
if ($pos = strpos($url, '?')) {
|
||||||
@@ -114,17 +84,11 @@ class Router
|
|||||||
return $url;
|
return $url;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Получить параметры маршрута
|
|
||||||
*/
|
|
||||||
public function getParams(): array
|
public function getParams(): array
|
||||||
{
|
{
|
||||||
return $this->params;
|
return $this->params;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Диспетчеризация запроса
|
|
||||||
*/
|
|
||||||
public function dispatch(string $url, string $method): void
|
public function dispatch(string $url, string $method): void
|
||||||
{
|
{
|
||||||
$match = $this->match($url, $method);
|
$match = $this->match($url, $method);
|
||||||
@@ -148,8 +112,6 @@ class Router
|
|||||||
throw new \Exception("Метод {$action} не найден в контроллере {$controllerClass}");
|
throw new \Exception("Метод {$action} не найден в контроллере {$controllerClass}");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Вызываем метод контроллера с параметрами
|
|
||||||
call_user_func_array([$controller, $action], $match['params']);
|
call_user_func_array([$controller, $action], $match['params']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,24 +2,15 @@
|
|||||||
|
|
||||||
namespace App\Core;
|
namespace App\Core;
|
||||||
|
|
||||||
/**
|
|
||||||
* View - класс для рендеринга представлений
|
|
||||||
*/
|
|
||||||
class View
|
class View
|
||||||
{
|
{
|
||||||
private static string $viewsPath = '';
|
private static string $viewsPath = '';
|
||||||
|
|
||||||
/**
|
|
||||||
* Установить путь к директории представлений
|
|
||||||
*/
|
|
||||||
public static function setViewsPath(string $path): void
|
public static function setViewsPath(string $path): void
|
||||||
{
|
{
|
||||||
self::$viewsPath = rtrim($path, '/');
|
self::$viewsPath = rtrim($path, '/');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Получить путь к директории представлений
|
|
||||||
*/
|
|
||||||
public static function getViewsPath(): string
|
public static function getViewsPath(): string
|
||||||
{
|
{
|
||||||
if (empty(self::$viewsPath)) {
|
if (empty(self::$viewsPath)) {
|
||||||
@@ -28,9 +19,6 @@ class View
|
|||||||
return self::$viewsPath;
|
return self::$viewsPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Отрендерить представление
|
|
||||||
*/
|
|
||||||
public static function render(string $view, array $data = [], ?string $layout = 'main'): string
|
public static function render(string $view, array $data = [], ?string $layout = 'main'): string
|
||||||
{
|
{
|
||||||
$viewPath = self::getViewsPath() . '/' . str_replace('.', '/', $view) . '.php';
|
$viewPath = self::getViewsPath() . '/' . str_replace('.', '/', $view) . '.php';
|
||||||
@@ -39,15 +27,12 @@ class View
|
|||||||
throw new \Exception("Представление не найдено: {$viewPath}");
|
throw new \Exception("Представление не найдено: {$viewPath}");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Извлекаем данные в переменные
|
|
||||||
extract($data);
|
extract($data);
|
||||||
|
|
||||||
// Буферизируем вывод контента
|
|
||||||
ob_start();
|
ob_start();
|
||||||
require $viewPath;
|
require $viewPath;
|
||||||
$content = ob_get_clean();
|
$content = ob_get_clean();
|
||||||
|
|
||||||
// Если есть layout, оборачиваем контент
|
|
||||||
if ($layout !== null) {
|
if ($layout !== null) {
|
||||||
$layoutPath = self::getViewsPath() . '/layouts/' . $layout . '.php';
|
$layoutPath = self::getViewsPath() . '/layouts/' . $layout . '.php';
|
||||||
|
|
||||||
@@ -63,9 +48,6 @@ class View
|
|||||||
return $content;
|
return $content;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Отрендерить partial (часть шаблона)
|
|
||||||
*/
|
|
||||||
public static function partial(string $partial, array $data = []): string
|
public static function partial(string $partial, array $data = []): string
|
||||||
{
|
{
|
||||||
$partialPath = self::getViewsPath() . '/partials/' . $partial . '.php';
|
$partialPath = self::getViewsPath() . '/partials/' . $partial . '.php';
|
||||||
@@ -81,49 +63,31 @@ class View
|
|||||||
return ob_get_clean();
|
return ob_get_clean();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Экранирование HTML
|
|
||||||
*/
|
|
||||||
public static function escape(string $value): string
|
public static function escape(string $value): string
|
||||||
{
|
{
|
||||||
return htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
|
return htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Сокращенный алиас для escape
|
|
||||||
*/
|
|
||||||
public static function e(string $value): string
|
public static function e(string $value): string
|
||||||
{
|
{
|
||||||
return self::escape($value);
|
return self::escape($value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Форматирование цены
|
|
||||||
*/
|
|
||||||
public static function formatPrice($price): string
|
public static function formatPrice($price): string
|
||||||
{
|
{
|
||||||
return number_format((float)$price, 0, '', ' ') . ' ₽';
|
return number_format((float)$price, 0, '', ' ') . ' ₽';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Форматирование даты
|
|
||||||
*/
|
|
||||||
public static function formatDate(string $date, string $format = 'd.m.Y'): string
|
public static function formatDate(string $date, string $format = 'd.m.Y'): string
|
||||||
{
|
{
|
||||||
return date($format, strtotime($date));
|
return date($format, strtotime($date));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Форматирование даты и времени
|
|
||||||
*/
|
|
||||||
public static function formatDateTime(string $date, string $format = 'd.m.Y H:i'): string
|
public static function formatDateTime(string $date, string $format = 'd.m.Y H:i'): string
|
||||||
{
|
{
|
||||||
return date($format, strtotime($date));
|
return date($format, strtotime($date));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Получить flash-сообщения
|
|
||||||
*/
|
|
||||||
public static function getFlashMessages(): array
|
public static function getFlashMessages(): array
|
||||||
{
|
{
|
||||||
$messages = $_SESSION['flash'] ?? [];
|
$messages = $_SESSION['flash'] ?? [];
|
||||||
@@ -131,25 +95,16 @@ class View
|
|||||||
return $messages;
|
return $messages;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Проверить, авторизован ли пользователь
|
|
||||||
*/
|
|
||||||
public static function isAuthenticated(): bool
|
public static function isAuthenticated(): bool
|
||||||
{
|
{
|
||||||
return isset($_SESSION['isLoggedIn']) && $_SESSION['isLoggedIn'] === true;
|
return isset($_SESSION['isLoggedIn']) && $_SESSION['isLoggedIn'] === true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Проверить, является ли пользователь администратором
|
|
||||||
*/
|
|
||||||
public static function isAdmin(): bool
|
public static function isAdmin(): bool
|
||||||
{
|
{
|
||||||
return self::isAuthenticated() && isset($_SESSION['isAdmin']) && $_SESSION['isAdmin'] === true;
|
return self::isAuthenticated() && isset($_SESSION['isAdmin']) && $_SESSION['isAdmin'] === true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Получить данные текущего пользователя
|
|
||||||
*/
|
|
||||||
public static function currentUser(): ?array
|
public static function currentUser(): ?array
|
||||||
{
|
{
|
||||||
if (!self::isAuthenticated()) {
|
if (!self::isAuthenticated()) {
|
||||||
@@ -165,20 +120,13 @@ class View
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Генерация URL
|
|
||||||
*/
|
|
||||||
public static function url(string $path): string
|
public static function url(string $path): string
|
||||||
{
|
{
|
||||||
return '/' . ltrim($path, '/');
|
return '/' . ltrim($path, '/');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Генерация URL для ассетов
|
|
||||||
*/
|
|
||||||
public static function asset(string $path): string
|
public static function asset(string $path): string
|
||||||
{
|
{
|
||||||
return '/assets/' . ltrim($path, '/');
|
return '/assets/' . ltrim($path, '/');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,17 +4,11 @@ namespace App\Models;
|
|||||||
|
|
||||||
use App\Core\Model;
|
use App\Core\Model;
|
||||||
|
|
||||||
/**
|
|
||||||
* Cart - модель корзины
|
|
||||||
*/
|
|
||||||
class Cart extends Model
|
class Cart extends Model
|
||||||
{
|
{
|
||||||
protected string $table = 'cart';
|
protected string $table = 'cart';
|
||||||
protected string $primaryKey = 'cart_id';
|
protected string $primaryKey = 'cart_id';
|
||||||
|
|
||||||
/**
|
|
||||||
* Получить корзину пользователя
|
|
||||||
*/
|
|
||||||
public function getUserCart(int $userId): array
|
public function getUserCart(int $userId): array
|
||||||
{
|
{
|
||||||
$sql = "SELECT
|
$sql = "SELECT
|
||||||
@@ -33,9 +27,6 @@ class Cart extends Model
|
|||||||
return $this->query($sql, [$userId]);
|
return $this->query($sql, [$userId]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Получить общую сумму корзины
|
|
||||||
*/
|
|
||||||
public function getCartTotal(int $userId): array
|
public function getCartTotal(int $userId): array
|
||||||
{
|
{
|
||||||
$sql = "SELECT
|
$sql = "SELECT
|
||||||
@@ -52,9 +43,6 @@ class Cart extends Model
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Получить количество товаров в корзине
|
|
||||||
*/
|
|
||||||
public function getCount(int $userId): int
|
public function getCount(int $userId): int
|
||||||
{
|
{
|
||||||
$sql = "SELECT COALESCE(SUM(quantity), 0) as total FROM {$this->table} WHERE user_id = ?";
|
$sql = "SELECT COALESCE(SUM(quantity), 0) as total FROM {$this->table} WHERE user_id = ?";
|
||||||
@@ -62,19 +50,14 @@ class Cart extends Model
|
|||||||
return (int) $result['total'];
|
return (int) $result['total'];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Добавить товар в корзину
|
|
||||||
*/
|
|
||||||
public function addItem(int $userId, int $productId, int $quantity = 1): bool
|
public function addItem(int $userId, int $productId, int $quantity = 1): bool
|
||||||
{
|
{
|
||||||
// Проверяем, есть ли уже этот товар в корзине
|
|
||||||
$existing = $this->findWhere([
|
$existing = $this->findWhere([
|
||||||
'user_id' => $userId,
|
'user_id' => $userId,
|
||||||
'product_id' => $productId
|
'product_id' => $productId
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if ($existing) {
|
if ($existing) {
|
||||||
// Увеличиваем количество
|
|
||||||
$newQuantity = $existing['quantity'] + $quantity;
|
$newQuantity = $existing['quantity'] + $quantity;
|
||||||
return $this->update($existing['cart_id'], [
|
return $this->update($existing['cart_id'], [
|
||||||
'quantity' => $newQuantity,
|
'quantity' => $newQuantity,
|
||||||
@@ -82,7 +65,6 @@ class Cart extends Model
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Добавляем новую запись
|
|
||||||
return $this->create([
|
return $this->create([
|
||||||
'user_id' => $userId,
|
'user_id' => $userId,
|
||||||
'product_id' => $productId,
|
'product_id' => $productId,
|
||||||
@@ -90,9 +72,6 @@ class Cart extends Model
|
|||||||
]) !== null;
|
]) !== null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Обновить количество товара в корзине
|
|
||||||
*/
|
|
||||||
public function updateQuantity(int $userId, int $productId, int $quantity): bool
|
public function updateQuantity(int $userId, int $productId, int $quantity): bool
|
||||||
{
|
{
|
||||||
$sql = "UPDATE {$this->table}
|
$sql = "UPDATE {$this->table}
|
||||||
@@ -101,27 +80,18 @@ class Cart extends Model
|
|||||||
return $this->execute($sql, [$quantity, $userId, $productId]);
|
return $this->execute($sql, [$quantity, $userId, $productId]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Удалить товар из корзины
|
|
||||||
*/
|
|
||||||
public function removeItem(int $userId, int $productId): bool
|
public function removeItem(int $userId, int $productId): bool
|
||||||
{
|
{
|
||||||
$sql = "DELETE FROM {$this->table} WHERE user_id = ? AND product_id = ?";
|
$sql = "DELETE FROM {$this->table} WHERE user_id = ? AND product_id = ?";
|
||||||
return $this->execute($sql, [$userId, $productId]);
|
return $this->execute($sql, [$userId, $productId]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Очистить корзину пользователя
|
|
||||||
*/
|
|
||||||
public function clearCart(int $userId): bool
|
public function clearCart(int $userId): bool
|
||||||
{
|
{
|
||||||
$sql = "DELETE FROM {$this->table} WHERE user_id = ?";
|
$sql = "DELETE FROM {$this->table} WHERE user_id = ?";
|
||||||
return $this->execute($sql, [$userId]);
|
return $this->execute($sql, [$userId]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Проверить, есть ли товар в корзине
|
|
||||||
*/
|
|
||||||
public function hasItem(int $userId, int $productId): bool
|
public function hasItem(int $userId, int $productId): bool
|
||||||
{
|
{
|
||||||
$item = $this->findWhere([
|
$item = $this->findWhere([
|
||||||
@@ -131,9 +101,6 @@ class Cart extends Model
|
|||||||
return $item !== null;
|
return $item !== null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Получить товар из корзины
|
|
||||||
*/
|
|
||||||
public function getItem(int $userId, int $productId): ?array
|
public function getItem(int $userId, int $productId): ?array
|
||||||
{
|
{
|
||||||
return $this->findWhere([
|
return $this->findWhere([
|
||||||
@@ -142,4 +109,3 @@ class Cart extends Model
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,17 +4,11 @@ namespace App\Models;
|
|||||||
|
|
||||||
use App\Core\Model;
|
use App\Core\Model;
|
||||||
|
|
||||||
/**
|
|
||||||
* Category - модель категории товаров
|
|
||||||
*/
|
|
||||||
class Category extends Model
|
class Category extends Model
|
||||||
{
|
{
|
||||||
protected string $table = 'categories';
|
protected string $table = 'categories';
|
||||||
protected string $primaryKey = 'category_id';
|
protected string $primaryKey = 'category_id';
|
||||||
|
|
||||||
/**
|
|
||||||
* Получить активные категории
|
|
||||||
*/
|
|
||||||
public function getActive(): array
|
public function getActive(): array
|
||||||
{
|
{
|
||||||
$sql = "SELECT * FROM {$this->table}
|
$sql = "SELECT * FROM {$this->table}
|
||||||
@@ -23,9 +17,6 @@ class Category extends Model
|
|||||||
return $this->query($sql);
|
return $this->query($sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Получить родительские категории (без parent_id)
|
|
||||||
*/
|
|
||||||
public function getParent(): array
|
public function getParent(): array
|
||||||
{
|
{
|
||||||
$sql = "SELECT * FROM {$this->table}
|
$sql = "SELECT * FROM {$this->table}
|
||||||
@@ -34,9 +25,6 @@ class Category extends Model
|
|||||||
return $this->query($sql);
|
return $this->query($sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Получить подкатегории
|
|
||||||
*/
|
|
||||||
public function getChildren(int $parentId): array
|
public function getChildren(int $parentId): array
|
||||||
{
|
{
|
||||||
$sql = "SELECT * FROM {$this->table}
|
$sql = "SELECT * FROM {$this->table}
|
||||||
@@ -45,17 +33,11 @@ class Category extends Model
|
|||||||
return $this->query($sql, [$parentId]);
|
return $this->query($sql, [$parentId]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Получить категорию по slug
|
|
||||||
*/
|
|
||||||
public function findBySlug(string $slug): ?array
|
public function findBySlug(string $slug): ?array
|
||||||
{
|
{
|
||||||
return $this->findWhere(['slug' => $slug]);
|
return $this->findWhere(['slug' => $slug]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Получить все категории с количеством товаров
|
|
||||||
*/
|
|
||||||
public function getAllWithProductCount(): array
|
public function getAllWithProductCount(): array
|
||||||
{
|
{
|
||||||
$sql = "SELECT c1.*, c2.name as parent_name,
|
$sql = "SELECT c1.*, c2.name as parent_name,
|
||||||
@@ -66,9 +48,6 @@ class Category extends Model
|
|||||||
return $this->query($sql);
|
return $this->query($sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Создать категорию
|
|
||||||
*/
|
|
||||||
public function createCategory(array $data): ?int
|
public function createCategory(array $data): ?int
|
||||||
{
|
{
|
||||||
$slug = $this->generateSlug($data['name']);
|
$slug = $this->generateSlug($data['name']);
|
||||||
@@ -83,9 +62,6 @@ class Category extends Model
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Обновить категорию
|
|
||||||
*/
|
|
||||||
public function updateCategory(int $id, array $data): bool
|
public function updateCategory(int $id, array $data): bool
|
||||||
{
|
{
|
||||||
$updateData = [
|
$updateData = [
|
||||||
@@ -101,23 +77,17 @@ class Category extends Model
|
|||||||
return $this->update($id, $updateData);
|
return $this->update($id, $updateData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Безопасное удаление категории
|
|
||||||
*/
|
|
||||||
public function safeDelete(int $id): array
|
public function safeDelete(int $id): array
|
||||||
{
|
{
|
||||||
// Проверяем наличие товаров
|
|
||||||
$sql = "SELECT COUNT(*) as cnt FROM products WHERE category_id = ?";
|
$sql = "SELECT COUNT(*) as cnt FROM products WHERE category_id = ?";
|
||||||
$result = $this->queryOne($sql, [$id]);
|
$result = $this->queryOne($sql, [$id]);
|
||||||
$productCount = (int) $result['cnt'];
|
$productCount = (int) $result['cnt'];
|
||||||
|
|
||||||
// Проверяем наличие дочерних категорий
|
|
||||||
$sql = "SELECT COUNT(*) as cnt FROM {$this->table} WHERE parent_id = ?";
|
$sql = "SELECT COUNT(*) as cnt FROM {$this->table} WHERE parent_id = ?";
|
||||||
$result = $this->queryOne($sql, [$id]);
|
$result = $this->queryOne($sql, [$id]);
|
||||||
$childCount = (int) $result['cnt'];
|
$childCount = (int) $result['cnt'];
|
||||||
|
|
||||||
if ($productCount > 0 || $childCount > 0) {
|
if ($productCount > 0 || $childCount > 0) {
|
||||||
// Скрываем вместо удаления
|
|
||||||
$this->update($id, ['is_active' => false]);
|
$this->update($id, ['is_active' => false]);
|
||||||
return [
|
return [
|
||||||
'deleted' => false,
|
'deleted' => false,
|
||||||
@@ -126,19 +96,14 @@ class Category extends Model
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Удаляем полностью
|
|
||||||
$this->delete($id);
|
$this->delete($id);
|
||||||
return ['deleted' => true, 'hidden' => false];
|
return ['deleted' => true, 'hidden' => false];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Генерация slug из названия
|
|
||||||
*/
|
|
||||||
private function generateSlug(string $name): string
|
private function generateSlug(string $name): string
|
||||||
{
|
{
|
||||||
$slug = mb_strtolower($name);
|
$slug = mb_strtolower($name);
|
||||||
|
|
||||||
// Транслитерация
|
|
||||||
$transliteration = [
|
$transliteration = [
|
||||||
'а' => 'a', 'б' => 'b', 'в' => 'v', 'г' => 'g', 'д' => 'd',
|
'а' => 'a', 'б' => 'b', 'в' => 'v', 'г' => 'g', 'д' => 'd',
|
||||||
'е' => 'e', 'ё' => 'yo', 'ж' => 'zh', 'з' => 'z', 'и' => 'i',
|
'е' => 'e', 'ё' => 'yo', 'ж' => 'zh', 'з' => 'z', 'и' => 'i',
|
||||||
@@ -156,4 +121,3 @@ class Category extends Model
|
|||||||
return $slug;
|
return $slug;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,17 +5,11 @@ namespace App\Models;
|
|||||||
use App\Core\Model;
|
use App\Core\Model;
|
||||||
use App\Core\Database;
|
use App\Core\Database;
|
||||||
|
|
||||||
/**
|
|
||||||
* Order - модель заказа
|
|
||||||
*/
|
|
||||||
class Order extends Model
|
class Order extends Model
|
||||||
{
|
{
|
||||||
protected string $table = 'orders';
|
protected string $table = 'orders';
|
||||||
protected string $primaryKey = 'order_id';
|
protected string $primaryKey = 'order_id';
|
||||||
|
|
||||||
/**
|
|
||||||
* Создать заказ из корзины
|
|
||||||
*/
|
|
||||||
public function createFromCart(int $userId, array $cartItems, array $orderData): ?array
|
public function createFromCart(int $userId, array $cartItems, array $orderData): ?array
|
||||||
{
|
{
|
||||||
$db = Database::getInstance();
|
$db = Database::getInstance();
|
||||||
@@ -23,7 +17,6 @@ class Order extends Model
|
|||||||
try {
|
try {
|
||||||
$db->beginTransaction();
|
$db->beginTransaction();
|
||||||
|
|
||||||
// Считаем итоговую сумму
|
|
||||||
$subtotal = 0;
|
$subtotal = 0;
|
||||||
foreach ($cartItems as $item) {
|
foreach ($cartItems as $item) {
|
||||||
$subtotal += $item['price'] * $item['quantity'];
|
$subtotal += $item['price'] * $item['quantity'];
|
||||||
@@ -33,10 +26,8 @@ class Order extends Model
|
|||||||
$deliveryPrice = (float) ($orderData['delivery_price'] ?? 2000);
|
$deliveryPrice = (float) ($orderData['delivery_price'] ?? 2000);
|
||||||
$finalAmount = $subtotal - $discountAmount + $deliveryPrice;
|
$finalAmount = $subtotal - $discountAmount + $deliveryPrice;
|
||||||
|
|
||||||
// Генерируем номер заказа
|
|
||||||
$orderNumber = 'ORD-' . date('Ymd-His') . '-' . rand(1000, 9999);
|
$orderNumber = 'ORD-' . date('Ymd-His') . '-' . rand(1000, 9999);
|
||||||
|
|
||||||
// Создаем заказ
|
|
||||||
$orderId = $this->create([
|
$orderId = $this->create([
|
||||||
'user_id' => $userId,
|
'user_id' => $userId,
|
||||||
'order_number' => $orderNumber,
|
'order_number' => $orderNumber,
|
||||||
@@ -61,18 +52,15 @@ class Order extends Model
|
|||||||
throw new \Exception('Не удалось создать заказ');
|
throw new \Exception('Не удалось создать заказ');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Добавляем товары в заказ
|
|
||||||
foreach ($cartItems as $item) {
|
foreach ($cartItems as $item) {
|
||||||
$this->addOrderItem($orderId, $item);
|
$this->addOrderItem($orderId, $item);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Уменьшаем количество товаров на складе
|
|
||||||
$productModel = new Product();
|
$productModel = new Product();
|
||||||
foreach ($cartItems as $item) {
|
foreach ($cartItems as $item) {
|
||||||
$productModel->decreaseStock($item['product_id'], $item['quantity']);
|
$productModel->decreaseStock($item['product_id'], $item['quantity']);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Очищаем корзину
|
|
||||||
$cartModel = new Cart();
|
$cartModel = new Cart();
|
||||||
$cartModel->clearCart($userId);
|
$cartModel->clearCart($userId);
|
||||||
|
|
||||||
@@ -90,9 +78,6 @@ class Order extends Model
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Добавить товар в заказ
|
|
||||||
*/
|
|
||||||
private function addOrderItem(int $orderId, array $item): void
|
private function addOrderItem(int $orderId, array $item): void
|
||||||
{
|
{
|
||||||
$sql = "INSERT INTO order_items
|
$sql = "INSERT INTO order_items
|
||||||
@@ -111,9 +96,6 @@ class Order extends Model
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Получить заказ с подробностями
|
|
||||||
*/
|
|
||||||
public function getWithDetails(int $orderId): ?array
|
public function getWithDetails(int $orderId): ?array
|
||||||
{
|
{
|
||||||
$sql = "SELECT o.*, u.email as user_email, u.full_name as user_full_name
|
$sql = "SELECT o.*, u.email as user_email, u.full_name as user_full_name
|
||||||
@@ -130,9 +112,6 @@ class Order extends Model
|
|||||||
return $order;
|
return $order;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Получить товары заказа
|
|
||||||
*/
|
|
||||||
public function getOrderItems(int $orderId): array
|
public function getOrderItems(int $orderId): array
|
||||||
{
|
{
|
||||||
$sql = "SELECT oi.*, p.image_url
|
$sql = "SELECT oi.*, p.image_url
|
||||||
@@ -142,9 +121,6 @@ class Order extends Model
|
|||||||
return $this->query($sql, [$orderId]);
|
return $this->query($sql, [$orderId]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Получить заказы пользователя
|
|
||||||
*/
|
|
||||||
public function getUserOrders(int $userId, int $limit = 50): array
|
public function getUserOrders(int $userId, int $limit = 50): array
|
||||||
{
|
{
|
||||||
$sql = "SELECT * FROM {$this->table}
|
$sql = "SELECT * FROM {$this->table}
|
||||||
@@ -154,9 +130,6 @@ class Order extends Model
|
|||||||
return $this->query($sql, [$userId, $limit]);
|
return $this->query($sql, [$userId, $limit]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Получить все заказы для админки
|
|
||||||
*/
|
|
||||||
public function getAllForAdmin(int $limit = 50): array
|
public function getAllForAdmin(int $limit = 50): array
|
||||||
{
|
{
|
||||||
$sql = "SELECT o.*, u.email as user_email
|
$sql = "SELECT o.*, u.email as user_email
|
||||||
@@ -167,9 +140,6 @@ class Order extends Model
|
|||||||
return $this->query($sql, [$limit]);
|
return $this->query($sql, [$limit]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Обновить статус заказа
|
|
||||||
*/
|
|
||||||
public function updateStatus(int $orderId, string $status): bool
|
public function updateStatus(int $orderId, string $status): bool
|
||||||
{
|
{
|
||||||
$updateData = [
|
$updateData = [
|
||||||
@@ -184,25 +154,19 @@ class Order extends Model
|
|||||||
return $this->update($orderId, $updateData);
|
return $this->update($orderId, $updateData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Получить статистику заказов
|
|
||||||
*/
|
|
||||||
public function getStats(): array
|
public function getStats(): array
|
||||||
{
|
{
|
||||||
$stats = [];
|
$stats = [];
|
||||||
|
|
||||||
// Общее количество заказов
|
|
||||||
$result = $this->queryOne("SELECT COUNT(*) as cnt FROM {$this->table}");
|
$result = $this->queryOne("SELECT COUNT(*) as cnt FROM {$this->table}");
|
||||||
$stats['total'] = (int) $result['cnt'];
|
$stats['total'] = (int) $result['cnt'];
|
||||||
|
|
||||||
// Выручка
|
|
||||||
$result = $this->queryOne(
|
$result = $this->queryOne(
|
||||||
"SELECT COALESCE(SUM(final_amount), 0) as revenue
|
"SELECT COALESCE(SUM(final_amount), 0) as revenue
|
||||||
FROM {$this->table} WHERE status = 'completed'"
|
FROM {$this->table} WHERE status = 'completed'"
|
||||||
);
|
);
|
||||||
$stats['revenue'] = (float) $result['revenue'];
|
$stats['revenue'] = (float) $result['revenue'];
|
||||||
|
|
||||||
// По статусам
|
|
||||||
$statuses = $this->query(
|
$statuses = $this->query(
|
||||||
"SELECT status, COUNT(*) as cnt FROM {$this->table} GROUP BY status"
|
"SELECT status, COUNT(*) as cnt FROM {$this->table} GROUP BY status"
|
||||||
);
|
);
|
||||||
@@ -214,4 +178,3 @@ class Order extends Model
|
|||||||
return $stats;
|
return $stats;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,17 +4,11 @@ namespace App\Models;
|
|||||||
|
|
||||||
use App\Core\Model;
|
use App\Core\Model;
|
||||||
|
|
||||||
/**
|
|
||||||
* Product - модель товара
|
|
||||||
*/
|
|
||||||
class Product extends Model
|
class Product extends Model
|
||||||
{
|
{
|
||||||
protected string $table = 'products';
|
protected string $table = 'products';
|
||||||
protected string $primaryKey = 'product_id';
|
protected string $primaryKey = 'product_id';
|
||||||
|
|
||||||
/**
|
|
||||||
* Получить товар с категорией
|
|
||||||
*/
|
|
||||||
public function findWithCategory(int $id): ?array
|
public function findWithCategory(int $id): ?array
|
||||||
{
|
{
|
||||||
$sql = "SELECT p.*, c.name as category_name, c.slug as category_slug
|
$sql = "SELECT p.*, c.name as category_name, c.slug as category_slug
|
||||||
@@ -24,9 +18,6 @@ class Product extends Model
|
|||||||
return $this->queryOne($sql, [$id]);
|
return $this->queryOne($sql, [$id]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Получить доступные товары
|
|
||||||
*/
|
|
||||||
public function getAvailable(array $filters = [], int $limit = 50): array
|
public function getAvailable(array $filters = [], int $limit = 50): array
|
||||||
{
|
{
|
||||||
$sql = "SELECT p.*, c.name as category_name
|
$sql = "SELECT p.*, c.name as category_name
|
||||||
@@ -35,13 +26,11 @@ class Product extends Model
|
|||||||
WHERE p.is_available = TRUE";
|
WHERE p.is_available = TRUE";
|
||||||
$params = [];
|
$params = [];
|
||||||
|
|
||||||
// Фильтр по категории
|
|
||||||
if (!empty($filters['category_id'])) {
|
if (!empty($filters['category_id'])) {
|
||||||
$sql .= " AND p.category_id = ?";
|
$sql .= " AND p.category_id = ?";
|
||||||
$params[] = $filters['category_id'];
|
$params[] = $filters['category_id'];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Фильтр по цене
|
|
||||||
if (!empty($filters['min_price'])) {
|
if (!empty($filters['min_price'])) {
|
||||||
$sql .= " AND p.price >= ?";
|
$sql .= " AND p.price >= ?";
|
||||||
$params[] = $filters['min_price'];
|
$params[] = $filters['min_price'];
|
||||||
@@ -51,21 +40,18 @@ class Product extends Model
|
|||||||
$params[] = $filters['max_price'];
|
$params[] = $filters['max_price'];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Фильтр по цветам
|
|
||||||
if (!empty($filters['colors']) && is_array($filters['colors'])) {
|
if (!empty($filters['colors']) && is_array($filters['colors'])) {
|
||||||
$placeholders = implode(',', array_fill(0, count($filters['colors']), '?'));
|
$placeholders = implode(',', array_fill(0, count($filters['colors']), '?'));
|
||||||
$sql .= " AND p.color IN ({$placeholders})";
|
$sql .= " AND p.color IN ({$placeholders})";
|
||||||
$params = array_merge($params, $filters['colors']);
|
$params = array_merge($params, $filters['colors']);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Фильтр по материалам
|
|
||||||
if (!empty($filters['materials']) && is_array($filters['materials'])) {
|
if (!empty($filters['materials']) && is_array($filters['materials'])) {
|
||||||
$placeholders = implode(',', array_fill(0, count($filters['materials']), '?'));
|
$placeholders = implode(',', array_fill(0, count($filters['materials']), '?'));
|
||||||
$sql .= " AND p.material IN ({$placeholders})";
|
$sql .= " AND p.material IN ({$placeholders})";
|
||||||
$params = array_merge($params, $filters['materials']);
|
$params = array_merge($params, $filters['materials']);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Поиск по названию/описанию
|
|
||||||
if (!empty($filters['search'])) {
|
if (!empty($filters['search'])) {
|
||||||
$sql .= " AND (p.name ILIKE ? OR p.description ILIKE ?)";
|
$sql .= " AND (p.name ILIKE ? OR p.description ILIKE ?)";
|
||||||
$search = '%' . $filters['search'] . '%';
|
$search = '%' . $filters['search'] . '%';
|
||||||
@@ -79,9 +65,6 @@ class Product extends Model
|
|||||||
return $this->query($sql, $params);
|
return $this->query($sql, $params);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Получить все товары (включая недоступные) для админки
|
|
||||||
*/
|
|
||||||
public function getAllForAdmin(bool $showAll = true): array
|
public function getAllForAdmin(bool $showAll = true): array
|
||||||
{
|
{
|
||||||
$sql = "SELECT p.*, c.name as category_name
|
$sql = "SELECT p.*, c.name as category_name
|
||||||
@@ -97,9 +80,6 @@ class Product extends Model
|
|||||||
return $this->query($sql);
|
return $this->query($sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Получить похожие товары
|
|
||||||
*/
|
|
||||||
public function getSimilar(int $productId, int $categoryId, int $limit = 3): array
|
public function getSimilar(int $productId, int $categoryId, int $limit = 3): array
|
||||||
{
|
{
|
||||||
$sql = "SELECT * FROM {$this->table}
|
$sql = "SELECT * FROM {$this->table}
|
||||||
@@ -111,9 +91,6 @@ class Product extends Model
|
|||||||
return $this->query($sql, [$categoryId, $productId, $limit]);
|
return $this->query($sql, [$categoryId, $productId, $limit]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Получить доступные цвета
|
|
||||||
*/
|
|
||||||
public function getAvailableColors(): array
|
public function getAvailableColors(): array
|
||||||
{
|
{
|
||||||
$sql = "SELECT DISTINCT color FROM {$this->table}
|
$sql = "SELECT DISTINCT color FROM {$this->table}
|
||||||
@@ -123,9 +100,6 @@ class Product extends Model
|
|||||||
return array_column($result, 'color');
|
return array_column($result, 'color');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Получить доступные материалы
|
|
||||||
*/
|
|
||||||
public function getAvailableMaterials(): array
|
public function getAvailableMaterials(): array
|
||||||
{
|
{
|
||||||
$sql = "SELECT DISTINCT material FROM {$this->table}
|
$sql = "SELECT DISTINCT material FROM {$this->table}
|
||||||
@@ -135,9 +109,6 @@ class Product extends Model
|
|||||||
return array_column($result, 'material');
|
return array_column($result, 'material');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Создать товар
|
|
||||||
*/
|
|
||||||
public function createProduct(array $data): ?int
|
public function createProduct(array $data): ?int
|
||||||
{
|
{
|
||||||
$slug = $this->generateSlug($data['name']);
|
$slug = $this->generateSlug($data['name']);
|
||||||
@@ -161,9 +132,6 @@ class Product extends Model
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Обновить товар
|
|
||||||
*/
|
|
||||||
public function updateProduct(int $id, array $data): bool
|
public function updateProduct(int $id, array $data): bool
|
||||||
{
|
{
|
||||||
$updateData = [
|
$updateData = [
|
||||||
@@ -183,9 +151,6 @@ class Product extends Model
|
|||||||
return $this->update($id, $updateData);
|
return $this->update($id, $updateData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Уменьшить количество товара на складе
|
|
||||||
*/
|
|
||||||
public function decreaseStock(int $productId, int $quantity): bool
|
public function decreaseStock(int $productId, int $quantity): bool
|
||||||
{
|
{
|
||||||
$sql = "UPDATE {$this->table}
|
$sql = "UPDATE {$this->table}
|
||||||
@@ -195,18 +160,12 @@ class Product extends Model
|
|||||||
return $this->execute($sql, [$quantity, $productId]);
|
return $this->execute($sql, [$quantity, $productId]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Проверить наличие товара
|
|
||||||
*/
|
|
||||||
public function checkStock(int $productId, int $quantity): bool
|
public function checkStock(int $productId, int $quantity): bool
|
||||||
{
|
{
|
||||||
$product = $this->find($productId);
|
$product = $this->find($productId);
|
||||||
return $product && $product['is_available'] && $product['stock_quantity'] >= $quantity;
|
return $product && $product['is_available'] && $product['stock_quantity'] >= $quantity;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Генерация slug
|
|
||||||
*/
|
|
||||||
private function generateSlug(string $name): string
|
private function generateSlug(string $name): string
|
||||||
{
|
{
|
||||||
$slug = mb_strtolower($name);
|
$slug = mb_strtolower($name);
|
||||||
@@ -227,13 +186,9 @@ class Product extends Model
|
|||||||
return trim($slug, '-');
|
return trim($slug, '-');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Генерация артикула
|
|
||||||
*/
|
|
||||||
private function generateSku(string $name): string
|
private function generateSku(string $name): string
|
||||||
{
|
{
|
||||||
$prefix = strtoupper(substr(preg_replace('/[^a-zA-Z0-9]/', '', $name), 0, 6));
|
$prefix = strtoupper(substr(preg_replace('/[^a-zA-Z0-9]/', '', $name), 0, 6));
|
||||||
return 'PROD-' . $prefix . '-' . rand(100, 999);
|
return 'PROD-' . $prefix . '-' . rand(100, 999);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,46 +4,30 @@ namespace App\Models;
|
|||||||
|
|
||||||
use App\Core\Model;
|
use App\Core\Model;
|
||||||
|
|
||||||
/**
|
|
||||||
* User - модель пользователя
|
|
||||||
*/
|
|
||||||
class User extends Model
|
class User extends Model
|
||||||
{
|
{
|
||||||
protected string $table = 'users';
|
protected string $table = 'users';
|
||||||
protected string $primaryKey = 'user_id';
|
protected string $primaryKey = 'user_id';
|
||||||
|
|
||||||
/**
|
|
||||||
* Найти пользователя по email
|
|
||||||
*/
|
|
||||||
public function findByEmail(string $email): ?array
|
public function findByEmail(string $email): ?array
|
||||||
{
|
{
|
||||||
return $this->findWhere(['email' => $email]);
|
return $this->findWhere(['email' => $email]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Проверить пароль пользователя
|
|
||||||
*/
|
|
||||||
public function verifyPassword(string $password, string $hash): bool
|
public function verifyPassword(string $password, string $hash): bool
|
||||||
{
|
{
|
||||||
return password_verify($password, $hash);
|
return password_verify($password, $hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Хешировать пароль
|
|
||||||
*/
|
|
||||||
public function hashPassword(string $password): string
|
public function hashPassword(string $password): string
|
||||||
{
|
{
|
||||||
return password_hash($password, PASSWORD_DEFAULT);
|
return password_hash($password, PASSWORD_DEFAULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Создать нового пользователя
|
|
||||||
*/
|
|
||||||
public function register(array $data): ?int
|
public function register(array $data): ?int
|
||||||
{
|
{
|
||||||
$config = require dirname(__DIR__, 2) . '/config/app.php';
|
$config = require dirname(__DIR__, 2) . '/config/app.php';
|
||||||
|
|
||||||
// Проверяем, является ли email администраторским
|
|
||||||
$isAdmin = in_array(strtolower($data['email']), $config['admin_emails'] ?? []);
|
$isAdmin = in_array(strtolower($data['email']), $config['admin_emails'] ?? []);
|
||||||
|
|
||||||
return $this->create([
|
return $this->create([
|
||||||
@@ -52,14 +36,11 @@ class User extends Model
|
|||||||
'full_name' => $data['full_name'],
|
'full_name' => $data['full_name'],
|
||||||
'phone' => $data['phone'] ?? null,
|
'phone' => $data['phone'] ?? null,
|
||||||
'city' => $data['city'] ?? null,
|
'city' => $data['city'] ?? null,
|
||||||
'is_admin' => $isAdmin,
|
'is_admin' => $isAdmin ? 'true' : 'false',
|
||||||
'is_active' => true
|
'is_active' => 'true'
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Авторизация пользователя
|
|
||||||
*/
|
|
||||||
public function authenticate(string $email, string $password): ?array
|
public function authenticate(string $email, string $password): ?array
|
||||||
{
|
{
|
||||||
$user = $this->findByEmail($email);
|
$user = $this->findByEmail($email);
|
||||||
@@ -76,7 +57,6 @@ class User extends Model
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Обновляем время последнего входа
|
|
||||||
$this->update($user['user_id'], [
|
$this->update($user['user_id'], [
|
||||||
'last_login' => date('Y-m-d H:i:s')
|
'last_login' => date('Y-m-d H:i:s')
|
||||||
]);
|
]);
|
||||||
@@ -84,9 +64,6 @@ class User extends Model
|
|||||||
return $user;
|
return $user;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Получить активных пользователей
|
|
||||||
*/
|
|
||||||
public function getActive(int $limit = 50): array
|
public function getActive(int $limit = 50): array
|
||||||
{
|
{
|
||||||
$sql = "SELECT * FROM {$this->table}
|
$sql = "SELECT * FROM {$this->table}
|
||||||
@@ -96,9 +73,6 @@ class User extends Model
|
|||||||
return $this->query($sql, [$limit]);
|
return $this->query($sql, [$limit]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Получить всех пользователей с пагинацией
|
|
||||||
*/
|
|
||||||
public function getAllPaginated(int $limit = 50, int $offset = 0): array
|
public function getAllPaginated(int $limit = 50, int $offset = 0): array
|
||||||
{
|
{
|
||||||
$sql = "SELECT * FROM {$this->table}
|
$sql = "SELECT * FROM {$this->table}
|
||||||
@@ -107,18 +81,12 @@ class User extends Model
|
|||||||
return $this->query($sql, [$limit, $offset]);
|
return $this->query($sql, [$limit, $offset]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Проверить существование email
|
|
||||||
*/
|
|
||||||
public function emailExists(string $email): bool
|
public function emailExists(string $email): bool
|
||||||
{
|
{
|
||||||
$user = $this->findByEmail($email);
|
$user = $this->findByEmail($email);
|
||||||
return $user !== null;
|
return $user !== null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Обновить профиль пользователя
|
|
||||||
*/
|
|
||||||
public function updateProfile(int $userId, array $data): bool
|
public function updateProfile(int $userId, array $data): bool
|
||||||
{
|
{
|
||||||
$allowedFields = ['full_name', 'phone', 'city'];
|
$allowedFields = ['full_name', 'phone', 'city'];
|
||||||
@@ -128,9 +96,6 @@ class User extends Model
|
|||||||
return $this->update($userId, $updateData);
|
return $this->update($userId, $updateData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Изменить пароль
|
|
||||||
*/
|
|
||||||
public function changePassword(int $userId, string $newPassword): bool
|
public function changePassword(int $userId, string $newPassword): bool
|
||||||
{
|
{
|
||||||
return $this->update($userId, [
|
return $this->update($userId, [
|
||||||
@@ -139,9 +104,6 @@ class User extends Model
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Заблокировать/разблокировать пользователя
|
|
||||||
*/
|
|
||||||
public function setActive(int $userId, bool $active): bool
|
public function setActive(int $userId, bool $active): bool
|
||||||
{
|
{
|
||||||
return $this->update($userId, [
|
return $this->update($userId, [
|
||||||
@@ -150,4 +112,3 @@ class User extends Model
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,12 +2,12 @@
|
|||||||
|
|
||||||
<h2><?= $isEdit ? 'Редактирование категории' : 'Добавление категории' ?></h2>
|
<h2><?= $isEdit ? 'Редактирование категории' : 'Добавление категории' ?></h2>
|
||||||
|
|
||||||
<a href="/cite_practica/admin/categories" class="btn btn-primary" style="margin-bottom: 20px;">
|
<a href="/admin/categories" class="btn btn-primary" style="margin-bottom: 20px;">
|
||||||
<i class="fas fa-arrow-left"></i> Назад к списку
|
<i class="fas fa-arrow-left"></i> Назад к списку
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<div class="form-container">
|
<div class="form-container">
|
||||||
<form action="/cite_practica/admin/categories/<?= $isEdit ? 'edit/' . $category['category_id'] : 'add' ?>" method="POST">
|
<form action="/admin/categories/<?= $isEdit ? 'edit/' . $category['category_id'] : 'add' ?>" method="POST">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Название категории *</label>
|
<label>Название категории *</label>
|
||||||
<input type="text" name="name" class="form-control"
|
<input type="text" name="name" class="form-control"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">
|
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">
|
||||||
<h2>Управление категориями</h2>
|
<h2>Управление категориями</h2>
|
||||||
<a href="/cite_practica/admin/categories/add" class="btn btn-success">
|
<a href="/admin/categories/add" class="btn btn-success">
|
||||||
<i class="fas fa-plus"></i> Добавить категорию
|
<i class="fas fa-plus"></i> Добавить категорию
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@@ -42,10 +42,10 @@
|
|||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="action-buttons">
|
<div class="action-buttons">
|
||||||
<a href="/cite_practica/admin/categories/edit/<?= $category['category_id'] ?>" class="btn btn-sm btn-warning" title="Редактировать">
|
<a href="/admin/categories/edit/<?= $category['category_id'] ?>" class="btn btn-sm btn-warning" title="Редактировать">
|
||||||
<i class="fas fa-edit"></i>
|
<i class="fas fa-edit"></i>
|
||||||
</a>
|
</a>
|
||||||
<form action="/cite_practica/admin/categories/delete/<?= $category['category_id'] ?>" method="POST" style="display: inline;"
|
<form action="/admin/categories/delete/<?= $category['category_id'] ?>" method="POST" style="display: inline;"
|
||||||
onsubmit="return confirm('Удалить категорию?');">
|
onsubmit="return confirm('Удалить категорию?');">
|
||||||
<button type="submit" class="btn btn-sm btn-danger" title="Удалить">
|
<button type="submit" class="btn btn-sm btn-danger" title="Удалить">
|
||||||
<i class="fas fa-trash"></i>
|
<i class="fas fa-trash"></i>
|
||||||
|
|||||||
@@ -28,16 +28,16 @@
|
|||||||
<div style="margin-top: 30px;">
|
<div style="margin-top: 30px;">
|
||||||
<h3>Быстрые действия</h3>
|
<h3>Быстрые действия</h3>
|
||||||
<div style="display: flex; gap: 15px; margin-top: 15px; flex-wrap: wrap;">
|
<div style="display: flex; gap: 15px; margin-top: 15px; flex-wrap: wrap;">
|
||||||
<a href="/cite_practica/admin/products/add" class="btn btn-success">
|
<a href="/admin/products/add" class="btn btn-success">
|
||||||
<i class="fas fa-plus"></i> Добавить товар
|
<i class="fas fa-plus"></i> Добавить товар
|
||||||
</a>
|
</a>
|
||||||
<a href="/cite_practica/admin/categories/add" class="btn btn-primary">
|
<a href="/admin/categories/add" class="btn btn-primary">
|
||||||
<i class="fas fa-plus"></i> Добавить категорию
|
<i class="fas fa-plus"></i> Добавить категорию
|
||||||
</a>
|
</a>
|
||||||
<a href="/cite_practica/admin/orders" class="btn btn-primary">
|
<a href="/admin/orders" class="btn btn-primary">
|
||||||
<i class="fas fa-shopping-cart"></i> Просмотреть заказы
|
<i class="fas fa-shopping-cart"></i> Просмотреть заказы
|
||||||
</a>
|
</a>
|
||||||
<a href="/cite_practica/catalog" class="btn btn-primary">
|
<a href="/catalog" class="btn btn-primary">
|
||||||
<i class="fas fa-store"></i> Перейти в каталог
|
<i class="fas fa-store"></i> Перейти в каталог
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php use App\Core\View; ?>
|
<?php use App\Core\View; ?>
|
||||||
|
|
||||||
<a href="/cite_practica/admin/orders" class="btn btn-primary" style="margin-bottom: 20px;">
|
<a href="/admin/orders" class="btn btn-primary" style="margin-bottom: 20px;">
|
||||||
<i class="fas fa-arrow-left"></i> Назад к заказам
|
<i class="fas fa-arrow-left"></i> Назад к заказам
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
<div class="form-container">
|
<div class="form-container">
|
||||||
<h3>Статус заказа</h3>
|
<h3>Статус заказа</h3>
|
||||||
<form action="/cite_practica/admin/orders/<?= $order['order_id'] ?>/status" method="POST">
|
<form action="/admin/orders/<?= $order['order_id'] ?>/status" method="POST">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<select name="status" class="form-control">
|
<select name="status" class="form-control">
|
||||||
<option value="pending" <?= $order['status'] === 'pending' ? 'selected' : '' ?>>Ожидает</option>
|
<option value="pending" <?= $order['status'] === 'pending' ? 'selected' : '' ?>>Ожидает</option>
|
||||||
@@ -60,7 +60,7 @@
|
|||||||
<?php foreach ($order['items'] as $item): ?>
|
<?php foreach ($order['items'] as $item): ?>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<img src="/cite_practica/<?= htmlspecialchars($item['image_url'] ?? 'img/1.jpg') ?>"
|
<img src="/<?= htmlspecialchars($item['image_url'] ?? 'img/1.jpg') ?>"
|
||||||
style="width: 50px; height: 50px; object-fit: cover; border-radius: 4px;">
|
style="width: 50px; height: 50px; object-fit: cover; border-radius: 4px;">
|
||||||
</td>
|
</td>
|
||||||
<td><?= htmlspecialchars($item['product_name']) ?></td>
|
<td><?= htmlspecialchars($item['product_name']) ?></td>
|
||||||
|
|||||||
@@ -50,7 +50,7 @@
|
|||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<a href="/cite_practica/admin/orders/<?= $order['order_id'] ?>" class="btn btn-sm btn-primary">
|
<a href="/admin/orders/<?= $order['order_id'] ?>" class="btn btn-sm btn-primary">
|
||||||
<i class="fas fa-eye"></i> Подробнее
|
<i class="fas fa-eye"></i> Подробнее
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
|
|||||||
@@ -2,12 +2,12 @@
|
|||||||
|
|
||||||
<h2><?= $isEdit ? 'Редактирование товара' : 'Добавление товара' ?></h2>
|
<h2><?= $isEdit ? 'Редактирование товара' : 'Добавление товара' ?></h2>
|
||||||
|
|
||||||
<a href="/cite_practica/admin/products" class="btn btn-primary" style="margin-bottom: 20px;">
|
<a href="/admin/products" class="btn btn-primary" style="margin-bottom: 20px;">
|
||||||
<i class="fas fa-arrow-left"></i> Назад к списку
|
<i class="fas fa-arrow-left"></i> Назад к списку
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<div class="form-container">
|
<div class="form-container">
|
||||||
<form action="/cite_practica/admin/products/<?= $isEdit ? 'edit/' . $product['product_id'] : 'add' ?>" method="POST">
|
<form action="/admin/products/<?= $isEdit ? 'edit/' . $product['product_id'] : 'add' ?>" method="POST">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Название товара *</label>
|
<label>Название товара *</label>
|
||||||
<input type="text" name="name" class="form-control"
|
<input type="text" name="name" class="form-control"
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">
|
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">
|
||||||
<h2>Управление товарами</h2>
|
<h2>Управление товарами</h2>
|
||||||
<a href="/cite_practica/admin/products/add" class="btn btn-success">
|
<a href="/admin/products/add" class="btn btn-success">
|
||||||
<i class="fas fa-plus"></i> Добавить товар
|
<i class="fas fa-plus"></i> Добавить товар
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@@ -16,8 +16,8 @@
|
|||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<div style="margin-bottom: 15px;">
|
<div style="margin-bottom: 15px;">
|
||||||
<a href="/cite_practica/admin/products" class="btn btn-sm <?= !$showAll ? 'btn-primary' : '' ?>">Активные</a>
|
<a href="/admin/products" class="btn btn-sm <?= !$showAll ? 'btn-primary' : '' ?>">Активные</a>
|
||||||
<a href="/cite_practica/admin/products?show_all=1" class="btn btn-sm <?= $showAll ? 'btn-primary' : '' ?>">Все товары</a>
|
<a href="/admin/products?show_all=1" class="btn btn-sm <?= $showAll ? 'btn-primary' : '' ?>">Все товары</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<table>
|
<table>
|
||||||
@@ -38,7 +38,7 @@
|
|||||||
<tr style="<?= !$product['is_available'] ? 'opacity: 0.5;' : '' ?>">
|
<tr style="<?= !$product['is_available'] ? 'opacity: 0.5;' : '' ?>">
|
||||||
<td><?= $product['product_id'] ?></td>
|
<td><?= $product['product_id'] ?></td>
|
||||||
<td>
|
<td>
|
||||||
<img src="/cite_practica/<?= htmlspecialchars($product['image_url'] ?? 'img/1.jpg') ?>"
|
<img src="/<?= htmlspecialchars($product['image_url'] ?? 'img/1.jpg') ?>"
|
||||||
style="width: 50px; height: 50px; object-fit: cover; border-radius: 4px;">
|
style="width: 50px; height: 50px; object-fit: cover; border-radius: 4px;">
|
||||||
</td>
|
</td>
|
||||||
<td><?= htmlspecialchars($product['name']) ?></td>
|
<td><?= htmlspecialchars($product['name']) ?></td>
|
||||||
@@ -54,13 +54,13 @@
|
|||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="action-buttons">
|
<div class="action-buttons">
|
||||||
<a href="/cite_practica/product/<?= $product['product_id'] ?>" class="btn btn-sm btn-primary" title="Просмотр">
|
<a href="/product/<?= $product['product_id'] ?>" class="btn btn-sm btn-primary" title="Просмотр">
|
||||||
<i class="fas fa-eye"></i>
|
<i class="fas fa-eye"></i>
|
||||||
</a>
|
</a>
|
||||||
<a href="/cite_practica/admin/products/edit/<?= $product['product_id'] ?>" class="btn btn-sm btn-warning" title="Редактировать">
|
<a href="/admin/products/edit/<?= $product['product_id'] ?>" class="btn btn-sm btn-warning" title="Редактировать">
|
||||||
<i class="fas fa-edit"></i>
|
<i class="fas fa-edit"></i>
|
||||||
</a>
|
</a>
|
||||||
<form action="/cite_practica/admin/products/delete/<?= $product['product_id'] ?>" method="POST" style="display: inline;"
|
<form action="/admin/products/delete/<?= $product['product_id'] ?>" method="POST" style="display: inline;"
|
||||||
onsubmit="return confirm('Скрыть товар?');">
|
onsubmit="return confirm('Скрыть товар?');">
|
||||||
<button type="submit" class="btn btn-sm btn-danger" title="Скрыть">
|
<button type="submit" class="btn btn-sm btn-danger" title="Скрыть">
|
||||||
<i class="fas fa-eye-slash"></i>
|
<i class="fas fa-eye-slash"></i>
|
||||||
|
|||||||
@@ -47,7 +47,7 @@
|
|||||||
|
|
||||||
<div class="auth-actions">
|
<div class="auth-actions">
|
||||||
<span class="auth-text">Нет аккаунта?</span>
|
<span class="auth-text">Нет аккаунта?</span>
|
||||||
<a href="/cite_practica/register" class="login-btn">Зарегистрироваться</a>
|
<a href="/register" class="login-btn">Зарегистрироваться</a>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@@ -70,13 +70,13 @@ $(document).ready(function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: '/cite_practica/login',
|
url: '/login',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: { email: email, password: password, redirect: redirect },
|
data: { email: email, password: password, redirect: redirect },
|
||||||
dataType: 'json',
|
dataType: 'json',
|
||||||
success: function(result) {
|
success: function(result) {
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
window.location.href = result.redirect || '/cite_practica/catalog';
|
window.location.href = result.redirect || '/catalog';
|
||||||
} else {
|
} else {
|
||||||
alert(result.message || 'Ошибка авторизации');
|
alert(result.message || 'Ошибка авторизации');
|
||||||
}
|
}
|
||||||
@@ -88,4 +88,3 @@ $(document).ready(function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -18,10 +18,6 @@
|
|||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<div style="background: #e8f4fd; padding: 15px; border-radius: 5px; margin: 20px auto; max-width: 800px; text-align: center; font-size: 14px; color: #0c5460;">
|
|
||||||
<i class="fas fa-info-circle"></i> Для доступа к каталогу и оформления заказов необходимо зарегистрироваться
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="profile-container">
|
<div class="profile-container">
|
||||||
<div class="profile-left-col">
|
<div class="profile-left-col">
|
||||||
<div class="logo" style="color: white;">AETERNA</div>
|
<div class="logo" style="color: white;">AETERNA</div>
|
||||||
@@ -39,8 +35,11 @@
|
|||||||
|
|
||||||
<div class="profile-right-col">
|
<div class="profile-right-col">
|
||||||
<div class="profile-form-block">
|
<div class="profile-form-block">
|
||||||
|
<div style="margin-bottom: 20px; padding: 12px 15px; background: #e8f4fd; border-radius: 5px; font-size: 13px; color: #0c5460; text-align: center;">
|
||||||
|
<i class="fas fa-info-circle"></i> Для доступа к каталогу и оформления заказов необходимо зарегистрироваться
|
||||||
|
</div>
|
||||||
<h2>РЕГИСТРАЦИЯ</h2>
|
<h2>РЕГИСТРАЦИЯ</h2>
|
||||||
<form class="profile-form" action="/cite_practica/register" method="POST" id="registrationForm">
|
<form class="profile-form" action="/register" method="POST" id="registrationForm">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<label for="fio">ФИО *</label>
|
<label for="fio">ФИО *</label>
|
||||||
<input type="text" id="fio" name="fio" placeholder="Введите ваше ФИО"
|
<input type="text" id="fio" name="fio" placeholder="Введите ваше ФИО"
|
||||||
@@ -82,7 +81,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<a href="/cite_practica/login" style="display: block; margin: 15px 0; text-align: center; color: #453227;">
|
<a href="/login" style="display: block; margin: 15px 0; text-align: center; color: #453227;">
|
||||||
Уже есть аккаунт? Войти
|
Уже есть аккаунт? Войти
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
@@ -92,4 +91,3 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ use App\Core\View;
|
|||||||
|
|
||||||
<main class="container">
|
<main class="container">
|
||||||
<div class="breadcrumbs">
|
<div class="breadcrumbs">
|
||||||
<a href="/cite_practica/">Главная</a> • <span class="current-page">Корзина</span>
|
<a href="/">Главная</a> • <span class="current-page">Корзина</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h2 style="color: #453227; margin: 20px 0;">Товары в корзине</h2>
|
<h2 style="color: #453227; margin: 20px 0;">Товары в корзине</h2>
|
||||||
@@ -49,7 +49,7 @@ use App\Core\View;
|
|||||||
<div class="empty-cart">
|
<div class="empty-cart">
|
||||||
<i class="fas fa-shopping-cart" style="font-size: 48px; color: #ccc; margin-bottom: 20px;"></i>
|
<i class="fas fa-shopping-cart" style="font-size: 48px; color: #ccc; margin-bottom: 20px;"></i>
|
||||||
<p>Ваша корзина пуста</p>
|
<p>Ваша корзина пуста</p>
|
||||||
<a href="/cite_practica/catalog" class="btn primary-btn" style="margin-top: 20px; display: inline-block; background: #453227; color: white; padding: 12px 30px; border-radius: 4px; text-decoration: none;">
|
<a href="/catalog" class="btn primary-btn" style="margin-top: 20px; display: inline-block; background: #453227; color: white; padding: 12px 30px; border-radius: 4px; text-decoration: none;">
|
||||||
Продолжить покупки
|
Продолжить покупки
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@@ -60,7 +60,17 @@ use App\Core\View;
|
|||||||
<?php foreach ($cartItems as $item): ?>
|
<?php foreach ($cartItems as $item): ?>
|
||||||
<div class="products__item" data-product-id="<?= $item['product_id'] ?>" data-price="<?= $item['price'] ?>">
|
<div class="products__item" data-product-id="<?= $item['product_id'] ?>" data-price="<?= $item['price'] ?>">
|
||||||
<div class="products__image">
|
<div class="products__image">
|
||||||
<img src="/cite_practica/<?= htmlspecialchars($item['image_url'] ?? 'img/1.jpg') ?>"
|
<?php
|
||||||
|
$cartImageUrl = $item['image_url'] ?? '';
|
||||||
|
if (empty($cartImageUrl)) {
|
||||||
|
$cartImageUrl = '/assets/images/1.jpg';
|
||||||
|
} elseif (strpos($cartImageUrl, '/img2/') === 0) {
|
||||||
|
$cartImageUrl = str_replace('/img2/', '/assets/images/', $cartImageUrl);
|
||||||
|
} elseif (strpos($cartImageUrl, 'img2/') === 0) {
|
||||||
|
$cartImageUrl = str_replace('img2/', '/assets/images/', $cartImageUrl);
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<img src="<?= htmlspecialchars($cartImageUrl) ?>"
|
||||||
alt="<?= htmlspecialchars($item['name']) ?>">
|
alt="<?= htmlspecialchars($item['name']) ?>">
|
||||||
</div>
|
</div>
|
||||||
<div class="products__details">
|
<div class="products__details">
|
||||||
@@ -170,13 +180,12 @@ use App\Core\View;
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
// Обновление количества
|
|
||||||
$('.products__qty-btn').on('click', function(e) {
|
$('.products__qty-btn').on('click', function(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const productId = $(this).data('id');
|
var productId = $(this).data('id');
|
||||||
const isPlus = $(this).hasClass('plus');
|
var isPlus = $(this).hasClass('plus');
|
||||||
const $qtyValue = $(this).siblings('.products__qty-value');
|
var $qtyValue = $(this).siblings('.products__qty-value');
|
||||||
let quantity = parseInt($qtyValue.text());
|
var quantity = parseInt($qtyValue.text());
|
||||||
|
|
||||||
if (isPlus) { quantity++; }
|
if (isPlus) { quantity++; }
|
||||||
else if (quantity > 1) { quantity--; }
|
else if (quantity > 1) { quantity--; }
|
||||||
@@ -185,7 +194,7 @@ $(document).ready(function() {
|
|||||||
$qtyValue.text(quantity);
|
$qtyValue.text(quantity);
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: '/cite_practica/cart/update',
|
url: '/cart/update',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: { product_id: productId, quantity: quantity },
|
data: { product_id: productId, quantity: quantity },
|
||||||
dataType: 'json',
|
dataType: 'json',
|
||||||
@@ -198,16 +207,15 @@ $(document).ready(function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Удаление товара
|
|
||||||
$('.remove-from-cart').on('click', function(e) {
|
$('.remove-from-cart').on('click', function(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const productId = $(this).data('id');
|
var productId = $(this).data('id');
|
||||||
const $item = $(this).closest('.products__item');
|
var $item = $(this).closest('.products__item');
|
||||||
|
|
||||||
if (!confirm('Удалить товар из корзины?')) return;
|
if (!confirm('Удалить товар из корзины?')) return;
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: '/cite_practica/cart/remove',
|
url: '/cart/remove',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: { product_id: productId },
|
data: { product_id: productId },
|
||||||
dataType: 'json',
|
dataType: 'json',
|
||||||
@@ -227,31 +235,30 @@ $(document).ready(function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
function updateTotals() {
|
function updateTotals() {
|
||||||
let productsTotal = 0;
|
var productsTotal = 0;
|
||||||
let totalCount = 0;
|
var totalCount = 0;
|
||||||
|
|
||||||
$('.products__item').each(function() {
|
$('.products__item').each(function() {
|
||||||
const price = parseInt($(this).data('price'));
|
var price = parseInt($(this).data('price'));
|
||||||
const quantity = parseInt($(this).find('.products__qty-value').text());
|
var quantity = parseInt($(this).find('.products__qty-value').text());
|
||||||
productsTotal += price * quantity;
|
productsTotal += price * quantity;
|
||||||
totalCount += quantity;
|
totalCount += quantity;
|
||||||
});
|
});
|
||||||
|
|
||||||
const delivery = parseFloat($('input[name="delivery_price"]').val());
|
var delivery = parseFloat($('input[name="delivery_price"]').val());
|
||||||
const discount = parseFloat($('input[name="discount"]').val());
|
var discount = parseFloat($('input[name="discount"]').val());
|
||||||
const finalTotal = productsTotal + delivery - discount;
|
var finalTotal = productsTotal + delivery - discount;
|
||||||
|
|
||||||
$('.products-total').text(productsTotal.toLocaleString('ru-RU') + ' ₽');
|
$('.products-total').text(productsTotal.toLocaleString('ru-RU') + ' ₽');
|
||||||
$('.summary-count').text(totalCount);
|
$('.summary-count').text(totalCount);
|
||||||
$('.final-total').text(finalTotal.toLocaleString('ru-RU') + ' ₽');
|
$('.final-total').text(finalTotal.toLocaleString('ru-RU') + ' ₽');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Промокод
|
|
||||||
$('#applyPromo').click(function() {
|
$('#applyPromo').click(function() {
|
||||||
const promoCode = $('#promo_code').val().toUpperCase();
|
var promoCode = $('#promo_code').val().toUpperCase();
|
||||||
if (promoCode === 'SALE10') {
|
if (promoCode === 'SALE10') {
|
||||||
const productsTotal = parseFloat($('.products-total').text().replace(/[^0-9]/g, ''));
|
var productsTotal = parseFloat($('.products-total').text().replace(/[^0-9]/g, ''));
|
||||||
const discount = Math.round(productsTotal * 0.1);
|
var discount = Math.round(productsTotal * 0.1);
|
||||||
$('input[name="discount"]').val(discount);
|
$('input[name="discount"]').val(discount);
|
||||||
$('.discount-total').text(discount.toLocaleString('ru-RU') + ' ₽');
|
$('.discount-total').text(discount.toLocaleString('ru-RU') + ' ₽');
|
||||||
showNotification('Промокод применен! Скидка 10%');
|
showNotification('Промокод применен! Скидка 10%');
|
||||||
@@ -266,7 +273,6 @@ $(document).ready(function() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Оформление заказа
|
|
||||||
$('#orderForm').submit(function(e) {
|
$('#orderForm').submit(function(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
@@ -278,7 +284,7 @@ $(document).ready(function() {
|
|||||||
$('#submit-order').prop('disabled', true).text('ОБРАБОТКА...');
|
$('#submit-order').prop('disabled', true).text('ОБРАБОТКА...');
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: '/cite_practica/order',
|
url: '/order',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: $(this).serialize(),
|
data: $(this).serialize(),
|
||||||
dataType: 'json',
|
dataType: 'json',
|
||||||
@@ -286,7 +292,7 @@ $(document).ready(function() {
|
|||||||
if (result.success) {
|
if (result.success) {
|
||||||
showNotification('Заказ успешно оформлен!');
|
showNotification('Заказ успешно оформлен!');
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
window.location.href = '/cite_practica/';
|
window.location.href = '/';
|
||||||
}, 1500);
|
}, 1500);
|
||||||
} else {
|
} else {
|
||||||
showNotification('Ошибка: ' + result.message, 'error');
|
showNotification('Ошибка: ' + result.message, 'error');
|
||||||
@@ -301,4 +307,3 @@ $(document).ready(function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -7,10 +7,10 @@
|
|||||||
К сожалению, запрошенная страница не существует или была перемещена.
|
К сожалению, запрошенная страница не существует или была перемещена.
|
||||||
</p>
|
</p>
|
||||||
<div style="margin-top: 30px;">
|
<div style="margin-top: 30px;">
|
||||||
<a href="/cite_practica/" class="btn primary-btn" style="display: inline-block; background: #453227; color: white; padding: 12px 30px; border-radius: 4px; text-decoration: none;">
|
<a href="/" class="btn primary-btn" style="display: inline-block; background: #453227; color: white; padding: 12px 30px; border-radius: 4px; text-decoration: none;">
|
||||||
<i class="fas fa-home"></i> На главную
|
<i class="fas fa-home"></i> На главную
|
||||||
</a>
|
</a>
|
||||||
<a href="/cite_practica/catalog" class="btn" style="display: inline-block; background: #617365; color: white; padding: 12px 30px; border-radius: 4px; text-decoration: none; margin-left: 10px;">
|
<a href="/catalog" class="btn" style="display: inline-block; background: #617365; color: white; padding: 12px 30px; border-radius: 4px; text-decoration: none; margin-left: 10px;">
|
||||||
<i class="fas fa-store"></i> В каталог
|
<i class="fas fa-store"></i> В каталог
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
Произошла ошибка при обработке вашего запроса. Мы уже работаем над её устранением.
|
Произошла ошибка при обработке вашего запроса. Мы уже работаем над её устранением.
|
||||||
</p>
|
</p>
|
||||||
<div style="margin-top: 30px;">
|
<div style="margin-top: 30px;">
|
||||||
<a href="/cite_practica/" class="btn primary-btn" style="display: inline-block; background: #453227; color: white; padding: 12px 30px; border-radius: 4px; text-decoration: none;">
|
<a href="/" class="btn primary-btn" style="display: inline-block; background: #453227; color: white; padding: 12px 30px; border-radius: 4px; text-decoration: none;">
|
||||||
<i class="fas fa-home"></i> На главную
|
<i class="fas fa-home"></i> На главную
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -4,16 +4,16 @@
|
|||||||
<div class="container hero__content">
|
<div class="container hero__content">
|
||||||
<div class="hero__image-block">
|
<div class="hero__image-block">
|
||||||
<div class="hero__circle"></div>
|
<div class="hero__circle"></div>
|
||||||
<img src="/img/chair.PNG" alt="Кресло и торшер" class="hero__img">
|
<img src="/assets/images/chair.PNG" alt="Кресло и торшер" class="hero__img">
|
||||||
</div>
|
</div>
|
||||||
<div class="hero__text-block">
|
<div class="hero__text-block">
|
||||||
<h1>ДОБАВЬТЕ ИЗЫСКАННОСТИ В СВОЙ ИНТЕРЬЕР</h1>
|
<h1>ДОБАВЬТЕ ИЗЫСКАННОСТИ В СВОЙ ИНТЕРЬЕР</h1>
|
||||||
<p class="hero__usp-text">Мы создаем мебель, которая сочетает в себе безупречный дизайн, натуральные материалы, продуманный функционал, чтобы ваш день начинался и заканчивался с комфортом.</p>
|
<p class="hero__usp-text">Мы создаем мебель, которая сочетает в себе безупречный дизайн, натуральные материалы, продуманный функционал, чтобы ваш день начинался и заканчивался с комфортом.</p>
|
||||||
|
|
||||||
<?php if ($isLoggedIn): ?>
|
<?php if ($isLoggedIn): ?>
|
||||||
<a href="/cite_practica/catalog" class="btn primary-btn">ПЕРЕЙТИ В КАТАЛОГ</a>
|
<a href="/catalog" class="btn primary-btn">ПЕРЕЙТИ В КАТАЛОГ</a>
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<a href="/cite_practica/login" class="btn primary-btn">ПЕРЕЙТИ В КАТАЛОГ</a>
|
<a href="/login" class="btn primary-btn">ПЕРЕЙТИ В КАТАЛОГ</a>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -44,17 +44,17 @@
|
|||||||
|
|
||||||
<div class="promo-images">
|
<div class="promo-images">
|
||||||
<div class="promo-image-col">
|
<div class="promo-image-col">
|
||||||
<img src="/img/спальня.jpg" alt="Кровать и тумба">
|
<img src="/assets/images/спальня.jpg" alt="Кровать и тумба">
|
||||||
<div class="image-overlay-text">
|
<div class="image-overlay-text">
|
||||||
<h4>НОВИНКИ В КАТЕГОРИЯХ <br>МЯГКАЯ МЕБЕЛЬ</h4>
|
<h4>НОВИНКИ В КАТЕГОРИЯХ <br>МЯГКАЯ МЕБЕЛЬ</h4>
|
||||||
<a href="/cite_practica/catalog" class="overlay-link">ПЕРЕЙТИ</a>
|
<a href="/catalog" class="overlay-link">ПЕРЕЙТИ</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="promo-image-col">
|
<div class="promo-image-col">
|
||||||
<img src="/img/диван.jpg" alt="Диван в гостиной">
|
<img src="/assets/images/диван.jpg" alt="Диван в гостиной">
|
||||||
<div class="image-overlay-text">
|
<div class="image-overlay-text">
|
||||||
<h4>РАСПРОДАЖА <br>ПРЕДМЕТЫ ДЕКОРА</h4>
|
<h4>РАСПРОДАЖА <br>ПРЕДМЕТЫ ДЕКОРА</h4>
|
||||||
<a href="/cite_practica/catalog" class="overlay-link">ПЕРЕЙТИ</a>
|
<a href="/catalog" class="overlay-link">ПЕРЕЙТИ</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -68,11 +68,11 @@
|
|||||||
<h2>О НАС</h2>
|
<h2>О НАС</h2>
|
||||||
<p class="text-justified">Компания AETERNA - российский производитель качественной корпусной и мягкой мебели для дома и офиса. С 2015 года мы успешно реализуем проекты любой сложности, сочетая современные технологии, проверенные материалы и классическое мастерство.</p>
|
<p class="text-justified">Компания AETERNA - российский производитель качественной корпусной и мягкой мебели для дома и офиса. С 2015 года мы успешно реализуем проекты любой сложности, сочетая современные технологии, проверенные материалы и классическое мастерство.</p>
|
||||||
</div>
|
</div>
|
||||||
<img src="/img/кресло_1.jpg" alt="Фиолетовое кресло" class="about__img about__img--small">
|
<img src="/assets/images/кресло_1.jpg" alt="Фиолетовое кресло" class="about__img about__img--small">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="about__column about__column--right">
|
<div class="about__column about__column--right">
|
||||||
<img src="/img/диван_1.jpg" alt="Белый диван с подушками" class="about__img about__img--large">
|
<img src="/assets/images/диван_1.jpg" alt="Белый диван с подушками" class="about__img about__img--large">
|
||||||
<p class="about__caption">Наша сеть включает 30+ российских фабрик, отобранных по строгим стандартам качества. Мы сотрудничаем исключительно с лидерами рынка, чья продукция доказала свое превосходство временем.</p>
|
<p class="about__caption">Наша сеть включает 30+ российских фабрик, отобранных по строгим стандартам качества. Мы сотрудничаем исключительно с лидерами рынка, чья продукция доказала свое превосходство временем.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -83,12 +83,20 @@
|
|||||||
<div class="solutions-slider">
|
<div class="solutions-slider">
|
||||||
<div class="solutions-slider__slides">
|
<div class="solutions-slider__slides">
|
||||||
<div class="solutions-slider__slide">
|
<div class="solutions-slider__slide">
|
||||||
<img src="/img/слайдер_1.jpg" class="solution-img" alt="Готовое решение для гостиной">
|
<img src="/assets/images/слайдер_1.jpg" class="solution-img" alt="Готовое решение для гостиной">
|
||||||
<div class="solution-text-overlay">
|
<div class="solution-text-overlay">
|
||||||
<h2>ГОТОВОЕ РЕШЕНИЕ<br>ДЛЯ ВАШЕЙ ГОСТИНОЙ</h2><br>
|
<h2>ГОТОВОЕ РЕШЕНИЕ<br>ДЛЯ ВАШЕЙ ГОСТИНОЙ</h2><br>
|
||||||
<p>УСПЕЙТЕ ЗАКАЗАТЬ СЕЙЧАС</p>
|
<p>УСПЕЙТЕ ЗАКАЗАТЬ СЕЙЧАС</p>
|
||||||
</div>
|
</div>
|
||||||
<a href="/cite_practica/catalog" class="solution-image-link">Подробнее</a>
|
<a href="/catalog" class="solution-image-link">Подробнее</a>
|
||||||
|
</div>
|
||||||
|
<div class="solutions-slider__slide">
|
||||||
|
<img src="/assets/images/слайдер_6.jpg" class="solution-img" alt="Готовое решение для спальни">
|
||||||
|
<div class="solution-text-overlay">
|
||||||
|
<h2>ГОТОВОЕ РЕШЕНИЕ<br>ДЛЯ ВАШЕЙ СПАЛЬНИ</h2><br>
|
||||||
|
<p>УСПЕЙТЕ ЗАКАЗАТЬ СЕЙЧАС</p>
|
||||||
|
</div>
|
||||||
|
<a href="/catalog" class="solution-image-link">Подробнее</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -150,4 +158,3 @@
|
|||||||
<button class="btn primary-btn">Задать вопрос</button>
|
<button class="btn primary-btn">Задать вопрос</button>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|||||||
@@ -44,25 +44,25 @@
|
|||||||
<h1><i class="fas fa-user-shield"></i> Админ-панель AETERNA</h1>
|
<h1><i class="fas fa-user-shield"></i> Админ-панель AETERNA</h1>
|
||||||
<div>
|
<div>
|
||||||
<span><?= htmlspecialchars($user['email'] ?? 'Администратор') ?></span>
|
<span><?= htmlspecialchars($user['email'] ?? 'Администратор') ?></span>
|
||||||
<a href="/cite_practica/catalog" class="btn btn-primary" style="margin-left: 10px;">В каталог</a>
|
<a href="/catalog" class="btn btn-primary" style="margin-left: 10px;">В каталог</a>
|
||||||
<a href="/cite_practica/logout" class="btn btn-danger" style="margin-left: 10px;">Выйти</a>
|
<a href="/logout" class="btn btn-danger" style="margin-left: 10px;">Выйти</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="admin-tabs">
|
<div class="admin-tabs">
|
||||||
<a href="/cite_practica/admin" class="admin-tab <?= ($action ?? '') === 'dashboard' ? 'active' : '' ?>">
|
<a href="/admin" class="admin-tab <?= ($action ?? '') === 'dashboard' ? 'active' : '' ?>">
|
||||||
<i class="fas fa-tachometer-alt"></i> Дашборд
|
<i class="fas fa-tachometer-alt"></i> Дашборд
|
||||||
</a>
|
</a>
|
||||||
<a href="/cite_practica/admin/products" class="admin-tab <?= str_contains($_SERVER['REQUEST_URI'] ?? '', '/products') ? 'active' : '' ?>">
|
<a href="/admin/products" class="admin-tab <?= str_contains($_SERVER['REQUEST_URI'] ?? '', '/products') ? 'active' : '' ?>">
|
||||||
<i class="fas fa-box"></i> Товары
|
<i class="fas fa-box"></i> Товары
|
||||||
</a>
|
</a>
|
||||||
<a href="/cite_practica/admin/categories" class="admin-tab <?= str_contains($_SERVER['REQUEST_URI'] ?? '', '/categories') ? 'active' : '' ?>">
|
<a href="/admin/categories" class="admin-tab <?= str_contains($_SERVER['REQUEST_URI'] ?? '', '/categories') ? 'active' : '' ?>">
|
||||||
<i class="fas fa-tags"></i> Категории
|
<i class="fas fa-tags"></i> Категории
|
||||||
</a>
|
</a>
|
||||||
<a href="/cite_practica/admin/orders" class="admin-tab <?= str_contains($_SERVER['REQUEST_URI'] ?? '', '/orders') ? 'active' : '' ?>">
|
<a href="/admin/orders" class="admin-tab <?= str_contains($_SERVER['REQUEST_URI'] ?? '', '/orders') ? 'active' : '' ?>">
|
||||||
<i class="fas fa-shopping-cart"></i> Заказы
|
<i class="fas fa-shopping-cart"></i> Заказы
|
||||||
</a>
|
</a>
|
||||||
<a href="/cite_practica/admin/users" class="admin-tab <?= str_contains($_SERVER['REQUEST_URI'] ?? '', '/users') ? 'active' : '' ?>">
|
<a href="/admin/users" class="admin-tab <?= str_contains($_SERVER['REQUEST_URI'] ?? '', '/users') ? 'active' : '' ?>">
|
||||||
<i class="fas fa-users"></i> Пользователи
|
<i class="fas fa-users"></i> Пользователи
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@@ -72,4 +72,3 @@
|
|||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
||||||
|
|||||||
@@ -47,16 +47,16 @@
|
|||||||
<?= \App\Core\View::partial('footer') ?>
|
<?= \App\Core\View::partial('footer') ?>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
function showNotification(message, type = 'success') {
|
function showNotification(message, type) {
|
||||||
const notification = $('#notification');
|
type = type || 'success';
|
||||||
|
var notification = $('#notification');
|
||||||
notification.text(message);
|
notification.text(message);
|
||||||
notification.removeClass('success error').addClass(type + ' show');
|
notification.removeClass('success error').addClass(type + ' show');
|
||||||
setTimeout(function() { notification.removeClass('show'); }, 3000);
|
setTimeout(function() { notification.removeClass('show'); }, 3000);
|
||||||
}
|
}
|
||||||
|
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
// Обновляем счетчик корзины
|
$.get('/cart/count', function(response) {
|
||||||
$.get('/cite_practica/cart/count', function(response) {
|
|
||||||
if (response.success) {
|
if (response.success) {
|
||||||
$('.cart-count').text(response.cart_count);
|
$('.cart-count').text(response.cart_count);
|
||||||
}
|
}
|
||||||
@@ -65,4 +65,3 @@
|
|||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<main class="delivery-page">
|
<main class="delivery-page">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="breadcrumbs">
|
<div class="breadcrumbs">
|
||||||
<a href="/cite_practica/">Главная</a> • <span class="current-page">Доставка и оплата</span>
|
<a href="/">Главная</a> • <span class="current-page">Доставка и оплата</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h1 style="color: #453227; margin: 30px 0;">Доставка и оплата</h1>
|
<h1 style="color: #453227; margin: 30px 0;">Доставка и оплата</h1>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<main class="services-page">
|
<main class="services-page">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="breadcrumbs">
|
<div class="breadcrumbs">
|
||||||
<a href="/cite_practica/">Главная</a> • <span class="current-page">Услуги</span>
|
<a href="/">Главная</a> • <span class="current-page">Услуги</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h1 style="color: #453227; margin: 30px 0;">Наши услуги</h1>
|
<h1 style="color: #453227; margin: 30px 0;">Наши услуги</h1>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<main class="warranty-page">
|
<main class="warranty-page">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="breadcrumbs">
|
<div class="breadcrumbs">
|
||||||
<a href="/cite_practica/">Главная</a> • <span class="current-page">Гарантия и возврат</span>
|
<a href="/">Главная</a> • <span class="current-page">Гарантия и возврат</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h1 style="color: #453227; margin: 30px 0;">Гарантия и возврат</h1>
|
<h1 style="color: #453227; margin: 30px 0;">Гарантия и возврат</h1>
|
||||||
|
|||||||
@@ -7,17 +7,17 @@
|
|||||||
<div class="footer__col">
|
<div class="footer__col">
|
||||||
<h5>ПОКУПАТЕЛЮ</h5>
|
<h5>ПОКУПАТЕЛЮ</h5>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="/cite_practica/catalog">Каталог</a></li>
|
<li><a href="/catalog">Каталог</a></li>
|
||||||
<li><a href="/cite_practica/services">Услуги</a></li>
|
<li><a href="/services">Услуги</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="footer__col">
|
<div class="footer__col">
|
||||||
<h5>ПОМОЩЬ</h5>
|
<h5>ПОМОЩЬ</h5>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="/cite_practica/delivery">Доставка и оплата</a></li>
|
<li><a href="/delivery">Доставка и оплата</a></li>
|
||||||
<li><a href="/cite_practica/warranty">Гарантия и возврат</a></li>
|
<li><a href="/warranty">Гарантия и возврат</a></li>
|
||||||
<li><a href="/cite_practica/#faq">Ответы на вопросы</a></li>
|
<li><a href="/#faq">Ответы на вопросы</a></li>
|
||||||
<li><a href="#footer">Контакты</a></li>
|
<li><a href="#footer">Контакты</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@@ -42,7 +42,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="copyright">
|
<div class="copyright">
|
||||||
<p>© 2025 AETERNA. Все права защищены.</p>
|
<p>© <?= date('Y') ?> AETERNA. Все права защищены.</p>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
|||||||
@@ -6,24 +6,24 @@ $user = $user ?? \App\Core\View::currentUser();
|
|||||||
<header class="header">
|
<header class="header">
|
||||||
<div class="header__top">
|
<div class="header__top">
|
||||||
<div class="container header__top-content">
|
<div class="container header__top-content">
|
||||||
<a href="/cite_practica/" class="logo">AETERNA</a>
|
<a href="/" class="logo">AETERNA</a>
|
||||||
|
|
||||||
<div class="search-catalog">
|
<div class="search-catalog">
|
||||||
<div class="catalog-dropdown">
|
<div class="catalog-dropdown">
|
||||||
Все категории <span>▼</span>
|
Все категории <span>▼</span>
|
||||||
<div class="catalog-dropdown__menu">
|
<div class="catalog-dropdown__menu">
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="/cite_practica/catalog">Все товары</a></li>
|
<li><a href="/catalog">Все товары</a></li>
|
||||||
<li><a href="/cite_practica/catalog?category=1">Диваны</a></li>
|
<li><a href="/catalog?category=1">Диваны</a></li>
|
||||||
<li><a href="/cite_practica/catalog?category=2">Кровати</a></li>
|
<li><a href="/catalog?category=2">Кровати</a></li>
|
||||||
<li><a href="/cite_practica/catalog?category=3">Шкафы</a></li>
|
<li><a href="/catalog?category=3">Шкафы</a></li>
|
||||||
<li><a href="/cite_practica/catalog?category=4">Стулья</a></li>
|
<li><a href="/catalog?category=4">Стулья</a></li>
|
||||||
<li><a href="/cite_practica/catalog?category=5">Столы</a></li>
|
<li><a href="/catalog?category=5">Столы</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="search-box">
|
<div class="search-box">
|
||||||
<form method="GET" action="/cite_practica/catalog" style="display: flex; width: 100%;">
|
<form method="GET" action="/catalog" style="display: flex; width: 100%;">
|
||||||
<input type="text" name="search" placeholder="Поиск товаров" style="border: none; width: 100%; padding: 10px;">
|
<input type="text" name="search" placeholder="Поиск товаров" style="border: none; width: 100%; padding: 10px;">
|
||||||
<button type="submit" style="background: none; border: none; cursor: pointer;">
|
<button type="submit" style="background: none; border: none; cursor: pointer;">
|
||||||
<span class="search-icon"><i class="fas fa-search"></i></span>
|
<span class="search-icon"><i class="fas fa-search"></i></span>
|
||||||
@@ -34,7 +34,7 @@ $user = $user ?? \App\Core\View::currentUser();
|
|||||||
|
|
||||||
<div class="header__icons--top">
|
<div class="header__icons--top">
|
||||||
<?php if ($isLoggedIn): ?>
|
<?php if ($isLoggedIn): ?>
|
||||||
<a href="/cite_practica/cart" class="icon cart-icon">
|
<a href="/cart" class="icon cart-icon">
|
||||||
<i class="fas fa-shopping-cart"></i>
|
<i class="fas fa-shopping-cart"></i>
|
||||||
<span class="cart-count">0</span>
|
<span class="cart-count">0</span>
|
||||||
</a>
|
</a>
|
||||||
@@ -68,17 +68,17 @@ $user = $user ?? \App\Core\View::currentUser();
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ul class="user-profile-links">
|
<ul class="user-profile-links">
|
||||||
<li><a href="/cite_practica/cart"><i class="fas fa-shopping-bag"></i> Корзина</a></li>
|
<li><a href="/cart"><i class="fas fa-shopping-bag"></i> Корзина</a></li>
|
||||||
<?php if ($isAdmin): ?>
|
<?php if ($isAdmin): ?>
|
||||||
<li><a href="/cite_practica/admin"><i class="fas fa-user-shield"></i> Админ-панель</a></li>
|
<li><a href="/admin"><i class="fas fa-user-shield"></i> Админ-панель</a></li>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
<li><a href="/cite_practica/logout" class="logout-link"><i class="fas fa-sign-out-alt"></i> Выйти</a></li>
|
<li><a href="/logout" class="logout-link"><i class="fas fa-sign-out-alt"></i> Выйти</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<a href="/cite_practica/login" class="icon"><i class="far fa-user"></i></a>
|
<a href="/login" class="icon"><i class="far fa-user"></i></a>
|
||||||
<a href="/cite_practica/login" style="font-size: 12px; color: #666; margin-left: 5px;">Войти</a>
|
<a href="/login" style="font-size: 12px; color: #666; margin-left: 5px;">Войти</a>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -87,7 +87,7 @@ $user = $user ?? \App\Core\View::currentUser();
|
|||||||
<div class="header__bottom">
|
<div class="header__bottom">
|
||||||
<div class="container header__bottom-content">
|
<div class="container header__bottom-content">
|
||||||
<div class="catalog-menu">
|
<div class="catalog-menu">
|
||||||
<a href="/cite_practica/catalog" class="catalog-link">
|
<a href="/catalog" class="catalog-link">
|
||||||
<span class="catalog-lines">☰</span>
|
<span class="catalog-lines">☰</span>
|
||||||
Каталог
|
Каталог
|
||||||
</a>
|
</a>
|
||||||
@@ -95,10 +95,10 @@ $user = $user ?? \App\Core\View::currentUser();
|
|||||||
|
|
||||||
<nav class="nav">
|
<nav class="nav">
|
||||||
<ul class="nav-list">
|
<ul class="nav-list">
|
||||||
<li><a href="/cite_practica/">Главная</a></li>
|
<li><a href="/">Главная</a></li>
|
||||||
<li><a href="/cite_practica/services">Услуги</a></li>
|
<li><a href="/services">Услуги</a></li>
|
||||||
<li><a href="/cite_practica/delivery">Доставка и оплата</a></li>
|
<li><a href="/delivery">Доставка и оплата</a></li>
|
||||||
<li><a href="/cite_practica/warranty">Гарантия</a></li>
|
<li><a href="/warranty">Гарантия</a></li>
|
||||||
<li><a href="#footer">Контакты</a></li>
|
<li><a href="#footer">Контакты</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
@@ -106,4 +106,3 @@ $user = $user ?? \App\Core\View::currentUser();
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ use App\Core\View;
|
|||||||
<main class="catalog-main">
|
<main class="catalog-main">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="breadcrumbs">
|
<div class="breadcrumbs">
|
||||||
<a href="/cite_practica/">Главная</a> • <span class="current-page">Каталог</span>
|
<a href="/">Главная</a> • <span class="current-page">Каталог</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<?php if (!empty($success)): ?>
|
<?php if (!empty($success)): ?>
|
||||||
@@ -48,10 +48,10 @@ use App\Core\View;
|
|||||||
<i class="fas fa-user-shield"></i> Панель управления каталогом
|
<i class="fas fa-user-shield"></i> Панель управления каталогом
|
||||||
</h3>
|
</h3>
|
||||||
<div>
|
<div>
|
||||||
<a href="/cite_practica/admin/products" class="admin-btn"><i class="fas fa-boxes"></i> Управление каталогом</a>
|
<a href="/admin/products" class="admin-btn"><i class="fas fa-boxes"></i> Управление каталогом</a>
|
||||||
<a href="/cite_practica/admin/products/add" class="admin-btn"><i class="fas fa-plus"></i> Добавить товар</a>
|
<a href="/admin/products/add" class="admin-btn"><i class="fas fa-plus"></i> Добавить товар</a>
|
||||||
<a href="/cite_practica/admin/categories" class="admin-btn"><i class="fas fa-tags"></i> Категории</a>
|
<a href="/admin/categories" class="admin-btn"><i class="fas fa-tags"></i> Категории</a>
|
||||||
<a href="/cite_practica/admin/orders" class="admin-btn"><i class="fas fa-shopping-cart"></i> Заказы</a>
|
<a href="/admin/orders" class="admin-btn"><i class="fas fa-shopping-cart"></i> Заказы</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
@@ -67,14 +67,14 @@ use App\Core\View;
|
|||||||
|
|
||||||
<div class="catalog-wrapper">
|
<div class="catalog-wrapper">
|
||||||
<aside class="catalog-sidebar">
|
<aside class="catalog-sidebar">
|
||||||
<form method="GET" action="/cite_practica/catalog" id="filterForm">
|
<form method="GET" action="/catalog" id="filterForm">
|
||||||
<div class="filter-group">
|
<div class="filter-group">
|
||||||
<h4 class="filter-title">КАТЕГОРИИ</h4>
|
<h4 class="filter-title">КАТЕГОРИИ</h4>
|
||||||
<ul class="filter-list">
|
<ul class="filter-list">
|
||||||
<li><a href="/cite_practica/catalog" class="<?= empty($filters['category_id']) ? 'active-category' : '' ?>">Все товары</a></li>
|
<li><a href="/catalog" class="<?= empty($filters['category_id']) ? 'active-category' : '' ?>">Все товары</a></li>
|
||||||
<?php foreach ($categories as $category): ?>
|
<?php foreach ($categories as $category): ?>
|
||||||
<li>
|
<li>
|
||||||
<a href="/cite_practica/catalog?category=<?= $category['category_id'] ?>"
|
<a href="/catalog?category=<?= $category['category_id'] ?>"
|
||||||
class="<?= $filters['category_id'] == $category['category_id'] ? 'active-category' : '' ?>">
|
class="<?= $filters['category_id'] == $category['category_id'] ? 'active-category' : '' ?>">
|
||||||
<?= htmlspecialchars($category['name']) ?>
|
<?= htmlspecialchars($category['name']) ?>
|
||||||
</a>
|
</a>
|
||||||
@@ -129,7 +129,7 @@ use App\Core\View;
|
|||||||
<?php if (!empty($filters['search'])): ?>
|
<?php if (!empty($filters['search'])): ?>
|
||||||
<p style="color: #666;">
|
<p style="color: #666;">
|
||||||
Результаты поиска: "<strong><?= htmlspecialchars($filters['search']) ?></strong>"
|
Результаты поиска: "<strong><?= htmlspecialchars($filters['search']) ?></strong>"
|
||||||
<a href="/cite_practica/catalog" style="margin-left: 10px; color: #617365;">
|
<a href="/catalog" style="margin-left: 10px; color: #617365;">
|
||||||
<i class="fas fa-times"></i> Очистить
|
<i class="fas fa-times"></i> Очистить
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
@@ -144,9 +144,19 @@ use App\Core\View;
|
|||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<?php foreach ($products as $product): ?>
|
<?php foreach ($products as $product): ?>
|
||||||
<div class="product-card <?= !$product['is_available'] ? 'unavailable' : '' ?>"
|
<div class="product-card <?= !$product['is_available'] ? 'unavailable' : '' ?>"
|
||||||
onclick="window.location.href='/cite_practica/product/<?= $product['product_id'] ?>'"
|
onclick="window.location.href='/product/<?= $product['product_id'] ?>'"
|
||||||
data-product-id="<?= $product['product_id'] ?>">
|
data-product-id="<?= $product['product_id'] ?>">
|
||||||
<img src="/cite_practica/<?= htmlspecialchars($product['image_url'] ?? 'img/1.jpg') ?>"
|
<?php
|
||||||
|
$imageUrl = $product['image_url'] ?? '';
|
||||||
|
if (empty($imageUrl)) {
|
||||||
|
$imageUrl = '/assets/images/1.jpg';
|
||||||
|
} elseif (strpos($imageUrl, '/img2/') === 0) {
|
||||||
|
$imageUrl = str_replace('/img2/', '/assets/images/', $imageUrl);
|
||||||
|
} elseif (strpos($imageUrl, 'img2/') === 0) {
|
||||||
|
$imageUrl = str_replace('img2/', '/assets/images/', $imageUrl);
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<img src="<?= htmlspecialchars($imageUrl) ?>"
|
||||||
alt="<?= htmlspecialchars($product['name']) ?>">
|
alt="<?= htmlspecialchars($product['name']) ?>">
|
||||||
<div class="product-info">
|
<div class="product-info">
|
||||||
<div class="name"><?= htmlspecialchars($product['name']) ?></div>
|
<div class="name"><?= htmlspecialchars($product['name']) ?></div>
|
||||||
@@ -174,7 +184,7 @@ $('#priceSlider').on('input', function() {
|
|||||||
|
|
||||||
function addToCart(productId, productName) {
|
function addToCart(productId, productName) {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: '/cite_practica/cart/add',
|
url: '/cart/add',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: { product_id: productId, quantity: 1 },
|
data: { product_id: productId, quantity: 1 },
|
||||||
dataType: 'json',
|
dataType: 'json',
|
||||||
@@ -192,4 +202,3 @@ function addToCart(productId, productName) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -35,10 +35,10 @@ use App\Core\View;
|
|||||||
|
|
||||||
<main class="container">
|
<main class="container">
|
||||||
<div class="breadcrumbs">
|
<div class="breadcrumbs">
|
||||||
<a href="/cite_practica/">Главная</a> •
|
<a href="/">Главная</a> •
|
||||||
<a href="/cite_practica/catalog">Каталог</a> •
|
<a href="/catalog">Каталог</a> •
|
||||||
<?php if ($product['category_name']): ?>
|
<?php if ($product['category_name']): ?>
|
||||||
<a href="/cite_practica/catalog?category=<?= $product['category_id'] ?>">
|
<a href="/catalog?category=<?= $product['category_id'] ?>">
|
||||||
<?= htmlspecialchars($product['category_name']) ?>
|
<?= htmlspecialchars($product['category_name']) ?>
|
||||||
</a> •
|
</a> •
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
@@ -48,7 +48,17 @@ use App\Core\View;
|
|||||||
<div class="product__section">
|
<div class="product__section">
|
||||||
<div class="product__gallery">
|
<div class="product__gallery">
|
||||||
<div class="product__main-image">
|
<div class="product__main-image">
|
||||||
<img src="/cite_practica/<?= htmlspecialchars($product['image_url'] ?? 'img/1.jpg') ?>"
|
<?php
|
||||||
|
$imageUrl = $product['image_url'] ?? '';
|
||||||
|
if (empty($imageUrl)) {
|
||||||
|
$imageUrl = '/assets/images/1.jpg';
|
||||||
|
} elseif (strpos($imageUrl, '/img2/') === 0) {
|
||||||
|
$imageUrl = str_replace('/img2/', '/assets/images/', $imageUrl);
|
||||||
|
} elseif (strpos($imageUrl, 'img2/') === 0) {
|
||||||
|
$imageUrl = str_replace('img2/', '/assets/images/', $imageUrl);
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<img src="<?= htmlspecialchars($imageUrl) ?>"
|
||||||
alt="<?= htmlspecialchars($product['name']) ?>">
|
alt="<?= htmlspecialchars($product['name']) ?>">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -134,7 +144,7 @@ use App\Core\View;
|
|||||||
|
|
||||||
<?php if ($isAdmin): ?>
|
<?php if ($isAdmin): ?>
|
||||||
<div style="margin-top: 20px;">
|
<div style="margin-top: 20px;">
|
||||||
<a href="/cite_practica/admin/products/edit/<?= $product['product_id'] ?>" class="btn" style="background: #ffc107; color: #333;">
|
<a href="/admin/products/edit/<?= $product['product_id'] ?>" class="btn" style="background: #ffc107; color: #333;">
|
||||||
<i class="fas fa-edit"></i> Редактировать
|
<i class="fas fa-edit"></i> Редактировать
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@@ -147,8 +157,18 @@ use App\Core\View;
|
|||||||
<h2>Похожие товары</h2>
|
<h2>Похожие товары</h2>
|
||||||
<div class="products-grid">
|
<div class="products-grid">
|
||||||
<?php foreach ($similarProducts as $similar): ?>
|
<?php foreach ($similarProducts as $similar): ?>
|
||||||
<div class="product-card" onclick="window.location.href='/cite_practica/product/<?= $similar['product_id'] ?>'" style="cursor: pointer;">
|
<?php
|
||||||
<img src="/cite_practica/<?= htmlspecialchars($similar['image_url'] ?? 'img/1.jpg') ?>"
|
$simImageUrl = $similar['image_url'] ?? '';
|
||||||
|
if (empty($simImageUrl)) {
|
||||||
|
$simImageUrl = '/assets/images/1.jpg';
|
||||||
|
} elseif (strpos($simImageUrl, '/img2/') === 0) {
|
||||||
|
$simImageUrl = str_replace('/img2/', '/assets/images/', $simImageUrl);
|
||||||
|
} elseif (strpos($simImageUrl, 'img2/') === 0) {
|
||||||
|
$simImageUrl = str_replace('img2/', '/assets/images/', $simImageUrl);
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<div class="product-card" onclick="window.location.href='/product/<?= $similar['product_id'] ?>'" style="cursor: pointer;">
|
||||||
|
<img src="<?= htmlspecialchars($simImageUrl) ?>"
|
||||||
alt="<?= htmlspecialchars($similar['name']) ?>">
|
alt="<?= htmlspecialchars($similar['name']) ?>">
|
||||||
<div class="product-info">
|
<div class="product-info">
|
||||||
<h3 style="font-size: 16px; color: #453227;"><?= htmlspecialchars($similar['name']) ?></h3>
|
<h3 style="font-size: 16px; color: #453227;"><?= htmlspecialchars($similar['name']) ?></h3>
|
||||||
@@ -180,7 +200,7 @@ $(document).ready(function() {
|
|||||||
function addToCart(productId) {
|
function addToCart(productId) {
|
||||||
const quantity = $('.product__qty-value').val();
|
const quantity = $('.product__qty-value').val();
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: '/cite_practica/cart/add',
|
url: '/cart/add',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: { product_id: productId, quantity: quantity },
|
data: { product_id: productId, quantity: quantity },
|
||||||
dataType: 'json',
|
dataType: 'json',
|
||||||
@@ -198,13 +218,12 @@ function addToCart(productId) {
|
|||||||
function buyNow(productId) {
|
function buyNow(productId) {
|
||||||
const quantity = $('.product__qty-value').val();
|
const quantity = $('.product__qty-value').val();
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: '/cite_practica/cart/add',
|
url: '/cart/add',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: { product_id: productId, quantity: quantity },
|
data: { product_id: productId, quantity: quantity },
|
||||||
success: function() {
|
success: function() {
|
||||||
window.location.href = '/cite_practica/cart';
|
window.location.href = '/cart';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
1382
catalog.php
@@ -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();
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
762
cite_mebel.php
@@ -1,762 +0,0 @@
|
|||||||
<?php
|
|
||||||
session_start();
|
|
||||||
|
|
||||||
// Упрощенная проверка для каталога
|
|
||||||
if (isset($_GET['action']) && $_GET['action'] == 'go_to_catalog') {
|
|
||||||
if (!isset($_SESSION['isLoggedIn']) || $_SESSION['isLoggedIn'] !== true) {
|
|
||||||
// Перенаправляем на вход
|
|
||||||
header('Location: вход.php?redirect=' . urlencode('catalog.php'));
|
|
||||||
exit();
|
|
||||||
} else {
|
|
||||||
// Если авторизован - пускаем в каталог
|
|
||||||
header('Location: catalog.php');
|
|
||||||
exit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<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>
|
|
||||||
|
|
||||||
/* Стили для профиля пользователя */
|
|
||||||
.user-profile-dropdown {
|
|
||||||
position: relative;
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-profile-toggle {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 10px;
|
|
||||||
cursor: pointer;
|
|
||||||
padding: 8px 12px;
|
|
||||||
border-radius: 4px;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-profile-toggle:hover {
|
|
||||||
background-color: rgba(0, 0, 0, 0.05);
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-avatar {
|
|
||||||
width: 36px;
|
|
||||||
height: 36px;
|
|
||||||
border-radius: 50%;
|
|
||||||
background: linear-gradient(135deg, #617365 0%, #453227 100%);
|
|
||||||
color: white;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-info {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-email {
|
|
||||||
font-size: 12px;
|
|
||||||
color: #666;
|
|
||||||
max-width: 120px;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-status {
|
|
||||||
font-size: 10px;
|
|
||||||
padding: 2px 8px;
|
|
||||||
border-radius: 12px;
|
|
||||||
text-transform: uppercase;
|
|
||||||
font-weight: bold;
|
|
||||||
text-align: center;
|
|
||||||
margin-top: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-status.admin {
|
|
||||||
background-color: #617365;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-status.user {
|
|
||||||
background-color: #28a745;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dropdown-arrow {
|
|
||||||
font-size: 12px;
|
|
||||||
color: #666;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-profile-menu {
|
|
||||||
display: none;
|
|
||||||
position: absolute;
|
|
||||||
top: 100%;
|
|
||||||
right: 0;
|
|
||||||
background: white;
|
|
||||||
min-width: 280px;
|
|
||||||
border-radius: 8px;
|
|
||||||
box-shadow: 0 5px 20px rgba(0,0,0,0.15);
|
|
||||||
z-index: 1000;
|
|
||||||
margin-top: 10px;
|
|
||||||
border: 1px solid #e0e0e0;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-profile-header {
|
|
||||||
padding: 15px;
|
|
||||||
background: #f8f9fa;
|
|
||||||
border-bottom: 1px solid #e0e0e0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-profile-name {
|
|
||||||
font-weight: bold;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
color: #333;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-profile-details {
|
|
||||||
font-size: 12px;
|
|
||||||
color: #666;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-profile-details small {
|
|
||||||
display: block;
|
|
||||||
margin-bottom: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-profile-details i {
|
|
||||||
width: 16px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-profile-links {
|
|
||||||
list-style: none;
|
|
||||||
padding: 10px 0;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-profile-links li {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-profile-links a {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 10px;
|
|
||||||
padding: 10px 15px;
|
|
||||||
color: #333;
|
|
||||||
text-decoration: none;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
border-left: 3px solid transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-profile-links a:hover {
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
border-left-color: #453227;
|
|
||||||
color: #453227;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-profile-links i {
|
|
||||||
width: 20px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logout-item {
|
|
||||||
border-top: 1px solid #e0e0e0;
|
|
||||||
margin-top: 5px;
|
|
||||||
padding-top: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logout-link {
|
|
||||||
color: #dc3545 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logout-link:hover {
|
|
||||||
background-color: #ffe6e6 !important;
|
|
||||||
border-left-color: #dc3545 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cart-icon {
|
|
||||||
position: relative;
|
|
||||||
margin-right: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cart-count {
|
|
||||||
position: absolute;
|
|
||||||
top: -8px;
|
|
||||||
right: -8px;
|
|
||||||
background: #dc3545;
|
|
||||||
color: white;
|
|
||||||
border-radius: 50%;
|
|
||||||
width: 18px;
|
|
||||||
height: 18px;
|
|
||||||
font-size: 11px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Стиль для заблокированных ссылок */
|
|
||||||
.link-disabled {
|
|
||||||
cursor: not-allowed !important;
|
|
||||||
opacity: 0.6 !important;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.link-disabled:hover::after {
|
|
||||||
content: "🔒 Требуется авторизация";
|
|
||||||
position: absolute;
|
|
||||||
top: -30px;
|
|
||||||
left: 50%;
|
|
||||||
transform: translateX(-50%);
|
|
||||||
background: #333;
|
|
||||||
color: white;
|
|
||||||
padding: 5px 10px;
|
|
||||||
border-radius: 4px;
|
|
||||||
font-size: 12px;
|
|
||||||
white-space: nowrap;
|
|
||||||
z-index: 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Кнопка каталога с замком */
|
|
||||||
.catalog-locked {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.catalog-locked::before {
|
|
||||||
content: "🔒";
|
|
||||||
position: absolute;
|
|
||||||
top: -5px;
|
|
||||||
right: -5px;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
</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="javascript:void(0)" onclick="checkAuth('catalog.php?category=1')">Диваны</a></li>
|
|
||||||
<li><a href="javascript:void(0)" onclick="checkAuth('catalog.php?category=2')">Кровати</a></li>
|
|
||||||
<li><a href="javascript:void(0)" onclick="checkAuth('catalog.php?category=3')">Шкафы</a></li>
|
|
||||||
<li><a href="javascript:void(0)" onclick="checkAuth('catalog.php?category=4')">Стулья</a></li>
|
|
||||||
<li><a href="javascript:void(0)" onclick="checkAuth('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>
|
|
||||||
|
|
||||||
<!-- Вставьте этот код в секцию header__icons--top вместо текущего -->
|
|
||||||
<!-- Вставьте этот код в секцию header__icons--top вместо текущего -->
|
|
||||||
<div class="header__icons--top">
|
|
||||||
<?php if (isset($_SESSION['isLoggedIn']) && $_SESSION['isLoggedIn'] === true): ?>
|
|
||||||
<!-- Иконка корзины -->
|
|
||||||
<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">
|
|
||||||
<?php
|
|
||||||
$email = $_SESSION['user_email'] ?? '';
|
|
||||||
echo !empty($email) ? strtoupper(substr($email, 0, 1)) : 'U';
|
|
||||||
?>
|
|
||||||
</div>
|
|
||||||
<div class="user-info">
|
|
||||||
<div class="user-email"><?= htmlspecialchars($email) ?></div>
|
|
||||||
<div class="user-status <?= isset($_SESSION['isAdmin']) && $_SESSION['isAdmin'] ? 'admin' : 'user' ?>">
|
|
||||||
<?= isset($_SESSION['isAdmin']) && $_SESSION['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($_SESSION['full_name'] ?? $email) ?>
|
|
||||||
</div>
|
|
||||||
<div class="user-profile-details">
|
|
||||||
<small><i class="far fa-envelope"></i> <?= htmlspecialchars($email) ?></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 (isset($_SESSION['isAdmin']) && $_SESSION['isAdmin']): ?>
|
|
||||||
<li>
|
|
||||||
<a href="catalog_admin.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 <?= (isset($_SESSION['isLoggedIn']) && $_SESSION['isLoggedIn'] === true) ? '' : 'catalog-locked' ?>">
|
|
||||||
<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" class="active">Главная</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>
|
|
||||||
<section class="hero">
|
|
||||||
<div class="container hero__content">
|
|
||||||
<div class="hero__image-block">
|
|
||||||
<div class="hero__circle"></div>
|
|
||||||
<img src="img/chair.PNG" alt="Кресло и торшер" class="hero__img">
|
|
||||||
</div>
|
|
||||||
<div class="hero__text-block">
|
|
||||||
<h1>ДОБАВЬТЕ ИЗЫСКАННОСТИ В СВОЙ ИНТЕРЬЕР</h1>
|
|
||||||
<p class="hero__usp-text">Мы создаем мебель, которая сочетает в себе безупречный дизайн, натуральные материалы, продуманный функционал, чтобы ваш день начинался и заканчивался с комфортом.</p>
|
|
||||||
|
|
||||||
<?php if (isset($_SESSION['isLoggedIn']) && $_SESSION['isLoggedIn'] === true): ?>
|
|
||||||
<a href="?action=go_to_catalog" class="btn primary-btn">ПЕРЕЙТИ В КАТАЛОГ</a>
|
|
||||||
<?php else: ?>
|
|
||||||
<a href="javascript:void(0)" onclick="checkAuth('catalog.php')" class="btn primary-btn link-disabled">ПЕРЕЙТИ В КАТАЛОГ</a>
|
|
||||||
<?php endif; ?>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="advantages">
|
|
||||||
<div class="container">
|
|
||||||
<div class="advantages__header">
|
|
||||||
<h2>ПОЧЕМУ <br>ВЫБИРАЮТ НАС?</h2>
|
|
||||||
<div class="advantages__items">
|
|
||||||
<div class="advantage-item">
|
|
||||||
<span class="advantage-item__number">1</span>
|
|
||||||
<h4>ГАРАНТИЯ ВЫСОЧАЙШЕГО КАЧЕСТВА</h4>
|
|
||||||
<p>Собственное производство и строгий контроль на всех этапах.</p>
|
|
||||||
</div>
|
|
||||||
<div class="advantage-item">
|
|
||||||
<span class="advantage-item__number">2</span>
|
|
||||||
<h4>ИСПОЛЬЗОВАНИЕ НАДЕЖНЫХ МАТЕРИАЛОВ</h4>
|
|
||||||
<p>Гарантия безопасности и долговечности.</p>
|
|
||||||
</div>
|
|
||||||
<div class="advantage-item">
|
|
||||||
<span class="advantage-item__number">3</span>
|
|
||||||
<h4>ИНДИВИДУАЛЬНЫЙ ПОДХОД И ГИБКОСТЬ УСЛОВИЙ</h4>
|
|
||||||
<p>Реализуем проекты любой сложности по вашим техническим заданиям.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="promo-images">
|
|
||||||
<div class="promo-image-col">
|
|
||||||
<img src="img/спальня.jpg" alt="Кровать и тумба">
|
|
||||||
<div class="image-overlay-text">
|
|
||||||
<h4>НОВИНКИ В КАТЕГОРИЯХ <br>МЯГКАЯ МЕБЕЛЬ</h4>
|
|
||||||
<a href="#" class="overlay-link">ПЕРЕЙТИ</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="promo-image-col">
|
|
||||||
<img src="img/диван.jpg" alt="Диван в гостиной">
|
|
||||||
<div class="image-overlay-text">
|
|
||||||
<h4>РАСПРОДАЖА <br>ПРЕДМЕТЫ ДЕКОРА</h4>
|
|
||||||
<a href="#" class="overlay-link">ПЕРЕЙТИ</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="about">
|
|
||||||
<div class="container about__content">
|
|
||||||
<div class="about__column about__column--left">
|
|
||||||
<div class="about__text-block">
|
|
||||||
<h2>О НАС</h2>
|
|
||||||
<p class="text-justified">Компания AETERNA - российский производитель качественной корпусной и мягкой мебели для дома и офиса. С 2015 года мы успешно реализуем проекты любой сложности, сочетая современные технологии, проверенные материалы и классическое мастерство.</p>
|
|
||||||
</div>
|
|
||||||
<img src="img/кресло_1.jpg" alt="Фиолетовое кресло" class="about__img about__img--small">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="about__column about__column--right">
|
|
||||||
<img src="img/диван_1.jpg" alt="Белый диван с подушками" class="about__img about__img--large">
|
|
||||||
<p class="about__caption">Наша сеть включает 30+ российских фабрик, отобранных по строгим стандартам качества. Мы сотрудничаем исключительно с лидерами рынка, чья продукция доказала свое превосходство временем.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="solutions">
|
|
||||||
<div class="container">
|
|
||||||
<div class="solutions-slider">
|
|
||||||
<div class="solutions-slider__slides">
|
|
||||||
<div class="solutions-slider__slide">
|
|
||||||
<img src="img/слайдер_1.jpg" class="solution-img" alt="Готовое решение для гостиной">
|
|
||||||
<div class="solution-text-overlay">
|
|
||||||
<h2>ГОТОВОЕ РЕШЕНИЕ<br>ДЛЯ ВАШЕЙ ГОСТИНОЙ</h2> <br>
|
|
||||||
<p>УСПЕЙТЕ ЗАКАЗАТЬ СЕЙЧАС</p>
|
|
||||||
</div>
|
|
||||||
<a href="#" class="solution-image-link">Подробнее</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="solutions-slider__slide">
|
|
||||||
<img src="img/слайдер_6.jpg" class="solution-img" alt="Готовое решение для спальни">
|
|
||||||
<div class="solution-text-overlay">
|
|
||||||
<h2>ГОТОВОЕ РЕШЕНИЕ<br>ДЛЯ ВАШЕЙ СПАЛЬНИ</h2> <br>
|
|
||||||
<p>УСПЕЙТЕ ЗАКАЗАТЬ СЕЙЧАС</p>
|
|
||||||
</div>
|
|
||||||
<a href="#" class="solution-image-link">Подробнее</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="stats">
|
|
||||||
<div class="container">
|
|
||||||
<div class="stats__items">
|
|
||||||
<div class="stat-item">
|
|
||||||
<div class="stat-number">10+</div>
|
|
||||||
<div class="stat-label">Лет работы</div>
|
|
||||||
</div>
|
|
||||||
<div class="stat-item">
|
|
||||||
<div class="stat-number">30 000+</div>
|
|
||||||
<div class="stat-label">Довольных покупателей</div>
|
|
||||||
</div>
|
|
||||||
<div class="stat-item">
|
|
||||||
<div class="stat-number">4500+</div>
|
|
||||||
<div class="stat-label">Реализованных заказов</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="faq" id="faq">
|
|
||||||
<div class="container">
|
|
||||||
<h2>ОТВЕТЫ НА ВОПРОСЫ</h2>
|
|
||||||
<div class="faq__items">
|
|
||||||
<div class="faq-item">
|
|
||||||
<span class="number-circle">1</span>
|
|
||||||
<div class="faq-item__content">
|
|
||||||
<h4>Сколько времени занимает доставка?</h4>
|
|
||||||
<p>Доставка готовых позиций занимает 1-3 дня. Мебель на заказ изготавливается от 14 до 45 рабочих дней, в зависимости от сложности. Точные сроки озвучит ваш менеджер при оформлении заказа.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="faq-item">
|
|
||||||
<span class="number-circle">2</span>
|
|
||||||
<div class="faq-item__content">
|
|
||||||
<h4>Нужно ли вносить предоплату?</h4>
|
|
||||||
<p>Да, для запуска заказа в производство необходима предоплата в размере 50-70% от стоимости, в зависимости от изделия. Оставшаяся сумма оплачивается при доставке и приемке мебели.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="faq-item">
|
|
||||||
<span class="number-circle">3</span>
|
|
||||||
<div class="faq-item__content">
|
|
||||||
<h4>Предоставляется ли рассрочка или кредит?</h4>
|
|
||||||
<p>Да, мы сотрудничаем с несколькими банками и предлагаем рассрочку на 6 или 12 месяцев без первоначального взноса, а также кредит на более длительный срок. Все условия уточняйте у вашего менеджера.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="faq-item">
|
|
||||||
<span class="number-circle">4</span>
|
|
||||||
<div class="faq-item__content">
|
|
||||||
<h4>Что делать, если мебель пришла с дефектом?</h4>
|
|
||||||
<p>В этом случае необходимо в течение 7 дней со дня доставки сообщить нам о проблеме, прислать фото/видео дефекта. Мы оперативно решим вопрос о бесплатной замене или ремонте изделия.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<button class="btn primary-btn">Задать вопрос</button>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</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="javascript:void(0)" onclick="checkAuth('catalog.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.html#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>
|
|
||||||
<!-- Статус пользователя (будет показан после авторизации) -->
|
|
||||||
<div class="user-status-overlay" id="userStatus" style="display: none; position: fixed; top: 20px; right: 20px; z-index: 1000; background: white; padding: 10px 15px; border-radius: 5px; box-shadow: 0 2px 10px rgba(0,0,0,0.1);">
|
|
||||||
<div style="display: flex; align-items: center; gap: 10px;">
|
|
||||||
<i class="fas fa-user" id="statusIcon"></i>
|
|
||||||
<div>
|
|
||||||
<div id="statusText" style="font-weight: bold;"></div>
|
|
||||||
<div id="userEmail" style="font-size: 12px; color: #666;"></div>
|
|
||||||
</div>
|
|
||||||
<button onclick="logout()" style="background: none; border: none; color: #666; cursor: pointer; margin-left: 10px;">
|
|
||||||
<i class="fas fa-sign-out-alt"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Модальное окно для быстрого входа -->
|
|
||||||
<div class="quick-login-modal" id="quickLoginModal" style="display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); z-index: 2000; align-items: center; justify-content: center;">
|
|
||||||
<div style="background: white; padding: 30px; border-radius: 10px; width: 90%; max-width: 400px; text-align: center;">
|
|
||||||
<h3 style="margin-bottom: 20px; color: #453227;">
|
|
||||||
<i class="fas fa-sign-in-alt"></i> Быстрый вход
|
|
||||||
</h3>
|
|
||||||
<div style="margin-bottom: 20px;">
|
|
||||||
<button onclick="quickLogin('admin')" style="width: 100%; padding: 12px; background: #617365; color: white; border: none; border-radius: 4px; margin-bottom: 10px; cursor: pointer;">
|
|
||||||
<i class="fas fa-user-shield"></i> Войти как Администратор
|
|
||||||
</button>
|
|
||||||
<button onclick="quickLogin('user')" style="width: 100%; padding: 12px; background: #28a745; color: white; border: none; border-radius: 4px; cursor: pointer;">
|
|
||||||
<i class="fas fa-user"></i> Войти как Пользователь
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div style="font-size: 12px; color: #666; margin-top: 15px;">
|
|
||||||
Для полного функционала перейдите на страницу входа
|
|
||||||
</div>
|
|
||||||
<div style="margin-top: 20px; display: flex; gap: 10px; justify-content: center;">
|
|
||||||
<a href="вход.html" class="btn primary-btn" style="padding: 8px 15px; font-size: 14px;">
|
|
||||||
Полный вход
|
|
||||||
</a>
|
|
||||||
<button onclick="hideQuickLogin()" class="btn" style="padding: 8px 15px; font-size: 14px; background: #6c757d; color: white;">
|
|
||||||
Отмена
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
// Проверка авторизации
|
|
||||||
function checkAuth(redirectUrl) {
|
|
||||||
// Просто перенаправляем на страницу с action
|
|
||||||
window.location.href = 'cite_mebel.php?action=go_to_catalog';
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Проверяем статус при загрузке
|
|
||||||
$(document).ready(function() {
|
|
||||||
// Синхронизируем PHP сессию с localStorage
|
|
||||||
<?php if (isset($_SESSION['isLoggedIn']) && $_SESSION['isLoggedIn'] === true): ?>
|
|
||||||
if (localStorage.getItem('isLoggedIn') !== 'true') {
|
|
||||||
localStorage.setItem('isLoggedIn', 'true');
|
|
||||||
localStorage.setItem('user_email', '<?= addslashes($_SESSION['user_email'] ?? '') ?>');
|
|
||||||
localStorage.setItem('isAdmin', '<?= isset($_SESSION['isAdmin']) && $_SESSION['isAdmin'] ? 'true' : 'false' ?>');
|
|
||||||
}
|
|
||||||
<?php endif; ?>
|
|
||||||
|
|
||||||
// Блокируем все ссылки на каталог если не авторизован
|
|
||||||
const isLoggedIn = localStorage.getItem('isLoggedIn') === 'true';
|
|
||||||
|
|
||||||
if (!isLoggedIn) {
|
|
||||||
// Находим ВСЕ ссылки на каталог
|
|
||||||
$('a[href*="catalog"]').each(function() {
|
|
||||||
const originalHref = $(this).attr('href');
|
|
||||||
if (originalHref && originalHref.includes('catalog')) {
|
|
||||||
$(this).attr('href', 'javascript:void(0)');
|
|
||||||
$(this).addClass('link-disabled');
|
|
||||||
$(this).click(function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
checkAuth(originalHref);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Блокируем поиск
|
|
||||||
$('#searchInput').prop('disabled', true).attr('placeholder', 'Войдите для поиска');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Быстрый вход для тестирования (можно удалить в продакшене)
|
|
||||||
function quickLogin(role) {
|
|
||||||
let email, password;
|
|
||||||
|
|
||||||
if (role === 'admin') {
|
|
||||||
email = 'admin@aeterna.ru';
|
|
||||||
password = 'admin123';
|
|
||||||
} else {
|
|
||||||
email = 'user@test.com';
|
|
||||||
password = 'user123';
|
|
||||||
}
|
|
||||||
|
|
||||||
$.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
|
|
||||||
localStorage.setItem('isLoggedIn', 'true');
|
|
||||||
localStorage.setItem('user_email', email);
|
|
||||||
localStorage.setItem('isAdmin', (role === 'admin').toString());
|
|
||||||
|
|
||||||
alert('Вы вошли как ' + (role === 'admin' ? 'администратор' : 'пользователь'));
|
|
||||||
location.reload();
|
|
||||||
}
|
|
||||||
} catch(e) {
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
$(document).ready(function() {
|
|
||||||
// Открытие/закрытие меню профиля
|
|
||||||
$('.user-profile-toggle').click(function(e) {
|
|
||||||
e.stopPropagation();
|
|
||||||
$('.user-profile-menu').toggle();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Закрытие меню при клике вне его
|
|
||||||
$(document).click(function() {
|
|
||||||
$('.user-profile-menu').hide();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Обработка выхода
|
|
||||||
$('.logout-link').click(function(e) {
|
|
||||||
if (!confirm('Вы действительно хотите выйти?')) {
|
|
||||||
e.preventDefault();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Обновляем время входа при загрузке страницы
|
|
||||||
updateLoginTime();
|
|
||||||
});
|
|
||||||
|
|
||||||
function updateLoginTime() {
|
|
||||||
// Можно добавить AJAX запрос для обновления времени последнего входа
|
|
||||||
// или использовать время из сессии
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<!-- Кнопки быстрого тестирования -->
|
|
||||||
<div style="position: fixed; bottom: 20px; right: 20px; z-index: 1000; background: white; padding: 10px; border-radius: 5px; box-shadow: 0 2px 10px rgba(0,0,0,0.1);">
|
|
||||||
<h4 style="margin: 0 0 10px 0; font-size: 14px; color: #453227;">Быстрый вход:</h4>
|
|
||||||
<button onclick="window.location.href='профиль.php?quick_login=admin'" style="background: #617365; color: white; border: none; padding: 8px 15px; border-radius: 4px; cursor: pointer; margin: 5px; width: 100%;">
|
|
||||||
<i class="fas fa-user-shield"></i> Войти как Админ
|
|
||||||
</button>
|
|
||||||
<button onclick="window.location.href='профиль.php?quick_login=user'" style="background: #28a745; color: white; border: none; padding: 8px 15px; border-radius: 4px; cursor: pointer; margin: 5px; width: 100%;">
|
|
||||||
<i class="fas fa-user"></i> Войти как Пользователь
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,51 +1,38 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
/**
|
|
||||||
* Конфигурация приложения
|
|
||||||
*/
|
|
||||||
return [
|
return [
|
||||||
// Название приложения
|
|
||||||
'name' => 'AETERNA',
|
'name' => 'AETERNA',
|
||||||
|
'debug' => getenv('APP_DEBUG') ?: true,
|
||||||
// Режим отладки
|
'url' => getenv('APP_URL') ?: 'http://localhost:8080',
|
||||||
'debug' => true,
|
'base_path' => '',
|
||||||
|
|
||||||
// URL приложения
|
|
||||||
'url' => 'http://localhost',
|
|
||||||
|
|
||||||
// Базовый путь (для Docker)
|
|
||||||
'base_path' => '/cite_practica',
|
|
||||||
|
|
||||||
// Часовой пояс
|
|
||||||
'timezone' => 'Europe/Moscow',
|
'timezone' => 'Europe/Moscow',
|
||||||
|
|
||||||
// Локаль
|
|
||||||
'locale' => 'ru_RU',
|
'locale' => 'ru_RU',
|
||||||
|
|
||||||
// Email администраторов (получают права администратора при регистрации)
|
|
||||||
'admin_emails' => [
|
'admin_emails' => [
|
||||||
'admin@aeterna.ru',
|
'admin@aeterna.ru',
|
||||||
'administrator@aeterna.ru',
|
'administrator@aeterna.ru',
|
||||||
'aeterna@mail.ru'
|
'aeterna@mail.ru'
|
||||||
],
|
],
|
||||||
|
|
||||||
// Настройки сессии
|
|
||||||
'session' => [
|
'session' => [
|
||||||
'lifetime' => 120, // минуты
|
'lifetime' => 120,
|
||||||
'secure' => false,
|
'secure' => false,
|
||||||
'http_only' => true
|
'http_only' => true
|
||||||
],
|
],
|
||||||
|
|
||||||
// Настройки доставки
|
|
||||||
'delivery' => [
|
'delivery' => [
|
||||||
'default_price' => 2000,
|
'default_price' => 2000,
|
||||||
'free_from' => 50000, // Бесплатная доставка от этой суммы
|
'free_from' => 50000,
|
||||||
],
|
],
|
||||||
|
|
||||||
// Промокоды
|
|
||||||
'promo_codes' => [
|
'promo_codes' => [
|
||||||
'SALE10' => ['type' => 'percent', 'value' => 10],
|
'SALE10' => ['type' => 'percent', 'value' => 10],
|
||||||
'FREE' => ['type' => 'free_delivery', 'value' => 0],
|
'FREE' => ['type' => 'free_delivery', 'value' => 0],
|
||||||
|
],
|
||||||
|
|
||||||
|
'paths' => [
|
||||||
|
'storage' => 'storage',
|
||||||
|
'uploads' => 'storage/uploads',
|
||||||
|
'assets' => 'public/assets',
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@@ -1,14 +1,17 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
/**
|
|
||||||
* Конфигурация базы данных
|
|
||||||
*/
|
|
||||||
return [
|
return [
|
||||||
'driver' => 'pgsql',
|
'driver' => getenv('DB_DRIVER') ?: 'pgsql',
|
||||||
'host' => '185.130.224.177',
|
'host' => getenv('DB_HOST') ?: '185.130.224.177',
|
||||||
'port' => '5481',
|
'port' => getenv('DB_PORT') ?: '5481',
|
||||||
'database' => 'postgres',
|
'database' => getenv('DB_DATABASE') ?: 'postgres',
|
||||||
'username' => 'admin',
|
'username' => getenv('DB_USERNAME') ?: 'admin',
|
||||||
'password' => '38feaad2840ccfda0e71243a6faaecfd',
|
'password' => getenv('DB_PASSWORD') ?: '38feaad2840ccfda0e71243a6faaecfd',
|
||||||
'charset' => 'utf8',
|
'charset' => 'utf8',
|
||||||
|
|
||||||
|
'options' => [
|
||||||
|
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||||
|
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||||
|
PDO::ATTR_EMULATE_PREPARES => false,
|
||||||
|
]
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,46 +1,32 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
/**
|
|
||||||
* Определение маршрутов приложения
|
|
||||||
*
|
|
||||||
* @var \App\Core\Router $router
|
|
||||||
*/
|
|
||||||
|
|
||||||
// ========== Главная страница ==========
|
|
||||||
$router->get('/', 'HomeController', 'index');
|
$router->get('/', 'HomeController', 'index');
|
||||||
$router->get('/home', 'HomeController', 'index');
|
$router->get('/home', 'HomeController', 'index');
|
||||||
|
|
||||||
// ========== Авторизация ==========
|
|
||||||
$router->get('/login', 'AuthController', 'loginForm');
|
$router->get('/login', 'AuthController', 'loginForm');
|
||||||
$router->post('/login', 'AuthController', 'login');
|
$router->post('/login', 'AuthController', 'login');
|
||||||
$router->get('/register', 'AuthController', 'registerForm');
|
$router->get('/register', 'AuthController', 'registerForm');
|
||||||
$router->post('/register', 'AuthController', 'register');
|
$router->post('/register', 'AuthController', 'register');
|
||||||
$router->get('/logout', 'AuthController', 'logout');
|
$router->get('/logout', 'AuthController', 'logout');
|
||||||
|
|
||||||
// ========== Каталог и товары ==========
|
|
||||||
$router->get('/catalog', 'ProductController', 'catalog');
|
$router->get('/catalog', 'ProductController', 'catalog');
|
||||||
$router->get('/product/{id}', 'ProductController', 'show');
|
$router->get('/product/{id}', 'ProductController', 'show');
|
||||||
|
|
||||||
// ========== Корзина ==========
|
|
||||||
$router->get('/cart', 'CartController', 'index');
|
$router->get('/cart', 'CartController', 'index');
|
||||||
$router->post('/cart/add', 'CartController', 'add');
|
$router->post('/cart/add', 'CartController', 'add');
|
||||||
$router->post('/cart/update', 'CartController', 'update');
|
$router->post('/cart/update', 'CartController', 'update');
|
||||||
$router->post('/cart/remove', 'CartController', 'remove');
|
$router->post('/cart/remove', 'CartController', 'remove');
|
||||||
$router->get('/cart/count', 'CartController', 'count');
|
$router->get('/cart/count', 'CartController', 'count');
|
||||||
|
|
||||||
// ========== Заказы ==========
|
|
||||||
$router->get('/checkout', 'OrderController', 'checkout');
|
$router->get('/checkout', 'OrderController', 'checkout');
|
||||||
$router->post('/order', 'OrderController', 'create');
|
$router->post('/order', 'OrderController', 'create');
|
||||||
|
|
||||||
// ========== Статические страницы ==========
|
|
||||||
$router->get('/services', 'PageController', 'services');
|
$router->get('/services', 'PageController', 'services');
|
||||||
$router->get('/delivery', 'PageController', 'delivery');
|
$router->get('/delivery', 'PageController', 'delivery');
|
||||||
$router->get('/warranty', 'PageController', 'warranty');
|
$router->get('/warranty', 'PageController', 'warranty');
|
||||||
|
|
||||||
// ========== Админ-панель ==========
|
|
||||||
$router->get('/admin', 'AdminController', 'dashboard');
|
$router->get('/admin', 'AdminController', 'dashboard');
|
||||||
|
|
||||||
// Управление товарами
|
|
||||||
$router->get('/admin/products', 'AdminController', 'products');
|
$router->get('/admin/products', 'AdminController', 'products');
|
||||||
$router->get('/admin/products/add', 'AdminController', 'addProduct');
|
$router->get('/admin/products/add', 'AdminController', 'addProduct');
|
||||||
$router->post('/admin/products/add', 'AdminController', 'storeProduct');
|
$router->post('/admin/products/add', 'AdminController', 'storeProduct');
|
||||||
@@ -48,7 +34,6 @@ $router->get('/admin/products/edit/{id}', 'AdminController', 'editProduct');
|
|||||||
$router->post('/admin/products/edit/{id}', 'AdminController', 'updateProduct');
|
$router->post('/admin/products/edit/{id}', 'AdminController', 'updateProduct');
|
||||||
$router->post('/admin/products/delete/{id}', 'AdminController', 'deleteProduct');
|
$router->post('/admin/products/delete/{id}', 'AdminController', 'deleteProduct');
|
||||||
|
|
||||||
// Управление категориями
|
|
||||||
$router->get('/admin/categories', 'AdminController', 'categories');
|
$router->get('/admin/categories', 'AdminController', 'categories');
|
||||||
$router->get('/admin/categories/add', 'AdminController', 'addCategory');
|
$router->get('/admin/categories/add', 'AdminController', 'addCategory');
|
||||||
$router->post('/admin/categories/add', 'AdminController', 'storeCategory');
|
$router->post('/admin/categories/add', 'AdminController', 'storeCategory');
|
||||||
@@ -56,11 +41,8 @@ $router->get('/admin/categories/edit/{id}', 'AdminController', 'editCategory');
|
|||||||
$router->post('/admin/categories/edit/{id}', 'AdminController', 'updateCategory');
|
$router->post('/admin/categories/edit/{id}', 'AdminController', 'updateCategory');
|
||||||
$router->post('/admin/categories/delete/{id}', 'AdminController', 'deleteCategory');
|
$router->post('/admin/categories/delete/{id}', 'AdminController', 'deleteCategory');
|
||||||
|
|
||||||
// Управление заказами
|
|
||||||
$router->get('/admin/orders', 'AdminController', 'orders');
|
$router->get('/admin/orders', 'AdminController', 'orders');
|
||||||
$router->get('/admin/orders/{id}', 'AdminController', 'orderDetails');
|
$router->get('/admin/orders/{id}', 'AdminController', 'orderDetails');
|
||||||
$router->post('/admin/orders/{id}/status', 'AdminController', 'updateOrderStatus');
|
$router->post('/admin/orders/{id}/status', 'AdminController', 'updateOrderStatus');
|
||||||
|
|
||||||
// Управление пользователями
|
|
||||||
$router->get('/admin/users', 'AdminController', 'users');
|
$router->get('/admin/users', 'AdminController', 'users');
|
||||||
|
|
||||||
|
|||||||
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();
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
@@ -11,6 +11,8 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
- APACHE_RUN_USER=www-data
|
- APACHE_RUN_USER=www-data
|
||||||
- APACHE_RUN_GROUP=www-data
|
- APACHE_RUN_GROUP=www-data
|
||||||
|
- APP_ENV=development
|
||||||
|
- APP_DEBUG=true
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
networks:
|
networks:
|
||||||
- aeterna-network
|
- aeterna-network
|
||||||
@@ -18,4 +20,3 @@ services:
|
|||||||
networks:
|
networks:
|
||||||
aeterna-network:
|
aeterna-network:
|
||||||
driver: bridge
|
driver: bridge
|
||||||
|
|
||||||
|
|||||||
@@ -1,20 +1,16 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
# Включаем mod_rewrite
|
a2enmod rewrite headers expires 2>/dev/null || true
|
||||||
a2enmod rewrite 2>/dev/null || true
|
|
||||||
|
|
||||||
# Устанавливаем права только на нужные директории, исключая .git
|
|
||||||
find /var/www/html -maxdepth 1 -type d ! -name '.git' -exec chown -R www-data:www-data {} \; 2>/dev/null || true
|
|
||||||
find /var/www/html -maxdepth 1 -type f -exec chown www-data:www-data {} \; 2>/dev/null || true
|
|
||||||
|
|
||||||
# Устанавливаем права на ключевые директории
|
|
||||||
chown -R www-data:www-data /var/www/html/app 2>/dev/null || true
|
chown -R www-data:www-data /var/www/html/app 2>/dev/null || true
|
||||||
chown -R www-data:www-data /var/www/html/config 2>/dev/null || true
|
chown -R www-data:www-data /var/www/html/config 2>/dev/null || true
|
||||||
chown -R www-data:www-data /var/www/html/public 2>/dev/null || true
|
chown -R www-data:www-data /var/www/html/public 2>/dev/null || true
|
||||||
chown -R www-data:www-data /var/www/html/uploads 2>/dev/null || true
|
chown -R www-data:www-data /var/www/html/storage 2>/dev/null || true
|
||||||
|
|
||||||
echo "Apache configured successfully"
|
chmod -R 775 /var/www/html/storage 2>/dev/null || true
|
||||||
|
|
||||||
|
echo "AETERNA - Apache configured successfully"
|
||||||
|
echo "DocumentRoot: /var/www/html/public"
|
||||||
|
|
||||||
# Запускаем Apache в foreground режиме
|
|
||||||
exec apache2-foreground
|
exec apache2-foreground
|
||||||
|
|||||||
@@ -1,25 +1,29 @@
|
|||||||
<VirtualHost *:80>
|
<VirtualHost *:80>
|
||||||
ServerAdmin admin@aeterna.local
|
ServerAdmin admin@aeterna.local
|
||||||
DocumentRoot /var/www/html
|
DocumentRoot /var/www/html/public
|
||||||
ServerName localhost
|
ServerName localhost
|
||||||
|
|
||||||
<Directory /var/www/html>
|
<Directory /var/www/html/public>
|
||||||
Options Indexes FollowSymLinks
|
Options -Indexes +FollowSymLinks
|
||||||
AllowOverride All
|
AllowOverride All
|
||||||
Require all granted
|
Require all granted
|
||||||
</Directory>
|
</Directory>
|
||||||
|
|
||||||
# Логирование
|
Alias /uploads /var/www/html/storage/uploads
|
||||||
|
<Directory /var/www/html/storage/uploads>
|
||||||
|
Options -Indexes
|
||||||
|
AllowOverride None
|
||||||
|
Require all granted
|
||||||
|
</Directory>
|
||||||
|
|
||||||
ErrorLog ${APACHE_LOG_DIR}/error.log
|
ErrorLog ${APACHE_LOG_DIR}/error.log
|
||||||
CustomLog ${APACHE_LOG_DIR}/access.log combined
|
CustomLog ${APACHE_LOG_DIR}/access.log combined
|
||||||
|
|
||||||
# Кодировка по умолчанию
|
|
||||||
AddDefaultCharset UTF-8
|
AddDefaultCharset UTF-8
|
||||||
|
|
||||||
# Типы файлов
|
|
||||||
AddType text/css .css
|
AddType text/css .css
|
||||||
AddType text/less .less
|
AddType text/less .less
|
||||||
AddType text/javascript .js
|
AddType text/javascript .js
|
||||||
AddType image/svg+xml .svg
|
AddType image/svg+xml .svg
|
||||||
|
AddType image/webp .webp
|
||||||
</VirtualHost>
|
</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>";
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
62
get_cart.php
@@ -1,62 +0,0 @@
|
|||||||
<?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()
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
<?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]);
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
<?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()]);
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
@@ -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/1_1.jpg
|
Before Width: | Height: | Size: 11 KiB |
BIN
img/2_2.jpg
|
Before Width: | Height: | Size: 42 KiB |
BIN
img/3_3.jpg
|
Before Width: | Height: | Size: 51 KiB |
BIN
img/5_5.jpg
|
Before Width: | Height: | Size: 15 KiB |
BIN
img/6_6.jpg
|
Before Width: | Height: | Size: 42 KiB |
BIN
img/7_7.jpg
|
Before Width: | Height: | Size: 36 KiB |
@@ -1,62 +0,0 @@
|
|||||||
|
|
||||||
.error-message {
|
|
||||||
color: #ff0000;
|
|
||||||
font-size: 12px;
|
|
||||||
margin-top: 5px;
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form__input.error {
|
|
||||||
border-color: #ff0000;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form__group {
|
|
||||||
position: relative;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Стили для сообщений внизу страницы */
|
|
||||||
.page-messages {
|
|
||||||
position: fixed;
|
|
||||||
bottom: 20px;
|
|
||||||
left: 50%;
|
|
||||||
transform: translateX(-50%);
|
|
||||||
z-index: 1000;
|
|
||||||
width: 90%;
|
|
||||||
max-width: 500px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message {
|
|
||||||
padding: 15px;
|
|
||||||
margin: 10px 0;
|
|
||||||
border-radius: 5px;
|
|
||||||
text-align: center;
|
|
||||||
font-weight: bold;
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message.error {
|
|
||||||
background-color: #ffebee;
|
|
||||||
color: #c62828;
|
|
||||||
border: 1px solid #ffcdd2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message.success {
|
|
||||||
background-color: #e8f5e9;
|
|
||||||
color: #453227;
|
|
||||||
border: 1px solid #c8e6c9;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message.warning {
|
|
||||||
background-color: #fff3e0;
|
|
||||||
color: #ef6c00;
|
|
||||||
border: 1px solid #ffe0b2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.privacy-error {
|
|
||||||
color: #ff0000;
|
|
||||||
font-size: 12px;
|
|
||||||
margin-top: 5px;
|
|
||||||
display: none;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
BIN
img2/1.jpg
|
Before Width: | Height: | Size: 78 KiB |
BIN
img2/5.jpg
|
Before Width: | Height: | Size: 90 KiB |
BIN
img2/9_9.jpg
|
Before Width: | Height: | Size: 16 KiB |
BIN
img2/chair.PNG
|
Before Width: | Height: | Size: 144 KiB |
BIN
img2/диван.jpg
|
Before Width: | Height: | Size: 148 KiB |
BIN
img2/диван_1.jpg
|
Before Width: | Height: | Size: 127 KiB |
BIN
img2/кресло.jpg
|
Before Width: | Height: | Size: 228 KiB |
|
Before Width: | Height: | Size: 294 KiB |
|
Before Width: | Height: | Size: 187 KiB |
|
Before Width: | Height: | Size: 188 KiB |
|
Before Width: | Height: | Size: 110 KiB |
|
Before Width: | Height: | Size: 106 KiB |
|
Before Width: | Height: | Size: 83 KiB |
|
Before Width: | Height: | Size: 306 KiB |
BIN
img2/спальня.jpg
|
Before Width: | Height: | Size: 151 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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
?>
|
|
||||||