Complete Admin interface patterns
This commit is contained in:
@@ -1,5 +1,9 @@
|
|||||||
# GovOPlaN Admin
|
# 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 -->
|
<!-- govoplan-repository-type:start -->
|
||||||
**Repository type:** module (platform).
|
**Repository type:** module (platform).
|
||||||
<!-- govoplan-repository-type:end -->
|
<!-- 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",
|
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.",
|
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.",
|
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"),
|
audience=("tenant_admin", "system_admin", "operator"),
|
||||||
metadata={"kind": "reference"},
|
metadata={
|
||||||
|
"kind": "reference",
|
||||||
|
"help_contexts": [
|
||||||
|
"admin.workspace",
|
||||||
|
"admin.overview",
|
||||||
|
"admin.section-navigation",
|
||||||
|
],
|
||||||
|
},
|
||||||
),
|
),
|
||||||
DocumentationTopic(
|
DocumentationTopic(
|
||||||
id="admin.governance-and-module-lifecycle",
|
id="admin.governance-and-module-lifecycle",
|
||||||
@@ -40,7 +47,15 @@ manifest = ModuleManifest(
|
|||||||
documentation_types=("admin",),
|
documentation_types=("admin",),
|
||||||
audience=("system_admin", "operator", "module_admin"),
|
audience=("system_admin", "operator", "module_admin"),
|
||||||
related_modules=("access", "audit", "ops"),
|
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(
|
DocumentationTopic(
|
||||||
id="admin.module-lifecycle-workflow",
|
id="admin.module-lifecycle-workflow",
|
||||||
@@ -61,6 +76,12 @@ manifest = ModuleManifest(
|
|||||||
"admin.module-lifecycle",
|
"admin.module-lifecycle",
|
||||||
"admin.module-lifecycle.queue-blocker",
|
"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": {
|
"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": {
|
"peerDependencies": {
|
||||||
"@govoplan/core-webui": "^0.1.9",
|
"@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 { fetchAdminOverview, type AdminOverview } from "../../api/admin";
|
||||||
import { Card, MetricCard } from "@govoplan/core-webui";
|
import { Card, MetricCard } from "@govoplan/core-webui";
|
||||||
import { Button } 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>;}) {
|
export default function AdminOverviewPanel({ settings, onSelect, availableSections }: {settings: ApiSettings;onSelect: (section: string) => void;availableSections: ReadonlySet<string>;}) {
|
||||||
const [overview, setOverview] = useState<AdminOverview | null>(null);
|
const [overview, setOverview] = useState<AdminOverview | null>(null);
|
||||||
@@ -28,7 +29,7 @@ export default function AdminOverviewPanel({ settings, onSelect, availableSectio
|
|||||||
some((value) => value !== null && value !== undefined));
|
some((value) => value !== null && value !== undefined));
|
||||||
|
|
||||||
return (
|
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 && <>
|
{overview && <>
|
||||||
{hasSystemMetrics && <>
|
{hasSystemMetrics && <>
|
||||||
<div className="admin-overview-section-label">i18n:govoplan-admin.system.bc0792d8</div>
|
<div className="admin-overview-section-label">i18n:govoplan-admin.system.bc0792d8</div>
|
||||||
@@ -40,7 +41,7 @@ export default function AdminOverviewPanel({ settings, onSelect, availableSectio
|
|||||||
</div>
|
</div>
|
||||||
</>}
|
</>}
|
||||||
|
|
||||||
{hasAnySection(availableSections, platformSectionIds) && <Card title="ADMINISTRATION">
|
{hasAnySection(availableSections, platformSectionIds) && <Card title={ADMIN_INTERFACE_I18N.administrationHeading}>
|
||||||
<div className="admin-overview-grid">
|
<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-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")} />}
|
{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>
|
</div>
|
||||||
</Card>}
|
</Card>}
|
||||||
|
|
||||||
{hasAnySection(availableSections, globalSectionIds) && <Card title="GLOBAL">
|
{hasAnySection(availableSections, globalSectionIds) && <Card title={ADMIN_INTERFACE_I18N.globalHeading}>
|
||||||
<div className="admin-overview-grid">
|
<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-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")} />}
|
{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" />
|
<Metric title="i18n:govoplan-admin.api_keys.94fcf3c2" value={overview.active_api_key_count} text="i18n:govoplan-admin.active_tenant_automation_credentials.240c659e" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{hasAnySection(availableSections, tenantSectionIds) && <Card title="TENANT">
|
{hasAnySection(availableSections, tenantSectionIds) && <Card title={ADMIN_INTERFACE_I18N.tenantHeading}>
|
||||||
<div className="admin-overview-grid">
|
<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-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")} />}
|
{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>
|
</div>
|
||||||
</Card>}
|
</Card>}
|
||||||
|
|
||||||
{hasAnySection(availableSections, groupSectionIds) && <Card title="GROUP">
|
{hasAnySection(availableSections, groupSectionIds) && <Card title={ADMIN_INTERFACE_I18N.groupHeading}>
|
||||||
<div className="admin-overview-grid">
|
<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-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")} />}
|
{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>
|
</div>
|
||||||
</Card>}
|
</Card>}
|
||||||
|
|
||||||
{hasAnySection(availableSections, userSectionIds) && <Card title="USER">
|
{hasAnySection(availableSections, userSectionIds) && <Card title={ADMIN_INTERFACE_I18N.userHeading}>
|
||||||
<div className="admin-overview-grid">
|
<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-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")} />}
|
{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 { useEffect, useMemo, useRef, useState } from "react";
|
||||||
import { Check, RefreshCw } from "lucide-react";
|
import { Check, RefreshCw } from "lucide-react";
|
||||||
import type { ApiSettings } from "@govoplan/core-webui";
|
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 {
|
import {
|
||||||
approveConfigurationChangeRequest,
|
approveConfigurationChangeRequest,
|
||||||
fetchConfigurationChangesDelta,
|
fetchConfigurationChangesDelta,
|
||||||
type ConfigurationChangeRecord,
|
type ConfigurationChangeRecord,
|
||||||
type ConfigurationChangeRequest } from
|
type ConfigurationChangeRequest } from
|
||||||
"../../api/admin";
|
"../../api/admin";
|
||||||
|
import { ADMIN_GOVERNANCE_DOCUMENTATION, ADMIN_INTERFACE_I18N } from "./interfacePatterns";
|
||||||
|
|
||||||
const DELTA_KEY = "admin:configuration-changes";
|
const DELTA_KEY = "admin:configuration-changes";
|
||||||
|
|
||||||
@@ -19,6 +20,7 @@ export default function ConfigurationChangesPanel({ settings, canApprove }: {set
|
|||||||
const { getDeltaWatermark, setDeltaWatermark, resetDeltaWatermark } = useDeltaWatermarks();
|
const { getDeltaWatermark, setDeltaWatermark, resetDeltaWatermark } = useDeltaWatermarks();
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [busyId, setBusyId] = useState("");
|
const [busyId, setBusyId] = useState("");
|
||||||
|
const [approving, setApproving] = useState<ConfigurationChangeRequest | null>(null);
|
||||||
const [error, setError] = useState("");
|
const [error, setError] = useState("");
|
||||||
const [message, setMessage] = useState("");
|
const [message, setMessage] = useState("");
|
||||||
|
|
||||||
@@ -68,6 +70,7 @@ export default function ConfigurationChangesPanel({ settings, canApprove }: {set
|
|||||||
setError(adminErrorMessage(err));
|
setError(adminErrorMessage(err));
|
||||||
} finally {
|
} finally {
|
||||||
setBusyId("");
|
setBusyId("");
|
||||||
|
setApproving(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,9 +101,16 @@ export default function ConfigurationChangesPanel({ settings, canApprove }: {set
|
|||||||
id: "approve",
|
id: "approve",
|
||||||
label: "i18n:govoplan-admin.approve.7b2c7f14",
|
label: "i18n:govoplan-admin.approve.7b2c7f14",
|
||||||
icon: <Check size={16} aria-hidden="true" />,
|
icon: <Check size={16} aria-hidden="true" />,
|
||||||
applicable: canApprove && request.status === "pending_approval",
|
applicable: request.status === "pending_approval",
|
||||||
disabled: busyId === request.id,
|
disabled: !canApprove || Boolean(busyId),
|
||||||
onClick: () => void approve(request)
|
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 (
|
return (
|
||||||
|
<>
|
||||||
<AdminPageLayout
|
<AdminPageLayout
|
||||||
title="i18n:govoplan-admin.configuration_changes.82933bbb"
|
title="i18n:govoplan-admin.configuration_changes.82933bbb"
|
||||||
description="i18n:govoplan-admin.safety_controlled_configuration_requests_approva.e8259509"
|
description="i18n:govoplan-admin.safety_controlled_configuration_requests_approva.e8259509"
|
||||||
loading={loading}
|
loading={loading}
|
||||||
error={error}
|
error={error}
|
||||||
success={message}
|
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">
|
<Card title="i18n:govoplan-admin.requests.f7194e6a">
|
||||||
<DataGrid
|
<DataGrid
|
||||||
@@ -142,7 +153,17 @@ export default function ConfigurationChangesPanel({ settings, canApprove }: {set
|
|||||||
emptyText="i18n:govoplan-admin.no_applied_configuration_changes.6a3ae4a7"
|
emptyText="i18n:govoplan-admin.no_applied_configuration_changes.6a3ae4a7"
|
||||||
/>
|
/>
|
||||||
</Card>
|
</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 { useEffect, useMemo, useState } from "react";
|
||||||
import { Check, Download, Play, RefreshCw } from "lucide-react";
|
import { Check, Download, Play, RefreshCw } from "lucide-react";
|
||||||
import type { ApiSettings, AuthInfo } from "@govoplan/core-webui";
|
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 {
|
import {
|
||||||
applyConfigurationPackage,
|
applyConfigurationPackage,
|
||||||
createConfigurationChangeRequest,
|
createConfigurationChangeRequest,
|
||||||
@@ -17,6 +17,7 @@ import {
|
|||||||
createChangeRequestReferenceProvider,
|
createChangeRequestReferenceProvider,
|
||||||
createTenantReferenceProvider
|
createTenantReferenceProvider
|
||||||
} from "./configurationReferenceProviders";
|
} from "./configurationReferenceProviders";
|
||||||
|
import { ADMIN_GOVERNANCE_DOCUMENTATION, ADMIN_INTERFACE_I18N } from "./interfacePatterns";
|
||||||
|
|
||||||
const SAMPLE_ACCESS_PACKAGE = {
|
const SAMPLE_ACCESS_PACKAGE = {
|
||||||
package_id: "govoplan.access.minimal-office",
|
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 [lastRequest, setLastRequest] = useState<ConfigurationChangeRequest | null>(null);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [busy, setBusy] = useState("");
|
const [busy, setBusy] = useState("");
|
||||||
|
const [confirmApply, setConfirmApply] = useState(false);
|
||||||
const [error, setError] = useState("");
|
const [error, setError] = useState("");
|
||||||
const [message, setMessage] = useState("");
|
const [message, setMessage] = useState("");
|
||||||
|
|
||||||
@@ -234,13 +236,14 @@ export default function ConfigurationPackagesPanel({ settings, auth, canWrite }:
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
<AdminPageLayout
|
<AdminPageLayout
|
||||||
title="i18n:govoplan-admin.configuration_packages.eb2f05f1"
|
title="i18n:govoplan-admin.configuration_packages.eb2f05f1"
|
||||||
description="i18n:govoplan-admin.import_preflight_approve_apply_and_export_module.29aec929"
|
description="i18n:govoplan-admin.import_preflight_approve_apply_and_export_module.29aec929"
|
||||||
loading={loading}
|
loading={loading}
|
||||||
error={error || parsedPackage.error || parsedSuppliedData.error || ""}
|
error={error || parsedPackage.error || parsedSuppliedData.error || ""}
|
||||||
success={message}
|
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">
|
<Card title="i18n:govoplan-admin.catalog.4a88d27b">
|
||||||
<div className="button-row compact-actions">
|
<div className="button-row compact-actions">
|
||||||
@@ -257,12 +260,12 @@ export default function ConfigurationPackagesPanel({ settings, auth, canWrite }:
|
|||||||
<ToggleSwitch
|
<ToggleSwitch
|
||||||
checked={manualReferences}
|
checked={manualReferences}
|
||||||
onChange={setManualReferences}
|
onChange={setManualReferences}
|
||||||
label="Enter reference IDs manually"
|
label={ADMIN_INTERFACE_I18N.enterReferencesManually}
|
||||||
help="Use only when importing a package that intentionally references a target no longer returned by the live catalog."
|
help={ADMIN_INTERFACE_I18N.manualReferenceHelp}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="wide">
|
<div className="wide">
|
||||||
<FormField label="i18n:govoplan-admin.tenant_id.59eba244">
|
<FormField label="i18n:govoplan-admin.tenant_id.59eba244" documentation={ADMIN_GOVERNANCE_DOCUMENTATION}>
|
||||||
{manualReferences ? (
|
{manualReferences ? (
|
||||||
<input value={tenantId} onChange={(event) => setTenantId(event.target.value)} placeholder="active tenant" />
|
<input value={tenantId} onChange={(event) => setTenantId(event.target.value)} placeholder="active tenant" />
|
||||||
) : (
|
) : (
|
||||||
@@ -273,15 +276,15 @@ export default function ConfigurationPackagesPanel({ settings, auth, canWrite }:
|
|||||||
setChangeRequestId("");
|
setChangeRequestId("");
|
||||||
}}
|
}}
|
||||||
provider={tenantProvider}
|
provider={tenantProvider}
|
||||||
aria-label="Configuration package tenant"
|
aria-label={ADMIN_INTERFACE_I18N.tenantPickerLabel}
|
||||||
placeholder="Select a tenant"
|
placeholder={ADMIN_INTERFACE_I18N.selectTenant}
|
||||||
disabled={Boolean(busy)}
|
disabled={Boolean(busy)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</FormField>
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
<div className="wide">
|
<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 ? (
|
{manualReferences ? (
|
||||||
<input value={changeRequestId} onChange={(event) => setChangeRequestId(event.target.value)} placeholder="cfgreq-..." />
|
<input value={changeRequestId} onChange={(event) => setChangeRequestId(event.target.value)} placeholder="cfgreq-..." />
|
||||||
) : (
|
) : (
|
||||||
@@ -289,9 +292,9 @@ export default function ConfigurationPackagesPanel({ settings, auth, canWrite }:
|
|||||||
value={changeRequestId}
|
value={changeRequestId}
|
||||||
onChange={(value) => setChangeRequestId(value)}
|
onChange={(value) => setChangeRequestId(value)}
|
||||||
provider={changeRequestProvider}
|
provider={changeRequestProvider}
|
||||||
aria-label="Configuration package change request"
|
aria-label={ADMIN_INTERFACE_I18N.requestPickerLabel}
|
||||||
placeholder="Select an eligible request (optional)"
|
placeholder={ADMIN_INTERFACE_I18N.selectEligibleRequest}
|
||||||
emptyText="No eligible configuration requests."
|
emptyText={ADMIN_INTERFACE_I18N.noEligibleRequests}
|
||||||
disabled={Boolean(busy)}
|
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>
|
<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>
|
||||||
<div className="button-row compact-actions">
|
<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 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)}><Check size={16} /> i18n:govoplan-admin.request_approval.6245aea1</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={() => void applyPackage()} disabled={!canWrite || !canRun || Boolean(busy)}><Check size={16} /> i18n:govoplan-admin.apply.cfea419c</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)}><Download size={16} /> i18n:govoplan-admin.export_access.9a2eb91e</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>
|
</div>
|
||||||
{lastRequest && <p className="muted small-note">i18n:govoplan-admin.last_request.4508ef35 {lastRequest.id} ({lastRequest.status})</p>}
|
{lastRequest && <p className="muted small-note">i18n:govoplan-admin.last_request.4508ef35 {lastRequest.id} ({lastRequest.status})</p>}
|
||||||
</Card>
|
</Card>
|
||||||
@@ -324,7 +327,20 @@ export default function ConfigurationPackagesPanel({ settings, auth, canWrite }:
|
|||||||
{exportText && <Card title="i18n:govoplan-admin.export.f3e4fadb">
|
{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)} />
|
<textarea className="code-panel module-install-plan-commands" rows={16} value={exportText} onChange={(event) => setExportText(event.target.value)} />
|
||||||
</Card>}
|
</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 PermissionItem,
|
||||||
type TenantAdminItem } from
|
type TenantAdminItem } from
|
||||||
"../../api/admin";
|
"../../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 = {
|
const emptyDraft = {
|
||||||
slug: "",
|
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",
|
id: "actions", header: "i18n:govoplan-admin.actions.c3cd636a", width: 150, sticky: "end", resizable: false, align: "right",
|
||||||
render: (row) => <TableActionGroup actions={[
|
render: (row) => <TableActionGroup actions={[
|
||||||
{ id: "inspect", label: i18nMessage("i18n:govoplan-admin.inspect_value.9d5d1071", { value0: row.name }), icon: <Search />, onClick: () => setViewing(row) },
|
{ 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: "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, onClick: () => setDeleting(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]);
|
[canWrite, kind, tenants]);
|
||||||
@@ -208,7 +209,7 @@ export default function GovernanceTemplatesPanel({
|
|||||||
loading={loading}
|
loading={loading}
|
||||||
error={error}
|
error={error}
|
||||||
success={success}
|
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">
|
<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 })} />
|
<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"}
|
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)}
|
onClose={() => !busy && setEditing(null)}
|
||||||
className="admin-dialog admin-dialog-wide"
|
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">
|
<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>
|
<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 { useEffect, useState } from "react";
|
||||||
import type { ApiSettings } from "@govoplan/core-webui";
|
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 { Check, Clock, FileText, Pencil, Send } from "lucide-react";
|
||||||
import {
|
import {
|
||||||
cancelModuleInstallerRequest,
|
cancelModuleInstallerRequest,
|
||||||
@@ -42,6 +42,7 @@ import {
|
|||||||
type ModuleInstallerWorkflowStageId,
|
type ModuleInstallerWorkflowStageId,
|
||||||
type ModuleInstallerWorkflowStageState
|
type ModuleInstallerWorkflowStageState
|
||||||
} from "./moduleInstallerWorkflow";
|
} from "./moduleInstallerWorkflow";
|
||||||
|
import { ADMIN_INTERFACE_I18N, MODULE_LIFECYCLE_DOCUMENTATION } from "./interfacePatterns";
|
||||||
|
|
||||||
const MODULE_INSTALLER_I18N = {
|
const MODULE_INSTALLER_I18N = {
|
||||||
queueUnavailable: "i18n:govoplan-admin.queue_supervised_run_unavailable.f1a20202",
|
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 [planBusy, setPlanBusy] = useState(false);
|
||||||
const [error, setError] = useState("");
|
const [error, setError] = useState("");
|
||||||
const [success, setSuccess] = useState("");
|
const [success, setSuccess] = useState("");
|
||||||
|
const [confirmClearPlan, setConfirmClearPlan] = useState(false);
|
||||||
|
const [confirmMaintenance, setConfirmMaintenance] = useState(false);
|
||||||
|
const [cancelRequestId, setCancelRequestId] = useState("");
|
||||||
|
|
||||||
async function load() {
|
async function load() {
|
||||||
setLoading(true);
|
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.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";
|
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 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) {
|
function setModuleDesired(module: ModuleCatalogItem, enabled: boolean) {
|
||||||
if (module.protected) return;
|
if (module.protected) return;
|
||||||
@@ -358,13 +371,14 @@ export default function ModuleManagementPanel({ settings, canWrite, canAccessMai
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
<AdminPageLayout
|
<AdminPageLayout
|
||||||
title="i18n:govoplan-admin.system_modules.629539f2"
|
title="i18n:govoplan-admin.system_modules.629539f2"
|
||||||
description="i18n:govoplan-admin.installed_modules_active_runtime_state_and_saved.f33907dd"
|
description="i18n:govoplan-admin.installed_modules_active_runtime_state_and_saved.f33907dd"
|
||||||
loading={loading}
|
loading={loading}
|
||||||
error={error}
|
error={error}
|
||||||
success={success}
|
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 && <>
|
{catalog && <>
|
||||||
<div className="metric-grid module-management-metrics">
|
<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>
|
<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>}
|
{moduleStateRequest && <p>i18n:govoplan-admin.change_request_ready.b17e8c57 <code>{moduleStateRequest.request.id}</code></p>}
|
||||||
{!maintenanceEnabled && <div className="button-row compact-actions">
|
{!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"}
|
{maintenanceBusy ? "i18n:govoplan-admin.enabling.2b8e03e6" : "i18n:govoplan-admin.enable_maintenance_mode.75f98d57"}
|
||||||
</Button>
|
</Button>
|
||||||
</div>}
|
</div>}
|
||||||
@@ -444,9 +458,10 @@ export default function ModuleManagementPanel({ settings, canWrite, canAccessMai
|
|||||||
label={desiredEnabled ? "i18n:govoplan-admin.enabled.df174a3f" : "i18n:govoplan-admin.disabled.f4f4473d"}
|
label={desiredEnabled ? "i18n:govoplan-admin.enabled.df174a3f" : "i18n:govoplan-admin.disabled.f4f4473d"}
|
||||||
checked={desiredEnabled}
|
checked={desiredEnabled}
|
||||||
disabled={!canWrite || module.protected || busy}
|
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)} />
|
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>
|
||||||
</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.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>}
|
{item.license_reason && <p className={`module-package-catalog-description${item.license_allowed ? "" : " alert warning"}`}>{item.license_reason}</p>}
|
||||||
</div>
|
</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>
|
||||||
)}
|
)}
|
||||||
</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>
|
<p>i18n:govoplan-admin.plan_package_installs_and_removals_here_then_app.4aeb03bf</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="button-row compact-actions">
|
<div className="button-row compact-actions">
|
||||||
<Button onClick={addPlanItem} disabled={!canWrite || planBusy}>i18n:govoplan-admin.add_plan_item.fa55f028</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={() => void clearPlan()} disabled={!canWrite || planBusy || draftPlanItems.length === 0}>i18n:govoplan-admin.clear.719ea396</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}>{planBusy ? "i18n:govoplan-admin.saving.ae7e8875" : "i18n:govoplan-admin.save_plan.842dc280"}</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>
|
||||||
</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.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.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>
|
<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.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"} />
|
<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 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><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 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><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>
|
<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>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -649,13 +664,13 @@ export default function ModuleManagementPanel({ settings, canWrite, canAccessMai
|
|||||||
actor: MODULE_INSTALLER_I18N.responsibleActor,
|
actor: MODULE_INSTALLER_I18N.responsibleActor,
|
||||||
target: MODULE_INSTALLER_I18N.resolutionTarget
|
target: MODULE_INSTALLER_I18N.resolutionTarget
|
||||||
}}
|
}}
|
||||||
documentation={{ topicId: "admin.module-lifecycle-workflow", documentationType: "admin" }} />
|
documentation={MODULE_LIFECYCLE_DOCUMENTATION} />
|
||||||
) : null}
|
) : null}
|
||||||
<div className="module-installer-request-grid">
|
<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.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} />
|
<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} />
|
<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} />
|
<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.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.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>
|
<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>}
|
{request.error && <p className="module-installer-run-error">{request.error}</p>}
|
||||||
</div>
|
</div>
|
||||||
<div className="module-installer-run-actions">
|
<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 === "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}>i18n:govoplan-admin.retry.9f5cd8a2</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>}
|
{request.record_path && <code>{request.record_path}</code>}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -740,7 +755,47 @@ export default function ModuleManagementPanel({ settings, canWrite, canAccessMai
|
|||||||
</div>}
|
</div>}
|
||||||
</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 { FormField } from "@govoplan/core-webui";
|
||||||
import { ToggleSwitch } 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 { 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";
|
const DELTA_KEY = "admin:system-settings";
|
||||||
|
|
||||||
@@ -143,11 +144,11 @@ export default function SystemSettingsPanel({ settings, canWrite, canAccessMaint
|
|||||||
loading={loading}
|
loading={loading}
|
||||||
error={error}
|
error={error}
|
||||||
success={success}
|
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">
|
<div className="admin-settings-form">
|
||||||
<Card title="i18n:govoplan-admin.defaults_for_newly_created_tenants.9d0f4ccd">
|
<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}>
|
<select value={draft.default_locale} onChange={(event) => setDraft({ ...draft, default_locale: event.target.value })} disabled={!canWrite || busy || defaultLocaleOptions.length === 0}>
|
||||||
{defaultLocaleOptions.map((code) => {
|
{defaultLocaleOptions.map((code) => {
|
||||||
const language = draft.available_languages.find((item) => item.code === 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 })} />
|
<input value={packageDraft.nativeLabel} disabled={!canWrite || busy} placeholder="i18n:govoplan-admin.native_language_name_placeholder" onChange={(event) => setPackageDraft({ ...packageDraft, nativeLabel: event.target.value })} />
|
||||||
</FormField>
|
</FormField>
|
||||||
<div className="button-row compact-actions">
|
<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>
|
||||||
</div>
|
</div>
|
||||||
<p className="muted small-note">i18n:govoplan-admin.language_packages_help</p>
|
<p className="muted small-note">i18n:govoplan-admin.language_packages_help</p>
|
||||||
</Card>
|
</Card>
|
||||||
<Card title="i18n:govoplan-admin.tenant_administration_capabilities.5d265972">
|
<Card title="i18n:govoplan-admin.tenant_administration_capabilities.5d265972">
|
||||||
<div className="settings-list">
|
<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_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 })} label="i18n:govoplan-admin.allow_tenant_defined_roles_by_default.b1f79ee9" />
|
<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 })} label="i18n:govoplan-admin.allow_tenant_api_keys_by_default.58a6cf17" />
|
<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>
|
</div>
|
||||||
<p className="muted small-note">i18n:govoplan-admin.these_settings_are_enforced_by_the_backend_centr.8112ed6f</p>
|
<p className="muted small-note">i18n:govoplan-admin.these_settings_are_enforced_by_the_backend_centr.8112ed6f</p>
|
||||||
</Card>
|
</Card>
|
||||||
@@ -190,16 +191,17 @@ export default function SystemSettingsPanel({ settings, canWrite, canAccessMaint
|
|||||||
<div className="settings-list">
|
<div className="settings-list">
|
||||||
<ToggleSwitch
|
<ToggleSwitch
|
||||||
checked={draft.maintenance_mode.enabled}
|
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 } })}
|
onChange={(checked) => setDraft({ ...draft, maintenance_mode: { ...draft.maintenance_mode, enabled: checked } })}
|
||||||
label="i18n:govoplan-admin.restrict_authenticated_api_access_to_maintenance.2bc47195" />
|
label="i18n:govoplan-admin.restrict_authenticated_api_access_to_maintenance.2bc47195" />
|
||||||
|
|
||||||
</div>
|
</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
|
<textarea
|
||||||
rows={3}
|
rows={3}
|
||||||
value={draft.maintenance_mode.message ?? ""}
|
value={draft.maintenance_mode.message ?? ""}
|
||||||
disabled={!canWrite}
|
disabled={!canWrite || busy}
|
||||||
onChange={(event) => setDraft({ ...draft, maintenance_mode: { ...draft.maintenance_mode, message: event.target.value } })} />
|
onChange={(event) => setDraft({ ...draft, maintenance_mode: { ...draft.maintenance_mode, message: event.target.value } })} />
|
||||||
|
|
||||||
</FormField>
|
</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 = {
|
export const generatedTranslations: PlatformTranslations = {
|
||||||
"en": {
|
"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.module_lifecycle_progress.f1a20201": "Module lifecycle progress",
|
||||||
"i18n:govoplan-admin.queue_supervised_run_unavailable.f1a20202": "Queueing a supervised run is unavailable",
|
"i18n:govoplan-admin.queue_supervised_run_unavailable.f1a20202": "Queueing a supervised run is unavailable",
|
||||||
"i18n:govoplan-admin.required_action.f1a20203": "Required action",
|
"i18n:govoplan-admin.required_action.f1a20203": "Required action",
|
||||||
@@ -399,6 +438,45 @@ export const generatedTranslations: PlatformTranslations = {
|
|||||||
"i18n:govoplan-admin.working.049ac820": "Working..."
|
"i18n:govoplan-admin.working.049ac820": "Working..."
|
||||||
},
|
},
|
||||||
"de": {
|
"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.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.queue_supervised_run_unavailable.f1a20202": "Ein überwachter Lauf kann nicht eingereiht werden",
|
||||||
"i18n:govoplan-admin.required_action.f1a20203": "Erforderliche Maßnahme",
|
"i18n:govoplan-admin.required_action.f1a20203": "Erforderliche Maßnahme",
|
||||||
|
|||||||
+8
-8
@@ -130,17 +130,17 @@ const adminSections: AdminSectionsUiCapability = {
|
|||||||
export const adminModule: PlatformWebModule = {
|
export const adminModule: PlatformWebModule = {
|
||||||
id: "admin",
|
id: "admin",
|
||||||
label: "i18n:govoplan-admin.admin.4e7afebc",
|
label: "i18n:govoplan-admin.admin.4e7afebc",
|
||||||
version: "1.0.0",
|
version: "0.1.8",
|
||||||
dependencies: ["access"],
|
dependencies: ["access"],
|
||||||
translations,
|
translations,
|
||||||
viewSurfaces: [
|
viewSurfaces: [
|
||||||
{ id: "admin.section.overview", moduleId: "admin", kind: "section", label: "Administration overview", order: 0 },
|
{ 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: "System settings", order: 10 },
|
{ 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: "Configuration changes", order: 20 },
|
{ 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: "Configuration packages", order: 30 },
|
{ 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: "Role templates", order: 40 },
|
{ 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: "Group templates", order: 50 },
|
{ 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: "Modules", order: 85 }
|
{ id: "admin.section.system-modules", moduleId: "admin", kind: "section", label: "i18n:govoplan-admin.modules.04e9462c", order: 85 }
|
||||||
],
|
],
|
||||||
uiCapabilities: {
|
uiCapabilities: {
|
||||||
"admin.sections": adminSections,
|
"admin.sections": adminSections,
|
||||||
|
|||||||
Reference in New Issue
Block a user