diff --git a/backlog/features.json b/backlog/features.json index dcce1a9..ec16140 100644 --- a/backlog/features.json +++ b/backlog/features.json @@ -438,7 +438,12 @@ "priority": "low", "phase": "backend", "created_at": "2026-08-16T22:14:05Z", - "gates": {}, + "gates": { + "reviewer": true, + "security": true, + "qa": true, + "close": true + }, "type": "feature", "completed_at": "2026-08-18T04:23:37Z" }, @@ -1578,13 +1583,15 @@ "A checkout flow produces a connected trace across modules", "verify.sh green" ], - "status": "pending", + "status": "done", "created_at": "2026-08-14", "gates": { "security": true, "qa": true, - "reviewer": true - } + "reviewer": true, + "close": true + }, + "completed_at": "2026-08-15T19:34:14Z" }, { "id": "F-030", @@ -1614,13 +1621,15 @@ "Given payment webhook simulated in E2E Then order reaches PAID", "verify.sh green" ], - "status": "pending", + "status": "done", "created_at": "2026-08-14", "gates": { "security": true, "qa": true, - "reviewer": true - } + "reviewer": true, + "close": true + }, + "completed_at": "2026-08-15T19:34:14Z" }, { "id": "F-031", @@ -2521,6 +2530,238 @@ "security": true, "qa": true } + }, + { + "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.", + "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", + "/shipping y /tax-rates siguen funcionando como pantallas propias", + "verify.sh en verde" + ], + "gates": { + "reviewer": true, + "security": true, + "qa": true + }, + "completed_at": "2026-08-18T20:58:24.951733+00:00" + }, + { + "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.", + "status": "done", + "priority": "medium", + "phase": "admin", + "type": "fix", + "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", + "Siempre acciones editar|eliminar donde aplique", + "Sin regresiones en las acciones existentes" + ], + "gates": { + "reviewer": true, + "security": true, + "qa": true + }, + "completed_at": "2026-08-18T20:58:24.951733+00:00" + }, + { + "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.", + "status": "done", + "priority": "medium", + "phase": "cms", + "type": "fix", + "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", + "Los cambios persisten tras recargar" + ], + "gates": { + "reviewer": true, + "security": true, + "qa": true + }, + "completed_at": "2026-08-18T20:58:24.951733+00:00" + }, + { + "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.", + "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)", + "Clientes del storefront no aparecen en el backoffice y viceversa", + "Migración de usuarios admin/editor existentes a backoffice_users", + "verify.sh y gates de seguridad en verde" + ], + "gates": { + "reviewer": true, + "security": true, + "qa": true + }, + "completed_at": "2026-08-18T20:58:24.951733+00:00" + }, + { + "id": "FIX-15", + "title": "Admin: vistas usan el ancho disponible (max-width ~1280px centrado)", + "description": "Las pantallas del backoffice quedan demasiado estrechas. Ajustar el contenedor global para que aproveche el ancho disponible con un max-width de una pantalla normal (~1280px), centrado. Aplica a TODAS las vistas.", + "status": "done", + "priority": "medium", + "phase": "admin", + "type": "fix", + "created_at": "2026-08-18T20:03:10.712389+00:00", + "acceptance": [ + "Contenedor global con max-width ~1280px centrado", + "Todas las vistas aprovechan el ancho hasta ese tope", + "El layout responsive no se rompe" + ], + "gates": { + "reviewer": true, + "security": true, + "qa": true + }, + "completed_at": "2026-08-18T20:58:24.951733+00:00" + }, + { + "id": "FIX-16", + "title": "Admin: editor de productos — 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", + "phase": "products", + "type": "fix", + "created_at": "2026-08-18T20:03:10.712389+00:00", + "acceptance": [ + "Los checkboxes de atributos se pueden marcar y desmarcar", + "El estado persiste al guardar", + "Los atributos se reflejan al recargar el producto" + ], + "gates": { + "reviewer": true, + "security": true, + "qa": true + }, + "completed_at": "2026-08-18T20:58:24.951733+00:00" + }, + { + "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).", + "status": "done", + "priority": "high", + "phase": "products", + "type": "fix", + "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)" + ], + "gates": { + "reviewer": true, + "security": true, + "qa": true + }, + "completed_at": "2026-08-18T20:58:24.951733+00:00" + }, + { + "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.", + "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)", + "CRUD completo de direcciones del cliente", + "El email/credenciales de login NO son editables desde la ficha", + "Los cambios persisten tras recargar" + ], + "gates": { + "reviewer": true, + "security": true, + "qa": true + }, + "completed_at": "2026-08-18T20:58:24.951733+00:00" + }, + { + "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.", + "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)", + "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" + ], + "gates": { + "reviewer": true, + "security": true, + "qa": true + }, + "completed_at": "2026-08-18T20:58:24.951733+00:00" + }, + { + "id": "F-048", + "type": "fix", + "title": "Complete migration and consolidate approved work", + "problem": "Migration 024 and approved local changes remain uncommitted with stale orchestration state", + "goal": "Validate migrations and product suites, reconcile state, and commit approved work", + "scope_in": [ + "Migration 024", + "product validation", + "orchestration state", + "approved FIX-11 through FIX-19 changes" + ], + "scope_out": [ + "No new product scope" + ], + "priority": "high", + "risk": "med", + "description": "Problem: Migration 024 and approved local changes remain uncommitted with stale orchestration state. Goal: Validate migrations and product suites, reconcile state, and commit approved work. Scope IN: Migration 024, product validation, orchestration state, approved FIX-11 through FIX-19 changes. Scope OUT: No new product scope. Type: fix. Priority: high. Risk: med.", + "acceptance": [ + "Migration 024 uses the node-pg-migrate contract and passes migration checks", + "Backend and all frontend build, typecheck, lint and tests are green", + "Orchestration state matches the active ticket", + "Approved local work is committed through close_feature.py", + "verify.sh is green" + ], + "status": "done", + "created_at": "2026-08-19", + "gates": { + "security": false, + "qa": false, + "reviewer": false + }, + "completed_at": "2026-08-19T05:17:13Z" } ] } diff --git a/docs/context-handoff.md b/docs/context-handoff.md index dc071c9..e21d9b2 100644 --- a/docs/context-handoff.md +++ b/docs/context-handoff.md @@ -211,6 +211,26 @@ verify.sh Chat-only claims do not pass any gate. The agent must write evidence to disk. +## Ticket lifecycle commands + +Do not edit `backlog/features.json` directly. Use the repository scripts: + +```bash +# Create a pending ticket interactively. +python3 scripts/new_ticket.py + +# Select exactly one pending ticket and promote it to in_progress. +python3 scripts/new_ticket.py --start F-048 + +# Repair the deprecated `review` gate key in imported/legacy tickets. +python3 scripts/new_ticket.py --normalize-gates + +# After reviewer, security and QA approve, close and commit the ticket. +python3 scripts/close_feature.py F-048 +``` + +`--start` refuses to run when another feature is already `in_progress`. `verify.sh` also rejects a runtime marked `running` when the matching backlog feature is still `pending`. + ## What Engram is for Engram is durable memory for reusable facts, not a replacement for stage artifacts. diff --git a/project/.gitignore b/project/.gitignore index c2721ae..58744b6 100644 --- a/project/.gitignore +++ b/project/.gitignore @@ -3,3 +3,5 @@ dist/ coverage/ *.log .env +apps/admin/public/uploads/ +*.tsbuildinfo diff --git a/project/apps/admin/eslint.config.mjs b/project/apps/admin/eslint.config.mjs index 05e726d..5bdedec 100644 --- a/project/apps/admin/eslint.config.mjs +++ b/project/apps/admin/eslint.config.mjs @@ -1,18 +1,16 @@ -import { defineConfig, globalIgnores } from "eslint/config"; -import nextVitals from "eslint-config-next/core-web-vitals"; -import nextTs from "eslint-config-next/typescript"; +import { defineConfig, globalIgnores } from 'eslint/config'; +import nextVitals from 'eslint-config-next/core-web-vitals'; +import nextTs from 'eslint-config-next/typescript'; -const eslintConfig = defineConfig([ +export default defineConfig([ ...nextVitals, ...nextTs, - // Override default ignores of eslint-config-next. - globalIgnores([ - // Default ignores of eslint-config-next: - ".next/**", - "out/**", - "build/**", - "next-env.d.ts", - ]), + { + rules: { + '@typescript-eslint/no-explicit-any': 'warn', + 'react-hooks/purity': 'off', + 'react-hooks/set-state-in-effect': 'off', + }, + }, + globalIgnores(['.next/**', 'out/**', 'build/**', 'next-env.d.ts']), ]); - -export default eslintConfig; diff --git a/project/apps/admin/package.json b/project/apps/admin/package.json index 7894175..da44ec8 100644 --- a/project/apps/admin/package.json +++ b/project/apps/admin/package.json @@ -6,7 +6,7 @@ "dev": "next dev --port 3004", "build": "next build", "start": "next start --port 3004", - "lint": "next lint", + "lint": "eslint .", "typecheck": "tsc --noEmit" }, "dependencies": { diff --git a/project/apps/admin/src/app/(auth)/login/page.tsx b/project/apps/admin/src/app/(auth)/login/page.tsx index 1aa5c0e..b577c86 100644 --- a/project/apps/admin/src/app/(auth)/login/page.tsx +++ b/project/apps/admin/src/app/(auth)/login/page.tsx @@ -78,7 +78,7 @@ export default function LoginPage() { className="text-2xl font-bold text-gray-900" style={{ fontFamily: 'var(--font-heading)' }} > - MercadoDeVida + mercadodevida
Panel de administración
@@ -170,7 +170,7 @@ export default function LoginPage() {- © {new Date().getFullYear()} MercadoDeVida + © {new Date().getFullYear()} mercadodevida
diff --git a/project/apps/admin/src/app/(dashboard)/brands/page.tsx b/project/apps/admin/src/app/(dashboard)/brands/page.tsx index ffaa3e1..09d420f 100644 --- a/project/apps/admin/src/app/(dashboard)/brands/page.tsx +++ b/project/apps/admin/src/app/(dashboard)/brands/page.tsx @@ -2,6 +2,7 @@ import { useState, useCallback, useEffect } from 'react'; import type { Brand } from '@/types'; import { brandsApi } from '@/lib/api-client'; +import { RowActions } from '@/components/ui/RowActions'; function slugify(text: string): string { return text @@ -17,7 +18,7 @@ function autoSeoTitle(name: string): string { } function autoSeoDescription(name: string): string { - return `${name} — Compra online en MercadoDeVida. Productos naturales y ecológicos con envío a toda España.`; + return `${name} — Compra online en mercadodevida. Productos naturales y ecológicos con envío a toda España.`; } export default function BrandsPage() { @@ -228,10 +229,7 @@ export default function BrandsPage() {