From 60eb2efb204cc173496c42bef96cf17ff38156cd Mon Sep 17 00:00:00 2001 From: alma Date: Thu, 15 Jan 2026 23:34:25 +0100 Subject: [PATCH] widget courrier refactor --- components/email.tsx | 157 +++++++++++++++++++++++++++++++++---------- 1 file changed, 123 insertions(+), 34 deletions(-) diff --git a/components/email.tsx b/components/email.tsx index 4deab4f..9418e65 100644 --- a/components/email.tsx +++ b/components/email.tsx @@ -1,10 +1,14 @@ "use client"; import { useEffect, useState } from "react"; +import { useSession } from "next-auth/react"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { RefreshCw, MessageSquare, Mail, MailOpen, Loader2 } from "lucide-react"; import Link from 'next/link'; +import { useUnifiedRefresh } from "@/hooks/use-unified-refresh"; +import { REFRESH_INTERVALS } from "@/lib/constants/refresh-intervals"; +import { Badge } from "@/components/ui/badge"; interface Email { id: string; @@ -24,21 +28,28 @@ interface EmailResponse { } export function Email() { + const { data: session, status } = useSession(); const [emails, setEmails] = useState([]); const [loading, setLoading] = useState(false); + const [refreshing, setRefreshing] = useState(false); const [error, setError] = useState(null); const [mailUrl, setMailUrl] = useState(null); const [accounts, setAccounts] = useState>([]); + const [unreadCount, setUnreadCount] = useState(0); + const [accountErrors, setAccountErrors] = useState>({}); useEffect(() => { - loadAccounts(); - }, []); - - useEffect(() => { - if (accounts.length > 0) { - fetchEmails(); + if (status === 'authenticated') { + loadAccounts(); } - }, [accounts]); + }, [status]); + + useEffect(() => { + if (accounts.length > 0 && status === 'authenticated') { + fetchEmails(false); + fetchUnreadCount(); + } + }, [accounts, status]); const loadAccounts = async () => { try { @@ -57,32 +68,50 @@ export function Email() { } }; - const fetchEmails = async (isRefresh = false) => { - setLoading(true); + const fetchEmails = async (forceRefresh = false) => { + // Only show loading spinner on initial load, not on auto-refresh + if (!emails.length) { + setLoading(true); + } + setRefreshing(true); setError(null); + setAccountErrors({}); try { // Fetch emails from all accounts in parallel const emailPromises = accounts.map(async (account) => { - const url = `/api/courrier?folder=INBOX&page=1&perPage=5&accountId=${encodeURIComponent(account.id)}${isRefresh ? '&refresh=true' : ''}`; - const response = await fetch(url); - if (!response.ok) { - console.warn(`Failed to fetch emails for account ${account.id}`); + try { + const url = `/api/courrier?folder=INBOX&page=1&perPage=5&accountId=${encodeURIComponent(account.id)}${forceRefresh ? '&refresh=true' : ''}`; + const response = await fetch(url); + if (!response.ok) { + const errorMsg = `Failed to fetch emails for ${account.email}`; + console.warn(errorMsg); + setAccountErrors(prev => ({ ...prev, [account.id]: errorMsg })); + return []; + } + const data = await response.json(); + if (data.error || !data.emails) { + const errorMsg = data.error || `No emails returned for ${account.email}`; + setAccountErrors(prev => ({ ...prev, [account.id]: errorMsg })); + return []; + } + // Add accountId to each email for proper identification + return data.emails.map((email: any) => ({ + ...email, + accountId: account.id + })); + } catch (err) { + const errorMsg = `Error fetching emails for ${account.email}: ${err instanceof Error ? err.message : 'Unknown error'}`; + console.error(errorMsg, err); + setAccountErrors(prev => ({ ...prev, [account.id]: errorMsg })); return []; } - const data = await response.json(); - if (data.error || !data.emails) { - return []; - } - // Add accountId to each email for proper identification - return data.emails.map((email: any) => ({ - ...email, - accountId: account.id - })); }); - const allEmailsArrays = await Promise.all(emailPromises); - const allEmails = allEmailsArrays.flat(); + const allEmailsArrays = await Promise.allSettled(emailPromises); + const allEmails = allEmailsArrays + .filter((result): result is PromiseFulfilledResult => result.status === 'fulfilled') + .flatMap(result => result.value); // Transform and sort all emails const transformedEmails = allEmails @@ -103,15 +132,65 @@ export function Email() { setEmails(transformedEmails); setMailUrl('/courrier'); + + // Show error only if all accounts failed + if (allEmails.length === 0 && accounts.length > 0 && Object.keys(accountErrors).length === accounts.length) { + setError('Failed to load emails from all accounts'); + } } catch (error) { console.error('Error fetching emails:', error); - setError('Failed to load emails'); + setError(error instanceof Error ? error.message : 'Failed to load emails'); setEmails([]); } finally { setLoading(false); + setRefreshing(false); } }; + const fetchUnreadCount = async () => { + try { + const response = await fetch('/api/courrier/unread-counts'); + if (response.ok) { + const data = await response.json(); + if (data && typeof data === 'object') { + // Sum up unread counts from all accounts and folders + let totalUnread = 0; + Object.values(data).forEach((accountCounts: any) => { + if (typeof accountCounts === 'object') { + Object.values(accountCounts).forEach((count: any) => { + if (typeof count === 'number') { + totalUnread += count; + } + }); + } + }); + setUnreadCount(totalUnread); + } + } + } catch (err) { + console.error('Error fetching unread count:', err); + // Don't set error state for unread count failures + } + }; + + // Integrate unified refresh for automatic polling + const { refresh } = useUnifiedRefresh({ + resource: 'email', + interval: REFRESH_INTERVALS.EMAIL, // 1 minute + enabled: status === 'authenticated', + onRefresh: async () => { + await fetchEmails(false); // Use cache for auto-refresh + await fetchUnreadCount(); + }, + priority: 'medium', + }); + + // Manual refresh handler (bypasses cache) + const handleManualRefresh = async () => { + await fetchEmails(true); // Force refresh, bypass cache + await fetchUnreadCount(); + }; + const formatDate = (dateString: string) => { try { const date = new Date(dateString); @@ -132,23 +211,33 @@ export function Email() { Courrier + {unreadCount > 0 && ( + + {unreadCount} + + )} {error ? ( -
- {error} +
+

{error}

+ {Object.keys(accountErrors).length > 0 && ( +
+ {Object.entries(accountErrors).map(([accountId, errMsg]) => ( +

{errMsg}

+ ))} +
+ )}
) : loading && emails.length === 0 ? (
@@ -157,7 +246,7 @@ export function Email() {
) : emails.length === 0 ? (
-

Aucun email non lu

+

Aucun email

) : (