381 lines
18 KiB
HTML
381 lines
18 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}Motorcycle Adventures Romania{% endblock %}
|
|
|
|
{% block head %}
|
|
<style>
|
|
/* 3-row grid card layout */
|
|
.map-card {
|
|
display: flex;
|
|
flex-direction: column;
|
|
min-height: 800px;
|
|
background: rgba(255,255,255,0.07);
|
|
border-radius: 1.5rem;
|
|
box-shadow: 0 25px 50px -12px rgba(0,0,0,0.25);
|
|
border: 2px solid #10b981;
|
|
padding: 0;
|
|
overflow: hidden;
|
|
}
|
|
.map-card-square {
|
|
width: 100%;
|
|
max-width: 700px;
|
|
aspect-ratio: 1 / 1;
|
|
margin: 0 auto;
|
|
background: rgba(255,255,255,0.07);
|
|
border-radius: 1.5rem;
|
|
box-shadow: 0 25px 50px -12px rgba(0,0,0,0.25);
|
|
border: 2px solid #10b981;
|
|
display: flex;
|
|
flex-direction: column;
|
|
overflow: hidden;
|
|
}
|
|
.map-card-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 0.75rem 1.25rem 0.5rem 1.25rem;
|
|
}
|
|
.map-card-title {
|
|
font-size: 1.1rem;
|
|
font-weight: bold;
|
|
color: #fff;
|
|
}
|
|
.map-card-count {
|
|
font-size: 0.95rem;
|
|
color: #a5b4fc;
|
|
text-align: right;
|
|
}
|
|
.map-card-content {
|
|
flex: 1 1 0%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
min-height: 0;
|
|
min-width: 0;
|
|
padding: 0;
|
|
}
|
|
.map-iframe-container {
|
|
width: 100%;
|
|
height: 100%;
|
|
aspect-ratio: 1 / 1;
|
|
background: #f0f0f0;
|
|
border-radius: 1rem;
|
|
box-shadow: 0 25px 50px -12px rgba(0,0,0,0.25);
|
|
border: 2px solid #10b981;
|
|
position: relative;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
margin: 0 auto;
|
|
overflow: hidden;
|
|
}
|
|
.map-iframe {
|
|
width: 100%;
|
|
height: 100%;
|
|
aspect-ratio: 1 / 1;
|
|
border: none;
|
|
display: block;
|
|
border-radius: 1rem;
|
|
background: #f0f0f0;
|
|
}
|
|
/* Loading state for iframe */
|
|
.iframe-loading {
|
|
position: absolute;
|
|
top: 50%;
|
|
left: 50%;
|
|
transform: translate(-50%, -50%);
|
|
z-index: 10;
|
|
background: rgba(255, 255, 255, 0.95);
|
|
padding: 20px;
|
|
border-radius: 12px;
|
|
text-align: center;
|
|
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
|
|
border: 1px solid rgba(255,255,255,0.2);
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: 12px;
|
|
min-width: 200px;
|
|
transition: opacity 0.3s ease;
|
|
}
|
|
|
|
.iframe-loading.hidden {
|
|
opacity: 0;
|
|
pointer-events: none;
|
|
}
|
|
.map-card-footer {
|
|
padding: 0.5rem 1.25rem 0.75rem 1.25rem;
|
|
background: transparent;
|
|
text-align: center;
|
|
}
|
|
@media (max-width: 900px) {
|
|
.map-iframe-container, .map-iframe {
|
|
height: 40vh;
|
|
min-height: 200px;
|
|
}
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="min-h-screen bg-gradient-to-br from-blue-900 via-purple-900 to-teal-900">
|
|
<!-- Header Section -->
|
|
<div class="relative overflow-hidden">
|
|
<div class="absolute inset-0 bg-black/20"></div>
|
|
<div class="relative max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 pt-2 pb-2">
|
|
<div class="text-center mb-2">
|
|
<h1 class="text-2xl md:text-3xl font-bold text-white mb-1">
|
|
🏍️ Motorcycle Adventures Romania
|
|
</h1>
|
|
<p class="text-base text-blue-100 max-w-2xl mx-auto">
|
|
Discover epic motorcycle routes, share your adventures, and connect with fellow riders across Romania's stunning landscapes.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Interactive Map Section (Wide Rectangular Card) -->
|
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 mb-12">
|
|
<div class="w-11/12 md:w-10/12 lg:w-5/6 xl:w-11/12 mx-auto bg-white/10 backdrop-blur-sm rounded-2xl p-4 border border-white/20 flex flex-col" style="">
|
|
<div class="flex justify-between items-center mb-2">
|
|
<h2 class="text-xl font-bold text-white">🗺️ Interactive Route Map</h2>
|
|
<div class="text-sm text-blue-200">
|
|
<span id="route-count">{{ posts_with_routes|length }}</span> routes discovered
|
|
</div>
|
|
</div>
|
|
<div class="flex-1 flex items-center justify-center">
|
|
<div class="w-full aspect-[10/7] rounded-xl overflow-hidden border-2 border-emerald-500 bg-gray-100 relative">
|
|
<!-- Loading indicator -->
|
|
<div id="iframe-loading" class="absolute inset-0 flex flex-col items-center justify-center bg-white/80 z-10">
|
|
<div class="animate-spin rounded-full h-5 w-5 border-b-2 border-orange-600 mb-2"></div>
|
|
<span class="text-gray-700 text-sm">Loading interactive map...</span>
|
|
</div>
|
|
<iframe
|
|
id="map-iframe"
|
|
src="{{ url_for('static', filename='map_iframe.html') }}"
|
|
class="w-full h-full border-0 rounded-xl bg-gray-100 aspect-[10/7]"
|
|
title="Adventure Routes Map"
|
|
onload="hideIframeLoading()">
|
|
</iframe>
|
|
</div>
|
|
</div>
|
|
<p class="text-blue-200 text-xs mt-2 text-center">
|
|
Click on any route to view the adventure story • Routes are updated live as new trips are shared
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Action Buttons -->
|
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 mb-8">
|
|
<div class="flex flex-wrap justify-center gap-4">
|
|
{% if current_user.is_authenticated %}
|
|
<a href="{{ url_for('community.new_post') }}"
|
|
class="inline-flex items-center px-6 py-3 border border-transparent text-base font-medium rounded-lg text-white bg-gradient-to-r from-orange-500 to-red-600 hover:from-orange-600 hover:to-red-700 transform hover:scale-105 transition-all duration-200 shadow-lg">
|
|
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"></path>
|
|
</svg>
|
|
Share Your Adventure
|
|
</a>
|
|
<a href="{{ url_for('auth.logout') }}"
|
|
class="inline-flex items-center px-6 py-3 border border-white/30 text-base font-medium rounded-lg text-white hover:bg-white/10 transition-all duration-200">
|
|
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1"></path>
|
|
</svg>
|
|
Logout
|
|
</a>
|
|
{% else %}
|
|
<a href="{{ url_for('auth.register') }}"
|
|
class="inline-flex items-center px-6 py-3 border border-transparent text-base font-medium rounded-lg text-white bg-gradient-to-r from-orange-500 to-red-600 hover:from-orange-600 hover:to-red-700 transform hover:scale-105 transition-all duration-200 shadow-lg">
|
|
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M18 9v3m0 0v3m0-3h3m-3 0h-3m-2-5a4 4 0 11-8 0 4 4 0 018 0zM3 20a6 6 0 0112 0v1H3v-1z"></path>
|
|
</svg>
|
|
Join Community
|
|
</a>
|
|
<a href="{{ url_for('auth.login') }}"
|
|
class="inline-flex items-center px-6 py-3 border border-white/30 text-base font-medium rounded-lg text-white hover:bg-white/10 transition-all duration-200">
|
|
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 16l-4-4m0 0l4-4m-4 4h14m-5 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h7a3 3 0 013 3v1"></path>
|
|
</svg>
|
|
Login
|
|
</a>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Latest Adventures Grid -->
|
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
|
<div class="flex justify-between items-center mb-8">
|
|
<h2 class="text-3xl font-bold text-white">Latest Adventures</h2>
|
|
<div class="text-blue-200 text-sm">
|
|
Showing {{ posts.items|length }} of {{ posts.total }} stories
|
|
</div>
|
|
</div>
|
|
|
|
{% if posts.items %}
|
|
<div class="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
|
|
{% for post in posts.items %}
|
|
<article class="bg-white/10 backdrop-blur-sm rounded-xl overflow-hidden shadow-xl hover:shadow-2xl transition-all duration-300 border border-white/20 hover:border-white/40 transform hover:-translate-y-1">
|
|
{% set cover_image = post.images.filter_by(is_cover=True).first() %}
|
|
{% if cover_image %}
|
|
<div class="aspect-w-16 aspect-h-9 bg-gray-900">
|
|
<img src="{{ cover_image.get_url() }}"
|
|
alt="{{ post.title }}"
|
|
class="w-full h-48 object-cover">
|
|
</div>
|
|
{% elif post.images %}
|
|
<div class="aspect-w-16 aspect-h-9 bg-gray-900">
|
|
<img src="{{ post.images[0].get_url() }}"
|
|
alt="{{ post.title }}"
|
|
class="w-full h-48 object-cover">
|
|
</div>
|
|
{% endif %}
|
|
|
|
<div class="p-5">
|
|
<div class="flex items-center mb-3">
|
|
<div class="flex-shrink-0">
|
|
<div class="w-8 h-8 bg-gradient-to-r from-orange-500 to-red-600 rounded-full flex items-center justify-center">
|
|
<span class="text-white font-bold text-xs">{{ post.author.nickname[0].upper() }}</span>
|
|
</div>
|
|
</div>
|
|
<div class="ml-2">
|
|
<p class="text-sm font-medium text-white">{{ post.author.nickname }}</p>
|
|
<p class="text-xs text-blue-200">{{ post.created_at.strftime('%b %d, %Y') }}</p>
|
|
</div>
|
|
</div>
|
|
|
|
<h3 class="text-lg font-bold text-white mb-2 line-clamp-2">
|
|
<a href="{{ url_for('community.post_detail', id=post.id) }}"
|
|
class="hover:text-orange-400 transition-colors">
|
|
{{ post.title }}
|
|
</a>
|
|
</h3>
|
|
|
|
{% if post.subtitle %}
|
|
<p class="text-sm text-blue-200 mb-2 line-clamp-1">{{ post.subtitle }}</p>
|
|
{% endif %}
|
|
|
|
<p class="text-blue-100 text-sm mb-4 line-clamp-2">
|
|
{{ post.content[:120] }}{% if post.content|length > 120 %}...{% endif %}
|
|
</p>
|
|
|
|
<div class="flex items-center justify-between">
|
|
<div class="flex items-center space-x-3 text-xs text-blue-200">
|
|
{% if post.gpx_files %}
|
|
<span class="flex items-center bg-green-500/20 px-2 py-1 rounded-full">
|
|
<svg class="w-3 h-3 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z"></path>
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 11a3 3 0 11-6 0 3 3 0 016 0z"></path>
|
|
</svg>
|
|
GPX
|
|
</span>
|
|
{% endif %}
|
|
<span class="flex items-center">
|
|
<svg class="w-3 h-3 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"></path>
|
|
</svg>
|
|
{{ post.comments.count() }}
|
|
</span>
|
|
<span class="flex items-center">
|
|
<svg class="w-3 h-3 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z"></path>
|
|
</svg>
|
|
{{ post.likes.count() }}
|
|
</span>
|
|
</div>
|
|
|
|
<a href="{{ url_for('community.post_detail', id=post.id) }}"
|
|
class="text-orange-400 hover:text-orange-300 font-medium text-xs transition-colors">
|
|
Read More →
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</article>
|
|
{% endfor %}
|
|
</div>
|
|
|
|
<!-- Pagination -->
|
|
{% if posts.pages > 1 %}
|
|
<div class="mt-12 flex justify-center">
|
|
<nav class="flex items-center space-x-2">
|
|
{% if posts.has_prev %}
|
|
<a href="{{ url_for('community.index', page=posts.prev_num) }}"
|
|
class="px-4 py-2 text-white bg-white/10 backdrop-blur-sm rounded-lg hover:bg-white/20 transition-colors">
|
|
← Previous
|
|
</a>
|
|
{% endif %}
|
|
|
|
{% for page_num in posts.iter_pages() %}
|
|
{% if page_num %}
|
|
{% if page_num != posts.page %}
|
|
<a href="{{ url_for('community.index', page=page_num) }}"
|
|
class="px-3 py-2 text-white bg-white/10 backdrop-blur-sm rounded-lg hover:bg-white/20 transition-colors">
|
|
{{ page_num }}
|
|
</a>
|
|
{% else %}
|
|
<span class="px-3 py-2 text-white bg-orange-500 rounded-lg">{{ page_num }}</span>
|
|
{% endif %}
|
|
{% else %}
|
|
<span class="px-3 py-2 text-blue-200">…</span>
|
|
{% endif %}
|
|
{% endfor %}
|
|
|
|
{% if posts.has_next %}
|
|
<a href="{{ url_for('community.index', page=posts.next_num) }}"
|
|
class="px-4 py-2 text-white bg-white/10 backdrop-blur-sm rounded-lg hover:bg-white/20 transition-colors">
|
|
Next →
|
|
</a>
|
|
{% endif %}
|
|
</nav>
|
|
</div>
|
|
{% endif %}
|
|
{% else %}
|
|
<!-- Empty State -->
|
|
<div class="text-center py-16">
|
|
<div class="max-w-md mx-auto">
|
|
<svg class="w-16 h-16 text-blue-300 mx-auto mb-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 20H5a2 2 0 01-2-2V6a2 2 0 012-2h10a2 2 0 012 2v1m2 13a2 2 0 01-2-2V7m2 13a2 2 0 002-2V9.5a2 2 0 00-2-2h-2m-4-3H9M7 16h6M7 8h6v4H7V8z"></path>
|
|
</svg>
|
|
<h3 class="text-2xl font-bold text-white mb-4">No Adventures Yet</h3>
|
|
<p class="text-blue-200 mb-6">
|
|
Be the first to share your motorcycle adventure and map your route across Romania!
|
|
</p>
|
|
{% if current_user.is_authenticated %}
|
|
<a href="{{ url_for('community.new_post') }}"
|
|
class="inline-flex items-center px-6 py-3 border border-transparent text-base font-medium rounded-lg text-white bg-gradient-to-r from-orange-500 to-red-600 hover:from-orange-600 hover:to-red-700 transform hover:scale-105 transition-all duration-200">
|
|
Share Your First Adventure
|
|
</a>
|
|
{% else %}
|
|
<a href="{{ url_for('auth.register') }}"
|
|
class="inline-flex items-center px-6 py-3 border border-transparent text-base font-medium rounded-lg text-white bg-gradient-to-r from-orange-500 to-red-600 hover:from-orange-600 hover:to-red-700 transform hover:scale-105 transition-all duration-200">
|
|
Join to Share Adventures
|
|
</a>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
// Function to hide iframe loading indicator
|
|
function hideIframeLoading() {
|
|
const loadingEl = document.getElementById('iframe-loading');
|
|
if (loadingEl) {
|
|
loadingEl.classList.add('hidden');
|
|
setTimeout(() => {
|
|
loadingEl.style.display = 'none';
|
|
}, 300);
|
|
}
|
|
}
|
|
|
|
// Fallback to hide loading after 5 seconds if iframe doesn't trigger onload
|
|
setTimeout(() => {
|
|
const loadingEl = document.getElementById('iframe-loading');
|
|
if (loadingEl && !loadingEl.classList.contains('hidden')) {
|
|
console.log('Iframe loading timeout, hiding loading indicator');
|
|
hideIframeLoading();
|
|
}
|
|
}, 5000);
|
|
</script>
|
|
{% endblock %}
|