diff --git a/app/api/courrier/route.ts b/app/api/courrier/route.ts index e5ab7b8b..d77d2185 100644 --- a/app/api/courrier/route.ts +++ b/app/api/courrier/route.ts @@ -72,23 +72,14 @@ export async function GET(request: Request) { const adjustedStart = Math.min(start, mailbox.exists); const adjustedEnd = Math.min(end, mailbox.exists); - // Fetch messages from the current folder with content + // Fetch messages from the current folder const messages = await client.fetch(`${adjustedStart}:${adjustedEnd}`, { envelope: true, flags: true, - bodyStructure: true, - source: true, // Get the full email source - bodyParts: ['text/plain', 'text/html'] // Get both text and HTML content + bodyStructure: true }); for await (const message of messages) { - // Get the email content - let content = ''; - if (message.bodyParts) { - // Prefer HTML content if available - content = message.bodyParts.get('text/html')?.toString() || message.bodyParts.get('text/plain')?.toString() || ''; - } - result.push({ id: message.uid, from: message.envelope.from?.[0]?.address || '', @@ -100,14 +91,9 @@ export async function GET(request: Request) { starred: message.flags.has('\\Flagged'), folder: mailbox.path, hasAttachments: message.bodyStructure?.type === 'multipart', - flags: Array.from(message.flags), - content: content, - source: message.source?.toString() || '' // Include full email source for parsing + flags: Array.from(message.flags) }); } - - // Sort results by date, most recent first - result.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()); } return NextResponse.json({ diff --git a/app/courrier/page.tsx b/app/courrier/page.tsx index 93035c19..36152f31 100644 --- a/app/courrier/page.tsx +++ b/app/courrier/page.tsx @@ -500,32 +500,22 @@ export default function CourrierPage() { try { console.log('Checking for stored credentials...'); const response = await fetch('/api/courrier'); - const data = await response.json(); - if (!response.ok) { - console.log('API response error:', data); - if (data.error === 'No mail credentials found. Please configure your email account.') { + const errorData = await response.json(); + console.log('API response error:', errorData); + if (errorData.error === 'No stored credentials found') { console.log('No credentials found, redirecting to login...'); router.push('/courrier/login'); return; } - if (data.error === 'Unauthorized') { - console.log('User not authenticated, redirecting to auth...'); - router.push('/api/auth/signin'); - return; - } - setError(data.error || 'Failed to check credentials'); - setLoading(false); - return; + throw new Error(errorData.error || 'Failed to check credentials'); } - - // If we get here, credentials are valid and we have emails console.log('Credentials verified, loading emails...'); setLoading(false); loadEmails(); } catch (err) { console.error('Error checking credentials:', err); - setError('Failed to connect to mail server. Please try again later.'); + setError(err instanceof Error ? err.message : 'Failed to check credentials'); setLoading(false); } }; @@ -808,14 +798,7 @@ export default function CourrierPage() { // Sort emails by date (most recent first) const sortedEmails = useMemo(() => { return [...emails].sort((a, b) => { - const dateA = new Date(a.date); - const dateB = new Date(b.date); - // First sort by date - const dateDiff = dateB.getTime() - dateA.getTime(); - if (dateDiff !== 0) return dateDiff; - - // If dates are equal, maintain stable order using email ID - return Number(b.id) - Number(a.id); + return new Date(b.date).getTime() - new Date(a.date).getTime(); }); }, [emails]); @@ -836,7 +819,7 @@ export default function CourrierPage() { email.subject.toLowerCase().includes(query) || email.from.toLowerCase().includes(query) || email.to.toLowerCase().includes(query) || - (email.content || '').toLowerCase().includes(query) + email.content.toLowerCase().includes(query) ); }, [sortedEmails, searchQuery]); @@ -1038,7 +1021,33 @@ export default function CourrierPage() { {/* Scrollable content area */} - {renderEmailPreview(selectedEmail)} +
+ + + {selectedEmail.fromName?.charAt(0) || selectedEmail.from.charAt(0)} + + +
+

+ {selectedEmail.fromName} <{selectedEmail.from}> +

+

+ to {selectedEmail.to} +

+ {selectedEmail.cc && ( +

+ cc {selectedEmail.cc} +

+ )} +
+
+ {formatDate(new Date(selectedEmail.date))} +
+
+ +
+ {renderEmailContent(selectedEmail)} +
) : ( @@ -1073,7 +1082,7 @@ export default function CourrierPage() { const renderEmailListItem = (email: Email) => (
handleEmailSelect(email.id)} @@ -1087,29 +1096,31 @@ export default function CourrierPage() { />
-
-

- {email.subject || '(No subject)'} -

- +
+
+ + {email.fromName || email.from} + + {!email.read && ( + + )} +
+ {formatDate(new Date(email.date))}
-
- - {email.fromName || email.from} - - {!email.read && ( - - )} -
-
- {email.content ? ( - - ) : ( - 'No content available' - )} -
+

+ {email.subject || '(No subject)'} +

+ +
+
+ {email.starred && ( + + )} + {email.attachments && email.attachments.length > 0 && ( + + )}
); @@ -1369,63 +1380,23 @@ export default function CourrierPage() { ); }; - // Update searchEmails to just set the search query since filteredEmails is computed by useMemo const searchEmails = (query: string) => { - setSearchQuery(query); + setSearchQuery(query.trim()); }; const handleSearchChange = (e: React.ChangeEvent) => { - searchEmails(e.target.value); + const query = e.target.value; + setSearchQuery(query); }; const renderEmailPreview = (email: Email) => { if (!email) return null; return ( -
- {/* Email header section */} -
- - - {email.fromName?.charAt(0) || email.from.charAt(0)} - - -
-

- {email.fromName || email.from} <{email.from}> -

-

- to {email.to} -

- {email.cc && ( -

- cc {email.cc} -

- )} -
-
- {formatDate(new Date(email.date))} -
-
- - {/* Email content section */} -
+
+

{email.subject}

+
{renderEmailContent(email)}
- - {/* Attachments section */} - {email.attachments && email.attachments.length > 0 && ( -
-

Attachments

-
- {email.attachments.map((attachment, index) => ( -
- - {attachment.name} -
- ))} -
-
- )}
); }; diff --git a/components/email.tsx b/components/email.tsx index 657ebbd3..d12a9493 100644 --- a/components/email.tsx +++ b/components/email.tsx @@ -47,7 +47,7 @@ export function Email() { if (!isRefresh) setLoading(true); try { - const response = await fetch('/api/courrier?folder=INBOX&page=1&limit=5'); + const response = await fetch('/api/mail'); if (!response.ok) { const errorData = await response.json(); @@ -60,8 +60,8 @@ export function Email() { throw new Error(data.error); } - const validatedEmails = (data.emails || []).map((email: any) => ({ - id: email.id?.toString() || Date.now().toString(), + const validatedEmails = data.emails.map((email: any) => ({ + id: email.id || Date.now().toString(), subject: email.subject || '(No subject)', from: email.from || '', fromName: email.fromName || email.from?.split('@')[0] || 'Unknown', @@ -72,7 +72,7 @@ export function Email() { })); setEmails(validatedEmails); - setMailUrl('/courrier'); + setMailUrl(data.mailUrl || 'https://espace.slm-lab.net/apps/courrier/'); setError(null); } catch (err) { setError(err instanceof Error ? err.message : 'Error fetching emails'); @@ -169,7 +169,7 @@ export function Email() {
router.push('/courrier')} + onClick={() => router.push('/mail')} >
diff --git a/lib/imap.ts b/lib/imap.ts index 1f15788e..42ac66c3 100644 --- a/lib/imap.ts +++ b/lib/imap.ts @@ -54,12 +54,12 @@ export async function markAsRead(emailIds: (string | number)[], isRead: boolean) const imap = await getImapClient(); try { await imap.connect(); - const lock = await imap.getMailboxLock('INBOX'); - try { - for (const id of emailIds) { + const lock = await imap.getMailboxLock('INBOX'); + try { + for (const id of emailIds) { const stringId = typeof id === 'number' ? id.toString() : id; const message = await imap.fetchOne(stringId, { uid: true }); - if (message) { + if (message) { const uid = typeof message.uid === 'number' ? message.uid.toString() : message.uid; await imap.messageFlagsAdd(uid, isRead ? ['\\Seen'] : [], { uid: true }); }