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 { fetchTenantSettingsDelta, updateTenantSettings, type TenantSettingsDeltaSections, type TenantSettingsItem } from "../../api/admin"; import { AdminPageLayout, adminErrorMessage, useDeltaWatermarks, useUnsavedDraftGuard } from "@govoplan/core-webui"; const DELTA_KEY = "access:tenant-settings"; const fallback: TenantSettingsItem = { id: "", slug: "", name: "", default_locale: "en", available_languages: [ { code: "en", label: "English", native_label: "English" }, { code: "de", label: "German", native_label: "Deutsch" } ], system_enabled_language_codes: ["en", "de"], enabled_language_codes: ["en", "de"], settings: {} }; export default function TenantSettingsPanel({ settings, canWrite, onAuthRefresh }: {settings: ApiSettings;canWrite: boolean;onAuthRefresh: () => Promise;}) { 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 { getDeltaWatermark, setDeltaWatermark, resetDeltaWatermark } = useDeltaWatermarks(); const defaultLocaleOptions = localeOptions(draft.default_locale, draft.enabled_language_codes); const dirty = tenantSettingsDraftKey(draft) !== tenantSettingsDraftKey(savedDraft); useUnsavedDraftGuard({ dirty, onSave: save, onDiscard: () => setDraft(savedDraft) }); async function load() { setLoading(true); setError(""); setSuccess(""); try { const wasDirty = tenantSettingsDraftKey(draft) !== tenantSettingsDraftKey(savedDraft); const loaded = await fetchTenantSettingsDelta(settings, { since: getDeltaWatermark(DELTA_KEY) }); setDeltaWatermark(DELTA_KEY, loaded.watermark); if (loaded.full && loaded.item) { setSavedDraft(loaded.item); if (!wasDirty) setDraft(loaded.item); } else if (loaded.changed_sections.length) { setSavedDraft((current) => applyTenantSettingsSections(current, loaded.sections)); if (!wasDirty) setDraft((current) => applyTenantSettingsSections(current, loaded.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(""); setSuccess(""); try { const saved = await updateTenantSettings(settings, { default_locale: draft.default_locale, enabled_language_codes: draft.enabled_language_codes }); setDraft(saved); setSavedDraft(saved); resetDeltaWatermark(DELTA_KEY); setSuccess("i18n:govoplan-access.tenant_general_settings_saved.485e7681"); await onAuthRefresh(); 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.system_enabled_language_codes.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 }); } return ( }>
{draft.system_enabled_language_codes.map((code) => { const language = draft.available_languages.find((item) => item.code === code); return ( ); })}

i18n:govoplan-access.tenant_languages_help

i18n:govoplan-access.tenant.3ca93c78
{draft.name || "-"}
i18n:govoplan-access.slug.094da9b9
{draft.slug || "-"}
i18n:govoplan-access.available.7c62a142
{draft.available_languages.map((item) => item.code.toUpperCase()).join(", ") || "-"}
); } function languageOptionLabel(language: {code: string;label: string;native_label?: string | null}): string { return `${language.code.toUpperCase()} - ${language.native_label || language.label}`; } function localeOptions(current: string, enabled: string[]): string[] { return [...new Set([current, ...enabled].filter((item) => item && item.trim()))]; } function tenantSettingsDraftKey(item: TenantSettingsItem): string { return JSON.stringify({ default_locale: item.default_locale, enabled_language_codes: item.enabled_language_codes }); } function applyTenantSettingsSections(item: TenantSettingsItem, sections: TenantSettingsDeltaSections): TenantSettingsItem { return { ...item, ...(sections.identity ?? {}), ...(sections.locale ?? {}), ...(sections.languages ?? {}), ...(sections.settings ? { settings: sections.settings } : {}) }; }