import React, { useCallback, useEffect, useState } from 'react'; import { FiRefreshCw, FiList, FiLogOut, FiSettings, FiKey, FiTrash2, FiPlus, FiInbox, FiUsers, FiUser, FiHardDrive, FiDollarSign, FiActivity, } from 'react-icons/fi'; import Login from './components/Login'; import Toast from './components/Toast'; import LoadingOverlay from './components/LoadingOverlay'; import UsageBar, { formatBytes } from './components/UsageBar'; import MailboxSettingsModal from './components/MailboxSettingsModal'; import NewMailboxModal from './components/NewMailboxModal'; import PasswordResetModal from './components/PasswordResetModal'; import ConfirmDialog from './components/ConfirmDialog'; import AuditLogModal from './components/AuditLogModal'; import AdminUsersModal from './components/AdminUsersModal'; import ChangeMyPasswordModal from './components/ChangeMyPasswordModal'; import DomainQuotaModal from './components/DomainQuotaModal'; import BillingModal from './components/BillingModal'; import HealthModal from './components/HealthModal'; import HealthBanner from './components/HealthBanner'; import { authAPI, domainsAPI, mailboxesAPI, healthAPI } from './services/api'; function App() { const [user, setUser] = useState(null); const [bootChecked, setBootChecked] = useState(false); const [domains, setDomains] = useState([]); const [selectedDomain, setSelectedDomain] = useState(null); const [mailboxes, setMailboxes] = useState([]); const [busyMessage, setBusyMessage] = useState(''); const [toast, setToast] = useState(null); const [settingsTarget, setSettingsTarget] = useState(null); const [pwTarget, setPwTarget] = useState(null); const [deleteTarget, setDeleteTarget] = useState(null); const [showNew, setShowNew] = useState(false); const [showAudit, setShowAudit] = useState(false); const [showAdmins, setShowAdmins] = useState(false); const [showChangePw, setShowChangePw] = useState(false); const [showDomainQuota, setShowDomainQuota] = useState(false); const [showBilling, setShowBilling] = useState(false); const [showHealth, setShowHealth] = useState(false); // Persisted health status for the currently selected domain (drives the banner). const [healthStatus, setHealthStatus] = useState(null); const showToast = useCallback((message, type = 'success') => { setToast({ message, type }); }, []); const isSuperAdmin = user?.role === 'super_admin'; const hideDomainList = !isSuperAdmin && domains.length <= 1; const loadDomains = useCallback(async (resync = false) => { const list = await domainsAPI.list(resync); setDomains(list); return list; }, []); const loadMailboxes = useCallback(async (domain, refreshQuota = false) => { if (!domain) { setMailboxes([]); return; } const list = await mailboxesAPI.list(domain, refreshQuota); setMailboxes(list); }, []); // Load (or re-load) the persisted health status for a domain. // Cheap call — just reads from PostgreSQL, no checks are run. const loadHealthStatus = useCallback(async (domain) => { if (!domain) { setHealthStatus(null); return; } try { const status = await healthAPI.getStatus(domain); setHealthStatus(status); // null if never checked, that's fine } catch (err) { // Silent: don't block the UI just because health status load failed. console.warn('Failed to load health status:', err); setHealthStatus(null); } }, []); useEffect(() => { (async () => { try { const me = await authAPI.me(); setUser(me); } catch { setUser(null); } finally { setBootChecked(true); } })(); }, []); useEffect(() => { if (!user) return; (async () => { setBusyMessage('Loading domains...'); try { const list = await loadDomains(isSuperAdmin); const first = list[0]?.domain || null; setSelectedDomain(first); if (first) { setBusyMessage('Refreshing quotas...'); await loadMailboxes(first, true); await loadHealthStatus(first); } } catch (err) { showToast(`Failed to load: ${err.message}`, 'error'); } finally { setBusyMessage(''); } })(); // eslint-disable-next-line react-hooks/exhaustive-deps }, [user]); const selectDomain = async (domain) => { if (domain === selectedDomain) return; setSelectedDomain(domain); setBusyMessage('Loading mailboxes...'); try { await loadMailboxes(domain, true); await loadHealthStatus(domain); } catch (err) { showToast(`Failed to load mailboxes: ${err.message}`, 'error'); } finally { setBusyMessage(''); } }; const handleResync = async () => { setBusyMessage('Re-syncing from DMS...'); try { await domainsAPI.resync(); const list = await loadDomains(false); if (selectedDomain && !list.find((d) => d.domain === selectedDomain)) { const first = list[0]?.domain || null; setSelectedDomain(first); if (first) { await loadMailboxes(first, false); await loadHealthStatus(first); } } else if (selectedDomain) { await loadMailboxes(selectedDomain, false); } showToast('DMS sync complete', 'success'); } catch (err) { showToast(`Sync failed: ${err.message}`, 'error'); } finally { setBusyMessage(''); } }; const handleLogout = async () => { try { await authAPI.logout(); } catch { /* ignore */ } setUser(null); setDomains([]); setMailboxes([]); setSelectedDomain(null); setHealthStatus(null); }; const handleDelete = async () => { if (!deleteTarget) return; try { await mailboxesAPI.remove(deleteTarget); showToast(`Deleted ${deleteTarget}`, 'success'); setDeleteTarget(null); await loadMailboxes(selectedDomain, false); await loadDomains(false); } catch (err) { showToast(`Delete failed: ${err.message}`, 'error'); } }; const handleMailboxCreated = async () => { setBusyMessage('Refreshing...'); try { await loadDomains(false); await loadMailboxes(selectedDomain, true); } finally { setBusyMessage(''); } }; const handleQuotaApplied = async () => { setBusyMessage('Refreshing quotas...'); try { await loadMailboxes(selectedDomain, true); } finally { setBusyMessage(''); } }; // Called by HealthModal after a fresh check completes — re-load the // persisted summary so the banner reflects the new state. const handleHealthChecked = async () => { if (selectedDomain) await loadHealthStatus(selectedDomain); }; if (!bootChecked) { return
Loading...
; } if (!user) { return ( <> {toast && setToast(null)} />} ); } return (

MailAdmin

{user.email} · {user.role}

{isSuperAdmin && ( <> )}
{!hideDomainList && (

Domains on this node

{isSuperAdmin ? 'Domains are discovered dynamically from DMS accounts.' : 'Your assigned domains.'}

{domains.length === 0 ? (

No domains available.

) : (
{domains.map((d) => { const active = d.domain === selectedDomain; return ( ); })}
)}
)}
{/* Health banner (shows only when last check found problems) */} setShowHealth(true)} />

{selectedDomain || 'Mailboxes'}

Create/delete mailboxes, reset passwords, edit rules. Quotas are refreshed when you open a domain.

{isSuperAdmin && ( )}
{mailboxes.length === 0 ? (

No mailboxes for this domain

{selectedDomain ? 'Click "New mailbox" to create the first one.' : 'Select a domain first.'}

) : (
{mailboxes.map((m) => ( ))}
Email Status Usage Updated Actions
{m.email_address}
{m.node_name}
{m.status} {new Date(m.updated_at).toLocaleString()}
)}
setSettingsTarget(null)} onToast={showToast} /> setShowNew(false)} onCreated={handleMailboxCreated} onToast={showToast} /> setPwTarget(null)} onToast={showToast} /> setDeleteTarget(null)} /> {isSuperAdmin && ( setShowAudit(false)} onToast={showToast} /> )} {isSuperAdmin && ( setShowAdmins(false)} onToast={showToast} /> )} {isSuperAdmin && ( setShowDomainQuota(false)} onApplied={handleQuotaApplied} onToast={showToast} /> )} {isSuperAdmin && ( setShowBilling(false)} onToast={showToast} /> )} setShowHealth(false)} onCheckedReport={handleHealthChecked} onToast={showToast} /> setShowChangePw(false)} onToast={showToast} /> {busyMessage && } {toast && setToast(null)} />}
); } export default App;