Complete Admin interface patterns
This commit is contained in:
@@ -1,5 +1,9 @@
|
||||
# GovOPlaN Admin
|
||||
|
||||
The Admin-owned workspace sections, lifecycle stages, consequence classes,
|
||||
contextual-help contract, and verification evidence are recorded in
|
||||
[docs/INTERFACE_PATTERN_MIGRATION.md](docs/INTERFACE_PATTERN_MIGRATION.md).
|
||||
|
||||
<!-- govoplan-repository-type:start -->
|
||||
**Repository type:** module (platform).
|
||||
<!-- govoplan-repository-type:end -->
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
# Admin interface pattern migration
|
||||
|
||||
This document records the interface-pattern coverage for the surfaces
|
||||
contributed by `govoplan-admin`. The Access module hosts the administration
|
||||
tree; Admin supplies only its declared sections through `admin.sections`.
|
||||
|
||||
## Surface inventory
|
||||
|
||||
| Surface | Archetype | Authority and state model |
|
||||
| --- | --- | --- |
|
||||
| Administration overview | Navigation dashboard | Counts are server projections. Links exist only for sections already admitted by capabilities, permissions, and the active View. |
|
||||
| System settings | Adaptive configuration | A local draft is compared with the saved server state. Reload preserves an unsaved draft, Save requires system write authority, and maintenance controls also require maintenance authority. |
|
||||
| Configuration changes | Governed work queue and evidence list | Open approval requests and immutable applied history are separate server-authoritative grids. Approval requires an explicit confirmation. |
|
||||
| Configuration packages | Guided preflight/apply/export workspace | JSON input is validated locally, dry-run evidence is shown separately, approval is optional where policy allows it, and application is explicitly confirmed. Reference selectors replace raw IDs unless historical manual mode is selected deliberately. |
|
||||
| Role and group templates | Governed definition directory | Definitions, tenant availability, and role permissions are edited in one draft. Deletion is confirmed and remains blocked by materialized dependencies in the backend. |
|
||||
| Module management | Guided lifecycle workflow and operations evidence | Desired runtime state, package plan, preflight, maintenance gate, daemon request, and durable run evidence are distinct stages. Disabled controls name the earliest blocker. |
|
||||
|
||||
## Consequence classes
|
||||
|
||||
- Overview navigation, reload, filtering, inspecting evidence, dry runs, and
|
||||
exports are reversible.
|
||||
- Settings, templates, desired module state, package plans, and approval
|
||||
requests are governed mutations with permission and validation reasons.
|
||||
- Approving a change, applying a configuration package, enabling maintenance,
|
||||
clearing a saved module plan, cancelling an installer request, and deleting
|
||||
a governance template require shared confirmation surfaces.
|
||||
- Actual package mutation remains outside the API process and is performed by
|
||||
the supervised installer. Run and rollback evidence remains durable.
|
||||
|
||||
## Shared controls and verification
|
||||
|
||||
Admin uses Core `AdminPageLayout`, `DataGrid`, `ReferenceSelect`, `StageRail`,
|
||||
`Dialog`, `ConfirmDialog`, `TableActionGroup`, `ActionBlockerHint`,
|
||||
`DocumentationHelpLink`, `ToggleSwitch`, and status/alert primitives. This
|
||||
inherits Core focus restoration, keyboard order, responsive overflow,
|
||||
disabled-action tooltips, and accessible dialog semantics.
|
||||
|
||||
The focused WebUI check rejects browser-native confirmation calls, private
|
||||
sibling-module imports, untranslated Admin-owned structural headings, missing
|
||||
contextual help, and missing disabled reasons on consequential controls. The
|
||||
manifest regression test fixes the help-context and surface declaration.
|
||||
@@ -28,9 +28,16 @@ manifest = ModuleManifest(
|
||||
title="Use the administration workspace",
|
||||
summary="The administration workspace shows only the sections supplied by enabled modules and allowed by the current account's permissions.",
|
||||
body="System and tenant administration share one workspace. Available sections can include settings, configuration changes and packages, governance templates, groups, and module lifecycle controls. A missing section normally means that its owning module is disabled or the current account lacks the required authority.",
|
||||
documentation_types=("user",),
|
||||
documentation_types=("admin", "user"),
|
||||
audience=("tenant_admin", "system_admin", "operator"),
|
||||
metadata={"kind": "reference"},
|
||||
metadata={
|
||||
"kind": "reference",
|
||||
"help_contexts": [
|
||||
"admin.workspace",
|
||||
"admin.overview",
|
||||
"admin.section-navigation",
|
||||
],
|
||||
},
|
||||
),
|
||||
DocumentationTopic(
|
||||
id="admin.governance-and-module-lifecycle",
|
||||
@@ -40,7 +47,15 @@ manifest = ModuleManifest(
|
||||
documentation_types=("admin",),
|
||||
audience=("system_admin", "operator", "module_admin"),
|
||||
related_modules=("access", "audit", "ops"),
|
||||
metadata={"kind": "reference"},
|
||||
metadata={
|
||||
"kind": "reference",
|
||||
"help_contexts": [
|
||||
"admin.system-settings",
|
||||
"admin.configuration-changes",
|
||||
"admin.configuration-packages",
|
||||
"admin.governance-templates",
|
||||
],
|
||||
},
|
||||
),
|
||||
DocumentationTopic(
|
||||
id="admin.module-lifecycle-workflow",
|
||||
@@ -61,6 +76,12 @@ manifest = ModuleManifest(
|
||||
"admin.module-lifecycle",
|
||||
"admin.module-lifecycle.queue-blocker",
|
||||
],
|
||||
"help_contexts": [
|
||||
"admin.module-lifecycle",
|
||||
"admin.module-lifecycle.queue-blocker",
|
||||
"admin.module-lifecycle.plan",
|
||||
"admin.module-lifecycle.evidence",
|
||||
],
|
||||
},
|
||||
),
|
||||
),
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import unittest
|
||||
|
||||
from govoplan_admin.backend.manifest import manifest
|
||||
|
||||
|
||||
class InterfaceDocumentationContractTests(unittest.TestCase):
|
||||
def test_admin_topics_publish_stable_help_contexts(self) -> None:
|
||||
topics = {topic.id: topic for topic in manifest.documentation}
|
||||
expected = {
|
||||
"admin.workspace": {"admin.workspace", "admin.overview"},
|
||||
"admin.governance-and-module-lifecycle": {
|
||||
"admin.system-settings",
|
||||
"admin.configuration-changes",
|
||||
"admin.configuration-packages",
|
||||
"admin.governance-templates",
|
||||
},
|
||||
"admin.module-lifecycle-workflow": {
|
||||
"admin.module-lifecycle",
|
||||
"admin.module-lifecycle.queue-blocker",
|
||||
"admin.module-lifecycle.plan",
|
||||
"admin.module-lifecycle.evidence",
|
||||
},
|
||||
}
|
||||
for topic_id, contexts in expected.items():
|
||||
self.assertIn(topic_id, topics)
|
||||
metadata = topics[topic_id].metadata or {}
|
||||
self.assertTrue(
|
||||
contexts.issubset(set(metadata.get("help_contexts", ()))),
|
||||
topic_id,
|
||||
)
|
||||
|
||||
def test_admin_contributed_surfaces_remain_declared(self) -> None:
|
||||
surface_ids = {
|
||||
surface.id for surface in manifest.frontend.view_surfaces
|
||||
}
|
||||
self.assertEqual(
|
||||
surface_ids,
|
||||
{
|
||||
"admin.section.overview",
|
||||
"admin.section.system-settings",
|
||||
"admin.section.system-configuration-changes",
|
||||
"admin.section.system-configuration-packages",
|
||||
"admin.section.system-role-templates",
|
||||
"admin.section.system-groups",
|
||||
"admin.section.system-modules",
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
+2
-1
@@ -13,7 +13,8 @@
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"test:installer-workflow": "node --experimental-strip-types --test tests/module-installer-workflow.test.ts"
|
||||
"test:installer-workflow": "node --experimental-strip-types --test tests/module-installer-workflow.test.ts",
|
||||
"test:interface-patterns": "node scripts/test-interface-pattern-language.mjs"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.9",
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { resolve } from "node:path";
|
||||
|
||||
const root = resolve(import.meta.dirname, "..");
|
||||
const read = (path) => readFileSync(resolve(root, path), "utf8");
|
||||
|
||||
const overview = read("src/features/admin/AdminOverviewPanel.tsx");
|
||||
const settings = read("src/features/admin/SystemSettingsPanel.tsx");
|
||||
const changes = read("src/features/admin/ConfigurationChangesPanel.tsx");
|
||||
const packages = read("src/features/admin/ConfigurationPackagesPanel.tsx");
|
||||
const templates = read("src/features/admin/GovernanceTemplatesPanel.tsx");
|
||||
const modules = read("src/features/admin/ModuleManagementPanel.tsx");
|
||||
const moduleSource = read("src/module.ts");
|
||||
const allSource = [overview, settings, changes, packages, templates, modules].join("\n");
|
||||
|
||||
for (const source of [overview, settings, changes, packages, templates, modules]) {
|
||||
assert.match(source, /AdminPageLayout/);
|
||||
assert.match(source, /DocumentationHelpLink/);
|
||||
assert.match(source, /disabledReason|help=/);
|
||||
}
|
||||
|
||||
for (const source of [changes, packages, templates, modules]) {
|
||||
assert.match(source, /ConfirmDialog/);
|
||||
}
|
||||
|
||||
assert.match(changes, /DataGrid/);
|
||||
assert.match(packages, /ReferenceSelect/);
|
||||
assert.match(packages, /manualReferences/);
|
||||
assert.match(templates, /TableActionGroup/);
|
||||
assert.match(modules, /StageRail/);
|
||||
assert.match(modules, /ActionBlockerHint/);
|
||||
assert.match(moduleSource, /version: "0\.1\.8"/);
|
||||
|
||||
assert.doesNotMatch(overview, /title="(?:ADMINISTRATION|GLOBAL|TENANT|GROUP|USER)"/);
|
||||
assert.doesNotMatch(allSource, /window\.(alert|confirm|prompt)\s*\(/);
|
||||
assert.doesNotMatch(allSource, /@govoplan\/(?!core-webui)[^"']+-webui\//);
|
||||
|
||||
console.log("Admin interface pattern-language checks passed.");
|
||||
@@ -3,7 +3,8 @@ import type { ApiSettings } from "@govoplan/core-webui";
|
||||
import { fetchAdminOverview, type AdminOverview } from "../../api/admin";
|
||||
import { Card, MetricCard } from "@govoplan/core-webui";
|
||||
import { Button } from "@govoplan/core-webui";
|
||||
import { AdminPageLayout, adminErrorMessage } from "@govoplan/core-webui";
|
||||
import { AdminPageLayout, DocumentationHelpLink, adminErrorMessage } from "@govoplan/core-webui";
|
||||
import { ADMIN_INTERFACE_I18N, ADMIN_WORKSPACE_DOCUMENTATION } from "./interfacePatterns";
|
||||
|
||||
export default function AdminOverviewPanel({ settings, onSelect, availableSections }: {settings: ApiSettings;onSelect: (section: string) => void;availableSections: ReadonlySet<string>;}) {
|
||||
const [overview, setOverview] = useState<AdminOverview | null>(null);
|
||||
@@ -28,7 +29,7 @@ export default function AdminOverviewPanel({ settings, onSelect, availableSectio
|
||||
some((value) => value !== null && value !== undefined));
|
||||
|
||||
return (
|
||||
<AdminPageLayout title="i18n:govoplan-admin.administration.b8be3d12" description="i18n:govoplan-admin.system_wide_governance_and_tenant_local_access_m.cda72499" loading={loading} error={error} actions={<Button onClick={() => void load()} disabled={loading}>i18n:govoplan-admin.reload.cce71553</Button>}>
|
||||
<AdminPageLayout title="i18n:govoplan-admin.administration.b8be3d12" description="i18n:govoplan-admin.system_wide_governance_and_tenant_local_access_m.cda72499" loading={loading} error={error} actions={<><DocumentationHelpLink reference={ADMIN_WORKSPACE_DOCUMENTATION} /><Button onClick={() => void load()} disabled={loading} disabledReason={loading ? ADMIN_INTERFACE_I18N.loading : undefined}>i18n:govoplan-admin.reload.cce71553</Button></>}>
|
||||
{overview && <>
|
||||
{hasSystemMetrics && <>
|
||||
<div className="admin-overview-section-label">i18n:govoplan-admin.system.bc0792d8</div>
|
||||
@@ -40,7 +41,7 @@ export default function AdminOverviewPanel({ settings, onSelect, availableSectio
|
||||
</div>
|
||||
</>}
|
||||
|
||||
{hasAnySection(availableSections, platformSectionIds) && <Card title="ADMINISTRATION">
|
||||
{hasAnySection(availableSections, platformSectionIds) && <Card title={ADMIN_INTERFACE_I18N.administrationHeading}>
|
||||
<div className="admin-overview-grid">
|
||||
{availableSections.has("system-modules") && <AreaLink title="i18n:govoplan-admin.modules.04e9462c" text="i18n:govoplan-admin.installed_modules_runtime_state_and_startup_stat.38dd7028" onClick={() => onSelect("system-modules")} />}
|
||||
{availableSections.has("system-configuration-packages") && <AreaLink title="i18n:govoplan-admin.configuration_packages.eb2f05f1" text="i18n:govoplan-admin.preflight_approve_apply_and_export_configuration.276bd0f8" onClick={() => onSelect("system-configuration-packages")} />}
|
||||
@@ -50,7 +51,7 @@ export default function AdminOverviewPanel({ settings, onSelect, availableSectio
|
||||
</div>
|
||||
</Card>}
|
||||
|
||||
{hasAnySection(availableSections, globalSectionIds) && <Card title="GLOBAL">
|
||||
{hasAnySection(availableSections, globalSectionIds) && <Card title={ADMIN_INTERFACE_I18N.globalHeading}>
|
||||
<div className="admin-overview-grid">
|
||||
{availableSections.has("system-tenants") && <AreaLink title="i18n:govoplan-admin.tenants.1f7ae776" text="i18n:govoplan-admin.create_suspend_and_govern_tenant_spaces.77992a39" onClick={() => onSelect("system-tenants")} />}
|
||||
{availableSections.has("system-roles") && <AreaLink title="i18n:govoplan-admin.system_roles.a9461aa6" text="i18n:govoplan-admin.instance_wide_roles_assigned_directly_to_global_.91050488" onClick={() => onSelect("system-roles")} />}
|
||||
@@ -71,7 +72,7 @@ export default function AdminOverviewPanel({ settings, onSelect, availableSectio
|
||||
<Metric title="i18n:govoplan-admin.api_keys.94fcf3c2" value={overview.active_api_key_count} text="i18n:govoplan-admin.active_tenant_automation_credentials.240c659e" />
|
||||
</div>
|
||||
|
||||
{hasAnySection(availableSections, tenantSectionIds) && <Card title="TENANT">
|
||||
{hasAnySection(availableSections, tenantSectionIds) && <Card title={ADMIN_INTERFACE_I18N.tenantHeading}>
|
||||
<div className="admin-overview-grid">
|
||||
{availableSections.has("tenant-roles") && <AreaLink title="i18n:govoplan-admin.roles.47dcc27d" text="i18n:govoplan-admin.tenant_permission_bundles_and_system_managed_rol.bb563bea" onClick={() => onSelect("tenant-roles")} />}
|
||||
{availableSections.has("tenant-groups") && <AreaLink title="i18n:govoplan-admin.groups.ae9629f4" text="i18n:govoplan-admin.tenant_memberships_and_inherited_roles.16eda9f6" onClick={() => onSelect("tenant-groups")} />}
|
||||
@@ -85,7 +86,7 @@ export default function AdminOverviewPanel({ settings, onSelect, availableSectio
|
||||
</div>
|
||||
</Card>}
|
||||
|
||||
{hasAnySection(availableSections, groupSectionIds) && <Card title="GROUP">
|
||||
{hasAnySection(availableSections, groupSectionIds) && <Card title={ADMIN_INTERFACE_I18N.groupHeading}>
|
||||
<div className="admin-overview-grid">
|
||||
{availableSections.has("tenant-group-file-connectors") && <AreaLink title="i18n:govoplan-admin.file_connections.1e362326" text="i18n:govoplan-admin.group_file_connector_policy_limits" onClick={() => onSelect("tenant-group-file-connectors")} />}
|
||||
{availableSections.has("tenant-group-mail-servers") && <AreaLink title="i18n:govoplan-admin.mail_servers.d627326a" text="i18n:govoplan-admin.group_mail_server_policy_limits" onClick={() => onSelect("tenant-group-mail-servers")} />}
|
||||
@@ -93,7 +94,7 @@ export default function AdminOverviewPanel({ settings, onSelect, availableSectio
|
||||
</div>
|
||||
</Card>}
|
||||
|
||||
{hasAnySection(availableSections, userSectionIds) && <Card title="USER">
|
||||
{hasAnySection(availableSections, userSectionIds) && <Card title={ADMIN_INTERFACE_I18N.userHeading}>
|
||||
<div className="admin-overview-grid">
|
||||
{availableSections.has("tenant-user-file-connectors") && <AreaLink title="i18n:govoplan-admin.file_connections.1e362326" text="i18n:govoplan-admin.user_file_connector_policy_limits" onClick={() => onSelect("tenant-user-file-connectors")} />}
|
||||
{availableSections.has("tenant-user-mail-servers") && <AreaLink title="i18n:govoplan-admin.mail_servers.d627326a" text="i18n:govoplan-admin.user_mail_server_policy_limits" onClick={() => onSelect("tenant-user-mail-servers")} />}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { Check, RefreshCw } from "lucide-react";
|
||||
import type { ApiSettings } from "@govoplan/core-webui";
|
||||
import { AdminPageLayout, Button, Card, DataGrid, StatusBadge, TableActionGroup, adminErrorMessage, formatDateTime, i18nMessage, mergeDeltaRows, useDeltaWatermarks, type DataGridColumn } from "@govoplan/core-webui";
|
||||
import { AdminPageLayout, Button, Card, ConfirmDialog, DataGrid, DocumentationHelpLink, StatusBadge, TableActionGroup, adminErrorMessage, formatDateTime, i18nMessage, mergeDeltaRows, useDeltaWatermarks, type DataGridColumn } from "@govoplan/core-webui";
|
||||
import {
|
||||
approveConfigurationChangeRequest,
|
||||
fetchConfigurationChangesDelta,
|
||||
type ConfigurationChangeRecord,
|
||||
type ConfigurationChangeRequest } from
|
||||
"../../api/admin";
|
||||
import { ADMIN_GOVERNANCE_DOCUMENTATION, ADMIN_INTERFACE_I18N } from "./interfacePatterns";
|
||||
|
||||
const DELTA_KEY = "admin:configuration-changes";
|
||||
|
||||
@@ -19,6 +20,7 @@ export default function ConfigurationChangesPanel({ settings, canApprove }: {set
|
||||
const { getDeltaWatermark, setDeltaWatermark, resetDeltaWatermark } = useDeltaWatermarks();
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [busyId, setBusyId] = useState("");
|
||||
const [approving, setApproving] = useState<ConfigurationChangeRequest | null>(null);
|
||||
const [error, setError] = useState("");
|
||||
const [message, setMessage] = useState("");
|
||||
|
||||
@@ -68,6 +70,7 @@ export default function ConfigurationChangesPanel({ settings, canApprove }: {set
|
||||
setError(adminErrorMessage(err));
|
||||
} finally {
|
||||
setBusyId("");
|
||||
setApproving(null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,9 +101,16 @@ export default function ConfigurationChangesPanel({ settings, canApprove }: {set
|
||||
id: "approve",
|
||||
label: "i18n:govoplan-admin.approve.7b2c7f14",
|
||||
icon: <Check size={16} aria-hidden="true" />,
|
||||
applicable: canApprove && request.status === "pending_approval",
|
||||
disabled: busyId === request.id,
|
||||
onClick: () => void approve(request)
|
||||
applicable: request.status === "pending_approval",
|
||||
disabled: !canApprove || Boolean(busyId),
|
||||
disabledReason: request.status !== "pending_approval"
|
||||
? "i18n:govoplan-admin.request_is_not_pending_approval.6bf3c031"
|
||||
: !canApprove
|
||||
? ADMIN_INTERFACE_I18N.governanceWriteRequired
|
||||
: busyId
|
||||
? ADMIN_INTERFACE_I18N.busy
|
||||
: undefined,
|
||||
onClick: () => setApproving(request)
|
||||
}]} />
|
||||
}
|
||||
];
|
||||
@@ -115,13 +125,14 @@ export default function ConfigurationChangesPanel({ settings, canApprove }: {set
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<AdminPageLayout
|
||||
title="i18n:govoplan-admin.configuration_changes.82933bbb"
|
||||
description="i18n:govoplan-admin.safety_controlled_configuration_requests_approva.e8259509"
|
||||
loading={loading}
|
||||
error={error}
|
||||
success={message}
|
||||
actions={<Button onClick={() => void load()} disabled={loading}><RefreshCw size={16} /> i18n:govoplan-admin.reload.cce71553</Button>}>
|
||||
actions={<><DocumentationHelpLink reference={ADMIN_GOVERNANCE_DOCUMENTATION} /><Button onClick={() => void load()} disabled={loading || Boolean(busyId)} disabledReason={loading ? ADMIN_INTERFACE_I18N.loading : busyId ? ADMIN_INTERFACE_I18N.busy : undefined}><RefreshCw size={16} /> i18n:govoplan-admin.reload.cce71553</Button></>}>
|
||||
|
||||
<Card title="i18n:govoplan-admin.requests.f7194e6a">
|
||||
<DataGrid
|
||||
@@ -142,7 +153,17 @@ export default function ConfigurationChangesPanel({ settings, canApprove }: {set
|
||||
emptyText="i18n:govoplan-admin.no_applied_configuration_changes.6a3ae4a7"
|
||||
/>
|
||||
</Card>
|
||||
</AdminPageLayout>);
|
||||
</AdminPageLayout>
|
||||
<ConfirmDialog
|
||||
open={Boolean(approving)}
|
||||
title={ADMIN_INTERFACE_I18N.approveTitle}
|
||||
message={ADMIN_INTERFACE_I18N.approveMessage}
|
||||
confirmLabel="i18n:govoplan-admin.approve.7b2c7f14"
|
||||
busy={Boolean(busyId)}
|
||||
onCancel={() => setApproving(null)}
|
||||
onConfirm={() => approving && void approve(approving)}
|
||||
/>
|
||||
</>);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { Check, Download, Play, RefreshCw } from "lucide-react";
|
||||
import type { ApiSettings, AuthInfo } from "@govoplan/core-webui";
|
||||
import { AdminPageLayout, Button, Card, DataGrid, FormField, ReferenceSelect, StatusBadge, ToggleSwitch, adminErrorMessage, i18nMessage, type DataGridColumn } from "@govoplan/core-webui";
|
||||
import { AdminPageLayout, Button, Card, ConfirmDialog, DataGrid, DocumentationHelpLink, FormField, ReferenceSelect, StatusBadge, ToggleSwitch, adminErrorMessage, i18nMessage, type DataGridColumn } from "@govoplan/core-webui";
|
||||
import {
|
||||
applyConfigurationPackage,
|
||||
createConfigurationChangeRequest,
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
createChangeRequestReferenceProvider,
|
||||
createTenantReferenceProvider
|
||||
} from "./configurationReferenceProviders";
|
||||
import { ADMIN_GOVERNANCE_DOCUMENTATION, ADMIN_INTERFACE_I18N } from "./interfacePatterns";
|
||||
|
||||
const SAMPLE_ACCESS_PACKAGE = {
|
||||
package_id: "govoplan.access.minimal-office",
|
||||
@@ -78,6 +79,7 @@ export default function ConfigurationPackagesPanel({ settings, auth, canWrite }:
|
||||
const [lastRequest, setLastRequest] = useState<ConfigurationChangeRequest | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [busy, setBusy] = useState("");
|
||||
const [confirmApply, setConfirmApply] = useState(false);
|
||||
const [error, setError] = useState("");
|
||||
const [message, setMessage] = useState("");
|
||||
|
||||
@@ -234,13 +236,14 @@ export default function ConfigurationPackagesPanel({ settings, auth, canWrite }:
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<AdminPageLayout
|
||||
title="i18n:govoplan-admin.configuration_packages.eb2f05f1"
|
||||
description="i18n:govoplan-admin.import_preflight_approve_apply_and_export_module.29aec929"
|
||||
loading={loading}
|
||||
error={error || parsedPackage.error || parsedSuppliedData.error || ""}
|
||||
success={message}
|
||||
actions={<Button onClick={() => void loadCatalog()} disabled={loading || Boolean(busy)}><RefreshCw size={16} /> i18n:govoplan-admin.reload.cce71553</Button>}>
|
||||
actions={<><DocumentationHelpLink reference={ADMIN_GOVERNANCE_DOCUMENTATION} /><Button onClick={() => void loadCatalog()} disabled={loading || Boolean(busy)} disabledReason={loading ? ADMIN_INTERFACE_I18N.loading : busy ? ADMIN_INTERFACE_I18N.busy : undefined}><RefreshCw size={16} /> i18n:govoplan-admin.reload.cce71553</Button></>}>
|
||||
|
||||
<Card title="i18n:govoplan-admin.catalog.4a88d27b">
|
||||
<div className="button-row compact-actions">
|
||||
@@ -257,12 +260,12 @@ export default function ConfigurationPackagesPanel({ settings, auth, canWrite }:
|
||||
<ToggleSwitch
|
||||
checked={manualReferences}
|
||||
onChange={setManualReferences}
|
||||
label="Enter reference IDs manually"
|
||||
help="Use only when importing a package that intentionally references a target no longer returned by the live catalog."
|
||||
label={ADMIN_INTERFACE_I18N.enterReferencesManually}
|
||||
help={ADMIN_INTERFACE_I18N.manualReferenceHelp}
|
||||
/>
|
||||
</div>
|
||||
<div className="wide">
|
||||
<FormField label="i18n:govoplan-admin.tenant_id.59eba244">
|
||||
<FormField label="i18n:govoplan-admin.tenant_id.59eba244" documentation={ADMIN_GOVERNANCE_DOCUMENTATION}>
|
||||
{manualReferences ? (
|
||||
<input value={tenantId} onChange={(event) => setTenantId(event.target.value)} placeholder="active tenant" />
|
||||
) : (
|
||||
@@ -273,15 +276,15 @@ export default function ConfigurationPackagesPanel({ settings, auth, canWrite }:
|
||||
setChangeRequestId("");
|
||||
}}
|
||||
provider={tenantProvider}
|
||||
aria-label="Configuration package tenant"
|
||||
placeholder="Select a tenant"
|
||||
aria-label={ADMIN_INTERFACE_I18N.tenantPickerLabel}
|
||||
placeholder={ADMIN_INTERFACE_I18N.selectTenant}
|
||||
disabled={Boolean(busy)}
|
||||
/>
|
||||
)}
|
||||
</FormField>
|
||||
</div>
|
||||
<div className="wide">
|
||||
<FormField label="i18n:govoplan-admin.change_request_id.96ee3239">
|
||||
<FormField label="i18n:govoplan-admin.change_request_id.96ee3239" documentation={ADMIN_GOVERNANCE_DOCUMENTATION}>
|
||||
{manualReferences ? (
|
||||
<input value={changeRequestId} onChange={(event) => setChangeRequestId(event.target.value)} placeholder="cfgreq-..." />
|
||||
) : (
|
||||
@@ -289,9 +292,9 @@ export default function ConfigurationPackagesPanel({ settings, auth, canWrite }:
|
||||
value={changeRequestId}
|
||||
onChange={(value) => setChangeRequestId(value)}
|
||||
provider={changeRequestProvider}
|
||||
aria-label="Configuration package change request"
|
||||
placeholder="Select an eligible request (optional)"
|
||||
emptyText="No eligible configuration requests."
|
||||
aria-label={ADMIN_INTERFACE_I18N.requestPickerLabel}
|
||||
placeholder={ADMIN_INTERFACE_I18N.selectEligibleRequest}
|
||||
emptyText={ADMIN_INTERFACE_I18N.noEligibleRequests}
|
||||
disabled={Boolean(busy)}
|
||||
/>
|
||||
)}
|
||||
@@ -301,10 +304,10 @@ export default function ConfigurationPackagesPanel({ settings, auth, canWrite }:
|
||||
<label className="wide"><span>i18n:govoplan-admin.supplied_data_json.6932bfb7</span><textarea rows={6} value={suppliedDataText} onChange={(event) => setSuppliedDataText(event.target.value)} /></label>
|
||||
</div>
|
||||
<div className="button-row compact-actions">
|
||||
<Button onClick={() => void runDryRun()} disabled={!canRun || Boolean(busy)}><Play size={16} /> i18n:govoplan-admin.dry_run.3d14659c</Button>
|
||||
<Button onClick={() => void requestApproval()} disabled={!canWrite || !parsedPackage.value || Boolean(busy)}><Check size={16} /> i18n:govoplan-admin.request_approval.6245aea1</Button>
|
||||
<Button variant="primary" onClick={() => void applyPackage()} disabled={!canWrite || !canRun || Boolean(busy)}><Check size={16} /> i18n:govoplan-admin.apply.cfea419c</Button>
|
||||
<Button onClick={() => void exportAccessPackage()} disabled={Boolean(busy)}><Download size={16} /> i18n:govoplan-admin.export_access.9a2eb91e</Button>
|
||||
<Button onClick={() => void runDryRun()} disabled={!canRun || Boolean(busy)} disabledReason={busy ? ADMIN_INTERFACE_I18N.busy : !canRun ? ADMIN_INTERFACE_I18N.validJsonRequired : undefined}><Play size={16} /> i18n:govoplan-admin.dry_run.3d14659c</Button>
|
||||
<Button onClick={() => void requestApproval()} disabled={!canWrite || !parsedPackage.value || Boolean(busy)} disabledReason={busy ? ADMIN_INTERFACE_I18N.busy : !canWrite ? ADMIN_INTERFACE_I18N.writeRequired : !parsedPackage.value ? ADMIN_INTERFACE_I18N.packageRequired : undefined}><Check size={16} /> i18n:govoplan-admin.request_approval.6245aea1</Button>
|
||||
<Button variant="primary" onClick={() => setConfirmApply(true)} disabled={!canWrite || !canRun || Boolean(busy)} disabledReason={busy ? ADMIN_INTERFACE_I18N.busy : !canWrite ? ADMIN_INTERFACE_I18N.writeRequired : !canRun ? ADMIN_INTERFACE_I18N.validJsonRequired : undefined}><Check size={16} /> i18n:govoplan-admin.apply.cfea419c</Button>
|
||||
<Button onClick={() => void exportAccessPackage()} disabled={Boolean(busy)} disabledReason={busy ? ADMIN_INTERFACE_I18N.busy : undefined}><Download size={16} /> i18n:govoplan-admin.export_access.9a2eb91e</Button>
|
||||
</div>
|
||||
{lastRequest && <p className="muted small-note">i18n:govoplan-admin.last_request.4508ef35 {lastRequest.id} ({lastRequest.status})</p>}
|
||||
</Card>
|
||||
@@ -324,7 +327,20 @@ export default function ConfigurationPackagesPanel({ settings, auth, canWrite }:
|
||||
{exportText && <Card title="i18n:govoplan-admin.export.f3e4fadb">
|
||||
<textarea className="code-panel module-install-plan-commands" rows={16} value={exportText} onChange={(event) => setExportText(event.target.value)} />
|
||||
</Card>}
|
||||
</AdminPageLayout>);
|
||||
</AdminPageLayout>
|
||||
<ConfirmDialog
|
||||
open={confirmApply}
|
||||
title={ADMIN_INTERFACE_I18N.applyTitle}
|
||||
message={ADMIN_INTERFACE_I18N.applyMessage}
|
||||
confirmLabel={ADMIN_INTERFACE_I18N.applyConfirm}
|
||||
busy={busy === "apply"}
|
||||
onCancel={() => setConfirmApply(false)}
|
||||
onConfirm={() => {
|
||||
setConfirmApply(false);
|
||||
void applyPackage();
|
||||
}}
|
||||
/>
|
||||
</>);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,8 @@ import {
|
||||
type PermissionItem,
|
||||
type TenantAdminItem } from
|
||||
"../../api/admin";
|
||||
import { AdminIconButton, AdminPageLayout, AdminSelectionList, TableActionGroup, adminErrorMessage, i18nMessage, useUnsavedDraftGuard } from "@govoplan/core-webui";
|
||||
import { AdminIconButton, AdminPageLayout, AdminSelectionList, DocumentationHelpLink, TableActionGroup, adminErrorMessage, i18nMessage, useUnsavedDraftGuard } from "@govoplan/core-webui";
|
||||
import { ADMIN_GOVERNANCE_DOCUMENTATION, ADMIN_INTERFACE_I18N, mutationDisabledReason } from "./interfacePatterns";
|
||||
|
||||
const emptyDraft = {
|
||||
slug: "",
|
||||
@@ -189,8 +190,8 @@ export default function GovernanceTemplatesPanel({
|
||||
id: "actions", header: "i18n:govoplan-admin.actions.c3cd636a", width: 150, sticky: "end", resizable: false, align: "right",
|
||||
render: (row) => <TableActionGroup actions={[
|
||||
{ id: "inspect", label: i18nMessage("i18n:govoplan-admin.inspect_value.9d5d1071", { value0: row.name }), icon: <Search />, onClick: () => setViewing(row) },
|
||||
{ id: "edit", label: i18nMessage("i18n:govoplan-admin.edit_value.fad75899", { value0: row.name }), icon: <Pencil />, disabled: !canWrite, onClick: () => openEdit(row) },
|
||||
{ id: "delete", label: i18nMessage("i18n:govoplan-admin.delete_value.4d18989e", { value0: row.name }), icon: <Trash2 />, variant: "danger", disabled: !canWrite, onClick: () => setDeleting(row) }
|
||||
{ id: "edit", label: i18nMessage("i18n:govoplan-admin.edit_value.fad75899", { value0: row.name }), icon: <Pencil />, disabled: !canWrite, disabledReason: !canWrite ? ADMIN_INTERFACE_I18N.governanceWriteRequired : undefined, onClick: () => openEdit(row) },
|
||||
{ id: "delete", label: i18nMessage("i18n:govoplan-admin.delete_value.4d18989e", { value0: row.name }), icon: <Trash2 />, variant: "danger", disabled: !canWrite, disabledReason: !canWrite ? ADMIN_INTERFACE_I18N.governanceWriteRequired : undefined, onClick: () => setDeleting(row) }
|
||||
]} />
|
||||
}],
|
||||
[canWrite, kind, tenants]);
|
||||
@@ -208,7 +209,7 @@ export default function GovernanceTemplatesPanel({
|
||||
loading={loading}
|
||||
error={error}
|
||||
success={success}
|
||||
actions={<><Button onClick={() => void load()} disabled={loading}>i18n:govoplan-admin.reload.cce71553</Button><AdminIconButton label={kind === "group" ? "i18n:govoplan-admin.add_group_template.b74d8f0f" : "i18n:govoplan-admin.add_tenant_role.fcd904ea"} icon={<Plus />} variant="primary" onClick={openCreate} disabled={!canWrite} /></>}>
|
||||
actions={<><DocumentationHelpLink reference={ADMIN_GOVERNANCE_DOCUMENTATION} /><Button onClick={() => void load()} disabled={loading || busy} disabledReason={loading ? ADMIN_INTERFACE_I18N.loading : busy ? ADMIN_INTERFACE_I18N.busy : undefined}>i18n:govoplan-admin.reload.cce71553</Button><AdminIconButton label={kind === "group" ? "i18n:govoplan-admin.add_group_template.b74d8f0f" : "i18n:govoplan-admin.add_tenant_role.fcd904ea"} icon={<Plus />} variant="primary" onClick={openCreate} disabled={!canWrite} disabledReason={!canWrite ? ADMIN_INTERFACE_I18N.governanceWriteRequired : undefined} /></>}>
|
||||
|
||||
<div className="admin-table-surface">
|
||||
<DataGrid id={`admin-system-${kind}-templates-v3`} rows={items} columns={columns} initialFit="container" getRowKey={(row) => row.id} emptyText={i18nMessage("i18n:govoplan-admin.no_central_value_templates_found.081149fa", { value0: kind })} />
|
||||
@@ -220,7 +221,7 @@ export default function GovernanceTemplatesPanel({
|
||||
title={editing === "new" ? kind === "group" ? "i18n:govoplan-admin.create_group_template.72407248" : "i18n:govoplan-admin.create_tenant_role.f58db104" : kind === "group" ? "i18n:govoplan-admin.edit_group_template.9bc72d21" : "i18n:govoplan-admin.edit_tenant_role.c15a260d"}
|
||||
onClose={() => !busy && setEditing(null)}
|
||||
className="admin-dialog admin-dialog-wide"
|
||||
footer={<><Button onClick={() => setEditing(null)} disabled={busy}>i18n:govoplan-admin.cancel.77dfd213</Button><Button variant="primary" onClick={() => void save()} disabled={busy || !draft.name.trim() || !draft.slug.trim()}>{busy ? "i18n:govoplan-admin.saving.56a2285c" : kind === "group" ? "i18n:govoplan-admin.save_template.0885fab2" : "i18n:govoplan-admin.save_tenant_role.8fc0d37d"}</Button></>}>
|
||||
footer={<><Button onClick={() => setEditing(null)} disabled={busy} disabledReason={busy ? ADMIN_INTERFACE_I18N.busy : undefined}>i18n:govoplan-admin.cancel.77dfd213</Button><Button variant="primary" onClick={() => void save()} disabledReason={mutationDisabledReason({ busy, permitted: canWrite, complete: Boolean(draft.name.trim() && draft.slug.trim()) })}>{busy ? "i18n:govoplan-admin.saving.56a2285c" : kind === "group" ? "i18n:govoplan-admin.save_template.0885fab2" : "i18n:govoplan-admin.save_tenant_role.8fc0d37d"}</Button></>}>
|
||||
|
||||
<div className="admin-form-grid two-columns">
|
||||
<FormField label="i18n:govoplan-admin.name.709a2322"><input value={draft.name} onChange={(event) => setDraft({ ...draft, name: event.target.value })} /></FormField>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import type { ApiSettings } from "@govoplan/core-webui";
|
||||
import { ActionBlockerHint, AdminPageLayout, adminErrorMessage, Button, dispatchPlatformModulesChanged, DocumentationHelpLink, formatDateTime, MetricCard, StageRail, StatusBadge, ToggleSwitch, i18nMessage, useUnsavedDraftGuard, type FormatDateTimeOptions } from "@govoplan/core-webui";
|
||||
import { ActionBlockerHint, AdminPageLayout, adminErrorMessage, Button, ConfirmDialog, dispatchPlatformModulesChanged, DocumentationHelpLink, formatDateTime, MetricCard, StageRail, StatusBadge, ToggleSwitch, i18nMessage, useUnsavedDraftGuard, type FormatDateTimeOptions } from "@govoplan/core-webui";
|
||||
import { Check, Clock, FileText, Pencil, Send } from "lucide-react";
|
||||
import {
|
||||
cancelModuleInstallerRequest,
|
||||
@@ -42,6 +42,7 @@ import {
|
||||
type ModuleInstallerWorkflowStageId,
|
||||
type ModuleInstallerWorkflowStageState
|
||||
} from "./moduleInstallerWorkflow";
|
||||
import { ADMIN_INTERFACE_I18N, MODULE_LIFECYCLE_DOCUMENTATION } from "./interfacePatterns";
|
||||
|
||||
const MODULE_INSTALLER_I18N = {
|
||||
queueUnavailable: "i18n:govoplan-admin.queue_supervised_run_unavailable.f1a20202",
|
||||
@@ -67,6 +68,9 @@ export default function ModuleManagementPanel({ settings, canWrite, canAccessMai
|
||||
const [planBusy, setPlanBusy] = useState(false);
|
||||
const [error, setError] = useState("");
|
||||
const [success, setSuccess] = useState("");
|
||||
const [confirmClearPlan, setConfirmClearPlan] = useState(false);
|
||||
const [confirmMaintenance, setConfirmMaintenance] = useState(false);
|
||||
const [cancelRequestId, setCancelRequestId] = useState("");
|
||||
|
||||
async function load() {
|
||||
setLoading(true);
|
||||
@@ -143,6 +147,15 @@ export default function ModuleManagementPanel({ settings, canWrite, canAccessMai
|
||||
moduleStateRequest ? "i18n:govoplan-admin.apply_approved_request.ab218d9a" : "i18n:govoplan-admin.create_request_and_apply.da79e153" :
|
||||
moduleStateRequest ? "i18n:govoplan-admin.waiting_for_maintenance.60d00019" : "i18n:govoplan-admin.create_dry_run_request.05b9cbdb";
|
||||
const saveDisabled = !canWrite || !dirty || busy || !maintenanceEnabled && Boolean(moduleStateRequest);
|
||||
const saveDisabledReason = busy
|
||||
? ADMIN_INTERFACE_I18N.busy
|
||||
: !canWrite
|
||||
? ADMIN_INTERFACE_I18N.writeRequired
|
||||
: !dirty
|
||||
? ADMIN_INTERFACE_I18N.noPendingChanges
|
||||
: !maintenanceEnabled && moduleStateRequest
|
||||
? ADMIN_INTERFACE_I18N.maintenanceRequired
|
||||
: undefined;
|
||||
|
||||
function setModuleDesired(module: ModuleCatalogItem, enabled: boolean) {
|
||||
if (module.protected) return;
|
||||
@@ -358,13 +371,14 @@ export default function ModuleManagementPanel({ settings, canWrite, canAccessMai
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<AdminPageLayout
|
||||
title="i18n:govoplan-admin.system_modules.629539f2"
|
||||
description="i18n:govoplan-admin.installed_modules_active_runtime_state_and_saved.f33907dd"
|
||||
loading={loading}
|
||||
error={error}
|
||||
success={success}
|
||||
actions={<><DocumentationHelpLink reference={{ topicId: "admin.module-lifecycle-workflow", documentationType: "admin" }} /><Button onClick={() => void load()} disabled={loading || busy}>i18n:govoplan-admin.reload.cce71553</Button><Button variant="primary" onClick={() => void save()} disabled={saveDisabled}>{busy ? "i18n:govoplan-admin.working.049ac820" : saveLabel}</Button></>}>
|
||||
actions={<><DocumentationHelpLink reference={MODULE_LIFECYCLE_DOCUMENTATION} /><Button onClick={() => void load()} disabled={loading || busy} disabledReason={loading ? ADMIN_INTERFACE_I18N.loading : busy ? ADMIN_INTERFACE_I18N.busy : undefined}>i18n:govoplan-admin.reload.cce71553</Button><Button variant="primary" onClick={() => void save()} disabled={saveDisabled} disabledReason={saveDisabledReason}>{busy ? "i18n:govoplan-admin.working.049ac820" : saveLabel}</Button></>}>
|
||||
|
||||
{catalog && <>
|
||||
<div className="metric-grid module-management-metrics">
|
||||
@@ -409,7 +423,7 @@ export default function ModuleManagementPanel({ settings, canWrite, canAccessMai
|
||||
<p className="alert warning">i18n:govoplan-admin.changing_enabled_modules_requires_a_dry_run_chan.10b6f5ed</p>
|
||||
{moduleStateRequest && <p>i18n:govoplan-admin.change_request_ready.b17e8c57 <code>{moduleStateRequest.request.id}</code></p>}
|
||||
{!maintenanceEnabled && <div className="button-row compact-actions">
|
||||
<Button onClick={() => void enableMaintenanceMode()} disabled={!canWrite || !canAccessMaintenance || maintenanceBusy}>
|
||||
<Button onClick={() => setConfirmMaintenance(true)} disabled={!canWrite || !canAccessMaintenance || maintenanceBusy} disabledReason={maintenanceBusy ? ADMIN_INTERFACE_I18N.busy : !canWrite ? ADMIN_INTERFACE_I18N.writeRequired : !canAccessMaintenance ? ADMIN_INTERFACE_I18N.maintenanceAuthorityRequired : undefined}>
|
||||
{maintenanceBusy ? "i18n:govoplan-admin.enabling.2b8e03e6" : "i18n:govoplan-admin.enable_maintenance_mode.75f98d57"}
|
||||
</Button>
|
||||
</div>}
|
||||
@@ -444,9 +458,10 @@ export default function ModuleManagementPanel({ settings, canWrite, canAccessMai
|
||||
label={desiredEnabled ? "i18n:govoplan-admin.enabled.df174a3f" : "i18n:govoplan-admin.disabled.f4f4473d"}
|
||||
checked={desiredEnabled}
|
||||
disabled={!canWrite || module.protected || busy}
|
||||
help={!canWrite ? ADMIN_INTERFACE_I18N.writeRequired : module.protected ? ADMIN_INTERFACE_I18N.protectedDefinition : busy ? ADMIN_INTERFACE_I18N.busy : undefined}
|
||||
onChange={(checked) => setModuleDesired(module, checked)} />
|
||||
|
||||
{module.install_uninstall_supported && <Button onClick={() => void addUninstallPlan(module)} disabled={!canWrite || planBusy || module.current_enabled || module.desired_enabled}>i18n:govoplan-admin.plan_uninstall.e62804ab</Button>}
|
||||
{module.install_uninstall_supported && <Button onClick={() => void addUninstallPlan(module)} disabled={!canWrite || planBusy || module.current_enabled || module.desired_enabled} disabledReason={planBusy ? ADMIN_INTERFACE_I18N.busy : !canWrite ? ADMIN_INTERFACE_I18N.writeRequired : module.current_enabled || module.desired_enabled ? ADMIN_INTERFACE_I18N.deactivateBeforeUninstall : undefined}>i18n:govoplan-admin.plan_uninstall.e62804ab</Button>}
|
||||
</div>
|
||||
</div>);
|
||||
|
||||
@@ -510,7 +525,7 @@ export default function ModuleManagementPanel({ settings, canWrite, canAccessMai
|
||||
{item.recovery_notes && <p className="module-package-catalog-description">{item.recovery_notes}</p>}
|
||||
{item.license_reason && <p className={`module-package-catalog-description${item.license_allowed ? "" : " alert warning"}`}>{item.license_reason}</p>}
|
||||
</div>
|
||||
<Button onClick={() => void addCatalogItem(item)} disabled={!canWrite || planBusy || !packageCatalog.valid || !item.license_allowed || !["install", "update"].includes(item.action)}>{catalogPlanButtonLabel(item, catalog)}</Button>
|
||||
<Button onClick={() => void addCatalogItem(item)} disabled={!canWrite || planBusy || !packageCatalog.valid || !item.license_allowed || !["install", "update"].includes(item.action)} disabledReason={planBusy ? ADMIN_INTERFACE_I18N.busy : !canWrite ? ADMIN_INTERFACE_I18N.writeRequired : !packageCatalog.valid || !item.license_allowed || !["install", "update"].includes(item.action) ? ADMIN_INTERFACE_I18N.catalogBlocked : undefined}>{catalogPlanButtonLabel(item, catalog)}</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>}
|
||||
@@ -523,9 +538,9 @@ export default function ModuleManagementPanel({ settings, canWrite, canAccessMai
|
||||
<p>i18n:govoplan-admin.plan_package_installs_and_removals_here_then_app.4aeb03bf</p>
|
||||
</div>
|
||||
<div className="button-row compact-actions">
|
||||
<Button onClick={addPlanItem} disabled={!canWrite || planBusy}>i18n:govoplan-admin.add_plan_item.fa55f028</Button>
|
||||
<Button onClick={() => void clearPlan()} disabled={!canWrite || planBusy || draftPlanItems.length === 0}>i18n:govoplan-admin.clear.719ea396</Button>
|
||||
<Button variant="primary" onClick={() => void savePlan()} disabled={!canWrite || planBusy || !planDirty || !planValid}>{planBusy ? "i18n:govoplan-admin.saving.ae7e8875" : "i18n:govoplan-admin.save_plan.842dc280"}</Button>
|
||||
<Button onClick={addPlanItem} disabled={!canWrite || planBusy} disabledReason={planBusy ? ADMIN_INTERFACE_I18N.busy : !canWrite ? ADMIN_INTERFACE_I18N.writeRequired : undefined}>i18n:govoplan-admin.add_plan_item.fa55f028</Button>
|
||||
<Button onClick={() => setConfirmClearPlan(true)} disabled={!canWrite || planBusy || draftPlanItems.length === 0} disabledReason={planBusy ? ADMIN_INTERFACE_I18N.busy : !canWrite ? ADMIN_INTERFACE_I18N.writeRequired : draftPlanItems.length === 0 ? ADMIN_INTERFACE_I18N.planRequired : undefined}>i18n:govoplan-admin.clear.719ea396</Button>
|
||||
<Button variant="primary" onClick={() => void savePlan()} disabled={!canWrite || planBusy || !planDirty || !planValid} disabledReason={planBusy ? ADMIN_INTERFACE_I18N.busy : !canWrite ? ADMIN_INTERFACE_I18N.writeRequired : !planDirty ? ADMIN_INTERFACE_I18N.noPendingChanges : !planValid ? ADMIN_INTERFACE_I18N.planRequired : undefined}>{planBusy ? "i18n:govoplan-admin.saving.ae7e8875" : "i18n:govoplan-admin.save_plan.842dc280"}</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -607,14 +622,14 @@ export default function ModuleManagementPanel({ settings, canWrite, canAccessMai
|
||||
<label><span>i18n:govoplan-admin.action.97c89a4d</span><select value={item.action} disabled={!canWrite || planBusy} onChange={(event) => updatePlanItem(index, { action: event.target.value as ModuleInstallPlanItem["action"] })}><option value="install">i18n:govoplan-admin.install.fd6c3ebf</option><option value="update">i18n:govoplan-admin.update.503a059f</option><option value="uninstall">i18n:govoplan-admin.uninstall.a735da1d</option></select></label>
|
||||
<label><span>i18n:govoplan-admin.module.b8ff0289</span><input value={item.module_id} disabled={!canWrite || planBusy} onChange={(event) => updatePlanItem(index, { module_id: event.target.value })} placeholder="files" /></label>
|
||||
<label><span>i18n:govoplan-admin.python_package.34591c71</span><input value={item.python_package ?? ""} disabled={!canWrite || planBusy} onChange={(event) => updatePlanItem(index, { python_package: event.target.value })} placeholder="govoplan-files" /></label>
|
||||
<ToggleSwitch label="i18n:govoplan-admin.destroy_data.34557c3c" checked={Boolean(item.destroy_data)} onChange={(checked) => updatePlanItem(index, { destroy_data: checked })} disabled={!canWrite || planBusy || item.action !== "uninstall"} />
|
||||
<ToggleSwitch label="i18n:govoplan-admin.data_safety_reviewed.b4774edc" checked={Boolean(item.data_safety_acknowledged)} onChange={(checked) => updatePlanItem(index, { data_safety_acknowledged: checked })} disabled={!canWrite || planBusy || item.action === "uninstall"} />
|
||||
<ToggleSwitch label="i18n:govoplan-admin.destroy_data.34557c3c" checked={Boolean(item.destroy_data)} onChange={(checked) => updatePlanItem(index, { destroy_data: checked })} disabled={!canWrite || planBusy || item.action !== "uninstall"} help={!canWrite ? ADMIN_INTERFACE_I18N.writeRequired : planBusy ? ADMIN_INTERFACE_I18N.busy : item.action !== "uninstall" ? ADMIN_INTERFACE_I18N.uninstallOnly : undefined} />
|
||||
<ToggleSwitch label="i18n:govoplan-admin.data_safety_reviewed.b4774edc" checked={Boolean(item.data_safety_acknowledged)} onChange={(checked) => updatePlanItem(index, { data_safety_acknowledged: checked })} disabled={!canWrite || planBusy || item.action === "uninstall"} help={!canWrite ? ADMIN_INTERFACE_I18N.writeRequired : planBusy ? ADMIN_INTERFACE_I18N.busy : item.action === "uninstall" ? ADMIN_INTERFACE_I18N.installUpdateOnly : undefined} />
|
||||
<label className="wide"><span>i18n:govoplan-admin.python_ref.1af35d0d</span><input value={item.python_ref ?? ""} disabled={!canWrite || planBusy || item.action === "uninstall"} onChange={(event) => updatePlanItem(index, { python_ref: event.target.value })} placeholder="govoplan-files @ git+ssh://...@v0.1.4" /></label>
|
||||
<label><span>i18n:govoplan-admin.webui_package.19645536</span><input value={item.webui_package ?? ""} disabled={!canWrite || planBusy} onChange={(event) => updatePlanItem(index, { webui_package: event.target.value })} placeholder="@govoplan/files-webui" /></label>
|
||||
<label className="wide"><span>i18n:govoplan-admin.webui_ref.cbae3559</span><input value={item.webui_ref ?? ""} disabled={!canWrite || planBusy || item.action === "uninstall"} onChange={(event) => updatePlanItem(index, { webui_ref: event.target.value })} placeholder="git+ssh://...#v0.1.4" /></label>
|
||||
<label><span>i18n:govoplan-admin.status.bae7d5be</span><select value={item.status} disabled={!canWrite || planBusy} onChange={(event) => updatePlanItem(index, { status: event.target.value as ModuleInstallPlanItem["status"] })}><option value="planned">i18n:govoplan-admin.planned.9cbe42aa</option><option value="applied">i18n:govoplan-admin.applied.a3e4a569</option><option value="blocked">i18n:govoplan-admin.blocked.99613c74</option></select></label>
|
||||
<label className="wide"><span>i18n:govoplan-admin.notes.70440046</span><input value={item.notes ?? ""} disabled={!canWrite || planBusy} onChange={(event) => updatePlanItem(index, { notes: event.target.value })} /></label>
|
||||
<div className="module-install-plan-actions"><Button onClick={() => setDraftPlanItems((items) => items.filter((_, itemIndex) => itemIndex !== index))} disabled={!canWrite || planBusy}>i18n:govoplan-admin.remove.e963907d</Button></div>
|
||||
<div className="module-install-plan-actions"><Button onClick={() => setDraftPlanItems((items) => items.filter((_, itemIndex) => itemIndex !== index))} disabled={!canWrite || planBusy} disabledReason={planBusy ? ADMIN_INTERFACE_I18N.busy : !canWrite ? ADMIN_INTERFACE_I18N.writeRequired : undefined}>i18n:govoplan-admin.remove.e963907d</Button></div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -649,13 +664,13 @@ export default function ModuleManagementPanel({ settings, canWrite, canAccessMai
|
||||
actor: MODULE_INSTALLER_I18N.responsibleActor,
|
||||
target: MODULE_INSTALLER_I18N.resolutionTarget
|
||||
}}
|
||||
documentation={{ topicId: "admin.module-lifecycle-workflow", documentationType: "admin" }} />
|
||||
documentation={MODULE_LIFECYCLE_DOCUMENTATION} />
|
||||
) : null}
|
||||
<div className="module-installer-request-grid">
|
||||
<ToggleSwitch label="i18n:govoplan-admin.run_migrations.db6e0ce2" checked={requestOptions.migrateDatabase} onChange={(checked) => setRequestOptions((current) => ({ ...current, migrateDatabase: checked }))} disabled={!canWrite || planBusy} />
|
||||
<ToggleSwitch label="i18n:govoplan-admin.build_webui.fe8ccad7" checked={requestOptions.buildWebui} onChange={(checked) => setRequestOptions((current) => ({ ...current, buildWebui: checked }))} disabled={!canWrite || planBusy} />
|
||||
<ToggleSwitch label="i18n:govoplan-admin.activate_installs.731d22a1" checked={requestOptions.activateInstalledModules} onChange={(checked) => setRequestOptions((current) => ({ ...current, activateInstalledModules: checked }))} disabled={!canWrite || planBusy} />
|
||||
<ToggleSwitch label="i18n:govoplan-admin.drop_uninstalls_from_startup.0e2a5c5b" checked={requestOptions.removeUninstalledModulesFromDesired} onChange={(checked) => setRequestOptions((current) => ({ ...current, removeUninstalledModulesFromDesired: checked }))} disabled={!canWrite || planBusy} />
|
||||
<ToggleSwitch label="i18n:govoplan-admin.run_migrations.db6e0ce2" checked={requestOptions.migrateDatabase} onChange={(checked) => setRequestOptions((current) => ({ ...current, migrateDatabase: checked }))} disabled={!canWrite || planBusy} help={!canWrite ? ADMIN_INTERFACE_I18N.writeRequired : planBusy ? ADMIN_INTERFACE_I18N.busy : undefined} />
|
||||
<ToggleSwitch label="i18n:govoplan-admin.build_webui.fe8ccad7" checked={requestOptions.buildWebui} onChange={(checked) => setRequestOptions((current) => ({ ...current, buildWebui: checked }))} disabled={!canWrite || planBusy} help={!canWrite ? ADMIN_INTERFACE_I18N.writeRequired : planBusy ? ADMIN_INTERFACE_I18N.busy : undefined} />
|
||||
<ToggleSwitch label="i18n:govoplan-admin.activate_installs.731d22a1" checked={requestOptions.activateInstalledModules} onChange={(checked) => setRequestOptions((current) => ({ ...current, activateInstalledModules: checked }))} disabled={!canWrite || planBusy} help={!canWrite ? ADMIN_INTERFACE_I18N.writeRequired : planBusy ? ADMIN_INTERFACE_I18N.busy : undefined} />
|
||||
<ToggleSwitch label="i18n:govoplan-admin.drop_uninstalls_from_startup.0e2a5c5b" checked={requestOptions.removeUninstalledModulesFromDesired} onChange={(checked) => setRequestOptions((current) => ({ ...current, removeUninstalledModulesFromDesired: checked }))} disabled={!canWrite || planBusy} help={!canWrite ? ADMIN_INTERFACE_I18N.writeRequired : planBusy ? ADMIN_INTERFACE_I18N.busy : undefined} />
|
||||
<label><span>i18n:govoplan-admin.health_urls.3b86647c</span><textarea value={requestOptions.healthUrls} disabled={!canWrite || planBusy} onChange={(event) => setRequestOptions((current) => ({ ...current, healthUrls: event.target.value }))} placeholder="http://127.0.0.1:8000/health" /></label>
|
||||
<label><span>i18n:govoplan-admin.restart_commands.035997f9</span><textarea value={requestOptions.restartCommands} disabled={!canWrite || planBusy} onChange={(event) => setRequestOptions((current) => ({ ...current, restartCommands: event.target.value }))} placeholder="systemctl restart govoplan.service" /></label>
|
||||
<label><span>i18n:govoplan-admin.db_backup_command.5bded3ac</span><textarea value={requestOptions.databaseBackupCommand} disabled={!canWrite || planBusy} onChange={(event) => setRequestOptions((current) => ({ ...current, databaseBackupCommand: event.target.value }))} placeholder={"pg_dump --format=custom \"$GOVOPLAN_DATABASE_URL\" > \"$GOVOPLAN_DATABASE_BACKUP_PATH\""} /></label>
|
||||
@@ -696,8 +711,8 @@ export default function ModuleManagementPanel({ settings, canWrite, canAccessMai
|
||||
{request.error && <p className="module-installer-run-error">{request.error}</p>}
|
||||
</div>
|
||||
<div className="module-installer-run-actions">
|
||||
{request.status === "queued" && <Button onClick={() => void cancelInstallerRequest(request.request_id)} disabled={!canWrite || !canAccessMaintenance || !maintenanceEnabled || planBusy}>i18n:govoplan-admin.cancel.77dfd213</Button>}
|
||||
{(request.status === "failed" || request.status === "cancelled") && <Button onClick={() => void retryInstallerRequest(request.request_id)} disabled={!canWrite || !canAccessMaintenance || !maintenanceEnabled || planBusy}>i18n:govoplan-admin.retry.9f5cd8a2</Button>}
|
||||
{request.status === "queued" && <Button onClick={() => setCancelRequestId(request.request_id)} disabled={!canWrite || !canAccessMaintenance || !maintenanceEnabled || planBusy} disabledReason={planBusy ? ADMIN_INTERFACE_I18N.busy : !canWrite ? ADMIN_INTERFACE_I18N.writeRequired : !canAccessMaintenance ? ADMIN_INTERFACE_I18N.maintenanceAuthorityRequired : !maintenanceEnabled ? ADMIN_INTERFACE_I18N.maintenanceRequired : undefined}>i18n:govoplan-admin.cancel.77dfd213</Button>}
|
||||
{(request.status === "failed" || request.status === "cancelled") && <Button onClick={() => void retryInstallerRequest(request.request_id)} disabled={!canWrite || !canAccessMaintenance || !maintenanceEnabled || planBusy} disabledReason={planBusy ? ADMIN_INTERFACE_I18N.busy : !canWrite ? ADMIN_INTERFACE_I18N.writeRequired : !canAccessMaintenance ? ADMIN_INTERFACE_I18N.maintenanceAuthorityRequired : !maintenanceEnabled ? ADMIN_INTERFACE_I18N.maintenanceRequired : undefined}>i18n:govoplan-admin.retry.9f5cd8a2</Button>}
|
||||
{request.record_path && <code>{request.record_path}</code>}
|
||||
</div>
|
||||
</div>
|
||||
@@ -740,7 +755,47 @@ export default function ModuleManagementPanel({ settings, canWrite, canAccessMai
|
||||
</div>}
|
||||
</div>}
|
||||
</>}
|
||||
</AdminPageLayout>);
|
||||
</AdminPageLayout>
|
||||
<ConfirmDialog
|
||||
open={confirmClearPlan}
|
||||
title={ADMIN_INTERFACE_I18N.clearPlanTitle}
|
||||
message={ADMIN_INTERFACE_I18N.clearPlanMessage}
|
||||
confirmLabel="i18n:govoplan-admin.clear.719ea396"
|
||||
tone="danger"
|
||||
busy={planBusy}
|
||||
onCancel={() => setConfirmClearPlan(false)}
|
||||
onConfirm={() => {
|
||||
setConfirmClearPlan(false);
|
||||
void clearPlan();
|
||||
}}
|
||||
/>
|
||||
<ConfirmDialog
|
||||
open={confirmMaintenance}
|
||||
title={ADMIN_INTERFACE_I18N.enableMaintenanceTitle}
|
||||
message={ADMIN_INTERFACE_I18N.enableMaintenanceMessage}
|
||||
confirmLabel="i18n:govoplan-admin.enable_maintenance_mode.75f98d57"
|
||||
busy={maintenanceBusy}
|
||||
onCancel={() => setConfirmMaintenance(false)}
|
||||
onConfirm={() => {
|
||||
setConfirmMaintenance(false);
|
||||
void enableMaintenanceMode();
|
||||
}}
|
||||
/>
|
||||
<ConfirmDialog
|
||||
open={Boolean(cancelRequestId)}
|
||||
title={ADMIN_INTERFACE_I18N.cancelRequestTitle}
|
||||
message={ADMIN_INTERFACE_I18N.cancelRequestMessage}
|
||||
confirmLabel="i18n:govoplan-admin.cancel.77dfd213"
|
||||
tone="danger"
|
||||
busy={planBusy}
|
||||
onCancel={() => setCancelRequestId("")}
|
||||
onConfirm={() => {
|
||||
const requestId = cancelRequestId;
|
||||
setCancelRequestId("");
|
||||
if (requestId) void cancelInstallerRequest(requestId);
|
||||
}}
|
||||
/>
|
||||
</>);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,8 @@ import { Card } from "@govoplan/core-webui";
|
||||
import { FormField } from "@govoplan/core-webui";
|
||||
import { ToggleSwitch } from "@govoplan/core-webui";
|
||||
import { fetchSystemSettingsDelta, updateSystemSettings, type LanguagePackage, type PrivacyRetentionLimitPermissions, type PrivacyRetentionPolicy, type SystemSettingsDeltaSections, type SystemSettingsItem } from "../../api/admin";
|
||||
import { AdminPageLayout, AdminSelectionList, adminErrorMessage, useDeltaWatermarks, useUnsavedDraftGuard } from "@govoplan/core-webui";
|
||||
import { AdminPageLayout, AdminSelectionList, DocumentationHelpLink, adminErrorMessage, useDeltaWatermarks, useUnsavedDraftGuard } from "@govoplan/core-webui";
|
||||
import { ADMIN_GOVERNANCE_DOCUMENTATION, ADMIN_INTERFACE_I18N, mutationDisabledReason } from "./interfacePatterns";
|
||||
|
||||
const DELTA_KEY = "admin:system-settings";
|
||||
|
||||
@@ -143,11 +144,11 @@ export default function SystemSettingsPanel({ settings, canWrite, canAccessMaint
|
||||
loading={loading}
|
||||
error={error}
|
||||
success={success}
|
||||
actions={<><Button onClick={() => void load()} disabled={loading}>i18n:govoplan-admin.reload.cce71553</Button><Button variant="primary" onClick={() => void save()} disabled={!canWrite || busy}>{busy ? "i18n:govoplan-admin.saving.56a2285c" : "i18n:govoplan-admin.save_settings.913aba9f"}</Button></>}>
|
||||
actions={<><DocumentationHelpLink reference={ADMIN_GOVERNANCE_DOCUMENTATION} /><Button onClick={() => void load()} disabled={loading || busy} disabledReason={loading ? ADMIN_INTERFACE_I18N.loading : busy ? ADMIN_INTERFACE_I18N.busy : undefined}>i18n:govoplan-admin.reload.cce71553</Button><Button variant="primary" onClick={() => void save()} disabledReason={mutationDisabledReason({ busy, permitted: canWrite, changed: dirty })}>{busy ? "i18n:govoplan-admin.saving.56a2285c" : "i18n:govoplan-admin.save_settings.913aba9f"}</Button></>}>
|
||||
|
||||
<div className="admin-settings-form">
|
||||
<Card title="i18n:govoplan-admin.defaults_for_newly_created_tenants.9d0f4ccd">
|
||||
<FormField label="i18n:govoplan-admin.default_locale.b99d021f">
|
||||
<FormField label="i18n:govoplan-admin.default_locale.b99d021f" documentation={ADMIN_GOVERNANCE_DOCUMENTATION}>
|
||||
<select value={draft.default_locale} onChange={(event) => setDraft({ ...draft, default_locale: event.target.value })} disabled={!canWrite || busy || defaultLocaleOptions.length === 0}>
|
||||
{defaultLocaleOptions.map((code) => {
|
||||
const language = draft.available_languages.find((item) => item.code === code);
|
||||
@@ -173,16 +174,16 @@ export default function SystemSettingsPanel({ settings, canWrite, canAccessMaint
|
||||
<input value={packageDraft.nativeLabel} disabled={!canWrite || busy} placeholder="i18n:govoplan-admin.native_language_name_placeholder" onChange={(event) => setPackageDraft({ ...packageDraft, nativeLabel: event.target.value })} />
|
||||
</FormField>
|
||||
<div className="button-row compact-actions">
|
||||
<Button onClick={installLanguagePackage} disabled={!canWrite || busy || !normalizeLanguageCode(packageDraft.code) || !packageDraft.label.trim()}>i18n:govoplan-admin.install_language_package</Button>
|
||||
<Button onClick={installLanguagePackage} disabledReason={mutationDisabledReason({ busy, permitted: canWrite, complete: Boolean(normalizeLanguageCode(packageDraft.code) && packageDraft.label.trim()) })}>i18n:govoplan-admin.install_language_package</Button>
|
||||
</div>
|
||||
</div>
|
||||
<p className="muted small-note">i18n:govoplan-admin.language_packages_help</p>
|
||||
</Card>
|
||||
<Card title="i18n:govoplan-admin.tenant_administration_capabilities.5d265972">
|
||||
<div className="settings-list">
|
||||
<ToggleSwitch checked={draft.allow_tenant_custom_groups} onChange={(checked) => setDraft({ ...draft, allow_tenant_custom_groups: checked })} label="i18n:govoplan-admin.allow_tenant_defined_groups_by_default.32099d5a" />
|
||||
<ToggleSwitch checked={draft.allow_tenant_custom_roles} onChange={(checked) => setDraft({ ...draft, allow_tenant_custom_roles: checked })} label="i18n:govoplan-admin.allow_tenant_defined_roles_by_default.b1f79ee9" />
|
||||
<ToggleSwitch checked={draft.allow_tenant_api_keys} onChange={(checked) => setDraft({ ...draft, allow_tenant_api_keys: checked })} label="i18n:govoplan-admin.allow_tenant_api_keys_by_default.58a6cf17" />
|
||||
<ToggleSwitch checked={draft.allow_tenant_custom_groups} onChange={(checked) => setDraft({ ...draft, allow_tenant_custom_groups: checked })} disabled={!canWrite || busy} help={!canWrite ? ADMIN_INTERFACE_I18N.writeRequired : busy ? ADMIN_INTERFACE_I18N.busy : undefined} label="i18n:govoplan-admin.allow_tenant_defined_groups_by_default.32099d5a" />
|
||||
<ToggleSwitch checked={draft.allow_tenant_custom_roles} onChange={(checked) => setDraft({ ...draft, allow_tenant_custom_roles: checked })} disabled={!canWrite || busy} help={!canWrite ? ADMIN_INTERFACE_I18N.writeRequired : busy ? ADMIN_INTERFACE_I18N.busy : undefined} label="i18n:govoplan-admin.allow_tenant_defined_roles_by_default.b1f79ee9" />
|
||||
<ToggleSwitch checked={draft.allow_tenant_api_keys} onChange={(checked) => setDraft({ ...draft, allow_tenant_api_keys: checked })} disabled={!canWrite || busy} help={!canWrite ? ADMIN_INTERFACE_I18N.writeRequired : busy ? ADMIN_INTERFACE_I18N.busy : undefined} label="i18n:govoplan-admin.allow_tenant_api_keys_by_default.58a6cf17" />
|
||||
</div>
|
||||
<p className="muted small-note">i18n:govoplan-admin.these_settings_are_enforced_by_the_backend_centr.8112ed6f</p>
|
||||
</Card>
|
||||
@@ -190,16 +191,17 @@ export default function SystemSettingsPanel({ settings, canWrite, canAccessMaint
|
||||
<div className="settings-list">
|
||||
<ToggleSwitch
|
||||
checked={draft.maintenance_mode.enabled}
|
||||
disabled={!canWrite || !canAccessMaintenance}
|
||||
disabled={!canWrite || !canAccessMaintenance || busy}
|
||||
help={!canWrite ? ADMIN_INTERFACE_I18N.writeRequired : !canAccessMaintenance ? ADMIN_INTERFACE_I18N.maintenanceAuthorityRequired : busy ? ADMIN_INTERFACE_I18N.busy : undefined}
|
||||
onChange={(checked) => setDraft({ ...draft, maintenance_mode: { ...draft.maintenance_mode, enabled: checked } })}
|
||||
label="i18n:govoplan-admin.restrict_authenticated_api_access_to_maintenance.2bc47195" />
|
||||
|
||||
</div>
|
||||
<FormField label="i18n:govoplan-admin.maintenance_message.ca62571f">
|
||||
<FormField label="i18n:govoplan-admin.maintenance_message.ca62571f" help={!canWrite ? ADMIN_INTERFACE_I18N.writeRequired : undefined} documentation={ADMIN_GOVERNANCE_DOCUMENTATION}>
|
||||
<textarea
|
||||
rows={3}
|
||||
value={draft.maintenance_mode.message ?? ""}
|
||||
disabled={!canWrite}
|
||||
disabled={!canWrite || busy}
|
||||
onChange={(event) => setDraft({ ...draft, maintenance_mode: { ...draft.maintenance_mode, message: event.target.value } })} />
|
||||
|
||||
</FormField>
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
import type { DocumentationHelpReference } from "@govoplan/core-webui";
|
||||
|
||||
export const ADMIN_WORKSPACE_DOCUMENTATION = {
|
||||
topicId: "admin.workspace",
|
||||
documentationType: "admin"
|
||||
} satisfies DocumentationHelpReference;
|
||||
|
||||
export const ADMIN_GOVERNANCE_DOCUMENTATION = {
|
||||
topicId: "admin.governance-and-module-lifecycle",
|
||||
documentationType: "admin"
|
||||
} satisfies DocumentationHelpReference;
|
||||
|
||||
export const MODULE_LIFECYCLE_DOCUMENTATION = {
|
||||
topicId: "admin.module-lifecycle-workflow",
|
||||
documentationType: "admin"
|
||||
} satisfies DocumentationHelpReference;
|
||||
|
||||
export const ADMIN_INTERFACE_I18N = {
|
||||
loading: "i18n:govoplan-admin.administration_data_is_loading.6bf3c001",
|
||||
busy: "i18n:govoplan-admin.an_administration_operation_is_in_progress.6bf3c002",
|
||||
writeRequired: "i18n:govoplan-admin.system_administration_write_permission_is_required.6bf3c003",
|
||||
governanceWriteRequired: "i18n:govoplan-admin.system_governance_write_permission_is_required.6bf3c004",
|
||||
completeRequiredFields: "i18n:govoplan-admin.complete_the_required_fields_before_saving.6bf3c005",
|
||||
noPendingChanges: "i18n:govoplan-admin.make_a_change_before_saving.6bf3c006",
|
||||
validJsonRequired: "i18n:govoplan-admin.provide_valid_package_and_supplied_data_json.6bf3c007",
|
||||
packageRequired: "i18n:govoplan-admin.provide_valid_package_json_before_requesting_approval.6bf3c008",
|
||||
planRequired: "i18n:govoplan-admin.add_at_least_one_valid_plan_item.6bf3c009",
|
||||
savePlanFirst: "i18n:govoplan-admin.save_the_changed_plan_before_queueing_it.6bf3c010",
|
||||
maintenanceAuthorityRequired: "i18n:govoplan-admin.system_maintenance_authority_is_required.6bf3c011",
|
||||
maintenanceRequired: "i18n:govoplan-admin.enable_maintenance_mode_before_this_action.6bf3c012",
|
||||
protectedDefinition: "i18n:govoplan-admin.this_protected_module_cannot_be_changed.6bf3c013",
|
||||
deactivateBeforeUninstall: "i18n:govoplan-admin.deactivate_the_module_before_planning_uninstall.6bf3c014",
|
||||
catalogBlocked: "i18n:govoplan-admin.resolve_the_catalog_license_or_action_blocker_first.6bf3c015",
|
||||
approveTitle: "i18n:govoplan-admin.approve_configuration_change.6bf3c016",
|
||||
approveMessage: "i18n:govoplan-admin.approving_records_your_authority_and_may_unlock_application.6bf3c017",
|
||||
applyTitle: "i18n:govoplan-admin.apply_configuration_package.6bf3c018",
|
||||
applyMessage: "i18n:govoplan-admin.apply_the_current_package_to_the_selected_scope.6bf3c019",
|
||||
applyConfirm: "i18n:govoplan-admin.apply_package.6bf3c020",
|
||||
enterReferencesManually: "i18n:govoplan-admin.enter_reference_ids_manually.6bf3c021",
|
||||
manualReferenceHelp: "i18n:govoplan-admin.use_manual_ids_only_for_intentional_historical_references.6bf3c022",
|
||||
tenantPickerLabel: "i18n:govoplan-admin.configuration_package_tenant.6bf3c023",
|
||||
selectTenant: "i18n:govoplan-admin.select_a_tenant.6bf3c024",
|
||||
requestPickerLabel: "i18n:govoplan-admin.configuration_package_change_request.6bf3c025",
|
||||
selectEligibleRequest: "i18n:govoplan-admin.select_an_eligible_request_optional.6bf3c026",
|
||||
noEligibleRequests: "i18n:govoplan-admin.no_eligible_configuration_requests.6bf3c027",
|
||||
administrationHeading: "i18n:govoplan-admin.administration.b8be3d12",
|
||||
globalHeading: "i18n:govoplan-admin.global.6bf3c028",
|
||||
tenantHeading: "i18n:govoplan-admin.tenant.6bf3c029",
|
||||
groupHeading: "i18n:govoplan-admin.group.171a0606",
|
||||
userHeading: "i18n:govoplan-admin.user.6bf3c030",
|
||||
clearPlanTitle: "i18n:govoplan-admin.clear_saved_module_plan.6bf3c032",
|
||||
clearPlanMessage: "i18n:govoplan-admin.clear_all_saved_module_install_update_and_uninstall_items.6bf3c033",
|
||||
enableMaintenanceTitle: "i18n:govoplan-admin.enable_maintenance_mode.6bf3c034",
|
||||
enableMaintenanceMessage: "i18n:govoplan-admin.restrict_normal_authenticated_access_while_module_work_runs.6bf3c035",
|
||||
cancelRequestTitle: "i18n:govoplan-admin.cancel_installer_request.6bf3c036",
|
||||
cancelRequestMessage: "i18n:govoplan-admin.cancel_the_queued_request_before_the_daemon_accepts_it.6bf3c037",
|
||||
uninstallOnly: "i18n:govoplan-admin.this_option_only_applies_to_uninstall_plans.6bf3c038",
|
||||
installUpdateOnly: "i18n:govoplan-admin.this_option_only_applies_to_install_and_update_plans.6bf3c039"
|
||||
} as const;
|
||||
|
||||
export function mutationDisabledReason({
|
||||
busy,
|
||||
permitted,
|
||||
complete = true,
|
||||
changed = true
|
||||
}: {
|
||||
busy: boolean;
|
||||
permitted: boolean;
|
||||
complete?: boolean;
|
||||
changed?: boolean;
|
||||
}): string | undefined {
|
||||
if (busy) return ADMIN_INTERFACE_I18N.busy;
|
||||
if (!permitted) return ADMIN_INTERFACE_I18N.writeRequired;
|
||||
if (!complete) return ADMIN_INTERFACE_I18N.completeRequiredFields;
|
||||
if (!changed) return ADMIN_INTERFACE_I18N.noPendingChanges;
|
||||
return undefined;
|
||||
}
|
||||
@@ -2,6 +2,45 @@ import type { PlatformTranslations } from "@govoplan/core-webui";
|
||||
|
||||
export const generatedTranslations: PlatformTranslations = {
|
||||
"en": {
|
||||
"i18n:govoplan-admin.administration_data_is_loading.6bf3c001": "Administration data is loading.",
|
||||
"i18n:govoplan-admin.an_administration_operation_is_in_progress.6bf3c002": "An administration operation is in progress.",
|
||||
"i18n:govoplan-admin.system_administration_write_permission_is_required.6bf3c003": "System administration write permission is required.",
|
||||
"i18n:govoplan-admin.system_governance_write_permission_is_required.6bf3c004": "System governance write permission is required.",
|
||||
"i18n:govoplan-admin.complete_the_required_fields_before_saving.6bf3c005": "Complete the required fields before saving.",
|
||||
"i18n:govoplan-admin.make_a_change_before_saving.6bf3c006": "Make a change before saving.",
|
||||
"i18n:govoplan-admin.provide_valid_package_and_supplied_data_json.6bf3c007": "Provide valid package and supplied-data JSON.",
|
||||
"i18n:govoplan-admin.provide_valid_package_json_before_requesting_approval.6bf3c008": "Provide valid package JSON before requesting approval.",
|
||||
"i18n:govoplan-admin.add_at_least_one_valid_plan_item.6bf3c009": "Add at least one valid plan item.",
|
||||
"i18n:govoplan-admin.save_the_changed_plan_before_queueing_it.6bf3c010": "Save the changed plan before queueing it.",
|
||||
"i18n:govoplan-admin.system_maintenance_authority_is_required.6bf3c011": "System maintenance authority is required.",
|
||||
"i18n:govoplan-admin.enable_maintenance_mode_before_this_action.6bf3c012": "Enable maintenance mode before this action.",
|
||||
"i18n:govoplan-admin.this_protected_module_cannot_be_changed.6bf3c013": "This protected module cannot be changed.",
|
||||
"i18n:govoplan-admin.deactivate_the_module_before_planning_uninstall.6bf3c014": "Deactivate the module before planning its uninstall.",
|
||||
"i18n:govoplan-admin.resolve_the_catalog_license_or_action_blocker_first.6bf3c015": "Resolve the catalog, license, or package-action blocker first.",
|
||||
"i18n:govoplan-admin.approve_configuration_change.6bf3c016": "Approve configuration change",
|
||||
"i18n:govoplan-admin.approving_records_your_authority_and_may_unlock_application.6bf3c017": "Approving records your authority and may unlock application of this configuration change.",
|
||||
"i18n:govoplan-admin.apply_configuration_package.6bf3c018": "Apply configuration package",
|
||||
"i18n:govoplan-admin.apply_the_current_package_to_the_selected_scope.6bf3c019": "Apply the current package to the selected scope? This may create or update module-owned configuration.",
|
||||
"i18n:govoplan-admin.apply_package.6bf3c020": "Apply package",
|
||||
"i18n:govoplan-admin.enter_reference_ids_manually.6bf3c021": "Enter reference IDs manually",
|
||||
"i18n:govoplan-admin.use_manual_ids_only_for_intentional_historical_references.6bf3c022": "Use manual IDs only when a package intentionally references a target no longer returned by the live catalog.",
|
||||
"i18n:govoplan-admin.configuration_package_tenant.6bf3c023": "Configuration package tenant",
|
||||
"i18n:govoplan-admin.select_a_tenant.6bf3c024": "Select a tenant",
|
||||
"i18n:govoplan-admin.configuration_package_change_request.6bf3c025": "Configuration package change request",
|
||||
"i18n:govoplan-admin.select_an_eligible_request_optional.6bf3c026": "Select an eligible request (optional)",
|
||||
"i18n:govoplan-admin.no_eligible_configuration_requests.6bf3c027": "No eligible configuration requests.",
|
||||
"i18n:govoplan-admin.global.6bf3c028": "Global",
|
||||
"i18n:govoplan-admin.tenant.6bf3c029": "Tenant",
|
||||
"i18n:govoplan-admin.user.6bf3c030": "User",
|
||||
"i18n:govoplan-admin.request_is_not_pending_approval.6bf3c031": "This request is not pending approval.",
|
||||
"i18n:govoplan-admin.clear_saved_module_plan.6bf3c032": "Clear saved module plan",
|
||||
"i18n:govoplan-admin.clear_all_saved_module_install_update_and_uninstall_items.6bf3c033": "Clear all saved module install, update, and uninstall items?",
|
||||
"i18n:govoplan-admin.enable_maintenance_mode.6bf3c034": "Enable maintenance mode",
|
||||
"i18n:govoplan-admin.restrict_normal_authenticated_access_while_module_work_runs.6bf3c035": "Restrict normal authenticated access while supervised module work runs?",
|
||||
"i18n:govoplan-admin.cancel_installer_request.6bf3c036": "Cancel installer request",
|
||||
"i18n:govoplan-admin.cancel_the_queued_request_before_the_daemon_accepts_it.6bf3c037": "Cancel this queued request before the installer daemon accepts it?",
|
||||
"i18n:govoplan-admin.this_option_only_applies_to_uninstall_plans.6bf3c038": "This option only applies to uninstall plans.",
|
||||
"i18n:govoplan-admin.this_option_only_applies_to_install_and_update_plans.6bf3c039": "This option only applies to install and update plans.",
|
||||
"i18n:govoplan-admin.module_lifecycle_progress.f1a20201": "Module lifecycle progress",
|
||||
"i18n:govoplan-admin.queue_supervised_run_unavailable.f1a20202": "Queueing a supervised run is unavailable",
|
||||
"i18n:govoplan-admin.required_action.f1a20203": "Required action",
|
||||
@@ -399,6 +438,45 @@ export const generatedTranslations: PlatformTranslations = {
|
||||
"i18n:govoplan-admin.working.049ac820": "Working..."
|
||||
},
|
||||
"de": {
|
||||
"i18n:govoplan-admin.administration_data_is_loading.6bf3c001": "Administrationsdaten werden geladen.",
|
||||
"i18n:govoplan-admin.an_administration_operation_is_in_progress.6bf3c002": "Eine Administrationsaktion wird gerade ausgeführt.",
|
||||
"i18n:govoplan-admin.system_administration_write_permission_is_required.6bf3c003": "Eine Schreibberechtigung für die Systemadministration ist erforderlich.",
|
||||
"i18n:govoplan-admin.system_governance_write_permission_is_required.6bf3c004": "Eine Schreibberechtigung für die System-Governance ist erforderlich.",
|
||||
"i18n:govoplan-admin.complete_the_required_fields_before_saving.6bf3c005": "Füllen Sie vor dem Speichern die Pflichtfelder aus.",
|
||||
"i18n:govoplan-admin.make_a_change_before_saving.6bf3c006": "Nehmen Sie vor dem Speichern eine Änderung vor.",
|
||||
"i18n:govoplan-admin.provide_valid_package_and_supplied_data_json.6bf3c007": "Geben Sie gültiges Paket- und Zusatzdaten-JSON ein.",
|
||||
"i18n:govoplan-admin.provide_valid_package_json_before_requesting_approval.6bf3c008": "Geben Sie vor der Genehmigungsanfrage gültiges Paket-JSON ein.",
|
||||
"i18n:govoplan-admin.add_at_least_one_valid_plan_item.6bf3c009": "Fügen Sie mindestens einen gültigen Planeintrag hinzu.",
|
||||
"i18n:govoplan-admin.save_the_changed_plan_before_queueing_it.6bf3c010": "Speichern Sie den geänderten Plan vor dem Einreihen.",
|
||||
"i18n:govoplan-admin.system_maintenance_authority_is_required.6bf3c011": "Die Berechtigung für die Systemwartung ist erforderlich.",
|
||||
"i18n:govoplan-admin.enable_maintenance_mode_before_this_action.6bf3c012": "Aktivieren Sie vor dieser Aktion den Wartungsmodus.",
|
||||
"i18n:govoplan-admin.this_protected_module_cannot_be_changed.6bf3c013": "Dieses geschützte Modul kann nicht geändert werden.",
|
||||
"i18n:govoplan-admin.deactivate_the_module_before_planning_uninstall.6bf3c014": "Deaktivieren Sie das Modul, bevor Sie die Deinstallation planen.",
|
||||
"i18n:govoplan-admin.resolve_the_catalog_license_or_action_blocker_first.6bf3c015": "Beheben Sie zuerst die Katalog-, Lizenz- oder Paketaktionssperre.",
|
||||
"i18n:govoplan-admin.approve_configuration_change.6bf3c016": "Konfigurationsänderung genehmigen",
|
||||
"i18n:govoplan-admin.approving_records_your_authority_and_may_unlock_application.6bf3c017": "Die Genehmigung dokumentiert Ihre Autorität und kann die Anwendung dieser Konfigurationsänderung freigeben.",
|
||||
"i18n:govoplan-admin.apply_configuration_package.6bf3c018": "Konfigurationspaket anwenden",
|
||||
"i18n:govoplan-admin.apply_the_current_package_to_the_selected_scope.6bf3c019": "Das aktuelle Paket auf den ausgewählten Bereich anwenden? Dadurch kann modulbezogene Konfiguration erstellt oder geändert werden.",
|
||||
"i18n:govoplan-admin.apply_package.6bf3c020": "Paket anwenden",
|
||||
"i18n:govoplan-admin.enter_reference_ids_manually.6bf3c021": "Referenz-IDs manuell eingeben",
|
||||
"i18n:govoplan-admin.use_manual_ids_only_for_intentional_historical_references.6bf3c022": "Verwenden Sie manuelle IDs nur, wenn ein Paket absichtlich auf ein nicht mehr im Live-Katalog enthaltenes Ziel verweist.",
|
||||
"i18n:govoplan-admin.configuration_package_tenant.6bf3c023": "Mandant des Konfigurationspakets",
|
||||
"i18n:govoplan-admin.select_a_tenant.6bf3c024": "Mandanten auswählen",
|
||||
"i18n:govoplan-admin.configuration_package_change_request.6bf3c025": "Änderungsantrag für das Konfigurationspaket",
|
||||
"i18n:govoplan-admin.select_an_eligible_request_optional.6bf3c026": "Geeigneten Antrag auswählen (optional)",
|
||||
"i18n:govoplan-admin.no_eligible_configuration_requests.6bf3c027": "Keine geeigneten Konfigurationsanträge vorhanden.",
|
||||
"i18n:govoplan-admin.global.6bf3c028": "Global",
|
||||
"i18n:govoplan-admin.tenant.6bf3c029": "Mandant",
|
||||
"i18n:govoplan-admin.user.6bf3c030": "Benutzer",
|
||||
"i18n:govoplan-admin.request_is_not_pending_approval.6bf3c031": "Dieser Antrag wartet nicht auf eine Genehmigung.",
|
||||
"i18n:govoplan-admin.clear_saved_module_plan.6bf3c032": "Gespeicherten Modulplan leeren",
|
||||
"i18n:govoplan-admin.clear_all_saved_module_install_update_and_uninstall_items.6bf3c033": "Alle gespeicherten Installations-, Aktualisierungs- und Deinstallationseinträge löschen?",
|
||||
"i18n:govoplan-admin.enable_maintenance_mode.6bf3c034": "Wartungsmodus aktivieren",
|
||||
"i18n:govoplan-admin.restrict_normal_authenticated_access_while_module_work_runs.6bf3c035": "Den normalen authentifizierten Zugriff während der beaufsichtigten Modularbeiten einschränken?",
|
||||
"i18n:govoplan-admin.cancel_installer_request.6bf3c036": "Installationsantrag abbrechen",
|
||||
"i18n:govoplan-admin.cancel_the_queued_request_before_the_daemon_accepts_it.6bf3c037": "Diesen eingereihten Antrag abbrechen, bevor der Installationsdienst ihn annimmt?",
|
||||
"i18n:govoplan-admin.this_option_only_applies_to_uninstall_plans.6bf3c038": "Diese Option gilt nur für Deinstallationspläne.",
|
||||
"i18n:govoplan-admin.this_option_only_applies_to_install_and_update_plans.6bf3c039": "Diese Option gilt nur für Installations- und Aktualisierungspläne.",
|
||||
"i18n:govoplan-admin.module_lifecycle_progress.f1a20201": "Fortschritt des Modullebenszyklus",
|
||||
"i18n:govoplan-admin.queue_supervised_run_unavailable.f1a20202": "Ein überwachter Lauf kann nicht eingereiht werden",
|
||||
"i18n:govoplan-admin.required_action.f1a20203": "Erforderliche Maßnahme",
|
||||
|
||||
+8
-8
@@ -130,17 +130,17 @@ const adminSections: AdminSectionsUiCapability = {
|
||||
export const adminModule: PlatformWebModule = {
|
||||
id: "admin",
|
||||
label: "i18n:govoplan-admin.admin.4e7afebc",
|
||||
version: "1.0.0",
|
||||
version: "0.1.8",
|
||||
dependencies: ["access"],
|
||||
translations,
|
||||
viewSurfaces: [
|
||||
{ id: "admin.section.overview", moduleId: "admin", kind: "section", label: "Administration overview", order: 0 },
|
||||
{ id: "admin.section.system-settings", moduleId: "admin", kind: "section", label: "System settings", order: 10 },
|
||||
{ id: "admin.section.system-configuration-changes", moduleId: "admin", kind: "section", label: "Configuration changes", order: 20 },
|
||||
{ id: "admin.section.system-configuration-packages", moduleId: "admin", kind: "section", label: "Configuration packages", order: 30 },
|
||||
{ id: "admin.section.system-role-templates", moduleId: "admin", kind: "section", label: "Role templates", order: 40 },
|
||||
{ id: "admin.section.system-groups", moduleId: "admin", kind: "section", label: "Group templates", order: 50 },
|
||||
{ id: "admin.section.system-modules", moduleId: "admin", kind: "section", label: "Modules", order: 85 }
|
||||
{ id: "admin.section.overview", moduleId: "admin", kind: "section", label: "i18n:govoplan-admin.overview.0efc2e6b", order: 0 },
|
||||
{ id: "admin.section.system-settings", moduleId: "admin", kind: "section", label: "i18n:govoplan-admin.system_general_settings.7cd662ed", order: 10 },
|
||||
{ id: "admin.section.system-configuration-changes", moduleId: "admin", kind: "section", label: "i18n:govoplan-admin.configuration_changes.82933bbb", order: 20 },
|
||||
{ id: "admin.section.system-configuration-packages", moduleId: "admin", kind: "section", label: "i18n:govoplan-admin.configuration_packages.eb2f05f1", order: 30 },
|
||||
{ id: "admin.section.system-role-templates", moduleId: "admin", kind: "section", label: "i18n:govoplan-admin.tenant_roles.51aca82d", order: 40 },
|
||||
{ id: "admin.section.system-groups", moduleId: "admin", kind: "section", label: "i18n:govoplan-admin.central_groups.5c9b5b66", order: 50 },
|
||||
{ id: "admin.section.system-modules", moduleId: "admin", kind: "section", label: "i18n:govoplan-admin.modules.04e9462c", order: 85 }
|
||||
],
|
||||
uiCapabilities: {
|
||||
"admin.sections": adminSections,
|
||||
|
||||
Reference in New Issue
Block a user