From 10b6c48f1bbc7afab0b6e177336dc750edd8cf1b Mon Sep 17 00:00:00 2001 From: Ashwin Kumar Date: Fri, 10 Apr 2026 04:55:44 +0200 Subject: [PATCH] feat(smtp): update SMTP management page with test functionality and email templates link - Redesign SMTP management page with two-column layout - Add SMTP connection test functionality - Add link to email templates page - Show current SMTP status - Add quick help section --- src/routes/admin/smtp.tsx | 471 +++++++++++++++++++++++++------------- 1 file changed, 308 insertions(+), 163 deletions(-) diff --git a/src/routes/admin/smtp.tsx b/src/routes/admin/smtp.tsx index f743366..3367ccc 100644 --- a/src/routes/admin/smtp.tsx +++ b/src/routes/admin/smtp.tsx @@ -1,4 +1,6 @@ -import { Show, createSignal, onMount } from 'solid-js'; +import { Show, createSignal, onMount, createResource } from "solid-js"; +import { useNavigate } from "@solidjs/router"; +import { api } from "~/lib/api"; type SmtpConfig = { host: string; @@ -13,87 +15,54 @@ type SmtpConfig = { }; const DEFAULT_CONFIG: SmtpConfig = { - host: '', + host: "", port: 587, secure: false, - username: '', - password: '', - fromEmail: '', - fromName: 'NxtGIG', - replyToEmail: '', + username: "", + password: "", + fromEmail: "", + fromName: "NXTGAUGE", + replyToEmail: "", enabled: true, }; -const READ_ENDPOINTS = [ - '/api/admin/smtp-config', - '/api/admin/settings/smtp', - '/api/admin/system-config/smtp', - '/api/gateway/admin/smtp-config', -]; - -const WRITE_ENDPOINTS = [ - '/api/admin/smtp-config', - '/api/admin/settings/smtp', - '/api/admin/system-config/smtp', - '/api/gateway/admin/smtp-config', -]; - -function authHeaders() { - const token = typeof sessionStorage !== 'undefined' - ? (sessionStorage.getItem('nxtgauge_admin_access_token') || '') - : ''; - return { - 'Content-Type': 'application/json', - Accept: 'application/json', - ...(token ? { Authorization: `Bearer ${token}` } : {}), - }; -} - -function normalizePayload(payload: any): SmtpConfig { - const src = payload?.config || payload?.data || payload || {}; - return { - host: String(src.host || ''), - port: Number(src.port || 587), - secure: src.secure === true || String(src.secure || '').toLowerCase() === 'true', - username: String(src.username || src.user || ''), - password: String(src.password || ''), - fromEmail: String(src.fromEmail || src.from_email || ''), - fromName: String(src.fromName || src.from_name || 'NxtGIG'), - replyToEmail: String(src.replyToEmail || src.reply_to_email || ''), - enabled: src.enabled !== false, - }; -} - export default function SmtpManagementPage() { - const [loading, setLoading] = createSignal(false); + const navigate = useNavigate(); const [saving, setSaving] = createSignal(false); - const [error, setError] = createSignal(''); - const [success, setSuccess] = createSignal(''); + const [testing, setTesting] = createSignal(false); + const [error, setError] = createSignal(""); + const [success, setSuccess] = createSignal(""); const [showPassword, setShowPassword] = createSignal(false); + const [testEmail, setTestEmail] = createSignal(""); const [cfg, setCfg] = createSignal(DEFAULT_CONFIG); - const load = async () => { - setLoading(true); - setError(''); + const [smtpStatus] = createResource(async () => { try { - let loaded = false; - for (const endpoint of READ_ENDPOINTS) { - const res = await fetch(endpoint, { method: 'GET', headers: authHeaders(), credentials: 'include' }).catch(() => null); - if (!res || !res.ok) continue; - const payload = await res.json().catch(() => ({})); - setCfg(normalizePayload(payload)); - loaded = true; - break; - } - if (!loaded) setCfg(DEFAULT_CONFIG); - } catch (e: any) { - setError(e?.message || 'Failed to load SMTP configuration.'); - } finally { - setLoading(false); + // Get current env config from backend + const res = await api.get("/admin/smtp-config"); + return res.data; + } catch (e) { + return null; } - }; + }); - onMount(() => void load()); + onMount(() => { + // Load from environment if available + const envHost = import.meta.env.VITE_SMTP_HOST || ""; + const envPort = import.meta.env.VITE_SMTP_PORT || 587; + const envUser = import.meta.env.VITE_SMTP_USER || ""; + const envFrom = import.meta.env.VITE_SMTP_FROM || ""; + + if (envHost) { + setCfg((prev) => ({ + ...prev, + host: envHost, + port: Number(envPort), + username: envUser, + fromEmail: envFrom, + })); + } + }); const setField = (key: K, value: SmtpConfig[K]) => { setCfg((prev) => ({ ...prev, [key]: value })); @@ -102,10 +71,11 @@ export default function SmtpManagementPage() { const save = async (e: Event) => { e.preventDefault(); setSaving(true); - setError(''); - setSuccess(''); + setError(""); + setSuccess(""); + try { - const payload = { + await api.post("/admin/smtp-config", { host: cfg().host.trim(), port: Number(cfg().port || 0), secure: cfg().secure, @@ -115,116 +85,291 @@ export default function SmtpManagementPage() { from_name: cfg().fromName.trim(), reply_to_email: cfg().replyToEmail.trim(), enabled: cfg().enabled, - }; + }); - let saved = false; - for (const endpoint of WRITE_ENDPOINTS) { - const methods: Array<'PUT' | 'PATCH' | 'POST'> = ['PUT', 'PATCH', 'POST']; - for (const method of methods) { - const res = await fetch(endpoint, { - method, - headers: authHeaders(), - credentials: 'include', - body: JSON.stringify(payload), - }).catch(() => null); - if (!res || !res.ok) continue; - saved = true; - break; - } - if (saved) break; - } - - if (!saved) throw new Error('Could not save SMTP configuration. Please verify backend endpoint wiring.'); - setSuccess('SMTP configuration saved successfully.'); - await load(); + setSuccess("SMTP configuration saved successfully."); } catch (e: any) { - setError(e?.message || 'Failed to save SMTP configuration.'); + setError(e?.message || "Failed to save SMTP configuration."); } finally { setSaving(false); } }; - const inputCls = 'w-full rounded-lg border border-gray-200 px-3 py-2 text-sm outline-none focus:border-[#FF5E13] focus:ring-1 focus:ring-[#FF5E13]'; - const labelCls = 'mb-1.5 block text-sm font-medium text-gray-700'; + const testConnection = async () => { + if (!testEmail()) { + setError("Please enter a test email address"); + return; + } + + setTesting(true); + setError(""); + setSuccess(""); + + try { + await api.post("/admin/smtp-test", { + to_email: testEmail(), + config: { + host: cfg().host.trim(), + port: Number(cfg().port || 0), + secure: cfg().secure, + username: cfg().username.trim(), + password: cfg().password, + from_email: cfg().fromEmail.trim(), + from_name: cfg().fromName.trim(), + }, + }); + + setSuccess(`Test email sent to ${testEmail()}. Check your inbox!`); + setTestEmail(""); + } catch (e: any) { + setError(e?.message || "Failed to send test email. Check your SMTP settings."); + } finally { + setTesting(false); + } + }; + + const inputCls = + "w-full rounded-lg border border-gray-200 px-3 py-2 text-sm outline-none focus:border-orange-500 focus:ring-1 focus:ring-orange-500"; + const labelCls = "mb-1.5 block text-sm font-medium text-gray-700"; return ( -
-
-

SMTP Management

-

Manage transactional email provider credentials and sender defaults.

+
+
+

SMTP Management

+

+ Configure transactional email settings and test connectivity. +

-
{error()}
+
+ {error()} +
-
{success()}
+
+ {success()} +
-
- -

Loading configuration...

-
+
+ {/* SMTP Configuration Form */} +
+
+

SMTP Configuration

-
-
- - setField('host', e.currentTarget.value)} placeholder="smtp.example.com" /> -
-
- - setField('port', Number(e.currentTarget.value || 0))} /> -
-
- - setField('username', e.currentTarget.value)} /> -
-
- -
- setField('password', e.currentTarget.value)} - /> - +
+
+
+ + setField("fromEmail", e.currentTarget.value)} + placeholder="noreply@nxtgauge.com" + /> +
+
+ + setField("fromName", e.currentTarget.value)} + placeholder="NXTGAUGE" + /> +
+
+ +
+ + +
+ +
+ +
+ +
+ + {/* Email Templates Link */} +
+

Email Templates

+

+ Manage and preview the 35 branded email templates used for notifications. +

+ +
+
+ + {/* Test Connection Panel */} +
+
+

Test Connection

+

+ Send a test email to verify your SMTP configuration is working correctly. +

+ +
+
+ + setTestEmail(e.currentTarget.value)} + placeholder="your@email.com" + /> +
+ +
-
-
- - setField('fromEmail', e.currentTarget.value)} placeholder="no-reply@nxtgig.com" /> -
-
- - setField('fromName', e.currentTarget.value)} /> -
-
- - setField('replyToEmail', e.currentTarget.value)} /> -
-
- - -
-
- - -
- - + + + {/* SMTP Status */} +
+

Current Status

+ + +

Loading status...

+
+ + +
+
+ SMTP Status + + {cfg().enabled && cfg().host ? "Configured" : "Not Configured"} + +
+ +
+ Host + {cfg().host || "—"} +
+ +
+ Port + {cfg().port || "—"} +
+ +
+ Security + + {cfg().secure ? "SSL/TLS" : "STARTTLS/None"} + +
+ +
+ From + {cfg().fromEmail || "—"} +
+
+
+
+ + {/* Quick Help */} +
+

Quick Help

+
    +
  • • Use App Password for Gmail
  • +
  • • Port 587 for STARTTLS
  • +
  • • Port 465 for SSL/TLS
  • +
  • • Enable 2FA for security
  • +
+
+
+
); }