Files
shop-server/docs/superpowers/specs/2026-05-21-avatar-display-fixes-design.md
T
2026-05-21 20:09:22 +05:00

10 KiB
Raw Blame History

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), рендер через <UserAvatar size={28} />

Файлы:

  • 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 (тоже может использовать)

Проблема: В ReviewsBlock ссылка на товар показывается всегда, даже если товар скрыт из каталога.

Решение:

  • API public-reviews: добавить объект product: { id, title, published, slug } в каждый элемент фида
  • Тип PublicReviewFeedItem: обновить поле с productId/productTitle на product: { id, title, published, slug }
  • ReviewsBlock: если product.published === true — ссылка <RouterLink>, иначе — просто текст <Typography>

Файлы:

  • 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 ? <UserMenu ...> : <IconButton href="/auth"><PersonIcon /></IconButton>

Файлы:

  • 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 → <UserAvatar />      │
└───────────────────────────────────────────────────────┘

┌─ Order chat ──────────────────────────────────────────┐
│  GET /api/orders/:id → { user: { avatar, ... } }      │
│  → OrderChat → ChatMessageBubble(avatar={<UserAvatar/>})│
│  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 товаров