From 48a07ef4ef4909677d701163cc9e313d89d872a3 Mon Sep 17 00:00:00 2001 From: mAi Date: Wed, 27 May 2026 20:29:05 +0200 Subject: [PATCH] feat(procedures): U3 fold Verfahrensablauf tree + 3-way detail filter (m/paliad#151) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mounts the full Verfahrensablauf wizard — proceeding picker, perspective chooser, date inputs, scenario flag rows, detail-mode toggle, view toggle, timeline-container — under the /tools/procedures "Verfahren wählen" tab. Per-rule scenario_flags chips (P0 SSoT) and the Aufnehmen/Entfernen affordances reach the unified page unchanged since they're delegated handlers on the timeline-container. Refactor steps: - Extracted the wizard body markup into a shared TSX component (components/VerfahrensablaufBody) used by both verfahrensablauf.tsx (legacy) and procedures.tsx (unified). U4 will retire the legacy page; the shared component lets U3 ship without code duplication. - Lifted the verfahrensablauf.ts DOMContentLoaded body into initVerfahrensablauf() and re-exported it. The legacy auto-boot stays in place but skips itself when #procedures-panel-proceeding is present, so the unified page imports the module without double-init. procedures.ts calls initVerfahrensablauf() the first time the proceeding tab activates, gated by a one-shot flag to preserve module-local selectedType / lastResponse across tab toggles. --- frontend/src/client/i18n.ts | 2 - frontend/src/client/procedures.ts | 19 +- frontend/src/client/verfahrensablauf.ts | 24 +- .../src/components/VerfahrensablaufBody.tsx | 293 ++++++++++++++ frontend/src/i18n-keys.ts | 1 - frontend/src/procedures.tsx | 15 +- frontend/src/verfahrensablauf.tsx | 356 +----------------- 7 files changed, 348 insertions(+), 362 deletions(-) create mode 100644 frontend/src/components/VerfahrensablaufBody.tsx diff --git a/frontend/src/client/i18n.ts b/frontend/src/client/i18n.ts index 81b328c..6a4b40e 100644 --- a/frontend/src/client/i18n.ts +++ b/frontend/src/client/i18n.ts @@ -218,7 +218,6 @@ const translations: Record> = { "procedures.tab.search": "Direkt suchen", "procedures.tab.wizard": "Gef\u00fchrt", "procedures.tab.akte": "Aus Akte", - "procedures.panel.proceeding.placeholder": "Verfahrenswahl folgt in U3 \u2014 das \u00dcbersichts-Baumdiagramm wird hier eingebettet.", "procedures.panel.akte.placeholder": "Akten-Einstieg folgt in einem sp\u00e4teren Slice.", "nav.procedures": "Verfahren & Fristen", @@ -3429,7 +3428,6 @@ const translations: Record> = { "procedures.tab.search": "Direct search", "procedures.tab.wizard": "Guided", "procedures.tab.akte": "From matter", - "procedures.panel.proceeding.placeholder": "Pick-proceeding view ships in U3 \u2014 the overview tree mounts here.", "procedures.panel.akte.placeholder": "Matter entry ships in a later slice.", "nav.procedures": "Procedures & Deadlines", diff --git a/frontend/src/client/procedures.ts b/frontend/src/client/procedures.ts index b933a09..0723a22 100644 --- a/frontend/src/client/procedures.ts +++ b/frontend/src/client/procedures.ts @@ -8,8 +8,8 @@ // // U0 — Skeleton + tab toggling. // U1 — Direkt suchen mounts Mode A. -// U2 — Geführt mounts Mode B wizard (this slice). -// U3 — Verfahren wählen mounts Verfahrensablauf tree + 3-way detail filter. +// U2 — Geführt mounts Mode B wizard. +// U3 — Verfahren wählen wires the Verfahrensablauf wizard + detail-mode toggle. // // Mode A renders its shell into #fristen-overhaul-root (replacing // children); Mode B renders into #fristen-overhaul-mode-host; the @@ -24,6 +24,7 @@ import { initSidebar } from "./sidebar"; import { mountModeA } from "./fristenrechner-mode-a"; import { mountResultView } from "./fristenrechner-result"; import { mountWizard } from "./fristenrechner-wizard"; +import { initVerfahrensablauf } from "./verfahrensablauf"; type ProceduresTab = "proceeding" | "search" | "wizard" | "akte"; @@ -80,6 +81,13 @@ function setActiveTabUI(tab: ProceduresTab): void { } } +// Verfahrensablauf wiring is idempotent-unfriendly (module-local +// selectedType + lastResponse + listeners that re-bind on every +// proceeding click). Wire it exactly once per page load; on subsequent +// activations the existing DOM + listeners are reused so picked +// proceeding / dates / flags persist across tab switches. +let verfahrensablaufWired = false; + async function activateTab(tab: ProceduresTab): Promise { setActiveTabUI(tab); if (tab === "search") { @@ -92,7 +100,12 @@ async function activateTab(tab: ProceduresTab): Promise { await mountWizard(); return; } - // U3 will mount Verfahrensablauf into proceeding-panel. + if (tab === "proceeding") { + if (!verfahrensablaufWired) { + initVerfahrensablauf(); + verfahrensablaufWired = true; + } + } } function wireTabs(): void { diff --git a/frontend/src/client/verfahrensablauf.ts b/frontend/src/client/verfahrensablauf.ts index dc76c12..3cdc058 100644 --- a/frontend/src/client/verfahrensablauf.ts +++ b/frontend/src/client/verfahrensablauf.ts @@ -1011,10 +1011,14 @@ function initPerspectiveControls() { }); } -document.addEventListener("DOMContentLoaded", () => { - initI18n(); - initSidebar(); - +// initVerfahrensablauf wires the entire Verfahrensablauf wizard against +// whatever DOM is currently present (proceeding-btn buttons, +// trigger-date input, flag checkboxes, timeline-container, …). +// Re-callable on demand: m/paliad#151 mounts this against the +// /tools/procedures "Verfahren wählen" tab the first time it activates. +// initI18n() + initSidebar() are NOT included here — both are page-boot +// concerns owned by whichever entrypoint hosts the wiring. +export function initVerfahrensablauf(): void { document.querySelectorAll(".proceeding-btn").forEach((btn) => { btn.addEventListener("click", () => selectProceeding(btn)); }); @@ -1257,4 +1261,16 @@ document.addEventListener("DOMContentLoaded", () => { const writeURL = urlProceeding !== "" && !urlHit; selectProceeding(initialBtn, { writeURL }); } +} + +// Legacy /tools/verfahrensablauf entrypoint auto-boot. The unified +// /tools/procedures page imports this module too but owns its own boot — +// the guard checks for the procedures-only #procedures-panel-proceeding +// element so the auto-boot doesn't fire twice. U4 drops the legacy page +// + this auto-boot together. +document.addEventListener("DOMContentLoaded", () => { + if (document.getElementById("procedures-panel-proceeding")) return; + initI18n(); + initSidebar(); + initVerfahrensablauf(); }); diff --git a/frontend/src/components/VerfahrensablaufBody.tsx b/frontend/src/components/VerfahrensablaufBody.tsx new file mode 100644 index 0000000..4951387 --- /dev/null +++ b/frontend/src/components/VerfahrensablaufBody.tsx @@ -0,0 +1,293 @@ +import { h } from "../jsx"; + +interface ProceedingDef { + code: string; + i18nKey: string; + name: string; +} + +function proceedingBtn(p: ProceedingDef): string { + return ( + + ); +} + +// Slice B1 (m/paliad#124 §18.1): the 3 separate Berufung tiles +// (upc.apl.merits / upc.apl.cost / upc.apl.order) collapse into ONE +// unified "Berufung" tile (upc.apl). After picking it, the user +// selects which decision the appeal is directed AT via the +// .appeal-target-row chip group below — the engine then filters +// rules whose applies_to_target contains the picked slug. +const UPC_TYPES: ProceedingDef[] = [ + { code: "upc.inf.cfi", i18nKey: "deadlines.upc.inf.cfi", name: "Verletzungsverfahren" }, + { code: "upc.rev.cfi", i18nKey: "deadlines.upc.rev.cfi", name: "Nichtigkeitsklage" }, + { code: "upc.ccr.cfi", i18nKey: "deadlines.upc.ccr.cfi", name: "Widerklage auf Nichtigkeit" }, + { code: "upc.pi.cfi", i18nKey: "deadlines.upc.pi.cfi", name: "Einstw. Maßnahmen" }, + { code: "upc.apl.unified", i18nKey: "deadlines.upc.apl.unified", name: "Berufung" }, + { code: "upc.dmgs.cfi", i18nKey: "deadlines.upc.dmgs.cfi", name: "Schadensbemessung" }, + { code: "upc.disc.cfi", i18nKey: "deadlines.upc.disc.cfi", name: "Bucheinsicht" }, +]; + +const DE_INF_TYPES: ProceedingDef[] = [ + { code: "de.inf.lg", i18nKey: "deadlines.de.inf.lg", name: "LG (1. Instanz)" }, + { code: "de.inf.olg", i18nKey: "deadlines.de.inf.olg", name: "OLG (Berufung)" }, + { code: "de.inf.bgh", i18nKey: "deadlines.de.inf.bgh", name: "BGH (Revision / NZB)" }, +]; + +const DE_NULL_TYPES: ProceedingDef[] = [ + { code: "de.null.bpatg", i18nKey: "deadlines.de.null.bpatg", name: "BPatG (1. Instanz)" }, + { code: "de.null.bgh", i18nKey: "deadlines.de.null.bgh", name: "BGH (Berufung)" }, +]; + +const EPA_TYPES: ProceedingDef[] = [ + { code: "epa.opp.opd", i18nKey: "deadlines.epa.opp.opd", name: "Einspruchsverfahren" }, + { code: "epa.opp.boa", i18nKey: "deadlines.epa.opp.boa", name: "Beschwerdeverfahren" }, + { code: "epa.grant.exa", i18nKey: "deadlines.epa.grant.exa", name: "EP-Erteilungsverfahren" }, +]; + +const DPMA_TYPES: ProceedingDef[] = [ + { code: "dpma.opp.dpma", i18nKey: "deadlines.dpma.opp.dpma", name: "Einspruch DPMA" }, + { code: "dpma.appeal.bpatg", i18nKey: "deadlines.dpma.appeal.bpatg", name: "Beschwerde BPatG (DPMA)" }, + { code: "dpma.appeal.bgh", i18nKey: "deadlines.dpma.appeal.bgh", name: "Rechtsbeschwerde BGH" }, +]; + +// Shared Verfahrensablauf wizard body. Renders the proceeding picker, +// perspective + date inputs, scenario flag rows, detail-mode toggle, +// view toggle, and the timeline-container that client/verfahrensablauf.ts +// (via initVerfahrensablauf()) wires against. Used by both +// /tools/verfahrensablauf (legacy) and /tools/procedures (unified). +export function VerfahrensablaufBody({ todayIso }: { todayIso: string }): string { + return ( +
+
+

+ 1 + Verfahrensart wählen +

+ +
+

UPC

+
+ {UPC_TYPES.map((p) => proceedingBtn(p))} +
+
+ +
+

Deutsche Gerichte

+
+
Verletzungsverfahren
+
+ {DE_INF_TYPES.map((p) => proceedingBtn(p))} +
+
+
+
Nichtigkeitsverfahren
+
+ {DE_NULL_TYPES.map((p) => proceedingBtn(p))} +
+
+
+ +
+

EPA

+
+ {EPA_TYPES.map((p) => proceedingBtn(p))} +
+
+ +
+

DPMA

+
+ {DPMA_TYPES.map((p) => proceedingBtn(p))} +
+
+ + +
+ + + + +
+ ); +} diff --git a/frontend/src/i18n-keys.ts b/frontend/src/i18n-keys.ts index 8b1958b..e64e18a 100644 --- a/frontend/src/i18n-keys.ts +++ b/frontend/src/i18n-keys.ts @@ -2212,7 +2212,6 @@ export type I18nKey = | "procedures.filter.search.placeholder" | "procedures.heading" | "procedures.panel.akte.placeholder" - | "procedures.panel.proceeding.placeholder" | "procedures.subtitle" | "procedures.tab.akte" | "procedures.tab.proceeding" diff --git a/frontend/src/procedures.tsx b/frontend/src/procedures.tsx index 385fabd..a4bea0f 100644 --- a/frontend/src/procedures.tsx +++ b/frontend/src/procedures.tsx @@ -4,6 +4,7 @@ import { PaliadinWidget } from "./components/PaliadinWidget"; import { BottomNav } from "./components/BottomNav"; import { Footer } from "./components/Footer"; import { PWAHead } from "./components/PWAHead"; +import { VerfahrensablaufBody } from "./components/VerfahrensablaufBody"; // U0 — Skeleton for the unified procedural-events tool // (m/paliad#151, design docs/design-unified-procedural-events-tool-2026-05-27.md). @@ -22,6 +23,7 @@ import { PWAHead } from "./components/PWAHead"; // later slices mount their UI into. No data wiring. export function renderProcedures(): string { + const today = new Date().toISOString().split("T")[0]; return "" + ( @@ -141,9 +143,16 @@ export function renderProcedures(): string { Each later slice fills the corresponding host. */}
-
- Verfahrenswahl folgt in U3 — das Übersichts-Baumdiagramm wird hier eingebettet. -
+ {/* Verfahrensablauf wizard body — shared TSX component + used by /tools/verfahrensablauf (legacy) and the + unified /tools/procedures page. procedures.ts calls + initVerfahrensablauf() on the first activation of + this tab, which wires the .proceeding-btn clicks, + timeline-container, detail-mode toggle, etc. against + the markup. The legacy page's auto-boot is guarded + against the procedures-only #procedures-panel-proceeding + element so it doesn't fire twice. */} +