# S05: [A] Sprint 0 Refactoring Tasks

**Goal:** Complete Sprint 0 structural refactoring: split monolithic API client into domain modules, add route-level code splitting for admin/creator pages, and normalize bare-list API endpoints to paginated response shape.
**Demo:** After this: Any structural refactoring from M018 audit is complete

## Tasks
- [x] **T01: Split 945-line public-client.ts into 10 domain API modules with shared client.ts and barrel index.ts, updating all 17 consumers** — ## Description

The 945-line `public-client.ts` monolith contains all API functions across 10 domains. Split it into focused domain modules under `frontend/src/api/` with a barrel `index.ts` that re-exports everything for backward compatibility.

## Steps

1. Read `frontend/src/api/public-client.ts` fully to identify exact domain boundaries and shared infrastructure.
2. Create `frontend/src/api/client.ts` with shared infrastructure: `request<T>()` helper, `ApiError` class, `AUTH_TOKEN_KEY`, base URL config, and the auto-inject Authorization header logic.
3. Create domain modules, each importing `request` and `ApiError` from `./client`:
   - `search.ts` — searchApi, fetchSuggestions, fetchPopularSearches + related types
   - `techniques.ts` — fetchTechniques, fetchTechnique, fetchRandomTechnique, versions + types
   - `creators.ts` — fetchCreators, fetchCreator + types
   - `topics.ts` — fetchTopics, fetchSubTopicTechniques + types
   - `stats.ts` — fetchStats + types
   - `reports.ts` — submitReport, fetchReports, updateReport + types
   - `admin-pipeline.ts` — all pipeline admin functions + types (largest group, ~15 functions)
   - `admin-techniques.ts` — fetchAdminTechniquePages + types
   - `auth.ts` — authRegister, authLogin, authGetMe, authUpdateProfile + types
4. Create `frontend/src/api/index.ts` barrel that re-exports everything from all modules.
5. Delete `frontend/src/api/public-client.ts`.
6. Update the 16 consumer files to import from `../api` (barrel) instead of `../api/public-client`. The barrel preserves all names, so only the import path changes.
7. Run `npm run build` and fix any TypeScript errors.

## Must-Haves

- [ ] `request<T>()` helper and `ApiError` class in shared `client.ts` — not duplicated
- [ ] Authorization header auto-injection logic stays in `client.ts`
- [ ] `AUTH_TOKEN_KEY` exported from `client.ts` for `AuthContext.tsx`
- [ ] Barrel `index.ts` re-exports all functions and types
- [ ] `public-client.ts` deleted — no stale copy
- [ ] All 16 consumers import from barrel (no `public-client` references remain)
- [ ] `npm run build` passes with zero errors

## Verification

- `npm run build` exits 0 with zero TypeScript errors
- `! rg 'public-client' frontend/src/ -g '*.ts' -g '*.tsx'` — no references to old file
- `test -f frontend/src/api/index.ts && test -f frontend/src/api/client.ts` — core files exist
- `ls frontend/src/api/*.ts | wc -l` — at least 11 files (client + 9 domain + index)

## Inputs

- `frontend/src/api/public-client.ts` — the monolith to split
- `frontend/src/context/AuthContext.tsx` — imports AUTH_TOKEN_KEY from public-client
- `frontend/src/pages/TopicsBrowse.tsx` — consumer
- `frontend/src/pages/Home.tsx` — consumer
- `frontend/src/pages/SearchResults.tsx` — consumer
- `frontend/src/pages/AdminPipeline.tsx` — consumer
- `frontend/src/pages/AdminTechniquePages.tsx` — consumer
- `frontend/src/pages/AdminReports.tsx` — consumer
- `frontend/src/pages/CreatorsBrowse.tsx` — consumer
- `frontend/src/pages/CreatorDetail.tsx` — consumer
- `frontend/src/pages/CreatorSettings.tsx` — consumer
- `frontend/src/pages/TechniquePage.tsx` — consumer
- `frontend/src/components/SearchAutocomplete.tsx` — consumer
- `frontend/src/components/ReportIssueModal.tsx` — consumer
- `frontend/src/components/TableOfContents.tsx` — consumer
- `frontend/src/utils/citations.tsx` — consumer

## Expected Output

- `frontend/src/api/client.ts` — shared request helper, ApiError, AUTH_TOKEN_KEY, base URL
- `frontend/src/api/search.ts` — search domain functions
- `frontend/src/api/techniques.ts` — technique CRUD functions
- `frontend/src/api/creators.ts` — creator browse/detail functions
- `frontend/src/api/topics.ts` — topics/subtopics functions
- `frontend/src/api/stats.ts` — stats endpoint function
- `frontend/src/api/reports.ts` — report CRUD functions
- `frontend/src/api/admin-pipeline.ts` — pipeline admin functions
- `frontend/src/api/admin-techniques.ts` — admin technique functions
- `frontend/src/api/auth.ts` — auth endpoint functions
- `frontend/src/api/index.ts` — barrel re-exporting all modules
  - Estimate: 1h
  - Files: frontend/src/api/public-client.ts, frontend/src/api/client.ts, frontend/src/api/search.ts, frontend/src/api/techniques.ts, frontend/src/api/creators.ts, frontend/src/api/topics.ts, frontend/src/api/stats.ts, frontend/src/api/reports.ts, frontend/src/api/admin-pipeline.ts, frontend/src/api/admin-techniques.ts, frontend/src/api/auth.ts, frontend/src/api/index.ts
  - Verify: cd frontend && npm run build && cd .. && ! rg 'public-client' frontend/src/ -g '*.ts' -g '*.tsx' && ls frontend/src/api/*.ts | wc -l
- [x] **T02: Replaced 6 static page imports with React.lazy + Suspense in App.tsx, producing 7 JS chunks from the previous monolithic bundle** — ## Description

All 17 page components are eagerly imported in `App.tsx`. Wrap admin and creator pages in `React.lazy()` + `Suspense` so they load on-demand, reducing initial bundle size.

## Steps

1. Read `frontend/src/App.tsx` to see the current import structure.
2. Replace static imports with `React.lazy()` for these pages:
   - `AdminPipeline`
   - `AdminTechniquePages`
   - `AdminReports`
   - `CreatorDashboard`
   - `CreatorSettings`
   - `About`
3. Import `Suspense` from React (add to existing React import).
4. Create a simple `<LoadingFallback />` component inline (a centered loading message) or a shared `Suspense` wrapper.
5. Wrap each lazy-loaded route's `element` in `<Suspense fallback={<LoadingFallback />}>`. Group related routes to minimize repetition.
6. Run `npm run build` and verify the output produces multiple JS chunks in `dist/assets/`.

## Must-Haves

- [ ] `AdminPipeline`, `AdminTechniquePages`, `AdminReports`, `CreatorDashboard`, `CreatorSettings`, `About` are lazy-loaded
- [ ] Core public pages (`Home`, `SearchResults`, `TechniquePage`, `CreatorsBrowse`, `CreatorDetail`, `TopicsBrowse`, `SubTopicPage`) remain eagerly imported
- [ ] `Login` and `Register` remain eagerly imported (auth flow needs instant availability)
- [ ] `Suspense` fallback renders a loading indicator
- [ ] `npm run build` produces split chunks

## Verification

- `npm run build` exits 0
- `ls frontend/dist/assets/*.js | wc -l` — more JS chunks than before (expect 7+ files vs previous ~2-3)
- `rg 'React.lazy' frontend/src/App.tsx` shows 6 lazy imports

## Inputs

- `frontend/src/App.tsx` — current eager import structure

## Expected Output

- `frontend/src/App.tsx` — updated with React.lazy imports and Suspense wrappers
  - Estimate: 30m
  - Files: frontend/src/App.tsx
  - Verify: cd frontend && npm run build && ls dist/assets/*.js | wc -l && rg 'React.lazy' src/App.tsx
- [x] **T03: Normalized /topics and /videos endpoints from bare lists to paginated {items, total} response shape, updating backend schemas and two frontend consumers** — ## Description

Two API endpoints return bare lists instead of the standard `{items, total, offset, limit}` paginated shape. Normalize both backend endpoints and update the frontend consumers.

## Steps

1. **Backend — `/topics`:** In `backend/routers/topics.py`, modify `list_topics()` to return `{"items": topics, "total": len(topics)}` instead of the bare list. Add a `TopicListResponse` schema to `backend/schemas.py` or return a dict. Update the `response_model` accordingly.
2. **Backend — `/videos`:** In `backend/routers/videos.py`, modify `list_videos()` to return `{"items": videos, "total": total, "offset": offset, "limit": limit}` — it already accepts offset/limit params but returns a bare list. Add a `SELECT count(*)` query for the total. Add a `VideoListResponse` schema or use dict.
3. **Frontend — fetchTopics:** In `frontend/src/api/topics.ts` (created by T01), update `fetchTopics()` return type from `Promise<TopicCategory[]>` to `Promise<{items: TopicCategory[], total: number}>` or a typed response interface.
4. **Frontend — TopicsBrowse.tsx:** Update to destructure `.items` from the fetchTopics response.
5. **Frontend — Home.tsx:** Update its fetchTopics consumption to read `.items`.
6. **No frontend consumer for `/videos`** — the admin pipeline page uses a different endpoint (`/admin/pipeline/videos`). Confirm with `rg` that no frontend code calls the public `/videos` endpoint directly.
7. Run backend tests and frontend build to verify.

## Must-Haves

- [ ] `GET /api/v1/topics` returns `{items: [...], total: N}`
- [ ] `GET /api/v1/videos` returns `{items: [...], total: N, offset: M, limit: L}`
- [ ] `TopicsBrowse.tsx` reads `.items` from topics response
- [ ] `Home.tsx` reads `.items` from topics response
- [ ] `npm run build` passes
- [ ] Backend tests pass

## Verification

- `cd frontend && npm run build` exits 0
- `cd backend && python -m pytest tests/ -x -q` passes (run in Docker if needed)
- `rg 'response_model=list' backend/routers/topics.py backend/routers/videos.py` returns no matches (bare lists removed)

## Inputs

- `backend/routers/topics.py` — bare list return
- `backend/routers/videos.py` — bare list return
- `backend/schemas.py` — existing schemas
- `frontend/src/api/topics.ts` — fetchTopics function (from T01)
- `frontend/src/pages/TopicsBrowse.tsx` — topics consumer
- `frontend/src/pages/Home.tsx` — topics consumer

## Expected Output

- `backend/routers/topics.py` — paginated response
- `backend/routers/videos.py` — paginated response
- `backend/schemas.py` — new response schemas
- `frontend/src/api/topics.ts` — updated return type
- `frontend/src/pages/TopicsBrowse.tsx` — reads .items
- `frontend/src/pages/Home.tsx` — reads .items
  - Estimate: 45m
  - Files: backend/routers/topics.py, backend/routers/videos.py, backend/schemas.py, frontend/src/api/topics.ts, frontend/src/pages/TopicsBrowse.tsx, frontend/src/pages/Home.tsx
  - Verify: cd frontend && npm run build && cd ../backend && python -m pytest tests/ -x -q 2>/dev/null; rg 'response_model=list' backend/routers/topics.py backend/routers/videos.py
