Админка пресетов — CRUD системных стилей
Управление каталогом системных пресетов AI Студии. Пресеты доступны всем пользователям сервиса (системные = глобальные на франшизу), редактируются только владельцами с gensvc.preset.admin.
Файлы
ai-photo-studio-frontend/src/pages/AdminPresetsPage.tsx— список + grid + фильтры + пагинацияai-photo-studio-frontend/src/pages/AdminPresetCreatePage.tsx— создание нового пресетаai-photo-studio-frontend/src/components/admin/PresetGrid.tsx— сетка карточек с hover-actionsai-photo-studio-frontend/src/components/admin/ConfirmDialog.tsx— модалка подтверждения
Роуты
| Path | Component | Назначение |
|---|---|---|
/admin/presets | AdminPresetsPage | Список со всеми системными пресетами |
/admin/presets/new | AdminPresetCreatePage | Форма создания нового пресета |
Доступ
Permission gensvc.preset.admin. Роуты обёрнуты в <RequirePermission perm="gensvc.preset.admin"> — без него редирект на /no-access.
Layout — список
┌─────────────────────────────────────────────────────────┐
│ Админка пресетов [+ Новый пресет] │
├─────────────────────────────────────────────────────────┤
│ [Все] [Активные] [Архив] │ [Категория ▾] │ [Поиск...] │ ← Filters bar
│ 278 пресетов│
├─────────────────────────────────────────────────────────┤
│ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ │
│ │ img │ │ img │ │ img │ │ img │ │ img │ │ img │ │ ← PresetGrid
│ │ name│ │ name│ │ name│ │ name│ │ name│ │ name│ │
│ │ slug│ │ slug│ │ slug│ │ slug│ │ slug│ │ slug│ │
│ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘ │
│ ... 50 карточек на страницу ... │
├─────────────────────────────────────────────────────────┤
│ [Показать ещё (228)] │
└─────────────────────────────────────────────────────────┘
Фильтры (Filters bar)
| Фильтр | Тип | Поведение |
|---|---|---|
| Tabs «Все / Активные / Архив» | enabled: all | true | false | Чипы. Изменение → reset offset → fetch |
| Категория ▾ | select | category=... query, all = без фильтра |
| Поиск | input | Дебаунс 250 мс. Ищет по name_en, name_ru, slug |
Один useEffect, один fetch
Раньше было два
useEffectс пересекающимися deps — на смену фильтра шёл двойной API-запрос. Сейчас остался один: при смене enabled/category/q setOffset(0) в setter’е, единственный effect отрабатывает на новых deps.
Карточка пресета (PresetGrid item)
Каждая карточка: квадрат 220×220, превью reference-изображения, под ним name_ru, мелким — slug.
При hover на карточку:
- Лёгкая тень + scale.
- Overlay из 4 круглых кнопок:
- ✏️ Редактировать — переход к форме (placeholder, в текущем MVP not-implemented)
- 🖼 Заменить изображение — file picker →
POST /v1/admin/presets/{id}/reference - 🚫 / 👁 Toggle enabled —
PATCH /v1/admin/presets/{id}{ enabled: !current } - 🗑 Удалить —
ConfirmDialog(см. ниже) →DELETE /v1/admin/presets/{id}
Пресеты с enabled: false отображаются с полупрозрачностью.
ConfirmDialog
Модалка подтверждения удаления.
role="dialog",aria-modal,aria-labelledby.- Escape → cancel (через
document.addEventListener("keydown")в effect). - Default focus при открытии — кнопка «Отмена» (безопаснее для destructive prompts).
- Prop
danger: true→ кнопка confirm красная (colors.danger);false→ стандартныйcolors.red. - Overlay click → cancel.
Создание пресета (/admin/presets/new)
Форма:
- Slug (auto-generate из name_en при отсутствии)
- Name EN, Name RU
- Category (select)
- Reference image (file picker, jpg/png/webp ≤10 МБ)
- Submit →
POST /v1/admin/presets→ редирект на/admin/presets
Ошибки валидации — inline под полями.
Пагинация — load more
PAGE_SIZE = 50, total — из meta.total ответа списка. canLoadMore = items.length < total. На клике — setOffset(prev => prev + PAGE_SIZE), единственный useEffect перезапросит и append (fetchJobs(append=offset>0)).
Состояния
| Состояние | UI |
|---|---|
loading && items.length === 0 | Spinner по центру |
error | Красная плашка с текстом ошибки |
items.length === 0 (после загрузки) | Empty state: «Пресеты не найдены. Попробуйте изменить фильтры» |
Загрузка loadMore | Spinner снизу под grid |
Связанные API
GET /v1/admin/presets?enabled=&category=&q=&limit=&offset=— список (admin)POST /v1/admin/presets— создатьPATCH /v1/admin/presets/{id}— обновить (enabled / name / category)POST /v1/admin/presets/{id}/reference— заменить картинку (multipart)DELETE /v1/admin/presets/{id}— удалить (мягко, потом hard если нет FK)
Все защищены RequirePermission("gensvc.preset.admin") на бэке.
Похожие админ-экраны (paste-template)
- Шаблоны постеров (
/admin/poster-templates) —AdminPosterTemplatesPage.tsx. Та же раскладка: Tabs / Category / Search / Grid / ConfirmDialog. CRUD наPOST/PATCH/DELETE /v1/admin/poster-templates. - Композиция: тарелки и фоны (
/admin/composition) —AdminCompositionAssetsPage.tsx. Аналогично, плюс togglekind: vessel \| backgroundв фильтре.
Эти экраны следуют тому же паттерну (filters bar + hover-grid + ConfirmDialog), отдельной спекой не выделены пока — копия данной с заменой сущности.