refactor(admin): split system settings into section modules
This commit is contained in:
@@ -0,0 +1,186 @@
|
||||
import { InfoTooltip } from "~/components/ui/InfoTooltip.js";
|
||||
import {
|
||||
CHECKBOX_ROW_CLASS,
|
||||
INPUT_CLASS,
|
||||
LABEL_CLASS,
|
||||
PANEL_CLASS,
|
||||
PRIMARY_BUTTON_CLASS,
|
||||
SECONDARY_BUTTON_CLASS,
|
||||
RuntimeSecretCard,
|
||||
type RuntimeSecrets,
|
||||
type SaveResult,
|
||||
} from "./shared.js";
|
||||
|
||||
type SmtpSettingsPanelProps = {
|
||||
smtpHost: string;
|
||||
smtpPort: number;
|
||||
smtpUser: string;
|
||||
smtpFrom: string;
|
||||
smtpTls: boolean;
|
||||
smtpSaved: boolean;
|
||||
smtpTestResult: SaveResult | null;
|
||||
smtpSecret: RuntimeSecrets["smtpPassword"];
|
||||
isSaving: boolean;
|
||||
isTesting: boolean;
|
||||
onSmtpHostChange: (value: string) => void;
|
||||
onSmtpPortChange: (value: number) => void;
|
||||
onSmtpUserChange: (value: string) => void;
|
||||
onSmtpFromChange: (value: string) => void;
|
||||
onSmtpTlsChange: (value: boolean) => void;
|
||||
onSave: () => void;
|
||||
onTest: () => void;
|
||||
};
|
||||
|
||||
export function SmtpSettingsPanel({
|
||||
smtpHost,
|
||||
smtpPort,
|
||||
smtpUser,
|
||||
smtpFrom,
|
||||
smtpTls,
|
||||
smtpSaved,
|
||||
smtpTestResult,
|
||||
smtpSecret,
|
||||
isSaving,
|
||||
isTesting,
|
||||
onSmtpHostChange,
|
||||
onSmtpPortChange,
|
||||
onSmtpUserChange,
|
||||
onSmtpFromChange,
|
||||
onSmtpTlsChange,
|
||||
onSave,
|
||||
onTest,
|
||||
}: SmtpSettingsPanelProps) {
|
||||
return (
|
||||
<div className={PANEL_CLASS}>
|
||||
<div>
|
||||
<h2 className="flex items-center text-base font-semibold text-gray-900 dark:text-gray-100">
|
||||
Email Notifications (SMTP){" "}
|
||||
<InfoTooltip content="Configure SMTP to send email notifications for vacation approvals and rejections." />
|
||||
</h2>
|
||||
<p className="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
||||
Used to send email notifications when vacation requests are approved or rejected.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<div>
|
||||
<label className={LABEL_CLASS}>
|
||||
<span className="flex items-center">
|
||||
SMTP Host{" "}
|
||||
<InfoTooltip content="The SMTP server hostname, for example smtp.gmail.com or smtp.office365.com." />
|
||||
</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
className={INPUT_CLASS}
|
||||
value={smtpHost}
|
||||
onChange={(event) => onSmtpHostChange(event.target.value)}
|
||||
placeholder="smtp.example.com"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className={LABEL_CLASS}>
|
||||
<span className="flex items-center">
|
||||
SMTP Port{" "}
|
||||
<InfoTooltip content="Common ports: 587 for STARTTLS, 465 for SSL/TLS, 25 for unencrypted." />
|
||||
</span>
|
||||
</label>
|
||||
<input
|
||||
type="number"
|
||||
className={INPUT_CLASS}
|
||||
value={smtpPort}
|
||||
onChange={(event) => onSmtpPortChange(parseInt(event.target.value, 10))}
|
||||
min={1}
|
||||
max={65535}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className={LABEL_CLASS}>
|
||||
<span className="flex items-center">
|
||||
SMTP Username{" "}
|
||||
<InfoTooltip content="Authentication username for the SMTP server." />
|
||||
</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
className={INPUT_CLASS}
|
||||
value={smtpUser}
|
||||
onChange={(event) => onSmtpUserChange(event.target.value)}
|
||||
placeholder="user@example.com"
|
||||
autoComplete="off"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className={LABEL_CLASS}>
|
||||
<span className="flex items-center">
|
||||
From Address{" "}
|
||||
<InfoTooltip content="The sender email address shown in notification emails." />
|
||||
</span>
|
||||
</label>
|
||||
<input
|
||||
type="email"
|
||||
className={INPUT_CLASS}
|
||||
value={smtpFrom}
|
||||
onChange={(event) => onSmtpFromChange(event.target.value)}
|
||||
placeholder="noreply@capakraken.app"
|
||||
/>
|
||||
</div>
|
||||
<div className={`${CHECKBOX_ROW_CLASS} pt-0 md:mt-[1.65rem]`}>
|
||||
<input
|
||||
type="checkbox"
|
||||
id="smtpTls"
|
||||
checked={smtpTls}
|
||||
onChange={(event) => onSmtpTlsChange(event.target.checked)}
|
||||
className="rounded border-gray-300 text-brand-600"
|
||||
/>
|
||||
<label
|
||||
htmlFor="smtpTls"
|
||||
className="cursor-pointer text-sm text-gray-700 dark:text-gray-300"
|
||||
>
|
||||
Use TLS
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<RuntimeSecretCard
|
||||
title="SMTP Password"
|
||||
description="SMTP credentials are provisioned outside the application and injected at runtime."
|
||||
secret={smtpSecret}
|
||||
optionalNote="Provision SMTP_PASSWORD in the deployment target used by the API service."
|
||||
/>
|
||||
|
||||
<div className="flex items-center gap-3">
|
||||
<button
|
||||
type="button"
|
||||
onClick={onSave}
|
||||
disabled={isSaving}
|
||||
className={PRIMARY_BUTTON_CLASS}
|
||||
>
|
||||
{isSaving ? "Saving…" : "Save SMTP Settings"}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={onTest}
|
||||
disabled={isTesting}
|
||||
className={SECONDARY_BUTTON_CLASS}
|
||||
>
|
||||
{isTesting ? "Testing…" : "Test Connection"}
|
||||
</button>
|
||||
{smtpSaved ? (
|
||||
<span className="text-sm font-medium text-green-600 dark:text-green-400">Saved!</span>
|
||||
) : null}
|
||||
{smtpTestResult ? (
|
||||
<span
|
||||
className={`text-sm font-medium ${
|
||||
smtpTestResult.ok
|
||||
? "text-green-600 dark:text-green-400"
|
||||
: "text-red-500 dark:text-red-400"
|
||||
}`}
|
||||
>
|
||||
{smtpTestResult.ok ? "✓ Connection successful" : `✗ ${smtpTestResult.error}`}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user