Appearance
Cabinet: Favorites + Settings
Scope
Two features for the personal cabinet:
- Favorites — API integration, UI buttons, favorites page
- Settings — profile editing, avatar, password, sessions, business profile
1. Favorites
Store refactoring (app/stores/favorites.ts)
Current store holds only Set<number> locally. Refactor to:
- Initialization: in
app/plugins/auth.tsafterfetchUser(), if authenticated — callGET /vendor/favorites, save IDs toSetand full product data toMap<number, Product> - toggle(productId): optimistic UI — update
Setimmediately, thenPOST /vendor/favoritesorDELETE /vendor/favorites/{product_id}. On error — rollback - items: computed from
Mapvalues for the favorites page
API calls
GET /vendor/favorites → load all favorites (Product[])
POST /vendor/favorites → { product_id } — add
DELETE /vendor/favorites/{product_id} → removeFavoriteButton component
Location: app/features/favorites/ui/FavoriteButton.vue
- Props:
productId: number - Renders heart icon (outline/filled)
v-if="isAuthenticated"— hidden for guests- On click:
favoritesStore.toggle(productId) - Scale transition animation on toggle
Integration points
ProductCard.vue— addFavoriteButtonin top-right corner/product/[id].vue— add next to price
Favorites page (/cabinet/favorites)
- Header — "Избранное" + counter ("12 товаров")
- Grid — same
ProductCardas catalog. Grid: 1 col mobile, 2 onsm, 3 onlg - Empty state — heart icon + "Вы пока ничего не добавили в избранное" + "Перейти в каталог" button
- Data from
favoritesStore.items(loaded on init) - On unfavorite — card disappears immediately (optimistic)
2. Settings
Routing
/cabinet/settings → redirect to /cabinet/settings/profile
/cabinet/settings/profile → profile (name, phone, avatar, geo)
/cabinet/settings/security → password + active sessions
/cabinet/settings/business → business profile (if account_type === 'business')Navigation
Horizontal tabs (UTabs) at top of settings area for switching between profile / security / business. "Бизнес" tab visible only when account_type === 'business'.
File structure
app/pages/cabinet/settings/
├── index.vue → redirect to profile
├── profile.vue
├── security.vue
└── business.vue
app/features/cabinet-settings/
├── composables/
│ ├── useProfileForm.ts
│ ├── useSecurityForm.ts
│ └── useBusinessForm.ts
└── ui/
└── SettingsTabs.vue → shared tabs navigationProfile page (/cabinet/settings/profile)
Fields:
- Avatar — current photo + "Изменить" button. Upload via
POST /vendor/me/avatar(max 5MB). Local preview before upload - Display name (
display_name) — text input, required - Phone (
phone) — text input with mask - Geo — GeoSelect cascade (region → city → district → metro). Reuse existing
GeoSelectfromfeatures/geo-select - Account type (
account_type) —URadioGroup: «Частное лицо» / «Бизнес». When switched to 'business', "Бизнес" tab appears in settings tabs. Saved viaPUT /vendor/mealong with other profile fields
Behavior:
- Load data:
GET /vendor/meon mount - Save:
PUT /vendor/me— "Сохранить" button, toast on success - Validation: Zod schema, backend errors via
useApiError - Avatar uploaded separately, updates
authStore.user.avatar_url
Security page (/cabinet/settings/security)
Password change:
- Three fields: current password, new password, confirmation
- Validation: Zod schema matching backend rules (8-128 chars, uppercase, lowercase, digit, special char, no 3+ identical/sequential)
- API:
PUT /vendor/mewithcurrent_password+password - On success: toast + clear fields
Active sessions:
- Session list:
GET /vendor/sessions— list with device info, IP, last activity - Current session marked with "Текущая" badge
- "Завершить" button per session:
DELETE /vendor/sessions/{id} - "Завершить все кроме текущей" button:
POST /auth/logout-all
Business profile page (/cabinet/settings/business)
Access:
- Tab visible only when
authStore.user.account_type === 'business'(switched on profile page) - If
personal— tab hidden, direct URL access redirects to/cabinet/settings/profile
Fields:
- Company name (
company_name) — required - INN (
inn) — text input with mask (10 or 12 digits) - Address (
address) — text input
Behavior:
- Data from
GET /vendor/me(fieldbusiness_profile) - Save:
PUT /vendor/mewith nestedbusiness_profile
Guest behavior
- Favorite button (
FavoriteButton) hidden for unauthenticated users (v-if="isAuthenticated") - All cabinet pages protected by
authmiddleware