- Удалены классические Django CBV views (ChatListView, ChatDetailView, ~400 строк)
- Удален MessageForm для HTML форм
- Удалены 6 HTML templates (chat_list.html, chat_detail.html, messengers/*)
- Утилиты _coerce_ts и user_can_access_chat перенесены в utils.py
- Обновлены импорты в communications/api/viewsets.py
Причина: Проект использует Next.js frontend + REST API + WebSocket.
Классические server-side rendered views больше не используются.
Результат:
- System check: 0 errors
- Код стал чище и проще для поддержки
- Нет зависимости от Django templates
- API и WebSocket остались полностью функциональны
- Создан модуль calendarEventDragDrop.js с классами DragDropState и CalendarUtils
- Реализовано перетаскивание событий между календарями через модальное окно
- Добавлена кнопка 'Переместить все' для массового перемещения
- Модальное окно отображает источник и цель перемещения
- Поддержка legacy-календарей (company/department/personal)
- Добавлены стили для подсветки drop-target (папки и календари)
- Backend: исправлена обработка null значений в serializer
- Backend: добавлен endpoint list_raw для получения немодерируемых событий
- Использованы стандартные Bootstrap стили для модалов
- Исправлено всплытие событий dragover/drop (stopPropagation)
- Эмодзи иконки для типов календарей (����)
- Toast уведомления об успехе/ошибках перемещения
Реализовано:
1. Множественный выбор календарей через чекбоксы
- Заменил <select> на чекбоксы для выбора нескольких календарей
- События создаются одновременно во всех выбранных календарях
- При редактировании - обновляется только первый выбранный
- Цветовые индикаторы для каждого календаря
2. Современный дизайн событий
- Двухцветная схема: левая граница = цвет календаря, фон = цвет события
- Полупрозрачный фон события (80%) для лучшей видимости
- Тени и hover эффекты для объёма
- Закругленные углы (4px)
- Плавные анимации (transform + transition)
3. Улучшенное отображение накладывающихся событий
- dayMaxEvents: автоматически показывать '+N еще'
- dayMaxEventRows: 4 - максимум 4 строки на день
- moreLinkClick: 'popover' - красивый popover для остальных
- eventMaxStack: 3 - до 3 событий в стеке
- eventOrder: сортировка по началу, длительности, алфавиту
4. Новый CSS файл calendar-improvements.css
- Современные стили для FullCalendar
- Улучшенный popover с градиентом
- Стили для выходных дней (розоватый фон)
- Hover эффекты на события
- Анимация fadeInEvent при загрузке
- Улучшенная читаемость текущего дня
- Адаптивность для мобильных устройств
- Кастомные scrollbar для списков
5. Передача цвета календаря в события
- calendar_color добавляется в extendedProps
- Используется __calendar из интеграции
- Отдельная обработка eventColor и calendarColor
6. Визуальные улучшения
- Текст события: font-weight 500, line-height 1.3
- Отступы: paddingLeft 6px (с цветом календаря)
- Тень текста для читаемости на любом фоне
- Паттерн для ежегодных событий (диагональные полосы)
- Индикатор текущего дня: синий круг с белым текстом
Результат:
✅ События визуально различимы по календарям (левая граница)
✅ Сохранён индивидуальный цвет события (основной фон)
✅ Множество событий в день отображается компактно (+N еще)
✅ Современный Material Design стиль
✅ Плавные анимации и hover эффекты
✅ Улучшенная читаемость и UX
Реализовано:
1. Селектор календаря в форме создания события
- Заменил статический текст 'Календарь: Компания' на <select>
- Показывает legacy календари (Компания, Личный, Отделы)
- Показывает новые Calendar записи из БД
- Разделитель между legacy и новыми календарями
2. Функция populateCalendarSelector()
- Динамическое заполнение селектора при открытии формы
- Использует window.calendarIntegration.calendars
- Фильтрует legacy календари (startsWith('legacy-'))
- Устанавливает значение по умолчанию по state.type
3. Обновлена логика создания/редактирования событий
- Убран старый код с state.type (personal/dept/company)
- Новая логика читает target_calendar из формы:
* 'company' → без доп. параметров (общие события)
* 'personal' → employee_id из meta[name='user-id']
* 'dept-{id}' → department_id
* numeric ID → calendar_id (новые календари)
- Поддержка смешанных ID (string для legacy, int для новых)
- Работает как для POST (создание), так и для PATCH (редактирование)
4. При редактировании события
- Автоматическое определение календаря из data:
* data.calendar_id → numeric ID
* data.employee_id → 'personal'
* data.department_id → 'dept-{id}'
* иначе → 'company'
- Селектор устанавливается в правильное положение
Результат:
✅ События можно создавать в любом календаре (legacy + новые)
✅ События можно редактировать и перемещать между календарями
✅ Сохранение calendar_id в БД для новых календарей
✅ Обратная совместимость с legacy системой (employee_id/department_id)
✅ UI с иконками � для новых календарей
- calendarManager.js: добавлена функция createLegacyCalendars() для создания pseudo-calendar объектов
- loadCalendars() теперь объединяет legacy (Компания, Личный, Отделы) и новые календари в единый массив
- calendarWidgetIntegration.js: обновлена логика fetchEventsForVisibleCalendars для работы с legacy ID
- legacy-company: все события без фильтров
- legacy-personal: события текущего сотрудника (employee_id)
- legacy-dept-{id}: события конкретного отдела (department_id)
- calendar_desktop.html: удален старый dropdown, оставлен только унифицированный список с чекбоксами
- Все календари теперь в одном списке с возможностью выбора через чекбоксы
- Цвета: Компания (красный), Личный (синий), Отделы (зеленый), новые календари (свои цвета)
- Restored original dropdown for Company/Department/Personal selection
- Added new 'My Calendars' collapsible list as ADDITIONAL feature
- Legacy dropdown remains primary navigation
- New calendar list is collapsed by default, accessed via list-check icon
- Two systems work in parallel: legacy (dropdown) + new (multi-calendar)
This fixes the broken calendar type selection functionality.
- Fixed calendar_scripts.html to wait for DOM ready before initialization
- Added retry logic if widget not found on first attempt
- Changed integration fallback: show ALL events when no calendars configured
- Changed behavior: if no calendars selected, show all events (legacy mode)
- This ensures backward compatibility with existing events
User feedback: Integration wasn't initializing on page load, button '+' didn't work.
Solution: Auto-init with retry + always show events (no empty state).
- Removed duplicate modal opening via data-bs-toggle attribute
- Changed to programmatic opening via openForCreate() only
- Added calendar:edit event listener for edit functionality
- Updated button selector from data-bs-target to data-action
Issue: Button had both Bootstrap modal trigger and JS event listener,
causing double initialization and infinite loading state.
Solution: Use only programmatic modal opening through calendarManageModal API.
- Created calendarWidgetIntegration.js as integration layer
- Modified calendarWidget.js to use new calendar filtering system
- Added fallback to legacy mode for backward compatibility
- Updated calendar_scripts.html to initialize integration
- Implemented fetchEventsForVisibleCalendars with calendar_id filtering
- Added event deduplication and color override from calendars
- Exposed window.calendarIntegration for global access
- Added comprehensive logging for debugging
Technical decisions:
- Non-invasive integration via optional chaining
- Callback-based synchronization (onCalendarToggle, onCalendarsChange)
- Parallel event loading with Promise.all()
- Automatic legacy mode when no calendars configured
- Добавлено поле Employee.username для кэширования LDAP логина
- API endpoint обновлен: поддержка кэша и force_refresh параметра
- Новый дизайн: убран blur, добавлена иконка обновления
- Автоматическая загрузка при инициализации (из кэша или LDAP)
- Визуальная индикация источника данных (cached/fresh)
- Доступно всем пользователям без ограничений
- Использована встроенная система пермишенов DRF вместо ручной проверки
- Добавлен ldap_info в required_perms_by_action с правом view_ldap_info
- Добавлен ldap_info в get_permissions() для применения IsSelfOrStaff
- Удалена избыточная ручная проверка прав внутри метода ldap_info()
- Проверка прав теперь выполняется автоматически через get_object()
- Пользователь может видеть свой LDAP логин (IsSelfOrStaff)
- Админы и пользователи с view_ldap_info могут видеть любые логины
- Добавлено поле с LDAP логином (sAMAccountName) в профиле сотрудника
- Реализован blur-эффект с раскрытием по клику для безопасности
- Создан API endpoint /api/v1/employees/{id}/ldap-info/ для получения LDAP данных
- Добавлен новый пермишен employees.view_ldap_info
- Стили: SCSS компонент _ldap-login.scss с анимациями и состояниями
- JavaScript: класс LdapLoginField с AJAX запросами и обработкой ошибок
- Доступ только для staff или пользователей с правом view_ldap_info
- Данные запрашиваются из Active Directory on-demand (не хранятся в БД)
- Исправлен 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 иконка категории
- Добавлена bottom navbar с 5 кнопками (Главная, Уведомления, Поиск, Календарь, Ещё)
- Панель отображается только на мобильных устройствах (до 768px)
- Использованы системные CSS переменные и стили
- Удалена кнопка гамбургера из верхней панели (дублирование функционала)
- Добавлена синхронизация бейджей уведомлений
- Поддержка светлой и темной темы
- Backdrop-filter для полупрозрачности в iOS-стиле
- Добавлена автоматическая отписка от Web Push при выходе
- При входе принудительно переподписываем пользователя
- Очищаем старую подписку из localStorage перед новой
- Это исправляет AbortError из-за старых подписок с другими VAPID ключами
- Add WebPushSubscription model for storing push subscriptions
- Generate VAPID keys for Web Push authentication
- Add API endpoints for push subscribe/unsubscribe
- Create Service Worker (sw.js) for handling push events
- Create JavaScript module for managing push subscriptions
- Integrate send_web_push_notification into NotificationService
- Add Service Worker registration to base.html
- Update requirements.txt with pywebpush package
BREAKING CHANGES:
- Removed OU=Roles from LDAP structure, role groups now created directly in department OU
- Role groups use ROLE_<Name> naming convention (similar to DEP_, POS_)
New features:
- RoleAssignment model: allows assigning roles to ANY employee, not just department members
- New API endpoints: /assign/, /revoke/, /assignments/ on DepartmentRoleViewSet
- Permission system updated: has_dept_perm() checks RoleAssignment first
- Employee profile shows external roles with 'внешняя роль' badge
- Employee list filter includes role-only employees with department_relation field
- Role assignment modal now searches all employees via AJAX
LDAP changes:
- _ensure_role_group(): creates ROLE_<Name> group in department OU
- assign_role(), revoke_role(): manage RoleAssignment + LDAP sync
- rename_role_group(), delete_role_group(): full lifecycle support
- Removed all references to OU=Roles
Migrations:
- 0035: Create RoleAssignment model
- 0036: Data migration from EmployeeDepartment.role
Tests:
- 7 new tests for role assignment (all passing)
Docs:
- 9 detailed reports on architecture, models, API, LDAP, tests
- Полная переделка шаблона request_detail.html с единообразными стилями
- Новая структура: основная карточка (8 колонок) + боковая панель комментариев (4 колонки)
- Единообразные иконки типов заявлений (calendar-check, capsule, umbrella-fill и т.д.)
- Единообразные бейджи статуса и типа как в списке
- Информационные блоки для автора, описания, получателей, одобрившего
- Форма комментариев с правильным стилем message-field--compact
- Emoji picker с поддержкой dropdown-menu поверх контента
- AJAX обработка отправки и удаления комментариев
- Новый CSS файл request-detail.css с адаптивностью
- Добавлена функция _add_type_display() в views.py для отображения типа
Улучшения:
- Единство дизайна со страницей списка заявлений
- Правильное позиционирование emoji picker (fixed positioning)
- Sticky боковая панель с комментариями
- Форма комментария как в ленте (message-field--compact)
- Кнопки одобрения/отклонения с правильными иконками
Исправлено:
- Добавлена правильная структура card-header с аватаром 48x48px
- Добавлены все необходимые детали: получатели, копия, согласующий
- Исправлены стили для badge и card-body
- Добавлена корректная форма комментария с emoji picker и auto-expand
- Подключены правильные JavaScript компоненты
- Исправлен формат дат (d.m.Y H:i)
- Добавлена передача can_process в контексте view
Добавлено:
- Иконки для каждого раздела (получатели, копия, согласующий)
- Ссылки на карточки сотрудников
- Правильные Badge с subtle цветами
- Форма комментария с message-field компонентом
- Approver (тот кто принял решение) не получает уведомление о своем действии
- Исключены employee.id и approver_id из recipients при approve/reject
- Упрощен signal create_request_notifications (убран лишний запрос в БД)
- Теперь передается актуальный request_obj с установленным approver_id