Загрузка данных


Всё в полном порядке, не переживайте! На демонстрационных экзаменах структуру проекта можно делить по-разному. Разделение на **10 PHP-файлов** — это даже более грамотный подход. Это называется разделением логики (когда обработка данных и внешний вид страницы не перемешиваются в одном файле).
Давайте перестроим структуру именно так, как вам нужно. Мы разделим код так, чтобы каждый шаг был изолирован, а дизайн для смартфона объединим.
### Архитектура из 10 PHP-файлов и 1 HTML/CSS
Вот как теперь выглядит ваша папка moinesam в Visual Studio Code. Создайте именно эти файлы:
 1. db.php — подключение к вашей базе данных в phpMyAdmin.
 2. header.php — верхняя часть сайта (шапка, меню), чтобы не копировать её на все страницы.
 3. footer.php — нижняя часть сайта (подвал с копирайтом).
 4. index.php — главная страница (проверяет, вошел ли пользователь, и перенаправляет его).
 5. register.php — страница с формой регистрации (HTML).
 6. register_handler.php — **(Обработчик)** код, который берет данные из формы регистрации и отправляет в базу данных.
 7. login.php — страница с формой авторизации (HTML).
 8. login_handler.php — **(Обработчик)** код, который проверяет логин и пароль в базе данных.
 9. dashboard.php — личный кабинет пользователя (создание заявки и просмотр истории).
 10. admin.php — панель администратора для управления статусами.
**И 1 отдельный файл для мобильного дизайна:**
 * Внутри папки создайте файл mobile.html (внутри него будет и HTML, и CSS в теге <style>), где последовательно один за другим будут нарисованы экраны для телефона 390x844 px.
### Пошаговый план реализации в VS Code
Давайте напишем код для этих файлов, чтобы они заработали как единое целое.
#### 1. Файл db.php (Подключение)
Этот код мы уже знаем. Он просто связывает систему с MySQL.
```php
<?php
if (session_status() === PHP_SESSION_NONE) { session_start(); }
try {
    $pdo = new PDO("mysql:host=127.0.0.1;dbname=cleaning_db;charset=utf8mb4", "root", "root", [
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
    ]);
} catch (\PDOException $e) {
    die("Ошибка подключения: " . $e->getMessage());
}
?>

```
#### 2. Файл header.php (Шапка сайта)
```php
<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <title>Портал клининга «Мой Не Сам»</title>
    <style>
        body { font-family: Arial, sans-serif; margin: 20px; background: #f9f9f9; }
        nav { background: #333; padding: 10px; border-radius: 5px; margin-bottom: 20px; }
        nav a { color: white; margin-right: 15px; text-decoration: none; }
        .error { color: red; font-weight: bold; }
    </style>
</head>
<body>
<nav>
    <a href="index.php">Главная</a>
    <?php if(isset($_SESSION['user_id'])): ?>
        <a href="dashboard.php">Личный кабинет</a>
        <a href="logout.php">Выйти</a>
    <?php else: ?>
        <a href="login.php">Войти</a>
        <a href="register.php">Регистрация</a>
    <?php endif; ?>
</nav>

```
#### 3. Файл footer.php (Подвал сайта)
```php
<hr>
<p style="text-align: center; color: #666;">&copy; 2026 Портал клининговых услуг «Мой Не Сам»</p>
</body>
</html>

```
#### 4. Файл index.php (Главная)
```php
<?php
require_once 'db.php';
header('Location: login.php'); // Сразу перенаправляем на страницу входа по умолчанию
exit;
?>

```
#### 5. Файл register.php (Форма регистрации)
```php
<?php require_once 'db.php'; include 'header.php'; ?>
<h2>Регистрация в системе</h2>
<?php if(isset($_SESSION['reg_error'])): ?>
    <p class="error"><?= $_SESSION['reg_error']; unset($_SESSION['reg_error']); ?></p>
<?php endif; ?>

<form action="register_handler.php" method="POST">
    <p><input type="text" name="full_name" placeholder="ФИО" required></p>
    <p><input type="text" name="phone" placeholder="Телефон" required></p>
    <p><input type="email" name="email" placeholder="E-mail" required></p>
    <p><input type="text" name="login" placeholder="Логин (уникальный)" required></p>
    <p><input type="password" name="password" placeholder="Пароль" required></p>
    <button type="submit">Зарегистрироваться</button>
</form>
<?php include 'footer.php'; ?>

```
#### 6. Файл register_handler.php (Обработчик регистрации)
Этот файл не выводит дизайн, он только обрабатывает данные.
```php
<?php
require_once 'db.php';

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $login = trim($_POST['login']);
    
    // Проверяем, нет ли уже такого логина
    $check = $pdo->prepare("SELECT id FROM users WHERE login = ?");
    $check->execute([$login]);
    
    if ($check->fetch()) {
        $_SESSION['reg_error'] = "Этот логин уже занят!";
        header('Location: register.php');
        exit;
    }

    // Сохраняем пользователя (пароль хэшируем для безопасности)
    $hashed_password = password_hash($_POST['password'], PASSWORD_DEFAULT);
    $stmt = $pdo->prepare("INSERT INTO users (full_name, phone, email, login, password) VALUES (?, ?, ?, ?, ?)");
    $stmt->execute([$_POST['full_name'], $_POST['phone'], $_POST['email'], $login, $hashed_password]);

    header('Location: login.php');
    exit;
}
?>

```
#### 7. Файл login.php (Форма авторизации)
```php
<?php require_once 'db.php'; include 'header.php'; ?>
<h2>Авторизация</h2>
<?php if(isset($_SESSION['login_error'])): ?>
    <p class="error"><?= $_SESSION['login_error']; unset($_SESSION['login_error']); ?></p>
<?php endif; ?>

<form action="login_handler.php" method="POST">
    <p><input type="text" name="login" placeholder="Логин" required></p>
    <p><input type="password" name="password" placeholder="Пароль" required></p>
    <button type="submit">Войти</button>
</form>
<?php include 'footer.php'; ?>

```
#### 8. Файл login_handler.php (Обработчик авторизации)
```php
<?php
require_once 'db.php';

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $login = trim($_POST['login']);
    $password = $_POST['password'];

    // Проверка на жестко заданного админа из задания
    if ($login === 'adminka' && $password === 'password') {
        $_SESSION['user_id'] = 'admin';
        $_SESSION['role'] = 'admin';
        header('Location: admin.php');
        exit;
    }

    // Ищем обычного пользователя
    $stmt = $pdo->prepare("SELECT * FROM users WHERE login = ?");
    $stmt->execute([$login]);
    $user = $stmt->fetch();

    if ($user && password_verify($password, $user['password'])) {
        $_SESSION['user_id'] = $user['id'];
        $_SESSION['role'] = 'user';
        header('Location: dashboard.php');
        exit;
    } else {
        $_SESSION['login_error'] = "Некорректный логин или пароль!";
        header('Location: login.php');
        exit;
    }
}
?>

```
#### 9. Файл dashboard.php (Личный кабинет и создание заявки)
Здесь по заданию пользователь видит историю и может отправить новую заявку.
```php
<?php 
require_once 'db.php'; 
if (!isset($_SESSION['user_id']) || $_SESSION['role'] !== 'user') { header('Location: login.php'); exit; }

// Если отправлена новая заявка
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $stmt = $pdo->prepare("INSERT INTO requests (user_id, address, contact_data, service_type, request_date, request_time, payment_type) VALUES (?, ?, ?, ?, ?, ?, ?)");
    $stmt->execute([
        $_SESSION['user_id'], $_POST['address'], $_POST['contact_data'],
        $_POST['service_type'], $_POST['request_date'], $_POST['request_time'], $_POST['payment_type']
    ]);
    header('Location: dashboard.php'); exit;
}

// Получаем историю заявок пользователя
$stmt = $pdo->prepare("SELECT * FROM requests WHERE user_id = ? ORDER BY id DESC");
$stmt->execute([$_SESSION['user_id']]);
$my_requests = $stmt->fetchAll();

include 'header.php'; 
?>

<h2>Оставить заявку на уборку</h2>
<form method="POST">
    <p><input type="text" name="address" placeholder="Ваш адрес" required></p>
    <p><input type="text" name="contact_data" placeholder="Контактные данные" required></p>
    <p>Вид услуги: 
        <select name="service_type" required>
            <option>Общий клининг</option>
            <option>Генеральная уборка</option>
            <option>Послестроительная уборка</option>
            <option>Химчистка ковров и мебели</option>
        </select>
    </p>
    <p>Дата: <input type="date" name="request_date" required></p>
    <p>Время: <input type="time" name="request_time" required></p>
    <p>Оплата: 
        <input type="radio" name="payment_type" value="Наличные" checked> Наличные
        <input type="radio" name="payment_type" value="Банковская карта"> Карта
    </p>
    <button type="submit">Сформировать заявку</button>
</form>

<h2>История ваших заявок</h2>
<table border="1" cellpadding="10" cellspacing="0">
    <tr><th>ID</th><th>Услуга</th><th>Дата/Время</th><th>Статус</th><th>Причина отмены</th></tr>
    <?php foreach($my_requests as $req): ?>
    <tr>
        <td><?= $req['id'] ?></td>
        <td><?= $req['service_type'] ?></td>
        <td><?= $req['request_date'] ?> <?= $req['request_time'] ?></td>
        <td><?= $req['status'] ?></td>
        <td><?= $req['reject_reason'] ?? '-' ?></td>
    </tr>
    <?php endforeach; ?>
</table>

<?php include 'footer.php'; ?>

```
#### 10. Файл admin.php (Панель Администратора)
```php
<?php
require_once 'db.php';
if (!isset($_SESSION['role']) || $_SESSION['role'] !== 'admin') { header('Location: login.php'); exit; }

// Обновление статуса администратором
if (isset($_POST['change_status'])) {
    $reason = ($_POST['status'] === 'Отменено') ? $_POST['reject_reason'] : null;
    $stmt = $pdo->prepare("UPDATE requests SET status = ?, reject_reason = ? WHERE id = ?");
    $stmt->execute([$_POST['status'], $reason, $_POST['req_id']]);
    header('Location: admin.php'); exit;
}

// Подгружаем вообще все заявки из базы данных
$requests = $pdo->query("SELECT requests.*, users.full_name FROM requests JOIN users ON requests.user_id = users.id ORDER BY requests.id DESC")->fetchAll();

include 'header.php';
?>
<h2>Панель администратора</h2>
<table border="1" cellpadding="10" cellspacing="0">
    <tr>
        <th>ФИО клиента</th><th>Контакты</th><th>Адрес</th><th>Услуга</th><th>Статус</th><th>Действие</th>
    </tr>
    <?php foreach($requests as $r): ?>
    <tr>
        <td><?= htmlspecialchars($r['full_name']) ?></td>
        <td><?= htmlspecialchars($r['contact_data']) ?></td>
        <td><?= htmlspecialchars($r['address']) ?></td>
        <td><?= $r['service_type'] ?></td>
        <td><strong><?= $r['status'] ?></strong></td>
        <td>
            <form method="POST">
                <input type="hidden" name="req_id" value="<?= $r['id'] ?>">
                <select name="status">
                    <option value="В работе">В работу</option>
                    <option value="Выполнено">Выполнено</option>
                    <option value="Отменено">Отменить</option>
                </select>
                <input type="text" name="reject_reason" placeholder="Причина отмены (если отмена)">
                <button type="submit" name="change_status">Ок</button>
            </form>
        </td>
    </tr>
    <?php endforeach; ?>
</table>
<?php include 'footer.php'; ?>

```
### Один файл мобильного дизайна (mobile.html)
Поскольку в задании сказано, что интегрировать этот дизайн в код не нужно, мы создаем красивую страницу-презентацию, где один под другим отображаются макеты экранов размером ровно 390x844 px.
Создайте файл mobile.html и вставьте туда этот код:
```html
<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <title>Мобильный дизайн (390x844)</title>
    <style>
        body { background: #f0f2f5; display: flex; flex-direction: column; align-items: center; gap: 40px; padding: 20px; font-family: sans-serif; }
        .phone-mockup { width: 390px; height: 844px; background: white; border: 8px solid #222; border-radius: 40px; box-shadow: 0 10px 30px rgba(0,0,0,0.15); overflow-y: auto; padding: 30px 20px; box-sizing: border-box; display: flex; flex-direction: column; gap: 20px; }
        .screen-title { text-align: center; font-size: 14px; color: #888; font-weight: bold; text-transform: uppercase; margin-bottom: -20px; }
        h1 { font-size: 24px; text-align: center; color: #0056b3; }
        input, select { width: 100%; padding: 12px; border: 1px solid #ddd; border-radius: 8px; box-sizing: border-box; }
        button { background: #0056b3; color: white; border: none; padding: 14px; border-radius: 8px; width: 100%; cursor: pointer; font-size: 16px; }
        .card { border: 1px solid #eee; padding: 15px; border-radius: 8px; background: #fafafa; }
    </style>
</head>
<body>

    <div class="screen-title">Экран авторизации</div>
    <div class="phone-mockup">
        <h1>Мой Не Сам</h1>
        <p style="text-align:center; color:#666;">Вход для клиентов</p>
        <input type="text" placeholder="Логин">
        <input type="password" placeholder="Пароль">
        <button>Войти</button>
        <p style="text-align:center; font-size:14px;"><a href="#">Регистрация</a></p>
    </div>

    <div class="screen-title">Экран регистрации</div>
    <div class="phone-mockup">
        <h1>Регистрация</h1>
        <input type="text" placeholder="ФИО">
        <input type="text" placeholder="Телефон">
        <input type="email" placeholder="E-mail">
        <input type="text" placeholder="Придумайте логин">
        <input type="password" placeholder="Придумайте пароль">
        <button>Зарегистрироваться</button>
    </div>

    <div class="screen-title">Создание заявки и история</div>
    <div class="phone-mockup">
        <h2 style="color:#0056b3; font-size:20px;">Новая заявка</h2>
        <input type="text" placeholder="Адрес доставки услуг">
        <select><option>Общий клининг</option><option>Генеральная уборка</option></select>
        <input type="date">
        <input type="time">
        <button>Отправить заявку</button>
        
        <h2 style="font-size:18px; margin-top:20px;">Мои заявки</h2>
        <div class="card">
            <strong>Генеральная уборка</strong><br>
            <span style="font-size:12px; color:#999;">12.06.2026 в 14:00</span><br>
            <span style="color:green; font-size:14px;">Статус: Выполнено</span>
        </div>
    </div>

</body>
</html>

```
### Как это теперь запустить?
 1. Включаете OpenServer.
 2. В браузере открываете http://localhost/moinesam/login.php — это рабочий сайт. Вы можете зарегистрироваться, войти под пользователем, создать заявку, а потом зайти под админом (adminka / password) и изменить её статус.
 3. Чтобы показать экспертам мобильный дизайн, откройте в браузере http://localhost/moinesam/mobile.html. Они увидят красивые макеты мобильного приложения ровно в требуемом разрешении.