Каталог — Категории
Роут: /catalog/categories
API: GET /api/v1/categories, POST /api/v1/categories, PATCH /api/v1/categories/{id}, DELETE /api/v1/categories/{id}
Что видит пользователь
Страница с деревом категорий каталога. Вверху — заголовок “Категории”. Дерево отображается рекурсивно (неограниченная вложенность). Внизу дерева — кнопка “Добавить корневую категорию” (только Franchise).
Дерево категорий
Отображение узла
Каждый узел дерева содержит:
| Элемент | Описание |
|---|---|
| Стрелка свёртки | ▸ / ▾ — если есть дочерние категории |
| Название | Текст названия категории |
| Индикатор статуса | Зелёная точка (active) / Серая точка (inactive) |
| Inline-действия | Иконки действий (только Franchise, видны при hover) |
Inline-действия (Franchise)
| Действие | Иконка | Что происходит |
|---|---|---|
| Добавить подкатегорию | ⊕ | Появляется inline text input под узлом. Enter — сохранить, Esc — отмена. API: POST /api/v1/categories с parent_id |
| Переименовать | ✏ | Название превращается в inline text input. Enter — сохранить, Esc — отмена. API: PATCH /api/v1/categories/{id} |
| Удалить | ✗ | Модалка подтверждения (см. ниже). API: DELETE /api/v1/categories/{id} |
| Деактивировать / Активировать | Toggle | Переключение статуса (см. ниже). API: PATCH /api/v1/categories/{id} |
Порядок отображения
- У каждой категории есть числовое поле
display_order - MVP: ручной ввод порядкового номера при создании/редактировании
- Phase 2: drag-and-drop для перетаскивания узлов
Кнопки
| Кнопка | Где | Видимость |
|---|---|---|
| ”Добавить корневую категорию” | Внизу дерева | Franchise |
При клике — появляется inline text input в конце дерева (корневой уровень). Enter — сохранить (POST /api/v1/categories без parent_id), Esc — отмена.
Модалки подтверждений
Удаление
Триггер: клик ✗ на узле
API: DELETE /api/v1/categories/{id}
Сценарий 1 — Категория содержит подкатегории (CATEGORY_HAS_CHILDREN):
- Заголовок: “Невозможно удалить”
- Текст: “Категория содержит подкатегории. Сначала удалите или перенесите их.”
- Кнопка: “Понятно” (закрывает модалку)
Сценарий 2 — В категории есть товары (CATEGORY_HAS_PRODUCTS):
- Заголовок: “Невозможно удалить”
- Текст: “В категории есть товары. Сначала перенесите их.”
- Кнопка: “Понятно” (закрывает модалку)
Сценарий 3 — нет препятствий:
- Заголовок: “Удаление категории”
- Текст: “Удалить категорию [название]? Это действие нельзя отменить.”
- Кнопки: “Отмена” / “Удалить” (красная)
- После успеха: убрать узел из дерева, toast “Категория удалена”
Каскадная деактивация
Триггер: toggle деактивации на узле, у которого есть дочерние категории
- Заголовок: “Деактивация категории”
- Текст: “Деактивировать [название] и все подкатегории? Товары в них тоже будут деактивированы.”
- Кнопки: “Отмена” / “Деактивировать” (оранжевая)
- После успеха: обновить статусы всех затронутых узлов в дереве, toast “Категория и подкатегории деактивированы”
Каскадная активация
(Доработано в ADR-013)
Триггер: toggle активации на узле, у которого есть неактивные дочерние категории
- Заголовок: “Активация категории”
- Текст: “Активировать [название]?”
- Кнопки: “Только эту” / “Вместе с подкатегориями” (зелёная) / “Отмена”
- “Только эту” →
PATCH /api/v1/categories/{id}сis_active: true, cascade: false - “Вместе с подкатегориями” →
PATCH /api/v1/categories/{id}сis_active: true, cascade: true - После успеха: обновить статусы в дереве, toast
Если у узла нет дочерних — модалка не показывается, активация мгновенная.
Состояния
| Состояние | Что показываем |
|---|---|
| Загрузка | Skeleton-дерево (3-4 placeholder-строки с отступами) |
| Пусто (Franchise) | “Категории пока не созданы” + кнопка “Добавить корневую категорию” |
| Пусто (Franchisee/Manager) | “Категории пока не созданы” |
| Ошибка загрузки | ”Не удалось загрузить данные” + кнопка “Повторить” |
Ролевая видимость
Franchise
- Видит дерево категорий
- Все действия: добавление, переименование, удаление, деактивация/активация, кнопка “Добавить корневую категорию”
Franchisee
- Видит дерево категорий в режиме readonly
- Нет inline-действий
- Нет кнопки “Добавить корневую категорию”
Manager
- Видит дерево категорий в режиме readonly
- Нет inline-действий
- Нет кнопки “Добавить корневую категорию”
Cashier
- 403 Forbidden — нет доступа к странице
- Редирект на дашборд