diff --git a/app/courrier/page.tsx b/app/courrier/page.tsx index f3bcb455..7926deb6 100644 --- a/app/courrier/page.tsx +++ b/app/courrier/page.tsx @@ -36,6 +36,7 @@ import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuIte // Import components import EmailSidebar from '@/components/email/EmailSidebar'; import EmailList from '@/components/email/EmailList'; +import EmailSidebarContent from '@/components/email/EmailSidebarContent'; import EmailDetailView from '@/components/email/EmailDetailView'; import ComposeEmail from '@/components/email/ComposeEmail'; import { DeleteConfirmDialog } from '@/components/email/EmailDialogs'; @@ -742,29 +743,323 @@ export default function CourrierPage() {
{/* Panel 1: Sidebar - Always visible */} -
- { - const account = accounts.find(a => a.id === accountId); - if (account) { - setSelectedAccount(account); - changeFolder(folder); - } - }} - onRefresh={() => { - if (selectedAccount) { - loadEmails(selectedAccount.id, currentFolder); - } - }} - onCompose={() => { - setComposeType('new'); - setShowComposeModal(true); - }} - isLoading={isLoading} - /> +
+ {/* Courrier Title */} +
+
+ + COURRIER +
+
+ + {/* Compose button and refresh button */} +
+ + +
+ + {/* Scrollable area for accounts and folders */} +
+ {/* Accounts Section */} +
+
+ Accounts + +
+ + {/* Display all accounts */} +
+ {/* Form for adding a new account */} + {showAddAccountForm && ( +
+

Add IMAP Account

+
{ + e.preventDefault(); + setLoading(true); + + const formData = new FormData(e.currentTarget); + + // Pull values from form with proper type handling + const formValues = { + email: formData.get('email')?.toString() || '', + password: formData.get('password')?.toString() || '', + host: formData.get('host')?.toString() || '', + port: parseInt(formData.get('port')?.toString() || '993'), + secure: formData.get('secure') === 'on', + display_name: formData.get('display_name')?.toString() || '', + smtp_host: formData.get('smtp_host')?.toString() || '', + smtp_port: formData.get('smtp_port')?.toString() ? + parseInt(formData.get('smtp_port')?.toString() || '587') : undefined, + smtp_secure: formData.get('smtp_secure') === 'on' + }; + + // If display_name is empty, use email + if (!formValues.display_name) { + formValues.display_name = formValues.email; + } + + try { + // First test the connection + const testResponse = await fetch('/api/courrier/test-connection', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + email: formValues.email, + password: formValues.password, + host: formValues.host, + port: formValues.port, + secure: formValues.secure + }) + }); + + const testResult = await testResponse.json(); + + if (!testResponse.ok) { + throw new Error(testResult.error || 'Connection test failed'); + } + + console.log('Connection test successful:', testResult); + + // Only declare realAccounts once before using for color assignment + const realAccounts = accounts.filter(a => a.id !== 'loading-account'); + const saveResponse = await fetch('/api/courrier/account', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(formValues) + }); + const saveResult = await saveResponse.json(); + if (!saveResponse.ok) { + throw new Error(saveResult.error || 'Failed to add account'); + } + const realAccount = saveResult.account; + realAccount.color = colorPalette[realAccounts.length % colorPalette.length]; + realAccount.folders = testResult.details.sampleFolders || ['INBOX', 'Sent', 'Drafts', 'Trash']; + setAccounts(prev => [...prev, realAccount]); + setShowAddAccountForm(false); + toast({ + title: "Account added successfully", + description: `Your email account ${formValues.email} has been added.`, + duration: 5000 + }); + } catch (error) { + console.error('Error adding account:', error); + toast({ + title: "Failed to add account", + description: error instanceof Error ? error.message : 'Unknown error', + variant: "destructive", + duration: 5000 + }); + } finally { + setLoading(false); + } + }}> +
+ + + IMAP + SMTP + + + +
+ +
+
+ +
+
+ +
+
+ +
+
+
+ +
+
+
+ + +
+
+
+
+ + +
+ +
+
+
+ +
+
+
+ + +
+
+
+
+ Note: SMTP settings needed for sending emails +
+
+
+ +
+ + +
+
+
+
+ )} + + {accounts.map((account) => ( +
+
handleAccountSelect(account)} + tabIndex={0} + role="button" + onKeyDown={e => { if (e.key === 'Enter' || e.key === ' ') handleAccountSelect(account); }} + > +
+ {account.name} + {/* More options button (⋮) */} + {account.id !== 'loading-account' && ( + + + + + + { e.stopPropagation(); setAccountToEdit(account); setShowEditModal(true); }}> + Edit + + { e.stopPropagation(); setAccountToDelete(account); setShowDeleteDialog(true); }}> + Delete + + + + )} + {/* Expand/collapse arrow */} + {account.id !== 'loading-account' && ( + + )} +
+ {/* Show folders for any expanded account */} + {expandedAccounts[account.id] && account.folders && account.folders.length > 0 && ( +
+ {account.folders.map((folder) => renderFolderButton(folder, account.id))} +
+ )} +
+ ))} +
+
+
{/* Panel 2: Email List - Always visible */} diff --git a/components/email/EmailSidebarContent.tsx b/components/email/EmailSidebarContent.tsx new file mode 100644 index 00000000..2b2a6862 --- /dev/null +++ b/components/email/EmailSidebarContent.tsx @@ -0,0 +1,68 @@ +import React from 'react'; +import { + Inbox, Send, Star, Trash, Folder, + AlertOctagon, Archive, Edit +} from 'lucide-react'; +import { Button } from '@/components/ui/button'; + +interface EmailSidebarContentProps { + mailboxes: string[]; + currentFolder: string; + onFolderChange: (folder: string) => void; +} + +export default function EmailSidebarContent({ + mailboxes, + currentFolder, + onFolderChange +}: EmailSidebarContentProps) { + + // Helper to format folder names + const formatFolderName = (folder: string) => { + return folder.charAt(0).toUpperCase() + folder.slice(1).toLowerCase(); + }; + + // Helper to get folder icons + const getFolderIcon = (folder: string) => { + const folderLower = folder.toLowerCase(); + + if (folderLower.includes('inbox')) { + return ; + } else if (folderLower.includes('sent')) { + return ; + } else if (folderLower.includes('trash')) { + return ; + } else if (folderLower.includes('archive')) { + return ; + } else if (folderLower.includes('draft')) { + return ; + } else if (folderLower.includes('spam') || folderLower.includes('junk')) { + return ; + } else { + return ; + } + }; + + return ( + + ); +} \ No newline at end of file