diff --git a/src/app/dashboard/jobmonitor/page.tsx b/src/app/dashboard/jobmonitor/page.tsx index 6e35f4b1..3fcbaf54 100644 --- a/src/app/dashboard/jobmonitor/page.tsx +++ b/src/app/dashboard/jobmonitor/page.tsx @@ -1,4 +1,4 @@ -import { JobDataGrid } from "@/components/data/JobDataGrid"; +import { JobDataGrid } from "@/components/ui/JobDataGrid"; export default async function Page() { return ( diff --git a/src/app/dashboard/page.tsx b/src/app/dashboard/page.tsx index f171f6c0..6d5d7447 100644 --- a/src/app/dashboard/page.tsx +++ b/src/app/dashboard/page.tsx @@ -1,7 +1,5 @@ +import UserDashboard from "@/components/applications/UserDashboard"; + export default function Page() { - return ( -
- Hello User -
- ); + return ; } diff --git a/src/app/globals.css b/src/app/globals.css index 05c1ecd9..e69de29b 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -1,23 +0,0 @@ -:root { - --foreground-rgb: 0, 0, 0; - --background-start-rgb: 214, 219, 220; - --background-end-rgb: 255, 255, 255; -} - -@media (prefers-color-scheme: dark) { - :root { - --foreground-rgb: 0, 0, 0; - --background-start-rgb: 255, 255, 255; - --background-end-rgb: 255, 255, 255; - } -} - -body { - color: rgb(var(--foreground-rgb)); - background: linear-gradient( - to bottom, - transparent, - rgb(var(--background-end-rgb)) - ) - rgb(var(--background-start-rgb)); -} diff --git a/src/app/layout.tsx b/src/app/layout.tsx index e4b1ce0a..657ccd2e 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,6 +1,6 @@ -import "./globals.css"; import { Inter } from "next/font/google"; import { OIDCProvider } from "@/components/auth/OIDCUtils"; +import { ThemeProvider } from "@/contexts/ThemeProvider"; const inter = Inter({ subsets: ["latin"] }); @@ -21,7 +21,9 @@ export default function RootLayout({ return ( - {children} + + {children} + ); diff --git a/src/components/applications/UserDashboard.tsx b/src/components/applications/UserDashboard.tsx new file mode 100644 index 00000000..6480d94d --- /dev/null +++ b/src/components/applications/UserDashboard.tsx @@ -0,0 +1,31 @@ +"use client"; +import * as React from "react"; +import CssBaseline from "@mui/material/CssBaseline"; +import { Box } from "@mui/material"; +import { useMUITheme } from "@/hooks/theme"; +import { ThemeProvider as MUIThemeProvider } from "@mui/material/styles"; + +/** + * Build the showcase content with an AppBar + * @param props - children + * @returns showcase content + */ +export default function UserDashboard() { + const theme = useMUITheme(); + + return ( + + + + + Hello User + + + + ); +} diff --git a/src/components/layout/Dashboard.tsx b/src/components/layout/Dashboard.tsx index 22b13d08..abee9897 100644 --- a/src/components/layout/Dashboard.tsx +++ b/src/components/layout/Dashboard.tsx @@ -9,6 +9,9 @@ import Toolbar from "@mui/material/Toolbar"; import Stack from "@mui/material/Stack"; import { LoginButton } from "../ui/LoginButton"; import DashboardDrawer from "../ui/DashboardDrawer"; +import { useMUITheme } from "@/hooks/theme"; +import { ThemeProvider as MUIThemeProvider } from "@mui/material/styles"; +import ThemeToggleButton from "../ui/ThemeToggleButton"; interface DashboardProps { children: React.ReactNode; @@ -22,6 +25,7 @@ interface DashboardProps { * @return an dashboard layout */ export default function Dashboard(props: DashboardProps) { + const theme = useMUITheme(); /** State management for mobile drawer */ const [mobileOpen, setMobileOpen] = React.useState(false); const handleDrawerToggle = () => { @@ -34,66 +38,70 @@ export default function Dashboard(props: DashboardProps) { return ( - - - - + + + + + + + + - - - - -
- - - - - - {/* Here two types of drawers are rendered: +
+ + + + + + + + + {/* 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} - + + + + + {props.children} + + ); } diff --git a/src/components/layout/Showcase.tsx b/src/components/layout/Showcase.tsx index 3e6737a8..9b6b2916 100644 --- a/src/components/layout/Showcase.tsx +++ b/src/components/layout/Showcase.tsx @@ -11,6 +11,9 @@ import { LoginButton } from "../ui/LoginButton"; import { Box, Stack } from "@mui/material"; import { DashboardButton } from "../ui/DashboardButton"; import Image from "next/image"; +import ThemeToggleButton from "../ui/ThemeToggleButton"; +import { useMUITheme } from "@/hooks/theme"; +import { ThemeProvider as MUIThemeProvider } from "@mui/material/styles"; /** * Build the showcase content with an AppBar @@ -18,6 +21,8 @@ import Image from "next/image"; * @returns showcase content */ export default function Showcase() { + const theme = useMUITheme(); + const Item = styled(Paper)(({ theme }) => ({ padding: theme.spacing(6), elevation: 0, @@ -25,73 +30,76 @@ export default function Showcase() { return ( - - - - - - - -
- - - - - - - - - - - - - The DIRAC interware is a software framework that enables - communities to interact with distributed computing resources. - DIRAC forms a layer between users and resources, hiding - diversities across computing and storage resources. - - - -
- DIRAC showcase1 -
-
- -
- DIRAC showcase 2 -
-
- - - DIRAC has been adopted by several HEP and non-HEP experiments - communities, with different goals, intents, resources and - workflows: it is experiment agnostic, extensible, and flexible. - + + + + + + + + +
+ + + + + + + + + + + + + + The DIRAC interware is a software framework that enables + communities to interact with distributed computing resources. + DIRAC forms a layer between users and resources, hiding + diversities across computing and storage resources. + + + +
+ DIRAC showcase1 +
+
+ +
+ DIRAC showcase 2 +
+
+ + + DIRAC has been adopted by several HEP and non-HEP experiments + communities, with different goals, intents, resources and + workflows: it is experiment agnostic, extensible, and flexible. + +
- -
+ + ); } diff --git a/src/components/ui/DashboardButton.tsx b/src/components/ui/DashboardButton.tsx index f29ff92a..a226b418 100644 --- a/src/components/ui/DashboardButton.tsx +++ b/src/components/ui/DashboardButton.tsx @@ -1,5 +1,6 @@ import { useOidcAccessToken } from "@axa-fr/react-oidc"; import { Button } from "@mui/material"; +import { deepOrange, lightGreen } from "@mui/material/colors"; import Link from "next/link"; /** @@ -14,7 +15,14 @@ export function DashboardButton() { } return ( - ); diff --git a/src/components/data/JobDataGrid.tsx b/src/components/ui/JobDataGrid.tsx similarity index 100% rename from src/components/data/JobDataGrid.tsx rename to src/components/ui/JobDataGrid.tsx diff --git a/src/components/ui/LoginButton.tsx b/src/components/ui/LoginButton.tsx index 4af5ffc7..4b6b29a0 100644 --- a/src/components/ui/LoginButton.tsx +++ b/src/components/ui/LoginButton.tsx @@ -12,7 +12,6 @@ import { Tooltip, } from "@mui/material"; import { deepOrange, lightGreen } from "@mui/material/colors"; -import NextLink from "next/link"; import React from "react"; /** diff --git a/src/components/ui/ThemeToggleButton.tsx b/src/components/ui/ThemeToggleButton.tsx new file mode 100644 index 00000000..62636e2f --- /dev/null +++ b/src/components/ui/ThemeToggleButton.tsx @@ -0,0 +1,16 @@ +import { IconButton } from "@mui/material"; +import DarkModeIcon from "@mui/icons-material/DarkMode"; +import LightModeIcon from "@mui/icons-material/LightMode"; +import { useTheme } from "@/hooks/theme"; + +const ThemeToggleButton: React.FC = () => { + const { theme, toggleTheme } = useTheme(); + + return ( + + {theme === "light" ? : } + + ); +}; + +export default ThemeToggleButton; diff --git a/src/contexts/ThemeProvider.tsx b/src/contexts/ThemeProvider.tsx new file mode 100644 index 00000000..c5968850 --- /dev/null +++ b/src/contexts/ThemeProvider.tsx @@ -0,0 +1,32 @@ +"use client"; +import { createContext, useState } from "react"; + +type ThemeContextType = { + theme: "light" | "dark"; + toggleTheme: () => void; +}; + +type ThemeProviderProps = { + children: React.ReactNode; +}; + +export const ThemeContext = createContext( + undefined, +); + +// ThemeProvider component to provide the theme context to its children +export const ThemeProvider = ({ children }: ThemeProviderProps) => { + // State to manage the current theme mode + const [theme, setTheme] = useState<"light" | "dark">("light"); + + // Function to toggle the theme mode + const toggleTheme = () => { + setTheme((prevTheme) => (prevTheme === "light" ? "dark" : "light")); + }; + + return ( + + {children} + + ); +}; diff --git a/src/hooks/theme.tsx b/src/hooks/theme.tsx new file mode 100644 index 00000000..9831b2e5 --- /dev/null +++ b/src/hooks/theme.tsx @@ -0,0 +1,29 @@ +import { ThemeContext } from "@/contexts/ThemeProvider"; +import { createTheme } from "@mui/material/styles"; +import { useContext } from "react"; + +// Custom hook to access the theme context +export const useTheme = () => { + const context = useContext(ThemeContext); + if (!context) { + throw new Error("useTheme must be used within a ThemeProvider"); + } + return context; +}; + +// Custom hook to generate and return the Material-UI theme based on the current mode +export const useMUITheme = () => { + const { theme } = useTheme(); + + // Creating a Material-UI theme based on the current mode + const muiTheme = createTheme({ + palette: { + mode: theme, + primary: { + main: "#ffffff", + }, + }, + }); + + return muiTheme; +};