Admin Franchise web — BR 4.1
Контракты
- Фронт-спеки:
- Внешние меню — Список
- Внешние меню — Конструктор
- Шаблоны монитора (рендер делается в Catalog Service, фронт админки только превью открывает)
- API клиент: см. Admin BFF · API клиент в web
Что делаем
Раздел и роутинг
-
web/src/App.tsx:<Route path="external-menus" element={<PermissionRoute permission="external_menus.read"><ExternalMenusListPage /></PermissionRoute>} /><Route path="external-menus/:id/edit" element={<PermissionRoute permission="external_menus.read"><ExternalMenuEditorPage /></PermissionRoute>} />
-
web/src/components/layout/Sidebar.tsx— добавить пункт «Внешние меню» в группу «Каталог» (если есть) или отдельным пунктом. Видимость поexternal_menus.read.
Список меню
-
web/src/pages/external-menus/ListPage.tsx:- Таблица с колонками + фильтры + поиск + пагинация (по образцу
pages/employees/ListPage.tsx) - Вкладки «Активные» / «Корзина» (через query param
archived=true) - Кнопки в шапке: «Добавить меню» (открывает мастер) + «Корзина» (toggle)
- Меню действий строки: Открыть / Опубликовать / Снять / Скопировать URL / Скачать ZIP / Скачать JSON / Превью / Дублировать / Удалить
- 4 модалки подтверждений (см. фронт-спеку)
- Таблица с колонками + фильтры + поиск + пагинация (по образцу
Wizard создания
-
web/src/components/external-menus/CreateMenuWizard.tsx:- 2-шаговая модалка
- Шаг 1: имя, канал, ТТ, шаблон (для tv_screen)
- Шаг 2: slug (только для tv_screen) — auto/custom toggle
- Валидация уникальности имени + slug на лету (debounce 500 ms)
- На submit →
createExternalMenu()→ редирект на/external-menus/{id}/edit
Редактор (главная страница)
-
web/src/pages/external-menus/EditorPage.tsx:- Двухпанельный layout
- Загрузка через
getExternalMenu(id)+listProducts({ franchise_id })+listCategories() - Шапка: имя (inline-edit), live URL копируемый, действия меню, кнопка публикации
- Левая панель: дерево каталога с фильтрами и поиском
- Правая панель: дерево external_menu
-
web/src/components/external-menus/CatalogPanel.tsx:- Дерево категорий с раскрытием
- Поиск товаров (debounce 300 ms)
- Фильтр по категории
- Toggle «Скрывать уже добавленные»
- Иконки для добавленных (⚿) / не-добавленных (•)
- Drag handler с использованием
react-dndили@dnd-kit/core
-
web/src/components/external-menus/StructurePanel.tsx:- Дерево категорий (accordion)
- Drop-зона для drag из CatalogPanel →
addItem() - Reorder категорий drag-n-drop →
reorderCategories() - Перенос item между категориями →
updateItem()с новым category_id - Кнопка «+ Добавить категорию» с inline-инпутом
- Меню действий категории (✏ переименовать, 🎨 иконка, ❌ удалить)
-
web/src/components/external-menus/ItemRow.tsx:- Строка item: drag-handle, имя (с пометкой override), цена (effective + small caption если override), toggle visibility, ⚙ override-форма
- Состояния: ok / orphan (красный banner) / в стоп-листе (жёлтый banner)
-
web/src/components/external-menus/OverrideForm.tsx:- Inline-форма с полями: override_name, override_description, override_price, visible
- Подсказки про эффективную цену (3 значения: catalog / price_list / effective)
- Кнопки: Сохранить / Очистить overrides / Удалить из меню
- Auto-save через debounce 600 ms (опционально)
Модалки
-
web/src/components/external-menus/PublishModal.tsx— подтверждение публикации -
web/src/components/external-menus/DeleteModal.tsx— soft delete -
web/src/components/external-menus/RestoreModal.tsx— восстановление с обработкой 409 (NAME/SLUG conflict с inline-полем) -
web/src/components/external-menus/MenuSettingsModal.tsx— настройки меню (открывается по ✏)
State управление
- Использовать существующий паттерн (zustand / react-query — что в проекте). У
erp-admin/webуже используется state, посмотреть в существующих pages - Локальный state в
EditorPageдля текущей раскрытой override-формы, drag-state, etc.
Permissions
- Расширить
usePermissionshook (если есть):useCanReadExternalMenus()→ booleanuseCanEditExternalMenus()→ boolean
- Все edit-действия гейтятся через эти хуки
Тесты
- React Testing Library:
ListPage— рендер списка с разными статусами, переход между вкладками, кнопки гейтятся по permissionCreateMenuWizard— валидация имени, slug regex, navigation между шагамиEditorPage— drag-drop добавление, override-форма сохраняетсяPublishModal— disabled для пустого меню, активна для непустого
Не делаем
- ❌ Embedded live preview iframe в редакторе — отложено в P1. В P0 владелец открывает превью отдельной вкладкой
- ❌ Drag-drop reorder с touch-events для планшетов — P1
- ❌ Bulk actions в списке (Mass delete, Mass publish) — P1
- ❌ Версионирование UI — нет в BR 4.1
Verification
- На
erp-test.nirbi.ru→ /external-menus → список открывается, кнопка «Добавить меню» работает - Создать меню «Test Bar» канал tv_screen → попадаем в редактор
- Drag товар из левой в правую → появляется item в категории «Без категории» (или дефолтной)
- Создать категорию «Кофе» → перетянуть item в неё
- Open override-форму → ввести цену 290 → сохранить → effective_price обновился
- Опубликовать → live URL стал кликабельный, скопировался в буфер
- Открыть live URL в новой вкладке (in kiosk-mode preview) → меню рендерится
- Изменить override_name → ждать ≤30 сек → меню в kiosk-вкладке обновилось
- Удалить меню → перешло в Корзину
- Восстановить → вернулось в Активные как draft