feat: manage system appearance defaults
This commit is contained in:
@@ -15,6 +15,7 @@ from govoplan_core.audit.logging import audit_from_principal, audit_operation_co
|
|||||||
from govoplan_admin.backend.db.models import GovernanceTemplate, GovernanceTemplateAssignment
|
from govoplan_admin.backend.db.models import GovernanceTemplate, GovernanceTemplateAssignment
|
||||||
from govoplan_core.admin.common import AdminConflictError, AdminValidationError
|
from govoplan_core.admin.common import AdminConflictError, AdminValidationError
|
||||||
from govoplan_core.core.access import CAPABILITY_ACCESS_ADMINISTRATION, AccessAdministration
|
from govoplan_core.core.access import CAPABILITY_ACCESS_ADMINISTRATION, AccessAdministration
|
||||||
|
from govoplan_core.core.appearance import appearance_settings, update_appearance_settings
|
||||||
from govoplan_core.core.change_sequence import (
|
from govoplan_core.core.change_sequence import (
|
||||||
decode_sequence_watermark,
|
decode_sequence_watermark,
|
||||||
encode_sequence_watermark,
|
encode_sequence_watermark,
|
||||||
@@ -159,6 +160,7 @@ SYSTEM_SETTINGS_SECTIONS = (
|
|||||||
"privacy_retention_policy",
|
"privacy_retention_policy",
|
||||||
"maintenance_mode",
|
"maintenance_mode",
|
||||||
"navigation",
|
"navigation",
|
||||||
|
"appearance",
|
||||||
"settings",
|
"settings",
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -295,6 +297,7 @@ def _system_settings_item(session: Session) -> SystemSettingsItem:
|
|||||||
maintenance_mode = saved_maintenance_mode(session)
|
maintenance_mode = saved_maintenance_mode(session)
|
||||||
i18n_payload = system_i18n_payload(item)
|
i18n_payload = system_i18n_payload(item)
|
||||||
navigation = navigation_preferences_from_settings(item.settings)
|
navigation = navigation_preferences_from_settings(item.settings)
|
||||||
|
appearance_palette, appearance_palette_locked = appearance_settings(item.settings)
|
||||||
return SystemSettingsItem(
|
return SystemSettingsItem(
|
||||||
default_locale=item.default_locale,
|
default_locale=item.default_locale,
|
||||||
allow_tenant_custom_groups=item.allow_tenant_custom_groups,
|
allow_tenant_custom_groups=item.allow_tenant_custom_groups,
|
||||||
@@ -306,6 +309,8 @@ def _system_settings_item(session: Session) -> SystemSettingsItem:
|
|||||||
enabled_language_codes=i18n_payload["enabled_languages"],
|
enabled_language_codes=i18n_payload["enabled_languages"],
|
||||||
settings=item.settings or {},
|
settings=item.settings or {},
|
||||||
navigation=navigation.as_dict() if navigation is not None else None,
|
navigation=navigation.as_dict() if navigation is not None else None,
|
||||||
|
appearance_palette=appearance_palette or "default",
|
||||||
|
appearance_palette_locked=appearance_palette_locked,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -325,6 +330,10 @@ def _system_settings_sections(item: SystemSettingsItem) -> dict[str, Any]:
|
|||||||
"privacy_retention_policy": payload["privacy_retention_policy"],
|
"privacy_retention_policy": payload["privacy_retention_policy"],
|
||||||
"maintenance_mode": payload["maintenance_mode"],
|
"maintenance_mode": payload["maintenance_mode"],
|
||||||
"navigation": payload["navigation"],
|
"navigation": payload["navigation"],
|
||||||
|
"appearance": {
|
||||||
|
"appearance_palette": payload["appearance_palette"],
|
||||||
|
"appearance_palette_locked": payload["appearance_palette_locked"],
|
||||||
|
},
|
||||||
"settings": payload["settings"],
|
"settings": payload["settings"],
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1783,6 +1792,23 @@ def write_system_settings(
|
|||||||
before_sections = _system_settings_sections(_system_settings_item(session))
|
before_sections = _system_settings_sections(_system_settings_item(session))
|
||||||
before_privacy = privacy_policy_from_settings(item).model_dump(mode="json")
|
before_privacy = privacy_policy_from_settings(item).model_dump(mode="json")
|
||||||
before_maintenance = saved_maintenance_mode(session).as_dict()
|
before_maintenance = saved_maintenance_mode(session).as_dict()
|
||||||
|
before_appearance_palette, before_appearance_locked = appearance_settings(item.settings)
|
||||||
|
appearance_palette_changed = (
|
||||||
|
"appearance_palette" in payload.model_fields_set
|
||||||
|
and payload.appearance_palette is not None
|
||||||
|
and payload.appearance_palette != (before_appearance_palette or "default")
|
||||||
|
)
|
||||||
|
if (
|
||||||
|
(
|
||||||
|
"appearance_palette_locked" in payload.model_fields_set
|
||||||
|
and payload.appearance_palette_locked != before_appearance_locked
|
||||||
|
)
|
||||||
|
or (before_appearance_locked and appearance_palette_changed)
|
||||||
|
) and not has_scope(principal, "admin:policies:write"):
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
|
||||||
|
detail="Changing the system appearance lock or its locked value requires admin:policies:write.",
|
||||||
|
)
|
||||||
if payload.privacy_retention_policy is not None:
|
if payload.privacy_retention_policy is not None:
|
||||||
privacy_value = payload.privacy_retention_policy.model_dump(mode="json")
|
privacy_value = payload.privacy_retention_policy.model_dump(mode="json")
|
||||||
try:
|
try:
|
||||||
@@ -1819,6 +1845,13 @@ def write_system_settings(
|
|||||||
available_languages=available_languages,
|
available_languages=available_languages,
|
||||||
enabled_language_codes=enabled_language_codes,
|
enabled_language_codes=enabled_language_codes,
|
||||||
)
|
)
|
||||||
|
if {"appearance_palette", "appearance_palette_locked"}.intersection(payload.model_fields_set):
|
||||||
|
current_palette, current_locked = appearance_settings(item.settings)
|
||||||
|
item.settings = update_appearance_settings(
|
||||||
|
item.settings,
|
||||||
|
default_palette=payload.appearance_palette or current_palette or "default",
|
||||||
|
palette_locked=payload.appearance_palette_locked if payload.appearance_palette_locked is not None else current_locked,
|
||||||
|
)
|
||||||
if "navigation" in payload.model_fields_set:
|
if "navigation" in payload.model_fields_set:
|
||||||
item.settings = update_navigation_preferences(
|
item.settings = update_navigation_preferences(
|
||||||
item.settings,
|
item.settings,
|
||||||
|
|||||||
@@ -51,6 +51,8 @@ class SystemSettingsItem(BaseModel):
|
|||||||
enabled_language_codes: list[str] = Field(default_factory=list)
|
enabled_language_codes: list[str] = Field(default_factory=list)
|
||||||
settings: dict[str, Any] = Field(default_factory=dict)
|
settings: dict[str, Any] = Field(default_factory=dict)
|
||||||
navigation: NavigationPreferencesPayload | None = None
|
navigation: NavigationPreferencesPayload | None = None
|
||||||
|
appearance_palette: Literal["default", "civic_blue", "forest", "plum"] = "default"
|
||||||
|
appearance_palette_locked: bool = False
|
||||||
|
|
||||||
|
|
||||||
class SystemSettingsDeltaResponse(BaseModel):
|
class SystemSettingsDeltaResponse(BaseModel):
|
||||||
@@ -75,6 +77,8 @@ class SystemSettingsUpdateRequest(BaseModel):
|
|||||||
available_languages: list[LanguagePackageItem] | None = None
|
available_languages: list[LanguagePackageItem] | None = None
|
||||||
enabled_language_codes: list[str] | None = None
|
enabled_language_codes: list[str] | None = None
|
||||||
navigation: NavigationPreferencesPayload | None = None
|
navigation: NavigationPreferencesPayload | None = None
|
||||||
|
appearance_palette: Literal["default", "civic_blue", "forest", "plum"] | None = None
|
||||||
|
appearance_palette_locked: bool | None = None
|
||||||
change_request_id: str | None = None
|
change_request_id: str | None = None
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ manifest = ModuleManifest(
|
|||||||
id="admin.workspace",
|
id="admin.workspace",
|
||||||
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. System appearance settings select a validated palette default. Changing the separate palette lock additionally requires policy-write authority; a system lock suppresses tenant and personal palette choices, while an unlocked default remains inheritable and overridable.",
|
||||||
documentation_types=("admin", "user"),
|
documentation_types=("admin", "user"),
|
||||||
audience=("tenant_admin", "system_admin", "operator"),
|
audience=("tenant_admin", "system_admin", "operator"),
|
||||||
metadata={
|
metadata={
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { ApiSettings, DeltaDeletedItem, NavigationPreferences, PrivacyRetentionPolicy } from "@govoplan/core-webui";
|
import type { ApiSettings, DeltaDeletedItem, NavigationPreferences, PrivacyRetentionPolicy, UserUiPalette } from "@govoplan/core-webui";
|
||||||
import { apiDownload, apiFetch, apiPath, apiQuery } from "@govoplan/core-webui";
|
import { apiDownload, apiFetch, apiPath, apiQuery } from "@govoplan/core-webui";
|
||||||
export { fetchAdminOverview, fetchPermissionCatalog, fetchTenants } from "@govoplan/core-webui";
|
export { fetchAdminOverview, fetchPermissionCatalog, fetchTenants } from "@govoplan/core-webui";
|
||||||
export type {
|
export type {
|
||||||
@@ -34,6 +34,8 @@ export type SystemSettingsItem = {
|
|||||||
enabled_language_codes: string[];
|
enabled_language_codes: string[];
|
||||||
settings: Record<string, unknown>;
|
settings: Record<string, unknown>;
|
||||||
navigation: NavigationPreferences | null;
|
navigation: NavigationPreferences | null;
|
||||||
|
appearance_palette: UserUiPalette;
|
||||||
|
appearance_palette_locked: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SystemSettingsDeltaSections = Partial<{
|
export type SystemSettingsDeltaSections = Partial<{
|
||||||
@@ -44,6 +46,7 @@ export type SystemSettingsDeltaSections = Partial<{
|
|||||||
maintenance_mode: SystemSettingsItem["maintenance_mode"];
|
maintenance_mode: SystemSettingsItem["maintenance_mode"];
|
||||||
settings: SystemSettingsItem["settings"];
|
settings: SystemSettingsItem["settings"];
|
||||||
navigation: SystemSettingsItem["navigation"];
|
navigation: SystemSettingsItem["navigation"];
|
||||||
|
appearance: Pick<SystemSettingsItem, "appearance_palette" | "appearance_palette_locked">;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
export type SystemSettingsUpdatePayload = {
|
export type SystemSettingsUpdatePayload = {
|
||||||
@@ -56,6 +59,8 @@ export type SystemSettingsUpdatePayload = {
|
|||||||
available_languages?: LanguagePackage[] | null;
|
available_languages?: LanguagePackage[] | null;
|
||||||
enabled_language_codes?: string[] | null;
|
enabled_language_codes?: string[] | null;
|
||||||
navigation?: NavigationPreferences | null;
|
navigation?: NavigationPreferences | null;
|
||||||
|
appearance_palette?: UserUiPalette;
|
||||||
|
appearance_palette_locked?: boolean;
|
||||||
change_request_id?: string | null;
|
change_request_id?: string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { Button } from "@govoplan/core-webui";
|
|||||||
import { Card } from "@govoplan/core-webui";
|
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 { AppearancePalettePreview, AppearancePaletteSelect } from "@govoplan/core-webui";
|
||||||
import { fetchSystemSettingsDelta, updateSystemSettings, type SystemSettingsItem } from "../../api/admin";
|
import { fetchSystemSettingsDelta, updateSystemSettings, type SystemSettingsItem } from "../../api/admin";
|
||||||
import { AdminPageLayout, DocumentationHelpLink, NavigationPreferenceEditor, adminErrorMessage, configurableNavigationItemsForModules, dispatchPlatformModulesChanged, useDeltaWatermarks, usePlatformModules, useUnsavedDraftGuard } from "@govoplan/core-webui";
|
import { AdminPageLayout, DocumentationHelpLink, NavigationPreferenceEditor, adminErrorMessage, configurableNavigationItemsForModules, dispatchPlatformModulesChanged, useDeltaWatermarks, usePlatformModules, useUnsavedDraftGuard } from "@govoplan/core-webui";
|
||||||
import { ADMIN_GOVERNANCE_DOCUMENTATION, ADMIN_INTERFACE_I18N, mutationDisabledReason } from "./interfacePatterns";
|
import { ADMIN_GOVERNANCE_DOCUMENTATION, ADMIN_INTERFACE_I18N, mutationDisabledReason } from "./interfacePatterns";
|
||||||
@@ -11,7 +12,7 @@ import { SYSTEM_SETTINGS_FALLBACK, applySystemSettingsSections } from "./systemS
|
|||||||
|
|
||||||
const DELTA_KEY = "admin:system-settings";
|
const DELTA_KEY = "admin:system-settings";
|
||||||
|
|
||||||
export default function SystemSettingsPanel({ settings, canWrite, canAccessMaintenance }: {settings: ApiSettings;canWrite: boolean;canAccessMaintenance: boolean;}) {
|
export default function SystemSettingsPanel({ settings, canWrite, canAccessMaintenance, canWritePolicy }: {settings: ApiSettings;canWrite: boolean;canAccessMaintenance: boolean;canWritePolicy: boolean;}) {
|
||||||
const modules = usePlatformModules();
|
const modules = usePlatformModules();
|
||||||
const navigationItems = configurableNavigationItemsForModules(modules);
|
const navigationItems = configurableNavigationItemsForModules(modules);
|
||||||
const [draft, setDraft] = useState<SystemSettingsItem>(SYSTEM_SETTINGS_FALLBACK);
|
const [draft, setDraft] = useState<SystemSettingsItem>(SYSTEM_SETTINGS_FALLBACK);
|
||||||
@@ -63,7 +64,9 @@ export default function SystemSettingsPanel({ settings, canWrite, canAccessMaint
|
|||||||
allow_tenant_custom_roles: draft.allow_tenant_custom_roles,
|
allow_tenant_custom_roles: draft.allow_tenant_custom_roles,
|
||||||
allow_tenant_api_keys: draft.allow_tenant_api_keys,
|
allow_tenant_api_keys: draft.allow_tenant_api_keys,
|
||||||
maintenance_mode: draft.maintenance_mode,
|
maintenance_mode: draft.maintenance_mode,
|
||||||
navigation: draft.navigation
|
navigation: draft.navigation,
|
||||||
|
appearance_palette: draft.appearance_palette,
|
||||||
|
appearance_palette_locked: draft.appearance_palette_locked
|
||||||
});
|
});
|
||||||
setDraft(saved);
|
setDraft(saved);
|
||||||
setSavedDraft(saved);
|
setSavedDraft(saved);
|
||||||
@@ -102,6 +105,24 @@ export default function SystemSettingsPanel({ settings, canWrite, canAccessMaint
|
|||||||
disabled={!canWrite || busy}
|
disabled={!canWrite || busy}
|
||||||
/>
|
/>
|
||||||
</Card>
|
</Card>
|
||||||
|
<Card title="i18n:govoplan-admin.appearance_defaults">
|
||||||
|
<FormField label="i18n:govoplan-admin.system_palette_default" help="i18n:govoplan-admin.system_palette_default_help">
|
||||||
|
<AppearancePaletteSelect
|
||||||
|
value={draft.appearance_palette}
|
||||||
|
onChange={(appearance_palette) => setDraft({ ...draft, appearance_palette: appearance_palette ?? "default" })}
|
||||||
|
disabled={!canWrite || busy || (draft.appearance_palette_locked && !canWritePolicy)}
|
||||||
|
/>
|
||||||
|
</FormField>
|
||||||
|
<ToggleSwitch
|
||||||
|
checked={draft.appearance_palette_locked}
|
||||||
|
onChange={(appearance_palette_locked) => setDraft({ ...draft, appearance_palette_locked })}
|
||||||
|
disabled={!canWrite || !canWritePolicy || busy}
|
||||||
|
help={!canWritePolicy ? "i18n:govoplan-admin.appearance_lock_policy_permission" : undefined}
|
||||||
|
label="i18n:govoplan-admin.lock_system_palette"
|
||||||
|
/>
|
||||||
|
<AppearancePalettePreview palette={draft.appearance_palette} />
|
||||||
|
<p className="muted small-note">i18n:govoplan-admin.system_palette_precedence_help</p>
|
||||||
|
</Card>
|
||||||
<Card title="i18n:govoplan-admin.maintenance_mode.98cca5c6">
|
<Card title="i18n:govoplan-admin.maintenance_mode.98cca5c6">
|
||||||
<div className="settings-list">
|
<div className="settings-list">
|
||||||
<ToggleSwitch
|
<ToggleSwitch
|
||||||
@@ -133,6 +154,8 @@ function systemSettingsDraftKey(item: SystemSettingsItem): string {
|
|||||||
allow_tenant_custom_roles: item.allow_tenant_custom_roles,
|
allow_tenant_custom_roles: item.allow_tenant_custom_roles,
|
||||||
allow_tenant_api_keys: item.allow_tenant_api_keys,
|
allow_tenant_api_keys: item.allow_tenant_api_keys,
|
||||||
maintenance_mode: item.maintenance_mode,
|
maintenance_mode: item.maintenance_mode,
|
||||||
navigation: item.navigation
|
navigation: item.navigation,
|
||||||
|
appearance_palette: item.appearance_palette,
|
||||||
|
appearance_palette_locked: item.appearance_palette_locked
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,9 @@ export const SYSTEM_SETTINGS_FALLBACK: SystemSettingsItem = {
|
|||||||
],
|
],
|
||||||
enabled_language_codes: ["de", "en"],
|
enabled_language_codes: ["de", "en"],
|
||||||
settings: {},
|
settings: {},
|
||||||
navigation: null
|
navigation: null,
|
||||||
|
appearance_palette: "default",
|
||||||
|
appearance_palette_locked: false
|
||||||
};
|
};
|
||||||
|
|
||||||
export function applySystemSettingsSections(
|
export function applySystemSettingsSections(
|
||||||
@@ -56,6 +58,7 @@ export function applySystemSettingsSections(
|
|||||||
: {}),
|
: {}),
|
||||||
...(sections.maintenance_mode ? { maintenance_mode: sections.maintenance_mode } : {}),
|
...(sections.maintenance_mode ? { maintenance_mode: sections.maintenance_mode } : {}),
|
||||||
...(sections.navigation !== undefined ? { navigation: sections.navigation } : {}),
|
...(sections.navigation !== undefined ? { navigation: sections.navigation } : {}),
|
||||||
|
...(sections.appearance ?? {}),
|
||||||
...(sections.settings ? { settings: sections.settings } : {})
|
...(sections.settings ? { settings: sections.settings } : {})
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,12 @@ import type { PlatformTranslations } from "@govoplan/core-webui";
|
|||||||
|
|
||||||
export const generatedTranslations: PlatformTranslations = {
|
export const generatedTranslations: PlatformTranslations = {
|
||||||
"en": {
|
"en": {
|
||||||
|
"i18n:govoplan-admin.appearance_defaults": "Appearance defaults",
|
||||||
|
"i18n:govoplan-admin.system_palette_default": "System palette default",
|
||||||
|
"i18n:govoplan-admin.system_palette_default_help": "Validated default inherited by tenants and users without an explicit child choice.",
|
||||||
|
"i18n:govoplan-admin.appearance_lock_policy_permission": "Policy-write permission is required to change this lock.",
|
||||||
|
"i18n:govoplan-admin.lock_system_palette": "Lock the system palette",
|
||||||
|
"i18n:govoplan-admin.system_palette_precedence_help": "A system lock suppresses tenant and personal palette choices. Without a lock, explicit child choices take precedence.",
|
||||||
"i18n:govoplan-admin.data_subject_requests.ds001": "Data-subject requests",
|
"i18n:govoplan-admin.data_subject_requests.ds001": "Data-subject requests",
|
||||||
"i18n:govoplan-admin.data_subject_requests.ds002": "Search, export, and govern erasure requests with explicit provider coverage and retained evidence.",
|
"i18n:govoplan-admin.data_subject_requests.ds002": "Search, export, and govern erasure requests with explicit provider coverage and retained evidence.",
|
||||||
"i18n:govoplan-admin.data_subject_requests.ds003": "Create request",
|
"i18n:govoplan-admin.data_subject_requests.ds003": "Create request",
|
||||||
@@ -529,6 +535,12 @@ export const generatedTranslations: PlatformTranslations = {
|
|||||||
"i18n:govoplan-admin.working.049ac820": "Working..."
|
"i18n:govoplan-admin.working.049ac820": "Working..."
|
||||||
},
|
},
|
||||||
"de": {
|
"de": {
|
||||||
|
"i18n:govoplan-admin.appearance_defaults": "Darstellungsstandards",
|
||||||
|
"i18n:govoplan-admin.system_palette_default": "Systemstandard für die Farbpalette",
|
||||||
|
"i18n:govoplan-admin.system_palette_default_help": "Geprüfter Standard für Mandanten und Benutzer ohne eigene Auswahl.",
|
||||||
|
"i18n:govoplan-admin.appearance_lock_policy_permission": "Zum Ändern dieser Sperre ist das Recht zum Bearbeiten von Richtlinien erforderlich.",
|
||||||
|
"i18n:govoplan-admin.lock_system_palette": "Systempalette verbindlich festlegen",
|
||||||
|
"i18n:govoplan-admin.system_palette_precedence_help": "Eine Systemsperre unterdrückt Mandanten- und Benutzerauswahlen. Ohne Sperre haben ausdrückliche untergeordnete Auswahlen Vorrang.",
|
||||||
"i18n:govoplan-admin.data_subject_requests.ds001": "Betroffenenanfragen",
|
"i18n:govoplan-admin.data_subject_requests.ds001": "Betroffenenanfragen",
|
||||||
"i18n:govoplan-admin.data_subject_requests.ds002": "Auskunfts- und Löschanfragen mit expliziter Anbieterabdeckung und Aufbewahrungsnachweisen suchen, exportieren und steuern.",
|
"i18n:govoplan-admin.data_subject_requests.ds002": "Auskunfts- und Löschanfragen mit expliziter Anbieterabdeckung und Aufbewahrungsnachweisen suchen, exportieren und steuern.",
|
||||||
"i18n:govoplan-admin.data_subject_requests.ds003": "Anfrage anlegen",
|
"i18n:govoplan-admin.data_subject_requests.ds003": "Anfrage anlegen",
|
||||||
|
|||||||
+2
-1
@@ -48,7 +48,8 @@ const adminSections: AdminSectionsUiCapability = {
|
|||||||
render: ({ settings, auth }) => createElement(SystemSettingsPanel, {
|
render: ({ settings, auth }) => createElement(SystemSettingsPanel, {
|
||||||
settings,
|
settings,
|
||||||
canWrite: hasScope(auth, "system:settings:write"),
|
canWrite: hasScope(auth, "system:settings:write"),
|
||||||
canAccessMaintenance: hasScope(auth, "system:maintenance:access")
|
canAccessMaintenance: hasScope(auth, "system:maintenance:access"),
|
||||||
|
canWritePolicy: hasScope(auth, "admin:policies:write")
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user