mAi: #113 - fix admin rules list 'undefined' proceeding name

ProceedingType TS interface in admin-rules-list.ts and admin-rules-edit.ts
declared `name_de` but the Go ProceedingType model serialises `db:"name"`
as JSON key `name` (DE is the primary on the wire). Result: `pt.name_de`
was undefined for every row, so `${pt.code} · ${pt.name_de}` produced the
literal "upc.apl.cost · undefined" in the list (and the same in proceeding
selects of the edit page).

Frontend-only fix:
- Rename the field to `name` to match the API contract.
- Guard the label builder: if the active-language name is missing, fall
  back to just the proceeding code rather than rendering "code · " (or
  worse, the original "code · undefined" string).

Other admin pages that fetch /api/proceeding-types-db (deadlines-new,
deadlines-detail, project-form, fristenrechner) already read `pt.name`
correctly, so the bug was scoped to these two files. TriggerEvent's
`name_de` field is real and stays untouched.
This commit is contained in:
mAi
2026-05-25 16:52:07 +02:00
parent b1340e2be4
commit 001542a3ce
2 changed files with 19 additions and 5 deletions

View File

@@ -51,7 +51,10 @@ interface Rule {
interface ProceedingType { interface ProceedingType {
id: number; id: number;
code: string; code: string;
name_de: string; // `name` is the German display name on the wire; the Go `ProceedingType`
// model serialises `db:"name"` as JSON key `name`. Don't reach for
// `name_de` — that field does not exist in this payload (m/paliad#113).
name: string;
name_en: string; name_en: string;
} }
@@ -169,7 +172,8 @@ function fillProceedingSelect(selectId: string, list: ProceedingType[]) {
for (const pt of list) { for (const pt of list) {
const opt = document.createElement("option"); const opt = document.createElement("option");
opt.value = String(pt.id); opt.value = String(pt.id);
opt.textContent = `${pt.code} · ${getLang() === "en" ? pt.name_en : pt.name_de}`; const name = getLang() === "en" ? pt.name_en : pt.name;
opt.textContent = name ? `${pt.code} · ${name}` : pt.code;
sel.appendChild(opt); sel.appendChild(opt);
} }
} }

View File

@@ -29,7 +29,11 @@ interface Rule {
interface ProceedingType { interface ProceedingType {
id: number; id: number;
code: string; code: string;
name_de: string; // `name` is the German display name on the wire; the Go `ProceedingType`
// model serialises `db:"name"` as JSON key `name` (the schema treats DE
// as primary). EN lives in `name_en`. Don't reach for `name_de` — that
// field does not exist in this payload (cf. m/paliad#113).
name: string;
name_en: string; name_en: string;
category: string; category: string;
} }
@@ -125,7 +129,12 @@ function proceedingLabel(id: number | null | undefined): string {
if (id == null) return "—"; if (id == null) return "—";
const pt = proceedings.find((p) => p.id === id); const pt = proceedings.find((p) => p.id === id);
if (!pt) return `#${id}`; if (!pt) return `#${id}`;
const name = getLang() === "en" ? pt.name_en : pt.name_de; const name = getLang() === "en" ? pt.name_en : pt.name;
// Guard against a proceeding row that's missing the active-language
// name (or against a stale field-name mismatch slipping back in).
// Show the code on its own rather than "code · undefined" — that
// literal string is the smell that surfaced this bug (m/paliad#113).
if (!name) return pt.code;
return `${pt.code} · ${name}`; return `${pt.code} · ${name}`;
} }
@@ -153,7 +162,8 @@ async function loadProceedings(): Promise<void> {
for (const pt of proceedings) { for (const pt of proceedings) {
const opt = document.createElement("option"); const opt = document.createElement("option");
opt.value = String(pt.id); opt.value = String(pt.id);
opt.textContent = `${pt.code} · ${getLang() === "en" ? pt.name_en : pt.name_de}`; const name = getLang() === "en" ? pt.name_en : pt.name;
opt.textContent = name ? `${pt.code} · ${name}` : pt.code;
sel.appendChild(opt); sel.appendChild(opt);
} }
} }