207 lines
13 KiB
HTML
207 lines
13 KiB
HTML
{% extends "common/base.html" %}
|
|
{% from "components/header.html" import header %}
|
|
|
|
{% block title %}{{ conversation.name }} - {% if site_settings.company_name %}DocuPulse for {{ site_settings.company_name }}{% else %}DocuPulse{% endif %}{% endblock %}
|
|
|
|
{% block extra_css %}
|
|
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
|
|
<link href="https://cdn.jsdelivr.net/npm/select2-bootstrap-5-theme@1.3.0/dist/select2-bootstrap-5-theme.min.css" rel="stylesheet" />
|
|
<link rel="stylesheet" href="{{ url_for('static', filename='css/conversation.css') }}">
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
{{ header(
|
|
title=conversation.name,
|
|
description=conversation.description or "No description",
|
|
button_text="Back to Conversations",
|
|
button_url=url_for('conversations.conversations'),
|
|
icon="fa-comments",
|
|
button_class="btn-secondary",
|
|
button_icon="fa-arrow-left"
|
|
) }}
|
|
|
|
<div class="container-fluid py-4">
|
|
<div class="row">
|
|
<!-- Main Chat Area -->
|
|
<div class="col-md-8">
|
|
<div class="card shadow-sm">
|
|
<div class="card-body">
|
|
<div class="chat-messages" style="min-height: 400px; max-height: 600px; overflow-y: auto;" id="chatMessages">
|
|
{% if messages %}
|
|
{% for message in messages %}
|
|
<div class="message {% if message.user.id == current_user.id %}sent{% else %}received{% endif %}" data-message-id="{{ message.id }}">
|
|
{% if message.user.id != current_user.id %}
|
|
<img src="{{ url_for('profile_pic', filename=message.user.profile_picture) if message.user.profile_picture else url_for('static', filename='default-avatar.png') }}"
|
|
alt="{{ message.user.username }}"
|
|
class="rounded-circle me-2"
|
|
style="width: 40px; height: 40px; object-fit: cover;">
|
|
{% endif %}
|
|
<div class="message-content">
|
|
<div class="message-info">
|
|
<span class="fw-medium">{{ message.user.username }} {{ message.user.last_name }}</span>
|
|
<span class="text-muted ms-2">{{ message.created_at.strftime('%b %d, %Y %H:%M') }}</span>
|
|
</div>
|
|
{{ message.content }}
|
|
{% if message.attachments %}
|
|
<div class="attachments mt-2">
|
|
{% for attachment in message.attachments %}
|
|
<div class="attachment mb-1">
|
|
<a href="{{ url_for('conversations.download_attachment', message_id=message.id, attachment_index=loop.index0) }}" class="btn btn-sm">
|
|
<i class="fas fa-paperclip me-1"></i>
|
|
{{ attachment.name }}
|
|
<small class="ms-1">({{ (attachment.size / 1024)|round|int }} KB)</small>
|
|
</a>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
{% if message.user.id == current_user.id %}
|
|
<img src="{{ url_for('profile_pic', filename=message.user.profile_picture) if message.user.profile_picture else url_for('static', filename='default-avatar.png') }}"
|
|
alt="{{ message.user.username }}"
|
|
class="rounded-circle ms-2"
|
|
style="width: 40px; height: 40px; object-fit: cover;">
|
|
{% endif %}
|
|
</div>
|
|
{% endfor %}
|
|
{% else %}
|
|
<div class="text-center text-muted py-5">
|
|
<i class="fas fa-comments fa-3x mb-3"></i>
|
|
<p>No messages yet. Start the conversation!</p>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
<div class="card-footer bg-white border-top">
|
|
<form id="messageForm" class="d-flex flex-column gap-2">
|
|
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
|
|
<div class="d-flex gap-2 align-items-center">
|
|
<input type="text" class="form-control" id="messageInput" placeholder="Type your message..." required style="min-width:0; height: 38px;">
|
|
<label for="fileInput" class="btn btn-outline-secondary btn-sm mb-0 d-flex align-items-center justify-content-center" style="height: 38px; width: 38px;">
|
|
<i class="fas fa-paperclip"></i>
|
|
</label>
|
|
<input type="file" id="fileInput" class="d-none" multiple accept=".pdf,.docx,.doc,.txt,.rtf,.odt,.md,.csv,.xlsx,.xls,.ods,.xlsm,.pptx,.ppt,.odp,.jpg,.jpeg,.png,.gif,.bmp,.svg,.webp,.tiff,.zip,.rar,.7z,.tar,.gz,.py,.js,.html,.css,.json,.xml,.sql,.sh,.bat,.mp3,.wav,.ogg,.m4a,.flac,.mp4,.avi,.mov,.wmv,.flv,.mkv,.webm,.dwg,.dxf,.ai,.psd,.eps,.indd,.eml,.msg,.vcf,.ics">
|
|
<button type="submit" class="btn btn-primary d-flex align-items-center justify-content-center" style="height: 38px; width: 38px;">
|
|
<i class="fas fa-paper-plane"></i>
|
|
<i class="fas fa-circle-notch fa-spin ms-1 d-none" style="font-size: 0.875rem;"></i>
|
|
</button>
|
|
</div>
|
|
<div class="d-flex align-items-center" style="min-height: 1.2em;">
|
|
<small id="selectedFiles" class="text-muted"></small>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Members Sidebar -->
|
|
<div class="col-md-4">
|
|
<div class="card shadow-sm">
|
|
<div class="card-header bg-white d-flex justify-content-between align-items-center">
|
|
<h5 class="card-title mb-0">
|
|
<i class="fas fa-users me-2"></i>Members
|
|
</h5>
|
|
{% if current_user.is_admin %}
|
|
<button type="button" class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#manageMembersModal">
|
|
<i class="fas fa-user-edit me-1"></i>Manage Members
|
|
</button>
|
|
{% endif %}
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="list-group list-group-flush">
|
|
{% for member in conversation.members %}
|
|
<div class="list-group-item d-flex align-items-center">
|
|
<img src="{{ url_for('profile_pic', filename=member.profile_picture) if member.profile_picture else url_for('static', filename='default-avatar.png') }}"
|
|
alt="{{ member.username }}"
|
|
class="rounded-circle me-2"
|
|
style="width: 40px; height: 40px; object-fit: cover;">
|
|
<div class="flex-grow-1">
|
|
<div class="fw-medium">{{ member.username }} {{ member.last_name }}</div>
|
|
<div class="text-muted small">{{ member.email }}</div>
|
|
</div>
|
|
{% if member.id == conversation.created_by %}
|
|
<span class="badge bg-primary">
|
|
<i class="fas fa-crown me-1"></i>Creator
|
|
</span>
|
|
{% endif %}
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{% if current_user.is_admin %}
|
|
<!-- Manage Members Modal -->
|
|
<div class="modal fade" id="manageMembersModal" tabindex="-1" aria-labelledby="manageMembersModalLabel" aria-hidden="true">
|
|
<div class="modal-dialog modal-dialog-centered">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title" id="manageMembersModalLabel">Manage Members</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<form method="POST" action="{{ url_for('conversations.edit_conversation', conversation_id=conversation.id, redirect=url_for('conversations.conversation', conversation_id=conversation.id)) }}" id="membersForm">
|
|
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
|
|
<div class="mb-3">
|
|
<label for="user_id" class="form-label">Select User</label>
|
|
<select class="form-select select2" id="user_id" name="user_id">
|
|
<option value="">Search for a user...</option>
|
|
{% for user in all_users %}
|
|
{% if user.id != current_user.id %}
|
|
<option value="{{ user.id }}" data-email="{{ user.email }}" data-avatar="{{ url_for('profile_pic', filename=user.profile_picture) if user.profile_picture else url_for('static', filename='default-avatar.png') }}">{{ user.username }} {{ user.last_name }}</option>
|
|
{% endif %}
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
<button type="button" class="btn btn-primary w-100 mb-3" id="addMemberBtn">
|
|
<i class="fas fa-user-plus me-2"></i>Add Member
|
|
</button>
|
|
|
|
<div class="list-group" id="selectedMembersList">
|
|
<!-- Render all current members -->
|
|
{% for member in conversation.members %}
|
|
<div class="list-group-item d-flex align-items-center member-row" data-user-id="{{ member.id }}">
|
|
<img class="member-avatar" src="{{ url_for('profile_pic', filename=member.profile_picture) if member.profile_picture else url_for('static', filename='default-avatar.png') }}">
|
|
<div class="flex-grow-1">
|
|
<div class="fw-bold">{{ member.username }} {{ member.last_name }}</div>
|
|
<div class="text-muted small">{{ member.email }}</div>
|
|
</div>
|
|
{% if member.id == conversation.created_by %}
|
|
<span class="badge badge-creator ms-2"><i class="fas fa-user"></i> Creator</span>
|
|
{% else %}
|
|
<button type="button" class="btn btn-remove-member ms-2">
|
|
<i class="fas fa-user-minus me-1"></i>Remove
|
|
</button>
|
|
{% endif %}
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</form>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
<button type="submit" form="membersForm" class="btn btn-primary">Save Changes</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% block extra_js %}
|
|
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js"></script>
|
|
<script>
|
|
// Set global variables needed by the JavaScript files
|
|
window.conversationId = "{{ conversation.id }}";
|
|
window.currentUserId = "{{ current_user.id }}";
|
|
window.sendMessageUrl = "{{ url_for('conversations.send_message', conversation_id=conversation.id) }}";
|
|
</script>
|
|
<script src="{{ url_for('static', filename='js/chat-manager.js') }}"></script>
|
|
<script src="{{ url_for('static', filename='js/conversation.js') }}"></script>
|
|
<script src="{{ url_for('static', filename='js/member-management.js') }}"></script>
|
|
{% endblock %}
|
|
{% endblock content %} |