Files
moto-adv-website/app/templates/community/profile_new.html
ske087 60ef02ced9 Major UI/UX redesign and feature enhancements
🎨 Complete Tailwind CSS conversion
- Redesigned post detail page with modern gradient backgrounds
- Updated profile page with consistent design language
- Converted from Bootstrap to Tailwind CSS throughout

 New Features & Improvements
- Enhanced community post management system
- Added admin panel with analytics dashboard
- Improved post creation and editing workflows
- Interactive GPS map integration with Leaflet.js
- Photo gallery with modal view and hover effects
- Adventure statistics and metadata display
- Like system and community engagement features

🔧 Technical Improvements
- Fixed template syntax errors and CSRF token issues
- Updated database models and relationships
- Enhanced media file management
- Improved responsive design patterns
- Added proper error handling and validation

📱 Mobile-First Design
- Responsive grid layouts
- Touch-friendly interactions
- Optimized for all screen sizes
- Modern card-based UI components

🏍️ Adventure Platform Features
- GPS track visualization and statistics
- Photo uploads with thumbnail generation
- GPX file downloads for registered users
- Community comments and discussions
- Post approval workflow for admins
- Difficulty rating system with star indicators
2025-07-24 02:44:25 +03:00

313 lines
16 KiB
HTML

{% extends "base.html" %}
{% block title %}My Profile - {{ current_user.nickname }}{% endblock %}
{% block content %}
<div class="container-fluid mt-4">
<!-- Header Section -->
<div class="row mb-4">
<div class="col-12">
<div class="card shadow-sm">
<div class="card-body">
<div class="row align-items-center">
<div class="col-md-8">
<h1 class="mb-3">
<i class="fas fa-user-circle text-primary"></i>
{{ current_user.nickname }}'s Profile
</h1>
<p class="text-muted mb-0">
<i class="fas fa-envelope"></i> {{ current_user.email }}
<i class="fas fa-calendar"></i> Member since {{ current_user.created_at.strftime('%B %Y') }}
</p>
</div>
<div class="col-md-4 text-md-end">
<a href="{{ url_for('community.new_post') }}" class="btn btn-primary btn-lg">
<i class="fas fa-plus"></i> Create New Adventure
</a>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Stats Section -->
<div class="row mb-4">
<div class="col-md-6">
<div class="card text-center border-success">
<div class="card-body">
<i class="fas fa-check-circle fa-3x text-success mb-3"></i>
<h3 class="text-success">{{ published_count }}</h3>
<p class="text-muted mb-0">Published Adventures</p>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card text-center border-warning">
<div class="card-body">
<i class="fas fa-clock fa-3x text-warning mb-3"></i>
<h3 class="text-warning">{{ pending_count }}</h3>
<p class="text-muted mb-0">Pending Review</p>
</div>
</div>
</div>
</div>
<!-- Posts Section -->
<div class="row">
<div class="col-12">
<div class="card shadow-sm">
<div class="card-header bg-dark text-white">
<h4 class="mb-0">
<i class="fas fa-list"></i> My Adventures
</h4>
</div>
<div class="card-body">
{% if posts.items %}
<div class="row">
{% for post in posts.items %}
<div class="col-lg-6 col-xl-4 mb-4">
<div class="card h-100 {{ 'border-success' if post.published else 'border-warning' }}">
<!-- Cover Image -->
{% set cover_image = post.images | selectattr('is_cover', 'equalto', True) | first %}
{% if cover_image %}
<div class="position-relative">
<img src="{{ cover_image.get_thumbnail_url() }}"
alt="{{ post.title }}"
class="card-img-top"
style="height: 200px; object-fit: cover;">
<div class="position-absolute top-0 end-0 m-2">
<span class="badge {{ 'bg-success' if post.published else 'bg-warning text-dark' }}">
{{ 'Published' if post.published else 'Pending Review' }}
</span>
</div>
</div>
{% else %}
<div class="card-img-top bg-gradient-primary d-flex align-items-center justify-content-center text-white" style="height: 200px;">
<div class="text-center">
<i class="fas fa-mountain fa-3x mb-2"></i>
<div class="position-absolute top-0 end-0 m-2">
<span class="badge {{ 'bg-success' if post.published else 'bg-warning text-dark' }}">
{{ 'Published' if post.published else 'Pending Review' }}
</span>
</div>
</div>
</div>
{% endif %}
<div class="card-body d-flex flex-column">
<!-- Title and Subtitle -->
<h5 class="card-title">{{ post.title }}</h5>
{% if post.subtitle %}
<p class="card-text text-muted small">{{ post.subtitle }}</p>
{% endif %}
<!-- Difficulty -->
<div class="mb-2">
<span class="badge bg-warning text-dark">
{{ '⭐' * post.difficulty }}
{% if post.difficulty == 1 %}Easy{% elif post.difficulty == 2 %}Moderate{% elif post.difficulty == 3 %}Challenging{% elif post.difficulty == 4 %}Hard{% else %}Expert{% endif %}
</span>
</div>
<!-- Content Preview -->
<p class="card-text flex-grow-1">
{{ (post.content[:100] + '...') if post.content|length > 100 else post.content }}
</p>
<!-- Meta Information -->
<div class="text-muted small mb-3">
<div>
<i class="fas fa-calendar"></i>
Created: {{ post.created_at.strftime('%Y-%m-%d') }}
</div>
{% if post.updated_at and post.updated_at != post.created_at %}
<div>
<i class="fas fa-edit"></i>
Updated: {{ post.updated_at.strftime('%Y-%m-%d') }}
</div>
{% endif %}
<div>
<i class="fas fa-images"></i>
{{ post.images.count() }} images
<i class="fas fa-route"></i>
{{ post.gpx_files.count() }} GPS tracks
</div>
</div>
<!-- Action Buttons -->
<div class="d-grid gap-2">
<div class="btn-group" role="group">
{% if post.published %}
<a href="{{ url_for('community.post_detail', id=post.id) }}"
class="btn btn-outline-primary btn-sm">
<i class="fas fa-eye"></i> View
</a>
{% endif %}
<a href="{{ url_for('community.edit_post', id=post.id) }}"
class="btn btn-outline-warning btn-sm">
<i class="fas fa-edit"></i> Edit
</a>
<button type="button" class="btn btn-outline-danger btn-sm"
onclick="confirmDelete({{ post.id }}, '{{ post.title|replace("'", "\\'") }}')">
<i class="fas fa-trash"></i> Delete
</button>
</div>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
<!-- Pagination -->
{% if posts.pages > 1 %}
<nav aria-label="Posts pagination" class="mt-4">
<ul class="pagination justify-content-center">
{% if posts.has_prev %}
<li class="page-item">
<a class="page-link" href="{{ url_for('community.profile', page=posts.prev_num) }}">
<i class="fas fa-chevron-left"></i> Previous
</a>
</li>
{% endif %}
{% for page_num in posts.iter_pages() %}
{% if page_num %}
{% if page_num != posts.page %}
<li class="page-item">
<a class="page-link" href="{{ url_for('community.profile', page=page_num) }}">{{ page_num }}</a>
</li>
{% else %}
<li class="page-item active">
<span class="page-link">{{ page_num }}</span>
</li>
{% endif %}
{% else %}
<li class="page-item disabled">
<span class="page-link">...</span>
</li>
{% endif %}
{% endfor %}
{% if posts.has_next %}
<li class="page-item">
<a class="page-link" href="{{ url_for('community.profile', page=posts.next_num) }}">
Next <i class="fas fa-chevron-right"></i>
</a>
</li>
{% endif %}
</ul>
</nav>
{% endif %}
{% else %}
<!-- No Posts State -->
<div class="text-center py-5">
<i class="fas fa-mountain fa-5x text-muted mb-4"></i>
<h3 class="text-muted">No Adventures Yet</h3>
<p class="text-muted mb-4">You haven't shared any motorcycle adventures yet. Start your journey!</p>
<a href="{{ url_for('community.new_post') }}" class="btn btn-primary btn-lg">
<i class="fas fa-plus"></i> Create Your First Adventure
</a>
</div>
{% endif %}
</div>
</div>
</div>
</div>
</div>
<!-- Delete Confirmation Modal -->
<div class="modal fade" id="deleteModal" tabindex="-1" aria-labelledby="deleteModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header bg-danger text-white">
<h5 class="modal-title" id="deleteModalLabel">
<i class="fas fa-exclamation-triangle"></i> Confirm Deletion
</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p>Are you sure you want to delete the adventure:</p>
<h6 id="deletePostTitle" class="text-danger"></h6>
<p class="text-muted small mb-0">
<i class="fas fa-exclamation-circle"></i>
This action cannot be undone. All images and GPS tracks will also be deleted.
</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
<i class="fas fa-times"></i> Cancel
</button>
<button type="button" class="btn btn-danger" id="confirmDeleteBtn">
<i class="fas fa-trash"></i> Yes, Delete Adventure
</button>
</div>
</div>
</div>
</div>
<script>
let deletePostId = null;
function confirmDelete(postId, postTitle) {
deletePostId = postId;
document.getElementById('deletePostTitle').textContent = postTitle;
const modal = new bootstrap.Modal(document.getElementById('deleteModal'));
modal.show();
}
document.getElementById('confirmDeleteBtn').addEventListener('click', function() {
if (deletePostId) {
const btn = this;
const originalText = btn.innerHTML;
// Show loading state
btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Deleting...';
btn.disabled = true;
// Send delete request
fetch(`/community/delete-post/${deletePostId}`, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json'
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
// Hide modal
const modal = bootstrap.Modal.getInstance(document.getElementById('deleteModal'));
modal.hide();
// Show success message and reload page
const alert = document.createElement('div');
alert.className = 'alert alert-success alert-dismissible fade show';
alert.innerHTML = `
<i class="fas fa-check-circle"></i> ${data.message}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
`;
document.querySelector('.container-fluid').insertBefore(alert, document.querySelector('.container-fluid').firstChild);
// Reload page after delay
setTimeout(() => {
window.location.reload();
}, 2000);
} else {
throw new Error(data.error || 'Failed to delete post');
}
})
.catch(error => {
console.error('Error:', error);
alert('An error occurred while deleting the post: ' + error.message);
})
.finally(() => {
// Restore button state
btn.innerHTML = originalText;
btn.disabled = false;
});
}
});
</script>
{% endblock %}