From 0ad05e08ce14fde839194f62f77d01028af9a557 Mon Sep 17 00:00:00 2001 From: alma Date: Sun, 4 May 2025 22:41:45 +0200 Subject: [PATCH] courrier --- components/main-nav.tsx | 151 ++++++++++++++++++---------------- components/sidebar.tsx | 79 ++++++++++-------- components/ui/scroll-area.tsx | 42 +++++----- 3 files changed, 147 insertions(+), 125 deletions(-) diff --git a/components/main-nav.tsx b/components/main-nav.tsx index b32b73b5..f2766bbe 100644 --- a/components/main-nav.tsx +++ b/components/main-nav.tsx @@ -1,6 +1,6 @@ "use client"; -import { useState } from "react"; +import { useState, useCallback, useMemo, memo } from "react"; import { Calendar, MessageSquare, @@ -50,7 +50,8 @@ const requestNotificationPermission = async () => { } }; -export function MainNav() { +// Use React.memo to memoize the entire component +export const MainNav = memo(function MainNav() { const [isSidebarOpen, setIsSidebarOpen] = useState(false); const { data: session, status } = useSession(); const [userStatus, setUserStatus] = useState<'online' | 'busy' | 'away'>('online'); @@ -59,11 +60,8 @@ export function MainNav() { // Use the unread announcements hook with memo to prevent unnecessary re-renders const { hasUnread } = useUnreadAnnouncements(); - console.log("Session:", session); - console.log("Status:", status); - - // Updated function to get user initials - const getUserInitials = () => { + // Updated function to get user initials - memoize this + const getUserInitials = useCallback(() => { if (session?.user?.name) { // Split the full name and get initials const names = session.user.name.split(' '); @@ -74,15 +72,15 @@ export function MainNav() { return names[0].slice(0, 2).toUpperCase(); } return "?"; - }; + }, [session?.user?.name]); - // Function to get display name - const getDisplayName = () => { + // Function to get display name - memoize this + const getDisplayName = useCallback(() => { return session?.user?.name || "User"; - }; + }, [session?.user?.name]); - // Function to get user role - const getUserRole = () => { + // Function to get user role - memoize this + const getUserRole = useCallback(() => { if (session?.user?.role) { if (Array.isArray(session.user.role)) { // Filter out technical roles and format remaining ones @@ -108,17 +106,15 @@ export function MainNav() { return session.user.role; } return ""; - }; + }, [session?.user?.role]); - // Function to check if user has a specific role - const hasRole = (requiredRoles: string[]) => { + // Function to check if user has a specific role - memoize this + const hasRole = useCallback((requiredRoles: string[]) => { if (!session?.user?.role) { - console.log('No user roles found'); return false; } const userRoles = Array.isArray(session.user.role) ? session.user.role : [session.user.role]; - console.log('Raw user roles:', userRoles); // Clean up user roles by removing prefixes and converting to lowercase const cleanUserRoles = userRoles.map(role => @@ -126,21 +122,18 @@ export function MainNav() { .replace(/^ROLE_/, '') // Remove ROLE_ prefix .toLowerCase() ); - console.log('Clean user roles:', cleanUserRoles); // Clean required roles const cleanRequiredRoles = requiredRoles.map(role => role.toLowerCase()); - console.log('Clean required roles:', cleanRequiredRoles); // Check if user has any of the required roles const hasAnyRole = cleanRequiredRoles.some(role => cleanUserRoles.includes(role)); - console.log('Has any role:', hasAnyRole); return hasAnyRole; - }; + }, [session?.user?.role]); // Status configurations - const statusConfig = { + const statusConfig = useMemo(() => ({ online: { color: 'text-green-500', label: 'Online', @@ -156,10 +149,10 @@ export function MainNav() { label: 'Away', notifications: false }, - }; + }), []); // Handle status change - const handleStatusChange = async (newStatus: 'online' | 'busy' | 'away') => { + const handleStatusChange = useCallback(async (newStatus: 'online' | 'busy' | 'away') => { setUserStatus(newStatus); if (newStatus !== 'online') { @@ -181,19 +174,19 @@ export function MainNav() { // Re-enable notifications if going back online requestNotificationPermission(); } - }; + }, []); // Base menu items (available for everyone) - const baseMenuItems = [ + const baseMenuItems = useMemo(() => [ { title: "QG", icon: Target, href: '/qg', }, - ]; + ], []); // Role-specific menu items - const roleSpecificItems = [ + const roleSpecificItems = useMemo(() => [ { title: "ShowCase", icon: Lightbulb, @@ -212,18 +205,64 @@ export function MainNav() { href: '/the-message', requiredRoles: ["mediation", "expression"], }, - ]; + ], []); // Get visible menu items based on user roles - const visibleMenuItems = [ + const visibleMenuItems = useMemo(() => [ ...baseMenuItems, ...roleSpecificItems.filter(item => hasRole(item.requiredRoles)) - ]; + ], [baseMenuItems, roleSpecificItems, hasRole]); // Format current date and time - const now = new Date(); - const formattedDate = format(now, "d MMMM yyyy", { locale: fr }); - const formattedTime = format(now, "HH:mm"); + const dateTimeDisplay = useMemo(() => { + const now = new Date(); + const formattedDate = format(now, "d MMMM yyyy", { locale: fr }); + const formattedTime = format(now, "HH:mm"); + return { formattedDate, formattedTime }; + }, []); + + // Handle sidebar toggle + const toggleSidebar = useCallback(() => { + setIsSidebarOpen(prev => !prev); + }, []); + + // Handle notes dialog + const toggleNotesDialog = useCallback(() => { + setIsNotesDialogOpen(prev => !prev); + }, []); + + // Handle logout + const handleLogout = useCallback(async () => { + try { + // First sign out from NextAuth + await signOut({ + callbackUrl: '/signin', + redirect: false + }); + + // Then redirect to Keycloak logout with proper parameters + const keycloakLogoutUrl = new URL( + `${process.env.NEXT_PUBLIC_KEYCLOAK_ISSUER}/protocol/openid-connect/logout` + ); + + // Add required parameters + keycloakLogoutUrl.searchParams.append( + 'post_logout_redirect_uri', + window.location.origin + ); + keycloakLogoutUrl.searchParams.append( + 'id_token_hint', + session?.accessToken || '' + ); + + // Redirect to Keycloak logout + window.location.href = keycloakLogoutUrl.toString(); + } catch (error) { + console.error('Error during logout:', error); + // Fallback to simple redirect if something goes wrong + window.location.href = '/signin'; + } + }, [session?.accessToken]); return ( <> @@ -232,7 +271,7 @@ export function MainNav() { {/* Left side */}
+
+ ))} + + ), [visibleMenuItems, pathname, handleNavigation]); return ( <> @@ -247,25 +268,15 @@ export function Sidebar({ isOpen, onClose }: SidebarProps) { /> - {/* Menu Items */} -
- {visibleMenuItems.map((item) => ( - - ))} + {/* Menu Items - Use memoized menu */} + {renderMenu} + + {/* Calendar Navigation */} +
+
); -} +}); diff --git a/components/ui/scroll-area.tsx b/components/ui/scroll-area.tsx index 0b4a48d8..c668a260 100644 --- a/components/ui/scroll-area.tsx +++ b/components/ui/scroll-area.tsx @@ -5,25 +5,8 @@ import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area" import { cn } from "@/lib/utils" -const ScrollArea = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, children, ...props }, ref) => ( - - - {children} - - - - -)) -ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName - -const ScrollBar = React.forwardRef< +// Memoize ScrollBar component to prevent unnecessary re-renders +const ScrollBar = React.memo(React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef >(({ className, orientation = "vertical", ...props }, ref) => ( @@ -42,7 +25,26 @@ const ScrollBar = React.forwardRef< > -)) +))) ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName +// Memoize ScrollArea component to prevent unnecessary re-renders +const ScrollArea = React.memo(React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + {children} + + + + +))) +ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName + export { ScrollArea, ScrollBar }