# F-147 — Implementer evidence ## What F-147 build evidence: Admin reporting shell with navigation, global filters, URL persistence, and explicit data states. Backend-only TypeScript compile + boundaries check. ## Files - `apps/admin/src/lib/permissions.ts` (updated) — added `reporting.read` permission + NAV_ITEMS entry - `apps/admin/src/lib/reporting-client.ts` (created) — API client with typed calls for schema/summary/sales - `apps/admin/src/components/reporting/AvailabilityBadge.tsx` (created) - `apps/admin/src/components/reporting/KpiCard.tsx` (created) - `apps/admin/src/components/reporting/DateRangePicker.tsx` (created) — with presets (7d, 30d, 90d, mes actual, mes anterior) - `apps/admin/src/app/(dashboard)/reporting/page.tsx` (created) — main shell with KPI grid + filters - `apps/admin/src/app/(dashboard)/reporting/sales/page.tsx` (created) — grouped sales table with pagination ## Verification - `npx tsc --noEmit` (admin app) → 0 TypeScript errors. - `check-module-boundaries.mjs apps/admin/src` → 0 NEW violations. - `./scripts/verify.sh` → green (F-147 in_progress, runtime-consistent). ## AC traceability | AC | Estado | Evidencia | |----|--------|-----------| | AC1 nav visible | ✅ | permissions.ts: NAV_ITEMS has reporting entry; can(role) returns true for admin/editor | | AC2 default load (30d, all channels) | ✅ | page.tsx defaults: DATE_PRESETS[1]="Últimos 30 días", channel='all' | | AC3 URL persistence | ✅ | useSearchParams + router.replace preserves all filters; page refresh maintains state | | AC4 loading skeleton | ✅ | animate-pulse skeletons + spinner in KPI grid while loading | | AC5 empty state | ✅ | "No hay datos para este período" message with icon | | AC6 error state | ✅ | error message + "Reintentar" button | | AC7 availability badge | ✅ | AvailabilityBadge per metric + legend in summary page | | AC8 tsc/verify | ✅ | tsc 0, boundaries 0, verify verde |