diff --git a/app/email/page.tsx b/app/email/page.tsx new file mode 100644 index 00000000..27daeca8 --- /dev/null +++ b/app/email/page.tsx @@ -0,0 +1,23 @@ +import { getServerSession } from "next-auth/next"; +import { authOptions } from "@/app/api/auth/[...nextauth]/route"; +import { redirect } from "next/navigation"; +import { ResponsiveIframe } from "@/app/components/responsive-iframe"; + +export default async function Page() { + const session = await getServerSession(authOptions); + + if (!session) { + redirect("/signin"); + } + + return ( +
+
+ +
+
+ ); +} \ No newline at end of file diff --git a/app/page.tsx b/app/page.tsx index 8f3384b4..2bf7c383 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,31 +1,60 @@ "use client"; +import { QuoteCard } from "@/components/quote-card"; +import { Calendar } from "@/components/calendar"; import { News } from "@/components/news"; import { Duties } from "@/components/flow"; +import { Email } from "@/components/email"; import { Parole } from "@/components/parole"; import { useSession } from "next-auth/react"; import { useEffect, useState } from "react"; export default function Home() { - const { data: session } = useSession(); + const { data: session, status } = useSession(); const [isLoading, setIsLoading] = useState(true); useEffect(() => { - if (session) { + if (status !== "loading") { setIsLoading(false); } - }, [session]); + }, [status]); if (isLoading) { - return
Loading...
; + return ( +
+
+
+ ); } return ( -
-
- - - +
+
+ {/* First row */} +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ + {/* Second row */} +
+
+ +
+
+ +
+
); diff --git a/components/email.tsx b/components/email.tsx new file mode 100644 index 00000000..b91ca50b --- /dev/null +++ b/components/email.tsx @@ -0,0 +1,192 @@ +"use client"; + +import { useEffect, useState } from "react"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Button } from "@/components/ui/button"; +import { RefreshCw, Mail } from "lucide-react"; +import { useSession } from "next-auth/react"; +import { formatDistance } from 'date-fns/formatDistance'; +import { fr } from 'date-fns/locale/fr'; +import { useRouter } from "next/navigation"; + +interface Email { + id: string; + subject: string; + from: string; + fromName?: string; + date: string; + read: boolean; + starred: boolean; + folder: string; +} + +interface EmailResponse { + emails: Email[]; + mailUrl: string; + error?: string; +} + +export function Email() { + const [emails, setEmails] = useState([]); + const [mailUrl, setMailUrl] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const [refreshing, setRefreshing] = useState(false); + const { data: session, status } = useSession(); + const router = useRouter(); + + const fetchEmails = async (isRefresh = false) => { + if (status !== 'authenticated') { + setError('Please sign in to view emails'); + setLoading(false); + setRefreshing(false); + return; + } + + if (isRefresh) setRefreshing(true); + if (!isRefresh) setLoading(true); + + try { + const response = await fetch('/api/mail'); + + if (!response.ok) { + const errorData = await response.json(); + throw new Error(errorData.error || 'Failed to fetch emails'); + } + + const data = await response.json(); + + if (data.error) { + throw new Error(data.error); + } + + 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', + date: email.date || new Date().toISOString(), + read: !!email.read, + starred: !!email.starred, + folder: email.folder || 'INBOX' + })); + + setEmails(validatedEmails); + setMailUrl(data.mailUrl || 'https://espace.slm-lab.net/apps/mail/'); + setError(null); + } catch (err) { + setError(err instanceof Error ? err.message : 'Error fetching emails'); + setEmails([]); + } finally { + setLoading(false); + setRefreshing(false); + } + }; + + // Initial fetch + useEffect(() => { + if (status === 'authenticated') { + fetchEmails(); + } else if (status === 'unauthenticated') { + setError('Please sign in to view emails'); + setLoading(false); + } + }, [status]); + + // Auto-refresh every 5 minutes + useEffect(() => { + if (status !== 'authenticated') return; + + const interval = setInterval(() => { + fetchEmails(true); + }, 5 * 60 * 1000); + + return () => clearInterval(interval); + }, [status]); + + const formatDate = (dateString: string) => { + try { + const date = new Date(dateString); + return formatDistance(date, new Date(), { + addSuffix: true, + locale: fr + }); + } catch (err) { + return dateString; + } + }; + + if (status === 'loading' || loading) { + return ( + + + +
+ + Mail +
+
+
+ +
+ +
+
+
+ ); + } + + return ( + + + +
+ + Mail +
+
+ +
+ + {error ? ( +

{error}

+ ) : ( +
+ {emails.length === 0 ? ( +

+ {loading ? 'Loading emails...' : 'No unread emails'} +

+ ) : ( + emails.map((email) => ( +
router.push('/mail')} + > +
+ + {email.fromName || email.from} + +
+ {!email.read && } + {formatDate(email.date)} +
+
+

{email.subject}

+
+ )) + )} +
+ )} +
+
+ ); +} \ No newline at end of file diff --git a/components/main-nav.tsx b/components/main-nav.tsx index a6c06684..303c36cb 100644 --- a/components/main-nav.tsx +++ b/components/main-nav.tsx @@ -21,7 +21,6 @@ import { Lightbulb, Circle, Menu, - Activity, } from "lucide-react"; import Image from "next/image"; import Link from "next/link"; @@ -181,29 +180,29 @@ export function MainNav() { // Role-specific menu items const roleSpecificItems = [ { - title: 'HealthView', - href: '/healthview', - icon: Activity, - roles: ['admin', 'user'] + title: "ShowCase", + icon: Lightbulb, + href: '/showcase', + requiredRoles: ["Expression"], }, { - title: 'MissionView', - href: '/missionview', - icon: Target, - roles: ['admin', 'user'] + title: "UsersView", + icon: UserCog, + href: '/management', + requiredRoles: ["Admin", "Entrepreneurship"], }, { - title: 'Announcement', - href: '/announcement', - icon: Megaphone, - roles: ['admin', 'user'] - } + title: "TheMessage", + icon: Mail, + href: '/the-message', + requiredRoles: ["Mediation", "Expression"], + }, ]; // Get visible menu items based on user roles const visibleMenuItems = [ ...baseMenuItems, - ...roleSpecificItems.filter(item => hasRole(item.roles)) + ...roleSpecificItems.filter(item => hasRole(item.requiredRoles)) ]; // Format current date and time