feat(sidebar): add Verfahrensablauf nav entry

t-paliad-168 deliverable 2. New "Verfahrensablauf" entry under
Werkzeuge, right after Fristenrechner — opens
/tools/fristenrechner?path=a (Pathway A wizard, browse-/learn-mode).

Uses a distinct open-book icon to read separate from the closed-book
Glossar. Both /tools/fristenrechner sidebar entries share the same
pathname, so SSR navItem matching can't pick the right "active" one
on its own — fixVerfahrensablaufActive() in sidebar.ts disambiguates
based on ?path=a at hydration.

i18n key: nav.verfahrensablauf (DE: "Verfahrensablauf",
EN: "Procedure Roadmap"). i18n-keys.ts is regenerated by build.ts.
This commit is contained in:
m
2026-05-08 23:04:29 +02:00
parent 7238b12b05
commit 7fef64159b
3 changed files with 33 additions and 0 deletions

View File

@@ -75,6 +75,7 @@ export function initSidebar() {
initPaliadinLinks();
initUserViewsGroup();
initThemeToggle();
fixVerfahrensablaufActive();
const sidebar = document.querySelector<HTMLElement>(".sidebar");
if (!sidebar) return;
initSidebarResize(sidebar);
@@ -443,6 +444,30 @@ function initUserViewsGroup(): void {
});
}
// fixVerfahrensablaufActive disambiguates the two /tools/fristenrechner
// sidebar entries (t-paliad-168). The SSR navItem helper compares
// hrefs against pathname only, which can't tell ?path=a apart from
// the no-query Fristenrechner — both would render as Fristenrechner=
// active. At the client we know the search params; flip the active
// class so the sidebar lights up the entry the user actually opened.
function fixVerfahrensablaufActive(): void {
if (window.location.pathname !== "/tools/fristenrechner") return;
const path = new URLSearchParams(window.location.search).get("path");
const fristenrechner = document.querySelector<HTMLAnchorElement>(
'a.sidebar-item[href="/tools/fristenrechner"]',
);
const verfahrensablauf = document.querySelector<HTMLAnchorElement>(
'a.sidebar-item[href="/tools/fristenrechner?path=a"]',
);
if (path === "a") {
fristenrechner?.classList.remove("active");
verfahrensablauf?.classList.add("active");
} else {
verfahrensablauf?.classList.remove("active");
fristenrechner?.classList.add("active");
}
}
function renderUserViewItem(view: UserViewLite, currentPath: string): HTMLElement {
const a = document.createElement("a");
a.href = `/views/${encodeURIComponent(view.slug)}`;

View File

@@ -7,6 +7,10 @@ const ICON_CLOCK = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" s
const ICON_DOWNLOAD = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>';
const ICON_LINK = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg>';
const ICON_BOOK = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></svg>';
// Open-book icon for the /tools/fristenrechner?path=a "Verfahrensablauf"
// nav entry (t-paliad-168). Distinct from ICON_BOOK (Glossar, closed)
// so the two affordances read as different at a glance.
const ICON_BOOK_OPEN = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M2 4h7a3 3 0 0 1 3 3v13a2 2 0 0 0-2-2H2z"/><path d="M22 4h-7a3 3 0 0 0-3 3v13a2 2 0 0 1 2-2h8z"/></svg>';
const ICON_TABLE = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><line x1="3" y1="9" x2="21" y2="9"/><line x1="3" y1="15" x2="21" y2="15"/><line x1="9" y1="3" x2="9" y2="21"/></svg>';
const ICON_CHECK = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M9 11l3 3L22 4"/><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/></svg>';
const ICON_GLOBE = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10A15.3 15.3 0 0 1 12 2z"/></svg>';
@@ -157,6 +161,7 @@ export function Sidebar({ currentPath, authenticated = true }: SidebarProps): st
Gerichte / Glossar), then content (Links / Downloads). */}
{group("nav.group.werkzeuge", "Werkzeuge",
navItem("/tools/fristenrechner", ICON_CLOCK, "nav.fristenrechner", "Fristenrechner", currentPath) +
navItem("/tools/fristenrechner?path=a", ICON_BOOK_OPEN, "nav.verfahrensablauf", "Verfahrensablauf", currentPath) +
navItem("/tools/kostenrechner", ICON_CALC, "nav.kostenrechner", "Kostenrechner", currentPath) +
navItem("/tools/gebuehrentabellen", ICON_TABLE, "nav.gebuehrentabellen", "Gebührentabellen", currentPath) +
navItem("/checklists", ICON_CHECK, "nav.checklisten", "Checklisten", currentPath) +

View File

@@ -970,6 +970,8 @@ export type I18nKey =
| "deadlines.step1.selected"
| "deadlines.step1.summary.adhoc.suffix"
| "deadlines.step2"
| "deadlines.step2.browse.desc"
| "deadlines.step2.browse.title"
| "deadlines.step2.file.desc"
| "deadlines.step2.file.title"
| "deadlines.step2.happened.desc"
@@ -1466,6 +1468,7 @@ export type I18nKey =
| "nav.team"
| "nav.termine"
| "nav.user_views.new"
| "nav.verfahrensablauf"
| "notes.cancel"
| "notes.delete"
| "notes.delete.confirm"