445 Commits

Author SHA1 Message Date
db69efb329 fix: удален дубликат кода в CalendarSubscriptionSerializer
- Удалены повторяющиеся строки в методе validate()
- Fixes: IndentationError: unexpected indent на строке 487
2026-02-11 12:33:06 +03:00
b03ef1308e fix: удален дубликат urlpatterns в api/v1/urls.py
- Удалены повторяющиеся строки с notifications и router.urls
- Исправлена длинная строка с MessageViewSet
- Fixes: IndentationError: unexpected indent
2026-02-11 12:30:49 +03:00
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
299559ab4f fix(calendar): restore legacy calendar type dropdown selector
- 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.
2026-02-11 11:45:07 +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
f7d3cd8c91 feat(calendar): add comprehensive test suite for new Calendar API
- Added make_event fixture for creating test events
- Added CalendarSubscription.color_override null=True migration
- Added computed fields (is_personal, is_global, is_department) to CalendarSerializer
- Fixed CalendarViewSet.create() to use CalendarSerializer for response
- Created test_new_calendar_api.py with 29 comprehensive tests:
  * 11 CRUD tests (list, create, retrieve, update, delete)
  * 6 subscription tests (subscribe, unsubscribe, my-subscriptions)
  * 3 event filtering tests (calendar_id filtering, legacy separation)
  * 3 visibility tests (public, private, department)
  * 6 permission tests (is_owner, can_user_view, can_user_edit)

Test results:
- ✅ 34 legacy tests passing (backward compatibility maintained)
- ✅ 29 new tests passing (new functionality validated)
- ✅ Total: 63 tests passing
2026-02-11 08:35:24 +03:00
526ebe6b76 fix(calendar): add calendar_id to serializer fields and fix related_name for departments
- Added calendar_id to CalendarEventWriteSerializer Meta.fields
- Fixed related_name from employee_departments to departments_links
- Updated make_event fixture to support calendar parameter
- Added comprehensive test suite for new Calendar and CalendarSubscription API
- Added fixtures: make_calendar, make_subscription, auth_client, user fixtures
- All legacy tests passing (34 passed)
2026-02-11 08:27:00 +03:00
870a08ee66 feat(calendar): add API layer for optional calendars
Phase 2: API implementation
- Updated CalendarEventWriteSerializer to support calendar_id parameter
- Added CalendarSerializer and CalendarWriteSerializer for Calendar CRUD
- Added CalendarSubscriptionSerializer for subscription management
- Created CalendarViewSet with subscribe/unsubscribe actions
- Created CalendarSubscriptionViewSet for managing subscriptions
- Updated CalendarEventsViewSet to support calendar_id filtering
- Added CalendarManager with get_available_for_user() method
- Added permission methods to Calendar model (is_owner, can_user_view, can_user_edit, can_user_manage)
- Registered new ViewSets in URL routing

API endpoints:
- GET /api/v1/calendar/calendars/ - list available calendars
- POST /api/v1/calendar/calendars/ - create new calendar
- GET /api/v1/calendar/calendars/{id}/ - retrieve calendar details
- PATCH /api/v1/calendar/calendars/{id}/ - update calendar
- DELETE /api/v1/calendar/calendars/{id}/ - delete calendar
- POST /api/v1/calendar/calendars/{id}/subscribe/ - subscribe to calendar
- POST /api/v1/calendar/calendars/{id}/unsubscribe/ - unsubscribe from calendar
- GET /api/v1/calendar/calendars/my-calendars/ - list user's calendars
- GET /api/v1/calendar/subscriptions/ - list user's subscriptions
- POST /api/v1/calendar/subscriptions/ - create subscription
- GET /api/v1/calendar/events/?calendar_id={id} - filter events by calendar

Backward compatibility: all legacy endpoints continue to work
2026-02-11 08:18:47 +03:00
ef734f19c0 feat(calendar): add optional Calendar and CalendarSubscription models
- Add Calendar model with visibility settings and auto-subscription
- Add CalendarSubscription model for user subscriptions with permissions
- Add optional 'calendar' field to CalendarEvent (null=True for backward compatibility)
- Update CalendarEvent logic: if calendar=NULL uses legacy (department/employee), if calendar set uses new logic
- Add admin interfaces for Calendar and CalendarSubscription
- Add inline subscription editor in Calendar admin
- Migration 0011: create new tables and add calendar FK to events
- Full backward compatibility: existing code works without changes
2026-02-11 08:12:28 +03:00
d5929e846c test: обновление тестов API коммуникаций и опросов 2026-02-11 07:30:27 +03:00
4dae6400d5 fix: исправлен URL создания чата на корректный REST endpoint /api/v1/communications/chats/ вместо /create/ (ошибка 405) 2026-02-09 12:19:45 +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
10f7287ae5 fix(employees): добавлено LDAP соединение для LdapRepository
- LdapRepository требует активное соединение в конструкторе
- Добавлен вызов _conn() для получения LDAP соединения
- Исправлена ошибка: TypeError: LdapRepository.__init__() missing 1 required positional argument: 'conn'
- Теперь используется: LdapRepository(_conn())
2026-02-06 14:57:37 +03:00
77238c1154 fix(employees): исправлен доступ к LdapSyncState 2026-02-06 14:54:38 +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
a9f38afd55 test(realtime): add comprehensive WebSocket consumer tests
- Add tests/realtime/ module with test suite for UserConsumer
- test_user_consumer.py: connection, auth, ping, chat management, typing
- test_routing.py: WebSocket URL routing validation
- test_integration.py: broadcast, notifications, multi-user scenarios
- conftest.py: fixtures for WebSocket testing (ws_communicator, test_chat)

Tests cover:
- Authentication (anonymous rejection, authenticated access)
- Chat subscription and management (open/close, access control)
- Ping/pong keepalive mechanism
- Typing indicators
- Message actions (send, mark_read)
- Integration with channel layer for broadcasts
- Multi-user WebSocket scenarios
2026-01-21 18:04:40 +03:00
8c34b30f02 Update test configuration and add test refactoring utilities 2026-01-21 18:00:11 +03:00
4c398856ae Update API tests: improve fixtures and LDAP optional mode support 2026-01-21 18:00:11 +03:00
624784b788 Remove outdated test files and fixtures 2026-01-21 18:00:11 +03:00
ae340f4d03 refactor: extract serialize_message to separate module and remove obsolete ChatConsumer classes
- Create communications/serialization.py with serialize_message function
- Update imports in realtime/consumers.py, api views and serializers
- Remove communications/consumers.py (ChatConsumer and ChatListConsumer now obsolete)
- UserConsumer in realtime/consumers.py handles all WebSocket connections
- Add seed_test_data management command for database population
2026-01-21 17:59:46 +03:00
8363cbd541 feat: Complete CSS to SCSS migration (100%)
- Migrated all 28 CSS components to SCSS architecture
- Deleted 29 source CSS files (kept only compiled app.css & bootstrap-custom.css)
- Created 30 SCSS component modules in scss/components/
- Cleaned 24 templates from duplicate CSS links
- Updated main.scss with new imports (layout-spacing, spacing-utils, rightbar-calendar)
- Filled empty _navbar.scss with content
- Refactored css/ folder: removed obsolete files, updated README.md
- Created comprehensive documentation (migration complete, quick reference, reports)

Migration Statistics:
- Components migrated: 28 (~4,768 lines SCSS)
- CSS files deleted: 29
- Templates cleaned: 24
- app.css size: 95 KB (compiled from SCSS)
- bootstrap-custom.css: 301 KB
- Total: 396 KB

Phase 6 Complete: 100% SCSS architecture achieved ✅
2026-01-15 13:48:11 +03:00
3f5781cd52 refactor(communications): переименование viewsets.py в views.py
Структурная реорганизация:
- Удален legacy views.py (оставалась только get_available_reactions)
- Переименован viewsets.py → views.py (теперь основной модуль)
- ViewSets теперь в стандартном location (views.py)

Обновлены импорты:
- api/v1/urls.py: from .communications.views import ViewSets
- Удален неиспользуемый endpoint: communications/reactions/available/

Удалено legacy FBV (было 18 функций):
- get_user_chats() → ChatViewSet.list()
- upload_message_with_attachments() → MessageViewSet.upload()
- load_chat_messages() → ChatViewSet.messages()
- load_chat_messages_around() → ChatViewSet.messages_around()
- add_reaction() → MessageViewSet.react()
- remove_reaction() → MessageViewSet.unreact()
- pin_chat() → ChatViewSet.pin()
- toggle_chat_notifications() → ChatViewSet.notifications()
- forward_messages() → MessageViewSet.forward()
- bulk_delete_messages() → MessageViewSet.bulk_delete()
- edit_message() → MessageViewSet.update()
- delete_message() → MessageViewSet.destroy()
- create_chat() → ChatViewSet.create()
- get_available_reactions() → удалена (не используется)

Итоги:
- Удалено 1,722 строк дублирующего кода (-56%)
- 100% переход на ViewSets
- Соответствие DRF best practices

Ref: docs/completed/LEGACY_CODE_CLEANUP.md
2026-01-15 12:16:15 +03:00
ba4a7f1eb9 refactor(communications): удален дублирующий poll_views.py
- Удален файл api/v1/communications/poll_views.py (399 строк)
- Причина: полностью дублировал функциональность PollViewSet
- Все 4 функции перенесены в PollViewSet (завершено 14 января)

Удаленные FBV → ViewSet методы:
- create_poll() → PollViewSet.create()
- vote_poll() → PollViewSet.vote()
- close_poll() → PollViewSet.close()
- get_poll_results() → PollViewSet.results()

Ref: docs/completed/LEGACY_CODE_CLEANUP.md
2026-01-15 12:16:03 +03:00
9849c4084e refactor(communications): обновление кода для новых моделей
Обновлен consumers.py:
- serialize_message: использование forward_metadata вместо forwarded_from_*
- _get_message: prefetch forward_metadata и MessageEditHistory
- _edit_message: создание MessageEditHistory записей вместо JSON

Обновлен serializers.py:
- MessageCreateSerializer: удалены forwarded_from_* из fields
- Чистка Meta.fields от удаленных полей модели

Обратная совместимость:
- WebSocket payload не изменился (forwarded_from структура сохранена)
- API endpoints работают без изменений

Ref: COMMUNICATIONS_API_REFACTORING.md
2026-01-15 12:15:55 +03:00
18cbce9bf9 refactor(communications): нормализация Message модели (Phase 2)
Создание новых нормализованных моделей:
- MessageEditHistory: история редактирования (вместо JSON поля)
- MessageForwardMetadata: метаданные пересылки (OneToOne связь)

Миграции:
- 0025: создание MessageEditHistory и MessageForwardMetadata
- 0026: миграция данных из JSON/полей в новые таблицы (без потерь)
- 0027: удаление старых денормализованных полей

Удалены из Message:
- edit_history (JSONField) → MessageEditHistory модель
- forwarded_from_author (FK)
- forwarded_from_message_id (IntegerField)
- forwarded_from_created_at (DateTimeField)
- forwarded_from_chat_name (CharField)
- thread_reply_count (IntegerField)

Обновлена admin.py:
- Добавлен MessageEditHistoryInline
- Зарегистрированы новые модели

Преимущества:
- Соблюдение 3НФ (нормальная форма Бойса-Кодда)
- FK constraints для целостности данных
- Удобные запросы через ORM (вместо JSON)
- История редактирования с автором и временем
- Счетчик пересылок (forward_count)

Ref: docs/reports/MODELS_ARCHITECTURE_AUDIT.md
2026-01-15 12:15:44 +03:00
474bdd16ac refactor(communications): удалены неиспользуемые модели
- Удалены 4 модели: ForwardedMessage, MessageReply, ChatAccessPermission, CrossChatMessage
- Миграции: 0023 (удаление MessageReply и др.), 0024 (удаление ForwardedMessage)
- Обоснование: модели не использовались в коде, 0 записей в БД
- Уменьшено количество моделей с 15 до 11 (-27%)
- Очищено ~400 строк мертвого кода

Ref: docs/reports/MODELS_ARCHITECTURE_AUDIT.md
2026-01-15 12:15:32 +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
e4e2f3cdf3 fix(chat): убраны лишние отступы у изображений и видео
Проблема:
- У медиафайлов были большие пустые поля сверху и снизу
- Причины: padding/margin от .attachment-item и .bubble .attachment-item a
- inline стили конфликтовали с CSS

Решение:

CSS (_chat-detail.scss):
- Добавлен модификатор .attachment-item--media для медиа-контейнеров
- .attachment-item--media имеет margin-bottom: 0 (вместо 8px)
- Вложенные <a> в медиа-контейнерах: padding: 0, line-height: 0
- .chat-media: display: block (было inline-block), vertical-align: top
- Селектор для файлов: .attachment-item:not(.attachment-item--media)

JavaScript (messageRendererV2.js):
- Добавлен класс attachment-item--media к изображениям и видео
- Убраны inline стили (style="display: block; ...")
- Чистая разметка без конфликтов

Результат: медиафайлы отображаются плотно без пустых полей
2026-01-14 11:31:15 +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
f00f7c0be5 fix(ui): исправлен рендеринг аватаров - устранена деформация и выравнивание
Проблема:
- Аватары деформировались из-за 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
2026-01-14 11:18:35 +03:00
c1f83e1357 test: ChatControllerV2 - use classList for scroll element visibility 2026-01-13 16:15:10 +03:00
6e4348b4f5 test: UnreadBadgeManager - use classList instead of inline styles 2026-01-13 16:15:02 +03:00
b92fd8849e test: Template changes - add badge element to button 2026-01-13 16:14:33 +03:00
ac271df622 test: CSS changes only - badge styles 2026-01-13 16:07:25 +03:00
682714ba15 refactor: Clean architecture + unified badge styling
PART 1: Extract UnreadBadgeManager (Clean Architecture)

Created managers/unreadBadgeManager.js:
✅ Single Responsibility: Manages ONLY badge UI
✅ Methods: increment(), reset(), setCount(), hasUnread()
✅ Handles all DOM manipulation internally
✅ Reusable and testable

Cleaned ChatControllerV2:
❌ Removed 150+ lines of UI logic
❌ Removed duplicate systems (_unreadCount + _newMessagesCount)
❌ Removed deprecated methods:
   - _incrementUnreadBadge()
   - _resetUnreadBadge()
   - _showNewMessagesIndicator()
   - _hideNewMessagesIndicator()
   - _findOrCreateNewMessagesButton()
✅ Now uses: this.badgeManager.increment/reset()

PART 2: Unified Badge Styling

Aligned unread-badge with sticky-date design:
- Same typography (font-size, weight, line-height)
- Same spacing (padding: 6px 12px)
- Same border-radius (10-12px)
- Same shadows (box-shadow hierarchy)
- Same transitions (0.2s ease)
- Same animation patterns (scale, hover effects)
- Added border: 2px for contrast with background

Result:
✅ Consistent UI across all badges
✅ Matches Telegram-like design system
✅ Better visual hierarchy
✅ ~950 lines in Controller (was 1125)
✅ Clean separation of concerns
2026-01-13 15:36:17 +03:00
115af05a0b fix: Syntax error in loadLatest method
Fixed incorrect method placement that broke loadNewer.
loadLatest now properly placed after loadNewer method.
2026-01-13 15:24:38 +03:00
1dd01ae2c1 feat: Smart button visibility and load-to-present on click
Two types of bottom:
1. Historical bottom (hasMoreAfter=true):
   - Button ALWAYS visible (even at scroll bottom)
   - Click → loads latest 50 messages to present time
   - Renders and scrolls to bottom

2. Present bottom (hasMoreAfter=false):
   - Button hidden when isNearBottom()
   - Click → smart scroll (to unread or bottom)

Changes:
- _setupSmartButtonVisibility() monitors hasMoreAfter
- _handleScrollButtonClick() loads history if hasMoreAfter
- Added public loadLatest() method
- Emits 'chat:messagesLoaded' event for UI updates

Result:
- Historical view: button always shows, loads to present
- Present view: button hides at bottom, smart unread scroll
- No more stuck in history without way to present
2026-01-13 15:21:52 +03:00
3ba512a937 feat: Smart scroll button with unread badge and auto-scroll
Implements Telegram-like behavior for new messages:

1. Smart Auto-scroll (WS messages):
   ✅ User at bottom + present time → auto-scroll to new message
   ✅ User NOT at bottom + present time → show unread badge
   ❌ Historical view (hasMoreAfter=true) → no action

2. Smart Scroll Button (#scrollBtn):
   - Shows unread badge when new messages arrive
   - Click behavior:
     * Has unread + lastReadId → scroll to first unread
     * No unread → scroll to bottom
   - Auto-hides badge when user scrolls to bottom

3. Badge Styling:
   - Red badge with count on top-right of button
   - Pulse animation on appearance
   - Button subtle pulse when has unread

Key Logic:
- Only auto-scroll when hasMoreAfter=false (present time)
- Historical bottom (hasMoreAfter=true) doesn't trigger auto-scroll
- Badge resets on: manual scroll to bottom, button click
- Respects user's scroll position unless at bottom
2026-01-13 15:07:52 +03:00
0e2a08fb38 chore: Add remaining test files and utilities
Added:
- Test chat API script
- Date group CSS and JS utilities
- IntersectionObserver modules
- Bidirectional loading test files
- Autoscroll testing checklist
2026-01-13 15:02:29 +03:00