intermittent commit

This commit is contained in:
2026-07-14 13:22:10 +02:00
parent 8aa1943581
commit e6f7c45f0a
76 changed files with 6039 additions and 2188 deletions
@@ -1,8 +1,8 @@
import { useState } from "react";
import { useEffect, useState } from "react";
import Button from "../Button";
import { CredentialFields } from "../CredentialPanel";
import DismissibleAlert from "../DismissibleAlert";
import FormField from "../FormField";
import PasswordField from "../PasswordField";
import SegmentedControl from "../SegmentedControl";
import ToggleSwitch from "../ToggleSwitch";
@@ -48,6 +48,9 @@ export type MailServerFolderLookupResult = {
details?: Record<string, unknown> | null;
};
export type MailServerSettingsSection = "smtp" | "imap" | "advanced";
export type MailServerSettingsMode = "all" | "server" | "credentials";
export type MailServerSettingsPanelProps = {
smtp: MailServerSmtpSettings;
imap: MailServerImapSettings;
@@ -97,12 +100,13 @@ export type MailServerSettingsPanelProps = {
className?: string;
floatingResults?: boolean;
initialSection?: MailServerSettingsSection;
visibleSections?: readonly MailServerSettingsSection[];
mode?: MailServerSettingsMode;
};
export const mailServerSecurityOptions = ["plain", "tls", "starttls"] as const;
export type MailServerSecurityOption = typeof mailServerSecurityOptions[number];
const securityOptions = mailServerSecurityOptions;
type MailServerSettingsSection = "smtp" | "imap" | "advanced";
export function defaultSmtpPort(security: MailServerSecurity | null | undefined): number {
if (security === "tls") return 465;
@@ -234,7 +238,9 @@ export default function MailServerSettingsPanel({
disabled = false,
className = "",
floatingResults = false,
initialSection = "smtp"
initialSection = "smtp",
visibleSections,
mode = "all"
}: MailServerSettingsPanelProps) {
const smtpFieldsDisabled = disabled || smtpDisabled;
const smtpCredentialFieldsDisabled = disabled || smtpCredentialDisabled;
@@ -254,11 +260,29 @@ export default function MailServerSettingsPanel({
const appendTargetHelp = append ?
"i18n:govoplan-core.folder_for_sent_message_copies_leave_as_auto_unl.a62586e9" :
"i18n:govoplan-core.folder_used_when_this_imap_account_is_used_for_s.08503f5e";
const [activeSection, setActiveSection] = useState<MailServerSettingsSection>(initialSection);
const sections: {id: MailServerSettingsSection;label: string;}[] = [
const allSections: {id: MailServerSettingsSection;label: string;}[] = [
{ id: "smtp", label: "i18n:govoplan-core.smtp.efff9cca" },
{ id: "imap", label: "i18n:govoplan-core.imap.271f9ef2" },
{ id: "advanced", label: "i18n:govoplan-core.advanced.4d064726" }];
const visibleSectionSet = new Set(visibleSections ?? allSections.map((section) => section.id));
const sections = allSections.filter((section) => visibleSectionSet.has(section.id));
const fallbackSection = sections[0]?.id ?? "smtp";
const resolvedInitialSection = sections.some((section) => section.id === initialSection) ? initialSection : fallbackSection;
const sectionKey = sections.map((section) => section.id).join("|");
const [activeSection, setActiveSection] = useState<MailServerSettingsSection>(resolvedInitialSection);
const showSectionSwitcher = sections.length > 1;
const showServerFields = mode !== "credentials";
const showCredentialFields = mode !== "server";
useEffect(() => {
if (!sections.some((section) => section.id === activeSection)) {
setActiveSection(resolvedInitialSection);
return;
}
if (initialSection !== activeSection && sections.some((section) => section.id === initialSection)) {
setActiveSection(initialSection);
}
}, [activeSection, initialSection, resolvedInitialSection, sectionKey]);
function patchSmtpSecurity(security: MailServerSecurity) {
@@ -283,6 +307,7 @@ export default function MailServerSettingsPanel({
return (
<div className={`mail-server-settings-panel ${className}`.trim()}>
{showSectionSwitcher &&
<SegmentedControl
className="mail-server-segmented-control"
size="equal"
@@ -291,27 +316,30 @@ export default function MailServerSettingsPanel({
onChange={setActiveSection}
options={sections}
/>
}
<div className="mail-server-settings-view">
{activeSection === "smtp" &&
<section className="mail-server-subsection" role="tabpanel" aria-label="i18n:govoplan-core.smtp_settings.f103e570">
<div className="form-grid compact responsive-form-grid mail-server-form-grid">
{showServerFields &&
<>
<div className="mail-server-field-heading">i18n:govoplan-core.server.cb0cb170</div>
<FormField label="i18n:govoplan-core.host.3960ec4c"><input value={stringValue(smtp.host)} disabled={smtpFieldsDisabled} onChange={(event) => onSmtpChange({ host: event.target.value })} /></FormField>
<FormField label="i18n:govoplan-core.port.fe035157"><input type="number" min={1} max={65535} value={smtpPort} disabled={smtpFieldsDisabled} onChange={(event) => onSmtpChange({ port: event.target.value })} /></FormField>
<FormField label="i18n:govoplan-core.security.f25ce1b8"><SecuritySelect value={smtpSecurity} disabled={smtpFieldsDisabled} onChange={patchSmtpSecurity} /></FormField>
<div className="mail-server-field-heading">i18n:govoplan-core.credentials.dd097a22</div>
<FormField label="i18n:govoplan-core.username.84c29015"><input value={stringValue(smtpCredentialValues.username)} disabled={smtpCredentialFieldsDisabled} onChange={(event) => patchSmtpCredentials({ username: event.target.value })} /></FormField>
<FormField label="i18n:govoplan-core.password.8be3c943">
<PasswordField
value={stringValue(smtpCredentialValues.password)}
</>
}
{showCredentialFields &&
<CredentialFields
values={smtpCredentialValues}
onChange={patchSmtpCredentials}
disabled={smtpCredentialFieldsDisabled}
saved={smtpPasswordSaved}
savedPlaceholder={smtpSavedPasswordPlaceholder}
autoComplete="new-password"
onValueChange={(password) => patchSmtpCredentials({ password })} />
</FormField>
savedPassword={smtpPasswordSaved}
savedPasswordPlaceholder={smtpSavedPasswordPlaceholder}
heading="i18n:govoplan-core.credentials.dd097a22"
headingClassName="mail-server-field-heading" />
}
</div>
{onTestSmtp &&
<div className="button-row compact-actions mail-server-actions">
@@ -325,22 +353,24 @@ export default function MailServerSettingsPanel({
{activeSection === "imap" &&
<section className="mail-server-subsection" role="tabpanel" aria-label="i18n:govoplan-core.imap_settings.ab8d8247">
<div className="form-grid compact responsive-form-grid mail-server-form-grid">
{showServerFields &&
<>
<div className="mail-server-field-heading">i18n:govoplan-core.server.cb0cb170</div>
<FormField label="i18n:govoplan-core.host.3960ec4c"><input value={stringValue(imap.host)} disabled={imapFieldsDisabled} onChange={(event) => onImapChange({ host: event.target.value })} /></FormField>
<FormField label="i18n:govoplan-core.port.fe035157"><input type="number" min={1} max={65535} value={imapPort} disabled={imapFieldsDisabled} onChange={(event) => onImapChange({ port: event.target.value })} /></FormField>
<FormField label="i18n:govoplan-core.security.f25ce1b8"><SecuritySelect value={imapSecurity} disabled={imapFieldsDisabled} onChange={patchImapSecurity} /></FormField>
<div className="mail-server-field-heading">i18n:govoplan-core.credentials.dd097a22</div>
<FormField label="i18n:govoplan-core.username.84c29015"><input value={stringValue(imapCredentialValues.username)} disabled={imapCredentialFieldsDisabled} onChange={(event) => patchImapCredentials({ username: event.target.value })} /></FormField>
<FormField label="i18n:govoplan-core.password.8be3c943">
<PasswordField
value={stringValue(imapCredentialValues.password)}
</>
}
{showCredentialFields &&
<CredentialFields
values={imapCredentialValues}
onChange={patchImapCredentials}
disabled={imapCredentialFieldsDisabled}
saved={imapPasswordSaved}
savedPlaceholder={imapSavedPasswordPlaceholder}
autoComplete="new-password"
onValueChange={(password) => patchImapCredentials({ password })} />
</FormField>
savedPassword={imapPasswordSaved}
savedPasswordPlaceholder={imapSavedPasswordPlaceholder}
heading="i18n:govoplan-core.credentials.dd097a22"
headingClassName="mail-server-field-heading" />
}
</div>
{onTestImap &&
<div className="button-row compact-actions mail-server-actions">