import { useEffect, useState } from "react"; import type { ApiSettings } from "@govoplan/core-webui"; import { Button } from "@govoplan/core-webui"; import { Card } from "@govoplan/core-webui"; import { FormField } from "@govoplan/core-webui"; import { ToggleSwitch } from "@govoplan/core-webui"; import { fetchSystemSettingsDelta, updateSystemSettings, type LanguagePackage, type PrivacyRetentionLimitPermissions, type PrivacyRetentionPolicy, type SystemSettingsDeltaSections, type SystemSettingsItem } from "../../api/admin"; import { AdminPageLayout, adminErrorMessage, useDeltaWatermarks, useUnsavedDraftGuard } from "@govoplan/core-webui"; const DELTA_KEY = "admin:system-settings"; const defaultAllowLowerLevelLimits: PrivacyRetentionLimitPermissions = { store_raw_campaign_json: true, raw_campaign_json_retention_days: true, generated_eml_retention_days: true, stored_report_detail_retention_days: true, mock_mailbox_retention_days: true, audit_detail_retention_days: true, audit_detail_level: true }; const defaultPrivacyPolicy: PrivacyRetentionPolicy = { store_raw_campaign_json: true, raw_campaign_json_retention_days: null, generated_eml_retention_days: null, stored_report_detail_retention_days: null, mock_mailbox_retention_days: null, audit_detail_retention_days: null, audit_detail_level: "full", allow_lower_level_limits: defaultAllowLowerLevelLimits }; const fallback: SystemSettingsItem = { default_locale: "en", allow_tenant_custom_groups: true, allow_tenant_custom_roles: true, allow_tenant_api_keys: true, privacy_retention_policy: defaultPrivacyPolicy, maintenance_mode: { enabled: false, message: "" }, available_languages: [ { code: "en", label: "English", native_label: "English" }, { code: "de", label: "German", native_label: "Deutsch" } ], enabled_language_codes: ["en", "de"], settings: {} }; export default function SystemSettingsPanel({ settings, canWrite, canAccessMaintenance }: {settings: ApiSettings;canWrite: boolean;canAccessMaintenance: boolean;}) { const [draft, setDraft] = useState(fallback); const [savedDraft, setSavedDraft] = useState(fallback); const [loading, setLoading] = useState(true); const [busy, setBusy] = useState(false); const [error, setError] = useState(""); const [success, setSuccess] = useState(""); const [packageDraft, setPackageDraft] = useState({ code: "", label: "", nativeLabel: "" }); const { getDeltaWatermark, setDeltaWatermark, resetDeltaWatermark } = useDeltaWatermarks(); const defaultLocaleOptions = localeOptions(draft.default_locale, draft.enabled_language_codes); const dirty = systemSettingsDraftKey(draft) !== systemSettingsDraftKey(savedDraft); useUnsavedDraftGuard({ dirty, onSave: save, onDiscard: () => { setDraft(savedDraft); setPackageDraft({ code: "", label: "", nativeLabel: "" }); } }); async function load() { setLoading(true); setError(""); try { const wasDirty = systemSettingsDraftKey(draft) !== systemSettingsDraftKey(savedDraft); const response = await fetchSystemSettingsDelta(settings, { since: getDeltaWatermark(DELTA_KEY) }); setDeltaWatermark(DELTA_KEY, response.watermark); if (response.full && response.item) { setSavedDraft(response.item); if (!wasDirty) setDraft(response.item); } else if (response.changed_sections.length) { setSavedDraft((current) => applySystemSettingsSections(current, response.sections)); if (!wasDirty) setDraft((current) => applySystemSettingsSections(current, response.sections)); } } catch (err) {setError(adminErrorMessage(err));} finally {setLoading(false);} } useEffect(() => { resetDeltaWatermark(DELTA_KEY); void load(); }, [settings.accessToken, settings.apiBaseUrl, resetDeltaWatermark]); async function save(): Promise { setBusy(true); setError(""); try { const saved = await updateSystemSettings(settings, { default_locale: draft.default_locale, allow_tenant_custom_groups: draft.allow_tenant_custom_groups, allow_tenant_custom_roles: draft.allow_tenant_custom_roles, allow_tenant_api_keys: draft.allow_tenant_api_keys, maintenance_mode: draft.maintenance_mode, available_languages: draft.available_languages, enabled_language_codes: draft.enabled_language_codes }); setDraft(saved); setSavedDraft(saved); resetDeltaWatermark(DELTA_KEY); setSuccess("i18n:govoplan-admin.system_settings_saved.dac4f17b"); return true; } catch (err) {setError(adminErrorMessage(err));return false;} finally {setBusy(false);} } function toggleLanguage(code: string, checked: boolean) { const enabled = new Set(draft.enabled_language_codes); if (checked) enabled.add(code); else enabled.delete(code); const nextEnabled = draft.available_languages.map((item) => item.code).filter((item) => enabled.has(item)); const defaultLocale = nextEnabled.includes(draft.default_locale) ? draft.default_locale : (nextEnabled[0] ?? draft.default_locale); setDraft({ ...draft, enabled_language_codes: nextEnabled, default_locale: defaultLocale }); } function installLanguagePackage() { const code = normalizeLanguageCode(packageDraft.code); if (!code || !packageDraft.label.trim()) return; const nextPackage: LanguagePackage = { code, label: packageDraft.label.trim(), native_label: packageDraft.nativeLabel.trim() || packageDraft.label.trim() }; const existing = new Set(draft.available_languages.map((item) => item.code)); const available = existing.has(code) ? draft.available_languages.map((item) => item.code === code ? nextPackage : item) : [...draft.available_languages, nextPackage]; const enabled = draft.enabled_language_codes.includes(code) ? draft.enabled_language_codes : [...draft.enabled_language_codes, code]; setDraft({ ...draft, available_languages: available, enabled_language_codes: enabled, default_locale: draft.default_locale || code }); setPackageDraft({ code: "", label: "", nativeLabel: "" }); } return ( }>
{draft.available_languages.map((item) => ( ))}
setPackageDraft({ ...packageDraft, code: event.target.value })} /> setPackageDraft({ ...packageDraft, label: event.target.value })} /> setPackageDraft({ ...packageDraft, nativeLabel: event.target.value })} />

i18n:govoplan-admin.language_packages_help

setDraft({ ...draft, allow_tenant_custom_groups: checked })} label="i18n:govoplan-admin.allow_tenant_defined_groups_by_default.32099d5a" /> setDraft({ ...draft, allow_tenant_custom_roles: checked })} label="i18n:govoplan-admin.allow_tenant_defined_roles_by_default.b1f79ee9" /> setDraft({ ...draft, allow_tenant_api_keys: checked })} label="i18n:govoplan-admin.allow_tenant_api_keys_by_default.58a6cf17" />

i18n:govoplan-admin.these_settings_are_enforced_by_the_backend_centr.8112ed6f

setDraft({ ...draft, maintenance_mode: { ...draft.maintenance_mode, enabled: checked } })} label="i18n:govoplan-admin.restrict_authenticated_api_access_to_maintenance.2bc47195" />