668 lines
19 KiB
Markdown
668 lines
19 KiB
Markdown
# 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
|