Law/context/10_development_execution_plan.md
2026-02-26 18:55:02 +03:00

214 lines
42 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# План Разработки (Execution Plan)
## Назначение
Этот файл фиксирует последовательность работ до завершения проекта.
Используется ИИ-агентом как рабочий чеклист реализации.
## Правило статусов
- `сделано` — реализовано в коде, покрыто тестами или подтверждено рабочим сценарием.
- `к разработке` — еще не реализовано полностью или реализовано как заглушка.
## Порядок выполнения
Работы выполняются строго сверху вниз по `ID`.
Переход к следующему пункту возможен только после:
1. Обновления кода.
2. Обновления/добавления тестов.
3. Проверки `unittest` и миграций.
4. Обновления контекста (`context/*.md`) при изменении требований.
## Дорожная карта
| ID | Статус | Блок | Задача для ИИ-агента | Критерий готовности |
|---|---|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---|
| P01 | сделано | База проекта | Поднять базовую архитектуру FastAPI + PostgreSQL + Redis + Celery + админку и лендинг | API запускается, миграции применяются, базовые страницы доступны |
| P02 | сделано | Модели и миграции | Создать основные таблицы (`requests`, `topics`, `statuses`, `form_fields`, `messages`, `attachments`, `status_history`, `audit_log`, `admin_users`, `otp_sessions`, `quotes`) + системные поля | Миграции `0001-0003` проходят, тесты миграций зеленые |
| P03 | сделано | Универсальный CRUD | Реализовать универсальный CRUD + RBAC по таблицам + аудит изменений | CRUD работает для справочников и пользователей, аудит пишется |
| P04 | сделано | Пользователи и роли | CRUD пользователей, хеширование паролей, роль `ADMIN/LAWYER`, профильная тема (`primary_topic_code`) | Тесты на CRUD пользователей и пароли проходят |
| P05 | сделано | Базовый auto-assign | Реализовать автоназначение через 24 часа по профильной теме и активной нагрузке | `auto_assign_unclaimed` назначает корректно, тесты проходят |
| P06 | сделано | Базовая админ-панель | Перевод `admin.html` -> `admin.jsx`, универсальные таблицы/модалки/фильтры | Работа с сущностями через единый UI доступна |
| P07 | сделано | Темы юриста (расширение) | Добавить связующую таблицу `admin_user_topics` для дополнительных тем юриста; обновить CRUD/UI/фильтры | У юриста: 1 основная + N дополнительных тем; выбор тем в UI и в API |
| P08 | сделано | Назначение заявок (ручное) | Добавить endpoint «Взять в работу» для юриста с атомарной блокировкой (без гонок), только если заявка не назначена | Два юриста не могут одновременно взять одну заявку; takeover запрещен |
| P09 | сделано | Переназначение | Добавить ADMIN-only ручное переназначение уже назначенной заявки | Юрист не может перехватывать назначенные заявки, админ может переназначить |
| P10 | сделано | Алгоритм auto-assign v2 | Доработать автоназначение: приоритет `primary_topic` -> `admin_user_topics` -> минимальная активная нагрузка (`is_terminal=false`) | Воркеры и тесты покрывают новый приоритет и edge-cases |
| P11 | сделано | OTP create/view | Внедрить полноценный OTP-поток: OTP обязателен для создания заявки и просмотра по `track_number`, cookie JWT 7 дней | Без OTP нельзя создать/смотреть заявку; повторный OTP не нужен при валидной cookie |
| P12 | сделано | Публичный кабинет клиента | Реализовать публичные endpoints и UI: просмотр статуса, чат, файлы, история изменений по `track_number` | Клиент может вести диалог и видеть прогресс заявки в одном контуре |
| P13 | сделано | Read/unread маркеры | Добавить request-level маркеры «есть обновления» для клиента/юриста; открытие заявки сбрасывает маркер; одноразовая зеленая точка типа события | В списке заявок корректно отображаются непрочитанные обновления |
| P14 | сделано | Статусные флоу по темам | Добавить настройку линейных флоу с допустимыми вариациями переходов (Jira-like), валидировать переходы | Нельзя выполнить переход вне разрешенной цепочки |
| P15 | сделано | Иммутабельность по статусам | На смене статуса «замораживать» сообщения и вложения предыдущих статусов + писать `status_history` | Попытка изменения старых сообщений/файлов отклоняется API |
| P16 | сделано | Шаблоны данных | Разделить шаблоны: (1) обязательные поля создания по теме; (2) шаблон дозапроса документов в работе, расширяемый юристом только для текущей заявки | Админ настраивает базу, юрист расширяет только в рамках конкретной заявки |
| P17 | сделано | Файловый контур | Довести загрузку/скачивание файлов: лимиты 25MB/файл и 250MB/заявка, хранение метаданных, контроль суммарного объема | Лимиты enforced сервером, загрузки и скачивание стабильны |
| P18 | сделано | SLA-конфиг | Настройка SLA на каждый переход статуса для каждой темы | SLA-конфиг хранится в БД, валидируется в админке |
| P19 | сделано | SLA-check и overdue | Реализовать `sla_check`: контроль просрочек по переходам, расчет FRT/времени в статусе | Метрики и флаги просрочек обновляются по расписанию |
| P20 | сделано | Уведомления | Уведомления в Telegram (если подключен) + внутренние уведомления сайта по изменениям | При событиях (сообщения/файлы/статусы/SLA) уведомления доставляются |
| P21 | сделано | Dashboard LAWYER/ADMIN | Расширить дашборды: назначенные/неназначенные, активные по статусам, непрочитанные, SLA, по каждому юристу: активная загрузка, сумма активных заявок, вал оплаченных за месяц, зарплата за месяц | Дашборды соответствуют ролям и данным из БД |
| P22 | сделано | Hardening/release | Полировка безопасности, логирования, лимитов, отказоустойчивости, документации API/UI и runbook | Проект готов к стабилизации и приемке |
| P23 | сделано | Mobile UX | Мобильная адаптация лендинга и клиентских форм (заявка, OTP, кабинет клиента: чат, файлы, история) | UI корректно работает на 320-768px, элементы доступны и читаемы без горизонтального скролла |
| P24 | сделано | Тарифы юристов | Добавить ставку и процент юриста (по умолчанию в профиле), а также фиксируемые в заявке поля ставки/суммы (override админом) | Финансовые поля заявки фиксируются и не зависят от последующих правок профиля; клиенту не показываются |
| P25 | сделано | Биллинг-статус | Добавить тип статуса «выставление счета»: генерация счета из шаблона, отправка клиенту и фиксация события оплаты по смене статуса администратором на `Оплачено` | Для темы можно включить billing-этап, счет формируется и доставляется; факт оплаты фиксируется по событиям `Оплачено` (возможны множественные события в одной заявке) |
| P26 | сделано | Security Audit | Внедрить аудит безопасности и защиту ПДн для S3/файлов по требованиям РФ и кибербезопасности | Реализован журнал доступа, шифрование, RBAC/least-privilege, политика хранения и контроль инцидентов |
| P27 | сделано | Итоговое тестирование E2E | Покрыть ключевые бизнес-сценарии: OTP, claim, auto-assign v2, чат, файлы, SLA, уведомления, read markers и выполнить финальный регрессионный прогон | Набор автотестов фиксирует регрессии критичных сценариев и подтверждает готовность перед приемкой |
| P28 | сделано | Справочники: все таблицы БД | Обеспечить отображение в «Справочниках» всех таблиц БД (кроме выделенных разделов «Заявки» и «Счета»), включая `clients` при наличии отдельной таблицы клиентов | Создана таблица `clients` через миграцию, добавлены ссылки в `requests`/`invoices`, справочник `clients` доступен через universal meta |
| P29 | сделано | Модальная форма заявки | Оставить создание заявки только в модальном окне; убрать дублирующие/вводящие в заблуждение блоки с формы и лендинга, вернуть явный выбор темы обращения | На лендинге модалка создания заявки содержит выбор темы; блок и поле рекомендаций удалены |
| P30 | сделано | Отдельная страница клиента | Вынести работу с заявкой клиента (статус, чат, файлы, счета, таймлайн) на отдельную страницу, а не в нижний блок лендинга | Реализована отдельная страница `client.html` с полным рабочим контуром заявки |
| P31 | сделано | Вход клиента через OTP-модалку | Добавить на лендинг кнопку перехода в страницу работы с заявкой через модалку авторизации: телефон + OTP (если нет валидной 7-дневной cookie/JWT) | На лендинге добавлена OTP-модалка входа по телефону; при валидной сессии переход в клиентскую страницу выполняется сразу |
| P32 | сделано | Переключение между заявками клиента | Спроектировать и реализовать переключение между несколькими заявками авторизованного клиента на странице работы с заявкой | На странице клиента добавлен селектор «Мои заявки» и серверный endpoint `/api/public/requests/my` |
| P33 | сделано | Выделенный сервис чата | Выделить чат (клиент↔юрист) в отдельный сервис/контур с API-границей, сохранив текущие бизнес-правила, RBAC и read/unread поведение | Добавлен отдельный сервисный слой `chat_service` и отдельные API-контуры `/api/public/chat/*`, `/api/admin/chat/*`; UI и тесты переведены и проходят |
| P34 | сделано | UX цитат в «Первая консультация» | Перенести цитаты в ненавязчивый формат внутри блока «Первая консультация», убрать визуальный шум и конкуренцию с основным CTA | Цитаты перенесены в блок «Первая консультация» в hero-панели как компактный элемент без конкуренции с CTA |
| P35 | сделано | Предпросмотр документов | Добавить предпросмотр загруженных документов (pdf/jpg/mp4) в модальном окне или выделенной зоне страницы заявки, не ломая текущую загрузку/скачивание | Предпросмотр реализован в `client.html` и в рабочей вкладке заявки `admin.jsx` (`/admin.html?view=request&requestId=...`); сохранено действие «Открыть / скачать», добавлен backend тест inline-preview |
| P36 | сделано | Навигация в админ-панель | Убрать кнопку «Админ-панель» с лендинга, исправить редиректы/роутинг (`/admin`, `/admin.html`) чтобы не было перехода на неверный host/port | Кнопка админки удалена с лендинга; `/admin` корректно переводит на `/admin.html`; добавлен e2e smoke `admin_entry_flow` |
| P37 | сделано | Админ-авторизация и креды | Привести к единому правилу bootstrap-креды администратора (`admin@example.com` + согласованный пароль), обновить документацию/контекст и smoke-проверки логина | Реализован bootstrap-login с автосозданием администратора `admin@example.com` / `admin123`; добавлены автотесты `tests/test_admin_auth.py` |
| P38 | сделано | Конструктор маршрутов статусов | Реализовать для администратора визуальный конструктор маршрутов статусов по каждой теме: вариативные переходы (в т.ч. возврат на предыдущий статус, переход в завершение и альтернативные ветки), SLA на переход, список обязательных документов/данных для закрытия шага | Добавлен UI-конструктор в справочнике переходов статусов (выбор темы, визуальные карточки статусов и исходящих переходов), расширены поля перехода (`required_data_keys`, `required_mime_types`), переходы валидируются API по требованиям шага (данные заявки + MIME вложений), добавлены backend/e2e тесты |
| P39 | сделано | Канбан по заявкам (LAWYER/ADMIN) | Реализовать канбан-доску заявок с унификацией разных статусных флоу через группы колонок (например: `Новые`, `В работе`, `Ожидание`, `Завершены`) + карточки заявок с ключевыми данными (дата создания, клиент, описание, новые сообщения/файлы, SLA deadline/дедлайн дела) | Добавлен backend endpoint `/api/admin/requests/kanban` (role-scope, группы статусов, SLA/case deadline, допустимые переходы); в `admin.jsx` добавлена секция `Канбан` с карточками, claim для юриста, drag&drop/быстрый перевод по допустимым переходам, open-in-place; покрыто unittest и e2e (`kanban_role_flow`) |
| P40 | сделано | Декомпозиция: подготовка сборки фронта | Подготовить модульную декомпозицию фронта: перевести entrypoint `admin.jsx` -> `admin/index.jsx`, включить `esbuild --bundle` в `frontend/Dockerfile`, зафиксировать совместимость `admin.html` и Docker Compose | Реализовано: добавлен `app/web/admin/index.jsx`, сборка переведена на `esbuild admin/index.jsx --bundle`, smoke e2e входа/навигации (`admin_entry_flow`) и сборка в контейнере проходят |
| P41 | сделано | Декомпозиция `admin.jsx`: shared-слой | Вынести из `admin.jsx` константы/маппинги/табличные конфиги и pure-utils (`format`, `filters`, `route`, `reference`) в отдельные модули | Реализовано: добавлены `app/web/admin/shared/constants.js`, `app/web/admin/shared/utils.js`, `app/web/admin/shared/state.js`; `admin.jsx` сокращен до ~4800 строк и использует shared-импорты; e2e smoke `admin_entry_flow`, `admin_role_flow`, `kanban_role_flow` зеленые |
| P42 | сделано | Декомпозиция `admin.jsx`: feature-слой | Разделить UI и логику на feature-модули (`kanban`, `request-workspace`, `config-dictionaries`, `invoices`, `dashboard`) + вынести кастомные hooks/services (`useAdminApi`, `useTablesState`, `useRequestWorkspace`, `useKanban`) | Корневой `App` выполняет orchestration/layout, feature-код изолирован по папкам, сценарии ADMIN/LAWYER/CLIENT не деградировали |
| P43 | к разработке | Декомпозиция backend CRUD | Разбить `app/api/admin/crud.py` на модули: `router`, `access`, `meta`, `payloads`, `service`, `audit` без изменения API-контракта и RBAC | Эндпоинты CRUD/meta работают как раньше, покрытие тестами сохранено/расширено, файл-монолит устранен |
| P44 | к разработке | Декомпозиция backend Requests | Разбить `app/api/admin/requests.py` на модули: `router`, `kanban`, `status_flow`, `data_templates`, `permissions`, `service` с сохранением текущего поведения | Эндпоинты заявок/канбана/маршрутов статусов проходят текущие тесты, ролевые ограничения и SLA-логика без регрессий |
| P45 | к разработке | Декомпозиция тестового слоя | Разделить `tests/test_admin_universal_crud.py` на тематические пакеты (`tests/admin/*`) + вынести общие фикстуры/фабрики | Тесты запускаются пакетно и по подмодулям, время/диагностика прогонов улучшаются, покрытие не снижается |
| P46 | к разработке | Финализация декомпозиции | Обновить runbook/контекст по новым путям модулей и тестов, выполнить полный регрессионный прогон (unittest + e2e) и закрыть технический долг по монолитам | `context/11_test_runbook.md` и связанные контексты актуальны, полный прогон тестов зеленый, декомпозиция завершена |
| P47 | к разработке | Запросы клиента по заявке (модель/миграции) | Добавить отдельную таблицу клиентских обращений по заявке (рабочее имя таблицы: `request_service_requests`, чтобы не конфликтовать с `requests`): тип `enum` (`CURATOR_CONTACT`, `LAWYER_CHANGE_REQUEST`), статус обработки, текст обращения, ссылки на заявку/клиента/назначенного юриста, read/unread флаги для ADMIN/LAWYER/CURATOR, аудит | Миграция применена, таблица доступна в БД, API/модели позволяют создать оба типа запросов, read/unread и аудит фиксируются |
| P48 | к разработке | RBAC и видимость запросов (куратор/смена юриста) | Реализовать правила видимости и доступа: запрос к куратору видят ADMIN (и будущий `CURATOR`) + назначенный юрист; запрос о смене юриста не видит назначенный юрист, видит ADMIN (и будущий `CURATOR` при включении роли); предусмотреть доступ к чату заявки для куратора и отправку сообщений от его имени | Правила видимости соблюдаются серверно, назначенный юрист не видит `LAWYER_CHANGE_REQUEST`, кураторский доступ к чату и чтение/запись работают по RBAC |
| P49 | к разработке | Клиентский UI: запрос к куратору / смена юриста | Добавить в клиентском контуре действия: (1) запрос консультации к администратору/куратору по делу; (2) запрос о смене юриста; показывать статус обработки и связанные уведомления по заявке, не раскрывая служебные поля | Клиент может создать оба типа запросов из UI заявки, видит подтверждение и статус, запросы связываются с конкретной заявкой |
| P50 | к разработке | Админ-панель: вкладка «Запросы» + индикатор в topbar | Добавить отдельную вкладку `Запросы` наравне с `Заявки` и `Счета`; таблица в общем стиле (фильтры/сортировка/пагинация), а также отдельную topbar-иконку (левее `!` и конверта), которая подсвечивается красным при непрочитанных запросах и открывает таблицу с фильтром по непрочитанным | Вкладка `Запросы` доступна ADMIN (и CURATOR при появлении роли), topbar-иконка показывает unread и открывает отфильтрованный список, визуально согласовано с текущими индикаторами |
| P51 | к разработке | Тесты: запросы к куратору / смена юриста | Добавить backend + e2e покрытия: создание запросов клиентом, RBAC-изоляция по типам, подсветка заявок/иконки в админке, видимость для юриста/админа/куратора, доступ к чату от куратора | Автотесты покрывают оба типа запросов и corner cases (невидимость запроса о смене юриста назначенному юристу, unread/reset, фильтрация в таблице `Запросы`) |
| P52 | сделано | Лендинг: карусель выдающихся юристов | Добавить на лендинг карусель сотрудников (выдающиеся юристы) с фотографиями; в выдачу попадают только пользователи ролей `LAWYER`/`ADMIN`, у которых заполнен `avatar_url` (фото в профиле) | На лендинге отображается карусель карточек сотрудников с фото, именем и подписью; без фото сотрудник в карусель не попадает |
| P53 | сделано | Справочник карусели сотрудников | Добавить отдельную таблицу/справочник для управления каруселью на лендинге: ссылка на сотрудника, порядок, активность, подпись, признак закрепления (`pinned`) и CRUD в админке | Администратор может добавлять/убирать сотрудников, менять порядок, задавать подпись и `pinned`; лендинг использует этот справочник для выдачи карусели |
## Критический маршрут (обязательный порядок)
1. `P07 -> P08 -> P09 -> P10` (полный контур назначения).
2. `P11 -> P12 -> P13` (публичный клиентский контур).
3. `P14 -> P15 -> P16` (процесс работы по заявке).
4. `P17 -> P18 -> P24 -> P25 -> P19 -> P20 -> P21` (файлы, SLA, тарифы/биллинг, аналитика).
5. `P22 -> P23 -> P26 -> P27` (стабилизация, mobile UX, security-аудит, итоговые тесты в конце).
6. `P28 -> P29 -> P30 -> P31 -> P32 -> P33 -> P35 -> P34 -> P36 -> P37` (итерация UX/клиентского входа/чат-сервиса/навигации/доступов).
7. `P38 -> P39` (конструктор маршрутов и канбан-представление заявок для ролей).
8. `P40 -> P41 -> P42 -> P43 -> P44 -> P45 -> P46` (декомпозиция фронта/бэка/тестов и финальная стабилизация).
9. `P47 -> P48 -> P49 -> P50 -> P51` (контур клиентских запросов к куратору/админу и запросов на смену юриста).
10. `P52 -> P53` (карусель сотрудников на лендинге и админское управление составом/порядком).
## Детализация P38-P39 (новый контур)
### P38. Конструктор маршрутов статусов
1. Граф переходов по теме:
хранение `from_status -> to_status`, признак `enabled`, `sort_order`, `sla_hours`.
2. Вариативные переходы:
поддержать переходы назад, терминальные выходы и параллельные ветки.
3. Требования на шаг:
для каждого целевого статуса хранить список обязательных документов/данных для закрытия этапа.
4. Валидация:
серверно проверять допустимость перехода и наличие обязательных данных перед сменой статуса.
5. UI конструктора:
визуальный редактор узлов/связей + форма параметров шага (SLA, требования, terminal).
### P39. Канбан заявок
1. Унификация статусов:
ввести группировку статусов в канбан-колонки (`НОВЫЕ`, `В РАБОТЕ`, `ОЖИДАНИЕ`, `ЗАВЕРШЕНЫ`).
2. Маппинг:
каждый статус темы привязать к одной канбан-группе (конфигурируемо администратором).
3. Ролевой scope:
`LAWYER` видит свои + неназначенные заявки, `ADMIN` — все заявки всех юристов.
4. Карточка канбана:
`track_number`, дата создания, клиент, краткое описание, индикаторы новых сообщений/файлов, SLA-дедлайн/дата дела.
5. Действия:
перевод карточки между колонками только по допустимым серверным переходам.
## Детализация P40-P46 (декомпозиция монолитов)
### P40-P42. Фронтенд (admin)
1. Инфраструктурный шаг:
сначала подготовить сборку под модули (`index.jsx`, `--bundle`), затем приступать к разбиению.
2. Shared-слой:
вынести константы, конфиги таблиц/лейблов и pure-utils в `app/web/admin/*`.
3. Feature-слой:
выделить отдельные модули и hooks для Kanban, Workspace заявки, Справочников, Счетов, Dashboard.
4. Ограничение:
декомпозиция без изменения пользовательского поведения и API-контрактов.
5. Итог `P42`:
вынесены feature-секции (`kanban`, `request-workspace`, `dashboard`, `requests`, `invoices`, `config-dictionaries`, `quotes`, `availableTables`, `meta`) и hooks/services (`useAdminApi`, `useKanban`, `useRequestWorkspace`, `useTablesState`, `useTableActions`, `useTableFilterActions`, `useAdminCatalogLoaders`); `admin.jsx` сокращен до orchestration/layout уровня, role e2e-регресс подтвержден.
### P43-P44. Backend (admin API)
1. Разделение `crud.py`:
разнести права, метаданные, нормализацию payload, сервис CRUD и аудит по отдельным файлам.
2. Разделение `requests.py`:
выделить канбан, workflow статусов, шаблоны данных, проверки прав и сервисный слой.
3. Ограничение:
сохранить текущие URL/контракты эндпоинтов и существующую RBAC/SLA-логику.
### P45-P46. Тесты и контекст
1. Разделение тестов:
разнести большой `test_admin_universal_crud.py` по тематическим модулям и общим helper/fixture.
2. Финальная верификация:
обновить runbook, затем выполнить полный прогон backend + e2e перед переводом блока в `сделано`.
## Детализация P47-P51 (запросы к куратору / смена юриста)
### P47. Таблица клиентских запросов по заявке
1. Новая сущность:
добавить отдельную таблицу (рабочее имя `request_service_requests`, чтобы не конфликтовать с `requests`).
2. Поля:
`request_id`, `client_id`, `type(enum)`, `status(enum)`, `body`, `created_by_client`, `assigned_lawyer_id`, `resolved_by_admin_id`, `read/unread` маркеры по ролям, системные поля.
3. Типы запросов:
`CURATOR_CONTACT`, `LAWYER_CHANGE_REQUEST`.
4. Аудит:
создание/изменение статуса/прочтение логировать в `audit_log`.
### P48. Видимость и RBAC
1. `CURATOR_CONTACT`:
видят ADMIN (и будущий `CURATOR`) + назначенный юрист; подсветка заявки для администратора/куратора и ведущего юриста.
2. `LAWYER_CHANGE_REQUEST`:
не видит назначенный юрист; видит ADMIN (и будущий `CURATOR`, если роль будет добавлена).
3. Куратор:
предусмотреть серверные точки расширения под роль `CURATOR` (даже если пока используется ADMIN как куратор).
4. Чат:
куратор получает доступ к чтению/записи в чат заявки от своего имени по RBAC.
### P49. Клиентский UI
1. Кнопки в карточке заявки:
«Обратиться к куратору/администратору» и «Запросить смену юриста».
2. Формы:
модальные окна с текстом обращения, валидацией длины и подтверждением отправки.
3. Отображение:
клиент видит только свои запросы и их статус обработки.
### P50. Админский UI
1. Новая вкладка `Запросы`:
таблица в стиле `Заявки/Счета` (фильтр, сортировка, пагинация, server-side).
2. Topbar-иконка:
отдельный индикатор (левее `!` и конверта), красный `unread` + переход в `Запросы` с фильтром по непрочитанным.
3. Подсветка заявок:
заявка с открытыми клиентскими запросами должна визуально отмечаться в списке `Заявки` и/или карточке.
### P51. Тестирование
1. Backend:
позитивные/негативные тесты по типам запросов и RBAC.
2. E2E:
клиент создает оба типа запросов; админ видит их в новой вкладке и через topbar-иконку; назначенный юрист видит только `CURATOR_CONTACT`.
3. Corner cases:
unread/read reset, повторные запросы, пустой/слишком длинный текст, доступ к чужой заявке.
## Детализация P52-P53 (карусель сотрудников на лендинге)
### P52. Лендинг-карусель
1. Отображение:
карусель карточек сотрудников в блоке лендинга (UI/UX в стиле проекта, адаптивная для mobile/desktop).
2. Ограничение выборки:
в выдачу попадают только `LAWYER`/`ADMIN` с заполненным `avatar_url`.
3. Контент карточки:
фото, имя, подпись (из справочника карусели), при необходимости роль.
4. Сортировка:
сначала `pinned`, затем `sort_order`.
### P53. Справочник управления каруселью
1. Новая таблица:
отдельный справочник (например, `landing_featured_staff`) с полями: `admin_user_id`, `caption`, `sort_order`, `enabled`, `pinned` + системные поля.
2. Админский CRUD:
универсальная таблица/форма в справочниках для добавления, удаления, редактирования и сортировки.
3. Валидация:
серверно разрешать только сотрудников ролей `LAWYER`/`ADMIN`; при отсутствии фото элемент не попадет в публичную выдачу.
4. Публичный endpoint/выдача:
лендинг получает отдельную публичную выборку по активным элементам карусели.
## Правила выполнения для ИИ-агента
1. Не менять бизнес-правила без обновления `context/*.md`.
2. Любую новую таблицу добавлять только через миграции + тест миграций.
3. На каждый новый endpoint добавлять позитивный и негативный автотест.
4. Для RBAC: сначала ограничить доступ, затем открывать минимально необходимое.
5. Для операций назначения использовать транзакционную защиту от гонок.
6. Для статусов и SLA использовать только серверную валидацию (не доверять фронту).
7. Перед переводом пункта в `сделано` выполнять проверки из `context/11_test_runbook.md`.
8. UI e2e запускать через фиксированный compose-сервис `e2e` (образ `law-e2e-playwright:1.58.2`) для стабильного повторяемого прогона без повторной загрузки браузеров.