ываыв
This commit is contained in:
@@ -21,6 +21,14 @@ export function SseProvider() {
|
||||
const es = createEventStream(token)
|
||||
sourceRef.current = es
|
||||
|
||||
function invalidateOrderQueries(orderId: unknown) {
|
||||
if (!orderId) return
|
||||
queryClient.invalidateQueries({ queryKey: ['me', 'orders', orderId] })
|
||||
queryClient.invalidateQueries({ queryKey: ['admin', 'orders', 'detail', orderId] })
|
||||
queryClient.invalidateQueries({ queryKey: ['admin', 'orders'] })
|
||||
queryClient.invalidateQueries({ queryKey: ['admin', 'orders', 'summary'] })
|
||||
}
|
||||
|
||||
function handleEvent(eventName: string) {
|
||||
return function (event: MessageEvent) {
|
||||
try {
|
||||
@@ -31,22 +39,13 @@ export function SseProvider() {
|
||||
case 'message:new':
|
||||
queryClient.invalidateQueries({ queryKey: ['me', 'messages', 'unread-count'] })
|
||||
queryClient.invalidateQueries({ queryKey: ['me', 'conversations'] })
|
||||
if (orderId) {
|
||||
queryClient.invalidateQueries({ queryKey: ['me', 'orders', orderId] })
|
||||
queryClient.invalidateQueries({ queryKey: ['admin', 'orders', orderId] })
|
||||
}
|
||||
invalidateOrderQueries(orderId)
|
||||
break
|
||||
case 'order:statusChanged':
|
||||
if (orderId) {
|
||||
queryClient.invalidateQueries({ queryKey: ['me', 'orders', orderId] })
|
||||
queryClient.invalidateQueries({ queryKey: ['admin', 'orders', orderId] })
|
||||
}
|
||||
invalidateOrderQueries(orderId)
|
||||
break
|
||||
case 'order:updated':
|
||||
if (orderId) {
|
||||
queryClient.invalidateQueries({ queryKey: ['me', 'orders', orderId] })
|
||||
queryClient.invalidateQueries({ queryKey: ['admin', 'orders', orderId] })
|
||||
}
|
||||
invalidateOrderQueries(orderId)
|
||||
break
|
||||
case 'order:new':
|
||||
queryClient.invalidateQueries({ queryKey: ['admin', 'orders', 'summary'] })
|
||||
|
||||
@@ -108,7 +108,9 @@ describe('SseProvider', () => {
|
||||
expect(mockInvalidateQueries).toHaveBeenCalledWith({ queryKey: ['me', 'messages', 'unread-count'] })
|
||||
expect(mockInvalidateQueries).toHaveBeenCalledWith({ queryKey: ['me', 'conversations'] })
|
||||
expect(mockInvalidateQueries).toHaveBeenCalledWith({ queryKey: ['me', 'orders', 'o1'] })
|
||||
expect(mockInvalidateQueries).toHaveBeenCalledWith({ queryKey: ['admin', 'orders', 'o1'] })
|
||||
expect(mockInvalidateQueries).toHaveBeenCalledWith({ queryKey: ['admin', 'orders', 'detail', 'o1'] })
|
||||
expect(mockInvalidateQueries).toHaveBeenCalledWith({ queryKey: ['admin', 'orders'] })
|
||||
expect(mockInvalidateQueries).toHaveBeenCalledWith({ queryKey: ['admin', 'orders', 'summary'] })
|
||||
})
|
||||
|
||||
it('invalidates order queries on order:statusChanged', () => {
|
||||
@@ -117,7 +119,9 @@ describe('SseProvider', () => {
|
||||
const handler = mockEventHandlers['order:statusChanged']
|
||||
handler(new MessageEvent('order:statusChanged', { data: JSON.stringify({ orderId: 'o2' }) }))
|
||||
expect(mockInvalidateQueries).toHaveBeenCalledWith({ queryKey: ['me', 'orders', 'o2'] })
|
||||
expect(mockInvalidateQueries).toHaveBeenCalledWith({ queryKey: ['admin', 'orders', 'o2'] })
|
||||
expect(mockInvalidateQueries).toHaveBeenCalledWith({ queryKey: ['admin', 'orders', 'detail', 'o2'] })
|
||||
expect(mockInvalidateQueries).toHaveBeenCalledWith({ queryKey: ['admin', 'orders'] })
|
||||
expect(mockInvalidateQueries).toHaveBeenCalledWith({ queryKey: ['admin', 'orders', 'summary'] })
|
||||
})
|
||||
|
||||
it('invalidates order queries on order:updated', () => {
|
||||
@@ -126,7 +130,9 @@ describe('SseProvider', () => {
|
||||
const handler = mockEventHandlers['order:updated']
|
||||
handler(new MessageEvent('order:updated', { data: JSON.stringify({ orderId: 'o3' }) }))
|
||||
expect(mockInvalidateQueries).toHaveBeenCalledWith({ queryKey: ['me', 'orders', 'o3'] })
|
||||
expect(mockInvalidateQueries).toHaveBeenCalledWith({ queryKey: ['admin', 'orders', 'o3'] })
|
||||
expect(mockInvalidateQueries).toHaveBeenCalledWith({ queryKey: ['admin', 'orders', 'detail', 'o3'] })
|
||||
expect(mockInvalidateQueries).toHaveBeenCalledWith({ queryKey: ['admin', 'orders'] })
|
||||
expect(mockInvalidateQueries).toHaveBeenCalledWith({ queryKey: ['admin', 'orders', 'summary'] })
|
||||
})
|
||||
|
||||
it('invalidates admin queries on order:new', () => {
|
||||
|
||||
@@ -215,7 +215,7 @@ export function OrderDetailContent({ detail, orderId }: { detail: AdminOrderDeta
|
||||
/>
|
||||
)
|
||||
return (
|
||||
<ChatMessageBubble key={m.id} authorType={isAdminMsg ? 'user' : 'admin'} avatar={avatarNode}>
|
||||
<ChatMessageBubble key={m.id} authorType={isAdminMsg ? 'admin' : 'user'} avatar={avatarNode}>
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
{isAdminMsg ? 'Админ (вы)' : 'Пользователь'} · {new Date(m.createdAt).toLocaleString()}
|
||||
</Typography>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { ReactNode } from 'react'
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
||||
import { render, screen, waitFor } from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
@@ -29,6 +30,12 @@ vi.mock('@/shared/ui/RichTextMessageEditor.lazy', () => ({
|
||||
}) => <textarea aria-label="Ответ админа" value={value} onChange={(e) => onChange(e.target.value)} />,
|
||||
}))
|
||||
|
||||
vi.mock('@/shared/ui/ChatMessageBubble', () => ({
|
||||
ChatMessageBubble: ({ authorType, children }: { authorType: 'admin' | 'user'; children: ReactNode }) => (
|
||||
<div data-testid={`chat-message-${authorType}`}>{children}</div>
|
||||
),
|
||||
}))
|
||||
|
||||
const setAdminOrderStatusMock = vi.mocked(setAdminOrderStatus)
|
||||
|
||||
function createDetail(overrides?: Partial<AdminOrderDetailResponse['item']>): AdminOrderDetailResponse['item'] {
|
||||
@@ -175,4 +182,30 @@ describe('OrderDetailContent quick status transitions', () => {
|
||||
await user.click(paidButton)
|
||||
expect(setAdminOrderStatusMock).toHaveBeenCalledTimes(2)
|
||||
})
|
||||
|
||||
it('передает фактический authorType в пузырь сообщения', () => {
|
||||
renderComponent(
|
||||
createDetail({
|
||||
messages: [
|
||||
{
|
||||
id: 'message-admin',
|
||||
authorType: 'admin',
|
||||
text: 'Ответ администратора',
|
||||
attachmentUrl: null,
|
||||
createdAt: '2026-05-28T10:00:00.000Z',
|
||||
},
|
||||
{
|
||||
id: 'message-user',
|
||||
authorType: 'user',
|
||||
text: 'Сообщение покупателя',
|
||||
attachmentUrl: null,
|
||||
createdAt: '2026-05-28T10:01:00.000Z',
|
||||
},
|
||||
],
|
||||
}),
|
||||
)
|
||||
|
||||
expect(screen.getByTestId('chat-message-admin')).toHaveTextContent('Админ (вы)')
|
||||
expect(screen.getByTestId('chat-message-user')).toHaveTextContent('Пользователь')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { canTransitionOrderStatus, getAdminNextOrderStatuses } from '../order'
|
||||
|
||||
describe('client order status helpers', () => {
|
||||
it('returns delivery-specific next statuses', () => {
|
||||
expect(getAdminNextOrderStatuses('IN_PROGRESS', 'delivery')).toEqual(['SHIPPED', 'CANCELLED'])
|
||||
})
|
||||
|
||||
it('returns pickup-specific next statuses', () => {
|
||||
expect(getAdminNextOrderStatuses('IN_PROGRESS', 'pickup')).toEqual(['READY_FOR_PICKUP', 'CANCELLED'])
|
||||
})
|
||||
|
||||
it('checks pickup transition without falling back to delivery rules', () => {
|
||||
expect(canTransitionOrderStatus('IN_PROGRESS', 'READY_FOR_PICKUP', 'pickup')).toBe(true)
|
||||
expect(canTransitionOrderStatus('IN_PROGRESS', 'SHIPPED', 'pickup')).toBe(false)
|
||||
})
|
||||
})
|
||||
@@ -11,7 +11,7 @@ export function getAdminNextOrderStatuses(status: string, deliveryType: 'deliver
|
||||
return sharedGetNextAdminStatuses(status, deliveryType) as OrderStatus[]
|
||||
}
|
||||
|
||||
export function canTransitionOrderStatus(from: string, to: string): boolean {
|
||||
export function canTransitionOrderStatus(from: string, to: string, deliveryType: 'delivery' | 'pickup'): boolean {
|
||||
if (from === to) return true
|
||||
return getAdminNextOrderStatuses(from, 'delivery').includes(to as OrderStatus)
|
||||
return getAdminNextOrderStatuses(from, deliveryType).includes(to as OrderStatus)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user