chore: sync GovOPlaN module split state
This commit is contained in:
169
webui/src/api/docs.ts
Normal file
169
webui/src/api/docs.ts
Normal 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}` : ""}`);
|
||||
}
|
||||
950
webui/src/features/docs/DocsPage.tsx
Normal file
950
webui/src/features/docs/DocsPage.tsx
Normal 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(" | ") || "-";
|
||||
}
|
||||
186
webui/src/i18n/generatedTranslations.ts
Normal file
186
webui/src/i18n/generatedTranslations.ts
Normal 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
2
webui/src/index.ts
Normal 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
27
webui/src/module.ts
Normal 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;
|
||||
Reference in New Issue
Block a user