Merge: t-paliad-323 Slice S6 — Fristenrechner cleanup (m/paliad#146 SHIPPED)
knuth shipped S6, the final slice of the Fristenrechner overhaul: - frontend/src/client/fristenrechner.ts shrinks by 137 LoC (legacy Pathway-B neutralised; row-stack subtree wired off behind ?legacy=1). - internal/handlers/fristenrechner_event_categories.go dropped — the /api/tools/fristenrechner/event-categories endpoint is gone (route deregistered in handlers.go). - paliad.event_categories table stays for future tools (the hidden 'Ich möchte einreichen' forward-workflow), per design §7-S6. - Deferred follow-ups (knuth's scope discipline): drop the legacy concept-card response shape from /search + lift the dead-code row-stack subtree out of fristenrechner.ts in a separate cleanup PR. Filed as scope note on m/paliad#146 (issuecomment-10414). S1-S6 complete: - S17ea4151— backend (search ?kind=events + /follow-ups) - S29ab8dd8— result view under ?overhaul=1 - S32a2c5b8— Mode A direct search - S470985d8— Mode B 5-row wizard - S54571bd4— flip overhaul default - S6ba3e079— cascade endpoint drop + legacy neutralise Procedure-mode (upper half of fristenrechner.tsx) untouched per design. paliad.event_categories table retained for future tools.
This commit is contained in:
@@ -2655,33 +2655,16 @@ interface EventCategoryNode {
|
||||
let eventCategoryTree: EventCategoryNode[] | null = null;
|
||||
let eventCategoryFetchInflight: Promise<EventCategoryNode[]> | 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<string> = 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<EventCategoryNode[]> {
|
||||
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);
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user