Appearance
Image Polling + Draft Redirect
Проблема
- Нет polling статуса image — после upload бэкенд возвращает
status: 'processing', фронтенд никогда не проверяет когда станетready. UI показывает вечный спиннер. - Потеря контекста черновика —
ensureDraftForUpload()создаёт product, но URL остаётся/cabinet/products/new. После reload — новая форма, черновик потерян.
Решение
1. Redirect после создания черновика
Правило: любой успешный saveDraft() в mode === 'create', который создал product → redirect на /cabinet/products/{id}/edit с replace: true.
Передача состояния через Pinia (без мерцания):
Новый store app/stores/draftTransfer.ts:
ts
state: {
formSnapshot: ProductForm | null // состояние формы
images: ProductImage[] // уже загруженные изображения
pendingFiles: File[] // файлы, ожидающие upload
productId: number | null
}
actions: {
save(form, images, pendingFiles, productId)
take() → { formSnapshot, images, pendingFiles, productId } | null // возвращает и очищает
}Поток (кнопка "Сохранить черновик" на /new):
saveDraft()→ POST → полученproductIddraftTransferStore.save(form, images, [], productId)navigateTo('/cabinet/products/{id}/edit', { replace: true })- Edit page mount →
draftTransferStore.take() - Данные есть → гидрирует форму из snapshot мгновенно, пропускает
loadProduct()
Поток (upload фото на /new):
onAddFiles()→ файлы выбраныensureDraftForUpload()→saveDraft()→productIdполученdraftTransferStore.save(form, images, files, productId)- Redirect на edit
- Edit page mount →
take()→ гидрирует форму + вызываетaddFiles(pendingFiles)
2. Polling статуса изображений
Где: useImageUpload.ts
Триггер: после uploadFile() вернул image с status === 'processing'
Механизм:
setInterval(2500)— поллимGET /vendor/products/{productId}- Из ответа берём
images[], обновляем только те, у которыхstatusизменился - Когда все images
readyилиfailed→clearInterval
Защита:
onUnmounted()→ clearInterval- Максимум 24 попытки (60 сек) → таймаут → ставим
status: 'error'с сообщением - Один активный polling на весь composable
Контекст бэкенда:
- Обработка асинхронная через Redis queue (worker
app:process-images) - Pipeline: download original → GD resize (3 размера) → watermark (кроме thumbnail) → 2 формата (WebP + JPEG) → upload в public S3
- Обработка занимает 1-5 секунд
- Retry на бэке: 3 попытки с exponential backoff (1s → 4s → 16s)
ready→ все 6 URL заполнены одновременноfailed→error_messageзаполнен- Нет отдельного endpoint для статуса — только GET /vendor/products/
Файлы
Создать:
| Файл | Назначение |
|---|---|
app/stores/draftTransfer.ts | Pinia store для передачи состояния при redirect |
Изменить:
| Файл | Что меняется |
|---|---|
useImageUpload.ts | Polling после upload при status === 'processing' |
useProductForm.ts | saveDraft() в create mode — signal для redirect |
ProductFormPage.vue | Redirect после save в create + гидрация из Pinia на mount в edit + upload pending files |
product.schema.ts | Проверить imageStatusSchema — 'error' vs 'failed' |
Не меняется: new.vue, edit.vue, backend, routing.
Порядок реализации
- Проверить/исправить
imageStatusSchema(error vs failed) - Создать
useDraftTransferStore - Добавить polling в
useImageUpload - Добавить redirect + гидрацию в
ProductFormPage - Тесты