feat(webui): open contextual configured handbooks

This commit is contained in:
2026-07-21 18:42:31 +02:00
parent fa9119bea7
commit b3b0cf0fca
6 changed files with 46 additions and 11 deletions

View File

@@ -9,20 +9,23 @@ import { usePlatformModules } from "../platform/ModuleContext";
import type { PlatformWebModule } from "../types";
import { helpContextForPathname, helpQueryForContext, type HelpContext } from "../utils/helpContext";
import { usePlatformLanguage } from "../i18n/LanguageContext";
import type { AuthInfo } from "../types";
import { hasAnyScope } from "../utils/permissions";
const EXTERNAL_DOCS_BASE_URL = "https://govoplan.add-ideas.de";
export default function HelpMenu() {
export default function HelpMenu({ auth }: { auth: AuthInfo | null }) {
const [open, setOpen] = useState(false);
const [aboutOpen, setAboutOpen] = useState(false);
const [contextOpen, setContextOpen] = useState(false);
const wrapRef = useRef<HTMLDivElement>(null);
const location = useLocation();
const navigate = useGuardedNavigate();
const helpContext = helpContextForPathname(location.pathname);
const helpContext = helpContextForPathname(location.pathname, location.search);
const modules = usePlatformModules();
const { translateText } = usePlatformLanguage();
const docsAvailable = modules.some((module) => module.id === "docs" && module.routes?.some((route) => route.path === "/docs"));
const adminDocsAvailable = hasAnyScope(auth, ["docs:documentation:admin", "system:settings:read", "admin:settings:read"]);
useEffect(() => {
function openContextHelp(event: KeyboardEvent) {
@@ -53,6 +56,7 @@ export default function HelpMenu() {
function openDocs(type: "user" | "admin") {
setOpen(false);
setContextOpen(false);
if (!docsAvailable) {
window.open(externalDocsUrl(type, helpContext), "_blank", "noopener,noreferrer");
return;
@@ -87,15 +91,17 @@ export default function HelpMenu() {
<button className="dropdown-item" onClick={() => openDocs("user")} title={docsAvailable ? translateText("i18n:govoplan-core.open_user_documentation.084af515") : "Open hosted user documentation"}>
<BookOpen size={16} /> {translateText("i18n:govoplan-core.user_docs.1e38e8d3")}
</button>
<button className="dropdown-item" onClick={() => openDocs("admin")} title={docsAvailable ? translateText("i18n:govoplan-core.open_admin_documentation.6adbdae3") : "Open hosted admin documentation"}>
<BookOpen size={16} /> {translateText("i18n:govoplan-core.admin_docs.bf504a56")}
</button>
{adminDocsAvailable &&
<button className="dropdown-item" onClick={() => openDocs("admin")} title={docsAvailable ? translateText("i18n:govoplan-core.open_admin_documentation.6adbdae3") : "Open hosted admin documentation"}>
<BookOpen size={16} /> {translateText("i18n:govoplan-core.admin_docs.bf504a56")}
</button>
}
<hr />
<a className="dropdown-item" href="https://git.add-ideas.de/add-ideas" target="_blank" rel="noreferrer"><GitBranch size={16} /> i18n:govoplan-core.gitlab.9a9b09d9</a>
<button className="dropdown-item" onClick={() => {setAboutOpen(true);setOpen(false);}}><Info size={16} /> {translateText("i18n:govoplan-core.about.6b21fb79")}</button>
</div>
}
{contextOpen && <ContextHelpModal context={helpContext} onClose={() => setContextOpen(false)} />}
{contextOpen && <ContextHelpModal context={helpContext} onOpenDocs={() => openDocs("user")} onClose={() => setContextOpen(false)} />}
{aboutOpen && <AboutModal modules={modules} onClose={() => setAboutOpen(false)} />}
</div>);
@@ -106,7 +112,7 @@ function externalDocsUrl(type: "user" | "admin", context: HelpContext): string {
return `${EXTERNAL_DOCS_BASE_URL}/?${params.toString()}`;
}
function ContextHelpModal({ context, onClose }: {context: HelpContext;onClose: () => void;}) {
function ContextHelpModal({ context, onOpenDocs, onClose }: {context: HelpContext;onOpenDocs: () => void;onClose: () => void;}) {
const { translateText } = usePlatformLanguage();
return (
<Dialog
@@ -123,6 +129,7 @@ function ContextHelpModal({ context, onClose }: {context: HelpContext;onClose: (
<div className="help-panel-section">
<h3>{translateText("i18n:govoplan-core.next_actions.7b09055a")}</h3>
<p className="muted">{translateText("i18n:govoplan-core.the_first_guided_help_content_can_cover_campaign.14a6bd8a")}</p>
<Button onClick={onOpenDocs}><BookOpen size={16} /> {translateText("i18n:govoplan-core.open_user_documentation.084af515")}</Button>
</div>
</Dialog>);

View File

@@ -219,7 +219,7 @@ export default function Titlebar({ settings, auth, onAuthChange, maintenanceMode
<div className="titlebar-spacer" />
<LanguageMenu />
<HelpMenu />
<HelpMenu auth={auth} />
{auth && notificationsAvailable &&
<button className="titlebar-icon-link titlebar-notification-button" onClick={openNotificationCenter} title={translateText("i18n:govoplan-core.notifications.753a22b2")} aria-label={translateText("i18n:govoplan-core.notifications.753a22b2")}>

View File

@@ -31,16 +31,24 @@ const topLevelContexts: Record<string, Omit<HelpContext, "route">> = {
campaigns: { id: "campaigns.list", title: "i18n:govoplan-core.campaigns.01a23a28" },
templates: { id: "templates.list", title: "i18n:govoplan-core.templates.f25b700e" },
files: { id: "files.list", title: "i18n:govoplan-core.files.6ce6c512" },
mail: { id: "mail.list", title: "Mail" },
"address-book": { id: "address-book.list", title: "i18n:govoplan-core.address_book.f6327f59" },
reports: { id: "reports.list", title: "i18n:govoplan-core.reports.88bc3fe3" },
settings: { id: "app.settings", title: "i18n:govoplan-core.settings.c7f73bb5" },
admin: { id: "app.admin", title: "i18n:govoplan-core.admin.4e7afebc" }
};
export function helpContextForPathname(pathname: string): HelpContext {
export function helpContextForPathname(pathname: string, search = ""): HelpContext {
const route = pathname || "/";
const segments = route.split("/").filter(Boolean);
if (segments[0] === "settings") {
const section = new URLSearchParams(search).get("section") || "";
if (section === "mail-profiles") {
return { id: "mail.profiles", title: "Mail profiles", route: `${route}${search}` };
}
}
if (segments[0] === "campaigns" && segments[1]) {
if (!segments[2]) return { id: "campaign.overview", title: "i18n:govoplan-core.campaign_overview.43c3d159", route };
if (segments[2] === "wizard") {
@@ -63,4 +71,4 @@ export function helpQueryForContext(context: HelpContext): string {
function capitalize(value: string): string {
return value.replace(/-/g, " ").replace(/\b\w/g, (letter) => letter.toUpperCase());
}
}