Initial commit: Korea Tourism Agency website with AdminJS

- Full-stack Node.js/Express application with PostgreSQL
- Modern ES modules architecture
- AdminJS admin panel with Sequelize ORM
- Tourism routes, guides, articles, bookings management
- Responsive Bootstrap 5 frontend
- Docker containerization with docker-compose
- Complete database schema with migrations
- Authentication system for admin panel
- Dynamic placeholder images for tour categories
This commit is contained in:
2025-11-29 18:13:17 +09:00
commit 409e6c146b
53 changed files with 16195 additions and 0 deletions

283
views/index.ejs Normal file
View File

@@ -0,0 +1,283 @@
<!-- Hero Section -->
<section class="hero-section position-relative overflow-hidden">
<div class="hero-background"></div>
<div class="hero-overlay"></div>
<div class="container position-relative">
<div class="row align-items-center min-vh-100 py-5">
<div class="col-lg-6" data-aos="fade-right">
<h1 class="hero-title display-3 fw-bold text-white mb-4">
Откройте красоту
<span class="text-gradient">Кореи</span>
</h1>
<p class="hero-subtitle fs-5 text-white-50 mb-5">
Погрузитесь в аутентичную корейскую культуру, насладитесь захватывающими пейзажами и
незабываемыми приключениями с нашими экспертными местными гидами.
</p>
<div class="hero-buttons d-flex flex-wrap gap-3">
<a href="/routes" class="btn btn-primary btn-lg px-4 py-3 rounded-pill">
<i class="fas fa-route me-2"></i>Исследовать туры
</a>
<a href="/guides" class="btn btn-outline-light btn-lg px-4 py-3 rounded-pill">
<i class="fas fa-user-tie me-2"></i>Наши гиды
</a>
</div>
</div>
<div class="col-lg-6" data-aos="fade-left" data-aos-delay="200">
<div class="hero-image-container">
<img src="/images/korea-hero.jpg" alt="Красивая Корея" class="img-fluid rounded-4 shadow-lg">
<div class="floating-card position-absolute">
<div class="card border-0 shadow-lg">
<div class="card-body p-4">
<div class="d-flex align-items-center">
<div class="icon-circle bg-primary text-white me-3">
<i class="fas fa-users"></i>
</div>
<div>
<h6 class="fw-bold mb-0">1000+ довольных туристов</h6>
<small class="text-muted">Присоединяйтесь к нам</small>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Quick Search Section -->
<section class="search-section py-5 bg-light">
<div class="container">
<div class="row justify-content-center">
<div class="col-lg-8">
<div class="card border-0 shadow-lg" data-aos="fade-up">
<div class="card-body p-4">
<h4 class="text-center mb-4 fw-bold">Найдите своё идеальное корейское приключение</h4>
<form class="search-form">
<div class="row g-3">
<div class="col-md-4">
<select class="form-select form-select-lg" name="type">
<option value="">Тип тура</option>
<option value="city">Городские туры</option>
<option value="mountain">Горные походы</option>
<option value="fishing">Рыбалка</option>
</select>
</div>
<div class="col-md-4">
<input type="date" class="form-control form-control-lg" name="date">
</div>
<div class="col-md-4">
<button type="submit" class="btn btn-primary btn-lg w-100">
<i class="fas fa-search me-2"></i>Найти туры
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Featured Tours -->
<section class="featured-tours py-5">
<div class="container">
<div class="text-center mb-5" data-aos="fade-up">
<h2 class="section-title display-5 fw-bold mb-3">Рекомендуемые туры</h2>
<p class="section-subtitle fs-5 text-muted">Отобранные впечатления, которые показывают лучшее в Корее</p>
</div>
<div class="row g-4">
<% if (featuredRoutes && featuredRoutes.length > 0) { %>
<% featuredRoutes.forEach(function(route, index) { %>
<div class="col-lg-4 col-md-6" data-aos="fade-up" data-aos-delay="<%= index * 100 %>">
<div class="tour-card card border-0 shadow-lg h-100 overflow-hidden">
<div class="position-relative">
<%
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 && route.image_url.trim()) ? route.image_url : placeholderImage %>"
alt="<%= route.title %>"
class="card-img-top tour-image">
<div class="tour-overlay position-absolute top-0 start-0 w-100 h-100 d-flex align-items-end">
<div class="tour-price p-3">
<span class="badge bg-primary fs-6 px-3 py-2">
<%= formatCurrency(route.price) %>
</span>
</div>
</div>
<div class="tour-type position-absolute top-0 end-0 m-3">
<% if (route.type === 'city') { %>
<span class="badge bg-info"><i class="fas fa-city me-1"></i>Город</span>
<% } else if (route.type === 'mountain') { %>
<span class="badge bg-success"><i class="fas fa-mountain me-1"></i>Горы</span>
<% } else if (route.type === 'fishing') { %>
<span class="badge bg-warning"><i class="fas fa-fish me-1"></i>Рыбалка</span>
<% } %>
</div>
</div>
<div class="card-body p-4">
<h5 class="card-title fw-bold mb-3">
<a href="/routes/<%= route.id %>" class="text-decoration-none text-dark">
<%= route.title %>
</a>
</h5>
<p class="card-text text-muted mb-3">
<%= truncateText(route.description, 100) %>
</p>
<div class="tour-meta d-flex justify-content-between align-items-center mb-3">
<span class="text-muted">
<i class="fas fa-clock me-1"></i><%= route.duration %>h
</span>
<span class="text-muted">
<i class="fas fa-users me-1"></i>Max 10 people
</span>
</div>
<a href="/routes/<%= route.id %>" class="btn btn-outline-primary w-100">
View Details
</a>
</div>
</div>
</div>
<% }); %>
<% } else { %>
<div class="col-12 text-center py-5">
<p class="text-muted fs-5">No featured tours available at the moment.</p>
<a href="/routes" class="btn btn-primary">Посмотреть все туры</a>
</div>
<% } %>
</div>
<div class="text-center mt-5" data-aos="fade-up">
<a href="/routes" class="btn btn-primary btn-lg px-5 py-3 rounded-pill">
<i class="fas fa-eye me-2"></i>Посмотреть все туры
</a>
</div>
</div>
</section>
<!-- Guide Stats -->
<section class="stats-section py-5 bg-gradient-primary text-white">
<div class="container">
<div class="row text-center g-4">
<div class="col-lg-3 col-md-6" data-aos="fade-up" data-aos-delay="0">
<div class="stat-item">
<div class="stat-icon mb-3">
<i class="fas fa-route display-4"></i>
</div>
<h3 class="stat-number fw-bold display-5 mb-2">50+</h3>
<p class="stat-label fs-5">Unique Tours</p>
</div>
</div>
<div class="col-lg-3 col-md-6" data-aos="fade-up" data-aos-delay="100">
<div class="stat-item">
<div class="stat-icon mb-3">
<i class="fas fa-user-tie display-4"></i>
</div>
<h3 class="stat-number fw-bold display-5 mb-2"><%= guideStats.total_guides || 0 %></h3>
<p class="stat-label fs-5">Экспертные гиды</p>
</div>
</div>
<div class="col-lg-3 col-md-6" data-aos="fade-up" data-aos-delay="200">
<div class="stat-item">
<div class="stat-icon mb-3">
<i class="fas fa-users display-4"></i>
</div>
<h3 class="stat-number fw-bold display-5 mb-2">1000+</h3>
<p class="stat-label fs-5">Happy Travelers</p>
</div>
</div>
<div class="col-lg-3 col-md-6" data-aos="fade-up" data-aos-delay="300">
<div class="stat-item">
<div class="stat-icon mb-3">
<i class="fas fa-star display-4"></i>
</div>
<h3 class="stat-number fw-bold display-5 mb-2">4.9</h3>
<p class="stat-label fs-5">Average Rating</p>
</div>
</div>
</div>
</div>
</section>
<!-- Recent Articles -->
<section class="articles-section py-5">
<div class="container">
<div class="text-center mb-5" data-aos="fade-up">
<h2 class="section-title display-5 fw-bold mb-3">Полезная информация о путешествиях</h2>
<p class="section-subtitle fs-5 text-muted">Tips, guides, and stories from Korea</p>
</div>
<div class="row g-4">
<% if (recentArticles && recentArticles.length > 0) { %>
<% recentArticles.forEach(function(article, index) { %>
<div class="col-lg-4 col-md-6" data-aos="fade-up" data-aos-delay="<%= index * 100 %>">
<article class="article-card card border-0 shadow-lg h-100">
<img src="<%= (article.image_url && article.image_url.trim()) ? article.image_url : '/images/placeholder.jpg' %>"
alt="<%= article.title %>"
class="card-img-top article-image">
<div class="card-body p-4">
<h5 class="card-title fw-bold mb-3">
<a href="/articles/<%= article.id %>" class="text-decoration-none text-dark">
<%= article.title %>
</a>
</h5>
<p class="card-text text-muted mb-3">
<%= truncateText(article.excerpt, 120) %>
</p>
<div class="article-meta d-flex justify-content-between align-items-center">
<small class="text-muted">
<i class="fas fa-calendar me-1"></i>
<%= formatDate(article.created_at) %>
</small>
<a href="/articles/<%= article.id %>" class="btn btn-sm btn-outline-primary">
Подробнее
</a>
</div>
</div>
</article>
</div>
<% }); %>
<% } else { %>
<div class="col-12 text-center py-5">
<p class="text-muted fs-5">No articles available at the moment.</p>
<a href="/articles" class="btn btn-primary">Browse All Articles</a>
</div>
<% } %>
</div>
<div class="text-center mt-5" data-aos="fade-up">
<a href="/articles" class="btn btn-outline-primary btn-lg px-5 py-3 rounded-pill">
<i class="fas fa-newspaper me-2"></i>Читать больше статей
</a>
</div>
</div>
</section>
<!-- CTA Section -->
<section class="cta-section py-5 bg-dark text-white">
<div class="container">
<div class="row align-items-center">
<div class="col-lg-8" data-aos="fade-right">
<h2 class="display-5 fw-bold mb-3">Готовы к вашему корейскому приключению?</h2>
<p class="fs-5 text-white-50 mb-0">
Join thousands of travelers who have discovered the magic of Korea with our expert guides.
</p>
</div>
<div class="col-lg-4 text-lg-end" data-aos="fade-left">
<a href="/contact" class="btn btn-primary btn-lg px-5 py-3 rounded-pill">
<i class="fas fa-envelope me-2"></i>Get in Touch
</a>
</div>
</div>
</div>
</section>