Files
moto-adv-website/app/templates/chat/embed.html
ske087 1661f5f588 feat: Complete chat system implementation and password reset enhancement
- Add comprehensive chat system with modern UI design
- Implement admin-based password reset system
- Fix template syntax errors and 500 server errors
- Add chat routes, API endpoints, and database models
- Enhance user interface with Tailwind CSS card-based design
- Implement community guidelines and quick action features
- Add responsive design for mobile and desktop compatibility
- Create support chat functionality with admin integration
- Fix JavaScript inheritance in base template
- Add database migration for chat system tables

Features:
 Modern chat interface with room management
 Admin-based password reset workflow
 Real-time chat with mobile app support
 Professional UI with gradient cards and hover effects
 Community guidelines and safety features
 Responsive design for all devices
 Error-free template rendering
2025-08-09 20:44:25 +03:00

314 lines
9.9 KiB
HTML

<!-- Embeddable Chat Widget for Posts and Pages -->
<div class="chat-embed-widget" data-post-id="{{ post.id if post else '' }}" style="margin: 1rem 0;">
<div class="chat-embed-header" onclick="toggleChatEmbed(this)">
<div class="d-flex align-items-center justify-content-between">
<div class="d-flex align-items-center">
<i class="fas fa-comments me-2"></i>
<span class="chat-embed-title">
{% if post %}
Discussion: {{ post.title[:50] }}{% if post.title|length > 50 %}...{% endif %}
{% else %}
Join the Discussion
{% endif %}
</span>
<span class="badge bg-primary ms-2" id="messageCount-{{ post.id if post else 'general' }}">
{{ message_count or 0 }} messages
</span>
</div>
<i class="fas fa-chevron-down chat-embed-toggle"></i>
</div>
</div>
<div class="chat-embed-content" style="display: none;">
<div class="chat-embed-messages" id="embedMessages-{{ post.id if post else 'general' }}">
{% if recent_messages %}
{% for message in recent_messages %}
<div class="chat-embed-message">
<div class="message-header">
<strong>{{ message.user.nickname }}</strong>
{% if message.user.is_admin %}
<span class="badge bg-danger ms-1">ADMIN</span>
{% endif %}
<small class="text-muted ms-2">{{ message.created_at.strftime('%H:%M') }}</small>
</div>
<div class="message-content">{{ message.content }}</div>
</div>
{% endfor %}
{% if message_count > recent_messages|length %}
<div class="text-center mt-2">
<small class="text-muted">+ {{ message_count - recent_messages|length }} more messages</small>
</div>
{% endif %}
{% else %}
<div class="text-center text-muted py-3">
<i class="fas fa-comments fa-2x mb-2"></i>
<p>No messages yet. Start the conversation!</p>
</div>
{% endif %}
</div>
{% if current_user.is_authenticated %}
<div class="chat-embed-input">
<div class="input-group">
<input type="text"
class="form-control"
placeholder="Type your message..."
id="embedInput-{{ post.id if post else 'general' }}"
maxlength="500"
onkeypress="handleEmbedEnter(event, '{{ post.id if post else 'general' }}')">
<button class="btn btn-primary"
type="button"
onclick="sendEmbedMessage('{{ post.id if post else 'general' }}')">
<i class="fas fa-paper-plane"></i>
</button>
</div>
<small class="text-muted">Max 500 characters</small>
</div>
{% else %}
<div class="chat-embed-login text-center py-3">
<p class="text-muted mb-2">Join the discussion</p>
<a href="{{ url_for('auth.login') }}" class="btn btn-primary btn-sm me-2">Login</a>
<a href="{{ url_for('auth.register') }}" class="btn btn-outline-primary btn-sm">Register</a>
</div>
{% endif %}
<div class="chat-embed-actions text-center mt-2">
{% if room_id %}
<a href="{{ url_for('chat.room', room_id=room_id) }}" class="btn btn-sm btn-outline-primary">
<i class="fas fa-expand-alt me-1"></i>
Open Full Chat
</a>
{% endif %}
<a href="{{ url_for('chat.index') }}" class="btn btn-sm btn-outline-secondary ms-2">
<i class="fas fa-comments me-1"></i>
All Chats
</a>
</div>
</div>
</div>
<style>
.chat-embed-widget {
border: 1px solid #e0e0e0;
border-radius: 12px;
background: white;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
overflow: hidden;
transition: all 0.3s ease;
}
.chat-embed-widget:hover {
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.15);
}
.chat-embed-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 1rem;
cursor: pointer;
transition: all 0.3s ease;
}
.chat-embed-header:hover {
background: linear-gradient(135deg, #5a6fd8 0%, #6a4190 100%);
}
.chat-embed-title {
font-weight: 600;
}
.chat-embed-toggle {
transition: transform 0.3s ease;
}
.chat-embed-widget.expanded .chat-embed-toggle {
transform: rotate(180deg);
}
.chat-embed-content {
border-top: 1px solid #e0e0e0;
}
.chat-embed-messages {
max-height: 300px;
overflow-y: auto;
padding: 1rem;
background: #f8f9fa;
}
.chat-embed-message {
background: white;
border-radius: 8px;
padding: 0.75rem;
margin-bottom: 0.5rem;
border-left: 3px solid #667eea;
}
.chat-embed-message:last-child {
margin-bottom: 0;
}
.chat-embed-message .message-header {
margin-bottom: 0.25rem;
}
.chat-embed-message .message-content {
color: #495057;
word-wrap: break-word;
}
.chat-embed-input {
padding: 1rem;
background: white;
border-top: 1px solid #e0e0e0;
}
.chat-embed-login {
padding: 1rem;
background: #f8f9fa;
border-top: 1px solid #e0e0e0;
}
.chat-embed-actions {
padding: 0.5rem 1rem 1rem;
background: white;
border-top: 1px solid #e0e0e0;
}
@media (max-width: 768px) {
.chat-embed-widget {
margin: 1rem -15px;
border-radius: 0;
}
.chat-embed-messages {
max-height: 200px;
}
}
</style>
<script>
function toggleChatEmbed(header) {
const widget = header.closest('.chat-embed-widget');
const content = widget.querySelector('.chat-embed-content');
const isExpanded = widget.classList.contains('expanded');
if (isExpanded) {
content.style.display = 'none';
widget.classList.remove('expanded');
} else {
content.style.display = 'block';
widget.classList.add('expanded');
// Load recent messages if not already loaded
const postId = widget.dataset.postId;
if (postId) {
loadEmbedMessages(postId);
}
}
}
function loadEmbedMessages(postId) {
const messagesContainer = document.getElementById(`embedMessages-${postId}`);
fetch(`/api/v1/chat/embed/messages?post_id=${postId}&limit=5`)
.then(response => response.json())
.then(data => {
if (data.success && data.messages.length > 0) {
let messagesHTML = '';
data.messages.forEach(message => {
messagesHTML += `
<div class="chat-embed-message">
<div class="message-header">
<strong>${message.user.nickname}</strong>
${message.user.is_admin ? '<span class="badge bg-danger ms-1">ADMIN</span>' : ''}
<small class="text-muted ms-2">${new Date(message.created_at).toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'})}</small>
</div>
<div class="message-content">${message.content}</div>
</div>
`;
});
if (data.total_count > data.messages.length) {
messagesHTML += `
<div class="text-center mt-2">
<small class="text-muted">+ ${data.total_count - data.messages.length} more messages</small>
</div>
`;
}
messagesContainer.innerHTML = messagesHTML;
// Update message count
const countBadge = document.getElementById(`messageCount-${postId}`);
if (countBadge) {
countBadge.textContent = `${data.total_count} messages`;
}
}
})
.catch(error => {
console.error('Error loading embed messages:', error);
});
}
function handleEmbedEnter(event, postId) {
if (event.key === 'Enter') {
sendEmbedMessage(postId);
}
}
function sendEmbedMessage(postId) {
const input = document.getElementById(`embedInput-${postId}`);
const content = input.value.trim();
if (!content) return;
// Disable input while sending
input.disabled = true;
fetch('/api/v1/chat/embed/send', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
content: content,
post_id: postId || null
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
input.value = '';
// Reload messages to show the new one
loadEmbedMessages(postId);
} else {
alert('Error sending message: ' + data.error);
}
})
.catch(error => {
console.error('Error:', error);
alert('Failed to send message');
})
.finally(() => {
input.disabled = false;
input.focus();
});
}
// Auto-load messages when widget is first expanded
document.addEventListener('DOMContentLoaded', function() {
// Add click handlers to all chat embed widgets
document.querySelectorAll('.chat-embed-widget').forEach(widget => {
widget.addEventListener('click', function(e) {
if (e.target.closest('.chat-embed-header')) {
const postId = widget.dataset.postId;
if (postId && widget.classList.contains('expanded')) {
loadEmbedMessages(postId);
}
}
});
});
});
</script>