# 2026-05-21 — Avatar & Display Name Fixes
## Overview
8 замечаний по отображению аватаров, имён, ссылок и stock-статусов в админке и на клиенте.
## Approach
Локальные изолированные правки (Подход 1). Каждый пункт правится в своём контексте без переиспользования общих компонентов с `/me` — минимизирует риск регрессии.
---
## 1. Admin settings page (`/admin/settings`)
**Проблема:** Админ не может настроить displayName/avatar. Страница `/me/settings` существует, но админ редиректится с `/me` на `/admin`.
**Решение:**
- Новая FSD-страница `client/src/pages/admin-settings/`
- Пункт «Настройки» в сайдбаре `AdminLayoutPage` (после «Уведомления»)
- Форма: редактирование `displayName`, выбор/генерация аватара (DiceBear, 16 стилей), загрузка своего аватара. Копирует UI с `/me/settings` (SettingsPage), но как отдельный компонент, не шаринг.
- API: `GET /api/admin/profile` и `PATCH /api/admin/profile` (новый роут в `server/src/routes/api/`)
- Роут защищён `verifyAdmin`, работает с полями: `displayName`, `avatar`, `avatarType`, `avatarStyle`
- После сохранения — инвалидация `$user` стора на клиенте, чтобы хедер подхватил новый аватар
**Файлы:**
- `client/src/pages/admin-settings/ui/AdminSettingsPage.tsx` (новый)
- `client/src/pages/admin-layout/ui/AdminLayoutPage.tsx` (добавить пункт меню)
- `server/src/routes/api/admin-profile.js` (новый)
- `server/src/index.js` (зарегистрировать роут)
---
## 2. Admin avatar in header
**Проблема:** В хедере админ видит только кнопку «Выход», без аватара.
**Решение:**
- В `AppHeader` для админа: `IconButton` с `UserAvatar` + выпадающее меню с пунктами «Настройки» (`/admin/settings`) и «Выход»
- Аватар из `AuthUser.avatar/avatarType/avatarStyle`, при отсутствии — DiceBear fallback
- Компонент по аналогии с `UserMenu`, но упрощённый: только 2 пункта, без профиля покупателя. Можно сделать как `AdminUserMenu` в `features/user/user-menu/` или прямо в `AppHeader`
**Файлы:**
- `client/src/app/layout/AppHeader.tsx` (заменить кнопку «Выход» на меню с аватаром)
---
## 3. Avatar column in admin users table
**Проблема:** В таблице пользователей (`/admin/users`) нет колонки с аватарами.
**Решение:**
- `AdminUser` тип (`entities/user/model/types.ts`): добавить `avatar`, `avatarType`, `avatarStyle` (опциональные)
- Серверный `GET /api/admin/users`: добавить эти поля в SELECT
- `AdminUsersPage`: колонка «Аватар» первой (перед email), рендер через ``
**Файлы:**
- `client/src/entities/user/model/types.ts`
- `client/src/pages/admin-users/ui/AdminUsersPage.tsx`
- `server/src/routes/api/admin-users.js`
---
## 4. Avatars in order messages
**Проблема:** `ChatMessageBubble` показывает только текст «Админ»/«Вы»/«Пользователь», без аватаров.
**Решение:**
- `ChatMessageBubble`: добавить опциональный проп `avatar?: ReactNode` — рендерится слева от сообщения для `authorType='admin'`, справа для `'user'`
- `OrderChat` (пользователь): для админских сообщений — DiceBear по `'admin'` seed, для своих — аватар из `AuthUser`
- `OrderDetailContent` (админ): для пользователя — аватар из `order.user.avatar/avatarType/avatarStyle`, для админа — из `AuthUser`
- API `GET /api/orders/:id` и `GET /api/admin/orders/:id`: добавить `user { avatar, avatarType, avatarStyle }` в ответ
- Клиентский тип заказа: добавить эти поля в `user`
**Файлы:**
- `client/src/shared/ui/ChatMessageBubble.tsx`
- `client/src/features/order-chat/ui/OrderChat.tsx`
- `client/src/features/order-detail/ui/OrderDetailContent.tsx`
- `server/src/routes/user-orders.js` (GET /:id)
- `server/src/routes/api/admin-orders.js` (GET /:id)
- Типы заказа на клиенте
---
## 5. Actual user avatars in reviews
**Проблема:** В отзывах всегда генерируется DiceBear по строке `authorDisplay`, а не используется реальный аватар пользователя.
**Решение:**
- API `public-reviews`: добавить `authorAvatar`, `authorAvatarType`, `authorAvatarStyle` в ответ (из `user.avatar/avatarType/avatarStyle`)
- Тип `PublicProductReviewItem` и `PublicReviewFeedItem`: добавить эти поля
- `ReviewsBlock` и `ProductReviewsList`: передавать реальные значения в `UserAvatar` вместо `null`
**Файлы:**
- `server/src/routes/api/public-reviews.js`
- `client/src/entities/review/api/reviews-api.ts` (типы)
- `client/src/widgets/reviews-block/ui/ReviewsBlock.tsx`
- `client/src/features/product-review/ui/ProductReviewsList.tsx`
- `server/src/routes/api/admin-reviews.js` (тоже может использовать)
---
## 6. Product link in reviews only if published
**Проблема:** В `ReviewsBlock` ссылка на товар показывается всегда, даже если товар скрыт из каталога.
**Решение:**
- API `public-reviews`: добавить объект `product: { id, title, published, slug }` в каждый элемент фида
- Тип `PublicReviewFeedItem`: обновить поле с `productId`/`productTitle` на `product: { id, title, published, slug }`
- `ReviewsBlock`: если `product.published === true` — ссылка ``, иначе — просто текст ``
**Файлы:**
- `server/src/routes/api/public-reviews.js`
- `client/src/entities/review/api/reviews-api.ts`
- `client/src/widgets/reviews-block/ui/ReviewsBlock.tsx`
---
## 7. "Out of stock" chip visibility in catalog
**Проблема:** Чип «Нет в наличии» существует в DOM, но визуально не виден в каталоге.
**Решение:**
- Проверить `z-index` чипа в `ProductCard` — поднять выше (например `zIndex: 2`), чтобы не перекрывался `CardMedia` или другими элементами
- Предположительно проблема в том, что чип рендерится до изображения в DOM, и изображение перекрывает его по z-order
**Файлы:**
- `client/src/entities/product/ui/ProductCard.tsx`
---
## 8. Person icon for unauthenticated users
**Проблема:** До авторизации в хедере нет иконки пользователя.
**Решение:**
- В `AppHeader`: когда `user === null` и `!loading`, показывать `IconButton` с `PersonIcon`, ведущую на `/auth`
- Сейчас `UserMenu` не рендерится без `user` — добавить условие `user ? : `
**Файлы:**
- `client/src/app/layout/AppHeader.tsx`
---
## Data flow summary
```
┌─ Admin settings ─────────────────────────────────────┐
│ PATCH /api/admin/profile → DB → invalidate $user │
│ → AppHeader reads $user.avatar → UserAvatar │
└───────────────────────────────────────────────────────┘
┌─ Admin users table ───────────────────────────────────┐
│ GET /api/admin/users → { ..., avatar, avatarType, │
│ avatarStyle } → AdminUsersPage → │
└───────────────────────────────────────────────────────┘
┌─ Order chat ──────────────────────────────────────────┐
│ GET /api/orders/:id → { user: { avatar, ... } } │
│ → OrderChat → ChatMessageBubble(avatar={})│
│ Admin avatar: from AuthUser store │
└───────────────────────────────────────────────────────┘
┌─ Reviews ─────────────────────────────────────────────┐
│ GET /api/public-reviews → { authorAvatar, ..., │
│ product: { published, ... } } │
│ → ReviewsBlock/ProductReviewsList → UserAvatar + link│
└───────────────────────────────────────────────────────┘
```
## Testing
- **Client unit tests:** Проверить рендер аватаров в `ProductReviewsList`, `ReviewsBlock`, `AdminUsersPage`, `ChatMessageBubble`, `AppHeader` для разных состояний (авторизован/неавторизован/админ)
- **Server tests:** Проверить новые поля в ответах API
- **Manual:** Проверить видимость чипа «Нет в наличии», отображение ссылки в отзывах для published/unpublished товаров