feat: govern tenant appearance overrides
This commit is contained in:
@@ -50,6 +50,9 @@ export type TenantSettingsItem = {
|
||||
system_appearance_palette_locked: boolean;
|
||||
effective_appearance_palette: UserUiPalette;
|
||||
effective_appearance_source: "tenant" | "system" | "tenant_lock" | "system_lock";
|
||||
appearance_custom_overrides_allowed: boolean | null;
|
||||
system_appearance_custom_overrides_allowed: boolean;
|
||||
effective_appearance_custom_overrides_allowed: boolean;
|
||||
settings: Record<string, unknown>;
|
||||
};
|
||||
|
||||
@@ -61,7 +64,7 @@ export type TenantSettingsDeltaSections = Partial<{
|
||||
"available_languages" | "system_enabled_language_codes" | "enabled_language_codes"
|
||||
>;
|
||||
navigation: TenantSettingsItem["navigation"];
|
||||
appearance: Pick<TenantSettingsItem, "appearance_palette" | "appearance_palette_locked" | "system_appearance_palette" | "system_appearance_palette_locked" | "effective_appearance_palette" | "effective_appearance_source">;
|
||||
appearance: Pick<TenantSettingsItem, "appearance_palette" | "appearance_palette_locked" | "system_appearance_palette" | "system_appearance_palette_locked" | "effective_appearance_palette" | "effective_appearance_source" | "appearance_custom_overrides_allowed" | "system_appearance_custom_overrides_allowed" | "effective_appearance_custom_overrides_allowed">;
|
||||
settings: TenantSettingsItem["settings"];
|
||||
}>;
|
||||
|
||||
@@ -161,6 +164,7 @@ export function updateTenantSettings(
|
||||
navigation?: NavigationPreferences | null;
|
||||
appearance_palette?: UserUiPalette | null;
|
||||
appearance_palette_locked?: boolean;
|
||||
appearance_custom_overrides_allowed?: boolean | null;
|
||||
}
|
||||
): Promise<TenantSettingsItem> {
|
||||
return apiFetch(settings, "/api/v1/admin/tenant/settings", {
|
||||
|
||||
@@ -51,7 +51,10 @@ const fallback: TenantSettingsItem = {
|
||||
system_appearance_palette: "default",
|
||||
system_appearance_palette_locked: false,
|
||||
effective_appearance_palette: "default",
|
||||
effective_appearance_source: "system"
|
||||
effective_appearance_source: "system",
|
||||
appearance_custom_overrides_allowed: null,
|
||||
system_appearance_custom_overrides_allowed: false,
|
||||
effective_appearance_custom_overrides_allowed: false
|
||||
};
|
||||
|
||||
export default function TenantSettingsPanel({
|
||||
@@ -76,6 +79,11 @@ export default function TenantSettingsPanel({
|
||||
const { getDeltaWatermark, setDeltaWatermark, resetDeltaWatermark } = useDeltaWatermarks();
|
||||
const defaultLocaleOptions = localeOptions(draft.default_locale, draft.enabled_language_codes);
|
||||
const dirty = tenantSettingsDraftKey(draft) !== tenantSettingsDraftKey(savedDraft);
|
||||
const customOverridesEffectivelyAllowed =
|
||||
draft.system_appearance_custom_overrides_allowed
|
||||
&& draft.appearance_custom_overrides_allowed !== false
|
||||
&& !draft.system_appearance_palette_locked
|
||||
&& !draft.appearance_palette_locked;
|
||||
const saveDisabledReason = tenantMutationDisabledReason({
|
||||
busy,
|
||||
permitted: canWrite,
|
||||
@@ -127,7 +135,8 @@ export default function TenantSettingsPanel({
|
||||
enabled_language_codes: draft.enabled_language_codes,
|
||||
navigation: draft.navigation,
|
||||
appearance_palette: draft.appearance_palette,
|
||||
appearance_palette_locked: draft.appearance_palette_locked
|
||||
appearance_palette_locked: draft.appearance_palette_locked,
|
||||
appearance_custom_overrides_allowed: draft.appearance_custom_overrides_allowed
|
||||
});
|
||||
setDraft(saved);
|
||||
setSavedDraft(saved);
|
||||
@@ -230,10 +239,36 @@ export default function TenantSettingsPanel({
|
||||
help={!canWritePolicy ? "i18n:govoplan-tenancy.appearance_lock_policy_permission" : draft.system_appearance_palette_locked ? "i18n:govoplan-tenancy.system_palette_is_locked" : undefined}
|
||||
label="i18n:govoplan-tenancy.lock_tenant_palette"
|
||||
/>
|
||||
<FormField
|
||||
label="i18n:govoplan-tenancy.custom_overrides_policy"
|
||||
help={!canWritePolicy ? "i18n:govoplan-tenancy.appearance_policy_permission" : "i18n:govoplan-tenancy.custom_overrides_policy_help"}
|
||||
>
|
||||
<select
|
||||
value={draft.appearance_custom_overrides_allowed === null ? "inherit" : draft.appearance_custom_overrides_allowed ? "allow" : "block"}
|
||||
disabled={!canWrite || !canWritePolicy || busy}
|
||||
onChange={(event) => {
|
||||
const appearance_custom_overrides_allowed = event.target.value === "inherit" ? null : event.target.value === "allow";
|
||||
setDraft({
|
||||
...draft,
|
||||
appearance_custom_overrides_allowed,
|
||||
effective_appearance_custom_overrides_allowed:
|
||||
draft.system_appearance_custom_overrides_allowed
|
||||
&& appearance_custom_overrides_allowed !== false
|
||||
&& !draft.system_appearance_palette_locked
|
||||
&& !draft.appearance_palette_locked
|
||||
});
|
||||
}}
|
||||
>
|
||||
<option value="inherit">i18n:govoplan-tenancy.inherit_system_policy</option>
|
||||
<option value="allow" disabled={!draft.system_appearance_custom_overrides_allowed}>i18n:govoplan-tenancy.allow_for_tenant_users</option>
|
||||
<option value="block">i18n:govoplan-tenancy.block_for_tenant_users</option>
|
||||
</select>
|
||||
</FormField>
|
||||
<AppearancePalettePreview palette={draft.system_appearance_palette_locked ? draft.system_appearance_palette : draft.appearance_palette ?? draft.system_appearance_palette} />
|
||||
<DescriptionList variant="inline">
|
||||
<div><dt>i18n:govoplan-tenancy.effective_source</dt><dd>{draft.system_appearance_palette_locked ? "i18n:govoplan-tenancy.system_lock" : draft.appearance_palette ? "i18n:govoplan-tenancy.tenant_default" : "i18n:govoplan-tenancy.system_default"}</dd></div>
|
||||
<div><dt>i18n:govoplan-tenancy.user_override</dt><dd>{draft.system_appearance_palette_locked || draft.appearance_palette_locked ? "i18n:govoplan-tenancy.blocked_by_policy" : "i18n:govoplan-tenancy.allowed"}</dd></div>
|
||||
<div><dt>i18n:govoplan-tenancy.advanced_color_overrides</dt><dd>{customOverridesEffectivelyAllowed ? "i18n:govoplan-tenancy.allowed" : "i18n:govoplan-tenancy.blocked_by_policy"}</dd></div>
|
||||
</DescriptionList>
|
||||
</Card>
|
||||
</div>
|
||||
@@ -255,7 +290,8 @@ function tenantSettingsDraftKey(item: TenantSettingsItem): string {
|
||||
enabled_language_codes: item.enabled_language_codes,
|
||||
navigation: item.navigation,
|
||||
appearance_palette: item.appearance_palette,
|
||||
appearance_palette_locked: item.appearance_palette_locked
|
||||
appearance_palette_locked: item.appearance_palette_locked,
|
||||
appearance_custom_overrides_allowed: item.appearance_custom_overrides_allowed
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,13 @@ export const generatedTranslations: PlatformTranslations = {
|
||||
"i18n:govoplan-tenancy.tenant_palette_default": "Tenant palette default",
|
||||
"i18n:govoplan-tenancy.tenant_palette_default_help": "Inherit the system palette or select the default for this tenant.",
|
||||
"i18n:govoplan-tenancy.appearance_lock_policy_permission": "Policy-write permission is required to change this lock.",
|
||||
"i18n:govoplan-tenancy.custom_overrides_policy": "Personal color override policy",
|
||||
"i18n:govoplan-tenancy.custom_overrides_policy_help": "Inherit the system decision, or explicitly allow or block the validated advanced editor for this tenant.",
|
||||
"i18n:govoplan-tenancy.appearance_policy_permission": "Policy-write permission is required to change this appearance policy.",
|
||||
"i18n:govoplan-tenancy.inherit_system_policy": "Inherit system policy",
|
||||
"i18n:govoplan-tenancy.allow_for_tenant_users": "Allow for tenant users",
|
||||
"i18n:govoplan-tenancy.block_for_tenant_users": "Block for tenant users",
|
||||
"i18n:govoplan-tenancy.advanced_color_overrides": "Advanced color overrides",
|
||||
"i18n:govoplan-tenancy.system_palette_is_locked": "The system palette is locked and takes precedence.",
|
||||
"i18n:govoplan-tenancy.lock_tenant_palette": "Lock the tenant palette",
|
||||
"i18n:govoplan-tenancy.effective_source": "Effective source",
|
||||
@@ -118,6 +125,13 @@ export const generatedTranslations: PlatformTranslations = {
|
||||
"i18n:govoplan-tenancy.tenant_palette_default": "Mandantenstandard für die Farbpalette",
|
||||
"i18n:govoplan-tenancy.tenant_palette_default_help": "Systempalette übernehmen oder einen Standard für diesen Mandanten auswählen.",
|
||||
"i18n:govoplan-tenancy.appearance_lock_policy_permission": "Zum Ändern dieser Sperre ist das Recht zum Bearbeiten von Richtlinien erforderlich.",
|
||||
"i18n:govoplan-tenancy.custom_overrides_policy": "Richtlinie für persönliche Farbanpassungen",
|
||||
"i18n:govoplan-tenancy.custom_overrides_policy_help": "Die Systementscheidung übernehmen oder den geprüften erweiterten Editor für diesen Mandanten ausdrücklich zulassen oder sperren.",
|
||||
"i18n:govoplan-tenancy.appearance_policy_permission": "Zum Ändern dieser Darstellungsrichtlinie ist das Recht zum Bearbeiten von Richtlinien erforderlich.",
|
||||
"i18n:govoplan-tenancy.inherit_system_policy": "Systemrichtlinie übernehmen",
|
||||
"i18n:govoplan-tenancy.allow_for_tenant_users": "Für Mandantenbenutzer zulassen",
|
||||
"i18n:govoplan-tenancy.block_for_tenant_users": "Für Mandantenbenutzer sperren",
|
||||
"i18n:govoplan-tenancy.advanced_color_overrides": "Erweiterte Farbanpassungen",
|
||||
"i18n:govoplan-tenancy.system_palette_is_locked": "Die Systempalette ist verbindlich und hat Vorrang.",
|
||||
"i18n:govoplan-tenancy.lock_tenant_palette": "Mandantenpalette verbindlich festlegen",
|
||||
"i18n:govoplan-tenancy.effective_source": "Wirksame Quelle",
|
||||
|
||||
Reference in New Issue
Block a user