3 Commits
v0.1.6 ... main

Author SHA1 Message Date
c9e1fb287f intermittent commit 2026-07-14 13:22:10 +02:00
11ecf362a3 Release v0.1.8 2026-07-11 16:49:00 +02:00
24edb7eb8a Release v0.1.7 2026-07-11 02:34:55 +02:00
13 changed files with 537 additions and 201 deletions

View File

@@ -1,5 +1,9 @@
# GovOPlaN Admin
<!-- govoplan-repository-type:start -->
**Repository type:** module (platform).
<!-- govoplan-repository-type:end -->
`govoplan-admin` owns generic system administration API and WebUI contributions
during the GovOPlaN module split.
@@ -43,3 +47,19 @@ Package mutation is intentionally not executed inside the FastAPI request. The
admin UI records operator intent and queues or renders commands for the trusted
installer process described in
`/mnt/DATA/git/govoplan-core/docs/MODULE_ARCHITECTURE.md`.
## Package Surfaces
The admin UI intentionally exposes two different package concepts:
- **Configuration packages** are import/export bundles for module-owned
configuration data. They support dry-run diagnostics, approval, apply, and
export workflows without installing Python or WebUI packages.
- **Module package catalog** and **operator install plan** live under
**Modules**. They describe approved release artifacts, install/update/remove
plans, preflight blockers, maintenance-mode requirements, installer-daemon
requests, and rollback visibility.
These surfaces should stay separate in navigation and copy. If future UI work
combines them visually, it must still preserve the operator distinction between
configuration mutation and package installation.

View File

@@ -1,6 +1,6 @@
{
"name": "@govoplan/admin-webui",
"version": "0.1.6",
"version": "0.1.8",
"private": true,
"type": "module",
"main": "webui/src/index.ts",
@@ -18,7 +18,7 @@
"LICENSE"
],
"peerDependencies": {
"@govoplan/core-webui": "^0.1.6",
"@govoplan/core-webui": "^0.1.8",
"lucide-react": "^1.23.0",
"react": "^19.0.0",
"react-dom": "^19.0.0",

View File

@@ -4,14 +4,14 @@ build-backend = "setuptools.build_meta"
[project]
name = "govoplan-admin"
version = "0.1.6"
version = "0.1.8"
description = "GovOPlaN generic administration module."
readme = "README.md"
requires-python = ">=3.12"
authors = [{ name = "GovOPlaN" }]
dependencies = [
"govoplan-core>=0.1.6",
"govoplan-access>=0.1.6",
"govoplan-core>=0.1.8",
"govoplan-access>=0.1.8",
]
[tool.setuptools.packages.find]

View File

@@ -31,6 +31,7 @@ from govoplan_core.core.configuration_control import (
)
from govoplan_core.core.module_management import (
PROTECTED_MODULES,
ModuleInstallPlan,
ModuleInstallPlanItem,
ModuleManagementError,
configured_enabled_modules,
@@ -49,6 +50,7 @@ from govoplan_core.core.module_installer import (
default_installer_runtime_dir,
list_module_installer_runs,
list_module_installer_requests,
module_install_catalog_companion_module_ids,
module_installer_daemon_status,
module_install_preflight,
module_installer_lock_status,
@@ -57,6 +59,12 @@ from govoplan_core.core.module_installer import (
read_module_installer_request,
retry_module_installer_request,
)
from govoplan_core.core.module_installer_notifications import (
emit_module_installer_notification,
installer_notification_body,
installer_notification_priority,
installer_notification_subject,
)
from govoplan_core.core.module_license import module_license_decision, module_license_diagnostics
from govoplan_core.core.module_package_catalog import record_module_package_catalog_acceptance, validate_module_package_catalog
from govoplan_core.core.maintenance import MAINTENANCE_ACCESS_SCOPE, MaintenanceMode, saved_maintenance_mode, save_maintenance_mode
@@ -145,6 +153,11 @@ def _request_registry(request: Request) -> PlatformRegistry:
return registry
def _optional_request_registry(request: Request) -> PlatformRegistry | None:
registry = getattr(request.app.state, "govoplan_registry", None)
return registry if isinstance(registry, PlatformRegistry) else None
def _request_lifecycle(request: Request) -> ModuleLifecycleManager:
lifecycle = getattr(request.app.state, "govoplan_lifecycle", None)
if not isinstance(lifecycle, ModuleLifecycleManager):
@@ -152,6 +165,29 @@ def _request_lifecycle(request: Request) -> ModuleLifecycleManager:
return lifecycle
def _emit_installer_request_notification(
*,
session: Session,
registry: PlatformRegistry | None,
tenant_id: str | None,
request: dict[str, object],
event_kind: str,
recipient_id: str | None = None,
) -> None:
status_value = str(request.get("status") or event_kind.rsplit(".", 1)[-1])
emit_module_installer_notification(
session=session,
registry=registry,
tenant_id=tenant_id,
request=request,
event_kind=event_kind,
subject=installer_notification_subject(event_kind, request),
body_text=installer_notification_body(event_kind, request),
recipient_id=recipient_id,
priority=installer_notification_priority(status_value),
)
def _http_admin_error(exc: Exception) -> HTTPException:
if isinstance(exc, AdminConflictError):
return HTTPException(status_code=status.HTTP_409_CONFLICT, detail=str(exc))
@@ -429,17 +465,27 @@ def _webui_root() -> Path | None:
def _upsert_install_plan_item(session: Session, item: ModuleInstallPlanItem):
return _upsert_install_plan_items(session, [item])
def _upsert_install_plan_items(session: Session, items: list[ModuleInstallPlanItem]):
existing = saved_module_install_plan(session)
replacement_ids = {item.module_id for item in items if item.status == "planned"}
retained = [
current
for current in existing.items
if not (current.status == "planned" and current.module_id == item.module_id)
if not (current.status == "planned" and current.module_id in replacement_ids)
]
return save_module_install_plan(session, [*retained, item])
return save_module_install_plan(session, [*retained, *items])
def _catalog_plan_item(module_id: str, available_module_ids: set[str]) -> tuple[ModuleInstallPlanItem, dict[str, object]]:
result = validate_module_package_catalog()
def _catalog_plan_item(
module_id: str,
available_module_ids: set[str],
*,
validation: dict[str, object] | None = None,
) -> tuple[ModuleInstallPlanItem, dict[str, object]]:
result = validation or validate_module_package_catalog()
if not result.get("valid"):
raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
@@ -651,6 +697,7 @@ def read_module_install_request_detail(
@router.post("/system/modules/install-requests", response_model=ModuleInstallerRequestItem)
def create_module_install_request(
payload: ModuleInstallerRequestCreateRequest,
request_context: Request,
session: Session = Depends(get_session),
principal: ApiPrincipal = Depends(require_scope("system:settings:write")),
):
@@ -663,8 +710,17 @@ def create_module_install_request(
request = queue_module_installer_request(
runtime_dir=runtime_dir,
requested_by=principal.user.id,
tenant_id=principal.tenant_id,
options=payload.options.model_dump(exclude_none=True),
)
_emit_installer_request_notification(
session=session,
registry=_optional_request_registry(request_context),
tenant_id=principal.tenant_id,
request=request,
event_kind="module_installer.request.queued",
recipient_id=principal.user.id,
)
audit_from_principal(
session,
principal,
@@ -686,6 +742,7 @@ def create_module_install_request(
@router.post("/system/modules/install-requests/{request_id}/cancel", response_model=ModuleInstallerRequestItem)
def cancel_module_install_request(
request_id: str,
request_context: Request,
session: Session = Depends(get_session),
principal: ApiPrincipal = Depends(require_scope("system:settings:write")),
):
@@ -703,6 +760,14 @@ def cancel_module_install_request(
)
except ModuleInstallerError as exc:
raise HTTPException(status_code=status.HTTP_422_UNPROCESSABLE_CONTENT, detail=str(exc)) from exc
_emit_installer_request_notification(
session=session,
registry=_optional_request_registry(request_context),
tenant_id=str(request.get("tenant_id") or principal.tenant_id),
request=request,
event_kind="module_installer.request.cancelled",
recipient_id=str(request.get("requested_by") or principal.user.id),
)
audit_from_principal(
session,
principal,
@@ -724,6 +789,7 @@ def cancel_module_install_request(
@router.post("/system/modules/install-requests/{request_id}/retry", response_model=ModuleInstallerRequestItem)
def retry_module_install_request(
request_id: str,
request_context: Request,
session: Session = Depends(get_session),
principal: ApiPrincipal = Depends(require_scope("system:settings:write")),
):
@@ -741,6 +807,14 @@ def retry_module_install_request(
)
except ModuleInstallerError as exc:
raise HTTPException(status_code=status.HTTP_422_UNPROCESSABLE_CONTENT, detail=str(exc)) from exc
_emit_installer_request_notification(
session=session,
registry=_optional_request_registry(request_context),
tenant_id=str(request.get("tenant_id") or principal.tenant_id),
request=request,
event_kind="module_installer.request.queued",
recipient_id=principal.user.id,
)
audit_from_principal(
session,
principal,
@@ -801,7 +875,19 @@ def plan_module_install_from_catalog(
lifecycle = getattr(request.app.state, "govoplan_lifecycle", None)
available = dict(lifecycle.available_modules) if isinstance(lifecycle, ModuleLifecycleManager) else available_module_manifests(ignore_load_errors=True)
item, validation = _catalog_plan_item(module_id, set(available))
plan = _upsert_install_plan_item(session, item)
existing = saved_module_install_plan(session)
retained = [
current
for current in existing.items
if not (current.status == "planned" and current.module_id == item.module_id)
]
candidate_plan = ModuleInstallPlan(items=tuple([*retained, item]))
companion_ids = module_install_catalog_companion_module_ids(candidate_plan, available, validation=validation)
companion_items = [
_catalog_plan_item(companion_id, set(available), validation=validation)[0]
for companion_id in companion_ids
]
plan = _upsert_install_plan_items(session, [item, *companion_items])
record_module_package_catalog_acceptance(validation)
except ModuleManagementError as exc:
raise HTTPException(status_code=status.HTTP_422_UNPROCESSABLE_CONTENT, detail=str(exc)) from exc
@@ -816,6 +902,7 @@ def plan_module_install_from_catalog(
module_id=module_id,
outcome="planned",
items=[item.as_dict() for item in plan.items],
companion_module_ids=list(companion_ids),
catalog_validation={
"valid": validation.get("valid"),
"channel": validation.get("channel"),
@@ -825,7 +912,10 @@ def plan_module_install_from_catalog(
)
session.commit()
planned_action = "update" if item.action == "update" else "install"
return _module_install_plan_response(session, request, notes=[f"Catalog {planned_action} entry planned for {module_id}."])
notes = [f"Catalog {planned_action} entry planned for {module_id}."]
if companion_ids:
notes.append("Required companion catalog entries added: " + ", ".join(companion_ids))
return _module_install_plan_response(session, request, notes=notes)
@router.post("/system/modules/{module_id}/uninstall-plan", response_model=ModuleInstallPlanResponse)

View File

@@ -3,61 +3,10 @@ from __future__ import annotations
from datetime import datetime
from typing import Any, Literal
from pydantic import BaseModel, ConfigDict, Field, field_validator
from pydantic import BaseModel, ConfigDict, Field
from govoplan_core.api.v1.schemas import DeltaDeletedItem
RETENTION_DAY_KEYS = (
"raw_campaign_json_retention_days",
"generated_eml_retention_days",
"stored_report_detail_retention_days",
"mock_mailbox_retention_days",
"audit_detail_retention_days",
)
RETENTION_POLICY_FIELD_KEYS = (
"store_raw_campaign_json",
*RETENTION_DAY_KEYS,
"audit_detail_level",
)
def default_allow_lower_level_limits() -> dict[str, bool]:
return {key: True for key in RETENTION_POLICY_FIELD_KEYS}
def normalize_allow_lower_level_limits(value: Any, *, fill_defaults: bool) -> dict[str, bool] | None:
if value in (None, ""):
return default_allow_lower_level_limits() if fill_defaults else None
if not isinstance(value, dict):
raise ValueError("allow_lower_level_limits must be an object")
normalized = default_allow_lower_level_limits() if fill_defaults else {}
for key, allowed in value.items():
clean_key = str(key)
if clean_key not in RETENTION_POLICY_FIELD_KEYS:
raise ValueError(f"Unknown retention policy field: {clean_key}")
normalized[clean_key] = bool(allowed)
return normalized
class PrivacyRetentionPolicyItem(BaseModel):
model_config = ConfigDict(extra="forbid")
store_raw_campaign_json: bool = True
raw_campaign_json_retention_days: int | None = Field(default=None, ge=0)
generated_eml_retention_days: int | None = Field(default=None, ge=0)
stored_report_detail_retention_days: int | None = Field(default=None, ge=0)
mock_mailbox_retention_days: int | None = Field(default=None, ge=0)
audit_detail_retention_days: int | None = Field(default=None, ge=0)
audit_detail_level: Literal["full", "redacted", "minimal"] = "full"
allow_lower_level_limits: dict[str, bool] = Field(default_factory=default_allow_lower_level_limits)
@field_validator("allow_lower_level_limits", mode="before")
@classmethod
def _normalize_allow_lower_level_limits(cls, value: Any) -> Any:
return normalize_allow_lower_level_limits(value, fill_defaults=True)
from govoplan_core.privacy.schemas import PrivacyRetentionPolicyItem
class MaintenanceModeItem(BaseModel):
@@ -182,6 +131,7 @@ class ModuleInstallPlanItem(BaseModel):
python_ref: str | None = Field(default=None, max_length=1000)
webui_package: str | None = Field(default=None, max_length=200)
webui_ref: str | None = Field(default=None, max_length=1000)
data_safety_acknowledged: bool = False
destroy_data: bool = False
status: Literal["planned", "applied", "blocked"] = "planned"
notes: str | None = Field(default=None, max_length=1000)
@@ -201,6 +151,63 @@ class ModuleInstallChecklistItem(BaseModel):
detail: str | None = None
class ModuleInstallTargetItem(BaseModel):
module_id: str
action: Literal["install", "update", "uninstall"]
source: Literal["manual", "catalog"]
current_version: str | None = None
target_version: str | None = None
python_package: str | None = None
python_ref: str | None = None
webui_package: str | None = None
webui_ref: str | None = None
migration_safety: Literal["automatic", "requires_review", "forward_only", "destructive"] = "automatic"
migration_notes: str | None = None
current_version_min: str | None = None
current_version_max_exclusive: str | None = None
bridge_release: bool = False
bridge_notes: str | None = None
allow_downgrade: bool = False
allow_same_version: bool = False
recovery_tested: bool = False
recovery_notes: str | None = None
data_safety_acknowledged: bool = False
class ModuleMigrationPlanStep(BaseModel):
module_id: str
action: Literal["install", "update", "uninstall"]
phase: Literal["upgrade", "retirement"]
source: Literal["manifest", "catalog", "pending"]
has_migration_metadata: bool = False
metadata_pending: bool = False
migration_safety: Literal["automatic", "requires_review", "forward_only", "destructive"] = "automatic"
current_version: str | None = None
target_version: str | None = None
reason: str | None = None
class ModuleMigrationTaskPlanItem(BaseModel):
module_id: str
task_id: str
phase: Literal["pre_migration_check", "pre_migration_prepare", "post_migration_backfill", "post_migration_verify"]
summary: str
task_version: str = "1"
safety: Literal["automatic", "requires_review", "forward_only", "destructive"] = "automatic"
idempotent: bool = True
timeout_seconds: int | None = None
source: Literal["manifest", "catalog", "pending"] = "manifest"
has_executor: bool = False
metadata_pending: bool = False
class ModuleMigrationExecutionPlan(BaseModel):
enabled_modules: list[str] = Field(default_factory=list)
requires_database_migration: bool = False
steps: list[ModuleMigrationPlanStep] = Field(default_factory=list)
tasks: list[ModuleMigrationTaskPlanItem] = Field(default_factory=list)
class ModuleInstallPreflightResponse(BaseModel):
allowed: bool
maintenance_mode: bool
@@ -210,6 +217,8 @@ class ModuleInstallPreflightResponse(BaseModel):
rollback_commands: list[str] = Field(default_factory=list)
issues: list[ModuleInstallPreflightIssue] = Field(default_factory=list)
checklist: list[ModuleInstallChecklistItem] = Field(default_factory=list)
target_plan: list[ModuleInstallTargetItem] = Field(default_factory=list)
migration_plan: ModuleMigrationExecutionPlan = Field(default_factory=ModuleMigrationExecutionPlan)
class ModuleInstallPlanResponse(BaseModel):
@@ -328,6 +337,20 @@ class ModulePackageCatalogItem(BaseModel):
description: str | None = None
version: str | None = None
action: Literal["install", "update", "uninstall"] = "install"
dependencies: list[str] = Field(default_factory=list)
optional_dependencies: list[str] = Field(default_factory=list)
migration_safety: Literal["automatic", "requires_review", "forward_only", "destructive"] = "automatic"
migration_notes: str | None = None
migration_after: list[str] = Field(default_factory=list)
migration_before: list[str] = Field(default_factory=list)
current_version_min: str | None = None
current_version_max_exclusive: str | None = None
bridge_release: bool = False
bridge_notes: str | None = None
allow_downgrade: bool = False
allow_same_version: bool = False
recovery_tested: bool = False
recovery_notes: str | None = None
python_package: str | None = None
python_ref: str | None = None
webui_package: str | None = None

View File

@@ -17,7 +17,7 @@ def _route_factory(context: ModuleContext):
manifest = ModuleManifest(
id="admin",
name="Admin",
version="0.1.6",
version="0.1.8",
required_capabilities=(CAPABILITY_AUTH_PRINCIPAL_RESOLVER, CAPABILITY_AUTH_PERMISSION_EVALUATOR),
route_factory=_route_factory,
migration_spec=MigrationSpec(module_id="admin", metadata=Base.metadata),

View File

@@ -1,6 +1,6 @@
{
"name": "@govoplan/admin-webui",
"version": "0.1.6",
"version": "0.1.8",
"private": true,
"type": "module",
"main": "src/index.ts",
@@ -13,7 +13,7 @@
}
},
"peerDependencies": {
"@govoplan/core-webui": "^0.1.6",
"@govoplan/core-webui": "^0.1.8",
"lucide-react": "^1.23.0",
"react": "^19.0.0",
"react-dom": "^19.0.0",

View File

@@ -1,74 +1,16 @@
import type { ApiSettings, DeltaDeletedItem } from "@govoplan/core-webui";
import { apiFetch } from "@govoplan/core-webui";
export type PermissionItem = {
scope: string;
label: string;
description: string;
category: string;
level: "tenant" | "system";
system_template_id?: string | null;
system_required?: boolean;
};
export type AdminOverview = {
active_tenant_id: string;
active_tenant_name: string;
tenant_count?: number | null;
system_account_count?: number | null;
system_group_template_count?: number | null;
system_role_template_count?: number | null;
user_count: number;
active_user_count: number;
group_count: number;
role_count: number;
active_api_key_count: number;
capabilities: string[];
};
export type TenantAdminItem = {
id: string;
slug: string;
name: string;
description?: string | null;
default_locale: string;
settings: Record<string, unknown>;
allow_custom_groups?: boolean | null;
allow_custom_roles?: boolean | null;
allow_api_keys?: boolean | null;
effective_governance: Record<string, boolean>;
is_active: boolean;
counts: Record<string, number>;
created_at: string;
updated_at: string;
};
export type PrivacyRetentionPolicyFieldKey =
| "store_raw_campaign_json"
| "raw_campaign_json_retention_days"
| "generated_eml_retention_days"
| "stored_report_detail_retention_days"
| "mock_mailbox_retention_days"
| "audit_detail_retention_days"
| "audit_detail_level";
export type PrivacyRetentionLimitPermissions = Record<PrivacyRetentionPolicyFieldKey, boolean>;
export type PrivacyRetentionLimitPermissionPatch = Partial<PrivacyRetentionLimitPermissions>;
export type PrivacyRetentionPolicy = {
store_raw_campaign_json: boolean;
raw_campaign_json_retention_days?: number | null;
generated_eml_retention_days?: number | null;
stored_report_detail_retention_days?: number | null;
mock_mailbox_retention_days?: number | null;
audit_detail_retention_days?: number | null;
audit_detail_level: "full" | "redacted" | "minimal";
allow_lower_level_limits: PrivacyRetentionLimitPermissions;
};
export type PrivacyRetentionPolicyPatch = Partial<Omit<PrivacyRetentionPolicy, "allow_lower_level_limits">> & {
allow_lower_level_limits?: PrivacyRetentionLimitPermissionPatch;
};
import type { ApiSettings, DeltaDeletedItem, PrivacyRetentionPolicy } from "@govoplan/core-webui";
import { apiFetch, apiGetList, apiPath, apiQuery } from "@govoplan/core-webui";
export { fetchAdminOverview, fetchPermissionCatalog, fetchTenants } from "@govoplan/core-webui";
export type {
AdminOverview,
PrivacyRetentionLimitPermissionPatch,
PrivacyRetentionLimitPermissions,
PrivacyRetentionPolicy,
PrivacyRetentionPolicyFieldKey,
PrivacyRetentionPolicyPatch,
PermissionItem,
TenantAdminItem
} from "@govoplan/core-webui";
export type MaintenanceMode = {
enabled: boolean;
@@ -192,10 +134,7 @@ type DeltaResponseFields = {
};
function deltaSuffix(options: { since?: string | null; limit?: number } = {}): string {
const params = new URLSearchParams();
if (options.since) params.set("since", options.since);
if (options.limit) params.set("limit", String(options.limit));
return params.toString() ? `?${params.toString()}` : "";
return apiQuery(options);
}
export type SystemSettingsDeltaResponse = {
@@ -249,13 +188,14 @@ export type ModuleCatalogResponse = {
export type ModuleInstallPlanItem = {
module_id: string;
action: "install" | "uninstall";
action: "install" | "update" | "uninstall";
source: "manual" | "catalog";
catalog?: Record<string, unknown> | null;
python_package?: string | null;
python_ref?: string | null;
webui_package?: string | null;
webui_ref?: string | null;
data_safety_acknowledged: boolean;
destroy_data: boolean;
status: "planned" | "applied" | "blocked";
notes?: string | null;
@@ -275,6 +215,63 @@ export type ModuleInstallChecklistItem = {
detail?: string | null;
};
export type ModuleInstallTargetItem = {
module_id: string;
action: "install" | "update" | "uninstall";
source: "manual" | "catalog";
current_version?: string | null;
target_version?: string | null;
python_package?: string | null;
python_ref?: string | null;
webui_package?: string | null;
webui_ref?: string | null;
migration_safety: "automatic" | "requires_review" | "forward_only" | "destructive";
migration_notes?: string | null;
current_version_min?: string | null;
current_version_max_exclusive?: string | null;
bridge_release: boolean;
bridge_notes?: string | null;
allow_downgrade: boolean;
allow_same_version: boolean;
recovery_tested: boolean;
recovery_notes?: string | null;
data_safety_acknowledged: boolean;
};
export type ModuleMigrationPlanStep = {
module_id: string;
action: "install" | "update" | "uninstall";
phase: "upgrade" | "retirement";
source: "manifest" | "catalog" | "pending";
has_migration_metadata: boolean;
metadata_pending: boolean;
migration_safety: "automatic" | "requires_review" | "forward_only" | "destructive";
current_version?: string | null;
target_version?: string | null;
reason?: string | null;
};
export type ModuleMigrationTaskPlanItem = {
module_id: string;
task_id: string;
phase: "pre_migration_check" | "pre_migration_prepare" | "post_migration_backfill" | "post_migration_verify";
summary: string;
task_version: string;
safety: "automatic" | "requires_review" | "forward_only" | "destructive";
idempotent: boolean;
timeout_seconds?: number | null;
source: "manifest" | "catalog" | "pending";
has_executor: boolean;
metadata_pending: boolean;
};
export type ModuleMigrationExecutionPlan = {
enabled_modules: string[];
requires_database_migration: boolean;
steps: ModuleMigrationPlanStep[];
tasks: ModuleMigrationTaskPlanItem[];
};
export type ModuleInstallPreflight = {
allowed: boolean;
maintenance_mode: boolean;
@@ -284,6 +281,8 @@ export type ModuleInstallPreflight = {
rollback_commands: string[];
issues: ModuleInstallPreflightIssue[];
checklist: ModuleInstallChecklistItem[];
target_plan: ModuleInstallTargetItem[];
migration_plan: ModuleMigrationExecutionPlan;
};
export type ModuleInstallPlanResponse = {
@@ -389,7 +388,21 @@ export type ModulePackageCatalogItem = {
name: string;
description?: string | null;
version?: string | null;
action: "install" | "uninstall";
action: "install" | "update" | "uninstall";
dependencies: string[];
optional_dependencies: string[];
migration_safety: "automatic" | "requires_review" | "forward_only" | "destructive";
migration_notes?: string | null;
migration_after: string[];
migration_before: string[];
current_version_min?: string | null;
current_version_max_exclusive?: string | null;
bridge_release: boolean;
bridge_notes?: string | null;
allow_downgrade: boolean;
allow_same_version: boolean;
recovery_tested: boolean;
recovery_notes?: string | null;
python_package?: string | null;
python_ref?: string | null;
webui_package?: string | null;
@@ -467,20 +480,6 @@ export type GovernanceTemplateItem = {
updated_at: string;
};
export function fetchAdminOverview(settings: ApiSettings): Promise<AdminOverview> {
return apiFetch(settings, "/api/v1/admin/overview");
}
export async function fetchPermissionCatalog(settings: ApiSettings): Promise<PermissionItem[]> {
const response = await apiFetch<{ permissions: PermissionItem[] }>(settings, "/api/v1/admin/permissions");
return response.permissions;
}
export async function fetchTenants(settings: ApiSettings): Promise<TenantAdminItem[]> {
const response = await apiFetch<{ tenants: TenantAdminItem[] }>(settings, "/api/v1/admin/tenants");
return response.tenants;
}
export function fetchSystemSettings(settings: ApiSettings): Promise<SystemSettingsItem> {
return apiFetch(settings, "/api/v1/admin/system/settings");
}
@@ -510,19 +509,11 @@ export function fetchModuleInstallPlan(settings: ApiSettings): Promise<ModuleIns
}
export function fetchModuleInstallerRuns(settings: ApiSettings, options: { page_size?: number; cursor?: string | null } = {}): Promise<ModuleInstallerRunListResponse> {
const params = new URLSearchParams();
if (options.page_size) params.set("page_size", String(options.page_size));
if (options.cursor) params.set("cursor", options.cursor);
const suffix = params.toString() ? `?${params.toString()}` : "";
return apiFetch(settings, `/api/v1/admin/system/modules/install-runs${suffix}`);
return apiFetch(settings, apiPath("/api/v1/admin/system/modules/install-runs", options));
}
export function fetchModuleInstallerRequests(settings: ApiSettings, options: { page_size?: number; cursor?: string | null } = {}): Promise<ModuleInstallerRequestListResponse> {
const params = new URLSearchParams();
if (options.page_size) params.set("page_size", String(options.page_size));
if (options.cursor) params.set("cursor", options.cursor);
const suffix = params.toString() ? `?${params.toString()}` : "";
return apiFetch(settings, `/api/v1/admin/system/modules/install-requests${suffix}`);
return apiFetch(settings, apiPath("/api/v1/admin/system/modules/install-requests", options));
}
export function createModuleInstallerRequest(settings: ApiSettings, options: ModuleInstallerRequestOptions): Promise<ModuleInstallerRequestItem> {
@@ -564,9 +555,7 @@ export function clearModuleInstallPlan(settings: ApiSettings): Promise<ModuleIns
}
export async function fetchGovernanceTemplates(settings: ApiSettings, kind?: "group" | "role"): Promise<GovernanceTemplateItem[]> {
const suffix = kind ? `?kind=${encodeURIComponent(kind)}` : "";
const response = await apiFetch<{ templates: GovernanceTemplateItem[] }>(settings, `/api/v1/admin/system/governance-templates${suffix}`);
return response.templates;
return apiGetList<GovernanceTemplateItem, "templates">(settings, "/api/v1/admin/system/governance-templates", "templates", { kind });
}
export function createGovernanceTemplate(settings: ApiSettings, payload: Omit<GovernanceTemplateItem, "id" | "created_at" | "updated_at" | "effective_permission_count"> & { change_request_id?: string | null }): Promise<GovernanceTemplateItem> {
@@ -578,8 +567,7 @@ export function updateGovernanceTemplate(settings: ApiSettings, templateId: stri
}
export function deleteGovernanceTemplate(settings: ApiSettings, templateId: string, changeRequestId?: string | null): Promise<void> {
const suffix = changeRequestId ? `?change_request_id=${encodeURIComponent(changeRequestId)}` : "";
return apiFetch(settings, `/api/v1/admin/system/governance-templates/${templateId}${suffix}`, { method: "DELETE" });
return apiFetch(settings, apiPath(`/api/v1/admin/system/governance-templates/${templateId}`, { change_request_id: changeRequestId }), { method: "DELETE" });
}
export function fetchConfigurationChanges(settings: ApiSettings): Promise<{ requests: ConfigurationChangeRequest[]; history: ConfigurationChangeRecord[] }> {

View File

@@ -43,7 +43,7 @@ export default function AdminOverviewPanel({ settings, onSelect, availableSectio
{hasAnySection(availableSections, platformSectionIds) && <Card title="ADMINISTRATION">
<div className="admin-overview-grid">
{availableSections.has("system-modules") && <AreaLink title="i18n:govoplan-admin.modules.04e9462c" text="i18n:govoplan-admin.installed_modules_runtime_state_and_startup_stat.38dd7028" onClick={() => onSelect("system-modules")} />}
{availableSections.has("system-configuration-packages") && <AreaLink title="i18n:govoplan-admin.packages.0a999012" 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")} />}
{availableSections.has("system-settings") && <AreaLink title="i18n:govoplan-admin.maintenance.94de303b" text="i18n:govoplan-admin.instance_defaults_and_tenant_governance_capabili.99d6b2fa" onClick={() => onSelect("system-settings")} />}
{availableSections.has("system-configuration-changes") && <AreaLink title="i18n:govoplan-admin.changes.8aa57de6" text="i18n:govoplan-admin.configuration_requests_approvals_and_version_his.19f37335" onClick={() => onSelect("system-configuration-changes")} />}
{availableSections.has("system-audit") && <AreaLink title="i18n:govoplan-admin.audit.fa1703dd" text="i18n:govoplan-admin.system_level_administrative_history.49f76723" onClick={() => onSelect("system-audit")} />}

View File

@@ -19,7 +19,7 @@ import {
type PermissionItem,
type TenantAdminItem } from
"../../api/admin";
import { AdminIconButton, AdminPageLayout, AdminSelectionList, adminErrorMessage, joinLabels, i18nMessage, useUnsavedDraftGuard } from "@govoplan/core-webui";
import { AdminIconButton, AdminPageLayout, AdminSelectionList, adminErrorMessage, i18nMessage, useUnsavedDraftGuard } from "@govoplan/core-webui";
const emptyDraft = {
slug: "",
@@ -54,6 +54,7 @@ export default function GovernanceTemplatesPanel({
const [error, setError] = useState("");
const [success, setSuccess] = useState("");
const dirty = editing !== null && draftKey(draft) !== savedDraftKey;
const permissionsByScope = useMemo(() => new Map(permissions.map((permission) => [permission.scope, permission])), [permissions]);
useUnsavedDraftGuard({
dirty,
@@ -238,7 +239,7 @@ export default function GovernanceTemplatesPanel({
</Dialog>
<Dialog open={Boolean(viewing)} title={viewing?.name || "i18n:govoplan-admin.template_details.d5d75e4d"} onClose={() => setViewing(null)} className="admin-dialog admin-dialog-wide" footer={<Button onClick={() => setViewing(null)}>i18n:govoplan-admin.close.bbfa773e</Button>}>
{viewing && <dl className="admin-details-grid"><div><dt>i18n:govoplan-admin.kind.e00ac23f</dt><dd>{viewing.kind}</dd></div><div><dt>i18n:govoplan-admin.slug.094da9b9</dt><dd>{viewing.slug}</dd></div><div><dt>i18n:govoplan-admin.status.bae7d5be</dt><dd>{viewing.is_active ? "i18n:govoplan-admin.active.a733b809" : "i18n:govoplan-admin.inactive.09af574c"}</dd></div><div><dt>i18n:govoplan-admin.tenants.1f7ae776</dt><dd>{viewing.assignments.length || "i18n:govoplan-admin.none.6eef6648"}</dd></div><div><dt>i18n:govoplan-admin.description.55f8ebc8</dt><dd>{viewing.description || "—"}</dd></div><div><dt>i18n:govoplan-admin.permissions.d06d5557</dt><dd>{viewing.permissions.length ? joinLabels(viewing.permissions.map((name) => ({ name }))) : "—"}</dd></div></dl>}
{viewing && <dl className="admin-details-grid"><div><dt>i18n:govoplan-admin.kind.e00ac23f</dt><dd>{viewing.kind}</dd></div><div><dt>i18n:govoplan-admin.slug.094da9b9</dt><dd>{viewing.slug}</dd></div><div><dt>i18n:govoplan-admin.status.bae7d5be</dt><dd>{viewing.is_active ? "i18n:govoplan-admin.active.a733b809" : "i18n:govoplan-admin.inactive.09af574c"}</dd></div><div><dt>i18n:govoplan-admin.tenants.1f7ae776</dt><dd>{viewing.assignments.length || "i18n:govoplan-admin.none.6eef6648"}</dd></div><div><dt>i18n:govoplan-admin.description.55f8ebc8</dt><dd>{viewing.description || "—"}</dd></div><div><dt>i18n:govoplan-admin.permissions.d06d5557</dt><dd>{viewing.permissions.length ? <PermissionDetails scopes={viewing.permissions} permissionsByScope={permissionsByScope} /> : "—"}</dd></div></dl>}
</Dialog>
<ConfirmDialog open={Boolean(deleting)} title={kind === "group" ? "i18n:govoplan-admin.delete_group_template.8745d842" : "i18n:govoplan-admin.delete_tenant_role.4efa0813"} message={i18nMessage("i18n:govoplan-admin.delete_value_removal_is_blocked_while_a_material.d4eba73d", { value0: deleting?.name })} confirmLabel={kind === "group" ? "i18n:govoplan-admin.delete_template.399bf72a" : "i18n:govoplan-admin.delete_tenant_role.4efa0813"} tone="danger" busy={busy} onCancel={() => setDeleting(null)} onConfirm={() => void remove()} />
@@ -249,3 +250,31 @@ export default function GovernanceTemplatesPanel({
function draftKey(draft: typeof emptyDraft): string {
return JSON.stringify(draft);
}
function PermissionDetails({ scopes, permissionsByScope }: {scopes: string[];permissionsByScope: ReadonlyMap<string, PermissionItem>;}) {
const groups = groupPermissionScopes(scopes, permissionsByScope);
return (
<div className="admin-permission-details">
{groups.map((group) => <section key={group.module}>
<strong>{group.module}</strong>
<ul>
{group.permissions.map((permission) => <li key={permission.scope}>
<span>{permission.label}</span>
<code>{permission.scope}</code>
</li>)}
</ul>
</section>)}
</div>);
}
function groupPermissionScopes(scopes: string[], permissionsByScope: ReadonlyMap<string, PermissionItem>) {
const groups = new Map<string, {scope: string;label: string}[]>();
for (const scope of [...scopes].sort()) {
const moduleId = scope.split(":", 1)[0] || "other";
const permission = permissionsByScope.get(scope);
const group = groups.get(moduleId) ?? [];
group.push({ scope, label: permission?.label || scope });
groups.set(moduleId, group);
}
return [...groups.entries()].map(([module, permissions]) => ({ module, permissions }));
}

View File

@@ -26,6 +26,9 @@ import {
type ModuleInstallerRunListResponse,
type ModuleInstallPlanItem,
type ModuleInstallPlanResponse,
type ModuleInstallTargetItem,
type ModuleMigrationPlanStep,
type ModuleMigrationTaskPlanItem,
type ModuleLicenseDiagnostics,
type ModulePackageCatalogResponse,
type ModulePackageCatalogItem } from
@@ -414,15 +417,25 @@ export default function ModuleManagementPanel({ settings, canWrite, canAccessMai
{item.python_package && <span>{item.python_package}</span>}
{item.webui_package && <span>{item.webui_package}</span>}
{item.tags.length > 0 && <span>{item.tags.join(", ")}</span>}
{item.dependencies.length > 0 && <span>i18n:govoplan-admin.dependencies.9f4f78d1 {item.dependencies.join(", ")}</span>}
{item.migration_safety !== "automatic" && <span>i18n:govoplan-admin.migration_safety.729bdb3c {migrationSafetyLabel(item.migration_safety)}</span>}
{item.migration_after.length > 0 && <span>i18n:govoplan-admin.after.695c89be {item.migration_after.join(", ")}</span>}
{item.migration_before.length > 0 && <span>i18n:govoplan-admin.before.a11cf31f {item.migration_before.join(", ")}</span>}
{item.bridge_release && <span>i18n:govoplan-admin.bridge_release.176cf241</span>}
{(item.current_version_min || item.current_version_max_exclusive) && <span>i18n:govoplan-admin.current_window.73e25c9f {currentWindowLabel(item)}</span>}
{item.recovery_tested && <span>i18n:govoplan-admin.recovery_tested.e831e2f1</span>}
{item.license_features.length > 0 && <span>i18n:govoplan-admin.license.de13bf1a {item.license_features.join(", ")}</span>}
{item.license_missing_features.length > 0 && <span>i18n:govoplan-admin.missing.feb2bbaa {item.license_missing_features.join(", ")}</span>}
{item.provides_interfaces.length > 0 && <span>i18n:govoplan-admin.provides.221b70d9 {providedInterfacesLabel(item)}</span>}
{item.requires_interfaces.length > 0 && <span>i18n:govoplan-admin.requires.a4fc9357 {requiredInterfacesLabel(item)}</span>}
</div>
{item.description && <p className="module-package-catalog-description">{item.description}</p>}
{item.migration_notes && <p className="module-package-catalog-description">{item.migration_notes}</p>}
{item.bridge_notes && <p className="module-package-catalog-description">{item.bridge_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>}
</div>
<Button onClick={() => void addCatalogItem(item)} disabled={!canWrite || planBusy || !packageCatalog.valid || !item.license_allowed || item.action !== "install"}>i18n:govoplan-admin.plan_install.e82bffe6</Button>
<Button onClick={() => void addCatalogItem(item)} disabled={!canWrite || planBusy || !packageCatalog.valid || !item.license_allowed || !["install", "update"].includes(item.action)}>{catalogPlanButtonLabel(item, catalog)}</Button>
</div>
)}
</div>}
@@ -450,6 +463,43 @@ export default function ModuleManagementPanel({ settings, canWrite, canAccessMai
<span>{installPlan.preflight.restart_required ? "i18n:govoplan-admin.restart_reload_required_after_package_changes.3a99cd63" : "i18n:govoplan-admin.no_package_restart_pending.efe6b8ce"}</span>
{installPlan.preflight.frontend_rebuild_required && <span>i18n:govoplan-admin.webui_rebuild_required.010ce49f</span>}
</div>
{installPlan.preflight.target_plan.length > 0 && <div className="module-install-checklist">
<strong>i18n:govoplan-admin.target_plan.524ea0c8</strong>
{installPlan.preflight.target_plan.map((target) =>
<div key={`${target.module_id}-${target.action}`} className={`module-install-checklist-item status-${targetStatus(target)}`}>
<strong>{target.module_id} - {target.action}</strong>
<span>{targetVersionLabel(target)}</span>
<p>{migrationSafetyLabel(target.migration_safety)}{target.data_safety_acknowledged ? " - i18n:govoplan-admin.acknowledged.d08d7c6d" : ""}</p>
{target.bridge_release && <p>i18n:govoplan-admin.bridge_release.176cf241</p>}
{target.recovery_tested && <p>i18n:govoplan-admin.recovery_tested.e831e2f1</p>}
{target.python_ref && <p>{target.python_ref}</p>}
{target.migration_notes && <p>{target.migration_notes}</p>}
{target.bridge_notes && <p>{target.bridge_notes}</p>}
{target.recovery_notes && <p>{target.recovery_notes}</p>}
</div>
)}
</div>}
{(installPlan.preflight.migration_plan.steps.length > 0 || installPlan.preflight.migration_plan.tasks.length > 0) && <div className="module-install-checklist">
<strong>i18n:govoplan-admin.migration_plan.f42b9d90</strong>
<span>i18n:govoplan-admin.enabled_modules.3c38e9ff {installPlan.preflight.migration_plan.enabled_modules.join(", ")}</span>
{installPlan.preflight.migration_plan.steps.map((step, index) =>
<div key={`${step.module_id}-${step.phase}-${index}`} className={`module-install-checklist-item status-${migrationStepStatus(step)}`}>
<strong>{index + 1}. {step.module_id} - {migrationPhaseLabel(step.phase)}</strong>
<span>{migrationSourceLabel(step.source)}{step.metadata_pending ? " - i18n:govoplan-admin.metadata_pending.25190d87" : ""}</span>
<p>{migrationSafetyLabel(step.migration_safety)}</p>
{step.reason && <p>{step.reason}</p>}
</div>
)}
{installPlan.preflight.migration_plan.tasks.length > 0 && <strong>i18n:govoplan-admin.migration_tasks.a4410d3a</strong>}
{installPlan.preflight.migration_plan.tasks.map((task) =>
<div key={`${task.module_id}-${task.task_id}-${task.phase}`} className={`module-install-checklist-item status-${migrationTaskStatus(task)}`}>
<strong>{task.module_id} - {migrationTaskPhaseLabel(task.phase)}</strong>
<span>{task.summary}</span>
<p>{migrationSourceLabel(task.source)}{task.metadata_pending ? " - i18n:govoplan-admin.executor_pending.7633225b" : ""}{task.has_executor ? " - i18n:govoplan-admin.executor_available.507de429" : ""}</p>
<p>{migrationSafetyLabel(task.safety)} - {task.idempotent ? "i18n:govoplan-admin.idempotent.47a0f2d6" : "i18n:govoplan-admin.not_idempotent.d3014c71"} - i18n:govoplan-admin.task_version.f0f26b92 {task.task_version}</p>
</div>
)}
</div>}
{installPlan.preflight.issues.length > 0 && <div className="module-install-preflight-issues">
{installPlan.preflight.issues.map((issue, index) =>
<div key={`${issue.code}-${issue.module_id ?? "global"}-${index}`} className={`module-install-preflight-issue severity-${issue.severity}`}>
@@ -479,10 +529,11 @@ export default function ModuleManagementPanel({ settings, canWrite, canAccessMai
{item.catalog?.sequence !== null && item.catalog?.sequence !== undefined && <StatusBadge status="inactive" label={i18nMessage("i18n:govoplan-admin.seq_value.0ae5fa47", { value0: item.catalog.sequence })} />}
{item.catalog?.key_id && <StatusBadge status="inactive" label={i18nMessage("i18n:govoplan-admin.key_value.847b914f", { value0: String(item.catalog.key_id) })} />}
</div>
<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="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.python_package.34591c71</span><input value={item.python_package ?? ""} disabled={!canWrite || planBusy} onChange={(event) => updatePlanItem(index, { python_package: event.target.value })} placeholder="govoplan-files" /></label>
<ToggleSwitch label="i18n:govoplan-admin.destroy_data.34557c3c" checked={Boolean(item.destroy_data)} onChange={(checked) => updatePlanItem(index, { destroy_data: checked })} disabled={!canWrite || planBusy || item.action !== "uninstall"} />
<ToggleSwitch label="i18n:govoplan-admin.data_safety_reviewed.b4774edc" checked={Boolean(item.data_safety_acknowledged)} onChange={(checked) => updatePlanItem(index, { data_safety_acknowledged: checked })} disabled={!canWrite || planBusy || item.action === "uninstall"} />
<label className="wide"><span>i18n:govoplan-admin.python_ref.1af35d0d</span><input value={item.python_ref ?? ""} disabled={!canWrite || planBusy || item.action === "uninstall"} onChange={(event) => updatePlanItem(index, { python_ref: event.target.value })} placeholder="govoplan-files @ git+ssh://...@v0.1.4" /></label>
<label><span>i18n:govoplan-admin.webui_package.19645536</span><input value={item.webui_package ?? ""} disabled={!canWrite || planBusy} onChange={(event) => updatePlanItem(index, { webui_package: event.target.value })} placeholder="@govoplan/files-webui" /></label>
<label className="wide"><span>i18n:govoplan-admin.webui_ref.cbae3559</span><input value={item.webui_ref ?? ""} disabled={!canWrite || planBusy || item.action === "uninstall"} onChange={(event) => updatePlanItem(index, { webui_ref: event.target.value })} placeholder="git+ssh://...#v0.1.4" /></label>
@@ -644,6 +695,65 @@ function requiredInterfacesLabel(item: ModulePackageCatalogItem): string {
}).join(", ");
}
function migrationSafetyLabel(value: ModulePackageCatalogItem["migration_safety"] | ModuleInstallTargetItem["migration_safety"]): string {
if (value === "requires_review") return "i18n:govoplan-admin.requires_review.8ba0b8c6";
if (value === "forward_only") return "i18n:govoplan-admin.forward_only.6c107a46";
if (value === "destructive") return "i18n:govoplan-admin.destructive.0051026c";
return "i18n:govoplan-admin.automatic.d9a7260f";
}
function targetStatus(target: ModuleInstallTargetItem): "done" | "warning" | "blocked" {
if (target.migration_safety === "destructive" && !target.data_safety_acknowledged) return "blocked";
if (target.migration_safety === "forward_only" && !target.data_safety_acknowledged) return "blocked";
if (target.migration_safety !== "automatic") return "warning";
return "done";
}
function targetVersionLabel(target: ModuleInstallTargetItem): string {
const current = target.current_version ?? "none";
const next = target.action === "uninstall" ? "removed" : target.target_version ?? "unknown";
return `${current} -> ${next}`;
}
function migrationPhaseLabel(value: ModuleMigrationPlanStep["phase"]): string {
if (value === "retirement") return "i18n:govoplan-admin.retirement.6b680dd2";
return "i18n:govoplan-admin.upgrade.12c5007d";
}
function migrationTaskPhaseLabel(value: ModuleMigrationTaskPlanItem["phase"]): string {
if (value === "pre_migration_check") return "i18n:govoplan-admin.pre_migration_check.f654164a";
if (value === "pre_migration_prepare") return "i18n:govoplan-admin.pre_migration_prepare.d18fdd26";
if (value === "post_migration_backfill") return "i18n:govoplan-admin.post_migration_backfill.21043d74";
return "i18n:govoplan-admin.post_migration_verify.6af9f28b";
}
function migrationSourceLabel(value: ModuleMigrationPlanStep["source"] | ModuleMigrationTaskPlanItem["source"]): string {
if (value === "catalog") return "i18n:govoplan-admin.catalog_metadata.9b96c41a";
if (value === "pending") return "i18n:govoplan-admin.pending_metadata.a24cfeb0";
return "i18n:govoplan-admin.manifest_metadata.b9ae362a";
}
function migrationStepStatus(step: ModuleMigrationPlanStep): "done" | "pending" | "warning" | "blocked" {
if (step.metadata_pending) return "warning";
if (step.migration_safety !== "automatic") return "warning";
return "pending";
}
function migrationTaskStatus(task: ModuleMigrationTaskPlanItem): "done" | "pending" | "warning" | "blocked" {
if (!task.idempotent) return "blocked";
if (!task.metadata_pending && !task.has_executor) return "blocked";
if (task.safety === "forward_only" || task.safety === "destructive") return "warning";
if (task.metadata_pending || task.safety === "requires_review") return "warning";
return "pending";
}
function currentWindowLabel(item: Pick<ModulePackageCatalogItem, "current_version_min" | "current_version_max_exclusive">): string {
return [
item.current_version_min ? `>=${item.current_version_min}` : "",
item.current_version_max_exclusive ? `<${item.current_version_max_exclusive}` : "",
].filter(Boolean).join(" ") || "any";
}
function ModuleStatus({ module, desiredEnabled }: {module: ModuleCatalogItem;desiredEnabled: boolean;}) {
if (module.current_enabled && !desiredEnabled) return <StatusBadge status="warning" label="i18n:govoplan-admin.will_disable.14bb193a" />;
if (!module.current_enabled && desiredEnabled) return <StatusBadge status="warning" label="i18n:govoplan-admin.will_enable.5d52d70b" />;
@@ -651,6 +761,12 @@ function ModuleStatus({ module, desiredEnabled }: {module: ModuleCatalogItem;des
return <StatusBadge status="inactive" label="i18n:govoplan-admin.inactive.09af574c" />;
}
function catalogPlanButtonLabel(item: ModulePackageCatalogItem, catalog: ModuleCatalogResponse | null): string {
if (item.action === "update") return "i18n:govoplan-admin.plan_update.86e6857a";
const installed = catalog?.modules.some((module) => module.id === item.module_id && module.installed) ?? false;
return installed ? "i18n:govoplan-admin.plan_update.86e6857a" : "i18n:govoplan-admin.plan_install.e82bffe6";
}
function sameSet(left: ReadonlySet<string>, right: ReadonlySet<string>) {
if (left.size !== right.size) return false;
for (const item of left) {
@@ -669,6 +785,7 @@ function emptyPlanItem(): ModuleInstallPlanItem {
python_ref: "",
webui_package: "",
webui_ref: "",
data_safety_acknowledged: false,
destroy_data: false,
status: "planned",
notes: ""
@@ -683,9 +800,10 @@ function normalizePlanItems(items: ModuleInstallPlanItem[]): ModuleInstallPlanIt
source: item.source === "catalog" ? "catalog" : "manual",
catalog: item.source === "catalog" ? item.catalog ?? null : null,
python_package: cleanOptional(item.python_package),
python_ref: item.action === "install" ? cleanOptional(item.python_ref) : null,
python_ref: item.action !== "uninstall" ? cleanOptional(item.python_ref) : null,
webui_package: cleanOptional(item.webui_package),
webui_ref: item.action === "install" ? cleanOptional(item.webui_ref) : null,
webui_ref: item.action !== "uninstall" ? cleanOptional(item.webui_ref) : null,
data_safety_acknowledged: item.action !== "uninstall" && Boolean(item.data_safety_acknowledged),
destroy_data: item.action === "uninstall" && Boolean(item.destroy_data),
status: item.status,
notes: cleanOptional(item.notes)
@@ -701,10 +819,10 @@ function cleanOptional(value?: string | null): string | null {
function planValidationError(items: ModuleInstallPlanItem[]): string {
for (const item of normalizePlanItems(items)) {
if (!item.module_id) return "i18n:govoplan-admin.every_plan_item_needs_a_module_id.23a7f206";
if (item.action === "install" && !item.python_ref) return i18nMessage("i18n:govoplan-admin.value_needs_a_python_package_reference.950b898e", { value0: item.module_id });
if (item.action === "install" && item.python_ref && !item.python_package) return i18nMessage("i18n:govoplan-admin.value_needs_a_python_package_name_for_rollback.6c89ed75", { value0: item.module_id });
if (item.action !== "uninstall" && !item.python_ref) return i18nMessage("i18n:govoplan-admin.value_needs_a_python_package_reference.950b898e", { value0: item.module_id });
if (item.action !== "uninstall" && item.python_ref && !item.python_package) return i18nMessage("i18n:govoplan-admin.value_needs_a_python_package_name_for_rollback.6c89ed75", { value0: item.module_id });
if (item.action === "uninstall" && !item.python_package) return i18nMessage("i18n:govoplan-admin.value_needs_a_python_package_name_for_uninstall.004ba6fa", { value0: item.module_id });
if (item.action === "install" && Boolean(item.webui_package) !== Boolean(item.webui_ref)) return i18nMessage("i18n:govoplan-admin.value_needs_both_webui_package_and_webui_referen.d2bab073", { value0: item.module_id });
if (item.action !== "uninstall" && Boolean(item.webui_package) !== Boolean(item.webui_ref)) return i18nMessage("i18n:govoplan-admin.value_needs_both_webui_package_and_webui_referen.d2bab073", { value0: item.module_id });
if (item.action === "uninstall" && item.webui_ref && !item.webui_package) return i18nMessage("i18n:govoplan-admin.value_has_a_webui_reference_but_no_webui_package.18f44149", { value0: item.module_id });
}
return "";

View File

@@ -6,13 +6,15 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-admin.action.97c89a4d": "Action",
"i18n:govoplan-admin.actions.c3cd636a": "Actions",
"i18n:govoplan-admin.activate_installs.731d22a1": "Activate installs",
"i18n:govoplan-admin.acknowledged.d08d7c6d": "Acknowledged",
"i18n:govoplan-admin.active_and_total_memberships.c0c20f10": "Active and total memberships.",
"i18n:govoplan-admin.active_tenant_automation_credentials.240c659e": "Active tenant automation credentials.",
"i18n:govoplan-admin.active_tenant.dfadf0aa": "Active tenant:",
"i18n:govoplan-admin.active.a733b809": "Active",
"i18n:govoplan-admin.after.695c89be": "After:",
"i18n:govoplan-admin.add_group_template.b74d8f0f": "Add group template",
"i18n:govoplan-admin.add_plan_item.fa55f028": "Add plan item",
"i18n:govoplan-admin.add_tenant_role.fcd904ea": "Add tenant role",
"i18n:govoplan-admin.add_tenant_role.fcd904ea": "Add role template",
"i18n:govoplan-admin.admin.4e7afebc": "Admin",
"i18n:govoplan-admin.administration.b8be3d12": "Administration",
"i18n:govoplan-admin.platform_administration": "Platform administration",
@@ -34,9 +36,12 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-admin.audit.fa1703dd": "Audit",
"i18n:govoplan-admin.available.7c62a142": "Available",
"i18n:govoplan-admin.blocked.99613c74": "Blocked",
"i18n:govoplan-admin.before.a11cf31f": "Before:",
"i18n:govoplan-admin.bridge_release.176cf241": "Bridge release",
"i18n:govoplan-admin.build_webui.ed5ef1fd": " --build-webui",
"i18n:govoplan-admin.build_webui.fe8ccad7": "Build WebUI",
"i18n:govoplan-admin.bypass_allowed.4e347c27": "Bypass allowed",
"i18n:govoplan-admin.catalog_metadata.9b96c41a": "Catalog metadata",
"i18n:govoplan-admin.bypass_denied.ab987400": "Bypass denied",
"i18n:govoplan-admin.cached_from.d73c5b2a": "· cached from",
"i18n:govoplan-admin.cancel.77dfd213": "Cancel",
@@ -70,10 +75,13 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-admin.create_group_template.72407248": "Create group template",
"i18n:govoplan-admin.create_request_and_apply.da79e153": "Create request and apply",
"i18n:govoplan-admin.create_suspend_and_govern_tenant_spaces.77992a39": "Create, suspend and govern tenant spaces.",
"i18n:govoplan-admin.create_tenant_role.f58db104": "Create tenant role",
"i18n:govoplan-admin.create_tenant_role.f58db104": "Create role template",
"i18n:govoplan-admin.created.0c78dab1": "Created:",
"i18n:govoplan-admin.created.accf40c8": "Created",
"i18n:govoplan-admin.current_window.73e25c9f": "Current window:",
"i18n:govoplan-admin.automatic.d9a7260f": "Automatic",
"i18n:govoplan-admin.daemon_execution.cc0fad8d": "Daemon execution",
"i18n:govoplan-admin.data_safety_reviewed.b4774edc": "Data safety reviewed",
"i18n:govoplan-admin.daemon_offline.314f19a4": "Daemon offline",
"i18n:govoplan-admin.daemon_value.e54f5a31": "Daemon: {value0}",
"i18n:govoplan-admin.db_backup_command.5bded3ac": "DB backup command",
@@ -92,11 +100,13 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-admin.defaults_for_newly_created_tenants.9d0f4ccd": "Defaults for newly created tenants",
"i18n:govoplan-admin.delete_group_template.8745d842": "Delete group template",
"i18n:govoplan-admin.delete_template.399bf72a": "Delete template",
"i18n:govoplan-admin.delete_tenant_role.4efa0813": "Delete tenant role",
"i18n:govoplan-admin.delete_tenant_role.4efa0813": "Delete role template",
"i18n:govoplan-admin.delete_value_removal_is_blocked_while_a_material.d4eba73d": "Delete {value0}? Removal is blocked while a materialized tenant definition still has members or assignments.",
"i18n:govoplan-admin.delete_value.4d18989e": "Delete {value0}",
"i18n:govoplan-admin.dependencies.9f4f78d1": "Dependencies:",
"i18n:govoplan-admin.dependents.072665c4": "Dependents:",
"i18n:govoplan-admin.description.55f8ebc8": "Description",
"i18n:govoplan-admin.destructive.0051026c": "Destructive",
"i18n:govoplan-admin.destroy_data.34557c3c": "Destroy data",
"i18n:govoplan-admin.disabled.f4f4473d": "Disabled",
"i18n:govoplan-admin.discovered_packages.660902de": "Discovered packages",
@@ -105,9 +115,12 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-admin.dry_run_change_request_created_value_enable_main.49826d07": "Dry-run change request created: {value0}. Enable maintenance mode, then apply the request.",
"i18n:govoplan-admin.dry_run.3d14659c": "Dry-run",
"i18n:govoplan-admin.edit_group_template.9bc72d21": "Edit group template",
"i18n:govoplan-admin.edit_tenant_role.c15a260d": "Edit tenant role",
"i18n:govoplan-admin.edit_tenant_role.c15a260d": "Edit role template",
"i18n:govoplan-admin.edit_value.fad75899": "Edit {value0}",
"i18n:govoplan-admin.enable_maintenance_mode.75f98d57": "Enable maintenance mode",
"i18n:govoplan-admin.enabled_modules.3c38e9ff": "Enabled modules:",
"i18n:govoplan-admin.executor_available.507de429": "Executor available",
"i18n:govoplan-admin.executor_pending.7633225b": "Executor pending",
"i18n:govoplan-admin.enabled.df174a3f": "Enabled",
"i18n:govoplan-admin.enabling_maintenance_mode_requires_system_mainte.062d3968": "Enabling maintenance mode requires system:maintenance:access.",
"i18n:govoplan-admin.enabling.2b8e03e6": "Enabling...",
@@ -121,6 +134,7 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-admin.exported_tenant_access_configuration.9122c85c": "Exported tenant access configuration",
"i18n:govoplan-admin.features.5df81ffa": "Features:",
"i18n:govoplan-admin.file_connections.1e362326": "File connections",
"i18n:govoplan-admin.forward_only.6c107a46": "Forward-only",
"i18n:govoplan-admin.finished.4b52fe3f": "Finished:",
"i18n:govoplan-admin.fragment.3f19d616": "Fragment",
"i18n:govoplan-admin.front_office.d9dcfee1": "Front office",
@@ -142,6 +156,7 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-admin.history.90ccd649": "History",
"i18n:govoplan-admin.id.474ae526": "Id",
"i18n:govoplan-admin.import_preflight_approve_apply_and_export_module.29aec929": "Import, preflight, approve, apply and export module-owned configuration packages.",
"i18n:govoplan-admin.idempotent.47a0f2d6": "Idempotent",
"i18n:govoplan-admin.inactive.09af574c": "Inactive",
"i18n:govoplan-admin.inspect_value.9d5d1071": "Inspect {value0}",
"i18n:govoplan-admin.install.fd6c3ebf": "Install",
@@ -169,6 +184,7 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-admin.license.de13bf1a": "License:",
"i18n:govoplan-admin.locked.a798882f": "Locked",
"i18n:govoplan-admin.mail_servers.d627326a": "Mail servers",
"i18n:govoplan-admin.manifest_metadata.b9ae362a": "Manifest metadata",
"i18n:govoplan-admin.maintenance_message.ca62571f": "Maintenance message",
"i18n:govoplan-admin.maintenance_mode_enabled_apply_the_module_state_.dda13e5c": "Maintenance mode enabled. Apply the module-state request when ready.",
"i18n:govoplan-admin.maintenance_mode_is_off_package_changes_should_b.1d262629": "Maintenance mode is off. Package changes should be applied only after enabling maintenance mode.",
@@ -177,6 +193,10 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-admin.maintenance.94de303b": "Maintenance",
"i18n:govoplan-admin.membership_status_groups_and_direct_roles_in_the.ab6c10b6": "Membership status, groups and direct roles in the active tenant.",
"i18n:govoplan-admin.message.68f4145f": "Message",
"i18n:govoplan-admin.metadata_pending.25190d87": "Metadata pending",
"i18n:govoplan-admin.migration_plan.f42b9d90": "Migration plan",
"i18n:govoplan-admin.migration_safety.729bdb3c": "Migration safety:",
"i18n:govoplan-admin.migration_tasks.a4410d3a": "Migration tasks",
"i18n:govoplan-admin.minimal_office_access.af48f49a": "Minimal office access",
"i18n:govoplan-admin.missing_entitlement.0cafa611": "Missing entitlement",
"i18n:govoplan-admin.missing.feb2bbaa": "Missing:",
@@ -204,6 +224,7 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-admin.none.6eef6648": "None",
"i18n:govoplan-admin.not_available.d1a17af1": "Not available",
"i18n:govoplan-admin.not_configured.811931bb": "Not configured",
"i18n:govoplan-admin.not_idempotent.d3014c71": "Not idempotent",
"i18n:govoplan-admin.not_recorded.9925ee3c": "not recorded",
"i18n:govoplan-admin.notes.70440046": "Notes",
"i18n:govoplan-admin.object.2883f191": "Object",
@@ -223,8 +244,14 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-admin.package.7431e3df": "Package",
"i18n:govoplan-admin.packages.0a999012": "Packages",
"i18n:govoplan-admin.pending.c515ec74": " pending",
"i18n:govoplan-admin.pending_metadata.a24cfeb0": "Pending metadata",
"i18n:govoplan-admin.permissions.d06d5557": "Permissions",
"i18n:govoplan-admin.plan_install.e82bffe6": "Plan install",
"i18n:govoplan-admin.plan_update.86e6857a": "Plan update",
"i18n:govoplan-admin.post_migration_backfill.21043d74": "Post-migration backfill",
"i18n:govoplan-admin.post_migration_verify.6af9f28b": "Post-migration verify",
"i18n:govoplan-admin.pre_migration_check.f654164a": "Pre-migration check",
"i18n:govoplan-admin.pre_migration_prepare.d18fdd26": "Pre-migration prepare",
"i18n:govoplan-admin.plan_package_installs_and_removals_here_then_app.4aeb03bf": "Plan package installs and removals here, then apply the rendered commands from an operator shell during maintenance mode.",
"i18n:govoplan-admin.plan_uninstall.e62804ab": "Plan uninstall",
"i18n:govoplan-admin.plan.ae2f98a0": "Plan",
@@ -241,6 +268,7 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-admin.queued_daemon_handoffs_created_from_the_admin_ui.d7ae7914": "Queued daemon handoffs created from the admin UI or CLI.",
"i18n:govoplan-admin.queueing_installer_requests_requires_maintenance.ae81f9ac": "Queueing installer requests requires maintenance access.",
"i18n:govoplan-admin.recent_supervised_installer_records_and_the_curr.29860245": "Recent supervised installer records and the current package-install lock.",
"i18n:govoplan-admin.recovery_tested.e831e2f1": "Recovery tested",
"i18n:govoplan-admin.registered_tenant_spaces.61e17d70": "Registered tenant spaces.",
"i18n:govoplan-admin.reload.cce71553": "Reload",
"i18n:govoplan-admin.remove.e963907d": "Remove",
@@ -253,10 +281,12 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-admin.required.d5793988": "Required:",
"i18n:govoplan-admin.required.eed6bfb4": "Required",
"i18n:govoplan-admin.requires.a4fc9357": "Requires:",
"i18n:govoplan-admin.requires_review.8ba0b8c6": "Review required",
"i18n:govoplan-admin.restart_commands.035997f9": "Restart commands",
"i18n:govoplan-admin.restart_reload_required_after_package_changes.3a99cd63": "Restart/reload required after package changes",
"i18n:govoplan-admin.restrict_authenticated_api_access_to_maintenance.2bc47195": "Restrict authenticated API access to maintenance operators",
"i18n:govoplan-admin.retention.c7199d9e": "Retention",
"i18n:govoplan-admin.retirement.6b680dd2": "Retirement",
"i18n:govoplan-admin.retry_of.3a6bb304": "Retry of:",
"i18n:govoplan-admin.retry.9f5cd8a2": "Retry",
"i18n:govoplan-admin.reusable_encrypted_smtp_imap_profiles_and_mail_p.31f150d1": "Reusable encrypted SMTP/IMAP profiles and mail policy.",
@@ -272,7 +302,7 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-admin.save_plan.842dc280": "Save plan",
"i18n:govoplan-admin.save_settings.913aba9f": "Save settings",
"i18n:govoplan-admin.save_template.0885fab2": "Save template",
"i18n:govoplan-admin.save_tenant_role.8fc0d37d": "Save tenant role",
"i18n:govoplan-admin.save_tenant_role.8fc0d37d": "Save role template",
"i18n:govoplan-admin.save_the_install_plan_before_queueing_a_daemon_r.3ba00691": "Save the install plan before queueing a daemon request.",
"i18n:govoplan-admin.saved.c0ae8f6e": "Saved",
"i18n:govoplan-admin.saving.56a2285c": "Saving…",
@@ -302,6 +332,8 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-admin.system_settings_saved.dac4f17b": "System settings saved.",
"i18n:govoplan-admin.system_wide_governance_and_tenant_local_access_m.cda72499": "System-wide governance and tenant-local access management, separated by scope and enforced by the backend.",
"i18n:govoplan-admin.system.bc0792d8": "System",
"i18n:govoplan-admin.target_plan.524ea0c8": "Target plan",
"i18n:govoplan-admin.task_version.f0f26b92": "Task version",
"i18n:govoplan-admin.target.61ad50a9": "Target",
"i18n:govoplan-admin.template_details.d5d75e4d": "Template details",
"i18n:govoplan-admin.tenant_administration_capabilities.5d265972": "Tenant administration capabilities",
@@ -315,9 +347,9 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-admin.tenant_locale_and_tenant_specific_settings.ac49c83b": "Tenant locale and tenant-specific settings.",
"i18n:govoplan-admin.tenant_memberships_and_inherited_roles.16eda9f6": "Tenant memberships and inherited roles.",
"i18n:govoplan-admin.tenant_permission_bundles_and_system_managed_rol.bb563bea": "Tenant permission bundles and system-managed role copies.",
"i18n:govoplan-admin.tenant_permissions.246294bc": "Tenant permissions",
"i18n:govoplan-admin.tenant_role.6b53115d": "Tenant role",
"i18n:govoplan-admin.tenant_roles.51aca82d": "Tenant roles",
"i18n:govoplan-admin.tenant_permissions.246294bc": "Permissions",
"i18n:govoplan-admin.tenant_role.6b53115d": "Role template",
"i18n:govoplan-admin.tenant_roles.51aca82d": "Role templates",
"i18n:govoplan-admin.tenant_users.cb800b38": "Tenant users",
"i18n:govoplan-admin.tenants.1f7ae776": "Tenants",
"i18n:govoplan-admin.these_settings_are_enforced_by_the_backend_centr.8112ed6f": "These settings are enforced by the backend. Central groups and tenant roles remain available even when local creation is disabled.",
@@ -328,6 +360,8 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-admin.unlocked.b3da7025": "Unlocked",
"i18n:govoplan-admin.unsigned.e91344ea": "Unsigned",
"i18n:govoplan-admin.untrusted.cdc7838a": "Untrusted",
"i18n:govoplan-admin.upgrade.12c5007d": "Upgrade",
"i18n:govoplan-admin.update.503a059f": "Update",
"i18n:govoplan-admin.update_enabled_modules.9b7ae790": "Update enabled modules",
"i18n:govoplan-admin.updated.f2f8570d": "Updated",
"i18n:govoplan-admin.user_file_connector_policy_limits": "File connector policy limits for user-owned spaces.",
@@ -361,13 +395,15 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-admin.action.97c89a4d": "Action",
"i18n:govoplan-admin.actions.c3cd636a": "Aktionen",
"i18n:govoplan-admin.activate_installs.731d22a1": "Installationen aktivieren",
"i18n:govoplan-admin.acknowledged.d08d7c6d": "Bestaetigt",
"i18n:govoplan-admin.active_and_total_memberships.c0c20f10": "Active and total memberships.",
"i18n:govoplan-admin.active_tenant_automation_credentials.240c659e": "Active tenant automation credentials.",
"i18n:govoplan-admin.active_tenant.dfadf0aa": "Active tenant:",
"i18n:govoplan-admin.active.a733b809": "Aktiv",
"i18n:govoplan-admin.after.695c89be": "Nach:",
"i18n:govoplan-admin.add_group_template.b74d8f0f": "Add group template",
"i18n:govoplan-admin.add_plan_item.fa55f028": "Add plan item",
"i18n:govoplan-admin.add_tenant_role.fcd904ea": "Add tenant role",
"i18n:govoplan-admin.add_tenant_role.fcd904ea": "Rollenvorlage hinzufügen",
"i18n:govoplan-admin.admin.4e7afebc": "Administration",
"i18n:govoplan-admin.administration.b8be3d12": "Administration",
"i18n:govoplan-admin.platform_administration": "Plattformadministration",
@@ -389,9 +425,12 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-admin.audit.fa1703dd": "Audit",
"i18n:govoplan-admin.available.7c62a142": "Verfügbar",
"i18n:govoplan-admin.blocked.99613c74": "Blockiert",
"i18n:govoplan-admin.before.a11cf31f": "Vor:",
"i18n:govoplan-admin.bridge_release.176cf241": "Brueckenrelease",
"i18n:govoplan-admin.build_webui.ed5ef1fd": " --build-webui",
"i18n:govoplan-admin.build_webui.fe8ccad7": "WebUI bauen",
"i18n:govoplan-admin.bypass_allowed.4e347c27": "Bypass allowed",
"i18n:govoplan-admin.catalog_metadata.9b96c41a": "Katalogmetadaten",
"i18n:govoplan-admin.bypass_denied.ab987400": "Bypass denied",
"i18n:govoplan-admin.cached_from.d73c5b2a": "· cached from",
"i18n:govoplan-admin.cancel.77dfd213": "Abbrechen",
@@ -425,10 +464,13 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-admin.create_group_template.72407248": "Create group template",
"i18n:govoplan-admin.create_request_and_apply.da79e153": "Create request and apply",
"i18n:govoplan-admin.create_suspend_and_govern_tenant_spaces.77992a39": "Create, suspend and govern tenant spaces.",
"i18n:govoplan-admin.create_tenant_role.f58db104": "Create tenant role",
"i18n:govoplan-admin.create_tenant_role.f58db104": "Rollenvorlage erstellen",
"i18n:govoplan-admin.created.0c78dab1": "Created:",
"i18n:govoplan-admin.created.accf40c8": "Erstellt",
"i18n:govoplan-admin.current_window.73e25c9f": "Aktuelles Fenster:",
"i18n:govoplan-admin.automatic.d9a7260f": "Automatisch",
"i18n:govoplan-admin.daemon_execution.cc0fad8d": "Daemon-Ausführung",
"i18n:govoplan-admin.data_safety_reviewed.b4774edc": "Datensicherheit geprueft",
"i18n:govoplan-admin.daemon_offline.314f19a4": "Daemon offline",
"i18n:govoplan-admin.daemon_value.e54f5a31": "Daemon: {value0}",
"i18n:govoplan-admin.db_backup_command.5bded3ac": "DB-Backup-Befehl",
@@ -447,11 +489,13 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-admin.defaults_for_newly_created_tenants.9d0f4ccd": "Standards für neu erstellte Mandanten",
"i18n:govoplan-admin.delete_group_template.8745d842": "Delete group template",
"i18n:govoplan-admin.delete_template.399bf72a": "Delete template",
"i18n:govoplan-admin.delete_tenant_role.4efa0813": "Delete tenant role",
"i18n:govoplan-admin.delete_tenant_role.4efa0813": "Rollenvorlage löschen",
"i18n:govoplan-admin.delete_value_removal_is_blocked_while_a_material.d4eba73d": "Delete {value0}? Removal is blocked while a materialized tenant definition still has members or assignments.",
"i18n:govoplan-admin.delete_value.4d18989e": "Delete {value0}",
"i18n:govoplan-admin.dependencies.9f4f78d1": "Abhaengigkeiten:",
"i18n:govoplan-admin.dependents.072665c4": "Dependents:",
"i18n:govoplan-admin.description.55f8ebc8": "Beschreibung",
"i18n:govoplan-admin.destructive.0051026c": "Destruktiv",
"i18n:govoplan-admin.destroy_data.34557c3c": "Daten zerstören",
"i18n:govoplan-admin.disabled.f4f4473d": "Disabled",
"i18n:govoplan-admin.discovered_packages.660902de": "Discovered packages",
@@ -460,9 +504,12 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-admin.dry_run_change_request_created_value_enable_main.49826d07": "Dry-run change request created: {value0}. Enable maintenance mode, then apply the request.",
"i18n:govoplan-admin.dry_run.3d14659c": "Dry-run",
"i18n:govoplan-admin.edit_group_template.9bc72d21": "Edit group template",
"i18n:govoplan-admin.edit_tenant_role.c15a260d": "Edit tenant role",
"i18n:govoplan-admin.edit_tenant_role.c15a260d": "Rollenvorlage bearbeiten",
"i18n:govoplan-admin.edit_value.fad75899": "Edit {value0}",
"i18n:govoplan-admin.enable_maintenance_mode.75f98d57": "Enable maintenance mode",
"i18n:govoplan-admin.enabled_modules.3c38e9ff": "Aktive Module:",
"i18n:govoplan-admin.executor_available.507de429": "Ausfuehrer verfuegbar",
"i18n:govoplan-admin.executor_pending.7633225b": "Ausfuehrer ausstehend",
"i18n:govoplan-admin.enabled.df174a3f": "Aktiviert",
"i18n:govoplan-admin.enabling_maintenance_mode_requires_system_mainte.062d3968": "Enabling maintenance mode requires system:maintenance:access.",
"i18n:govoplan-admin.enabling.2b8e03e6": "Enabling...",
@@ -476,6 +523,7 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-admin.exported_tenant_access_configuration.9122c85c": "Exported tenant access configuration",
"i18n:govoplan-admin.features.5df81ffa": "Features:",
"i18n:govoplan-admin.file_connections.1e362326": "Dateiverbindungen",
"i18n:govoplan-admin.forward_only.6c107a46": "Nur vorwaerts",
"i18n:govoplan-admin.finished.4b52fe3f": "Finished:",
"i18n:govoplan-admin.fragment.3f19d616": "Fragment",
"i18n:govoplan-admin.front_office.d9dcfee1": "Front office",
@@ -497,6 +545,7 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-admin.history.90ccd649": "History",
"i18n:govoplan-admin.id.474ae526": "Id",
"i18n:govoplan-admin.import_preflight_approve_apply_and_export_module.29aec929": "Modul-eigene Konfigurationspakete importieren, prüfen, freigeben, anwenden und exportieren.",
"i18n:govoplan-admin.idempotent.47a0f2d6": "Idempotent",
"i18n:govoplan-admin.inactive.09af574c": "Inaktiv",
"i18n:govoplan-admin.inspect_value.9d5d1071": "Inspect {value0}",
"i18n:govoplan-admin.install.fd6c3ebf": "Installieren",
@@ -524,6 +573,7 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-admin.license.de13bf1a": "License:",
"i18n:govoplan-admin.locked.a798882f": "Gesperrt",
"i18n:govoplan-admin.mail_servers.d627326a": "Mail servers",
"i18n:govoplan-admin.manifest_metadata.b9ae362a": "Manifestmetadaten",
"i18n:govoplan-admin.maintenance_message.ca62571f": "Wartungsmeldung",
"i18n:govoplan-admin.maintenance_mode_enabled_apply_the_module_state_.dda13e5c": "Maintenance mode enabled. Apply the module-state request when ready.",
"i18n:govoplan-admin.maintenance_mode_is_off_package_changes_should_b.1d262629": "Maintenance mode is off. Package changes should be applied only after enabling maintenance mode.",
@@ -532,6 +582,10 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-admin.maintenance.94de303b": "Wartung",
"i18n:govoplan-admin.membership_status_groups_and_direct_roles_in_the.ab6c10b6": "Membership status, groups and direct roles in the active tenant.",
"i18n:govoplan-admin.message.68f4145f": "Nachricht",
"i18n:govoplan-admin.metadata_pending.25190d87": "Metadaten ausstehend",
"i18n:govoplan-admin.migration_plan.f42b9d90": "Migrationsplan",
"i18n:govoplan-admin.migration_safety.729bdb3c": "Migrationssicherheit:",
"i18n:govoplan-admin.migration_tasks.a4410d3a": "Migrationsaufgaben",
"i18n:govoplan-admin.minimal_office_access.af48f49a": "Minimal office access",
"i18n:govoplan-admin.missing_entitlement.0cafa611": "Missing entitlement",
"i18n:govoplan-admin.missing.feb2bbaa": "Missing:",
@@ -559,6 +613,7 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-admin.none.6eef6648": "Keine",
"i18n:govoplan-admin.not_available.d1a17af1": "Not available",
"i18n:govoplan-admin.not_configured.811931bb": "Nicht konfiguriert",
"i18n:govoplan-admin.not_idempotent.d3014c71": "Nicht idempotent",
"i18n:govoplan-admin.not_recorded.9925ee3c": "not recorded",
"i18n:govoplan-admin.notes.70440046": "Notes",
"i18n:govoplan-admin.object.2883f191": "Object",
@@ -578,8 +633,14 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-admin.package.7431e3df": "Package",
"i18n:govoplan-admin.packages.0a999012": "Pakete",
"i18n:govoplan-admin.pending.c515ec74": " pending",
"i18n:govoplan-admin.pending_metadata.a24cfeb0": "Ausstehende Metadaten",
"i18n:govoplan-admin.permissions.d06d5557": "Berechtigungen",
"i18n:govoplan-admin.plan_install.e82bffe6": "Plan install",
"i18n:govoplan-admin.plan_update.86e6857a": "Aktualisierung planen",
"i18n:govoplan-admin.post_migration_backfill.21043d74": "Nach-Migration Backfill",
"i18n:govoplan-admin.post_migration_verify.6af9f28b": "Nach-Migration Pruefung",
"i18n:govoplan-admin.pre_migration_check.f654164a": "Vor-Migration Check",
"i18n:govoplan-admin.pre_migration_prepare.d18fdd26": "Vor-Migration Vorbereitung",
"i18n:govoplan-admin.plan_package_installs_and_removals_here_then_app.4aeb03bf": "Plan package installs and removals here, then apply the rendered commands from an operator shell during maintenance mode.",
"i18n:govoplan-admin.plan_uninstall.e62804ab": "Plan uninstall",
"i18n:govoplan-admin.plan.ae2f98a0": "Plan",
@@ -596,6 +657,7 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-admin.queued_daemon_handoffs_created_from_the_admin_ui.d7ae7914": "Queued daemon handoffs created from the admin UI or CLI.",
"i18n:govoplan-admin.queueing_installer_requests_requires_maintenance.ae81f9ac": "Das Einreihen von Installer-Anfragen erfordert Wartungszugriff.",
"i18n:govoplan-admin.recent_supervised_installer_records_and_the_curr.29860245": "Aktuelle überwachte Installer-Datensätze und die aktuelle Paketinstallationssperre.",
"i18n:govoplan-admin.recovery_tested.e831e2f1": "Recovery getestet",
"i18n:govoplan-admin.registered_tenant_spaces.61e17d70": "Registered tenant spaces.",
"i18n:govoplan-admin.reload.cce71553": "Neu laden",
"i18n:govoplan-admin.remove.e963907d": "Entfernen",
@@ -608,10 +670,12 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-admin.required.d5793988": "Required:",
"i18n:govoplan-admin.required.eed6bfb4": "Required",
"i18n:govoplan-admin.requires.a4fc9357": "Requires:",
"i18n:govoplan-admin.requires_review.8ba0b8c6": "Pruefung erforderlich",
"i18n:govoplan-admin.restart_commands.035997f9": "Neustartbefehle",
"i18n:govoplan-admin.restart_reload_required_after_package_changes.3a99cd63": "Restart/reload required after package changes",
"i18n:govoplan-admin.restrict_authenticated_api_access_to_maintenance.2bc47195": "Authentifizierten API-Zugriff auf Wartungsoperatoren beschränken",
"i18n:govoplan-admin.retention.c7199d9e": "Retention",
"i18n:govoplan-admin.retirement.6b680dd2": "Stilllegung",
"i18n:govoplan-admin.retry_of.3a6bb304": "Retry of:",
"i18n:govoplan-admin.retry.9f5cd8a2": "Erneut versuchen",
"i18n:govoplan-admin.reusable_encrypted_smtp_imap_profiles_and_mail_p.31f150d1": "Reusable encrypted SMTP/IMAP profiles and mail policy.",
@@ -627,7 +691,7 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-admin.save_plan.842dc280": "Save plan",
"i18n:govoplan-admin.save_settings.913aba9f": "Einstellungen speichern",
"i18n:govoplan-admin.save_template.0885fab2": "Save template",
"i18n:govoplan-admin.save_tenant_role.8fc0d37d": "Save tenant role",
"i18n:govoplan-admin.save_tenant_role.8fc0d37d": "Rollenvorlage speichern",
"i18n:govoplan-admin.save_the_install_plan_before_queueing_a_daemon_r.3ba00691": "Speichern Sie den Installationsplan, bevor eine Daemon-Anfrage eingereiht wird.",
"i18n:govoplan-admin.saved.c0ae8f6e": "Gespeichert",
"i18n:govoplan-admin.saving.56a2285c": "Saving…",
@@ -657,6 +721,8 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-admin.system_settings_saved.dac4f17b": "Systemeinstellungen gespeichert.",
"i18n:govoplan-admin.system_wide_governance_and_tenant_local_access_m.cda72499": "System-wide governance and tenant-local access management, separated by scope and enforced by the backend.",
"i18n:govoplan-admin.system.bc0792d8": "System",
"i18n:govoplan-admin.target_plan.524ea0c8": "Zielplan",
"i18n:govoplan-admin.task_version.f0f26b92": "Aufgabenversion",
"i18n:govoplan-admin.target.61ad50a9": "Target",
"i18n:govoplan-admin.template_details.d5d75e4d": "Template details",
"i18n:govoplan-admin.tenant_administration_capabilities.5d265972": "Tenant administration capabilities",
@@ -670,9 +736,9 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-admin.tenant_locale_and_tenant_specific_settings.ac49c83b": "Tenant locale and tenant-specific settings.",
"i18n:govoplan-admin.tenant_memberships_and_inherited_roles.16eda9f6": "Tenant memberships and inherited roles.",
"i18n:govoplan-admin.tenant_permission_bundles_and_system_managed_rol.bb563bea": "Tenant permission bundles and system-managed role copies.",
"i18n:govoplan-admin.tenant_permissions.246294bc": "Tenant permissions",
"i18n:govoplan-admin.tenant_role.6b53115d": "Tenant role",
"i18n:govoplan-admin.tenant_roles.51aca82d": "Mandantenrollen",
"i18n:govoplan-admin.tenant_permissions.246294bc": "Berechtigungen",
"i18n:govoplan-admin.tenant_role.6b53115d": "Rollenvorlage",
"i18n:govoplan-admin.tenant_roles.51aca82d": "Rollenvorlagen",
"i18n:govoplan-admin.tenant_users.cb800b38": "Mandantenbenutzer",
"i18n:govoplan-admin.tenants.1f7ae776": "Mandanten",
"i18n:govoplan-admin.these_settings_are_enforced_by_the_backend_centr.8112ed6f": "Diese Einstellungen werden vom Backend erzwungen. Zentrale Gruppen und Mandantenrollen bleiben verfügbar, auch wenn lokale Erstellung deaktiviert ist.",
@@ -683,6 +749,8 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-admin.unlocked.b3da7025": "Entsperrt",
"i18n:govoplan-admin.unsigned.e91344ea": "Unsigniert",
"i18n:govoplan-admin.untrusted.cdc7838a": "Nicht vertrauenswürdig",
"i18n:govoplan-admin.upgrade.12c5007d": "Upgrade",
"i18n:govoplan-admin.update.503a059f": "Aktualisieren",
"i18n:govoplan-admin.update_enabled_modules.9b7ae790": "Update enabled modules",
"i18n:govoplan-admin.updated.f2f8570d": "Aktualisiert",
"i18n:govoplan-admin.user_file_connector_policy_limits": "Dateiverbindungsrichtlinien fuer benutzereigene Bereiche.",

View File

@@ -54,7 +54,7 @@ const adminSections: AdminSectionsUiCapability = {
},
{
id: "system-configuration-packages",
label: "i18n:govoplan-admin.packages.0a999012",
label: "i18n:govoplan-admin.configuration_packages.eb2f05f1",
group: "SYSTEM",
order: 30,
allOf: ["system:settings:read"],