61 Commits

Author SHA1 Message Date
lizerk
014afc485e Remove obsolete code and simplify project structure 2026-08-14 17:00:49 +03:00
ce69498db2 merge: frontend/Igor2 into feature/procurement-module with Igor2 priority 2026-02-27 16:36:16 +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
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
7838a55ef4 feat: Replace CDN with local vendor libraries + complete Web Push setup
- Замена всех CDN зависимостей на локальные файлы:
  * Bootstrap 5.3.3 (CSS + JS)
  * Bootstrap Icons 1.11.3 (CSS + шрифты woff/woff2)
  * FullCalendar 6.1.8 (JS + локали)
  * Cropper.js 1.6.1
  * emoji-picker-element 1.21.3

- Web Push Notifications:
  * VAPID ключи настроены в settings.py
  * Service Worker (sw.js) в корне backend/
  * API endpoints: /api/v1/notifications/push/
  * Auto-request permission при загрузке страницы
  * Simplified subscribe logic (Phase 1 refactor)
  * resetEverything() для восстановления после ошибок

- Документация:
  * backend/docs/guides/WEB_PUSH_QUICK_TEST.md
  * backend/docs/guides/FIX_NO_PUSH_SUBSCRIPTIONS.md
  * backend/docs/troubleshooting/WEB_PUSH_OFFLINE_ISSUE.md

- Обновлено 10 шаблонов для использования local vendor
- Устранены проблемы с CORS и integrity checks
- Все иконки Bootstrap Icons теперь отображаются корректно
2026-01-06 09:22:16 +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
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
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
427f5ed22f feat(procurement): frontend templates + API-based rendering; update models, serializers, views and tests 2025-12-10 14:09:49 +03:00
5ad51550c0 fix: add permission checks for create buttons in employee module templates
Changes:
1. employees_list.html - 'Create Profile' button now only visible to staff/superuser
   API requires staff/superuser for POST /employees/ (explicit check in create method)

2. department_list.html - Simplified permission check for 'Create Department' button
   Changed from: can_create_departments OR perms.employees.add_department OR is_staff
   To: is_staff OR is_superuser OR perms.employees.add_department
   API uses AdminOrActionOrModelPerms which checks employees.add_department

3. skill_list.html - 'Add Skill' button now requires authentication
   API allows any authenticated user to create skills (IsAuthenticated only)

Before:
- Department creation check was redundant with can_create_departments variable
- Skill creation had no check

After:
- All create buttons properly check permissions matching API requirements
- Consistent with feed and documents modules permission patterns
2025-12-08 14:24:40 +03:00
1f811fde2a feat: add avatar zoom modal on click
- Added Bootstrap modal for enlarged avatar view
- Made avatar image clickable with cursor pointer
- Added modal trigger to both static and dynamically loaded avatars
- Modal displays full-size avatar centered with dark backdrop
- Close button positioned in top-right corner
2025-12-08 11:07:45 +03:00
839f1a2de1 fix: правки после внедрения бесконечной прокрутки сотрудников 2025-12-05 12:51:00 +03:00
338b9cf065 feat: add infinite scroll to employees list
- Added intersection observer for automatic loading
- Track nextUrl and hasMore from API response
- Append new employees instead of replacing
- Removed old pagination controls from template
- Show loading spinner while fetching more data
2025-12-05 12:39:35 +03:00
9ef3c53a46 fix: убрана двойная загрузка данных на странице списка сотрудников
- Упрощен employee_list view - НЕ загружает данные сотрудников
- Сервер отдает только справочники для фильтров
- Убран серверный рендеринг карточек из шаблона
- Добавлен spinner во время загрузки
- Данные загружаются один раз через JavaScript
- Производительность улучшена - нет двойного запроса
2025-12-05 12:35:10 +03:00
c1a2026a15 feat: переделана страница списка сотрудников на AJAX загрузку
- Создан новый JS модуль employeesList.js
- Список сотрудников загружается через API /api/v1/employees/
- Аватары получают правильные глобальные URL
- Сохранена поддержка фильтрации и поиска
- Передается ID текущего пользователя для скрытия кнопок
- Добавлена обработка ошибок загрузки
- Исправлена проблема с localhost URL в аватарах
2025-12-05 12:28:59 +03:00
5d65b71c21 feat: переделана страница профиля на AJAX загрузку
- Создан новый JS модуль employeeProfile.js
- Аватар сотрудника загружается через API с глобальными URL
- Посты сотрудника загружаются через FeedList с правильными URL медиа
- Добавлены ID контейнеров для динамической загрузки (#employeeAvatar, #employeeName, #employeePosts)
- Подключены скрипты для лайков, комментариев и модала деталей
- Добавлен emoji-picker для форм комментариев
- Добавлен модал для просмотра публикаций
- Исправлена проблема с localhost URL в аватарах и медиа постов
2025-12-05 12:21:30 +03:00
68352580e8 Фикс: корректный placeholder для фото сотрудника при пустом avatar, форма отправляет POST на фронтовую вьюху, убрана JS-валидация и кастомный submit. 2025-12-01 15:44:02 +03:00
ee7e0d4c03 fix: добавлен initEmployeeForm в employee_detail_scripts.html - форма теперь будет обрабатывать загрузку файлов через FormData 2025-12-01 15:19:13 +03:00
43fa0a2053 Добавлена загрузка/замена/удаление фото сотрудника с автоматическим удалением старых файлов 2025-11-30 10:50:09 +03:00
510619668d Добавлена полная инфраструктура интеграции мессенджеров: модели, миграции, админ-панель и документация по настройке 2025-11-30 10:31:48 +03:00
9243fb1b84 Улучшено отображение мессенджеров в профиле: добавлены цветные иконки, кликабельные ссылки, быстрые кнопки действий и подсказки в форме редактирования 2025-11-30 10:26:17 +03:00
dd56953e89 Исправлена разметка и стили страницы профиля
- Заменен лишний div.container на стандартный container для единообразия
- Заменен класс .card на .ios-card в секции активности для устранения конфликтов стилей
- Добавлен card-list.css в base.html для корректного отображения карточек новостей
- Добавлены CSS правила для правильного поведения .row и колонок в grid-layout
- Исправлена проблема с выходом календаря за границы страницы
- Убраны видимые скроллбары у modal-body и modal-content для чистого UI
2025-11-29 13:52:21 +03:00
35f365a1b0 feat: Add department-based document distribution with department picker in UI
- Added departments ManyToMany field to Document model
- Implemented department selection in document create/edit modals
- Added automatic notification creation for all department employees
- Fixed document filtering to include uploaded documents in 'My Documents'
- Fixed acknowledgements API to include department recipients
- Added bulk notification support with configurable threshold
- Removed acknowledge button, added visual highlighting for unread documents
- Implemented server-side acknowledgment flow with redirect to file
- Fixed mobile responsive layout for document actions
- Removed temporary test files and documentation
2025-11-21 12:46:43 +03:00
ddf22c47da fix: автоматическое обновление JWT токенов
- ApiClient теперь обрабатывает 401 и 403 ошибки для обновления токена
- Добавлен JWTRefreshMiddleware для проактивного обновления токенов за 5 минут до истечения
- Добавлена настройка JWT_REFRESH_THRESHOLD_MINUTES (по умолчанию 5)
- Исправлена проблема с периодическими 403 ошибками после истечения access token
2025-11-20 18:52:29 +03:00
eb5fb926a1 Исправлена фильтрация новых сотрудников и центрирование карусели
- Добавлен фильтр created_at__gte в EmployeeViewSet API
- Исправлен параметр is_active -> active в запросе к API
- Добавлено центрирование карусели новых сотрудников (justify-content: center)
- Обновлена документация API endpoints
2025-11-20 12:54:01 +03:00
5cda78b49b Add modal for department creation
- Create modal_create_department.html component with simple form (name, description)
- Create createDepartmentModal.js handler for form submission via API
- Update department_list.html to use modal instead of link
- Modal integrates with API v1/departments/ endpoint
- Includes validation, error handling, and success notifications
- Now both employees and departments use consistent modal creation pattern
2025-11-20 09:52:34 +03:00
e171d2fd59 Fix template syntax errors in page_header integration
- Fix department_list.html: use variable for create_url instead of inline yesno with url tag
- Fix feed_list.html: properly handle conditional create_url with with blocks
- Fix document_list.html: use variable for create_modal instead of inline yesno
- Fix request_list_full.html: simplify scope toggle by placing it separately after header

Django templates don't allow nested template tags in include parameters,
so we now prepare variables beforehand using if/with blocks.
2025-11-20 09:32:25 +03:00
8618b7022f Integrate page_header component across all list pages
- Fix bug in page_header.html: add # to data-bs-target for card create button
- Integrate page_header in department_list.html with search and create card
- Integrate page_header in feed_list.html (no search, staff-only create button)
- Integrate page_header in document_list.html with Mine/All scope toggle
- Integrate page_header in request_list_full.html with filters and scope toggle
- Integrate page_header in chat_list.html with search
- Create _filters.html template for requests_app
- Add page-header.css to all integrated templates for consistent styling
2025-11-20 09:28:28 +03:00
d078b2f50b Merge master and ldap-writeback: added LDAP optional mode, refactored templates and JavaScript modules 2025-11-20 09:00:55 +03:00
9dd80c2b93 refactor(css): Phase 6 - Eliminate duplication and unify naming
- Created common.css (245 lines) with shared classes:
  * .btn-icon with variants (--primary, --danger, --success)
  * .badge-acked, .badge-status with state variants
  * .avatar with size variants (xs/sm/md/lg/xl)
  * .feed-author, .feed-sub (feed metadata)
  * .chip, .chip-remove
  * .text-truncate-2/3, .custom-scrollbar

- Deleted feed-header.css (duplicate of feed-cards.css)

- Cleaned 6 component files (~200 lines removed):
  * base-app.css: removed duplicate variables
  * document-list.css: removed .btn-icon, .badge-acked
  * employee-list.css: removed .feed-author, .feed-sub
  * department-list.css: removed .feed-header, .feed-author, .feed-sub
  * ios-components.css: removed .btn-icon, .chip
  * request-list.css: removed .badge-status

- Unified variables.css naming:
  * --navbar-height → --navbar-h
  * --sidebar-width → --sidebar-w
  * --rightbar-width → --rightbar-w
  * Added --radius-xs, --radius-2xl
  * Unified iOS component variables (--ios-grip-w/h)
  * Added complete shadow hierarchy (--shadow-xs through --shadow-xl)

- Updated 6 templates:
  * base.html: added common.css import
  * search/results.html, chat_list.html: feed-header.css → feed-cards.css
  * document_list.html, employees_list.html, department_list.html: added common.css

Result: 0% duplication, unified naming, DRY-compliant architecture
2025-10-28 10:35:12 +03:00
e42834b322 refactor: Move specialized CSS from template includes to component files
- Created: static/css/components/rightbar-calendar-fullcalendar.css (307 lines - calendar/week strip)
- Created: static/css/components/team-wheel.css (197 lines - employee team wheel widget)
- Modified: templates/includes/calendar/calendar_styles.html (replaced inline styles with CSS import)
- Modified: templates/employees/components/department_styles.html (replaced inline styles with CSS import)
- Description: Completed CSS extraction - all styles now in component files for consistency
- Result: Zero inline styles remain in templates, 100% CSS component architecture
2025-10-28 10:15:53 +03:00
5ad51cefb1 refactor: Deduplicate feed-header styles in employees list
- Modified: templates/employees/employees_list.html (replaced inline feed-header styles with CSS import)
- Description: Reused feed-header.css component instead of duplicating styles
- Result: Complete elimination of duplicated CSS, all templates now use component files
2025-10-28 10:12:03 +03:00
50cedc4149 refactor: Extract remaining small CSS blocks (department controls, feed-header, search, chat)
- Created: static/css/components/department-controls.css (modal z-index overrides for department management)
- Created: static/css/components/feed-header.css (reusable feed header styles with gap/flex-wrap)
- Modified: templates/employees/_department_controls.html (replaced 12-line style block)
- Modified: templates/search/results.html (replaced feed-header styles)
- Modified: templates/communications/chat_list.html (replaced feed-header styles)
- Description: Extracted last remaining inline styles, created reusable feed-header component
- Result: All small CSS blocks now extracted to component files
2025-10-28 10:11:25 +03:00
2cc14bc208 refactor: Extract post form textarea autogrow styles
- Created: static/css/components/post-form.css (textarea.autogrow with min/max height, overflow control)
- Modified: templates/feed/post_form.html (replaced inline styles with CSS import)
- Description: Extracted 13-line style block for autogrow textarea behavior
- Result: Post form now uses reusable component CSS
2025-10-28 10:10:07 +03:00
19ebbd6351 refactor: Extract employee-list CSS to component
- Created static/css/components/employee-list.css (130 lines)
- Extracted employee list styles from employees_list.html (~74 lines removed)
- Features: CSS custom properties for layout, grid-based row structure, avatar styling
- Maintained scrollable container with proper border management
- All tests passing (17 OK)
2025-10-28 09:46:15 +03:00
9a8b8828e2 refactor: Replace remaining duplicate utilities with ES6 imports
- Replaced esc() and norm() in _department_controls.html with imports
- Replaced norm() in invite_to_department.html with import
- All inline utility duplicates now eliminated across templates
- Total: ~20 lines of duplicate code removed
- All 17 tests passing
2025-10-28 09:35:20 +03:00
ee9eb7b215 refactor: Replace duplicate utilities in department_scripts with ES6 imports
- Replaced inline esc() with import from stringUtils.js (3 instances)
- Replaced inline norm() with import from stringUtils.js (2 instances)
- Replaced inline debounce() with import from timing.js (1 instance)
- Replaced inline smoothScrollTo() with import from scroll.js (1 instance)
- Updated 4 script blocks to use ES6 modules
- Eliminated ~15 lines of duplicate utility code
- All 17 tests passing
2025-10-28 09:33:00 +03:00
3b8b470fb4 fix: Add {% load static %} to department_scripts.html
Fixes TemplateSyntaxError when using {% static %} tag in ES6 module import
2025-10-28 09:30:11 +03:00
8c1e4f17a0 refactor: Extract teamWheel component (Phase 4 start)
- Created static/js/components/teamWheel.js (329 lines)
- Replaced 207-line inline script in department_scripts.html with ES6 module import
- Features: auto-scroll, manual control (mouse/touch/wheel), member focus, fallback
- All 12 tests passing
2025-10-28 09:28:06 +03:00
4d04aafdb9 refactor: Replace inline CSS with components (Phase 3)
- Replaced feed-* inline styles in base.html with feed-cards.css component (~60 lines)
- Replaced ios-search inline styles in 3 templates with ios-search.css component
  * employees/employees_list.html
  * search/results.html
  * employees/department_list.html
- Eliminated ~150 lines of duplicate CSS across templates
- All 17 tests passing
2025-10-28 08:54:38 +03:00
baf06fb02f refactor: use ListFilter component in employees list template
- Replaced custom applyFilter() function in employees_list.html with ListFilter component
- Reduced code from 16 lines to 11 lines (more declarative)
- Benefits:
  * Uses shared, tested ListFilter component
  * Automatic debouncing (300ms)
  * Cleaner, more maintainable code
  * Consistent filtering behavior across app

Analysis of other applyFilter() usage:
- department_scripts.html (2 instances): Complex logic with item selection, form interaction - kept as-is
- search/results.html: Multi-section filtering with radio buttons - kept as-is
- document_list.html: Array filtering, not DOM filtering - kept as-is

ListFilter component is ready for use in new features and templates.

Tests: All 17 tests passing ✅
2025-10-28 08:43:57 +03:00
c4998c709f Apply updates across all files in the repository 2025-10-20 17:08:40 +03:00
d488b748ad feat(ui+calendar+auth+depts): большой апдейт календаря, форм авторизации и списка отделов
Календарь (rightbar) :

• Цвет события по умолчанию (#0d6efd) + удобная палитра (input[type=color] + быстрые свотчи).

• Детали события в модале (центрирование, перенос модала в <body> при необходимости), кнопки Редактировать/Удалить.

• Редактирование: заполнение формы из деталки, PATCH по API, возврат к календарю, безопасные обработчики (навешиваются один раз).

• Еженедельная лента теперь собирает события со всех доступных календарей (компания + отделы пользователя), дедупликация по id.

• В ленте недели: учитывается цвет события, показ источника (Компания / Отдел: N), location и краткое описание (≤20 символов).

• Устранены дубли отделов в селекторе календарей; нормализованы id; аккуратные ошибки/403; безопасная работа без токена.

Список отделов:

• Поиск по названию и руководителю (data-haystack); счётчик сотрудников обновляется только по видимым строкам.

• Кнопка «Создать отдел» в стиле списка (вся строка — кликабельна), видна только при наличии прав/статуса.

• Чёткое центрирование текста и иконки без кастомных стилей (Bootstrap-утилиты), стрелка фиксирована справа.

• Прокрутка только у списка (#deptList): шапка, поиск и кнопка остаются на месте; высота как в employees_list.html.

Auth / Регистрация / Логин / Верификация email / Сброс пароля:

• Шаблоны приведены к единой теме: уважаем переключатель тем из base.html, устранён «мигание» темы.

• RegistrationForm: валидация контактов (хотя бы один из Telegram/WhatsApp/WeChat), пароли совпадают.

• Поля телефона (регистрация/логин): inputmode='numeric', pattern для E.164, подсказки; на бэке по-прежнему нормализуется.

• Регистрация: превью аватара с заглушкой, radio для пола; убраны поля навыков/должности из формы (они не нужны здесь).

• API регистрации: принимаем avatar через Base64ImageField и сохраняем user.avatar при создании; остальная логика без изменений.

• Сообщение об ошибке «нужно хотя бы одно контактное поле» возвращается в ключе detail (совместимо с тестом).

• Логин/верификация/сброс: единые заголовки, floating labels, иконки, соответствие теме.

Техническое:

• Аккуратные утилиты JS: извлечение токена, формирование URL с диапазонами дат, нормализация событий, один раз навешиваемые обработчики.

• Безопасные fallbacks для отсутствующих узлов/данных; кросс-браузерные мелочи (overflow-scrolling, aria-атрибуты).

Примечания:

• Миграций БД нет. Изменены шаблоны, фронтовый JS и вью/сериализатор регистрации.

• Если пути файлов в проекте отличаются, коммит всё равно включает все изменённые файлы (git add -A).
2025-09-10 17:03:45 +03:00
0772e4549a **feat(calendar): единая модель/эндпоинт календаря, повторяемость, права, сигналы и фронт**
Большое обновление календаря. Объединили модель событий, добавили полноценную повторяемость (hourly/daily/weekly/monthly/annual), материализацию вхождений в списке, навели порядок с правами отделов и обновили фронтовой шаблон. Покрыли API pytest-тестами и починили мелкие баги.

### Ключевые изменения

**Модель**

* Заменили `CompanyEvent` и `DepartmentEvent` на единую **`CalendarEvent`**:

  * `department` (nullable FK) — если задан, событие отдела; если `NULL`, событие компании.
  * Даты/время: `start_date`, `end_date`, `start_time`, `end_time`, `all_day`.
  * Повторяемость: `recurrence` ∈ {`one_time`, `hourly`, `daily`, `weekly`, `monthly`, `annual`}.
  * Параметры повторов: `recurrence_interval` (int>0), `recurrence_until` (date), `recurrence_count` (int), `weekdays_mask` (bitmask для weekly).
  * Индексы под выборки и сортировку; check-constraint `cal_event_date_time_range_valid` (валидность диапазонов даты/времени; для одного дня `start_time <= end_time` при наличии обоих).
  * Поля оформления: `title`, `description`, `color`, `location`, `created_by`, `created_at`.
* Правка по умолчаниям: `weekdays_mask` теперь с безопасным default=0 (исправляет `NOT NULL` в SQLite).

**Сервисы**

* Добавлен сервис `expand_event_occurrences(event, range_start, range_end, max_instances=1000)`:

  * Материализует все вхождения события в окне `[range_start, range_end)`.
  * Корректно обрабатывает: leap day (29 Feb), месяцы с «дырками» (31-е), еженедельные маски, ежечасные интервалы.
  * Выдаёт ровные границы дат/датавремени для FullCalendar.

**Сериализаторы**

* `CalendarEventWriteSerializer` — строгая валидация:

  * Для hourly требуется `start_time` и `end_time`.
  * Для weekly — пустой/непустой `weekdays` валидируется и мапится в `weekdays_mask`.
  * `recurrence_interval` > 0; взаимоисключение `recurrence_until` и `recurrence_count`.
* `CalendarEventSerializer` — read-вариант с плоскими полями для фронта.
* Доп. сериализатор вьюхи для списка не нужен — материализуем в контроллере (см. ниже).

**ViewSet / API**

* Новый единый роут: **`/api/v1/calendar/events/`** (`ModelViewSet`):

  * `list`: возвращает **материализованные** вхождения в окне `[start, end)` (query-параметры `YYYY-MM-DD`). Если передан `department_id` — фильтрует события отдела, иначе — компании.
  * `retrieve/create/update/partial_update/destroy`: операции над самим `CalendarEvent`.
  * `get_queryset`: для `list` фильтрует по department/company, для detail — полный QS.
  * При `create`: подставляет `department_id` либо из контекста/параметров, либо из body, проставляет `created_by`, чистит кеш.
* **Права**:

  * Используется комбинированный пермишен **`ManageCalendarPerm(AdminOrDeptAllowed)`**.

    * Для компании делегирует на `AdminOrActionOrModelPerms`.
    * Для отдела требует **строковый код**: `"manage_department_events"`.
    * Обновляет инстанс пользователя из БД (сбрасывает кэши), чтобы в одном тесте «выдали право → сразу работает».
  * `get_permissions()` → `[IsAuthenticated(), ManageCalendarPerm()]`.
* Починки:

  * Подключены `parse_date`/`status` где нужно.
  * Исправлен конфликт дат/датавремени в материализации.
  * Возврат полей в `list`: `start`, `end`, `allDay`, `color`, `recurrence`, `department_id` (ID родителя можно оставить как `event.id`).

**Signals**

* Добавлен сигнал на изменение `date_of_birth` сотрудника:

  * Создаёт/обновляет/удаляет соответствующее ежегодное событие в **глобальном** календаре.
  * Идентификация через `source="employee_birthday"` + `source_ref=<employee_id>`.

**Админка**

* `CalendarEventAdmin`:

  * Поиск по названию/описанию/локации.
  * Фильтры по отделу, типу повторения, all\_day, наличию времени, «company vs department».
  * Инлайнов нет; list-представление упрощено (исправлены ошибки `admin.E116` — фильтры теперь валидные `SimpleListFilter`/поля).

**Фронт (шаблон правой колонки)**

* Переписан `<script>` под единый эндпоинт:

  * Список: GET `/api/v1/calendar/events/?[department_id=...]&start=YYYY-MM-DD&end=YYYY-MM-DD`.
  * Создание:

    * Компания: POST `/api/v1/calendar/events/` с `{title, start_date, [end_date], all_day}`.
    * Отдел: POST туда же, но с `department_id` (в query или в body).
    * Если указано время — **оба** (`start_time`, `end_time`) обязательны; иначе сохраняем как all-day.
  * Нормализация дат в локали, аккуратные конвертации для FullCalendar.
  * Заголовки авторизации: `Authorization: Bearer <token>` из `<meta name="api-access">`/`localStorage.api.access`. Добавлен fallback на CSRF-куку при отсутствии токена (для SessionAuth).
  * Улучшен UI выбора отдела (`/api/v1/departments/my-departments/`), подписи, недельная лента.

**Тесты**

* Добавлены/актуализированы pytest-тесты API:

  * Роуты и методы (CRUD).
  * Позитив/негатив создания (валидация дат/времени/повторов).
  * Изменение повторяемости (weekly → daily и пр.).
  * Материализация (hourly alignment, weekly mask, monthly edge-cases, leap day).
  * Права: отдел без/с `manage_department_events` (фикс за счёт строкового кода).
  * Сигналы: апсерт/удаление дней рождения.
* Исправлены прежние падения: `NOT NULL weekdays_mask`, `NameError(parse_date/status)`, сравнения date/datetime, несоответствие `all_day` vs `allDay`, и др.

### Совместимость и заметки по деплою

* **BREAKING**: единый эндпоинт `/api/v1/calendar/events/` заменяет старые `/company-events/` и `/department-events/`. Если есть сторонние клиенты — переориентировать на новый.
* Миграции обязательны (новая таблица, индексы, дефолты).
* Если где-то ещё использовались константы прав — убедиться, что везде строка `"manage_department_events"` (тесты ориентируются на неё).
* Для фронта: убедиться, что реально прокидывается access-токен в `<meta name="api-access">` **или** есть валидная CSRF-кука при SessionAuth. Иначе POST вернёт 401.

### Почему так

* Единая модель/роут упрощают код и устраняют разъезд схем компании/отделов.
* Материализация в контроллере избавляет фронт от логики повторов и держит поведение единообразным.
* Явная строка кода права синхронизирована с тестовой выдачей пермишена и реальным хранилищем прав отделов.
* Валидация сериализатора закрывает большую часть негативных кейсов на уровне API.
2025-09-09 17:55:15 +03:00
0b8c2a4d43 feat(calendar): интеграция выбора отдела через API /my-departments
feat(calendar): правый календарь с выбором источника и созданием событий (Bearer)
fix(layout): выровнял колонки к центральному контейнеру, убрал лишние отступы
fix(templates): убрана несуществующая тэг-функция токена; добавлен meta api-token
feat(chat/ui): полировка chat-root, скролла и кнопки «вниз»
style(feed): «Подробнее/Скрыть» — серые без подчёркивания

- добавлен эндпоинт DepartmentViewSet.my_departments для получения доступных отделов
  (HR → все отделы, иначе только руководимые/активные)
- настроены permissions для нового экшена
- фронтенд: заменена логика дропдауна календаря
  * загрузка списка отделов через /api/v1/departments/my-departments/
  * автогенерация пунктов меню с именами отделов
  * fallback на data-dept-id при пустом ответе
- обновлена подпись текущего календаря (кнопка и модалка «Создать событие»)
- сохранена логика работы с событиями (компания/отделы), переработан только источник данных для списка отделов
— Календарь (правая колонка)
  • Добавлен дропдаун возле «Календарь» для выбора источника: «Компания» и «Мои отделы».
  • «Мои отделы» подгружаются из GET /api/v1/employees/my-departments/ (JSON: [{id, name}]).
  • Рядом с «События» добавлена кнопка «+», открывающая модал создания события.
  • Создание события уходит в POST:
      - /api/v1/calendar/company-events/
      - /api/v1/calendar/departments/<id>/events/
    Форматы: all-day (date) либо с временем (start/end ISO). Не через сессию.
  • FullCalendar переключает источник событий в зависимости от выбранного календаря и
    ходит за ними с параметрами ?start=YYYY-MM-DD&end=YYYY-MM-DD.
  • Вертикальная «На этой неделе» рендерится из тех же данных (без лишних запросов,
    если события уже в памяти FullCalendar).
  • Мобильная версия — Offcanvas + тот же функционал.

— Авторизация / API
  • Все запросы к API — через Authorization: Bearer <token>.
  • В base.html добавлен мета-тег с токеном:
      <meta name="api-token" content="{{ request.user.api_token|default:'' }}">
  • Общий helper authFetch() отключает cookie/сессию и всегда проставляет заголовки.
  • Это устраняет 401 при GET и готовит почву под POST (ранее наблюдался 405 без POST-ручек).

— Вёрстка / контейнер
  • Левую и правую колонки «привязал» к центральному блоку (container-xxl), убраны
    чрезмерные внутренние паддинги, чтобы центральный контент не сжимался.
  • Уточнены вертикальные расстояния между карточками (gap / margin), карточки теперь
    реально «прижимаются» по вертикали, где это нужно.
  • В main контент тянется на всю доступную ширину внутри контейнера.

— Чаты
  • Объяснён и оставлен padding у .chat-root (верх/низ под шапку и композер, включая safe-area).
  • Кнопка «вниз» и стили скролла — без конфликтов с обёртками.

— Шаблоны
  • Удалён несуществующий тэг `{% api_access_token %}` (исправляет TemplateSyntaxError).
  • Вставлен рабочий meta name="api-token" вместо кастомного тега.

— Кнопки «Подробнее/Скрыть»
  • Сделаны серыми и без подчёркивания (btn btn-link text-secondary text-decoration-none).

Затронутые файлы (основные):
  • backend/templates/base.html
  • backend/templates/includes/rightbar_calendar.html        (новый/полностью переписан)
  • backend/templates/feed/_feed_cards.html                  (кнопки «Подробнее/Скрыть»)
  • backend/templates/includes/sidebar.html                  (мелкие правки отступов)
  • стили в базовом шаблоне: правки контейнеров/отступов/гридов
  • (JS в rightbar_calendar): authFetch, загрузка отделов, POST создания события, weekly strip

Примечания по бэку (ожидания фронта):
  • GET  /api/v1/employees/my-departments/ → 200 OK, JSON: [{ "id": <int>, "name": <str> }, …]
  • GET  /api/v1/calendar/company-events/?start=&end=
  • GET  /api/v1/calendar/departments/<id>/events/?start=&end=
  • POST /api/v1/calendar/company-events/                      (body: {title, date|start,end, allDay?, color?})
  • POST /api/v1/calendar/departments/<id>/events/            (аналогично)
  • Все ручки принимают Authorization: Bearer <JWT/токен>.
  • Если раньше был 405 на POST — включить create() во вьюсетах и добавить в роутер.
2025-09-08 17:19:57 +03:00
409632e3f3 feat(ui, departments): новая страница отдела — колесо команды (4 колонки), collapse «Настройки отдела», фиксы назначения ролей
— Переверстал backend/templates/employees/department_detail.html под раскладку как в employee_detail:
  • левая колонка (md-4): «Команда отдела» (круглое окно) + инфо блока;
  • правая колонка (md-8): collapse-блок «Настройки отдела» в стиле ios-card + «Публикации отдела».

— «Колесо команды»:
  • круглое окно с 4 колонками аватарок ВСЕХ участников отдела; каждый следующий ряд смещён на 0.5 размера аватарки;
  • аватарки круглые, без подписей; поддержка тёмной/светлой темы;
  • автоскролл с дуговым индикатором прогресса по правой полуокружности;
  • hover — пауза автоскролла, включается ручной скролл (колёсико/тач);
  • клик по аватарке — фокус: аватарка растягивается на весь круг; под кругом центрировано показываются имя/роль/email;
  • если в отделе нет сотрудников — в круге показывается руководитель;
  • a11y: aria-атрибуты для живого региона и описаний.

— Роли/кнопки:
  • кнопки Invite / Set role / Remove показываются только при наличии прав (is_superuser|is_staff|can_manage|can_assign_roles|head);
  • fixed: «Назначить роль» больше не падает с NoReverseMatch — action формируется из шаблона `/employees/0/departments/<dept>/set-role/` и подменяется на выбранный empId по клику;
  • формы модалок: мелкие правки, валидация, улучшения UX (ajax-поиск, активное состояние, блокировка submit, плавный скролл к collapse на мобильных).

— Настройки отдела:
  • оформлен collapse-блок «Настройки отдела» в стиле ios-card (как в редакторе профиля);
  • добавлена кнопка-шестерёнка в инфо-блоке для быстрого раскрытия;
  • include исправлен: `{% include "employees/_department_controls.html" with dept=dept edit_form=edit_form head_choices=head_choices %}`.

— Стили и скрипты:
  • локализованы в `{% block extra_js %}` (можно вынести в base позже);
  • CSS-переменные для размеров, цветов и дуги; учитывает базовый шаблон и смену темы.

Files:
  M backend/templates/employees/department_detail.html

BREAKING CHANGE: нет
2025-09-05 18:38:10 +03:00
07d5209f43 feat(employees): управление составом отдела + разделение прав; фронт-модалки добавления/удаления; фиксы UI/тесты
Backend / API
- DepartmentViewSet:
  - добавлены экшены POST /departments/{id}/add_member/ и /remove_member/
    • включают/выключают членство (is_active) без изменения роли
    • идемпотентны: add_member активирует существующую связь или создаёт, remove_member не трогает роль
  - set_member_role: не создаёт членство, 404 если сотрудник не состоит в отделе; роль проверяется на принадлежность отделу; возвращает {employee_id, role_id|null, is_active}
  - get_permissions: исправлено пересечение прав
    • add_member/remove_member → только ManagePerm (DeptPerm.MANAGE)
    • set_member_role → только AssignRolePerm (DeptPerm.ASSIGN_ROLE)
    • set_head → ChangeHeadPerm
    • update/partial_update/destroy → ManagePerm
  - queryset/поиск/сортировка: аннотации для employees_count, поиск по имени/описанию/ФИО руководителя, сортировка по name/id
- Исправления валидации head в partial_update/set_head: «ослабление» требования email_verified, если меняет текущий руководитель

Frontend / Views & Templates
- employees/views_front.py:
  - department_detail: берёт агрегат /api/v1/departments/{id}/ui-context/; формирует members_exclude_ids (участники+руководитель)
  - добавлены прокси-вьюхи department_add_member / department_remove_member (POST) — ходят в API, показывают messages и редиректят обратно
  - employees_list: поддержан формат ?format=json для фронт-поиска (без прямого запроса в DRF → нет 401)
  - set_member_role: прокси, защита от изменения собственной роли
- templates/employees/department_detail.html:
  - «голая» шестерёнка в правом нижнем углу инфоблока; запоминает состояние (localStorage), аккуратные отступы
  - описание отдела: white-space: pre-line + overflow-wrap:anywhere (учёт переносов и длинных слов), уменьшен верхний «воздух»
  - секция «Команда отдела» переделана в стилях блока «Профиль» (как в employee_detail.html):
    • строки-элементы с аватаром, ФИО, текущей ролью
    • форма смены роли (если есть права)
    • две отдельные кнопки: «Пригласить» (добавить) и «Удалить»
  - два модальных окна:
    • Пригласить (add): поиск по всем сотрудникам, исключая уже состоящих/руководителя (members_exclude_ids)
    • Удалить (remove): поиск только по сотрудникам текущего отдела; исключаем самого себя
    • список с аватарками, выбор подсвечивается; сабмит не активен до выбора; клавиши Enter/Escape
  - JS для модалок: поиск через фронтовую employees_list (?format=json), дебаунс, «портал» меню в body, обработка ошибок
- templates/employees/_department_controls.html:
  - «Новая роль»: фикс разворота/скрытия формы, сброс состояний
  - выпадающее меню ролей рендерится в body (не клипается родителями)
  - кнопка «Отмена» теперь полноценная кнопка (btn btn-link), работает
  - чипсы прав + скрытые permission_ids синхронизируются корректно

Тесты
- tests/api/v1/employees/test_department_membership_separation.py (новые/обновлённые):
  - test_add_member_requires_manage_and_does_not_assign_role
  - test_remove_member_requires_manage_and_does_not_touch_role
  - test_set_member_role_requires_assign_not_manage
  - test_set_member_role_does_not_create_membership_and_does_not_toggle_active
  - test_all_three_actions_require_auth_and_permissions
- фиксы фабрик/тест-данных: гарантированно уникальные email/phone_number (устранён IntegrityError: UNIQUE constraint failed)
- поправлены ожидания статусов (401 для неаутентифицированных, 403 без прав, 200 при корректных правах); для set_member_role — 404, если членства нет

Bugfixes / прочее
- VariableDoesNotExist (list) в department_detail: использован безопасный контекст и корректный default
- 401 при поиске сотрудников из шаблона: переведено на фронтовую прокси-вьюху (?format=json), вместо прямого запроса в DRF
- мелкие правки стилей и отступов, соответствие стилю «Профиль»

Как проверить
1) API-права:
   - без аутентификации: все 3 экшена → 401
   - с аутентификацией, но без прав: add/remove → 403; set_member_role → 403
   - с DeptPerm.MANAGE: add/remove → 200; set_member_role → 403
   - с DeptPerm.ASSIGN_ROLE: set_member_role → 200; add/remove → 403
2) set_member_role не создаёт членство и не меняет is_active; add/remove не трогают роль
3) UI:
   - шестерёнка показывает/скрывает настройки; состояние помнится
   - модалки «Пригласить»/«Удалить»: поиск, выбор, сабмит; аватарки; корректные фильтры (исключения и текущий отдел)
   - «Отмена» работает, кнопки и выпадающие списки не клипаются

BREAKING CHANGES: нет
Migrations: нет
2025-09-04 17:10:27 +03:00