Группы клиентов — Карточка
Три режима одного экрана: просмотр, создание, редактирование. Структура карточки зависит от типа группы (статическая vs динамическая).
Статус реализации
Backend готов, фронт в админке не начат. См. Группы клиентов — Список.
Источники
- Бизнес: Группы клиентов
- BR: BR 3.1
- API: Customer Service API
Общий layout
Шапка карточки:
- Breadcrumb: «Группы клиентов» → «{name или “Новая группа”}»
- Заголовок — название группы
- Chip типа (🔒 Статическая / ⚙️ Динамическая)
- Счётчик: «{N} клиентов»
- Для dynamic — «Последний пересчёт: DD.MM.YYYY HH:mm»
- Кнопки (зависят от режима — см. ниже)
Табы под шапкой (различаются для static vs dynamic):
Для type=static
- Общее
- Участники
Для type=dynamic
- Общее
- Правила
- Текущие участники
В режиме new
- Сначала экран выбора типа (radio static/dynamic) + название + описание
- После создания — редирект в edit с соответствующими вкладками
Вкладка 1: Общее
| Поле | Режим | Тип | Required | Описание |
|---|---|---|---|---|
| Название | text input | string | Да | Уникально в рамках франшизы, max 100 символов |
| Описание | textarea | string | Нет | |
| Тип | radio (в new) / readonly chip (edit/view) | static / dynamic | Да | Неизменяем после создания |
| Создана | readonly (view/edit) | date | — | DD.MM.YYYY |
| Создал | readonly link на сотрудника (view/edit) | — | — |
Правила валидации:
- Название не пустое
- Уникальность на сервере — при ошибке
GROUP_NAME_TAKENпоказываем inline «Группа с таким названием уже существует»
Вкладка 2 (static): Участники
Таблица клиентов, состоящих в группе.
Таблица
| Колонка | Данные |
|---|---|
| ФИО | кликабельный, → карточка клиента |
| Телефон | |
| Дата добавления | added_at |
| Действие | Кнопка «× Убрать» |
Действия
- Кнопка «+ Добавить клиентов» в шапке вкладки:
- Открывает модалку «Поиск клиентов»
- Поле поиска по ФИО / телефону / email (debounce 300ms) →
GET /customers?search=... - Результаты с чекбоксами (multi-select)
- Кнопка «Добавить выбранных» → POST /customer-groups/{id}/members { customer_ids: […] }
- Toast «Добавлено N клиентов»
- Кнопка «× Убрать» на строке → модалка подтверждения «Убрать клиента из группы?» → DELETE
Фильтры / поиск
- Поиск внутри группы: по ФИО, телефону, email
- Пагинация: 50 на страницу
Состояния
- Пусто: «В группе пока нет участников. Добавьте клиентов из базы»
- Loading: skeleton
- Ошибка: toast
Вкладка 2 (dynamic): Правила
Конструктор правил автозаполнения для dynamic группы. До 5 правил, логика AND.
Структура
Блок «Правила»:
- Каждое правило — отдельная карточка с dropdown типа + форма параметров
- Между правилами — текст «И» (чтобы было видно что AND)
- Кнопка «+ Добавить правило» (disabled если правил уже 5)
Каталог типов правил (dropdown)
| Тип | Параметры UI |
|---|---|
| Сумма покупок за всё время | Оператор (>, >=, <, ⇐, between) + поле суммы (или from/to для between) |
| Сумма покупок за период | Оператор + сумма + поле «за последние N дней» |
| Сумма покупок в диапазон дат | Оператор + сумма + date pickers from/to |
| Дни без активности | Оператор (>, >=) + поле «дней» |
| День рождения | 2 поля: «дней до» + «дней после» (default 7/7) |
| Город | Multi-select города (tags) |
| Зона доставки | Multi-select зон из Store Service |
| Пол | Multi-select: Мужской / Женский / Другое / Не указан |
| Источник регистрации | Multi-select: POS / Веб / Приложение / Админка / Импорт |
| Включение групп | Multi-select других групп (клиент должен быть в них) |
| Исключение групп | Multi-select (клиент не должен быть в них) |
Действия
- «× Удалить правило» на каждом
- «Сохранить правила» (внизу, sticky): PATCH /customer-groups/{id} с
rules_json→ по успеху toast «Правила сохранены, группа пересчитывается» - «Пересчитать сейчас» — кнопка рядом с «Сохранить» (вручную триггерит POST /recompute)
Превью
Справа / внизу от конструктора — виджет:
- «В группе сейчас: N клиентов»
- «Последний пересчёт:
DD.MM.YYYY HH:mm» (или «Пересчитывается…» если только что Пересчитать нажали) - При изменении правил (не сохранённых) — «Изменения не сохранены» с подсказкой нажать «Сохранить»
Состояния
- Loading правил: skeleton
- Сохранение: кнопка в loading
- Ошибка
VALIDATION_ERROR(невалидные правила) — inline-ошибки под конкретными полями
Вкладка 3 (dynamic): Текущие участники
Read-only таблица клиентов, попадающих под правила.
Таблица
Колонки: ФИО (кликабельная), Телефон, Email, Дата добавления (added_at — когда пересчёт их включил)
Info-блок сверху
- «Списком управляют правила. Чтобы добавить или убрать клиента — измените правила.»
Поиск / пагинация
- Поиск по ФИО / телефону
- 50 на страницу
Режим «Просмотр» (view)
- Все поля / правила readonly
- Кнопки:
- «Редактировать» →
/customer-groups/{id}/edit - «Удалить» → модалка подтверждения (описана ниже)
- Для dynamic: «Пересчитать сейчас» — запускает пересчёт без перехода в edit
- «Редактировать» →
Режим «Создание» (new)
- Шаг 1: выбор типа (radio) + название + описание → POST /customer-groups
- Для static — после создания redirect в edit, вкладка «Участники» (пустая, с CTA «Добавить клиентов»)
- Для dynamic — после создания redirect в edit, вкладка «Правила» (пустая, с CTA «Добавить первое правило»)
Режим «Редактирование» (edit)
- Поля редактируемы
- Тип нельзя менять — radio readonly с пометкой «Тип нельзя изменить после создания»
- Кнопки:
- «Сохранить» — PATCH /customer-groups/{id}
- «Отмена» → view
Модалка удаления
- Заголовок: «Удалить группу?»
- Текст: «Группа {name} будет удалена. Членство клиентов в группе обнулится, но сами клиенты останутся.»
- Кнопки: «Отмена» / «Удалить» (красная)
- После успеха: redirect на
/customer-groups+ toast «Группа удалена»
Ролевой доступ
| Роль | Доступ |
|---|---|
| Владелец франшизы | Полный CRUD |
| Владелец партнёра | Нет доступа |
Обычный сотрудник с customer_groups.read | Только просмотр (view) |
Обычный сотрудник с customer_groups.edit | CRUD (обычно такой permission не выдаётся) |
Ошибки API
| Код | Отображение |
|---|---|
GROUP_NOT_FOUND | Not found page |
GROUP_NAME_TAKEN | Inline под полем «Название» |
GROUP_WRONG_TYPE | Toast (не должно происходить в нормальном flow — UI скрывает неподходящие действия) |
VALIDATION_ERROR (правила) | Inline под конкретным правилом |