Игорь Надеин 016ee1ef50 feat(employees): переработка формы редактирования, создание должности и мобильный выбор групп + валидации и UX-полировка
Основное
- Переписан шаблон `templates/employees/_employee_edit.html`:
  - Разделы: «Личные данные», «Контакты», «Должность» (аккордеон-блоки с единым отступом `.py-3` в заголовках).
  - Поля: first_name, last_name, patronymic, gender (коды 1/2, допускается `null`), birth_date.
  - Контакты: phone_number, email, telegram, whatsapp, wechat.
  - Валидация:
    - требуем минимум один контакт (любой из: email/phone_number/telegram/whatsapp/wechat);
    - e-mail валидируется HTML5 + regex-фолбэком; live-очистка ошибок.
  - Должность:
    - выпадающий список из `all_positions`;
    - доступ к редактированию/созданию скрывается/показывается по флагам контекста (см. ниже);
    - «Создать должность» (name/description) + выбор групп прав.
  - Выбор групп прав:
    - мобильный модальный пикер (поиск, чекбоксы, чипсы выбранных значений, скрытый input с csv id);
    - модал центрируется (`.modal-dialog-centered`), выносится в `document.body`, задран z-index (`.modal{z-index:1080}`, backdrop 1075), убран «просвет» (принудительный `background-color`).
  - Кнопки: индикаторы загрузки (label + spinner), аккуратная блокировка в процессе.

JS (внутри того же шаблона)
- Сборка payload для `PATCH`:
  - отправляем только изменённые поля;
  - для `gender`, `birth_date`, `position_id` при очистке отправляем `null`;
  - `gender` приводим к числу;
  - поддержка `skills_ids` (мультиселект): сравнение с `data-init`, отправка массива только при изменении;
  - `avatar`: чтение файла → dataURL (Base64ImageField), очистка через пустую строку.
- Валидация перед отправкой:
  - `ensureOneContact()` — хотя бы один контакт обязателен;
  - `validateEmail()` — валидный `email`, если поле непустое.
- Обработка ошибок API: подсветка конкретных полей (`.is-invalid` + `.invalid-feedback`), fallback-alert для общих ошибок.
- Успех: закрытие обёртки `#editFormWrap` (если есть collapse) + `location.reload()`.
- Блок «Создать должность»:
  - `POST` в `positions_create_url` с `name/description` и (если разрешено) `groups`;
  - затем `PATCH` сотрудника на `endpoint` с `position_id` созданной должности;
  - обновление селекта должностей.
- Исправления:
  - валидация e-mail перенесена внутрь IIFE (был reference error на `form`);
  - удалён двойной вызов `ensureOneContact()`; оставлена единая связка `ensureOneContact() || !validateEmail()`.

Контекст (ожидается из context_processors, без правок views_front)
- Передавать в шаблон (минимум):
  - `all_positions`: список объектов `{id, name}` (для выпадающего списка);
  - `all_groups`: список объектов `{id, name}` (для модального выбора);
  - `positions_create_url`: URL `POST /api/v1/positions/` (создание должности);
  - флаги доступа:
    - `can_edit_position` — право менять должность сотрудника (или глобальные staff/superuser);
    - `can_create_position` — право создавать должности;
    - `can_assign_position_groups` — право назначать групповые права должности.
  - Также в шаблоне допускаются `request.user.is_staff`/`is_superuser` (поддержаны как запасной сигнал в условиях показа).

API
- Используется уже имеющийся `PositionViewSet` (`/api/v1/positions/`):
  - создание должности с полем `groups` (список id) через `PositionSerializer`.
- Добавлен простой эндпоинт групп (если ещё не было):
  - `GET /api/v1/groups/` (list, опционально `?search=` по `name`);
  - сериализатор: `id`, `name`.
  - Примечание: в шаблоне сейчас используется предзагруженный `all_groups`; эндпоинт нужен для возможной ajax-подгрузки/фильтрации в будущем.

UI/стили
- Единый вертикальный ритм заголовков секций через `.py-3`.
- Модальный выбор групп по центру экрана; корректный stacking-context и непрозрачность.
- Мобильная дружелюбность: полноэкранный на sm-и ниже (`modal-fullscreen-sm-down`), поиск, чеклист, чипсы выбранных.

Как проверить
1) Откройте карточку сотрудника → «Редактировать».
2) Смените/очистите пол, дату рождения, должность — убедитесь, что очистка отправляет `null`, а изменение — нужное значение.
3) Удалите все контакты → форма подсветит ошибки и не отправится; добавьте валидный e-mail — ошибки исчезнут.
4) Создайте новую должность с группами → должность появится в селекте и применится сотруднику.
5) Проверить поведение прав:
   - без `can_edit_position`/staff/superuser — блок должности только для чтения;
   - без `can_assign_position_groups` — кнопка выбора групп скрыта.

Refs: DRF serializers (`EmployeeSerializer`, `PositionSerializer`, `Base64ImageField`), `PositionViewSet` (create + groups).
2025-09-01 17:15:05 +03:00
2025-08-20 11:19:02 +03:00
Description
Разработка — в develop; локальный CI работает через Git hooks и quality gates перед коммитом/пушем. После проверки — слияние в master. Push в master запускает blue-green деплой на CORP через Gitea Actions и Telegram-отчёт. Статус деплоя — «Действия».
https://corp.robotail.pro/ 86 MiB
Languages
Python 47.6%
TypeScript 29.8%
JavaScript 15.7%
HTML 4.1%
SCSS 2.1%
Other 0.7%