m/paliad#61 Slice C frontend pass.
Discovery (Geteilte Vorlagen):
- New 4th tab on /checklists between "Meine Vorlagen" and "Vorhandene
Instanzen". Filters the merged catalog response to authored entries
not owned by the caller (firm-visible OR globally-promoted OR
share-recipient). Tab state round-trips via ?tab=gallery.
- Regime filter pills (UPC / DE / EPA / OTHER) operate independently
from the main Vorlagen tab.
- Cards show regime badge, item count, author line, visibility chip.
- Self-filter relies on /api/me email match — loadMe() fires once on
page boot and is idempotent.
Versioning UI on /checklists/instances/{id}:
- "Vorlage aktualisiert" badge appears when the instance's
template_version is known AND lags the live template version (only
for authored templates; static templates never bump). Shows "v{from}
→ v{to}" delta.
- "Änderungen anzeigen" button opens a diff modal that compares the
instance's template_snapshot against the live template body.
Item-level grouping by (section title, item label). Surfaces added /
removed / changed items with localised section labels. Empty state
when only metadata changed.
i18n: 13 new keys per language (DE + EN) under
checklisten.tab.gallery, checklisten.gallery.*, checklisten.filter.other,
and checklisten.instance.{outdated,diff}.*. Total 2666 keys.
Build hygiene: bun run build clean; i18n scan clean. Go build/vet/test
+ TestBootSmoke ./cmd/server/ all green.
Three i18n bugs from the t-paliad-101 QA sweep, fixed together:
B2 — Fristenrechner deadline notes leaked German into the EN locale.
Migration 032 adds paliad.deadline_rules.deadline_notes_en (TEXT NULL)
and backfills English translations for all 30 rules that carry a
deadline_notes value (UPC RoP / EPC / ZPO terminology). The frontend
prefers _en when locale=EN and falls back to deadline_notes (DE) when
the column is NULL, so future seeds without an EN translation render
in DE rather than empty. UIDeadline DTO gains notesEN. The bulk
"Als Frist(en) speichern" CTA now stores the locale-matched note text
so EN users get an EN note alongside the EN title.
B8 — trigger-event picker labels were English-only when DE locale was
active (102 rows, name_de defaulted to '' in 028, frontend already had
the locale switch but no data). Migration 033 backfills name_de for
all 102 trigger events using standard German UPC RoP terminology
(Klageschrift, Klageerwiderung, Replik, Duplik, Nichtigkeitswiderklage,
Verletzungswiderklage, Berufungsschrift/-begründung, Anschlussberufung,
Schutzschrift, Beweissicherung, etc.).
S3 — frontend/src/client/checklists-instance.ts:154 had a hardcoded
"Project" label in both branches of the locale ternary; the DE branch
now reads "Projekt", matching the surrounding meta-item labels' pattern
(Court / Authority → Gericht / Behörde, Reference → Rechtsgrundlage).
F-8 from the t-paliad-074 audit. Replaces silent `?? key` fallback with a
typed key surface so drift caught at compile/build time, not in prod.
- New `frontend/src/i18n-keys.ts` (generated): `I18nKey` literal union of
all 1288 keys in `i18n.ts`. Regenerated by `frontend/build.ts` on every
build; written only when content changes (no spurious diffs).
- `t(key: I18nKey)` is now strict — `t("fristn.detail.title")` fails
`tsc --noEmit`. New `tDyn(key: string)` is the explicit escape hatch
for runtime-composed keys (`tDyn(\`fristen.status.${x}\`)`); 27 dynamic
call sites migrated.
- Build-time scan in `build.ts` walks `src/**/*.{ts,tsx}` for literal
`data-i18n` / `data-i18n-placeholder` / `data-i18n-title` attributes
and aborts the build on any value not in the key set. Skips `${...}`
interpolations (can't resolve statically). Applied before bundling so
no artefact ships when an unknown literal is present.
Surfaced and fixed during migration:
- `data-i18n="fristen.save.modal.project"` (fristenrechner.ts:145) →
`fristen.save.modal.akte` — F-04-class bug, would render the raw key.
- `t("termine.field.project.none")` (appointments-new.ts:30) →
`termine.field.akte.none` — same class.
- `t("checklisten.instance.project.open")` (checklists-instance.ts:155)
→ `checklisten.instance.akte.open` — same class.
- 4 duplicate-key entries in `i18n.ts` removed (TS1117): `nav.termine`
and `akten.detail.tab.termine` each appeared twice in DE and twice in
EN with identical values.
Out of scope (per brief): the German-vs-English i18n-key namespace split
flagged as F-9, JSX intrinsic typing, and the `akten` → `projects`
half-rename in checklists-detail.ts. Those stay tsc-noisy until separate
tasks land.