feat(F-092): completed feature

This commit is contained in:
chattie
2026-08-20 21:51:46 +02:00
parent 4d0970df5d
commit 715a8dac56
10 changed files with 181 additions and 70 deletions

View File

@@ -4162,6 +4162,40 @@
"close": true
},
"completed_at": "2026-08-20T19:47:31Z"
},
{
"id": "F-092",
"type": "fix",
"title": "Move expiration date to product General tab",
"problem": "The product editor currently renders Fecha de caducidad inside the SEO tab, but operators need this operational product field in the General tab alongside name, brand, categories, and attributes.",
"goal": "Render the existing expirationDate field in the General tab and remove it from SEO without changing its value, validation, or save payload.",
"scope_in": [
"admin ProductEditor General and SEO tabs"
],
"scope_out": [
"No backend change",
"no data migration",
"no change to expirationDate API contract"
],
"priority": "med",
"risk": "low",
"description": "Problem: The product editor currently renders Fecha de caducidad inside the SEO tab, but operators need this operational product field in the General tab alongside name, brand, categories, and attributes.. Goal: Render the existing expirationDate field in the General tab and remove it from SEO without changing its value, validation, or save payload.. Scope IN: admin ProductEditor General and SEO tabs. Scope OUT: No backend change, no data migration, no change to expirationDate API contract. Type: fix. Priority: med. Risk: low.",
"acceptance": [
"- General tab displays the Fecha de caducidad date input",
"- SEO tab no longer displays the expiration date field",
"- Existing value loads and saves through expirationDate unchanged",
"- Product editor typecheck/lint pass",
"- verify.sh is green"
],
"status": "done",
"created_at": "2026-08-20",
"gates": {
"reviewer": true,
"security": true,
"qa": true,
"close": true
},
"completed_at": "2026-08-20T19:51:46Z"
}
]
}

View File

@@ -264,6 +264,19 @@ export function ProductEditor({ productId }: ProductEditorProps) {
</select>
</div>
</div>
<div>
<div className="flex items-center justify-between mb-1.5">
<label className="text-sm font-semibold text-gray-900">Fecha de caducidad</label>
<span className="text-xs text-gray-400">opcional</span>
</div>
<input
type="date"
value={expirationDate}
onChange={(e) => setExpirationDate(e.target.value)}
className="w-full px-4 py-2.5 border border-gray-300 rounded-xl text-sm focus:ring-2 focus:ring-[#2D6A4F] outline-none"
/>
<p className="mt-1 text-xs text-gray-400">Se mostrará en el listado de productos y en la tienda.</p>
</div>
<div>
<label className="flex items-center gap-2 mb-3">
<input type="checkbox" checked={featured} onChange={e => setFeatured(e.target.checked)}
@@ -389,20 +402,6 @@ export function ProductEditor({ productId }: ProductEditorProps) {
<div className="mt-1 text-xs text-gray-400">{seoDesc.length}/160</div>
</div>
{/* Fecha de caducidad */}
<div>
<div className="flex items-center justify-between mb-1.5">
<label className="text-sm font-semibold text-gray-900">Fecha de caducidad</label>
<span className="text-xs text-gray-400">opcional</span>
</div>
<input
type="date"
value={expirationDate}
onChange={(e) => setExpirationDate(e.target.value)}
className="w-full px-4 py-2.5 border border-gray-300 rounded-xl text-sm focus:ring-2 focus:ring-[#2D6A4F] outline-none"
/>
<p className="mt-1 text-xs text-gray-400">Se mostrará en el listado de productos y en la tienda.</p>
</div>
</section>
)}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,15 @@
# F-092 — Implementer evidence
## Change
Moved the existing `Fecha de caducidad` date input in `project/apps/admin/src/features/products/components/ProductEditor.tsx` from the SEO tab section into the General tab section.
The same `expirationDate` state, `onChange` handler, load behavior, and save payload remain unchanged. The SEO tab no longer renders the field.
## Deployment validation
- Admin `npm run build` → exit 0
- Admin `npx tsc --noEmit` → exit 0
- Admin ESLint on `ProductEditor.tsx` → 0 errors (pre-existing warnings only)
- Admin restarted on port 3004 → HTTP 200
- Backend rebuilt/restarted so the existing brand and expiration serialization is live in `dist`.

View File

@@ -0,0 +1,15 @@
{
"feature_id": "F-092",
"agent": "leader",
"verdict": "APPROVED",
"summary": "F-092 moves Fecha de caducidad from SEO to General and redeploys the admin while keeping data behavior unchanged.",
"evidence": [
"reviewer.json verdict=APPROVED",
"security.json verdict=APPROVED",
"qa.json verdict=APPROVED",
"Admin build exit 0",
"Admin HTTP smoke test returned 200",
"scripts/verify.sh exit 0"
],
"timestamp": "2026-08-20T19:51:50Z"
}

View File

@@ -0,0 +1,14 @@
{
"feature_id": "F-092",
"agent": "qa",
"verdict": "APPROVED",
"summary": "The expiration date placement and rebuilt admin deployment pass the available checks.",
"evidence": [
"Admin npm run build exit 0",
"Admin npx tsc --noEmit exit 0",
"Admin ESLint ProductEditor exit 0 with warnings only",
"GET http://192.168.18.93:3004/ returned HTTP 200",
"scripts/verify.sh exit 0"
],
"timestamp": "2026-08-20T19:51:40Z"
}

View File

@@ -0,0 +1,13 @@
{
"feature_id": "F-092",
"agent": "reviewer",
"verdict": "APPROVED",
"summary": "The expiration date control is now in General and retains the existing state, API payload, and date input behavior; SEO no longer contains the operational field.",
"evidence": [
"ProductEditor renders Fecha de caducidad inside the general tab block",
"SEO tab expiration block was removed",
"expirationDate state and save payload are unchanged",
"Admin production build succeeds"
],
"timestamp": "2026-08-20T19:51:20Z"
}

View File

@@ -0,0 +1,13 @@
{
"feature_id": "F-092",
"agent": "security",
"verdict": "APPROVED",
"summary": "This is a presentation-only move. Existing date input validation, API payload, authorization, and backend handling remain unchanged.",
"evidence": [
"No backend or database changes",
"Existing type=date control remains in use",
"No new input or data exposure introduced",
"Existing product update authorization and validation are unchanged"
],
"timestamp": "2026-08-20T19:51:30Z"
}

View File

@@ -1,14 +1,22 @@
# Feature actual
## Feature activa: F-091 (in_progress) — Duplicate SKU/EAN update conflicts are opaque and can be submitted twice
## Feature activa: F-092 (in_progress) — Move expiration date to product General tab
Backlog: 159 features (149 done, 9 pending, 1 in_progress).
Backlog: 160 features (150 done, 9 pending, 1 in_progress).
Últimas features cerradas: **F-080**, **F-081**, **F-082**, **F-083**, **F-084**, **F-085**, **F-086**, **F-087**.
## Incidencia actual (2026-08-20)
El PATCH de variantes devuelve `409 PRODUCT_VARIANT_CODE_EXISTS` cuando el SKU o EAN ya está usado por otra variante. El editor muestra solo `Error` y la combinación Enter + blur puede intentar enviar dos veces. F-091 conserva la unicidad, evita envíos simultáneos y muestra el campo en conflicto.
`Fecha de caducidad` está en la pestaña SEO de la ficha de producto. F-092 la mueve a la pestaña General sin cambiar el estado, payload ni guardado.
## Última incidencia resuelta (2026-08-20)
F-091 cerrada con todos los gates aprobados. Los conflictos de SKU/EAN indican ahora qué campo está duplicado y Enter + blur no generan PATCH simultáneos.
## Incidencia anterior (2026-08-20)
El PATCH de variantes devolvía `409 PRODUCT_VARIANT_CODE_EXISTS` cuando el SKU o EAN ya estaba usado por otra variante. El editor mostraba solo `Error` y la combinación Enter + blur podía intentar enviar dos veces.
## Última incidencia resuelta (2026-08-20)

View File

@@ -1,62 +1,13 @@
{
"feature_id": "F-091",
"feature_id": "F-092",
"stage": "close",
"agent": "leader",
"action": "Validate F-091 gates and close SKU/EAN conflict fix",
"action": "Validate F-092 gates and close expiration date placement",
"state": "running",
"next_agent": "leader",
"waiting_for": "verify.sh green",
"updated_at": "2026-08-20T19:47:22Z",
"updated_at": "2026-08-20T19:51:36Z",
"timeline": [
{
"ts": "2026-08-20T19:32:56Z",
"agent": "leader",
"stage": "intake",
"state": "running",
"message": "Triage product listing link route mismatch: /productos vs frontend /products"
},
{
"ts": "2026-08-20T19:33:07Z",
"agent": "implementer",
"stage": "build",
"state": "running",
"message": "Change admin product listing link from /productos/:slug to frontend /products/:slug"
},
{
"ts": "2026-08-20T19:33:31Z",
"agent": "reviewer",
"stage": "review_gate",
"state": "running",
"message": "Review corrected customer frontend route in admin product listing"
},
{
"ts": "2026-08-20T19:33:41Z",
"agent": "security",
"stage": "security_gate",
"state": "running",
"message": "Check F-089 link-only change for unsafe URL or target behavior"
},
{
"ts": "2026-08-20T19:33:49Z",
"agent": "qa",
"stage": "qa_gate",
"state": "running",
"message": "Run F-089 route regression, admin typecheck/lint, and verify"
},
{
"ts": "2026-08-20T19:34:01Z",
"agent": "leader",
"stage": "close",
"state": "running",
"message": "Validate F-089 gates and close route correction"
},
{
"ts": "2026-08-20T19:34:21Z",
"agent": "leader",
"stage": "close",
"state": "done",
"message": "F-089 cerrado: enlace del listado usa /products/:slug y verify.sh verde"
},
{
"ts": "2026-08-20T19:38:52Z",
"agent": "leader",
@@ -147,6 +98,55 @@
"stage": "close",
"state": "running",
"message": "Validate F-091 gates and close SKU/EAN conflict fix"
},
{
"ts": "2026-08-20T19:47:40Z",
"agent": "leader",
"stage": "close",
"state": "done",
"message": "F-091 cerrado: conflictos SKU/EAN identificados y doble envío prevenido"
},
{
"ts": "2026-08-20T19:48:12Z",
"agent": "leader",
"stage": "intake",
"state": "running",
"message": "Triage expiration date placement: move from SEO tab to General tab"
},
{
"ts": "2026-08-20T19:48:26Z",
"agent": "implementer",
"stage": "build",
"state": "running",
"message": "Move existing expirationDate control from SEO section into General section"
},
{
"ts": "2026-08-20T19:51:05Z",
"agent": "reviewer",
"stage": "review_gate",
"state": "running",
"message": "Review expiration date placement and unchanged save contract"
},
{
"ts": "2026-08-20T19:51:17Z",
"agent": "security",
"stage": "security_gate",
"state": "running",
"message": "Check expiration date move for unchanged validation and payload"
},
{
"ts": "2026-08-20T19:51:27Z",
"agent": "qa",
"stage": "qa_gate",
"state": "running",
"message": "Run F-092 admin checks, deployment smoke test, and verify"
},
{
"ts": "2026-08-20T19:51:36Z",
"agent": "leader",
"stage": "close",
"state": "running",
"message": "Validate F-092 gates and close expiration date placement"
}
]
}