34084-vm/index.php

444 lines
18 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
declare(strict_types=1);
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
require_once 'db/config.php';
// --- DB MIGRATIONS ---
try {
$pdo = db();
$pdo->exec('CREATE TABLE IF NOT EXISTS `migrations` (`migration` VARCHAR(255) NOT NULL, PRIMARY KEY (`migration`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;');
$executed_migrations_stmt = $pdo->query('SELECT migration FROM migrations');
$executed_migrations = $executed_migrations_stmt->fetchAll(PDO::FETCH_COLUMN);
$migration_files = glob('db/migrations/*.sql');
sort($migration_files);
foreach ($migration_files as $file) {
$migration_name = basename($file);
if (!in_array($migration_name, $executed_migrations)) {
$sql = file_get_contents($file);
$pdo->exec($sql);
$stmt = $pdo->prepare('INSERT INTO migrations (migration) VALUES (?)');
$stmt->execute([$migration_name]);
}
}
} catch (PDOException $e) {
error_log('Migration Error: ' . $e->getMessage());
}
// --- END DB MIGRATIONS ---
$p_title = 'Цветочный магазин';
$p_description = 'Красивые букеты на любой случай.';
try {
$pdo = db();
$stmt = $pdo->query('SELECT * FROM bouquets ORDER BY name ASC');
$bouquets = $stmt->fetchAll();
} catch (PDOException $e) {
error_log('DB Error: ' . $e->getMessage());
$bouquets = [];
$db_error = 'К сожалению, мы не смогли загрузить букеты в данный момент. Пожалуйста, попробуйте еще раз позже.';
}
$status_message = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$customer_name = trim($_POST['name'] ?? '');
$bouquet_id = (int)($_POST['bouquet'] ?? 0);
$phone = trim($_POST['phone'] ?? '');
$address = trim($_POST['address'] ?? '');
$delivery_date = trim($_POST['delivery_date'] ?? '');
$note = trim($_POST['note'] ?? '');
if (empty($customer_name) || empty($bouquet_id) || empty($phone) || empty($address) || empty($delivery_date)) {
$status_message = '<div class="alert alert-danger">Пожалуйста, заполните все обязательные поля.</div>';
} else {
try {
$sql = "INSERT INTO orders (customer_name, bouquet_id, phone, address, delivery_date, note) VALUES (:name, :bouquet, :phone, :address, :date, :note)";
$stmt = $pdo->prepare($sql);
$stmt->execute([
':name' => $customer_name,
':bouquet' => $bouquet_id,
':phone' => $phone,
':address' => $address,
':date' => $delivery_date,
':note' => $note
]);
$status_message = '<div class="alert alert-success">Спасибо за ваш заказ! Мы скоро с вами свяжемся.</div>';
} catch (PDOException $e) {
error_log('Order submission error: ' . $e->getMessage());
$status_message = '<div class="alert alert-danger">Не удалось обработать ваш заказ. Пожалуйста, попробуйте еще раз.</div>';
}
}
}
?>
<!doctype html>
<html lang="ru">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="description" content="<?= htmlspecialchars($p_description) ?>">
<title><?= htmlspecialchars($p_title) ?></title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;500&family=Playfair+Display:wght@700&display=swap" rel="stylesheet">
<style>
:root {
--bg-color: #ffffff;
--text-color: #000000;
--border-color: #eeeeee;
--body-font: 'Montserrat', sans-serif;
--title-font: 'Playfair Display', serif;
}
body {
background-color: var(--bg-color);
font-family: var(--body-font);
color: var(--text-color);
}
.navbar {
font-family: var(--body-font);
background-color: var(--bg-color) !important;
border-bottom: 1px solid var(--border-color);
box-shadow: none;
}
.navbar-brand {
font-family: var(--title-font);
font-weight: 700;
font-size: 1.75rem;
color: var(--text-color) !important;
}
.nav-link {
font-weight: 500;
color: var(--text-color) !important;
text-transform: uppercase;
font-size: 0.9rem;
letter-spacing: 0.5px;
}
.header-icons .nav-link {
font-size: 1.2rem;
}
.hero {
position: relative;
padding: 8rem 2rem;
text-align: center;
color: #ffffff;
background: url('assets/pasted-20250911-145034-379d8f9f.jpg') no-repeat center center;
background-size: cover;
border-bottom: 1px solid var(--border-color);
}
.hero::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.3);
z-index: 1;
}
.hero .container {
position: relative;
z-index: 2;
display: flex;
justify-content: center;
align-items: center;
}
.hero h1, .section-title, .card-title {
font-family: var(--title-font);
font-weight: 700;
}
.hero h1 {
font-family: var(--title-font);
font-size: 4.5rem;
color: #ffffff;
text-shadow: none;
margin: 0 2rem;
}
.hero .lead {
color: #ffffff;
text-shadow: none;
font-size: 1rem;
text-transform: uppercase;
letter-spacing: 1px;
}
.hero-arrow {
color: #fff;
font-size: 2.5rem;
text-decoration: none;
opacity: 0.8;
}
.section-title {
color: var(--text-color);
font-size: 2rem;
}
.btn-primary {
background-color: var(--text-color);
border-color: var(--text-color);
color: var(--bg-color);
font-weight: 700;
padding: 0.75rem 1.5rem;
transition: none;
border-radius: 0;
}
.btn-primary:hover {
background-color: #333333;
border-color: #333333;
}
.form-control, .form-select {
border-radius: 0;
}
.form-control:focus, .form-select:focus {
border-color: var(--text-color);
box-shadow: 0 0 0 0.2rem rgba(0, 0, 0, 0.15);
}
footer {
background-color: var(--bg-color);
color: var(--text-color);
padding: 2rem 0;
margin-top: 4rem;
border-top: 1px solid var(--border-color);
}
.bouquets-list .row {
gap: 3rem 0;
}
.bouquet-card {
display: flex;
flex-direction: row;
align-items: stretch;
border: none;
background-color: transparent;
gap: 2rem;
margin-bottom: 2rem !important;
}
.bouquet-card-reverse {
flex-direction: row-reverse;
}
.bouquet-card .card-image {
flex: 1 1 50%;
}
.bouquet-card .card-image img {
width: 100%;
height: auto;
max-height: 500px;
object-fit: cover;
}
.bouquet-card .card-title-container {
flex: 0 0 50px;
display: flex;
justify-content: center;
align-items: center;
height: 300px;
}
.bouquet-card .card-title-vertical {
display: flex;
flex-direction: column-reverse;
align-items: center;
gap: 1rem;
}
.bouquet-card .card-title-vertical .dot {
width: 8px;
height: 8px;
background-color: #000;
border-radius: 50%;
}
.bouquet-card .card-title-vertical .line {
width: 1px;
height: 70px;
background-color: #000;
}
.bouquet-card .card-title {
writing-mode: vertical-rl;
transform: rotate(180deg);
font-family: 'Poppins', sans-serif;
font-weight: 600;
font-size: 1.5rem;
white-space: nowrap;
margin: 0;
}
.bouquet-card .card-body {
flex: 1 1 45%;
padding: 1rem;
}
.bouquet-card .card-text {
font-family: 'Poppins', sans-serif;
font-size: 1rem;
color: #6c757d;
line-height: 1.6;
margin-bottom: 2rem;
}
.bouquet-card .btn-outline-dark {
font-family: 'Poppins', sans-serif;
font-weight: 500;
font-size: 1rem;
padding: 0.75rem 2rem;
border-radius: 50px;
transition: all 0.3s ease;
}
.bouquet-card .price {
font-family: 'Poppins', sans-serif;
font-size: 1.75rem;
font-weight: 600;
color: #000;
}
.section-title-container {
display: flex;
align-items: center;
gap: 1rem;
justify-content: center;
margin-bottom: 3rem;
}
.decorator-dot {
width: 8px;
height: 8px;
background-color: var(--text-color);
border-radius: 50%;
}
.decorator-line {
width: 40px;
height: 1px;
background-color: var(--text-color);
}
</style>
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap" rel="stylesheet">
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-light sticky-top">
<div class="container">
<a class="navbar-brand" href="#">Fleur</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav mx-auto">
<li class="nav-item"><a class="nav-link" href="#">Главная</a></li>
<li class="nav-item"><a class="nav-link" href="#bouquets">Букеты</a></li>
<li class="nav-item"><a class="nav-link" href="#order">Заказ</a></li>
<li class="nav-item"><a class="nav-link" href="/admin.php">Админ</a></li>
</ul>
<ul class="navbar-nav header-icons">
<li class="nav-item"><a class="nav-link" href="#">&#128269;</a></li>
<li class="nav-item"><a class="nav-link" href="#">&#128100;</a></li>
<li class="nav-item"><a class="nav-link" href="#">&#128092;</a></li>
</ul>
</div>
</div>
</nav>
<header class="hero">
<div class="container">
<a href="#" class="hero-arrow">&#10229;</a>
<div>
<h1 class="display-3">Искусство простоты</h1>
<p class="lead">Букеты ручной работы</p>
</div>
<a href="#" class="hero-arrow">&#10230;</a>
</div>
</header>
<main class="container-fluid my-5">
<section id="bouquets" class="my-5">
<div class="section-title-container">
<div class="decorator-dot"></div>
<div class="decorator-line"></div>
<h2 class="section-title mb-0">Наши Букеты</h2>
</div>
<?php if (!empty($db_error)): ?>
<div class="alert alert-danger"><?= htmlspecialchars($db_error) ?></div>
<?php elseif (empty($bouquets)): ?>
<div class="alert alert-info">Букеты скоро появятся.</div>
<?php else: ?>
<div class="bouquets-list">
<div class="row">
<?php
$image_urls = [
'https://media-api.xogrp.com/images/1edfbffe-ca90-4381-a78f-d8c9d9485765~rs_768.h-cr_0.209.1094.1303',
'https://m.media-amazon.com/images/I/71fpxULF4xL._UF894,1000_QL80_.jpg',
'https://www.odealarose.com/blog/wp-content/uploads/2025/04/flowers-with-eucalyptus-768x512.jpg',
'https://www.lyttonrosebotanical.com/cdn/shop/files/rn-image_picker_lib_temp_a4ee8946-d08b-47d3-a6d9-0e22b4607417.jpg?v=1733076272&width=1946'
];
foreach ($bouquets as $key => $bouquet):
$image_url = $image_urls[$key % count($image_urls)];
?>
<div class="col-12">
<div class="card bouquet-card <?= ($key % 2 !== 0) ? 'bouquet-card-reverse' : '' ?>">
<div class="card-image">
<img src="<?= $image_url ?>" alt="<?= htmlspecialchars($bouquet['name']) ?>">
</div>
<div class="card-title-container">
<div class="card-title-vertical">
<h5 class="card-title"><?= htmlspecialchars($bouquet['name']) ?></h5>
<div class="line"></div>
<div class="dot"></div>
</div>
</div>
<div class="card-body d-flex flex-column <?= ($key % 2 !== 0) ? 'text-lg-end' : '' ?>">
<div class="mt-auto">
<p class="card-text mb-4"><?= htmlspecialchars($bouquet['description']) ?></p>
<div class="price mb-4"><?= htmlspecialchars(number_format((float)$bouquet['price'], 2)) ?> руб.</div>
<a href="#order" class="btn btn-outline-dark">Заказать</a>
</div>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>
</section>
<section id="order" class="my-5 pt-5">
<h2 class="section-title text-center mb-4">Оформить заказ</h2>
<div class="row justify-content-center">
<div class="col-lg-8">
<?= $status_message ?>
<div class="card p-4 shadow-sm border-0">
<form action="/#order" method="POST" novalidate>
<div class="mb-3">
<label for="name" class="form-label">Ваше имя</label>
<input type="text" class="form-control" id="name" name="name" required>
</div>
<div class="mb-3">
<label for="bouquet" class="form-label">Выберите букет</label>
<select class="form-select" id="bouquet" name="bouquet" required>
<option value="" disabled selected>Выберите...</option>
<?php foreach ($bouquets as $bouquet): ?>
<option value="<?= $bouquet['id'] ?>"><?= htmlspecialchars($bouquet['name']) ?> - <?= htmlspecialchars(number_format((float)$bouquet['price'], 2)) ?> руб.</option>
<?php endforeach; ?>
</select>
</div>
<div class="mb-3">
<label for="phone" class="form-label">Номер телефона</label>
<input type="tel" class="form-control" id="phone" name="phone" required>
</div>
<div class="mb-3">
<label for="address" class="form-label">Адрес доставки</label>
<textarea class="form-control" id="address" name="address" rows="3" required></textarea>
</div>
<div class="mb-3">
<label for="delivery_date" class="form-label">Дата доставки</label>
<input type="date" class="form-control" id="delivery_date" name="delivery_date" required>
</div>
<div class="mb-3">
<label for="note" class="form-label">Примечание (необязательно)</label>
<textarea class="form-control" id="note" name="note" rows="3" maxlength="300"></textarea>
</div>
<div class="d-grid">
<button type="submit" class="btn btn-primary btn-lg">Отправить заказ</button>
</div>
</form>
</div>
</div>
</div>
</section>
</main>
<footer class="text-center">
<div class="container">
<p>&copy; <?= date('Y') ?> Fleur. Все права защищены.</p>
</div>
</footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
</body>
</html>