import { useEffect, useMemo, useState, type ReactNode } from "react"; import { ConnectionTree, FieldLabel, LoadingFrame, MailServerSettingsPanel, PolicyLockedHint, PolicyPathHelp, PolicyRow, PolicySourcePath, PolicyTable, ToggleSwitch, hasMailImapSettings, mailImapSettingsPayload, mailServerSecurityOptions, mailSmtpSettingsPayload, mailTextOrNull, mailTransportCredentialsPayload, mergeDeltaRows, normalizeMailServerSecurity, normalizePolicySourcePathItems, useDeltaWatermarks, type ConnectionTreeColumn, type MailServerConnectionTestResult, type MailServerCredentialSettings, type MailServerFolderLookupResult, type MailServerImapSettings, type MailServerSmtpSettings, type NormalizedPolicySourcePathItem, type PolicySourcePathItem } from "@govoplan/core-webui"; import { Pencil, Plus, Trash2 } from "lucide-react"; import type { ApiSettings } from "../../types"; import { createMailServerProfile, deactivateMailServerProfile, fetchMailSettingsDelta, getMailProfilePolicy, listImapFolders, listMailProfileImapFolders, mailProfilePatternKeys, mailProfilePolicyLimitKeys, updateMailProfilePolicy, testImapSettings, testMailProfileImap, testMailProfileSmtp, testSmtpSettings, updateMailServerProfile, type MailCredentialPolicy, type MailImapTestPayload, type MailProfilePatternKey, type MailProfilePatternRules, type MailProfilePolicy, type MailProfilePolicyLimitKey, type MailProfileScope, type MailSecurity, type MailServerProfile, type MailServerProfilePayload, type MailServerProfileUpdatePayload, type MailSmtpTestPayload } from "../../api/mail"; import { validateMailPolicy } from "./mailPolicyValidation"; import { Button } from "@govoplan/core-webui"; import { Card } from "@govoplan/core-webui"; import { ConfirmDialog } from "@govoplan/core-webui"; import { Dialog } from "@govoplan/core-webui"; import { DismissibleAlert } from "@govoplan/core-webui"; import { FormField, i18nMessage, useUnsavedDraftGuard } from "@govoplan/core-webui"; export type MailProfileTargetOption = { id: string; label: string; secondary?: string | null; }; type ProfileDraft = { name: string; slug: string; description: string; isActive: boolean; smtpHost: string; smtpPort: string; smtpSecurity: MailSecurity; smtpUsername: string; smtpPassword: string; smtpTimeout: string; imapHost: string; imapPort: string; imapSecurity: MailSecurity; imapUsername: string; imapPassword: string; imapSentFolder: string; imapTimeout: string; }; type MailProfileScopeManagerProps = { settings: ApiSettings; scopeType: MailProfileScope; scopeId?: string | null; targetOptions?: MailProfileTargetOption[]; targetLabel?: string; profileTitle?: string; policyTitle?: string; canWriteProfiles: boolean; canManageCredentials: boolean; canWritePolicy: boolean; }; type MailProfilePolicyEditorProps = { settings: ApiSettings; scopeType: MailProfileScope; scopeId?: string | null; campaignId?: string | null; profiles: MailServerProfile[]; ownerUserId?: string | null; ownerGroupId?: string | null; canWrite: boolean; locked?: boolean; title?: string; description?: string; onSaved?: () => void | Promise; }; type EditingProfile = MailServerProfile | "new" | null; type PolicyFlagValue = "inherit" | "allow" | "deny"; type CredentialInheritanceValue = "inherit" | "profile" | "local"; type MailProfileTreeRow = {kind: "profile";id: string;profile: MailServerProfile;} | {kind: "credential";id: string;profile: MailServerProfile;protocol: "smtp" | "imap";}; const securityOptions = mailServerSecurityOptions as readonly MailSecurity[]; const patternLabels: Record = { smtp_hosts: "i18n:govoplan-mail.smtp_hostnames.36eb51d8", imap_hosts: "i18n:govoplan-mail.imap_hostnames.ac9c1d78", envelope_senders: "i18n:govoplan-mail.envelope_senders.269065cd", from_headers: "i18n:govoplan-mail.from_headers.b3ea473b", recipient_domains: "i18n:govoplan-mail.recipient_domains.cb9b7b44" }; const blankPolicy: MailProfilePolicy = { allowed_profile_ids: [], allow_user_profiles: null, allow_group_profiles: null, allow_campaign_profiles: null, smtp_credentials: {}, imap_credentials: {}, whitelist: {}, blacklist: {}, allow_lower_level_limits: {} }; export function MailProfileScopeManager({ settings, scopeType, scopeId = null, targetOptions = [], targetLabel = "i18n:govoplan-mail.target.61ad50a9", profileTitle = "i18n:govoplan-mail.mail_server_profiles.b1726682", policyTitle = "i18n:govoplan-mail.mail_profile_policy.f2ac4b92", canWriteProfiles, canManageCredentials, canWritePolicy }: MailProfileScopeManagerProps) { const [profiles, setProfiles] = useState([]); const [selectedTargetId, setSelectedTargetId] = useState(scopeId || targetOptions[0]?.id || ""); const [editing, setEditing] = useState(null); const [deactivating, setDeactivating] = useState(null); const [draft, setDraft] = useState(emptyProfileDraft()); const [savedProfileDraftKey, setSavedProfileDraftKey] = useState(profileDraftKey(emptyProfileDraft())); const [loading, setLoading] = useState(true); const [busy, setBusy] = useState(false); const [error, setError] = useState(""); const [success, setSuccess] = useState(""); const [profileEffectivePolicy, setProfileEffectivePolicy] = useState(null); const { getDeltaWatermark, setDeltaWatermark, resetDeltaWatermark } = useDeltaWatermarks(); const requiresTarget = scopeType === "user" || scopeType === "group" || scopeType === "campaign"; const targetSelectionRequired = requiresTarget && !scopeId; const hasSelectableTarget = targetOptions.length > 0; const activeScopeId = scopeId || selectedTargetId || null; const deltaKey = `mail:settings:${scopeType}:${activeScopeId ?? ""}`; const scopeReady = !requiresTarget || Boolean(activeScopeId); const targetEmptyText = i18nMessage("i18n:govoplan-mail.no_value_available", { value0: targetPluralLabel(scopeType, targetLabel) }); const profileDirty = editing !== null && profileDraftKey(draft) !== savedProfileDraftKey; useUnsavedDraftGuard({ dirty: profileDirty, onSave: saveProfile, onDiscard: closeProfileEditor }); useEffect(() => { if (scopeId) { setSelectedTargetId(scopeId); return; } if (targetOptions.length > 0 && !targetOptions.some((option) => option.id === selectedTargetId)) { setSelectedTargetId(targetOptions[0].id); return; } if (targetOptions.length === 0 && selectedTargetId) setSelectedTargetId(""); }, [scopeId, selectedTargetId, targetOptions]); useEffect(() => { resetDeltaWatermark(deltaKey); void loadProfiles(true); }, [deltaKey, scopeReady, settings.accessToken, settings.apiBaseUrl, settings.apiKey, resetDeltaWatermark]); async function loadProfiles(forceFull = false) { setLoading(true); setError(""); if (!scopeReady) { setProfiles([]); setProfileEffectivePolicy(null); resetDeltaWatermark(deltaKey); setLoading(false); return; } try { let nextProfiles = forceFull ? [] : profiles; let nextEffectivePolicy = forceFull ? null : profileEffectivePolicy; let nextWatermark = forceFull ? null : getDeltaWatermark(deltaKey); let hasMore = false; do { const response = await fetchMailSettingsDelta(settings, { scope_type: scopeType, scope_id: activeScopeId, include_inactive: true, since: nextWatermark }); nextProfiles = response.full ? response.profiles : mergeDeltaRows(nextProfiles, response.profiles, response.deleted, (profile) => profile.id, { deletedResourceType: "mail_profile", sort: sortMailProfiles }); if (response.policy) { nextEffectivePolicy = response.policy.effective_policy ?? null; } nextWatermark = response.watermark ?? null; hasMore = response.has_more; } while (hasMore); setProfiles(nextProfiles); setProfileEffectivePolicy(nextEffectivePolicy); setDeltaWatermark(deltaKey, nextWatermark); } catch (err) { setProfiles([]); setProfileEffectivePolicy(null); resetDeltaWatermark(deltaKey); setError(errorMessage(err)); } finally { setLoading(false); } } const scopedProfiles = useMemo( () => profiles.filter((profile) => profileBelongsToEditableScope(profile, scopeType, activeScopeId)), [activeScopeId, profiles, scopeType] ); function openCreate() { const nextDraft = emptyProfileDraft(); setDraft(nextDraft); setSavedProfileDraftKey(profileDraftKey(nextDraft)); setEditing("new"); setError(""); setSuccess(""); } function openEdit(profile: MailServerProfile) { const nextDraft = profileToDraft(profile); setDraft(nextDraft); setSavedProfileDraftKey(profileDraftKey(nextDraft)); setEditing(profile); setError(""); setSuccess(""); } function closeProfileEditor() { setEditing(null); const nextDraft = emptyProfileDraft(); setDraft(nextDraft); setSavedProfileDraftKey(profileDraftKey(nextDraft)); } async function saveProfile(): Promise { if (!editing || !scopeReady) return false; setBusy(true); setError(""); setSuccess(""); try { if (editing === "new") { const payload = createProfilePayload(draft, scopeType, activeScopeId); const created = await createMailServerProfile(settings, payload); setSuccess(i18nMessage("i18n:govoplan-mail.profile_value_created.2a088d8d", { value0: created.name })); } else { const payload = updateProfilePayload(draft, editing); const updated = await updateMailServerProfile(settings, editing.id, payload); setSuccess(i18nMessage("i18n:govoplan-mail.profile_value_updated.fdbad0ea", { value0: updated.name })); } setEditing(null); await loadProfiles(); return true; } catch (err) { setError(errorMessage(err)); return false; } finally { setBusy(false); } } async function deactivateProfile() { if (!deactivating) return; setBusy(true); setError(""); setSuccess(""); try { await deactivateMailServerProfile(settings, deactivating.id); setSuccess(i18nMessage("i18n:govoplan-mail.profile_value_deactivated.fa7fcc1a", { value0: deactivating.name })); setDeactivating(null); await loadProfiles(); } catch (err) { setError(errorMessage(err)); } finally { setBusy(false); } } const treeRows = useMemo( () => scopedProfiles.map((profile) => ({ kind: "profile", id: `profile:${profile.id}`, profile })), [scopedProfiles] ); const columns = useMemo[]>(() => [ { id: "profile", header: "i18n:govoplan-mail.server_credential.7fb9a24e", width: "minmax(260px, 1.4fr)", render: (row) => row.kind === "profile" ?
{row.profile.name}
{row.profile.slug} · {scopeLabel(row.profile)}
: }, { id: "transport", header: "i18n:govoplan-mail.transport.c10d76c9", width: "minmax(220px, 1fr)", render: (row) => row.kind === "profile" ? {transportLabel(row.profile.smtp)}{row.profile.imap ? i18nMessage("i18n:govoplan-mail.value.48afe802", { value0: transportLabel(row.profile.imap) }) : ""} : }, { id: "policy", header: "i18n:govoplan-mail.policy.bb9cf141", width: "minmax(150px, 0.8fr)", render: (row) => row.kind === "profile" ? scopeLabel(row.profile) : mailCredentialPolicySummary(profileEffectivePolicy, row.protocol) }, { id: "status", header: "i18n:govoplan-mail.status.bae7d5be", width: "110px", render: (row) => row.kind === "profile" ? {row.profile.is_active ? "i18n:govoplan-mail.active.a733b809" : "i18n:govoplan-mail.inactive.09af574c"} : {mailCredentialConfigured(row.profile, row.protocol) ? "i18n:govoplan-mail.saved.c0ae8f6e" : "i18n:govoplan-mail.local.dc99d54d"} }], [profileEffectivePolicy]); function mailProfileChildren(row: MailProfileTreeRow): MailProfileTreeRow[] { if (row.kind !== "profile") return []; const children: MailProfileTreeRow[] = [{ kind: "credential", id: `credential:${row.profile.id}:smtp`, profile: row.profile, protocol: "smtp" }]; if (row.profile.imap) children.push({ kind: "credential", id: `credential:${row.profile.id}:imap`, profile: row.profile, protocol: "imap" }); return children; } function renderMailProfileActions(row: MailProfileTreeRow) { if (row.kind === "credential") { return (
); } return (
); } return (
{targetSelectionRequired &&
} {scopeReady && <>
}> row.id} getChildren={mailProfileChildren} renderActions={renderMailProfileActions} emptyText="i18n:govoplan-mail.no_profiles_in_this_scope.302b21d8" /> } {error && {error}} {success && {success}} !busy && setEditing(null)} className="admin-dialog admin-dialog-wide mail-profile-dialog" closeDisabled={busy} footer={<>}> setDeactivating(null)} onConfirm={() => void deactivateProfile()} /> ); } export function MailProfilePolicyEditor({ settings, scopeType, scopeId = null, campaignId = null, profiles, ownerUserId = null, ownerGroupId = null, canWrite, locked = false, title = "i18n:govoplan-mail.mail_profile_policy.f2ac4b92", description = "i18n:govoplan-mail.allowed_profiles_and_wildcard_rules_for_this_sco.0f82b3e4", onSaved }: MailProfilePolicyEditorProps) { const [policy, setPolicy] = useState(blankPolicy); const [effectivePolicy, setEffectivePolicy] = useState(null); const [parentPolicy, setParentPolicy] = useState(null); const [effectivePolicySources, setEffectivePolicySources] = useState([]); const [savedPolicyKey, setSavedPolicyKey] = useState(policyDraftKey(blankPolicy)); const [loading, setLoading] = useState(false); const [busy, setBusy] = useState(false); const [error, setError] = useState(""); const [success, setSuccess] = useState(""); const [profileEffectivePolicy, setProfileEffectivePolicy] = useState(null); const requiresTarget = scopeType === "user" || scopeType === "group" || scopeType === "campaign"; const scopeReady = !requiresTarget || Boolean(scopeId); const policyDirty = scopeReady && policyDraftKey(policy) !== savedPolicyKey; useUnsavedDraftGuard({ dirty: policyDirty, onSave: savePolicy, onDiscard: () => setPolicy(JSON.parse(savedPolicyKey) as MailProfilePolicy) }); useEffect(() => {void loadPolicy();}, [settings.accessToken, settings.apiBaseUrl, settings.apiKey, scopeType, scopeId, campaignId]); async function loadPolicy() { setError(""); setSuccess(""); if (!scopeReady) { setPolicy(blankPolicy); setSavedPolicyKey(policyDraftKey(blankPolicy)); setEffectivePolicy(null); setParentPolicy(null); setEffectivePolicySources([]); return; } setLoading(true); try { const response = await getMailProfilePolicy(settings, scopeType, scopeId, campaignId); const loadedPolicy = normalizePolicy(response.policy); setPolicy(loadedPolicy); setSavedPolicyKey(policyDraftKey(loadedPolicy)); setEffectivePolicy(response.effective_policy ? normalizePolicy(response.effective_policy) : null); setParentPolicy(response.parent_policy ? normalizePolicy(response.parent_policy) : null); setEffectivePolicySources(response.effective_policy_sources ?? []); } catch (err) { setPolicy(blankPolicy); setSavedPolicyKey(policyDraftKey(blankPolicy)); setEffectivePolicy(null); setParentPolicy(null); setEffectivePolicySources([]); setError(errorMessage(err)); } finally { setLoading(false); } } async function savePolicy(): Promise { if (!scopeReady) return false; setBusy(true); setError(""); setSuccess(""); try { const response = await updateMailProfilePolicy(settings, scopeType, normalizePolicyForSave(policy, parentPolicy, scopeType), scopeId); const savedPolicy = normalizePolicy(response.policy); setPolicy(savedPolicy); setSavedPolicyKey(policyDraftKey(savedPolicy)); setEffectivePolicy(response.effective_policy ? normalizePolicy(response.effective_policy) : null); setParentPolicy(response.parent_policy ? normalizePolicy(response.parent_policy) : null); setEffectivePolicySources(response.effective_policy_sources ?? []); setSuccess("i18n:govoplan-mail.mail_profile_policy_saved.666847bf"); await onSaved?.(); return true; } catch (err) { setError(errorMessage(err)); return false; } finally { setBusy(false); } } const candidateProfiles = useMemo( () => profileCandidatesForPolicy(profiles, scopeType, scopeId, ownerUserId, ownerGroupId), [ownerGroupId, ownerUserId, profiles, scopeId, scopeType] ); const isSystem = scopeType === "system"; const displayPolicy = useMemo(() => isSystem ? concreteSystemPolicy(policy) : policy, [isSystem, policy]); const selectedProfileIds = new Set(policy.allowed_profile_ids ?? []); const disabled = locked || busy || loading || !canWrite || !scopeReady; const parentAllowedProfileIds = parentPolicy?.allowed_profile_ids?.length ? new Set(parentPolicy.allowed_profile_ids) : null; const parentBlocksUserProfiles = parentPolicy?.allow_user_profiles === false; const parentBlocksGroupProfiles = parentPolicy?.allow_group_profiles === false; const parentBlocksCampaignProfiles = parentPolicy?.allow_campaign_profiles === false; const smtpCredentialLocked = !parentAllowsMailLimit("smtp_credentials.inherit"); const imapCredentialLocked = !parentAllowsMailLimit("imap_credentials.inherit"); const showAllowColumn = scopeType !== "campaign"; const showEffectiveColumn = !isSystem; const profileAllowListLocked = !parentAllowsMailLimit("allowed_profile_ids"); const blockedProfileDefinitions = [ parentBlocksUserProfiles ? "user" : "", parentBlocksGroupProfiles ? "group" : "", parentBlocksCampaignProfiles ? "i18n:govoplan-mail.campaign_local_settings.920ecb62" : ""]. filter(Boolean).join(", "); const lockedCredentialKinds = [smtpCredentialLocked ? "i18n:govoplan-mail.smtp.efff9cca" : "", imapCredentialLocked ? "i18n:govoplan-mail.imap.271f9ef2" : ""].filter(Boolean).join(" and "); const effectivePolicyPath = effectivePolicySources.length > 0 ? effectivePolicySources : mailPolicySourcePath(scopeType); function patchPolicy(patch: Partial) { setPolicy((current) => normalizePolicy({ ...current, ...patch })); } function toggleAllowedProfile(profileId: string, checked: boolean) { const next = new Set(policy.allowed_profile_ids ?? []); if (checked) next.add(profileId);else next.delete(profileId); patchPolicy({ allowed_profile_ids: [...next].sort() }); } function setFlag(key: "allow_user_profiles" | "allow_group_profiles" | "allow_campaign_profiles", value: PolicyFlagValue) { patchPolicy({ [key]: flagToBoolean(value) }); } function setCredentialPolicy(protocol: "smtp" | "imap", patch: Partial) { const key = protocol === "smtp" ? "smtp_credentials" : "imap_credentials"; patchPolicy({ [key]: normalizeCredentialPolicy({ ...(policy[key] ?? {}), ...patch }) }); } function setPattern(kind: "whitelist" | "blacklist", key: MailProfilePatternKey, text: string) { const nextRules = { ...(policy[kind] ?? {}) }; const parsed = parsePatternList(text); if (parsed.length > 0) nextRules[key] = parsed;else delete nextRules[key]; patchPolicy({ [kind]: nextRules }); } function parentAllowsMailLimit(key: MailProfilePolicyLimitKey): boolean { return !parentPolicy || parentPolicy.allow_lower_level_limits?.[key] !== false; } function localAllowsMailLimit(key: MailProfilePolicyLimitKey): boolean { const localValue = policy.allow_lower_level_limits?.[key]; if (localValue !== undefined) return localValue && parentAllowsMailLimit(key); return parentAllowsMailLimit(key); } function setAllowLowerLevelLimit(key: MailProfilePolicyLimitKey, allowed: boolean) { patchPolicy({ allow_lower_level_limits: { ...(policy.allow_lower_level_limits ?? {}), [key]: allowed } }); } function lowerLevelLimitToggle(key: MailProfilePolicyLimitKey, label: ReactNode = "i18n:govoplan-mail.allow_override.ffa6e9a0"): ReactNode | undefined { if (!showAllowColumn) return undefined; const parentLocked = !parentAllowsMailLimit(key); return ( setAllowLowerLevelLimit(key, checked)} label={label} />); } return ( }>
{description &&

{description}

} {error && {error}} {success && {success}}

i18n:govoplan-mail.profile_allow_list.507dfe6c

{lowerLevelLimitToggle("allowed_profile_ids")}

{selectedProfileIds.size === 0 ? "i18n:govoplan-mail.no_local_profile_allow_list_is_set.31072e39" : i18nMessage("i18n:govoplan-mail.value_profile_s_allowed_by_this_scope.6fe9ba44", { value0: selectedProfileIds.size })}

{candidateProfiles.map((profile) => )} {candidateProfiles.length === 0 &&

i18n:govoplan-mail.no_profiles_are_visible_for_this_policy_scope.1ec7bd85

}
{parentAllowedProfileIds &&

i18n:govoplan-mail.an_ancestor_allow_list_limits_selectable_profile.499ec179 {parentAllowedProfileIds.size} i18n:govoplan-mail.profile_s.742e9200

}

i18n:govoplan-mail.lower_level_mail_definitions.d39a0a1d

setFlag("allow_user_profiles", value)} />} effective={showEffectiveColumn ? effectiveBooleanLabel(effectivePolicy?.allow_user_profiles, effectivePolicy) : undefined} allowControl={showAllowColumn ?
{lowerLevelLimitToggle("allow_user_profiles")}
: undefined} effectiveHelp={showEffectiveColumn ? mailBooleanPolicyPathHelp("allow_user_profiles", effectivePolicyPath) : undefined} /> setFlag("allow_group_profiles", value)} />} effective={showEffectiveColumn ? effectiveBooleanLabel(effectivePolicy?.allow_group_profiles, effectivePolicy) : undefined} allowControl={showAllowColumn ?
{lowerLevelLimitToggle("allow_group_profiles")}
: undefined} effectiveHelp={showEffectiveColumn ? mailBooleanPolicyPathHelp("allow_group_profiles", effectivePolicyPath) : undefined} /> setFlag("allow_campaign_profiles", value)} />} effective={showEffectiveColumn ? effectiveBooleanLabel(effectivePolicy?.allow_campaign_profiles, effectivePolicy) : undefined} allowControl={showAllowColumn ?
{lowerLevelLimitToggle("allow_campaign_profiles")}
: undefined} effectiveHelp={showEffectiveColumn ? mailBooleanPolicyPathHelp("allow_campaign_profiles", effectivePolicyPath) : undefined} />
{blockedProfileDefinitions && i18n:govoplan-mail.explicit_allow_is_unavailable_for.8d05fd4a {blockedProfileDefinitions} i18n:govoplan-mail.because_an_ancestor_policy_blocks_those_definiti.5de3e30d}

i18n:govoplan-mail.credential_inheritance.ec8d7191

setCredentialPolicy("smtp", { inherit: valueToCredentialInheritance(value) })} />} effective={showEffectiveColumn ? effectivePolicy ? credentialInheritanceLabel(effectivePolicy.smtp_credentials) : "i18n:govoplan-mail.loading.b04ba49f" : undefined} allowControl={showAllowColumn ?
{lowerLevelLimitToggle("smtp_credentials.inherit")}
: undefined} effectiveHelp={showEffectiveColumn ? mailCredentialPolicyPathHelp("smtp_credentials", effectivePolicyPath) : undefined} /> setCredentialPolicy("imap", { inherit: valueToCredentialInheritance(value) })} />} effective={showEffectiveColumn ? effectivePolicy ? credentialInheritanceLabel(effectivePolicy.imap_credentials) : "i18n:govoplan-mail.loading.b04ba49f" : undefined} allowControl={showAllowColumn ?
{lowerLevelLimitToggle("imap_credentials.inherit")}
: undefined} effectiveHelp={showEffectiveColumn ? mailCredentialPolicyPathHelp("imap_credentials", effectivePolicyPath) : undefined} />
{lockedCredentialKinds && {lockedCredentialKinds} i18n:govoplan-mail.credential_inheritance_is_locked_by_an_ancestor_.1ee5d263}

i18n:govoplan-mail.wildcard_rules.54fb3fc0

i18n:govoplan-mail.policy_target.a19dcee9 i18n:govoplan-mail.whitelist.53c2ad30 i18n:govoplan-mail.blacklist.7b2dd04c {showAllowColumn && i18n:govoplan-mail.lower_levels.940821ee}
{mailProfilePatternKeys.map((key) =>
{patternLabels[key]}
setPattern("whitelist", key, text)} /> setPattern("blacklist", key, text)} /> {showAllowColumn &&
{lowerLevelLimitToggle(i18nMessage("i18n:govoplan-mail.whitelist_value.d4cc3755", { value0: key }) as MailProfilePolicyLimitKey, "i18n:govoplan-mail.whitelist.53c2ad30")} {lowerLevelLimitToggle(i18nMessage("i18n:govoplan-mail.blacklist_value.556334d0", { value0: key }) as MailProfilePolicyLimitKey, "i18n:govoplan-mail.blacklist.7b2dd04c")}
}
)}
{showEffectiveColumn && effectivePolicy &&

i18n:govoplan-mail.policy_path.1ba91ee5

i18n:govoplan-mail.effective_values_are_shown_in_the_table_rows_abo.b27b900d

}
); } function ProfileForm({ settings, draft, setDraft, editing, busy, canWrite, canManageCredentials, effectivePolicy }: {settings: ApiSettings;draft: ProfileDraft;setDraft: (draft: ProfileDraft) => void;editing: EditingProfile;busy: boolean;canWrite: boolean;canManageCredentials: boolean;effectivePolicy?: MailProfilePolicy | null;}) { const [smtpTestResult, setSmtpTestResult] = useState(null); const [imapTestResult, setImapTestResult] = useState(null); const [folderResult, setFolderResult] = useState(null); const [mailActionState, setMailActionState] = useState<"smtp" | "imap" | "folders" | null>(null); const disabled = busy || !canWrite; const credentialDisabled = disabled || !canManageCredentials; const existingProfile = editing !== "new" ? editing : null; const draftHasImap = hasDraftImapSettings(draft); const useSavedSmtpTest = Boolean(existingProfile && !draft.smtpPassword && existingProfile.smtp_password_configured); const useSavedImapTest = Boolean(existingProfile && !draft.imapPassword && existingProfile.imap_password_configured); const policyMessages = useMemo(() => validateMailPolicy(effectivePolicy, { smtpHost: draft.smtpHost, imapHost: draft.imapHost }), [draft.imapHost, draft.smtpHost, effectivePolicy]); useEffect(() => { setSmtpTestResult(null); setImapTestResult(null); setFolderResult(null); setMailActionState(null); }, [editing]); function patchSmtpSettings(patch: Partial) { setDraft({ ...draft, smtpHost: patch.host !== undefined ? String(patch.host ?? "") : draft.smtpHost, smtpPort: patch.port !== undefined ? String(patch.port ?? "") : draft.smtpPort, smtpSecurity: patch.security !== undefined ? readSecurity(String(patch.security || "starttls"), "starttls") : draft.smtpSecurity, smtpTimeout: patch.timeout_seconds !== undefined ? String(patch.timeout_seconds ?? "") : draft.smtpTimeout }); } function patchImapSettings(patch: Partial) { setDraft({ ...draft, imapHost: patch.host !== undefined ? String(patch.host ?? "") : draft.imapHost, imapPort: patch.port !== undefined ? String(patch.port ?? "") : draft.imapPort, imapSecurity: patch.security !== undefined ? readSecurity(String(patch.security || "tls"), "tls") : draft.imapSecurity, imapSentFolder: patch.sent_folder !== undefined ? String(patch.sent_folder ?? "") : draft.imapSentFolder, imapTimeout: patch.timeout_seconds !== undefined ? String(patch.timeout_seconds ?? "") : draft.imapTimeout }); } function patchSmtpCredentials(patch: Partial) { setDraft({ ...draft, smtpUsername: patch.username !== undefined ? String(patch.username ?? "") : draft.smtpUsername, smtpPassword: patch.password !== undefined ? String(patch.password ?? "") : draft.smtpPassword }); } function patchImapCredentials(patch: Partial) { setDraft({ ...draft, imapUsername: patch.username !== undefined ? String(patch.username ?? "") : draft.imapUsername, imapPassword: patch.password !== undefined ? String(patch.password ?? "") : draft.imapPassword }); } async function runSmtpTest() { setMailActionState("smtp"); setSmtpTestResult(null); try { setSmtpTestResult(useSavedSmtpTest && existingProfile ? await testMailProfileSmtp(settings, existingProfile.id) : await testSmtpSettings(settings, rawSmtpPayload(draft, false))); } catch (err) { setSmtpTestResult({ ok: false, protocol: "smtp", message: errorMessage(err), details: {} }); } finally { setMailActionState(null); } } async function runImapTest() { if (!draftHasImap) return; setMailActionState("imap"); setImapTestResult(null); try { setImapTestResult(useSavedImapTest && existingProfile ? await testMailProfileImap(settings, existingProfile.id) : await testImapSettings(settings, rawImapPayload(draft, false))); } catch (err) { setImapTestResult({ ok: false, protocol: "imap", message: errorMessage(err), details: {} }); } finally { setMailActionState(null); } } async function runFolderLookup() { if (!draftHasImap) return; setMailActionState("folders"); setFolderResult(null); try { setFolderResult(useSavedImapTest && existingProfile ? await listMailProfileImapFolders(settings, existingProfile.id) : await listImapFolders(settings, rawImapPayload(draft, false))); } catch (err) { setFolderResult({ ok: false, message: errorMessage(err), folders: [] }); } finally { setMailActionState(null); } } function useDetectedSentFolder() { const folder = folderResult?.detected_sent_folder; if (!folder || disabled || !draftHasImap) return; setDraft({ ...draft, imapSentFolder: folder }); } return (
setDraft({ ...draft, name: event.target.value })} /> setDraft({ ...draft, slug: event.target.value })} placeholder="i18n:govoplan-mail.generated_from_name.33d69a91" />