- Интегрирован django-service-objects 0.7.1 для Service Layer Pattern
- Перенесена вся логика дней рождения из calendar_app → employees:
* UpsertBirthdayEventService - создание/обновление событий ДР
* DeleteBirthdayEventService - удаление событий ДР
* BulkSyncBirthdaysService - массовая синхронизация
* signals_birthday.py - автосинхронизация через post_save/post_delete
* management command sync_birthdays - CLI команда синхронизации
* тесты (11 tests, 6 passing)
- Обновлена миграция 0013_migrate_to_django_scheduler:
* Добавлен ШАГ 5: автоматическая синхронизация дней рождения (131 событие)
* Исключены старые события ДР из миграции (source.endswith(':birthday'))
* Исправлены имена сотрудников (f'{last_name} {first_name}' вместо get_full_name())
* Добавлен цвет #FFC107 для событий дней рождения
- Очищен calendar_app от birthday сервисов:
* Удалены calendar_app/services/birthday_events.py
* Удалены calendar_app/signals_scheduler.py
* Удалены calendar_app/management/commands/sync_birthdays.py
* Удалены calendar_app/tests/test_services.py
* Обновлен apps.py - удалены ссылки на signals_scheduler
- API улучшения:
* Добавлен api/v1/schedule/permissions.py (IsCalendarOwnerOrAdmin)
* Обновлен views.py с правильными permissions
- Документация:
* DJANGO_SERVICE_OBJECTS_INTEGRATION.md - руководство по Service Layer
* CALENDAR_MIGRATION_ANALYSIS.md - анализ миграции
* MODERNIZATION_ANALYSIS.md - анализ модернизации кодовой базы
Цель: подготовка к будущему удалению calendar_app, централизация
логики сотрудников в employees приложении.
Refs: #birthday-refactoring
- Добавлена проверка Array.isArray() в NotificationCenter
- Добавлена защита от non-array в useNotifications хук
- Исправлен импорт date-fns/locale для версии 4+
- В случае ошибки API устанавливается пустой массив []
- Добавлена проверка Array.isArray() в NotificationCenter
- Добавлена защита от non-array в useNotifications хук
- Исправлен импорт date-fns/locale для версии 4+
- В случае ошибки API устанавливается пустой массив []
Frontend:
- Установлен Sonner для toast уведомлений
- Создан Service Worker (public/sw.js) для Web Push
- Реализован хук useWebPush для управления подписками
- Добавлены утилиты Web Push (lib/push.ts)
- Создан компонент NotificationCenter с UI
- Интегрирован в AppShell (header desktop + mobile)
- Добавлен Toaster в Providers
API методы:
- getVapidPublicKey() - получение VAPID ключа
- subscribePush() - создание подписки
- unsubscribePush() - удаление подписки
- getPushSubscriptions() - список подписок
Возможности:
- � Web Push (работают даже когда вкладка закрыта)
- � NotificationCenter с badge и историей
- � Toast уведомления (Sonner)
- ⚙️ Настройки push подписок
- � Респонсивный дизайн
Документация: frontend/NOTIFICATIONS_IMPLEMENTATION.md
Frontend:
- Установлен Sonner для toast уведомлений
- Создан Service Worker (public/sw.js) для Web Push
- Реализован хук useWebPush для управления подписками
- Добавлены утилиты Web Push (lib/push.ts)
- Создан компонент NotificationCenter с UI
- Интегрирован в AppShell (header desktop + mobile)
- Добавлен Toaster в Providers
API методы:
- getVapidPublicKey() - получение VAPID ключа
- subscribePush() - создание подписки
- unsubscribePush() - удаление подписки
- getPushSubscriptions() - список подписок
Возможности:
- � Web Push (работают даже когда вкладка закрыта)
- � NotificationCenter с badge и историей
- � Toast уведомления (Sonner)
- ⚙️ Настройки push подписок
- � Респонсивный дизайн
Документация: frontend/NOTIFICATIONS_IMPLEMENTATION.md
- Добавлен django-push-notifications==3.1.0 в requirements.txt
- Настроен PUSH_NOTIFICATIONS_SETTINGS в settings.py
- Создана миграция данных 0008_migrate_webpush_to_library.py (9 подписок)
- Обновлен send_web_push_notification() в services.py (150→25 строк)
- Обновлены API endpoints: subscribe_push, unsubscribe_push, get_push_subscriptions
- Ожидается 10-50x ускорение при отправке push уведомлений
- Обратная совместимость: API не изменилось, старая модель сохранена
- Полный отчет: backend/docs/reports/DJANGO_PUSH_NOTIFICATIONS_INTEGRATION.md
- Добавлен django-push-notifications==3.1.0 в requirements.txt
- Настроен PUSH_NOTIFICATIONS_SETTINGS в settings.py
- Создана миграция данных 0008_migrate_webpush_to_library.py (9 подписок)
- Обновлен send_web_push_notification() в services.py (150→25 строк)
- Обновлены API endpoints: subscribe_push, unsubscribe_push, get_push_subscriptions
- Ожидается 10-50x ускорение при отправке push уведомлений
- Обратная совместимость: API не изменилось, старая модель сохранена
- Полный отчет: backend/docs/reports/DJANGO_PUSH_NOTIFICATIONS_INTEGRATION.md
Расширение поиска: добавлены модели procurement, documents, notifications, schedule.
Всего 12 моделей доступны для полнотекстового поиска через django-watson.
Расширение поиска: добавлены модели procurement, documents, notifications, schedule.
Всего 12 моделей доступны для полнотекстового поиска через django-watson.
- Добавлены 5 новых моделей в watson search:
* ProcurementRequest (заявки на закупку)
* Equipment (оборудование)
* Document (документы)
* Notification (уведомления)
* Event из django-scheduler (расписание)
- Backend API: форматтеры для новых типов моделей
- Frontend: добавлены иконки и названия для новых типов
- Тесты: исправлены фикстуры, все 13 тестов проходят
- Индексы: 3895 записей проиндексировано
Итого: 12 моделей доступны для полнотекстового поиска
- Добавлены 5 новых моделей в watson search:
* ProcurementRequest (заявки на закупку)
* Equipment (оборудование)
* Document (документы)
* Notification (уведомления)
* Event из django-scheduler (расписание)
- Backend API: форматтеры для новых типов моделей
- Frontend: добавлены иконки и названия для новых типов
- Тесты: исправлены фикстуры, все 13 тестов проходят
- Индексы: 3895 записей проиндексировано
Итого: 12 моделей доступны для полнотекстового поиска
Backend:
- Created REST API endpoint /api/v1/search/
- Added SearchResultSerializer and SearchResponseSerializer
- Implemented search_api_view with model formatters (employee, department, post, request, chat, message, event)
- Registered search endpoint in api/v1/urls.py
- Added permission check for requests (HR can see all, users only their own)
Frontend:
- Added SearchResponse, SearchResult, SearchModelType types
- Added apiClient.search() method in lib/api.ts
- Completely rewrote search/page.tsx:
* Single API call instead of 6 parallel requests
* Cleaner UI with icons for each result type
* Real-time search with loading states
* Meta information display (email, phone, status, etc.)
* Better performance and UX
Benefits:
- Frontend: 220 lines → 178 lines (19% reduction)
- 1 API request instead of 6 (83% less network calls)
- Server-side full-text search via watson (better relevance)
- Unified search results with metadata
Testing:
- API endpoint verified at /api/v1/search/?q=test
- Returns JSON with query, results, counts, total
- Requires authentication (IsAuthenticated decorator)
Backend:
- Created REST API endpoint /api/v1/search/
- Added SearchResultSerializer and SearchResponseSerializer
- Implemented search_api_view with model formatters (employee, department, post, request, chat, message, event)
- Registered search endpoint in api/v1/urls.py
- Added permission check for requests (HR can see all, users only their own)
Frontend:
- Added SearchResponse, SearchResult, SearchModelType types
- Added apiClient.search() method in lib/api.ts
- Completely rewrote search/page.tsx:
* Single API call instead of 6 parallel requests
* Cleaner UI with icons for each result type
* Real-time search with loading states
* Meta information display (email, phone, status, etc.)
* Better performance and UX
Benefits:
- Frontend: 220 lines → 178 lines (19% reduction)
- 1 API request instead of 6 (83% less network calls)
- Server-side full-text search via watson (better relevance)
- Unified search results with metadata
Testing:
- API endpoint verified at /api/v1/search/?q=test
- Returns JSON with query, results, counts, total
- Requires authentication (IsAuthenticated decorator)
- Удалено поле last_read_at из ChatReadState, используется только last_read_message FK
- Исправлена логика автоотметки: отмечается последнее ЗАГРУЖЕННОЕ сообщение, а не последнее в чате
- Убран вызов chat.mark_read() при WebSocket open_chat (отмечал последнее в чате вместо загруженного)
- Добавлен signal для мониторинга изменений ChatReadState
- Исправлена логика скролла: убран автоскролл при дозагрузке вниз
- Добавлена проверка разрывов при WebSocket сообщениях (предотвращает пропуск сообщений)
- Реализована автоотметка WebSocket сообщений (только если пользователь внизу)
- Асимметричная загрузка: 24 контекста + 6 новых сообщений
- Добавлен ScrollableMessageList компонент для корректной работы скролла
- Миграции: 0028 (удаление last_read_at), 0029 (индекс на last_read_message)
- Удалено поле last_read_at из ChatReadState, используется только last_read_message FK
- Исправлена логика автоотметки: отмечается последнее ЗАГРУЖЕННОЕ сообщение, а не последнее в чате
- Убран вызов chat.mark_read() при WebSocket open_chat (отмечал последнее в чате вместо загруженного)
- Добавлен signal для мониторинга изменений ChatReadState
- Исправлена логика скролла: убран автоскролл при дозагрузке вниз
- Добавлена проверка разрывов при WebSocket сообщениях (предотвращает пропуск сообщений)
- Реализована автоотметка WebSocket сообщений (только если пользователь внизу)
- Асимметричная загрузка: 24 контекста + 6 новых сообщений
- Добавлен ScrollableMessageList компонент для корректной работы скролла
- Миграции: 0028 (удаление last_read_at), 0029 (индекс на last_read_message)
- Добавлены API методы в api.ts:
* getChats, getChat, createChat
* getChatMessages, sendMessage, sendMessageWithFiles
* updateMessage, deleteMessage
* markChatAsRead, togglePinChat, toggleChatNotifications
- Создан компонент NewChatModal:
* Поиск и выбор сотрудников
* Поддержка приватных и групповых чатов
* Автоматическое название для групп
- Интегрирована кнопка 'Новый чат' в Chatscope UI
- При создании чата автоматически открывается диалог
- Добавлены API методы в api.ts:
* getChats, getChat, createChat
* getChatMessages, sendMessage, sendMessageWithFiles
* updateMessage, deleteMessage
* markChatAsRead, togglePinChat, toggleChatNotifications
- Создан компонент NewChatModal:
* Поиск и выбор сотрудников
* Поддержка приватных и групповых чатов
* Автоматическое название для групп
- Интегрирована кнопка 'Новый чат' в Chatscope UI
- При создании чата автоматически открывается диалог
- Установлены пакеты @chatscope/chat-ui-kit-react и @chatscope/chat-ui-kit-styles
- Создана новая страница /messages-chatscope с полнофункциональным мессенджером
- Добавлен пункт 'Мессенджер (NEW)' в навигационное меню
- Реализована интеграция с существующим Django API
- Добавлена поддержка WebSocket для real-time сообщений
- Список чатов с поиском и аватарами
- Отображение сообщений с датами и вложениями
- Индикатор 'печатает...' через WebSocket
- Установлены пакеты @chatscope/chat-ui-kit-react и @chatscope/chat-ui-kit-styles
- Создана новая страница /messages-chatscope с полнофункциональным мессенджером
- Добавлен пункт 'Мессенджер (NEW)' в навигационное меню
- Реализована интеграция с существующим Django API
- Добавлена поддержка WebSocket для real-time сообщений
- Список чатов с поиском и аватарами
- Отображение сообщений с датами и вложениями
- Индикатор 'печатает...' через WebSocket
- Extract CalendarCard to separate component (467 lines)
- Create calendarService for API and caching (156 lines)
- Add useCalendarEvents hook (96 lines)
- Create CalendarSidebar wrapper component (29 lines)
- Reduce AppShell.tsx from 1277 to 634 lines (-50%)
- Implement proper separation of concerns
- Move global cache to service layer
- Improve code maintainability and testability
- Extract CalendarCard to separate component (467 lines)
- Create calendarService for API and caching (156 lines)
- Add useCalendarEvents hook (96 lines)
- Create CalendarSidebar wrapper component (29 lines)
- Reduce AppShell.tsx from 1277 to 634 lines (-50%)
- Implement proper separation of concerns
- Move global cache to service layer
- Improve code maintainability and testability
- Реализована возможность конвертации повторяющегося события в обычное:
* При снятии галочки 'Повторяющееся событие' rule_id сбрасывается в null
* Событие становится одиночным, все occurrence исчезают
- Добавлен метод updateRule() в apiClient:
* Позволяет обновлять существующие правила повторения
* Изменения применяются ко всем occurrence события
* Использует PUT запрос к /api/v1/schedule/rules/{id}/
- Исправлено использование repeatMode:
* Заменен устаревший useCount на repeatMode ('until' | 'count' | 'forever')
* Корректная обработка end_recurring_period только для режима 'until'
- Добавлена универсальная функция normalizeByweekday():
* Решена проблема с разными форматами byweekday с backend:
- Массив: [0, 4] ✓
- Строка: '0,4' ✓
- Число: 4 ✓
* Функция используется во всех компонентах:
- EventModal: авто-выбор дней, отображение кнопок, сохранение
- ViewEventDetailsModal: отображение дней недели
* Исправлены ошибки '.map is not a function' и '.includes is not a function'
- Улучшена логика handleSave() в EventModal:
* Проверка необходимости сброса rule при снятии галочки
* Разделение логики создания и обновления правил
* Нормализация byweekday перед отправкой на backend
Closes: полная поддержка CRUD операций для повторяющихся событий
- Реализована возможность конвертации повторяющегося события в обычное:
* При снятии галочки 'Повторяющееся событие' rule_id сбрасывается в null
* Событие становится одиночным, все occurrence исчезают
- Добавлен метод updateRule() в apiClient:
* Позволяет обновлять существующие правила повторения
* Изменения применяются ко всем occurrence события
* Использует PUT запрос к /api/v1/schedule/rules/{id}/
- Исправлено использование repeatMode:
* Заменен устаревший useCount на repeatMode ('until' | 'count' | 'forever')
* Корректная обработка end_recurring_period только для режима 'until'
- Добавлена универсальная функция normalizeByweekday():
* Решена проблема с разными форматами byweekday с backend:
- Массив: [0, 4] ✓
- Строка: '0,4' ✓
- Число: 4 ✓
* Функция используется во всех компонентах:
- EventModal: авто-выбор дней, отображение кнопок, сохранение
- ViewEventDetailsModal: отображение дней недели
* Исправлены ошибки '.map is not a function' и '.includes is not a function'
- Улучшена логика handleSave() в EventModal:
* Проверка необходимости сброса rule при снятии галочки
* Разделение логики создания и обновления правил
* Нормализация byweekday перед отправкой на backend
Closes: полная поддержка CRUD операций для повторяющихся событий
- Заменен boolean useCount на enum repeatMode: 'until' | 'count' | 'forever'
- Добавлен третий radio button 'Бесконечно' в UI
- При выборе 'forever' count и end_recurring_period = undefined
- Для бесконечного режима показывается пояснение вместо input
- loadRule() правильно определяет repeatMode при загрузке правила
- При включении isRecurring по умолчанию repeatMode = 'until'
Проблема: нельзя было создать событие без ограничений по дате или количеству.
Решение: RFC 5545 RRULE поддерживает бесконечное повторение (без count/until).
- Заменен boolean useCount на enum repeatMode: 'until' | 'count' | 'forever'
- Добавлен третий radio button 'Бесконечно' в UI
- При выборе 'forever' count и end_recurring_period = undefined
- Для бесконечного режима показывается пояснение вместо input
- loadRule() правильно определяет repeatMode при загрузке правила
- При включении isRecurring по умолчанию repeatMode = 'until'
Проблема: нельзя было создать событие без ограничений по дате или количеству.
Решение: RFC 5545 RRULE поддерживает бесконечное повторение (без count/until).
- Убрана блокировка редактирования для существующих повторяющихся событий
- loadRule() теперь заполняет поля формы параметрами из Rule:
* frequency, interval, byweekday, count
* isRecurring = true
* useCount устанавливается если есть count
- Удален readonly блок с информацией о повторении
- Редактируемые поля теперь доступны для всех событий
- При сохранении будет обновлен Rule через API
Проблема: нельзя было изменять параметры повторения (частота, дни недели, count).
Решение: все occurrences используют общий Rule - просто редактируем его параметры.