# 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('@/entities/order/api/admin-order-api') return { ...actual, setAdminOrderStatus: setAdminOrderStatusMock, postAdminOrderMessage: vi.fn(async () => undefined), } }) function buildDetail(patch: Partial): AdminOrderDetailResponse['item'] { return { id: 'o1', status: 'PENDING_PAYMENT', deliveryType: 'delivery', deliveryCarrier: null, paymentMethod: 'online', itemsSubtotalCents: 10000, deliveryFeeCents: 500, deliveryFeeLocked: false, totalCents: 10500, currency: 'RUB', addressSnapshotJson: null, comment: null, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), user: { id: 'u1', email: 'a@example.com', displayName: null, avatar: null, avatarStyle: null, }, items: [], messages: [], ...patch, } } describe('OrderDetailContent quick status actions', () => { it('renders quick action buttons for next statuses', () => { const qc = new QueryClient() render( , ) expect(screen.getByRole('button', { name: /Оплачен/i })).toBeInTheDocument() }) it('calls setAdminOrderStatus on click', () => { const qc = new QueryClient() render( , ) fireEvent.click(screen.getByRole('button', { name: /Оплачен/i })) expect(setAdminOrderStatusMock).toHaveBeenCalledWith('o1', 'PAID') }) }) ``` - [ ] **Step 2: Run test to verify it fails** Run: `cd client && npm test -- src/features/order-detail/ui/__tests__/OrderDetailContent.test.tsx` Expected: FAIL because old `Select` UI is still used. - [ ] **Step 3: Replace select with quick action buttons** ```tsx Быстрый переход статуса {nextStatuses.length === 0 ? ( Статус финальный, смена недоступна ) : ( {nextStatuses.map((nextStatus) => { const isCancel = nextStatus === 'CANCELLED' return ( ) })} )} ``` - [ ] **Step 4: Add pending/empty-state assertions and rerun tests** ```tsx it('disables all quick action buttons while mutation is pending', () => { const qc = new QueryClient() setAdminOrderStatusMock.mockImplementationOnce(() => new Promise(() => {})) render( , ) const paidButton = screen.getByRole('button', { name: /Оплачен/i }) fireEvent.click(paidButton) expect(paidButton).toBeDisabled() }) it('shows final state note when no transitions available', () => { const qc = new QueryClient() render( , ) expect(screen.getByText('Статус финальный, смена недоступна')).toBeInTheDocument() }) ``` Run: `cd client && npm test -- src/features/order-detail/ui/__tests__/OrderDetailContent.test.tsx` Expected: PASS. - [ ] **Step 5: Commit** ```bash git add client/src/features/order-detail/ui/OrderDetailContent.tsx client/src/features/order-detail/ui/__tests__/OrderDetailContent.test.tsx git commit -m "feat: replace admin order status select with quick actions" ``` --- ### Task 4: Регрессия, линт и финальная проверка **Files:** - Modify (if needed): `client/src/pages/admin-orders/ui/__tests__/AdminOrdersPage.test.tsx` - Modify (if needed): `client/src/features/order-detail/ui/__tests__/OrderDetailContent.test.tsx` - Modify (if needed): `client/src/shared/lib/__tests__/order-requires-price-approval.test.ts` - [ ] **Step 1: Run focused test suite for changed units** Run: `cd client && npm test -- src/shared/lib/__tests__/order-requires-price-approval.test.ts src/pages/admin-orders/ui/__tests__/AdminOrdersPage.test.tsx src/features/order-detail/ui/__tests__/OrderDetailContent.test.tsx` Expected: PASS. - [ ] **Step 2: Run frontend lint** Run: `cd client && npm run lint` Expected: PASS with no new lint errors. - [ ] **Step 3: Run format check** Run: `cd client && npm run format:check` Expected: PASS, no formatting violations. - [ ] **Step 4: Fix issues if any and re-run exact failed command** Run (example): `cd client && npm run lint` Expected: PASS after fixes. - [ ] **Step 5: Final commit** ```bash git add client/src/pages/admin-orders/ui/AdminOrdersPage.tsx client/src/features/order-detail/ui/OrderDetailContent.tsx client/src/shared/lib/order-requires-price-approval.ts client/src/shared/lib/__tests__/order-requires-price-approval.test.ts client/src/pages/admin-orders/ui/__tests__/AdminOrdersPage.test.tsx client/src/features/order-detail/ui/__tests__/OrderDetailContent.test.tsx git commit -m "feat: improve admin orders flow for price approval and status updates" ```