Образы¶
Обзор¶
Образы — комбинации вещей из гардероба, организованные по слотам (верх, низ, обувь, аксессуар). Пользователь собирает образ в конструкторе, система автоматически генерирует название через AI. Образы можно просматривать, шерить и удалять.
Страницы модуля¶
| Страница | Маршрут | Назначение |
|---|---|---|
| OutfitsPage | /outfits |
Список всех образов (свои / чужие / все) |
| OutfitBuilderPage | /outfits/build |
Конструктор образа |
| ItemSelectPage | /outfits/build/select |
Выбор вещи для конкретного слота |
| OutfitDetailPage | /outfits/:outfitId |
Детальный просмотр образа |
State diagram: конструктор образов¶
stateDiagram-v2
[*] --> Init: Открытие /outfits/build
Init --> SlotsLoaded: useLocalSlots() загружены
SlotsLoaded --> Empty: initSlots(slots)
Empty --> Filling: Выбор вещи для слота
Filling --> Filling: Добавление вещи в другой слот
Filling --> Empty: clearAll()
Filling --> TitleSuggesting: suggestItems.length > 0
TitleSuggesting --> TitleReady: API вернул название
TitleReady --> Filling: Пользователь изменил название
Filling --> Creating: handleCreate()
Creating --> DuplicateCheck: findDuplicateOutfit()
DuplicateCheck --> Created: Нет дупликата
DuplicateCheck --> Filling: Дупликат найден (warning)
Created --> [*]: navigate('/outfits')
state Filling {
[*] --> SlotView: Листает слоты
SlotView --> ItemSelect: "Все →" (navigate to select page)
ItemSelect --> SlotView: Выбрал вещь (navigate back)
}

Sequence diagram: создание образа¶
sequenceDiagram
participant U as Пользователь
participant Builder as OutfitBuilderPage
participant Store as outfitBuilderStore
participant API as outfitsApi
participant IDB as IndexedDB
U->>Builder: Открывает /outfits/build
Builder->>Store: initSlots(slots)
Store-->>Builder: slots[] (верх, низ, обувь)
loop Для каждого слота
U->>Builder: Выбирает вещь из горизонтального ряда
Builder->>Store: setSlotItem(slotId, item)
end
Note over Builder, API: Автогенерация названия
Builder->>API: POST /outfits/suggest-title { outfit_items }
API-->>Builder: { title: "Городской casual" }
Builder->>Store: setTitle(suggestion)
U->>Builder: Нажимает "Создать образ"
Builder->>IDB: findDuplicateOutfit(items)
IDB-->>Builder: null (нет дубликата)
Builder->>API: offlineOutfitsService.create({ title, outfit_items })
API-->>Builder: Outfit created
Builder->>Store: clearAll()
Builder->>U: navigate('/outfits')

Слоты¶
Слоты определяют позиции вещей в образе:
| Слот | Код | Назначение |
|---|---|---|
| Верх | top | Футболки, рубашки, худи, куртки |
| Низ | bottom | Джинсы, брюки, юбки |
| Обувь | shoes | Кроссовки, туфли, ботинки |
Слот "Платье" (dress) фильтруется из конструктора: slots.filter((sl) => sl.code !== 'dress').
Автогенерация названия¶
При изменении состава вещей (с debounce 500ms):
1. Отправляется POST /outfits/suggest-title с текущими outfit_items
2. API возвращает AI-сгенерированное название
3. Название применяется автоматически если пользователь не менял его вручную
Логика auto-apply:
const canAutoApply =
currentTitle.length === 0 ||
(lastAutoTitle.length > 0 && currentTitle === lastAutoTitle);
API-модуль¶
// outfitsApi (frontend/src/api/outfits.ts)
outfitsApi.list(params) // GET /outfits
outfitsApi.get(id) // GET /outfits/:id
outfitsApi.create(data) // POST /outfits
outfitsApi.suggestTitle(data) // POST /outfits/suggest-title
outfitsApi.update(id, data) // PATCH /outfits/:id
outfitsApi.delete(id) // DELETE /outfits/:id
outfitsApi.save(id) // POST /outfits/:id/save
outfitsApi.unsave(id) // DELETE /outfits/:id/save
outfitsApi.share(id, format) // POST /outfits/:id/share
outfitsApi.similar(id, params) // GET /outfits/:id/similar
Навигация¶
- → Конструктор: outfit-builder.md
- → Детали и список: outfit-details.md