# 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 |
