'use client'; import { AuthGuard } from '@/components/AuthGuard'; import { DashboardLayout } from '@/components/DashboardLayout'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { mailAccountsApi, MailAccount } from '@/lib/api'; import { Plus, Edit2, Trash2, CheckCircle, XCircle, AlertTriangle, Power, RefreshCw } from 'lucide-react'; import { useState } from 'react'; import Image from 'next/image'; import { AddMailAccountModal } from '@/components/AddMailAccountModal'; // Map provider_name (from backend) to the SVG icon filename in /public/providers/ const PROVIDER_ICON_MAP: Record = { 'Gmail': 'gmail', 'GMX': 'gmx', 'WEB.DE': 'webde', 'Outlook / Hotmail': 'outlook', 'Yahoo Mail': 'yahoo', 'AOL Mail': 'aol', 'T-Online': 'tonline', '1&1 / IONOS': 'ionos', 'Freenet': 'freenet', 'Posteo': 'posteo', 'mail.de': 'mailde', 'iCloud Mail': 'icloud', 'Proton Mail': 'protonmail', }; /** * Full-width logo banner rendered at the top of a card. * Uses next/image fill + object-contain so every logo – regardless of its * native aspect ratio (1:1 square up to ~6:1 wordmark) – fits correctly * inside the fixed-height strip without distortion. */ function ProviderLogoBanner({ providerName }: { providerName?: string | null }) { const icon = providerName ? PROVIDER_ICON_MAP[providerName] : undefined; if (!icon) return null; return (
{`${providerName}
); } export default function AccountsPage() { const [isModalOpen, setIsModalOpen] = useState(false); const [editingAccount, setEditingAccount] = useState(null); const [pullingIds, setPullingIds] = useState>(new Set()); const [successIds, setSuccessIds] = useState>(new Set()); const queryClient = useQueryClient(); const { data: accounts, isLoading } = useQuery({ queryKey: ['mail-accounts'], queryFn: mailAccountsApi.list, }); const deleteMutation = useMutation({ mutationFn: mailAccountsApi.delete, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['mail-accounts'] }); }, }); const toggleMutation = useMutation({ mutationFn: mailAccountsApi.toggle, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['mail-accounts'] }); }, }); const handleEdit = (account: MailAccount) => { setEditingAccount(account); setIsModalOpen(true); }; const handleDelete = async (id: number) => { if (confirm('Are you sure you want to delete this mail account?')) { try { await deleteMutation.mutateAsync(id); } catch { alert('Failed to delete account'); } } }; const handleToggle = async (id: number) => { try { await toggleMutation.mutateAsync(id); } catch { alert('Failed to update account'); } }; const handlePullNow = async (id: number) => { setPullingIds((prev) => new Set(prev).add(id)); try { await mailAccountsApi.pullNow(id); setSuccessIds((prev) => new Set(prev).add(id)); setTimeout(() => { setSuccessIds((prev) => { const next = new Set(prev); next.delete(id); return next; }); }, 2000); } catch { alert('Failed to queue pull'); } finally { setPullingIds((prev) => { const next = new Set(prev); next.delete(id); return next; }); } }; const handleCloseModal = () => { setIsModalOpen(false); setEditingAccount(null); }; return (

Mail Accounts

{isLoading ? (
) : accounts && accounts.length > 0 ? (
{accounts.map((account) => (
{/* Provider logo banner – full-width strip that accommodates any aspect ratio */}

{account.name}

{account.email_address}

{account.is_enabled ? ( Enabled ) : ( Disabled )}
Protocol: {account.protocol.toUpperCase()}
Host: {account.host}:{account.port}
SSL: {account.use_ssl ? 'Yes' : 'No'}
Interval: Every {account.check_interval_minutes} min
{account.last_check_at && (
Last checked: {new Date(account.last_check_at).toLocaleString()}
)} {account.last_error_message && (

{account.last_error_message}

)}
))}
) : (

No mail accounts configured yet

)}
{isModalOpen && ( )}
); }