Клиенты — Карточка

Три режима одного экрана: просмотр, создание, редактирование.

Статус реализации

Backend готов, фронт не начат. См. Клиенты — Список.

Источники


Общий layout (все режимы)

Шапка карточки:

  • Breadcrumb: «Клиенты» → «{ФИО или “Новый клиент”}»
  • Заголовок — имя клиента (в edit/new — пусто / placeholder «Создание клиента»)
  • Chips групп клиента (справа от имени; только view/edit)
  • Бейджи (только view/edit):
    • «🎂 ДР через N дней» (если ДР в ±14 дней)
    • «Новый клиент» (если регистрация < 30 дней назад)
  • Справа — кнопки действий (зависят от режима)

Табы под шапкой:

  1. Профиль (все режимы)
  2. Адреса (все режимы; в new — вкладка есть, но контент после сохранения клиента)
  3. Заказы (только view/edit)
  4. Группы (только view/edit)

Переключение вкладок не меняет URL (состояние в компоненте). Default — «Профиль».

Сайдбар (только view/edit; справа или под шапкой на узких экранах):

  • LTV: {total_spent} ₽
  • Средний чек: {avg_check} ₽
  • Заказов: {orders_count}
  • Последний визит: {last_visit_at} (или «Никогда»)
  • Дней с последнего визита: {days_since}

Вкладка 1: Профиль

ПолеРежимТипRequiredОписание
Телефонtext input с маской +7 (___) ___-__-__stringДаНормализация в E.164 на blur. Уникальность проверяется на сервере
Emailtext inputstringНетLower-case, валидация по RFC
Имяtext inputstringДа
Фамилияtext inputstringНет
Дата рожденияdate pickerdateНет
Полradio (Мужской / Женский / Другое / Не указан)enumНетDefault «Не указан»
ЗаметкиtextareatextНетСвободный текст
Источник регистрацииreadonly chip (view/edit), hidden в new (auto=admin)enumAutoВ view показываем chip
Кто завёлreadonly link на сотрудника (view/edit)Auto
Согласие на обработку ПДcheckbox + датаНет«Клиент дал согласие на обработку ПД». При галке отмечается consent_signed_at = now(). В view — chip «Согласие получено DD.MM.YYYY» или «Не получено» (красный)

Валидации (inline под полями):

  • Телефон: после E.164 нормализации должен быть валидным российским номером (+7XXXXXXXXXX). Иначе — «Неверный формат телефона»
  • Email: стандартная RFC-валидация. Иначе — «Неверный формат email»
  • Имя: не пустое, не короче 1 символа

Вкладка 2: Адреса

Таблица адресов клиента + inline-форма добавления/редактирования.

Таблица

КолонкаДанные
DefaultИконка 🏠 если is_default=true
Городcity
Улицаstreet
Квартираapartment или «—»
Зона доставкиназвание из Store Service (delivery_zone_id) или «—»
Заметкиnotes (truncate, tooltip при hover)
Действия«Редактировать» / «Удалить» / «Сделать default»

Форма добавления / редактирования (модалка или inline-раскрытие)

Поля (обязательные отмечены):

  • Город *
  • Улица *
  • Квартира
  • Подъезд
  • Этаж
  • Код домофона
  • Зона доставки (select из Store Service delivery_zones)
  • Заметки
  • По умолчанию (checkbox) — при отметке предыдущий default снимается

Действия

  • «+ Добавить адрес» — открывает форму
  • «Сделать default» — PATCH с is_default=true (старый снимается в одной транзакции сервером)
  • «Удалить» — DELETE, без модалки (адрес — это не PII по чувствительности)

Состояние «нет адресов»

  • «У клиента ещё нет адресов. Добавьте первый — он станет default»

Вкладка 3: Заказы

Список заказов клиента. Агрегация на Admin BFF из Order Service (GET /api/v1/admin/orders?customer_id=X).

Таблица

КолонкаДанные
Датаcreated_at (формат DD.MM.YYYY HH:mm)
Номерorder_number (с бейджем ТТ в подписи)
Торговая точкаstore.name
Суммаtotal
СтатусChip: new / ready / closed / cancelled
Способ оплатыpayment_method

Фильтры

  • Диапазон дат (from / to)
  • Статус (все / closed / cancelled)

Переходы

  • Клик по номеру/сумме → /orders/{id} (карточка заказа)

Состояние «нет заказов»

  • «У клиента ещё нет заказов»

Вкладка 4: Группы

Chips групп клиента (static + dynamic).

Отображение

  • Chip группы с иконкой типа:
    • 🔒 Static (ручное членство)
    • ⚙️ Dynamic (по правилам)
  • Клик по chip → переход в карточку группы
  • Для static-групп: рядом с chip — кнопка «×» → удалить клиента из этой группы (видна только с customer_groups.edit)

Действия

  • «+ Добавить в группу» (если есть customer_groups.edit) — модалка поиска статических групп → multi-select → добавить. Для dynamic групп недоступно (членство автоматическое)

Пояснение (info-блок)

  • Для dynamic групп: «Клиент автоматически добавлен по правилам группы. Чтобы убрать — измените правила»

Режим «Просмотр» (view)

  • Все поля readonly
  • Кнопки в шапке:
    • «Редактировать» → /customers/{id}/edit (если customers.edit)
    • «Удалить» → модалка подтверждения (если customers.delete + owner франшизы)
  • При клике «Удалить»:
    • Модалка: «Клиент Иван Петров будет удалён. Персональные данные обезличены, но история заказов сохранится. Это действие необратимо.»
    • Кнопки: «Отмена» / «Удалить» (красная)
    • После успеха → redirect на /customers + toast «Клиент удалён»

Режим «Создание» (new)

  • Обязательные поля с красной звёздочкой
  • Кнопки в шапке:
    • «Сохранить» — POST /customers. После успеха → redirect на /customers/{id} (view)
    • «Отмена» → /customers (без сохранения)
  • Вкладки «Заказы» и «Группы» не показываются
  • На вкладке «Адреса» — плашка «Сохраните клиента сначала, потом добавьте адреса»

Режим «Редактирование» (edit)

  • Поля редактируемы (кроме readonly: source, registered_by, registered_at)
  • Кнопки в шапке:
    • «Сохранить» — PATCH /customers/{id}
    • «Отмена» → /customers/{id} (view, без изменений)
  • Если изменён телефон — на blur происходит pre-flight через GET /customers/search?phone=... → если занят, inline-ошибка «Телефон уже используется другим клиентом»

Ошибки при сохранении

Код от APIОтображение
VALIDATION_ERRORInline-ошибки под конкретными полями
CUSTOMER_PHONE_TAKENInline-ошибка под phone: «Клиент с таким телефоном уже есть в системе» + ссылка «Открыть его карточку» (требует resolve phone → id, через GET /customers/search)
CUSTOMER_NOT_FOUNDToast «Клиент не найден» + redirect на /customers
FORBIDDENToast «Нет прав»
Сеть / 500Toast «Не удалось сохранить» + кнопка «Повторить»

Состояния

СостояниеЧто показываем
Загрузка (view)Skeleton шапки + вкладок
Not found (404)Страница «Клиент не найден или был удалён» + кнопка «← Назад к списку»
Сохранение (new/edit)Кнопка «Сохранить» в loading, форма disabled
Ошибка (фатальная)«Не удалось загрузить клиента» + «Повторить»

Ролевой доступ (сводно)

ДействиеВладелец франшизыПартнёрОбычный сотрудник (c customers.read)
Просмотр
Редактирование✅ если есть customers.edit
Удаление
Merge

Ссылки