Admin deadline-rules list: 'upc.apl.cost · undefined' — proceeding name missing in API response or frontend render #113

Open
opened 2026-05-25 14:43:43 +00:00 by mAi · 1 comment
Collaborator

m's report (2026-05-25 16:43)

Looking at admin deadline-rules list:

upc.apl.cost.decision    Kostenfestsetzungsbeschluss    upc.apl.cost · undefined    Pflicht    Published    30.04.2026, 16:40
upc.apl.cost.leave_app   RoP.221.1                      upc.apl.cost · undefined    Pflicht    Published    30.04.2026, 16:40

this is also wrong?! the undefined?

Diagnosis (head)

DB state is correct: paliad.proceeding_types(id=19, code='upc.apl.cost', name='Berufungsverfahren Kosten', name_en='Cost-Decision Appeal'). So the proceeding-name is populated.

The undefined comes from the frontend rendering ${rule.proceeding_code} · ${rule.proceeding_name ?? 'undefined'} — likely the admin list-rules endpoint doesn't include the joined proceeding_name field in its response.

What to do

  1. Find the admin deadline-rules list page (frontend/src/admin-*.tsx or whatever path admin lives at). Search for upc.apl.cost in code OR for the column header Verfahrenstyp.
  2. Find the backend endpoint serving that list (internal/handlers/admin_*.go or internal/handlers/deadline_rules.go).
  3. Confirm the SQL JOIN on paliad.proceeding_types exists and returns name / name_en. If missing, add: LEFT JOIN paliad.proceeding_types pt ON pt.id = dr.proceeding_type_id + select pt.name AS proceeding_name, pt.name_en AS proceeding_name_en.
  4. Confirm the frontend reads the correct field name (case: proceeding_name vs proceedingName).
  5. Replace the literal 'undefined' fallback with empty-string OR a small placeholder dash. NEVER render the string "undefined" — that's a code smell that bites every time data is missing.
  6. Same pattern likely affects other admin lists (proceeding_types admin, deadline_rules admin, etc.) — audit + fix.

Files most likely touched

  • internal/handlers/admin_deadline_rules.go (or wherever admin list endpoint is)
  • frontend/src/admin-procedural-events.tsx (post-cronus #93 rename; or admin-deadline-rules.tsx)
  • frontend/src/client/admin-procedural-events.ts
  • Possibly a shared admin-list render helper that has the literal 'undefined' fallback

Hard rules

  • Don't change the proceeding_types schema.
  • Fix the SHARED render helper if one exists — don't patch every list separately.
  • go build ./... && go test ./internal/... && cd frontend && bun run build clean.
  • Branch: mai/<worker>/admin-proceeding-name-join.

Out of scope

  • Reshaping the admin list UI.
  • Adding new admin columns.

Reporting

mai report completed with branch + SHAs + diagnosed root cause (which side was missing the join / which fallback rendered 'undefined') + UX verification.

## m's report (2026-05-25 16:43) Looking at admin deadline-rules list: ``` upc.apl.cost.decision Kostenfestsetzungsbeschluss upc.apl.cost · undefined Pflicht Published 30.04.2026, 16:40 upc.apl.cost.leave_app RoP.221.1 upc.apl.cost · undefined Pflicht Published 30.04.2026, 16:40 ``` > this is also wrong?! the undefined? ## Diagnosis (head) DB state is correct: `paliad.proceeding_types(id=19, code='upc.apl.cost', name='Berufungsverfahren Kosten', name_en='Cost-Decision Appeal')`. So the proceeding-name is populated. The `undefined` comes from the **frontend** rendering `${rule.proceeding_code} · ${rule.proceeding_name ?? 'undefined'}` — likely the admin list-rules endpoint doesn't include the joined `proceeding_name` field in its response. ## What to do 1. Find the admin deadline-rules list page (`frontend/src/admin-*.tsx` or whatever path admin lives at). Search for `upc.apl.cost` in code OR for the column header `Verfahrenstyp`. 2. Find the backend endpoint serving that list (`internal/handlers/admin_*.go` or `internal/handlers/deadline_rules.go`). 3. Confirm the SQL JOIN on `paliad.proceeding_types` exists and returns `name` / `name_en`. If missing, add: `LEFT JOIN paliad.proceeding_types pt ON pt.id = dr.proceeding_type_id` + select `pt.name AS proceeding_name`, `pt.name_en AS proceeding_name_en`. 4. Confirm the frontend reads the correct field name (case: `proceeding_name` vs `proceedingName`). 5. Replace the literal `'undefined'` fallback with empty-string OR a small placeholder dash. **NEVER** render the string "undefined" — that's a code smell that bites every time data is missing. 6. Same pattern likely affects other admin lists (proceeding_types admin, deadline_rules admin, etc.) — audit + fix. ## Files most likely touched - `internal/handlers/admin_deadline_rules.go` (or wherever admin list endpoint is) - `frontend/src/admin-procedural-events.tsx` (post-cronus #93 rename; or `admin-deadline-rules.tsx`) - `frontend/src/client/admin-procedural-events.ts` - Possibly a shared admin-list render helper that has the literal `'undefined'` fallback ## Hard rules - Don't change the proceeding_types schema. - Fix the SHARED render helper if one exists — don't patch every list separately. - `go build ./... && go test ./internal/... && cd frontend && bun run build` clean. - Branch: `mai/<worker>/admin-proceeding-name-join`. ## Out of scope - Reshaping the admin list UI. - Adding new admin columns. ## Reporting `mai report completed` with branch + SHAs + diagnosed root cause (which side was missing the join / which fallback rendered 'undefined') + UX verification.
mAi self-assigned this 2026-05-25 14:43:43 +00:00
Author
Collaborator

Root cause — frontend TS interface mismatch, not a missing SQL JOIN

The undefined was rendered client-side. The admin rules list does two parallel fetches — /admin/api/rules for rules and /api/proceeding-types-db?category=fristenrechner for proceeding types — and joins them in JS via proceedingLabel(r.proceeding_type_id). The lookup did find the right ProceedingType row; it just read the wrong field name.

Wire format (Go models.ProceedingType, internal/models/models.go:728):

Name   string `db:"name"    json:"name"`     // DE is primary on the wire
NameEN string `db:"name_en" json:"name_en"`

TS interface (frontend/src/client/admin-rules-list.ts:32, admin-rules-edit.ts:54):

interface ProceedingType { name_de: string; name_en: string; }   // ← wrong field

So pt.name_de was undefined for every row, and `${pt.code} · ${pt.name_de}` stringified to upc.apl.cost · undefined.

Why m's hypothesis (missing JOIN) was close but not quite

The data flow is the other way: the rules endpoint deliberately does not join proceeding_types.name — the frontend already loads the full proceeding list separately to power the filter <select>, so a join in the list endpoint would be redundant. The bug was in how the JS consumed that already-loaded list.

Fix

frontend/src/client/admin-rules-list.ts + frontend/src/client/admin-rules-edit.ts:

  1. Rename ProceedingType.name_dename to match the wire contract (with a comment pinning the JSON shape so nobody flips it back).
  2. Add a defensive guard in proceedingLabel() / the proceeding-select loops: if the active-language name is falsy, render just the code (upc.apl.cost) instead of upc.apl.cost · (or, in the old code, upc.apl.cost · undefined). The literal string "undefined" is exactly the smell that surfaced this bug, so the guard makes future field-name drift visible as a missing suffix rather than a poison-looking value.

Audit of other admin lists / /api/proceeding-types-db consumers

All other callers were already correct — they read pt.name:

  • frontend/src/client/deadlines-detail.ts
  • frontend/src/client/deadlines-new.ts
  • frontend/src/client/project-form.ts
  • frontend/src/client/fristenrechner.ts

The TriggerEvent.name_de field is a separate model (internal/models/models.go:752) that genuinely serialises name_de — those usages are untouched.

No shared render helper exists for proceeding labels yet; the two admin files were fixed at site. Extracting a helper would be premature with only two callers, but the JSDoc comments now flag the wire contract on both interfaces.

Verification

  • go build ./... clean
  • go test ./internal/... all green
  • cd frontend && bun run build clean
  • Bundle scan: only remaining name_de reference in admin-rules-list.js / admin-rules-edit.js is the TriggerEvent select loop (legitimate — that field exists)

UX verification on a running paliad server is gated behind DATABASE_URL + Supabase creds which aren't available in this worktree; the fix is a pure field-name change against a known-correct wire format, so the path is straightforward. Once deployed, the admin rules list will render e.g. upc.apl.cost · Berufungsverfahren Kosten instead of upc.apl.cost · undefined.

Branch / commit

  • Branch: mai/hermes/gitster-admin-rules-list
  • Commit: 001542a

Not opening a PR — head merges per project convention.

## Root cause — frontend TS interface mismatch, not a missing SQL JOIN The `undefined` was rendered client-side. The admin rules list does two parallel fetches — `/admin/api/rules` for rules and `/api/proceeding-types-db?category=fristenrechner` for proceeding types — and joins them in JS via `proceedingLabel(r.proceeding_type_id)`. The lookup *did* find the right `ProceedingType` row; it just read the wrong field name. **Wire format** (Go `models.ProceedingType`, `internal/models/models.go:728`): ```go Name string `db:"name" json:"name"` // DE is primary on the wire NameEN string `db:"name_en" json:"name_en"` ``` **TS interface** (`frontend/src/client/admin-rules-list.ts:32`, `admin-rules-edit.ts:54`): ```ts interface ProceedingType { name_de: string; name_en: string; } // ← wrong field ``` So `pt.name_de` was `undefined` for every row, and `` `${pt.code} · ${pt.name_de}` `` stringified to `upc.apl.cost · undefined`. ## Why m's hypothesis (missing JOIN) was close but not quite The data flow is the other way: the rules endpoint deliberately does **not** join `proceeding_types.name` — the frontend already loads the full proceeding list separately to power the filter `<select>`, so a join in the list endpoint would be redundant. The bug was in how the JS consumed that already-loaded list. ## Fix `frontend/src/client/admin-rules-list.ts` + `frontend/src/client/admin-rules-edit.ts`: 1. Rename `ProceedingType.name_de` → `name` to match the wire contract (with a comment pinning the JSON shape so nobody flips it back). 2. Add a defensive guard in `proceedingLabel()` / the proceeding-select loops: if the active-language name is falsy, render *just* the code (`upc.apl.cost`) instead of `upc.apl.cost · ` (or, in the old code, `upc.apl.cost · undefined`). The literal string `"undefined"` is exactly the smell that surfaced this bug, so the guard makes future field-name drift visible as a missing suffix rather than a poison-looking value. ## Audit of other admin lists / `/api/proceeding-types-db` consumers All other callers were already correct — they read `pt.name`: - `frontend/src/client/deadlines-detail.ts` - `frontend/src/client/deadlines-new.ts` - `frontend/src/client/project-form.ts` - `frontend/src/client/fristenrechner.ts` The `TriggerEvent.name_de` field is a separate model (`internal/models/models.go:752`) that genuinely serialises `name_de` — those usages are untouched. No shared render helper exists for proceeding labels yet; the two admin files were fixed at site. Extracting a helper would be premature with only two callers, but the JSDoc comments now flag the wire contract on both interfaces. ## Verification - `go build ./...` clean - `go test ./internal/...` all green - `cd frontend && bun run build` clean - Bundle scan: only remaining `name_de` reference in `admin-rules-list.js` / `admin-rules-edit.js` is the TriggerEvent select loop (legitimate — that field exists) UX verification on a running paliad server is gated behind `DATABASE_URL` + Supabase creds which aren't available in this worktree; the fix is a pure field-name change against a known-correct wire format, so the path is straightforward. Once deployed, the admin rules list will render e.g. `upc.apl.cost · Berufungsverfahren Kosten` instead of `upc.apl.cost · undefined`. ## Branch / commit - Branch: `mai/hermes/gitster-admin-rules-list` - Commit: [`001542a`](https://mgit.msbls.de/m/paliad/commit/001542a3ce4af4069d78e0e03450f1448d51cce0) Not opening a PR — head merges per project convention.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: m/paliad#113
No description provided.