-
Hello {accessTokenPayload["preferred_username"]}
To start with, select an application in the side bar
diff --git a/packages/diracx-web-components/components/DashboardLayout/ApplicationDialog.stories.tsx b/packages/diracx-web-components/components/DashboardLayout/ApplicationDialog.stories.tsx
index f1a6320e..9931a9b6 100644
--- a/packages/diracx-web-components/components/DashboardLayout/ApplicationDialog.stories.tsx
+++ b/packages/diracx-web-components/components/DashboardLayout/ApplicationDialog.stories.tsx
@@ -1,13 +1,12 @@
import React from "react";
import type { Meta, StoryObj } from "@storybook/react";
-import { ThemeProvider } from "@mui/material";
import { useArgs } from "@storybook/core/preview-api";
import { ApplicationsContext } from "../../contexts/ApplicationsProvider";
import { useOidcAccessToken } from "../../mocks/react-oidc.mock";
import { applicationList } from "../ApplicationList";
-import { useMUITheme } from "../../hooks/theme";
+import { ThemeProvider } from "../../contexts/ThemeProvider";
import ApplicationDialog from "./ApplicationDialog";
const meta = {
@@ -31,9 +30,8 @@ const meta = {
);
},
(Story) => {
- const theme = useMUITheme();
return (
-
+
);
diff --git a/packages/diracx-web-components/components/DashboardLayout/Dashboard.tsx b/packages/diracx-web-components/components/DashboardLayout/Dashboard.tsx
index 3949dfba..6b561739 100644
--- a/packages/diracx-web-components/components/DashboardLayout/Dashboard.tsx
+++ b/packages/diracx-web-components/components/DashboardLayout/Dashboard.tsx
@@ -2,16 +2,15 @@
import React from "react";
import AppBar from "@mui/material/AppBar";
import Box from "@mui/material/Box";
-import CssBaseline from "@mui/material/CssBaseline";
import IconButton from "@mui/material/IconButton";
import { Menu } from "@mui/icons-material";
import Toolbar from "@mui/material/Toolbar";
import Stack from "@mui/material/Stack";
-import { ThemeProvider as MUIThemeProvider } from "@mui/material/styles";
+import { Typography, useMediaQuery, useTheme } from "@mui/material";
import { ProfileButton } from "./ProfileButton";
import { ThemeToggleButton } from "./ThemeToggleButton";
import DashboardDrawer from "./DashboardDrawer";
-import { useMUITheme } from "@/hooks/theme";
+import { useApplicationTitle, useApplicationType } from "@/hooks/application";
interface DashboardProps {
/** The content to be displayed in the main area */
@@ -30,85 +29,123 @@ interface DashboardProps {
* @return an dashboard layout
*/
export default function Dashboard(props: DashboardProps) {
- const theme = useMUITheme();
+ const { children, drawerWidth = 240, logoURL } = props;
+
+ const appTitle = useApplicationTitle();
+ const appType = useApplicationType();
+
+ /** Theme and media query */
+ const theme = useTheme();
+ const isMobile = useMediaQuery(theme.breakpoints.down("sm"));
+
/** State management for mobile drawer */
const [mobileOpen, setMobileOpen] = React.useState(false);
const handleDrawerToggle = () => {
setMobileOpen(!mobileOpen);
};
- /** Drawer width */
- const drawerWidth = props.drawerWidth || 240;
-
return (
-
-
-
-
-
+
+
+
+ {isMobile && (
-
+
-
-
-
-
-
-
+ {appTitle}
+
+
+ {appType}
+
-
-
- {/* Here two types of drawers are rendered:
- 1. Temporary drawer: Visible on small screens (xs) and is collapsible.
- 2. Permanent drawer: Visible on larger screens (sm) and stays fixed.
- Depending on the screen size, only one will be visible at a time. */}
-
-
-
-
- {props.children}
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {children}
+
);
}
diff --git a/packages/diracx-web-components/components/DashboardLayout/DashboardDrawer.stories.tsx b/packages/diracx-web-components/components/DashboardLayout/DashboardDrawer.stories.tsx
index 7d1f633d..f9f02092 100644
--- a/packages/diracx-web-components/components/DashboardLayout/DashboardDrawer.stories.tsx
+++ b/packages/diracx-web-components/components/DashboardLayout/DashboardDrawer.stories.tsx
@@ -3,12 +3,11 @@ import type { Meta, StoryObj } from "@storybook/react";
import { Box } from "@mui/material";
import { Dashboard } from "@mui/icons-material";
-import { ThemeProvider as MUIThemeProvider } from "@mui/material/styles";
-import { useMUITheme } from "../../hooks/theme";
import { useOidc, useOidcAccessToken } from "../../mocks/react-oidc.mock";
import { ApplicationsContext } from "../../contexts/ApplicationsProvider";
import { applicationList } from "../ApplicationList";
import { DashboardGroup } from "../../types";
+import { ThemeProvider } from "../../contexts/ThemeProvider";
import DashboardDrawer from "./DashboardDrawer";
const meta = {
@@ -20,7 +19,6 @@ const meta = {
tags: ["autodocs"],
decorators: [
(Story) => {
- const theme = useMUITheme();
const [userDashboard, setUserDashboard] = React.useState<
DashboardGroup[]
>([
@@ -41,11 +39,11 @@ const meta = {
-
+
-
+
);
},
diff --git a/packages/diracx-web-components/components/DashboardLayout/DashboardDrawer.tsx b/packages/diracx-web-components/components/DashboardLayout/DashboardDrawer.tsx
index 6edcb0e0..d49b86b1 100644
--- a/packages/diracx-web-components/components/DashboardLayout/DashboardDrawer.tsx
+++ b/packages/diracx-web-components/components/DashboardLayout/DashboardDrawer.tsx
@@ -12,6 +12,7 @@ import {
Popover,
TextField,
Toolbar,
+ useTheme,
} from "@mui/material";
import { MenuBook, Add, SvgIconComponent } from "@mui/icons-material";
import React, {
@@ -25,7 +26,6 @@ import { extractClosestEdge } from "@atlaskit/pragmatic-drag-and-drop-hitbox/clo
import DrawerItemGroup from "./DrawerItemGroup";
import AppDialog from "./ApplicationDialog";
import { ApplicationsContext } from "@/contexts/ApplicationsProvider";
-import { useMUITheme } from "@/hooks/theme";
import { DashboardGroup } from "@/types";
interface DashboardDrawerProps {
@@ -70,6 +70,8 @@ export default function DashboardDrawer(props: DashboardDrawerProps) {
const [popAnchorEl, setPopAnchorEl] = React.useState
(
null,
);
+ const [renamingItemId, setRenamingItemId] = useState(null);
+ const [renamingGroupId, setRenamingGroupId] = useState(null);
const [renameValue, setRenameValue] = React.useState("");
// Define the applications that are accessible to users.
@@ -78,7 +80,7 @@ export default function DashboardDrawer(props: DashboardDrawerProps) {
const logoURL = props.logoURL || "/DIRAC-logo.png";
- const theme = useMUITheme();
+ const theme = useTheme();
useEffect(() => {
// Handle changes to app instances when drag and drop occurs.
@@ -306,8 +308,14 @@ export default function DashboardDrawer(props: DashboardDrawerProps) {
handleCloseContextMenu();
};
- const handleRenameClick = (event: React.MouseEvent) => {
- setPopAnchorEl(event.currentTarget);
+ const handleRenameClick = () => {
+ if (contextState.type === "group") {
+ setRenamingGroupId(contextState.id);
+ } else if (contextState.type === "item") {
+ setRenamingItemId(contextState.id);
+ }
+ setRenameValue("");
+ handleCloseContextMenu();
};
const popClose = () => {
@@ -394,6 +402,12 @@ export default function DashboardDrawer(props: DashboardDrawerProps) {
group={group}
setUserDashboard={setUserDashboard}
handleContextMenu={handleContextMenu}
+ renamingGroupId={renamingGroupId}
+ setRenamingGroupId={setRenamingGroupId}
+ renamingItemId={renamingItemId}
+ setRenamingItemId={setRenamingItemId}
+ renameValue={renameValue}
+ setRenameValue={setRenameValue}
/>
))}
@@ -443,8 +457,9 @@ export default function DashboardDrawer(props: DashboardDrawerProps) {
{contextState.type && (
)}
-
-
+ {contextState.type === null && (
+
+ )}
{
- const theme = useMUITheme();
return (
-
+
-
+
);
},
],
diff --git a/packages/diracx-web-components/components/DashboardLayout/DrawerItem.tsx b/packages/diracx-web-components/components/DashboardLayout/DrawerItem.tsx
index 994d4aa2..4782a9e3 100644
--- a/packages/diracx-web-components/components/DashboardLayout/DrawerItem.tsx
+++ b/packages/diracx-web-components/components/DashboardLayout/DrawerItem.tsx
@@ -5,6 +5,8 @@ import {
ListItemIcon,
Icon,
ListItemText,
+ useTheme,
+ TextField,
} from "@mui/material";
import { DragIndicator } from "@mui/icons-material";
import { combine } from "@atlaskit/pragmatic-drag-and-drop/combine";
@@ -19,11 +21,10 @@ import {
extractClosestEdge,
} 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 as MUIThemeProvider } from "@mui/material/styles";
import { ThemeProvider } from "@/contexts/ThemeProvider";
-import { useMUITheme } from "@/hooks/theme";
import { useSearchParamsUtils } from "@/hooks/searchParamsUtils";
import { useApplicationId } from "@/hooks/application";
+import { DashboardGroup } from "@/types";
/**
* Represents a drawer item component.
@@ -34,6 +35,11 @@ export default function DrawerItem({
item: { title, id, icon },
index,
groupTitle,
+ renamingItemId,
+ setRenamingItemId,
+ renameValue,
+ setRenameValue,
+ setUserDashboard,
}: {
/** The item object containing the title, id, and icon. */
item: { title: string; id: string; icon: React.ComponentType };
@@ -41,12 +47,22 @@ export default function DrawerItem({
index: number;
/** The title of the group. */
groupTitle: string;
+ /** The ID of the item being renamed. */
+ renamingItemId: string | null;
+ /** The function to set the renaming item ID. */
+ setRenamingItemId: React.Dispatch>;
+ /** The value of the rename input. */
+ renameValue: string;
+ /** The function to set the rename input value. */
+ setRenameValue: React.Dispatch>;
+ /** The function to set the user dashboard state. */
+ setUserDashboard: React.Dispatch>;
}) {
// Ref to use for the draggable element
const dragRef = React.useRef(null);
// Ref to use for the handle of the draggable element, must be a child of the draggable element
const handleRef = React.useRef(null);
- const theme = useMUITheme();
+ const theme = useTheme();
const { setParam } = useSearchParamsUtils();
// Represents the closest edge to the mouse cursor
const [closestEdge, setClosestEdge] = useState(null);
@@ -75,15 +91,13 @@ export default function DrawerItem({
// Wraps the preview in the theme provider to ensure the correct theme is applied
// This is necessary because the preview is rendered outside the main app
-
-
-
-
-
+
+
+
,
);
return () => root.unmount();
@@ -143,6 +157,26 @@ export default function DrawerItem({
);
}, [index, groupTitle, icon, theme, title, id]);
+ // Handle renaming of the item
+ const handleItemRename = () => {
+ if (renameValue.trim() === "") return;
+ setUserDashboard((groups) =>
+ groups.map((group) => {
+ if (group.title === groupTitle) {
+ return {
+ ...group,
+ items: group.items.map((item) =>
+ item.id === id ? { ...item, title: renameValue } : item,
+ ),
+ };
+ }
+ return group;
+ }),
+ );
+ setRenamingItemId(null);
+ setRenameValue("");
+ };
+
return (
<>
-
+ {renamingItemId === id ? (
+ setRenameValue(e.target.value)}
+ onBlur={handleItemRename}
+ onKeyDown={(e) => {
+ if (e.key === "Enter") {
+ handleItemRename();
+ } else if (e.key === "Escape") {
+ setRenamingItemId(null);
+ }
+ }}
+ autoFocus
+ size="small"
+ />
+ ) : (
+
+ )}
-
+
diff --git a/packages/diracx-web-components/components/DashboardLayout/DrawerItemGroup.stories.tsx b/packages/diracx-web-components/components/DashboardLayout/DrawerItemGroup.stories.tsx
index 798d2fbf..112fe598 100644
--- a/packages/diracx-web-components/components/DashboardLayout/DrawerItemGroup.stories.tsx
+++ b/packages/diracx-web-components/components/DashboardLayout/DrawerItemGroup.stories.tsx
@@ -2,11 +2,10 @@ import React from "react";
import type { Meta, StoryObj } from "@storybook/react";
import { useArgs } from "@storybook/core/preview-api";
import { Paper } from "@mui/material";
-import { ThemeProvider as MUIThemeProvider } from "@mui/material/styles";
import { Dashboard } from "@mui/icons-material";
-import { useMUITheme } from "../../hooks/theme";
import { useOidc, useOidcAccessToken } from "../../mocks/react-oidc.mock";
import { DashboardGroup } from "../../types/DashboardGroup";
+import { ThemeProvider } from "../../contexts/ThemeProvider";
import DrawerItemGroup from "./DrawerItemGroup";
const meta = {
@@ -18,13 +17,12 @@ const meta = {
tags: ["autodocs"],
decorators: [
(Story) => {
- const theme = useMUITheme();
return (
-
+
-
+
);
},
],
diff --git a/packages/diracx-web-components/components/DashboardLayout/DrawerItemGroup.tsx b/packages/diracx-web-components/components/DashboardLayout/DrawerItemGroup.tsx
index a2b07184..a918e3c8 100644
--- a/packages/diracx-web-components/components/DashboardLayout/DrawerItemGroup.tsx
+++ b/packages/diracx-web-components/components/DashboardLayout/DrawerItemGroup.tsx
@@ -1,5 +1,10 @@
"use client";
-import { Accordion, AccordionDetails, AccordionSummary } from "@mui/material";
+import {
+ Accordion,
+ AccordionDetails,
+ AccordionSummary,
+ TextField,
+} from "@mui/material";
import { ExpandMore, Apps } from "@mui/icons-material";
import React, { useEffect } from "react";
import { dropTargetForElements } from "@atlaskit/pragmatic-drag-and-drop/element/adapter";
@@ -17,6 +22,12 @@ export default function DrawerItemGroup({
group: { title, extended: expanded, items },
setUserDashboard,
handleContextMenu,
+ renamingGroupId,
+ setRenamingGroupId,
+ renamingItemId,
+ setRenamingItemId,
+ renameValue,
+ setRenameValue,
}: {
/** The group object containing the title, expanded state, and items. */
group: DashboardGroup;
@@ -27,6 +38,18 @@ export default function DrawerItemGroup({
type: "group" | "item" | null,
id: string | null,
) => (event: React.MouseEvent) => void;
+ /** The ID of the group being renamed. */
+ renamingGroupId: string | null;
+ /** The function to set the renaming group ID. */
+ setRenamingGroupId: React.Dispatch>;
+ /** The ID of the item being renamed. */
+ renamingItemId: string | null;
+ /** The function to set the renaming item ID. */
+ setRenamingItemId: React.Dispatch>;
+ /** The value of the rename input. */
+ renameValue: string;
+ /** The function to set the rename input value. */
+ setRenameValue: React.Dispatch>;
}) {
// Ref to use for the drag and drop target
const dropRef = React.useRef(null);
@@ -61,6 +84,19 @@ export default function DrawerItemGroup({
),
);
};
+
+ // Handle renaming of the group
+ const handleGroupRename = () => {
+ if (renameValue.trim() === "") return;
+ setUserDashboard((groups) =>
+ groups.map((group) =>
+ group.title === title ? { ...group, title: renameValue } : group,
+ ),
+ );
+ setRenamingGroupId(null);
+ setRenameValue("");
+ };
+
return (
{/* Accordion summary */}
- }>{title}
+ }>
+ {renamingGroupId === title ? (
+ setRenameValue(e.target.value)}
+ onBlur={handleGroupRename}
+ onKeyDown={(e) => {
+ if (e.key === "Enter") {
+ handleGroupRename();
+ } else if (e.key === "Escape") {
+ setRenamingGroupId(null);
+ }
+ }}
+ autoFocus
+ size="small"
+ />
+ ) : (
+ {title}
+ )}
+
{/* Accordion details */}
{items.map(({ title: itemTitle, id, icon }, index) => (
@@ -82,6 +137,11 @@ export default function DrawerItemGroup({
item={{ title: itemTitle, id, icon: icon || Apps }}
index={index}
groupTitle={title}
+ renamingItemId={renamingItemId}
+ setRenamingItemId={setRenamingItemId}
+ renameValue={renameValue}
+ setRenameValue={setRenameValue}
+ setUserDashboard={setUserDashboard}
/>
))}
diff --git a/packages/diracx-web-components/components/DashboardLayout/ProfileButton.stories.tsx b/packages/diracx-web-components/components/DashboardLayout/ProfileButton.stories.tsx
index 31f54922..8b919929 100644
--- a/packages/diracx-web-components/components/DashboardLayout/ProfileButton.stories.tsx
+++ b/packages/diracx-web-components/components/DashboardLayout/ProfileButton.stories.tsx
@@ -2,9 +2,8 @@ import React from "react";
import type { Meta, StoryObj } from "@storybook/react";
import { Paper } from "@mui/material";
-import { ThemeProvider as MUIThemeProvider } from "@mui/material/styles";
-import { useMUITheme } from "../../hooks/theme";
import { useOidc, useOidcAccessToken } from "../../mocks/react-oidc.mock";
+import { ThemeProvider } from "../../contexts/ThemeProvider";
import { ProfileButton } from "./ProfileButton";
const meta = {
@@ -16,13 +15,12 @@ const meta = {
tags: ["autodocs"],
decorators: [
(Story) => {
- const theme = useMUITheme();
return (
-