CAT-09: Категории дерево с inline CRUD
Что сделать
Создать страницу категорий с рекурсивным деревом и inline-действиями.
Смотри спеку
Каталог — Категории — полная спека.
Конкретно
Страница /catalog/categories. API: GET /api/v1/admin/categories.
Заголовок “Категории”. Дерево отображается рекурсивно (неограниченная вложенность).
Отображение узла:
- Стрелка свёртки
▸/▾(если есть дочерние) - Название категории
- Индикатор статуса: зелёная точка (active) / серая точка (inactive)
- Inline-действия (Franchise only, видны при hover)
Inline-действия (Franchise only):
| Действие | Иконка | Что происходит |
|---|---|---|
| Добавить подкатегорию | ⊕ | Inline text input под узлом. Enter → POST /api/v1/admin/categories с parent_id. Esc → отмена |
| Переименовать | ✏ | Название → inline text input. Enter → PATCH /api/v1/admin/categories/:id. Esc → отмена |
| Удалить | ✗ | Модалка подтверждения |
| Деактивировать/Активировать | Toggle | Переключение статуса PATCH /api/v1/admin/categories/:id |
Кнопка “Добавить корневую категорию” внизу дерева (Franchise only). При клике — inline text input на корневом уровне. Enter → POST /api/v1/admin/categories без parent_id. Esc → отмена.
Модалка удаления:
CATEGORY_HAS_CHILDREN: “Невозможно удалить. Категория содержит подкатегории. Сначала удалите или перенесите их.” → кнопка “Понятно”CATEGORY_HAS_PRODUCTS: “Невозможно удалить. В категории есть товары. Сначала перенесите их.” → кнопка “Понятно”- Нет препятствий: “Удаление категории. Удалить категорию [название]? Это действие нельзя отменить.” → “Отмена” / “Удалить” (красная). После успеха: убрать узел, toast “Категория удалена”
Модалка каскадной деактивации (при деактивации узла с дочерними):
- “Деактивация категории. Деактивировать [название] и все подкатегории? Товары в них тоже будут деактивированы.”
- Кнопки: “Отмена” / “Деактивировать” (оранжевая)
- После успеха: обновить статусы в дереве, toast “Категория и подкатегории деактивированы”
- Активация — НЕ каскадная (только выбранная категория)
Состояния:
- Загрузка: skeleton-дерево (3-4 placeholder-строки с отступами)
- Пусто (Franchise): “Категории пока не созданы” + кнопка “Добавить корневую категорию”
- Пусто (Franchisee/Manager): “Категории пока не созданы”
- Ошибка: “Не удалось загрузить данные” + кнопка “Повторить”
Ролевая видимость:
- Franchise: полное дерево + все действия
- Franchisee: readonly дерево
- Manager: readonly дерево
- Cashier: 403 → редирект на дашборд
Файлы
web/src/pages/catalog/CategoriesPage.tsx— создать
Зависит от
- CAT-01
- CAT-02