feat(F-085): completed feature
This commit is contained in:
26
work/artifacts/F-085/architect.md
Normal file
26
work/artifacts/F-085/architect.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# F-085 — Architect: /tax-rates TIPO column is not editable
|
||||
|
||||
## Root cause
|
||||
|
||||
`/admin/tax-rates` PATCH route (`project/src/modules/pricing/api/pricing.routes.ts`) only accepts `{ name, ratePercent, active }`; `appliesTo` is not in the body schema and not written to the DB. The admin page renders the column as plain `<td>{r.appliesTo}</td>`.
|
||||
|
||||
## Design
|
||||
|
||||
1. Extend the PATCH body Zod schema with `appliesTo: z.enum(['general','reduced','super-reduced']).optional()`. Server validates the value against the enum (no DB constraint, but rejection is fast and the error message is clear).
|
||||
2. Add a new UPDATE branch for `applies_to` mirroring the existing ones.
|
||||
3. In the admin page, the TIPO cell is now click-to-edit:
|
||||
- Click `<button>` → switches to a `<select autoFocus>` with the three valid values.
|
||||
- On `change`, if the new value differs, fire PATCH immediately and close edit mode.
|
||||
- On `blur`, if value still equals the original, just close; if different and `change` already fired, no-op.
|
||||
4. Success path updates the row in-place (`rates.map(...)`).
|
||||
|
||||
## Risk
|
||||
|
||||
Low. Server-side enum validation; no SQL injection; no new deps.
|
||||
|
||||
## Acceptance mapping
|
||||
- "TIPO cell enters edit mode on click and shows a select with allowed types" → button → select.
|
||||
- "Selecting a new type and confirming triggers PATCH /tax-rates/:id" → onChange path.
|
||||
- "Invalid types are rejected client and server side" → Zod enum on server; select restricts client.
|
||||
- "Successful change is reflected without a full page reload" → in-place state update.
|
||||
- "verify.sh is green" → typecheck + lint clean.
|
||||
28
work/artifacts/F-085/implementer.md
Normal file
28
work/artifacts/F-085/implementer.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# F-085 — Implementer evidence
|
||||
|
||||
## What was implemented
|
||||
|
||||
`/tax-rates` TIPO column is now inline-editable. Server validates the value against the same enum used by pricing.
|
||||
|
||||
### Files changed
|
||||
|
||||
- `project/src/modules/pricing/api/pricing.routes.ts`
|
||||
- PATCH `/admin/tax-rates/:id` body schema now accepts `appliesTo: z.enum(['general','reduced','super-reduced']).optional()`.
|
||||
- Added an UPDATE branch `applies_to = $N` mirroring the existing branches.
|
||||
- `project/apps/admin/src/app/(dashboard)/tax-rates/page.tsx`
|
||||
- New state: `tipoEditing`, `savingTipo`.
|
||||
- New handler: `saveTipo(id, newTipo)` → `taxApi.update(id, { appliesTo })` → in-place row update.
|
||||
- TIPO cell: `<button>` shows current value with hover affordance; click swaps to a `<select autoFocus>` with the three allowed options. On change, PATCH fires immediately. On blur (without change), edit mode closes.
|
||||
|
||||
## Validation
|
||||
|
||||
- `npx tsc --noEmit` → exit 0
|
||||
- `npx eslint` on touched files → exit 0
|
||||
|
||||
## Acceptance trace
|
||||
|
||||
- "TIPO cell enters edit mode on a single click and shows a select with allowed types" → button → select.
|
||||
- "Selecting a new type and confirming triggers PATCH /tax-rates/:id" → onChange handler.
|
||||
- "Invalid types are rejected client and server side" → Zod enum on server; only three options in select.
|
||||
- "Successful change is reflected in the cell without a full page reload" → in-place `rates.map`.
|
||||
- "verify.sh is green" → tsc + eslint clean.
|
||||
14
work/artifacts/F-085/leader-close.json
Normal file
14
work/artifacts/F-085/leader-close.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"feature_id": "F-085",
|
||||
"agent": "leader",
|
||||
"verdict": "APPROVED",
|
||||
"summary": "All gates approved. F-085 makes the TIPO column in /tax-rates inline-editable: backend accepts appliesTo (Zod-validated enum), admin cell is click-to-edit with autoFocus select.",
|
||||
"evidence": [
|
||||
"work/artifacts/F-085/reviewer.json verdict=APPROVED",
|
||||
"work/artifacts/F-085/security.json verdict=APPROVED",
|
||||
"work/artifacts/F-085/qa.json verdict=APPROVED",
|
||||
"npx tsc --noEmit exit 0",
|
||||
"npx eslint exit 0"
|
||||
],
|
||||
"timestamp": "2026-08-20T04:14:30Z"
|
||||
}
|
||||
15
work/artifacts/F-085/qa.json
Normal file
15
work/artifacts/F-085/qa.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"feature_id": "F-085",
|
||||
"verdict": "APPROVED",
|
||||
"trace": [
|
||||
{ "acceptance": "TIPO cell enters edit mode on click and shows a select with allowed types", "result": "PASS", "evidence": "Button onClick sets tipoEditing; render switches to select with the three options." },
|
||||
{ "acceptance": "Selecting a new type and confirming triggers PATCH /tax-rates/:id", "result": "PASS", "evidence": "onChange calls saveTipo → taxApi.update(id, { appliesTo })." },
|
||||
{ "acceptance": "Invalid types are rejected client and server side", "result": "PASS", "evidence": "Client select only offers three options; server Zod enum rejects anything else with 400." },
|
||||
{ "acceptance": "Successful change is reflected in the cell without a full page reload", "result": "PASS", "evidence": "rates.map updates the row in-place after PATCH success." },
|
||||
{ "acceptance": "verify.sh is green", "result": "PASS", "evidence": "tsc exit 0; eslint exit 0." }
|
||||
],
|
||||
"regression_checks": ["Tasa inline edit", "Estado toggle", "Active filter"],
|
||||
"verdict_reason": "All acceptance criteria trace to PASS.",
|
||||
"reviewer": "qa",
|
||||
"reviewed_at": "2026-08-20T04:14:00Z"
|
||||
}
|
||||
16
work/artifacts/F-085/reviewer.json
Normal file
16
work/artifacts/F-085/reviewer.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"feature_id": "F-085",
|
||||
"verdict": "APPROVED",
|
||||
"checks": [
|
||||
{ "name": "Backend accepts appliesTo in PATCH body", "result": "PASS", "notes": "Zod enum validates 'general' | 'reduced' | 'super-reduced'." },
|
||||
{ "name": "Backend writes applies_to column", "result": "PASS", "notes": "New branch sets `applies_to = $N` only when patch.appliesTo defined." },
|
||||
{ "name": "Admin cell is click-to-edit", "result": "PASS", "notes": "Button → select with autoFocus; onChange PATCH; onBlur closes." },
|
||||
{ "name": "State updates without full reload", "result": "PASS", "notes": "rates.map writes the new appliesTo into the row." },
|
||||
{ "name": "Other columns unaffected", "result": "PASS", "notes": "Tasa, Estado and Acciones still use the existing edit pattern / toggles." }
|
||||
],
|
||||
"lint": { "errors_introduced": 0 },
|
||||
"typecheck": "PASS",
|
||||
"verdict_reason": "Backend extended with one optional field + UI swap; behaviour matches acceptance.",
|
||||
"reviewer": "reviewer",
|
||||
"reviewed_at": "2026-08-20T04:13:00Z"
|
||||
}
|
||||
15
work/artifacts/F-085/security.json
Normal file
15
work/artifacts/F-085/security.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"feature_id": "F-085",
|
||||
"verdict": "APPROVED",
|
||||
"checks": [
|
||||
{ "name": "Enum validation", "result": "PASS", "notes": "Zod enum rejects arbitrary strings; no SQL injection." },
|
||||
{ "name": "Auth/RBAC unchanged", "result": "PASS", "notes": "Same admin-only route." },
|
||||
{ "name": "Dependencies", "result": "PASS", "notes": "No new packages." }
|
||||
],
|
||||
"sast": "PASS",
|
||||
"dependency_review": "PASS",
|
||||
"secret_scan": "PASS",
|
||||
"verdict_reason": "Same admin-only surface; new field strictly validated.",
|
||||
"reviewer": "security",
|
||||
"reviewed_at": "2026-08-20T04:13:30Z"
|
||||
}
|
||||
Reference in New Issue
Block a user