refactor(webui): centralize mail profile controls

This commit is contained in:
2026-07-21 12:23:51 +02:00
parent e67da95df6
commit 7cf5a69a85
2 changed files with 14 additions and 31 deletions

View File

@@ -1,5 +1,5 @@
import { useEffect, useMemo, useState, type ReactNode } from "react"; 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 MailServerImapSettings, type MailServerSmtpSettings, type NormalizedPolicySourcePathItem, type PolicySourcePathItem } from "@govoplan/core-webui"; import { ConnectionTree, FieldLabel, LoadingFrame, MailServerSettingsPanel, PolicyLockedHint, PolicyPathHelp, PolicyRow, PolicySourcePath, PolicyTable, StatusBadge, TableActionGroup, ToggleSwitch, hasMailImapSettings, mailImapSettingsPayload, mailServerSecurityOptions, mailSmtpSettingsPayload, mailTextOrNull, mailTransportCredentialsPayload, mergeDeltaRows, normalizeMailServerSecurity, normalizePolicySourcePathItems, useDeltaWatermarks, type ConnectionTreeColumn, type MailServerConnectionTestResult, type MailServerCredentialSettings, type MailServerImapSettings, type MailServerSmtpSettings, type NormalizedPolicySourcePathItem, type PolicySourcePathItem } from "@govoplan/core-webui";
import { Pencil, Plus, Trash2 } from "lucide-react"; import { Pencil, Plus, Trash2 } from "lucide-react";
import type { ApiSettings } from "../../types"; import type { ApiSettings } from "../../types";
import { import {
@@ -344,10 +344,10 @@ export function MailProfileScopeManager({
header: "i18n:govoplan-mail.status.bae7d5be", header: "i18n:govoplan-mail.status.bae7d5be",
width: "110px", width: "110px",
render: (row) => row.kind === "profile" ? render: (row) => row.kind === "profile" ?
<span className={`status-badge ${row.profile.is_active ? "success" : "neutral"}`}>{row.profile.is_active ? "i18n:govoplan-mail.active.a733b809" : "i18n:govoplan-mail.inactive.09af574c"}</span> : <StatusBadge status={row.profile.is_active ? "active" : "inactive"} label={row.profile.is_active ? "i18n:govoplan-mail.active.a733b809" : "i18n:govoplan-mail.inactive.09af574c"} /> :
row.kind === "server" ? row.kind === "server" ?
<span className={`status-badge ${transportConfigured(row.profile, row.protocol) ? "success" : "neutral"}`}>{transportConfigured(row.profile, row.protocol) ? "i18n:govoplan-core.configured.668c5fff" : "i18n:govoplan-core.not_configured.811931bb"}</span> : <StatusBadge status={transportConfigured(row.profile, row.protocol) ? "success" : "inactive"} label={transportConfigured(row.profile, row.protocol) ? "i18n:govoplan-core.configured.668c5fff" : "i18n:govoplan-core.not_configured.811931bb"} /> :
<span className={`status-badge ${mailCredentialConfigured(row.profile, row.protocol) ? "success" : "neutral"}`}>{mailCredentialConfigured(row.profile, row.protocol) ? "i18n:govoplan-mail.saved.c0ae8f6e" : "i18n:govoplan-mail.local.dc99d54d"}</span> <StatusBadge status={mailCredentialConfigured(row.profile, row.protocol) ? "success" : "inactive"} label={mailCredentialConfigured(row.profile, row.protocol) ? "i18n:govoplan-mail.saved.c0ae8f6e" : "i18n:govoplan-mail.local.dc99d54d"} />
}], }],
[profileEffectivePolicy]); [profileEffectivePolicy]);
@@ -362,24 +362,21 @@ export function MailProfileScopeManager({
function renderMailProfileActions(row: MailProfileTreeRow) { function renderMailProfileActions(row: MailProfileTreeRow) {
if (row.kind === "server") { if (row.kind === "server") {
const label = `Edit ${row.protocol.toUpperCase()} server`; const label = `Edit ${row.protocol.toUpperCase()} server`;
return ( return <TableActionGroup actions={[{
<div className="admin-icon-actions"> id: "edit-server", label, icon: <Pencil size={16} />, disabled: !canWriteProfiles || busy, onClick: () => openEdit(row.profile, { kind: "server", protocol: row.protocol })
<Button type="button" className="admin-icon-button" title={label} aria-label={label} onClick={() => openEdit(row.profile, { kind: "server", protocol: row.protocol })} disabled={!canWriteProfiles || busy}><Pencil size={16} /></Button> }]} />;
</div>);
} }
if (row.kind === "credential") { if (row.kind === "credential") {
return ( return <TableActionGroup actions={[{
<div className="admin-icon-actions"> id: "edit-credentials", label: i18nMessage("i18n:govoplan-mail.edit_value_credentials.1e2dc4f6", { value0: row.protocol.toUpperCase() }), icon: <Pencil size={16} />, disabled: !canWriteProfiles || busy, onClick: () => openEdit(row.profile, { kind: "credentials", protocol: row.protocol })
<Button type="button" className="admin-icon-button" title={i18nMessage("i18n:govoplan-mail.edit_value_credentials.1e2dc4f6", { value0: row.protocol.toUpperCase() })} aria-label={i18nMessage("i18n:govoplan-mail.edit_value_credentials.1e2dc4f6", { value0: row.protocol.toUpperCase() })} onClick={() => openEdit(row.profile, { kind: "credentials", protocol: row.protocol })} disabled={!canWriteProfiles || busy}><Pencil size={16} /></Button> }]} />;
</div>);
} }
return ( return <TableActionGroup actions={[
<div className="admin-icon-actions"> { id: "edit", label: i18nMessage("i18n:govoplan-mail.edit_value.fad75899", { value0: row.profile.name }), icon: <Pencil size={16} />, disabled: !canWriteProfiles || busy, onClick: () => openEdit(row.profile) },
<Button type="button" className="admin-icon-button" title={i18nMessage("i18n:govoplan-mail.edit_value.fad75899", { value0: row.profile.name })} aria-label={i18nMessage("i18n:govoplan-mail.edit_value.fad75899", { value0: row.profile.name })} onClick={() => openEdit(row.profile)} disabled={!canWriteProfiles || busy}><Pencil size={16} /></Button> { id: "deactivate", label: i18nMessage("i18n:govoplan-mail.deactivate_value.a276a667", { value0: row.profile.name }), icon: <Trash2 size={16} />, variant: "danger", applicable: row.profile.is_active, disabled: !canWriteProfiles || busy, onClick: () => setDeactivating(row.profile) }
<Button type="button" variant="danger" className="admin-icon-button" title={i18nMessage("i18n:govoplan-mail.deactivate_value.a276a667", { value0: row.profile.name })} aria-label={i18nMessage("i18n:govoplan-mail.deactivate_value.a276a667", { value0: row.profile.name })} onClick={() => setDeactivating(row.profile)} disabled={!canWriteProfiles || busy || !row.profile.is_active}><Trash2 size={16} /></Button> ]} />;
</div>);
} }

View File

@@ -1,7 +1,3 @@
.admin-form-grid.three-columns {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
.button-icon-label { .button-icon-label {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
@@ -204,16 +200,6 @@
color: var(--text-strong); color: var(--text-strong);
} }
.status-badge.success {
background: var(--success-bg);
color: var(--success-text);
}
.status-badge.neutral {
background: var(--status-neutral-bg);
color: var(--text-soft);
}
@media (max-width: 900px) { @media (max-width: 900px) {
.admin-form-grid.three-columns, .admin-form-grid.three-columns,
.mail-policy-pattern-grid, .mail-policy-pattern-grid,