chore: sync GovOPlaN module split state

This commit is contained in:
2026-07-10 12:51:21 +02:00
parent 670f0d9b6f
commit 5413ddfc32
22 changed files with 2732 additions and 27 deletions

169
webui/src/api/docs.ts Normal file
View File

@@ -0,0 +1,169 @@
import { apiFetch, type ApiSettings } from "@govoplan/core-webui";
export type DocsModule = {
id: string;
name: string;
version: string;
dependencies: string[];
optional_dependencies: string[];
permission_count: number;
role_template_count: number;
nav_count: number;
route_count: number;
frontend_package?: string | null;
backend_route_contributed: boolean;
migration_module_id?: string | null;
capabilities: string[];
documentation_count: number;
documentation_provider_count: number;
};
export type DocsRoute = {
module_id: string;
path: string;
label: string;
icon?: string | null;
section?: string | null;
source: string;
component?: string | null;
required_all: string[];
required_any: string[];
order: number;
visible: boolean;
reason: string;
};
export type DocsPermission = {
scope: string;
label: string;
description: string;
category: string;
level: "system" | "tenant";
module_id: string;
resource: string;
action: string;
deprecated: boolean;
granted: boolean;
};
export type DocsOptionalModuleEvidence = {
module_id: string;
source_module_id: string;
status: "installed" | "not_installed" | string;
reason: string;
};
export type DocsSource = {
label: string;
source: string;
layer: string;
};
export type DocsDocumentationCondition = {
required_modules: string[];
any_modules: string[];
missing_modules: string[];
required_capabilities: string[];
required_scopes: string[];
any_scopes: string[];
configuration_keys: string[];
};
export type DocsDocumentationLink = {
label: string;
href: string;
kind: string;
};
export type DocsTopicKind = "workflow" | "reference" | "pattern" | "system" | string;
export type DocsDocumentationTopic = {
id: string;
source_module_id: string;
kind: DocsTopicKind;
anchor_id: string;
title: string;
summary: string;
body: string;
layer: "always" | "configured" | "available" | "evidence" | string;
target_layer: string;
documentation_types: Array<"admin" | "user" | string>;
active: boolean;
reason: string;
blockers: {
modules: string[];
capabilities: string[];
scopes: string[];
};
audience: string[];
order: number;
i18n_key: string;
locale: string;
translation_locale: string;
conditions: DocsDocumentationCondition[];
links: DocsDocumentationLink[];
related_modules: string[];
unlocks: string[];
configuration_keys: string[];
metadata: Record<string, unknown>;
};
export type DocsContext = {
actor: {
tenant_id: string;
user_id: string;
scope_count: number;
documentation_type: "admin" | "user";
locale: string;
};
summary: {
module_count: number;
visible_route_count: number;
available_route_count: number;
permission_count: number;
granted_permission_count: number;
optional_module_count: number;
documentation_topic_count: number;
configured_documentation_topic_count: number;
workflow_topic_count: number;
reference_topic_count: number;
pattern_topic_count: number;
system_topic_count: number;
};
topic_groups: {
workflow: DocsDocumentationTopic[];
reference: DocsDocumentationTopic[];
pattern: DocsDocumentationTopic[];
system: DocsDocumentationTopic[];
[kind: string]: DocsDocumentationTopic[];
};
layers: {
always: {
documentation: DocsDocumentationTopic[];
};
configured: {
modules: DocsModule[];
routes: DocsRoute[];
permissions: DocsPermission[];
documentation: DocsDocumentationTopic[];
};
available: {
routes: DocsRoute[];
permissions: DocsPermission[];
documentation: DocsDocumentationTopic[];
};
evidence: {
optional_modules: DocsOptionalModuleEvidence[];
sources: DocsSource[];
documentation: DocsDocumentationTopic[];
};
};
};
export function fetchDocsContext(settings: ApiSettings, options: { documentationType?: "admin" | "user"; locale?: string } = {}): Promise<DocsContext> {
const params = new URLSearchParams();
if (options.documentationType) params.set("type", options.documentationType);
if (options.locale) params.set("locale", options.locale);
const query = params.toString();
return apiFetch(settings, `/api/v1/docs/context${query ? `?${query}` : ""}`);
}

View File

@@ -0,0 +1,950 @@
import { useEffect, useMemo, useState } from "react";
import { Link, useLocation } from "react-router-dom";
import { ChevronDown, ChevronRight, RefreshCw } from "lucide-react";
import {
Button,
DismissibleAlert,
ExplorerTree,
LoadingFrame,
PageTitle,
SegmentedControl,
StatusBadge,
adminErrorMessage,
useGuardedNavigate,
usePlatformLanguage,
type ApiSettings
} from "@govoplan/core-webui";
import {
fetchDocsContext,
type DocsContext,
type DocsDocumentationTopic,
type DocsModule,
type DocsOptionalModuleEvidence,
type DocsRoute,
type DocsSource
} from "../../api/docs";
type DocumentationType = "admin" | "user";
type DocsPageNode =
| {
id: string;
title: string;
kind: "topic";
topic: DocsDocumentationTopic;
}
| {
id: string;
title: string;
kind: "topic-list";
topics: DocsDocumentationTopic[];
emptyText: string;
}
| {
id: string;
title: string;
kind: "admin-reference";
};
type DocsTreeNode = {
id: string;
title: string;
page: DocsPageNode;
children: DocsTreeNode[];
};
type OutlineItem = {
id: string;
label: string;
};
export default function DocsPage({ settings }: { settings: ApiSettings }) {
const location = useLocation();
const navigate = useGuardedNavigate();
const { language } = usePlatformLanguage();
const [context, setContext] = useState<DocsContext | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState("");
const [documentationType, setDocumentationType] = useState<DocumentationType>(() => documentationTypeFromSearch(location.search));
const [expandedNodes, setExpandedNodes] = useState<Set<string>>(() => new Set());
const locale = localeFromSearch(location.search) ?? language;
const adminDocs = documentationType === "admin";
async function load() {
setLoading(true);
setError("");
try {
setContext(await fetchDocsContext(settings, { documentationType, locale }));
} catch (err) {
setError(adminErrorMessage(err));
} finally {
setLoading(false);
}
}
useEffect(() => {
const nextType = documentationTypeFromSearch(location.search);
setDocumentationType((current) => current === nextType ? current : nextType);
}, [location.search]);
useEffect(() => { void load(); }, [settings.apiBaseUrl, settings.apiKey, settings.accessToken, documentationType, locale]);
const treeNodes = useMemo(() => docsTreeNodes(context, adminDocs), [context, adminDocs]);
const pages = useMemo(() => flattenTreePages(treeNodes), [treeNodes]);
const selectedPage = selectedPageFromSearch(location.search, pages);
const topicById = useMemo(() => topicIndex(context), [context]);
const visibleRoutes = context?.layers.configured.routes ?? [];
const availableRoutes = context?.layers.available.routes ?? [];
const grantedPermissions = context?.layers.configured.permissions.filter((item) => item.granted) ?? [];
const moduleRows = useMemo(() => context?.layers.configured.modules ?? [], [context]);
const outlineItems = outlineForPage(selectedPage, adminDocs);
useEffect(() => {
setExpandedNodes(new Set(defaultExpandedNodeIds(treeNodes)));
}, [treeNodes]);
useEffect(() => {
if (!selectedPage) return;
const ancestorIds = ancestorNodeIdsForPage(treeNodes, selectedPage.id);
if (!ancestorIds.length) return;
setExpandedNodes((current) => {
const next = new Set(current);
for (const nodeId of ancestorIds) {
next.add(nodeId);
}
return next;
});
}, [treeNodes, selectedPage?.id]);
return (
<div className="workspace module-workspace docs-workspace">
<aside className="section-sidebar docs-outline" aria-label="i18n:govoplan-docs.documentation_outline.6f836b99">
<div className="docs-sidebar-header">
<div className="section-title">i18n:govoplan-docs.help_center.f3f3a34b</div>
<AudienceToggle selected={documentationType} onSelect={selectDocumentationType} />
</div>
<nav className="docs-tree" aria-label="i18n:govoplan-docs.documentation_outline.6f836b99">
<ExplorerTree
nodes={treeNodes}
getNodeId={(node) => node.id}
getNodeLabel={(node) => node.title}
getNodeChildren={(node) => node.children}
activeId={activeNodeIdForPage(treeNodes, selectedPage?.id ?? "")}
expandedIds={expandedNodes}
depth={0}
childrenBaseClassName="docs-tree-children"
nodeContainerClassName="docs-tree-node"
nodeWrapBaseClassName="docs-tree-row"
toggleBaseClassName="docs-tree-toggle"
nodeButtonBaseClassName="docs-tree-page"
getNodeWrapStyle={(_node, context) => ({ paddingLeft: `${context.depth * 14}px` })}
getNodeButtonClassName={(node) => selectedPage?.id === node.page.id ? "is-active" : ""}
renderToggleIcon={(_node, context) => context.hasChildren ? context.expanded ? <ChevronDown size={15} /> : <ChevronRight size={15} /> : <span className="docs-tree-toggle-placeholder" />}
renderNodeContent={(node) => node.title}
onToggle={(node) => toggleNode(node.id)}
onOpen={(node) => selectPage(node.page)}
/>
</nav>
</aside>
<section className="workspace-content docs-workspace-content">
<div className="content-pad workspace-data-page docs-page">
<div className="page-heading split workspace-heading">
<div>
<PageTitle loading={loading}>i18n:govoplan-docs.help_center.f3f3a34b</PageTitle>
<p>{adminDocs ? "i18n:govoplan-docs.technical_documentation_for_the_modules_and_conf.267e739e" : "i18n:govoplan-docs.guidance_for_the_functions_available_in_this_ins.723b7cad"}</p>
</div>
<div className="button-row compact-actions">
<Button onClick={() => void load()} disabled={loading}><RefreshCw size={16} /> i18n:govoplan-docs.reload.cce71553</Button>
</div>
</div>
{error && <DismissibleAlert tone="danger" resetKey={error} floating>{error}</DismissibleAlert>}
<LoadingFrame loading={loading} label="i18n:govoplan-docs.loading_documentation_context.1c091645">
<div className="docs-content">
<main className="docs-page-main">
<SelectedPageContent
page={selectedPage}
adminDocs={adminDocs}
documentationType={documentationType}
topicById={topicById}
moduleRows={moduleRows}
visibleRoutes={visibleRoutes}
availableRoutes={availableRoutes}
grantedPermissions={grantedPermissions}
evidenceModules={context?.layers.evidence.optional_modules ?? []}
evidenceSources={context?.layers.evidence.sources ?? []}
/>
</main>
<PageOutline items={outlineItems} />
</div>
</LoadingFrame>
</div>
</section>
</div>
);
function selectDocumentationType(type: DocumentationType) {
const params = new URLSearchParams(location.search);
params.set("type", type);
navigate(`${location.pathname}?${params.toString()}`, { replace: true });
}
function selectPage(page: DocsPageNode) {
const params = new URLSearchParams(location.search);
params.set("topic", page.id);
navigate(`${location.pathname}?${params.toString()}`, { replace: true });
}
function toggleNode(nodeId: string) {
setExpandedNodes((current) => {
const next = new Set(current);
if (next.has(nodeId)) {
next.delete(nodeId);
} else {
next.add(nodeId);
}
return next;
});
}
}
function AudienceToggle({ selected, onSelect }: { selected: DocumentationType; onSelect: (type: DocumentationType) => void }) {
return (
<SegmentedControl
className="docs-audience-toggle"
role="group"
size="equal"
width="fill"
ariaLabel="i18n:govoplan-docs.documentation_type.5a66690c"
value={selected}
onChange={onSelect}
options={[
{ id: "admin", label: "i18n:govoplan-docs.admin_docs.bf504a56" },
{ id: "user", label: "i18n:govoplan-docs.user_docs.1e38e8d3" }
]}
/>
);
}
function PageOutline({ items }: { items: OutlineItem[] }) {
return (
<aside className="docs-page-outline-box" aria-label="i18n:govoplan-docs.page_outline.e5940949">
<h2>i18n:govoplan-docs.page_outline.e5940949</h2>
{items.length ?
<nav>
{items.map((item) => <a key={item.id} href={`#${item.id}`}>{item.label}</a>)}
</nav> :
<p className="muted">i18n:govoplan-docs.no_outline_items.3ea2842e</p>
}
</aside>
);
}
function SelectedPageContent({
page,
adminDocs,
documentationType,
topicById,
moduleRows,
visibleRoutes,
availableRoutes,
grantedPermissions,
evidenceModules,
evidenceSources
}: {
page: DocsPageNode | null;
adminDocs: boolean;
documentationType: DocumentationType;
topicById: Map<string, DocsDocumentationTopic>;
moduleRows: DocsModule[];
visibleRoutes: DocsRoute[];
availableRoutes: DocsRoute[];
grantedPermissions: Array<{ scope: string; label: string; category: string }>;
evidenceModules: DocsOptionalModuleEvidence[];
evidenceSources: DocsSource[];
}) {
if (!page) {
return (
<section className="docs-section">
<h2>i18n:govoplan-docs.help_center.f3f3a34b</h2>
<p className="muted">i18n:govoplan-docs.no_system_topics_found.d01a8068</p>
</section>
);
}
if (page.kind === "topic") {
return <TopicPage topic={page.topic} showTechnical={adminDocs} documentationType={documentationType} topicById={topicById} />;
}
if (page.kind === "admin-reference") {
return (
<section className="docs-section" id={page.id}>
<h2>{page.title}</h2>
<section id="docs-admin-modules" className="docs-reference-block">
<h3>i18n:govoplan-docs.modules.04e9462c</h3>
<ModuleTable modules={moduleRows} />
</section>
<section id="docs-admin-routes" className="docs-reference-block">
<h3>i18n:govoplan-docs.visible_routes.6c9b1605</h3>
<RouteTable routes={visibleRoutes} emptyText="i18n:govoplan-docs.no_visible_routes_found_for_this_actor.3feeaf0b" />
<h3>i18n:govoplan-docs.available_routes.c2635868</h3>
<RouteTable routes={availableRoutes} emptyText="i18n:govoplan-docs.no_hidden_installed_routes_found.22f62845" />
</section>
<section id="docs-admin-permissions" className="docs-reference-block">
<h3>i18n:govoplan-docs.granted_permissions.0a232e78</h3>
<PermissionList permissions={grantedPermissions} />
<EvidenceList modules={evidenceModules} sources={evidenceSources} />
</section>
</section>
);
}
return (
<section className="docs-section" id={page.id}>
<h2>{page.title}</h2>
<DocumentationTopicList topics={page.topics} emptyText={page.emptyText} showTechnical={adminDocs} documentationType={documentationType} topicById={topicById} />
</section>
);
}
function TopicPage({ topic, showTechnical, documentationType, topicById }: { topic: DocsDocumentationTopic; showTechnical: boolean; documentationType: DocumentationType; topicById: Map<string, DocsDocumentationTopic> }) {
return (
<article className={`docs-topic docs-topic-page docs-topic-${topic.kind}`} id={topicAnchorId(topic)}>
<div className="docs-topic-meta">
<StatusBadge status={topic.active ? "success" : "warning"} label={topic.active ? topic.kind : topic.target_layer} />
<span className="muted">{topic.source_module_id}</span>
{showTechnical && <span className="muted">{topic.i18n_key} · {topic.translation_locale}</span>}
</div>
<h2>{topic.title}</h2>
{topic.summary && <p id={`${topicAnchorId(topic)}-summary`} className="docs-topic-summary">{topic.summary}</p>}
<div id={`${topicAnchorId(topic)}-details`}>
<BodyText value={topic.body} />
<TopicMetadata topic={topic} showTechnical={showTechnical} documentationType={documentationType} topicById={topicById} />
{!topic.active && <UnavailableDocumentationReason topic={topic} />}
</div>
{!!topic.unlocks.length && <p className="muted">i18n:govoplan-docs.unlocks.67d569db {topic.unlocks.join(" ")}</p>}
{!!topic.links.length && <TopicLinks topic={topic} />}
</article>
);
}
function DocumentationTopicList({ topics, emptyText, showTechnical = false, documentationType, topicById }: { topics: DocsDocumentationTopic[]; emptyText: string; showTechnical?: boolean; documentationType: DocumentationType; topicById: Map<string, DocsDocumentationTopic> }) {
if (!topics.length) return <p className="muted">{emptyText}</p>;
return (
<div className="docs-topic-list">
{topics.map((topic) =>
<article className={`docs-topic docs-topic-${topic.kind}`} id={topicAnchorId(topic)} key={`${topic.source_module_id}-${topic.id}`}>
<div className="docs-topic-meta">
<StatusBadge status={topic.active ? "success" : "warning"} label={topic.active ? topic.kind : topic.target_layer} />
<span className="muted">{topic.source_module_id}</span>
{showTechnical && <span className="muted">{topic.i18n_key} · {topic.translation_locale}</span>}
</div>
<h3>{topic.title}</h3>
{topic.summary && <p className="docs-topic-summary">{topic.summary}</p>}
<BodyText value={topic.body} />
<TopicMetadata topic={topic} showTechnical={showTechnical} documentationType={documentationType} topicById={topicById} />
{!topic.active && <UnavailableDocumentationReason topic={topic} />}
{!!topic.unlocks.length && <p className="muted">i18n:govoplan-docs.unlocks.67d569db {topic.unlocks.join(" ")}</p>}
{!!topic.links.length && <TopicLinks topic={topic} />}
</article>
)}
</div>
);
}
function TopicMetadata({ topic, showTechnical, documentationType, topicById }: { topic: DocsDocumentationTopic; showTechnical: boolean; documentationType: DocumentationType; topicById: Map<string, DocsDocumentationTopic> }) {
if (topic.kind === "workflow") return <WorkflowDetails topic={topic} documentationType={documentationType} topicById={topicById} />;
if (topic.kind === "reference") return <ReferenceDetails topic={topic} showTechnical={showTechnical} documentationType={documentationType} topicById={topicById} />;
if (topic.kind === "pattern") return <PatternDetails topic={topic} showTechnical={showTechnical} documentationType={documentationType} topicById={topicById} />;
return <RelatedTopics topic={topic} documentationType={documentationType} topicById={topicById} />;
}
function WorkflowDetails({ topic, documentationType, topicById }: { topic: DocsDocumentationTopic; documentationType: DocumentationType; topicById: Map<string, DocsDocumentationTopic> }) {
const prerequisites = metadataList(topic.metadata, "prerequisites");
const steps = metadataList(topic.metadata, "steps");
const outcome = metadataString(topic.metadata, "outcome");
const result = metadataString(topic.metadata, "result");
const verification = metadataString(topic.metadata, "verification");
const prefix = topicAnchorId(topic);
return (
<div className="docs-topic-details">
{outcome && <DetailBlock id={`${prefix}-outcome`} title="i18n:govoplan-docs.outcome.10172bd3" value={outcome} />}
{!!prerequisites.length && <DetailList id={`${prefix}-prerequisites`} title="i18n:govoplan-docs.prerequisites.fdf2407f" items={prerequisites} />}
{!!steps.length &&
<div className="docs-detail-block" id={`${prefix}-steps`}>
<h4>i18n:govoplan-docs.steps.6041435e</h4>
<ol>
{steps.map((step) => <li key={step}>{step}</li>)}
</ol>
</div>
}
{result && <DetailBlock id={`${prefix}-result`} title="i18n:govoplan-docs.result.1f4fbf43" value={result} />}
{verification && <DetailBlock id={`${prefix}-verification`} title="i18n:govoplan-docs.verification.4b5f10dd" value={verification} />}
<RelatedTopics topic={topic} documentationType={documentationType} topicById={topicById} />
</div>
);
}
function ReferenceDetails({ topic, showTechnical, documentationType, topicById }: { topic: DocsDocumentationTopic; showTechnical: boolean; documentationType: DocumentationType; topicById: Map<string, DocsDocumentationTopic> }) {
const route = metadataString(topic.metadata, "route");
const screen = metadataString(topic.metadata, "screen");
const section = metadataString(topic.metadata, "section");
const fields = metadataRecords(topic.metadata, "fields");
return (
<div className="docs-topic-details">
{(route || screen || section) &&
<dl className="detail-list compact">
{screen && <div><dt>i18n:govoplan-docs.screen.c4878ec4</dt><dd>{screen}</dd></div>}
{section && <div><dt>i18n:govoplan-docs.section.5e498158</dt><dd>{section}</dd></div>}
{showTechnical && route && <div><dt>i18n:govoplan-docs.route.4999528e</dt><dd>{route}</dd></div>}
</dl>
}
{!!fields.length && <ReferenceFieldTable id={`${topicAnchorId(topic)}-fields`} fields={fields} showTechnical={showTechnical} />}
<RelatedTopics topic={topic} documentationType={documentationType} topicById={topicById} />
</div>
);
}
function ReferenceFieldTable({ id, fields, showTechnical }: { id: string; fields: Record<string, unknown>[]; showTechnical: boolean }) {
return (
<div className="admin-table-wrap" id={id}>
<table className="admin-table">
<thead>
<tr>
<th>i18n:govoplan-docs.field.7558c082</th>
<th>i18n:govoplan-docs.meaning.584d8aa0</th>
{showTechnical && <th>i18n:govoplan-docs.api_mapping.e969f6f7</th>}
{showTechnical && <th>i18n:govoplan-docs.permission.d71c9448</th>}
<th>i18n:govoplan-docs.validation.ef7f6d9c</th>
</tr>
</thead>
<tbody>
{fields.map((field) =>
<tr key={metadataString(field, "field_id") || metadataString(field, "label")}>
<td><strong>{metadataString(field, "label")}</strong></td>
<td>
{metadataString(field, "user_description")}
{showTechnical && metadataString(field, "admin_description") && <span className="muted block">{metadataString(field, "admin_description")}</span>}
{showTechnical && metadataString(field, "provenance") && <span className="muted block">{metadataString(field, "provenance")}</span>}
</td>
{showTechnical && <td>{metadataString(field, "api_path")}<span className="muted block">{metadataString(field, "api_field")}</span></td>}
{showTechnical && <td>{metadataString(field, "permission_scope") || "-"}</td>}
<td>{metadataString(field, "validation") || "-"}</td>
</tr>
)}
</tbody>
</table>
</div>
);
}
function PatternDetails({ topic, showTechnical, documentationType, topicById }: { topic: DocsDocumentationTopic; showTechnical: boolean; documentationType: DocumentationType; topicById: Map<string, DocsDocumentationTopic> }) {
const purpose = metadataString(topic.metadata, "purpose");
const whenUsed = metadataString(topic.metadata, "when_used");
const explanation = showTechnical ? metadataString(topic.metadata, "admin_explanation") : metadataString(topic.metadata, "user_explanation");
const componentRefs = metadataList(topic.metadata, "component_refs");
const prefix = topicAnchorId(topic);
return (
<div className="docs-topic-details">
{purpose && <DetailBlock id={`${prefix}-purpose`} title="i18n:govoplan-docs.purpose.4af40581" value={purpose} />}
{whenUsed && <DetailBlock id={`${prefix}-when-used`} title="i18n:govoplan-docs.when_used.b870b361" value={whenUsed} />}
{explanation && <DetailBlock id={`${prefix}-details`} title="i18n:govoplan-docs.details.a6b3c45f" value={explanation} />}
{showTechnical && !!componentRefs.length && <DetailList id={`${prefix}-components`} title="i18n:govoplan-docs.components.e81e57d8" items={componentRefs} />}
<RelatedTopics topic={topic} documentationType={documentationType} topicById={topicById} />
</div>
);
}
function DetailBlock({ id, title, value }: { id: string; title: string; value: string }) {
return (
<div className="docs-detail-block" id={id}>
<h4>{title}</h4>
<p>{value}</p>
</div>
);
}
function DetailList({ id, title, items }: { id: string; title: string; items: string[] }) {
return (
<div className="docs-detail-block" id={id}>
<h4>{title}</h4>
<ul>
{items.map((item) => <li key={item}>{item}</li>)}
</ul>
</div>
);
}
function RelatedTopics({ topic, documentationType, topicById }: { topic: DocsDocumentationTopic; documentationType: DocumentationType; topicById: Map<string, DocsDocumentationTopic> }) {
const related = metadataList(topic.metadata, "related_topic_ids")
.map((id) => topicById.get(id))
.filter((item): item is DocsDocumentationTopic => Boolean(item));
if (!related.length) return null;
return (
<p className="docs-topic-links">
<span>i18n:govoplan-docs.related_topics.d5eb8b74 </span>
{related.map((item, index) =>
<span key={item.id}>
{index > 0 && " | "}
<Link to={topicPageHref(item, documentationType)}>{item.title}</Link>
</span>
)}
</p>
);
}
function TopicLinks({ topic }: { topic: DocsDocumentationTopic }) {
return (
<p className="docs-topic-links">
{topic.links.map((link, index) =>
<span key={link.href}>
{index > 0 && " | "}
<a href={link.href} target={link.kind === "public" ? "_blank" : undefined} rel={link.kind === "public" ? "noreferrer" : undefined}>{link.label}</a>
</span>
)}
</p>
);
}
function BodyText({ value }: { value: string }) {
if (!value) return null;
return (
<>
{value.split("\n").filter(Boolean).map((line) => <p className="docs-topic-body" key={line}>{line}</p>)}
</>
);
}
function UnavailableDocumentationReason({ topic }: { topic: DocsDocumentationTopic }) {
const rows = [
["i18n:govoplan-docs.modules.04e9462c", topic.blockers.modules],
["i18n:govoplan-docs.capabilities.ca09c54b", topic.blockers.capabilities],
["i18n:govoplan-docs.permissions.d06d5557", topic.blockers.scopes]
].filter(([, values]) => Array.isArray(values) && values.length);
return (
<div className="docs-unavailable-reason">
<p className="muted">{topic.reason}</p>
{!!rows.length &&
<dl className="detail-list compact">
{rows.map(([label, values]) =>
<div key={String(label)}>
<dt>{label}</dt>
<dd>{(values as string[]).join(", ")}</dd>
</div>
)}
</dl>
}
</div>
);
}
function docsTreeNodes(context: DocsContext | null, adminDocs: boolean): DocsTreeNode[] {
const allTopics = allDocumentationTopics(context);
const systemTopics = context?.topic_groups.system ?? [];
const workflowTopics = context?.topic_groups.workflow ?? [];
const referenceTopics = context?.topic_groups.reference ?? [];
const patternTopics = context?.topic_groups.pattern ?? [];
const systemTopicIds = new Set(systemTopics.map((topic) => topic.id));
const moduleTopics = allTopics.filter((topic) => !systemTopicIds.has(topic.id));
const workingTopics = uniqueTopics([...patternTopics, ...workflowTopics]);
const troubleshootingTopics = allTopics.filter((topic) => !topic.active || topic.layer === "evidence");
const basicsNode = categoryTreeNode(
"basics",
"i18n:govoplan-docs.basics.5fcebeef",
patternTopics,
"i18n:govoplan-docs.no_basic_topics_found.ee5ec3c6"
);
const commonTasksNode = categoryTreeNode(
"common-tasks",
"i18n:govoplan-docs.common_tasks.9f825c48",
workflowTopics,
"i18n:govoplan-docs.no_common_task_topics_found.c17814cf"
);
const nodes: DocsTreeNode[] = [
categoryTreeNode(
"about-govoplan",
"i18n:govoplan-docs.about_govoplan.6b2d7127",
systemTopics,
"i18n:govoplan-docs.no_about_topics_found.7cd8af6d"
),
{
id: "tree:working-with-govoplan",
title: "i18n:govoplan-docs.working_with_govoplan.81908ef4",
page: {
id: "working-with-govoplan:overview",
title: "i18n:govoplan-docs.working_with_govoplan.81908ef4",
kind: "topic-list",
topics: workingTopics,
emptyText: "i18n:govoplan-docs.no_working_with_govoplan_topics_found.b6db8384"
},
children: [basicsNode, commonTasksNode]
},
categoryTreeNode(
"advanced",
"i18n:govoplan-docs.advanced.203a8d6b",
referenceTopics,
"i18n:govoplan-docs.no_advanced_topics_found.271166c9"
),
{
id: "tree:modules",
title: "i18n:govoplan-docs.modules.04e9462c",
page: {
id: "modules:overview",
title: "i18n:govoplan-docs.modules.04e9462c",
kind: "topic-list",
topics: moduleTopics,
emptyText: "i18n:govoplan-docs.no_module_topics_found.0cbbdc9b"
},
children: moduleTreeNodes(moduleTopics, context)
}
];
if (adminDocs) {
nodes.push({
id: "tree:administration",
title: "i18n:govoplan-docs.administration.101aedaf",
page: {
id: "administration:overview",
title: "i18n:govoplan-docs.administration.101aedaf",
kind: "topic-list",
topics: referenceTopics,
emptyText: "i18n:govoplan-docs.no_administration_topics_found.6213dff2"
},
children: [
...referenceTopics.map(topicTreeNode),
{
id: "tree:administration:technical-reference",
title: "i18n:govoplan-docs.technical_reference.f271430d",
page: {
id: "system:technical-reference",
title: "i18n:govoplan-docs.technical_reference.f271430d",
kind: "admin-reference"
},
children: []
}
]
});
}
if (troubleshootingTopics.length) {
nodes.push(categoryTreeNode(
"troubleshooting",
"i18n:govoplan-docs.troubleshooting_and_support.3d42f434",
troubleshootingTopics,
"i18n:govoplan-docs.no_troubleshooting_topics_found.8c275468"
));
}
return nodes;
}
function categoryTreeNode(id: string, title: string, topics: DocsDocumentationTopic[], emptyText: string): DocsTreeNode {
const sortedTopics = [...topics].sort((left, right) => left.order - right.order || left.title.localeCompare(right.title));
return {
id: `tree:${id}`,
title,
page: {
id: `${id}:overview`,
title,
kind: "topic-list",
topics: sortedTopics,
emptyText
},
children: nestedTopicNodes(sortedTopics)
};
}
function topicPage(topic: DocsDocumentationTopic): DocsPageNode {
return {
id: topic.id,
title: topic.title,
kind: "topic",
topic
};
}
function topicTreeNode(topic: DocsDocumentationTopic): DocsTreeNode {
return {
id: `tree:topic:${topic.id}`,
title: topic.title,
page: topicPage(topic),
children: []
};
}
function moduleTreeNodes(topics: DocsDocumentationTopic[], context: DocsContext | null): DocsTreeNode[] {
const topicsByModule = new Map<string, DocsDocumentationTopic[]>();
for (const topic of topics) {
const moduleTopics = topicsByModule.get(topic.source_module_id) ?? [];
moduleTopics.push(topic);
topicsByModule.set(topic.source_module_id, moduleTopics);
}
const configuredModules = context?.layers.configured.modules ?? [];
const moduleOrder = new Map(configuredModules.map((module, index) => [module.id, index]));
return Array.from(topicsByModule.entries())
.sort(([left], [right]) => (moduleOrder.get(left) ?? 999) - (moduleOrder.get(right) ?? 999) || moduleTitle(left, context).localeCompare(moduleTitle(right, context)))
.map(([moduleId, moduleTopics]) => {
const sortedTopics = [...moduleTopics].sort((left, right) => left.order - right.order || left.title.localeCompare(right.title));
const title = moduleTitle(moduleId, context);
return {
id: `tree:module:${moduleId}`,
title,
page: {
id: `module:${moduleId}`,
title,
kind: "topic-list",
topics: sortedTopics,
emptyText: "i18n:govoplan-docs.no_system_topics_found.d01a8068"
},
children: nestedTopicNodes(sortedTopics)
};
});
}
function nestedTopicNodes(topics: DocsDocumentationTopic[]): DocsTreeNode[] {
const nodes = new Map(topics.map((topic) => [topic.id, topicTreeNode(topic)]));
const roots: DocsTreeNode[] = [];
for (const topic of topics) {
const node = nodes.get(topic.id);
if (!node) continue;
const parentId = topicParentId(topic);
const parent = parentId ? nodes.get(parentId) : undefined;
if (parent && parentId !== topic.id) {
parent.children.push(node);
} else {
roots.push(node);
}
}
return roots;
}
function topicParentId(topic: DocsDocumentationTopic): string {
return metadataString(topic.metadata, "tree_parent_id") || metadataString(topic.metadata, "parent_topic_id") || metadataString(topic.metadata, "parent_id");
}
function moduleTitle(moduleId: string, context: DocsContext | null): string {
const module = context?.layers.configured.modules.find((item) => item.id === moduleId);
if (module) return module.name;
return moduleId
.replace(/^govoplan[-_]/, "")
.split(/[-_]+/)
.filter(Boolean)
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
.join(" ") || moduleId;
}
function flattenTreePages(nodes: DocsTreeNode[]): DocsPageNode[] {
return nodes.flatMap((node) => [node.page, ...flattenTreePages(node.children)]);
}
function defaultExpandedNodeIds(nodes: DocsTreeNode[]): string[] {
return nodes.filter((node) => node.children.length).map((node) => node.id);
}
function ancestorNodeIdsForPage(nodes: DocsTreeNode[], pageId: string, ancestors: string[] = []): string[] {
for (const node of nodes) {
if (node.page.id === pageId) return ancestors;
const match = ancestorNodeIdsForPage(node.children, pageId, [...ancestors, node.id]);
if (match.length) return match;
}
return [];
}
function activeNodeIdForPage(nodes: DocsTreeNode[], pageId: string): string {
if (!pageId) return "";
for (const node of nodes) {
if (node.page.id === pageId) return node.id;
const childId = activeNodeIdForPage(node.children, pageId);
if (childId) return childId;
}
return "";
}
function allDocumentationTopics(context: DocsContext | null): DocsDocumentationTopic[] {
const topics = Object.values(context?.topic_groups ?? {}).flat();
return uniqueTopics(topics).sort((left, right) => left.order - right.order || left.title.localeCompare(right.title));
}
function uniqueTopics(topics: DocsDocumentationTopic[]): DocsDocumentationTopic[] {
const seen = new Set<string>();
return topics.filter((topic) => {
if (seen.has(topic.id)) return false;
seen.add(topic.id);
return true;
});
}
function selectedPageFromSearch(search: string, pages: DocsPageNode[]): DocsPageNode | null {
if (!pages.length) return null;
const requested = new URLSearchParams(search).get("topic") || "";
return pages.find((page) => page.id === requested) ?? pages[0];
}
function outlineForPage(page: DocsPageNode | null, showTechnical: boolean): OutlineItem[] {
if (!page) return [];
if (page.kind === "admin-reference") {
return [
{ id: page.id, label: page.title },
{ id: "docs-admin-modules", label: "i18n:govoplan-docs.modules.04e9462c" },
{ id: "docs-admin-routes", label: "i18n:govoplan-docs.routes.03730e58" },
{ id: "docs-admin-permissions", label: "i18n:govoplan-docs.permissions.d06d5557" }
];
}
if (page.kind === "topic-list") {
return [
{ id: page.id, label: page.title },
...page.topics.map((topic) => ({ id: topicAnchorId(topic), label: topic.title }))
];
}
const topic = page.topic;
const prefix = topicAnchorId(topic);
const items: OutlineItem[] = [{ id: prefix, label: topic.title }];
if (topic.summary) items.push({ id: `${prefix}-summary`, label: "i18n:govoplan-docs.summary.d6b9936d" });
if (topic.kind === "workflow") {
if (metadataString(topic.metadata, "outcome")) items.push({ id: `${prefix}-outcome`, label: "i18n:govoplan-docs.outcome.10172bd3" });
if (metadataList(topic.metadata, "prerequisites").length) items.push({ id: `${prefix}-prerequisites`, label: "i18n:govoplan-docs.prerequisites.fdf2407f" });
if (metadataList(topic.metadata, "steps").length) items.push({ id: `${prefix}-steps`, label: "i18n:govoplan-docs.steps.6041435e" });
if (metadataString(topic.metadata, "result")) items.push({ id: `${prefix}-result`, label: "i18n:govoplan-docs.result.1f4fbf43" });
if (metadataString(topic.metadata, "verification")) items.push({ id: `${prefix}-verification`, label: "i18n:govoplan-docs.verification.4b5f10dd" });
}
if (topic.kind === "reference" && metadataRecords(topic.metadata, "fields").length) {
items.push({ id: `${prefix}-fields`, label: "i18n:govoplan-docs.field.7558c082" });
}
if (topic.kind === "pattern") {
if (metadataString(topic.metadata, "purpose")) items.push({ id: `${prefix}-purpose`, label: "i18n:govoplan-docs.purpose.4af40581" });
if (metadataString(topic.metadata, "when_used")) items.push({ id: `${prefix}-when-used`, label: "i18n:govoplan-docs.when_used.b870b361" });
if (showTechnical && metadataList(topic.metadata, "component_refs").length) items.push({ id: `${prefix}-components`, label: "i18n:govoplan-docs.components.e81e57d8" });
}
return items;
}
function documentationTypeFromSearch(search: string): DocumentationType {
return new URLSearchParams(search).get("type") === "user" ? "user" : "admin";
}
function localeFromSearch(search: string): string | null {
const value = new URLSearchParams(search).get("locale");
if (!value) return null;
return value.toLowerCase().startsWith("de") ? "de" : "en";
}
function topicAnchorId(topic: DocsDocumentationTopic): string {
return topic.anchor_id || `docs-topic-${topic.source_module_id}-${topic.id}`.replace(/[^a-z0-9_-]+/gi, "-").toLowerCase();
}
function topicPageHref(topic: DocsDocumentationTopic, documentationType: DocumentationType): string {
const params = new URLSearchParams();
params.set("type", documentationType);
params.set("topic", topic.id);
return `?${params.toString()}#${topicAnchorId(topic)}`;
}
function topicIndex(context: DocsContext | null): Map<string, DocsDocumentationTopic> {
const index = new Map<string, DocsDocumentationTopic>();
for (const topic of allDocumentationTopics(context)) {
index.set(topic.id, topic);
}
return index;
}
function metadataString(metadata: Record<string, unknown>, key: string): string {
const value = metadata[key];
return typeof value === "string" ? value : "";
}
function metadataList(metadata: Record<string, unknown>, key: string): string[] {
const value = metadata[key];
if (!Array.isArray(value)) return [];
return value.filter((item): item is string => typeof item === "string");
}
function metadataRecords(metadata: Record<string, unknown>, key: string): Record<string, unknown>[] {
const value = metadata[key];
if (!Array.isArray(value)) return [];
return value.filter((item): item is Record<string, unknown> => Boolean(item) && typeof item === "object" && !Array.isArray(item));
}
function ModuleTable({ modules }: { modules: DocsModule[] }) {
if (!modules.length) return <p className="muted">i18n:govoplan-docs.no_configured_modules_found.f6f9ce24</p>;
return (
<div className="admin-table-wrap">
<table className="admin-table">
<thead>
<tr><th>i18n:govoplan-docs.module.b8ff0289</th><th>i18n:govoplan-docs.routes.03730e58</th><th>i18n:govoplan-docs.permissions.d06d5557</th><th>i18n:govoplan-docs.frontend.152d1cf2</th><th>i18n:govoplan-docs.capabilities.ca09c54b</th></tr>
</thead>
<tbody>
{modules.map((module) =>
<tr key={module.id}>
<td><strong>{module.name}</strong><span className="muted block">{module.id} {module.version}</span></td>
<td>{module.route_count} i18n:govoplan-docs.route.200e2a66 {module.nav_count} nav</td>
<td>{module.permission_count}</td>
<td>{module.frontend_package || "-"}</td>
<td>{module.capabilities.length ? module.capabilities.join(", ") : "-"}{module.documentation_count || module.documentation_provider_count ? <span className="muted block">{module.documentation_count} i18n:govoplan-docs.docs.5dc1e9b8 {module.documentation_provider_count} provider</span> : null}</td>
</tr>
)}
</tbody>
</table>
</div>
);
}
function RouteTable({ routes, emptyText }: { routes: DocsRoute[]; emptyText: string }) {
if (!routes.length) return <p className="muted">{emptyText}</p>;
return (
<div className="admin-table-wrap">
<table className="admin-table">
<thead>
<tr><th>i18n:govoplan-docs.route.4999528e</th><th>i18n:govoplan-docs.module.b8ff0289</th><th>i18n:govoplan-docs.source.6da13add</th><th>i18n:govoplan-docs.requirements.09a428f9</th><th>i18n:govoplan-docs.status.bae7d5be</th></tr>
</thead>
<tbody>
{routes.map((route) =>
<tr key={`${route.source}-${route.module_id}-${route.path}`}>
<td><strong>{route.label}</strong><span className="muted block">{route.path}</span></td>
<td>{route.module_id}</td>
<td>{route.source}</td>
<td>{routeRequirements(route)}</td>
<td><StatusBadge status={route.visible ? "success" : "warning"} label={route.visible ? "visible" : route.reason} /></td>
</tr>
)}
</tbody>
</table>
</div>
);
}
function PermissionList({ permissions }: { permissions: Array<{ scope: string; label: string; category: string }> }) {
if (!permissions.length) return <p className="muted">i18n:govoplan-docs.no_granted_platform_permissions_found.36010898</p>;
return (
<dl className="detail-list">
{permissions.slice(0, 24).map((permission) =>
<div key={permission.scope}>
<dt>{permission.category}</dt>
<dd><strong>{permission.label}</strong><span className="muted"> · {permission.scope}</span></dd>
</div>
)}
{permissions.length > 24 && <div><dt>i18n:govoplan-docs.more.4bab2d8f</dt><dd>{permissions.length - 24} i18n:govoplan-docs.additional_permissions.8042cb01</dd></div>}
</dl>
);
}
function EvidenceList({ modules, sources }: { modules: DocsOptionalModuleEvidence[]; sources: DocsSource[] }) {
if (!modules.length && !sources.length) return <p className="muted">i18n:govoplan-docs.no_evidence_sources_found.be3bb2f6</p>;
return (
<dl className="detail-list">
{modules.map((item) =>
<div key={`${item.source_module_id}-${item.module_id}`}>
<dt><StatusBadge status={item.status === "installed" ? "success" : "inactive"} label={item.status} /></dt>
<dd><strong>{item.module_id}</strong><span className="muted"> · {item.reason}</span></dd>
</div>
)}
{sources.map((item) =>
<div key={item.source}>
<dt>{item.layer}</dt>
<dd><strong>{item.label}</strong><span className="muted"> · {item.source}</span></dd>
</div>
)}
</dl>
);
}
function routeRequirements(route: DocsRoute): string {
const parts = [];
if (route.required_all.length) parts.push(`all: ${route.required_all.join(", ")}`);
if (route.required_any.length) parts.push(`any: ${route.required_any.join(", ")}`);
return parts.join(" | ") || "-";
}

View File

@@ -0,0 +1,186 @@
import type { PlatformTranslations } from "@govoplan/core-webui";
export const generatedTranslations: PlatformTranslations = {
"en": {
"i18n:govoplan-docs.additional_permissions.8042cb01": "additional permissions",
"i18n:govoplan-docs.about_govoplan.6b2d7127": "About GovOPlaN",
"i18n:govoplan-docs.admin_docs.bf504a56": "Admin docs",
"i18n:govoplan-docs.administration.101aedaf": "Administration",
"i18n:govoplan-docs.advanced.203a8d6b": "Advanced",
"i18n:govoplan-docs.available_documentation.db8bcc42": "Available documentation",
"i18n:govoplan-docs.available_routes.c2635868": "Available routes",
"i18n:govoplan-docs.basics.5fcebeef": "Basics",
"i18n:govoplan-docs.capabilities.ca09c54b": "Capabilities",
"i18n:govoplan-docs.common_tasks.9f825c48": "Common tasks",
"i18n:govoplan-docs.configured_documentation.fb6eaaad": "Configured documentation",
"i18n:govoplan-docs.docs.5dc1e9b8": "docs,",
"i18n:govoplan-docs.docs.68a41942": "Docs",
"i18n:govoplan-docs.docs.e6706405": "DOCS",
"i18n:govoplan-docs.api_mapping.e969f6f7": "API mapping",
"i18n:govoplan-docs.components.e81e57d8": "Components",
"i18n:govoplan-docs.details.a6b3c45f": "Details",
"i18n:govoplan-docs.documentation_mode.35f872f3": "Documentation mode",
"i18n:govoplan-docs.documentation_outline.6f836b99": "Documentation outline",
"i18n:govoplan-docs.documentation_type.5a66690c": "Documentation type",
"i18n:govoplan-docs.evidence.7ea014de": "Evidence",
"i18n:govoplan-docs.field.7558c082": "Field",
"i18n:govoplan-docs.frontend.152d1cf2": "Frontend",
"i18n:govoplan-docs.granted_permissions.0a232e78": "Granted permissions",
"i18n:govoplan-docs.guidance_for_the_functions_available_in_this_ins.723b7cad": "Guidance for the functions available in this installation.",
"i18n:govoplan-docs.help_center.f3f3a34b": "Help Center",
"i18n:govoplan-docs.loading_documentation_context.1c091645": "Loading documentation context...",
"i18n:govoplan-docs.meaning.584d8aa0": "Meaning",
"i18n:govoplan-docs.module.b8ff0289": "Module",
"i18n:govoplan-docs.modules.04e9462c": "Modules",
"i18n:govoplan-docs.more_examples.383f0e0e": "More examples",
"i18n:govoplan-docs.more.4bab2d8f": "More",
"i18n:govoplan-docs.no_about_topics_found.7cd8af6d": "No GovOPlaN overview topics found.",
"i18n:govoplan-docs.no_administration_topics_found.6213dff2": "No administration topics found.",
"i18n:govoplan-docs.no_advanced_topics_found.271166c9": "No advanced topics found.",
"i18n:govoplan-docs.no_always_present_documentation_sections_found.3b89b31e": "No always-present documentation sections found.",
"i18n:govoplan-docs.no_available_documentation_topics_found.38b84a6d": "No available documentation topics found.",
"i18n:govoplan-docs.no_basic_topics_found.ee5ec3c6": "No basic topics found.",
"i18n:govoplan-docs.no_common_task_topics_found.c17814cf": "No common task topics found.",
"i18n:govoplan-docs.no_configured_documentation_topics_found.04dcfb92": "No configured documentation topics found.",
"i18n:govoplan-docs.no_configured_modules_found.f6f9ce24": "No configured modules found.",
"i18n:govoplan-docs.no_evidence_documentation_topics_found.c59bd849": "No evidence documentation topics found.",
"i18n:govoplan-docs.no_evidence_sources_found.be3bb2f6": "No evidence sources found.",
"i18n:govoplan-docs.no_granted_platform_permissions_found.36010898": "No granted platform permissions found.",
"i18n:govoplan-docs.no_hidden_installed_routes_found.22f62845": "No hidden installed routes found.",
"i18n:govoplan-docs.no_module_topics_found.0cbbdc9b": "No module topics found.",
"i18n:govoplan-docs.no_outline_items.3ea2842e": "No outline items for this page.",
"i18n:govoplan-docs.no_pattern_topics_found.35b70eee": "No design-pattern topics found.",
"i18n:govoplan-docs.no_reference_topics_found.724d5a68": "No reference topics found.",
"i18n:govoplan-docs.no_system_topics_found.d01a8068": "No system documentation topics found.",
"i18n:govoplan-docs.no_visible_routes_found_for_this_actor.3feeaf0b": "No visible routes found for this actor.",
"i18n:govoplan-docs.no_workflow_topics_found.a82b52da": "No workflow topics found.",
"i18n:govoplan-docs.no_troubleshooting_topics_found.8c275468": "No troubleshooting topics found.",
"i18n:govoplan-docs.no_working_with_govoplan_topics_found.b6db8384": "No working-with-GovOPlaN topics found.",
"i18n:govoplan-docs.outcome.10172bd3": "Outcome",
"i18n:govoplan-docs.page_outline.e5940949": "On this page",
"i18n:govoplan-docs.patterns.a2bc4d8f": "Patterns",
"i18n:govoplan-docs.permission.d71c9448": "Permission",
"i18n:govoplan-docs.permissions.d06d5557": "Permissions",
"i18n:govoplan-docs.prerequisites.fdf2407f": "Prerequisites",
"i18n:govoplan-docs.purpose.4af40581": "Purpose",
"i18n:govoplan-docs.reference.1fd9d50a": "Reference",
"i18n:govoplan-docs.related_topics.d5eb8b74": "Related topics:",
"i18n:govoplan-docs.reload.cce71553": "Reload",
"i18n:govoplan-docs.requirements.09a428f9": "Requirements",
"i18n:govoplan-docs.result.1f4fbf43": "Result",
"i18n:govoplan-docs.route.200e2a66": "route,",
"i18n:govoplan-docs.route.4999528e": "Route",
"i18n:govoplan-docs.routes.03730e58": "Routes",
"i18n:govoplan-docs.screen.c4878ec4": "Screen",
"i18n:govoplan-docs.section.5e498158": "Section",
"i18n:govoplan-docs.source.6da13add": "Source",
"i18n:govoplan-docs.status.bae7d5be": "Status",
"i18n:govoplan-docs.steps.6041435e": "Steps",
"i18n:govoplan-docs.summary.d6b9936d": "Summary",
"i18n:govoplan-docs.technical_documentation_for_the_modules_and_conf.267e739e": "Technical documentation for the modules and configuration active in this installation.",
"i18n:govoplan-docs.technical_reference.f271430d": "Technical reference",
"i18n:govoplan-docs.this_system.b13a51ad": "This system",
"i18n:govoplan-docs.toggle_tree_node.4070b59f": "Expand or collapse",
"i18n:govoplan-docs.troubleshooting_and_support.3d42f434": "Troubleshooting and support",
"i18n:govoplan-docs.unlocks.67d569db": "Unlocks:",
"i18n:govoplan-docs.user_docs.1e38e8d3": "User docs",
"i18n:govoplan-docs.validation.ef7f6d9c": "Validation",
"i18n:govoplan-docs.verification.4b5f10dd": "Verification",
"i18n:govoplan-docs.visible_routes.6c9b1605": "Visible routes",
"i18n:govoplan-docs.when_used.b870b361": "When used",
"i18n:govoplan-docs.working_with_govoplan.81908ef4": "Working with GovOPlaN",
"i18n:govoplan-docs.workflows.4f4a16d1": "Workflows",
"i18n:govoplan-docs.your_documentation.8a4cd9a3": "Your documentation"
},
"de": {
"i18n:govoplan-docs.additional_permissions.8042cb01": "additional permissions",
"i18n:govoplan-docs.about_govoplan.6b2d7127": "Über GovOPlaN",
"i18n:govoplan-docs.admin_docs.bf504a56": "Administrationsdokumentation",
"i18n:govoplan-docs.administration.101aedaf": "Administration",
"i18n:govoplan-docs.advanced.203a8d6b": "Fortgeschritten",
"i18n:govoplan-docs.available_documentation.db8bcc42": "Verfügbare Dokumentation",
"i18n:govoplan-docs.available_routes.c2635868": "Verfügbare Routen",
"i18n:govoplan-docs.basics.5fcebeef": "Grundlagen",
"i18n:govoplan-docs.capabilities.ca09c54b": "Fähigkeiten",
"i18n:govoplan-docs.common_tasks.9f825c48": "Häufige Aufgaben",
"i18n:govoplan-docs.configured_documentation.fb6eaaad": "Configured documentation",
"i18n:govoplan-docs.docs.5dc1e9b8": "docs,",
"i18n:govoplan-docs.docs.68a41942": "Dokumentation",
"i18n:govoplan-docs.docs.e6706405": "DOCS",
"i18n:govoplan-docs.api_mapping.e969f6f7": "API-Zuordnung",
"i18n:govoplan-docs.components.e81e57d8": "Komponenten",
"i18n:govoplan-docs.details.a6b3c45f": "Details",
"i18n:govoplan-docs.documentation_mode.35f872f3": "Dokumentationsmodus",
"i18n:govoplan-docs.documentation_outline.6f836b99": "Dokumentationsübersicht",
"i18n:govoplan-docs.documentation_type.5a66690c": "Dokumentationstyp",
"i18n:govoplan-docs.evidence.7ea014de": "Evidence",
"i18n:govoplan-docs.field.7558c082": "Feld",
"i18n:govoplan-docs.frontend.152d1cf2": "Frontend",
"i18n:govoplan-docs.granted_permissions.0a232e78": "Gewährte Berechtigungen",
"i18n:govoplan-docs.guidance_for_the_functions_available_in_this_ins.723b7cad": "Anleitung für die in dieser Installation verfügbaren Funktionen.",
"i18n:govoplan-docs.help_center.f3f3a34b": "Hilfezentrum",
"i18n:govoplan-docs.loading_documentation_context.1c091645": "Dokumentationskontext wird geladen...",
"i18n:govoplan-docs.meaning.584d8aa0": "Bedeutung",
"i18n:govoplan-docs.module.b8ff0289": "Modul",
"i18n:govoplan-docs.modules.04e9462c": "Module",
"i18n:govoplan-docs.more_examples.383f0e0e": "More examples",
"i18n:govoplan-docs.more.4bab2d8f": "Weitere",
"i18n:govoplan-docs.no_about_topics_found.7cd8af6d": "Keine GovOPlaN-Übersichtsthemen gefunden.",
"i18n:govoplan-docs.no_administration_topics_found.6213dff2": "Keine Administrationsthemen gefunden.",
"i18n:govoplan-docs.no_advanced_topics_found.271166c9": "Keine fortgeschrittenen Themen gefunden.",
"i18n:govoplan-docs.no_always_present_documentation_sections_found.3b89b31e": "Keine immer vorhandenen Dokumentationsabschnitte gefunden.",
"i18n:govoplan-docs.no_available_documentation_topics_found.38b84a6d": "Keine verfügbaren Dokumentationsthemen gefunden.",
"i18n:govoplan-docs.no_basic_topics_found.ee5ec3c6": "Keine Grundlagenthemen gefunden.",
"i18n:govoplan-docs.no_common_task_topics_found.c17814cf": "Keine Themen zu häufigen Aufgaben gefunden.",
"i18n:govoplan-docs.no_configured_documentation_topics_found.04dcfb92": "Keine konfigurierten Dokumentationsthemen gefunden.",
"i18n:govoplan-docs.no_configured_modules_found.f6f9ce24": "Keine konfigurierten Module gefunden.",
"i18n:govoplan-docs.no_evidence_documentation_topics_found.c59bd849": "Keine Nachweis-Dokumentationsthemen gefunden.",
"i18n:govoplan-docs.no_evidence_sources_found.be3bb2f6": "Keine Nachweisquellen gefunden.",
"i18n:govoplan-docs.no_granted_platform_permissions_found.36010898": "Keine gewährten Plattformberechtigungen gefunden.",
"i18n:govoplan-docs.no_hidden_installed_routes_found.22f62845": "Keine ausgeblendeten installierten Routen gefunden.",
"i18n:govoplan-docs.no_module_topics_found.0cbbdc9b": "Keine Modulthemen gefunden.",
"i18n:govoplan-docs.no_outline_items.3ea2842e": "Keine Gliederungspunkte für diese Seite.",
"i18n:govoplan-docs.no_pattern_topics_found.35b70eee": "Keine Themen zu Entwurfsmustern gefunden.",
"i18n:govoplan-docs.no_reference_topics_found.724d5a68": "Keine Referenzthemen gefunden.",
"i18n:govoplan-docs.no_system_topics_found.d01a8068": "Keine Systemdokumentation gefunden.",
"i18n:govoplan-docs.no_visible_routes_found_for_this_actor.3feeaf0b": "Keine sichtbaren Routen für diesen Akteur gefunden.",
"i18n:govoplan-docs.no_workflow_topics_found.a82b52da": "Keine Ablaufanleitungen gefunden.",
"i18n:govoplan-docs.no_troubleshooting_topics_found.8c275468": "Keine Themen zu Fehlerbehebung gefunden.",
"i18n:govoplan-docs.no_working_with_govoplan_topics_found.b6db8384": "Keine Themen zum Arbeiten mit GovOPlaN gefunden.",
"i18n:govoplan-docs.outcome.10172bd3": "Ziel",
"i18n:govoplan-docs.page_outline.e5940949": "Auf dieser Seite",
"i18n:govoplan-docs.patterns.a2bc4d8f": "Muster",
"i18n:govoplan-docs.permission.d71c9448": "Berechtigung",
"i18n:govoplan-docs.permissions.d06d5557": "Berechtigungen",
"i18n:govoplan-docs.prerequisites.fdf2407f": "Voraussetzungen",
"i18n:govoplan-docs.purpose.4af40581": "Zweck",
"i18n:govoplan-docs.reference.1fd9d50a": "Referenz",
"i18n:govoplan-docs.related_topics.d5eb8b74": "Verwandte Themen:",
"i18n:govoplan-docs.reload.cce71553": "Neu laden",
"i18n:govoplan-docs.requirements.09a428f9": "Anforderungen",
"i18n:govoplan-docs.result.1f4fbf43": "Ergebnis",
"i18n:govoplan-docs.route.200e2a66": "route,",
"i18n:govoplan-docs.route.4999528e": "Route",
"i18n:govoplan-docs.routes.03730e58": "Routen",
"i18n:govoplan-docs.screen.c4878ec4": "Ansicht",
"i18n:govoplan-docs.section.5e498158": "Bereich",
"i18n:govoplan-docs.source.6da13add": "Quelle",
"i18n:govoplan-docs.status.bae7d5be": "Status",
"i18n:govoplan-docs.steps.6041435e": "Schritte",
"i18n:govoplan-docs.summary.d6b9936d": "Zusammenfassung",
"i18n:govoplan-docs.technical_documentation_for_the_modules_and_conf.267e739e": "Technische Dokumentation für die in dieser Installation aktiven Module und Konfiguration.",
"i18n:govoplan-docs.technical_reference.f271430d": "Technische Referenz",
"i18n:govoplan-docs.this_system.b13a51ad": "Dieses System",
"i18n:govoplan-docs.toggle_tree_node.4070b59f": "Auf- oder zuklappen",
"i18n:govoplan-docs.troubleshooting_and_support.3d42f434": "Fehlerbehebung und Support",
"i18n:govoplan-docs.unlocks.67d569db": "Schaltet frei:",
"i18n:govoplan-docs.user_docs.1e38e8d3": "Benutzerdokumentation",
"i18n:govoplan-docs.validation.ef7f6d9c": "Prüfung",
"i18n:govoplan-docs.verification.4b5f10dd": "Kontrolle",
"i18n:govoplan-docs.visible_routes.6c9b1605": "Sichtbare Routen",
"i18n:govoplan-docs.when_used.b870b361": "Wann verwendet",
"i18n:govoplan-docs.working_with_govoplan.81908ef4": "Arbeiten mit GovOPlaN",
"i18n:govoplan-docs.workflows.4f4a16d1": "Abläufe",
"i18n:govoplan-docs.your_documentation.8a4cd9a3": "Your documentation"
}
};

2
webui/src/index.ts Normal file
View File

@@ -0,0 +1,2 @@
export { docsModule as default, docsModule } from "./module";
export { default as DocsPage } from "./features/docs/DocsPage";

27
webui/src/module.ts Normal file
View File

@@ -0,0 +1,27 @@
import { createElement, lazy } from "react";
import type { PlatformWebModule } from "@govoplan/core-webui";
import { generatedTranslations } from "./i18n/generatedTranslations";
const DocsPage = lazy(() => import("./features/docs/DocsPage"));
const docsReadScopes = ["docs:documentation:read", "system:settings:read", "admin:settings:read"];
const translations = {
en: generatedTranslations.en,
de: generatedTranslations.de
};
export const docsModule: PlatformWebModule = {
id: "docs",
label: "i18n:govoplan-docs.docs.68a41942",
version: "1.0.0",
dependencies: ["access"],
optionalDependencies: ["policy", "audit", "ops", "workflow", "search"],
translations,
navItems: [{ to: "/docs", label: "i18n:govoplan-docs.docs.68a41942", iconName: "reports", anyOf: docsReadScopes, order: 880 }],
routes: [
{ path: "/docs", anyOf: docsReadScopes, order: 880, render: ({ settings }) => createElement(DocsPage, { settings }) }]
};
export default docsModule;