feat(fristenrechner/inbox-chip): wire inbox into B1 cascade narrowing
Completes the #15 vision: the inbox chip now narrows the B1 decision tree alongside Pathway A's picker and B2's fine-bucket forum filter. Picking CMS hides DE / EPA / DPMA cascade entries; picking beA / Posteingang hides UPC / EPA / DPMA entries. Neutral nodes (top-level branches, Mündliche Verhandlung sub-states, court-generic events like Ladung / Kostenfestsetzung) stay visible from every inbox setting so the user can always reach the cross-jurisdictional middle of the tree. Migration 065 adds paliad.event_categories.forums (text[]) with a CHECK on {upc, de, epa, dpma}, a partial GIN index, and a two-step backfill: 1. Regex on slug for nodes that carry the forum token explicitly. Token-bounded by ^/./- so .dpma doesn't trip the de pattern. 2. Explicit slug list for stragglers (BGH / BPatG / Versäumnisurteil / Hinweisbeschluss are DE-only; r116-eingaben is EPA-only). NULL stays neutral. Migration applied to live Supabase; tracker at v65. Backend: EventCategoryNode JSON gains an optional `forums` array; EventCategoryService.Tree SELECT includes the column and threads it through to the response. Frontend: new module-level currentInboxChannel mirrors the chip state so renderB1Cascade can ask "which forum is active?" without re-deriving from the URL on every step. inboxFilterAllowsForums(forums) gates each child node — neutral arrays (undefined / empty) always pass; tagged arrays must include the active forum. applyInboxFilter re-renders the cascade so chip clicks reflow B1 in place. Pathway A picker filter and B2 fine-bucket sync remain orthogonal — same chip, three filters. Refs m/paliad#15 (B1 follow-up).
This commit is contained in:
@@ -2409,6 +2409,9 @@ interface EventCategoryNode {
|
||||
icon?: string;
|
||||
sort_order: number;
|
||||
is_leaf: boolean;
|
||||
// #15 follow-up: coarse forum tags ('upc' | 'de' | 'epa' | 'dpma').
|
||||
// Empty / undefined = neutral, node visible from every inbox setting.
|
||||
forums?: string[];
|
||||
children?: EventCategoryNode[];
|
||||
}
|
||||
|
||||
@@ -2494,7 +2497,12 @@ function renderB1Cascade(currentSlug: string) {
|
||||
|
||||
const trail = buildBreadcrumb(eventCategoryTree, currentSlug);
|
||||
const node = trail.length > 0 ? trail[trail.length - 1] : null;
|
||||
const childScope = node ? (node.children || []) : eventCategoryTree;
|
||||
const rawChildScope = node ? (node.children || []) : eventCategoryTree;
|
||||
// #15 follow-up: drop children whose forums tag doesn't match the
|
||||
// active inbox channel. Nodes with no forums (neutral) stay visible
|
||||
// so the user always reaches court-event / Schriftsatz parents that
|
||||
// span jurisdictions.
|
||||
const childScope = rawChildScope.filter((c) => inboxFilterAllowsForums(c.forums));
|
||||
|
||||
const breadcrumbHtml = trail.length === 0
|
||||
? ""
|
||||
@@ -2804,6 +2812,13 @@ type InboxChannel = "cms" | "bea" | "posteingang" | null;
|
||||
|
||||
const INBOX_CHANNEL_VALUES = new Set<string>(["cms", "bea", "posteingang"]);
|
||||
|
||||
// currentInboxChannel mirrors the chip's active state so the B1 cascade
|
||||
// renderer (which lives in a different section of this file) can ask
|
||||
// "which forum is active right now?" without re-deriving from URL on
|
||||
// every render. Updated by applyInboxFilter on hydrate / click /
|
||||
// popstate.
|
||||
let currentInboxChannel: InboxChannel = null;
|
||||
|
||||
function readInboxFromURL(): InboxChannel {
|
||||
const raw = new URLSearchParams(window.location.search).get("inbox");
|
||||
return raw && INBOX_CHANNEL_VALUES.has(raw) ? (raw as InboxChannel) : null;
|
||||
@@ -2854,6 +2869,7 @@ function applyFineForumsFromInbox(ch: InboxChannel) {
|
||||
}
|
||||
|
||||
function applyInboxFilter(ch: InboxChannel) {
|
||||
currentInboxChannel = ch;
|
||||
const forum = inboxChannelToForum(ch);
|
||||
|
||||
document.querySelectorAll<HTMLButtonElement>(".fristen-inbox-chip").forEach((btn) => {
|
||||
@@ -2871,6 +2887,25 @@ function applyInboxFilter(ch: InboxChannel) {
|
||||
const groupForum = g.dataset.forum || "";
|
||||
g.hidden = forum !== null && groupForum !== forum;
|
||||
});
|
||||
|
||||
// Re-render the B1 cascade so its button set picks up the new forum
|
||||
// narrowing. Render is a no-op if the tree hasn't loaded yet or the
|
||||
// cascade DOM isn't mounted (Pathway B not visible) — both guards
|
||||
// already inside renderB1Cascade.
|
||||
if (eventCategoryTree) {
|
||||
renderB1Cascade(readB1PathFromURL());
|
||||
}
|
||||
}
|
||||
|
||||
// inboxFilterAllowsForums returns true when a node with the given
|
||||
// forums tags should be visible under the current inbox chip. Neutral
|
||||
// nodes (forums undefined / empty) are always visible. When no inbox
|
||||
// is active, every node is visible.
|
||||
function inboxFilterAllowsForums(forums: string[] | undefined): boolean {
|
||||
if (!forums || forums.length === 0) return true;
|
||||
const active = inboxChannelToForum(currentInboxChannel);
|
||||
if (active === null) return true;
|
||||
return forums.includes(active);
|
||||
}
|
||||
|
||||
async function persistInboxPref(ch: InboxChannel) {
|
||||
|
||||
Reference in New Issue
Block a user