# Admin Orders UX Improvements Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Добавить в списке заказов маркер `Цена не подтверждена` и заменить смену статуса в деталке на быстрые кнопки допустимых переходов.
**Architecture:** Изменения ограничены фронтендом (`client`) и опираются на текущие поля заказа (`status`, `deliveryType`, `deliveryFeeLocked`) и текущую логику переходов `getAdminNextOrderStatuses`. Серверные API и контракты не меняются, синхронизация данных остается через существующую инвалидацию React Query.
**Tech Stack:** React, TypeScript, MUI, TanStack React Query, Vitest, Testing Library.
---
## File Structure
**Create:**
- `client/src/shared/lib/order-requires-price-approval.ts`
- `client/src/shared/lib/__tests__/order-requires-price-approval.test.ts`
- `client/src/features/order-detail/ui/__tests__/OrderDetailContent.test.tsx`
- `client/src/pages/admin-orders/ui/__tests__/AdminOrdersPage.test.tsx`
**Modify:**
- `client/src/pages/admin-orders/ui/AdminOrdersPage.tsx`
- `client/src/features/order-detail/ui/OrderDetailContent.tsx`
---
### Task 1: Вычисление признака "цена не подтверждена"
**Files:**
- Create: `client/src/shared/lib/order-requires-price-approval.ts`
- Test: `client/src/shared/lib/__tests__/order-requires-price-approval.test.ts`
- [ ] **Step 1: Write the failing unit test**
```ts
import { describe, expect, it } from 'vitest'
import { orderRequiresPriceApproval } from '../order-requires-price-approval'
describe('orderRequiresPriceApproval', () => {
it('returns true for delivery pending payment with unlocked delivery fee', () => {
expect(
orderRequiresPriceApproval({
status: 'PENDING_PAYMENT',
deliveryType: 'delivery',
deliveryFeeLocked: false,
}),
).toBe(true)
})
it('returns false when delivery fee is already locked', () => {
expect(
orderRequiresPriceApproval({
status: 'PENDING_PAYMENT',
deliveryType: 'delivery',
deliveryFeeLocked: true,
}),
).toBe(false)
})
it('returns false for pickup even if payment is pending', () => {
expect(
orderRequiresPriceApproval({
status: 'PENDING_PAYMENT',
deliveryType: 'pickup',
deliveryFeeLocked: false,
}),
).toBe(false)
})
it('returns false for non-pending statuses', () => {
expect(
orderRequiresPriceApproval({
status: 'PAID',
deliveryType: 'delivery',
deliveryFeeLocked: false,
}),
).toBe(false)
})
})
```
- [ ] **Step 2: Run test to verify it fails**
Run: `cd client && npm test -- src/shared/lib/__tests__/order-requires-price-approval.test.ts`
Expected: FAIL with module/function not found.
- [ ] **Step 3: Write minimal implementation**
```ts
type PriceApprovalOrder = {
status: string
deliveryType: 'delivery' | 'pickup'
deliveryFeeLocked: boolean
}
export function orderRequiresPriceApproval(order: PriceApprovalOrder): boolean {
return order.status === 'PENDING_PAYMENT' && order.deliveryType === 'delivery' && order.deliveryFeeLocked === false
}
```
- [ ] **Step 4: Run test to verify it passes**
Run: `cd client && npm test -- src/shared/lib/__tests__/order-requires-price-approval.test.ts`
Expected: PASS (4 tests).
- [ ] **Step 5: Commit**
```bash
git add client/src/shared/lib/order-requires-price-approval.ts client/src/shared/lib/__tests__/order-requires-price-approval.test.ts
git commit -m "test: add price approval predicate for admin orders"
```
---
### Task 2: Маркер "Цена не подтверждена" в списке заказов
**Files:**
- Modify: `client/src/pages/admin-orders/ui/AdminOrdersPage.tsx`
- Test: `client/src/pages/admin-orders/ui/__tests__/AdminOrdersPage.test.tsx`
- [ ] **Step 1: Write the failing component test for chip visibility**
```tsx
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { render, screen } from '@testing-library/react'
import { describe, expect, it, vi } from 'vitest'
import { AdminOrdersPage } from '../AdminOrdersPage'
const fetchAdminOrdersMock = vi.fn()
vi.mock('@/entities/order/api/admin-order-api', () => ({
fetchAdminOrders: fetchAdminOrdersMock,
fetchAdminOrder: vi.fn(),
}))
describe('AdminOrdersPage price approval marker', () => {
it('shows "Цена не подтверждена" for eligible order', async () => {
fetchAdminOrdersMock.mockResolvedValueOnce({
items: [
{
id: 'order-1',
status: 'PENDING_PAYMENT',
deliveryType: 'delivery',
deliveryFeeLocked: false,
totalCents: 10000,
currency: 'RUB',
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
user: { id: 'u1', email: 'a@example.com' },
itemsCount: 1,
},
],
total: 1,
page: 1,
pageSize: 20,
})
const qc = new QueryClient()
render(
,
)
expect(await screen.findByText('Цена не подтверждена')).toBeInTheDocument()
})
})
```
- [ ] **Step 2: Run test to verify it fails**
Run: `cd client && npm test -- src/pages/admin-orders/ui/__tests__/AdminOrdersPage.test.tsx`
Expected: FAIL because chip text is not rendered yet.
- [ ] **Step 3: Implement marker in `AdminOrdersPage`**
```tsx
import Chip from '@mui/material/Chip'
import { orderRequiresPriceApproval } from '@/shared/lib/order-requires-price-approval'
// ...
{group.items.map((o) => {
const needsPriceApproval = orderRequiresPriceApproval({
status: o.status,
deliveryType: o.deliveryType,
deliveryFeeLocked: o.deliveryFeeLocked,
})
return (
{o.id.slice(-8)}
{needsPriceApproval && }
{/* ... */}
)
})}
```
- [ ] **Step 4: Add negative case and rerun test**
```tsx
it('does not show marker for non-eligible order', async () => {
fetchAdminOrdersMock.mockResolvedValueOnce({
items: [
{
id: 'order-2',
status: 'PENDING_PAYMENT',
deliveryType: 'delivery',
deliveryFeeLocked: true,
totalCents: 10000,
currency: 'RUB',
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
user: { id: 'u2', email: 'b@example.com' },
itemsCount: 2,
},
],
total: 1,
page: 1,
pageSize: 20,
})
const qc = new QueryClient()
render(
,
)
expect(await screen.findByText('order-2'.slice(-8))).toBeInTheDocument()
expect(screen.queryByText('Цена не подтверждена')).not.toBeInTheDocument()
})
```
Run: `cd client && npm test -- src/pages/admin-orders/ui/__tests__/AdminOrdersPage.test.tsx`
Expected: PASS.
- [ ] **Step 5: Commit**
```bash
git add client/src/pages/admin-orders/ui/AdminOrdersPage.tsx client/src/pages/admin-orders/ui/__tests__/AdminOrdersPage.test.tsx
git commit -m "feat: show price approval marker in admin orders list"
```
---
### Task 3: Быстрые кнопки смены статуса в деталке
**Files:**
- Modify: `client/src/features/order-detail/ui/OrderDetailContent.tsx`
- Test: `client/src/features/order-detail/ui/__tests__/OrderDetailContent.test.tsx`
- [ ] **Step 1: Write failing tests for quick actions**
```tsx
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { fireEvent, render, screen } from '@testing-library/react'
import { describe, expect, it, vi } from 'vitest'
import { OrderDetailContent } from '../OrderDetailContent'
import type { AdminOrderDetailResponse } from '@/entities/order/api/admin-order-api'
const setAdminOrderStatusMock = vi.fn(async () => undefined)
vi.mock('@/entities/order/api/admin-order-api', async () => {
const actual = await vi.importActual