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

219 lines
11 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-gradient-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>
<!-- Guide Availability Calendar -->
<section class="py-4 bg-light">
<div class="container">
<div class="row">
<div class="col-lg-8 mx-auto">
<div class="card">
<div class="card-header text-center">
<h5 class="mb-0"><i class="fas fa-calendar-alt me-2"></i>Календарь доступности гидов</h5>
</div>
<div class="card-body">
<div id="guides-calendar-container"></div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Guides Grid -->
<section class="py-5">
<div class="container">
<% if (guides.length === 0) { %>
<div class="text-center py-5">
<i class="fas fa-user-tie fa-3x text-muted mb-3"></i>
<h3>Гиды не найдены</h3>
<p class="text-muted">В данный момент нет доступных гидов.</p>
</div>
<% } else { %>
<div class="row">
<% guides.forEach(function(guide) { %>
<div class="col-lg-4 col-md-6 mb-4">
<div class="card h-100 shadow-sm guide-card" data-guide-id="<%= guide.id %>" style="cursor: pointer; transition: transform 0.2s;">
<% if (guide.image_url && guide.image_url.trim()) { %>
<img src="<%= guide.image_url %>" class="card-img-top" alt="<%= guide.name %>" style="height: 250px; object-fit: cover;">
<% } else { %>
<img src="/images/placeholder.jpg" class="card-img-top" alt="<%= guide.name %>" style="height: 250px; object-fit: cover;">
<% } %>
<div class="card-body d-flex flex-column">
<div class="d-flex justify-content-between align-items-start mb-2">
<h5 class="card-title mb-0"><%= guide.name %></h5>
<div class="rating-display" data-target-type="guide" data-target-id="<%= guide.id %>">
<div class="d-flex align-items-center">
<span class="rating-percentage text-warning fw-bold">
<%
let percentage = parseFloat(guide.rating_percentage || 0);
%>
<%= Math.round(percentage) %>%
</span>
<small class="text-muted ms-1">
<%
let likes = parseInt(guide.likes || 0);
let dislikes = parseInt(guide.dislikes || 0);
let total = likes + dislikes;
%>
(<%= likes %>/<%= total %>)
</small>
</div>
<div class="rating-buttons mt-1">
<button class="btn btn-sm btn-outline-success like-btn" data-rating="1">
<i class="fas fa-thumbs-up"></i>
</button>
<button class="btn btn-sm btn-outline-danger dislike-btn" data-rating="-1">
<i class="fas fa-thumbs-down"></i>
</button>
</div>
</div>
</div>
<div class="mb-2">
<% if (guide.specialization === 'city') { %>
<span class="badge bg-primary">
<i class="fas fa-city me-1"></i>Городские туры
</span>
<% } else if (guide.specialization === 'mountain') { %>
<span class="badge bg-success">
<i class="fas fa-mountain me-1"></i>Горные походы
</span>
<% } else if (guide.specialization === 'fishing') { %>
<span class="badge bg-info">
<i class="fas fa-fish me-1"></i>Морская рыбалка
</span>
<% } %>
</div>
<div class="mb-2">
<small class="text-muted">
<i class="fas fa-briefcase me-1"></i>
Опыт: <%= guide.experience || 0 %> лет
</small>
</div>
<% if (guide.bio) { %>
<p class="card-text text-muted flex-grow-1"><%= guide.bio.length > 100 ? guide.bio.substring(0, 100) + '...' : guide.bio %></p>
<% } %>
<div class="mt-auto">
<% if (guide.languages) { %>
<div class="mb-2">
<small class="text-muted">
<i class="fas fa-language me-1"></i>
<%= guide.languages %>
</small>
</div>
<% } %>
<div class="d-flex justify-content-between align-items-center">
<div class="text-muted small">
<i class="fas fa-calendar-check me-1"></i>
<%= guide.booking_count || 0 %> туров проведено
</div>
<% if (guide.hourly_rate) { %>
<div class="text-end">
<strong class="text-success">
₩<%= parseInt(guide.hourly_rate || 0).toLocaleString('ko-KR') %>/час
</strong>
</div>
<% } %>
</div>
</div>
</div>
</div>
</div>
<% }); %>
</div>
<% } %>
</div>
</section>
<script>
// Guide card click handler and rating system
document.addEventListener('DOMContentLoaded', function() {
const guideCards = document.querySelectorAll('.guide-card');
guideCards.forEach(card => {
card.addEventListener('click', function(e) {
// Don't trigger card click if clicking on rating buttons
if (!e.target.closest('.rating-buttons')) {
const guideId = this.dataset.guideId;
// Redirect to guide details or show modal
window.location.href = `/guides/${guideId}`;
}
});
// Add hover effect
card.addEventListener('mouseenter', function() {
this.style.transform = 'translateY(-5px)';
});
card.addEventListener('mouseleave', function() {
this.style.transform = 'translateY(0)';
});
});
// Rating system
document.addEventListener('click', function(e) {
if (e.target.closest('.like-btn') || e.target.closest('.dislike-btn')) {
e.preventDefault();
e.stopPropagation();
const button = e.target.closest('.like-btn') || e.target.closest('.dislike-btn');
const ratingDisplay = button.closest('.rating-display');
const targetType = ratingDisplay.dataset.targetType;
const targetId = ratingDisplay.dataset.targetId;
const rating = parseInt(button.dataset.rating);
// Send rating to server
fetch('/api/rating/' + targetType + '/' + targetId, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ rating: rating })
})
.then(response => response.json())
.then(data => {
if (data.success) {
// Update rating display
const percentageSpan = ratingDisplay.querySelector('.rating-percentage');
const countSpan = ratingDisplay.querySelector('small');
const percentage = data.percentage || 0;
const likes = data.likes || 0;
const total = data.total || 0;
percentageSpan.textContent = Math.round(percentage) + '%';
countSpan.textContent = `(${likes}/${total})`;
// Update button states
ratingDisplay.querySelectorAll('.rating-buttons button').forEach(btn => {
btn.classList.remove('btn-success', 'btn-danger');
btn.classList.add(btn.classList.contains('like-btn') ? 'btn-outline-success' : 'btn-outline-danger');
});
// Highlight selected button
if (rating === 1) {
button.classList.remove('btn-outline-success');
button.classList.add('btn-success');
} else if (rating === -1) {
button.classList.remove('btn-outline-danger');
button.classList.add('btn-danger');
}
}
})
.catch(error => {
console.error('Ошибка при отправке рейтинга:', error);
});
}
});
});
</script>