Features Added: 🔥 Modern Chat System: - Real-time messaging with modern Tailwind CSS design - Post-linked discussions for adventure sharing - Chat categories (general, technical-support, adventure-planning) - Mobile-responsive interface with gradient backgrounds - JavaScript polling for live message updates 🎯 Comprehensive Admin Panel: - Chat room management with merge capabilities - Password reset system with email templates - User management with admin controls - Chat statistics and analytics dashboard - Room binding to posts and categorization �� Mobile API Integration: - RESTful API endpoints at /api/v1/chat - Session-based authentication for mobile apps - Comprehensive endpoints for rooms, messages, users - Mobile app compatibility (React Native, Flutter) 🛠️ Technical Improvements: - Enhanced database models with ChatRoom categories - Password reset token system with email verification - Template synchronization fixes for Docker deployment - Migration scripts for database schema updates - Improved error handling and validation 🎨 UI/UX Enhancements: - Modern card-based layouts matching app design - Consistent styling across chat and admin interfaces - Mobile-optimized touch interactions - Professional gradient designs and glass morphism effects 📚 Documentation: - Updated README with comprehensive API documentation - Added deployment instructions for Docker (port 8100) - Configuration guide for production environments - Mobile integration examples and endpoints This update transforms the platform into a comprehensive motorcycle adventure community with modern chat capabilities and professional admin management tools.
244 lines
12 KiB
HTML
244 lines
12 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}Create Chat Room{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="min-h-screen bg-gradient-to-br from-blue-900 via-purple-900 to-teal-900 pt-16">
|
|
<!-- Header Section -->
|
|
<div class="relative overflow-hidden py-12">
|
|
<div class="absolute inset-0 bg-black/20"></div>
|
|
<div class="relative max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 text-center">
|
|
<div class="bg-white/10 backdrop-blur-sm rounded-2xl p-6 border border-white/20">
|
|
<h1 class="text-3xl font-bold text-white mb-2">
|
|
<i class="fas fa-plus-circle mr-3"></i>Create New Chat Room
|
|
</h1>
|
|
<p class="text-blue-200">Start a discussion with the motorcycle community</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 pb-12 -mt-6">
|
|
<div class="bg-white rounded-2xl shadow-xl overflow-hidden">
|
|
<div class="bg-gradient-to-r from-green-600 to-emerald-600 p-6">
|
|
<div class="flex items-center justify-between text-white">
|
|
<div class="flex items-center">
|
|
<i class="fas fa-comments text-2xl mr-3"></i>
|
|
<div>
|
|
<h2 class="text-xl font-bold">Room Configuration</h2>
|
|
<p class="text-green-100 text-sm">Set up your chat room details</p>
|
|
</div>
|
|
</div>
|
|
<a href="{{ url_for('chat.index') }}" class="bg-white/20 hover:bg-white/30 px-4 py-2 rounded-lg transition-all duration-200">
|
|
<i class="fas fa-arrow-left mr-2"></i>Back to Chat
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="p-8">
|
|
<form method="POST" action="{{ url_for('chat.create_room') }}" class="space-y-6">
|
|
<!-- Room Name -->
|
|
<div class="space-y-2">
|
|
<label for="room_name" class="block text-sm font-semibold text-gray-700">
|
|
<i class="fas fa-tag mr-2 text-green-600"></i>Room Name *
|
|
</label>
|
|
<input type="text" class="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-green-500 focus:border-transparent transition-all duration-200"
|
|
id="room_name" name="room_name"
|
|
placeholder="Enter a descriptive room name" required maxlength="100">
|
|
<p class="text-xs text-gray-500">Choose a clear, descriptive name for your chat room</p>
|
|
</div>
|
|
|
|
<!-- Description -->
|
|
<div class="space-y-2">
|
|
<label for="description" class="block text-sm font-semibold text-gray-700">
|
|
<i class="fas fa-align-left mr-2 text-green-600"></i>Description
|
|
</label>
|
|
<textarea class="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-green-500 focus:border-transparent transition-all duration-200"
|
|
id="description" name="description" rows="3"
|
|
placeholder="Describe what this room is about..." maxlength="500"></textarea>
|
|
<p class="text-xs text-gray-500">Optional: Help others understand the room's purpose</p>
|
|
</div>
|
|
|
|
<!-- Post Binding Section -->
|
|
<div class="space-y-4 p-6 bg-blue-50 rounded-xl border border-blue-200">
|
|
<div class="flex items-center">
|
|
<i class="fas fa-link mr-3 text-blue-600 text-lg"></i>
|
|
<h3 class="text-lg font-semibold text-gray-800">Link to Post (Optional)</h3>
|
|
</div>
|
|
|
|
<div class="space-y-2">
|
|
<label for="related_post_id" class="block text-sm font-semibold text-gray-700">
|
|
Select a post to discuss
|
|
</label>
|
|
<select class="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all duration-200"
|
|
id="related_post_id" name="related_post_id">
|
|
<option value="">No specific post - General discussion</option>
|
|
{% for post in posts %}
|
|
<option value="{{ post.id }}" {% if pre_selected_post and post.id == pre_selected_post %}selected{% endif %}>
|
|
{{ post.title }} - by {{ post.author.nickname }}
|
|
</option>
|
|
{% endfor %}
|
|
</select>
|
|
<p class="text-xs text-gray-500">
|
|
<i class="fas fa-info-circle mr-1"></i>
|
|
Link this room to a specific post for focused discussions
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Room Type -->
|
|
<div class="space-y-2">
|
|
<label for="room_type" class="block text-sm font-semibold text-gray-700">
|
|
<i class="fas fa-folder mr-2 text-green-600"></i>Room Category
|
|
</label>
|
|
<select class="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-green-500 focus:border-transparent transition-all duration-200"
|
|
id="room_type" name="room_type">
|
|
<option value="general">General Discussion</option>
|
|
<option value="technical">Technical Support</option>
|
|
<option value="social">Social Chat</option>
|
|
<option value="post_discussion">Post Discussion</option>
|
|
</select>
|
|
<p class="text-xs text-gray-500">Category will auto-update based on post selection</p>
|
|
</div>
|
|
|
|
<!-- Privacy Setting -->
|
|
<div class="space-y-3 p-6 bg-amber-50 rounded-xl border border-amber-200">
|
|
<div class="flex items-center">
|
|
<i class="fas fa-shield-alt mr-3 text-amber-600 text-lg"></i>
|
|
<h3 class="text-lg font-semibold text-gray-800">Privacy Settings</h3>
|
|
</div>
|
|
|
|
<div class="flex items-start space-x-3">
|
|
<input class="mt-1 w-4 h-4 text-amber-600 border-gray-300 rounded focus:ring-amber-500"
|
|
type="checkbox" id="is_private" name="is_private">
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700" for="is_private">
|
|
Make this room private
|
|
</label>
|
|
<p class="text-xs text-gray-500 mt-1">
|
|
Private rooms are only visible to invited members. Public rooms can be joined by anyone.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Action Buttons -->
|
|
<div class="flex flex-col sm:flex-row gap-4 pt-6">
|
|
<a href="{{ url_for('chat.index') }}"
|
|
class="flex-1 px-6 py-3 bg-gray-100 text-gray-700 font-semibold rounded-xl hover:bg-gray-200 transition-all duration-200 text-center">
|
|
<i class="fas fa-times mr-2"></i>Cancel
|
|
</a>
|
|
<button type="submit"
|
|
class="flex-1 px-6 py-3 bg-gradient-to-r from-green-600 to-emerald-600 text-white font-semibold rounded-xl hover:from-green-700 hover:to-emerald-700 transition-all duration-200">
|
|
<i class="fas fa-plus mr-2"></i>Create Chat Room
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Recent Posts Preview -->
|
|
{% if posts %}
|
|
<div class="mt-8 bg-white rounded-2xl shadow-xl overflow-hidden">
|
|
<div class="bg-gradient-to-r from-blue-600 to-purple-600 p-6">
|
|
<div class="flex items-center text-white">
|
|
<i class="fas fa-newspaper text-2xl mr-3"></i>
|
|
<div>
|
|
<h3 class="text-xl font-bold">Recent Community Posts</h3>
|
|
<p class="text-blue-100 text-sm">Available for discussion rooms</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="p-6">
|
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
{% for post in posts[:6] %}
|
|
<div class="bg-gray-50 rounded-xl p-4 hover:bg-gray-100 transition-all duration-200 cursor-pointer post-preview"
|
|
data-post-id="{{ post.id }}" data-post-title="{{ post.title }}">
|
|
<h4 class="font-semibold text-gray-800 mb-2 line-clamp-2">
|
|
{{ post.title }}
|
|
</h4>
|
|
<p class="text-gray-600 text-sm mb-3 line-clamp-3">
|
|
{{ post.content[:120] }}{% if post.content|length > 120 %}...{% endif %}
|
|
</p>
|
|
<div class="flex items-center justify-between text-xs text-gray-500">
|
|
<span>by {{ post.author.nickname }}</span>
|
|
<span>{{ post.created_at.strftime('%m/%d') }}</span>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
// Auto-update room type when post is selected
|
|
document.getElementById('related_post_id').addEventListener('change', function() {
|
|
const roomTypeSelect = document.getElementById('room_type');
|
|
if (this.value) {
|
|
roomTypeSelect.value = 'post_discussion';
|
|
document.getElementById('room_name').placeholder = 'Discussion: ' + this.options[this.selectedIndex].text.split(' - ')[0];
|
|
} else {
|
|
roomTypeSelect.value = 'general';
|
|
document.getElementById('room_name').placeholder = 'Enter a descriptive room name';
|
|
}
|
|
});
|
|
|
|
// Post preview selection
|
|
document.querySelectorAll('.post-preview').forEach(preview => {
|
|
preview.addEventListener('click', function() {
|
|
const postId = this.dataset.postId;
|
|
const postTitle = this.dataset.postTitle;
|
|
|
|
// Update the select dropdown
|
|
document.getElementById('related_post_id').value = postId;
|
|
|
|
// Update room name suggestion
|
|
document.getElementById('room_name').value = `Discussion: ${postTitle}`;
|
|
|
|
// Update room type
|
|
document.getElementById('room_type').value = 'post_discussion';
|
|
|
|
// Visual feedback
|
|
document.querySelectorAll('.post-preview').forEach(p => p.classList.remove('ring-2', 'ring-blue-500', 'bg-blue-100'));
|
|
this.classList.add('ring-2', 'ring-blue-500', 'bg-blue-100');
|
|
|
|
// Scroll to form
|
|
document.querySelector('form').scrollIntoView({ behavior: 'smooth' });
|
|
});
|
|
});
|
|
|
|
// Form validation
|
|
document.querySelector('form').addEventListener('submit', function(e) {
|
|
const roomName = document.getElementById('room_name').value.trim();
|
|
if (!roomName) {
|
|
e.preventDefault();
|
|
alert('Please enter a room name');
|
|
document.getElementById('room_name').focus();
|
|
}
|
|
});
|
|
</script>
|
|
|
|
<style>
|
|
.line-clamp-2 {
|
|
display: -webkit-box;
|
|
-webkit-line-clamp: 2;
|
|
-webkit-box-orient: vertical;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.line-clamp-3 {
|
|
display: -webkit-box;
|
|
-webkit-line-clamp: 3;
|
|
-webkit-box-orient: vertical;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.post-preview:hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
|
}
|
|
</style>
|
|
{% endblock %}
|