feat: manage system appearance defaults

This commit is contained in:
2026-08-20 07:03:28 +02:00
parent 89ba59fcea
commit 50c952565b
8 changed files with 88 additions and 7 deletions
@@ -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_core.admin.common import AdminConflictError, AdminValidationError
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 (
decode_sequence_watermark,
encode_sequence_watermark,
@@ -159,6 +160,7 @@ SYSTEM_SETTINGS_SECTIONS = (
"privacy_retention_policy",
"maintenance_mode",
"navigation",
"appearance",
"settings",
)
@@ -295,6 +297,7 @@ def _system_settings_item(session: Session) -> SystemSettingsItem:
maintenance_mode = saved_maintenance_mode(session)
i18n_payload = system_i18n_payload(item)
navigation = navigation_preferences_from_settings(item.settings)
appearance_palette, appearance_palette_locked = appearance_settings(item.settings)
return SystemSettingsItem(
default_locale=item.default_locale,
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"],
settings=item.settings or {},
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"],
"maintenance_mode": payload["maintenance_mode"],
"navigation": payload["navigation"],
"appearance": {
"appearance_palette": payload["appearance_palette"],
"appearance_palette_locked": payload["appearance_palette_locked"],
},
"settings": payload["settings"],
}
@@ -1783,6 +1792,23 @@ def write_system_settings(
before_sections = _system_settings_sections(_system_settings_item(session))
before_privacy = privacy_policy_from_settings(item).model_dump(mode="json")
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:
privacy_value = payload.privacy_retention_policy.model_dump(mode="json")
try:
@@ -1819,6 +1845,13 @@ def write_system_settings(
available_languages=available_languages,
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:
item.settings = update_navigation_preferences(
item.settings,
@@ -51,6 +51,8 @@ class SystemSettingsItem(BaseModel):
enabled_language_codes: list[str] = Field(default_factory=list)
settings: dict[str, Any] = Field(default_factory=dict)
navigation: NavigationPreferencesPayload | None = None
appearance_palette: Literal["default", "civic_blue", "forest", "plum"] = "default"
appearance_palette_locked: bool = False
class SystemSettingsDeltaResponse(BaseModel):
@@ -75,6 +77,8 @@ class SystemSettingsUpdateRequest(BaseModel):
available_languages: list[LanguagePackageItem] | None = None
enabled_language_codes: list[str] | 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