feat: govern accessible appearance overrides
This commit is contained in:
@@ -2,7 +2,7 @@ import DescriptionList from "../../components/DescriptionList";
|
||||
import ContentGrid, { FormGrid } from "../../components/ContentGrid";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { useSearchParams } from "react-router";
|
||||
import type { ApiSettings, AuthInfo, AuthUpdate, FilesConnectorsUiCapability, MailProfilesUiCapability, NavigationPreferences, SettingsSectionContribution, SettingsSectionsUiCapability, UserUiPalette, UserUiPreferences, UserUiTheme } from "../../types";
|
||||
import type { AppearanceOverridesDocument, ApiSettings, AuthInfo, AuthUpdate, FilesConnectorsUiCapability, MailProfilesUiCapability, NavigationPreferences, SettingsSectionContribution, SettingsSectionsUiCapability, UserUiPalette, UserUiPreferences, UserUiTheme } from "../../types";
|
||||
import Card from "../../components/Card";
|
||||
import FormField from "../../components/FormField";
|
||||
import PasswordField from "../../components/PasswordField";
|
||||
@@ -28,6 +28,7 @@ import CredentialEnvelopeManager from "../../components/CredentialEnvelopeManage
|
||||
import DocumentationHelpLink from "../../components/help/DocumentationHelpLink";
|
||||
import WorkspaceLayout from "../../components/WorkspaceLayout";
|
||||
import { AppearancePalettePreview, AppearancePaletteSelect, APPEARANCE_PALETTE_OPTIONS, appearancePaletteLabel } from "../../components/AppearancePaletteControl";
|
||||
import AppearanceOverridesEditor from "../../components/AppearanceOverridesEditor";
|
||||
|
||||
type SettingsSection = "profile" | "mail-profiles" | "file-connectors" | "interface" | "workspace" | "local-connection" | string;
|
||||
|
||||
@@ -37,7 +38,8 @@ const DEFAULT_UI_PREFERENCES: UserUiPreferences = {
|
||||
reduce_motion: false,
|
||||
sticky_section_sidebars: true,
|
||||
theme: "system",
|
||||
palette: null
|
||||
palette: null,
|
||||
appearance_overrides: null
|
||||
};
|
||||
|
||||
const UI_THEME_OPTIONS: Array<{ value: UserUiTheme; label: string }> = [
|
||||
@@ -158,6 +160,7 @@ export default function SettingsPage({
|
||||
const [stickySections, setStickySections] = useState(currentUiPreferences.sticky_section_sidebars);
|
||||
const [theme, setTheme] = useState<UserUiTheme>(currentUiPreferences.theme);
|
||||
const [palette, setPalette] = useState<UserUiPalette | null>(currentUiPreferences.palette);
|
||||
const [appearanceOverrides, setAppearanceOverrides] = useState<AppearanceOverridesDocument | null>(currentUiPreferences.appearance_overrides ?? null);
|
||||
const [navigation, setNavigation] = useState<NavigationPreferences | null>(currentUiPreferences.navigation ?? null);
|
||||
const [uiBusy, setUiBusy] = useState(false);
|
||||
const [uiResult, setUiResult] = useState("");
|
||||
@@ -175,6 +178,7 @@ export default function SettingsPage({
|
||||
stickySections !== currentUiPreferences.sticky_section_sidebars ||
|
||||
theme !== currentUiPreferences.theme ||
|
||||
palette !== currentUiPreferences.palette ||
|
||||
JSON.stringify(appearanceOverrides) !== JSON.stringify(currentUiPreferences.appearance_overrides ?? null) ||
|
||||
JSON.stringify(navigation) !== JSON.stringify(currentUiPreferences.navigation ?? null);
|
||||
|
||||
useUnsavedDraftGuard({
|
||||
@@ -230,6 +234,7 @@ export default function SettingsPage({
|
||||
setStickySections(currentUiPreferences.sticky_section_sidebars);
|
||||
setTheme(currentUiPreferences.theme);
|
||||
setPalette(currentUiPreferences.palette);
|
||||
setAppearanceOverrides(currentUiPreferences.appearance_overrides ?? null);
|
||||
setNavigation(currentUiPreferences.navigation ?? null);
|
||||
}, [
|
||||
currentUiPreferences.compact_tables,
|
||||
@@ -238,6 +243,7 @@ export default function SettingsPage({
|
||||
currentUiPreferences.sticky_section_sidebars,
|
||||
currentUiPreferences.theme,
|
||||
currentUiPreferences.palette,
|
||||
currentUiPreferences.appearance_overrides,
|
||||
currentUiPreferences.navigation
|
||||
]);
|
||||
|
||||
@@ -278,6 +284,7 @@ export default function SettingsPage({
|
||||
setStickySections(currentUiPreferences.sticky_section_sidebars);
|
||||
setTheme(currentUiPreferences.theme);
|
||||
setPalette(currentUiPreferences.palette);
|
||||
setAppearanceOverrides(currentUiPreferences.appearance_overrides ?? null);
|
||||
setNavigation(currentUiPreferences.navigation ?? null);
|
||||
}
|
||||
|
||||
@@ -289,6 +296,7 @@ export default function SettingsPage({
|
||||
sticky_section_sidebars: stickySections,
|
||||
theme,
|
||||
palette,
|
||||
appearance_overrides: appearanceOverrides,
|
||||
navigation
|
||||
};
|
||||
}
|
||||
@@ -498,13 +506,20 @@ export default function SettingsPage({
|
||||
<div><dt>i18n:govoplan-core.accent_color.e49578ed</dt><dd>{paletteLabel(palette ?? auth.user.appearance?.inherited_palette ?? "default")}</dd></div>
|
||||
<div><dt>i18n:govoplan-core.effective_source</dt><dd>{appearanceSourceLabel(auth.user.appearance?.source)}</dd></div>
|
||||
<div><dt>i18n:govoplan-core.accessibility</dt><dd>i18n:govoplan-core.palette_contrast_validated</dd></div>
|
||||
<div><dt>i18n:govoplan-core.advanced_theme_overrides</dt><dd>i18n:govoplan-core.not_configured</dd></div>
|
||||
<div><dt>i18n:govoplan-core.advanced_theme_overrides</dt><dd>{appearanceOverrides ? "i18n:govoplan-core.configured" : "i18n:govoplan-core.not_configured"}</dd></div>
|
||||
<div><dt>i18n:govoplan-core.language.89b86ab0</dt><dd>{languageLabel}</dd></div>
|
||||
<div><dt>i18n:govoplan-core.enabled.df174a3f</dt><dd>{enabledLanguages.map((item) => item.code.toUpperCase()).join(", ")}</dd></div>
|
||||
<div><dt>i18n:govoplan-core.available.7c62a142</dt><dd>{availableLanguages.map((item) => item.code.toUpperCase()).join(", ")}</dd></div>
|
||||
<div><dt>i18n:govoplan-core.density.f9160c22</dt><dd>{compactTables ? "i18n:govoplan-core.compact_preview.3e06901d" : "i18n:govoplan-core.comfortable.2313707a"}</dd></div>
|
||||
</DescriptionList>
|
||||
<p className="muted small-note">i18n:govoplan-core.advanced_theme_overrides_follow_up</p>
|
||||
<AppearanceOverridesEditor
|
||||
value={appearanceOverrides}
|
||||
onChange={setAppearanceOverrides}
|
||||
theme={theme}
|
||||
disabled={auth.user.appearance?.custom_overrides_allowed !== true}
|
||||
/>
|
||||
{auth.user.appearance?.custom_overrides_allowed !== true &&
|
||||
<p className="muted small-note">i18n:govoplan-core.appearance_override_policy_disabled</p>}
|
||||
</FormGrid>
|
||||
</Card>
|
||||
</ContentGrid>
|
||||
@@ -640,6 +655,7 @@ function normalizeUiPreferences(value: Partial<UserUiPreferences> | null | undef
|
||||
sticky_section_sidebars: Boolean(value?.sticky_section_sidebars ?? DEFAULT_UI_PREFERENCES.sticky_section_sidebars),
|
||||
theme,
|
||||
palette: normalizeOptionalUiPalette(value?.palette),
|
||||
appearance_overrides: value?.appearance_overrides ?? null,
|
||||
navigation: value?.navigation ?? null
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user