diff --git a/client/src/entities/order/api/admin-order-api.ts b/client/src/entities/order/api/admin-order-api.ts
index 2c38e16..6e20b8f 100644
--- a/client/src/entities/order/api/admin-order-api.ts
+++ b/client/src/entities/order/api/admin-order-api.ts
@@ -37,7 +37,14 @@ export type AdminOrderDetailResponse = {
comment: string | null
createdAt: string
updatedAt: string
- user: { id: string; email: string; displayName: string | null }
+ user: {
+ id: string
+ email: string
+ displayName: string | null
+ avatar?: string | null
+ avatarType?: string | null
+ avatarStyle?: string | null
+ }
items: Array<{
id: string
productId: string
diff --git a/client/src/features/order-chat/ui/OrderChat.tsx b/client/src/features/order-chat/ui/OrderChat.tsx
index a9c269d..978c28f 100644
--- a/client/src/features/order-chat/ui/OrderChat.tsx
+++ b/client/src/features/order-chat/ui/OrderChat.tsx
@@ -3,9 +3,12 @@ import Box from '@mui/material/Box'
import Button from '@mui/material/Button'
import Stack from '@mui/material/Stack'
import Typography from '@mui/material/Typography'
+import { useUnit } from 'effector-react'
+import { $user } from '@/shared/model/auth'
import { ChatMessageBubble } from '@/shared/ui/ChatMessageBubble'
import { OrderMessageBody } from '@/shared/ui/OrderMessageBody'
import { RichTextMessageEditor } from '@/shared/ui/RichTextMessageEditor'
+import { UserAvatar } from '@/shared/ui/UserAvatar'
type Message = {
id: string
@@ -24,6 +27,7 @@ type Props = {
export function OrderChat({ messages, isPending, onSend }: Props) {
const [text, setText] = useState('')
const canSend = text.replace(/<[^>]*>/g, ' ').trim().length > 0
+ const currentUser = useUnit($user)
const handleSend = () => {
if (!canSend || isPending) return
@@ -37,14 +41,28 @@ export function OrderChat({ messages, isPending, onSend }: Props) {
Чат по заказу
- {messages.map((m) => (
-
-
- {m.authorType === 'admin' ? 'Админ' : 'Вы'} · {new Date(m.createdAt).toLocaleString()}
-
-
-
- ))}
+ {messages.map((m) => {
+ const isAdminMsg = m.authorType === 'admin'
+ const avatarNode = isAdminMsg ? (
+
+ ) : currentUser ? (
+
+ ) : null
+ return (
+
+
+ {isAdminMsg ? 'Админ' : 'Вы'} · {new Date(m.createdAt).toLocaleString()}
+
+
+
+ )
+ })}
{messages.length === 0 && Пока сообщений нет.}
diff --git a/client/src/features/order-detail/ui/OrderDetailContent.tsx b/client/src/features/order-detail/ui/OrderDetailContent.tsx
index db2d20c..721c478 100644
--- a/client/src/features/order-detail/ui/OrderDetailContent.tsx
+++ b/client/src/features/order-detail/ui/OrderDetailContent.tsx
@@ -9,6 +9,7 @@ import Select from '@mui/material/Select'
import Stack from '@mui/material/Stack'
import Typography from '@mui/material/Typography'
import { useMutation, useQueryClient } from '@tanstack/react-query'
+import { useUnit } from 'effector-react'
import { postAdminOrderMessage, setAdminOrderStatus } from '@/entities/order/api/admin-order-api'
import type { AdminOrderDetailResponse } from '@/entities/order/api/admin-order-api'
import { deliveryCarrierLabelRu } from '@/shared/constants/delivery-carrier'
@@ -17,9 +18,11 @@ import { formatPriceRub } from '@/shared/lib/format-price'
import { invalidateQueryKeys } from '@/shared/lib/invalidate-query-keys'
import { parseOrderAddressSnapshot } from '@/shared/lib/order-address-snapshot'
import { orderStatusLabelRu } from '@/shared/lib/order-status-labels'
+import { $user } from '@/shared/model/auth'
import { ChatMessageBubble } from '@/shared/ui/ChatMessageBubble'
import { OrderMessageBody } from '@/shared/ui/OrderMessageBody'
import { RichTextMessageEditor } from '@/shared/ui/RichTextMessageEditor'
+import { UserAvatar } from '@/shared/ui/UserAvatar'
import { DeliveryFeeAdjustmentForm } from './DeliveryFeeAdjustmentForm'
export function OrderDetailContent({ detail, orderId }: { detail: AdminOrderDetailResponse['item']; orderId: string }) {
@@ -56,6 +59,7 @@ export function OrderDetailContent({ detail, orderId }: { detail: AdminOrderDeta
)
const canSendMessage = msg.replace(/<[^>]*>/g, ' ').trim().length > 0
+ const currentUser = useUnit($user)
return (
@@ -164,14 +168,36 @@ export function OrderDetailContent({ detail, orderId }: { detail: AdminOrderDeta
Сообщения
- {detail.messages.map((m) => (
-
-
- {m.authorType === 'admin' ? 'Админ (вы)' : 'Пользователь'} · {new Date(m.createdAt).toLocaleString()}
-
-
-
- ))}
+ {detail.messages.map((m) => {
+ const isAdminMsg = m.authorType === 'admin'
+ const avatarNode = isAdminMsg ? (
+ currentUser && (
+
+ )
+ ) : (
+
+ )
+ return (
+
+
+ {isAdminMsg ? 'Админ (вы)' : 'Пользователь'} · {new Date(m.createdAt).toLocaleString()}
+
+
+
+ )
+ })}
{detail.messages.length === 0 && Нет сообщений.}
diff --git a/client/src/features/user/user-menu/ui/UserMenu.tsx b/client/src/features/user/user-menu/ui/UserMenu.tsx
index d714df7..ab661c3 100644
--- a/client/src/features/user/user-menu/ui/UserMenu.tsx
+++ b/client/src/features/user/user-menu/ui/UserMenu.tsx
@@ -1,10 +1,10 @@
import { useState } from 'react'
+import PersonIcon from '@mui/icons-material/Person'
import Badge from '@mui/material/Badge'
import IconButton from '@mui/material/IconButton'
import ListItemText from '@mui/material/ListItemText'
import Menu from '@mui/material/Menu'
import MenuItem from '@mui/material/MenuItem'
-import PersonIcon from '@mui/icons-material/Person'
import type { AuthUser } from '@/shared/model/auth'
import { UserAvatar } from '@/shared/ui/UserAvatar'
diff --git a/client/src/shared/ui/ChatMessageBubble.tsx b/client/src/shared/ui/ChatMessageBubble.tsx
index 0fa56e5..b6e193c 100644
--- a/client/src/shared/ui/ChatMessageBubble.tsx
+++ b/client/src/shared/ui/ChatMessageBubble.tsx
@@ -1,29 +1,46 @@
import type { ReactNode } from 'react'
import Box from '@mui/material/Box'
+import Stack from '@mui/material/Stack'
import { alpha } from '@mui/material/styles'
type Author = 'admin' | 'user'
-export function ChatMessageBubble(props: { authorType: Author; children: ReactNode }) {
- const { authorType, children } = props
+type Props = {
+ authorType: Author
+ avatar?: ReactNode
+ children: ReactNode
+}
+
+export function ChatMessageBubble({ authorType, avatar, children }: Props) {
+ const isAdmin = authorType === 'admin'
return (
-
- authorType === 'admin'
- ? alpha(theme.palette.grey[500], theme.palette.mode === 'dark' ? 0.28 : 0.14)
- : alpha(theme.palette.primary.main, theme.palette.mode === 'dark' ? 0.28 : 0.1),
+ alignItems: 'flex-end',
}}
>
- {children}
-
+ {isAdmin && avatar && {avatar}}
+
+ isAdmin
+ ? alpha(theme.palette.grey[500], theme.palette.mode === 'dark' ? 0.28 : 0.14)
+ : alpha(theme.palette.primary.main, theme.palette.mode === 'dark' ? 0.28 : 0.1),
+ }}
+ >
+ {children}
+
+ {!isAdmin && avatar && {avatar}}
+
)
}
diff --git a/server/prisma/prisma/dev.db b/server/prisma/prisma/dev.db
index eda88e1..f8989a0 100644
Binary files a/server/prisma/prisma/dev.db and b/server/prisma/prisma/dev.db differ
diff --git a/server/src/routes/api/admin-orders.js b/server/src/routes/api/admin-orders.js
index 3092e76..eef31db 100644
--- a/server/src/routes/api/admin-orders.js
+++ b/server/src/routes/api/admin-orders.js
@@ -73,7 +73,7 @@ export async function registerAdminOrderRoutes(fastify) {
const order = await prisma.order.findUnique({
where: { id },
include: {
- user: { select: { id: true, email: true, displayName: true } },
+ user: { select: { id: true, email: true, displayName: true, avatar: true, avatarType: true, avatarStyle: true } },
items: true,
messages: { orderBy: { createdAt: 'asc' } },
},