130 Commits

Author SHA1 Message Date
db32f142fa fix: исправлена обработка строковых ID legacy календарей
Проблема: parseInt('legacy-company') возвращал NaN
Решение:
- Не парсим calendarId при toggle visibility
- Проверяем /^\d+$/ перед parseInt - только для числовых ID
- Добавлена валидация isNaN перед subscribe/unsubscribe
- Legacy календари (строковые ID) работают корректно
- Новые календари (числовые ID) конвертируются в number

Fixes: calendar_id=NaN запросы к API
2026-02-11 12:29:38 +03:00
7f6d2e8c13 feat: улучшена обработка ошибок валидации событий
- Добавлено подробное логирование payload и response при ошибках
- Специальная обработка 400 ошибок с детализацией по полям
- Alert показывает конкретные поля с ошибками валидации
- Помогает диагностировать проблемы типа start_date == end_date для all_day событий
2026-02-11 12:27:54 +03:00
67a331439c fix: исправлена обработка ID отделов и календарей
- calendarManager.js: правильное извлечение ID из объектов департаментов
  - departments возвращает массив {id, name}, а не просто ID
  - добавлена валидация и fallback для department_id
  - фильтрация null/undefined из visibleCalendarIds
  - добавлено подробное логирование для отладки

- calendarWidgetIntegration.js: убрано переопределение цветов событий
  - events сохраняют свой оригинальный цвет
  - добавлена только ссылка __calendar для метаданных

Fixes:
- department_id=NaN (неправильный парсинг ID отдела)
- calendar_id=NaN (null в visibleCalendarIds)
- legacy-dept-[object Object] (неправильный toString())
2026-02-11 12:22:32 +03:00
024d3085ea feat: унифицированный список календарей (legacy + новые)
- 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, оставлен только унифицированный список с чекбоксами
- Все календари теперь в одном списке с возможностью выбора через чекбоксы
- Цвета: Компания (красный), Личный (синий), Отделы (зеленый), новые календари (свои цвета)
2026-02-11 12:18:54 +03:00
13c298e51d fix(calendar): prevent empty calendar state from overriding legacy mode
- CalendarManager no longer updates state when no calendars found
- Shows friendly message 'Events displayed in classic mode'
- Integration stays uninitialized, uses legacy fetchEventsCombined
- This ensures existing events always visible without Calendar records
2026-02-11 11:27:57 +03:00
f8a8b3b2f2 fix(calendar): improve integration initialization and fallback to legacy mode
- 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).
2026-02-11 11:26:25 +03:00
6a99acf7e4 fix(calendar): resolve infinite loading on manage calendar button
- 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.
2026-02-11 10:59:10 +03:00
d9395def63 feat(calendar): integrate calendar manager with widget (Phase 3 - Part 2)
- 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
2026-02-11 10:10:30 +03:00
826298ff08 feat(calendar): add frontend components for calendar management (Phase 3 - Part 1)
Added API Integration:
- calendarsApi.js: Complete API wrapper for /api/v1/calendar/calendars/
  * CRUD operations (create, read, update, delete)
  * Subscribe/unsubscribe endpoints
  * My subscriptions endpoint
  * Cache invalidation support

Added UI Components:
- calendarManager.js: Calendar list management component
  * Displays available calendars with color indicators
  * Toggle calendar visibility (checkboxes)
  * Subscribe/unsubscribe buttons
  * Edit calendar button (for owners)
  * Real-time calendar filtering

- calendarManageModal.js: Calendar creation/editing modal
  * Create personal/public/department calendars
  * Edit calendar properties (title, description, color)
  * Delete calendar (owner only)
  * Department selection for department calendars
  * Auto-subscription settings

Added Styles:
- calendar-manager.scss: BEM-based styling
  * Calendar list with glassmorphism design
  * Color indicators and icons
  * Hover states and animations
  * Responsive design for mobile
  * Empty state styling

Updated Templates:
- calendar_desktop.html: Added calendar list UI
  * Replaced dropdown with collapsible calendar list
  * Added manage calendar button
  * Updated create event button icon

- calendar_modal_manage.html: New modal template
  * Form fields for calendar properties
  * Radio buttons for calendar types
  * Department selector
  * Auto-subscription checkboxes
  * Delete button for owners

- rightbar_calendar.html: Included new modal

Updated Imports:
- main.scss: Added calendar-manager component import

Status: Phase 3 Part 1 complete (UI components ready)
Next: Part 2 - Integration with calendarWidget.js for event filtering
2026-02-11 09:42:18 +03:00
9ea33128cc fix(ldap): убрана двойная инициализация LDAP login поля
- Удалена автоматическая инициализация из модуля
- Добавлено детальное логирование для отладки
- Инициализация остается только в employee_detail_scripts.html
2026-02-06 15:42:57 +03:00
3e4137daa4 feat(ldap): добавлено кэширование LDAP логина с возможностью обновления
- Добавлено поле Employee.username для кэширования LDAP логина
- API endpoint обновлен: поддержка кэша и force_refresh параметра
- Новый дизайн: убран blur, добавлена иконка обновления
- Автоматическая загрузка при инициализации (из кэша или LDAP)
- Визуальная индикация источника данных (cached/fresh)
- Доступно всем пользователям без ограничений
2026-02-06 15:33:48 +03:00
17c4e506cf refactor(employees): упрощена логика прав для LDAP endpoint
- Использована встроенная система пермишенов DRF вместо ручной проверки
- Добавлен ldap_info в required_perms_by_action с правом view_ldap_info
- Добавлен ldap_info в get_permissions() для применения IsSelfOrStaff
- Удалена избыточная ручная проверка прав внутри метода ldap_info()
- Проверка прав теперь выполняется автоматически через get_object()
- Пользователь может видеть свой LDAP логин (IsSelfOrStaff)
- Админы и пользователи с view_ldap_info могут видеть любые логины
2026-02-06 14:28:21 +03:00
c6cd2ba20d feat(employees): добавлен функционал отображения LDAP логина в профиле
- Добавлено поле с 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 (не хранятся в БД)
2026-02-06 14:24:31 +03:00
da7bf54c6c feat: Реализация полной функциональности опросов в чатах
- Исправлен 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: создание опросов, голосование, отображение результатов, список проголосовавших
2026-01-22 01:33:42 +03:00
fa0948c159 feat(communications): добавлена поддержка редактирования сообщений с изменением attachments
Изменения:
- Новый endpoint /api/v1/communications/messages/upload-temp/ для загрузки файлов без создания сообщения
- Поле message в MessageAttachment теперь nullable для временных вложений
- Логика перемещения attachments между сообщениями при редактировании
- Frontend использует upload-temp вместо создания временных сообщений
- Celery задача cleanup_orphaned_attachments для очистки забытых файлов (каждый час)
- Исправлена проблема с WebSocket обновлениями при редактировании
- Удалены debug логи после успешного тестирования

Технические детали:
- communications/models.py: добавлено null=True, blank=True к MessageAttachment.message
- communications/migrations/0022: миграция для изменения поля
- api/v1/communications/viewsets.py: новый action upload_temp() + логика перемещения в update()
- static/js/components/chatComposer.js: использование upload-temp вместо upload
- communications/tasks.py: новая Celery задача для очистки старых orphaned attachments
- eusrr_backend/settings.py: добавлен CELERY_BEAT_SCHEDULE с задачей очистки
2026-01-15 11:13:54 +03:00
53ec1a7b87 feat: implement Telegram-style image dimension pre-loading to fix auto-scroll
- Backend: added width/height fields to MessageAttachment model with Pillow auto-extraction
- Migration: 0021_add_image_dimensions + populate_image_dimensions management command
- API: serialize width/height in messageAttachmentSerializer and consumers.py
- Frontend: switched from CSS aspect-ratio to explicit pixel calculation (Telegram approach)
  * messageRendererV2.js: calculate exact dimensions (maxWidth=500px, proportional height)
  * Fixes scroll jump issue: images now reserve exact space before loading
  * Debug logging added to validate dimension rendering
- Testing: 18/18 images populated successfully with dimensions

This prevents scroll position shifts when images load after initial positioning,
implementing the same approach as Telegram Web.

Related: scroll positioning, image preloading, chat UX
2026-01-14 17:32:06 +03:00
9d5f70f16f feat(chat): реализован рендеринг изображений и видео в чате
Проблема:
- Изображения и видео отображались только как ссылки с иконками
- Нет preview изображений в composer
- Вся инфраструктура существовала (DB поля, CSS, WebSocket), но не была подключена

Реализация:

messageRendererV2.js (_renderAttachment):
- Добавлена проверка file_type === 'image': рендеринг <img class="chat-media chat-media--image">
- Добавлена проверка file_type === 'video': рендеринг <video class="chat-media chat-media--video"> с controls
- Используется thumbnail URL для изображений (если доступен), иначе file_url
- Атрибуты: loading="lazy", playsinline, preload="metadata" для оптимизации
- Файлы (PDF, Word, Audio) рендерятся как ссылки с иконками

chatComposer.js (renderPreview):
- Для изображений: показываем thumbnail через URL.createObjectURL
- Для других файлов: иконка (как раньше)
- Добавлен URL.revokeObjectURL при удалении для предотвращения утечек памяти
- Preview размеры: 60x60px с border-radius и object-fit: cover

CSS (_chat-detail.scss):
- Все стили .chat-media уже были готовы:
  * max-width: min(640px, 85vw)
  * border-radius: 12px
  * object-fit: cover (изображения) / contain (видео)
  * Responsive sizing для мобильных
- Стили применяются автоматически при рендеринге

Технические детали:
- WebSocket уже передает: file_type, file_url, thumbnail, mime_type
- Database модель MessageAttachment поддерживает thumbnail поле
- CSS классы .chat-media--image и .chat-media--video определены
- Lazy loading для изображений (атрибут loading="lazy")
- Video с preload="metadata" для быстрой загрузки первого кадра

Тестирование: Требуется загрузка реальных изображений/видео для проверки
2026-01-14 11:22:15 +03:00
d96d6633f0 chore: Add documentation and supporting files for bidirectional loading
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
2026-01-13 15:02:20 +03:00
f77626914e refactor: удалены .old файлы
- Deleted chatTestsV2.old.js
- Deleted postDetailModal.old.js

Финальная очистка deprecated кода завершена
2026-01-13 10:21:48 +03:00
2a012c297f refactor: удалены test/example файлы и deprecated templates
- Deleted chatHistoryLoader.js (@deprecated)
- Deleted chatControllerUsage.js (example)
- Deleted chatTests.js (manual test, 1732 lines)
- Deleted test_message_editing.html
- Deleted test_message_renderer.html
- Deleted chat_detail_scripts.html (неиспользуемый template)
2026-01-13 10:18:12 +03:00
697f8c6d37 refactor: удален legacy код, chatDetailV2 переименован в chatDetail
УДАЛЕНО (57KB, 1306 строк):
✅ scrollManager.legacy.js (23KB) - заменен на ScrollManagerV2
✅ messageLoader.legacy.js (16KB) - заменен на MessageLoaderV2
✅ messageRenderer.legacy.js (18KB) - заменен на MessageRendererV2
✅ chatDetail.js (старая версия) - имел deprecated обертки
✅ chatDetailTest.js - тестовый файл

ПЕРЕИМЕНОВАНО:
✅ chatDetailV2.js → chatDetail.js (стал основным)

ПРЕИМУЩЕСТВА:
- Прямой импорт ChatControllerV2
- Нет deprecated оберток
- Поддержка lastReadTimestamp fallback
- Правильная загрузка вокруг lastRead
- Минус 57KB мертвого кода
2026-01-13 10:14:11 +03:00
a8f7b8b461 fix: замена всех использований legacy классов на V2 2026-01-13 09:19:19 +03:00
f29bfc283d fix: исправлены импорты в messageEditing и chat/index 2026-01-13 09:18:25 +03:00
40fcdc8bd4 refactor: переименовал устаревшие файлы в .legacy
- messageLoader.js → messageLoader.legacy.js
- scrollManager.js → scrollManager.legacy.js
- messageRenderer.js → messageRenderer.legacy.js

Теперь используются V2 версии этих модулей
2026-01-13 09:16:38 +03:00
aba011731f feat: implement Telegram-style badges system
- 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
2026-01-12 17:37:41 +03:00
d5be62b34c refactor: восстановлены старые стили пузырей сообщений, аватаров и разделителей (pre-redesign)
- Восстановлен мягкий цвет пузырей: color-mix(92%) вместо жесткого var(--bs-primary)
- Аватары: border-radius 9999px + grid layout для идеальных кругов
- Разделители: правильный spacing, font-weight 700, pointer-events, color-mix прозрачность
- Добавлены оптимизации скролла: rootMargin 300px, limit 50 сообщений (Telegram-стиль)
- Добавлены новые модули: messageLoader, scrollManager, chatUtils
- Рефакторинг логики загрузки сообщений для предотвращения дубликатов
- Создана документация по архитектуре и тестированию
2026-01-12 17:21:41 +03:00
334d58c2f5 fix(messenger): полное устранение конфликтов прокрутки
ГЛУБОКИЙ АУДИТ выявил корневые причины прыжков:

НАЙДЕННЫЕ ПРОБЛЕМЫ:
1. chatMarkRead делал свою прокрутку ПОСЛЕ handleInitialMessages
   - mark.scrollIntoView() или autoscroll() в requestAnimationFrame
   - Конфликтовал с уже установленной прокруткой

2. Дублирование обработчика кнопки 'вниз':
   - chatMarkRead.js: btn.addEventListener
   - chat-detail-enhanced.js: scrollBtn.addEventListener
   - Два обработчика на одну кнопку!

3. Излишняя сложность в scrollToBottom():
   - Вызывал markReadApi.autoscroll() вместо прямой установки
   - Дополнительный уровень косвенности = источник багов

ИСПРАВЛЕНИЯ:
1. chatMarkRead НЕ скроллит при инициализации:
   - Divider создается, но не скроллится к нему
   - Прокрутка управляется ТОЛЬКО из handleInitialMessages
   - Единая точка ответственности

2. Убрано дублирование кнопки:
   - Удален обработчик из chat-detail-enhanced.js
   - Остался только в chatMarkRead.js

3. Упрощен scrollToBottom():
   - Прямая установка scrollTop вместо вызова markReadApi
   - Поддержка instant режима (без анимации)
   - Предсказуемое поведение

4. Добавлены комментарии и документация:
   - Пояснено почему НЕ скроллим в chatMarkRead
   - Описано назначение каждой функции прокрутки
   - Создан полный аудит в docs/completed/SCROLL_CONFLICTS_AUDIT.md

АРХИТЕКТУРА ПРОКРУТКИ:
✅ При загрузке: ТОЛЬКО handleInitialMessages
✅ Кнопка вниз: chatMarkRead.autoscroll()
✅ Новые сообщения: ТОЛЬКО если свое ИЛИ уже внизу
✅ Ввод текста: autoscroll() если внизу

РЕЗУЛЬТАТ:
✅ Нет прыжков - единая точка управления прокруткой
✅ Нет конфликтов - каждая функция делает одно
✅ Нет дублирования - один обработчик на событие
✅ Предсказуемость - прямые вызовы вместо косвенных

Файлы:
- userWebSocket.js: упрощен scrollToBottom
- chatMarkRead.js: убрана автопрокрутка при инициализации
- chat-detail-enhanced.js: убран дублирующий обработчик
- docs/completed/SCROLL_CONFLICTS_AUDIT.md: полный аудит
2026-01-12 11:11:02 +03:00
07cd6acaff fix(messenger): исправление прыжков при загрузке и дублирования скриптов
ПРОБЛЕМЫ:
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 расширения)

РЕЗУЛЬТАТЫ:
✅ Нет видимых прыжков - чат открывается сразу в конце
✅ Не прерывается чтение старых сообщений при приходе новых
✅ Правильный порядок инициализации компонентов
✅ Убрано дублирование загрузки скриптов
2026-01-12 11:03:41 +03:00
86174fca6f perf(messenger): оптимизация загрузки initial messages
Проблема: При открытии чата сначала загружались сообщения,
затем происходила видимая прокрутка вниз - плохой UX

Решение:
1. Скрываем контейнер (visibility: hidden) перед рендерингом
2. Используем DocumentFragment в renderMessages для батчевой вставки
3. Устанавливаем scrollTop ДО показа контейнера
4. Показываем контейнер - пользователь сразу видит конец диалога

Преимущества:
- Нет видимой прокрутки - сразу показывается конец чата
- Меньше reflow (1 вместо N при forEach)
- Быстрее рендеринг больших чатов (все элементы вставляются за раз)
- Лучший UX - нет "прыжков" при загрузке

Изменения:
- userWebSocket.js: добавлено скрытие/показ контейнера, прямая установка scrollTop
- messageRenderer.js: renderMessages использует DocumentFragment вместо множественных insertAdjacentHTML
2026-01-12 10:56:59 +03:00
845c59311c refactor(messenger): Phase 2 - унификация рендеринга сообщений
- Добавлен метод MessageRenderer.createMessageElement() для создания DOM элементов
- Обновлен chatComposer для использования MessageRenderer
- Обновлен chatHistoryLoader для использования MessageRenderer
- Обновлен chatDetail.js для передачи messageRenderer в модули
- Все модули теперь используют единый источник рендеринга
- Сохранена обратная совместимость через fallback на createMessageElement()

Phase 2 of 4: Rendering unification completed
2026-01-12 10:42:11 +03:00
5906bbaf86 refactor(chat): удалены неиспользуемые модули загрузки сообщений
- Удалён chatWebSocket.js (дублировал userWebSocket.js)
- Удалён initialMessagesLoader.js (не используется, загрузка через WS)
- Очищены импорты и закомментированный код в chatDetail.js
- Добавлен план рефакторинга в docs/in-progress/

Загрузка initial messages теперь происходит только через:
- userWebSocket.handleInitialMessages() - начальные сообщения
- chatHistoryLoader - подгрузка истории при скролле
2026-01-12 10:27:38 +03:00
42879e0981 feat(communications): Реализация управления уведомлениями чатов и оптимизация
- Добавлен API endpoint для включения/выключения уведомлений чата
- Реализован frontend для управления уведомлениями через меню чата
- Обновлены сигналы для проверки настроек notifications_enabled
- Стандартизирована логика уведомлений для всех типов чатов
- Оптимизированы запросы к БД (1 запрос вместо N для ChatUserSettings)
- Переведены детальные логи с INFO на DEBUG уровень

Файлы:
- backend/api/v1/communications/views.py (новый endpoint)
- backend/api/v1/urls.py (новый маршрут)
- backend/static/js/components/chatMenuActions.js (полная реализация)
- backend/communications/notification_signals.py (проверка настроек + оптимизация)
- backend/notifications/services.py (оптимизация логирования)
2026-01-08 14:39:40 +03:00
4a139180f7 feat: Auto-unsubscribe push on logout, force resubscribe on login
- Добавлена автоматическая отписка от Web Push при выходе
- При входе принудительно переподписываем пользователя
- Очищаем старую подписку из localStorage перед новой
- Это исправляет AbortError из-за старых подписок с другими VAPID ключами
2026-01-06 08:11:24 +03:00
932009780b fix: handle non-JSON error responses in requestCrudHandler 2026-01-05 14:35:19 +03:00
026bc46816 feat(department-roles): complete LDAP architecture redesign with RoleAssignment model
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
2025-12-30 10:35:34 +03:00
8ebe241174 feat(requests): добавлены фильтры по сотруднику и периоду дат 2025-12-29 15:32:27 +03:00
f6a1315d76 feat(requests): текст темы в списке заявлений теперь ссылка на страницу деталей 2025-12-29 14:47:13 +03:00
662273ac06 fix(requests): убрано двоеточие перед именем approver в списке заявлений 2025-12-29 14:40:45 +03:00
af31e7c591 refactor(requests): показывать имя approver вместо кнопок действий для уже обработанных заявлений 2025-12-29 14:38:48 +03:00
a417ddfb8f fix(requests): исправлена отправка уведомлений при создании заявления
ПРОБЛЕМА: Уведомления не отправлялись при создании нового заявления

ПРИЧИНА: Сигнал post_save срабатывал ДО установки ManyToMany связей (recipients, cc_users)

РЕШЕНИЕ:
- Добавлен сигнал m2m_changed для отслеживания установки recipients/cc_users
- При создании заявления оно помечается как ожидающее отправки уведомлений
- После установки recipients через .set() срабатывает m2m_changed и отправляет уведомления
- Используется глобальный set _pending_new_requests для отслеживания

УЛУЧШЕНИЯ:
- Добавлено подробное логирование процесса создания и отправки уведомлений
- Разные эмодзи-иконки для разных типов получателей (� адресовано, � в копии, ✅ на согласование)
- Все типы используют notification_type='request_new' (существующий в БД)

Файлы:
- notification_signals.py: добавлены сигналы m2m_changed
- serializers.py: добавлено логирование создания и установки recipients
2025-12-29 11:56:56 +03:00
1a5de8c61f fix(requests): убраны alert и немедленный reload при approve/reject на странице списка
- requestCrudHandler.js: заменены alert+reload на закрытие модального окна + событие
- requestList.js: добавлен обработчик события request:updated с отложенной перезагрузкой (500ms)
- Улучшен UX: модальное окно плавно закрывается, затем обновляется список
- Системное уведомление больше не появляется
2025-12-29 11:19:50 +03:00
087cff7901 fix: исключен approver из получателей уведомлений о решении
- Approver (тот кто принял решение) не получает уведомление о своем действии
- Исключены employee.id и approver_id из recipients при approve/reject
- Упрощен signal create_request_notifications (убран лишний запрос в БД)
- Теперь передается актуальный request_obj с установленным approver_id
2025-12-29 11:02:39 +03:00
eb70384928 � Мобильная оптимизация и новый тип заявления 'Отгул'
MOBILE FIXES:
- request-list.css: переделан мобильный layout с flexbox вместо grid
- page-header.css: оптимизирован padding/margin для мобильных экранов
- requestListHandler.js:
  * Добавлен AbortController с таймаутом (10/15 сек для мобильных)
  * Обработка offline/online событий сети
  * Информативные сообщения об ошибках (отсутствие интернета, таймаут)
  * Адаптивный Intersection Observer (rootMargin 200px для мобильных)
  * Лучшая обработка network failures

UI/UX:
- Скрыт счётчик на экранах < 480px
- Контент теперь отображается на всех разрешениях
- Исчезновение спиннера при ошибке сети
- Визуальное различие между ошибками загрузки

NEW FEATURE:
- Добавлен новый тип заявления 'Отгул' (day_off)
- Обновлена миграция 0009_alter_request_type
- Добавлен во все формы создания/редактирования
- Добавлен в фильтры списка
2025-12-29 10:00:32 +03:00
77e34e6e2b feat(avatar): center and enlarge face in overlay guide
Improved human silhouette overlay to better center the face and make it larger for optimal FaceID recognition.

Changes:

Face positioning:
- Moved face center from cy=120 to cy=150 (centered vertically)
- Face now occupies middle of frame instead of upper third
- Eyes align with horizontal center line at y=150
- Better balance between head and body in frame

Face size increase:
- Increased head ellipse from rx=75,ry=95 to rx=90,ry=115
- ~20% larger face area for better recognition
- More prominent facial features in final crop
- Improved face-to-frame ratio (70-80% coverage)

Body proportions updated:
- Neck: y=250 to y=285 (moved down)
- Shoulders: y=230 to y=285 (centered below face)
- Torso extends to bottom of frame
- More natural body proportions

Guide lines adjusted:
- Eye-level line: y=120 → y=150 (center of frame)
- Shoulder line: y=230 → y=285 (below center)
- Vertical center line unchanged (x=150)

UI text updates:
- Instructions emphasize face centering
- 'Расположите глаза на центральной линии'
- 'Голова занимает верхнюю половину фото'
- Changed bottom label from 'Пояс' to 'Низ кадра'

Benefits:
✓ Face more prominent in photo (better for FaceID)
✓ Centered composition (professional appearance)
✓ Balanced head-to-body ratio
✓ Easier for users to align correctly
✓ Improved recognition accuracy
2025-12-18 10:19:47 +03:00
28feac732e feat(avatar): change crop ratio to 3:4 portrait and improve overlay sync
Changed avatar cropper aspect ratio from 1:1 to 3:4 (portrait) for better FaceID recognition.
Improved human silhouette overlay synchronization with crop box during all interactions.

Key changes:

Aspect ratio:
- Changed from 1:1 (square) to 3:4 (portrait)
- Output size: 600x800px (instead of 800x800px)
- Better suited for full body positioning (head, shoulders, torso)

SVG overlay improvements:
- Updated viewBox from 400x400 to 300x400 (3:4 proportions)
- Redesigned human silhouette for portrait format
- Added torso outline up to waist level
- Additional guide line for shoulders
- Better proportions for face/head/shoulders alignment

Synchronization enhancements:
- Added cropstart, cropmove, cropend event handlers
- Overlay now follows crop box during all interactions:
  ✓ Moving the crop box
  ✓ Resizing the crop box
  ✓ Zooming the image
  ✓ Initial positioning
- Real-time position updates using getCropBoxData()

UI/UX updates:
- Updated instructions: 'Совместите тело с зелёным контуром'
- Face should occupy upper third of frame
- Portrait format 3:4 mentioned in requirements
- Changed label from 'Плечи' to 'Пояс' (waist)
- Templates updated with new size requirements (600x800px)

Technical details:
- minCropBoxHeight: 266px (200 * 4/3 for aspect ratio)
- preserveAspectRatio: none (for proper stretching)
- Event handlers ensure overlay follows crop box smoothly
- No performance impact from additional event listeners

Benefits:
✓ Better for FaceID (full body context)
✓ Standard portrait photo format
✓ More professional appearance
✓ Consistent with ID photo standards
✓ Improved overlay visibility and accuracy
2025-12-18 10:15:00 +03:00
c3a82378ff feat(avatar): add human silhouette overlay to avatar cropper
Added visual guide with human body contour overlay to help users properly position their face for FaceID.

Features:
- SVG overlay with human silhouette (head, neck, shoulders)
- Green dashed outline (70-80% face coverage guidance)
- Guide lines (vertical center, horizontal eye level)
- Text labels ('Верх головы', 'Плечи')
- Toggle checkbox to show/hide overlay
- Real-time overlay positioning synchronized with crop box

Technical implementation:
- SVG viewBox 400x400 with preserveAspectRatio
- Dynamic positioning using getCropBoxData()
- Event handlers: ready, crop, zoom
- Overlay updates on cropper interactions
- Pointer-events: none for non-intrusive UI

UI/UX improvements:
- Clear visual guidance for face positioning
- Non-intrusive overlay (can be toggled off)
- Color: #00ff00 (green) for high visibility
- Opacity: 0.8 for overlay, 0.4 for guide lines
- Auto-shown by default, user can disable

Benefits:
✓ Reduces user errors in face positioning
✓ Ensures consistent photo quality for FaceID
✓ Visual feedback for proper head/shoulders alignment
✓ Improves user experience and reduces support requests
2025-12-18 10:03:34 +03:00
53f1eb8096 feat(avatar): implement avatar cropper with FaceID requirements
Implemented avatar cropping component for FaceID system integration.

Features:
- Image cropper using Cropper.js library
- FaceID requirements validation (70-80% face coverage, 600x600px min)
- Square crop area (1:1 aspect ratio)
- Output: 800x800px JPEG at 92% quality
- Real-time size indicator
- Integrated in 2 places: registration + profile editor

Components:
- avatarCropperHandler.js: Main cropper module with AvatarCropper class
- Modal UI with Bootstrap 5
- FaceID instructions and guidelines
- File validation (size, format, dimensions)

Integration:
- auth/register.html: Registration form with cropper
- employees/employee_form_personal.html: Profile editor with cropper
- employees/employee_form_scripts.html: Scripts updated

Technical details:
- CDN: Cropper.js v1.6.1
- Min input size: 600x600px
- Max file size: 10 MB
- Formats: JPG, PNG, GIF
- Output format: JPEG (optimized for face recognition)

Validation:
✓ File format validation
✓ File size validation (10 MB max)
✓ Image dimensions validation (600x600px min)
✓ Real-time crop size indicator
✓ Error handling with user-friendly messages

UI/UX:
- Modal window with instructions
- Live preview of cropped image
- Size indicator (green/red based on requirements)
- Clear FaceID guidelines for users
- Responsive layout
2025-12-18 09:56:50 +03:00
9d47fc0c5c feat: add employee dismissal UI and filter inactive employees
- Added dismissal form on employee profile page with permissions
- Shows warning when dismissing (LDAP deactivation notice)
- Inactive employees filtered by default in employee list (active=true)
- Team wheel now shows only active employees
- Auto-submit filter form on status change
- Added redirect to set active=true by default on first visit
2025-12-17 09:14:34 +03:00
b6f1e09ba5 fix: prevent double form submission in employee edit form
Problem:
- When editing employee profile, got duplicate PATCH requests
- One request returned 502, another 200 OK
- Error appeared in console but operation succeeded
- Classic double-submit race condition

Root Cause:
initEmployeeForm() added new addEventListener('submit') on every call.
If function was called twice (e.g., on tab navigation or re-render),
two event handlers would fire simultaneously, sending parallel requests.

Solution:
1. Check if form already initialized via data-attribute
2. Set form.dataset.employeeFormInitialized = 'true' flag
3. Skip re-initialization if already initialized
4. Return API object for already initialized forms

This ensures only ONE submit handler per form, preventing:
- Duplicate PATCH requests
- 502 errors from race conditions
- Confusing UX with errors that don't break functionality

Now form submits cleanly with single request.
2025-12-08 13:37:20 +03:00
5d2d0841c8 feat: add detailed logging for request creation errors
Backend (API):
- Added logging of user ID and request data keys before validation
- Log validation errors with full details before raising exception
- Helps diagnose 400 Bad Request errors from users

Frontend (JS):
- Added console logging of all FormData entries before submit
- Improved error message display with field-specific validation errors
- Shows structured error messages instead of generic 'HTTP 400'

This will help identify why some users get 400 errors when creating requests.
2025-12-08 12:29:44 +03:00