# Client Duplication — Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development or superpowers:executing-plans. **Goal:** Устранить дублирование `useQuery` для корзины (4 копии → 1 хук), устранить дублирование статусов заказа. **Architecture:** Кастомный хук `useCartQuery` в `entities/cart/lib/`, единый источник `ORDER_STATUS_DATA`. **Tech Stack:** TypeScript, TanStack React Query, Vitest **Depends on:** none --- ### Task 1: useCartQuery хук + тесты **Files:** - Create: `client/src/entities/cart/lib/use-cart-query.ts` - Test: `client/src/entities/cart/lib/use-cart-query.test.tsx` - Modify: `client/src/widgets/catalog-slider/ui/AppHeader.tsx` - Modify: `client/src/pages/cart/CartPage.tsx` - Modify: `client/src/pages/checkout/CheckoutPage.tsx` - Modify: `client/src/features/cart/ui/ToggleCartIcon/ToggleCartIcon.tsx` - [ ] **Step 1: Write failing tests** ```tsx // client/src/entities/cart/lib/use-cart-query.test.tsx import { describe, it, expect, vi, beforeEach } from 'vitest' import { renderHook, waitFor } from '@testing-library/react' import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { useCartQuery } from './use-cart-query' import { fetchMyCart } from '../../api' vi.mock('../../api', () => ({ fetchMyCart: vi.fn(), })) vi.mock('@/shared/model/auth', () => ({ useAuthUser: vi.fn(), })) import { useAuthUser } from '@/shared/model/auth' function createWrapper() { const qc = new QueryClient() return ({ children }: { children: React.ReactNode }) => ( {children} ) } describe('useCartQuery', () => { beforeEach(() => { vi.clearAllMocks() }) it('returns query with correct key and enabled flag for authenticated user', async () => { vi.mocked(useAuthUser).mockReturnValue({ id: '1', email: 'test@test.com' }) vi.mocked(fetchMyCart).mockResolvedValue({ items: [] }) const { result } = renderHook(() => useCartQuery(), { wrapper: createWrapper() }) await waitFor(() => expect(result.current.isSuccess).toBe(true)) expect(fetchMyCart).toHaveBeenCalled() expect(result.current.queryKey).toEqual(['me', 'cart']) }) it('does not fetch when user is not authenticated', () => { vi.mocked(useAuthUser).mockReturnValue(null) const { result } = renderHook(() => useCartQuery(), { wrapper: createWrapper() }) expect(fetchMyCart).not.toHaveBeenCalled() expect(result.current.fetchStatus).toBe('idle') }) }) ``` - [ ] **Step 2: Run to verify failure** Run: `cd client && npx vitest run entities/cart/lib/use-cart-query.test.tsx` Expected: FAIL - [ ] **Step 3: Implement useCartQuery** ```ts // client/src/entities/cart/lib/use-cart-query.ts import { useQuery } from '@tanstack/react-query' import { useAuthUser } from '@/shared/model/auth' import { fetchMyCart } from '../../api' export function useCartQuery() { const user = useAuthUser() return useQuery({ queryKey: ['me', 'cart'], queryFn: fetchMyCart, enabled: Boolean(user), }) } ``` - [ ] **Step 4: Run tests** Run: `cd client && npx vitest run entities/cart/lib/use-cart-query.test.tsx` Expected: PASS - [ ] **Step 5: Apply to AppHeader.tsx** Before: ```tsx const { data: cart } = useQuery({ queryKey: ['me', 'cart'], queryFn: fetchMyCart, enabled: Boolean(user), }) ``` After: ```tsx import { useCartQuery } from '@/entities/cart/lib/use-cart-query' const { data: cart } = useCartQuery() ``` - [ ] **Step 6: Apply to CartPage.tsx, CheckoutPage.tsx, ToggleCartIcon.tsx** Same replacement in each file. - [ ] **Step 7: Run lint + test + build** ```bash cd client && npm run lint && npm test && npm run build ``` - [ ] **Step 8: Commit** ```bash git add client/src/entities/cart/lib/use-cart-query.ts client/src/entities/cart/lib/use-cart-query.test.tsx client/src/widgets/catalog-slider/ui/AppHeader.tsx client/src/pages/cart/CartPage.tsx client/src/pages/checkout/CheckoutPage.tsx client/src/features/cart/ui/ToggleCartIcon/ToggleCartIcon.tsx git commit -m "refactor: extract useCartQuery hook" ``` --- ### Task 2: Устранить дублирование статусов заказа **Files:** - Read: `client/src/shared/lib/order-status-data.ts` - Read: `client/src/shared/lib/order-status-labels.ts` - Delete: `client/src/shared/lib/order-status-labels.ts` - Modify: all files importing from `order-status-labels` - [ ] **Step 1: Найти все импорты orderStatusLabelRu** Run: `rg 'orderStatusLabelRu' client/src/ --include '*.ts' --include '*.tsx'` - [ ] **Step 2: Заменить импорты на ORDER_STATUS_DATA** Each file importing `{ orderStatusLabelRu }` from `order-status-labels`: - Change to import `{ ORDER_STATUS_DATA }` from `order-status-data` - Replace `orderStatusLabelRu(status)` with `ORDER_STATUS_DATA[status].label` - [ ] **Step 3: Delete order-status-labels.ts** - [ ] **Step 4: Run lint + test + build** ```bash cd client && npm run lint && npm test && npm run build ``` - [ ] **Step 5: Commit** ```bash git add client/src/shared/lib/order-status-labels.ts client/src/shared/lib/order-status-data.ts git commit -m "refactor: remove duplicate order status labels, use ORDER_STATUS_DATA as single source" ```