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.
604 lines
27 KiB
HTML
604 lines
27 KiB
HTML
{% extends "admin/base.html" %}
|
|
|
|
{% block title %}Manage Chats - Admin{% endblock %}
|
|
|
|
{% block admin_content %}
|
|
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pb-2 mb-3 border-bottom">
|
|
<h1 class="h2">
|
|
<i class="fas fa-comments me-2"></i>Manage Chat Rooms
|
|
</h1>
|
|
<div class="btn-toolbar mb-2 mb-md-0">
|
|
<button type="button" class="btn btn-sm btn-outline-secondary me-2" onclick="location.reload()">
|
|
<i class="fas fa-sync-alt"></i> Refresh
|
|
</button>
|
|
<button type="button" class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#createRoomModal">
|
|
<i class="fas fa-plus"></i> Create Room
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Statistics Cards -->
|
|
<div class="row mb-4">
|
|
<div class="col-xl-3 col-md-6 mb-4">
|
|
<div class="card border-left-primary h-100 py-2">
|
|
<div class="card-body">
|
|
<div class="row no-gutters align-items-center">
|
|
<div class="col me-2">
|
|
<div class="text-xs fw-bold text-primary text-uppercase mb-1">Total Rooms</div>
|
|
<div class="h5 mb-0 fw-bold text-gray-800">{{ total_rooms }}</div>
|
|
</div>
|
|
<div class="col-auto">
|
|
<i class="fas fa-comments fa-2x text-gray-300"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-xl-3 col-md-6 mb-4">
|
|
<div class="card border-left-success h-100 py-2">
|
|
<div class="card-body">
|
|
<div class="row no-gutters align-items-center">
|
|
<div class="col me-2">
|
|
<div class="text-xs fw-bold text-success text-uppercase mb-1">Linked to Posts</div>
|
|
<div class="h5 mb-0 fw-bold text-gray-800">{{ linked_rooms }}</div>
|
|
</div>
|
|
<div class="col-auto">
|
|
<i class="fas fa-link fa-2x text-gray-300"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-xl-3 col-md-6 mb-4">
|
|
<div class="card border-left-warning h-100 py-2">
|
|
<div class="card-body">
|
|
<div class="row no-gutters align-items-center">
|
|
<div class="col me-2">
|
|
<div class="text-xs fw-bold text-warning text-uppercase mb-1">Active Today</div>
|
|
<div class="h5 mb-0 fw-bold text-gray-800">{{ active_today }}</div>
|
|
</div>
|
|
<div class="col-auto">
|
|
<i class="fas fa-clock fa-2x text-gray-300"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-xl-3 col-md-6 mb-4">
|
|
<div class="card border-left-info h-100 py-2">
|
|
<div class="card-body">
|
|
<div class="row no-gutters align-items-center">
|
|
<div class="col me-2">
|
|
<div class="text-xs fw-bold text-info text-uppercase mb-1">Total Messages</div>
|
|
<div class="h5 mb-0 fw-bold text-gray-800">{{ total_messages }}</div>
|
|
</div>
|
|
<div class="col-auto">
|
|
<i class="fas fa-comment fa-2x text-gray-300"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Filters and Search -->
|
|
<div class="card mb-4">
|
|
<div class="card-body">
|
|
<form method="GET" class="row g-3">
|
|
<div class="col-md-3">
|
|
<label for="category" class="form-label">Category</label>
|
|
<select class="form-select" id="category" name="category">
|
|
<option value="">All Categories</option>
|
|
<option value="general" {{ 'selected' if request.args.get('category') == 'general' }}>General</option>
|
|
<option value="technical" {{ 'selected' if request.args.get('category') == 'technical' }}>Technical</option>
|
|
<option value="maintenance" {{ 'selected' if request.args.get('category') == 'maintenance' }}>Maintenance</option>
|
|
<option value="routes" {{ 'selected' if request.args.get('category') == 'routes' }}>Routes</option>
|
|
<option value="events" {{ 'selected' if request.args.get('category') == 'events' }}>Events</option>
|
|
<option value="safety" {{ 'selected' if request.args.get('category') == 'safety' }}>Safety</option>
|
|
<option value="gear" {{ 'selected' if request.args.get('category') == 'gear' }}>Gear & Equipment</option>
|
|
<option value="social" {{ 'selected' if request.args.get('category') == 'social' }}>Social</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label for="status" class="form-label">Status</label>
|
|
<select class="form-select" id="status" name="status">
|
|
<option value="">All Status</option>
|
|
<option value="active" {{ 'selected' if request.args.get('status') == 'active' }}>Active</option>
|
|
<option value="inactive" {{ 'selected' if request.args.get('status') == 'inactive' }}>Inactive</option>
|
|
<option value="linked" {{ 'selected' if request.args.get('status') == 'linked' }}>Linked to Post</option>
|
|
<option value="unlinked" {{ 'selected' if request.args.get('status') == 'unlinked' }}>Not Linked</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label for="search" class="form-label">Search</label>
|
|
<input type="text" class="form-control" id="search" name="search"
|
|
placeholder="Search room name or description..."
|
|
value="{{ request.args.get('search', '') }}">
|
|
</div>
|
|
<div class="col-md-2">
|
|
<label class="form-label"> </label>
|
|
<div class="d-grid">
|
|
<button type="submit" class="btn btn-primary">
|
|
<i class="fas fa-search"></i> Filter
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Chat Rooms Table -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h6 class="m-0 fw-bold text-primary">Chat Rooms</h6>
|
|
</div>
|
|
<div class="card-body">
|
|
{% if chat_rooms %}
|
|
<div class="table-responsive">
|
|
<table class="table table-bordered table-hover">
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th>Room Name</th>
|
|
<th>Category</th>
|
|
<th>Created By</th>
|
|
<th>Linked Post</th>
|
|
<th>Messages</th>
|
|
<th>Last Activity</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for room in chat_rooms %}
|
|
<tr>
|
|
<td>
|
|
<div class="d-flex align-items-center">
|
|
<div>
|
|
<a href="{{ url_for('chat.room', room_id=room.id) }}"
|
|
class="fw-bold text-decoration-none" target="_blank">
|
|
{{ room.name }}
|
|
</a>
|
|
{% if room.description %}
|
|
<div class="small text-muted">{{ room.description[:100] }}{% if room.description|length > 100 %}...{% endif %}</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</td>
|
|
<td>
|
|
<span class="badge bg-{{ 'success' if room.category == 'general' else 'info' if room.category == 'technical' else 'warning' if room.category == 'maintenance' else 'primary' }}">
|
|
{{ room.category.title() if room.category else 'Uncategorized' }}
|
|
</span>
|
|
</td>
|
|
<td>
|
|
<a href="{{ url_for('admin.user_detail', user_id=room.created_by.id) }}" class="text-decoration-none">
|
|
{{ room.created_by.nickname }}
|
|
</a>
|
|
</td>
|
|
<td>
|
|
{% if room.related_post %}
|
|
<a href="{{ url_for('admin.post_detail', post_id=room.related_post.id) }}"
|
|
class="text-decoration-none">
|
|
{{ room.related_post.title[:30] }}{% if room.related_post.title|length > 30 %}...{% endif %}
|
|
</a>
|
|
{% else %}
|
|
<span class="text-muted">Not linked</span>
|
|
{% endif %}
|
|
</td>
|
|
<td>
|
|
<span class="badge bg-secondary">{{ room.message_count or 0 }}</span>
|
|
</td>
|
|
<td>
|
|
{% if room.last_activity %}
|
|
<small>{{ room.last_activity.strftime('%Y-%m-%d %H:%M') }}</small>
|
|
{% else %}
|
|
<small class="text-muted">Never</small>
|
|
{% endif %}
|
|
</td>
|
|
<td>
|
|
<div class="btn-group" role="group">
|
|
<button type="button" class="btn btn-sm btn-outline-primary dropdown-toggle"
|
|
data-bs-toggle="dropdown">
|
|
Actions
|
|
</button>
|
|
<ul class="dropdown-menu">
|
|
<li>
|
|
<a class="dropdown-item" href="#"
|
|
onclick="editRoom({{ room.id }}, '{{ room.name }}', '{{ room.description or '' }}', '{{ room.category or '' }}', {{ room.related_post.id if room.related_post else 'null' }})">
|
|
<i class="fas fa-edit"></i> Edit Room
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a class="dropdown-item" href="#"
|
|
onclick="linkToPost({{ room.id }}, '{{ room.name }}')">
|
|
<i class="fas fa-link"></i> Link to Post
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a class="dropdown-item" href="#"
|
|
onclick="mergeRoom({{ room.id }}, '{{ room.name }}')">
|
|
<i class="fas fa-compress-arrows-alt"></i> Merge Room
|
|
</a>
|
|
</li>
|
|
<li><hr class="dropdown-divider"></li>
|
|
<li>
|
|
<a class="dropdown-item text-danger" href="#"
|
|
onclick="deleteRoom({{ room.id }}, '{{ room.name }}')">
|
|
<i class="fas fa-trash"></i> Delete Room
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<!-- Pagination -->
|
|
{% if pagination %}
|
|
<nav>
|
|
<ul class="pagination justify-content-center">
|
|
{% if pagination.has_prev %}
|
|
<li class="page-item">
|
|
<a class="page-link" href="{{ url_for('admin.manage_chats', page=pagination.prev_num, **request.args) }}">Previous</a>
|
|
</li>
|
|
{% endif %}
|
|
|
|
{% for page_num in pagination.iter_pages() %}
|
|
{% if page_num %}
|
|
{% if page_num != pagination.page %}
|
|
<li class="page-item">
|
|
<a class="page-link" href="{{ url_for('admin.manage_chats', page=page_num, **request.args) }}">{{ 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 pagination.has_next %}
|
|
<li class="page-item">
|
|
<a class="page-link" href="{{ url_for('admin.manage_chats', page=pagination.next_num, **request.args) }}">Next</a>
|
|
</li>
|
|
{% endif %}
|
|
</ul>
|
|
</nav>
|
|
{% endif %}
|
|
|
|
{% else %}
|
|
<div class="text-center py-4">
|
|
<i class="fas fa-comments fa-3x text-muted mb-3"></i>
|
|
<h5 class="text-muted">No chat rooms found</h5>
|
|
<p class="text-muted">Create a new room or adjust your filters.</p>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Edit Room Modal -->
|
|
<div class="modal fade" id="editRoomModal" tabindex="-1">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Edit Chat Room</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<form id="editRoomForm">
|
|
<div class="modal-body">
|
|
<input type="hidden" id="editRoomId">
|
|
<div class="mb-3">
|
|
<label for="editRoomName" class="form-label">Room Name</label>
|
|
<input type="text" class="form-control" id="editRoomName" required>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="editRoomDescription" class="form-label">Description</label>
|
|
<textarea class="form-control" id="editRoomDescription" rows="3"></textarea>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="editRoomCategory" class="form-label">Category</label>
|
|
<select class="form-select" id="editRoomCategory">
|
|
<option value="">Select Category</option>
|
|
<option value="general">General</option>
|
|
<option value="technical">Technical</option>
|
|
<option value="maintenance">Maintenance</option>
|
|
<option value="routes">Routes</option>
|
|
<option value="events">Events</option>
|
|
<option value="safety">Safety</option>
|
|
<option value="gear">Gear & Equipment</option>
|
|
<option value="social">Social</option>
|
|
</select>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="editLinkedPost" class="form-label">Linked Post</label>
|
|
<select class="form-select" id="editLinkedPost">
|
|
<option value="">No linked post</option>
|
|
{% for post in available_posts %}
|
|
<option value="{{ post.id }}">{{ post.title }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
<button type="submit" class="btn btn-primary">Save Changes</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Link to Post Modal -->
|
|
<div class="modal fade" id="linkPostModal" tabindex="-1">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Link Chat Room to Post</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<form id="linkPostForm">
|
|
<div class="modal-body">
|
|
<input type="hidden" id="linkRoomId">
|
|
<div class="alert alert-info">
|
|
<i class="fas fa-info-circle"></i>
|
|
Linking a chat room to a post will make it appear in the post's discussion section.
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="linkRoomName" class="form-label">Room Name</label>
|
|
<input type="text" class="form-control" id="linkRoomName" readonly>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="linkPostSelect" class="form-label">Select Post</label>
|
|
<select class="form-select" id="linkPostSelect" required>
|
|
<option value="">Choose a post...</option>
|
|
{% for post in available_posts %}
|
|
<option value="{{ post.id }}">{{ post.title }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
<button type="submit" class="btn btn-success">Link to Post</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Merge Room Modal -->
|
|
<div class="modal fade" id="mergeRoomModal" tabindex="-1">
|
|
<div class="modal-dialog modal-lg">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Merge Chat Room</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<form id="mergeRoomForm">
|
|
<div class="modal-body">
|
|
<input type="hidden" id="mergeSourceRoomId">
|
|
<div class="alert alert-warning">
|
|
<i class="fas fa-exclamation-triangle"></i>
|
|
<strong>Warning:</strong> This action will merge all messages from the source room into the target room.
|
|
The source room will be deleted. This cannot be undone.
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<h6>Source Room (will be deleted)</h6>
|
|
<div class="card bg-light">
|
|
<div class="card-body">
|
|
<h6 class="card-title" id="mergeSourceRoomName"></h6>
|
|
<p class="card-text small" id="mergeSourceRoomInfo"></p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<h6>Target Room (messages will be merged here)</h6>
|
|
<select class="form-select" id="mergeTargetRoom" required>
|
|
<option value="">Select target room...</option>
|
|
</select>
|
|
<div id="mergeTargetRoomPreview" class="mt-2" style="display: none;">
|
|
<div class="card bg-light">
|
|
<div class="card-body">
|
|
<h6 class="card-title" id="mergeTargetRoomName"></h6>
|
|
<p class="card-text small" id="mergeTargetRoomInfo"></p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
<button type="submit" class="btn btn-danger">Merge Rooms</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
// Edit room functionality
|
|
function editRoom(roomId, name, description, category, linkedPostId) {
|
|
document.getElementById('editRoomId').value = roomId;
|
|
document.getElementById('editRoomName').value = name;
|
|
document.getElementById('editRoomDescription').value = description;
|
|
document.getElementById('editRoomCategory').value = category;
|
|
document.getElementById('editLinkedPost').value = linkedPostId || '';
|
|
|
|
const modal = new bootstrap.Modal(document.getElementById('editRoomModal'));
|
|
modal.show();
|
|
}
|
|
|
|
// Link to post functionality
|
|
function linkToPost(roomId, roomName) {
|
|
document.getElementById('linkRoomId').value = roomId;
|
|
document.getElementById('linkRoomName').value = roomName;
|
|
|
|
const modal = new bootstrap.Modal(document.getElementById('linkPostModal'));
|
|
modal.show();
|
|
}
|
|
|
|
// Merge room functionality
|
|
function mergeRoom(roomId, roomName) {
|
|
document.getElementById('mergeSourceRoomId').value = roomId;
|
|
document.getElementById('mergeSourceRoomName').textContent = roomName;
|
|
|
|
// Load available rooms for merging
|
|
fetch(`/admin/api/chat-rooms?exclude=${roomId}`)
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
const select = document.getElementById('mergeTargetRoom');
|
|
select.innerHTML = '<option value="">Select target room...</option>';
|
|
|
|
data.rooms.forEach(room => {
|
|
const option = document.createElement('option');
|
|
option.value = room.id;
|
|
option.textContent = `${room.name} (${room.category}) - ${room.message_count} messages`;
|
|
option.dataset.roomData = JSON.stringify(room);
|
|
select.appendChild(option);
|
|
});
|
|
});
|
|
|
|
const modal = new bootstrap.Modal(document.getElementById('mergeRoomModal'));
|
|
modal.show();
|
|
}
|
|
|
|
// Delete room functionality
|
|
function deleteRoom(roomId, roomName) {
|
|
if (confirm(`Are you sure you want to delete the room "${roomName}"? This action cannot be undone.`)) {
|
|
fetch(`/admin/api/chat-rooms/${roomId}`, {
|
|
method: 'DELETE',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
}
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
location.reload();
|
|
} else {
|
|
alert('Error deleting room: ' + data.error);
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('Error:', error);
|
|
alert('Failed to delete room');
|
|
});
|
|
}
|
|
}
|
|
|
|
// Form submissions
|
|
document.getElementById('editRoomForm').addEventListener('submit', function(e) {
|
|
e.preventDefault();
|
|
|
|
const roomId = document.getElementById('editRoomId').value;
|
|
const formData = {
|
|
name: document.getElementById('editRoomName').value,
|
|
description: document.getElementById('editRoomDescription').value,
|
|
category: document.getElementById('editRoomCategory').value,
|
|
related_post_id: document.getElementById('editLinkedPost').value || null
|
|
};
|
|
|
|
fetch(`/admin/api/chat-rooms/${roomId}`, {
|
|
method: 'PUT',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify(formData)
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
location.reload();
|
|
} else {
|
|
alert('Error updating room: ' + data.error);
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('Error:', error);
|
|
alert('Failed to update room');
|
|
});
|
|
});
|
|
|
|
document.getElementById('linkPostForm').addEventListener('submit', function(e) {
|
|
e.preventDefault();
|
|
|
|
const roomId = document.getElementById('linkRoomId').value;
|
|
const postId = document.getElementById('linkPostSelect').value;
|
|
|
|
fetch(`/admin/api/chat-rooms/${roomId}/link-post`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({ post_id: postId })
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
location.reload();
|
|
} else {
|
|
alert('Error linking room to post: ' + data.error);
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('Error:', error);
|
|
alert('Failed to link room to post');
|
|
});
|
|
});
|
|
|
|
document.getElementById('mergeRoomForm').addEventListener('submit', function(e) {
|
|
e.preventDefault();
|
|
|
|
const sourceRoomId = document.getElementById('mergeSourceRoomId').value;
|
|
const targetRoomId = document.getElementById('mergeTargetRoom').value;
|
|
|
|
fetch(`/admin/api/chat-rooms/${sourceRoomId}/merge`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({ target_room_id: targetRoomId })
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
alert('Rooms merged successfully!');
|
|
location.reload();
|
|
} else {
|
|
alert('Error merging rooms: ' + data.error);
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('Error:', error);
|
|
alert('Failed to merge rooms');
|
|
});
|
|
});
|
|
|
|
// Target room preview
|
|
document.getElementById('mergeTargetRoom').addEventListener('change', function() {
|
|
const selectedOption = this.options[this.selectedIndex];
|
|
const preview = document.getElementById('mergeTargetRoomPreview');
|
|
|
|
if (selectedOption.value && selectedOption.dataset.roomData) {
|
|
const roomData = JSON.parse(selectedOption.dataset.roomData);
|
|
document.getElementById('mergeTargetRoomName').textContent = roomData.name;
|
|
document.getElementById('mergeTargetRoomInfo').textContent =
|
|
`Category: ${roomData.category} | Messages: ${roomData.message_count}`;
|
|
preview.style.display = 'block';
|
|
} else {
|
|
preview.style.display = 'none';
|
|
}
|
|
});
|
|
</script>
|
|
{% endblock %}
|