Verfahrensablauf timeline: optional duration indicator on event cards (hover by default, toggle for always-on) #133

Open
opened 2026-05-26 13:32:16 +00:00 by mAi · 1 comment
Collaborator

Gap

Fristenrechner (/tools/fristenrechner) already renders each rule's duration:

<span class="event-result-duration">${d.durationValue} ${unitLabel} ${timingLabel}</span>

in frontend/src/client/fristenrechner.ts:990 and :1663. The Verfahrensablauf timeline (/tools/verfahrensablauf) doesn't — frontend/src/client/views/verfahrensablauf-core.ts has zero duration references. Lawyers can't see why a deadline lands when it does without flipping to Fristenrechner.

Design

Two modes, both opt-in:

  1. Default: duration shown on hover as a tooltip on the event card. Implementation: title="2 Monate ab Zustellung" is the cheapest; a proper styled popover would be nicer but the hover-title is a defensible v1.
  2. Toggle: a header toggle (similar to the existing paliad.fristen.notes-show localStorage flag in verfahrensablauf.ts:F8) that renders the duration inline on every card — "2 Mo. ab Zustellung" next to the date.

Duration values are already on the payload returned from /api/fristenrechner/calculate — they live on each Deadline row as durationValue + durationUnit + timing (the same fields Fristenrechner consumes). No backend change needed; just plumb them through to the timeline renderer.

Acceptance

  1. Hover over any event card in the Verfahrensablauf timeline → see "<n> <unit> <timing>" as a tooltip (DE + EN labels via existing i18n).
  2. New header toggle Dauern anzeigen / Show durations next to the existing Notizen anzeigen toggle. Click → all cards render the duration inline. Preference persists via localStorage key paliad.verfahrensablauf.durations-show.
  3. Court-set / no-duration rules (e.g. trigger event itself) don't show a tooltip / inline label — only rules with durationValue > 0.
  4. bun run build + go test clean.
  5. No backend / API change.

Files

  • frontend/src/client/views/verfahrensablauf-core.ts — event-card rendering (find where dates render, attach title= or new inline span).
  • frontend/src/client/verfahrensablauf.ts — add durations-show localStorage helpers next to the existing notes-show ones (look for paliad.fristen.notes-show).
  • frontend/src/verfahrensablauf.tsx — add the toggle button next to the existing notes toggle (search for notes-toggle or similar).
  • frontend/src/client/i18n.ts + i18n-keys.ts — DE + EN strings for the toggle label, the duration formatter labels (reuse Fristenrechner's unitLabel + timingLabel helpers if exported, else copy them).

Scope guardrails

  • Pure frontend. No DB, no Go, no migration.
  • Reuse Fristenrechner's unitLabel + timingLabel (Mo./Tage/Wo./Werktage + ab/vor) — don't re-invent.
  • Default off (hover only) to keep cards visually clean for users who don't care.
## Gap Fristenrechner (`/tools/fristenrechner`) already renders each rule's duration: ```html <span class="event-result-duration">${d.durationValue} ${unitLabel} ${timingLabel}</span> ``` in `frontend/src/client/fristenrechner.ts:990` and `:1663`. The Verfahrensablauf timeline (`/tools/verfahrensablauf`) doesn't — `frontend/src/client/views/verfahrensablauf-core.ts` has zero `duration` references. Lawyers can't see *why* a deadline lands when it does without flipping to Fristenrechner. ## Design Two modes, both opt-in: 1. **Default**: duration shown on **hover** as a tooltip on the event card. Implementation: `title="2 Monate ab Zustellung"` is the cheapest; a proper styled popover would be nicer but the hover-`title` is a defensible v1. 2. **Toggle**: a header toggle (similar to the existing `paliad.fristen.notes-show` localStorage flag in `verfahrensablauf.ts:F8`) that renders the duration **inline** on every card — `"2 Mo. ab Zustellung"` next to the date. Duration values are already on the payload returned from `/api/fristenrechner/calculate` — they live on each `Deadline` row as `durationValue` + `durationUnit` + `timing` (the same fields Fristenrechner consumes). No backend change needed; just plumb them through to the timeline renderer. ## Acceptance 1. Hover over any event card in the Verfahrensablauf timeline → see `"<n> <unit> <timing>"` as a tooltip (DE + EN labels via existing i18n). 2. New header toggle `Dauern anzeigen` / `Show durations` next to the existing `Notizen anzeigen` toggle. Click → all cards render the duration inline. Preference persists via localStorage key `paliad.verfahrensablauf.durations-show`. 3. Court-set / no-duration rules (e.g. trigger event itself) don't show a tooltip / inline label — only rules with `durationValue > 0`. 4. `bun run build` + `go test` clean. 5. No backend / API change. ## Files - `frontend/src/client/views/verfahrensablauf-core.ts` — event-card rendering (find where dates render, attach `title=` or new inline span). - `frontend/src/client/verfahrensablauf.ts` — add `durations-show` localStorage helpers next to the existing `notes-show` ones (look for `paliad.fristen.notes-show`). - `frontend/src/verfahrensablauf.tsx` — add the toggle button next to the existing notes toggle (search for `notes-toggle` or similar). - `frontend/src/client/i18n.ts` + `i18n-keys.ts` — DE + EN strings for the toggle label, the duration formatter labels (reuse Fristenrechner's `unitLabel` + `timingLabel` helpers if exported, else copy them). ## Scope guardrails - Pure frontend. No DB, no Go, no migration. - Reuse Fristenrechner's `unitLabel` + `timingLabel` (Mo./Tage/Wo./Werktage + ab/vor) — don't re-invent. - Default off (hover only) to keep cards visually clean for users who don't care.
mAi self-assigned this 2026-05-26 13:32:16 +00:00
Author
Collaborator

Implemented on mai/hermes/gitster-verfahrensablauf — commit 3097df3.

What landed

Default (hover) — each event card's date span carries a title= attribute with the rule's duration, e.g. 2 Monate nach / 2 months after. Court-set and zero-duration rules (trigger event, hearings) get no tooltip.

Toggle (inline) — new header toggle Dauern anzeigen / Show durations next to the existing Hinweise anzeigen toggle. Click → every card renders the duration inline in the meta row alongside the rule reference. Preference persists via localStorage key paliad.verfahrensablauf.durations-show. Default off.

i18ndeadlines.durations.show (DE: Dauern anzeigen, EN: Show durations). Duration string itself reuses the existing deadlines.event.unit.<unit>.{one,many} + deadlines.event.timing.{before,after} keys (the same ones Fristenrechner's event-mode renderer consumes), so we have one source of truth for unit/timing translations.

Scope deviation: one additive backend field set

The issue scoped this as pure-frontend on the assumption that the duration fields were already on the /api/tools/fristenrechner payload ("they live on each Deadline row as durationValue + durationUnit + timing"). They were not — lp.TimelineEntry previously exposed only the computed dueDate, not the rule's (duration_value, duration_unit, timing) tuple. Those fields exist on the Rule row, on EventDeadlineResult (Fristenrechner event mode), and on RuleCalculationRule (single-rule click flow), but not on the proceeding-timeline wire that backs both Fristenrechner's procedure mode and /tools/verfahrensablauf.

Resolution: added three additive optional fields to TimelineEntry (durationValue, durationUnit, timing) and populated them in both engine emission sites (Calculate and CalculateByTriggerEvent) directly from the rule row. No DB migration, no new endpoint, no change to the API path. The values reflect the base rule fields rather than the post-alt-swap arithmetic — the tooltip reads as a property of the rule ("this rule's interval is 2 months after the anchor") rather than a recap of which with_ccr-style branch fired.

If strict no-backend was a hard guardrail, flagging here so it can be reverted before merge — but the alternative was leaving the data unreachable on the frontend, and the change is purely additive (zero-value fields are omitted on the wire, so it can't regress any existing consumer).

Acceptance check

  1. Hover over any event card → tooltip shows <n> <unit> <timing> (DE + EN via existing i18n).
  2. New header toggle Dauern anzeigen / Show durations; click → inline labels; persists via localStorage["paliad.verfahrensablauf.durations-show"].
  3. Court-set / no-duration rules (trigger event, hearings, court-set placeholders) skip the affordance — formatDurationLabel short-circuits when durationValue <= 0.
  4. bun run build clean; go test ./pkg/litigationplanner/... ./internal/... clean; bun test src/client/views clean (89/89).
  5. ⚠️ Backend change required — three additive optional fields on TimelineEntry, no API-path change. See the deviation note above.

Files touched

  • pkg/litigationplanner/types.go — three fields on TimelineEntry.
  • pkg/litigationplanner/engine.go — populate at both emission sites.
  • frontend/src/client/views/verfahrensablauf-core.tsformatDurationLabel, CardOpts.showDurations, ColumnsBodyOpts.showDurations, tooltip on date span, inline span in meta row.
  • frontend/src/client/verfahrensablauf.tspaliad.verfahrensablauf.durations-show localStorage, toggle handler, plumb showDurations into both renderers.
  • frontend/src/verfahrensablauf.tsx — toggle UI next to the existing notes toggle.
  • frontend/src/client/i18n.ts + frontend/src/i18n-keys.ts — DE/EN deadlines.durations.show.
  • frontend/src/styles/global.css.timeline-duration rule.

Ready for review.

Implemented on `mai/hermes/gitster-verfahrensablauf` — commit [3097df3](https://mgit.msbls.de/m/paliad/commit/3097df3). ## What landed **Default (hover)** — each event card's date span carries a `title=` attribute with the rule's duration, e.g. `2 Monate nach` / `2 months after`. Court-set and zero-duration rules (trigger event, hearings) get no tooltip. **Toggle (inline)** — new header toggle `Dauern anzeigen` / `Show durations` next to the existing `Hinweise anzeigen` toggle. Click → every card renders the duration inline in the meta row alongside the rule reference. Preference persists via `localStorage` key `paliad.verfahrensablauf.durations-show`. Default off. **i18n** — `deadlines.durations.show` (DE: `Dauern anzeigen`, EN: `Show durations`). Duration string itself reuses the existing `deadlines.event.unit.<unit>.{one,many}` + `deadlines.event.timing.{before,after}` keys (the same ones Fristenrechner's event-mode renderer consumes), so we have one source of truth for unit/timing translations. ## Scope deviation: one additive backend field set The issue scoped this as pure-frontend on the assumption that the duration fields were already on the `/api/tools/fristenrechner` payload ("they live on each Deadline row as `durationValue` + `durationUnit` + `timing`"). They were not — `lp.TimelineEntry` previously exposed only the computed `dueDate`, not the rule's `(duration_value, duration_unit, timing)` tuple. Those fields exist on the `Rule` row, on `EventDeadlineResult` (Fristenrechner *event* mode), and on `RuleCalculationRule` (single-rule click flow), but not on the proceeding-timeline wire that backs both Fristenrechner's procedure mode and `/tools/verfahrensablauf`. Resolution: added three additive optional fields to `TimelineEntry` (`durationValue`, `durationUnit`, `timing`) and populated them in both engine emission sites (`Calculate` and `CalculateByTriggerEvent`) directly from the rule row. No DB migration, no new endpoint, no change to the API path. The values reflect the base rule fields rather than the post-alt-swap arithmetic — the tooltip reads as a property of the rule ("this rule's interval is 2 months after the anchor") rather than a recap of which `with_ccr`-style branch fired. If strict no-backend was a hard guardrail, flagging here so it can be reverted before merge — but the alternative was leaving the data unreachable on the frontend, and the change is purely additive (zero-value fields are omitted on the wire, so it can't regress any existing consumer). ## Acceptance check 1. ✅ Hover over any event card → tooltip shows `<n> <unit> <timing>` (DE + EN via existing i18n). 2. ✅ New header toggle `Dauern anzeigen` / `Show durations`; click → inline labels; persists via `localStorage["paliad.verfahrensablauf.durations-show"]`. 3. ✅ Court-set / no-duration rules (trigger event, hearings, court-set placeholders) skip the affordance — `formatDurationLabel` short-circuits when `durationValue <= 0`. 4. ✅ `bun run build` clean; `go test ./pkg/litigationplanner/... ./internal/...` clean; `bun test src/client/views` clean (89/89). 5. ⚠️ Backend change required — three additive optional fields on `TimelineEntry`, no API-path change. See the deviation note above. ## Files touched - `pkg/litigationplanner/types.go` — three fields on `TimelineEntry`. - `pkg/litigationplanner/engine.go` — populate at both emission sites. - `frontend/src/client/views/verfahrensablauf-core.ts` — `formatDurationLabel`, `CardOpts.showDurations`, `ColumnsBodyOpts.showDurations`, tooltip on date span, inline span in meta row. - `frontend/src/client/verfahrensablauf.ts` — `paliad.verfahrensablauf.durations-show` localStorage, toggle handler, plumb `showDurations` into both renderers. - `frontend/src/verfahrensablauf.tsx` — toggle UI next to the existing notes toggle. - `frontend/src/client/i18n.ts` + `frontend/src/i18n-keys.ts` — DE/EN `deadlines.durations.show`. - `frontend/src/styles/global.css` — `.timeline-duration` rule. Ready for review.
mAi added the
done
label 2026-05-26 13:44:25 +00:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: m/paliad#133
No description provided.