Игорь Надеин 263a7cbe90 Исправлена разметка и стили страницы профиля
- Заменен лишний div.container на стандартный container для единообразия
- Заменен класс .card на .ios-card в секции активности для устранения конфликтов стилей
- Добавлен card-list.css в base.html для корректного отображения карточек новостей
- Добавлены CSS правила для правильного поведения .row и колонок в grid-layout
- Исправлена проблема с выходом календаря за границы страницы
- Убраны видимые скроллбары у modal-body и modal-content для чистого UI
2025-11-29 13:52:21 +03:00

265 lines
13 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{% extends "base.html" %}
{% load static %}
{% block title %}Чат{% endblock %}
{% block extra_css %}{% endblock %}
{% block content %}
<div class="container chat-root">
<div class="row gx-0">
<div class="col-12 chat-col">
<!-- Заголовок -->
<div class="section-header">
{% if chat.type == 'global' %}
<i class="bi-globe fs-2 text-primary"></i>
<h2 class="title mb-0">Глобальный чат</h2>
{% elif chat.type == 'department' %}
<i class="bi-diagram-3 fs-2 text-primary"></i>
<h2 class="title mb-0">Чат отдела: {{ chat.department.name }}</h2>
{% else %}
<i class="bi-person fs-2 text-primary"></i>
<h2 class="title mb-0">
{% for u in chat.participants.all %}
{% if u != user %}
{% if not forloop.first %}, {% endif %}
<a href="{% url 'employees:employee_detail' u.pk %}" class="text-decoration-none">
{{ u.get_full_name|default:u.username }}
</a>
{% endif %}
{% endfor %}
</h2>
{% endif %}
<div class="ms-auto">
<a href="{% url 'communications:chat_list' %}" class="btn btn-ghost">
<i class="bi-arrow-left"></i> К чатам
</a>
</div>
</div>
<!-- Корпус чата -->
<article class="card chat-box">
<!-- Кнопка «вниз» -->
<button id="scrollBtn" class="scroll-to-bottom" type="button" aria-label="Прокрутить вниз">
<i class="bi-arrow-down"></i>
</button>
<!-- Лента сообщений -->
<div id="chatScroll"
class="chat-scroll"
data-me-id="{{ user.id }}"
data-me-avatar="{% if user.avatar %}{{ user.avatar.url }}{% endif %}"
data-profile-url="{% url 'employees:profile' %}"
data-detail-url-template="{% url 'employees:employee_detail' 0 %}"
data-chat-id="{{ chat.id }}"
data-fetch-url="{% url 'api:v1:chat_messages' chat.pk %}"
data-page-size="{{ messages_page_size }}"
data-oldest-id="{{ messages_oldest_id }}"
data-oldest-ts="{{ messages_oldest_ts }}"
data-has-more="{% if messages_has_more %}1{% else %}0{% endif %}"
data-last-read-ts="{% if last_read_at %}{{ last_read_at|date:'U' }}000{% endif %}">
<div class="history-loader d-none" data-history-loader>
<div class="spinner-border spinner-border-sm text-secondary" role="status" aria-hidden="true"></div>
<span class="small">Загружаем ранние сообщения…</span>
</div>
{% if messages %}
{% for message in messages %}
{% ifchanged message.created_at|date:"d.m.Y" %}
<div class="day-divider"><span>{{ message.created_at|date:"d.m.Y" }}</span></div>
{% endifchanged %}
<!-- сообщение -->
<div class="d-flex mb-3 msg {% if message.author_id == user.id %}justify-content-end{% else %}justify-content-start{% endif %}"
data-id="{{ message.id }}"
data-ts="{{ message.created_at|date:'U' }}000"
data-author-id="{{ message.author_id }}">
{% if message.author_id != user.id %}
<a class="me-2 text-decoration-none" href="{% url 'employees:employee_detail' message.author.pk %}">
<span class="mini-ava border">
{% if message.author.avatar %}<img src="{{ message.author.avatar.url }}" alt="" loading="lazy">
{% else %}<i class="bi-person"></i>{% endif %}
</span>
</a>
{% endif %}
<div class="d-flex flex-column" style="max-width:80%;">
<div class="small text-secondary {% if message.author_id == user.id %}text-end{% endif %}">
{% if message.author_id == user.id %}
<a href="{% url 'employees:profile' %}" class="text-decoration-none">Вы</a>
{% else %}
<a href="{% url 'employees:employee_detail' message.author.pk %}" class="text-decoration-none fw-semibold">
{{ message.author.get_full_name|default:message.author.username }}
</a>
{% endif %}
· <time datetime="{{ message.created_at|date:'c' }}">{{ message.created_at|date:"H:i" }}</time>
</div>
<div class="mt-1 bubble {% if message.author_id == user.id %}bubble-me{% else %}bubble-other{% endif %}">
{% if message.content %}
{{ message.content|linebreaksbr }}
{% endif %}
{% if message.has_attachments %}
<div class="message-attachments mt-2">
{% for attachment in message.attachments.all %}
<div class="attachment-item mb-2">
{% if attachment.file_type == 'image' %}
<a href="{{ attachment.file.url }}" target="_blank" class="d-inline-block">
<img src="{{ attachment.file.url }}"
alt="{{ attachment.file_name }}"
loading="lazy"
class="chat-media chat-media--image">
</a>
{% elif attachment.file_type == 'audio' %}
<audio controls class="w-100">
<source src="{{ attachment.file.url }}" type="{{ attachment.mime_type }}">
</audio>
<div class="small text-secondary">{{ attachment.file_name }}</div>
{% elif attachment.file_type == 'video' %}
<video controls preload="metadata" playsinline class="chat-media chat-media--video">
<source src="{{ attachment.file.url }}" type="{{ attachment.mime_type }}">
Ваш браузер не поддерживает видео.
</video>
<div class="small text-secondary">{{ attachment.file_name }}</div>
{% else %}
<a href="{{ attachment.file.url }}"
class="d-flex align-items-center text-decoration-none p-2 border rounded"
download="{{ attachment.file_name }}">
<i class="bi-file-earmark fs-3 text-primary me-2"></i>
<div>
<div class="fw-semibold">{{ attachment.file_name }}</div>
<div class="small text-secondary">{{ attachment.file_size|filesizeformat }}</div>
</div>
</a>
{% endif %}
</div>
{% endfor %}
</div>
{% endif %}
</div>
</div>
{% if message.author_id == user.id %}
<a class="ms-2 text-decoration-none" href="{% url 'employees:profile' %}">
<span class="mini-ava border">
{% if user.avatar %}<img src="{{ user.avatar.url }}" alt="" loading="lazy">
{% else %}<i class="bi-person"></i>{% endif %}
</span>
</a>
{% endif %}
</div>
{% endfor %}
{% else %}
<div class="text-center text-secondary py-5">Пока нет сообщений.</div>
{% endif %}
</div>
<!-- Композер -->
<div class="chat-composer">
<div id="typing" class="small text-secondary ps-1 pt-1 d-none">Собеседник печатает…</div>
<form id="chatForm"
class="composer-wrap"
method="post"
action="{% url 'communications:chat_detail' chat.pk %}"
enctype="multipart/form-data"
data-chat-composer="1"
data-chat-id="{{ chat.id }}"
data-upload-url="{% url 'api:v1:upload_message' %}"
data-me-id="{{ user.id }}"
data-me-name="{{ user.get_full_name|default:user.username }}"
data-me-avatar="{% if user.avatar %}{{ user.avatar.url }}{% endif %}"
data-profile-url="{% url 'employees:profile' %}"
data-detail-url-template="{% url 'employees:employee_detail' 0 %}">
{% csrf_token %}
<div class="message-field">
<!-- Кнопка вложений с выпадающим меню -->
<div class="dropdown message-attach">
<button type="button" class="btn btn-ghost btn-attachment message-icon-btn"
id="attachmentDropdown"
data-bs-toggle="dropdown"
aria-expanded="false"
title="Прикрепить файл">
<i class="bi-paperclip"></i>
</button>
<ul class="dropdown-menu dropdown-menu-start" aria-labelledby="attachmentDropdown">
<li>
<a class="dropdown-item" href="#" id="attachDocument">
<i class="bi-file-earmark text-primary me-2"></i>
Документ
</a>
</li>
<li>
<a class="dropdown-item" href="#" id="attachImage">
<i class="bi-image text-success me-2"></i>
Фото или видео
</a>
</li>
<li>
<a class="dropdown-item" href="#" id="attachCamera">
<i class="bi-camera text-info me-2"></i>
Камера
</a>
</li>
<li>
<a class="dropdown-item" href="#" id="attachAudio">
<i class="bi-mic text-danger me-2"></i>
Аудио
</a>
</li>
</ul>
</div>
<!-- Кнопка выбора эмодзи -->
<div class="dropdown message-emoji">
<button type="button" class="btn btn-ghost btn-emoji message-icon-btn"
id="emojiDropdown"
data-bs-toggle="dropdown"
data-emoji-toggle="1"
aria-expanded="false"
title="Вставить смайлик">
<i class="bi-emoji-smile"></i>
</button>
<div class="dropdown-menu dropdown-menu-start message-emoji-menu" aria-labelledby="emojiDropdown">
<emoji-picker data-emoji-picker class="chat-emoji-picker"></emoji-picker>
</div>
</div>
<!-- Поле ввода текста -->
<textarea name="content"
id="id_content"
class="form-control composer-textarea message-input"
rows="1"
placeholder="Напишите сообщение…"></textarea>
<!-- Кнопка отправки -->
<button type="submit" class="btn btn-primary btn-send message-send" title="Отправить">
<i class="bi-send-fill"></i>
</button>
</div>
<!-- Скрытые input для файлов -->
<input type="file" id="documentInput" class="d-none" accept=".pdf,.doc,.docx,.xls,.xlsx,.txt,.zip,.rar">
<input type="file" id="imageInput" class="d-none" accept="image/*,video/*" multiple>
<input type="file" id="cameraInput" class="d-none" accept="image/*" capture="environment">
<input type="file" id="audioInput" class="d-none" accept="audio/*">
<!-- Превью прикрепленных файлов -->
<div id="attachmentPreview" class="attachment-preview d-none"></div>
</form>
</div>
</article>
</div>
</div>
</div>
{% endblock %}
{% block extra_js %}
{{ block.super }}
<script type="module" src="https://cdn.jsdelivr.net/npm/emoji-picker-element@^1/index.js"></script>
<script type="module" src="{% static 'js/components/chatComposer.js' %}"></script>
<script type="module" src="{% static 'js/components/chatHistoryLoader.js' %}"></script>
{% endblock %}