# Admin Panel Tasks — MercadoDeVida

## Convenciones

- Cada tarea = un archivo `ADM-XXX.md` en `specs/admin/`
- Cada tarea tiene: Goal, API contracts, Desktop UX, Responsive UX, Security, Error States, Acceptance Criteria, Tests
- Dependencias: otras tareas o BD-XXX

---

## BACKEND DEPENDENCIES (precondiciones)

| ID    | Descripción                              | Prioridad |
|-------|------------------------------------------|-----------|
| BD-01 | GET /auth/me → { user, role }           | CRÍTICA   |
| BD-02 | GET /promotions (listado con paginación) | ALTA      |
| BD-03 | PATCH /promotions/:id                    | ALTA      |
| BD-04 | DELETE /promotions/:id                   | MEDIA     |
| BD-05 | GET /reviews/admin (todas, con filtros)  | ALTA      |
| BD-06 | PATCH /products/:id/state                | MEDIA     |
| BD-07 | DELETE /products/:id                     | MEDIA     |
| BD-08 | DELETE /brands/:id                      | MEDIA     |
| BD-09 | POST /inventory/bulk-adjust              | BAJA      |
| BD-10 | GET /admin/audit                        | MEDIA     |
| BD-11 | GET /admin/stats (KPIs del dashboard)    | MEDIA     |

---

## PHASE 1 — Foundation

### ADM-001 — Crear proyecto Next.js en apps/admin

**Goal:** Nuevo workspace Next.js 16 + TypeScript + Tailwind bajo `apps/admin/`

**Dependencies:** Ninguna

**Modules:** `apps/admin/` (nuevo)

**API:** N/A (no hay código aún)

**Permissions:** N/A

**Desktop UX:** N/A

**Responsive UX:** N/A

**Security:** N/A

**Error States:** N/A

**Acceptance Criteria:**
- `apps/admin/` existe con `package.json`, `tsconfig.json`, `next.config.ts`, `tailwind.config.ts`
- `npm run build` compila sin errores en `apps/admin/`
- Rutas dinámicas ignoradas en `next.config.ts`

**Tests:** Unit: verificar que el proyecto compila

---

### ADM-002 — API Client + Types compartidos

**Goal:** Cliente HTTP tipado que consume el backend, con adapters por feature

**Dependencies:** BD-01 (GET /auth/me)

**Modules:** `apps/admin/lib/api-client.ts`, `apps/admin/lib/permissions.ts`, `apps/admin/types/`

**API:** Todos los endpoints del backend (sección 2.1 de SPEC.md)

**Permissions:** `can(user, permission)` function

**Desktop UX:** N/A

**Responsive UX:** N/A

**Security:**
- Token de sesión en cookie `HttpOnly`
- CSRF protection vía SameSite=Lax
- No exponer token en JS

**Error States:**
- 401 → redirect a /login
- 403 → toast "Sin permisos"
- 404 → página de no encontrado
- 500 → toast "Error del servidor"

**Acceptance Criteria:**
- `ApiClient` hace fetch al backend con cookie de sesión
- Mapeo de errores HTTP → mensajes de usuario
- Tipos para Products, Orders, Customers, etc.
- Feature API adapters (ordersApi, productsApi, etc.)

**Tests:**
- Unit: ApiClient.get/post/patch/delete
- Unit: can() permission checks
- Unit: error mapping

---

### ADM-003 — Login + Auth flow

**Goal:** Página de login funcional, logout, sesión persistente

**Dependencies:** BD-01, ADM-002

**Modules:** `apps/admin/app/(auth)/login/`, `apps/admin/app/api/auth/[...nextauth]/`

**API:** POST /auth/login, POST /auth/logout, GET /auth/me

**Permissions:** N/A

**Desktop UX:**
```
┌────────────────────────────────────────┐
│                                        │
│         [MercadoDeVida Logo]            │
│                                        │
│         Iniciar sesión                 │
│                                        │
│    Email    [_______________]          │
│    Password [_______________]           │
│                                        │
│    [    Iniciar sesión    ]            │
│                                        │
└────────────────────────────────────────┘
```

**Responsive UX:** Mismo layout, centrado en móvil

**Security:**
- HttpOnly cookie para sesión
- Rate limit del backend (429)
- No mostrar errores específicos de credenciales

**Error States:**
- Credenciales inválidas → "Email o contraseña incorrectos"
- Rate limited → "Demasiados intentos. Espera X segundos"
- Servidor caído → "Error de conexión. Intenta de nuevo"

**Acceptance Criteria:**
- Login exitoso → redirect a /admin
- Sesión válida al recargar → mantiene /admin
- Sesión inválida → redirect a /admin/login
- Logout → limpia cookie, redirect a /admin/login

**Tests:**
- E2E: login exitoso
- E2E: login fallido
- E2E: logout
- E2E: sesión expirada

---

## PHASE 2 — Admin Shell

### ADM-004 — Admin Shell (sidebar + layout)

**Goal:** Layout reutilizable con navegación, usuario, y protección de rutas

**Dependencies:** ADM-002, ADM-003

**Modules:** `apps/admin/app/(dashboard)/layout.tsx`, `apps/admin/components/admin/`

**API:** N/A

**Permissions:** `can()` en navegación

**Desktop UX:**
```
┌──────────────┬─────────────────────────────────┐
│ 🏠 Dashboard │  Header: [MercadoDeVida Admin]   │
│ 📦 Productos │  User: admin@mdv.es | Logout     │
│ 🧾 Pedidos   ├─────────────────────────────────┤
│ 📊 Inventario│                                 │
│ 👥 Clientes  │        CONTENT                  │
│ 🏷️ Categorías│                                 │
│ 🏷️ Marcas    │                                 │
│ 🏷️ Promociones│                                │
│ ⭐ Reseñas   │                                 │
│ 📄 CMS       │                                 │
│ ⚙️ Ajustes  │                                 │
└──────────────┴─────────────────────────────────┘
```

**Responsive UX:**
- Mobile: sidebar colapsado en hamburger menu
- Tablet: sidebar como drawer
- Desktop: sidebar fija 240px

**Security:**
- Middleware que verifica sesión en cada request
- Nav items ocultos según permisos
- Rutas /admin/* requieren auth (redirect a /admin/login)

**Error States:**
- Sin sesión → redirect login
- Error de permisos → toast

**Acceptance Criteria:**
- Sidebar muestra items según permisos del usuario
- Header muestra email + logout
- Mobile toggle funciona
- Navegación activa con estilo distintivo
- Loading skeleton en transición de auth

**Tests:**
- Component: Sidebar renders correct items for role
- E2E: unauthorized redirected to login
- E2E: sidebar navigation works

---

## PHASE 3 — MVP: Products

### ADM-005 — Product List Route

**Goal:** Ruta `/admin/products` con tabla paginada, búsqueda y filtros

**Dependencies:** ADM-004

**Modules:** `apps/admin/app/(dashboard)/products/page.tsx`

**API:** GET /products/search?q=&limit=20&offset=0

**Permissions:** products.read

**Desktop UX:**
```
Header + Sidebar
┌─────────────────────────────────────────────────────────┐
│ Productos                        [+ Crear producto]      │
├─────────────────────────────────────────────────────────┤
│ 🔍 Buscar...                              [Filtros ▼]   │
├────┬────────────────────┬────────┬────────┬────────────┤
│    │ Producto           │ Marca  │ Precio │ Estado     │
├────┼────────────────────┼────────┼────────┼────────────┤
│ □  │ Almendras Crudas   │ EcoVida│ €8.95  │ ● Activo   │
│ □  │ Vitamina D3+K2     │ NatPhar│ €12.50 │ ● Activo   │
├────┴────────────────────┴────────┴────────┴────────────┤
│ ← Anterior    Página 1 de 3    Siguiente →              │
└─────────────────────────────────────────────────────────┘
```

**Responsive UX:** Tabla scroll horizontal en móvil; cards en mobile

**Security:**products.read

**Error States:**
- Loading: skeleton
- Empty: "No hay productos"
- Error: retry button

**Acceptance Criteria:**
- Productos listados con nombre, marca, precio
- Búsqueda por nombre
- Paginación funcional
- Filtro por estado (activo/inactivo)
- Link a detalle

**Tests:**
- Unit: pagination params
- Component: ProductTable renders
- E2E: search filters products

---

### ADM-006 — Product Editor Shell

**Goal:** Shell del editor con tabs y detección de cambios sin guardar

**Dependencies:** ADM-005

**Modules:** `apps/admin/features/products/components/ProductEditor.tsx`

**API:** N/A (state local)

**Permissions:** products.write

**Desktop UX:** Tabs: General | Precios | Inventario | Imágenes | SEO | Publicar

**Responsive UX:** Tabs colapsados en accordion en móvil

**Security:**products.write

**Acceptance Criteria:**
- Cada tab es una sección separada
- Unsaved changes detected al navegar
- Confirm dialog antes de salir

**Tests:** Component: unsaved changes detected

---

### ADM-007 — Product General Fields

**Goal:** Campos generales: nombre, slug, descripción, marca, categorías

**Dependencies:** ADM-006

**Modules:** `apps/admin/features/products/components/sections/GeneralSection.tsx`

**API:** PATCH /products/:id (nombre, descripción, brandId, categoryIds)

**Permissions:** products.write

**Acceptance Criteria:**
- Campos pre-poblados con datos actuales
- Slug auto-generado desde nombre
- Selector de marca
- Selector de categorías (multiselect)
- Validación de requerido

**Tests:** Unit: slug auto-generation

---

### ADM-008 — Product Pricing Fields

**Goal:** Campos de precio: precio neto, tasa IVA, variantes

**Dependencies:** ADM-006

**Modules:** `apps/admin/features/products/components/sections/PricingSection.tsx`

**API:** GET /products/:id/variants, PATCH /products/:id/variants/:variantId

**Permissions:** products.write

**Acceptance Criteria:**
- Lista de variantes con SKU, EAN, atributos
- Precio por variante
- IVA (general 21%, reducido 10%)

**Tests:** Unit: gross price calculation (only display, backend owns truth)

---

### ADM-009 — Product Inventory Section

**Goal:** Ver y ajustar stock por variante

**Dependencies:** ADM-006

**Modules:** `apps/admin/features/products/components/sections/InventorySection.tsx`

**API:** GET /inventory/:variantId/availability, PUT /inventory/:variantId/stock

**Permissions:** inventory.write

**Acceptance Criteria:**
- Stock actual visible por variante
- Input para nuevo stock
- Botón "Actualizar"
- Confirmación de cambio

**Tests:** Integration: stock update flow

---

### ADM-010 — Product Images Section

**Goal:** Gestionar imágenes: subir, reordenar, eliminar

**Dependencies:** ADM-006

**Modules:** `apps/admin/features/products/components/sections/ImagesSection.tsx`

**API:** GET /products/:id/images, POST /products/:id/images, DELETE /products/:id/images/:imageId, PATCH /products/:id/images/reorder

**Permissions:** products.write

**Acceptance Criteria:**
- Grid de imágenes actuales
- Upload de nueva imagen
- Drag to reorder
- Marcar como principal
- Eliminar con confirmación

**Tests:** E2E: image upload + reorder

---

### ADM-011 — Product SEO Section

**Goal:** Campos SEO: title, description, index/noindex

**Dependencies:** ADM-006

**Modules:** `apps/admin/features/products/components/sections/SEOSection.tsx`

**API:** PATCH /products/:id (seoTitle, seoDescription)

**Permissions:** products.write

**Acceptance Criteria:**
- SEO title con contador (max 60 chars)
- Meta description con contador (max 160 chars)
- Preview de snippet Google
- Toggle index/noindex

**Tests:** Component: character counters

---

### ADM-012 — Product Create Flow

**Goal:** Ruta `/admin/products/new` para crear producto nuevo

**Dependencies:** ADM-006

**Modules:** `apps/admin/app/(dashboard)/products/new/page.tsx`

**API:** POST /products

**Permissions:** products.write

**Acceptance Criteria:**
- Formulario completo con todos los campos
- POST al crear
- Redirect a detalle del nuevo producto

**Tests:** E2E: create product end-to-end

---

## PHASE 3 — MVP: Orders

### ADM-013 — Order List Route

**Goal:** Ruta `/admin/orders` con listado paginado y filtros

**Dependencies:** ADM-004

**Modules:** `apps/admin/app/(dashboard)/orders/page.tsx`

**API:** GET /orders (el nuevo endpoint creado en F-047)

**Permissions:** orders.read

**Desktop UX:**
```
┌─────────────────────────────────────────────────────────┐
│ Pedidos                           Filtros: [Estado ▼]   │
├──────┬─────────────┬──────────┬────────┬────────────────┤
│ #ID  │ Cliente     │ Total    │ Estado │ Fecha           │
├──────┼─────────────┼──────────┼────────┼────────────────┤
│ abc… │ maria@…     │ €45.83   │ ● Paid │ 15 ago 2026    │
│ abc… │ juan@…      │ €23.10   │ ● Pend │ 14 ago 2026    │
├──────┴─────────────┴──────────┴────────┴────────────────┤
│ ← Anterior    Página 1    Siguiente →                   │
└─────────────────────────────────────────────────────────┘
```

**Responsive UX:** Cards en móvil

**Acceptance Criteria:**
- Filtros: estado (PENDING, PAID, PROCESSING, SHIPPED, DELIVERED, CANCELLED)
- Búsqueda por ID o email de cliente
- Paginación
- Link a detalle

**Tests:** E2E: filter orders by status

---

### ADM-014 — Order Detail Page

**Goal:** Ruta `/admin/orders/[id]` con detalle completo y acciones

**Dependencies:** ADM-013

**Modules:** `apps/admin/app/(dashboard)/orders/[id]/page.tsx`

**API:** GET /orders/:id/admin, GET /payments/orders/:id/transactions

**Permissions:** orders.read

**Acceptance Criteria:**
- Header: ID, estado badge, fecha
- Resumen: subtotal, descuentos, IVA, total
- Línea de tiempo de estados
- Lista de items con nombres, cantidades, precios
- Dirección de envío
- Transacciones de pago
- Botones de acción según estado

**Tests:** E2E: view order detail

---

### ADM-015 — Order State Transitions

**Goal:** Botones de acción para cambiar estado de pedido

**Dependencies:** ADM-014

**Modules:** `apps/admin/app/(dashboard)/orders/[id]/page.tsx` (actions)

**API:** POST /orders/:id/transitions/admin

**Permissions:** orders.write

**Acceptance Criteria:**
- Solo acciones válidas para el estado actual se muestran
- Confirmación antes de ejecutar
- Optimistic: esperar respuesta del backend
- Refresh del detalle post-transición
- Timeline actualizada

**State machine visible en UI:**
```
PENDING → [Marcar Pagado] | [Cancelar]
PAID → [Procesar] | [Cancelar] | [Reembolsar]
PROCESSING → [Enviar] | [Cancelar] | [Reembolsar]
SHIPPED → [Marcar Entregado] | [Reembolso parcial]
```

**Tests:** E2E: transition order state through valid path

---

## PHASE 3 — MVP: Inventory

### ADM-020 — Inventory Overview

**Goal:** Vista de inventario por variante con stock y estado

**Dependencies:** ADM-004

**Modules:** `apps/admin/app/(dashboard)/inventory/page.tsx`

**API:** GET /products/search (todas), GET /products/:id/variants, GET /inventory/:variantId/availability

**Permissions:** inventory.read

**Acceptance Criteria:**
- Tabla: SKU, producto, variante, stock disponible, estado
- Filtros: sin stock, stock bajo, en stock
- Búsqueda por SKU o nombre

**Tests:** E2E: filter by stock status

---

### ADM-021 — Quick Stock Adjustment

**Goal:** Ajuste rápido de stock desde la vista de inventario

**Dependencies:** ADM-020

**Modules:** Inline en inventory page

**API:** PUT /inventory/:variantId/stock

**Permissions:** inventory.write

**Acceptance Criteria:**
- Input inline para nuevo stock
- Validación: número >= 0
- Confirmación antes de guardar
- Refresco del valor tras guardado

**Tests:** Integration: stock adjustment flow

---

## PHASE 4 — Secondary Features

### ADM-023 — Customer List

**Goal:** `/admin/customers` — listado de clientes

**Dependencies:** ADM-004

**API:** GET /users

**Permissions:** customers.read

### ADM-024 — Customer Detail

**Goal:** `/admin/customers/[id]` — detalle con pedidos

**Dependencies:** ADM-023

**API:** GET /users/:id, GET /orders (filtrado por user)

**Permissions:** customers.read

### ADM-025 — Customer Edit

**Goal:** Editar datos de cliente

**Dependencies:** ADM-024

**API:** PATCH /users/:id

**Permissions:** customers.write

### ADM-026 — Categories CRUD

**Goal:** `/admin/categories` — gestión de categorías

**Dependencies:** ADM-004

**API:** GET /categories/tree, POST /categories, PATCH /categories/:id, DELETE /categories/:id

**Permissions:** categories.write

### ADM-027 — Brands CRUD

**Goal:** `/admin/brands` — gestión de marcas

**Dependencies:** ADM-004

**API:** GET /brands, POST /brands, PATCH /brands/:id

**Permissions:** brands.write

### ADM-031 — Dashboard Widgets

**Goal:** `/admin` — stats operativos

**Dependencies:** ADM-004, BD-11

**API:** GET /admin/stats (o agregación de endpoints existentes)

**Permissions:** orders.read, inventory.read

---

## PHASE 5 — Extensions

### ADM-032 — Promotions CRUD (requiere BD-02, BD-03)

**Goal:** `/admin/promotions` — gestión de promociones

**API:** GET /promotions, POST /promotions, PATCH /promotions/:id, DELETE /promotions/:id

**Permissions:** promotions.write

### ADM-035 — Reviews Moderation (requiere BD-05)

**Goal:** `/admin/reviews` — moderación de reseñas

**API:** GET /reviews/admin, PATCH /reviews/:id/moderate

**Permissions:** reviews.moderate

### ADM-038 — CMS Pages

**Goal:** `/admin/cms` — gestión de páginas de contenido

**API:** GET /cms/pages/:slug, POST /cms/pages, PATCH /cms/pages/:id, POST /cms/pages/:id/publish, POST /cms/pages/:id/unpublish

**Permissions:** cms.write

---

## Definición de Done por tarea

Cada tarea ADM-XXX se marca DONE cuando:
- [ ] SPEC.md de la tarea aprobado
- [ ] Código implementado
- [ ] Tests unitarios pasando
- [ ] Tests E2E pasando (si aplica)
- [ ] Build pasa sin errores TS
- [ ] Verificado con curl/curl manual
