
Кольцова А.А. Разработка прототипа системы бронирования велопрокатной организации
.pdf
121
<html lang="ru"> <head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Формирование отчетности</title>
<link rel="stylesheet" href="css/styles.css"> </head>
<body>
<div class="manager-container">
<h2>Формирование отчетности</h2>
<?php if (!empty($message)): ?>
<div class="alert alert-success"><?php echo htmlspecialchars($message, ENT_QUOTES, 'UTF-8'); ?></div> <?php endif; ?>
<?php if (!empty($error)): ?>
<div class="alert alert-error"><?php echo htmlspecialchars($error, ENT_QUOTES, 'UTF-8'); ?></div> <?php endif; ?>
<form method="POST" action="statistics_manager.php"> <label for="report_type">Тип отчета:</label>
<select name="report_type" id="report_type" required>
<option value="bookings">Отчет по бронированиям</option> <!-- Добавить другие типы отчетов при необходимости -->
</select>
<label for="start_date">Дата начала:</label>
<input type="date" name="start_date" id="start_date" required> <label for="end_date">Дата окончания:</label>
<input type="date" name="end_date" id="end_date" required> <label for="user_id">Пользователь:</label>
<select name="user_id" id="user_id">
<option value="">Все пользователи</option>
<?php while ($user = $users_result->fetch_assoc()): ?>
<option value="<?php echo $user['user_id']; ?>"><?php echo htmlspecialchars($user['username'], ENT_QUOTES, 'UTF-8'); ?></option>
<?php endwhile; ?> </select>
<label for="status_id">Статус бронирования:</label> <select name="status_id" id="status_id">
<option value="">Все статусы</option>
<?php while ($status = $status_result->fetch_assoc()): ?>
<option value="<?php echo $status['status_id']; ?>"><?php echo htmlspecialchars($status['status_name'], ENT_QUOTES, 'UTF-8'); ?></option>
<?php endwhile; ?> </select>
<button type="submit" name="generate_report">Сформировать отчет</button>
<button type="submit" name="download_excel" style="margin-top: 10px;">Скачать отчет в Excel</button> </form>
<?php if (!empty($report_data) && !isset($_POST['download_excel'])): ?>
<h3>Результаты отчета</h3>
<table>
<thead>
<tr>
<th>ID бронирования</th> <th>Начало</th> <th>Конец</th> <th>Пользователь</th> <th>Статус</th> <th>Велосипеды</th>
</tr>
</thead>
<tbody>
<?php foreach ($report_data as $data): ?> <tr>
<td><?php echo htmlspecialchars($data['booking_id'], ENT_QUOTES, 'UTF-8'); ?></td> <td><?php echo htmlspecialchars($data['start_time'], ENT_QUOTES, 'UTF-8'); ?></td> <td><?php echo htmlspecialchars($data['end_time'], ENT_QUOTES, 'UTF-8'); ?></td> <td><?php echo htmlspecialchars($data['username'], ENT_QUOTES, 'UTF-8'); ?></td> <td><?php echo htmlspecialchars($data['status_name'], ENT_QUOTES, 'UTF-8'); ?></td> <td><?php echo htmlspecialchars($data['bike_models'], ENT_QUOTES, 'UTF-8'); ?></td>
</tr>
<?php endforeach; ?>

122
</tbody>
</table> <?php endif; ?>
</div>
<?php include 'footer.php'; ?> </body>
</html>
13.Файл profile.php
Описание: Этот файл отображает личный кабинет пользователя. В нем содержится информация о пользователе, его бронированиях и форма для отмены бронирований или оставления отзывов.
<?php session_start(); include 'header.php'; include 'db.php';
// Проверка, что пользователь авторизован if (!isset($_SESSION['user_id'])) {
header("Location: login.php?error=Пожалуйста, войдите в систему."); exit();
}
$user_id = $_SESSION['user_id'];
// Обработка отмены бронирования
if ($_SERVER["REQUEST_METHOD"] == "POST" && isset($_POST['cancel_booking_id'])) { $cancel_booking_id = (int)$_POST['cancel_booking_id'];
$cancel_query = "UPDATE bookings SET status_id = 2 WHERE booking_id = $cancel_booking_id AND user_id = $user_id"; $conn->query($cancel_query);
}
// Обработка обратной связи
if ($_SERVER["REQUEST_METHOD"] == "POST" && isset($_POST['feedback_booking_id'])) { $feedback_booking_id = (int)$_POST['feedback_booking_id'];
$rating = (int)$_POST['rating'];
$comment = $conn->real_escape_string($_POST['comment']);
$feedback_query = "INSERT INTO feedback (user_id, booking_id, rating, comment) VALUES ($user_id, $feedback_booking_id, $rating, '$comment')";
$conn->query($feedback_query); header("Location: profile.php"); exit();
}
// Отложить отзыв
if ($_SERVER["REQUEST_METHOD"] == "POST" && isset($_POST['postpone_feedback'])) { $_SESSION['postpone_feedback'] = true;
header("Location: profile.php"); exit();
}
// Получение информации о пользователе
$user_query = "SELECT username, email, phone_number, booking_count FROM users WHERE user_id = $user_id"; $user_result = $conn->query($user_query);
$user = $user_result->fetch_assoc();
// Получение списка бронирований пользователя
$bookings_query = "SELECT B.booking_id, B.start_time, B.end_time, BS.status_name FROM bookings B
LEFT JOIN bookingstatus BS ON B.status_id = BS.status_id WHERE B.user_id = $user_id";
$bookings_result = $conn->query($bookings_query);
// Проверка наличия завершенных бронирований
$completed_booking_query = "SELECT booking_id FROM bookings WHERE user_id = $user_id AND status_id = 3"; $completed_booking_result = $conn->query($completed_booking_query);

123
$completed_booking = $completed_booking_result->fetch_assoc();
// Проверка наличия уже существующего отзыва
$existing_feedback_query = "SELECT feedback_id FROM feedback WHERE user_id = $user_id AND booking_id = " . (isset($completed_booking['booking_id']) ? $completed_booking['booking_id'] : 'NULL');
$existing_feedback_result = $conn->query($existing_feedback_query); $existing_feedback = $existing_feedback_result->fetch_assoc();
?>
<div class="profile-container"> <h1>Личный кабинет</h1> <div class="user-info">
<h2>Информация о пользователе</h2>
<p><strong>Имя пользователя:</strong> <?php echo htmlspecialchars($user['username'], ENT_QUOTES, 'UTF-8'); ?></p> <p><strong>Электронная почта:</strong> <?php echo htmlspecialchars($user['email'], ENT_QUOTES, 'UTF-8'); ?></p> <p><strong>Номер телефона:</strong> <?php echo htmlspecialchars($user['phone_number'], ENT_QUOTES, 'UTF-8');
?></p>
<p><strong>Количество бронирований:</strong> <?php echo htmlspecialchars($user['booking_count'], ENT_QUOTES, 'UTF-8'); ?></p>
</div>
<div class="user-bookings"> <h2>Мои бронирования</h2>
<?php if ($bookings_result->num_rows > 0): ?> <ul>
<?php while($booking = $bookings_result->fetch_assoc()): ?> <li>
<p><strong>ID бронирования:</strong> <?php echo htmlspecialchars($booking['booking_id'], ENT_QUOTES, 'UTF-8'); ?></p>
<p><strong>Начало:</strong> <?php echo htmlspecialchars($booking['start_time'], ENT_QUOTES, 'UTF-8');
?></p>
<p><strong>Конец:</strong> <?php echo htmlspecialchars($booking['end_time'], ENT_QUOTES, 'UTF-8');
?></p>
<p><strong>Статус:</strong> <?php echo htmlspecialchars($booking['status_name'], ENT_QUOTES, 'UTF-8');
?></p>
<p><strong>Велосипеды:</strong></p> <ul>
<?php
$bike_query = "SELECT BK.bike_model, BK.serial_number, BK.photo FROM bookingdetails BD
JOIN bikes BK ON BD.bike_id = BK.bike_id WHERE BD.booking_id = " . $booking['booking_id'];
$bike_result = $conn->query($bike_query); while($bike = $bike_result->fetch_assoc()): ?>
<li style="width: 300px; display: inline-block;">
<p>Модель: <?php echo htmlspecialchars($bike['bike_model'], ENT_QUOTES, 'UTF-8'); ?></p> <p>Серийный номер: <?php echo htmlspecialchars($bike['serial_number'], ENT_QUOTES, 'UTF-8');
?></p>
<?php if ($bike['photo']): ?>
<img src="data:image/jpeg;base64,<?php echo base64_encode($bike['photo']); ?>" alt="Фото велосипеда" style="width: 300px; height: 200px;">
<?php endif; ?> </li>
<?php endwhile; ?> </ul>
<?php if ($booking['status_name'] != 'Отменено'): ?> <form method="POST" action="profile.php">
<input type="hidden" name="cancel_booking_id" value="<?php echo $booking['booking_id']; ?>"> <button type="submit">Отменить</button>
</form> <?php endif; ?>
</li>
<?php endwhile; ?> </ul>
<?php else: ?>
<p>У вас нет активных бронирований.</p>
<?php endif; ?> </div>
</div>

124
<?php if ($completed_booking && !isset($_SESSION['postpone_feedback']) && !$existing_feedback): ?> <div id="feedback-notification" class="feedback-notification">
<div class="feedback-content">
<span class="close-btn" onclick="closeFeedback()">×</span>
<p>У вас есть завершенное бронирование. Пожалуйста, оставьте отзыв!</p> <form method="POST" action="profile.php">
<input type="hidden" name="feedback_booking_id" value="<?php echo $completed_booking['booking_id']; ?>"> <label for="rating">Оценка:</label>
<div class="stars">
<input type="radio" name="rating" class="star-1" id="star-1" value="1"> <label class="star" for="star-1"> </label>
<input type="radio" name="rating" class="star-2" id="star-2" value="2"> <label class="star" for="star-2"> </label>
<input type="radio" name="rating" class="star-3" id="star-3" value="3"> <label class="star" for="star-3"> </label>
<input type="radio" name="rating" class="star-4" id="star-4" value="4"> <label class="star" for="star-4"> </label>
<input type="radio" name="rating" class="star-5" id="star-5" value="5"> <label class="star" for="star-5"> </label>
</div>
<br>
<label for="comment" style="margin-top: 0px;">Комментарий:</label>
<textarea id="comment" name="comment" required style="height: 100px;"></textarea> <br>
<button type="submit">Отправить отзыв</button> </form>
</div>
</div>
<script>
function closeFeedback() { document.getElementById('feedback-notification').style.display = 'none';
}
window.onload = function() {
var notification = document.getElementById('feedback-notification'); notification.style.display = 'block';
}
document.querySelectorAll('.stars input').forEach((input) => { input.addEventListener('change', (e) => {
let value = e.target.value;
document.querySelectorAll('.stars label').forEach((label, index) => { if (index < value) {
label.style.color = 'gold'; } else {
label.style.color = 'gray';
}
});
});
});
</script> <?php endif; ?>
<style>
.feedback-notification { display: none; position: fixed;
top: 50%; left: 50%;
transform: translate(-50%, -50%); background: #f9f9f9;
padding: 20px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); z-index: 1000;
}
.feedback-content { position: relative; max-width: 400px; text-align: center;
}

125
.close-btn { position: absolute; top: 10px;
right: 10px; font-size: 24px; cursor: pointer;
}
.feedback-notification form { display: flex; flex-direction: column; gap: 10px;
}
.stars { display: flex;
justify-content: center; gap: 5px;
}
.stars input[type="radio"] { display: none;
}
.stars label { font-size: 30px; color: gray; cursor: pointer;
}
</style>
<?php include 'footer.php'; ?> </body>
</html>

126
Приложение Е
Руководство пользователя
Содержание:
1.Введение
2.Регистрация и вход
3.Использование системы
a.Главная страница
b.Просмотр каталога
c.Бронирование велосипедов
d.Личный кабинет
4.Административные функции
a.Панель администратора
b.Панель менеджера
5.Отчеты и статистика
1.Введение
Добро пожаловать в систему RIDE-RENT! Эта система предназначена для управления арендой велосипедов, позволяя пользователям бронировать велосипеды, а администраторам и менеджерам — управлять каталогом велосипедов и отслеживать бронирования.
2. Регистрация и вход
Регистрация
Перейдите на страницу регистрации, кликнув по ссылке "Регистрация" в верхней навигации (рис. 1).
Рисунок 1 – Ссылка на Регистрацию

127
Заполните форму, указав имя пользователя, адрес электронной почты, номер телефона
(необязательно) и пароль.
Нажмите кнопку "Зарегистрироваться"(рис. 2).
Рисунок 2 – Кнопка «Зарегистрироваться» Если все данные введены корректно, вы увидите сообщение об успешной регистрации.
Теперь вы можете войти в систему.
Вход
Перейдите на страницу входа, кликнув по ссылке "Вход" в верхней навигации (рис. 3).
Рисунок 3 – Ссылка на Вход Введите ваш адрес электронной почты и пароль.
Нажмите кнопку "Войти" (рис. 4).
Рисунок 4 – Кнопка «Войти»

128
После успешного входа вы будете перенаправлены на главную страницу или в личный кабинет, в зависимости от вашей роли.
3. Использование системы
Главная страница
На главной странице вы можете найти основную информацию о сервисе RIDE-RENT.
Здесь также можно быстро перейти к каталогу велосипедов или в личный кабинет.
Просмотр каталога
Перейдите на страницу каталога, кликнув по ссылке "Каталог" в верхней навигации
(рис. 5).
Рисунок 5 – Ссылка на Каталог Используйте фильтры, чтобы отфильтровать велосипеды по размеру рамы,
возрастному ограничению, доступности и категории.
Нажмите кнопку "Фильтровать", чтобы применить фильтры (рис. 6).
Рисунок 6 – Кнопка «Фильтровать» в каталоге В каталоге будут отображены доступные велосипеды с информацией о модели,
категории, статусе и других параметрах.
Для бронирования велосипеда нажмите кнопку "Бронировать" рядом с нужным велосипедом (рис. 7).

129
Рисунок 7 – Кнопка «Бронировать» в каталоге
Бронирование велосипедов
Добавьте велосипеды в бронирование, нажимая кнопку "Бронировать" в каталоге.
Перейдите на страницу бронирования, кликнув по ссылке "Перейти к бронированию"
в верхней навигации (рис. 8).
Рисунок 8 – Ссылка на переход к Бронированию Убедитесь, что выбранные велосипеды отображаются в списке.
Укажите дату и время начала и окончания аренды (рис. 9, 10).

130
Рисунок 9 – Выбор начала бронирования
Рисунок 10 – Выбор времени бронирования
Нажмите кнопку "Оформить бронирование".
После успешного оформления бронирования вы увидите сообщение о подтверждении.
Личный кабинет
Перейдите в личный кабинет, кликнув по ссылке "Личный кабинет" в верхней навигации (рис. 11).