Files
2026-08-17 22:23:10 +02:00

19 KiB

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