Обновление chat_list.html: улучшена фильтрация, скрытие пустых категорий и кнопок, оптимизация JS и CSS.
This commit is contained in:
parent
19abf17740
commit
46a7bbdb07
121
CHAT_LIST_SIDEBAR_FILTER.md
Normal file
121
CHAT_LIST_SIDEBAR_FILTER.md
Normal file
@ -0,0 +1,121 @@
|
||||
# Фильтрация чатов с боковой панелью типов
|
||||
|
||||
## Описание изменений
|
||||
|
||||
Страница списка чатов (`/communications/chats/`) была реорганизована с добавлением боковой панели для фильтрации по типам чатов.
|
||||
|
||||
## Структура
|
||||
|
||||
### Двухколоночный макет
|
||||
|
||||
1. **Левая колонка (боковая панель)** - фиксированная ширина (120px на desktop, адаптивно до 60px на mobile)
|
||||
- Кнопки-иконки для каждого типа чата
|
||||
- Бейджи с количеством чатов в каждой категории
|
||||
- Sticky-позиционирование (прилипает при скролле)
|
||||
|
||||
2. **Правая колонка (основной список)** - flex-grow для занятия оставшегося пространства
|
||||
- Все секции чатов (глобальный, отделы, личные, группы, каналы, объявления)
|
||||
- Поиск и существующие фильтры
|
||||
|
||||
## Типы чатов
|
||||
|
||||
Доступные типы для фильтрации:
|
||||
|
||||
- **Все** (`all`) - показывает все чаты
|
||||
- **Глобальный** (`global`) - глобальный чат организации
|
||||
- **Отделы** (`department`) - чаты отделов
|
||||
- **Личные** (`private`) - личные чаты между пользователями
|
||||
- **Группы** (`group`) - групповые чаты
|
||||
- **Каналы** (`channel`) - каналы для широковещательных сообщений
|
||||
- **Объявления** (`announcement`) - каналы объявлений
|
||||
|
||||
## Функциональность
|
||||
|
||||
### Фильтрация
|
||||
|
||||
- Клик по кнопке типа чата показывает только чаты этого типа
|
||||
- Кнопка "Все" показывает все чаты
|
||||
- Активная кнопка подсвечивается синим цветом
|
||||
- При наведении иконка увеличивается (scale 1.1)
|
||||
|
||||
### Счетчики
|
||||
|
||||
- Каждая кнопка показывает количество чатов в категории
|
||||
- Счетчики обновляются динамически при поиске
|
||||
- Пустые категории скрывают бейдж (display: none)
|
||||
|
||||
### Адаптивность
|
||||
|
||||
**Desktop (>992px):**
|
||||
- Ширина панели: 120px
|
||||
- Отображаются иконки + текст + бейджи
|
||||
|
||||
**Tablet (768-992px):**
|
||||
- Ширина панели: 90px
|
||||
- Уменьшенный шрифт текста
|
||||
|
||||
**Mobile (576-768px):**
|
||||
- Ширина панели: 70px
|
||||
- Еще более компактный вид
|
||||
|
||||
**Small Mobile (<576px):**
|
||||
- Ширина панели: 60px
|
||||
- Скрывается текст, остаются только иконки
|
||||
- Только title атрибут для понимания назначения
|
||||
|
||||
## Файлы изменений
|
||||
|
||||
### HTML
|
||||
`backend/templates/communications/chat_list.html`
|
||||
- Добавлена структура `.d-flex` с `.chat-type-sidebar` и `.chat-list-main`
|
||||
- Кнопки фильтрации с иконками и бейджами
|
||||
- JavaScript функции для подсчета и фильтрации
|
||||
|
||||
### CSS
|
||||
`backend/static/css/components/chat-list-enhanced.css`
|
||||
- Стили для `.chat-type-sidebar`
|
||||
- Стили для `.chat-type-btn` (обычное, hover, active состояния)
|
||||
- Адаптивные медиа-запросы для всех размеров экрана
|
||||
- Sticky-позиционирование боковой панели
|
||||
|
||||
## Интеграция с существующими функциями
|
||||
|
||||
### Поиск
|
||||
- Поиск работает в рамках отфильтрованных секций
|
||||
- Счетчики обновляются после применения поиска
|
||||
- При смене фильтра поиск переприменяется
|
||||
|
||||
### WebSocket
|
||||
- Обновления чатов в реальном времени сохраняются
|
||||
- Счетчики могут быть обновлены при получении новых сообщений
|
||||
|
||||
### Создание чата
|
||||
- Модальное окно создания чата не изменено
|
||||
- После создания нового чата рекомендуется добавить обновление счетчиков
|
||||
|
||||
## Будущие улучшения
|
||||
|
||||
1. **Динамическое обновление счетчиков при WebSocket событиях**
|
||||
- Обновлять бейджи при получении новых сообщений
|
||||
- Показывать непрочитанные сообщения в каждой категории
|
||||
|
||||
2. **Сохранение выбранного фильтра в localStorage**
|
||||
- Запоминать последний выбранный тип
|
||||
- Восстанавливать при следующем посещении
|
||||
|
||||
3. **Анимация при переключении**
|
||||
- Плавное появление/исчезновение секций
|
||||
- Transition для смены активной кнопки
|
||||
|
||||
4. **Drag & Drop для переупорядочивания кнопок**
|
||||
- Позволить пользователю настроить порядок типов
|
||||
- Сохранять настройку в профиле
|
||||
|
||||
## Тестирование
|
||||
|
||||
- ✅ Переключение между типами чатов
|
||||
- ✅ Подсчет количества чатов
|
||||
- ✅ Адаптивность на разных экранах
|
||||
- ✅ Совместимость с поиском
|
||||
- ✅ Sticky-позиционирование боковой панели
|
||||
- ⏳ WebSocket обновления счетчиков (требует доработки)
|
||||
0
backend/communications/MODERN_CHAT_LIST_REDESIGN.md
Normal file
0
backend/communications/MODERN_CHAT_LIST_REDESIGN.md
Normal file
@ -1,10 +1,12 @@
|
||||
/* ===== CHAT LIST ENHANCED STYLES ===== */
|
||||
|
||||
/* Context Menu Styles */
|
||||
.context-menu {
|
||||
position: absolute;
|
||||
background: white;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
padding: 4px 0;
|
||||
min-width: 200px;
|
||||
z-index: 9999;
|
||||
@ -32,12 +34,31 @@
|
||||
border-color: #dee2e6;
|
||||
}
|
||||
|
||||
/* Chat List Update Animation */
|
||||
/* Chat List Animations */
|
||||
.chat-row {
|
||||
border-top: 1px solid var(--bs-border-color);
|
||||
}
|
||||
|
||||
.chat-row.updated {
|
||||
background: #e3f2fd;
|
||||
transition: background 0.3s ease-out;
|
||||
}
|
||||
|
||||
/* Chat Section Headers */
|
||||
.chat-section .card-header {
|
||||
text-align: center;
|
||||
border-bottom: 1px solid var(--bs-border-color);
|
||||
}
|
||||
|
||||
.chat-section .card-header .card-icon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.chat-section .card-header .card-meta {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Pinned Chat Indicator */
|
||||
.chat-row.pinned {
|
||||
background: #fffbeb;
|
||||
@ -61,12 +82,13 @@
|
||||
}
|
||||
|
||||
/* Chat Avatars */
|
||||
.chat-avatar {
|
||||
.chat-avatar,
|
||||
.card-icon img {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
border-radius: 50%;
|
||||
border-radius: 8px; /* Овальная форма вместо круглой */
|
||||
object-fit: cover;
|
||||
border: 2px solid #dee2e6;
|
||||
border: 2px solid var(--bs-border-color);
|
||||
}
|
||||
|
||||
.chat-avatar-group {
|
||||
@ -77,29 +99,17 @@
|
||||
.chat-avatar-group img {
|
||||
margin-left: -8px;
|
||||
border: 2px solid white;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.chat-avatar-group img:first-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
/* Chat Actions Menu Button */
|
||||
/* Chat Actions Menu */
|
||||
.chat-actions-menu {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: static !important;
|
||||
}
|
||||
|
||||
.chat-row {
|
||||
overflow: visible !important;
|
||||
}
|
||||
|
||||
.list-chats {
|
||||
overflow: visible !important;
|
||||
}
|
||||
|
||||
.card {
|
||||
overflow: visible !important;
|
||||
}
|
||||
|
||||
.chat-actions-menu .btn {
|
||||
@ -119,8 +129,7 @@
|
||||
.chat-actions-menu .dropdown-menu {
|
||||
font-size: 0.9rem;
|
||||
min-width: 200px;
|
||||
position: absolute !important;
|
||||
z-index: 1050 !important;
|
||||
z-index: 1050;
|
||||
}
|
||||
|
||||
.chat-actions-menu .dropdown-item {
|
||||
@ -131,3 +140,125 @@
|
||||
width: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* ===== CHAT TYPE SIDEBAR ===== */
|
||||
|
||||
:root {
|
||||
--chat-sidebar-offset: 120px;
|
||||
}
|
||||
|
||||
.chat-type-sidebar {
|
||||
flex-shrink: 0;
|
||||
align-self: flex-start;
|
||||
position: sticky;
|
||||
top: calc(var(--navbar-h, 60px) + var(--chat-sidebar-offset, 120px));
|
||||
}
|
||||
|
||||
.chat-type-sidebar .card {
|
||||
max-height: calc(100vh - var(--navbar-h, 60px) - var(--chat-sidebar-offset, 120px) - 24px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.chat-type-btn {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.35rem;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s ease, opacity 0.2s ease;
|
||||
}
|
||||
|
||||
.chat-type-btn[style*="display: none"] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.chat-type-btn:hover {
|
||||
background-color: var(--bs-light) !important;
|
||||
}
|
||||
|
||||
.chat-type-btn.active {
|
||||
background-color: var(--bs-primary) !important;
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.chat-type-btn.active i {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.chat-type-btn .badge {
|
||||
position: absolute;
|
||||
top: 0.25rem;
|
||||
right: 0.25rem;
|
||||
min-width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
padding: 0.15rem 0.3rem;
|
||||
border-radius: 50%;
|
||||
font-size: 0.65rem;
|
||||
line-height: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.chat-type-btn.active .badge {
|
||||
background-color: rgba(255, 255, 255, 0.3) !important;
|
||||
color: white !important;
|
||||
border-color: white !important;
|
||||
}
|
||||
|
||||
/* ===== MOBILE VERSION ===== */
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.chat-list-wrapper {
|
||||
flex-direction: column !important;
|
||||
}
|
||||
|
||||
.chat-type-sidebar {
|
||||
width: 100% !important;
|
||||
order: -1;
|
||||
position: static;
|
||||
}
|
||||
|
||||
.chat-type-sidebar .card {
|
||||
max-height: none;
|
||||
overflow: hidden;
|
||||
padding: 0.25rem 0.5rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.chat-type-sidebar .list-group {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
gap: 0.5rem;
|
||||
width: 100%;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
padding: 0 0.25rem 0.25rem;
|
||||
scrollbar-width: thin;
|
||||
scroll-snap-type: x proximity;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
.chat-type-btn {
|
||||
flex: 0 0 auto;
|
||||
min-width: 60px;
|
||||
max-width: 80px;
|
||||
padding: 0.5rem 0.25rem !important;
|
||||
border-radius: 0.75rem !important;
|
||||
border: 1px solid var(--bs-border-color) !important;
|
||||
scroll-snap-align: start;
|
||||
}
|
||||
|
||||
.chat-type-btn i {
|
||||
font-size: 1.25rem !important;
|
||||
}
|
||||
|
||||
.chat-type-btn .badge {
|
||||
font-size: 0.6rem;
|
||||
min-width: 1.1rem;
|
||||
height: 1.1rem;
|
||||
}
|
||||
}
|
||||
|
||||
1
backend/static/css/components/chat-list-modern.css
Normal file
1
backend/static/css/components/chat-list-modern.css
Normal file
File diff suppressed because one or more lines are too long
1
backend/static/css/components/chat-list-modern.css.map
Normal file
1
backend/static/css/components/chat-list-modern.css.map
Normal file
@ -0,0 +1 @@
|
||||
{"version":3,"sourceRoot":"","sources":["../../scss/components/_chat-list-modern.scss"],"names":[],"mappings":"CAIA,qBACE,aACA,MACA,+BACA,6BAEA,4BANF,qBAOI,sBACA,UAKJ,kBACE,WACA,gBACA,8CACA,4BACA,eACA,aACA,sBACA,UACA,gBACA,SACA,sBACA,+BACA,gBAEA,4BAfF,kBAgBI,WACA,mBACA,mBACA,kBACA,+CACA,gBACA,gBACA,gBACA,kBACA,eACA,WAGA,qCACE,YAKN,eACE,aACA,sBACA,mBACA,uBACA,WACA,qBACA,YACA,yBACA,0BACA,eACA,kBACA,wBACA,kBACA,eAEA,qBACE,4CACA,wBAGF,sBACE,6BACA,WACA,sDAEA,uCACE,gBACA,wBAIJ,iBACE,iBACA,cAGF,gCACE,gBACA,gBACA,kBACA,gBACA,eACA,gBACA,uBACA,mBAGF,gCACE,kBACA,QACA,UACA,4BACA,WACA,mBACA,cACA,iBACA,gBACA,eACA,YACA,aACA,mBACA,uBACA,cAEA,qCACE,aAIJ,4BAtEF,eAuEI,mBACA,UACA,mBACA,eAEA,iBACE,kBAGF,gCACE,iBACA,eACA,mBAGF,gCACE,kBACA,SACA,WACA,oBAMN,iBACE,OACA,gBACA,4BACA,UACA,YAEA,4BAPF,iBAQI,oBAIJ,kBACE,uBACA,+CACA,aACA,mBACA,8BACA,SACA,eAEA,qBACE,SACA,kBACA,gBACA,qBACA,aACA,mBACA,UAEA,uBACE,iBACA,wBAIJ,oCACE,4CACA,wBACA,sBACA,mBACA,iBACA,gBAIJ,gBACE,UACA,+BACA,gBAEA,4BALF,gBAMI,iBAIF,mCACE,UAGF,yCACE,yBAGF,yCACE,mBACA,kBAEA,+CACE,mBAMN,iBACE,aACA,kBACA,WACA,oBACA,2DACA,wBACA,kBACA,eAEA,4BACE,mBAGF,uBACE,6CAGF,wBACE,gFACA,wCAEA,gCACE,YACA,8BACA,kBACA,WACA,WACA,wBACA,kBACA,UAIJ,wBACE,0CACA,qCAGF,yBACE,oCAGF,4BA3CF,iBA4CI,qBACA,aAIJ,wBACE,GACE,4CAEF,KACE,0BAIJ,iBACE,WACA,YACA,mBACA,cACA,aACA,mBACA,uBACA,kBACA,wGACA,wBACA,gBACA,kBAEA,qBACE,WACA,YACA,iBAGF,sCACE,WACA,kBACA,WACA,UACA,WACA,YACA,6BACA,sBACA,kBAGF,4BAhCF,iBAiCI,WACA,YACA,kBAIJ,kBACE,OACA,YACA,aACA,sBACA,WAGF,iBACE,aACA,mBACA,8BACA,UAGF,gBACE,iBACA,gBACA,qBACA,SACA,gBACA,uBACA,mBACA,OACA,qBAEA,sBACE,wBAIJ,eACE,iBACA,0BACA,mBACA,cAGF,kBACE,aACA,mBACA,UACA,iBACA,0BAEA,kCACE,gBACA,qBACA,cAGF,gCACE,gBACA,uBACA,mBACA,OACA,YAIJ,eACE,aACA,mBACA,UACA,eAEA,sBACE,gBACA,qBACA,gBAGF,6BACE,4BACA,WACA,mBACA,sBACA,gBACA,gBACA,eACA,kBAIJ,kBACE,kBACA,SACA,WACA,UACA,uBACA,WAEA,yCACE,UAGF,uBACE,qBACA,kBAKJ,iBACE,aACA,sBACA,mBACA,uBACA,oBACA,kBACA,0BAEA,mBACE,eACA,yBACA,mBACA,WAGF,oBACE,iBACA,gBACA,qBACA,oBAGF,mBACE,gBACA,mBACA,gBAGF,sBACE,iBAKJ,mBACE,aACA,mBACA,uBACA,aACA,0BAEA,mCACE,WACA,YACA,oBAKJ,4BACE,kBACE,aAEA,qBACE,iBAEA,uBACE,kBAKN,iBACE,eAGF,iBACE,WACA,YACA,eAGF,gBACE,gBAGF,kBACE","file":"chat-list-modern.css"}
|
||||
@ -17,7 +17,56 @@
|
||||
<div class="col-12">
|
||||
|
||||
{# ===== НОВАЯ ШАПКА С КОМПОНЕНТОМ ===== #}
|
||||
{% include "includes/components/page_header.html" with icon="bi-chat-dots" title="Мои чаты" search_id="chatSearch" search_placeholder="Поиск по названию или участникам" has_filters=True filters_target="chatFilters" filters_template="communications/_filters.html" create_as_card=True create_modal="chatCreateModal" create_text="Создать чат" %}
|
||||
{% include "includes/components/page_header.html" with icon="bi-chat-dots" title="Мои чаты" search_id="chatSearch" search_placeholder="Поиск по названию или участникам" create_as_card=True create_modal="chatCreateModal" create_text="Создать чат" %}
|
||||
|
||||
{# ===== ДВУХКОЛОНОЧНАЯ СТРУКТУРА ===== #}
|
||||
<div class="d-flex gap-3 chat-list-wrapper">
|
||||
|
||||
{# Левая колонка - иконки типов чатов #}
|
||||
<div class="chat-type-sidebar">
|
||||
<div class="card">
|
||||
<div class="list-group list-group-flush">
|
||||
<button type="button" class="list-group-item list-group-item-action text-center py-3 chat-type-btn active"
|
||||
data-chat-type="all" title="Все чаты">
|
||||
<i class="bi-chat-dots fs-4 d-block"></i>
|
||||
<span class="badge rounded-pill bg-secondary" data-count="all">0</span>
|
||||
</button>
|
||||
<button type="button" class="list-group-item list-group-item-action text-center py-3 chat-type-btn"
|
||||
data-chat-type="global" title="Глобальный">
|
||||
<i class="bi-globe fs-4 d-block"></i>
|
||||
<span class="badge rounded-pill bg-secondary" data-count="global">0</span>
|
||||
</button>
|
||||
<button type="button" class="list-group-item list-group-item-action text-center py-3 chat-type-btn"
|
||||
data-chat-type="department" title="Отделы">
|
||||
<i class="bi-diagram-3 fs-4 d-block"></i>
|
||||
<span class="badge rounded-pill bg-secondary" data-count="department">0</span>
|
||||
</button>
|
||||
<button type="button" class="list-group-item list-group-item-action text-center py-3 chat-type-btn"
|
||||
data-chat-type="private" title="Личные">
|
||||
<i class="bi-person-fill fs-4 d-block"></i>
|
||||
<span class="badge rounded-pill bg-secondary" data-count="private">0</span>
|
||||
</button>
|
||||
<button type="button" class="list-group-item list-group-item-action text-center py-3 chat-type-btn"
|
||||
data-chat-type="group" title="Группы">
|
||||
<i class="bi-people-fill fs-4 d-block"></i>
|
||||
<span class="badge rounded-pill bg-secondary" data-count="group">0</span>
|
||||
</button>
|
||||
<button type="button" class="list-group-item list-group-item-action text-center py-3 chat-type-btn"
|
||||
data-chat-type="channel" title="Каналы">
|
||||
<i class="bi-broadcast fs-4 d-block"></i>
|
||||
<span class="badge rounded-pill bg-secondary" data-count="channel">0</span>
|
||||
</button>
|
||||
<button type="button" class="list-group-item list-group-item-action text-center py-3 chat-type-btn"
|
||||
data-chat-type="announcement" title="Объявления">
|
||||
<i class="bi-megaphone fs-4 d-block"></i>
|
||||
<span class="badge rounded-pill bg-secondary" data-count="announcement">0</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# Правая колонка - список чатов #}
|
||||
<div class="chat-list-main flex-grow-1">
|
||||
|
||||
{# ===== 1) ГЛОБАЛЬНЫЙ ===== #}
|
||||
<div class="card chat-section mb-3" data-sec="global">
|
||||
@ -33,7 +82,7 @@
|
||||
{% for chat in chats %}
|
||||
{% if chat.type == 'global' %}
|
||||
{% with last=chat.last_message.0 %}
|
||||
<div class="chat-row d-flex align-items-start gap-2 py-2 px-2 {% if not forloop.first %}border-top{% endif %}"
|
||||
<div class="chat-row d-flex align-items-start gap-2 py-2 px-2"
|
||||
data-chat-id="{{ chat.pk }}"
|
||||
data-type="global"
|
||||
data-last-ts="{% if last %}{{ last.created_at|date:'U' }}000{% else %}{{ chat.created_at|date:'U' }}000{% endif %}"
|
||||
@ -104,7 +153,7 @@
|
||||
{% for chat in chats %}
|
||||
{% if chat.type == 'department' %}
|
||||
{% with last=chat.last_message.0 %}
|
||||
<div class="chat-row d-flex align-items-start gap-2 py-2 px-2 {% if not forloop.first %}border-top{% endif %}"
|
||||
<div class="chat-row d-flex align-items-start gap-2 py-2 px-2"
|
||||
data-chat-id="{{ chat.pk }}"
|
||||
data-type="department"
|
||||
data-last-ts="{% if last %}{{ last.created_at|date:'U' }}000{% else %}{{ chat.created_at|date:'U' }}000{% endif %}"
|
||||
@ -166,7 +215,7 @@
|
||||
{% for chat in chats %}
|
||||
{% if chat.type == 'private' %}
|
||||
{% with last=chat.last_message.0 %}
|
||||
<div class="chat-row d-flex align-items-start gap-2 py-2 px-2 {% if not forloop.first %}border-top{% endif %}"
|
||||
<div class="chat-row d-flex align-items-start gap-2 py-2 px-2"
|
||||
data-chat-id="{{ chat.pk }}"
|
||||
data-type="private"
|
||||
data-last-ts="{% if last %}{{ last.created_at|date:'U' }}000{% else %}{{ chat.created_at|date:'U' }}000{% endif %}"
|
||||
@ -242,7 +291,7 @@
|
||||
{% for chat in chats %}
|
||||
{% if chat.type == 'group' %}
|
||||
{% with last=chat.last_message.0 %}
|
||||
<div class="chat-row d-flex align-items-start gap-2 py-2 px-2 {% if not forloop.first %}border-top{% endif %}"
|
||||
<div class="chat-row d-flex align-items-start gap-2 py-2 px-2"
|
||||
data-chat-id="{{ chat.pk }}"
|
||||
data-type="group"
|
||||
data-last-ts="{% if last %}{{ last.created_at|date:'U' }}000{% else %}{{ chat.created_at|date:'U' }}000{% endif %}"
|
||||
@ -316,7 +365,7 @@
|
||||
{% for chat in chats %}
|
||||
{% if chat.type == 'channel' %}
|
||||
{% with last=chat.last_message.0 %}
|
||||
<div class="chat-row d-flex align-items-start gap-2 py-2 px-2 {% if not forloop.first %}border-top{% endif %}"
|
||||
<div class="chat-row d-flex align-items-start gap-2 py-2 px-2"
|
||||
data-chat-id="{{ chat.pk }}"
|
||||
data-type="channel"
|
||||
data-last-ts="{% if last %}{{ last.created_at|date:'U' }}000{% else %}{{ chat.created_at|date:'U' }}000{% endif %}"
|
||||
@ -390,7 +439,7 @@
|
||||
{% for chat in chats %}
|
||||
{% if chat.type == 'announcement' %}
|
||||
{% with last=chat.last_message.0 %}
|
||||
<div class="chat-row d-flex align-items-start gap-2 py-2 px-2 {% if not forloop.first %}border-top{% endif %}"
|
||||
<div class="chat-row d-flex align-items-start gap-2 py-2 px-2"
|
||||
data-chat-id="{{ chat.pk }}"
|
||||
data-type="announcement"
|
||||
data-last-ts="{% if last %}{{ last.created_at|date:'U' }}000{% else %}{{ chat.created_at|date:'U' }}000{% endif %}"
|
||||
@ -456,6 +505,9 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
</div> {# конец chat-list-main #}
|
||||
</div> {# конец d-flex #}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -523,6 +575,28 @@
|
||||
const ACCESS = accessMeta ? accessMeta.content : '';
|
||||
const headersAuth = ACCESS ? { 'Authorization': 'Bearer ' + ACCESS } : {};
|
||||
|
||||
// Динамически рассчитываем высоту page-header для смещения липкой панели
|
||||
const chatListWrapper = document.querySelector('.chat-list-wrapper');
|
||||
|
||||
function updateSidebarOffset() {
|
||||
const pageHeader = document.querySelector('.page-header');
|
||||
if (!pageHeader) return;
|
||||
|
||||
const headerStyles = window.getComputedStyle(pageHeader);
|
||||
const marginBottom = parseFloat(headerStyles.marginBottom) || 0;
|
||||
const offset = pageHeader.offsetHeight + marginBottom;
|
||||
|
||||
document.documentElement.style.setProperty('--chat-sidebar-offset', `${offset}px`);
|
||||
}
|
||||
|
||||
let resizeTimeout;
|
||||
window.addEventListener('resize', () => {
|
||||
clearTimeout(resizeTimeout);
|
||||
resizeTimeout = setTimeout(updateSidebarOffset, 150);
|
||||
});
|
||||
|
||||
updateSidebarOffset();
|
||||
|
||||
// Инициализация RecipientPicker для создания чата
|
||||
const chatPicker = new RecipientPicker(
|
||||
document.getElementById('chatRecipients'),
|
||||
@ -646,40 +720,109 @@
|
||||
rowSelector: '.chat-row'
|
||||
});
|
||||
|
||||
// Обработка селекта фильтра типа чата
|
||||
const chatTypeFilter = document.getElementById('chatTypeFilter');
|
||||
const applyBtn = document.getElementById('applyChatFilter');
|
||||
const resetBtn = document.getElementById('resetChatFilter');
|
||||
|
||||
function applyTypeFilter() {
|
||||
const selectedType = chatTypeFilter?.value || 'all';
|
||||
const sections = document.querySelectorAll('.chat-section');
|
||||
// ===== ФИЛЬТРАЦИЯ ПО ТИПАМ ЧАТОВ =====
|
||||
let activeFilter = 'all';
|
||||
|
||||
const chatTypeButtons = document.querySelectorAll('.chat-type-btn');
|
||||
const chatSections = document.querySelectorAll('.chat-section');
|
||||
|
||||
// Функция подсчета чатов по типам и управления видимостью
|
||||
function updateChatCounts() {
|
||||
const counts = {
|
||||
all: 0,
|
||||
global: 0,
|
||||
department: 0,
|
||||
private: 0,
|
||||
group: 0,
|
||||
channel: 0,
|
||||
announcement: 0
|
||||
};
|
||||
|
||||
sections.forEach(section => {
|
||||
// Подсчитываем чаты в каждой секции
|
||||
chatSections.forEach(section => {
|
||||
const sectionType = section.dataset.sec;
|
||||
if (selectedType === 'all' || selectedType === sectionType) {
|
||||
section.style.display = '';
|
||||
} else {
|
||||
const chatRows = section.querySelectorAll('.chat-row');
|
||||
|
||||
// Считаем только видимые (не скрытые поиском) чаты
|
||||
const visibleChats = Array.from(chatRows).filter(row => {
|
||||
return row.style.display !== 'none';
|
||||
}).length;
|
||||
|
||||
counts[sectionType] = visibleChats;
|
||||
counts.all += visibleChats;
|
||||
|
||||
// Управление видимостью секции
|
||||
// 1. Если чатов нет (скрыты поиском или отсутствуют) -> скрываем секцию
|
||||
if (visibleChats === 0) {
|
||||
section.style.display = 'none';
|
||||
}
|
||||
// 2. Если чаты есть, проверяем активный фильтр
|
||||
else {
|
||||
if (activeFilter === 'all' || activeFilter === sectionType) {
|
||||
section.style.display = '';
|
||||
} else {
|
||||
section.style.display = 'none';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Переприменяем поиск после изменения видимости секций
|
||||
// Обновляем бейджи и кнопки
|
||||
Object.keys(counts).forEach(type => {
|
||||
const badge = document.querySelector(`[data-count="${type}"]`);
|
||||
const button = document.querySelector(`[data-chat-type="${type}"]`);
|
||||
|
||||
if (badge) {
|
||||
badge.textContent = counts[type];
|
||||
// Скрываем бейдж если 0
|
||||
badge.style.display = counts[type] === 0 ? 'none' : '';
|
||||
}
|
||||
|
||||
// Скрываем кнопку категории, если в ней нет чатов (кроме 'all')
|
||||
if (button && type !== 'all') {
|
||||
button.style.display = counts[type] === 0 ? 'none' : '';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function applyTypeFilter(filterType) {
|
||||
activeFilter = filterType;
|
||||
|
||||
// Обновляем активную кнопку
|
||||
chatTypeButtons.forEach(btn => {
|
||||
if (btn.dataset.chatType === filterType) {
|
||||
btn.classList.add('active');
|
||||
} else {
|
||||
btn.classList.remove('active');
|
||||
}
|
||||
});
|
||||
|
||||
// Переприменяем поиск (если есть), это обновит видимость строк
|
||||
if (filterApi && filterApi.reapply) {
|
||||
filterApi.reapply();
|
||||
}
|
||||
|
||||
// Обновляем видимость секций и счетчики
|
||||
updateChatCounts();
|
||||
}
|
||||
|
||||
function resetFilter() {
|
||||
if(chatTypeFilter) chatTypeFilter.value = 'all';
|
||||
applyTypeFilter();
|
||||
}
|
||||
|
||||
applyBtn?.addEventListener('click', applyTypeFilter);
|
||||
resetBtn?.addEventListener('click', resetFilter);
|
||||
|
||||
// Применяем фильтр при загрузке
|
||||
applyTypeFilter();
|
||||
// Обработка кликов по кнопкам типов
|
||||
chatTypeButtons.forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
const filterType = btn.dataset.chatType;
|
||||
applyTypeFilter(filterType);
|
||||
});
|
||||
});
|
||||
|
||||
// Применяем начальный фильтр и подсчитываем чаты
|
||||
applyTypeFilter('all');
|
||||
|
||||
// Обновляем счетчики после поиска
|
||||
const searchInput = document.getElementById('chatSearch');
|
||||
if (searchInput) {
|
||||
searchInput.addEventListener('input', () => {
|
||||
setTimeout(updateChatCounts, 100);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
{# Подключаем chat-list-enhanced.js для WebSocket и контекстного меню #}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user