Quellcode durchsuchen

Add simple navigation and app bar

tags/Baseline_30082024_FRONTEND_UAT
Wayne vor 1 Jahr
Ursprung
Commit
9b0e508310
10 geänderte Dateien mit 287 neuen und 10 gelöschten Zeilen
  1. +0
    -4
      package-lock.json
  2. +32
    -0
      src/app/dashboard/layout.tsx
  3. +34
    -0
      src/components/AppBar/AppBar.tsx
  4. +47
    -0
      src/components/AppBar/NavigationToggle.tsx
  5. +61
    -0
      src/components/AppBar/Profile.tsx
  6. +1
    -0
      src/components/AppBar/index.ts
  7. +66
    -0
      src/components/NavigationContent/NavigationContent.tsx
  8. +1
    -0
      src/components/NavigationContent/index.ts
  9. +45
    -0
      src/theme/devias-material-kit/components.ts
  10. +0
    -6
      src/theme/devias-material-kit/typography.ts

+ 0
- 4
package-lock.json Datei anzeigen

@@ -2446,10 +2446,6 @@
"node": ">= 6"
}
},
"node_modules/eslint-plugin-import": {
"dev": true,
"peer": true
},
"node_modules/eslint-plugin-prettier": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.1.tgz",


+ 32
- 0
src/app/dashboard/layout.tsx Datei anzeigen

@@ -0,0 +1,32 @@
import type { Metadata } from "next";
import AppBar from "@/components/AppBar";
import { getServerSession } from "next-auth";
import { authOptions } from "@/config/authConfig";
import { redirect } from "next/navigation";

export const metadata: Metadata = {
title: "Dashboard",
};

export default async function DashboardLayout({
children,
}: {
children: React.ReactNode;
}) {
const session = await getServerSession(authOptions);
console.log(session);

if (!session?.user) {
redirect("/login");
}

return (
<>
<AppBar
profileName={session.user.name!}
avatarImageSrc={session.user.image || undefined}
/>
<main>{children}</main>
</>
);
}

+ 34
- 0
src/components/AppBar/AppBar.tsx Datei anzeigen

@@ -0,0 +1,34 @@
import MUIAppBar from "@mui/material/AppBar";
import Toolbar from "@mui/material/Toolbar";
import React from "react";
import Profile from "./Profile";
import Box from "@mui/material/Box";
import NavigationToggle from "./NavigationToggle";
import { I18nProvider } from "@/i18n";

export interface AppBarProps {
avatarImageSrc?: string;
profileName: string;
}

const AppBar: React.FC<AppBarProps> = ({ avatarImageSrc, profileName }) => {
return (
<MUIAppBar position="fixed">
<Toolbar>
<I18nProvider namespaces={["common"]}>
<NavigationToggle />
<Box
sx={{ flexGrow: 1, display: "flex", justifyContent: "flex-end" }}
>
<Profile
avatarImageSrc={avatarImageSrc}
profileName={profileName}
/>
</Box>
</I18nProvider>
</Toolbar>
</MUIAppBar>
);
};

export default AppBar;

+ 47
- 0
src/components/AppBar/NavigationToggle.tsx Datei anzeigen

@@ -0,0 +1,47 @@
"use client";

import IconButton from "@mui/material/IconButton";
import MenuIcon from "@mui/icons-material/Menu";
import NavigationContent from "../NavigationContent";
import React from "react";
import Drawer from "@mui/material/Drawer";

const NavigationToggle: React.FC = () => {
const [isOpened, setIsOpened] = React.useState(false);

const openNavigation = () => {
setIsOpened(true);
};
const closeNavigation = () => {
setIsOpened(false);
};

return (
<>
<Drawer variant="permanent" sx={{ display: { xs: "none", lg: "block" } }}>
<NavigationContent />
</Drawer>
<Drawer
sx={{ display: { lg: "none" } }}
open={isOpened}
onClose={closeNavigation}
ModalProps={{
keepMounted: true,
}}
>
<NavigationContent />
</Drawer>
<IconButton
sx={{ display: { lg: "none" } }}
onClick={openNavigation}
edge="start"
aria-label="menu"
color="inherit"
>
<MenuIcon fontSize="inherit" />
</IconButton>
</>
);
};

export default NavigationToggle;

+ 61
- 0
src/components/AppBar/Profile.tsx Datei anzeigen

@@ -0,0 +1,61 @@
"use client";

import IconButton from "@mui/material/IconButton";
import Menu from "@mui/material/Menu";
import MenuItem from "@mui/material/MenuItem";
import Avatar from "@mui/material/Avatar";
import React from "react";
import { AppBarProps } from "./AppBar";
import Divider from "@mui/material/Divider";
import Typography from "@mui/material/Typography";
import { useTranslation } from "react-i18next";
import { signOut } from "next-auth/react";

type Props = Pick<AppBarProps, "avatarImageSrc" | "profileName">;

const Profile: React.FC<Props> = ({ avatarImageSrc, profileName }) => {
const [profileMenuAnchorEl, setProfileMenuAnchorEl] =
React.useState<HTMLButtonElement>();
const openProfileMenu: React.MouseEventHandler<HTMLButtonElement> = (
event,
) => {
setProfileMenuAnchorEl(event.currentTarget);
};
const closeProfileMenu = () => {
setProfileMenuAnchorEl(undefined);
};

const { t } = useTranslation("login");

return (
<>
<IconButton aria-label="profile" onClick={openProfileMenu}>
<Avatar src={avatarImageSrc} />
</IconButton>
<Menu
id="profile-menu"
anchorEl={profileMenuAnchorEl}
anchorOrigin={{
vertical: "bottom",
horizontal: "right",
}}
keepMounted
transformOrigin={{
vertical: "top",
horizontal: "right",
}}
open={Boolean(profileMenuAnchorEl)}
onClose={closeProfileMenu}
MenuListProps={{ dense: true, disablePadding: true }}
>
<Typography sx={{ mx: "1.5rem", my: "0.5rem" }} fontWeight="bold">
{profileName}
</Typography>
<Divider />
<MenuItem onClick={() => signOut()}>{t("Sign out")}</MenuItem>
</Menu>
</>
);
};

export default Profile;

+ 1
- 0
src/components/AppBar/index.ts Datei anzeigen

@@ -0,0 +1 @@
export { default } from "./AppBar";

+ 66
- 0
src/components/NavigationContent/NavigationContent.tsx Datei anzeigen

@@ -0,0 +1,66 @@
import Divider from "@mui/material/Divider";
import Box from "@mui/material/Box";
import React from "react";
import List from "@mui/material/List";
import ListItemButton from "@mui/material/ListItemButton";
import ListItemText from "@mui/material/ListItemText";
import ListItemIcon from "@mui/material/ListItemIcon";
import WorkHistory from "@mui/icons-material/WorkHistory";
import Dashboard from "@mui/icons-material/Dashboard";
import RequestQuote from "@mui/icons-material/RequestQuote";
import Task from "@mui/icons-material/Task";
import Assignment from "@mui/icons-material/Assignment";
import Settings from "@mui/icons-material/Settings";
import Analytics from "@mui/icons-material/Analytics";
import Payments from "@mui/icons-material/Payments";
import { useTranslation } from "react-i18next";
import Typography from "@mui/material/Typography";
import { usePathname } from "next/navigation";
import Link from "next/link";

interface NavigationItem {
icon: React.ReactNode;
label: string;
path: string;
}

const navigationItems: NavigationItem[] = [
{ icon: <WorkHistory />, label: "User Workspace", path: "/workspace" },
{ icon: <Dashboard />, label: "Dashboard", path: "/dashboard" },
{ icon: <RequestQuote />, label: "Expense Claim", path: "/claim" },
{ icon: <Assignment />, label: "Project Management", path: "/projects" },
{ icon: <Task />, label: "Task Template", path: "/tasks" },
{ icon: <Payments />, label: "Invoice", path: "/invoice" },
{ icon: <Analytics />, label: "Analysis Report", path: "/analytics" },
{ icon: <Settings />, label: "Setting", path: "/settings" },
];

const NavigationContent: React.FC = () => {
const { t } = useTranslation("common");
const pathname = usePathname();

return (
<Box>
<Box sx={{ p: "1.5rem" }}>
{/* Replace this with company logo and/or name */}
<Typography variant="h4">TSMS</Typography>
</Box>
<Divider />
<List component="nav">
{navigationItems.map(({ icon, label, path }, index) => {
return (
<ListItemButton
key={`${label}-${index}`}
selected={pathname.includes(path)}
>
<ListItemIcon>{icon}</ListItemIcon>
<ListItemText primary={<Link href={path}>{t(label)}</Link>} />
</ListItemButton>
);
})}
</List>
</Box>
);
};

export default NavigationContent;

+ 1
- 0
src/components/NavigationContent/index.ts Datei anzeigen

@@ -0,0 +1 @@
export { default } from "./NavigationContent";

+ 45
- 0
src/theme/devias-material-kit/components.ts Datei anzeigen

@@ -283,6 +283,51 @@ const components: ThemeOptions["components"] = {
variant: "filled",
},
},
MuiMenuItem: {
styleOverrides: {
root: {
margin: "0.5rem",
borderRadius: 8,
},
},
},
MuiList: {
styleOverrides: {
padding: {
paddingBlock: "1rem",
paddingInline: "1rem",
},
},
},
MuiListItemButton: {
styleOverrides: {
root: {
borderRadius: 8,
marginBlockEnd: "0.5rem",
a: {
textDecoration: "none",
color: "inherit",
}
},
},
},
MuiListItemText: {
styleOverrides: {
root: {
marginInlineEnd: "2rem",
},
primary: {
fontWeight: 500,
},
},
},
MuiListItemIcon: {
styleOverrides: {
root: {
color: "inherit",
},
},
},
};

export default components;

+ 0
- 6
src/theme/devias-material-kit/typography.ts Datei anzeigen

@@ -79,12 +79,6 @@ const typography: TypographyOptions = {
fontSize: "1.125rem",
lineHeight: 1.2,
},
fontSize: 0,
fontWeightLight: undefined,
fontWeightRegular: undefined,
fontWeightMedium: undefined,
fontWeightBold: undefined,
htmlFontSize: 0,
};

export default typography;

Laden…
Abbrechen
Speichern