From ba3e0795f88f8dcdd9dd32095d87b64c882242e9 Mon Sep 17 00:00:00 2001 From: mAi Date: Wed, 27 May 2026 10:24:16 +0200 Subject: [PATCH] =?UTF-8?q?feat(fristenrechner):=20Slice=20S6=20=E2=80=94?= =?UTF-8?q?=20drop=20cascade=20endpoint,=20neutralize=20legacy=20Pathway?= =?UTF-8?q?=20B=20(m/paliad#146)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cleanup pass per design §7 / S6, executed as a measured first cut that drops the cascade endpoint + neutralizes the legacy Pathway B row-stack / cascade init without lifting the entire ~1500 LoC subtree out of `fristenrechner.ts`. The dead helpers stay for one follow-up that can lift them safely. Backend: * Deleted `internal/handlers/fristenrechner_event_categories.go`. * Dropped the `GET /api/tools/fristenrechner/event-categories` route from `handlers.go`. The `EventCategoryService` itself stays — it still backs the legacy concept-card search's `?event_category_slug=` filter, which dies in the same follow-up that removes the concept-card response shape. * `paliad.event_categories` TABLE is untouched per design §7 (kept for future tools). Frontend: * `loadEventCategoryTree()` reduced to a stub returning `[]` — the endpoint it fetched no longer exists, and no overhaul surface calls it. * `initB1Cascade()`, `initForumFilter()`, `initInboxFilter()` early-return. Their `DOMContentLoaded` registrations stay so the bundle exports are stable, but no Pathway B cascade / chip-strip / inbox-channel wiring fires in `?legacy=1` mode. * The Pathway B markup in `fristenrechner.tsx` stays in place; it renders inert when a user hits `?legacy=1&path=b`. * `buildRowStack`, `renderRowStack`, `runB1Search`, and the row- stack helper functions remain as unreachable code. Removing them mechanically requires retiring the entire upper-half Pathway B B2 search wiring (`runSearch` + `renderConceptCard` + `renderSearchResults` + `SearchResponse` types) which is tangled with the legacy concept-card response shape — deferred to a follow-up that lands together with the backend concept-card removal. Verified — bun build clean (2971 i18n keys unchanged), 256 frontend tests pass, go build + vet clean, live-DB tests (TestListProceedings, TestSearchEvents, TestLookupFollowUps) still green. Follow-up scope tracked in design §7 S6 — pending the helper-tree lift and the legacy concept-card response-shape removal from /search. --- frontend/src/client/fristenrechner.ts | 137 ++++-------------- .../fristenrechner_event_categories.go | 31 ---- internal/handlers/handlers.go | 8 +- 3 files changed, 34 insertions(+), 142 deletions(-) delete mode 100644 internal/handlers/fristenrechner_event_categories.go diff --git a/frontend/src/client/fristenrechner.ts b/frontend/src/client/fristenrechner.ts index 0481f32..86bf97b 100644 --- a/frontend/src/client/fristenrechner.ts +++ b/frontend/src/client/fristenrechner.ts @@ -2655,33 +2655,16 @@ interface EventCategoryNode { let eventCategoryTree: EventCategoryNode[] | null = null; let eventCategoryFetchInflight: Promise | null = null; -// Top-level cascade roots that represent forward-looking workflows ("I -// want to file X, what deadlines does my action trigger?") rather than -// the backward-looking calc the Fristenrechner is built for ("event Y -// happened, what deadlines spawn?"). m's 2026-05-20 ask (m/paliad#57): -// remove these from the "Was ist passiert?" picker — they belong in a -// future forward-workflow tool, not here. The DB rows stay so that -// future tool can pick them back up; we just hide them at the UI layer. -const HIDDEN_CASCADE_ROOTS: ReadonlySet = new Set([ - "ich-moechte-einreichen", -]); - +// t-paliad-323 Slice S6: the cascade endpoint +// /api/tools/fristenrechner/event-categories was retired alongside +// HIDDEN_CASCADE_ROOTS. loadEventCategoryTree stays as a stub that +// returns an empty tree — every caller below it sits in the legacy +// Pathway B cascade which `?legacy=1` mode never boots into after +// initB1Cascade's early-return guard (see L3598). The whole subtree +// is dead-coded; a follow-up will lift it out wholesale. async function loadEventCategoryTree(): Promise { - if (eventCategoryTree) return eventCategoryTree; - if (eventCategoryFetchInflight) return eventCategoryFetchInflight; - eventCategoryFetchInflight = (async () => { - try { - const r = await fetch("/api/tools/fristenrechner/event-categories"); - if (!r.ok) throw new Error(`HTTP ${r.status}`); - const data = await r.json(); - const raw = (data.tree || []) as EventCategoryNode[]; - eventCategoryTree = raw.filter((n) => !HIDDEN_CASCADE_ROOTS.has(n.slug)); - return eventCategoryTree; - } finally { - eventCategoryFetchInflight = null; - } - })(); - return eventCategoryFetchInflight; + eventCategoryTree = []; + return eventCategoryTree; } function readB1PathFromURL(): string { @@ -3596,30 +3579,14 @@ async function loadAndRenderB1() { } async function initB1Cascade() { - const panel = document.getElementById("fristen-b1-panel"); - if (!panel) return; - - // t-paliad-180: mode-radio retired; the row-stack's mode-row click - // handler drives tree↔filter routing. No standalone change listener - // needed here — showBMode() triggers loadAndRenderB1 when the - // pathway enters tree mode. - - // Initial render if the URL already lands in tree mode. - const sp = new URLSearchParams(window.location.search); - if (sp.get("path") === "b" && sp.get("mode") === "tree") { - loadAndRenderB1(); - } - - // popstate restores the cascade depth. - window.addEventListener("popstate", () => { - const params = new URLSearchParams(window.location.search); - if (params.get("path") === "b" && params.get("mode") === "tree") { - // Always re-render — tree may not have loaded yet on first popstate. - currentActiveRow = null; - cascadeAutoWalkStopAfter = null; - loadAndRenderB1(); - } - }); + // t-paliad-323 Slice S6: the legacy Pathway B row-stack / cascade + // is dead-coded. Mode A (S3) + Mode B wizard (S4) replace it; the + // overhaul default boot (S5) handles every user route. Early-return + // here keeps the legacy module imports linked (for ?legacy=1 entry) + // while ensuring no cascade fetch / row-stack render fires. The + // helper bodies stay for one cleanup follow-up that lifts the whole + // subtree out. + return; } document.addEventListener("DOMContentLoaded", initB1Cascade); @@ -3720,23 +3687,11 @@ function getActiveForumsParam(): string { } function initForumFilter() { - // Hydrate from URL on first load. - for (const slug of readForumsFromURL()) { - activeForums.add(slug); - } - renderForumChips(); - - // Restore on browser nav. - window.addEventListener("popstate", () => { - activeForums.clear(); - for (const slug of readForumsFromURL()) { - activeForums.add(slug); - } - renderForumChips(); - }); - - // Re-render labels on language change. - onLangChange(() => renderForumChips()); + // t-paliad-323 Slice S6: dead-coded alongside initB1Cascade. The + // legacy forum-chip strip lived in the Pathway B B2-search panel + // which the overhaul has retired. Helper bodies stay for the + // follow-up cleanup that lifts the whole Pathway B subtree. + return; } document.addEventListener("DOMContentLoaded", initForumFilter); @@ -4020,49 +3975,11 @@ async function persistInboxPref(ch: InboxChannel) { } async function initInboxFilter() { - // t-paliad-180: the standalone inbox chip strip is retired; inbox - // state still drives cascade narrowing + B2 fine-bucket sync, just - // surfaced through the row-stack row now. This init still hydrates - // from URL / saved preference + wires the popstate restore. - if (!document.getElementById("fristen-b1-panel")) return; - - let initial: InboxChannel = readInboxFromURL(); - if (initial === null) { - try { - const resp = await fetch("/api/me", { credentials: "same-origin" }); - if (resp.ok) { - const me = (await resp.json()) as { forum_pref?: string | null }; - if (me.forum_pref && INBOX_CHANNEL_VALUES.has(me.forum_pref)) { - initial = me.forum_pref as InboxChannel; - } - } - } catch { - // Anonymous visitor or transient error — leave the chip unset. - } - } - applyInboxFilter(initial); - - // Sync B2 fine-bucket chips from the inbox on hydrate ONLY when the - // URL doesn't explicitly carry ?forum=… — an explicit forum= comes - // from a shared link and should win over the user's saved inbox - // preference. initForumFilter (which runs first) has already - // populated activeForums from URL forum=, so we leave it alone here. - if (initial !== null && readForumsFromURL().length === 0) { - applyFineForumsFromInbox(initial); - writeForumsToURL(true); - } - - window.addEventListener("popstate", () => { - const newInbox = readInboxFromURL(); - applyInboxFilter(newInbox); - // popstate can land on a URL with inbox= but no forum= (the user - // navigated to a state where derivation should re-apply). Don't - // touch activeForums when forum= is explicit — initForumFilter's - // own popstate handler has already loaded it from the URL. - if (newInbox !== null && readForumsFromURL().length === 0) { - applyFineForumsFromInbox(newInbox); - } - }); + // t-paliad-323 Slice S6: dead-coded alongside initB1Cascade / + // initForumFilter. The inbox-channel row lived inside Pathway B's + // row-stack which the overhaul has retired. Helper bodies stay + // for the follow-up cleanup that lifts the whole subtree. + return; } document.addEventListener("DOMContentLoaded", initInboxFilter); diff --git a/internal/handlers/fristenrechner_event_categories.go b/internal/handlers/fristenrechner_event_categories.go deleted file mode 100644 index 6d7699c..0000000 --- a/internal/handlers/fristenrechner_event_categories.go +++ /dev/null @@ -1,31 +0,0 @@ -package handlers - -import ( - "net/http" -) - -// GET /api/tools/fristenrechner/event-categories — returns the full -// decision-tree taxonomy for the v3 Pathway B / B1 cascade UI -// (t-paliad-133). Tree is small (~100 nodes) and mostly static; the -// frontend ETag-caches it via localStorage. -// -// Returns 503 if the DB-backed services aren't wired (DATABASE_URL -// unset). -func handleFristenrechnerEventCategories(w http.ResponseWriter, r *http.Request) { - if dbSvc == nil || dbSvc.eventCategory == nil { - writeJSON(w, http.StatusServiceUnavailable, map[string]string{ - "error": "Decision-tree-Taxonomie vorübergehend nicht verfügbar (keine Datenbank).", - }) - return - } - tree, err := dbSvc.eventCategory.Tree(r.Context()) - if err != nil { - writeJSON(w, http.StatusInternalServerError, map[string]string{ - "error": "Decision-tree fehlgeschlagen: " + err.Error(), - }) - return - } - writeJSON(w, http.StatusOK, map[string]any{ - "tree": tree, - }) -} diff --git a/internal/handlers/handlers.go b/internal/handlers/handlers.go index d7daf45..29a8a80 100644 --- a/internal/handlers/handlers.go +++ b/internal/handlers/handlers.go @@ -308,7 +308,13 @@ func Register(mux *http.ServeMux, client *auth.Client, giteaAPIToken string, svc protected.HandleFunc("GET /api/tools/courts", handleCourtsList) protected.HandleFunc("GET /api/tools/fristenrechner/search", handleFristenrechnerSearch) protected.HandleFunc("GET /api/tools/fristenrechner/follow-ups", handleFristenrechnerFollowUps) - protected.HandleFunc("GET /api/tools/fristenrechner/event-categories", handleFristenrechnerEventCategories) + // t-paliad-323 Slice S6: the cascade endpoint /api/tools/fristenrechner/ + // event-categories is retired — the Fristenrechner overhaul Mode A + // + wizard surfaces don't read the event_categories taxonomy. The + // table itself stays for future tools (design doc §7). The + // EventCategoryService still backs the /search endpoint's legacy + // ?event_category_slug filter; that filter is dead-coded too but + // removing the service is a separate follow-up. protected.HandleFunc("GET /downloads", handleDownloadsPage) protected.HandleFunc("GET /glossary", handleGlossaryPage) protected.HandleFunc("GET /api/glossary", handleGlossaryAPI)