diff --git a/frontend/src/pages/SettingsPage.tsx b/frontend/src/pages/SettingsPage.tsx index 6f58c3e..cf0660a 100644 --- a/frontend/src/pages/SettingsPage.tsx +++ b/frontend/src/pages/SettingsPage.tsx @@ -12,7 +12,6 @@ import DeleteIcon from '@mui/icons-material/Delete' import AddIcon from '@mui/icons-material/Add' import CloudIcon from '@mui/icons-material/Cloud' import EmailIcon from '@mui/icons-material/Email' -import VerifiedUserIcon from '@mui/icons-material/VerifiedUser' import { settingsApi } from '../api/client' import type { AzureSsoConfig, SmtpConfig, PollingConfig, NotificationConfig } from '../types' @@ -38,8 +37,6 @@ export default function SettingsPage() { const [saving, setSaving] = useState(false) const [testingAzure, setTestingAzure] = useState(false) const [testingSmtp, setTestingSmtp] = useState(false) - const [testingIntegrity, setTestingIntegrity] = useState(false) - const [integrityResult, setIntegrityResult] = useState<{ intact: boolean; rows_checked: number; errors: Array<{ row_id: number; expected_hash: string; actual_hash: string }> } | null>(null) const [azureSsoTestResult, setAzureSsoTestResult] = useState<{ success: boolean; message: string } | null>(null) const [smtpTestResult, setSmtpTestResult] = useState<{ success: boolean; message: string } | null>(null) const [error, setError] = useState(null) @@ -76,7 +73,10 @@ export default function SettingsPage() { polling, ip_whitelist: ipWhitelist, web_tls_strategy: webTlsStrategy, - notification, + notification: { + ...notification, + email_from: smtp.from, // Use SMTP From Address as notification sender + }, }) setSuccess('Settings saved successfully') } catch (err: unknown) { @@ -90,20 +90,6 @@ export default function SettingsPage() { } } - const handleAuditIntegrity = async () => { - setTestingIntegrity(true) - setIntegrityResult(null) - try { - const { data } = await settingsApi.auditIntegrity() - setIntegrityResult(data) - } catch (err: unknown) { - const msg = err instanceof Error ? err.message : 'Verification failed' - setIntegrityResult({ intact: false, rows_checked: 0, errors: [{ row_id: 0, expected_hash: '', actual_hash: msg }] }) - } finally { - setTestingIntegrity(false) - } - } - const handleTestAzureSso = async () => { setTestingAzure(true) setAzureSsoTestResult(null) @@ -129,7 +115,10 @@ export default function SettingsPage() { polling, ip_whitelist: ipWhitelist, web_tls_strategy: webTlsStrategy, - notification, + notification: { + ...notification, + email_from: smtp.from, // Use SMTP From Address as notification sender + }, }) // Then test SMTP const { data } = await settingsApi.testSmtp() @@ -209,10 +198,10 @@ export default function SettingsPage() { - {/* Section 2: SMTP Configuration */} + {/* Section 2: SMTP Configuration & Email Notifications */} }> - SMTP Configuration + SMTP Configuration & Email Notifications @@ -226,15 +215,15 @@ export default function SettingsPage() { - setSmtp({ ...smtp, host: e.target.value })} /> + setSmtp({ ...smtp, host: e.target.value })} disabled={!smtp.enabled} /> - setSmtp({ ...smtp, port: Number(e.target.value) })} /> + setSmtp({ ...smtp, port: Number(e.target.value) })} disabled={!smtp.enabled} /> TLS Mode - setSmtp({ ...smtp, tls_mode: e.target.value })} disabled={!smtp.enabled}> None STARTTLS TLS (Implicit) @@ -242,13 +231,41 @@ export default function SettingsPage() { - setSmtp({ ...smtp, username: e.target.value })} /> + setSmtp({ ...smtp, username: e.target.value })} disabled={!smtp.enabled} /> - setSmtp({ ...smtp, password: e.target.value })} placeholder="Enter new password or leave masked" /> + setSmtp({ ...smtp, password: e.target.value })} placeholder="Enter new password or leave masked" disabled={!smtp.enabled} /> - setSmtp({ ...smtp, from: e.target.value })} helperText="noreply@example.com" /> + setSmtp({ ...smtp, from: e.target.value })} helperText="Sender address for both SMTP and notifications (e.g. noreply@example.com)" disabled={!smtp.enabled} /> + + + setNotification({ ...notification, email_enabled: e.target.checked })} />} + label="Enable Email Notifications" + disabled={!smtp.enabled} + /> + + Requires SMTP server to be enabled + + + + Notification Recipients + {notification.recipients.map((email, idx) => ( + + { + const updated = [...notification.recipients] + updated[idx] = e.target.value + setNotification({ ...notification, recipients: updated }) + }} placeholder="admin@example.com" sx={{ flexGrow: 1 }} disabled={!smtp.enabled || !notification.email_enabled} /> + { + setNotification({ ...notification, recipients: notification.recipients.filter((_, i) => i !== idx) }) + }}> + + ))} + - - - - - - {/* Section 7: Audit Integrity Verification */} - - }> - Audit Integrity Verification - - - - Verify the integrity of the audit log hash chain. This checks that all audit entries are properly linked and have not been tampered with. - - - {integrityResult && ( - - {integrityResult.intact - ? `Audit chain intact — ${integrityResult.rows_checked} rows verified` - : `Audit chain compromised! ${integrityResult.errors.length} error(s) found across ${integrityResult.rows_checked} rows checked`} - {integrityResult.errors.length > 0 && ( - - {integrityResult.errors.slice(0, 5).map((e, i) => ( - - Row {e.row_id}: expected {e.expected_hash.substring(0, 16)}... got {e.actual_hash.substring(0, 16)}... - - ))} - {integrityResult.errors.length > 5 && ( - ...and {integrityResult.errors.length - 5} more errors - )} - - )} - - )} - - setSuccess(null)} anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }} > - setSuccess(null)}>{success} + setSuccess(null)}> + {success} + )