Skip to content

Product Form Validation Design

2026-02-20 | Sprint 2, задача 3+6

Проблема

Форма объявления не валидирует поля на клиенте. Черновик и публикация требуют одинаковых полей. Нет лимитов на длину полей и размер/тип файлов.

Решение

Дефолтные значения

  • DEFAULT_TITLE = 'Моё объявление'
  • DEFAULT_PRICE = 1
  • При создании: форма инициализируется дефолтами
  • При сохранении черновика: если title пуст — подставляем DEFAULT_TITLE, если price 0 — подставляем DEFAULT_PRICE
  • При загрузке фото (ensureDraftForUpload): та же логика подстановки дефолтов

Zod-схемы

Общие лимиты (применяются и к draft, и к publish):

ПолеТипЛимит
titlestringmax 300
descriptionstringmax 5 000
priceinteger1–100 000 000
oem_numberstringmax 50
oem_numbers[]string[]max 20 шт, каждый max 50
manufacturerstringmax 100
addressstringmax 255

publishSchema (дополнительно):

  • title: min(1), не равен DEFAULT_TITLE
  • price: min(2) (больше дефолтной 1)

draftSchema:

  • Всё опционально, лимиты по длине сохраняются

Логика кнопок

  • "Сохранить черновик" — всегда активна (убираем :disabled)
  • "Опубликовать" — disabled пока canPublish === false
  • canPublish = title.trim() !== '' && title.trim() !== DEFAULT_TITLE && price > 1

Trim перед отправкой

В buildRequestBody() тримятся: title, description, oem_number, manufacturer, address, каждый элемент oem_numbers[].

Инпут цены

  • inputmode="numeric", парсим только цифры
  • Если результат > 100 000 000 — отбрасываем ввод
  • Форматирование через Intl.NumberFormat('ru-RU')

Валидация файлов (useImageUpload)

  • MIME: image/jpeg, image/png, image/webp
  • Размер: max 10 МБ на файл
  • Количество: max 10 фото (серверные + локальные), PRODUCT_MAX_PHOTOS = 10
  • При нарушении — toast с описанием ошибки, файл отклоняется
  • При достижении лимита — кнопка «Добавить» disabled + tooltip «Максимум 10 фотографий»
  • При ошибке загрузки — иконка ошибки + кнопка retry для повторной попытки

Безопасность

  • XSS: Vue экранирует {{ }} автоматически, v-html не используется
  • Фронтовая валидация — первая линия; бэкенд валидирует повторно
  • Файлы: проверка MIME на клиенте + Imagick на сервере

Файлы для изменения

  1. app/entities/product/model/product.schema.ts — Zod-схемы
  2. app/features/product-form/composables/useProductForm.ts — дефолты, trim, валидация
  3. app/features/product-form/ui/ProductFormPage.vue — UI (кнопки, maxlength, лимит цены)
  4. app/features/image-upload/composables/useImageUpload.ts — валидация файлов
  5. i18n/locales/ru.json — сообщения ошибок валидации