From edbb3f29119102e045a7c90edd0eb7cbae67229c Mon Sep 17 00:00:00 2001 From: theau Date: Tue, 13 May 2025 13:17:37 +0200 Subject: [PATCH 1/3] fix: export the hooks in application.tsx --- packages/diracx-web-components/src/hooks/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/diracx-web-components/src/hooks/index.ts b/packages/diracx-web-components/src/hooks/index.ts index c8e9222c..a0b2b44a 100644 --- a/packages/diracx-web-components/src/hooks/index.ts +++ b/packages/diracx-web-components/src/hooks/index.ts @@ -3,3 +3,4 @@ export * from "./oidcConfiguration"; export * from "./searchParamsUtils"; export * from "./theme"; export * from "./utils"; +export * from "./application"; From 2110a522984dc6b316931c32cb20d934f046e32f Mon Sep 17 00:00:00 2001 From: theau Date: Tue, 13 May 2025 14:35:04 +0200 Subject: [PATCH 2/3] chore(url): remove everything from the url --- .../DashboardLayout/DashboardDrawer.tsx | 4 +- .../components/DashboardLayout/DrawerItem.tsx | 10 +- .../src/components/Login/LoginForm.tsx | 31 +++-- .../src/contexts/ApplicationsProvider.tsx | 106 +++++------------- .../src/hooks/application.tsx | 15 +-- .../diracx-web-components/src/hooks/index.ts | 1 - .../test/LoginForm.test.tsx | 1 - .../diracx-web/src/app/(dashboard)/page.tsx | 7 +- 8 files changed, 52 insertions(+), 123 deletions(-) diff --git a/packages/diracx-web-components/src/components/DashboardLayout/DashboardDrawer.tsx b/packages/diracx-web-components/src/components/DashboardLayout/DashboardDrawer.tsx index 675a54ea..b9a23e74 100644 --- a/packages/diracx-web-components/src/components/DashboardLayout/DashboardDrawer.tsx +++ b/packages/diracx-web-components/src/components/DashboardLayout/DashboardDrawer.tsx @@ -77,7 +77,8 @@ export default function DashboardDrawer({ // Define the applications that are accessible to users. // Each application has an associated icon and path. - const [userDashboard, setUserDashboard] = useContext(ApplicationsContext); + const [userDashboard, setUserDashboard, , , setCurrentAppId] = + useContext(ApplicationsContext); const theme = useTheme(); @@ -247,6 +248,7 @@ export default function DashboardDrawer({ userDashboard.map((g) => (g.title === group.title ? group : g)), ); } + setCurrentAppId(newApp.id); }; let isContextStateStable = true; diff --git a/packages/diracx-web-components/src/components/DashboardLayout/DrawerItem.tsx b/packages/diracx-web-components/src/components/DashboardLayout/DrawerItem.tsx index 7a42e1ee..61940877 100644 --- a/packages/diracx-web-components/src/components/DashboardLayout/DrawerItem.tsx +++ b/packages/diracx-web-components/src/components/DashboardLayout/DrawerItem.tsx @@ -1,6 +1,6 @@ "use client"; -import React, { useEffect, useRef, useState } from "react"; +import React, { useContext, useEffect, useRef, useState } from "react"; import { createRoot } from "react-dom/client"; import { ListItemButton, @@ -24,8 +24,7 @@ import { } from "@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge"; import { setCustomNativeDragPreview } from "@atlaskit/pragmatic-drag-and-drop/element/set-custom-native-drag-preview"; import { ThemeProvider } from "../../contexts/ThemeProvider"; -import { useSearchParamsUtils } from "../../hooks/searchParamsUtils"; -import { useApplicationId } from "../../hooks/application"; +import { ApplicationsContext } from "../../contexts/ApplicationsProvider"; import { DashboardGroup } from "../../types"; interface DrawerItemProps { @@ -67,11 +66,10 @@ export default function DrawerItem({ // Ref to use for the handle of the draggable element, must be a child of the draggable element const handleRef = useRef(null); const theme = useTheme(); - const { setParam } = useSearchParamsUtils(); // Represents the closest edge to the mouse cursor const [closestEdge, setClosestEdge] = useState(null); - const appId = useApplicationId(); + const [, , , appId, setCurrentAppId] = useContext(ApplicationsContext); useEffect(() => { if (!dragRef.current || !handleRef.current) return; @@ -186,7 +184,7 @@ export default function DrawerItem({ setParam("appId", id)} + onClick={() => setCurrentAppId(id)} //setParam("appId", id)} sx={{ pl: 2, borderRadius: 2, pr: 1 }} ref={dragRef} selected={appId === id} diff --git a/packages/diracx-web-components/src/components/Login/LoginForm.tsx b/packages/diracx-web-components/src/components/Login/LoginForm.tsx index 3fbd696b..121cc3d3 100644 --- a/packages/diracx-web-components/src/components/Login/LoginForm.tsx +++ b/packages/diracx-web-components/src/components/Login/LoginForm.tsx @@ -1,6 +1,6 @@ "use client"; -import React, { useState, useEffect, useContext } from "react"; +import React, { useState, useEffect } from "react"; import Box from "@mui/material/Box"; import Typography from "@mui/material/Typography"; import FormControl from "@mui/material/FormControl"; @@ -15,8 +15,6 @@ import { useOidc } from "@axa-fr/react-oidc"; import { useMetadata, Metadata } from "../../hooks/metadata"; import { useOIDCContext } from "../../hooks/oidcConfiguration"; -import { useSearchParamsUtils } from "../../hooks/searchParamsUtils"; -import { NavigationContext } from "../../contexts/NavigationProvider"; import { useDiracxUrl } from "../../hooks"; interface LoginFormProps { @@ -32,7 +30,6 @@ interface LoginFormProps { export function LoginForm({ logoURL = "/DIRAC-logo-minimal.png", }: LoginFormProps) { - const { setPath } = useContext(NavigationContext); const diracxUrl = useDiracxUrl(); const { metadata, error, isLoading } = useMetadata(diracxUrl); const [selectedVO, setSelectedVO] = useState(null); @@ -40,8 +37,6 @@ export function LoginForm({ const { configuration, setConfiguration } = useOIDCContext(); const { isAuthenticated, login } = useOidc(configuration?.scope); - const { getParam } = useSearchParamsUtils(); - // Login if not authenticated useEffect(() => { if (configuration && configuration.scope && isAuthenticated === false) { @@ -50,18 +45,18 @@ export function LoginForm({ } }, [configuration, isAuthenticated, login]); - useEffect(() => { - // Redirect to dashboard if already authenticated - if (isAuthenticated) { - const redirect = getParam("redirect"); - console.log("Redirecting to:", redirect); - if (redirect) { - setPath(redirect); - } else { - setPath("/"); - } - } - }, [getParam, isAuthenticated, setPath]); + // useEffect(() => { + // // Redirect to dashboard if already authenticated + // if (isAuthenticated) { + // const redirect = getParam("redirect"); + // console.log("Redirecting to:", redirect); + // if (redirect) { + // setPath(redirect); + // } else { + // setPath("/"); + // } + // } + // }, [getParam, isAuthenticated, setPath]); // Get default group const getDefaultGroup = ( diff --git a/packages/diracx-web-components/src/contexts/ApplicationsProvider.tsx b/packages/diracx-web-components/src/contexts/ApplicationsProvider.tsx index ede1f5b4..436b3b9c 100644 --- a/packages/diracx-web-components/src/contexts/ApplicationsProvider.tsx +++ b/packages/diracx-web-components/src/contexts/ApplicationsProvider.tsx @@ -1,19 +1,10 @@ "use client"; -import React, { - createContext, - useCallback, - useMemo, - useEffect, - useState, -} from "react"; +import React, { createContext, useEffect, useState } from "react"; import { Monitor } from "@mui/icons-material"; -import JSONCrush from "jsoncrush"; -import { useSearchParamsUtils } from "../hooks/searchParamsUtils"; import { applicationList } from "../components/ApplicationList"; import { DashboardGroup } from "../types/DashboardGroup"; import ApplicationMetadata from "../types/ApplicationMetadata"; -import { DashboardItem } from "../types/DashboardItem"; // Create a context for the UserDashboard state export const ApplicationsContext = createContext< @@ -21,8 +12,10 @@ export const ApplicationsContext = createContext< DashboardGroup[], React.Dispatch>, ApplicationMetadata[], + string, // Id of the current application + React.Dispatch>, ] ->([[], () => {}, []]); +>([[], () => {}, [], "", () => {}]); interface ApplicationsProviderProps { children: React.ReactNode; @@ -45,78 +38,28 @@ export const ApplicationsProvider = ({ }: ApplicationsProviderProps) => { const [userDashboard, setUserDashboard] = useState([]); - const { getParam, setParam } = useSearchParamsUtils(); - - // save user dashboard to searchParams (but not icons) - const setUserDashboardParams = useCallback( - ( - groups: DashboardGroup[] | ((prev: DashboardGroup[]) => DashboardGroup[]), - ) => { - if (typeof groups === "function") { - groups = groups(userDashboard); - } - const newSections = groups.map((group) => { - return { - ...group, - items: group.items.map((item) => { - return { - ...item, - icon: () => null, - }; - }), - }; - }); - setParam("dashboard", JSONCrush.crush(JSON.stringify(newSections))); - }, - [setParam, userDashboard], - ); - - // get user sections from searchParams - const groupsParams = useMemo(() => getParam("dashboard"), [getParam]); + const [currentAppId, setCurrentAppId] = useState(""); useEffect(() => { if (userDashboard.length !== 0) return; - if (groupsParams) { - const uncrushed = JSONCrush.uncrush(groupsParams); - try { - const newSections: DashboardGroup[] = JSON.parse(uncrushed).map( - (group: DashboardGroup) => { - group.items = group.items.map((item: DashboardItem) => { - return { - ...item, - //get icon from appList - icon: - appList.find((app) => app.name === item.type)?.icon || null, - }; - }); - return group; - }, - ); - if (newSections !== userDashboard) { - setUserDashboard(newSections); - } - } catch (e) { - console.error("Error parsing user dashboard : ", uncrushed, e); - } - } else { - setUserDashboard( - defaultUserDashboard || [ - { - title: "My dashboard", - extended: true, - items: [ - { - title: "My Jobs", - type: "Job Monitor", - id: "JobMonitor0", - icon: Monitor, - }, - ], - }, - ], - ); - } - }, [appList, defaultUserDashboard, groupsParams]); + + setUserDashboard( + defaultUserDashboard || [ + { + title: "My dashboard", + extended: true, + items: [ + { + title: "My Jobs", + type: "Job Monitor", + id: "JobMonitor0", + icon: Monitor, + }, + ], + }, + ], + ); + }, [appList, defaultUserDashboard]); return ( { setUserDashboard(group); - setUserDashboardParams(group); }, appList, + currentAppId, + setCurrentAppId, ]} > {children} diff --git a/packages/diracx-web-components/src/hooks/application.tsx b/packages/diracx-web-components/src/hooks/application.tsx index 55e6f9c4..4f4bda69 100644 --- a/packages/diracx-web-components/src/hooks/application.tsx +++ b/packages/diracx-web-components/src/hooks/application.tsx @@ -2,18 +2,14 @@ import { useContext, useMemo } from "react"; import { ApplicationsContext } from "../contexts/ApplicationsProvider"; -import { useSearchParamsUtils } from "./searchParamsUtils"; /** - * Custom hook to access the application id from the URL + * Custom hook to access the application id from the context * @returns the application id */ export function useApplicationId() { - const { getParam } = useSearchParamsUtils(); - - return useMemo(() => { - return getParam("appId"); - }, [getParam]); + const [, , , appId] = useContext(ApplicationsContext); + return appId; } /** @@ -21,8 +17,7 @@ export function useApplicationId() { * @returns the application title */ export function useApplicationTitle() { - const [userDashboard] = useContext(ApplicationsContext); - const appId = useApplicationId(); + const [userDashboard, , , appId] = useContext(ApplicationsContext); return useMemo(() => { if (!userDashboard || !appId) return null; @@ -40,7 +35,7 @@ export function useApplicationTitle() { /** * Custom hook to access the application title based on the application id - * @returns the application title + * @returns the application type */ export function useApplicationType() { const [userDashboard] = useContext(ApplicationsContext); diff --git a/packages/diracx-web-components/src/hooks/index.ts b/packages/diracx-web-components/src/hooks/index.ts index a0b2b44a..c8e9222c 100644 --- a/packages/diracx-web-components/src/hooks/index.ts +++ b/packages/diracx-web-components/src/hooks/index.ts @@ -3,4 +3,3 @@ export * from "./oidcConfiguration"; export * from "./searchParamsUtils"; export * from "./theme"; export * from "./utils"; -export * from "./application"; diff --git a/packages/diracx-web-components/test/LoginForm.test.tsx b/packages/diracx-web-components/test/LoginForm.test.tsx index ab4f1075..c9055673 100644 --- a/packages/diracx-web-components/test/LoginForm.test.tsx +++ b/packages/diracx-web-components/test/LoginForm.test.tsx @@ -1,5 +1,4 @@ import { render, fireEvent, screen } from "@testing-library/react"; -import React from "react"; import { LoginForm } from "../src/components/Login/LoginForm"; import { ThemeProvider } from "../src/contexts/ThemeProvider"; import { useMetadata } from "../src/hooks/metadata"; diff --git a/packages/diracx-web/src/app/(dashboard)/page.tsx b/packages/diracx-web/src/app/(dashboard)/page.tsx index 055bc49d..699cb61d 100644 --- a/packages/diracx-web/src/app/(dashboard)/page.tsx +++ b/packages/diracx-web/src/app/(dashboard)/page.tsx @@ -1,6 +1,5 @@ "use client"; import { useContext, useMemo } from "react"; -import { useSearchParams } from "next/navigation"; import { BaseApp, applicationList, @@ -8,9 +7,7 @@ import { import { ApplicationsContext } from "@dirac-grid/diracx-web-components/contexts"; export default function Page() { - const searchParams = useSearchParams(); - const appId = searchParams.get("appId"); - const [userDashboard] = useContext(ApplicationsContext); + const [userDashboard, , , appId] = useContext(ApplicationsContext); const appType = useMemo(() => { const group = userDashboard.find((group) => @@ -23,5 +20,5 @@ export default function Page() { return applicationList.find((app) => app.name === appType)?.component; }, [appType]); - return Component ? : ; + return Component ? : ; } From 046fc6b9a0cd68952c549bc30b6f0572e6f5f96c Mon Sep 17 00:00:00 2001 From: theau Date: Tue, 13 May 2025 16:07:37 +0200 Subject: [PATCH 3/3] feat: clean the URL and save the dashboard in the sessionStorage --- .../components/DashboardLayout/DrawerItem.tsx | 15 +++++---- .../DashboardLayout/DrawerItemGroup.tsx | 32 ++++++++++--------- .../src/components/Login/LoginForm.tsx | 31 ++++++++++-------- .../src/contexts/ApplicationsProvider.tsx | 16 ++++++++-- .../src/types/DashboardItem.ts | 2 -- 5 files changed, 57 insertions(+), 39 deletions(-) diff --git a/packages/diracx-web-components/src/components/DashboardLayout/DrawerItem.tsx b/packages/diracx-web-components/src/components/DashboardLayout/DrawerItem.tsx index 61940877..e4129e92 100644 --- a/packages/diracx-web-components/src/components/DashboardLayout/DrawerItem.tsx +++ b/packages/diracx-web-components/src/components/DashboardLayout/DrawerItem.tsx @@ -10,7 +10,7 @@ import { useTheme, TextField, } from "@mui/material"; -import { DragIndicator } from "@mui/icons-material"; +import { DragIndicator, Apps } from "@mui/icons-material"; import { combine } from "@atlaskit/pragmatic-drag-and-drop/combine"; import { draggable, @@ -28,8 +28,8 @@ import { ApplicationsContext } from "../../contexts/ApplicationsProvider"; import { DashboardGroup } from "../../types"; interface DrawerItemProps { - /** The item object containing the title, id, and icon. */ - item: { title: string; id: string; icon: React.ComponentType }; + /** The item object containing the title, id, and the appType. */ + item: { title: string; id: string; type: string }; /** The index of the item. */ index: number; /** The title of the group. */ @@ -52,7 +52,7 @@ interface DrawerItemProps { * @returns The rendered JSX for the drawer item. */ export default function DrawerItem({ - item: { title, id, icon }, + item: { title, id, type }, index, groupTitle, renamingItemId, @@ -69,7 +69,10 @@ export default function DrawerItem({ // Represents the closest edge to the mouse cursor const [closestEdge, setClosestEdge] = useState(null); - const [, , , appId, setCurrentAppId] = useContext(ApplicationsContext); + const [, , appList, appId, setCurrentAppId] = useContext(ApplicationsContext); + const { icon } = appList.find((app) => app.name === type) || { + icon: Apps, + }; useEffect(() => { if (!dragRef.current || !handleRef.current) return; @@ -184,7 +187,7 @@ export default function DrawerItem({ setCurrentAppId(id)} //setParam("appId", id)} + onClick={() => setCurrentAppId(id)} sx={{ pl: 2, borderRadius: 2, pr: 1 }} ref={dragRef} selected={appId === id} diff --git a/packages/diracx-web-components/src/components/DashboardLayout/DrawerItemGroup.tsx b/packages/diracx-web-components/src/components/DashboardLayout/DrawerItemGroup.tsx index aaf543df..50076679 100644 --- a/packages/diracx-web-components/src/components/DashboardLayout/DrawerItemGroup.tsx +++ b/packages/diracx-web-components/src/components/DashboardLayout/DrawerItemGroup.tsx @@ -6,7 +6,7 @@ import { AccordionSummary, TextField, } from "@mui/material"; -import { ExpandMore, Apps } from "@mui/icons-material"; +import { ExpandMore } from "@mui/icons-material"; import React, { useEffect, useRef, useState } from "react"; import { dropTargetForElements } from "@atlaskit/pragmatic-drag-and-drop/element/adapter"; import { DashboardGroup } from "../../types/DashboardGroup"; @@ -139,20 +139,22 @@ export default function DrawerItemGroup({ {/* Accordion details */} - {items.map(({ title: itemTitle, id, icon }, index) => ( -
- -
- ))} + {items.map(({ title: itemTitle, id, type }, index) => { + return ( +
+ +
+ ); + })}
); diff --git a/packages/diracx-web-components/src/components/Login/LoginForm.tsx b/packages/diracx-web-components/src/components/Login/LoginForm.tsx index 121cc3d3..29e67c47 100644 --- a/packages/diracx-web-components/src/components/Login/LoginForm.tsx +++ b/packages/diracx-web-components/src/components/Login/LoginForm.tsx @@ -1,6 +1,6 @@ "use client"; -import React, { useState, useEffect } from "react"; +import React, { useState, useEffect, useContext } from "react"; import Box from "@mui/material/Box"; import Typography from "@mui/material/Typography"; import FormControl from "@mui/material/FormControl"; @@ -15,6 +15,8 @@ import { useOidc } from "@axa-fr/react-oidc"; import { useMetadata, Metadata } from "../../hooks/metadata"; import { useOIDCContext } from "../../hooks/oidcConfiguration"; +import { useSearchParamsUtils } from "../../hooks/searchParamsUtils"; +import { NavigationContext } from "../../contexts/NavigationProvider"; import { useDiracxUrl } from "../../hooks"; interface LoginFormProps { @@ -37,6 +39,9 @@ export function LoginForm({ const { configuration, setConfiguration } = useOIDCContext(); const { isAuthenticated, login } = useOidc(configuration?.scope); + const { getParam } = useSearchParamsUtils(); + const { setPath } = useContext(NavigationContext); + // Login if not authenticated useEffect(() => { if (configuration && configuration.scope && isAuthenticated === false) { @@ -45,18 +50,18 @@ export function LoginForm({ } }, [configuration, isAuthenticated, login]); - // useEffect(() => { - // // Redirect to dashboard if already authenticated - // if (isAuthenticated) { - // const redirect = getParam("redirect"); - // console.log("Redirecting to:", redirect); - // if (redirect) { - // setPath(redirect); - // } else { - // setPath("/"); - // } - // } - // }, [getParam, isAuthenticated, setPath]); + useEffect(() => { + // Redirect to dashboard if already authenticated + if (isAuthenticated) { + const redirect = getParam("redirect"); + console.log("Redirecting to:", redirect); + if (redirect) { + setPath(redirect); + } else { + setPath("/"); + } + } + }, [getParam, isAuthenticated, setPath]); // Get default group const getDefaultGroup = ( diff --git a/packages/diracx-web-components/src/contexts/ApplicationsProvider.tsx b/packages/diracx-web-components/src/contexts/ApplicationsProvider.tsx index 436b3b9c..c01dc17e 100644 --- a/packages/diracx-web-components/src/contexts/ApplicationsProvider.tsx +++ b/packages/diracx-web-components/src/contexts/ApplicationsProvider.tsx @@ -1,7 +1,6 @@ "use client"; import React, { createContext, useEffect, useState } from "react"; -import { Monitor } from "@mui/icons-material"; import { applicationList } from "../components/ApplicationList"; import { DashboardGroup } from "../types/DashboardGroup"; import ApplicationMetadata from "../types/ApplicationMetadata"; @@ -36,7 +35,14 @@ export const ApplicationsProvider = ({ appList = applicationList, defaultUserDashboard, }: ApplicationsProviderProps) => { - const [userDashboard, setUserDashboard] = useState([]); + const loadedDashboard = sessionStorage.getItem("savedDashboard"); + const parsedDashboard: DashboardGroup[] = loadedDashboard + ? JSON.parse(loadedDashboard) + : null; + + const [userDashboard, setUserDashboard] = useState( + parsedDashboard || [], + ); const [currentAppId, setCurrentAppId] = useState(""); @@ -53,7 +59,6 @@ export const ApplicationsProvider = ({ title: "My Jobs", type: "Job Monitor", id: "JobMonitor0", - icon: Monitor, }, ], }, @@ -61,6 +66,11 @@ export const ApplicationsProvider = ({ ); }, [appList, defaultUserDashboard]); + // Save the dashboard in session storage + useEffect(() => { + sessionStorage.setItem("savedDashboard", JSON.stringify(userDashboard)); + }, [userDashboard]); + return (