refactor(admin): split system settings into section modules

This commit is contained in:
2026-03-30 20:04:06 +02:00
parent a19d2cbae0
commit a36bca7ca7
11 changed files with 1753 additions and 1386 deletions
@@ -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>
);
}