diff --git a/backlog/features.json b/backlog/features.json
index e4c5519..b8d974e 100644
--- a/backlog/features.json
+++ b/backlog/features.json
@@ -23,7 +23,7 @@
{
"id": "ADM-006",
"title": "Admin: Product Editor Shell",
- "description": "Shell del editor de productos con tabs y detección de cambios sin guardar.",
+ "description": "Shell del editor de productos con tabs y detecci\u00f3n de cambios sin guardar.",
"status": "done",
"priority": "high",
"phase": "products",
@@ -39,7 +39,7 @@
{
"id": "ADM-007",
"title": "Admin: Product General Fields",
- "description": "Campos generales del producto: nombre, slug, descripción, marca, categorías.",
+ "description": "Campos generales del producto: nombre, slug, descripci\u00f3n, marca, categor\u00edas.",
"status": "done",
"priority": "high",
"phase": "products",
@@ -87,7 +87,7 @@
{
"id": "ADM-010",
"title": "Admin: Product Images Section",
- "description": "Gestionar imágenes: subir, reorder, eliminar, marcar principal.",
+ "description": "Gestionar im\u00e1genes: subir, reorder, eliminar, marcar principal.",
"status": "done",
"priority": "medium",
"phase": "products",
@@ -119,7 +119,7 @@
{
"id": "ADM-012",
"title": "Admin: Product Create Flow",
- "description": "Ruta /admin/products/new con formulario completo de creación.",
+ "description": "Ruta /admin/products/new con formulario completo de creaci\u00f3n.",
"status": "done",
"priority": "high",
"phase": "products",
@@ -135,7 +135,7 @@
{
"id": "ADM-016",
"title": "Admin: Inventory Overview",
- "description": "Vista de inventario por variante con stock y estado. Filtros y búsqueda.",
+ "description": "Vista de inventario por variante con stock y estado. Filtros y b\u00fasqueda.",
"status": "done",
"priority": "high",
"phase": "inventory",
@@ -151,7 +151,7 @@
{
"id": "ADM-017",
"title": "Admin: Quick Stock Adjustment",
- "description": "Ajuste rápido de stock inline con confirmación y refresh.",
+ "description": "Ajuste r\u00e1pido de stock inline con confirmaci\u00f3n y refresh.",
"status": "done",
"priority": "high",
"phase": "inventory",
@@ -167,7 +167,7 @@
{
"id": "ADM-018",
"title": "Admin: Customer List",
- "description": "Listado de clientes con búsqueda y paginación.",
+ "description": "Listado de clientes con b\u00fasqueda y paginaci\u00f3n.",
"status": "done",
"priority": "medium",
"phase": "customers",
@@ -218,7 +218,7 @@
{
"id": "ADM-021",
"title": "Admin: Categories CRUD",
- "description": "CRUD de categorías: crear, editar, eliminar categorías.",
+ "description": "CRUD de categor\u00edas: crear, editar, eliminar categor\u00edas.",
"status": "done",
"priority": "medium",
"phase": "categories",
@@ -284,7 +284,7 @@
{
"id": "ADM-025",
"title": "Admin: Reviews Moderation",
- "description": "Moderación de reseñas: aprobar, rechazar. Requiere BD-05 backend.",
+ "description": "Moderaci\u00f3n de rese\u00f1as: aprobar, rechazar. Requiere BD-05 backend.",
"status": "done",
"priority": "low",
"phase": "reviews",
@@ -301,7 +301,7 @@
{
"id": "ADM-026",
"title": "Admin: CMS Pages",
- "description": "Gestión de páginas CMS: crear, editar, publicar, despublicar.",
+ "description": "Gesti\u00f3n de p\u00e1ginas CMS: crear, editar, publicar, despublicar.",
"status": "done",
"priority": "low",
"phase": "cms",
@@ -318,7 +318,7 @@
{
"id": "BD-02",
"title": "Backend: GET /promotions (listado)",
- "description": "Endpoint GET /promotions con paginación y filtros para listado admin. Alta prioridad — bloquea ADM-024.",
+ "description": "Endpoint GET /promotions con paginaci\u00f3n y filtros para listado admin. Alta prioridad \u2014 bloquea ADM-024.",
"status": "done",
"priority": "high",
"phase": "backend",
@@ -334,7 +334,7 @@
{
"id": "BD-03",
"title": "Backend: PATCH /promotions/:id",
- "description": "Editar promoción existente: código, tipo, valor, fechas, límites.",
+ "description": "Editar promoci\u00f3n existente: c\u00f3digo, tipo, valor, fechas, l\u00edmites.",
"status": "done",
"priority": "high",
"phase": "backend",
@@ -350,7 +350,7 @@
{
"id": "BD-04",
"title": "Backend: DELETE /promotions/:id",
- "description": "Eliminar promoción por ID. MEDIA prioridad.",
+ "description": "Eliminar promoci\u00f3n por ID. MEDIA prioridad.",
"status": "done",
"priority": "medium",
"phase": "backend",
@@ -366,7 +366,7 @@
{
"id": "BD-05",
"title": "Backend: GET /reviews/admin",
- "description": "Listado de reseñas para moderación admin con filtros (pendiente/aprobada/rechazada). Alta prioridad — bloquea ADM-025.",
+ "description": "Listado de rese\u00f1as para moderaci\u00f3n admin con filtros (pendiente/aprobada/rechazada). Alta prioridad \u2014 bloquea ADM-025.",
"status": "done",
"priority": "high",
"phase": "backend",
@@ -433,7 +433,7 @@
{
"id": "BD-09",
"title": "Backend: POST /inventory/bulk-adjust",
- "description": "Bulk stock adjustment atómico. BAJA prioridad.",
+ "description": "Bulk stock adjustment at\u00f3mico. BAJA prioridad.",
"status": "done",
"priority": "low",
"phase": "backend",
@@ -1737,7 +1737,7 @@
},
{
"id": "F-037",
- "title": "Seed data: productos categorías marcas",
+ "title": "Seed data: productos categor\u00edas marcas",
"status": "done",
"depends_on": [
"F-036"
@@ -1928,7 +1928,7 @@
{
"id": "F-047",
"title": "Admin Panel completo",
- "description": "Panel de administración: listado y gestión de pedidos, productos y transiciones de estado.",
+ "description": "Panel de administraci\u00f3n: listado y gesti\u00f3n de pedidos, productos y transiciones de estado.",
"status": "done",
"created_at": "2026-08-16T21:33:10Z",
"gates": {
@@ -1943,8 +1943,8 @@
},
{
"id": "ADM-27",
- "title": "Admin: Auto-slug y auto-SEO para marcas, productos y categorías",
- "description": "En admin, al escribir el nombre de marca/producto/categoría, el slug y los campos SEO deben generarse automáticamente. El usuario puede editarlos manualmente después. Slug: slugify(name). SEO title: name. SEO description: una frase descriptiva basada en name. El tracking de edición manual (slugManual) debe existir para no sobreescribir lo que el usuario ya personalizó.",
+ "title": "Admin: Auto-slug y auto-SEO para marcas, productos y categor\u00edas",
+ "description": "En admin, al escribir el nombre de marca/producto/categor\u00eda, el slug y los campos SEO deben generarse autom\u00e1ticamente. El usuario puede editarlos manualmente despu\u00e9s. Slug: slugify(name). SEO title: name. SEO description: una frase descriptiva basada en name. El tracking de edici\u00f3n manual (slugManual) debe existir para no sobreescribir lo que el usuario ya personaliz\u00f3.",
"status": "done",
"priority": "high",
"gates": {
@@ -1966,7 +1966,7 @@
{
"id": "FIX-01",
"title": "Admin orders page: Cannot read properties of undefined (reading 'length')",
- "description": "Runtime TypeError en orders/page.tsx:95. orders se inicializa como [] y se renderiza antes de que load() complete. La solución es inicializar orders como null y usar orders?.length ?? 0 en el template.",
+ "description": "Runtime TypeError en orders/page.tsx:95. orders se inicializa como [] y se renderiza antes de que load() complete. La soluci\u00f3n es inicializar orders como null y usar orders?.length ?? 0 en el template.",
"status": "done",
"priority": "high",
"type": "fix",
@@ -1987,7 +1987,7 @@
{
"id": "ADM-28",
"title": "Branding: logo oficial y colores de marca",
- "description": "Reemplazar el SVG genérico del header por el logo oficial descargado de mercadodevida.es. El logo ya está en project/frontend/public/images/logo.png. Actualizar Header.tsx para usar Image con el logo real. Los colores (#2D6A4F) ya coinciden con la marca. Las páginas de contenido (terms, about, contact) requieren revisión humana antes de publicar — se haran luego.",
+ "description": "Reemplazar el SVG gen\u00e9rico del header por el logo oficial descargado de mercadodevida.es. El logo ya est\u00e1 en project/frontend/public/images/logo.png. Actualizar Header.tsx para usar Image con el logo real. Los colores (#2D6A4F) ya coinciden con la marca. Las p\u00e1ginas de contenido (terms, about, contact) requieren revisi\u00f3n humana antes de publicar \u2014 se haran luego.",
"status": "done",
"priority": "high",
"type": "feature",
@@ -2031,7 +2031,7 @@
{
"id": "ADM-29",
"title": "Backoffice: crear y gestionar usuarios admin",
- "description": "En admin, permitir crear, editar y eliminar usuarios backoffice. Cada usuario tiene email, nombre, rol (admin/editor/viewer) y contraseña. No confundir con clientes de storefront. Incluye listar usuarios, crear nuevo, editar, activar/desactivar, cambiar contraseña, eliminar.",
+ "description": "En admin, permitir crear, editar y eliminar usuarios backoffice. Cada usuario tiene email, nombre, rol (admin/editor/viewer) y contrase\u00f1a. No confundir con clientes de storefront. Incluye listar usuarios, crear nuevo, editar, activar/desactivar, cambiar contrase\u00f1a, eliminar.",
"status": "done",
"priority": "high",
"type": "feature",
@@ -2052,8 +2052,8 @@
},
{
"id": "ADM-30",
- "title": "Backoffice: ajustes generales de tienda (nombre, título, logo)",
- "description": "Panel de configuración general de la tienda en admin. Permite cambiar nombre comercial, título de la tienda (usado en
y SEO), descripción, dirección, teléfono, email de contacto, WhatsApp, horarios. Esta info ya existe en businessConfig.ts del backend — debe poder editarse desde admin y persistirse en DB.",
+ "title": "Backoffice: ajustes generales de tienda (nombre, t\u00edtulo, logo)",
+ "description": "Panel de configuraci\u00f3n general de la tienda en admin. Permite cambiar nombre comercial, t\u00edtulo de la tienda (usado en y SEO), descripci\u00f3n, direcci\u00f3n, tel\u00e9fono, email de contacto, WhatsApp, horarios. Esta info ya existe en businessConfig.ts del backend \u2014 debe poder editarse desde admin y persistirse en DB.",
"status": "done",
"priority": "medium",
"type": "feature",
@@ -2073,8 +2073,8 @@
},
{
"id": "ADM-31",
- "title": "Backoffice: gestión de tipos impositivos (IVA)",
- "description": "En admin, permitir configurar los tipos de IVA aplicables a productos. España: general 21%, reducido 10%, superreducido 4%. Cada producto o categoría puede tener un tipo de IVA asociado. El checkout calcula el IVA según el tipo del producto. Backend ya soporta vatRate en pricing — falta la UI de gestión de tipos impositivos.",
+ "title": "Backoffice: gesti\u00f3n de tipos impositivos (IVA)",
+ "description": "En admin, permitir configurar los tipos de IVA aplicables a productos. Espa\u00f1a: general 21%, reducido 10%, superreducido 4%. Cada producto o categor\u00eda puede tener un tipo de IVA asociado. El checkout calcula el IVA seg\u00fan el tipo del producto. Backend ya soporta vatRate en pricing \u2014 falta la UI de gesti\u00f3n de tipos impositivos.",
"status": "done",
"priority": "medium",
"type": "feature",
@@ -2095,8 +2095,8 @@
},
{
"id": "ADM-32",
- "title": "Backoffice: logs de errores y auditoría",
- "description": "En admin, pantalla de logs de errores del sistema. Muestra errores capturados por el backend (audit-logger) con timestamp, severity (error/warn/info), módulo, mensaje y stack trace. Permite filtrar por fecha, severity y módulo. Solo accesible para rol admin. Exportar a CSV.",
+ "title": "Backoffice: logs de errores y auditor\u00eda",
+ "description": "En admin, pantalla de logs de errores del sistema. Muestra errores capturados por el backend (audit-logger) con timestamp, severity (error/warn/info), m\u00f3dulo, mensaje y stack trace. Permite filtrar por fecha, severity y m\u00f3dulo. Solo accesible para rol admin. Exportar a CSV.",
"status": "done",
"priority": "low",
"type": "feature",
@@ -2117,8 +2117,8 @@
},
{
"id": "ADM-33",
- "title": "Backoffice: gestión de métodos de pago",
- "description": "En admin, panel para configurar los métodos de pago activos. Métodos soportados: tarjeta (Stripe), transferencia bancaria, Contra reembolso, Bizum. Cada método tiene nombre, descripción, instrucciones, icono y activo/inactivo. El checkout muestra solo los métodos activos. El módulo payments ya existe en backend — falta la UI de gestión.",
+ "title": "Backoffice: gesti\u00f3n de m\u00e9todos de pago",
+ "description": "En admin, panel para configurar los m\u00e9todos de pago activos. M\u00e9todos soportados: tarjeta (Stripe), transferencia bancaria, Contra reembolso, Bizum. Cada m\u00e9todo tiene nombre, descripci\u00f3n, instrucciones, icono y activo/inactivo. El checkout muestra solo los m\u00e9todos activos. El m\u00f3dulo payments ya existe en backend \u2014 falta la UI de gesti\u00f3n.",
"status": "done",
"priority": "medium",
"type": "feature",
@@ -2138,8 +2138,8 @@
},
{
"id": "ADM-34",
- "title": "Backoffice: gestión de métodos de envío",
- "description": "En admin, panel para configurar las opciones de envío disponibles. Métodos: estándar, express, recogida en tienda. Cada método tiene nombre, precio (fijo o gratis a partir de X), plazos de entrega, zona geográfica (provincia/CP), activo/inactivo. El módulo shipping ya existe en backend — falta la UI de gestión.",
+ "title": "Backoffice: gesti\u00f3n de m\u00e9todos de env\u00edo",
+ "description": "En admin, panel para configurar las opciones de env\u00edo disponibles. M\u00e9todos: est\u00e1ndar, express, recogida en tienda. Cada m\u00e9todo tiene nombre, precio (fijo o gratis a partir de X), plazos de entrega, zona geogr\u00e1fica (provincia/CP), activo/inactivo. El m\u00f3dulo shipping ya existe en backend \u2014 falta la UI de gesti\u00f3n.",
"status": "done",
"priority": "medium",
"type": "feature",
@@ -2159,8 +2159,8 @@
},
{
"id": "ADM-35",
- "title": "Backoffice: gestión de clientes — crear y editar",
- "description": "Customers page con botón crear cliente (modal), editar displayName/phone (modal). Tipos Customer actualizados con displayName y phone. customersApi.create() y customersApi.update() agregados.",
+ "title": "Backoffice: gesti\u00f3n de clientes \u2014 crear y editar",
+ "description": "Customers page con bot\u00f3n crear cliente (modal), editar displayName/phone (modal). Tipos Customer actualizados con displayName y phone. customersApi.create() y customersApi.update() agregados.",
"status": "done",
"priority": "high",
"type": "feature",
@@ -2181,8 +2181,8 @@
},
{
"id": "FE-01",
- "title": "Storefront: búsqueda semántica de productos",
- "description": "Reemplazar la búsqueda por keyword exacta en storefront por búsqueda semántica/vectorial. El usuario busca en lenguaje natural (ej: '什me ayudan las cápsulas de magnesio para dormir') y el sistema encuentra productos relevantes aunque no contengan esos términos exactos. Requiere: embedding de productos (descripción + nombre) en el backend, endpoint de búsqueda que use similitud vectorial o embedding-based matching, UI de resultados en /search. Para MVP se puede usar búsqueda por texto libre con ILIKE + ranking por relevancia antes de invertir en embeddings.",
+ "title": "Storefront: b\u00fasqueda sem\u00e1ntica de productos",
+ "description": "Reemplazar la b\u00fasqueda por keyword exacta en storefront por b\u00fasqueda sem\u00e1ntica/vectorial. El usuario busca en lenguaje natural (ej: '\u4ec0me ayudan las c\u00e1psulas de magnesio para dormir') y el sistema encuentra productos relevantes aunque no contengan esos t\u00e9rminos exactos. Requiere: embedding de productos (descripci\u00f3n + nombre) en el backend, endpoint de b\u00fasqueda que use similitud vectorial o embedding-based matching, UI de resultados en /search. Para MVP se puede usar b\u00fasqueda por texto libre con ILIKE + ranking por relevancia antes de invertir en embeddings.",
"status": "done",
"priority": "medium",
"type": "feature",
@@ -2204,8 +2204,8 @@
},
{
"id": "FIX-03",
- "title": "Storefront: categorías y marcas centradas",
- "description": "Las páginas de categorías y marcas en el storefront muestran el contenido apilado a la izquierda. Debería estar centrado.",
+ "title": "Storefront: categor\u00edas y marcas centradas",
+ "description": "Las p\u00e1ginas de categor\u00edas y marcas en el storefront muestran el contenido apilado a la izquierda. Deber\u00eda estar centrado.",
"status": "done",
"priority": "medium",
"type": "fix",
@@ -2225,8 +2225,8 @@
},
{
"id": "FIX-04",
- "title": "Storefront: logo, footer y tipografía de marca",
- "description": "Logos renovados con tipografía Almagro para 'Natural'. Header: [logo sticker] + [Natural Almagro]. Footer: [Natural Almagro / Mercado de Vida FreeSans] apilado. Fuente Almagro en Dropbox.",
+ "title": "Storefront: logo, footer y tipograf\u00eda de marca",
+ "description": "Logos renovados con tipograf\u00eda Almagro para 'Natural'. Header: [logo sticker] + [Natural Almagro]. Footer: [Natural Almagro / Mercado de Vida FreeSans] apilado. Fuente Almagro en Dropbox.",
"status": "done",
"priority": "high",
"type": "fix",
@@ -2246,8 +2246,8 @@
},
{
"id": "FIX-05",
- "title": "Storefront: normalizar header y tipografía",
- "description": "Header: solo logo (sticker). Favicon: sticker en 32x32. Tipografía: Open Sans en globals.css y layout.tsx.",
+ "title": "Storefront: normalizar header y tipograf\u00eda",
+ "description": "Header: solo logo (sticker). Favicon: sticker en 32x32. Tipograf\u00eda: Open Sans en globals.css y layout.tsx.",
"status": "done",
"priority": "high",
"type": "fix",
@@ -2268,8 +2268,8 @@
},
{
"id": "FE-02",
- "title": "Storefront: barra de búsqueda en el Header",
- "description": "Agregar input de búsqueda en el Header del storefront con autocompletado y navegación a /search",
+ "title": "Storefront: barra de b\u00fasqueda en el Header",
+ "description": "Agregar input de b\u00fasqueda en el Header del storefront con autocompletado y navegaci\u00f3n a /search",
"status": "done",
"priority": "high",
"labels": [
@@ -2304,8 +2304,8 @@
},
{
"id": "FIX-07",
- "title": "Admin: Categorías — emojis en acciones",
- "description": "Reemplazar texto \"Editar\" / \"Eliminar\" por iconos de lápiz y papelera en la tabla de categorías.",
+ "title": "Admin: Categor\u00edas \u2014 emojis en acciones",
+ "description": "Reemplazar texto \"Editar\" / \"Eliminar\" por iconos de l\u00e1piz y papelera en la tabla de categor\u00edas.",
"status": "done",
"priority": "low",
"phase": "admin",
@@ -2320,8 +2320,8 @@
},
{
"id": "FIX-08",
- "title": "Admin: Usuarios backoffice — emojis en acciones",
- "description": "Reemplazar texto \"Editar\" / \"Eliminar\" por iconos de lápiz y papelera en la tabla de usuarios.",
+ "title": "Admin: Usuarios backoffice \u2014 emojis en acciones",
+ "description": "Reemplazar texto \"Editar\" / \"Eliminar\" por iconos de l\u00e1piz y papelera en la tabla de usuarios.",
"status": "done",
"priority": "low",
"phase": "admin",
@@ -2337,7 +2337,7 @@
{
"id": "ADM-36",
"title": "Admin: Dashboard con visualizaciones",
- "description": "Reemplazar los 4 stat cards del dashboard por gráficos interactivos: ventas por día (línea), productos más vendidos (barras), estado de pedidos (donut), revenue mensual.",
+ "description": "Reemplazar los 4 stat cards del dashboard por gr\u00e1ficos interactivos: ventas por d\u00eda (l\u00ednea), productos m\u00e1s vendidos (barras), estado de pedidos (donut), revenue mensual.",
"status": "done",
"priority": "medium",
"phase": "admin",
@@ -2354,7 +2354,7 @@
{
"id": "ADM-37",
"title": "Admin: Editor de producto completo",
- "description": "Editor de producto completo con: EAN editable en variante; Atributos checkbox (Bio, Vegano, Sin Gluten, Keto, etc.); Canales obligatorios (online/offline/all); Imágenes con upload + URL + drag&drop; Precio de oferta en variante; PVP (bruto con IVA) editable en variante; Precio de coste en variante.",
+ "description": "Editor de producto completo con: EAN editable en variante; Atributos checkbox (Bio, Vegano, Sin Gluten, Keto, etc.); Canales obligatorios (online/offline/all); Im\u00e1genes con upload + URL + drag&drop; Precio de oferta en variante; PVP (bruto con IVA) editable en variante; Precio de coste en variante.",
"status": "done",
"priority": "critical",
"phase": "admin",
@@ -2370,7 +2370,7 @@
{
"id": "ADM-38",
"title": "Admin: Pedidos editables",
- "description": "Permitir editar pedidos desde el admin: cambiar estado, editar líneas de pedido (cantidades), añadir productos, aplicar descuentos, actualizar datos de envío y facturación.",
+ "description": "Permitir editar pedidos desde el admin: cambiar estado, editar l\u00edneas de pedido (cantidades), a\u00f1adir productos, aplicar descuentos, actualizar datos de env\u00edo y facturaci\u00f3n.",
"status": "done",
"priority": "high",
"phase": "admin",
@@ -2385,8 +2385,8 @@
},
{
"id": "ADM-39",
- "title": "Admin: Clientes — dirección y facturación",
- "description": "Desde la gestión de clientes, permitir ver/editar direcciones de envío y datos de facturación (nombre fiscal, CIF/NIF, dirección completa).",
+ "title": "Admin: Clientes \u2014 direcci\u00f3n y facturaci\u00f3n",
+ "description": "Desde la gesti\u00f3n de clientes, permitir ver/editar direcciones de env\u00edo y datos de facturaci\u00f3n (nombre fiscal, CIF/NIF, direcci\u00f3n completa).",
"status": "done",
"priority": "medium",
"phase": "admin",
@@ -2402,8 +2402,8 @@
},
{
"id": "ADM-40",
- "title": "Admin: Auditoría dentro de Ajustes",
- "description": "Mover la página de auditoría a la sección de Ajustes. Mostrar más detalle: dirección IP, user-agent, duración de la operación, diff de cambios.",
+ "title": "Admin: Auditor\u00eda dentro de Ajustes",
+ "description": "Mover la p\u00e1gina de auditor\u00eda a la secci\u00f3n de Ajustes. Mostrar m\u00e1s detalle: direcci\u00f3n IP, user-agent, duraci\u00f3n de la operaci\u00f3n, diff de cambios.",
"status": "done",
"priority": "medium",
"phase": "admin",
@@ -2419,8 +2419,8 @@
},
{
"id": "ADM-41",
- "title": "Admin: Ajustes reorganizados con categorías",
- "description": "Reorganizar Ajustes en subcategorías: General (nombre, tagline, contacto), Redes sociales, Footer (con support para {{year}} placeholder), Localizaciones (múltiples tiendas físicas con dirección, horarios de apertura), Envíos (zonas y tarifas ya hechas pero integrarlo aquí).",
+ "title": "Admin: Ajustes reorganizados con categor\u00edas",
+ "description": "Reorganizar Ajustes en subcategor\u00edas: General (nombre, tagline, contacto), Redes sociales, Footer (con support para {{year}} placeholder), Localizaciones (m\u00faltiples tiendas f\u00edsicas con direcci\u00f3n, horarios de apertura), Env\u00edos (zonas y tarifas ya hechas pero integrarlo aqu\u00ed).",
"status": "done",
"priority": "medium",
"phase": "admin",
@@ -2436,8 +2436,8 @@
},
{
"id": "ADM-42",
- "title": "Admin: CMS — páginas del footer editables",
- "description": "Agregar al CMS las páginas: About, Contacto, Shipping, Privacy, Terms, Cookies. Deben ser editables desde el panel CMS con WYSIWYG y previsualización.",
+ "title": "Admin: CMS \u2014 p\u00e1ginas del footer editables",
+ "description": "Agregar al CMS las p\u00e1ginas: About, Contacto, Shipping, Privacy, Terms, Cookies. Deben ser editables desde el panel CMS con WYSIWYG y previsualizaci\u00f3n.",
"status": "done",
"priority": "high",
"phase": "admin",
@@ -2453,7 +2453,7 @@
{
"id": "FE-03",
"title": "Storefront: Live search con preview",
- "description": "Reemplazar la búsqueda actual por un live search: al escribir (debounce 300ms) se muestranminiaturas + nombre de producto en un dropdown. Click navega a la ficha. Empty state y fallback a /search.",
+ "description": "Reemplazar la b\u00fasqueda actual por un live search: al escribir (debounce 300ms) se muestranminiaturas + nombre de producto en un dropdown. Click navega a la ficha. Empty state y fallback a /search.",
"status": "done",
"priority": "high",
"phase": "frontend",
@@ -2468,8 +2468,8 @@
},
{
"id": "FE-04",
- "title": "Storefront: Ficha de usuario — direcciones y facturación",
- "description": "Página /account o /profile donde el usuario puede gestionar sus direcciones de envío (crear/editar/eliminar) y datos de facturación.",
+ "title": "Storefront: Ficha de usuario \u2014 direcciones y facturaci\u00f3n",
+ "description": "P\u00e1gina /account o /profile donde el usuario puede gestionar sus direcciones de env\u00edo (crear/editar/eliminar) y datos de facturaci\u00f3n.",
"status": "done",
"priority": "medium",
"phase": "frontend",
@@ -2485,8 +2485,8 @@
},
{
"id": "FIX-09",
- "title": "Storefront: Checkout — seguir mostrando login aunque ya esté autenticado",
- "description": "Fix: cuando el usuario ya está logueado y llega a /checkout, no debe mostrar el formulario de login otra vez. Debe detectar la sesión y mostrar directamente el formulario de envío/pago.",
+ "title": "Storefront: Checkout \u2014 seguir mostrando login aunque ya est\u00e9 autenticado",
+ "description": "Fix: cuando el usuario ya est\u00e1 logueado y llega a /checkout, no debe mostrar el formulario de login otra vez. Debe detectar la sesi\u00f3n y mostrar directamente el formulario de env\u00edo/pago.",
"status": "done",
"priority": "critical",
"phase": "frontend",
@@ -2502,7 +2502,7 @@
{
"id": "FIX-10",
"title": "Storefront: Precio pegado al borde en tarjetas de producto",
- "description": "En las tarjetas de producto (grids de categorías, marcas, search) el precio se muestra muy pegado a la derecha. Agregar padding-right a .price o al contenedor.",
+ "description": "En las tarjetas de producto (grids de categor\u00edas, marcas, search) el precio se muestra muy pegado a la derecha. Agregar padding-right a .price o al contenedor.",
"status": "done",
"priority": "low",
"phase": "frontend",
@@ -2517,8 +2517,8 @@
},
{
"id": "FE-048",
- "title": "Búsqueda fuzzy con sugerencias tipográficas",
- "description": "Live search con fuzzy search que maneje errores tipográficos leves (fuzziness), sugerencias de corrección ortográfica y ranking mejorado para resultados parciales. Incluye: Levenshtein distance para typos, trigram similarity como fallback, y sugerencias de términos corregidos cuando no hay resultados.",
+ "title": "B\u00fasqueda fuzzy con sugerencias tipogr\u00e1ficas",
+ "description": "Live search con fuzzy search que maneje errores tipogr\u00e1ficos leves (fuzziness), sugerencias de correcci\u00f3n ortogr\u00e1fica y ranking mejorado para resultados parciales. Incluye: Levenshtein distance para typos, trigram similarity como fallback, y sugerencias de t\u00e9rminos corregidos cuando no hay resultados.",
"type": "feature",
"priority": "medium",
"status": "done",
@@ -2533,15 +2533,15 @@
},
{
"id": "FIX-11",
- "title": "Admin: quitar Envíos e IVA de Ajustes",
- "description": "En Ajustes (/settings) aparecen bloques de Envíos e IVA que ya existen como pantallas propias en el menú principal (/shipping y /tax-rates). Eliminar esas secciones duplicadas de Ajustes para tener una única fuente de verdad.",
+ "title": "Admin: quitar Env\u00edos e IVA de Ajustes",
+ "description": "En Ajustes (/settings) aparecen bloques de Env\u00edos e IVA que ya existen como pantallas propias en el men\u00fa principal (/shipping y /tax-rates). Eliminar esas secciones duplicadas de Ajustes para tener una \u00fanica fuente de verdad.",
"status": "done",
"priority": "medium",
"phase": "admin",
"type": "fix",
"created_at": "2026-08-18T20:03:10.712389+00:00",
"acceptance": [
- "/settings ya no muestra secciones de Envíos ni IVA",
+ "/settings ya no muestra secciones de Env\u00edos ni IVA",
"/shipping y /tax-rates siguen funcionando como pantallas propias",
"verify.sh en verde"
],
@@ -2554,8 +2554,8 @@
},
{
"id": "FIX-12",
- "title": "Admin: normalizar columna Acciones — sustituir texto por iconos (emoji)",
- "description": "Normalizar la columna de acciones del backoffice SUSTITUYENDO los botones de TEXTO ('Editar'/'Eliminar', como en brands y promotions) por botones-icono (emoji), replicando el patrón exacto de /categories y /users: botón con icono SVG, hover de color y atributo title ('Editar'/'Eliminar'). Aplicar a todas las tablas que aún usan texto.",
+ "title": "Admin: normalizar columna Acciones \u2014 sustituir texto por iconos (emoji)",
+ "description": "Normalizar la columna de acciones del backoffice SUSTITUYENDO los botones de TEXTO ('Editar'/'Eliminar', como en brands y promotions) por botones-icono (emoji), replicando el patr\u00f3n exacto de /categories y /users: bot\u00f3n con icono SVG, hover de color y atributo title ('Editar'/'Eliminar'). Aplicar a todas las tablas que a\u00fan usan texto.",
"status": "done",
"priority": "medium",
"phase": "admin",
@@ -2563,7 +2563,7 @@
"created_at": "2026-08-18T20:03:10.712389+00:00",
"acceptance": [
"Las tablas que usan botones de texto (brands, promotions, etc.) pasan a botones-icono",
- "Patrón idéntico al de /categories: icono SVG + hover + title",
+ "Patr\u00f3n id\u00e9ntico al de /categories: icono SVG + hover + title",
"Siempre acciones editar|eliminar donde aplique",
"Sin regresiones en las acciones existentes"
],
@@ -2576,8 +2576,8 @@
},
{
"id": "FIX-13",
- "title": "Admin: CMS — sustituir texto por iconos en publicar/despublicar y añadir edición",
- "description": "Páginas CMS: sustituir los botones de TEXTO 'Publicar'/'Despublicar' por botones-icono (emoji) siguiendo el patrón de /categories, y añadir una acción de EDITAR la página (actualmente inexistente) para modificar slug/título/cuerpo y guardar los cambios.",
+ "title": "Admin: CMS \u2014 sustituir texto por iconos en publicar/despublicar y a\u00f1adir edici\u00f3n",
+ "description": "P\u00e1ginas CMS: sustituir los botones de TEXTO 'Publicar'/'Despublicar' por botones-icono (emoji) siguiendo el patr\u00f3n de /categories, y a\u00f1adir una acci\u00f3n de EDITAR la p\u00e1gina (actualmente inexistente) para modificar slug/t\u00edtulo/cuerpo y guardar los cambios.",
"status": "done",
"priority": "medium",
"phase": "cms",
@@ -2585,7 +2585,7 @@
"created_at": "2026-08-18T20:03:10.712389+00:00",
"acceptance": [
"'Publicar'/'Despublicar' dejan de ser texto y pasan a botones-icono (emoji)",
- "Nueva acción para editar una página CMS existente y guardar cambios",
+ "Nueva acci\u00f3n para editar una p\u00e1gina CMS existente y guardar cambios",
"Los cambios persisten tras recargar"
],
"gates": {
@@ -2597,18 +2597,18 @@
},
{
"id": "FIX-14",
- "title": "Backoffice: separación física — tabla backoffice_users + auth propia",
- "description": "Los usuarios del backoffice y los clientes del storefront NO comparten tabla. Crear tabla física backoffice_users (los clientes storefront siguen en identity_users) con mecanismo de autenticación/sesión PROPIO y reforzado (pensar en seguridad: sesión/cookie independiente de la del storefront, hashing, rate-limit). Migrar los usuarios admin/editor existentes desde identity_users a backoffice_users.",
+ "title": "Backoffice: separaci\u00f3n f\u00edsica \u2014 tabla backoffice_users + auth propia",
+ "description": "Los usuarios del backoffice y los clientes del storefront NO comparten tabla. Crear tabla f\u00edsica backoffice_users (los clientes storefront siguen en identity_users) con mecanismo de autenticaci\u00f3n/sesi\u00f3n PROPIO y reforzado (pensar en seguridad: sesi\u00f3n/cookie independiente de la del storefront, hashing, rate-limit). Migrar los usuarios admin/editor existentes desde identity_users a backoffice_users.",
"status": "done",
"priority": "high",
"phase": "admin",
"type": "fix",
"created_at": "2026-08-18T20:03:10.712389+00:00",
"acceptance": [
- "Nueva tabla física backoffice_users; clientes storefront permanecen en identity_users",
- "Autenticación/sesión del backoffice con mecanismo propio y seguro (independiente de mdv_session del storefront)",
+ "Nueva tabla f\u00edsica backoffice_users; clientes storefront permanecen en identity_users",
+ "Autenticaci\u00f3n/sesi\u00f3n del backoffice con mecanismo propio y seguro (independiente de mdv_session del storefront)",
"Clientes del storefront no aparecen en el backoffice y viceversa",
- "Migración de usuarios admin/editor existentes a backoffice_users",
+ "Migraci\u00f3n de usuarios admin/editor existentes a backoffice_users",
"verify.sh y gates de seguridad en verde"
],
"gates": {
@@ -2641,7 +2641,7 @@
},
{
"id": "FIX-16",
- "title": "Admin: editor de productos — checkboxes de atributos seleccionables",
+ "title": "Admin: editor de productos \u2014 checkboxes de atributos seleccionables",
"description": "En el editor de productos, los checkboxes de atributos (Bio, Vegano, Sin Gluten, etc.) no se pueden marcar/seleccionar. Hacer que sean interactivos y que su estado se guarde correctamente.",
"status": "done",
"priority": "high",
@@ -2663,7 +2663,7 @@
{
"id": "FIX-17",
"title": "Admin: precios con 2 decimales (coma o punto), display 12,00 / 12,30",
- "description": "El precio se introduce y muestra con dos decimales, aceptando coma o punto indistintamente. Lo que escribe el usuario se muestra con 2 decimales: escribe 12 → muestra 12,00; escribe 12,30 → muestra 12,30. En storefront con símbolo € (12,50 €); en backoffice queda mejor con € también. Internamente guardar correctamente (céntimos).",
+ "description": "El precio se introduce y muestra con dos decimales, aceptando coma o punto indistintamente. Lo que escribe el usuario se muestra con 2 decimales: escribe 12 \u2192 muestra 12,00; escribe 12,30 \u2192 muestra 12,30. En storefront con s\u00edmbolo \u20ac (12,50 \u20ac); en backoffice queda mejor con \u20ac tambi\u00e9n. Internamente guardar correctamente (c\u00e9ntimos).",
"status": "done",
"priority": "high",
"phase": "products",
@@ -2671,10 +2671,10 @@
"created_at": "2026-08-18T20:03:10.712389+00:00",
"acceptance": [
"El input acepta 12,50 o 12.50",
- "Se muestra siempre con 2 decimales (12 → 12,00; 12,30 → 12,30)",
- "Storefront muestra '12,50 €'",
- "Backoffice muestra con 2 decimales (idealmente con €)",
- "Guardado correcto (céntimos internamente)"
+ "Se muestra siempre con 2 decimales (12 \u2192 12,00; 12,30 \u2192 12,30)",
+ "Storefront muestra '12,50 \u20ac'",
+ "Backoffice muestra con 2 decimales (idealmente con \u20ac)",
+ "Guardado correcto (c\u00e9ntimos internamente)"
],
"gates": {
"reviewer": true,
@@ -2685,15 +2685,15 @@
},
{
"id": "FIX-18",
- "title": "Admin: clientes — editar datos personales y direcciones (NO credenciales)",
- "description": "En la ficha de cliente se pueden editar los datos personales (nombre, teléfono) y las direcciones (crear/editar/eliminar). Los datos de login (email/contraseña) NO deben poder modificarse desde aquí; eso queda para un futuro 'reset password' cuando se implemente el soporte de cuenta.",
+ "title": "Admin: clientes \u2014 editar datos personales y direcciones (NO credenciales)",
+ "description": "En la ficha de cliente se pueden editar los datos personales (nombre, tel\u00e9fono) y las direcciones (crear/editar/eliminar). Los datos de login (email/contrase\u00f1a) NO deben poder modificarse desde aqu\u00ed; eso queda para un futuro 'reset password' cuando se implemente el soporte de cuenta.",
"status": "done",
"priority": "high",
"phase": "customers",
"type": "fix",
"created_at": "2026-08-18T20:03:10.712389+00:00",
"acceptance": [
- "Editar datos personales no sensibles (nombre, teléfono)",
+ "Editar datos personales no sensibles (nombre, tel\u00e9fono)",
"CRUD completo de direcciones del cliente",
"El email/credenciales de login NO son editables desde la ficha",
"Los cambios persisten tras recargar"
@@ -2707,20 +2707,20 @@
},
{
"id": "FIX-19",
- "title": "Admin: categorías — seleccionar padre + tipo parent/child (child es hoja)",
- "description": "En el formulario de categoría: (1) seleccionar una categoría padre para anidar, y (2) marcar categorías como 'parent' (flag booleano). Reglas de orden lógico: una PARENT es contenedor y puede tener CHILDS u otras PARENTS; una CHILD es hoja y NO puede tener hijos (si tuviera hijos sería parent). Migración: las categorías que hoy tienen hijos pasan a parent; el resto a child.",
+ "title": "Admin: categor\u00edas \u2014 seleccionar padre + tipo parent/child (child es hoja)",
+ "description": "En el formulario de categor\u00eda: (1) seleccionar una categor\u00eda padre para anidar, y (2) marcar categor\u00edas como 'parent' (flag booleano). Reglas de orden l\u00f3gico: una PARENT es contenedor y puede tener CHILDS u otras PARENTS; una CHILD es hoja y NO puede tener hijos (si tuviera hijos ser\u00eda parent). Migraci\u00f3n: las categor\u00edas que hoy tienen hijos pasan a parent; el resto a child.",
"status": "done",
"priority": "high",
"phase": "categories",
"type": "fix",
"created_at": "2026-08-18T20:03:10.712389+00:00",
"acceptance": [
- "Selector de categoría padre en crear/editar",
- "Flag booleano para marcar una categoría como 'parent' (contenedor)",
- "Una CHILD es hoja: no puede contener hijos (validación)",
+ "Selector de categor\u00eda padre en crear/editar",
+ "Flag booleano para marcar una categor\u00eda como 'parent' (contenedor)",
+ "Una CHILD es hoja: no puede contener hijos (validaci\u00f3n)",
"Una PARENT puede contener CHILDS u otras PARENTS",
- "Migración: categorías con hijos → parent; resto → child",
- "El árbol de categorías se actualiza correctamente"
+ "Migraci\u00f3n: categor\u00edas con hijos \u2192 parent; resto \u2192 child",
+ "El \u00e1rbol de categor\u00edas se actualiza correctamente"
],
"gates": {
"reviewer": true,
@@ -2862,12 +2862,12 @@
},
{
"id": "F-052",
- "title": "Imágenes de producto 404 en frontend y storefront",
+ "title": "Im\u00e1genes de producto 404 en frontend y storefront",
"status": "done",
"stage": null,
"type": "fix",
"priority": "high",
- "description": "Las imágenes de producto (URLs /uploads/...) devuelven 404 en el frontend (3003) y storefront (3005) porque los archivos están en public/uploads/ del admin (3004) y el backend no sirve archivos estáticos de uploads. Solución: copiar uploads a frontend/storefront public/, o añadir una ruta /uploads/ en el backend que sirva desde el dir del admin.",
+ "description": "Las im\u00e1genes de producto (URLs /uploads/...) devuelven 404 en el frontend (3003) y storefront (3005) porque los archivos est\u00e1n en public/uploads/ del admin (3004) y el backend no sirve archivos est\u00e1ticos de uploads. Soluci\u00f3n: copiar uploads a frontend/storefront public/, o a\u00f1adir una ruta /uploads/ en el backend que sirva desde el dir del admin.",
"created_at": "2026-08-19T08:23:15Z",
"updated_at": "2026-08-19T08:23:15Z",
"assignee": null,
@@ -2887,7 +2887,7 @@
"stage": null,
"type": "fix",
"priority": "low",
- "description": "En la página de edición de categorías del admin, el grid de 2 columnas (select de padre + checkbox promote) tiene el segundo item (flex con checkbox) desalineado verticalmente y sin ocupar todo el ancho. Necesita ajustarse para que ambos items estén alineados.",
+ "description": "En la p\u00e1gina de edici\u00f3n de categor\u00edas del admin, el grid de 2 columnas (select de padre + checkbox promote) tiene el segundo item (flex con checkbox) desalineado verticalmente y sin ocupar todo el ancho. Necesita ajustarse para que ambos items est\u00e9n alineados.",
"created_at": "2026-08-19T08:23:15Z",
"updated_at": "2026-08-19T08:23:15Z",
"assignee": null,
@@ -2897,7 +2897,7 @@
"qa": true,
"close": true
},
- "acceptance": "1. El select de categoría padre y el checkbox de promote están alineados verticalmente y ambos ocupan todo el ancho disponible.",
+ "acceptance": "1. El select de categor\u00eda padre y el checkbox de promote est\u00e1n alineados verticalmente y ambos ocupan todo el ancho disponible.",
"completed_at": "2026-08-19T08:48:35Z"
},
{
@@ -2907,7 +2907,7 @@
"stage": null,
"type": "fix",
"priority": "high",
- "description": "El backend (dist/infrastructure/http/server.js) muere sin dejar trazas en el log y deja a admin/frontend sin API. Síntoma: /api/auth/login devuelve 502 (admin proxy) o 500 (frontend route handler) cuando el backend no responde. Causa raíz desconocida (no hay error en el log). Mejoras: (1) investigar por qué muere; (2) hacer que monolith.sh detecte y reinicie el backend si muere; (3) usar process.env.NEXT_PUBLIC_API_URL en frontend/src/app/api/auth/login/route.ts en lugar de hardcodear http://127.0.0.1:3000.",
+ "description": "El backend (dist/infrastructure/http/server.js) muere sin dejar trazas en el log y deja a admin/frontend sin API. S\u00edntoma: /api/auth/login devuelve 502 (admin proxy) o 500 (frontend route handler) cuando el backend no responde. Causa ra\u00edz desconocida (no hay error en el log). Mejoras: (1) investigar por qu\u00e9 muere; (2) hacer que monolith.sh detecte y reinicie el backend si muere; (3) usar process.env.NEXT_PUBLIC_API_URL en frontend/src/app/api/auth/login/route.ts en lugar de hardcodear http://127.0.0.1:3000.",
"created_at": "2026-08-19T08:52:44Z",
"updated_at": "2026-08-19T08:52:44Z",
"assignee": null,
@@ -2917,7 +2917,7 @@
"qa": true,
"close": true
},
- "acceptance": "1. Backend se mantiene estable bajo carga normal (no muere sin causa aparente). 2. Si el backend muere, monolith.sh lo reinicia automáticamente. 3. /api/auth/login en 3003 usa NEXT_PUBLIC_API_URL configurable. 4. /api/auth/login responde códigos del backend (no 500/502) cuando el backend está vivo.",
+ "acceptance": "1. Backend se mantiene estable bajo carga normal (no muere sin causa aparente). 2. Si el backend muere, monolith.sh lo reinicia autom\u00e1ticamente. 3. /api/auth/login en 3003 usa NEXT_PUBLIC_API_URL configurable. 4. /api/auth/login responde c\u00f3digos del backend (no 500/502) cuando el backend est\u00e1 vivo.",
"completed_at": "2026-08-19T11:15:30Z"
},
{
@@ -4113,7 +4113,7 @@
"description": "Problem: The admin product listing receives products without brand and expirationDate because catalog serialization omits those fields. In the product editor inventory section, SKU and EAN saves update row state but the display renders from the original variants array, so edited values appear not to persist.. Goal: Include brand and expirationDate in catalog product serialization and render saved SKU/EAN values from the updated row state so edits remain visible after the PATCH succeeds.. Scope IN: backend catalog serialization, admin product listing, admin product inventory section. Scope OUT: No database schema change, no new fields, no API redesign. Type: fix. Priority: high. Risk: low.",
"acceptance": [
"- Product list response includes brand and expirationDate when present",
- "- Product listing displays the brand name and expiration date instead of — for populated products",
+ "- Product listing displays the brand name and expiration date instead of \u2014 for populated products",
"- Successful SKU edit remains visible after leaving edit mode",
"- Successful EAN edit remains visible after leaving edit mode",
"- Existing PATCH /products/:id/variants/:variantId behavior remains used",
@@ -4984,8 +4984,8 @@
"id": "F-116",
"type": "feature",
"title": "Translate legacy OpenCart categories to Spanish (Title Case)",
- "problem": "F-114 imported 39 categories with English names like HERBALIST, NUTS & SEEDS, BREAD & PASTRIES — operators want Spanish translations with first letter of each word capitalized to match the rest of the catalog",
- "goal": "Translate every legacy category name to Spanish using Title Case (Primera Letra en Mayúsculas), preserving the diacritics, and replace the existing entries in bulk",
+ "problem": "F-114 imported 39 categories with English names like HERBALIST, NUTS & SEEDS, BREAD & PASTRIES \u2014 operators want Spanish translations with first letter of each word capitalized to match the rest of the catalog",
+ "goal": "Translate every legacy category name to Spanish using Title Case (Primera Letra en May\u00fasculas), preserving the diacritics, and replace the existing entries in bulk",
"scope_in": [
"categories module",
"legacy catalog helper",
@@ -4998,7 +4998,7 @@
],
"priority": "med",
"risk": "low",
- "description": "Problem: F-114 imported 39 categories with English names like HERBALIST, NUTS & SEEDS, BREAD & PASTRIES — operators want Spanish translations with first letter of each word capitalized to match the rest of the catalog. Goal: Translate every legacy category name to Spanish using Title Case (Primera Letra en Mayúsculas), preserving the diacritics, and replace the existing entries in bulk. Scope IN: categories module, legacy catalog helper, seed script update, idempotent update. Scope OUT: no new categories, no brand renames. Type: feature. Priority: med. Risk: low.",
+ "description": "Problem: F-114 imported 39 categories with English names like HERBALIST, NUTS & SEEDS, BREAD & PASTRIES \u2014 operators want Spanish translations with first letter of each word capitalized to match the rest of the catalog. Goal: Translate every legacy category name to Spanish using Title Case (Primera Letra en May\u00fasculas), preserving the diacritics, and replace the existing entries in bulk. Scope IN: categories module, legacy catalog helper, seed script update, idempotent update. Scope OUT: no new categories, no brand renames. Type: feature. Priority: med. Risk: low.",
"acceptance": [
"Translation table covers every legacy category name",
"Names are translated to Spanish in Title Case",
@@ -5019,9 +5019,9 @@
{
"id": "F-117",
"type": "fix",
- "title": "Title Case a las categorías ya en español (F-116 oversight)",
- "problem": "F-116 renombró 30 categorías en español pero dejó en mayúsculas FRUTAS Y VERDURAS, SNACKS y GRANOLA que ya estaban en español en el legacy",
- "goal": "Forzar Title Case en las categorías que ya estaban en español: FRUTAS Y VERDURAS, SNACKS, GRANOLA",
+ "title": "Title Case a las categor\u00edas ya en espa\u00f1ol (F-116 oversight)",
+ "problem": "F-116 renombr\u00f3 30 categor\u00edas en espa\u00f1ol pero dej\u00f3 en may\u00fasculas FRUTAS Y VERDURAS, SNACKS y GRANOLA que ya estaban en espa\u00f1ol en el legacy",
+ "goal": "Forzar Title Case en las categor\u00edas que ya estaban en espa\u00f1ol: FRUTAS Y VERDURAS, SNACKS, GRANOLA",
"scope_in": [
"categories module",
"translation table",
@@ -5032,11 +5032,11 @@
],
"priority": "low",
"risk": "low",
- "description": "Problem: F-116 renombró 30 categorías en español pero dejó en mayúsculas FRUTAS Y VERDURAS, SNACKS y GRANOLA que ya estaban en español en el legacy. Goal: Forzar Title Case en las categorías que ya estaban en español: FRUTAS Y VERDURAS, SNACKS, GRANOLA. Scope IN: categories module, translation table, seed script. Scope OUT: no nuevas renombraciones fuera del alcance. Type: fix. Priority: low. Risk: low.",
+ "description": "Problem: F-116 renombr\u00f3 30 categor\u00edas en espa\u00f1ol pero dej\u00f3 en may\u00fasculas FRUTAS Y VERDURAS, SNACKS y GRANOLA que ya estaban en espa\u00f1ol en el legacy. Goal: Forzar Title Case en las categor\u00edas que ya estaban en espa\u00f1ol: FRUTAS Y VERDURAS, SNACKS, GRANOLA. Scope IN: categories module, translation table, seed script. Scope OUT: no nuevas renombraciones fuera del alcance. Type: fix. Priority: low. Risk: low.",
"acceptance": [
- "FRUTAS Y VERDURAS → Frutas y Verduras",
- "SNACKS → Snacks",
- "GRANOLA → Granola",
+ "FRUTAS Y VERDURAS \u2192 Frutas y Verduras",
+ "SNACKS \u2192 Snacks",
+ "GRANOLA \u2192 Granola",
"Re-running the seed is idempotent",
"Typecheck, tests, verify pass"
],
@@ -5068,10 +5068,10 @@
"risk": "low",
"description": "Problem: The 5 root categories (Alimentacion, Cosmetica e Higiene, Hogar y Mascotas, Limpieza Ecologica, Suplementos) lack Spanish diacritics; the user wants proper Spanish Title Case with accents. Goal: Update the 5 root categories to use proper Spanish accents. Scope IN: categories module, seed update. Scope OUT: no new categories, no brand renames. Type: fix. Priority: low. Risk: low.",
"acceptance": [
- "Alimentacion → Alimentación",
- "Cosmetica e Higiene → Cosmética e Higiene",
+ "Alimentacion \u2192 Alimentaci\u00f3n",
+ "Cosmetica e Higiene \u2192 Cosm\u00e9tica e Higiene",
"Hogar y Mascotas stays as-is (no accent needed)",
- "Limpieza Ecologica → Limpieza Ecológica",
+ "Limpieza Ecologica \u2192 Limpieza Ecol\u00f3gica",
"Proveedores stays as-is",
"Suplementos stays as-is",
"Idempotent (DB has unique slug; renaming in place is safe)"
@@ -5191,7 +5191,7 @@
"id": "F-122",
"type": "fix",
"title": "Remove redundant variant warning when product has only one variant",
- "problem": "PriceStockSection shows \"⚠️ Este producto tiene X variante(s) heredada(s). Se está editando la principal.\" when extraVariants > 0. The user wants this warning gone because each product has at most one variant (the legacy migrations were for when the model supported multiple variants, but the simplified model has only one). The warning is noise.",
+ "problem": "PriceStockSection shows \"\u26a0\ufe0f Este producto tiene X variante(s) heredada(s). Se est\u00e1 editando la principal.\" when extraVariants > 0. The user wants this warning gone because each product has at most one variant (the legacy migrations were for when the model supported multiple variants, but the simplified model has only one). The warning is noise.",
"goal": "Drop the warning and the legacy extraVariants state. Confirm the underlying API does not return extra variants.",
"scope_in": [
"PriceStockSection",
@@ -5202,7 +5202,7 @@
],
"priority": "low",
"risk": "low",
- "description": "Problem: PriceStockSection shows \"⚠️ Este producto tiene X variante(s) heredada(s). Se está editando la principal.\" when extraVariants > 0. The user wants this warning gone because each product has at most one variant (the legacy migrations were for when the model supported multiple variants, but the simplified model has only one). The warning is noise.. Goal: Drop the warning and the legacy extraVariants state. Confirm the underlying API does not return extra variants.. Scope IN: PriceStockSection, useEffect cleanup. Scope OUT: no behavior changes. Type: fix. Priority: low. Risk: low.",
+ "description": "Problem: PriceStockSection shows \"\u26a0\ufe0f Este producto tiene X variante(s) heredada(s). Se est\u00e1 editando la principal.\" when extraVariants > 0. The user wants this warning gone because each product has at most one variant (the legacy migrations were for when the model supported multiple variants, but the simplified model has only one). The warning is noise.. Goal: Drop the warning and the legacy extraVariants state. Confirm the underlying API does not return extra variants.. Scope IN: PriceStockSection, useEffect cleanup. Scope OUT: no behavior changes. Type: fix. Priority: low. Risk: low.",
"acceptance": [
"PriceStockSection no longer shows the legacy warning",
"extraVariants state removed",
@@ -5223,7 +5223,7 @@
"id": "F-123",
"type": "fix",
"title": "Remove 'Save product first' warning for single-variant products",
- "description": "F-121 dejó en ProductEditor.tsx los textos 'Guarda primero el producto para configurar precio, stock y EAN.' (línea 286) y 'Guarda primero el producto para subir imágenes.' (línea 374). El bundle servido en :3004 sigue mostrando la restricción cuando productId es undefined (modo creación). Como todos los productos son single-variant, la restricción no aporta valor. Fix: eliminar los avisos y permitir que PriceStockSection e ImagesSection rendericen siempre, con los botones de guardado deshabilitados y un caption 'Se guardará al crear el producto' hasta que exista productId.",
+ "description": "F-121 dej\u00f3 en ProductEditor.tsx los textos 'Guarda primero el producto para configurar precio, stock y EAN.' (l\u00ednea 286) y 'Guarda primero el producto para subir im\u00e1genes.' (l\u00ednea 374). El bundle servido en :3004 sigue mostrando la restricci\u00f3n cuando productId es undefined (modo creaci\u00f3n). Como todos los productos son single-variant, la restricci\u00f3n no aporta valor. Fix: eliminar los avisos y permitir que PriceStockSection e ImagesSection rendericen siempre, con los botones de guardado deshabilitados y un caption 'Se guardar\u00e1 al crear el producto' hasta que exista productId.",
"priority": "high",
"risk": "low",
"status": "done",
@@ -5239,8 +5239,8 @@
{
"id": "F-124",
"type": "fix",
- "title": "Two-column layout for Categorías + Atributos in product editor",
- "description": "En /products/[id] y /products/new, los bloques Categorías y Atributos se renderizan apilados ocupando todo el ancho. Como ambos son cortos (listas/checkbox grid), deberían ir lado a lado en desktop (grid-cols-2). Móvil sigue apilado.",
+ "title": "Two-column layout for Categor\u00edas + Atributos in product editor",
+ "description": "En /products/[id] y /products/new, los bloques Categor\u00edas y Atributos se renderizan apilados ocupando todo el ancho. Como ambos son cortos (listas/checkbox grid), deber\u00edan ir lado a lado en desktop (grid-cols-2). M\u00f3vil sigue apilado.",
"priority": "med",
"risk": "low",
"status": "done",
@@ -5256,8 +5256,8 @@
{
"id": "F-125",
"type": "feature",
- "title": "Allow backward status transitions in /orders/[id] + resend email on SHIPPED↔PROCESSING",
- "description": "En /orders/[id] editar pedido, los status no permiten volver atrás (PROCESSING→PENDING, SHIPPED→PROCESSING, etc.). El operador necesita: (1) poder revertir un pedido a un estado anterior si se equivocó; (2) cuando un pedido cambia entre PROCESSING y SHIPPED (en cualquier dirección), reenviar el email al cliente con el transportista/tracking actualizado (no solo en la transición inicial a SHIPPED).",
+ "title": "Allow backward status transitions in /orders/[id] + resend email on SHIPPED\u2194PROCESSING",
+ "description": "En /orders/[id] editar pedido, los status no permiten volver atr\u00e1s (PROCESSING\u2192PENDING, SHIPPED\u2192PROCESSING, etc.). El operador necesita: (1) poder revertir un pedido a un estado anterior si se equivoc\u00f3; (2) cuando un pedido cambia entre PROCESSING y SHIPPED (en cualquier direcci\u00f3n), reenviar el email al cliente con el transportista/tracking actualizado (no solo en la transici\u00f3n inicial a SHIPPED).",
"priority": "high",
"risk": "low",
"status": "done",
@@ -5274,7 +5274,7 @@
"id": "F-126",
"type": "bug",
"title": "Inventory search by partial EAN or product name",
- "description": "El buscador en /inventory no filtra correctamente al escribir parte o todo un EAN o nombre de producto. F-093 añadió búsqueda por nombre/EAN pero el operador reporta que sigue sin funcionar bien. Revisar el endpoint /inventory, el frontend /inventory page y confirmar que el query param q se mapea a un ILIKE correcto sobre el nombre del producto y el EAN de la variante.",
+ "description": "El buscador en /inventory no filtra correctamente al escribir parte o todo un EAN o nombre de producto. F-093 a\u00f1adi\u00f3 b\u00fasqueda por nombre/EAN pero el operador reporta que sigue sin funcionar bien. Revisar el endpoint /inventory, el frontend /inventory page y confirmar que el query param q se mapea a un ILIKE correcto sobre el nombre del producto y el EAN de la variante.",
"priority": "high",
"risk": "low",
"status": "done",
@@ -5291,7 +5291,7 @@
"id": "F-127",
"type": "bug",
"title": "Delete brands or categories still fails in admin UI",
- "description": "Tras F-120 (DELETE /categories/:id cascade en backend, migración 041) y F-022 (DELETE /brands/:id), el operador sigue reportando que no puede eliminar marcas o categorías. El backend está OK (curl DELETE funciona) pero la UI puede estar mostrando el error antiguo por caché del navegador, o el código frontend hace un check previo (hasChildren, hasProducts) que devuelve 409 y bloquea el botón.",
+ "description": "Tras F-120 (DELETE /categories/:id cascade en backend, migraci\u00f3n 041) y F-022 (DELETE /brands/:id), el operador sigue reportando que no puede eliminar marcas o categor\u00edas. El backend est\u00e1 OK (curl DELETE funciona) pero la UI puede estar mostrando el error antiguo por cach\u00e9 del navegador, o el c\u00f3digo frontend hace un check previo (hasChildren, hasProducts) que devuelve 409 y bloquea el bot\u00f3n.",
"priority": "high",
"risk": "low",
"status": "done",
@@ -5308,7 +5308,7 @@
"id": "F-128",
"type": "fix",
"title": "CMS page templates load with current content for in-place modification",
- "description": "En /cms, las plantillas del sistema (footer, política de privacidad, etc.) ya tienen contenido por defecto. Al pulsar Editar sobre una plantilla, se debería cargar el contenido actual (no vacío) en el editor Lexical para poder modificarlo, no partir de un textarea en blanco.",
+ "description": "En /cms, las plantillas del sistema (footer, pol\u00edtica de privacidad, etc.) ya tienen contenido por defecto. Al pulsar Editar sobre una plantilla, se deber\u00eda cargar el contenido actual (no vac\u00edo) en el editor Lexical para poder modificarlo, no partir de un textarea en blanco.",
"priority": "high",
"risk": "low",
"status": "done",
@@ -5325,7 +5325,7 @@
"id": "F-129",
"type": "fix",
"title": "Logs viewer: order DESC and remove autoscroll",
- "description": "En el visor de logs (/logs), los eventos deben ordenarse DESC (más reciente arriba). El autoscroll debe estar desactivado para que el usuario vea siempre el último evento en la parte superior sin necesidad de scroll. El polling puede seguir actualizando en vivo.",
+ "description": "En el visor de logs (/logs), los eventos deben ordenarse DESC (m\u00e1s reciente arriba). El autoscroll debe estar desactivado para que el usuario vea siempre el \u00faltimo evento en la parte superior sin necesidad de scroll. El polling puede seguir actualizando en vivo.",
"priority": "low",
"risk": "low",
"status": "done",
@@ -5359,7 +5359,7 @@
"id": "F-131",
"type": "bug",
"title": "Products list search in admin does not filter",
- "description": "El buscador de /products (admin panel) no filtra los productos al escribir. Revisar el input de búsqueda en la lista, el endpoint backend y los query params enviados. Debe buscar por nombre y slug, idealmente con ILIKE case-insensitive.",
+ "description": "El buscador de /products (admin panel) no filtra los productos al escribir. Revisar el input de b\u00fasqueda en la lista, el endpoint backend y los query params enviados. Debe buscar por nombre y slug, idealmente con ILIKE case-insensitive.",
"priority": "high",
"risk": "low",
"status": "done",
@@ -5376,7 +5376,7 @@
"id": "F-132",
"type": "bug",
"title": "Frontend home DYNAMIC_SERVER_USAGE: FeaturedProducts and CategoriesGrid use cache: 'no-store'",
- "description": "Build del frontend falla con DYNAMIC_SERVER_USAGE en / porque (1) FeaturedProducts hace fetch con cache: 'no-store' a /products/search?limit=8, y (2) CategoriesGrid hace fetch con cache: 'no-store' a /categories/tree. El home estaba intentándose prerenderizar estáticamente. Fix: usar ISR con revalidate=60 en el home page, o quitar cache: 'no-store' de los dos componentes para que la página pueda ser SSG/ISR. Adicionalmente hay 4 warnings de turbopack sobre filesystem access dinámico en frontend/src/app/uploads/[...path]/route.ts (path.join(root, filename)). Añadir turbopackIgnore comments para silenciarlos.",
+ "description": "Build del frontend falla con DYNAMIC_SERVER_USAGE en / porque (1) FeaturedProducts hace fetch con cache: 'no-store' a /products/search?limit=8, y (2) CategoriesGrid hace fetch con cache: 'no-store' a /categories/tree. El home estaba intent\u00e1ndose prerenderizar est\u00e1ticamente. Fix: usar ISR con revalidate=60 en el home page, o quitar cache: 'no-store' de los dos componentes para que la p\u00e1gina pueda ser SSG/ISR. Adicionalmente hay 4 warnings de turbopack sobre filesystem access din\u00e1mico en frontend/src/app/uploads/[...path]/route.ts (path.join(root, filename)). A\u00f1adir turbopackIgnore comments para silenciarlos.",
"priority": "high",
"risk": "low",
"status": "done",
@@ -5393,7 +5393,7 @@
"id": "F-133",
"type": "fix",
"title": "Inline custom weight input inside the Peso unitario dropdown",
- "description": "En apps/admin/src/features/products/components/sections/PriceStockSection.tsx, el campo Peso unitario (Gr) tiene un