Delete comment

This commit is contained in:
kirill.khorkov
2025-12-16 19:18:03 +03:00
parent 474fe41d41
commit 8a93cf8657
59 changed files with 9767 additions and 10403 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,116 +1,112 @@
<?php <?php
session_start(); session_start();
require_once __DIR__ . '/../config/database.php'; require_once __DIR__ . '/../config/database.php';
if (!isset($_SESSION['isLoggedIn']) || $_SESSION['isLoggedIn'] !== true) { if (!isset($_SESSION['isLoggedIn']) || $_SESSION['isLoggedIn'] !== true) {
echo json_encode(['success' => false, 'message' => 'Требуется авторизация']); echo json_encode(['success' => false, 'message' => 'Требуется авторизация']);
exit(); exit();
} }
if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_POST['product_id'])) { if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_POST['product_id'])) {
$product_id = intval($_POST['product_id']); $product_id = intval($_POST['product_id']);
$quantity = intval($_POST['quantity'] ?? 1); $quantity = intval($_POST['quantity'] ?? 1);
$user_id = $_SESSION['user_id'] ?? 0; $user_id = $_SESSION['user_id'] ?? 0;
if ($user_id == 0) { if ($user_id == 0) {
echo json_encode(['success' => false, 'message' => 'Пользователь не найден']); echo json_encode(['success' => false, 'message' => 'Пользователь не найден']);
exit(); exit();
} }
$db = Database::getInstance()->getConnection(); $db = Database::getInstance()->getConnection();
try { try {
// Проверяем наличие товара на складе
$checkStock = $db->prepare(" $checkStock = $db->prepare("
SELECT stock_quantity, name, price SELECT stock_quantity, name, price
FROM products FROM products
WHERE product_id = ? AND is_available = TRUE WHERE product_id = ? AND is_available = TRUE
"); ");
$checkStock->execute([$product_id]); $checkStock->execute([$product_id]);
$product = $checkStock->fetch(); $product = $checkStock->fetch();
if (!$product) { if (!$product) {
echo json_encode(['success' => false, 'message' => 'Товар не найден']); echo json_encode(['success' => false, 'message' => 'Товар не найден']);
exit(); exit();
} }
if ($product['stock_quantity'] < $quantity) { if ($product['stock_quantity'] < $quantity) {
echo json_encode(['success' => false, 'message' => 'Недостаточно товара на складе']); echo json_encode(['success' => false, 'message' => 'Недостаточно товара на складе']);
exit(); exit();
} }
// Проверяем, есть ли товар уже в корзине пользователя $checkCart = $db->prepare("
$checkCart = $db->prepare(" SELECT cart_id, quantity
SELECT cart_id, quantity FROM cart
FROM cart WHERE user_id = ? AND product_id = ?
WHERE user_id = ? AND product_id = ? ");
"); $checkCart->execute([$user_id, $product_id]);
$checkCart->execute([$user_id, $product_id]); $cartItem = $checkCart->fetch();
$cartItem = $checkCart->fetch();
if ($cartItem) {
if ($cartItem) {
// Обновляем количество $newQuantity = $cartItem['quantity'] + $quantity;
$newQuantity = $cartItem['quantity'] + $quantity;
if ($newQuantity > $product['stock_quantity']) {
// Проверяем общее количество echo json_encode(['success' => false, 'message' => 'Превышено доступное количество']);
if ($newQuantity > $product['stock_quantity']) { exit();
echo json_encode(['success' => false, 'message' => 'Превышено доступное количество']); }
exit();
} $updateStmt = $db->prepare("
UPDATE cart
$updateStmt = $db->prepare(" SET quantity = ?, updated_at = CURRENT_TIMESTAMP
UPDATE cart WHERE cart_id = ?
SET quantity = ?, updated_at = CURRENT_TIMESTAMP ");
WHERE cart_id = ? $updateStmt->execute([$newQuantity, $cartItem['cart_id']]);
"); } else {
$updateStmt->execute([$newQuantity, $cartItem['cart_id']]);
} else { $insertStmt = $db->prepare("
// Добавляем новый товар INSERT INTO cart (user_id, product_id, quantity)
$insertStmt = $db->prepare(" VALUES (?, ?, ?)
INSERT INTO cart (user_id, product_id, quantity) ");
VALUES (?, ?, ?) $insertStmt->execute([$user_id, $product_id, $quantity]);
"); }
$insertStmt->execute([$user_id, $product_id, $quantity]);
} if (!isset($_SESSION['cart'])) {
$_SESSION['cart'] = [];
// Обновляем сессию }
if (!isset($_SESSION['cart'])) {
$_SESSION['cart'] = []; if (isset($_SESSION['cart'][$product_id])) {
} $_SESSION['cart'][$product_id]['quantity'] += $quantity;
} else {
if (isset($_SESSION['cart'][$product_id])) { $_SESSION['cart'][$product_id] = [
$_SESSION['cart'][$product_id]['quantity'] += $quantity; 'quantity' => $quantity,
} else { 'name' => $product['name'],
$_SESSION['cart'][$product_id] = [ 'price' => $product['price'],
'quantity' => $quantity, 'added_at' => time()
'name' => $product['name'], ];
'price' => $product['price'], }
'added_at' => time()
]; $cartCountStmt = $db->prepare("
} SELECT SUM(quantity) as total
FROM cart
// Получаем общее количество товаров в корзине WHERE user_id = ?
$cartCountStmt = $db->prepare(" ");
SELECT SUM(quantity) as total $cartCountStmt->execute([$user_id]);
FROM cart $cart_count = $cartCountStmt->fetchColumn() ?: 0;
WHERE user_id = ?
"); echo json_encode([
$cartCountStmt->execute([$user_id]); 'success' => true,
$cart_count = $cartCountStmt->fetchColumn() ?: 0; 'cart_count' => $cart_count,
'message' => 'Товар добавлен в корзину'
echo json_encode([ ]);
'success' => true,
'cart_count' => $cart_count, } catch (PDOException $e) {
'message' => 'Товар добавлен в корзину' echo json_encode([
]); 'success' => false,
'message' => 'Ошибка базы данных: ' . $e->getMessage()
} catch (PDOException $e) { ]);
echo json_encode([ }
'success' => false, } else {
'message' => 'Ошибка базы данных: ' . $e->getMessage() echo json_encode(['success' => false, 'message' => 'Неверный запрос']);
]); }
}
} else {
echo json_encode(['success' => false, 'message' => 'Неверный запрос']);
}
?> ?>

View File

@@ -1,66 +1,63 @@
<?php <?php
// login_handler.php
session_start(); session_start();
require_once __DIR__ . '/../config/database.php'; require_once __DIR__ . '/../config/database.php';
if ($_SERVER['REQUEST_METHOD'] === 'POST') { if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$email = $_POST['email'] ?? ''; $email = $_POST['email'] ?? '';
$password = $_POST['password'] ?? ''; $password = $_POST['password'] ?? '';
if (empty($email) || empty($password)) { if (empty($email) || empty($password)) {
echo json_encode(['success' => false, 'message' => 'Заполните все поля']); echo json_encode(['success' => false, 'message' => 'Заполните все поля']);
exit(); exit();
} }
$db = Database::getInstance()->getConnection(); $db = Database::getInstance()->getConnection();
try { try {
// Проверяем пользователя в базе данных
$stmt = $db->prepare(" $stmt = $db->prepare("
SELECT user_id, email, password_hash, full_name, phone, city, is_admin, is_active SELECT user_id, email, password_hash, full_name, phone, city, is_admin, is_active
FROM users FROM users
WHERE email = ? WHERE email = ?
"); ");
$stmt->execute([$email]); $stmt->execute([$email]);
$user = $stmt->fetch(); $user = $stmt->fetch();
if (!$user) { if (!$user) {
echo json_encode(['success' => false, 'message' => 'Пользователь не найден']); echo json_encode(['success' => false, 'message' => 'Пользователь не найден']);
exit(); exit();
} }
if (!$user['is_active']) { if (!$user['is_active']) {
echo json_encode(['success' => false, 'message' => 'Аккаунт заблокирован']); echo json_encode(['success' => false, 'message' => 'Аккаунт заблокирован']);
exit(); exit();
} }
// Проверяем пароль if (!password_verify($password, $user['password_hash'])) {
if (!password_verify($password, $user['password_hash'])) { echo json_encode(['success' => false, 'message' => 'Неверный пароль']);
echo json_encode(['success' => false, 'message' => 'Неверный пароль']); exit();
exit(); }
}
$_SESSION['user_id'] = $user['user_id'];
// Сохраняем в сессию $_SESSION['user_email'] = $user['email'];
$_SESSION['user_id'] = $user['user_id']; $_SESSION['full_name'] = $user['full_name'];
$_SESSION['user_email'] = $user['email']; $_SESSION['user_phone'] = $user['phone'] ?? '';
$_SESSION['full_name'] = $user['full_name']; $_SESSION['user_city'] = $user['city'] ?? '';
$_SESSION['user_phone'] = $user['phone'] ?? ''; $_SESSION['isLoggedIn'] = true;
$_SESSION['user_city'] = $user['city'] ?? ''; $_SESSION['isAdmin'] = (bool)$user['is_admin'];
$_SESSION['isLoggedIn'] = true; $_SESSION['login_time'] = time();
$_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']]);
// Обновляем время последнего входа
$updateStmt = $db->prepare("UPDATE users SET last_login = CURRENT_TIMESTAMP WHERE user_id = ?"); echo json_encode(['success' => true, 'redirect' => 'catalog.php']);
$updateStmt->execute([$user['user_id']]);
} catch (PDOException $e) {
echo json_encode(['success' => true, 'redirect' => 'catalog.php']); echo json_encode(['success' => false, 'message' => 'Ошибка базы данных']);
}
} catch (PDOException $e) {
echo json_encode(['success' => false, 'message' => 'Ошибка базы данных']); } else {
} echo json_encode(['success' => false, 'message' => 'Неверный запрос']);
}
} else {
echo json_encode(['success' => false, 'message' => 'Неверный запрос']);
}
?> ?>

View File

@@ -1,14 +1,10 @@
<?php <?php
/**
* API для работы с корзиной
* Эндпоинты: add, update, remove, get, count
*/
session_start(); session_start();
require_once __DIR__ . '/../config/database.php'; require_once __DIR__ . '/../config/database.php';
header('Content-Type: application/json; charset=utf-8'); header('Content-Type: application/json; charset=utf-8');
// Проверка авторизации
if (!isset($_SESSION['isLoggedIn']) || $_SESSION['isLoggedIn'] !== true) { if (!isset($_SESSION['isLoggedIn']) || $_SESSION['isLoggedIn'] !== true) {
echo json_encode(['success' => false, 'message' => 'Требуется авторизация']); echo json_encode(['success' => false, 'message' => 'Требуется авторизация']);
exit(); exit();
@@ -24,66 +20,64 @@ try {
case 'add': case 'add':
$productId = (int)($_POST['product_id'] ?? 0); $productId = (int)($_POST['product_id'] ?? 0);
$quantity = (int)($_POST['quantity'] ?? 1); $quantity = (int)($_POST['quantity'] ?? 1);
if ($productId <= 0) { if ($productId <= 0) {
echo json_encode(['success' => false, 'message' => 'Неверный ID товара']); echo json_encode(['success' => false, 'message' => 'Неверный ID товара']);
exit(); exit();
} }
// Проверяем существование товара
$checkProduct = $db->prepare("SELECT product_id, stock_quantity FROM products WHERE product_id = ? AND is_available = TRUE"); $checkProduct = $db->prepare("SELECT product_id, stock_quantity FROM products WHERE product_id = ? AND is_available = TRUE");
$checkProduct->execute([$productId]); $checkProduct->execute([$productId]);
$product = $checkProduct->fetch(); $product = $checkProduct->fetch();
if (!$product) { if (!$product) {
echo json_encode(['success' => false, 'message' => 'Товар не найден']); echo json_encode(['success' => false, 'message' => 'Товар не найден']);
exit(); exit();
} }
// Проверяем, есть ли товар уже в корзине
$checkCart = $db->prepare("SELECT cart_id, quantity FROM cart WHERE user_id = ? AND product_id = ?"); $checkCart = $db->prepare("SELECT cart_id, quantity FROM cart WHERE user_id = ? AND product_id = ?");
$checkCart->execute([$userId, $productId]); $checkCart->execute([$userId, $productId]);
$cartItem = $checkCart->fetch(); $cartItem = $checkCart->fetch();
if ($cartItem) { if ($cartItem) {
// Обновляем количество
$newQuantity = $cartItem['quantity'] + $quantity; $newQuantity = $cartItem['quantity'] + $quantity;
$stmt = $db->prepare("UPDATE cart SET quantity = ?, updated_at = CURRENT_TIMESTAMP WHERE cart_id = ?"); $stmt = $db->prepare("UPDATE cart SET quantity = ?, updated_at = CURRENT_TIMESTAMP WHERE cart_id = ?");
$stmt->execute([$newQuantity, $cartItem['cart_id']]); $stmt->execute([$newQuantity, $cartItem['cart_id']]);
} else { } else {
// Добавляем новый товар
$stmt = $db->prepare("INSERT INTO cart (user_id, product_id, quantity) VALUES (?, ?, ?)"); $stmt = $db->prepare("INSERT INTO cart (user_id, product_id, quantity) VALUES (?, ?, ?)");
$stmt->execute([$userId, $productId, $quantity]); $stmt->execute([$userId, $productId, $quantity]);
} }
echo json_encode(['success' => true, 'message' => 'Товар добавлен в корзину']); echo json_encode(['success' => true, 'message' => 'Товар добавлен в корзину']);
break; break;
case 'update': case 'update':
$productId = (int)($_POST['product_id'] ?? 0); $productId = (int)($_POST['product_id'] ?? 0);
$quantity = (int)($_POST['quantity'] ?? 1); $quantity = (int)($_POST['quantity'] ?? 1);
if ($quantity <= 0) { if ($quantity <= 0) {
// Удаляем товар если количество 0
$stmt = $db->prepare("DELETE FROM cart WHERE user_id = ? AND product_id = ?"); $stmt = $db->prepare("DELETE FROM cart WHERE user_id = ? AND product_id = ?");
$stmt->execute([$userId, $productId]); $stmt->execute([$userId, $productId]);
} else { } else {
$stmt = $db->prepare("UPDATE cart SET quantity = ?, updated_at = CURRENT_TIMESTAMP WHERE user_id = ? AND product_id = ?"); $stmt = $db->prepare("UPDATE cart SET quantity = ?, updated_at = CURRENT_TIMESTAMP WHERE user_id = ? AND product_id = ?");
$stmt->execute([$quantity, $userId, $productId]); $stmt->execute([$quantity, $userId, $productId]);
} }
echo json_encode(['success' => true, 'message' => 'Корзина обновлена']); echo json_encode(['success' => true, 'message' => 'Корзина обновлена']);
break; break;
case 'remove': case 'remove':
$productId = (int)($_POST['product_id'] ?? 0); $productId = (int)($_POST['product_id'] ?? 0);
$stmt = $db->prepare("DELETE FROM cart WHERE user_id = ? AND product_id = ?"); $stmt = $db->prepare("DELETE FROM cart WHERE user_id = ? AND product_id = ?");
$stmt->execute([$userId, $productId]); $stmt->execute([$userId, $productId]);
echo json_encode(['success' => true, 'message' => 'Товар удален из корзины']); echo json_encode(['success' => true, 'message' => 'Товар удален из корзины']);
break; break;
case 'get': case 'get':
$stmt = $db->prepare(" $stmt = $db->prepare("
SELECT c.cart_id, c.product_id, c.quantity, p.name, p.price, p.image_url, p.stock_quantity SELECT c.cart_id, c.product_id, c.quantity, p.name, p.price, p.image_url, p.stock_quantity
@@ -94,13 +88,13 @@ try {
"); ");
$stmt->execute([$userId]); $stmt->execute([$userId]);
$items = $stmt->fetchAll(); $items = $stmt->fetchAll();
$total = 0; $total = 0;
foreach ($items as &$item) { foreach ($items as &$item) {
$item['subtotal'] = $item['price'] * $item['quantity']; $item['subtotal'] = $item['price'] * $item['quantity'];
$total += $item['subtotal']; $total += $item['subtotal'];
} }
echo json_encode([ echo json_encode([
'success' => true, 'success' => true,
'items' => $items, 'items' => $items,
@@ -108,27 +102,26 @@ try {
'count' => array_sum(array_column($items, 'quantity')) 'count' => array_sum(array_column($items, 'quantity'))
]); ]);
break; break;
case 'count': case 'count':
$stmt = $db->prepare("SELECT COALESCE(SUM(quantity), 0) FROM cart WHERE user_id = ?"); $stmt = $db->prepare("SELECT COALESCE(SUM(quantity), 0) FROM cart WHERE user_id = ?");
$stmt->execute([$userId]); $stmt->execute([$userId]);
$count = $stmt->fetchColumn(); $count = $stmt->fetchColumn();
echo json_encode(['success' => true, 'count' => (int)$count]); echo json_encode(['success' => true, 'count' => (int)$count]);
break; break;
case 'clear': case 'clear':
$stmt = $db->prepare("DELETE FROM cart WHERE user_id = ?"); $stmt = $db->prepare("DELETE FROM cart WHERE user_id = ?");
$stmt->execute([$userId]); $stmt->execute([$userId]);
echo json_encode(['success' => true, 'message' => 'Корзина очищена']); echo json_encode(['success' => true, 'message' => 'Корзина очищена']);
break; break;
default: default:
echo json_encode(['success' => false, 'message' => 'Неизвестное действие']); echo json_encode(['success' => false, 'message' => 'Неизвестное действие']);
} }
} catch (PDOException $e) { } catch (PDOException $e) {
echo json_encode(['success' => false, 'message' => 'Ошибка базы данных: ' . $e->getMessage()]); echo json_encode(['success' => false, 'message' => 'Ошибка базы данных: ' . $e->getMessage()]);
} }

View File

@@ -1,62 +1,61 @@
<?php <?php
// get_cart.php
session_start(); session_start();
require_once __DIR__ . '/../config/database.php'; require_once __DIR__ . '/../config/database.php';
if (!isset($_SESSION['isLoggedIn']) || $_SESSION['isLoggedIn'] !== true) { if (!isset($_SESSION['isLoggedIn']) || $_SESSION['isLoggedIn'] !== true) {
echo json_encode(['success' => false, 'message' => 'Требуется авторизация']); echo json_encode(['success' => false, 'message' => 'Требуется авторизация']);
exit(); exit();
} }
$user_id = $_SESSION['user_id'] ?? 0; $user_id = $_SESSION['user_id'] ?? 0;
if ($user_id == 0) { if ($user_id == 0) {
echo json_encode(['success' => false, 'message' => 'Пользователь не найден']); echo json_encode(['success' => false, 'message' => 'Пользователь не найден']);
exit(); exit();
} }
$db = Database::getInstance()->getConnection(); $db = Database::getInstance()->getConnection();
try { try {
// Получаем корзину из БД
$stmt = $db->prepare(" $stmt = $db->prepare("
SELECT SELECT
c.cart_id, c.cart_id,
c.product_id, c.product_id,
c.quantity, c.quantity,
p.name, p.name,
p.price, p.price,
p.image_url, p.image_url,
p.stock_quantity p.stock_quantity
FROM cart c FROM cart c
JOIN products p ON c.product_id = p.product_id JOIN products p ON c.product_id = p.product_id
WHERE c.user_id = ? AND p.is_available = TRUE WHERE c.user_id = ? AND p.is_available = TRUE
ORDER BY c.created_at DESC ORDER BY c.created_at DESC
"); ");
$stmt->execute([$user_id]); $stmt->execute([$user_id]);
$cart_items = $stmt->fetchAll(); $cart_items = $stmt->fetchAll();
// Обновляем сессию $_SESSION['cart'] = [];
$_SESSION['cart'] = []; foreach ($cart_items as $item) {
foreach ($cart_items as $item) { $_SESSION['cart'][$item['product_id']] = [
$_SESSION['cart'][$item['product_id']] = [ 'quantity' => $item['quantity'],
'quantity' => $item['quantity'], 'name' => $item['name'],
'name' => $item['name'], 'price' => $item['price'],
'price' => $item['price'], 'added_at' => time()
'added_at' => time() ];
]; }
}
echo json_encode([
echo json_encode([ 'success' => true,
'success' => true, 'cart_items' => $cart_items,
'cart_items' => $cart_items, 'total_items' => count($cart_items)
'total_items' => count($cart_items) ]);
]);
} catch (PDOException $e) {
} catch (PDOException $e) { echo json_encode([
echo json_encode([ 'success' => false,
'success' => false, 'message' => 'Ошибка базы данных: ' . $e->getMessage()
'message' => 'Ошибка базы данных: ' . $e->getMessage() ]);
]); }
}
?> ?>

View File

@@ -1,22 +1,22 @@
<?php <?php
// get_cart_count.php
session_start(); session_start();
require_once __DIR__ . '/../config/database.php'; require_once __DIR__ . '/../config/database.php';
if (!isset($_SESSION['isLoggedIn']) || $_SESSION['isLoggedIn'] !== true) { if (!isset($_SESSION['isLoggedIn']) || $_SESSION['isLoggedIn'] !== true) {
echo json_encode(['success' => false, 'cart_count' => 0]); echo json_encode(['success' => false, 'cart_count' => 0]);
exit(); exit();
} }
$user_id = $_SESSION['user_id'] ?? 0; $user_id = $_SESSION['user_id'] ?? 0;
$db = Database::getInstance()->getConnection(); $db = Database::getInstance()->getConnection();
try { try {
$stmt = $db->prepare("SELECT SUM(quantity) as total FROM cart WHERE user_id = ?"); $stmt = $db->prepare("SELECT SUM(quantity) as total FROM cart WHERE user_id = ?");
$stmt->execute([$user_id]); $stmt->execute([$user_id]);
$cart_count = $stmt->fetchColumn() ?: 0; $cart_count = $stmt->fetchColumn() ?: 0;
echo json_encode(['success' => true, 'cart_count' => $cart_count]); echo json_encode(['success' => true, 'cart_count' => $cart_count]);
} catch (PDOException $e) { } catch (PDOException $e) {
echo json_encode(['success' => false, 'cart_count' => 0]); echo json_encode(['success' => false, 'cart_count' => 0]);
} }

View File

@@ -1,33 +1,32 @@
<?php <?php
session_start(); session_start();
require_once __DIR__ . '/../config/database.php'; require_once __DIR__ . '/../config/database.php';
// Проверяем авторизацию администратора if (!isset($_SESSION['isAdmin']) || $_SESSION['isAdmin'] !== true) {
if (!isset($_SESSION['isAdmin']) || $_SESSION['isAdmin'] !== true) { echo json_encode(['success' => false, 'message' => 'Доступ запрещен']);
echo json_encode(['success' => false, 'message' => 'Доступ запрещен']); exit();
exit(); }
}
if (!isset($_GET['id'])) {
if (!isset($_GET['id'])) { echo json_encode(['success' => false, 'message' => 'ID не указан']);
echo json_encode(['success' => false, 'message' => 'ID не указан']); exit();
exit(); }
}
try {
try { $db = Database::getInstance()->getConnection();
$db = Database::getInstance()->getConnection(); $product_id = $_GET['id'];
$product_id = $_GET['id'];
$stmt = $db->prepare("SELECT * FROM products WHERE product_id = ?");
$stmt = $db->prepare("SELECT * FROM products WHERE product_id = ?"); $stmt->execute([$product_id]);
$stmt->execute([$product_id]); $product = $stmt->fetch();
$product = $stmt->fetch();
if ($product) {
if ($product) { echo json_encode($product);
echo json_encode($product); } else {
} else { echo json_encode(['success' => false, 'message' => 'Товар не найден']);
echo json_encode(['success' => false, 'message' => 'Товар не найден']); }
}
} catch (PDOException $e) {
} catch (PDOException $e) { echo json_encode(['success' => false, 'message' => 'Ошибка базы данных: ' . $e->getMessage()]);
echo json_encode(['success' => false, 'message' => 'Ошибка базы данных: ' . $e->getMessage()]); }
}
?> ?>

View File

@@ -1,134 +1,124 @@
<?php <?php
// process_order.php
session_start(); session_start();
require_once __DIR__ . '/../config/database.php'; require_once __DIR__ . '/../config/database.php';
if (!isset($_SESSION['isLoggedIn']) || $_SESSION['isLoggedIn'] !== true) { if (!isset($_SESSION['isLoggedIn']) || $_SESSION['isLoggedIn'] !== true) {
header('Location: login.php?error=auth_required'); header('Location: login.php?error=auth_required');
exit(); exit();
} }
$user_id = $_SESSION['user_id'] ?? 0; $user_id = $_SESSION['user_id'] ?? 0;
if ($user_id == 0) { if ($user_id == 0) {
header('Location: login.php?error=user_not_found'); header('Location: login.php?error=user_not_found');
exit(); exit();
} }
if ($_SERVER['REQUEST_METHOD'] === 'POST') { if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$db = Database::getInstance()->getConnection(); $db = Database::getInstance()->getConnection();
try { try {
$db->beginTransaction(); $db->beginTransaction();
// Получаем данные из формы $customer_name = $_POST['full_name'] ?? '';
$customer_name = $_POST['full_name'] ?? ''; $customer_email = $_POST['email'] ?? '';
$customer_email = $_POST['email'] ?? ''; $customer_phone = $_POST['phone'] ?? '';
$customer_phone = $_POST['phone'] ?? ''; $delivery_address = $_POST['address'] ?? '';
$delivery_address = $_POST['address'] ?? ''; $region = $_POST['region'] ?? '';
$region = $_POST['region'] ?? ''; $payment_method = $_POST['payment'] ?? 'card';
$payment_method = $_POST['payment'] ?? 'card'; $delivery_method = $_POST['delivery'] ?? 'courier';
$delivery_method = $_POST['delivery'] ?? 'courier'; $notes = $_POST['notes'] ?? '';
$notes = $_POST['notes'] ?? ''; $discount_amount = floatval($_POST['discount'] ?? 0);
$discount_amount = floatval($_POST['discount'] ?? 0); $delivery_cost = floatval($_POST['delivery_price'] ?? 2000);
$delivery_cost = floatval($_POST['delivery_price'] ?? 2000);
$order_number = 'ORD-' . date('Ymd-His') . '-' . rand(1000, 9999);
// Генерируем номер заказа
$order_number = 'ORD-' . date('Ymd-His') . '-' . rand(1000, 9999); $cartStmt = $db->prepare("
SELECT
// Получаем корзину пользователя c.product_id,
$cartStmt = $db->prepare(" c.quantity,
SELECT p.name,
c.product_id, p.price,
c.quantity, p.stock_quantity
p.name, FROM cart c
p.price, JOIN products p ON c.product_id = p.product_id
p.stock_quantity WHERE c.user_id = ?
FROM cart c ");
JOIN products p ON c.product_id = p.product_id $cartStmt->execute([$user_id]);
WHERE c.user_id = ? $cart_items = $cartStmt->fetchAll();
");
$cartStmt->execute([$user_id]); if (empty($cart_items)) {
$cart_items = $cartStmt->fetchAll(); throw new Exception('Корзина пуста');
}
if (empty($cart_items)) {
throw new Exception('Корзина пуста'); $total_amount = 0;
} foreach ($cart_items as $item) {
$total_amount += $item['price'] * $item['quantity'];
// Рассчитываем итоги }
$total_amount = 0;
foreach ($cart_items as $item) { $final_amount = $total_amount - $discount_amount + $delivery_cost;
$total_amount += $item['price'] * $item['quantity'];
} $orderStmt = $db->prepare("
INSERT INTO orders (
$final_amount = $total_amount - $discount_amount + $delivery_cost; user_id, order_number, total_amount, discount_amount,
delivery_cost, final_amount, status, payment_method,
// Создаем заказ delivery_method, delivery_address, customer_name,
$orderStmt = $db->prepare(" customer_email, customer_phone, notes
INSERT INTO orders ( ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
user_id, order_number, total_amount, discount_amount, RETURNING order_id
delivery_cost, final_amount, status, payment_method, ");
delivery_method, delivery_address, customer_name,
customer_email, customer_phone, notes $orderStmt->execute([
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) $user_id, $order_number, $total_amount, $discount_amount,
RETURNING order_id $delivery_cost, $final_amount, 'pending', $payment_method,
"); $delivery_method, $delivery_address, $customer_name,
$customer_email, $customer_phone, $notes
$orderStmt->execute([ ]);
$user_id, $order_number, $total_amount, $discount_amount,
$delivery_cost, $final_amount, 'pending', $payment_method, $order_id = $orderStmt->fetchColumn();
$delivery_method, $delivery_address, $customer_name,
$customer_email, $customer_phone, $notes foreach ($cart_items as $item) {
]);
$itemStmt = $db->prepare("
$order_id = $orderStmt->fetchColumn(); INSERT INTO order_items (
order_id, product_id, product_name,
// Добавляем товары в заказ и обновляем остатки quantity, unit_price, total_price
foreach ($cart_items as $item) { ) VALUES (?, ?, ?, ?, ?, ?)
// Добавляем в order_items ");
$itemStmt = $db->prepare("
INSERT INTO order_items ( $item_total = $item['price'] * $item['quantity'];
order_id, product_id, product_name, $itemStmt->execute([
quantity, unit_price, total_price $order_id, $item['product_id'], $item['name'],
) VALUES (?, ?, ?, ?, ?, ?) $item['quantity'], $item['price'], $item_total
"); ]);
$item_total = $item['price'] * $item['quantity']; $updateStmt = $db->prepare("
$itemStmt->execute([ UPDATE products
$order_id, $item['product_id'], $item['name'], SET stock_quantity = stock_quantity - ?,
$item['quantity'], $item['price'], $item_total updated_at = CURRENT_TIMESTAMP
]); WHERE product_id = ?
");
// Обновляем остатки на складе $updateStmt->execute([$item['quantity'], $item['product_id']]);
$updateStmt = $db->prepare(" }
UPDATE products
SET stock_quantity = stock_quantity - ?, $clearCartStmt = $db->prepare("DELETE FROM cart WHERE user_id = ?");
updated_at = CURRENT_TIMESTAMP $clearCartStmt->execute([$user_id]);
WHERE product_id = ?
"); unset($_SESSION['cart']);
$updateStmt->execute([$item['quantity'], $item['product_id']]);
} $db->commit();
// Очищаем корзину header('Location: order_success.php?id=' . $order_id);
$clearCartStmt = $db->prepare("DELETE FROM cart WHERE user_id = ?"); exit();
$clearCartStmt->execute([$user_id]);
} catch (Exception $e) {
// Очищаем сессию $db->rollBack();
unset($_SESSION['cart']); header('Location: checkout.php?error=' . urlencode($e->getMessage()));
exit();
$db->commit(); }
} else {
// Перенаправляем на страницу успеха header('Location: checkout.php');
header('Location: order_success.php?id=' . $order_id); exit();
exit(); }
} catch (Exception $e) {
$db->rollBack();
header('Location: checkout.php?error=' . urlencode($e->getMessage()));
exit();
}
} else {
header('Location: checkout.php');
exit();
}
?> ?>

View File

@@ -1,182 +1,151 @@
<?php <?php
// register_handler.php
session_start(); session_start();
require_once __DIR__ . '/../config/database.php'; require_once __DIR__ . '/../config/database.php';
if ($_SERVER['REQUEST_METHOD'] === 'POST') { if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$errors = []; $errors = [];
// Получаем данные из формы $full_name = trim($_POST['fio'] ?? '');
$full_name = trim($_POST['fio'] ?? ''); $city = trim($_POST['city'] ?? '');
$city = trim($_POST['city'] ?? ''); $email = trim($_POST['email'] ?? '');
$email = trim($_POST['email'] ?? ''); $phone = trim($_POST['phone'] ?? '');
$phone = trim($_POST['phone'] ?? ''); $password = $_POST['password'] ?? '';
$password = $_POST['password'] ?? ''; $confirm_password = $_POST['confirm-password'] ?? '';
$confirm_password = $_POST['confirm-password'] ?? '';
if (empty($full_name) || strlen($full_name) < 3) {
// Валидация данных $errors[] = 'ФИО должно содержать минимум 3 символа';
if (empty($full_name) || strlen($full_name) < 3) { }
$errors[] = 'ФИО должно содержать минимум 3 символа';
} if (empty($city) || strlen($city) < 2) {
$errors[] = 'Введите корректное название города';
if (empty($city) || strlen($city) < 2) { }
$errors[] = 'Введите корректное название города';
} if (empty($email) || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
$errors[] = 'Введите корректный email адрес';
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($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 (empty($password) || strlen($password) < 6) { }
$errors[] = 'Пароль должен содержать минимум 6 символов';
} if ($password !== $confirm_password) {
$errors[] = 'Пароли не совпадают';
if ($password !== $confirm_password) { }
$errors[] = 'Пароли не совпадают';
} if (!isset($_POST['privacy']) || $_POST['privacy'] !== 'on') {
$errors[] = 'Необходимо согласие с условиями обработки персональных данных';
// Проверка согласия с условиями }
if (!isset($_POST['privacy']) || $_POST['privacy'] !== 'on') {
$errors[] = 'Необходимо согласие с условиями обработки персональных данных'; if (!empty($errors)) {
} $_SESSION['registration_errors'] = $errors;
$_SESSION['old_data'] = [
// Если есть ошибки, возвращаем на форму 'fio' => $full_name,
if (!empty($errors)) { 'city' => $city,
$_SESSION['registration_errors'] = $errors; 'email' => $email,
$_SESSION['old_data'] = [ 'phone' => $phone
'fio' => $full_name, ];
'city' => $city, header('Location: register.php');
'email' => $email, exit();
'phone' => $phone }
];
header('Location: register.php'); $db = Database::getInstance()->getConnection();
exit();
} try {
// Подключаемся к базе данных $checkStmt = $db->prepare("SELECT user_id FROM users WHERE email = ?");
$db = Database::getInstance()->getConnection(); $checkStmt->execute([$email]);
try { if ($checkStmt->fetch()) {
// Проверяем, существует ли пользователь с таким email $_SESSION['registration_errors'] = [ользователь с таким email уже существует'];
$checkStmt = $db->prepare("SELECT user_id FROM users WHERE email = ?"); $_SESSION['old_data'] = [
$checkStmt->execute([$email]); 'fio' => $full_name,
'city' => $city,
if ($checkStmt->fetch()) { 'email' => $email,
$_SESSION['registration_errors'] = ['Пользователь с таким email уже существует']; 'phone' => $phone
$_SESSION['old_data'] = [ ];
'fio' => $full_name, header('Location: register.php');
'city' => $city, exit();
'email' => $email, }
'phone' => $phone
]; $password_hash = password_hash($password, PASSWORD_DEFAULT);
header('Location: register.php');
exit(); $is_admin = false;
} $admin_emails = ['admin@aeterna.ru', 'administrator@aeterna.ru', 'aeterna@mail.ru'];
if (in_array(strtolower($email), $admin_emails)) {
// Хэшируем пароль $is_admin = true;
$password_hash = password_hash($password, PASSWORD_DEFAULT); }
// Определяем, является ли пользователь администратором $stmt = $db->prepare("
$is_admin = false; INSERT INTO users (email, password_hash, full_name, phone, city, is_admin)
$admin_emails = ['admin@aeterna.ru', 'administrator@aeterna.ru', 'aeterna@mail.ru']; VALUES (?, ?, ?, ?, ?, ?)
if (in_array(strtolower($email), $admin_emails)) { RETURNING user_id
$is_admin = true; ");
}
$stmt->execute([
// РАЗНЫЕ ВАРИАНТЫ ДЛЯ ТЕСТИРОВАНИЯ - РАСКОММЕНТИРУЙТЕ НУЖНЫЙ $email,
$password_hash,
// Вариант 1: С явным преобразованием boolean (самый надежный) $full_name,
$stmt = $db->prepare(" $phone,
INSERT INTO users (email, password_hash, full_name, phone, city, is_admin) $city,
VALUES (?, ?, ?, ?, ?, ?) $is_admin ? 1 : 0
RETURNING user_id ]);
");
$user_id = $stmt->fetchColumn();
// Преобразуем boolean в integer для PostgreSQL
$stmt->execute([ if ($user_id) {
$email,
$password_hash, $_SESSION['user_id'] = $user_id;
$full_name, $_SESSION['user_email'] = $email;
$phone, $_SESSION['full_name'] = $full_name;
$city, $_SESSION['user_phone'] = $phone;
$is_admin ? 1 : 0 // Преобразуем в integer (1 или 0) $_SESSION['user_city'] = $city;
]); $_SESSION['isLoggedIn'] = true;
$_SESSION['isAdmin'] = $is_admin;
// Вариант 2: С использованием CAST в SQL (альтернатива) $_SESSION['login_time'] = time();
/*
$stmt = $db->prepare(" $updateStmt = $db->prepare("UPDATE users SET last_login = CURRENT_TIMESTAMP WHERE user_id = ?");
INSERT INTO users (email, password_hash, full_name, phone, city, is_admin) $updateStmt->execute([$user_id]);
VALUES (?, ?, ?, ?, ?, CAST(? AS boolean))
RETURNING user_id $_SESSION['registration_success'] = 'Регистрация прошла успешно! ' .
"); ($is_admin ? 'Вы зарегистрированы как администратор.' : 'Добро пожаловать в AETERNA!');
$stmt->execute([ header('Location: catalog.php');
$email, exit();
$password_hash, } else {
$full_name, throw new Exception('Ошибка при создании пользователя');
$phone, }
$city,
$is_admin ? 'true' : 'false' // Строковые значения true/false } catch (PDOException $e) {
]);
*/ error_log("Registration DB Error: " . $e->getMessage());
error_log("SQL State: " . $e->getCode());
$user_id = $stmt->fetchColumn(); error_log("Email: " . $email);
if ($user_id) { $_SESSION['registration_errors'] = ['Ошибка базы данных: ' . $e->getMessage()];
// Автоматически авторизуем пользователя $_SESSION['old_data'] = [
$_SESSION['user_id'] = $user_id; 'fio' => $full_name,
$_SESSION['user_email'] = $email; 'city' => $city,
$_SESSION['full_name'] = $full_name; 'email' => $email,
$_SESSION['user_phone'] = $phone; 'phone' => $phone
$_SESSION['user_city'] = $city; ];
$_SESSION['isLoggedIn'] = true; header('Location: register.php');
$_SESSION['isAdmin'] = $is_admin; exit();
$_SESSION['login_time'] = time(); } catch (Exception $e) {
error_log("Registration Error: " . $e->getMessage());
// Обновляем время последнего входа
$updateStmt = $db->prepare("UPDATE users SET last_login = CURRENT_TIMESTAMP WHERE user_id = ?"); $_SESSION['registration_errors'] = [$e->getMessage()];
$updateStmt->execute([$user_id]); header('Location: register.php');
exit();
// Перенаправляем на главную или каталог }
$_SESSION['registration_success'] = 'Регистрация прошла успешно! ' .
($is_admin ? 'Вы зарегистрированы как администратор.' : 'Добро пожаловать в AETERNA!'); } else {
header('Location: catalog.php'); header('Location: register.php');
exit(); 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();
}
?> ?>

View File

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

View File

@@ -1,346 +1,306 @@
// script.js
$(document).ready(function() {
$(document).ready(function() {
// Инициализация корзины let cart = {
let cart = { items: [
items: [ { id: 1, name: 'Кресло OPPORTUNITY', price: 16999, quantity: 1 },
{ id: 1, name: 'Кресло OPPORTUNITY', price: 16999, quantity: 1 }, { id: 2, name: 'Кресло GOLDEN', price: 19999, quantity: 1 },
{ id: 2, name: 'Кресло GOLDEN', price: 19999, quantity: 1 }, { id: 3, name: 'Светильник POLET', price: 7999, quantity: 1 }
{ id: 3, name: 'Светильник POLET', price: 7999, quantity: 1 } ],
], delivery: 2000,
delivery: 2000, discount: 0
discount: 0 };
};
function updateTotal() {
// Функция обновления общей суммы let productsTotal = 0;
function updateTotal() { let totalCount = 0;
let productsTotal = 0;
let totalCount = 0; $('.products__item').each(function() {
const $item = $(this);
// Пересчитываем товары const price = parseInt($item.data('price'));
$('.products__item').each(function() { const quantity = parseInt($item.find('.products__qty-value').text());
const $item = $(this);
const price = parseInt($item.data('price')); productsTotal += price * quantity;
const quantity = parseInt($item.find('.products__qty-value').text()); totalCount += quantity;
});
productsTotal += price * quantity;
totalCount += quantity; $('.products-total').text(productsTotal + ' ₽');
}); $('.summary-count').text(totalCount);
$('.total-count').text(totalCount + ' шт.');
// Обновляем отображение $('.cart-count').text(totalCount);
$('.products-total').text(productsTotal + ' ₽');
$('.summary-count').text(totalCount); const finalTotal = productsTotal + cart.delivery - cart.discount;
$('.total-count').text(totalCount + ' шт.'); $('.final-total').text(finalTotal + ' ');
$('.cart-count').text(totalCount); }
// Обновляем итоговую сумму function validateEmail(email) {
const finalTotal = productsTotal + cart.delivery - cart.discount; const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
$('.final-total').text(finalTotal + ' ₽'); return emailRegex.test(email);
} }
// Функция валидации email function validateFullName(name) {
function validateEmail(email) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; const nameRegex = /^[a-zA-Zа-яА-ЯёЁ\s\-']+$/;
return emailRegex.test(email);
} const words = name.trim().split(/\s+/);
// Функция валидации имени (ФИО) return nameRegex.test(name) && words.length >= 2;
function validateFullName(name) { }
// Проверяем, что имя содержит только буквы, пробелы, дефисы и апострофы
const nameRegex = /^[a-zA-Zа-яА-ЯёЁ\s\-']+$/; function validatePhone(phone) {
const phoneRegex = /^\+7\d{10}$/;
// Проверяем, что имя состоит минимум из 2 слов return phoneRegex.test(phone);
const words = name.trim().split(/\s+/); }
return nameRegex.test(name) && words.length >= 2; function showMessage(messageId, duration = 5000) {
} $('.message').hide();
// Функция валидации телефона $(messageId).fadeIn(300);
function validatePhone(phone) {
// Российский формат телефона: +7XXXXXXXXXX if (duration > 0) {
const phoneRegex = /^\+7\d{10}$/; setTimeout(() => {
return phoneRegex.test(phone); $(messageId).fadeOut(300);
} }, duration);
}
// Функция отображения сообщения }
function showMessage(messageId, duration = 5000) {
// Скрываем все сообщения function showPrivacyError(show) {
$('.message').hide(); if (show) {
$('#privacy-error').show();
// Показываем нужное сообщение } else {
$(messageId).fadeIn(300); $('#privacy-error').hide();
}
// Автоматически скрываем через указанное время }
if (duration > 0) {
setTimeout(() => { function showFieldError(fieldId, message) {
$(messageId).fadeOut(300); $(fieldId).removeClass('error-input');
}, duration); $(fieldId + '-error').remove();
}
} if (message) {
$(fieldId).addClass('error-input');
// Функция показа ошибки приватности $(fieldId).after('<div class="field-error" id="' + fieldId.replace('#', '') + '-error">' + message + '</div>');
function showPrivacyError(show) { }
if (show) { }
$('#privacy-error').show();
} else { $('#fullname').on('blur', function() {
$('#privacy-error').hide(); const value = $(this).val().trim();
} if (value) {
} if (!validateFullName(value)) {
showFieldError('#fullname', 'ФИО должно содержать только буквы и состоять минимум из 2 слов');
// Функция отображения ошибки конкретного поля } else {
function showFieldError(fieldId, message) { showFieldError('#fullname', '');
// Убираем старые ошибки }
$(fieldId).removeClass('error-input'); } else {
$(fieldId + '-error').remove(); showFieldError('#fullname', '');
}
if (message) { });
$(fieldId).addClass('error-input');
$(fieldId).after('<div class="field-error" id="' + fieldId.replace('#', '') + '-error">' + message + '</div>'); $('#email').on('blur', function() {
} const value = $(this).val().trim();
} if (value) {
if (!validateEmail(value)) {
// Валидация поля при потере фокуса с указанием конкретной ошибки showFieldError('#email', 'Введите корректный email адрес (например: example@mail.ru)');
$('#fullname').on('blur', function() { } else {
const value = $(this).val().trim(); showFieldError('#email', '');
if (value) { }
if (!validateFullName(value)) { } else {
showFieldError('#fullname', 'ФИО должно содержать только буквы и состоять минимум из 2 слов'); showFieldError('#email', '');
} else { }
showFieldError('#fullname', ''); });
}
} else { $('#phone').on('blur', function() {
showFieldError('#fullname', ''); const value = $(this).val().trim();
} if (value) {
}); if (!validatePhone(value)) {
showFieldError('#phone', 'Введите номер в формате +7XXXXXXXXXX (10 цифр после +7)');
$('#email').on('blur', function() { } else {
const value = $(this).val().trim(); showFieldError('#phone', '');
if (value) { }
if (!validateEmail(value)) { } else {
showFieldError('#email', 'Введите корректный email адрес (например: example@mail.ru)'); showFieldError('#phone', '');
} else { }
showFieldError('#email', ''); });
}
} else { $('#region').on('blur', function() {
showFieldError('#email', ''); const value = $(this).val().trim();
} if (!value) {
}); showFieldError('#region', 'Укажите регион доставки');
} else {
$('#phone').on('blur', function() { showFieldError('#region', '');
const value = $(this).val().trim(); }
if (value) { });
if (!validatePhone(value)) {
showFieldError('#phone', 'Введите номер в формате +7XXXXXXXXXX (10 цифр после +7)'); $('#address').on('blur', function() {
} else { const value = $(this).val().trim();
showFieldError('#phone', ''); if (!value) {
} showFieldError('#address', 'Укажите адрес доставки (улица, дом, квартира)');
} else { } else {
showFieldError('#phone', ''); showFieldError('#address', '');
} }
}); });
// Валидация обязательных полей $('.form__input').on('input', function() {
$('#region').on('blur', function() { const fieldId = '#' + $(this).attr('id');
const value = $(this).val().trim(); showFieldError(fieldId, '');
if (!value) { });
showFieldError('#region', 'Укажите регион доставки');
} else { $('.products__qty-btn.plus').click(function() {
showFieldError('#region', ''); const $qtyValue = $(this).siblings('.products__qty-value');
} let quantity = parseInt($qtyValue.text());
}); $qtyValue.text(quantity + 1);
updateTotal();
$('#address').on('blur', function() { });
const value = $(this).val().trim();
if (!value) { $('.products__qty-btn.minus').click(function() {
showFieldError('#address', 'Укажите адрес доставки (улица, дом, квартира)'); const $qtyValue = $(this).siblings('.products__qty-value');
} else { let quantity = parseInt($qtyValue.text());
showFieldError('#address', ''); if (quantity > 1) {
} $qtyValue.text(quantity - 1);
}); updateTotal();
}
// Очистка ошибки при начале ввода });
$('.form__input').on('input', function() {
const fieldId = '#' + $(this).attr('id'); $('.remove-from-cart').click(function() {
showFieldError(fieldId, ''); const $productItem = $(this).closest('.products__item');
}); $productItem.fadeOut(300, function() {
$(this).remove();
// Обработчик увеличения количества updateTotal();
$('.products__qty-btn.plus').click(function() {
const $qtyValue = $(this).siblings('.products__qty-value'); if ($('.products__item').length === 0) {
let quantity = parseInt($qtyValue.text()); $('.products__list').html('<div class="empty-cart">Корзина пуста</div>');
$qtyValue.text(quantity + 1); }
updateTotal(); });
}); });
// Обработчик уменьшения количества $('.promo__btn').click(function() {
$('.products__qty-btn.minus').click(function() { const promoCode = $('.promo__input').val().toUpperCase();
const $qtyValue = $(this).siblings('.products__qty-value');
let quantity = parseInt($qtyValue.text()); if (promoCode === 'SALE10') {
if (quantity > 1) { cart.discount = Math.round(parseInt($('.products-total').text()) * 0.1);
$qtyValue.text(quantity - 1); $('.discount-total').text(cart.discount + ' ₽');
updateTotal(); showMessage('#form-error', 3000);
} $('#form-error').text('Промокод применен! Скидка 10%').removeClass('error').addClass('success');
}); } else if (promoCode === 'FREE') {
cart.delivery = 0;
// Обработчик удаления товара $('.delivery-price').text('0 ₽');
$('.remove-from-cart').click(function() { showMessage('#form-error', 3000);
const $productItem = $(this).closest('.products__item'); $('#form-error').text('Промокод применен! Бесплатная доставка').removeClass('error').addClass('success');
$productItem.fadeOut(300, function() { } else if (promoCode) {
$(this).remove(); showMessage('#form-error', 3000);
updateTotal(); $('#form-error').text('Промокод недействителен').removeClass('success').addClass('error');
}
// Показываем сообщение, если корзина пуста
if ($('.products__item').length === 0) { updateTotal();
$('.products__list').html('<div class="empty-cart">Корзина пуста</div>'); });
}
}); $('input[name="delivery"]').change(function() {
}); if ($(this).val() === 'pickup') {
cart.delivery = 0;
// Обработчик применения промокода $('.delivery-price').text('0 ₽');
$('.promo__btn').click(function() { } else {
const promoCode = $('.promo__input').val().toUpperCase(); cart.delivery = 2000;
$('.delivery-price').text('2000 ₽');
if (promoCode === 'SALE10') { }
cart.discount = Math.round(parseInt($('.products-total').text()) * 0.1); updateTotal();
$('.discount-total').text(cart.discount + ' ₽'); });
showMessage('#form-error', 3000);
$('#form-error').text('Промокод применен! Скидка 10%').removeClass('error').addClass('success'); function validateForm() {
} else if (promoCode === 'FREE') { let isValid = true;
cart.delivery = 0; let errorMessages = [];
$('.delivery-price').text('0 ₽');
showMessage('#form-error', 3000); $('.field-error').remove();
$('#form-error').text('Промокод применен! Бесплатная доставка').removeClass('error').addClass('success'); $('.form__input').removeClass('error-input');
} else if (promoCode) {
showMessage('#form-error', 3000); const requiredFields = [
$('#form-error').text('Промокод недействителен').removeClass('success').addClass('error'); {
} id: '#fullname',
value: $('#fullname').val().trim(),
updateTotal(); validator: validateFullName,
}); required: true,
message: 'ФИО должно содержать только буквы и состоять минимум из 2 слов'
// Обработчик выбора доставки },
$('input[name="delivery"]').change(function() { {
if ($(this).val() === 'pickup') { id: '#phone',
cart.delivery = 0; value: $('#phone').val().trim(),
$('.delivery-price').text('0 ₽'); validator: validatePhone,
} else { required: true,
cart.delivery = 2000; message: 'Введите корректный номер телефона в формате +7XXXXXXXXXX'
$('.delivery-price').text('2000 ₽'); },
} {
updateTotal(); id: '#email',
}); value: $('#email').val().trim(),
validator: validateEmail,
// Функция проверки всех полей формы required: true,
function validateForm() { message: 'Введите корректный email адрес'
let isValid = true; },
let errorMessages = []; {
id: '#region',
// Очищаем все старые ошибки value: $('#region').val().trim(),
$('.field-error').remove(); validator: (val) => val.length > 0,
$('.form__input').removeClass('error-input'); required: true,
message: 'Поле "Регион" обязательно для заполнения'
// Проверка обязательных полей },
const requiredFields = [ {
{ id: '#address',
id: '#fullname', value: $('#address').val().trim(),
value: $('#fullname').val().trim(), validator: (val) => val.length > 0,
validator: validateFullName, required: true,
required: true, message: 'Поле "Адрес" обязательно для заполнения'
message: 'ФИО должно содержать только буквы и состоять минимум из 2 слов' }
}, ];
{
id: '#phone', requiredFields.forEach(field => {
value: $('#phone').val().trim(), if (field.required && (!field.value || !field.validator(field.value))) {
validator: validatePhone, isValid = false;
required: true, errorMessages.push(field.message);
message: 'Введите корректный номер телефона в формате +7XXXXXXXXXX' showFieldError(field.id, field.message);
}, }
{ });
id: '#email',
value: $('#email').val().trim(), if (!$('#privacy-checkbox').is(':checked')) {
validator: validateEmail, isValid = false;
required: true, showPrivacyError(true);
message: 'Введите корректный email адрес' errorMessages.push('Необходимо согласие на обработку персональных данных');
}, } else {
{ showPrivacyError(false);
id: '#region', }
value: $('#region').val().trim(),
validator: (val) => val.length > 0, if (!isValid && errorMessages.length > 0) {
required: true, showMessage('#form-error', 5000);
message: 'Поле "Регион" обязательно для заполнения' $('#form-error').text('Исправьте следующие ошибки: ' + errorMessages.join('; ')).removeClass('success').addClass('error');
},
{ $('html, body').animate({
id: '#address', scrollTop: $('.error-input').first().offset().top - 100
value: $('#address').val().trim(), }, 500);
validator: (val) => val.length > 0, }
required: true,
message: 'Поле "Адрес" обязательно для заполнения' return isValid;
} }
];
$('#submit-order').click(function() {
// Проверяем каждое поле if (!validateForm()) {
requiredFields.forEach(field => { return;
if (field.required && (!field.value || !field.validator(field.value))) { }
isValid = false;
errorMessages.push(field.message); $(this).prop('disabled', true).text('ОБРАБОТКА...');
showFieldError(field.id, field.message);
} setTimeout(() => {
}); showMessage('#order-success', 5000);
$(this).prop('disabled', false).text('ОФОРМИТЬ ЗАКАЗ');
// Проверка согласия на обработку данных
if (!$('#privacy-checkbox').is(':checked')) { }, 2000);
isValid = false; });
showPrivacyError(true);
errorMessages.push('Необходимо согласие на обработку персональных данных'); $('#phone').on('input', function() {
} else { let phone = $(this).val().replace(/\D/g, '');
showPrivacyError(false); if (phone.length > 0) {
} if (phone[0] !== '7' && phone[0] !== '8') {
phone = '7' + phone;
// Показываем общее сообщение, если есть ошибки }
if (!isValid && errorMessages.length > 0) { phone = '+7' + phone.substring(1, 11);
showMessage('#form-error', 5000); $(this).val(phone);
$('#form-error').text('Исправьте следующие ошибки: ' + errorMessages.join('; ')).removeClass('success').addClass('error'); }
});
// Прокручиваем к первой ошибке
$('html, body').animate({ updateTotal();
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();
});

View File

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

View File

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

View File

@@ -83,4 +83,3 @@
border-color: @color-primary; border-color: @color-primary;
} }
} }

View File

@@ -1,114 +1,107 @@
// check_auth.js
$(document).ready(function() { $(document).ready(function() {
// Проверка авторизации при загрузке страницы
checkAuthStatus(); checkAuthStatus();
// Обработка формы входа $('#loginForm').on('submit', function(e) {
$('#loginForm').on('submit', function(e) { e.preventDefault();
e.preventDefault();
const email = $('#login-email').val();
const email = $('#login-email').val(); const password = $('#login-password').val();
const password = $('#login-password').val(); const remember = $('#remember').is(':checked');
const remember = $('#remember').is(':checked');
$.ajax({
$.ajax({ url: 'login_handler.php',
url: 'login_handler.php', method: 'POST',
method: 'POST', data: {
data: { email: email,
email: email, password: password
password: password },
}, success: function(response) {
success: function(response) { try {
try { const result = JSON.parse(response);
const result = JSON.parse(response); if (result.success) {
if (result.success) {
// Сохраняем в localStorage если выбрано "Запомнить меня" if (remember) {
if (remember) { localStorage.setItem('rememberedEmail', email);
localStorage.setItem('rememberedEmail', email); } else {
} else { localStorage.removeItem('rememberedEmail');
localStorage.removeItem('rememberedEmail'); }
}
window.location.href = result.redirect || 'catalog.php';
// Перенаправляем } else {
window.location.href = result.redirect || 'catalog.php'; showMessage('error', result.message || 'Ошибка авторизации');
} else { }
showMessage('error', result.message || 'Ошибка авторизации'); } catch(e) {
} showMessage('error', 'Ошибка обработки ответа');
} catch(e) { }
showMessage('error', 'Ошибка обработки ответа'); },
} error: function() {
}, showMessage('error', 'Ошибка сервера');
error: function() { }
showMessage('error', 'Ошибка сервера'); });
} });
});
}); function checkAuthStatus() {
$.ajax({
// Проверка статуса авторизации url: 'check_auth_status.php',
function checkAuthStatus() { method: 'GET',
$.ajax({ success: function(response) {
url: 'check_auth_status.php', try {
method: 'GET', const result = JSON.parse(response);
success: function(response) { if (result.loggedIn) {
try { updateUserProfile(result.user);
const result = JSON.parse(response); }
if (result.loggedIn) { } catch(e) {
updateUserProfile(result.user); console.error('Ошибка проверки авторизации', e);
} }
} catch(e) { }
console.error('Ошибка проверки авторизации', e); });
} }
}
}); function updateUserProfile(user) {
}
if ($('#userEmail').length) {
// Обновление профиля пользователя $('#userEmail').text(user.email);
function updateUserProfile(user) { }
// Обновляем шапку, если есть элементы для профиля if ($('#userName').length) {
if ($('#userEmail').length) { $('#userName').text(user.full_name);
$('#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);
function showMessage(type, text) { } else {
const $message = $('#' + type + 'Message'); alert(text);
if ($message.length) { }
$message.text(text).fadeIn(); }
setTimeout(() => $message.fadeOut(), 5000);
} else { function checkAuth(redirectUrl) {
alert(text); $.ajax({
} url: 'check_auth_status.php',
} method: 'GET',
success: function(response) {
// Проверка авторизации для ссылок try {
function checkAuth(redirectUrl) { const result = JSON.parse(response);
$.ajax({ if (result.loggedIn) {
url: 'check_auth_status.php', window.location.href = redirectUrl;
method: 'GET', } else {
success: function(response) {
try { showLoginModal(redirectUrl);
const result = JSON.parse(response); }
if (result.loggedIn) { } catch(e) {
window.location.href = redirectUrl; showLoginModal(redirectUrl);
} else { }
// Показываем модальное окно или перенаправляем на вход }
showLoginModal(redirectUrl); });
} return false;
} catch(e) { }
showLoginModal(redirectUrl);
} function showLoginModal(redirectUrl) {
}
}); window.location.href = 'вход.php?redirect=' + encodeURIComponent(redirectUrl);
return false; }
} });
// Показать модальное окно входа
function showLoginModal(redirectUrl) {
// Можно реализовать модальное окно или перенаправить на страницу входа
window.location.href = 'вход.php?redirect=' + encodeURIComponent(redirectUrl);
}
});

View File

@@ -1,32 +1,32 @@
<?php <?php
// config/database.php
class Database { class Database {
private static $instance = null; private static $instance = null;
private $connection; private $connection;
private function __construct() { private function __construct() {
try { try {
$this->connection = new PDO( $this->connection = new PDO(
"pgsql:host=185.130.224.177;port=5481;dbname=postgres", "pgsql:host=185.130.224.177;port=5481;dbname=postgres",
"admin", "admin",
"38feaad2840ccfda0e71243a6faaecfd" "38feaad2840ccfda0e71243a6faaecfd"
); );
$this->connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $this->connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->connection->exec("SET NAMES 'utf8'"); $this->connection->exec("SET NAMES 'utf8'");
} catch(PDOException $e) { } catch(PDOException $e) {
die("Ошибка подключения: " . $e->getMessage()); die("Ошибка подключения: " . $e->getMessage());
} }
} }
public static function getInstance() { public static function getInstance() {
if (self::$instance == null) { if (self::$instance == null) {
self::$instance = new Database(); self::$instance = new Database();
} }
return self::$instance; return self::$instance;
} }
public function getConnection() { public function getConnection() {
return $this->connection; return $this->connection;
} }
} }
?> ?>

View File

@@ -1,16 +1,10 @@
<?php <?php
/**
* Функции авторизации для AETERNA
*/
require_once __DIR__ . '/../config/database.php'; require_once __DIR__ . '/../config/database.php';
/**
* Авторизация пользователя
*/
function loginUser(string $email, string $password): array { function loginUser(string $email, string $password): array {
$db = Database::getInstance()->getConnection(); $db = Database::getInstance()->getConnection();
try { try {
$stmt = $db->prepare(" $stmt = $db->prepare("
SELECT user_id, email, password_hash, full_name, phone, city, is_admin, is_active SELECT user_id, email, password_hash, full_name, phone, city, is_admin, is_active
@@ -31,7 +25,6 @@ function loginUser(string $email, string $password): array {
return ['success' => false, 'message' => 'Неверный пароль']; return ['success' => false, 'message' => 'Неверный пароль'];
} }
// Сохраняем в сессию
$_SESSION['user_id'] = $user['user_id']; $_SESSION['user_id'] = $user['user_id'];
$_SESSION['user_email'] = $user['email']; $_SESSION['user_email'] = $user['email'];
$_SESSION['full_name'] = $user['full_name']; $_SESSION['full_name'] = $user['full_name'];
@@ -41,7 +34,6 @@ function loginUser(string $email, string $password): array {
$_SESSION['isAdmin'] = (bool)$user['is_admin']; $_SESSION['isAdmin'] = (bool)$user['is_admin'];
$_SESSION['login_time'] = time(); $_SESSION['login_time'] = time();
// Обновляем время последнего входа
$updateStmt = $db->prepare("UPDATE users SET last_login = CURRENT_TIMESTAMP WHERE user_id = ?"); $updateStmt = $db->prepare("UPDATE users SET last_login = CURRENT_TIMESTAMP WHERE user_id = ?");
$updateStmt->execute([$user['user_id']]); $updateStmt->execute([$user['user_id']]);
@@ -52,43 +44,38 @@ function loginUser(string $email, string $password): array {
} }
} }
/**
* Регистрация нового пользователя
*/
function registerUser(array $data): array { function registerUser(array $data): array {
$db = Database::getInstance()->getConnection(); $db = Database::getInstance()->getConnection();
$email = trim($data['email'] ?? ''); $email = trim($data['email'] ?? '');
$password = $data['password'] ?? ''; $password = $data['password'] ?? '';
$fullName = trim($data['full_name'] ?? ''); $fullName = trim($data['full_name'] ?? '');
$phone = trim($data['phone'] ?? ''); $phone = trim($data['phone'] ?? '');
$city = trim($data['city'] ?? ''); $city = trim($data['city'] ?? '');
// Валидация
if (empty($email) || empty($password) || empty($fullName)) { if (empty($email) || empty($password) || empty($fullName)) {
return ['success' => false, 'message' => 'Заполните все обязательные поля']; return ['success' => false, 'message' => 'Заполните все обязательные поля'];
} }
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
return ['success' => false, 'message' => 'Некорректный email']; return ['success' => false, 'message' => 'Некорректный email'];
} }
if (strlen($password) < 6) { if (strlen($password) < 6) {
return ['success' => false, 'message' => 'Пароль должен содержать минимум 6 символов']; return ['success' => false, 'message' => 'Пароль должен содержать минимум 6 символов'];
} }
try { try {
// Проверяем существование пользователя
$checkStmt = $db->prepare("SELECT user_id FROM users WHERE email = ?"); $checkStmt = $db->prepare("SELECT user_id FROM users WHERE email = ?");
$checkStmt->execute([$email]); $checkStmt->execute([$email]);
if ($checkStmt->fetch()) { if ($checkStmt->fetch()) {
return ['success' => false, 'message' => 'Пользователь с таким email уже существует']; return ['success' => false, 'message' => 'Пользователь с таким email уже существует'];
} }
// Создаем пользователя
$passwordHash = password_hash($password, PASSWORD_DEFAULT); $passwordHash = password_hash($password, PASSWORD_DEFAULT);
$stmt = $db->prepare(" $stmt = $db->prepare("
INSERT INTO users (email, password_hash, full_name, phone, city, is_active) INSERT INTO users (email, password_hash, full_name, phone, city, is_active)
VALUES (?, ?, ?, ?, ?, TRUE) VALUES (?, ?, ?, ?, ?, TRUE)
@@ -96,8 +83,7 @@ function registerUser(array $data): array {
"); ");
$stmt->execute([$email, $passwordHash, $fullName, $phone, $city]); $stmt->execute([$email, $passwordHash, $fullName, $phone, $city]);
$userId = $stmt->fetchColumn(); $userId = $stmt->fetchColumn();
// Автоматически авторизуем
$_SESSION['user_id'] = $userId; $_SESSION['user_id'] = $userId;
$_SESSION['user_email'] = $email; $_SESSION['user_email'] = $email;
$_SESSION['full_name'] = $fullName; $_SESSION['full_name'] = $fullName;
@@ -106,20 +92,17 @@ function registerUser(array $data): array {
$_SESSION['isLoggedIn'] = true; $_SESSION['isLoggedIn'] = true;
$_SESSION['isAdmin'] = false; $_SESSION['isAdmin'] = false;
$_SESSION['login_time'] = time(); $_SESSION['login_time'] = time();
return ['success' => true, 'user_id' => $userId]; return ['success' => true, 'user_id' => $userId];
} catch (PDOException $e) { } catch (PDOException $e) {
return ['success' => false, 'message' => 'Ошибка базы данных: ' . $e->getMessage()]; return ['success' => false, 'message' => 'Ошибка базы данных: ' . $e->getMessage()];
} }
} }
/**
* Выход пользователя
*/
function logoutUser(): void { function logoutUser(): void {
$_SESSION = []; $_SESSION = [];
if (ini_get("session.use_cookies")) { if (ini_get("session.use_cookies")) {
$params = session_get_cookie_params(); $params = session_get_cookie_params();
setcookie(session_name(), '', time() - 42000, setcookie(session_name(), '', time() - 42000,
@@ -127,22 +110,18 @@ function logoutUser(): void {
$params["secure"], $params["httponly"] $params["secure"], $params["httponly"]
); );
} }
session_destroy(); session_destroy();
} }
/**
* Проверка является ли пользователь администратором
*/
function checkAdminAccess(): bool { function checkAdminAccess(): bool {
if (!isset($_SESSION['isLoggedIn']) || $_SESSION['isLoggedIn'] !== true) { if (!isset($_SESSION['isLoggedIn']) || $_SESSION['isLoggedIn'] !== true) {
return false; return false;
} }
if (!isset($_SESSION['isAdmin']) || $_SESSION['isAdmin'] !== true) { if (!isset($_SESSION['isAdmin']) || $_SESSION['isAdmin'] !== true) {
return false; return false;
} }
return true; return true;
} }

View File

@@ -47,4 +47,3 @@
</footer> </footer>
</body> </body>
</html> </html>

View File

@@ -1,25 +1,13 @@
<?php <?php
/**
* Общие функции для AETERNA
*/
/**
* Проверка авторизации пользователя
*/
function isLoggedIn(): bool { function isLoggedIn(): bool {
return isset($_SESSION['isLoggedIn']) && $_SESSION['isLoggedIn'] === true; return isset($_SESSION['isLoggedIn']) && $_SESSION['isLoggedIn'] === true;
} }
/**
* Проверка прав администратора
*/
function isAdmin(): bool { function isAdmin(): bool {
return isset($_SESSION['isAdmin']) && $_SESSION['isAdmin'] === true; return isset($_SESSION['isAdmin']) && $_SESSION['isAdmin'] === true;
} }
/**
* Требовать авторизацию - редирект на login если не авторизован
*/
function requireAuth(string $redirectUrl = 'login.php'): void { function requireAuth(string $redirectUrl = 'login.php'): void {
if (!isLoggedIn()) { if (!isLoggedIn()) {
header('Location: ' . $redirectUrl . '?error=auth_required&redirect=' . urlencode($_SERVER['REQUEST_URI'])); header('Location: ' . $redirectUrl . '?error=auth_required&redirect=' . urlencode($_SERVER['REQUEST_URI']));
@@ -27,9 +15,6 @@ function requireAuth(string $redirectUrl = 'login.php'): void {
} }
} }
/**
* Требовать права администратора
*/
function requireAdmin(string $redirectUrl = 'login.php'): void { function requireAdmin(string $redirectUrl = 'login.php'): void {
if (!isAdmin()) { if (!isAdmin()) {
header('Location: ' . $redirectUrl . '?error=admin_required'); header('Location: ' . $redirectUrl . '?error=admin_required');
@@ -37,14 +22,11 @@ function requireAdmin(string $redirectUrl = 'login.php'): void {
} }
} }
/**
* Получить текущего пользователя
*/
function getCurrentUser(): ?array { function getCurrentUser(): ?array {
if (!isLoggedIn()) { if (!isLoggedIn()) {
return null; return null;
} }
return [ return [
'user_id' => $_SESSION['user_id'] ?? 0, 'user_id' => $_SESSION['user_id'] ?? 0,
'email' => $_SESSION['user_email'] ?? '', 'email' => $_SESSION['user_email'] ?? '',
@@ -53,38 +35,23 @@ function getCurrentUser(): ?array {
]; ];
} }
/**
* Форматирование цены
*/
function formatPrice(float $price): string { function formatPrice(float $price): string {
return number_format($price, 0, '', ' ') . ' ₽'; return number_format($price, 0, '', ' ') . ' ₽';
} }
/**
* Безопасный вывод HTML
*/
function e(string $str): string { function e(string $str): string {
return htmlspecialchars($str, ENT_QUOTES, 'UTF-8'); return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
} }
/**
* Генерация номера заказа
*/
function generateOrderNumber(): string { function generateOrderNumber(): string {
return 'AET-' . date('Ymd') . '-' . strtoupper(substr(uniqid(), -6)); return 'AET-' . date('Ymd') . '-' . strtoupper(substr(uniqid(), -6));
} }
/**
* Генерация SKU для товара
*/
function generateSKU(string $productName): string { function generateSKU(string $productName): string {
$prefix = strtoupper(substr(preg_replace('/[^a-zA-Z0-9]/', '', transliterate($productName)), 0, 6)); $prefix = strtoupper(substr(preg_replace('/[^a-zA-Z0-9]/', '', transliterate($productName)), 0, 6));
return $prefix . '-' . rand(100, 999); return $prefix . '-' . rand(100, 999);
} }
/**
* Транслитерация кириллицы в латиницу
*/
function transliterate(string $str): string { function transliterate(string $str): string {
$converter = [ $converter = [
'а' => 'a', 'б' => 'b', 'в' => 'v', 'г' => 'g', 'д' => 'd', 'а' => 'a', 'б' => 'b', 'в' => 'v', 'г' => 'g', 'д' => 'd',
@@ -105,9 +72,6 @@ function transliterate(string $str): string {
return strtr($str, $converter); return strtr($str, $converter);
} }
/**
* Создание slug из строки
*/
function createSlug(string $str): string { function createSlug(string $str): string {
$slug = transliterate($str); $slug = transliterate($str);
$slug = strtolower($slug); $slug = strtolower($slug);
@@ -116,9 +80,6 @@ function createSlug(string $str): string {
return $slug; return $slug;
} }
/**
* Показать flash-сообщение
*/
function setFlashMessage(string $type, string $message): void { function setFlashMessage(string $type, string $message): void {
$_SESSION['flash_message'] = [ $_SESSION['flash_message'] = [
'type' => $type, 'type' => $type,
@@ -126,9 +87,6 @@ function setFlashMessage(string $type, string $message): void {
]; ];
} }
/**
* Получить и удалить flash-сообщение
*/
function getFlashMessage(): ?array { function getFlashMessage(): ?array {
if (isset($_SESSION['flash_message'])) { if (isset($_SESSION['flash_message'])) {
$message = $_SESSION['flash_message']; $message = $_SESSION['flash_message'];
@@ -137,4 +95,3 @@ function getFlashMessage(): ?array {
} }
return null; return null;
} }

View File

@@ -1,18 +1,11 @@
<?php <?php
/**
* Единый header для всех страниц AETERNA
* Подключение: require_once 'includes/header.php';
*/
// Запускаем сессию если еще не запущена
if (session_status() === PHP_SESSION_NONE) { if (session_status() === PHP_SESSION_NONE) {
session_start(); session_start();
} }
// Определяем текущую страницу
$currentPage = basename($_SERVER['PHP_SELF'], '.php'); $currentPage = basename($_SERVER['PHP_SELF'], '.php');
// Проверяем авторизацию
$isLoggedIn = isset($_SESSION['isLoggedIn']) && $_SESSION['isLoggedIn'] === true; $isLoggedIn = isset($_SESSION['isLoggedIn']) && $_SESSION['isLoggedIn'] === true;
$isAdmin = isset($_SESSION['isAdmin']) && $_SESSION['isAdmin'] === true; $isAdmin = isset($_SESSION['isAdmin']) && $_SESSION['isAdmin'] === true;
$userEmail = $_SESSION['user_email'] ?? ''; $userEmail = $_SESSION['user_email'] ?? '';
@@ -29,7 +22,7 @@ $fullName = $_SESSION['full_name'] ?? $userEmail;
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
<style> <style>
/* Стили для профиля пользователя */
.user-profile-dropdown { position: relative; display: inline-block; } .user-profile-dropdown { position: relative; display: inline-block; }
.user-profile-toggle { display: flex; align-items: center; gap: 10px; cursor: pointer; padding: 8px 12px; border-radius: 4px; transition: all 0.3s ease; } .user-profile-toggle { display: flex; align-items: center; gap: 10px; cursor: pointer; padding: 8px 12px; border-radius: 4px; transition: all 0.3s ease; }
.user-profile-toggle:hover { background-color: rgba(0, 0, 0, 0.05); } .user-profile-toggle:hover { background-color: rgba(0, 0, 0, 0.05); }
@@ -86,13 +79,12 @@ $fullName = $_SESSION['full_name'] ?? $userEmail;
<div class="header__icons--top"> <div class="header__icons--top">
<?php if ($isLoggedIn): ?> <?php if ($isLoggedIn): ?>
<!-- Иконка корзины -->
<a href="checkout.php" class="icon cart-icon"> <a href="checkout.php" class="icon cart-icon">
<i class="fas fa-shopping-cart"></i> <i class="fas fa-shopping-cart"></i>
<span class="cart-count" id="cartCount">0</span> <span class="cart-count" id="cartCount">0</span>
</a> </a>
<!-- Блок профиля -->
<div class="user-profile-dropdown"> <div class="user-profile-dropdown">
<div class="user-profile-toggle" id="profileToggle"> <div class="user-profile-toggle" id="profileToggle">
<div class="user-avatar"><?= !empty($userEmail) ? strtoupper(substr($userEmail, 0, 1)) : 'U' ?></div> <div class="user-avatar"><?= !empty($userEmail) ? strtoupper(substr($userEmail, 0, 1)) : 'U' ?></div>
@@ -149,19 +141,18 @@ $fullName = $_SESSION['full_name'] ?? $userEmail;
<script> <script>
$(document).ready(function() { $(document).ready(function() {
// Профиль пользователя - открытие/закрытие
$('#profileToggle').click(function(e) { $('#profileToggle').click(function(e) {
e.stopPropagation(); e.stopPropagation();
$('#profileMenu').toggle(); $('#profileMenu').toggle();
}); });
$(document).click(function(e) { $(document).click(function(e) {
if (!$(e.target).closest('.user-profile-dropdown').length) { if (!$(e.target).closest('.user-profile-dropdown').length) {
$('#profileMenu').hide(); $('#profileMenu').hide();
} }
}); });
// Обновление счетчика корзины
<?php if ($isLoggedIn): ?> <?php if ($isLoggedIn): ?>
$.ajax({ $.ajax({
url: 'api/cart.php?action=count', url: 'api/cart.php?action=count',
@@ -178,4 +169,3 @@ $(document).ready(function() {
<?php endif; ?> <?php endif; ?>
}); });
</script> </script>

View File

@@ -1,8 +1,4 @@
<?php <?php
/**
* Быстрый скрипт для назначения прав администратора пользователю admin@mail.ru
* Запуск: php migrations/grant_admin.php
*/
require_once __DIR__ . '/../config/database.php'; require_once __DIR__ . '/../config/database.php';
@@ -16,7 +12,6 @@ try {
$email = 'admin@mail.ru'; $email = 'admin@mail.ru';
// Проверяем, существует ли пользователь
$checkStmt = $db->prepare("SELECT user_id, email, full_name, is_admin, is_active FROM users WHERE email = ?"); $checkStmt = $db->prepare("SELECT user_id, email, full_name, is_admin, is_active FROM users WHERE email = ?");
$checkStmt->execute([$email]); $checkStmt->execute([$email]);
$user = $checkStmt->fetch(PDO::FETCH_ASSOC); $user = $checkStmt->fetch(PDO::FETCH_ASSOC);
@@ -31,9 +26,9 @@ try {
if ($user['is_admin']) { if ($user['is_admin']) {
echo "[INFO] Пользователь уже имеет права администратора\n"; echo "[INFO] Пользователь уже имеет права администратора\n";
} else { } else {
// Обновляем права
$updateStmt = $db->prepare(" $updateStmt = $db->prepare("
UPDATE users UPDATE users
SET is_admin = TRUE, SET is_admin = TRUE,
is_active = TRUE, is_active = TRUE,
updated_at = CURRENT_TIMESTAMP updated_at = CURRENT_TIMESTAMP
@@ -47,8 +42,6 @@ try {
echo "[WARN] Пользователь с email $email не найден\n"; echo "[WARN] Пользователь с email $email не найден\n";
echo "[INFO] Создаю нового пользователя с правами администратора...\n"; echo "[INFO] Создаю нового пользователя с правами администратора...\n";
// Создаем пользователя с правами админа
// Пароль по умолчанию: admin123
$password_hash = password_hash('admin123', PASSWORD_DEFAULT); $password_hash = password_hash('admin123', PASSWORD_DEFAULT);
$insertStmt = $db->prepare(" $insertStmt = $db->prepare("
@@ -73,7 +66,6 @@ try {
echo "[WARN] Рекомендуется сменить пароль после первого входа!\n"; echo "[WARN] Рекомендуется сменить пароль после первого входа!\n";
} }
// Проверяем результат
$verifyStmt = $db->prepare("SELECT user_id, email, full_name, is_admin, is_active FROM users WHERE email = ?"); $verifyStmt = $db->prepare("SELECT user_id, email, full_name, is_admin, is_active FROM users WHERE email = ?");
$verifyStmt->execute([$email]); $verifyStmt->execute([$email]);
$finalUser = $verifyStmt->fetch(PDO::FETCH_ASSOC); $finalUser = $verifyStmt->fetch(PDO::FETCH_ASSOC);
@@ -91,4 +83,3 @@ try {
echo "[ERROR] Ошибка: " . $e->getMessage() . "\n"; echo "[ERROR] Ошибка: " . $e->getMessage() . "\n";
exit(1); exit(1);
} }

View File

@@ -1,10 +1,5 @@
<?php <?php
/**
* Простой раннер миграций для PostgreSQL
* Запуск: php migrations/migrate.php
*/
// Подключаем конфиг базы данных
require_once __DIR__ . '/../config/database.php'; require_once __DIR__ . '/../config/database.php';
echo "===========================================\n"; echo "===========================================\n";
@@ -15,7 +10,6 @@ try {
$db = Database::getInstance()->getConnection(); $db = Database::getInstance()->getConnection();
echo "[OK] Подключение к базе данных успешно\n\n"; echo "[OK] Подключение к базе данных успешно\n\n";
// 1. Создаем таблицу для отслеживания миграций
$db->exec(" $db->exec("
CREATE TABLE IF NOT EXISTS migrations ( CREATE TABLE IF NOT EXISTS migrations (
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,
@@ -25,12 +19,10 @@ try {
"); ");
echo "[OK] Таблица migrations готова\n"; echo "[OK] Таблица migrations готова\n";
// 2. Получаем список уже примененных миграций
$stmt = $db->query("SELECT filename FROM migrations ORDER BY filename"); $stmt = $db->query("SELECT filename FROM migrations ORDER BY filename");
$applied = $stmt->fetchAll(PDO::FETCH_COLUMN); $applied = $stmt->fetchAll(PDO::FETCH_COLUMN);
echo "[INFO] Уже применено миграций: " . count($applied) . "\n\n"; echo "[INFO] Уже применено миграций: " . count($applied) . "\n\n";
// 3. Сканируем папку на SQL-файлы
$migrationFiles = glob(__DIR__ . '/*.sql'); $migrationFiles = glob(__DIR__ . '/*.sql');
sort($migrationFiles); sort($migrationFiles);
@@ -38,30 +30,26 @@ try {
foreach ($migrationFiles as $file) { foreach ($migrationFiles as $file) {
$filename = basename($file); $filename = basename($file);
// Пропускаем seed_data.sql - он запускается отдельно
if ($filename === 'seed_data.sql') { if ($filename === 'seed_data.sql') {
continue; continue;
} }
// Проверяем, была ли миграция уже применена
if (in_array($filename, $applied)) { if (in_array($filename, $applied)) {
echo "[SKIP] $filename (уже применена)\n"; echo "[SKIP] $filename (уже применена)\n";
continue; continue;
} }
// Применяем миграцию
echo "[RUN] Применяю $filename... "; echo "[RUN] Применяю $filename... ";
$sql = file_get_contents($file); $sql = file_get_contents($file);
try { try {
$db->exec($sql); $db->exec($sql);
// Записываем в таблицу миграций
$stmt = $db->prepare("INSERT INTO migrations (filename) VALUES (?)"); $stmt = $db->prepare("INSERT INTO migrations (filename) VALUES (?)");
$stmt->execute([$filename]); $stmt->execute([$filename]);
echo "OK\n"; echo "OK\n";
$newMigrations++; $newMigrations++;
} catch (PDOException $e) { } catch (PDOException $e) {
@@ -73,19 +61,18 @@ try {
} }
echo "\n-------------------------------------------\n"; echo "\n-------------------------------------------\n";
if ($newMigrations > 0) { if ($newMigrations > 0) {
echo "[SUCCESS] Применено новых миграций: $newMigrations\n"; echo "[SUCCESS] Применено новых миграций: $newMigrations\n";
} else { } else {
echo "[INFO] Все миграции уже применены\n"; echo "[INFO] Все миграции уже применены\n";
} }
// 4. Спрашиваем про seed_data
$seedFile = __DIR__ . '/seed_data.sql'; $seedFile = __DIR__ . '/seed_data.sql';
if (file_exists($seedFile)) { if (file_exists($seedFile)) {
echo "\n[?] Хотите загрузить начальные данные (seed_data.sql)?\n"; echo "\n[?] Хотите загрузить начальные данные (seed_data.sql)?\n";
echo " Запустите: php migrations/migrate.php --seed\n"; echo " Запустите: php migrations/migrate.php --seed\n";
if (isset($argv[1]) && $argv[1] === '--seed') { if (isset($argv[1]) && $argv[1] === '--seed') {
echo "\n[RUN] Загружаю seed_data.sql... "; echo "\n[RUN] Загружаю seed_data.sql... ";
try { try {
@@ -106,4 +93,3 @@ try {
echo "[ERROR] Ошибка подключения к БД: " . $e->getMessage() . "\n"; echo "[ERROR] Ошибка подключения к БД: " . $e->getMessage() . "\n";
exit(1); exit(1);
} }

File diff suppressed because it is too large Load Diff

View File

@@ -1,116 +1,112 @@
<?php <?php
session_start(); session_start();
require_once __DIR__ . '/../config/database.php'; require_once __DIR__ . '/../config/database.php';
if (!isset($_SESSION['isLoggedIn']) || $_SESSION['isLoggedIn'] !== true) { if (!isset($_SESSION['isLoggedIn']) || $_SESSION['isLoggedIn'] !== true) {
echo json_encode(['success' => false, 'message' => 'Требуется авторизация']); echo json_encode(['success' => false, 'message' => 'Требуется авторизация']);
exit(); exit();
} }
if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_POST['product_id'])) { if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_POST['product_id'])) {
$product_id = intval($_POST['product_id']); $product_id = intval($_POST['product_id']);
$quantity = intval($_POST['quantity'] ?? 1); $quantity = intval($_POST['quantity'] ?? 1);
$user_id = $_SESSION['user_id'] ?? 0; $user_id = $_SESSION['user_id'] ?? 0;
if ($user_id == 0) { if ($user_id == 0) {
echo json_encode(['success' => false, 'message' => 'Пользователь не найден']); echo json_encode(['success' => false, 'message' => 'Пользователь не найден']);
exit(); exit();
} }
$db = Database::getInstance()->getConnection(); $db = Database::getInstance()->getConnection();
try { try {
// Проверяем наличие товара на складе
$checkStock = $db->prepare(" $checkStock = $db->prepare("
SELECT stock_quantity, name, price SELECT stock_quantity, name, price
FROM products FROM products
WHERE product_id = ? AND is_available = TRUE WHERE product_id = ? AND is_available = TRUE
"); ");
$checkStock->execute([$product_id]); $checkStock->execute([$product_id]);
$product = $checkStock->fetch(); $product = $checkStock->fetch();
if (!$product) { if (!$product) {
echo json_encode(['success' => false, 'message' => 'Товар не найден']); echo json_encode(['success' => false, 'message' => 'Товар не найден']);
exit(); exit();
} }
if ($product['stock_quantity'] < $quantity) { if ($product['stock_quantity'] < $quantity) {
echo json_encode(['success' => false, 'message' => 'Недостаточно товара на складе']); echo json_encode(['success' => false, 'message' => 'Недостаточно товара на складе']);
exit(); exit();
} }
// Проверяем, есть ли товар уже в корзине пользователя $checkCart = $db->prepare("
$checkCart = $db->prepare(" SELECT cart_id, quantity
SELECT cart_id, quantity FROM cart
FROM cart WHERE user_id = ? AND product_id = ?
WHERE user_id = ? AND product_id = ? ");
"); $checkCart->execute([$user_id, $product_id]);
$checkCart->execute([$user_id, $product_id]); $cartItem = $checkCart->fetch();
$cartItem = $checkCart->fetch();
if ($cartItem) {
if ($cartItem) {
// Обновляем количество $newQuantity = $cartItem['quantity'] + $quantity;
$newQuantity = $cartItem['quantity'] + $quantity;
if ($newQuantity > $product['stock_quantity']) {
// Проверяем общее количество echo json_encode(['success' => false, 'message' => 'Превышено доступное количество']);
if ($newQuantity > $product['stock_quantity']) { exit();
echo json_encode(['success' => false, 'message' => 'Превышено доступное количество']); }
exit();
} $updateStmt = $db->prepare("
UPDATE cart
$updateStmt = $db->prepare(" SET quantity = ?, updated_at = CURRENT_TIMESTAMP
UPDATE cart WHERE cart_id = ?
SET quantity = ?, updated_at = CURRENT_TIMESTAMP ");
WHERE cart_id = ? $updateStmt->execute([$newQuantity, $cartItem['cart_id']]);
"); } else {
$updateStmt->execute([$newQuantity, $cartItem['cart_id']]);
} else { $insertStmt = $db->prepare("
// Добавляем новый товар INSERT INTO cart (user_id, product_id, quantity)
$insertStmt = $db->prepare(" VALUES (?, ?, ?)
INSERT INTO cart (user_id, product_id, quantity) ");
VALUES (?, ?, ?) $insertStmt->execute([$user_id, $product_id, $quantity]);
"); }
$insertStmt->execute([$user_id, $product_id, $quantity]);
} if (!isset($_SESSION['cart'])) {
$_SESSION['cart'] = [];
// Обновляем сессию }
if (!isset($_SESSION['cart'])) {
$_SESSION['cart'] = []; if (isset($_SESSION['cart'][$product_id])) {
} $_SESSION['cart'][$product_id]['quantity'] += $quantity;
} else {
if (isset($_SESSION['cart'][$product_id])) { $_SESSION['cart'][$product_id] = [
$_SESSION['cart'][$product_id]['quantity'] += $quantity; 'quantity' => $quantity,
} else { 'name' => $product['name'],
$_SESSION['cart'][$product_id] = [ 'price' => $product['price'],
'quantity' => $quantity, 'added_at' => time()
'name' => $product['name'], ];
'price' => $product['price'], }
'added_at' => time()
]; $cartCountStmt = $db->prepare("
} SELECT SUM(quantity) as total
FROM cart
// Получаем общее количество товаров в корзине WHERE user_id = ?
$cartCountStmt = $db->prepare(" ");
SELECT SUM(quantity) as total $cartCountStmt->execute([$user_id]);
FROM cart $cart_count = $cartCountStmt->fetchColumn() ?: 0;
WHERE user_id = ?
"); echo json_encode([
$cartCountStmt->execute([$user_id]); 'success' => true,
$cart_count = $cartCountStmt->fetchColumn() ?: 0; 'cart_count' => $cart_count,
'message' => 'Товар добавлен в корзину'
echo json_encode([ ]);
'success' => true,
'cart_count' => $cart_count, } catch (PDOException $e) {
'message' => 'Товар добавлен в корзину' echo json_encode([
]); 'success' => false,
'message' => 'Ошибка базы данных: ' . $e->getMessage()
} catch (PDOException $e) { ]);
echo json_encode([ }
'success' => false, } else {
'message' => 'Ошибка базы данных: ' . $e->getMessage() echo json_encode(['success' => false, 'message' => 'Неверный запрос']);
]); }
}
} else {
echo json_encode(['success' => false, 'message' => 'Неверный запрос']);
}
?> ?>

View File

@@ -1,71 +1,68 @@
<?php <?php
// login_handler.php
session_start(); session_start();
require_once __DIR__ . '/../config/database.php'; require_once __DIR__ . '/../config/database.php';
if ($_SERVER['REQUEST_METHOD'] === 'POST') { if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$email = $_POST['email'] ?? ''; $email = $_POST['email'] ?? '';
$password = $_POST['password'] ?? ''; $password = $_POST['password'] ?? '';
if (empty($email) || empty($password)) { if (empty($email) || empty($password)) {
echo json_encode(['success' => false, 'message' => 'Заполните все поля']); echo json_encode(['success' => false, 'message' => 'Заполните все поля']);
exit(); exit();
} }
$db = Database::getInstance()->getConnection(); $db = Database::getInstance()->getConnection();
try { try {
// Проверяем пользователя в базе данных
$stmt = $db->prepare(" $stmt = $db->prepare("
SELECT user_id, email, password_hash, full_name, phone, city, is_admin, is_active SELECT user_id, email, password_hash, full_name, phone, city, is_admin, is_active
FROM users FROM users
WHERE email = ? WHERE email = ?
"); ");
$stmt->execute([$email]); $stmt->execute([$email]);
$user = $stmt->fetch(PDO::FETCH_ASSOC); $user = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$user) { if (!$user) {
echo json_encode(['success' => false, 'message' => 'Пользователь не найден']); echo json_encode(['success' => false, 'message' => 'Пользователь не найден']);
exit(); exit();
} }
if (!$user['is_active']) { if (!$user['is_active']) {
echo json_encode(['success' => false, 'message' => 'Аккаунт заблокирован']); echo json_encode(['success' => false, 'message' => 'Аккаунт заблокирован']);
exit(); exit();
} }
// Проверяем пароль if (empty($user['password_hash'])) {
if (empty($user['password_hash'])) { echo json_encode(['success' => false, 'message' => 'Ошибка: пароль не найден в базе данных']);
echo json_encode(['success' => false, 'message' => 'Ошибка: пароль не найден в базе данных']); exit();
exit(); }
}
if (!password_verify($password, $user['password_hash'])) {
if (!password_verify($password, $user['password_hash'])) { echo json_encode(['success' => false, 'message' => 'Неверный пароль']);
echo json_encode(['success' => false, 'message' => 'Неверный пароль']); exit();
exit(); }
}
$_SESSION['user_id'] = $user['user_id'];
// Сохраняем в сессию $_SESSION['user_email'] = $user['email'];
$_SESSION['user_id'] = $user['user_id']; $_SESSION['full_name'] = $user['full_name'];
$_SESSION['user_email'] = $user['email']; $_SESSION['user_phone'] = $user['phone'] ?? '';
$_SESSION['full_name'] = $user['full_name']; $_SESSION['user_city'] = $user['city'] ?? '';
$_SESSION['user_phone'] = $user['phone'] ?? ''; $_SESSION['isLoggedIn'] = true;
$_SESSION['user_city'] = $user['city'] ?? ''; $_SESSION['isAdmin'] = (bool)$user['is_admin'];
$_SESSION['isLoggedIn'] = true; $_SESSION['login_time'] = time();
$_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']]);
// Обновляем время последнего входа
$updateStmt = $db->prepare("UPDATE users SET last_login = CURRENT_TIMESTAMP WHERE user_id = ?"); echo json_encode(['success' => true, 'redirect' => 'catalog.php']);
$updateStmt->execute([$user['user_id']]);
} catch (PDOException $e) {
echo json_encode(['success' => true, 'redirect' => 'catalog.php']); echo json_encode(['success' => false, 'message' => 'Ошибка базы данных']);
}
} catch (PDOException $e) {
echo json_encode(['success' => false, 'message' => 'Ошибка базы данных']); } else {
} echo json_encode(['success' => false, 'message' => 'Неверный запрос']);
}
} else {
echo json_encode(['success' => false, 'message' => 'Неверный запрос']);
}
?> ?>

View File

@@ -1,14 +1,10 @@
<?php <?php
/**
* API для работы с корзиной
* Эндпоинты: add, update, remove, get, count
*/
session_start(); session_start();
require_once __DIR__ . '/../config/database.php'; require_once __DIR__ . '/../config/database.php';
header('Content-Type: application/json; charset=utf-8'); header('Content-Type: application/json; charset=utf-8');
// Проверка авторизации
if (!isset($_SESSION['isLoggedIn']) || $_SESSION['isLoggedIn'] !== true) { if (!isset($_SESSION['isLoggedIn']) || $_SESSION['isLoggedIn'] !== true) {
echo json_encode(['success' => false, 'message' => 'Требуется авторизация']); echo json_encode(['success' => false, 'message' => 'Требуется авторизация']);
exit(); exit();
@@ -24,66 +20,64 @@ try {
case 'add': case 'add':
$productId = (int)($_POST['product_id'] ?? 0); $productId = (int)($_POST['product_id'] ?? 0);
$quantity = (int)($_POST['quantity'] ?? 1); $quantity = (int)($_POST['quantity'] ?? 1);
if ($productId <= 0) { if ($productId <= 0) {
echo json_encode(['success' => false, 'message' => 'Неверный ID товара']); echo json_encode(['success' => false, 'message' => 'Неверный ID товара']);
exit(); exit();
} }
// Проверяем существование товара
$checkProduct = $db->prepare("SELECT product_id, stock_quantity FROM products WHERE product_id = ? AND is_available = TRUE"); $checkProduct = $db->prepare("SELECT product_id, stock_quantity FROM products WHERE product_id = ? AND is_available = TRUE");
$checkProduct->execute([$productId]); $checkProduct->execute([$productId]);
$product = $checkProduct->fetch(); $product = $checkProduct->fetch();
if (!$product) { if (!$product) {
echo json_encode(['success' => false, 'message' => 'Товар не найден']); echo json_encode(['success' => false, 'message' => 'Товар не найден']);
exit(); exit();
} }
// Проверяем, есть ли товар уже в корзине
$checkCart = $db->prepare("SELECT cart_id, quantity FROM cart WHERE user_id = ? AND product_id = ?"); $checkCart = $db->prepare("SELECT cart_id, quantity FROM cart WHERE user_id = ? AND product_id = ?");
$checkCart->execute([$userId, $productId]); $checkCart->execute([$userId, $productId]);
$cartItem = $checkCart->fetch(); $cartItem = $checkCart->fetch();
if ($cartItem) { if ($cartItem) {
// Обновляем количество
$newQuantity = $cartItem['quantity'] + $quantity; $newQuantity = $cartItem['quantity'] + $quantity;
$stmt = $db->prepare("UPDATE cart SET quantity = ?, updated_at = CURRENT_TIMESTAMP WHERE cart_id = ?"); $stmt = $db->prepare("UPDATE cart SET quantity = ?, updated_at = CURRENT_TIMESTAMP WHERE cart_id = ?");
$stmt->execute([$newQuantity, $cartItem['cart_id']]); $stmt->execute([$newQuantity, $cartItem['cart_id']]);
} else { } else {
// Добавляем новый товар
$stmt = $db->prepare("INSERT INTO cart (user_id, product_id, quantity) VALUES (?, ?, ?)"); $stmt = $db->prepare("INSERT INTO cart (user_id, product_id, quantity) VALUES (?, ?, ?)");
$stmt->execute([$userId, $productId, $quantity]); $stmt->execute([$userId, $productId, $quantity]);
} }
echo json_encode(['success' => true, 'message' => 'Товар добавлен в корзину']); echo json_encode(['success' => true, 'message' => 'Товар добавлен в корзину']);
break; break;
case 'update': case 'update':
$productId = (int)($_POST['product_id'] ?? 0); $productId = (int)($_POST['product_id'] ?? 0);
$quantity = (int)($_POST['quantity'] ?? 1); $quantity = (int)($_POST['quantity'] ?? 1);
if ($quantity <= 0) { if ($quantity <= 0) {
// Удаляем товар если количество 0
$stmt = $db->prepare("DELETE FROM cart WHERE user_id = ? AND product_id = ?"); $stmt = $db->prepare("DELETE FROM cart WHERE user_id = ? AND product_id = ?");
$stmt->execute([$userId, $productId]); $stmt->execute([$userId, $productId]);
} else { } else {
$stmt = $db->prepare("UPDATE cart SET quantity = ?, updated_at = CURRENT_TIMESTAMP WHERE user_id = ? AND product_id = ?"); $stmt = $db->prepare("UPDATE cart SET quantity = ?, updated_at = CURRENT_TIMESTAMP WHERE user_id = ? AND product_id = ?");
$stmt->execute([$quantity, $userId, $productId]); $stmt->execute([$quantity, $userId, $productId]);
} }
echo json_encode(['success' => true, 'message' => 'Корзина обновлена']); echo json_encode(['success' => true, 'message' => 'Корзина обновлена']);
break; break;
case 'remove': case 'remove':
$productId = (int)($_POST['product_id'] ?? 0); $productId = (int)($_POST['product_id'] ?? 0);
$stmt = $db->prepare("DELETE FROM cart WHERE user_id = ? AND product_id = ?"); $stmt = $db->prepare("DELETE FROM cart WHERE user_id = ? AND product_id = ?");
$stmt->execute([$userId, $productId]); $stmt->execute([$userId, $productId]);
echo json_encode(['success' => true, 'message' => 'Товар удален из корзины']); echo json_encode(['success' => true, 'message' => 'Товар удален из корзины']);
break; break;
case 'get': case 'get':
$stmt = $db->prepare(" $stmt = $db->prepare("
SELECT c.cart_id, c.product_id, c.quantity, p.name, p.price, p.image_url, p.stock_quantity SELECT c.cart_id, c.product_id, c.quantity, p.name, p.price, p.image_url, p.stock_quantity
@@ -94,13 +88,13 @@ try {
"); ");
$stmt->execute([$userId]); $stmt->execute([$userId]);
$items = $stmt->fetchAll(); $items = $stmt->fetchAll();
$total = 0; $total = 0;
foreach ($items as &$item) { foreach ($items as &$item) {
$item['subtotal'] = $item['price'] * $item['quantity']; $item['subtotal'] = $item['price'] * $item['quantity'];
$total += $item['subtotal']; $total += $item['subtotal'];
} }
echo json_encode([ echo json_encode([
'success' => true, 'success' => true,
'items' => $items, 'items' => $items,
@@ -108,27 +102,26 @@ try {
'count' => array_sum(array_column($items, 'quantity')) 'count' => array_sum(array_column($items, 'quantity'))
]); ]);
break; break;
case 'count': case 'count':
$stmt = $db->prepare("SELECT COALESCE(SUM(quantity), 0) FROM cart WHERE user_id = ?"); $stmt = $db->prepare("SELECT COALESCE(SUM(quantity), 0) FROM cart WHERE user_id = ?");
$stmt->execute([$userId]); $stmt->execute([$userId]);
$count = $stmt->fetchColumn(); $count = $stmt->fetchColumn();
echo json_encode(['success' => true, 'count' => (int)$count]); echo json_encode(['success' => true, 'count' => (int)$count]);
break; break;
case 'clear': case 'clear':
$stmt = $db->prepare("DELETE FROM cart WHERE user_id = ?"); $stmt = $db->prepare("DELETE FROM cart WHERE user_id = ?");
$stmt->execute([$userId]); $stmt->execute([$userId]);
echo json_encode(['success' => true, 'message' => 'Корзина очищена']); echo json_encode(['success' => true, 'message' => 'Корзина очищена']);
break; break;
default: default:
echo json_encode(['success' => false, 'message' => 'Неизвестное действие']); echo json_encode(['success' => false, 'message' => 'Неизвестное действие']);
} }
} catch (PDOException $e) { } catch (PDOException $e) {
echo json_encode(['success' => false, 'message' => 'Ошибка базы данных: ' . $e->getMessage()]); echo json_encode(['success' => false, 'message' => 'Ошибка базы данных: ' . $e->getMessage()]);
} }

View File

@@ -1,62 +1,61 @@
<?php <?php
// get_cart.php
session_start(); session_start();
require_once __DIR__ . '/../config/database.php'; require_once __DIR__ . '/../config/database.php';
if (!isset($_SESSION['isLoggedIn']) || $_SESSION['isLoggedIn'] !== true) { if (!isset($_SESSION['isLoggedIn']) || $_SESSION['isLoggedIn'] !== true) {
echo json_encode(['success' => false, 'message' => 'Требуется авторизация']); echo json_encode(['success' => false, 'message' => 'Требуется авторизация']);
exit(); exit();
} }
$user_id = $_SESSION['user_id'] ?? 0; $user_id = $_SESSION['user_id'] ?? 0;
if ($user_id == 0) { if ($user_id == 0) {
echo json_encode(['success' => false, 'message' => 'Пользователь не найден']); echo json_encode(['success' => false, 'message' => 'Пользователь не найден']);
exit(); exit();
} }
$db = Database::getInstance()->getConnection(); $db = Database::getInstance()->getConnection();
try { try {
// Получаем корзину из БД
$stmt = $db->prepare(" $stmt = $db->prepare("
SELECT SELECT
c.cart_id, c.cart_id,
c.product_id, c.product_id,
c.quantity, c.quantity,
p.name, p.name,
p.price, p.price,
p.image_url, p.image_url,
p.stock_quantity p.stock_quantity
FROM cart c FROM cart c
JOIN products p ON c.product_id = p.product_id JOIN products p ON c.product_id = p.product_id
WHERE c.user_id = ? AND p.is_available = TRUE WHERE c.user_id = ? AND p.is_available = TRUE
ORDER BY c.created_at DESC ORDER BY c.created_at DESC
"); ");
$stmt->execute([$user_id]); $stmt->execute([$user_id]);
$cart_items = $stmt->fetchAll(); $cart_items = $stmt->fetchAll();
// Обновляем сессию $_SESSION['cart'] = [];
$_SESSION['cart'] = []; foreach ($cart_items as $item) {
foreach ($cart_items as $item) { $_SESSION['cart'][$item['product_id']] = [
$_SESSION['cart'][$item['product_id']] = [ 'quantity' => $item['quantity'],
'quantity' => $item['quantity'], 'name' => $item['name'],
'name' => $item['name'], 'price' => $item['price'],
'price' => $item['price'], 'added_at' => time()
'added_at' => time() ];
]; }
}
echo json_encode([
echo json_encode([ 'success' => true,
'success' => true, 'cart_items' => $cart_items,
'cart_items' => $cart_items, 'total_items' => count($cart_items)
'total_items' => count($cart_items) ]);
]);
} catch (PDOException $e) {
} catch (PDOException $e) { echo json_encode([
echo json_encode([ 'success' => false,
'success' => false, 'message' => 'Ошибка базы данных: ' . $e->getMessage()
'message' => 'Ошибка базы данных: ' . $e->getMessage() ]);
]); }
}
?> ?>

View File

@@ -1,22 +1,22 @@
<?php <?php
// get_cart_count.php
session_start(); session_start();
require_once __DIR__ . '/../config/database.php'; require_once __DIR__ . '/../config/database.php';
if (!isset($_SESSION['isLoggedIn']) || $_SESSION['isLoggedIn'] !== true) { if (!isset($_SESSION['isLoggedIn']) || $_SESSION['isLoggedIn'] !== true) {
echo json_encode(['success' => false, 'cart_count' => 0]); echo json_encode(['success' => false, 'cart_count' => 0]);
exit(); exit();
} }
$user_id = $_SESSION['user_id'] ?? 0; $user_id = $_SESSION['user_id'] ?? 0;
$db = Database::getInstance()->getConnection(); $db = Database::getInstance()->getConnection();
try { try {
$stmt = $db->prepare("SELECT SUM(quantity) as total FROM cart WHERE user_id = ?"); $stmt = $db->prepare("SELECT SUM(quantity) as total FROM cart WHERE user_id = ?");
$stmt->execute([$user_id]); $stmt->execute([$user_id]);
$cart_count = $stmt->fetchColumn() ?: 0; $cart_count = $stmt->fetchColumn() ?: 0;
echo json_encode(['success' => true, 'cart_count' => $cart_count]); echo json_encode(['success' => true, 'cart_count' => $cart_count]);
} catch (PDOException $e) { } catch (PDOException $e) {
echo json_encode(['success' => false, 'cart_count' => 0]); echo json_encode(['success' => false, 'cart_count' => 0]);
} }

View File

@@ -1,33 +1,32 @@
<?php <?php
session_start(); session_start();
require_once __DIR__ . '/../config/database.php'; require_once __DIR__ . '/../config/database.php';
// Проверяем авторизацию администратора if (!isset($_SESSION['isAdmin']) || $_SESSION['isAdmin'] !== true) {
if (!isset($_SESSION['isAdmin']) || $_SESSION['isAdmin'] !== true) { echo json_encode(['success' => false, 'message' => 'Доступ запрещен']);
echo json_encode(['success' => false, 'message' => 'Доступ запрещен']); exit();
exit(); }
}
if (!isset($_GET['id'])) {
if (!isset($_GET['id'])) { echo json_encode(['success' => false, 'message' => 'ID не указан']);
echo json_encode(['success' => false, 'message' => 'ID не указан']); exit();
exit(); }
}
try {
try { $db = Database::getInstance()->getConnection();
$db = Database::getInstance()->getConnection(); $product_id = $_GET['id'];
$product_id = $_GET['id'];
$stmt = $db->prepare("SELECT * FROM products WHERE product_id = ?");
$stmt = $db->prepare("SELECT * FROM products WHERE product_id = ?"); $stmt->execute([$product_id]);
$stmt->execute([$product_id]); $product = $stmt->fetch();
$product = $stmt->fetch();
if ($product) {
if ($product) { echo json_encode($product);
echo json_encode($product); } else {
} else { echo json_encode(['success' => false, 'message' => 'Товар не найден']);
echo json_encode(['success' => false, 'message' => 'Товар не найден']); }
}
} catch (PDOException $e) {
} catch (PDOException $e) { echo json_encode(['success' => false, 'message' => 'Ошибка базы данных: ' . $e->getMessage()]);
echo json_encode(['success' => false, 'message' => 'Ошибка базы данных: ' . $e->getMessage()]); }
}
?> ?>

View File

@@ -1,134 +1,124 @@
<?php <?php
// process_order.php
session_start(); session_start();
require_once __DIR__ . '/../config/database.php'; require_once __DIR__ . '/../config/database.php';
if (!isset($_SESSION['isLoggedIn']) || $_SESSION['isLoggedIn'] !== true) { if (!isset($_SESSION['isLoggedIn']) || $_SESSION['isLoggedIn'] !== true) {
header('Location: login.php?error=auth_required'); header('Location: login.php?error=auth_required');
exit(); exit();
} }
$user_id = $_SESSION['user_id'] ?? 0; $user_id = $_SESSION['user_id'] ?? 0;
if ($user_id == 0) { if ($user_id == 0) {
header('Location: login.php?error=user_not_found'); header('Location: login.php?error=user_not_found');
exit(); exit();
} }
if ($_SERVER['REQUEST_METHOD'] === 'POST') { if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$db = Database::getInstance()->getConnection(); $db = Database::getInstance()->getConnection();
try { try {
$db->beginTransaction(); $db->beginTransaction();
// Получаем данные из формы $customer_name = $_POST['full_name'] ?? '';
$customer_name = $_POST['full_name'] ?? ''; $customer_email = $_POST['email'] ?? '';
$customer_email = $_POST['email'] ?? ''; $customer_phone = $_POST['phone'] ?? '';
$customer_phone = $_POST['phone'] ?? ''; $delivery_address = $_POST['address'] ?? '';
$delivery_address = $_POST['address'] ?? ''; $region = $_POST['region'] ?? '';
$region = $_POST['region'] ?? ''; $payment_method = $_POST['payment'] ?? 'card';
$payment_method = $_POST['payment'] ?? 'card'; $delivery_method = $_POST['delivery'] ?? 'courier';
$delivery_method = $_POST['delivery'] ?? 'courier'; $notes = $_POST['notes'] ?? '';
$notes = $_POST['notes'] ?? ''; $discount_amount = floatval($_POST['discount'] ?? 0);
$discount_amount = floatval($_POST['discount'] ?? 0); $delivery_cost = floatval($_POST['delivery_price'] ?? 2000);
$delivery_cost = floatval($_POST['delivery_price'] ?? 2000);
$order_number = 'ORD-' . date('Ymd-His') . '-' . rand(1000, 9999);
// Генерируем номер заказа
$order_number = 'ORD-' . date('Ymd-His') . '-' . rand(1000, 9999); $cartStmt = $db->prepare("
SELECT
// Получаем корзину пользователя c.product_id,
$cartStmt = $db->prepare(" c.quantity,
SELECT p.name,
c.product_id, p.price,
c.quantity, p.stock_quantity
p.name, FROM cart c
p.price, JOIN products p ON c.product_id = p.product_id
p.stock_quantity WHERE c.user_id = ?
FROM cart c ");
JOIN products p ON c.product_id = p.product_id $cartStmt->execute([$user_id]);
WHERE c.user_id = ? $cart_items = $cartStmt->fetchAll();
");
$cartStmt->execute([$user_id]); if (empty($cart_items)) {
$cart_items = $cartStmt->fetchAll(); throw new Exception('Корзина пуста');
}
if (empty($cart_items)) {
throw new Exception('Корзина пуста'); $total_amount = 0;
} foreach ($cart_items as $item) {
$total_amount += $item['price'] * $item['quantity'];
// Рассчитываем итоги }
$total_amount = 0;
foreach ($cart_items as $item) { $final_amount = $total_amount - $discount_amount + $delivery_cost;
$total_amount += $item['price'] * $item['quantity'];
} $orderStmt = $db->prepare("
INSERT INTO orders (
$final_amount = $total_amount - $discount_amount + $delivery_cost; user_id, order_number, total_amount, discount_amount,
delivery_cost, final_amount, status, payment_method,
// Создаем заказ delivery_method, delivery_address, customer_name,
$orderStmt = $db->prepare(" customer_email, customer_phone, notes
INSERT INTO orders ( ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
user_id, order_number, total_amount, discount_amount, RETURNING order_id
delivery_cost, final_amount, status, payment_method, ");
delivery_method, delivery_address, customer_name,
customer_email, customer_phone, notes $orderStmt->execute([
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) $user_id, $order_number, $total_amount, $discount_amount,
RETURNING order_id $delivery_cost, $final_amount, 'pending', $payment_method,
"); $delivery_method, $delivery_address, $customer_name,
$customer_email, $customer_phone, $notes
$orderStmt->execute([ ]);
$user_id, $order_number, $total_amount, $discount_amount,
$delivery_cost, $final_amount, 'pending', $payment_method, $order_id = $orderStmt->fetchColumn();
$delivery_method, $delivery_address, $customer_name,
$customer_email, $customer_phone, $notes foreach ($cart_items as $item) {
]);
$itemStmt = $db->prepare("
$order_id = $orderStmt->fetchColumn(); INSERT INTO order_items (
order_id, product_id, product_name,
// Добавляем товары в заказ и обновляем остатки quantity, unit_price, total_price
foreach ($cart_items as $item) { ) VALUES (?, ?, ?, ?, ?, ?)
// Добавляем в order_items ");
$itemStmt = $db->prepare("
INSERT INTO order_items ( $item_total = $item['price'] * $item['quantity'];
order_id, product_id, product_name, $itemStmt->execute([
quantity, unit_price, total_price $order_id, $item['product_id'], $item['name'],
) VALUES (?, ?, ?, ?, ?, ?) $item['quantity'], $item['price'], $item_total
"); ]);
$item_total = $item['price'] * $item['quantity']; $updateStmt = $db->prepare("
$itemStmt->execute([ UPDATE products
$order_id, $item['product_id'], $item['name'], SET stock_quantity = stock_quantity - ?,
$item['quantity'], $item['price'], $item_total updated_at = CURRENT_TIMESTAMP
]); WHERE product_id = ?
");
// Обновляем остатки на складе $updateStmt->execute([$item['quantity'], $item['product_id']]);
$updateStmt = $db->prepare(" }
UPDATE products
SET stock_quantity = stock_quantity - ?, $clearCartStmt = $db->prepare("DELETE FROM cart WHERE user_id = ?");
updated_at = CURRENT_TIMESTAMP $clearCartStmt->execute([$user_id]);
WHERE product_id = ?
"); unset($_SESSION['cart']);
$updateStmt->execute([$item['quantity'], $item['product_id']]);
} $db->commit();
// Очищаем корзину header('Location: order_success.php?id=' . $order_id);
$clearCartStmt = $db->prepare("DELETE FROM cart WHERE user_id = ?"); exit();
$clearCartStmt->execute([$user_id]);
} catch (Exception $e) {
// Очищаем сессию $db->rollBack();
unset($_SESSION['cart']); header('Location: checkout.php?error=' . urlencode($e->getMessage()));
exit();
$db->commit(); }
} else {
// Перенаправляем на страницу успеха header('Location: checkout.php');
header('Location: order_success.php?id=' . $order_id); exit();
exit(); }
} catch (Exception $e) {
$db->rollBack();
header('Location: checkout.php?error=' . urlencode($e->getMessage()));
exit();
}
} else {
header('Location: checkout.php');
exit();
}
?> ?>

View File

@@ -1,175 +1,162 @@
<?php <?php
// register_handler.php
session_start(); session_start();
require_once __DIR__ . '/../config/database.php'; require_once __DIR__ . '/../config/database.php';
if ($_SERVER['REQUEST_METHOD'] === 'POST') { if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$errors = []; $errors = [];
// Получаем данные из формы $full_name = trim($_POST['fio'] ?? '');
$full_name = trim($_POST['fio'] ?? ''); $city = trim($_POST['city'] ?? '');
$city = trim($_POST['city'] ?? ''); $email = trim($_POST['email'] ?? '');
$email = trim($_POST['email'] ?? ''); $phone = trim($_POST['phone'] ?? '');
$phone = trim($_POST['phone'] ?? ''); $password = $_POST['password'] ?? '';
$password = $_POST['password'] ?? ''; $confirm_password = $_POST['confirm-password'] ?? '';
$confirm_password = $_POST['confirm-password'] ?? '';
if (empty($full_name) || strlen($full_name) < 3) {
// Валидация данных $errors[] = 'ФИО должно содержать минимум 3 символа';
if (empty($full_name) || strlen($full_name) < 3) { }
$errors[] = 'ФИО должно содержать минимум 3 символа';
} if (empty($city) || strlen($city) < 2) {
$errors[] = 'Введите корректное название города';
if (empty($city) || strlen($city) < 2) { }
$errors[] = 'Введите корректное название города';
} if (empty($email) || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
$errors[] = 'Введите корректный email адрес';
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($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 (empty($password) || strlen($password) < 6) { }
$errors[] = 'Пароль должен содержать минимум 6 символов';
} if ($password !== $confirm_password) {
$errors[] = 'Пароли не совпадают';
if ($password !== $confirm_password) { }
$errors[] = 'Пароли не совпадают';
} if (!isset($_POST['privacy']) || $_POST['privacy'] !== 'on') {
$errors[] = 'Необходимо согласие с условиями обработки персональных данных';
// Проверка согласия с условиями }
if (!isset($_POST['privacy']) || $_POST['privacy'] !== 'on') {
$errors[] = 'Необходимо согласие с условиями обработки персональных данных'; if (!empty($errors)) {
} $_SESSION['registration_errors'] = $errors;
$_SESSION['old_data'] = [
// Если есть ошибки, возвращаем на форму 'fio' => $full_name,
if (!empty($errors)) { 'city' => $city,
$_SESSION['registration_errors'] = $errors; 'email' => $email,
$_SESSION['old_data'] = [ 'phone' => $phone
'fio' => $full_name, ];
'city' => $city, header('Location: ../register.php');
'email' => $email, exit();
'phone' => $phone }
];
header('Location: ../register.php'); $db = Database::getInstance()->getConnection();
exit();
} try {
// Подключаемся к базе данных $checkStmt = $db->prepare("SELECT user_id FROM users WHERE email = ?");
$db = Database::getInstance()->getConnection(); $checkStmt->execute([$email]);
try { if ($checkStmt->fetch()) {
// Проверяем, существует ли пользователь с таким email $_SESSION['registration_errors'] = [ользователь с таким email уже существует'];
$checkStmt = $db->prepare("SELECT user_id FROM users WHERE email = ?"); $_SESSION['old_data'] = [
$checkStmt->execute([$email]); 'fio' => $full_name,
'city' => $city,
if ($checkStmt->fetch()) { 'email' => $email,
$_SESSION['registration_errors'] = ['Пользователь с таким email уже существует']; 'phone' => $phone
$_SESSION['old_data'] = [ ];
'fio' => $full_name, header('Location: ../register.php');
'city' => $city, exit();
'email' => $email, }
'phone' => $phone
]; $password_hash = password_hash($password, PASSWORD_DEFAULT);
header('Location: ../register.php');
exit(); $is_admin = false;
} $admin_emails = ['admin@aeterna.ru', 'administrator@aeterna.ru', 'aeterna@mail.ru'];
if (in_array(strtolower($email), $admin_emails)) {
// Хэшируем пароль $is_admin = true;
$password_hash = password_hash($password, PASSWORD_DEFAULT); }
// Определяем, является ли пользователь администратором $stmt = $db->prepare("
$is_admin = false; INSERT INTO users (email, password_hash, full_name, phone, city, is_admin, is_active)
$admin_emails = ['admin@aeterna.ru', 'administrator@aeterna.ru', 'aeterna@mail.ru']; VALUES (?, ?, ?, ?, ?, CAST(? AS boolean), TRUE)
if (in_array(strtolower($email), $admin_emails)) { RETURNING user_id
$is_admin = true; ");
}
$stmt->execute([
// Используем CAST для правильной передачи boolean в PostgreSQL $email,
$stmt = $db->prepare(" $password_hash,
INSERT INTO users (email, password_hash, full_name, phone, city, is_admin, is_active) $full_name,
VALUES (?, ?, ?, ?, ?, CAST(? AS boolean), TRUE) $phone,
RETURNING user_id $city,
"); $is_admin ? 'true' : 'false'
]);
$stmt->execute([
$email, $user_id = $stmt->fetchColumn();
$password_hash,
$full_name, if (!$user_id) {
$phone, throw new Exception('Ошибка при создании пользователя: user_id не получен');
$city, }
$is_admin ? 'true' : 'false' // Строковые значения true/false для CAST
]); $verifyStmt = $db->prepare("SELECT user_id, email, password_hash FROM users WHERE user_id = ?");
$verifyStmt->execute([$user_id]);
$user_id = $stmt->fetchColumn(); $verifyUser = $verifyStmt->fetch(PDO::FETCH_ASSOC);
if (!$user_id) { if (!$verifyUser) {
throw new Exception('Ошибка при создании пользователя: user_id не получен'); throw new Exception('Ошибка: пользователь не найден после создания');
} }
// Проверяем, что пользователь действительно создался if (empty($verifyUser['password_hash'])) {
$verifyStmt = $db->prepare("SELECT user_id, email, password_hash FROM users WHERE user_id = ?"); throw new Exception('Ошибка: пароль не сохранен');
$verifyStmt->execute([$user_id]); }
$verifyUser = $verifyStmt->fetch(PDO::FETCH_ASSOC);
$_SESSION['user_id'] = $user_id;
if (!$verifyUser) { $_SESSION['user_email'] = $email;
throw new Exception('Ошибка: пользователь не найден после создания'); $_SESSION['full_name'] = $full_name;
} $_SESSION['user_phone'] = $phone;
$_SESSION['user_city'] = $city;
// Проверяем, что пароль сохранился правильно $_SESSION['isLoggedIn'] = true;
if (empty($verifyUser['password_hash'])) { $_SESSION['isAdmin'] = (bool)$is_admin;
throw new Exception('Ошибка: пароль не сохранен'); $_SESSION['login_time'] = time();
}
$updateStmt = $db->prepare("UPDATE users SET last_login = CURRENT_TIMESTAMP WHERE user_id = ?");
// Автоматически авторизуем пользователя $updateStmt->execute([$user_id]);
$_SESSION['user_id'] = $user_id;
$_SESSION['user_email'] = $email; $_SESSION['registration_success'] = 'Регистрация прошла успешно! ' .
$_SESSION['full_name'] = $full_name; ($is_admin ? 'Вы зарегистрированы как администратор.' : 'Добро пожаловать в AETERNA!');
$_SESSION['user_phone'] = $phone;
$_SESSION['user_city'] = $city; header('Location: ../catalog.php');
$_SESSION['isLoggedIn'] = true; exit();
$_SESSION['isAdmin'] = (bool)$is_admin;
$_SESSION['login_time'] = time(); } catch (PDOException $e) {
// Обновляем время последнего входа error_log("Registration DB Error: " . $e->getMessage());
$updateStmt = $db->prepare("UPDATE users SET last_login = CURRENT_TIMESTAMP WHERE user_id = ?"); error_log("SQL State: " . $e->getCode());
$updateStmt->execute([$user_id]); error_log("Email: " . $email);
// Перенаправляем на главную или каталог $_SESSION['registration_errors'] = ['Ошибка базы данных: ' . $e->getMessage()];
$_SESSION['registration_success'] = 'Регистрация прошла успешно! ' . $_SESSION['old_data'] = [
($is_admin ? 'Вы зарегистрированы как администратор.' : 'Добро пожаловать в AETERNA!'); 'fio' => $full_name,
'city' => $city,
header('Location: ../catalog.php'); 'email' => $email,
exit(); 'phone' => $phone
];
} catch (PDOException $e) { header('Location: ../register.php');
// Логируем полную ошибку для отладки exit();
error_log("Registration DB Error: " . $e->getMessage()); } catch (Exception $e) {
error_log("SQL State: " . $e->getCode()); error_log("Registration Error: " . $e->getMessage());
error_log("Email: " . $email);
$_SESSION['registration_errors'] = [$e->getMessage()];
$_SESSION['registration_errors'] = ['Ошибка базы данных: ' . $e->getMessage()]; header('Location: ../register.php');
$_SESSION['old_data'] = [ exit();
'fio' => $full_name, }
'city' => $city,
'email' => $email, } else {
'phone' => $phone
]; header('Location: register.php');
header('Location: ../register.php'); exit();
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();
}
?> ?>

View File

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

View File

@@ -1,346 +1,306 @@
// script.js
$(document).ready(function() {
$(document).ready(function() {
// Инициализация корзины let cart = {
let cart = { items: [
items: [ { id: 1, name: 'Кресло OPPORTUNITY', price: 16999, quantity: 1 },
{ id: 1, name: 'Кресло OPPORTUNITY', price: 16999, quantity: 1 }, { id: 2, name: 'Кресло GOLDEN', price: 19999, quantity: 1 },
{ id: 2, name: 'Кресло GOLDEN', price: 19999, quantity: 1 }, { id: 3, name: 'Светильник POLET', price: 7999, quantity: 1 }
{ id: 3, name: 'Светильник POLET', price: 7999, quantity: 1 } ],
], delivery: 2000,
delivery: 2000, discount: 0
discount: 0 };
};
function updateTotal() {
// Функция обновления общей суммы let productsTotal = 0;
function updateTotal() { let totalCount = 0;
let productsTotal = 0;
let totalCount = 0; $('.products__item').each(function() {
const $item = $(this);
// Пересчитываем товары const price = parseInt($item.data('price'));
$('.products__item').each(function() { const quantity = parseInt($item.find('.products__qty-value').text());
const $item = $(this);
const price = parseInt($item.data('price')); productsTotal += price * quantity;
const quantity = parseInt($item.find('.products__qty-value').text()); totalCount += quantity;
});
productsTotal += price * quantity;
totalCount += quantity; $('.products-total').text(productsTotal + ' ₽');
}); $('.summary-count').text(totalCount);
$('.total-count').text(totalCount + ' шт.');
// Обновляем отображение $('.cart-count').text(totalCount);
$('.products-total').text(productsTotal + ' ₽');
$('.summary-count').text(totalCount); const finalTotal = productsTotal + cart.delivery - cart.discount;
$('.total-count').text(totalCount + ' шт.'); $('.final-total').text(finalTotal + ' ');
$('.cart-count').text(totalCount); }
// Обновляем итоговую сумму function validateEmail(email) {
const finalTotal = productsTotal + cart.delivery - cart.discount; const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
$('.final-total').text(finalTotal + ' ₽'); return emailRegex.test(email);
} }
// Функция валидации email function validateFullName(name) {
function validateEmail(email) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; const nameRegex = /^[a-zA-Zа-яА-ЯёЁ\s\-']+$/;
return emailRegex.test(email);
} const words = name.trim().split(/\s+/);
// Функция валидации имени (ФИО) return nameRegex.test(name) && words.length >= 2;
function validateFullName(name) { }
// Проверяем, что имя содержит только буквы, пробелы, дефисы и апострофы
const nameRegex = /^[a-zA-Zа-яА-ЯёЁ\s\-']+$/; function validatePhone(phone) {
const phoneRegex = /^\+7\d{10}$/;
// Проверяем, что имя состоит минимум из 2 слов return phoneRegex.test(phone);
const words = name.trim().split(/\s+/); }
return nameRegex.test(name) && words.length >= 2; function showMessage(messageId, duration = 5000) {
} $('.message').hide();
// Функция валидации телефона $(messageId).fadeIn(300);
function validatePhone(phone) {
// Российский формат телефона: +7XXXXXXXXXX if (duration > 0) {
const phoneRegex = /^\+7\d{10}$/; setTimeout(() => {
return phoneRegex.test(phone); $(messageId).fadeOut(300);
} }, duration);
}
// Функция отображения сообщения }
function showMessage(messageId, duration = 5000) {
// Скрываем все сообщения function showPrivacyError(show) {
$('.message').hide(); if (show) {
$('#privacy-error').show();
// Показываем нужное сообщение } else {
$(messageId).fadeIn(300); $('#privacy-error').hide();
}
// Автоматически скрываем через указанное время }
if (duration > 0) {
setTimeout(() => { function showFieldError(fieldId, message) {
$(messageId).fadeOut(300); $(fieldId).removeClass('error-input');
}, duration); $(fieldId + '-error').remove();
}
} if (message) {
$(fieldId).addClass('error-input');
// Функция показа ошибки приватности $(fieldId).after('<div class="field-error" id="' + fieldId.replace('#', '') + '-error">' + message + '</div>');
function showPrivacyError(show) { }
if (show) { }
$('#privacy-error').show();
} else { $('#fullname').on('blur', function() {
$('#privacy-error').hide(); const value = $(this).val().trim();
} if (value) {
} if (!validateFullName(value)) {
showFieldError('#fullname', 'ФИО должно содержать только буквы и состоять минимум из 2 слов');
// Функция отображения ошибки конкретного поля } else {
function showFieldError(fieldId, message) { showFieldError('#fullname', '');
// Убираем старые ошибки }
$(fieldId).removeClass('error-input'); } else {
$(fieldId + '-error').remove(); showFieldError('#fullname', '');
}
if (message) { });
$(fieldId).addClass('error-input');
$(fieldId).after('<div class="field-error" id="' + fieldId.replace('#', '') + '-error">' + message + '</div>'); $('#email').on('blur', function() {
} const value = $(this).val().trim();
} if (value) {
if (!validateEmail(value)) {
// Валидация поля при потере фокуса с указанием конкретной ошибки showFieldError('#email', 'Введите корректный email адрес (например: example@mail.ru)');
$('#fullname').on('blur', function() { } else {
const value = $(this).val().trim(); showFieldError('#email', '');
if (value) { }
if (!validateFullName(value)) { } else {
showFieldError('#fullname', 'ФИО должно содержать только буквы и состоять минимум из 2 слов'); showFieldError('#email', '');
} else { }
showFieldError('#fullname', ''); });
}
} else { $('#phone').on('blur', function() {
showFieldError('#fullname', ''); const value = $(this).val().trim();
} if (value) {
}); if (!validatePhone(value)) {
showFieldError('#phone', 'Введите номер в формате +7XXXXXXXXXX (10 цифр после +7)');
$('#email').on('blur', function() { } else {
const value = $(this).val().trim(); showFieldError('#phone', '');
if (value) { }
if (!validateEmail(value)) { } else {
showFieldError('#email', 'Введите корректный email адрес (например: example@mail.ru)'); showFieldError('#phone', '');
} else { }
showFieldError('#email', ''); });
}
} else { $('#region').on('blur', function() {
showFieldError('#email', ''); const value = $(this).val().trim();
} if (!value) {
}); showFieldError('#region', 'Укажите регион доставки');
} else {
$('#phone').on('blur', function() { showFieldError('#region', '');
const value = $(this).val().trim(); }
if (value) { });
if (!validatePhone(value)) {
showFieldError('#phone', 'Введите номер в формате +7XXXXXXXXXX (10 цифр после +7)'); $('#address').on('blur', function() {
} else { const value = $(this).val().trim();
showFieldError('#phone', ''); if (!value) {
} showFieldError('#address', 'Укажите адрес доставки (улица, дом, квартира)');
} else { } else {
showFieldError('#phone', ''); showFieldError('#address', '');
} }
}); });
// Валидация обязательных полей $('.form__input').on('input', function() {
$('#region').on('blur', function() { const fieldId = '#' + $(this).attr('id');
const value = $(this).val().trim(); showFieldError(fieldId, '');
if (!value) { });
showFieldError('#region', 'Укажите регион доставки');
} else { $('.products__qty-btn.plus').click(function() {
showFieldError('#region', ''); const $qtyValue = $(this).siblings('.products__qty-value');
} let quantity = parseInt($qtyValue.text());
}); $qtyValue.text(quantity + 1);
updateTotal();
$('#address').on('blur', function() { });
const value = $(this).val().trim();
if (!value) { $('.products__qty-btn.minus').click(function() {
showFieldError('#address', 'Укажите адрес доставки (улица, дом, квартира)'); const $qtyValue = $(this).siblings('.products__qty-value');
} else { let quantity = parseInt($qtyValue.text());
showFieldError('#address', ''); if (quantity > 1) {
} $qtyValue.text(quantity - 1);
}); updateTotal();
}
// Очистка ошибки при начале ввода });
$('.form__input').on('input', function() {
const fieldId = '#' + $(this).attr('id'); $('.remove-from-cart').click(function() {
showFieldError(fieldId, ''); const $productItem = $(this).closest('.products__item');
}); $productItem.fadeOut(300, function() {
$(this).remove();
// Обработчик увеличения количества updateTotal();
$('.products__qty-btn.plus').click(function() {
const $qtyValue = $(this).siblings('.products__qty-value'); if ($('.products__item').length === 0) {
let quantity = parseInt($qtyValue.text()); $('.products__list').html('<div class="empty-cart">Корзина пуста</div>');
$qtyValue.text(quantity + 1); }
updateTotal(); });
}); });
// Обработчик уменьшения количества $('.promo__btn').click(function() {
$('.products__qty-btn.minus').click(function() { const promoCode = $('.promo__input').val().toUpperCase();
const $qtyValue = $(this).siblings('.products__qty-value');
let quantity = parseInt($qtyValue.text()); if (promoCode === 'SALE10') {
if (quantity > 1) { cart.discount = Math.round(parseInt($('.products-total').text()) * 0.1);
$qtyValue.text(quantity - 1); $('.discount-total').text(cart.discount + ' ₽');
updateTotal(); showMessage('#form-error', 3000);
} $('#form-error').text('Промокод применен! Скидка 10%').removeClass('error').addClass('success');
}); } else if (promoCode === 'FREE') {
cart.delivery = 0;
// Обработчик удаления товара $('.delivery-price').text('0 ₽');
$('.remove-from-cart').click(function() { showMessage('#form-error', 3000);
const $productItem = $(this).closest('.products__item'); $('#form-error').text('Промокод применен! Бесплатная доставка').removeClass('error').addClass('success');
$productItem.fadeOut(300, function() { } else if (promoCode) {
$(this).remove(); showMessage('#form-error', 3000);
updateTotal(); $('#form-error').text('Промокод недействителен').removeClass('success').addClass('error');
}
// Показываем сообщение, если корзина пуста
if ($('.products__item').length === 0) { updateTotal();
$('.products__list').html('<div class="empty-cart">Корзина пуста</div>'); });
}
}); $('input[name="delivery"]').change(function() {
}); if ($(this).val() === 'pickup') {
cart.delivery = 0;
// Обработчик применения промокода $('.delivery-price').text('0 ₽');
$('.promo__btn').click(function() { } else {
const promoCode = $('.promo__input').val().toUpperCase(); cart.delivery = 2000;
$('.delivery-price').text('2000 ₽');
if (promoCode === 'SALE10') { }
cart.discount = Math.round(parseInt($('.products-total').text()) * 0.1); updateTotal();
$('.discount-total').text(cart.discount + ' ₽'); });
showMessage('#form-error', 3000);
$('#form-error').text('Промокод применен! Скидка 10%').removeClass('error').addClass('success'); function validateForm() {
} else if (promoCode === 'FREE') { let isValid = true;
cart.delivery = 0; let errorMessages = [];
$('.delivery-price').text('0 ₽');
showMessage('#form-error', 3000); $('.field-error').remove();
$('#form-error').text('Промокод применен! Бесплатная доставка').removeClass('error').addClass('success'); $('.form__input').removeClass('error-input');
} else if (promoCode) {
showMessage('#form-error', 3000); const requiredFields = [
$('#form-error').text('Промокод недействителен').removeClass('success').addClass('error'); {
} id: '#fullname',
value: $('#fullname').val().trim(),
updateTotal(); validator: validateFullName,
}); required: true,
message: 'ФИО должно содержать только буквы и состоять минимум из 2 слов'
// Обработчик выбора доставки },
$('input[name="delivery"]').change(function() { {
if ($(this).val() === 'pickup') { id: '#phone',
cart.delivery = 0; value: $('#phone').val().trim(),
$('.delivery-price').text('0 ₽'); validator: validatePhone,
} else { required: true,
cart.delivery = 2000; message: 'Введите корректный номер телефона в формате +7XXXXXXXXXX'
$('.delivery-price').text('2000 ₽'); },
} {
updateTotal(); id: '#email',
}); value: $('#email').val().trim(),
validator: validateEmail,
// Функция проверки всех полей формы required: true,
function validateForm() { message: 'Введите корректный email адрес'
let isValid = true; },
let errorMessages = []; {
id: '#region',
// Очищаем все старые ошибки value: $('#region').val().trim(),
$('.field-error').remove(); validator: (val) => val.length > 0,
$('.form__input').removeClass('error-input'); required: true,
message: 'Поле "Регион" обязательно для заполнения'
// Проверка обязательных полей },
const requiredFields = [ {
{ id: '#address',
id: '#fullname', value: $('#address').val().trim(),
value: $('#fullname').val().trim(), validator: (val) => val.length > 0,
validator: validateFullName, required: true,
required: true, message: 'Поле "Адрес" обязательно для заполнения'
message: 'ФИО должно содержать только буквы и состоять минимум из 2 слов' }
}, ];
{
id: '#phone', requiredFields.forEach(field => {
value: $('#phone').val().trim(), if (field.required && (!field.value || !field.validator(field.value))) {
validator: validatePhone, isValid = false;
required: true, errorMessages.push(field.message);
message: 'Введите корректный номер телефона в формате +7XXXXXXXXXX' showFieldError(field.id, field.message);
}, }
{ });
id: '#email',
value: $('#email').val().trim(), if (!$('#privacy-checkbox').is(':checked')) {
validator: validateEmail, isValid = false;
required: true, showPrivacyError(true);
message: 'Введите корректный email адрес' errorMessages.push('Необходимо согласие на обработку персональных данных');
}, } else {
{ showPrivacyError(false);
id: '#region', }
value: $('#region').val().trim(),
validator: (val) => val.length > 0, if (!isValid && errorMessages.length > 0) {
required: true, showMessage('#form-error', 5000);
message: 'Поле "Регион" обязательно для заполнения' $('#form-error').text('Исправьте следующие ошибки: ' + errorMessages.join('; ')).removeClass('success').addClass('error');
},
{ $('html, body').animate({
id: '#address', scrollTop: $('.error-input').first().offset().top - 100
value: $('#address').val().trim(), }, 500);
validator: (val) => val.length > 0, }
required: true,
message: 'Поле "Адрес" обязательно для заполнения' return isValid;
} }
];
$('#submit-order').click(function() {
// Проверяем каждое поле if (!validateForm()) {
requiredFields.forEach(field => { return;
if (field.required && (!field.value || !field.validator(field.value))) { }
isValid = false;
errorMessages.push(field.message); $(this).prop('disabled', true).text('ОБРАБОТКА...');
showFieldError(field.id, field.message);
} setTimeout(() => {
}); showMessage('#order-success', 5000);
$(this).prop('disabled', false).text('ОФОРМИТЬ ЗАКАЗ');
// Проверка согласия на обработку данных
if (!$('#privacy-checkbox').is(':checked')) { }, 2000);
isValid = false; });
showPrivacyError(true);
errorMessages.push('Необходимо согласие на обработку персональных данных'); $('#phone').on('input', function() {
} else { let phone = $(this).val().replace(/\D/g, '');
showPrivacyError(false); if (phone.length > 0) {
} if (phone[0] !== '7' && phone[0] !== '8') {
phone = '7' + phone;
// Показываем общее сообщение, если есть ошибки }
if (!isValid && errorMessages.length > 0) { phone = '+7' + phone.substring(1, 11);
showMessage('#form-error', 5000); $(this).val(phone);
$('#form-error').text('Исправьте следующие ошибки: ' + errorMessages.join('; ')).removeClass('success').addClass('error'); }
});
// Прокручиваем к первой ошибке
$('html, body').animate({ updateTotal();
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();
});

View File

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

View File

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

View File

@@ -83,4 +83,3 @@
border-color: @color-primary; border-color: @color-primary;
} }
} }

File diff suppressed because it is too large Load Diff

View File

@@ -1,114 +1,107 @@
// check_auth.js
$(document).ready(function() { $(document).ready(function() {
// Проверка авторизации при загрузке страницы
checkAuthStatus(); checkAuthStatus();
// Обработка формы входа $('#loginForm').on('submit', function(e) {
$('#loginForm').on('submit', function(e) { e.preventDefault();
e.preventDefault();
const email = $('#login-email').val();
const email = $('#login-email').val(); const password = $('#login-password').val();
const password = $('#login-password').val(); const remember = $('#remember').is(':checked');
const remember = $('#remember').is(':checked');
$.ajax({
$.ajax({ url: 'login_handler.php',
url: 'login_handler.php', method: 'POST',
method: 'POST', data: {
data: { email: email,
email: email, password: password
password: password },
}, success: function(response) {
success: function(response) { try {
try { const result = JSON.parse(response);
const result = JSON.parse(response); if (result.success) {
if (result.success) {
// Сохраняем в localStorage если выбрано "Запомнить меня" if (remember) {
if (remember) { localStorage.setItem('rememberedEmail', email);
localStorage.setItem('rememberedEmail', email); } else {
} else { localStorage.removeItem('rememberedEmail');
localStorage.removeItem('rememberedEmail'); }
}
window.location.href = result.redirect || 'catalog.php';
// Перенаправляем } else {
window.location.href = result.redirect || 'catalog.php'; showMessage('error', result.message || 'Ошибка авторизации');
} else { }
showMessage('error', result.message || 'Ошибка авторизации'); } catch(e) {
} showMessage('error', 'Ошибка обработки ответа');
} catch(e) { }
showMessage('error', 'Ошибка обработки ответа'); },
} error: function() {
}, showMessage('error', 'Ошибка сервера');
error: function() { }
showMessage('error', 'Ошибка сервера'); });
} });
});
}); function checkAuthStatus() {
$.ajax({
// Проверка статуса авторизации url: 'check_auth_status.php',
function checkAuthStatus() { method: 'GET',
$.ajax({ success: function(response) {
url: 'check_auth_status.php', try {
method: 'GET', const result = JSON.parse(response);
success: function(response) { if (result.loggedIn) {
try { updateUserProfile(result.user);
const result = JSON.parse(response); }
if (result.loggedIn) { } catch(e) {
updateUserProfile(result.user); console.error('Ошибка проверки авторизации', e);
} }
} catch(e) { }
console.error('Ошибка проверки авторизации', e); });
} }
}
}); function updateUserProfile(user) {
}
if ($('#userEmail').length) {
// Обновление профиля пользователя $('#userEmail').text(user.email);
function updateUserProfile(user) { }
// Обновляем шапку, если есть элементы для профиля if ($('#userName').length) {
if ($('#userEmail').length) { $('#userName').text(user.full_name);
$('#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);
function showMessage(type, text) { } else {
const $message = $('#' + type + 'Message'); alert(text);
if ($message.length) { }
$message.text(text).fadeIn(); }
setTimeout(() => $message.fadeOut(), 5000);
} else { function checkAuth(redirectUrl) {
alert(text); $.ajax({
} url: 'check_auth_status.php',
} method: 'GET',
success: function(response) {
// Проверка авторизации для ссылок try {
function checkAuth(redirectUrl) { const result = JSON.parse(response);
$.ajax({ if (result.loggedIn) {
url: 'check_auth_status.php', window.location.href = redirectUrl;
method: 'GET', } else {
success: function(response) {
try { showLoginModal(redirectUrl);
const result = JSON.parse(response); }
if (result.loggedIn) { } catch(e) {
window.location.href = redirectUrl; showLoginModal(redirectUrl);
} else { }
// Показываем модальное окно или перенаправляем на вход }
showLoginModal(redirectUrl); });
} return false;
} catch(e) { }
showLoginModal(redirectUrl);
} function showLoginModal(redirectUrl) {
}
}); window.location.href = 'вход.php?redirect=' + encodeURIComponent(redirectUrl);
return false; }
} });
// Показать модальное окно входа
function showLoginModal(redirectUrl) {
// Можно реализовать модальное окно или перенаправить на страницу входа
window.location.href = 'вход.php?redirect=' + encodeURIComponent(redirectUrl);
}
});

File diff suppressed because it is too large Load Diff

View File

@@ -1,114 +1,107 @@
// check_auth.js
$(document).ready(function() { $(document).ready(function() {
// Проверка авторизации при загрузке страницы
checkAuthStatus(); checkAuthStatus();
// Обработка формы входа $('#loginForm').on('submit', function(e) {
$('#loginForm').on('submit', function(e) { e.preventDefault();
e.preventDefault();
const email = $('#login-email').val();
const email = $('#login-email').val(); const password = $('#login-password').val();
const password = $('#login-password').val(); const remember = $('#remember').is(':checked');
const remember = $('#remember').is(':checked');
$.ajax({
$.ajax({ url: 'login_handler.php',
url: 'login_handler.php', method: 'POST',
method: 'POST', data: {
data: { email: email,
email: email, password: password
password: password },
}, success: function(response) {
success: function(response) { try {
try { const result = JSON.parse(response);
const result = JSON.parse(response); if (result.success) {
if (result.success) {
// Сохраняем в localStorage если выбрано "Запомнить меня" if (remember) {
if (remember) { localStorage.setItem('rememberedEmail', email);
localStorage.setItem('rememberedEmail', email); } else {
} else { localStorage.removeItem('rememberedEmail');
localStorage.removeItem('rememberedEmail'); }
}
window.location.href = result.redirect || 'catalog.php';
// Перенаправляем } else {
window.location.href = result.redirect || 'catalog.php'; showMessage('error', result.message || 'Ошибка авторизации');
} else { }
showMessage('error', result.message || 'Ошибка авторизации'); } catch(e) {
} showMessage('error', 'Ошибка обработки ответа');
} catch(e) { }
showMessage('error', 'Ошибка обработки ответа'); },
} error: function() {
}, showMessage('error', 'Ошибка сервера');
error: function() { }
showMessage('error', 'Ошибка сервера'); });
} });
});
}); function checkAuthStatus() {
$.ajax({
// Проверка статуса авторизации url: 'check_auth_status.php',
function checkAuthStatus() { method: 'GET',
$.ajax({ success: function(response) {
url: 'check_auth_status.php', try {
method: 'GET', const result = JSON.parse(response);
success: function(response) { if (result.loggedIn) {
try { updateUserProfile(result.user);
const result = JSON.parse(response); }
if (result.loggedIn) { } catch(e) {
updateUserProfile(result.user); console.error('Ошибка проверки авторизации', e);
} }
} catch(e) { }
console.error('Ошибка проверки авторизации', e); });
} }
}
}); function updateUserProfile(user) {
}
if ($('#userEmail').length) {
// Обновление профиля пользователя $('#userEmail').text(user.email);
function updateUserProfile(user) { }
// Обновляем шапку, если есть элементы для профиля if ($('#userName').length) {
if ($('#userEmail').length) { $('#userName').text(user.full_name);
$('#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);
function showMessage(type, text) { } else {
const $message = $('#' + type + 'Message'); alert(text);
if ($message.length) { }
$message.text(text).fadeIn(); }
setTimeout(() => $message.fadeOut(), 5000);
} else { function checkAuth(redirectUrl) {
alert(text); $.ajax({
} url: 'check_auth_status.php',
} method: 'GET',
success: function(response) {
// Проверка авторизации для ссылок try {
function checkAuth(redirectUrl) { const result = JSON.parse(response);
$.ajax({ if (result.loggedIn) {
url: 'check_auth_status.php', window.location.href = redirectUrl;
method: 'GET', } else {
success: function(response) {
try { showLoginModal(redirectUrl);
const result = JSON.parse(response); }
if (result.loggedIn) { } catch(e) {
window.location.href = redirectUrl; showLoginModal(redirectUrl);
} else { }
// Показываем модальное окно или перенаправляем на вход }
showLoginModal(redirectUrl); });
} return false;
} catch(e) { }
showLoginModal(redirectUrl);
} function showLoginModal(redirectUrl) {
}
}); window.location.href = 'вход.php?redirect=' + encodeURIComponent(redirectUrl);
return false; }
} });
// Показать модальное окно входа
function showLoginModal(redirectUrl) {
// Можно реализовать модальное окно или перенаправить на страницу входа
window.location.href = 'вход.php?redirect=' + encodeURIComponent(redirectUrl);
}
});

View File

@@ -1,32 +1,32 @@
<?php <?php
// config/database.php
class Database { class Database {
private static $instance = null; private static $instance = null;
private $connection; private $connection;
private function __construct() { private function __construct() {
try { try {
$this->connection = new PDO( $this->connection = new PDO(
"pgsql:host=185.130.224.177;port=5481;dbname=postgres", "pgsql:host=185.130.224.177;port=5481;dbname=postgres",
"admin", "admin",
"38feaad2840ccfda0e71243a6faaecfd" "38feaad2840ccfda0e71243a6faaecfd"
); );
$this->connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $this->connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->connection->exec("SET NAMES 'utf8'"); $this->connection->exec("SET NAMES 'utf8'");
} catch(PDOException $e) { } catch(PDOException $e) {
die("Ошибка подключения: " . $e->getMessage()); die("Ошибка подключения: " . $e->getMessage());
} }
} }
public static function getInstance() { public static function getInstance() {
if (self::$instance == null) { if (self::$instance == null) {
self::$instance = new Database(); self::$instance = new Database();
} }
return self::$instance; return self::$instance;
} }
public function getConnection() { public function getConnection() {
return $this->connection; return $this->connection;
} }
} }
?> ?>

View File

@@ -1,174 +1,174 @@
<?php <?php
// В начале файла
require_once __DIR__ . '/../config/database.php'; require_once __DIR__ . '/../config/database.php';
?> ?>
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>AETERNA - Доставка и оплата</title> <title>AETERNA - Доставка и оплата</title>
<link rel="stylesheet/less" type="text/css" href="style_for_cite.less"> <link rel="stylesheet/less" type="text/css" href="style_for_cite.less">
<script src="https://cdn.jsdelivr.net/npm/less"></script> <script src="https://cdn.jsdelivr.net/npm/less"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
<script src="check_auth.js"></script> <script src="check_auth.js"></script>
<style> <style>
/* Стили для блокировки доступа */
.link-disabled { .link-disabled {
cursor: not-allowed !important; cursor: not-allowed !important;
opacity: 0.7 !important; opacity: 0.7 !important;
pointer-events: none !important; pointer-events: none !important;
} }
.auth-required { .auth-required {
position: relative; position: relative;
} }
.auth-required::after { .auth-required::after {
content: "🔒"; content: "🔒";
position: absolute; position: absolute;
top: -5px; top: -5px;
right: -10px; right: -10px;
font-size: 10px; font-size: 10px;
} }
</style> </style>
</head> </head>
<body> <body>
<?php include 'header_common.php'; ?> <?php include 'header_common.php'; ?>
<main class="catalog-main"> <main class="catalog-main">
<div class="container"> <div class="container">
<div class="breadcrumbs"> <div class="breadcrumbs">
<a href="cite_mebel.php">Главная</a> • <span class="current-page">Доставка и оплата</span> <a href="cite_mebel.php">Главная</a> • <span class="current-page">Доставка и оплата</span>
</div> </div>
<div class="delivery-content"> <div class="delivery-content">
<h1>ДОСТАВКА И ОПЛАТА</h1> <h1>ДОСТАВКА И ОПЛАТА</h1>
<div class="delivery-section"> <div class="delivery-section">
<div class="delivery-card"> <div class="delivery-card">
<div class="delivery-icon"> <div class="delivery-icon">
<i class="fas fa-truck"></i> <i class="fas fa-truck"></i>
</div> </div>
<h3>Курьерская доставка</h3> <h3>Курьерская доставка</h3>
<div class="delivery-details"> <div class="delivery-details">
<div class="detail-item"> <div class="detail-item">
<span class="detail-label">Бесплатная доставка:</span> <span class="detail-label">Бесплатная доставка:</span>
<span class="detail-value">при заказе от 30 000 ₽</span> <span class="detail-value">при заказе от 30 000 ₽</span>
</div> </div>
<div class="detail-item"> <div class="detail-item">
<span class="detail-label">В пределах МКАД:</span> <span class="detail-label">В пределах МКАД:</span>
<span class="detail-value">1 500 ₽</span> <span class="detail-value">1 500 ₽</span>
</div> </div>
<div class="detail-item"> <div class="detail-item">
<span class="detail-label">За МКАД:</span> <span class="detail-label">За МКАД:</span>
<span class="detail-value">1 500 ₽ + 50 ₽/км</span> <span class="detail-value">1 500 ₽ + 50 ₽/км</span>
</div> </div>
<div class="detail-item"> <div class="detail-item">
<span class="detail-label">Время доставки:</span> <span class="detail-label">Время доставки:</span>
<span class="detail-value">с 9:00 до 21:00</span> <span class="detail-value">с 9:00 до 21:00</span>
</div> </div>
</div> </div>
</div> </div>
<div class="delivery-card"> <div class="delivery-card">
<div class="delivery-icon"> <div class="delivery-icon">
<i class="fas fa-store"></i> <i class="fas fa-store"></i>
</div> </div>
<h3>Самовывоз из шоурума</h3> <h3>Самовывоз из шоурума</h3>
<div class="delivery-details"> <div class="delivery-details">
<div class="detail-item"> <div class="detail-item">
<span class="detail-label">Адрес:</span> <span class="detail-label">Адрес:</span>
<span class="detail-value">г. Москва, ул. Дизайнерская, 15</span> <span class="detail-value">г. Москва, ул. Дизайнерская, 15</span>
</div> </div>
<div class="detail-item"> <div class="detail-item">
<span class="detail-label">Стоимость:</span> <span class="detail-label">Стоимость:</span>
<span class="detail-value">Бесплатно</span> <span class="detail-value">Бесплатно</span>
</div> </div>
<div class="detail-item"> <div class="detail-item">
<span class="detail-label">Время получения:</span> <span class="detail-label">Время получения:</span>
<span class="detail-value">в течение 2 часов после подтверждения</span> <span class="detail-value">в течение 2 часов после подтверждения</span>
</div> </div>
<div class="detail-item"> <div class="detail-item">
<span class="detail-label">Парковка:</span> <span class="detail-label">Парковка:</span>
<span class="detail-value">Бесплатная для клиентов</span> <span class="detail-value">Бесплатная для клиентов</span>
</div> </div>
</div> </div>
</div> </div>
<div class="delivery-card"> <div class="delivery-card">
<div class="delivery-icon"> <div class="delivery-icon">
<i class="fas fa-map-marked-alt"></i> <i class="fas fa-map-marked-alt"></i>
</div> </div>
<h3>Доставка по России</h3> <h3>Доставка по России</h3>
<div class="delivery-details"> <div class="delivery-details">
<div class="detail-item"> <div class="detail-item">
<span class="detail-label">Стоимость:</span> <span class="detail-label">Стоимость:</span>
<span class="detail-value">рассчитывается индивидуально</span> <span class="detail-value">рассчитывается индивидуально</span>
</div> </div>
<div class="detail-item"> <div class="detail-item">
<span class="detail-label">Сроки:</span> <span class="detail-label">Сроки:</span>
<span class="detail-value">от 3 до 14 дней</span> <span class="detail-value">от 3 до 14 дней</span>
</div> </div>
<div class="detail-item"> <div class="detail-item">
<span class="detail-label">Транспортные компании:</span> <span class="detail-label">Транспортные компании:</span>
<span class="detail-value">СДЭК, Boxberry, Деловые Линии</span> <span class="detail-value">СДЭК, Boxberry, Деловые Линии</span>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</main> </main>
<footer class="footer" id="footer"> <footer class="footer" id="footer">
<div class="container footer__content"> <div class="container footer__content">
<div class="footer__col footer__col--logo"> <div class="footer__col footer__col--logo">
<div class="logo">AETERNA</div> <div class="logo">AETERNA</div>
</div> </div>
<div class="footer__col"> <div class="footer__col">
<h5>ПОКУПАТЕЛЮ</h5> <h5>ПОКУПАТЕЛЮ</h5>
<ul> <ul>
<li><a href="/catalog.php">Каталог</a></li> <li><a href="/catalog.php">Каталог</a></li>
<li><a href="services.php">Услуги</a></li> <li><a href="services.php">Услуги</a></li>
</ul> </ul>
</div> </div>
<div class="footer__col"> <div class="footer__col">
<h5>ПОМОЩЬ</h5> <h5>ПОМОЩЬ</h5>
<ul> <ul>
<li><a href="доставка.php">Доставка и оплата</a></li> <li><a href="доставка.php">Доставка и оплата</a></li>
<li><a href="гарантия.php">Гарантия и возврат</a></li> <li><a href="гарантия.php">Гарантия и возврат</a></li>
<li><a href="cite_mebel.php#faq">Ответы на вопросы</a></li> <li><a href="cite_mebel.php#faq">Ответы на вопросы</a></li>
<li><a href="#footer">Контакты</a></li> <li><a href="#footer">Контакты</a></li>
</ul> </ul>
</div> </div>
<div class="footer__col"> <div class="footer__col">
<h5>КОНТАКТЫ</h5> <h5>КОНТАКТЫ</h5>
<p>aeterna@mail.ru</p> <p>aeterna@mail.ru</p>
<p>+7(912)999-12-23</p> <p>+7(912)999-12-23</p>
<div class="social-icons"> <div class="social-icons">
<span class="icon"><i class="fab fa-telegram"></i></span> <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-instagram"></i></span>
<span class="icon"><i class="fab fa-vk"></i></span> <span class="icon"><i class="fab fa-vk"></i></span>
</div> </div>
</div> </div>
<div class="footer__col"> <div class="footer__col">
<h5>ПРИНИМАЕМ К ОПЛАТЕ</h5> <h5>ПРИНИМАЕМ К ОПЛАТЕ</h5>
<div class="payment-icons"> <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-visa"></i></span>
<span class="pay-icon"><i class="fab fa-cc-mastercard"></i></span> <span class="pay-icon"><i class="fab fa-cc-mastercard"></i></span>
</div> </div>
</div> </div>
</div> </div>
<div class="copyright"> <div class="copyright">
<p>© 2025 AETERNA. Все права защищены.</p> <p>© 2025 AETERNA. Все права защищены.</p>
</div> </div>
</footer> </footer>
</body> </body>
</html> </html>

View File

@@ -47,4 +47,3 @@
</footer> </footer>
</body> </body>
</html> </html>

View File

@@ -1,18 +1,11 @@
<?php <?php
/**
* Единый header для страниц AETERNA (версия для public/)
* Подключение: include 'header_common.php';
*/
// Запускаем сессию если еще не запущена
if (session_status() === PHP_SESSION_NONE) { if (session_status() === PHP_SESSION_NONE) {
session_start(); session_start();
} }
// Определяем текущую страницу
$currentPage = basename($_SERVER['PHP_SELF'], '.php'); $currentPage = basename($_SERVER['PHP_SELF'], '.php');
// Проверяем авторизацию
$isLoggedIn = isset($_SESSION['isLoggedIn']) && $_SESSION['isLoggedIn'] === true; $isLoggedIn = isset($_SESSION['isLoggedIn']) && $_SESSION['isLoggedIn'] === true;
$isAdmin = isset($_SESSION['isAdmin']) && $_SESSION['isAdmin'] === true; $isAdmin = isset($_SESSION['isAdmin']) && $_SESSION['isAdmin'] === true;
$userEmail = $_SESSION['user_email'] ?? ''; $userEmail = $_SESSION['user_email'] ?? '';
@@ -49,13 +42,12 @@ $fullName = $_SESSION['full_name'] ?? $userEmail;
<div class="header__icons--top"> <div class="header__icons--top">
<?php if ($isLoggedIn): ?> <?php if ($isLoggedIn): ?>
<!-- Иконка корзины -->
<a href="checkout.php" class="icon cart-icon"> <a href="checkout.php" class="icon cart-icon">
<i class="fas fa-shopping-cart"></i> <i class="fas fa-shopping-cart"></i>
<span class="cart-count" id="cartCount">0</span> <span class="cart-count" id="cartCount">0</span>
</a> </a>
<!-- Блок профиля -->
<div class="user-profile-dropdown"> <div class="user-profile-dropdown">
<div class="user-profile-toggle" id="profileToggle"> <div class="user-profile-toggle" id="profileToggle">
<div class="user-avatar"><?= !empty($userEmail) ? strtoupper(substr($userEmail, 0, 1)) : 'U' ?></div> <div class="user-avatar"><?= !empty($userEmail) ? strtoupper(substr($userEmail, 0, 1)) : 'U' ?></div>
@@ -111,7 +103,7 @@ $fullName = $_SESSION['full_name'] ?? $userEmail;
</header> </header>
<style> <style>
/* Стили для профиля пользователя */
.user-profile-dropdown { position: relative; display: inline-block; } .user-profile-dropdown { position: relative; display: inline-block; }
.user-profile-toggle { display: flex; align-items: center; gap: 10px; cursor: pointer; padding: 8px 12px; border-radius: 4px; transition: all 0.3s ease; } .user-profile-toggle { display: flex; align-items: center; gap: 10px; cursor: pointer; padding: 8px 12px; border-radius: 4px; transition: all 0.3s ease; }
.user-profile-toggle:hover { background-color: rgba(0, 0, 0, 0.05); } .user-profile-toggle:hover { background-color: rgba(0, 0, 0, 0.05); }
@@ -137,19 +129,18 @@ $fullName = $_SESSION['full_name'] ?? $userEmail;
<script> <script>
$(document).ready(function() { $(document).ready(function() {
// Профиль пользователя - открытие/закрытие
$('#profileToggle').click(function(e) { $('#profileToggle').click(function(e) {
e.stopPropagation(); e.stopPropagation();
$('#profileMenu').toggle(); $('#profileMenu').toggle();
}); });
$(document).click(function(e) { $(document).click(function(e) {
if (!$(e.target).closest('.user-profile-dropdown').length) { if (!$(e.target).closest('.user-profile-dropdown').length) {
$('#profileMenu').hide(); $('#profileMenu').hide();
} }
}); });
// Обновление счетчика корзины
<?php if ($isLoggedIn): ?> <?php if ($isLoggedIn): ?>
$.ajax({ $.ajax({
url: 'api/cart.php?action=count', url: 'api/cart.php?action=count',
@@ -166,4 +157,3 @@ $(document).ready(function() {
<?php endif; ?> <?php endif; ?>
}); });
</script> </script>

View File

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

View File

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

View File

@@ -1,16 +1,10 @@
<?php <?php
/**
* Функции авторизации для AETERNA
*/
require_once __DIR__ . '/../config/database.php'; require_once __DIR__ . '/../config/database.php';
/**
* Авторизация пользователя
*/
function loginUser(string $email, string $password): array { function loginUser(string $email, string $password): array {
$db = Database::getInstance()->getConnection(); $db = Database::getInstance()->getConnection();
try { try {
$stmt = $db->prepare(" $stmt = $db->prepare("
SELECT user_id, email, password_hash, full_name, phone, city, is_admin, is_active SELECT user_id, email, password_hash, full_name, phone, city, is_admin, is_active
@@ -31,7 +25,6 @@ function loginUser(string $email, string $password): array {
return ['success' => false, 'message' => 'Неверный пароль']; return ['success' => false, 'message' => 'Неверный пароль'];
} }
// Сохраняем в сессию
$_SESSION['user_id'] = $user['user_id']; $_SESSION['user_id'] = $user['user_id'];
$_SESSION['user_email'] = $user['email']; $_SESSION['user_email'] = $user['email'];
$_SESSION['full_name'] = $user['full_name']; $_SESSION['full_name'] = $user['full_name'];
@@ -41,7 +34,6 @@ function loginUser(string $email, string $password): array {
$_SESSION['isAdmin'] = (bool)$user['is_admin']; $_SESSION['isAdmin'] = (bool)$user['is_admin'];
$_SESSION['login_time'] = time(); $_SESSION['login_time'] = time();
// Обновляем время последнего входа
$updateStmt = $db->prepare("UPDATE users SET last_login = CURRENT_TIMESTAMP WHERE user_id = ?"); $updateStmt = $db->prepare("UPDATE users SET last_login = CURRENT_TIMESTAMP WHERE user_id = ?");
$updateStmt->execute([$user['user_id']]); $updateStmt->execute([$user['user_id']]);
@@ -52,43 +44,38 @@ function loginUser(string $email, string $password): array {
} }
} }
/**
* Регистрация нового пользователя
*/
function registerUser(array $data): array { function registerUser(array $data): array {
$db = Database::getInstance()->getConnection(); $db = Database::getInstance()->getConnection();
$email = trim($data['email'] ?? ''); $email = trim($data['email'] ?? '');
$password = $data['password'] ?? ''; $password = $data['password'] ?? '';
$fullName = trim($data['full_name'] ?? ''); $fullName = trim($data['full_name'] ?? '');
$phone = trim($data['phone'] ?? ''); $phone = trim($data['phone'] ?? '');
$city = trim($data['city'] ?? ''); $city = trim($data['city'] ?? '');
// Валидация
if (empty($email) || empty($password) || empty($fullName)) { if (empty($email) || empty($password) || empty($fullName)) {
return ['success' => false, 'message' => 'Заполните все обязательные поля']; return ['success' => false, 'message' => 'Заполните все обязательные поля'];
} }
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
return ['success' => false, 'message' => 'Некорректный email']; return ['success' => false, 'message' => 'Некорректный email'];
} }
if (strlen($password) < 6) { if (strlen($password) < 6) {
return ['success' => false, 'message' => 'Пароль должен содержать минимум 6 символов']; return ['success' => false, 'message' => 'Пароль должен содержать минимум 6 символов'];
} }
try { try {
// Проверяем существование пользователя
$checkStmt = $db->prepare("SELECT user_id FROM users WHERE email = ?"); $checkStmt = $db->prepare("SELECT user_id FROM users WHERE email = ?");
$checkStmt->execute([$email]); $checkStmt->execute([$email]);
if ($checkStmt->fetch()) { if ($checkStmt->fetch()) {
return ['success' => false, 'message' => 'Пользователь с таким email уже существует']; return ['success' => false, 'message' => 'Пользователь с таким email уже существует'];
} }
// Создаем пользователя
$passwordHash = password_hash($password, PASSWORD_DEFAULT); $passwordHash = password_hash($password, PASSWORD_DEFAULT);
$stmt = $db->prepare(" $stmt = $db->prepare("
INSERT INTO users (email, password_hash, full_name, phone, city, is_active) INSERT INTO users (email, password_hash, full_name, phone, city, is_active)
VALUES (?, ?, ?, ?, ?, TRUE) VALUES (?, ?, ?, ?, ?, TRUE)
@@ -96,8 +83,7 @@ function registerUser(array $data): array {
"); ");
$stmt->execute([$email, $passwordHash, $fullName, $phone, $city]); $stmt->execute([$email, $passwordHash, $fullName, $phone, $city]);
$userId = $stmt->fetchColumn(); $userId = $stmt->fetchColumn();
// Автоматически авторизуем
$_SESSION['user_id'] = $userId; $_SESSION['user_id'] = $userId;
$_SESSION['user_email'] = $email; $_SESSION['user_email'] = $email;
$_SESSION['full_name'] = $fullName; $_SESSION['full_name'] = $fullName;
@@ -106,20 +92,17 @@ function registerUser(array $data): array {
$_SESSION['isLoggedIn'] = true; $_SESSION['isLoggedIn'] = true;
$_SESSION['isAdmin'] = false; $_SESSION['isAdmin'] = false;
$_SESSION['login_time'] = time(); $_SESSION['login_time'] = time();
return ['success' => true, 'user_id' => $userId]; return ['success' => true, 'user_id' => $userId];
} catch (PDOException $e) { } catch (PDOException $e) {
return ['success' => false, 'message' => 'Ошибка базы данных: ' . $e->getMessage()]; return ['success' => false, 'message' => 'Ошибка базы данных: ' . $e->getMessage()];
} }
} }
/**
* Выход пользователя
*/
function logoutUser(): void { function logoutUser(): void {
$_SESSION = []; $_SESSION = [];
if (ini_get("session.use_cookies")) { if (ini_get("session.use_cookies")) {
$params = session_get_cookie_params(); $params = session_get_cookie_params();
setcookie(session_name(), '', time() - 42000, setcookie(session_name(), '', time() - 42000,
@@ -127,22 +110,18 @@ function logoutUser(): void {
$params["secure"], $params["httponly"] $params["secure"], $params["httponly"]
); );
} }
session_destroy(); session_destroy();
} }
/**
* Проверка является ли пользователь администратором
*/
function checkAdminAccess(): bool { function checkAdminAccess(): bool {
if (!isset($_SESSION['isLoggedIn']) || $_SESSION['isLoggedIn'] !== true) { if (!isset($_SESSION['isLoggedIn']) || $_SESSION['isLoggedIn'] !== true) {
return false; return false;
} }
if (!isset($_SESSION['isAdmin']) || $_SESSION['isAdmin'] !== true) { if (!isset($_SESSION['isAdmin']) || $_SESSION['isAdmin'] !== true) {
return false; return false;
} }
return true; return true;
} }

View File

@@ -47,4 +47,3 @@
</footer> </footer>
</body> </body>
</html> </html>

View File

@@ -1,25 +1,13 @@
<?php <?php
/**
* Общие функции для AETERNA
*/
/**
* Проверка авторизации пользователя
*/
function isLoggedIn(): bool { function isLoggedIn(): bool {
return isset($_SESSION['isLoggedIn']) && $_SESSION['isLoggedIn'] === true; return isset($_SESSION['isLoggedIn']) && $_SESSION['isLoggedIn'] === true;
} }
/**
* Проверка прав администратора
*/
function isAdmin(): bool { function isAdmin(): bool {
return isset($_SESSION['isAdmin']) && $_SESSION['isAdmin'] === true; return isset($_SESSION['isAdmin']) && $_SESSION['isAdmin'] === true;
} }
/**
* Требовать авторизацию - редирект на login если не авторизован
*/
function requireAuth(string $redirectUrl = 'login.php'): void { function requireAuth(string $redirectUrl = 'login.php'): void {
if (!isLoggedIn()) { if (!isLoggedIn()) {
header('Location: ' . $redirectUrl . '?error=auth_required&redirect=' . urlencode($_SERVER['REQUEST_URI'])); header('Location: ' . $redirectUrl . '?error=auth_required&redirect=' . urlencode($_SERVER['REQUEST_URI']));
@@ -27,9 +15,6 @@ function requireAuth(string $redirectUrl = 'login.php'): void {
} }
} }
/**
* Требовать права администратора
*/
function requireAdmin(string $redirectUrl = 'login.php'): void { function requireAdmin(string $redirectUrl = 'login.php'): void {
if (!isAdmin()) { if (!isAdmin()) {
header('Location: ' . $redirectUrl . '?error=admin_required'); header('Location: ' . $redirectUrl . '?error=admin_required');
@@ -37,14 +22,11 @@ function requireAdmin(string $redirectUrl = 'login.php'): void {
} }
} }
/**
* Получить текущего пользователя
*/
function getCurrentUser(): ?array { function getCurrentUser(): ?array {
if (!isLoggedIn()) { if (!isLoggedIn()) {
return null; return null;
} }
return [ return [
'user_id' => $_SESSION['user_id'] ?? 0, 'user_id' => $_SESSION['user_id'] ?? 0,
'email' => $_SESSION['user_email'] ?? '', 'email' => $_SESSION['user_email'] ?? '',
@@ -53,38 +35,23 @@ function getCurrentUser(): ?array {
]; ];
} }
/**
* Форматирование цены
*/
function formatPrice(float $price): string { function formatPrice(float $price): string {
return number_format($price, 0, '', ' ') . ' ₽'; return number_format($price, 0, '', ' ') . ' ₽';
} }
/**
* Безопасный вывод HTML
*/
function e(string $str): string { function e(string $str): string {
return htmlspecialchars($str, ENT_QUOTES, 'UTF-8'); return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
} }
/**
* Генерация номера заказа
*/
function generateOrderNumber(): string { function generateOrderNumber(): string {
return 'AET-' . date('Ymd') . '-' . strtoupper(substr(uniqid(), -6)); return 'AET-' . date('Ymd') . '-' . strtoupper(substr(uniqid(), -6));
} }
/**
* Генерация SKU для товара
*/
function generateSKU(string $productName): string { function generateSKU(string $productName): string {
$prefix = strtoupper(substr(preg_replace('/[^a-zA-Z0-9]/', '', transliterate($productName)), 0, 6)); $prefix = strtoupper(substr(preg_replace('/[^a-zA-Z0-9]/', '', transliterate($productName)), 0, 6));
return $prefix . '-' . rand(100, 999); return $prefix . '-' . rand(100, 999);
} }
/**
* Транслитерация кириллицы в латиницу
*/
function transliterate(string $str): string { function transliterate(string $str): string {
$converter = [ $converter = [
'а' => 'a', 'б' => 'b', 'в' => 'v', 'г' => 'g', 'д' => 'd', 'а' => 'a', 'б' => 'b', 'в' => 'v', 'г' => 'g', 'д' => 'd',
@@ -105,9 +72,6 @@ function transliterate(string $str): string {
return strtr($str, $converter); return strtr($str, $converter);
} }
/**
* Создание slug из строки
*/
function createSlug(string $str): string { function createSlug(string $str): string {
$slug = transliterate($str); $slug = transliterate($str);
$slug = strtolower($slug); $slug = strtolower($slug);
@@ -116,9 +80,6 @@ function createSlug(string $str): string {
return $slug; return $slug;
} }
/**
* Показать flash-сообщение
*/
function setFlashMessage(string $type, string $message): void { function setFlashMessage(string $type, string $message): void {
$_SESSION['flash_message'] = [ $_SESSION['flash_message'] = [
'type' => $type, 'type' => $type,
@@ -126,9 +87,6 @@ function setFlashMessage(string $type, string $message): void {
]; ];
} }
/**
* Получить и удалить flash-сообщение
*/
function getFlashMessage(): ?array { function getFlashMessage(): ?array {
if (isset($_SESSION['flash_message'])) { if (isset($_SESSION['flash_message'])) {
$message = $_SESSION['flash_message']; $message = $_SESSION['flash_message'];
@@ -137,4 +95,3 @@ function getFlashMessage(): ?array {
} }
return null; return null;
} }

View File

@@ -1,18 +1,11 @@
<?php <?php
/**
* Единый header для всех страниц AETERNA
* Подключение: require_once 'includes/header.php';
*/
// Запускаем сессию если еще не запущена
if (session_status() === PHP_SESSION_NONE) { if (session_status() === PHP_SESSION_NONE) {
session_start(); session_start();
} }
// Определяем текущую страницу
$currentPage = basename($_SERVER['PHP_SELF'], '.php'); $currentPage = basename($_SERVER['PHP_SELF'], '.php');
// Проверяем авторизацию
$isLoggedIn = isset($_SESSION['isLoggedIn']) && $_SESSION['isLoggedIn'] === true; $isLoggedIn = isset($_SESSION['isLoggedIn']) && $_SESSION['isLoggedIn'] === true;
$isAdmin = isset($_SESSION['isAdmin']) && $_SESSION['isAdmin'] === true; $isAdmin = isset($_SESSION['isAdmin']) && $_SESSION['isAdmin'] === true;
$userEmail = $_SESSION['user_email'] ?? ''; $userEmail = $_SESSION['user_email'] ?? '';
@@ -29,7 +22,7 @@ $fullName = $_SESSION['full_name'] ?? $userEmail;
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
<style> <style>
/* Стили для профиля пользователя */
.user-profile-dropdown { position: relative; display: inline-block; } .user-profile-dropdown { position: relative; display: inline-block; }
.user-profile-toggle { display: flex; align-items: center; gap: 10px; cursor: pointer; padding: 8px 12px; border-radius: 4px; transition: all 0.3s ease; } .user-profile-toggle { display: flex; align-items: center; gap: 10px; cursor: pointer; padding: 8px 12px; border-radius: 4px; transition: all 0.3s ease; }
.user-profile-toggle:hover { background-color: rgba(0, 0, 0, 0.05); } .user-profile-toggle:hover { background-color: rgba(0, 0, 0, 0.05); }
@@ -88,13 +81,12 @@ $fullName = $_SESSION['full_name'] ?? $userEmail;
<div class="header__icons--top"> <div class="header__icons--top">
<?php if ($isLoggedIn): ?> <?php if ($isLoggedIn): ?>
<!-- Иконка корзины -->
<a href="checkout.php" class="icon cart-icon"> <a href="checkout.php" class="icon cart-icon">
<i class="fas fa-shopping-cart"></i> <i class="fas fa-shopping-cart"></i>
<span class="cart-count" id="cartCount">0</span> <span class="cart-count" id="cartCount">0</span>
</a> </a>
<!-- Блок профиля -->
<div class="user-profile-dropdown"> <div class="user-profile-dropdown">
<div class="user-profile-toggle" id="profileToggle"> <div class="user-profile-toggle" id="profileToggle">
<div class="user-avatar"><?= !empty($userEmail) ? strtoupper(substr($userEmail, 0, 1)) : 'U' ?></div> <div class="user-avatar"><?= !empty($userEmail) ? strtoupper(substr($userEmail, 0, 1)) : 'U' ?></div>
@@ -151,19 +143,18 @@ $fullName = $_SESSION['full_name'] ?? $userEmail;
<script> <script>
$(document).ready(function() { $(document).ready(function() {
// Профиль пользователя - открытие/закрытие
$('#profileToggle').click(function(e) { $('#profileToggle').click(function(e) {
e.stopPropagation(); e.stopPropagation();
$('#profileMenu').toggle(); $('#profileMenu').toggle();
}); });
$(document).click(function(e) { $(document).click(function(e) {
if (!$(e.target).closest('.user-profile-dropdown').length) { if (!$(e.target).closest('.user-profile-dropdown').length) {
$('#profileMenu').hide(); $('#profileMenu').hide();
} }
}); });
// Обновление счетчика корзины
<?php if ($isLoggedIn): ?> <?php if ($isLoggedIn): ?>
$.ajax({ $.ajax({
url: 'api/cart.php?action=count', url: 'api/cart.php?action=count',
@@ -180,4 +171,3 @@ $(document).ready(function() {
<?php endif; ?> <?php endif; ?>
}); });
</script> </script>

File diff suppressed because it is too large Load Diff

View File

@@ -1,299 +1,294 @@
<?php <?php
session_start(); session_start();
?> ?>
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>AETERNA - Вход</title> <title>AETERNA - Вход</title>
<link rel="stylesheet/less" type="text/css" href="style_for_cite.less"> <link rel="stylesheet/less" type="text/css" href="style_for_cite.less">
<script src="https://cdn.jsdelivr.net/npm/less"></script> <script src="https://cdn.jsdelivr.net/npm/less"></script>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
<style> <style>
/* Добавляем стили для статуса пользователя */
.user-status { .user-status {
position: fixed; position: fixed;
top: 20px; top: 20px;
right: 20px; right: 20px;
z-index: 1000; z-index: 1000;
background: white; background: white;
padding: 10px 15px; padding: 10px 15px;
border-radius: 5px; border-radius: 5px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1); box-shadow: 0 2px 10px rgba(0,0,0,0.1);
display: flex; display: flex;
align-items: center; align-items: center;
gap: 10px; gap: 10px;
} }
.user-status.admin { .user-status.admin {
border-left: 4px solid #617365; border-left: 4px solid #617365;
} }
.user-status.user { .user-status.user {
border-left: 4px solid #28a745; border-left: 4px solid #28a745;
} }
.status-icon { .status-icon {
font-size: 20px; font-size: 20px;
} }
.admin-icon { .admin-icon {
color: #617365; color: #617365;
} }
.user-icon { .user-icon {
color: #28a745; color: #28a745;
} }
</style> </style>
<script src="check_auth.js"></script> <script src="check_auth.js"></script>
<style> <style>
/* Стили для блокировки доступа */
.link-disabled { .link-disabled {
cursor: not-allowed !important; cursor: not-allowed !important;
opacity: 0.7 !important; opacity: 0.7 !important;
pointer-events: none !important; pointer-events: none !important;
} }
.auth-required { .auth-required {
position: relative; position: relative;
} }
.auth-required::after { .auth-required::after {
content: "🔒"; content: "🔒";
position: absolute; position: absolute;
top: -5px; top: -5px;
right: -10px; right: -10px;
font-size: 10px; font-size: 10px;
} }
</style> </style>
</head> </head>
<body> <body>
<!-- Статус пользователя -->
<div class="user-status" id="userStatus" style="display: none;"> <div class="user-status" id="userStatus" style="display: none;">
<i class="fas fa-user status-icon" id="statusIcon"></i> <i class="fas fa-user status-icon" id="statusIcon"></i>
<div> <div>
<div id="statusText"></div> <div id="statusText"></div>
<div id="userEmail" style="font-size: 12px; color: #666;"></div> <div id="userEmail" style="font-size: 12px; color: #666;"></div>
</div> </div>
<button onclick="logout()" style="background: none; border: none; color: #666; cursor: pointer;"> <button onclick="logout()" style="background: none; border: none; color: #666; cursor: pointer;">
<i class="fas fa-sign-out-alt"></i> <i class="fas fa-sign-out-alt"></i>
</button> </button>
</div> </div>
<header class="header"> <header class="header">
<div class="header__top"> <div class="header__top">
<div class="container header__top-content"> <div class="container header__top-content">
<div class="logo">AETERNA</div> <div class="logo">AETERNA</div>
<div class="search-catalog"> <div class="search-catalog">
<div class="catalog-dropdown"> <div class="catalog-dropdown">
Все категории <span>&#9660;</span> Все категории <span>&#9660;</span>
<div class="catalog-dropdown__menu"> <div class="catalog-dropdown__menu">
<ul> <ul>
<li>Диваны</li> <li>Диваны</li>
<li>Кровати</li> <li>Кровати</li>
<li>Шкафы</li> <li>Шкафы</li>
<li>Стулья</li> <li>Стулья</li>
<li>Столы</li> <li>Столы</li>
<li>Комоды</li> <li>Комоды</li>
<li>Тумбы</li> <li>Тумбы</li>
<li>Полки</li> <li>Полки</li>
<li>Стенки</li> <li>Стенки</li>
<li>Аксессуары</li> <li>Аксессуары</li>
</ul> </ul>
</div> </div>
</div> </div>
<div class="search-box"> <div class="search-box">
<input type="text" placeholder="Поиск товаров"> <input type="text" placeholder="Поиск товаров">
<span class="search-icon"><i class="fas fa-search"></i></span> <span class="search-icon"><i class="fas fa-search"></i></span>
</div> </div>
</div> </div>
<div class="header__icons header__icons--top"> <div class="header__icons header__icons--top">
<a href="checkout.php" class="icon"><i class="fas fa-shopping-cart"></i></a> <a href="checkout.php" class="icon"><i class="fas fa-shopping-cart"></i></a>
<a href="register.php" class="icon"><i class="far fa-user"></i></a> <a href="register.php" class="icon"><i class="far fa-user"></i></a>
</div> </div>
</div> </div>
</div> </div>
<div class="header__bottom"> <div class="header__bottom">
<div class="container header__bottom-content"> <div class="container header__bottom-content">
<div class="catalog-menu"> <div class="catalog-menu">
<a href="/catalog.php" class="catalog-link"> <a href="/catalog.php" class="catalog-link">
<div class="catalog-icon"> <div class="catalog-icon">
<span class="line"></span> <span class="line"></span>
<span class="line"></span> <span class="line"></span>
<span class="line"></span> <span class="line"></span>
</div> </div>
<span class="catalog-lines">☰</span> <span class="catalog-lines">☰</span>
Каталог Каталог
</a> </a>
</div> </div>
<nav class="nav"> <nav class="nav">
<ul class="nav-list"> <ul class="nav-list">
<li><a href="cite_mebel.php">Главная</a></li> <li><a href="cite_mebel.php">Главная</a></li>
<li><a href="services.php">Услуги</a></li> <li><a href="services.php">Услуги</a></li>
<li><a href="delivery.php">Доставка и оплата</a></li> <li><a href="delivery.php">Доставка и оплата</a></li>
<li><a href="warranty.php">Гарантия</a></li> <li><a href="warranty.php">Гарантия</a></li>
<li><a href="#footer">Контакты</a></li> <li><a href="#footer">Контакты</a></li>
</ul> </ul>
</nav> </nav>
<div class="header-phone">+7(912)999-12-23</div> <div class="header-phone">+7(912)999-12-23</div>
</div> </div>
</div> </div>
</header> </header>
<main class="profile-page-main"> <main class="profile-page-main">
<div class="profile-container"> <div class="profile-container">
<div class="profile-left-col"> <div class="profile-left-col">
<div class="logo">AETERNA</div> <div class="logo">AETERNA</div>
</div> </div>
<div class="profile-right-col"> <div class="profile-right-col">
<div class="profile-form-block"> <div class="profile-form-block">
<h2>ВХОД В АККАУНТ</h2> <h2>ВХОД В АККАУНТ</h2>
<form class="profile-form" action="#" method="POST" id="loginForm"> <form class="profile-form" action="#" method="POST" id="loginForm">
<div class="input-group"> <div class="input-group">
<label for="login-email">E-mail</label> <label for="login-email">E-mail</label>
<input type="email" id="login-email" name="email" placeholder="Ваш электронный адрес" required> <input type="email" id="login-email" name="email" placeholder="Ваш электронный адрес" required>
<div class="error-message" id="email-error"> <div class="error-message" id="email-error">
Введите корректный email адрес Введите корректный email адрес
</div> </div>
</div> </div>
<div class="input-group"> <div class="input-group">
<label for="login-password">Пароль</label> <label for="login-password">Пароль</label>
<input type="password" id="login-password" name="password" placeholder="Введите пароль" required> <input type="password" id="login-password" name="password" placeholder="Введите пароль" required>
<div class="error-message" id="password-error">Неверный пароль</div> <div class="error-message" id="password-error">Неверный пароль</div>
</div> </div>
<div class="form-options"> <div class="form-options">
<label class="remember-me"> <label class="remember-me">
<input type="checkbox" id="remember" name="remember"> <input type="checkbox" id="remember" name="remember">
Запомнить меня Запомнить меня
</label> </label>
<a href="#" class="forgot-password">Забыли пароль?</a> <a href="#" class="forgot-password">Забыли пароль?</a>
</div> </div>
<button type="submit" class="btn primary-btn save-btn">Войти</button> <button type="submit" class="btn primary-btn save-btn">Войти</button>
<div class="auth-actions"> <div class="auth-actions">
<span class="auth-text">Нет аккаунта?</span> <span class="auth-text">Нет аккаунта?</span>
<a href="register.php" class="login-btn">Зарегистрироваться</a> <a href="register.php" class="login-btn">Зарегистрироваться</a>
</div> </div>
</form> </form>
</div> </div>
</div> </div>
</div> </div>
</main> </main>
<!-- Блок для системных сообщений --> <div class="page-messages">
<div class="page-messages"> <div class="message error" id="errorMessage"></div>
<div class="message error" id="errorMessage"></div> <div class="message success" id="successMessage"></div>
<div class="message success" id="successMessage"></div> <div class="message warning" id="warningMessage"></div>
<div class="message warning" id="warningMessage"></div> </div>
</div>
<footer class="footer" id="footer">
<footer class="footer" id="footer"> <div class="container footer__content">
<div class="container footer__content"> <div class="footer__col footer__col--logo">
<div class="footer__col footer__col--logo"> <div class="logo">AETERNA</div>
<div class="logo">AETERNA</div> </div>
</div>
<div class="footer__col">
<div class="footer__col"> <h5>ПОКУПАТЕЛЮ</h5>
<h5>ПОКУПАТЕЛЮ</h5> <ul>
<ul> <li><a href="/catalog.php">Каталог</a></li>
<li><a href="/catalog.php">Каталог</a></li> <li><a href="услуги.html">Услуги</a></li>
<li><a href="услуги.html">Услуги</a></li> </ul>
</ul> </div>
</div>
<div class="footer__col">
<div class="footer__col"> <h5>ПОМОЩЬ</h5>
<h5>ПОМОЩЬ</h5> <ul>
<ul> <li><a href="Доставка.html">Доставка и оплата</a></li>
<li><a href="Доставка.html">Доставка и оплата</a></li> <li><a href="Гарантия.html">Гарантия и возврат</a></li>
<li><a href="Гарантия.html">Гарантия и возврат</a></li> <li><a href="cite_mebel.html#faq">Ответы на вопросы</a></li>
<li><a href="cite_mebel.html#faq">Ответы на вопросы</a></li> <li><a href="#footer">Контакты</a></li>
<li><a href="#footer">Контакты</a></li> </ul>
</ul> </div>
</div>
<div class="footer__col">
<div class="footer__col"> <h5>КОНТАКТЫ</h5>
<h5>КОНТАКТЫ</h5> <p>aeterna@mail.ru</p>
<p>aeterna@mail.ru</p> <p>+7(912)999-12-23</p>
<p>+7(912)999-12-23</p> <div class="social-icons">
<div class="social-icons"> <span class="icon"><i class="fab fa-telegram"></i></span>
<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-instagram"></i></span> <span class="icon"><i class="fab fa-vk"></i></span>
<span class="icon"><i class="fab fa-vk"></i></span> </div>
</div> </div>
</div>
<div class="footer__col">
<div class="footer__col"> <h5>ПРИНИМАЕМ К ОПЛАТЕ</h5>
<h5>ПРИНИМАЕМ К ОПЛАТЕ</h5> <div class="payment-icons">
<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-visa"></i></span> <span class="pay-icon"><i class="fab fa-cc-mastercard"></i></span>
<span class="pay-icon"><i class="fab fa-cc-mastercard"></i></span> </div>
</div> </div>
</div> </div>
</div> <div class="copyright">
<div class="copyright"> <p>© 2025 AETERNA. Все права защищены.</p>
<p>© 2025 AETERNA. Все права защищены.</p> </div>
</div> </footer>
</footer>
<script>
<script> $(document).ready(function() {
$(document).ready(function() {
// Проверяем редирект после входа const urlParams = new URLSearchParams(window.location.search);
const urlParams = new URLSearchParams(window.location.search); const redirectUrl = urlParams.get('redirect');
const redirectUrl = urlParams.get('redirect');
if (redirectUrl) {
// Если есть редирект - сохраняем его sessionStorage.setItem('redirectAfterLogin', redirectUrl);
if (redirectUrl) { }
sessionStorage.setItem('redirectAfterLogin', redirectUrl);
} $('#loginForm').on('submit', function(e) {
e.preventDefault();
// Обработка формы входа
$('#loginForm').on('submit', function(e) { const email = $('#login-email').val();
e.preventDefault(); const password = $('#login-password').val();
const email = $('#login-email').val(); if (!email || !password) {
const password = $('#login-password').val(); alert('Заполните все поля');
return;
// Валидация на клиенте }
if (!email || !password) {
alert('Заполните все поля'); $.ajax({
return; url: 'api/auth.php',
} method: 'POST',
data: {
// Отправляем данные на сервер для PHP сессии email: email,
$.ajax({ password: password
url: 'api/auth.php', },
method: 'POST', dataType: 'json',
data: { success: function(result) {
email: email, if (result.success) {
password: password
}, const savedRedirect = sessionStorage.getItem('redirectAfterLogin') || 'catalog.php';
dataType: 'json', sessionStorage.removeItem('redirectAfterLogin');
success: function(result) { window.location.href = savedRedirect;
if (result.success) { } else {
// Редирект на сохраненный URL или каталог alert(result.message || 'Ошибка авторизации');
const savedRedirect = sessionStorage.getItem('redirectAfterLogin') || 'catalog.php'; }
sessionStorage.removeItem('redirectAfterLogin'); },
window.location.href = savedRedirect; error: function() {
} else { alert('Ошибка сервера. Попробуйте позже.');
alert(result.message || 'Ошибка авторизации'); }
} });
}, });
error: function() { });
alert('Ошибка сервера. Попробуйте позже.'); </script>
}
}); </body>
}); </html>
});
</script>
</body>
</html>

View File

@@ -1,10 +1,8 @@
<?php <?php
session_start(); session_start();
// Очищаем все данные сессии
$_SESSION = []; $_SESSION = [];
// Удаляем cookie сессии
if (ini_get("session.use_cookies")) { if (ini_get("session.use_cookies")) {
$params = session_get_cookie_params(); $params = session_get_cookie_params();
setcookie(session_name(), '', time() - 42000, setcookie(session_name(), '', time() - 42000,
@@ -13,10 +11,7 @@ if (ini_get("session.use_cookies")) {
); );
} }
// Уничтожаем сессию
session_destroy(); session_destroy();
// Очищаем localStorage через JS
header('Location: index.php'); header('Location: index.php');
exit(); exit();

View File

@@ -1,492 +1,484 @@
<?php <?php
// product_page.php
session_start(); session_start();
require_once __DIR__ . '/../config/database.php'; require_once __DIR__ . '/../config/database.php';
// Проверка авторизации if (!isset($_SESSION['isLoggedIn']) || $_SESSION['isLoggedIn'] !== true) {
if (!isset($_SESSION['isLoggedIn']) || $_SESSION['isLoggedIn'] !== true) { header('Location: login.php?error=auth_required&redirect=' . urlencode($_SERVER['REQUEST_URI']));
header('Location: login.php?error=auth_required&redirect=' . urlencode($_SERVER['REQUEST_URI'])); exit();
exit(); }
}
if (!isset($_GET['id'])) {
if (!isset($_GET['id'])) { header('Location: catalog.php');
header('Location: catalog.php'); exit();
exit(); }
}
$product_id = intval($_GET['id']);
$product_id = intval($_GET['id']);
$db = Database::getInstance()->getConnection();
$db = Database::getInstance()->getConnection();
try {
try {
// Получаем информацию о товаре $productStmt = $db->prepare("
$productStmt = $db->prepare(" SELECT
SELECT p.*,
p.*, c.name as category_name,
c.name as category_name, c.slug as category_slug
c.slug as category_slug FROM products p
FROM products p LEFT JOIN categories c ON p.category_id = c.category_id
LEFT JOIN categories c ON p.category_id = c.category_id WHERE p.product_id = ? AND p.is_available = TRUE
WHERE p.product_id = ? AND p.is_available = TRUE ");
"); $productStmt->execute([$product_id]);
$productStmt->execute([$product_id]); $product = $productStmt->fetch();
$product = $productStmt->fetch();
if (!$product) {
if (!$product) { header('Location: catalog.php?error=product_not_found');
header('Location: catalog.php?error=product_not_found'); exit();
exit(); }
}
$similarStmt = $db->prepare("
// Получаем похожие товары SELECT * FROM products
$similarStmt = $db->prepare(" WHERE category_id = ?
SELECT * FROM products AND product_id != ?
WHERE category_id = ? AND is_available = TRUE
AND product_id != ? ORDER BY RANDOM()
AND is_available = TRUE LIMIT 3
ORDER BY RANDOM() ");
LIMIT 3 $similarStmt->execute([$product['category_id'], $product_id]);
"); $similarProducts = $similarStmt->fetchAll();
$similarStmt->execute([$product['category_id'], $product_id]);
$similarProducts = $similarStmt->fetchAll(); $reviewsStmt = $db->prepare("
SELECT rating, comment, created_at
// Получаем отзывы (если есть отдельная таблица reviews) FROM reviews
$reviewsStmt = $db->prepare(" WHERE product_id = ?
SELECT rating, comment, created_at ORDER BY created_at DESC
FROM reviews LIMIT 5
WHERE product_id = ? ");
ORDER BY created_at DESC $reviewsStmt->execute([$product_id]);
LIMIT 5 $reviews = $reviewsStmt->fetchAll();
");
$reviewsStmt->execute([$product_id]); } catch (PDOException $e) {
$reviews = $reviewsStmt->fetchAll(); die("Ошибка базы данных: " . $e->getMessage());
}
} catch (PDOException $e) {
die("Ошибка базы данных: " . $e->getMessage()); ?>
} <!DOCTYPE html>
<html lang="ru">
// HTML код страницы товара <head>
?> <meta charset="UTF-8">
<!DOCTYPE html> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<html lang="ru"> <title>AETERNA - <?= htmlspecialchars($product['name']) ?></title>
<head> <link rel="stylesheet/less" type="text/css" href="style_for_cite.less">
<meta charset="UTF-8"> <script src="https://cdn.jsdelivr.net/npm/less"></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<title>AETERNA - <?= htmlspecialchars($product['name']) ?></title> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
<link rel="stylesheet/less" type="text/css" href="style_for_cite.less"> <style>
<script src="https://cdn.jsdelivr.net/npm/less"></script> .product-attributes {
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> background: #f8f9fa;
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css"> padding: 20px;
<style> border-radius: 8px;
.product-attributes { margin: 20px 0;
background: #f8f9fa; }
padding: 20px; .attribute-row {
border-radius: 8px; display: flex;
margin: 20px 0; justify-content: space-between;
} padding: 10px 0;
.attribute-row { border-bottom: 1px solid #ddd;
display: flex; }
justify-content: space-between; .attribute-label {
padding: 10px 0; font-weight: bold;
border-bottom: 1px solid #ddd; color: #453227;
} }
.attribute-label { .attribute-value {
font-weight: bold; color: #617365;
color: #453227; }
} .stock-status {
.attribute-value { font-weight: bold;
color: #617365; padding: 5px 10px;
} border-radius: 4px;
.stock-status { display: inline-block;
font-weight: bold; }
padding: 5px 10px; .in-stock {
border-radius: 4px; background: #d4edda;
display: inline-block; color: #155724;
} }
.in-stock { .low-stock {
background: #d4edda; background: #fff3cd;
color: #155724; color: #856404;
} }
.low-stock { .out-of-stock {
background: #fff3cd; background: #f8d7da;
color: #856404; color: #721c24;
} }
.out-of-stock { </style>
background: #f8d7da; </head>
color: #721c24; <body>
} <header class="header">
</style> <div class="header__top">
</head> <div class="container header__top-content">
<body> <div class="logo">AETERNA</div>
<header class="header">
<div class="header__top"> <div class="search-catalog">
<div class="container header__top-content"> <div class="catalog-dropdown">
<div class="logo">AETERNA</div> Все категории <span>&#9660;</span>
<div class="catalog-dropdown__menu">
<div class="search-catalog"> <ul>
<div class="catalog-dropdown"> <li><a href="#">Диваны</a></li>
Все категории <span>&#9660;</span> <li><a href="#">Кровати</a></li>
<div class="catalog-dropdown__menu"> <li><a href="#">Шкафы</a></li>
<ul> <li><a href="#">Стулья</a></li>
<li><a href="#">Диваны</a></li> <li><a href="#">Столы</a></li>
<li><a href="#">Кровати</a></li> </ul>
<li><a href="#">Шкафы</a></li> </div>
<li><a href="#">Стулья</a></li> </div>
<li><a href="#">Столы</a></li> <div class="search-box">
</ul> <input type="text" placeholder="Поиск товаров">
</div> <span class="search-icon"><i class="fas fa-search"></i></span>
</div> </div>
<div class="search-box"> </div>
<input type="text" placeholder="Поиск товаров">
<span class="search-icon"><i class="fas fa-search"></i></span> <div class="header__icons--top">
</div>
</div> <a href="checkout.php" class="icon"><i class="fas fa-shopping-cart"></i></a>
<a href="register.php" class="icon"><i class="far fa-user"></i></a>
<div class="header__icons--top"> </div>
</div>
<a href="checkout.php" class="icon"><i class="fas fa-shopping-cart"></i></a> </div>
<a href="register.php" class="icon"><i class="far fa-user"></i></a>
</div> <div class="header__bottom">
</div> <div class="container header__bottom-content">
</div> <div class="catalog-menu">
<a href="/catalog.php" class="catalog-link active-catalog">
<div class="header__bottom"> <div class="catalog-icon">
<div class="container header__bottom-content"> <span class="line"></span>
<div class="catalog-menu"> <span class="line"></span>
<a href="/catalog.php" class="catalog-link active-catalog"> <span class="line"></span>
<div class="catalog-icon"> </div>
<span class="line"></span> <span class="catalog-lines"></span>
<span class="line"></span> Каталог
<span class="line"></span> </a>
</div> </div>
<span class="catalog-lines">☰</span>
Каталог <nav class="nav">
</a> <ul class="nav-list">
</div> <li><a href="cite_mebel.php">Главная</a></li>
<li><a href="services.php">Услуги</a></li>
<nav class="nav"> <li><a href="delivery.php">Доставка и оплата</a></li>
<ul class="nav-list"> <li><a href="warranty.php">Гарантия</a></li>
<li><a href="cite_mebel.php">Главная</a></li> <li><a href="#footer">Контакты</a></li>
<li><a href="services.php">Услуги</a></li> </ul>
<li><a href="delivery.php">Доставка и оплата</a></li> </nav>
<li><a href="warranty.php">Гарантия</a></li> <div class="header-phone">+7(912)999-12-23</div>
<li><a href="#footer">Контакты</a></li> </div>
</ul> </div>
</nav> </header>
<div class="header-phone">+7(912)999-12-23</div>
</div> <main class="container">
</div> <div class="breadcrumbs">
</header> <a href="cite_mebel.php">Главная</a> •
<a href="/catalog.php">Каталог</a> •
<main class="container"> <?php if ($product['category_name']): ?>
<div class="breadcrumbs"> <a href="/catalog.php?category=<?= $product['category_id'] ?>">
<a href="cite_mebel.php">Главная</a> • <?= htmlspecialchars($product['category_name']) ?>
<a href="/catalog.php">Каталог</a> • </a> •
<?php if ($product['category_name']): ?> <?php endif; ?>
<a href="/catalog.php?category=<?= $product['category_id'] ?>"> <span><?= htmlspecialchars($product['name']) ?></span>
<?= htmlspecialchars($product['category_name']) ?> </div>
</a> •
<?php endif; ?> <div class="product__section">
<span><?= htmlspecialchars($product['name']) ?></span> <div class="product__gallery">
</div> <div class="product__main-image">
<img src="<?= htmlspecialchars($product['image_url'] ?? 'img1/default.jpg') ?>"
<div class="product__section"> alt="<?= htmlspecialchars($product['name']) ?>"
<div class="product__gallery"> id="mainImage">
<div class="product__main-image"> </div>
<img src="<?= htmlspecialchars($product['image_url'] ?? 'img1/default.jpg') ?>" </div>
alt="<?= htmlspecialchars($product['name']) ?>"
id="mainImage"> <div class="product__info">
</div> <h1><?= htmlspecialchars($product['name']) ?></h1>
</div>
<div class="product__rating">
<div class="product__info"> <div class="stars">
<h1><?= htmlspecialchars($product['name']) ?></h1> <?php
$rating = $product['rating'] ?? 0;
<div class="product__rating"> $fullStars = floor($rating);
<div class="stars"> $hasHalfStar = $rating - $fullStars >= 0.5;
<?php
$rating = $product['rating'] ?? 0; for ($i = 1; $i <= 5; $i++) {
$fullStars = floor($rating); if ($i <= $fullStars) {
$hasHalfStar = $rating - $fullStars >= 0.5; echo '<span class="star filled">★</span>';
} elseif ($i == $fullStars + 1 && $hasHalfStar) {
for ($i = 1; $i <= 5; $i++) { echo '<span class="star half">★</span>';
if ($i <= $fullStars) { } else {
echo '<span class="star filled"></span>'; echo '<span class="star"></span>';
} elseif ($i == $fullStars + 1 && $hasHalfStar) { }
echo '<span class="star half">★</span>'; }
} else { ?>
echo '<span class="star">☆</span>'; </div>
} <span class="rating-value"><?= number_format($rating, 1) ?></span>
} <span class="reviews-count">(<?= $product['review_count'] ?> отзывов)</span>
?> </div>
</div>
<span class="rating-value"><?= number_format($rating, 1) ?></span> <div class="product__price">
<span class="reviews-count">(<?= $product['review_count'] ?> отзывов)</span> <span class="current-price">
</div> <?= number_format($product['price'], 0, '', ' ') ?> ₽
</span>
<div class="product__price"> <?php if ($product['old_price'] && $product['old_price'] > $product['price']): ?>
<span class="current-price"> <span class="old-price">
<?= number_format($product['price'], 0, '', ' ') ?> ₽ <?= number_format($product['old_price'], 0, '', ' ') ?> ₽
</span> </span>
<?php if ($product['old_price'] && $product['old_price'] > $product['price']): ?> <span class="discount-badge">
<span class="old-price"> -<?= round(($product['old_price'] - $product['price']) / $product['old_price'] * 100) ?>%
<?= number_format($product['old_price'], 0, '', ' ') ?> ₽ </span>
</span> <?php endif; ?>
<span class="discount-badge"> </div>
-<?= round(($product['old_price'] - $product['price']) / $product['old_price'] * 100) ?>%
</span> <div class="stock-status <?php
<?php endif; ?> if ($product['stock_quantity'] > 10) echo 'in-stock';
</div> elseif ($product['stock_quantity'] > 0) echo 'low-stock';
else echo 'out-of-stock';
<div class="stock-status <?php ?>">
if ($product['stock_quantity'] > 10) echo 'in-stock'; <?php
elseif ($product['stock_quantity'] > 0) echo 'low-stock'; if ($product['stock_quantity'] > 10) {
else echo 'out-of-stock'; echo '<i class="fas fa-check-circle"></i> В наличии';
?>"> } elseif ($product['stock_quantity'] > 0) {
<?php echo '<i class="fas fa-exclamation-circle"></i> Осталось мало: ' . $product['stock_quantity'] . ' шт.';
if ($product['stock_quantity'] > 10) { } else {
echo '<i class="fas fa-check-circle"></i> В наличии'; echo '<i class="fas fa-times-circle"></i> Нет в наличии';
} elseif ($product['stock_quantity'] > 0) { }
echo '<i class="fas fa-exclamation-circle"></i> Осталось мало: ' . $product['stock_quantity'] . ' шт.'; ?>
} else { </div>
echo '<i class="fas fa-times-circle"></i> Нет в наличии';
} <div class="product-attributes">
?> <div class="attribute-row">
</div> <span class="attribute-label">Артикул:</span>
<span class="attribute-value"><?= $product['sku'] ?? 'N/A' ?></span>
<div class="product-attributes"> </div>
<div class="attribute-row"> <div class="attribute-row">
<span class="attribute-label">Артикул:</span> <span class="attribute-label">Категория:</span>
<span class="attribute-value"><?= $product['sku'] ?? 'N/A' ?></span> <span class="attribute-value"><?= htmlspecialchars($product['category_name'] ?? 'Без категории') ?></span>
</div> </div>
<div class="attribute-row"> <div class="attribute-row">
<span class="attribute-label">Категория:</span> <span class="attribute-label">На складе:</span>
<span class="attribute-value"><?= htmlspecialchars($product['category_name'] ?? 'Без категории') ?></span> <span class="attribute-value"><?= $product['stock_quantity'] ?> шт.</span>
</div> </div>
<div class="attribute-row"> </div>
<span class="attribute-label">На складе:</span>
<span class="attribute-value"><?= $product['stock_quantity'] ?> шт.</span> <p class="product__description">
</div> <?= nl2br(htmlspecialchars($product['description'] ?? 'Описание отсутствует')) ?>
</div> </p>
<p class="product__description"> <?php if ($product['stock_quantity'] > 0): ?>
<?= nl2br(htmlspecialchars($product['description'] ?? 'Описание отсутствует')) ?> <div class="product__purchase">
</p> <div class="product__quantity">
<button class="product__qty-btn minus">-</button>
<?php if ($product['stock_quantity'] > 0): ?> <input type="number" class="product__qty-value" value="1" min="1" max="<?= $product['stock_quantity'] ?>">
<div class="product__purchase"> <button class="product__qty-btn plus">+</button>
<div class="product__quantity"> </div>
<button class="product__qty-btn minus">-</button>
<input type="number" class="product__qty-value" value="1" min="1" max="<?= $product['stock_quantity'] ?>"> <div class="product__actions">
<button class="product__qty-btn plus">+</button> <button class="btn primary-btn" onclick="addToCart(<?= $product['product_id'] ?>)">
</div> <i class="fas fa-shopping-cart"></i> В корзину
</button>
<div class="product__actions"> <button class="btn secondary-btn" onclick="buyNow(<?= $product['product_id'] ?>)">
<button class="btn primary-btn" onclick="addToCart(<?= $product['product_id'] ?>)"> <i class="fas fa-bolt"></i> Купить сейчас
<i class="fas fa-shopping-cart"></i> В корзину </button>
</button> </div>
<button class="btn secondary-btn" onclick="buyNow(<?= $product['product_id'] ?>)"> </div>
<i class="fas fa-bolt"></i> Купить сейчас <?php else: ?>
</button> <div class="product__actions">
</div> <button class="btn secondary-btn" onclick="notifyWhenAvailable(<?= $product['product_id'] ?>)">
</div> <i class="fas fa-bell"></i> Уведомить о поступлении
<?php else: ?> </button>
<div class="product__actions"> </div>
<button class="btn secondary-btn" onclick="notifyWhenAvailable(<?= $product['product_id'] ?>)"> <?php endif; ?>
<i class="fas fa-bell"></i> Уведомить о поступлении
</button> <?php if (isset($_SESSION['isAdmin']) && $_SESSION['isAdmin']): ?>
</div> <div class="admin-actions" style="margin-top: 20px;">
<?php endif; ?> <a href="admin_panel.php?action=edit&id=<?= $product['product_id'] ?>"
class="btn btn-warning">
<?php if (isset($_SESSION['isAdmin']) && $_SESSION['isAdmin']): ?> <i class="fas fa-edit"></i> Редактировать
<div class="admin-actions" style="margin-top: 20px;"> </a>
<a href="admin_panel.php?action=edit&id=<?= $product['product_id'] ?>" <button onclick="deleteProduct(<?= $product['product_id'] ?>)"
class="btn btn-warning"> class="btn btn-danger">
<i class="fas fa-edit"></i> Редактировать <i class="fas fa-trash"></i> Удалить
</a> </button>
<button onclick="deleteProduct(<?= $product['product_id'] ?>)" </div>
class="btn btn-danger"> <?php endif; ?>
<i class="fas fa-trash"></i> Удалить </div>
</button> </div>
</div>
<?php endif; ?> <?php if (!empty($similarProducts)): ?>
</div> <section class="similar-products">
</div> <h2>Похожие товары</h2>
<div class="products-grid">
<?php if (!empty($similarProducts)): ?> <?php foreach ($similarProducts as $similar): ?>
<section class="similar-products"> <div class="product-card">
<h2>Похожие товары</h2> <div class="product-image">
<div class="products-grid"> <img src="<?= htmlspecialchars($similar['image_url'] ?? 'img2/default.jpg') ?>"
<?php foreach ($similarProducts as $similar): ?> alt="<?= htmlspecialchars($similar['name']) ?>">
<div class="product-card"> </div>
<div class="product-image"> <div class="product-info">
<img src="<?= htmlspecialchars($similar['image_url'] ?? 'img2/default.jpg') ?>" <h3><?= htmlspecialchars($similar['name']) ?></h3>
alt="<?= htmlspecialchars($similar['name']) ?>"> <p class="product-price">
</div> <?= number_format($similar['price'], 0, '', ' ') ?> ₽
<div class="product-info"> </p>
<h3><?= htmlspecialchars($similar['name']) ?></h3> <a href="product_page.php?id=<?= $similar['product_id'] ?>"
<p class="product-price"> class="btn btn-primary">
<?= number_format($similar['price'], 0, '', ' ') ?> ₽ Подробнее
</p> </a>
<a href="product_page.php?id=<?= $similar['product_id'] ?>" </div>
class="btn btn-primary"> </div>
Подробнее <?php endforeach; ?>
</a> </div>
</div> </section>
</div> <?php endif; ?>
<?php endforeach; ?> </main>
</div>
</section> <footer class="footer" id="footer">
<?php endif; ?> <div class="container footer__content">
</main> <div class="footer__col footer--logo">
<div class="logo">AETERNA</div>
<footer class="footer" id="footer"> </div>
<div class="container footer__content">
<div class="footer__col footer--logo"> <div class="footer__col">
<div class="logo">AETERNA</div> <h5>ПОКУПАТЕЛЮ</h5>
</div> <ul>
<li><a href="/catalog.php">Каталог</a></li>
<div class="footer__col"> <li><a href="services.php">Услуги</a></li>
<h5>ПОКУПАТЕЛЮ</h5> </ul>
<ul> </div>
<li><a href="/catalog.php">Каталог</a></li>
<li><a href="services.php">Услуги</a></li> <div class="footer__col">
</ul> <h5>ПОМОЩЬ</h5>
</div> <ul>
<li><a href="delivery.php">Доставка и оплата</a></li>
<div class="footer__col"> <li><a href="warranty.php">Гарантия и возврат</a></li>
<h5>ПОМОЩЬ</h5> <li><a href="cite_mebel.php#faq">Ответы на вопросы</a></li>
<ul> <li><a href="#footer">Контакты</a></li>
<li><a href="delivery.php">Доставка и оплата</a></li> </ul>
<li><a href="warranty.php">Гарантия и возврат</a></li> </div>
<li><a href="cite_mebel.php#faq">Ответы на вопросы</a></li>
<li><a href="#footer">Контакты</a></li> <div class="footer__col">
</ul> <h5>КОНТАКТЫ</h5>
</div> <p>aeterna@mail.ru</p>
<p>+7(912)999-12-23</p>
<div class="footer__col"> <div class="social-icons">
<h5>КОНТАКТЫ</h5> <span class="icon"><i class="fab fa-telegram"></i></span>
<p>aeterna@mail.ru</p> <span class="icon"><i class="fab fa-instagram"></i></span>
<p>+7(912)999-12-23</p> <span class="icon"><i class="fab fa-vk"></i></span>
<div class="social-icons"> </div>
<span class="icon"><i class="fab fa-telegram"></i></span> </div>
<span class="icon"><i class="fab fa-instagram"></i></span>
<span class="icon"><i class="fab fa-vk"></i></span> <div class="footer__col">
</div> <h5>ПРИНИМАЕМ К ОПЛАТЕ</h5>
</div> <div class="payment-icons">
<span class="pay-icon"><i class="fab fa-cc-visa"></i></span>
<div class="footer__col"> <span class="pay-icon"><i class="fab fa-cc-mastercard"></i></span>
<h5>ПРИНИМАЕМ К ОПЛАТЕ</h5> </div>
<div class="payment-icons"> </div>
<span class="pay-icon"><i class="fab fa-cc-visa"></i></span> </div>
<span class="pay-icon"><i class="fab fa-cc-mastercard"></i></span> <div class="copyright">
</div> <p>© 2025 AETERNA. Все права защищены.</p>
</div> </div>
</div> </footer>
<div class="copyright">
<p>© 2025 AETERNA. Все права защищены.</p> <script>
</div> $(document).ready(function() {
</footer>
$('.product__qty-btn.plus').click(function() {
<script> const $input = $('.product__qty-value');
$(document).ready(function() { let value = parseInt($input.val());
// Управление количеством let max = parseInt($input.attr('max'));
$('.product__qty-btn.plus').click(function() { if (value < max) {
const $input = $('.product__qty-value'); $input.val(value + 1);
let value = parseInt($input.val()); }
let max = parseInt($input.attr('max')); });
if (value < max) {
$input.val(value + 1); $('.product__qty-btn.minus').click(function() {
} const $input = $('.product__qty-value');
}); let value = parseInt($input.val());
if (value > 1) {
$('.product__qty-btn.minus').click(function() { $input.val(value - 1);
const $input = $('.product__qty-value'); }
let value = parseInt($input.val()); });
if (value > 1) {
$input.val(value - 1); window.addToCart = function(productId) {
} const quantity = $('.product__qty-value').val();
});
$.ajax({
// Добавление в корзину url: 'add_to_cart.php',
window.addToCart = function(productId) { method: 'POST',
const quantity = $('.product__qty-value').val(); data: {
product_id: productId,
$.ajax({ quantity: quantity
url: 'add_to_cart.php', },
method: 'POST', success: function(response) {
data: { try {
product_id: productId, const result = JSON.parse(response);
quantity: quantity if (result.success) {
}, alert('Товар добавлен в корзину!');
success: function(response) {
try { $('.cart-count').text(result.cart_count);
const result = JSON.parse(response); } else {
if (result.success) { alert('Ошибка: ' + result.message);
alert('Товар добавлен в корзину!'); }
// Обновляем счетчик в шапке } catch(e) {
$('.cart-count').text(result.cart_count); alert('Товар добавлен в корзину!');
} else { }
alert('Ошибка: ' + result.message); }
} });
} catch(e) { };
alert('Товар добавлен в корзину!');
} window.buyNow = function(productId) {
} const quantity = $('.product__qty-value').val();
});
}; $.ajax({
url: 'add_to_cart.php',
// Покупка сейчас method: 'POST',
window.buyNow = function(productId) { data: {
const quantity = $('.product__qty-value').val(); product_id: productId,
quantity: quantity
$.ajax({ },
url: 'add_to_cart.php', success: function() {
method: 'POST', window.location.href = 'checkout.php';
data: { }
product_id: productId, });
quantity: quantity };
},
success: function() { window.notifyWhenAvailable = function(productId) {
window.location.href = 'checkout.php'; const email = prompt('Введите ваш email для уведомления:');
} if (email) {
}); $.ajax({
}; url: 'notify_available.php',
method: 'POST',
// Уведомление о поступлении data: {
window.notifyWhenAvailable = function(productId) { product_id: productId,
const email = prompt('Введите ваш email для уведомления:'); email: email
if (email) { },
$.ajax({ success: function(response) {
url: 'notify_available.php', alert('Вы будете уведомлены о поступлении товара!');
method: 'POST', }
data: { });
product_id: productId, }
email: email };
},
success: function(response) { window.deleteProduct = function(productId) {
alert('Вы будете уведомлены о поступлении товара!'); if (confirm('Вы уверены, что хотите удалить этот товар?')) {
} $.ajax({
}); url: 'catalog_admin_action.php',
} method: 'POST',
}; data: {
action: 'delete',
// Удаление товара (админ) product_id: productId
window.deleteProduct = function(productId) { },
if (confirm('Вы уверены, что хотите удалить этот товар?')) { success: function() {
$.ajax({ window.location.href = 'catalog.php';
url: 'catalog_admin_action.php', }
method: 'POST', });
data: { }
action: 'delete', };
product_id: productId });
}, </script>
success: function() { </body>
window.location.href = 'catalog.php';
}
});
}
};
});
</script>
</body>
</html> </html>

File diff suppressed because it is too large Load Diff

View File

@@ -1,113 +1,112 @@
<?php <?php
// В начале файла
require_once __DIR__ . '/../config/database.php'; require_once __DIR__ . '/../config/database.php';
?> ?>
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<title>AETERNA - Услуги</title> <title>AETERNA - Услуги</title>
<link rel="stylesheet/less" type="text/css" href="style_for_cite.less"> <link rel="stylesheet/less" type="text/css" href="style_for_cite.less">
<script src="https://cdn.jsdelivr.net/npm/less"></script> <script src="https://cdn.jsdelivr.net/npm/less"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
<script src="check_auth.js"></script> <script src="check_auth.js"></script>
<style> <style>
/* Стили для блокировки доступа */
.link-disabled { .link-disabled {
cursor: not-allowed !important; cursor: not-allowed !important;
opacity: 0.7 !important; opacity: 0.7 !important;
pointer-events: none !important; pointer-events: none !important;
} }
.auth-required { .auth-required {
position: relative; position: relative;
} }
.auth-required::after { .auth-required::after {
content: "🔒"; content: "🔒";
position: absolute; position: absolute;
top: -5px; top: -5px;
right: -10px; right: -10px;
font-size: 10px; font-size: 10px;
} }
</style> </style>
</head> </head>
<body> <body>
<?php include 'header_common.php'; ?> <?php include 'header_common.php'; ?>
<main>
<main> <section class="services-section">
<section class="services-section"> <div class="container services__wrapper">
<div class="container services__wrapper"> <div class="services__top-row">
<div class="services__top-row"> <div class="service-card service-card--green">
<div class="service-card service-card--green"> <h3 class="service-card__title">ДОСТАВКА</h3>
<h3 class="service-card__title">ДОСТАВКА</h3> <p class="service-card__text">Стоимость доставки зависит от таких факторов, как: вес, адрес, удаленность от города, дата</p>
<p class="service-card__text">Стоимость доставки зависит от таких факторов, как: вес, адрес, удаленность от города, дата</p> </div>
</div> <div class="service-card service-card--green">
<div class="service-card service-card--green"> <h3 class="service-card__title">СБОРКА</h3>
<h3 class="service-card__title">СБОРКА</h3> <p class="service-card__text">Стоимость сборки рассчитывается индивидуально, так как на цену влияет несколько факторов</p>
<p class="service-card__text">Стоимость сборки рассчитывается индивидуально, так как на цену влияет несколько факторов</p> </div>
</div> </div>
</div>
<div class="service-card service-card--beige">
<div class="service-card service-card--beige"> <h3 class="service-card__title">ДИЗАЙН‑ПРОЕКТ</h3>
<h3 class="service-card__title">ДИЗАЙН‑ПРОЕКТ</h3> <p class="service-card__text">Предоставляем услугу по составлению дизайн‑проекта. Учитываем индивидуальные пожелания каждого клиента. Работаем с интерьерами различной сложности.</p>
<p class="service-card__text">Предоставляем услугу по составлению дизайн‑проекта. Учитываем индивидуальные пожелания каждого клиента. Работаем с интерьерами различной сложности.</p> <div class="image-placeholder"></div>
<div class="image-placeholder"></div> </div>
</div> </div>
</div> </section>
</section> </main>
</main>
<footer class="footer" id="footer">
<footer class="footer" id="footer"> <div class="container footer__content">
<div class="container footer__content"> <div class="footer__col footer--logo">
<div class="footer__col footer--logo"> <div class="logo">AETERNA</div>
<div class="logo">AETERNA</div> </div>
</div>
<div class="footer__col">
<div class="footer__col"> <h5>ПОКУПАТЕЛЮ</h5>
<h5>ПОКУПАТЕЛЮ</h5> <ul>
<ul> <li><a href="/catalog.php">Каталог</a></li>
<li><a href="/catalog.php">Каталог</a></li> <li><a href="услуги.html">Услуги</a></li>
<li><a href="услуги.html">Услуги</a></li> </ul>
</ul> </div>
</div>
<div class="footer__col">
<div class="footer__col"> <h5>ПОМОЩЬ</h5>
<h5>ПОМОЩЬ</h5> <ul>
<ul> <li><a href="Доставка.html">Доставка и оплата</a></li>
<li><a href="Доставка.html">Доставка и оплата</a></li> <li><a href="Гарантия.html">Гарантия и возврат</a></li>
<li><a href="Гарантия.html">Гарантия и возврат</a></li> <li><a href="cite_mebel.html#faq">Ответы на вопросы</a></li>
<li><a href="cite_mebel.html#faq">Ответы на вопросы</a></li> <li><a href="#footer">Контакты</a></li>
<li><a href="#footer">Контакты</a></li> </ul>
</ul> </div>
</div>
<div class="footer__col">
<div class="footer__col"> <h5>КОНТАКТЫ</h5>
<h5>КОНТАКТЫ</h5> <p>aeterna@mail.ru</p>
<p>aeterna@mail.ru</p> <p>+7(912)999-12-23</p>
<p>+7(912)999-12-23</p> <div class="social-icons">
<div class="social-icons"> <span class="icon"><i class="fab fa-telegram"></i></span>
<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-instagram"></i></span> <span class="icon"><i class="fab fa-vk"></i></span>
<span class="icon"><i class="fab fa-vk"></i></span> </div>
</div> </div>
</div>
<div class="footer__col">
<div class="footer__col"> <h5>ПРИНИМАЕМ К ОПЛАТЕ</h5>
<h5>ПРИНИМАЕМ К ОПЛАТЕ</h5> <div class="payment-icons">
<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-visa"></i></span> <span class="pay-icon"><i class="fab fa-cc-mastercard"></i></span>
<span class="pay-icon"><i class="fab fa-cc-mastercard"></i></span> </div>
</div> </div>
</div> </div>
</div> <div class="copyright">
<div class="copyright"> <p>© 2025 AETERNA. Все права защищены.</p>
<p>© 2025 AETERNA. Все права защищены.</p> </div>
</div> </footer>
</footer>
</body>
</body>
</html> </html>

View File

@@ -1,206 +1,205 @@
<?php <?php
// В начале файла
require_once __DIR__ . '/../config/database.php'; require_once __DIR__ . '/../config/database.php';
?> ?>
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>AETERNA - Гарантия</title> <title>AETERNA - Гарантия</title>
<link rel="stylesheet/less" type="text/css" href="style_for_cite.less"> <link rel="stylesheet/less" type="text/css" href="style_for_cite.less">
<script src="https://cdn.jsdelivr.net/npm/less"></script> <script src="https://cdn.jsdelivr.net/npm/less"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
<script src="check_auth.js"></script> <script src="check_auth.js"></script>
<style> <style>
/* Стили для блокировки доступа */
.link-disabled { .link-disabled {
cursor: not-allowed !important; cursor: not-allowed !important;
opacity: 0.7 !important; opacity: 0.7 !important;
pointer-events: none !important; pointer-events: none !important;
} }
.auth-required { .auth-required {
position: relative; position: relative;
} }
.auth-required::after { .auth-required::after {
content: "🔒"; content: "🔒";
position: absolute; position: absolute;
top: -5px; top: -5px;
right: -10px; right: -10px;
font-size: 10px; font-size: 10px;
} }
</style> </style>
</head> </head>
<body> <body>
<?php include 'header_common.php'; ?> <?php include 'header_common.php'; ?>
<main class="catalog-main">
<main class="catalog-main"> <div class="container">
<div class="container"> <div class="breadcrumbs">
<div class="breadcrumbs"> <a href="cite_mebel.php">Главная</a> • <span class="current-page">Гарантия</span>
<a href="cite_mebel.php">Главная</a> • <span class="current-page">Гарантия</span> </div>
</div>
<div class="warranty-content">
<div class="warranty-content"> <h1>ГАРАНТИЙНЫЕ ОБЯЗАТЕЛЬСТВА</h1>
<h1>ГАРАНТИЙНЫЕ ОБЯЗАТЕЛЬСТВА</h1>
<div class="warranty-overview">
<div class="warranty-overview"> <div class="warranty-card">
<div class="warranty-card"> <div class="warranty-icon">
<div class="warranty-icon"> <i class="fas fa-couch"></i>
<i class="fas fa-couch"></i> </div>
</div> <h3>Мягкая мебель</h3>
<h3>Мягкая мебель</h3> <div class="warranty-period">18 месяцев</div>
<div class="warranty-period">18 месяцев</div> </div>
</div>
<div class="warranty-card">
<div class="warranty-card"> <div class="warranty-icon">
<div class="warranty-icon"> <i class="fas fa-archive"></i>
<i class="fas fa-archive"></i> </div>
</div> <h3>Корпусная мебель</h3>
<h3>Корпусная мебель</h3> <div class="warranty-period">24 месяца</div>
<div class="warranty-period">24 месяца</div> </div>
</div>
<div class="warranty-card">
<div class="warranty-card"> <div class="warranty-icon">
<div class="warranty-icon"> <i class="fas fa-lightbulb"></i>
<i class="fas fa-lightbulb"></i> </div>
</div> <h3>Элементы освещения</h3>
<h3>Элементы освещения</h3> <div class="warranty-period">12 месяцев</div>
<div class="warranty-period">12 месяцев</div> </div>
</div>
<div class="warranty-card">
<div class="warranty-card"> <div class="warranty-icon">
<div class="warranty-icon"> <i class="fas fa-cogs"></i>
<i class="fas fa-cogs"></i> </div>
</div> <h3>Фурнитура и механизмы</h3>
<h3>Фурнитура и механизмы</h3> <div class="warranty-period">36 месяцев</div>
<div class="warranty-period">36 месяцев</div> </div>
</div> </div>
</div>
<div class="coverage-section">
<div class="coverage-section"> <div class="coverage-covered">
<div class="coverage-covered"> <h2>Что покрывается гарантией</h2>
<h2>Что покрывается гарантией</h2> <div class="coverage-list">
<div class="coverage-list"> <div class="coverage-item covered">
<div class="coverage-item covered"> <i class="fas fa-check-circle"></i>
<i class="fas fa-check-circle"></i> <div class="coverage-text">
<div class="coverage-text"> <h4>Производственные дефекты</h4>
<h4>Производственные дефекты</h4> <p>Трещины, сколы, брак материалов</p>
<p>Трещины, сколы, брак материалов</p> </div>
</div> </div>
</div> <div class="coverage-item covered">
<div class="coverage-item covered"> <i class="fas fa-check-circle"></i>
<i class="fas fa-check-circle"></i> <div class="coverage-text">
<div class="coverage-text"> <h4>Неисправности механизмов</h4>
<h4>Неисправности механизмов</h4> <p>Трансформации, выдвижные системы</p>
<p>Трансформации, выдвижные системы</p> </div>
</div> </div>
</div> <div class="coverage-item covered">
<div class="coverage-item covered"> <i class="fas fa-check-circle"></i>
<i class="fas fa-check-circle"></i> <div class="coverage-text">
<div class="coverage-text"> <h4>Проблемы с фурнитурой</h4>
<h4>Проблемы с фурнитурой</h4> <p>Ручки, петли, направляющие</p>
<p>Ручки, петли, направляющие</p> </div>
</div> </div>
</div> <div class="coverage-item covered">
<div class="coverage-item covered"> <i class="fas fa-check-circle"></i>
<i class="fas fa-check-circle"></i> <div class="coverage-text">
<div class="coverage-text"> <h4>Дефекты покрытия</h4>
<h4>Дефекты покрытия</h4> <p>Отслоение шпона, краски, ламинации</p>
<p>Отслоение шпона, краски, ламинации</p> </div>
</div> </div>
</div> </div>
</div> </div>
</div>
<div class="coverage-not-covered">
<div class="coverage-not-covered"> <h2>Что не покрывается гарантией</h2>
<h2>Что не покрывается гарантией</h2> <div class="coverage-list">
<div class="coverage-list"> <div class="coverage-item not-covered">
<div class="coverage-item not-covered"> <i class="fas fa-times-circle"></i>
<i class="fas fa-times-circle"></i> <div class="coverage-text">
<div class="coverage-text"> <h4>Механические повреждения</h4>
<h4>Механические повреждения</h4> <p>Царапины, вмятины от неправильной эксплуатации</p>
<p>Царапины, вмятины от неправильной эксплуатации</p> </div>
</div> </div>
</div> <div class="coverage-item not-covered">
<div class="coverage-item not-covered"> <i class="fas fa-times-circle"></i>
<i class="fas fa-times-circle"></i> <div class="coverage-text">
<div class="coverage-text"> <h4>Следы износа</h4>
<h4>Следы износа</h4> <p>Естественное старение материалов</p>
<p>Естественное старение материалов</p> </div>
</div> </div>
</div> <div class="coverage-item not-covered">
<div class="coverage-item not-covered"> <i class="fas fa-times-circle"></i>
<i class="fas fa-times-circle"></i> <div class="coverage-text">
<div class="coverage-text"> <h4>Неправильная сборка</h4>
<h4>Неправильная сборка</h4> <p>Последствия самостоятельного ремонта</p>
<p>Последствия самостоятельного ремонта</p> </div>
</div> </div>
</div> <div class="coverage-item not-covered">
<div class="coverage-item not-covered"> <i class="fas fa-times-circle"></i>
<i class="fas fa-times-circle"></i> <div class="coverage-text">
<div class="coverage-text"> <h4>Внешние воздействия</h4>
<h4>Внешние воздействия</h4> <p>Повреждения от жидкостей, солнечных лучей</p>
<p>Повреждения от жидкостей, солнечных лучей</p> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </main>
</main>
<footer class="footer" id="footer">
<footer class="footer" id="footer"> <div class="container footer__content">
<div class="container footer__content"> <div class="footer__col footer--logo">
<div class="footer__col footer--logo"> <div class="logo">AETERNA</div>
<div class="logo">AETERNA</div> </div>
</div>
<div class="footer__col">
<div class="footer__col"> <h5>ПОКУПАТЕЛЮ</h5>
<h5>ПОКУПАТЕЛЮ</h5> <ul>
<ul> <li><a href="/catalog.php">Каталог</a></li>
<li><a href="/catalog.php">Каталог</a></li> <li><a href="services.php">Услуги</a></li>
<li><a href="services.php">Услуги</a></li> </ul>
</ul> </div>
</div>
<div class="footer__col">
<div class="footer__col"> <h5>ПОМОЩЬ</h5>
<h5>ПОМОЩЬ</h5> <ul>
<ul> <li><a href="delivery.php">Доставка и оплата</a></li>
<li><a href="delivery.php">Доставка и оплата</a></li> <li><a href="warranty.php">Гарантия и возврат</a></li>
<li><a href="warranty.php">Гарантия и возврат</a></li> <li><a href="cite_mebel.php#faq">Ответы на вопросы</a></li>
<li><a href="cite_mebel.php#faq">Ответы на вопросы</a></li> <li><a href="#footer">Контакты</a></li>
<li><a href="#footer">Контакты</a></li> </ul>
</ul> </div>
</div>
<div class="footer__col">
<div class="footer__col"> <h5>КОНТАКТЫ</h5>
<h5>КОНТАКТЫ</h5> <p>aeterna@mail.ru</p>
<p>aeterna@mail.ru</p> <p>+7(912)999-12-23</p>
<p>+7(912)999-12-23</p> <div class="social-icons">
<div class="social-icons"> <span class="icon"><i class="fab fa-telegram"></i></span>
<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-instagram"></i></span> <span class="icon"><i class="fab fa-vk"></i></span>
<span class="icon"><i class="fab fa-vk"></i></span> </div>
</div> </div>
</div>
<div class="footer__col">
<div class="footer__col"> <h5>ПРИНИМАЕМ К ОПЛАТЕ</h5>
<h5>ПРИНИМАЕМ К ОПЛАТЕ</h5> <div class="payment-icons">
<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-visa"></i></span> <span class="pay-icon"><i class="fab fa-cc-mastercard"></i></span>
<span class="pay-icon"><i class="fab fa-cc-mastercard"></i></span> </div>
</div> </div>
</div> </div>
</div> <div class="copyright">
<div class="copyright"> <p>© 2025 AETERNA. Все права защищены.</p>
<p>© 2025 AETERNA. Все права защищены.</p> </div>
</div> </footer>
</footer>
</body>
</body>
</html> </html>

View File

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

View File

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