Студия — экран генерации

Главный экран AI Студии. Пользователь загружает фото блюда, выбирает один из 4 режимов и параметров — задание уходит в фон, форма освобождается, результат появляется в Истории.

Файл

ai-photo-studio-frontend/src/pages/PhotoStudioPage.tsx

Роут

GET /photo — default route после успешного логина.

GET /photo?job=<job_id> — открывает существующий job в режиме «прогресс» (используется при retry с экрана Истории).

Доступ

Permission gensvc.access. Без него фронт редиректит на /no-access.

Режимы

ModeSelector в шапке экрана, четыре варианта:

РежимКлюч APIЧто нужно
Стильstyleimage + системный или личный preset (опц. extra_prompt ≤500 симв.)
Композицияcompositionimage + plate + background (опц. extra_prompt ≤500 симв.)
Преобразитьenhanceimage + обязательный extra_prompt (≤1000 симв.)
Постерposterimage + poster_template + обязательный extra_prompt (≤1000 симв.) — это headline

Смена режима сбрасывает выбранный preset/template/asset, фото и extra_prompt остаются.

Layout

┌──────────────────────────────────────────────────────────┐
│ [✓ Генерация запущена] (banner — после успешного submit) │  ← см. § Submit & banner
├──────────────────────────────────────────────────────────┤
│ Заголовок «Студия фото»                                  │
│ Подсказка под режим (RU)                                 │
│ ModeSelector: [Стиль] [Композиция] [Преобразить] [Постер]│
├──────────────────────────────────────────────────────────┤
│ ┌─ Dropzone ─┐  ┌─ Галерея пресетов / форма ─┐          │
│ │  фото      │  │  (зависит от режима)        │ height  │
│ │  (drag/    │  │                             │ 540px   │
│ │   drop)    │  │                             │         │
│ └────────────┘  └─────────────────────────────┘         │
├──────────────────────────────────────────────────────────┤
│ ▸ Дополнительные пожелания (collapsible, default closed) │
├──────────────────────────────────────────────────────────┤
│ [Размер ▾] [Кол-во ▾]              [Сгенерировать]      │
└──────────────────────────────────────────────────────────┘

В режиме Композиции — три колонки в основной области (Dropzone + Plate gallery + Background gallery), в остальных — две.

Поля и валидация

ПолеИсточникВалидация
imageDropzone (jpg/png/webp ≤10 МБ)min: обязательно
preset_id (style) или user_preset_id (style)PresetGalleryone-of: ровно один из двух обязателен
template_id (poster)PosterTemplateGalleryrequired в poster
plate_id, background_id (composition)AssetGallery × 2оба required в composition
extra_promptTextarea, ≤500/≤1000 (по режиму)required в enhance и poster, опц. в style/composition
sizeSizeSelectorone-of: delivery-1x1 / story-9x16 / banner-16x9 / menu-4x3
countCountSelector1..4

canSubmit зависит от режима: для style — нужны image + (preset_id\|user_preset_id); для enhanceimage + extra_prompt.trim() ≠ ""; для posterimage + template_id + extra_prompt; для compositionimage + plate + background.

Submit & banner

После клика «Сгенерировать»:

  1. pageState = "submitting" — кнопка показывает loader.

  2. POST /v1/jobs/{photo|enhance|poster|composition} (multipart) → ответ 202 за миллисекунды (см. ADR-022).

  3. Не переключаться в phase: "progress" — иначе форма заблокирована.

  4. Поставить submittedNotice = { jobId }, image = null, pageState = "idle".

  5. Сверху страницы появляется зелёный banner:

    Генерация запущена. Обычно занимает 3-6 минут. Можно закрыть вкладку — фото появится в Истории, придёт уведомление. [Открыть Историю →] [✕]

  6. Banner авто-исчезает через 12 секунд или по клику ✕. Кнопка «Открыть Историю» — <Link to="/history">.

  7. Форма доступна для нового submit (фото нужно выбрать заново).

Состояния pageState

stateDiagram-v2
    [*] --> idle
    idle --> submitting: click Submit
    submitting --> idle: 202 OK (новый flow — non-blocking)
    submitting --> idle: error (показать submitError)
    idle --> progress: ?job=<id> в URL (retry-flow из Истории)
    progress --> result: SSE done succeeded
    progress --> failed: SSE done failed
    failed --> progress: click "Повторить"
    result --> idle: click "Новая генерация"

Legacy progress phase

phase: "progress" остался только для retry-flow (/photo?job=<id> с Истории). На обычном submit мы туда не переходим — сразу освобождаем форму.

Отображаемые ошибки

  • submitError — alert красным баннером под ModeSelector (выше Dropzone). Сообщение из e.message или fallback «Ошибка создания задачи».
  • extraPromptOver — inline error под Textarea, кнопка Submit заблокирована.
  • enhanceCommentEmpty — после первого клика на Submit без текста, под Textarea: «Расскажите, какой результат хотите получить».

Связанные API

  • POST /v1/jobs/photo (style mode)
  • POST /v1/jobs/enhance
  • POST /v1/jobs/poster
  • POST /v1/jobs/composition
  • POST /v1/jobs/{id}/retry (retry-flow)
  • GET /v1/presets, GET /v1/poster-templates, GET /v1/composition-assets — для галерей
  • GET /v1/user-presets — личная библиотека стилей

Ссылки