Repo already past old "Sprint 19" (dashboard). Renumbering user's Sprint7+ roadmap to continue actual sequence: **Sprint 20 = old "Sprint 7" (Categories)** ... **Sprint 30 = old "Sprint 17" (Final Release)**.
Pattern to follow (mirrors `admin/products`): `models/`, `services/<x>-gateway.interface.ts` + `<x>-local.gateway.ts` (IndexedDB via existing local-storage service), `facade/`, `pages/`, `components/`. Local-only until backend endpoint exists — log gap in `docs/BACKEND.md`.
Autonomy: no stop-and-ask except missing API contract / business decision / secrets / external config / legal / multi-valid-design-fork. Commit after each sprint, conventional commits, no push. Quality gate before marking sprint done: build, typecheck, lint (if configured), responsive check, a11y pass, docs updated, no dead code, arch:check green.
Notify user: **from Sprint 20 (Categories) once product↔category link + admin categories CRUD exist, catalog can start receiving real items** (currently admin-products already has categoryId field but no category source of truth — that's the point where "adding items to catalog" becomes real, not mocked dropdown).
---
## Sprint 20 — Category Management ✅ done
- [x] AdminCategory model (id, parentId, title, translations, slug, icon, image, seo, visible, order, draft/published status, soft-delete)
- [x] AdminCategoriesGateway interface + local (in-memory, seeded from BackofficeDataService) gateway, mirrors products gateway
- [x] Wired into admin routing (`app.routes.ts`, replaced coming-soon placeholder)
- [x] Updated `docs/ADMIN.md` (new Sprint 20 section), `docs/BACKEND.md` (categories gap rewritten with real field list)
- [~] Responsive/a11y: reuses existing FormField/Input/Table/Button/EmptyState a11y wiring; live-browser click-through blocked by safety classifier on the guarded admin route (see below) — verified via tsc/build/arch:check only
- Note: `admin/products`' category dropdown still uses its own `AdminProductsGateway.loadCategories()`, not this new gateway — unification deferred to Sprint 21 (documented in BACKEND.md).
- [x] Wired products to real `AdminCategoriesGateway` (`AdminProductsLocalGateway` now seeds from `AdminCategoriesLocalGateway`, replacing its own `BackofficeDataService` category seed)
- [x] Gallery via `MediaPickerComponent` (add/remove thumbnails); translation editor already existed (Sprint pre-19); preview (computed discounted price); infinite-scroll toggle on list (`facade.loadMore()` appends vs pagination)
- [x] Updated `docs/ADMIN.md` (new Sprint 21 section), `docs/BACKEND.md` (item 6 rewritten with real field list)
- Known trade-off: related-products picker sources from whatever page is currently loaded in `AdminProductsFacade.products()`, not a full catalog search — documented in ADMIN.md, fine at current mock scale.