19 KiB
Admin Panel Tasks — MercadoDeVida
Convenciones
- Cada tarea = un archivo
ADM-XXX.mdenspecs/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 conpackage.json,tsconfig.json,next.config.ts,tailwind.config.tsnpm run buildcompila sin errores enapps/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:
ApiClienthace 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