Compare commits
8 Commits
b87450c12b
...
fix-auth-c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
07289608e5 | ||
|
|
f4f57bd153 | ||
|
|
fe288b3caf | ||
|
|
6a866dffe3 | ||
|
|
a7282f7363 | ||
|
|
8a93cf8657 | ||
|
|
474fe41d41 | ||
|
|
f597029525 |
8
.dockerignore
Normal file
@@ -0,0 +1,8 @@
|
||||
.git
|
||||
.gitignore
|
||||
.DS_Store
|
||||
node_modules
|
||||
*.log
|
||||
.env
|
||||
.env.local
|
||||
|
||||
10
Dockerfile
Normal file
@@ -0,0 +1,10 @@
|
||||
FROM php:8.2-apache
|
||||
|
||||
RUN apt-get update && apt-get install -y libpq-dev \
|
||||
&& docker-php-ext-install pdo pdo_pgsql \
|
||||
&& apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN a2enmod rewrite headers alias
|
||||
|
||||
WORKDIR /var/www/html
|
||||
|
||||
710
admin/index.php
@@ -1,710 +0,0 @@
|
||||
<?php
|
||||
// admin_panel.php - ПОЛНОСТЬЮ ИСПРАВЛЕННАЯ ВЕРСИЯ
|
||||
session_start();
|
||||
require_once __DIR__ . '/../config/database.php';
|
||||
|
||||
// Включаем отладку ошибок
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', 1);
|
||||
|
||||
// Проверка прав администратора
|
||||
if (!isset($_SESSION['isAdmin']) || $_SESSION['isAdmin'] !== true) {
|
||||
echo "<script>alert('Требуется авторизация администратора'); window.location.href = 'login.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="admin_panel.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 ($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>
|
||||
@@ -1,116 +0,0 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once __DIR__ . '/../config/database.php';
|
||||
|
||||
if (!isset($_SESSION['isLoggedIn']) || $_SESSION['isLoggedIn'] !== true) {
|
||||
echo json_encode(['success' => false, 'message' => 'Требуется авторизация']);
|
||||
exit();
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_POST['product_id'])) {
|
||||
$product_id = intval($_POST['product_id']);
|
||||
$quantity = intval($_POST['quantity'] ?? 1);
|
||||
$user_id = $_SESSION['user_id'] ?? 0;
|
||||
|
||||
if ($user_id == 0) {
|
||||
echo json_encode(['success' => false, 'message' => 'Пользователь не найден']);
|
||||
exit();
|
||||
}
|
||||
|
||||
$db = Database::getInstance()->getConnection();
|
||||
|
||||
try {
|
||||
// Проверяем наличие товара на складе
|
||||
$checkStock = $db->prepare("
|
||||
SELECT stock_quantity, name, price
|
||||
FROM products
|
||||
WHERE product_id = ? AND is_available = TRUE
|
||||
");
|
||||
$checkStock->execute([$product_id]);
|
||||
$product = $checkStock->fetch();
|
||||
|
||||
if (!$product) {
|
||||
echo json_encode(['success' => false, 'message' => 'Товар не найден']);
|
||||
exit();
|
||||
}
|
||||
|
||||
if ($product['stock_quantity'] < $quantity) {
|
||||
echo json_encode(['success' => false, 'message' => 'Недостаточно товара на складе']);
|
||||
exit();
|
||||
}
|
||||
|
||||
// Проверяем, есть ли товар уже в корзине пользователя
|
||||
$checkCart = $db->prepare("
|
||||
SELECT cart_id, quantity
|
||||
FROM cart
|
||||
WHERE user_id = ? AND product_id = ?
|
||||
");
|
||||
$checkCart->execute([$user_id, $product_id]);
|
||||
$cartItem = $checkCart->fetch();
|
||||
|
||||
if ($cartItem) {
|
||||
// Обновляем количество
|
||||
$newQuantity = $cartItem['quantity'] + $quantity;
|
||||
|
||||
// Проверяем общее количество
|
||||
if ($newQuantity > $product['stock_quantity']) {
|
||||
echo json_encode(['success' => false, 'message' => 'Превышено доступное количество']);
|
||||
exit();
|
||||
}
|
||||
|
||||
$updateStmt = $db->prepare("
|
||||
UPDATE cart
|
||||
SET quantity = ?, updated_at = CURRENT_TIMESTAMP
|
||||
WHERE cart_id = ?
|
||||
");
|
||||
$updateStmt->execute([$newQuantity, $cartItem['cart_id']]);
|
||||
} else {
|
||||
// Добавляем новый товар
|
||||
$insertStmt = $db->prepare("
|
||||
INSERT INTO cart (user_id, product_id, quantity)
|
||||
VALUES (?, ?, ?)
|
||||
");
|
||||
$insertStmt->execute([$user_id, $product_id, $quantity]);
|
||||
}
|
||||
|
||||
// Обновляем сессию
|
||||
if (!isset($_SESSION['cart'])) {
|
||||
$_SESSION['cart'] = [];
|
||||
}
|
||||
|
||||
if (isset($_SESSION['cart'][$product_id])) {
|
||||
$_SESSION['cart'][$product_id]['quantity'] += $quantity;
|
||||
} else {
|
||||
$_SESSION['cart'][$product_id] = [
|
||||
'quantity' => $quantity,
|
||||
'name' => $product['name'],
|
||||
'price' => $product['price'],
|
||||
'added_at' => time()
|
||||
];
|
||||
}
|
||||
|
||||
// Получаем общее количество товаров в корзине
|
||||
$cartCountStmt = $db->prepare("
|
||||
SELECT SUM(quantity) as total
|
||||
FROM cart
|
||||
WHERE user_id = ?
|
||||
");
|
||||
$cartCountStmt->execute([$user_id]);
|
||||
$cart_count = $cartCountStmt->fetchColumn() ?: 0;
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'cart_count' => $cart_count,
|
||||
'message' => 'Товар добавлен в корзину'
|
||||
]);
|
||||
|
||||
} catch (PDOException $e) {
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'message' => 'Ошибка базы данных: ' . $e->getMessage()
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
echo json_encode(['success' => false, 'message' => 'Неверный запрос']);
|
||||
}
|
||||
?>
|
||||
66
api/auth.php
@@ -1,66 +0,0 @@
|
||||
<?php
|
||||
// login_handler.php
|
||||
session_start();
|
||||
require_once __DIR__ . '/../config/database.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$email = $_POST['email'] ?? '';
|
||||
$password = $_POST['password'] ?? '';
|
||||
|
||||
if (empty($email) || empty($password)) {
|
||||
echo json_encode(['success' => false, 'message' => 'Заполните все поля']);
|
||||
exit();
|
||||
}
|
||||
|
||||
$db = Database::getInstance()->getConnection();
|
||||
|
||||
try {
|
||||
// Проверяем пользователя в базе данных
|
||||
$stmt = $db->prepare("
|
||||
SELECT user_id, email, password_hash, full_name, phone, city, is_admin, is_active
|
||||
FROM users
|
||||
WHERE email = ?
|
||||
");
|
||||
$stmt->execute([$email]);
|
||||
$user = $stmt->fetch();
|
||||
|
||||
if (!$user) {
|
||||
echo json_encode(['success' => false, 'message' => 'Пользователь не найден']);
|
||||
exit();
|
||||
}
|
||||
|
||||
if (!$user['is_active']) {
|
||||
echo json_encode(['success' => false, 'message' => 'Аккаунт заблокирован']);
|
||||
exit();
|
||||
}
|
||||
|
||||
// Проверяем пароль
|
||||
if (!password_verify($password, $user['password_hash'])) {
|
||||
echo json_encode(['success' => false, 'message' => 'Неверный пароль']);
|
||||
exit();
|
||||
}
|
||||
|
||||
// Сохраняем в сессию
|
||||
$_SESSION['user_id'] = $user['user_id'];
|
||||
$_SESSION['user_email'] = $user['email'];
|
||||
$_SESSION['full_name'] = $user['full_name'];
|
||||
$_SESSION['user_phone'] = $user['phone'] ?? '';
|
||||
$_SESSION['user_city'] = $user['city'] ?? '';
|
||||
$_SESSION['isLoggedIn'] = true;
|
||||
$_SESSION['isAdmin'] = (bool)$user['is_admin'];
|
||||
$_SESSION['login_time'] = time();
|
||||
|
||||
// Обновляем время последнего входа
|
||||
$updateStmt = $db->prepare("UPDATE users SET last_login = CURRENT_TIMESTAMP WHERE user_id = ?");
|
||||
$updateStmt->execute([$user['user_id']]);
|
||||
|
||||
echo json_encode(['success' => true, 'redirect' => 'catalog.php']);
|
||||
|
||||
} catch (PDOException $e) {
|
||||
echo json_encode(['success' => false, 'message' => 'Ошибка базы данных']);
|
||||
}
|
||||
|
||||
} else {
|
||||
echo json_encode(['success' => false, 'message' => 'Неверный запрос']);
|
||||
}
|
||||
?>
|
||||
134
api/cart.php
@@ -1,134 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* API для работы с корзиной
|
||||
* Эндпоинты: add, update, remove, get, count
|
||||
*/
|
||||
session_start();
|
||||
require_once __DIR__ . '/../config/database.php';
|
||||
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
// Проверка авторизации
|
||||
if (!isset($_SESSION['isLoggedIn']) || $_SESSION['isLoggedIn'] !== true) {
|
||||
echo json_encode(['success' => false, 'message' => 'Требуется авторизация']);
|
||||
exit();
|
||||
}
|
||||
|
||||
$userId = $_SESSION['user_id'] ?? 0;
|
||||
$action = $_GET['action'] ?? $_POST['action'] ?? '';
|
||||
|
||||
$db = Database::getInstance()->getConnection();
|
||||
|
||||
try {
|
||||
switch ($action) {
|
||||
case 'add':
|
||||
$productId = (int)($_POST['product_id'] ?? 0);
|
||||
$quantity = (int)($_POST['quantity'] ?? 1);
|
||||
|
||||
if ($productId <= 0) {
|
||||
echo json_encode(['success' => false, 'message' => 'Неверный ID товара']);
|
||||
exit();
|
||||
}
|
||||
|
||||
// Проверяем существование товара
|
||||
$checkProduct = $db->prepare("SELECT product_id, stock_quantity FROM products WHERE product_id = ? AND is_available = TRUE");
|
||||
$checkProduct->execute([$productId]);
|
||||
$product = $checkProduct->fetch();
|
||||
|
||||
if (!$product) {
|
||||
echo json_encode(['success' => false, 'message' => 'Товар не найден']);
|
||||
exit();
|
||||
}
|
||||
|
||||
// Проверяем, есть ли товар уже в корзине
|
||||
$checkCart = $db->prepare("SELECT cart_id, quantity FROM cart WHERE user_id = ? AND product_id = ?");
|
||||
$checkCart->execute([$userId, $productId]);
|
||||
$cartItem = $checkCart->fetch();
|
||||
|
||||
if ($cartItem) {
|
||||
// Обновляем количество
|
||||
$newQuantity = $cartItem['quantity'] + $quantity;
|
||||
$stmt = $db->prepare("UPDATE cart SET quantity = ?, updated_at = CURRENT_TIMESTAMP WHERE cart_id = ?");
|
||||
$stmt->execute([$newQuantity, $cartItem['cart_id']]);
|
||||
} else {
|
||||
// Добавляем новый товар
|
||||
$stmt = $db->prepare("INSERT INTO cart (user_id, product_id, quantity) VALUES (?, ?, ?)");
|
||||
$stmt->execute([$userId, $productId, $quantity]);
|
||||
}
|
||||
|
||||
echo json_encode(['success' => true, 'message' => 'Товар добавлен в корзину']);
|
||||
break;
|
||||
|
||||
case 'update':
|
||||
$productId = (int)($_POST['product_id'] ?? 0);
|
||||
$quantity = (int)($_POST['quantity'] ?? 1);
|
||||
|
||||
if ($quantity <= 0) {
|
||||
// Удаляем товар если количество 0
|
||||
$stmt = $db->prepare("DELETE FROM cart WHERE user_id = ? AND product_id = ?");
|
||||
$stmt->execute([$userId, $productId]);
|
||||
} else {
|
||||
$stmt = $db->prepare("UPDATE cart SET quantity = ?, updated_at = CURRENT_TIMESTAMP WHERE user_id = ? AND product_id = ?");
|
||||
$stmt->execute([$quantity, $userId, $productId]);
|
||||
}
|
||||
|
||||
echo json_encode(['success' => true, 'message' => 'Корзина обновлена']);
|
||||
break;
|
||||
|
||||
case 'remove':
|
||||
$productId = (int)($_POST['product_id'] ?? 0);
|
||||
|
||||
$stmt = $db->prepare("DELETE FROM cart WHERE user_id = ? AND product_id = ?");
|
||||
$stmt->execute([$userId, $productId]);
|
||||
|
||||
echo json_encode(['success' => true, 'message' => 'Товар удален из корзины']);
|
||||
break;
|
||||
|
||||
case 'get':
|
||||
$stmt = $db->prepare("
|
||||
SELECT c.cart_id, c.product_id, c.quantity, p.name, p.price, p.image_url, p.stock_quantity
|
||||
FROM cart c
|
||||
JOIN products p ON c.product_id = p.product_id
|
||||
WHERE c.user_id = ? AND p.is_available = TRUE
|
||||
ORDER BY c.created_at DESC
|
||||
");
|
||||
$stmt->execute([$userId]);
|
||||
$items = $stmt->fetchAll();
|
||||
|
||||
$total = 0;
|
||||
foreach ($items as &$item) {
|
||||
$item['subtotal'] = $item['price'] * $item['quantity'];
|
||||
$total += $item['subtotal'];
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'items' => $items,
|
||||
'total' => $total,
|
||||
'count' => array_sum(array_column($items, 'quantity'))
|
||||
]);
|
||||
break;
|
||||
|
||||
case 'count':
|
||||
$stmt = $db->prepare("SELECT COALESCE(SUM(quantity), 0) FROM cart WHERE user_id = ?");
|
||||
$stmt->execute([$userId]);
|
||||
$count = $stmt->fetchColumn();
|
||||
|
||||
echo json_encode(['success' => true, 'count' => (int)$count]);
|
||||
break;
|
||||
|
||||
case 'clear':
|
||||
$stmt = $db->prepare("DELETE FROM cart WHERE user_id = ?");
|
||||
$stmt->execute([$userId]);
|
||||
|
||||
echo json_encode(['success' => true, 'message' => 'Корзина очищена']);
|
||||
break;
|
||||
|
||||
default:
|
||||
echo json_encode(['success' => false, 'message' => 'Неизвестное действие']);
|
||||
}
|
||||
|
||||
} catch (PDOException $e) {
|
||||
echo json_encode(['success' => false, 'message' => 'Ошибка базы данных: ' . $e->getMessage()]);
|
||||
}
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
<?php
|
||||
// get_cart.php
|
||||
session_start();
|
||||
require_once __DIR__ . '/../config/database.php';
|
||||
|
||||
if (!isset($_SESSION['isLoggedIn']) || $_SESSION['isLoggedIn'] !== true) {
|
||||
echo json_encode(['success' => false, 'message' => 'Требуется авторизация']);
|
||||
exit();
|
||||
}
|
||||
|
||||
$user_id = $_SESSION['user_id'] ?? 0;
|
||||
|
||||
if ($user_id == 0) {
|
||||
echo json_encode(['success' => false, 'message' => 'Пользователь не найден']);
|
||||
exit();
|
||||
}
|
||||
|
||||
$db = Database::getInstance()->getConnection();
|
||||
|
||||
try {
|
||||
// Получаем корзину из БД
|
||||
$stmt = $db->prepare("
|
||||
SELECT
|
||||
c.cart_id,
|
||||
c.product_id,
|
||||
c.quantity,
|
||||
p.name,
|
||||
p.price,
|
||||
p.image_url,
|
||||
p.stock_quantity
|
||||
FROM cart c
|
||||
JOIN products p ON c.product_id = p.product_id
|
||||
WHERE c.user_id = ? AND p.is_available = TRUE
|
||||
ORDER BY c.created_at DESC
|
||||
");
|
||||
$stmt->execute([$user_id]);
|
||||
$cart_items = $stmt->fetchAll();
|
||||
|
||||
// Обновляем сессию
|
||||
$_SESSION['cart'] = [];
|
||||
foreach ($cart_items as $item) {
|
||||
$_SESSION['cart'][$item['product_id']] = [
|
||||
'quantity' => $item['quantity'],
|
||||
'name' => $item['name'],
|
||||
'price' => $item['price'],
|
||||
'added_at' => time()
|
||||
];
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'cart_items' => $cart_items,
|
||||
'total_items' => count($cart_items)
|
||||
]);
|
||||
|
||||
} catch (PDOException $e) {
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'message' => 'Ошибка базы данных: ' . $e->getMessage()
|
||||
]);
|
||||
}
|
||||
?>
|
||||
@@ -1,22 +0,0 @@
|
||||
<?php
|
||||
// get_cart_count.php
|
||||
session_start();
|
||||
require_once __DIR__ . '/../config/database.php';
|
||||
|
||||
if (!isset($_SESSION['isLoggedIn']) || $_SESSION['isLoggedIn'] !== true) {
|
||||
echo json_encode(['success' => false, 'cart_count' => 0]);
|
||||
exit();
|
||||
}
|
||||
|
||||
$user_id = $_SESSION['user_id'] ?? 0;
|
||||
$db = Database::getInstance()->getConnection();
|
||||
|
||||
try {
|
||||
$stmt = $db->prepare("SELECT SUM(quantity) as total FROM cart WHERE user_id = ?");
|
||||
$stmt->execute([$user_id]);
|
||||
$cart_count = $stmt->fetchColumn() ?: 0;
|
||||
|
||||
echo json_encode(['success' => true, 'cart_count' => $cart_count]);
|
||||
} catch (PDOException $e) {
|
||||
echo json_encode(['success' => false, 'cart_count' => 0]);
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once __DIR__ . '/../config/database.php';
|
||||
|
||||
// Проверяем авторизацию администратора
|
||||
if (!isset($_SESSION['isAdmin']) || $_SESSION['isAdmin'] !== true) {
|
||||
echo json_encode(['success' => false, 'message' => 'Доступ запрещен']);
|
||||
exit();
|
||||
}
|
||||
|
||||
if (!isset($_GET['id'])) {
|
||||
echo json_encode(['success' => false, 'message' => 'ID не указан']);
|
||||
exit();
|
||||
}
|
||||
|
||||
try {
|
||||
$db = Database::getInstance()->getConnection();
|
||||
$product_id = $_GET['id'];
|
||||
|
||||
$stmt = $db->prepare("SELECT * FROM products WHERE product_id = ?");
|
||||
$stmt->execute([$product_id]);
|
||||
$product = $stmt->fetch();
|
||||
|
||||
if ($product) {
|
||||
echo json_encode($product);
|
||||
} else {
|
||||
echo json_encode(['success' => false, 'message' => 'Товар не найден']);
|
||||
}
|
||||
|
||||
} catch (PDOException $e) {
|
||||
echo json_encode(['success' => false, 'message' => 'Ошибка базы данных: ' . $e->getMessage()]);
|
||||
}
|
||||
?>
|
||||
@@ -1,134 +0,0 @@
|
||||
<?php
|
||||
// process_order.php
|
||||
session_start();
|
||||
require_once __DIR__ . '/../config/database.php';
|
||||
|
||||
if (!isset($_SESSION['isLoggedIn']) || $_SESSION['isLoggedIn'] !== true) {
|
||||
header('Location: login.php?error=auth_required');
|
||||
exit();
|
||||
}
|
||||
|
||||
$user_id = $_SESSION['user_id'] ?? 0;
|
||||
|
||||
if ($user_id == 0) {
|
||||
header('Location: login.php?error=user_not_found');
|
||||
exit();
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$db = Database::getInstance()->getConnection();
|
||||
|
||||
try {
|
||||
$db->beginTransaction();
|
||||
|
||||
// Получаем данные из формы
|
||||
$customer_name = $_POST['full_name'] ?? '';
|
||||
$customer_email = $_POST['email'] ?? '';
|
||||
$customer_phone = $_POST['phone'] ?? '';
|
||||
$delivery_address = $_POST['address'] ?? '';
|
||||
$region = $_POST['region'] ?? '';
|
||||
$payment_method = $_POST['payment'] ?? 'card';
|
||||
$delivery_method = $_POST['delivery'] ?? 'courier';
|
||||
$notes = $_POST['notes'] ?? '';
|
||||
$discount_amount = floatval($_POST['discount'] ?? 0);
|
||||
$delivery_cost = floatval($_POST['delivery_price'] ?? 2000);
|
||||
|
||||
// Генерируем номер заказа
|
||||
$order_number = 'ORD-' . date('Ymd-His') . '-' . rand(1000, 9999);
|
||||
|
||||
// Получаем корзину пользователя
|
||||
$cartStmt = $db->prepare("
|
||||
SELECT
|
||||
c.product_id,
|
||||
c.quantity,
|
||||
p.name,
|
||||
p.price,
|
||||
p.stock_quantity
|
||||
FROM cart c
|
||||
JOIN products p ON c.product_id = p.product_id
|
||||
WHERE c.user_id = ?
|
||||
");
|
||||
$cartStmt->execute([$user_id]);
|
||||
$cart_items = $cartStmt->fetchAll();
|
||||
|
||||
if (empty($cart_items)) {
|
||||
throw new Exception('Корзина пуста');
|
||||
}
|
||||
|
||||
// Рассчитываем итоги
|
||||
$total_amount = 0;
|
||||
foreach ($cart_items as $item) {
|
||||
$total_amount += $item['price'] * $item['quantity'];
|
||||
}
|
||||
|
||||
$final_amount = $total_amount - $discount_amount + $delivery_cost;
|
||||
|
||||
// Создаем заказ
|
||||
$orderStmt = $db->prepare("
|
||||
INSERT INTO orders (
|
||||
user_id, order_number, total_amount, discount_amount,
|
||||
delivery_cost, final_amount, status, payment_method,
|
||||
delivery_method, delivery_address, customer_name,
|
||||
customer_email, customer_phone, notes
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
RETURNING order_id
|
||||
");
|
||||
|
||||
$orderStmt->execute([
|
||||
$user_id, $order_number, $total_amount, $discount_amount,
|
||||
$delivery_cost, $final_amount, 'pending', $payment_method,
|
||||
$delivery_method, $delivery_address, $customer_name,
|
||||
$customer_email, $customer_phone, $notes
|
||||
]);
|
||||
|
||||
$order_id = $orderStmt->fetchColumn();
|
||||
|
||||
// Добавляем товары в заказ и обновляем остатки
|
||||
foreach ($cart_items as $item) {
|
||||
// Добавляем в order_items
|
||||
$itemStmt = $db->prepare("
|
||||
INSERT INTO order_items (
|
||||
order_id, product_id, product_name,
|
||||
quantity, unit_price, total_price
|
||||
) VALUES (?, ?, ?, ?, ?, ?)
|
||||
");
|
||||
|
||||
$item_total = $item['price'] * $item['quantity'];
|
||||
$itemStmt->execute([
|
||||
$order_id, $item['product_id'], $item['name'],
|
||||
$item['quantity'], $item['price'], $item_total
|
||||
]);
|
||||
|
||||
// Обновляем остатки на складе
|
||||
$updateStmt = $db->prepare("
|
||||
UPDATE products
|
||||
SET stock_quantity = stock_quantity - ?,
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE product_id = ?
|
||||
");
|
||||
$updateStmt->execute([$item['quantity'], $item['product_id']]);
|
||||
}
|
||||
|
||||
// Очищаем корзину
|
||||
$clearCartStmt = $db->prepare("DELETE FROM cart WHERE user_id = ?");
|
||||
$clearCartStmt->execute([$user_id]);
|
||||
|
||||
// Очищаем сессию
|
||||
unset($_SESSION['cart']);
|
||||
|
||||
$db->commit();
|
||||
|
||||
// Перенаправляем на страницу успеха
|
||||
header('Location: order_success.php?id=' . $order_id);
|
||||
exit();
|
||||
|
||||
} catch (Exception $e) {
|
||||
$db->rollBack();
|
||||
header('Location: checkout.php?error=' . urlencode($e->getMessage()));
|
||||
exit();
|
||||
}
|
||||
} else {
|
||||
header('Location: checkout.php');
|
||||
exit();
|
||||
}
|
||||
?>
|
||||
@@ -1,182 +0,0 @@
|
||||
<?php
|
||||
// register_handler.php
|
||||
session_start();
|
||||
require_once __DIR__ . '/../config/database.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$errors = [];
|
||||
|
||||
// Получаем данные из формы
|
||||
$full_name = trim($_POST['fio'] ?? '');
|
||||
$city = trim($_POST['city'] ?? '');
|
||||
$email = trim($_POST['email'] ?? '');
|
||||
$phone = trim($_POST['phone'] ?? '');
|
||||
$password = $_POST['password'] ?? '';
|
||||
$confirm_password = $_POST['confirm-password'] ?? '';
|
||||
|
||||
// Валидация данных
|
||||
if (empty($full_name) || strlen($full_name) < 3) {
|
||||
$errors[] = 'ФИО должно содержать минимум 3 символа';
|
||||
}
|
||||
|
||||
if (empty($city) || strlen($city) < 2) {
|
||||
$errors[] = 'Введите корректное название города';
|
||||
}
|
||||
|
||||
if (empty($email) || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||
$errors[] = 'Введите корректный email адрес';
|
||||
}
|
||||
|
||||
if (empty($phone) || !preg_match('/^(\+7|8)[\s-]?\(?\d{3}\)?[\s-]?\d{3}[\s-]?\d{2}[\s-]?\d{2}$/', $phone)) {
|
||||
$errors[] = 'Введите корректный номер телефона';
|
||||
}
|
||||
|
||||
if (empty($password) || strlen($password) < 6) {
|
||||
$errors[] = 'Пароль должен содержать минимум 6 символов';
|
||||
}
|
||||
|
||||
if ($password !== $confirm_password) {
|
||||
$errors[] = 'Пароли не совпадают';
|
||||
}
|
||||
|
||||
// Проверка согласия с условиями
|
||||
if (!isset($_POST['privacy']) || $_POST['privacy'] !== 'on') {
|
||||
$errors[] = 'Необходимо согласие с условиями обработки персональных данных';
|
||||
}
|
||||
|
||||
// Если есть ошибки, возвращаем на форму
|
||||
if (!empty($errors)) {
|
||||
$_SESSION['registration_errors'] = $errors;
|
||||
$_SESSION['old_data'] = [
|
||||
'fio' => $full_name,
|
||||
'city' => $city,
|
||||
'email' => $email,
|
||||
'phone' => $phone
|
||||
];
|
||||
header('Location: register.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
// Подключаемся к базе данных
|
||||
$db = Database::getInstance()->getConnection();
|
||||
|
||||
try {
|
||||
// Проверяем, существует ли пользователь с таким email
|
||||
$checkStmt = $db->prepare("SELECT user_id FROM users WHERE email = ?");
|
||||
$checkStmt->execute([$email]);
|
||||
|
||||
if ($checkStmt->fetch()) {
|
||||
$_SESSION['registration_errors'] = ['Пользователь с таким email уже существует'];
|
||||
$_SESSION['old_data'] = [
|
||||
'fio' => $full_name,
|
||||
'city' => $city,
|
||||
'email' => $email,
|
||||
'phone' => $phone
|
||||
];
|
||||
header('Location: register.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
// Хэшируем пароль
|
||||
$password_hash = password_hash($password, PASSWORD_DEFAULT);
|
||||
|
||||
// Определяем, является ли пользователь администратором
|
||||
$is_admin = false;
|
||||
$admin_emails = ['admin@aeterna.ru', 'administrator@aeterna.ru', 'aeterna@mail.ru'];
|
||||
if (in_array(strtolower($email), $admin_emails)) {
|
||||
$is_admin = true;
|
||||
}
|
||||
|
||||
// РАЗНЫЕ ВАРИАНТЫ ДЛЯ ТЕСТИРОВАНИЯ - РАСКОММЕНТИРУЙТЕ НУЖНЫЙ
|
||||
|
||||
// Вариант 1: С явным преобразованием boolean (самый надежный)
|
||||
$stmt = $db->prepare("
|
||||
INSERT INTO users (email, password_hash, full_name, phone, city, is_admin)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
RETURNING user_id
|
||||
");
|
||||
|
||||
// Преобразуем boolean в integer для PostgreSQL
|
||||
$stmt->execute([
|
||||
$email,
|
||||
$password_hash,
|
||||
$full_name,
|
||||
$phone,
|
||||
$city,
|
||||
$is_admin ? 1 : 0 // Преобразуем в integer (1 или 0)
|
||||
]);
|
||||
|
||||
// Вариант 2: С использованием CAST в SQL (альтернатива)
|
||||
/*
|
||||
$stmt = $db->prepare("
|
||||
INSERT INTO users (email, password_hash, full_name, phone, city, is_admin)
|
||||
VALUES (?, ?, ?, ?, ?, CAST(? AS boolean))
|
||||
RETURNING user_id
|
||||
");
|
||||
|
||||
$stmt->execute([
|
||||
$email,
|
||||
$password_hash,
|
||||
$full_name,
|
||||
$phone,
|
||||
$city,
|
||||
$is_admin ? 'true' : 'false' // Строковые значения true/false
|
||||
]);
|
||||
*/
|
||||
|
||||
$user_id = $stmt->fetchColumn();
|
||||
|
||||
if ($user_id) {
|
||||
// Автоматически авторизуем пользователя
|
||||
$_SESSION['user_id'] = $user_id;
|
||||
$_SESSION['user_email'] = $email;
|
||||
$_SESSION['full_name'] = $full_name;
|
||||
$_SESSION['user_phone'] = $phone;
|
||||
$_SESSION['user_city'] = $city;
|
||||
$_SESSION['isLoggedIn'] = true;
|
||||
$_SESSION['isAdmin'] = $is_admin;
|
||||
$_SESSION['login_time'] = time();
|
||||
|
||||
// Обновляем время последнего входа
|
||||
$updateStmt = $db->prepare("UPDATE users SET last_login = CURRENT_TIMESTAMP WHERE user_id = ?");
|
||||
$updateStmt->execute([$user_id]);
|
||||
|
||||
// Перенаправляем на главную или каталог
|
||||
$_SESSION['registration_success'] = 'Регистрация прошла успешно! ' .
|
||||
($is_admin ? 'Вы зарегистрированы как администратор.' : 'Добро пожаловать в AETERNA!');
|
||||
|
||||
header('Location: catalog.php');
|
||||
exit();
|
||||
} else {
|
||||
throw new Exception('Ошибка при создании пользователя');
|
||||
}
|
||||
|
||||
} catch (PDOException $e) {
|
||||
// Логируем полную ошибку для отладки
|
||||
error_log("Registration DB Error: " . $e->getMessage());
|
||||
error_log("SQL State: " . $e->getCode());
|
||||
error_log("Email: " . $email);
|
||||
|
||||
$_SESSION['registration_errors'] = ['Ошибка базы данных: ' . $e->getMessage()];
|
||||
$_SESSION['old_data'] = [
|
||||
'fio' => $full_name,
|
||||
'city' => $city,
|
||||
'email' => $email,
|
||||
'phone' => $phone
|
||||
];
|
||||
header('Location: register.php');
|
||||
exit();
|
||||
} catch (Exception $e) {
|
||||
error_log("Registration Error: " . $e->getMessage());
|
||||
|
||||
$_SESSION['registration_errors'] = [$e->getMessage()];
|
||||
header('Location: register.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
} else {
|
||||
// Если запрос не POST, перенаправляем на форму регистрации
|
||||
header('Location: register.php');
|
||||
exit();
|
||||
}
|
||||
?>
|
||||
@@ -1,31 +0,0 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once __DIR__ . '/../config/database.php';
|
||||
|
||||
if (!isset($_SESSION['isLoggedIn']) || $_SESSION['isLoggedIn'] !== true) {
|
||||
echo json_encode(['success' => false, 'message' => 'Требуется авторизация']);
|
||||
exit();
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_POST['product_id'])) {
|
||||
$product_id = intval($_POST['product_id']);
|
||||
$quantity = intval($_POST['quantity'] ?? 1);
|
||||
$user_id = $_SESSION['user_id'] ?? 0;
|
||||
|
||||
$db = Database::getInstance()->getConnection();
|
||||
|
||||
try {
|
||||
$stmt = $db->prepare("
|
||||
UPDATE cart
|
||||
SET quantity = ?, updated_at = CURRENT_TIMESTAMP
|
||||
WHERE user_id = ? AND product_id = ?
|
||||
");
|
||||
$stmt->execute([$quantity, $user_id, $product_id]);
|
||||
|
||||
echo json_encode(['success' => true]);
|
||||
|
||||
} catch (PDOException $e) {
|
||||
echo json_encode(['success' => false, 'message' => 'Ошибка базы данных']);
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
Before Width: | Height: | Size: 43 KiB |
BIN
assets/img/1.jpg
|
Before Width: | Height: | Size: 78 KiB |
|
Before Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 183 KiB |
|
Before Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 177 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 77 KiB |
|
Before Width: | Height: | Size: 190 KiB |
BIN
assets/img/2.jpg
|
Before Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 89 KiB |
BIN
assets/img/3.jpg
|
Before Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 414 KiB |
BIN
assets/img/4.jpg
|
Before Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 26 KiB |
BIN
assets/img/444
|
Before Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 170 KiB |
|
Before Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 291 KiB |
|
Before Width: | Height: | Size: 68 KiB |
BIN
assets/img/5.jpg
|
Before Width: | Height: | Size: 90 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 198 KiB |
BIN
assets/img/6.jpg
|
Before Width: | Height: | Size: 113 KiB |
|
Before Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 372 KiB |
BIN
assets/img/7.jpg
|
Before Width: | Height: | Size: 96 KiB |
|
Before Width: | Height: | Size: 79 KiB |
|
Before Width: | Height: | Size: 265 KiB |
|
Before Width: | Height: | Size: 66 KiB |
|
Before Width: | Height: | Size: 555 KiB |
|
Before Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 479 KiB |
BIN
assets/img/8.jpg
|
Before Width: | Height: | Size: 91 KiB |
|
Before Width: | Height: | Size: 124 KiB |
|
Before Width: | Height: | Size: 176 KiB |
|
Before Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 430 KiB |
BIN
assets/img/9.jpg
|
Before Width: | Height: | Size: 167 KiB |
|
Before Width: | Height: | Size: 88 KiB |
|
Before Width: | Height: | Size: 1.0 MiB |
|
Before Width: | Height: | Size: 140 KiB |
|
Before Width: | Height: | Size: 67 KiB |
|
Before Width: | Height: | Size: 497 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 759 KiB |
|
Before Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 1.3 MiB |
|
Before Width: | Height: | Size: 654 KiB |
|
Before Width: | Height: | Size: 1.3 MiB |
|
Before Width: | Height: | Size: 1.5 MiB |
|
Before Width: | Height: | Size: 739 KiB |
|
Before Width: | Height: | Size: 144 KiB |
|
Before Width: | Height: | Size: 1.3 MiB |
|
Before Width: | Height: | Size: 1.4 MiB |
|
Before Width: | Height: | Size: 655 KiB |
|
Before Width: | Height: | Size: 148 KiB |
|
Before Width: | Height: | Size: 127 KiB |
|
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 |
|
Before Width: | Height: | Size: 151 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;
|
||||
}
|
||||
@@ -1,346 +0,0 @@
|
||||
// script.js
|
||||
|
||||
$(document).ready(function() {
|
||||
// Инициализация корзины
|
||||
let cart = {
|
||||
items: [
|
||||
{ id: 1, name: 'Кресло OPPORTUNITY', price: 16999, quantity: 1 },
|
||||
{ id: 2, name: 'Кресло GOLDEN', price: 19999, quantity: 1 },
|
||||
{ id: 3, name: 'Светильник POLET', price: 7999, quantity: 1 }
|
||||
],
|
||||
delivery: 2000,
|
||||
discount: 0
|
||||
};
|
||||
|
||||
// Функция обновления общей суммы
|
||||
function updateTotal() {
|
||||
let productsTotal = 0;
|
||||
let totalCount = 0;
|
||||
|
||||
// Пересчитываем товары
|
||||
$('.products__item').each(function() {
|
||||
const $item = $(this);
|
||||
const price = parseInt($item.data('price'));
|
||||
const quantity = parseInt($item.find('.products__qty-value').text());
|
||||
|
||||
productsTotal += price * quantity;
|
||||
totalCount += quantity;
|
||||
});
|
||||
|
||||
// Обновляем отображение
|
||||
$('.products-total').text(productsTotal + ' ₽');
|
||||
$('.summary-count').text(totalCount);
|
||||
$('.total-count').text(totalCount + ' шт.');
|
||||
$('.cart-count').text(totalCount);
|
||||
|
||||
// Обновляем итоговую сумму
|
||||
const finalTotal = productsTotal + cart.delivery - cart.discount;
|
||||
$('.final-total').text(finalTotal + ' ₽');
|
||||
}
|
||||
|
||||
// Функция валидации email
|
||||
function validateEmail(email) {
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
return emailRegex.test(email);
|
||||
}
|
||||
|
||||
// Функция валидации имени (ФИО)
|
||||
function validateFullName(name) {
|
||||
// Проверяем, что имя содержит только буквы, пробелы, дефисы и апострофы
|
||||
const nameRegex = /^[a-zA-Zа-яА-ЯёЁ\s\-']+$/;
|
||||
|
||||
// Проверяем, что имя состоит минимум из 2 слов
|
||||
const words = name.trim().split(/\s+/);
|
||||
|
||||
return nameRegex.test(name) && words.length >= 2;
|
||||
}
|
||||
|
||||
// Функция валидации телефона
|
||||
function validatePhone(phone) {
|
||||
// Российский формат телефона: +7XXXXXXXXXX
|
||||
const phoneRegex = /^\+7\d{10}$/;
|
||||
return phoneRegex.test(phone);
|
||||
}
|
||||
|
||||
// Функция отображения сообщения
|
||||
function showMessage(messageId, duration = 5000) {
|
||||
// Скрываем все сообщения
|
||||
$('.message').hide();
|
||||
|
||||
// Показываем нужное сообщение
|
||||
$(messageId).fadeIn(300);
|
||||
|
||||
// Автоматически скрываем через указанное время
|
||||
if (duration > 0) {
|
||||
setTimeout(() => {
|
||||
$(messageId).fadeOut(300);
|
||||
}, duration);
|
||||
}
|
||||
}
|
||||
|
||||
// Функция показа ошибки приватности
|
||||
function showPrivacyError(show) {
|
||||
if (show) {
|
||||
$('#privacy-error').show();
|
||||
} else {
|
||||
$('#privacy-error').hide();
|
||||
}
|
||||
}
|
||||
|
||||
// Функция отображения ошибки конкретного поля
|
||||
function showFieldError(fieldId, message) {
|
||||
// Убираем старые ошибки
|
||||
$(fieldId).removeClass('error-input');
|
||||
$(fieldId + '-error').remove();
|
||||
|
||||
if (message) {
|
||||
$(fieldId).addClass('error-input');
|
||||
$(fieldId).after('<div class="field-error" id="' + fieldId.replace('#', '') + '-error">' + message + '</div>');
|
||||
}
|
||||
}
|
||||
|
||||
// Валидация поля при потере фокуса с указанием конкретной ошибки
|
||||
$('#fullname').on('blur', function() {
|
||||
const value = $(this).val().trim();
|
||||
if (value) {
|
||||
if (!validateFullName(value)) {
|
||||
showFieldError('#fullname', 'ФИО должно содержать только буквы и состоять минимум из 2 слов');
|
||||
} else {
|
||||
showFieldError('#fullname', '');
|
||||
}
|
||||
} else {
|
||||
showFieldError('#fullname', '');
|
||||
}
|
||||
});
|
||||
|
||||
$('#email').on('blur', function() {
|
||||
const value = $(this).val().trim();
|
||||
if (value) {
|
||||
if (!validateEmail(value)) {
|
||||
showFieldError('#email', 'Введите корректный email адрес (например: example@mail.ru)');
|
||||
} else {
|
||||
showFieldError('#email', '');
|
||||
}
|
||||
} else {
|
||||
showFieldError('#email', '');
|
||||
}
|
||||
});
|
||||
|
||||
$('#phone').on('blur', function() {
|
||||
const value = $(this).val().trim();
|
||||
if (value) {
|
||||
if (!validatePhone(value)) {
|
||||
showFieldError('#phone', 'Введите номер в формате +7XXXXXXXXXX (10 цифр после +7)');
|
||||
} else {
|
||||
showFieldError('#phone', '');
|
||||
}
|
||||
} else {
|
||||
showFieldError('#phone', '');
|
||||
}
|
||||
});
|
||||
|
||||
// Валидация обязательных полей
|
||||
$('#region').on('blur', function() {
|
||||
const value = $(this).val().trim();
|
||||
if (!value) {
|
||||
showFieldError('#region', 'Укажите регион доставки');
|
||||
} else {
|
||||
showFieldError('#region', '');
|
||||
}
|
||||
});
|
||||
|
||||
$('#address').on('blur', function() {
|
||||
const value = $(this).val().trim();
|
||||
if (!value) {
|
||||
showFieldError('#address', 'Укажите адрес доставки (улица, дом, квартира)');
|
||||
} else {
|
||||
showFieldError('#address', '');
|
||||
}
|
||||
});
|
||||
|
||||
// Очистка ошибки при начале ввода
|
||||
$('.form__input').on('input', function() {
|
||||
const fieldId = '#' + $(this).attr('id');
|
||||
showFieldError(fieldId, '');
|
||||
});
|
||||
|
||||
// Обработчик увеличения количества
|
||||
$('.products__qty-btn.plus').click(function() {
|
||||
const $qtyValue = $(this).siblings('.products__qty-value');
|
||||
let quantity = parseInt($qtyValue.text());
|
||||
$qtyValue.text(quantity + 1);
|
||||
updateTotal();
|
||||
});
|
||||
|
||||
// Обработчик уменьшения количества
|
||||
$('.products__qty-btn.minus').click(function() {
|
||||
const $qtyValue = $(this).siblings('.products__qty-value');
|
||||
let quantity = parseInt($qtyValue.text());
|
||||
if (quantity > 1) {
|
||||
$qtyValue.text(quantity - 1);
|
||||
updateTotal();
|
||||
}
|
||||
});
|
||||
|
||||
// Обработчик удаления товара
|
||||
$('.remove-from-cart').click(function() {
|
||||
const $productItem = $(this).closest('.products__item');
|
||||
$productItem.fadeOut(300, function() {
|
||||
$(this).remove();
|
||||
updateTotal();
|
||||
|
||||
// Показываем сообщение, если корзина пуста
|
||||
if ($('.products__item').length === 0) {
|
||||
$('.products__list').html('<div class="empty-cart">Корзина пуста</div>');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Обработчик применения промокода
|
||||
$('.promo__btn').click(function() {
|
||||
const promoCode = $('.promo__input').val().toUpperCase();
|
||||
|
||||
if (promoCode === 'SALE10') {
|
||||
cart.discount = Math.round(parseInt($('.products-total').text()) * 0.1);
|
||||
$('.discount-total').text(cart.discount + ' ₽');
|
||||
showMessage('#form-error', 3000);
|
||||
$('#form-error').text('Промокод применен! Скидка 10%').removeClass('error').addClass('success');
|
||||
} else if (promoCode === 'FREE') {
|
||||
cart.delivery = 0;
|
||||
$('.delivery-price').text('0 ₽');
|
||||
showMessage('#form-error', 3000);
|
||||
$('#form-error').text('Промокод применен! Бесплатная доставка').removeClass('error').addClass('success');
|
||||
} else if (promoCode) {
|
||||
showMessage('#form-error', 3000);
|
||||
$('#form-error').text('Промокод недействителен').removeClass('success').addClass('error');
|
||||
}
|
||||
|
||||
updateTotal();
|
||||
});
|
||||
|
||||
// Обработчик выбора доставки
|
||||
$('input[name="delivery"]').change(function() {
|
||||
if ($(this).val() === 'pickup') {
|
||||
cart.delivery = 0;
|
||||
$('.delivery-price').text('0 ₽');
|
||||
} else {
|
||||
cart.delivery = 2000;
|
||||
$('.delivery-price').text('2000 ₽');
|
||||
}
|
||||
updateTotal();
|
||||
});
|
||||
|
||||
// Функция проверки всех полей формы
|
||||
function validateForm() {
|
||||
let isValid = true;
|
||||
let errorMessages = [];
|
||||
|
||||
// Очищаем все старые ошибки
|
||||
$('.field-error').remove();
|
||||
$('.form__input').removeClass('error-input');
|
||||
|
||||
// Проверка обязательных полей
|
||||
const requiredFields = [
|
||||
{
|
||||
id: '#fullname',
|
||||
value: $('#fullname').val().trim(),
|
||||
validator: validateFullName,
|
||||
required: true,
|
||||
message: 'ФИО должно содержать только буквы и состоять минимум из 2 слов'
|
||||
},
|
||||
{
|
||||
id: '#phone',
|
||||
value: $('#phone').val().trim(),
|
||||
validator: validatePhone,
|
||||
required: true,
|
||||
message: 'Введите корректный номер телефона в формате +7XXXXXXXXXX'
|
||||
},
|
||||
{
|
||||
id: '#email',
|
||||
value: $('#email').val().trim(),
|
||||
validator: validateEmail,
|
||||
required: true,
|
||||
message: 'Введите корректный email адрес'
|
||||
},
|
||||
{
|
||||
id: '#region',
|
||||
value: $('#region').val().trim(),
|
||||
validator: (val) => val.length > 0,
|
||||
required: true,
|
||||
message: 'Поле "Регион" обязательно для заполнения'
|
||||
},
|
||||
{
|
||||
id: '#address',
|
||||
value: $('#address').val().trim(),
|
||||
validator: (val) => val.length > 0,
|
||||
required: true,
|
||||
message: 'Поле "Адрес" обязательно для заполнения'
|
||||
}
|
||||
];
|
||||
|
||||
// Проверяем каждое поле
|
||||
requiredFields.forEach(field => {
|
||||
if (field.required && (!field.value || !field.validator(field.value))) {
|
||||
isValid = false;
|
||||
errorMessages.push(field.message);
|
||||
showFieldError(field.id, field.message);
|
||||
}
|
||||
});
|
||||
|
||||
// Проверка согласия на обработку данных
|
||||
if (!$('#privacy-checkbox').is(':checked')) {
|
||||
isValid = false;
|
||||
showPrivacyError(true);
|
||||
errorMessages.push('Необходимо согласие на обработку персональных данных');
|
||||
} else {
|
||||
showPrivacyError(false);
|
||||
}
|
||||
|
||||
// Показываем общее сообщение, если есть ошибки
|
||||
if (!isValid && errorMessages.length > 0) {
|
||||
showMessage('#form-error', 5000);
|
||||
$('#form-error').text('Исправьте следующие ошибки: ' + errorMessages.join('; ')).removeClass('success').addClass('error');
|
||||
|
||||
// Прокручиваем к первой ошибке
|
||||
$('html, body').animate({
|
||||
scrollTop: $('.error-input').first().offset().top - 100
|
||||
}, 500);
|
||||
}
|
||||
|
||||
return isValid;
|
||||
}
|
||||
|
||||
// Обработчик оформления заказа
|
||||
$('#submit-order').click(function() {
|
||||
// Проверка валидации всех полей
|
||||
if (!validateForm()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Симуляция отправки заказа
|
||||
$(this).prop('disabled', true).text('ОБРАБОТКА...');
|
||||
|
||||
setTimeout(() => {
|
||||
showMessage('#order-success', 5000);
|
||||
$(this).prop('disabled', false).text('ОФОРМИТЬ ЗАКАЗ');
|
||||
|
||||
// Здесь можно добавить редирект на страницу благодарности
|
||||
// window.location.href = 'спасибо.html';
|
||||
}, 2000);
|
||||
});
|
||||
|
||||
// Маска для телефона
|
||||
$('#phone').on('input', function() {
|
||||
let phone = $(this).val().replace(/\D/g, '');
|
||||
if (phone.length > 0) {
|
||||
if (phone[0] !== '7' && phone[0] !== '8') {
|
||||
phone = '7' + phone;
|
||||
}
|
||||
phone = '+7' + phone.substring(1, 11);
|
||||
$(this).val(phone);
|
||||
}
|
||||
});
|
||||
|
||||
// Инициализация при загрузке
|
||||
updateTotal();
|
||||
});
|
||||
@@ -1,384 +0,0 @@
|
||||
$(document).ready(function() {
|
||||
// Функции для отображения сообщений
|
||||
function showMessage(type, text) {
|
||||
const messageId = type + 'Message';
|
||||
const $message = $('#' + messageId);
|
||||
$message.text(text).fadeIn(300);
|
||||
setTimeout(() => {
|
||||
$message.fadeOut(300);
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
// Проверка, является ли email администратора
|
||||
function isAdminEmail(email) {
|
||||
const adminEmails = ['admin@aeterna.ru', 'administrator@aeterna.ru', 'aeterna@mail.ru'];
|
||||
return adminEmails.includes(email.toLowerCase());
|
||||
}
|
||||
|
||||
// Валидация ФИО (без цифр)
|
||||
function validateFIO(fio) {
|
||||
const words = fio.trim().split(/\s+/);
|
||||
// Проверяем, что минимум 2 слова и нет цифр
|
||||
const hasNoDigits = !/\d/.test(fio);
|
||||
return words.length >= 2 && words.every(word => word.length >= 2) && hasNoDigits;
|
||||
}
|
||||
|
||||
// Валидация города
|
||||
function validateCity(city) {
|
||||
return city.trim().length >= 2 && /^[а-яА-ЯёЁ\s-]+$/.test(city);
|
||||
}
|
||||
|
||||
// Валидация email
|
||||
function validateEmail(email) {
|
||||
const localPart = email.split('@')[0];
|
||||
|
||||
// Проверка на кириллические символы перед @
|
||||
if (/[а-яА-ЯёЁ]/.test(localPart)) {
|
||||
showError('email', 'В тексте перед знаком "@" не должно быть кириллических символов');
|
||||
return false;
|
||||
}
|
||||
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
if (!emailRegex.test(email)) {
|
||||
showError('email', 'Введите корректный email адрес');
|
||||
return false;
|
||||
}
|
||||
|
||||
hideError('email');
|
||||
return true;
|
||||
}
|
||||
|
||||
// Валидация телефона
|
||||
function validatePhone(phone) {
|
||||
const phoneRegex = /^(\+7|8)[\s-]?\(?\d{3}\)?[\s-]?\d{3}[\s-]?\d{2}[\s-]?\d{2}$/;
|
||||
return phoneRegex.test(phone.replace(/\s/g, ''));
|
||||
}
|
||||
|
||||
// Валидация пароля
|
||||
function validatePassword(password) {
|
||||
return password.length >= 6;
|
||||
}
|
||||
|
||||
// Показать/скрыть ошибку
|
||||
function showError(fieldId, message) {
|
||||
$('#' + fieldId).addClass('error');
|
||||
$('#' + fieldId + '-error').text(message).show();
|
||||
}
|
||||
|
||||
function hideError(fieldId) {
|
||||
$('#' + fieldId).removeClass('error');
|
||||
$('#' + fieldId + '-error').hide();
|
||||
}
|
||||
|
||||
// Валидация формы
|
||||
function validateForm() {
|
||||
let isValid = true;
|
||||
|
||||
// Валидация ФИО
|
||||
const fio = $('#fio').val();
|
||||
if (!validateFIO(fio)) {
|
||||
if (/\d/.test(fio)) {
|
||||
showError('fio', 'ФИО не должно содержать цифры');
|
||||
} else {
|
||||
showError('fio', 'ФИО должно содержать минимум 2 слова (каждое от 2 символов)');
|
||||
}
|
||||
isValid = false;
|
||||
} else {
|
||||
hideError('fio');
|
||||
}
|
||||
|
||||
// Валидация города
|
||||
const city = $('#city').val();
|
||||
if (!validateCity(city)) {
|
||||
showError('city', 'Укажите корректное название города (только русские буквы)');
|
||||
isValid = false;
|
||||
} else {
|
||||
hideError('city');
|
||||
}
|
||||
|
||||
// Валидация email
|
||||
const email = $('#email').val();
|
||||
if (!validateEmail(email)) {
|
||||
showError('email', 'Введите корректный email адрес');
|
||||
isValid = false;
|
||||
} else {
|
||||
hideError('email');
|
||||
}
|
||||
|
||||
// Валидация телефона
|
||||
const phone = $('#phone').val();
|
||||
if (!validatePhone(phone)) {
|
||||
showError('phone', 'Введите номер в формате: +7(912)999-12-23');
|
||||
isValid = false;
|
||||
} else {
|
||||
hideError('phone');
|
||||
}
|
||||
|
||||
// Валидация пароля
|
||||
const password = $('#password').val();
|
||||
if (!validatePassword(password)) {
|
||||
showError('password', 'Пароль должен содержать минимум 6 символов');
|
||||
isValid = false;
|
||||
} else {
|
||||
hideError('password');
|
||||
}
|
||||
|
||||
// Проверка совпадения паролей
|
||||
const confirmPassword = $('#confirm-password').val();
|
||||
if (password !== confirmPassword) {
|
||||
showError('confirm-password', 'Пароли не совпадают');
|
||||
isValid = false;
|
||||
} else {
|
||||
hideError('confirm-password');
|
||||
}
|
||||
|
||||
// Проверка согласия с условиями
|
||||
if (!$('#privacy').is(':checked')) {
|
||||
$('#privacy-error').show();
|
||||
isValid = false;
|
||||
} else {
|
||||
$('#privacy-error').hide();
|
||||
}
|
||||
|
||||
return isValid;
|
||||
}
|
||||
|
||||
// Реальная валидация при вводе
|
||||
$('input').on('blur', function() {
|
||||
const fieldId = $(this).attr('id');
|
||||
const value = $(this).val();
|
||||
|
||||
switch(fieldId) {
|
||||
case 'fio':
|
||||
if (!validateFIO(value)) {
|
||||
if (/\d/.test(value)) {
|
||||
showError(fieldId, 'ФИО не должно содержать цифры');
|
||||
} else {
|
||||
showError(fieldId, 'ФИО должно содержать минимум 2 слова');
|
||||
}
|
||||
} else {
|
||||
hideError(fieldId);
|
||||
}
|
||||
break;
|
||||
case 'city':
|
||||
if (!validateCity(value)) {
|
||||
showError(fieldId, 'Укажите корректное название города');
|
||||
} else {
|
||||
hideError(fieldId);
|
||||
}
|
||||
break;
|
||||
case 'email':
|
||||
if (!validateEmail(value)) {
|
||||
showError(fieldId, 'Введите корректный email адрес');
|
||||
} else {
|
||||
hideError(fieldId);
|
||||
}
|
||||
break;
|
||||
case 'phone':
|
||||
if (!validatePhone(value)) {
|
||||
showError(fieldId, 'Введите номер в формате: +7(912)999-12-23');
|
||||
} else {
|
||||
hideError(fieldId);
|
||||
}
|
||||
break;
|
||||
case 'password':
|
||||
if (!validatePassword(value)) {
|
||||
showError(fieldId, 'Пароль должен содержать минимум 6 символов');
|
||||
} else {
|
||||
hideError(fieldId);
|
||||
}
|
||||
break;
|
||||
case 'confirm-password':
|
||||
const password = $('#password').val();
|
||||
if (value !== password) {
|
||||
showError(fieldId, 'Пароли не совпадают');
|
||||
} else {
|
||||
hideError(fieldId);
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
// Обработка отправки формы
|
||||
$('#registrationForm').on('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
if (validateForm()) {
|
||||
const email = $('#email').val();
|
||||
const isAdmin = isAdminEmail(email);
|
||||
|
||||
// Сохраняем данные пользователя в localStorage
|
||||
const userData = {
|
||||
email: email,
|
||||
fio: $('#fio').val(),
|
||||
phone: $('#phone').val(),
|
||||
isAdmin: isAdmin,
|
||||
registered: new Date().toISOString()
|
||||
};
|
||||
|
||||
localStorage.setItem('userData', JSON.stringify(userData));
|
||||
localStorage.setItem('isLoggedIn', 'true');
|
||||
localStorage.setItem('isAdmin', isAdmin.toString());
|
||||
|
||||
// Эмуляция успешной регистрации
|
||||
showMessage('success', 'Регистрация прошла успешно! ' +
|
||||
(isAdmin ? 'Вы зарегистрированы как администратор.' : 'Добро пожаловать в AETERNA!'));
|
||||
|
||||
setTimeout(() => {
|
||||
// Перенаправление на главную страницу
|
||||
window.location.href = 'cite_mebel.php';
|
||||
}, 2000);
|
||||
} else {
|
||||
showMessage('error', 'Пожалуйста, исправьте ошибки в форме');
|
||||
}
|
||||
});
|
||||
|
||||
// Плавная прокрутка к якорям
|
||||
$('a[href^="#"]').on('click', function(event) {
|
||||
var target = $(this.getAttribute('href'));
|
||||
if (target.length) {
|
||||
event.preventDefault();
|
||||
$('html, body').stop().animate({
|
||||
scrollTop: target.offset().top
|
||||
}, 1000);
|
||||
}
|
||||
});
|
||||
|
||||
// Переключение между регистрацией и входом
|
||||
$('.login-btn').on('click', function() {
|
||||
showMessage('warning', 'Переход к форме входа...');
|
||||
setTimeout(() => {
|
||||
window.location.href = 'вход.php';
|
||||
}, 1000);
|
||||
});
|
||||
|
||||
// Обработка ссылки "Сменить пароль"
|
||||
$('.password-link').on('click', function(e) {
|
||||
e.preventDefault();
|
||||
showMessage('warning', 'Функция смены пароля будет доступна после регистрации');
|
||||
});
|
||||
|
||||
// Маска для телефона
|
||||
$('#phone').on('input', function() {
|
||||
let value = $(this).val().replace(/\D/g, '');
|
||||
if (value.startsWith('7') || value.startsWith('8')) {
|
||||
value = value.substring(1);
|
||||
}
|
||||
if (value.length > 0) {
|
||||
value = '+7(' + value;
|
||||
if (value.length > 6) value = value.substring(0, 6) + ')' + value.substring(6);
|
||||
if (value.length > 10) value = value.substring(0, 10) + '-' + value.substring(10);
|
||||
if (value.length > 13) value = value.substring(0, 13) + '-' + value.substring(13);
|
||||
if (value.length > 16) value = value.substring(0, 16);
|
||||
}
|
||||
$(this).val(value);
|
||||
});
|
||||
|
||||
// Запрет ввода цифр в поле ФИО
|
||||
$('#fio').on('input', function() {
|
||||
let value = $(this).val();
|
||||
// Удаляем цифры из значения
|
||||
value = value.replace(/\d/g, '');
|
||||
$(this).val(value);
|
||||
});
|
||||
});
|
||||
|
||||
// Для входа (файл вход.html)
|
||||
$(document).ready(function() {
|
||||
// Проверяем, если пользователь уже вошел
|
||||
if (localStorage.getItem('isLoggedIn') === 'true') {
|
||||
const userData = JSON.parse(localStorage.getItem('userData') || '{}');
|
||||
if (userData.email) {
|
||||
$('#login-email').val(userData.email);
|
||||
}
|
||||
}
|
||||
|
||||
// Функция проверки пароля администратора
|
||||
function checkAdminPassword(email, password) {
|
||||
// Административные аккаунты
|
||||
const adminAccounts = {
|
||||
'admin@aeterna.ru': 'admin123',
|
||||
'administrator@aeterna.ru': 'admin123',
|
||||
'aeterna@mail.ru': 'admin123'
|
||||
};
|
||||
|
||||
return adminAccounts[email.toLowerCase()] === password;
|
||||
}
|
||||
|
||||
// Валидация формы входа
|
||||
$('#loginForm').on('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
let isValid = true;
|
||||
const email = $('#login-email').val();
|
||||
const password = $('#login-password').val();
|
||||
|
||||
// Валидация email
|
||||
if (!isValidEmail(email)) {
|
||||
$('#email-error').show();
|
||||
isValid = false;
|
||||
} else {
|
||||
$('#email-error').hide();
|
||||
}
|
||||
|
||||
// Валидация пароля
|
||||
if (password.length < 6) {
|
||||
$('#password-error').show();
|
||||
isValid = false;
|
||||
} else {
|
||||
$('#password-error').hide();
|
||||
}
|
||||
|
||||
if (isValid) {
|
||||
// Здесь обычно отправка данных на сервер
|
||||
showMessage('success', 'Вы успешно вошли в систему!');
|
||||
|
||||
// Перенаправление на главную страницу через 1.5 секунды
|
||||
setTimeout(function() {
|
||||
window.location.href = 'cite_mebel.php';
|
||||
}, 1500);
|
||||
}
|
||||
});
|
||||
|
||||
// Функция показа сообщений
|
||||
function showMessage(type, text) {
|
||||
const messageId = type + 'Message';
|
||||
const $message = $('#' + messageId);
|
||||
|
||||
$message.text(text).show();
|
||||
|
||||
setTimeout(function() {
|
||||
$message.fadeOut();
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
// Скрываем сообщения об ошибках при фокусе на полях
|
||||
$('input').on('focus', function() {
|
||||
$(this).next('.error-message').hide();
|
||||
});
|
||||
|
||||
// Обработка чекбокса "Запомнить меня"
|
||||
$('#remember').on('change', function() {
|
||||
if ($(this).is(':checked')) {
|
||||
const email = $('#login-email').val();
|
||||
if (email) {
|
||||
localStorage.setItem('rememberedEmail', email);
|
||||
}
|
||||
} else {
|
||||
localStorage.removeItem('rememberedEmail');
|
||||
}
|
||||
});
|
||||
|
||||
// Автозаполнение email, если пользователь выбрал "Запомнить меня"
|
||||
const rememberedEmail = localStorage.getItem('rememberedEmail');
|
||||
if (rememberedEmail) {
|
||||
$('#login-email').val(rememberedEmail);
|
||||
$('#remember').prop('checked', true);
|
||||
}
|
||||
|
||||
// Обработка ссылки "Забыли пароль?"
|
||||
$('.forgot-password').on('click', function(e) {
|
||||
e.preventDefault();
|
||||
showMessage('info', 'Для восстановления пароля обратитесь к администратору');
|
||||
});
|
||||
});
|
||||
@@ -1,142 +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;
|
||||
}
|
||||
|
||||
/* Дополнительные стили для формы регистрации */
|
||||
.input-group {
|
||||
position: relative;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.profile-form input.error {
|
||||
border-color: #ff0000;
|
||||
background-color: #fff5f5;
|
||||
}
|
||||
|
||||
.privacy-checkbox {
|
||||
margin: 20px 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.privacy-checkbox label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.privacy-checkbox input[type="checkbox"] {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Исправление отступов для страницы регистрации */
|
||||
.profile-page-main {
|
||||
padding: 40px 0;
|
||||
min-height: calc(100vh - 200px);
|
||||
}
|
||||
|
||||
/* Убедимся, что контейнер не перекрывает шапку и футер */
|
||||
.profile-container {
|
||||
margin: 0 auto;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.input-hint {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
/* Стили для страницы входа */
|
||||
.form-options {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.remember-me {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 14px;
|
||||
color: #453227;
|
||||
}
|
||||
|
||||
.remember-me input[type="checkbox"] {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.forgot-password {
|
||||
font-size: 14px;
|
||||
color: #453227;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.forgot-password:hover {
|
||||
color: #617365;
|
||||
text-decoration: none;
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
// ===================================
|
||||
// === ПЕРЕМЕННЫЕ И МИКСИНЫ AETERNA ===
|
||||
// ===================================
|
||||
@color-primary: #617365;
|
||||
@color-secondary: #D1D1D1;
|
||||
@color-accent: #453227;
|
||||
@color-text-dark: #333;
|
||||
@color-text-light: #fff;
|
||||
@color-button: @color-accent;
|
||||
@color-beige: #A2A09A;
|
||||
|
||||
@font-logo: 'Anek Kannada', sans-serif;
|
||||
@font-main: 'Anonymous Pro', monospace;
|
||||
@font-heading: 'Playfair Display', serif;
|
||||
|
||||
@shadow-light: 0 5px 15px rgba(0, 0, 0, 0.2);
|
||||
@shadow-dark: 2px 2px 4px rgba(0, 0, 0, 0.3);
|
||||
|
||||
.flex-center(@gap: 0) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: @gap;
|
||||
}
|
||||
|
||||
.flex-between() {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.flex-column() {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.icon-base(@size: 18px, @hover-scale: 1.1) {
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
font-size: @size;
|
||||
&:hover {
|
||||
transform: scale(@hover-scale);
|
||||
}
|
||||
}
|
||||
|
||||
.image-overlay() {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
.flex-center(15px);
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
padding: 20px;
|
||||
color: @color-text-light;
|
||||
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.7);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.menu-base() {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
width: 250px;
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
|
||||
padding: 15px;
|
||||
z-index: 1000;
|
||||
margin-top: 5px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.input-base() {
|
||||
width: 100%;
|
||||
padding: 12px 15px;
|
||||
border: 1px solid #ccc;
|
||||
background-color: #fff;
|
||||
font-family: @font-main;
|
||||
font-size: 14px;
|
||||
outline: none;
|
||||
transition: border-color 0.3s ease;
|
||||
&:focus {
|
||||
border-color: @color-primary;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,32 +0,0 @@
|
||||
<?php
|
||||
// config/database.php
|
||||
class Database {
|
||||
private static $instance = null;
|
||||
private $connection;
|
||||
|
||||
private function __construct() {
|
||||
try {
|
||||
$this->connection = new PDO(
|
||||
"pgsql:host=185.130.224.177;port=5481;dbname=postgres",
|
||||
"admin",
|
||||
"38feaad2840ccfda0e71243a6faaecfd"
|
||||
);
|
||||
$this->connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
$this->connection->exec("SET NAMES 'utf8'");
|
||||
} catch(PDOException $e) {
|
||||
die("Ошибка подключения: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public static function getInstance() {
|
||||
if (self::$instance == null) {
|
||||
self::$instance = new Database();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
public function getConnection() {
|
||||
return $this->connection;
|
||||
}
|
||||
}
|
||||
?>
|
||||
27
docker-compose.yml
Normal file
@@ -0,0 +1,27 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
apache:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
container_name: cite_practica_apache
|
||||
ports:
|
||||
- "80:80"
|
||||
volumes:
|
||||
- ./public:/var/www/html/cite_practica:rw
|
||||
- ./docker/apache/vhosts.conf:/etc/apache2/sites-available/000-default.conf:ro
|
||||
environment:
|
||||
- APACHE_DOCUMENT_ROOT=/var/www/html/cite_practica
|
||||
command: >
|
||||
bash -c "
|
||||
echo '127.0.0.1 admin' >> /etc/hosts &&
|
||||
apache2-foreground
|
||||
"
|
||||
networks:
|
||||
- cite_practica_network
|
||||
restart: unless-stopped
|
||||
|
||||
networks:
|
||||
cite_practica_network:
|
||||
driver: bridge
|
||||
12
docker/apache/entrypoint.sh
Executable file
@@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
a2enmod rewrite
|
||||
a2enmod headers
|
||||
|
||||
echo "127.0.0.1 admin" >> /etc/hosts
|
||||
|
||||
a2ensite 000-default
|
||||
|
||||
exec apache2-foreground
|
||||
|
||||
26
docker/apache/vhosts.conf
Normal file
@@ -0,0 +1,26 @@
|
||||
<VirtualHost *:80>
|
||||
ServerName admin
|
||||
ServerAlias localhost
|
||||
DocumentRoot /var/www/html
|
||||
Alias /cite_practica /var/www/html/cite_practica
|
||||
|
||||
<Directory /var/www/html>
|
||||
Options Indexes FollowSymLinks
|
||||
AllowOverride All
|
||||
Require all granted
|
||||
</Directory>
|
||||
|
||||
<Directory /var/www/html/cite_practica>
|
||||
Options Indexes FollowSymLinks
|
||||
AllowOverride All
|
||||
Require all granted
|
||||
DirectoryIndex cite_mebel.php index.php index.html
|
||||
</Directory>
|
||||
|
||||
<FilesMatch \.php$>
|
||||
SetHandler application/x-httpd-php
|
||||
</FilesMatch>
|
||||
|
||||
ErrorLog ${APACHE_LOG_DIR}/cite_practica_error.log
|
||||
CustomLog ${APACHE_LOG_DIR}/cite_practica_access.log combined
|
||||
</VirtualHost>
|
||||
@@ -1,148 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Функции авторизации для AETERNA
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../config/database.php';
|
||||
|
||||
/**
|
||||
* Авторизация пользователя
|
||||
*/
|
||||
function loginUser(string $email, string $password): array {
|
||||
$db = Database::getInstance()->getConnection();
|
||||
|
||||
try {
|
||||
$stmt = $db->prepare("
|
||||
SELECT user_id, email, password_hash, full_name, phone, city, is_admin, is_active
|
||||
FROM users WHERE email = ?
|
||||
");
|
||||
$stmt->execute([$email]);
|
||||
$user = $stmt->fetch();
|
||||
|
||||
if (!$user) {
|
||||
return ['success' => false, 'message' => 'Пользователь не найден'];
|
||||
}
|
||||
|
||||
if (!$user['is_active']) {
|
||||
return ['success' => false, 'message' => 'Аккаунт заблокирован'];
|
||||
}
|
||||
|
||||
if (!password_verify($password, $user['password_hash'])) {
|
||||
return ['success' => false, 'message' => 'Неверный пароль'];
|
||||
}
|
||||
|
||||
// Сохраняем в сессию
|
||||
$_SESSION['user_id'] = $user['user_id'];
|
||||
$_SESSION['user_email'] = $user['email'];
|
||||
$_SESSION['full_name'] = $user['full_name'];
|
||||
$_SESSION['user_phone'] = $user['phone'] ?? '';
|
||||
$_SESSION['user_city'] = $user['city'] ?? '';
|
||||
$_SESSION['isLoggedIn'] = true;
|
||||
$_SESSION['isAdmin'] = (bool)$user['is_admin'];
|
||||
$_SESSION['login_time'] = time();
|
||||
|
||||
// Обновляем время последнего входа
|
||||
$updateStmt = $db->prepare("UPDATE users SET last_login = CURRENT_TIMESTAMP WHERE user_id = ?");
|
||||
$updateStmt->execute([$user['user_id']]);
|
||||
|
||||
return ['success' => true, 'user' => $user];
|
||||
|
||||
} catch (PDOException $e) {
|
||||
return ['success' => false, 'message' => 'Ошибка базы данных'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Регистрация нового пользователя
|
||||
*/
|
||||
function registerUser(array $data): array {
|
||||
$db = Database::getInstance()->getConnection();
|
||||
|
||||
$email = trim($data['email'] ?? '');
|
||||
$password = $data['password'] ?? '';
|
||||
$fullName = trim($data['full_name'] ?? '');
|
||||
$phone = trim($data['phone'] ?? '');
|
||||
$city = trim($data['city'] ?? '');
|
||||
|
||||
// Валидация
|
||||
if (empty($email) || empty($password) || empty($fullName)) {
|
||||
return ['success' => false, 'message' => 'Заполните все обязательные поля'];
|
||||
}
|
||||
|
||||
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||
return ['success' => false, 'message' => 'Некорректный email'];
|
||||
}
|
||||
|
||||
if (strlen($password) < 6) {
|
||||
return ['success' => false, 'message' => 'Пароль должен содержать минимум 6 символов'];
|
||||
}
|
||||
|
||||
try {
|
||||
// Проверяем существование пользователя
|
||||
$checkStmt = $db->prepare("SELECT user_id FROM users WHERE email = ?");
|
||||
$checkStmt->execute([$email]);
|
||||
|
||||
if ($checkStmt->fetch()) {
|
||||
return ['success' => false, 'message' => 'Пользователь с таким email уже существует'];
|
||||
}
|
||||
|
||||
// Создаем пользователя
|
||||
$passwordHash = password_hash($password, PASSWORD_DEFAULT);
|
||||
|
||||
$stmt = $db->prepare("
|
||||
INSERT INTO users (email, password_hash, full_name, phone, city, is_active)
|
||||
VALUES (?, ?, ?, ?, ?, TRUE)
|
||||
RETURNING user_id
|
||||
");
|
||||
$stmt->execute([$email, $passwordHash, $fullName, $phone, $city]);
|
||||
$userId = $stmt->fetchColumn();
|
||||
|
||||
// Автоматически авторизуем
|
||||
$_SESSION['user_id'] = $userId;
|
||||
$_SESSION['user_email'] = $email;
|
||||
$_SESSION['full_name'] = $fullName;
|
||||
$_SESSION['user_phone'] = $phone;
|
||||
$_SESSION['user_city'] = $city;
|
||||
$_SESSION['isLoggedIn'] = true;
|
||||
$_SESSION['isAdmin'] = false;
|
||||
$_SESSION['login_time'] = time();
|
||||
|
||||
return ['success' => true, 'user_id' => $userId];
|
||||
|
||||
} catch (PDOException $e) {
|
||||
return ['success' => false, 'message' => 'Ошибка базы данных: ' . $e->getMessage()];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Выход пользователя
|
||||
*/
|
||||
function logoutUser(): void {
|
||||
$_SESSION = [];
|
||||
|
||||
if (ini_get("session.use_cookies")) {
|
||||
$params = session_get_cookie_params();
|
||||
setcookie(session_name(), '', time() - 42000,
|
||||
$params["path"], $params["domain"],
|
||||
$params["secure"], $params["httponly"]
|
||||
);
|
||||
}
|
||||
|
||||
session_destroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверка является ли пользователь администратором
|
||||
*/
|
||||
function checkAdminAccess(): bool {
|
||||
if (!isset($_SESSION['isLoggedIn']) || $_SESSION['isLoggedIn'] !== true) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isset($_SESSION['isAdmin']) || $_SESSION['isAdmin'] !== true) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
<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.php">Каталог</a></li>
|
||||
<li><a href="services.php">Услуги</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="footer__col">
|
||||
<h5>ПОМОЩЬ</h5>
|
||||
<ul>
|
||||
<li><a href="delivery.php">Доставка и оплата</a></li>
|
||||
<li><a href="warranty.php">Гарантия и возврат</a></li>
|
||||
<li><a href="index.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>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,140 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Общие функции для AETERNA
|
||||
*/
|
||||
|
||||
/**
|
||||
* Проверка авторизации пользователя
|
||||
*/
|
||||
function isLoggedIn(): bool {
|
||||
return isset($_SESSION['isLoggedIn']) && $_SESSION['isLoggedIn'] === true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверка прав администратора
|
||||
*/
|
||||
function isAdmin(): bool {
|
||||
return isset($_SESSION['isAdmin']) && $_SESSION['isAdmin'] === true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Требовать авторизацию - редирект на login если не авторизован
|
||||
*/
|
||||
function requireAuth(string $redirectUrl = 'login.php'): void {
|
||||
if (!isLoggedIn()) {
|
||||
header('Location: ' . $redirectUrl . '?error=auth_required&redirect=' . urlencode($_SERVER['REQUEST_URI']));
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Требовать права администратора
|
||||
*/
|
||||
function requireAdmin(string $redirectUrl = 'login.php'): void {
|
||||
if (!isAdmin()) {
|
||||
header('Location: ' . $redirectUrl . '?error=admin_required');
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить текущего пользователя
|
||||
*/
|
||||
function getCurrentUser(): ?array {
|
||||
if (!isLoggedIn()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [
|
||||
'user_id' => $_SESSION['user_id'] ?? 0,
|
||||
'email' => $_SESSION['user_email'] ?? '',
|
||||
'full_name' => $_SESSION['full_name'] ?? '',
|
||||
'is_admin' => isAdmin()
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Форматирование цены
|
||||
*/
|
||||
function formatPrice(float $price): string {
|
||||
return number_format($price, 0, '', ' ') . ' ₽';
|
||||
}
|
||||
|
||||
/**
|
||||
* Безопасный вывод HTML
|
||||
*/
|
||||
function e(string $str): string {
|
||||
return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
|
||||
/**
|
||||
* Генерация номера заказа
|
||||
*/
|
||||
function generateOrderNumber(): string {
|
||||
return 'AET-' . date('Ymd') . '-' . strtoupper(substr(uniqid(), -6));
|
||||
}
|
||||
|
||||
/**
|
||||
* Генерация SKU для товара
|
||||
*/
|
||||
function generateSKU(string $productName): string {
|
||||
$prefix = strtoupper(substr(preg_replace('/[^a-zA-Z0-9]/', '', transliterate($productName)), 0, 6));
|
||||
return $prefix . '-' . rand(100, 999);
|
||||
}
|
||||
|
||||
/**
|
||||
* Транслитерация кириллицы в латиницу
|
||||
*/
|
||||
function transliterate(string $str): string {
|
||||
$converter = [
|
||||
'а' => 'a', 'б' => 'b', 'в' => 'v', 'г' => 'g', 'д' => 'd',
|
||||
'е' => 'e', 'ё' => 'e', 'ж' => 'zh', 'з' => 'z', 'и' => 'i',
|
||||
'й' => 'y', 'к' => 'k', 'л' => 'l', 'м' => 'm', 'н' => 'n',
|
||||
'о' => 'o', 'п' => 'p', 'р' => 'r', 'с' => 's', 'т' => 't',
|
||||
'у' => 'u', 'ф' => 'f', 'х' => 'h', 'ц' => 'c', 'ч' => 'ch',
|
||||
'ш' => 'sh', 'щ' => 'sch', 'ь' => '', 'ы' => 'y', 'ъ' => '',
|
||||
'э' => 'e', 'ю' => 'yu', 'я' => 'ya',
|
||||
'А' => 'A', 'Б' => 'B', 'В' => 'V', 'Г' => 'G', 'Д' => 'D',
|
||||
'Е' => 'E', 'Ё' => 'E', 'Ж' => 'Zh', 'З' => 'Z', 'И' => 'I',
|
||||
'Й' => 'Y', 'К' => 'K', 'Л' => 'L', 'М' => 'M', 'Н' => 'N',
|
||||
'О' => 'O', 'П' => 'P', 'Р' => 'R', 'С' => 'S', 'Т' => 'T',
|
||||
'У' => 'U', 'Ф' => 'F', 'Х' => 'H', 'Ц' => 'C', 'Ч' => 'Ch',
|
||||
'Ш' => 'Sh', 'Щ' => 'Sch', 'Ь' => '', 'Ы' => 'Y', 'Ъ' => '',
|
||||
'Э' => 'E', 'Ю' => 'Yu', 'Я' => 'Ya',
|
||||
];
|
||||
return strtr($str, $converter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Создание slug из строки
|
||||
*/
|
||||
function createSlug(string $str): string {
|
||||
$slug = transliterate($str);
|
||||
$slug = strtolower($slug);
|
||||
$slug = preg_replace('/[^a-z0-9]+/', '-', $slug);
|
||||
$slug = trim($slug, '-');
|
||||
return $slug;
|
||||
}
|
||||
|
||||
/**
|
||||
* Показать flash-сообщение
|
||||
*/
|
||||
function setFlashMessage(string $type, string $message): void {
|
||||
$_SESSION['flash_message'] = [
|
||||
'type' => $type,
|
||||
'message' => $message
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить и удалить flash-сообщение
|
||||
*/
|
||||
function getFlashMessage(): ?array {
|
||||
if (isset($_SESSION['flash_message'])) {
|
||||
$message = $_SESSION['flash_message'];
|
||||
unset($_SESSION['flash_message']);
|
||||
return $message;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||