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..e4129e92 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, @@ -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, @@ -24,13 +24,12 @@ 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 { - /** 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. */ @@ -53,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, @@ -67,11 +66,13 @@ 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 [, , appList, appId, setCurrentAppId] = useContext(ApplicationsContext); + const { icon } = appList.find((app) => app.name === type) || { + icon: Apps, + }; useEffect(() => { if (!dragRef.current || !handleRef.current) return; @@ -186,7 +187,7 @@ export default function DrawerItem({ 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 3fbd696b..29e67c47 100644 --- a/packages/diracx-web-components/src/components/Login/LoginForm.tsx +++ b/packages/diracx-web-components/src/components/Login/LoginForm.tsx @@ -32,7 +32,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); @@ -41,6 +40,7 @@ export function LoginForm({ const { isAuthenticated, login } = useOidc(configuration?.scope); const { getParam } = useSearchParamsUtils(); + const { setPath } = useContext(NavigationContext); // Login if not authenticated useEffect(() => { diff --git a/packages/diracx-web-components/src/contexts/ApplicationsProvider.tsx b/packages/diracx-web-components/src/contexts/ApplicationsProvider.tsx index ede1f5b4..c01dc17e 100644 --- a/packages/diracx-web-components/src/contexts/ApplicationsProvider.tsx +++ b/packages/diracx-web-components/src/contexts/ApplicationsProvider.tsx @@ -1,19 +1,9 @@ "use client"; -import React, { - createContext, - useCallback, - useMemo, - useEffect, - useState, -} from "react"; -import { Monitor } from "@mui/icons-material"; -import JSONCrush from "jsoncrush"; -import { useSearchParamsUtils } from "../hooks/searchParamsUtils"; +import React, { createContext, useEffect, useState } from "react"; 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 +11,10 @@ export const ApplicationsContext = createContext< DashboardGroup[], React.Dispatch>, ApplicationMetadata[], + string, // Id of the current application + React.Dispatch>, ] ->([[], () => {}, []]); +>([[], () => {}, [], "", () => {}]); interface ApplicationsProviderProps { children: React.ReactNode; @@ -43,80 +35,41 @@ 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 { 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], + const [userDashboard, setUserDashboard] = useState( + parsedDashboard || [], ); - // 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", + }, + ], + }, + ], + ); + }, [appList, defaultUserDashboard]); + + // Save the dashboard in session storage + useEffect(() => { + sessionStorage.setItem("savedDashboard", JSON.stringify(userDashboard)); + }, [userDashboard]); 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/types/DashboardItem.ts b/packages/diracx-web-components/src/types/DashboardItem.ts index 3b69eeaf..d059cf5e 100644 --- a/packages/diracx-web-components/src/types/DashboardItem.ts +++ b/packages/diracx-web-components/src/types/DashboardItem.ts @@ -1,6 +1,5 @@ "use client"; -import { SvgIconComponent } from "@mui/icons-material"; import { InternalFilter } from "./Filter"; // Define the type for the Dashboard Item state @@ -8,6 +7,5 @@ export interface DashboardItem { title: string; type: string; id: string; - icon: SvgIconComponent | null; data?: InternalFilter[]; } 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 ? : ; }