Files
tourrism_site/views/routes/booking.ejs
Andrey K. Choi 13c752b93a feat: Оптимизация навигации AdminJS в логические группы
- Объединены ресурсы в 5 логических групп: Контент сайта, Бронирования, Отзывы и рейтинги, Персонал и гиды, Администрирование
- Удалены дублирующие настройки navigation для чистой группировки
- Добавлены CSS стили для визуального отображения иерархии с отступами
- Добавлены эмодзи-иконки для каждого типа ресурсов через CSS
- Улучшена навигация с правильной вложенностью элементов
2025-11-30 21:57:58 +09:00

262 lines
13 KiB
Plaintext
Raw Permalink 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.

<!-- Hero Section -->
<section class="hero-section compact bg-primary text-white py-5">
<div class="container text-center">
<h1 class="display-4 fw-bold mb-3">Бронирование тура</h1>
<p class="lead">Выберите дату и гида для вашего незабываемого путешествия</p>
</div>
</section>
<div class="container py-5">
<div class="row">
<!-- Информация о маршруте -->
<div class="col-lg-8">
<div class="card mb-4">
<div class="card-header">
<h4 class="mb-0"><%= route.title %></h4>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-4">
<%
let placeholderImage = '/images/placeholder.jpg';
if (route.type === 'city') {
placeholderImage = '/images/city-tour-placeholder.webp';
} else if (route.type === 'mountain') {
placeholderImage = '/images/mountain-placeholder.jpg';
} else if (route.type === 'fishing') {
placeholderImage = '/images/fish-placeholder.jpg';
}
%>
<img src="<%= route.image_url || placeholderImage %>"
class="img-fluid rounded"
alt="<%= route.title %>">
</div>
<div class="col-md-8">
<p class="text-muted mb-3"><%= route.description %></p>
<div class="row g-3">
<div class="col-6">
<div class="d-flex align-items-center">
<i class="fas fa-clock text-primary me-2"></i>
<span><%= route.duration %> дней</span>
</div>
</div>
<div class="col-6">
<div class="d-flex align-items-center">
<i class="fas fa-users text-primary me-2"></i>
<span>До <%= route.max_group_size %> человек</span>
</div>
</div>
<div class="col-6">
<div class="d-flex align-items-center">
<i class="fas fa-star text-primary me-2"></i>
<span><%= route.difficulty_level === 'easy' ? 'Легкий' : route.difficulty_level === 'moderate' ? 'Средний' : 'Сложный' %></span>
</div>
</div>
<div class="col-6">
<div class="d-flex align-items-center">
<i class="fas fa-tag text-primary me-2"></i>
<span class="h5 text-primary mb-0">₩<%= formatCurrency(route.price) %></span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Выбор даты и проверка доступности -->
<div class="card mb-4">
<div class="card-header">
<h5 class="mb-0"><i class="fas fa-calendar-check me-2"></i>Проверка доступности</h5>
</div>
<div class="card-body">
<div id="route-availability-checker"></div>
</div>
</div>
<!-- Выбор гида -->
<div class="card mb-4" id="guide-selection-card" style="display: none;">
<div class="card-header">
<h5 class="mb-0"><i class="fas fa-user-tie me-2"></i>Выбор гида</h5>
</div>
<div class="card-body">
<div id="route-guide-selector"></div>
</div>
</div>
</div>
<!-- Форма бронирования -->
<div class="col-lg-4">
<div class="card sticky-top" style="top: 20px;">
<div class="card-header bg-primary text-white">
<h5 class="mb-0"><i class="fas fa-credit-card me-2"></i>Детали бронирования</h5>
</div>
<div class="card-body">
<form action="/bookings" method="POST" id="mainBookingForm">
<input type="hidden" name="route_id" value="<%= route.id %>">
<input type="hidden" name="guide_id" id="selectedGuideId">
<input type="hidden" name="preferred_date" id="selectedDate">
<!-- Выбранные детали -->
<div id="booking-summary" class="mb-4 p-3 bg-light rounded" style="display: none;">
<h6 class="fw-bold mb-2">Выбрано:</h6>
<div id="summary-content"></div>
</div>
<div class="mb-3">
<label for="people_count" class="form-label">Количество человек</label>
<input type="number" class="form-control" name="people_count" id="people_count"
min="1" max="<%= route.max_group_size %>" value="1" required>
</div>
<div class="mb-3">
<label for="customer_name" class="form-label">Ваше имя *</label>
<input type="text" class="form-control" name="customer_name" id="customer_name" required>
</div>
<div class="mb-3">
<label for="customer_email" class="form-label">Email *</label>
<input type="email" class="form-control" name="customer_email" id="customer_email" required>
</div>
<div class="mb-3">
<label for="customer_phone" class="form-label">Телефон *</label>
<input type="tel" class="form-control" name="customer_phone" id="customer_phone" required>
</div>
<div class="mb-3">
<label for="special_requirements" class="form-label">Особые пожелания</label>
<textarea class="form-control" name="special_requirements" id="special_requirements" rows="3"
placeholder="Любые специальные запросы или требования..."></textarea>
</div>
<!-- Итоговая стоимость -->
<div class="mb-4 p-3 bg-primary bg-opacity-10 rounded">
<div class="d-flex justify-content-between align-items-center">
<span class="fw-bold">Итого:</span>
<span class="h5 text-primary mb-0" id="total-price">₩<%= formatCurrency(route.price) %></span>
</div>
<small class="text-muted">За <span id="people-display">1</span> человека</small>
</div>
<div class="d-grid gap-2">
<button type="submit" class="btn btn-primary btn-lg" id="submitBookingBtn" disabled>
<i class="fas fa-credit-card me-2"></i>Забронировать тур
</button>
<a href="/routes/<%= route.id %>" class="btn btn-outline-secondary">
<i class="fas fa-arrow-left me-2"></i>Назад к описанию
</a>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const routePrice = <%= route.price %>;
const maxGroupSize = <%= route.max_group_size %>;
// Компонент проверки доступности
const availabilityChecker = new AvailabilityChecker({
container: document.getElementById('route-availability-checker'),
mode: 'detailed',
showSuggestions: true,
onAvailabilityCheck: function(result) {
if (result.availableGuides && result.availableGuides.length > 0) {
showGuideSelection(result.availableGuides, result.date);
} else {
hideGuideSelection();
}
}
});
// Функция показа секции выбора гида
function showGuideSelection(availableGuides, selectedDate) {
const guideCard = document.getElementById('guide-selection-card');
const guideSelectorContainer = document.getElementById('route-guide-selector');
guideCard.style.display = 'block';
const guideSelector = new GuideSelector({
container: guideSelectorContainer,
mode: 'booking',
showAvailability: false,
availableGuides: availableGuides,
selectedDate: selectedDate,
onGuideSelect: function(guide) {
updateBookingSummary(guide, selectedDate);
enableBookingForm(guide.id, selectedDate);
}
});
}
// Функция скрытия секции выбора гида
function hideGuideSelection() {
const guideCard = document.getElementById('guide-selection-card');
guideCard.style.display = 'none';
disableBookingForm();
}
// Обновление сводки бронирования
function updateBookingSummary(guide, date) {
const summaryContainer = document.getElementById('booking-summary');
const summaryContent = document.getElementById('summary-content');
summaryContent.innerHTML = `
<div class="mb-2">
<strong>Дата:</strong> ${formatDate(date)}
</div>
<div class="mb-2">
<strong>Гид:</strong> ${guide.name}
</div>
<div class="mb-2">
<strong>Специализация:</strong> ${guide.specialization || 'Универсальный'}
</div>
`;
summaryContainer.style.display = 'block';
}
// Активация формы бронирования
function enableBookingForm(guideId, date) {
document.getElementById('selectedGuideId').value = guideId;
document.getElementById('selectedDate').value = date;
document.getElementById('submitBookingBtn').disabled = false;
}
// Деактивация формы бронирования
function disableBookingForm() {
document.getElementById('selectedGuideId').value = '';
document.getElementById('selectedDate').value = '';
document.getElementById('submitBookingBtn').disabled = true;
document.getElementById('booking-summary').style.display = 'none';
}
// Обновление общей стоимости
const peopleCountInput = document.getElementById('people_count');
const totalPriceElement = document.getElementById('total-price');
const peopleDisplayElement = document.getElementById('people-display');
peopleCountInput.addEventListener('input', function() {
const peopleCount = parseInt(this.value) || 1;
const totalPrice = routePrice * peopleCount;
totalPriceElement.textContent = `₩${new Intl.NumberFormat('ru-RU').format(totalPrice)}`;
peopleDisplayElement.textContent = peopleCount;
});
// Функция форматирования даты
function formatDate(dateString) {
const date = new Date(dateString);
return date.toLocaleDateString('ru-RU', {
year: 'numeric',
month: 'long',
day: 'numeric'
});
}
});
</script>