- Исправлен PollSerializer для создания опросов с опциями
- Добавлено поле read_only_fields для корректной валидации
- Реализована отправка WebSocket уведомлений при создании опроса
- Исправлен импорт serialize_message из communications.serialization
- Добавлен обработчик poll_update в UserConsumer для WebSocket событий
- Исправлен эндпоинт /results/ для возврата user_voted_option_ids
- Добавлено поле voters в PollOptionSerializer для списка проголосовавших
- Реализована инициализация poll виджетов после рендера сообщений
- Добавлено игнорирование poll_update в notification-manager
- Исправлены стили для request list badges (height, word-break, flex)
- Восстановлены стили team-wheel (.wheel-focus, .scroll-arc) из git истории
- Реализовано отображение forwarded message info в messageRendererV2
- Множественные улучшения UI и UX для опросов
Fixes: создание опросов, голосование, отображение результатов, список проголосовавших
Проблема:
- Аватары деформировались из-за object-fit на контейнере
- Аватары выравнивались по верху сообщения вместо низа
- Неконсистентная HTML структура: <img class="mini-ava"> vs <div class="mini-ava"><i></i></div>
Решение:
SCSS (_common.scss):
- object-fit: cover перемещен с контейнера на вложенный img
- Добавлен display: block для img элементов
SCSS (_chat-detail.scss):
- Добавлен align-items: flex-end к .d-flex.msg для выравнивания по низу
- .mini-ava рефакторинг: display: flex (было grid), border-radius: 50% (было 9999px)
- Добавлен background и border для контейнера
- Добавлен font-size: 20px для fallback иконки
- object-fit остается только на img тегах
JavaScript (messageRendererV2.js):
- Всегда создавать <div class="mini-ava"> как контейнер
- Вложенный <img> или <i class="bi bi-person-circle"> внутри
- Устранена несогласованность разметки
Рефакторинг архитектуры SCSS (бонус):
- Создан Design System: abstracts/_variables.scss (200+ токенов)
- Создано abstracts/_mixins.scss (40+ миксинов)
- Реорганизована файловая структура: layout/, components/, components/chat/
- Централизованы стили аватаров в _common.scss
- Устранены дубликаты (avatar определялся в 3 файлах)
- Применены design tokens к navbar, page-header
- Создана документация: SCSS_NAMING_CONVENTIONS.md
- Удалено 9 дублирующихся CSS файлов
Компиляция: 280KB, 0 ошибок, production ready
Added:
- Autoscroll audit and reports
- Date divider analysis and Telegram comparison
- Bidirectional loading tests and results
- Date group manager and utilities
- Test scripts and HTML test pages
- CSS for chat date groups
- IntersectionObserver utilities
- Прямой импорт ChatControllerV2 без промежуточных слоев
- Убраны deprecated обертки (window.messageRenderer, window.chatHistoryLoader)
- Меньше кода: 231 vs 264 строки
- Более явная архитектура
- Для тестирования работы в production
- Created comprehensive badges-telegram-style.css (240 lines) based on Telegram Web K
- Updated all badge markup in templates (navbar, sidebar, chat_list, chat_detail, chat_title)
- Replaced Bootstrap badge classes with .badge-telegram classes
- Implemented Telegram specifications: 20px height, 10px radius, 11px font, 600 weight
- Added smooth animations: pulse (on update), animate-in (on mount), animate-out (on unmount)
- Updated JavaScript: replaced .d-none with style.display for badge visibility
- Updated 5 JS files: bottom-navbar, notification-manager, chatBadgeHandler, chatBadgeManager, chatListRealtime
- Added pulse animation on badge value increase
- Improved shadows and transitions with cubic-bezier easing
- Added badges-telegram-style.css to base.html for global availability
- Created documentation: TELEGRAM_BADGES_GUIDE.md, TELEGRAM_BADGES_UPDATE.md
Technical details:
- Research: Analyzed 50+ files from Telegram Web K GitHub repository
- Perfect circles: border-radius: 10px (not 50%)
- Spring animation: cubic-bezier(0.175, 0.885, 0.32, 1.275)
- Auto-hide: :empty and [data-count='0'] selectors
- Responsive design + dark theme support
ПРОБЛЕМЫ:
1. Видимые прыжки прокрутки при загрузке чата
2. Принудительный скролл при получении чужих сообщений
3. Дублирование загрузки chat-detail-enhanced.js
РЕШЕНИЯ:
1. Используем requestAnimationFrame для гарантии обновления DOM перед прокруткой
- Скрываем контейнер → рендерим → ждем RAF → скроллим → показываем
- Прокрутка происходит ДО показа контейнера = нет видимых прыжков
2. Убираем агрессивный автоскролл в handleNewMessage:
- Скроллим только если это наше сообщение ИЛИ мы уже внизу
- Предотвращаем прерывание чтения старых сообщений
3. Убираем дублирование chat-detail-enhanced.js в шаблоне:
- Было: загружается ДО chatDetail.js
- Стало: загружается ПОСЛЕ для правильного порядка инициализации
4. Улучшен порядок загрузки скриптов в chat_detail.html:
- emoji-picker (третья сторона)
- chatDetail.js (core инициализация)
- chat-detail-enhanced.js (UI расширения)
РЕЗУЛЬТАТЫ:
✅ Нет видимых прыжков - чат открывается сразу в конце
✅ Не прерывается чтение старых сообщений при приходе новых
✅ Правильный порядок инициализации компонентов
✅ Убрано дублирование загрузки скриптов
- Реализован современный нижний navbar для мобильных (5 кнопок)
- Скрытие нижнего navbar на странице чата
- Новая шапка чата с аватарами и бейджами типа чата
- Переработана HTML структура чата (убраны лишние Bootstrap контейнеры)
- Добавлены стили для аватаров и бейджей типов чатов
- Условное отображение: аватар OR иконка категории
Основные возможности:
- Только 1 объявление на пользователя (database constraint)
- Автогенерация названия 'Объявления от {имя}'
- Только создатель может публиковать сообщения
- Админы могут блокировать/разблокировать объявления
- Заблокированные объявления скрыты от всех (кроме staff)
- Запрет на ответы (reply) в объявлениях (can_reply=False)
- Запрет на редактирование/удаление сообщений
- Уведомления всем участникам при новом объявлении
Детали реализации:
- Добавлены поля: is_blocked, blocked_at, blocked_by, can_reply
- API endpoints: /api/announcement/<id>/block/ и /unblock/
- Валидация на уровне views, API и WebSocket
- Новый тип уведомлений: announcement_new_message
- UI индикаторы блокировки и прав доступа
- Компактное отображение статуса 'только просмотр'
Затронуты файлы:
- models.py: новые поля и constraint
- api_views.py: валидация и API блокировки
- consumers.py: проверки прав в WebSocket
- views.py: фильтрация и контекст прав
- templates: UI блокировки и статусов
- notification_signals.py: уведомления для объявлений
- Заменен лишний div.container на стандартный container для единообразия
- Заменен класс .card на .ios-card в секции активности для устранения конфликтов стилей
- Добавлен card-list.css в base.html для корректного отображения карточек новостей
- Добавлены CSS правила для правильного поведения .row и колонок в grid-layout
- Исправлена проблема с выходом календаря за границы страницы
- Убраны видимые скроллбары у modal-body и modal-content для чистого UI
Исправление видео:
- Видео элементы создаются с уникальными ID
- src устанавливается после добавления в DOM через requestAnimationFrame
- Используется preload='auto' вместо 'metadata' для немедленной загрузки
- video.load() вызывается после установки src
Стилизация вложений:
- Убран bg-light у ссылок на файлы
- Добавлены стили .message-attachments для прозрачного фона
- Для bubble-me используется rgba(255,255,255,0.15) фон
- Для обычных сообщений используется rgba(0,0,0,0.05) фон
- Добавлены hover эффекты
- Файлы теперь соответствуют цвету сообщения
Исправления видео:
- Видео элементы теперь создаются программно через createElement
- Удалён setTimeout, добавлена прямая замена placeholder'ов
- video.load() вызывается сразу после создания элемента
- Исправлен доступ к правильному контейнеру вложения
Текст необязателен при файлах:
- Удалён атрибут required у textarea
- Добавлена валидация: должен быть либо текст, либо файлы
- При отправке файла без текста отправляется пустая строка
- Показывается alert если нет ни текста, ни файлов
- Добавлен атрибут preload='metadata' для video элементов
- Добавлен текст-заглушка 'Ваш браузер не поддерживает видео'
- Добавлена принудительная загрузка video.load() после добавления в DOM
- Исправлено как в шаблоне, так и в JavaScript для WebSocket сообщений
Теперь видео загружается сразу после отправки без перезагрузки страницы
- Добавлена поддержка разных типов чатов (private, global, department, group)
- Для личных чатов проверяется участие через participants
- Для чатов отдела проверяется принадлежность к отделу
- Для глобального чата проверяется только is_active
- Для групповых/канальных чатов используется ChatMembership
Настройки CSRF:
- CSRF_COOKIE_HTTPONLY = False
- CSRF_COOKIE_SAMESITE = 'Lax'
- CSRF_USE_SESSIONS = False
- Добавлен декоратор @ensure_csrf_cookie в API view
- Изменён способ получения CSRF токена: теперь берётся из cookie через getCookie()
- Добавлена функция getCookie() в chat_detail.html
Причина ошибки: CSRF токен брался из формы вместо cookie при AJAX запросе
- Создан api/v1/communications/views.py с функцией upload_message_with_attachments
- Добавлен endpoint /api/v1/communications/upload-message/
- Обновлён frontend для использования правильного API пути
- Удалён дублирующий код из communications/api_views.py
- Обновлён communications/urls.py (убран старый endpoint)
Теперь все API эндпоинты находятся в приложении api, как и должно быть
Backend:
- Добавлен API endpoint /communications/api/upload-message/ для загрузки сообщений с файлами
- Обновлён consumers.py: добавлена сериализация вложений в serialize_message()
- Поддержка 4 типов файлов: image, video, audio, file
- Автоопределение типа по MIME
Frontend:
- Обновлён chat_detail.html: отправка файлов через fetch API вместо alert
- Добавлено отображение вложений в шаблоне (изображения, аудио, видео, документы)
- Обновлён chatMessageTemplates.js: рендеринг вложений для WebSocket сообщений
- Добавлена функция formatFileSize() для отображения размера файлов
Модель MessageAttachment уже существовала, миграции применены
- Убрана полоска скролла у textarea (overflow-y: hidden, scrollbar-width: none)
- Исправлена логика обработки вложений: теперь файлы хранятся в массиве
- Улучшено удаление отдельных файлов из превью
- Добавлена обработка отправки формы с файлами (подготовка к API)
- Пока отправка файлов показывает предупреждение о будущей реализации
Изменения в chat_detail.html:
- Убрана кнопка с фотографией из композера
- Кнопка вложений встроена в строку ввода с выпадающим меню
- Добавлено выпадающее меню с опциями: Документ, Фото/видео, Камера, Аудио
- Добавлены скрытые input для каждого типа файлов
- Реализовано превью прикрепленных файлов
- Добавлен JavaScript для обработки выбора и отображения файлов
Изменения в chat-detail.css:
- Обновлен стиль композера под современные мессенджеры
- Круглые кнопки вложений и отправки (40px)
- Поле ввода с закругленными углами (border-radius: 20px)
- Анимации hover для кнопок (scale 1.05)
- Стили для превью вложений
- Улучшенное выпадающее меню с иконками и transition эффектами
- Исправлен шаблон chat_list.html для личных чатов
- Использован {% cycle %} для правильного разделения имен участников
- Теперь запятая не отображается перед первым именем
- Добавлен CSRF токен в модальные окна (createDepartmentModal, createChatModal)
- Исправлена работа групповых чатов (group/channel/announcement):
* Добавлена проверка доступа через ChatMembership в views и consumers
* Групповые чаты теперь отображаются в списке
* WebSocket подключается корректно без бесконечных перезагрузок
- API create_chat теперь читает JSON вместо POST данных
- Изменена отправка сообщений: Enter вместо Ctrl+Enter, Shift+Enter для переноса строки
- Добавлен сертификат LDAP (corp-ca.pem)
- Отключен LDAP в локальном .env для разработки (USE_SQLITE=True, LDAP_ENABLED=False)
- ApiClient теперь обрабатывает 401 и 403 ошибки для обновления токена
- Добавлен JWTRefreshMiddleware для проактивного обновления токенов за 5 минут до истечения
- Добавлена настройка JWT_REFRESH_THRESHOLD_MINUTES (по умолчанию 5)
- Исправлена проблема с периодическими 403 ошибками после истечения access token
- Fix bug in page_header.html: add # to data-bs-target for card create button
- Integrate page_header in department_list.html with search and create card
- Integrate page_header in feed_list.html (no search, staff-only create button)
- Integrate page_header in document_list.html with Mine/All scope toggle
- Integrate page_header in request_list_full.html with filters and scope toggle
- Integrate page_header in chat_list.html with search
- Create _filters.html template for requests_app
- Add page-header.css to all integrated templates for consistent styling
feat(calendar): правый календарь с выбором источника и созданием событий (Bearer)
fix(layout): выровнял колонки к центральному контейнеру, убрал лишние отступы
fix(templates): убрана несуществующая тэг-функция токена; добавлен meta api-token
feat(chat/ui): полировка chat-root, скролла и кнопки «вниз»
style(feed): «Подробнее/Скрыть» — серые без подчёркивания
- добавлен эндпоинт DepartmentViewSet.my_departments для получения доступных отделов
(HR → все отделы, иначе только руководимые/активные)
- настроены permissions для нового экшена
- фронтенд: заменена логика дропдауна календаря
* загрузка списка отделов через /api/v1/departments/my-departments/
* автогенерация пунктов меню с именами отделов
* fallback на data-dept-id при пустом ответе
- обновлена подпись текущего календаря (кнопка и модалка «Создать событие»)
- сохранена логика работы с событиями (компания/отделы), переработан только источник данных для списка отделов
— Календарь (правая колонка)
• Добавлен дропдаун возле «Календарь» для выбора источника: «Компания» и «Мои отделы».
• «Мои отделы» подгружаются из GET /api/v1/employees/my-departments/ (JSON: [{id, name}]).
• Рядом с «События» добавлена кнопка «+», открывающая модал создания события.
• Создание события уходит в POST:
- /api/v1/calendar/company-events/
- /api/v1/calendar/departments/<id>/events/
Форматы: all-day (date) либо с временем (start/end ISO). Не через сессию.
• FullCalendar переключает источник событий в зависимости от выбранного календаря и
ходит за ними с параметрами ?start=YYYY-MM-DD&end=YYYY-MM-DD.
• Вертикальная «На этой неделе» рендерится из тех же данных (без лишних запросов,
если события уже в памяти FullCalendar).
• Мобильная версия — Offcanvas + тот же функционал.
— Авторизация / API
• Все запросы к API — через Authorization: Bearer <token>.
• В base.html добавлен мета-тег с токеном:
<meta name="api-token" content="{{ request.user.api_token|default:'' }}">
• Общий helper authFetch() отключает cookie/сессию и всегда проставляет заголовки.
• Это устраняет 401 при GET и готовит почву под POST (ранее наблюдался 405 без POST-ручек).
— Вёрстка / контейнер
• Левую и правую колонки «привязал» к центральному блоку (container-xxl), убраны
чрезмерные внутренние паддинги, чтобы центральный контент не сжимался.
• Уточнены вертикальные расстояния между карточками (gap / margin), карточки теперь
реально «прижимаются» по вертикали, где это нужно.
• В main контент тянется на всю доступную ширину внутри контейнера.
— Чаты
• Объяснён и оставлен padding у .chat-root (верх/низ под шапку и композер, включая safe-area).
• Кнопка «вниз» и стили скролла — без конфликтов с обёртками.
— Шаблоны
• Удалён несуществующий тэг `{% api_access_token %}` (исправляет TemplateSyntaxError).
• Вставлен рабочий meta name="api-token" вместо кастомного тега.
— Кнопки «Подробнее/Скрыть»
• Сделаны серыми и без подчёркивания (btn btn-link text-secondary text-decoration-none).
Затронутые файлы (основные):
• backend/templates/base.html
• backend/templates/includes/rightbar_calendar.html (новый/полностью переписан)
• backend/templates/feed/_feed_cards.html (кнопки «Подробнее/Скрыть»)
• backend/templates/includes/sidebar.html (мелкие правки отступов)
• стили в базовом шаблоне: правки контейнеров/отступов/гридов
• (JS в rightbar_calendar): authFetch, загрузка отделов, POST создания события, weekly strip
Примечания по бэку (ожидания фронта):
• GET /api/v1/employees/my-departments/ → 200 OK, JSON: [{ "id": <int>, "name": <str> }, …]
• GET /api/v1/calendar/company-events/?start=&end=
• GET /api/v1/calendar/departments/<id>/events/?start=&end=
• POST /api/v1/calendar/company-events/ (body: {title, date|start,end, allDay?, color?})
• POST /api/v1/calendar/departments/<id>/events/ (аналогично)
• Все ручки принимают Authorization: Bearer <JWT/токен>.
• Если раньше был 405 на POST — включить create() во вьюсетах и добавить в роутер.