feat(F-092): completed feature
This commit is contained in:
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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
15
work/artifacts/F-092/implementer.md
Normal file
15
work/artifacts/F-092/implementer.md
Normal 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`.
|
||||
15
work/artifacts/F-092/leader-close.json
Normal file
15
work/artifacts/F-092/leader-close.json
Normal 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"
|
||||
}
|
||||
14
work/artifacts/F-092/qa.json
Normal file
14
work/artifacts/F-092/qa.json
Normal 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"
|
||||
}
|
||||
13
work/artifacts/F-092/reviewer.json
Normal file
13
work/artifacts/F-092/reviewer.json
Normal 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"
|
||||
}
|
||||
13
work/artifacts/F-092/security.json
Normal file
13
work/artifacts/F-092/security.json
Normal 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"
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user