Files
shop-server/docs/superpowers/plans/2026-05-27-client-duplication.md
T
2026-05-27 20:56:08 +05:00

176 lines
5.3 KiB
Markdown

# 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 }) => (
<QueryClientProvider client={qc}>{children}</QueryClientProvider>
)
}
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"
```