feat(F-085): completed feature

This commit is contained in:
chattie
2026-08-20 06:10:48 +02:00
parent 3b5ea8f261
commit 7ece045e13
10 changed files with 190 additions and 23 deletions

View 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.

View 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.

View 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"
}

View 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"
}

View 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"
}

View 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"
}