CFE-15 Merge branch 'feature/CFE-15_loading_navigation_page_dashboard' into 'develop'

This commit is contained in:
AmirHossein Mahmoodi
2023-11-06 11:31:06 +00:00
10 changed files with 198 additions and 76 deletions

View File

@@ -15,6 +15,7 @@
"between": "میان",
"online_message": "شما به اینترنت وصل هستید",
"offline_message": "اتصال شما به اینترنت قطع شده است",
"routing_to": "در حال انتقال به صفحه",
"socket_is_connect_message": "شما به سوکت وصل هستید",
"socket_is_not_connect_message": "اتصال شما به سوکت قطع شده است",
"header": {
@@ -264,10 +265,10 @@
},
"CallAction": {
"call_history_of": "عملیات های مربوط به تماس",
"categories" : "موضوع ها",
"subcategories" : "زیر موضوع ها",
"description" : "توضیحات تماس (اختیاری)",
"text_field_label" : "توضیحات",
"text_field_palacholder" : "لطفا توضیحات خود را وارد نمایید"
"categories": "موضوع ها",
"subcategories": "زیر موضوع ها",
"description": "توضیحات تماس (اختیاری)",
"text_field_label": "توضیحات",
"text_field_palacholder": "لطفا توضیحات خود را وارد نمایید"
}
}

View File

@@ -0,0 +1,65 @@
import LoadingHardPage from "@/core/components/LoadingHardPage";
import RolePermissionMiddleware from "@/middlewares/RolePermission";
import BreadCrumbs from "@/components/layouts/Dashboard/Breadcrumbs";
import FullPageLayout from "@/layouts/FullPageLayout";
import {useEffect, useMemo, useState} from "react";
import sidebarMenu from "@/core/data/sidebarMenu";
import {useRouter} from "next/router";
import {useTranslations} from "next-intl";
const Content = (props) => {
const t = useTranslations()
const router = useRouter()
const [routing, setRouting] = useState(false)
const [routingItem, setRoutingItem] = useState({})
const pageList = useMemo(() => {
const list = []
for (const menu of sidebarMenu) {
for (const item of menu) {
if (item.type === 'menu') {
for (const subItem of item.subItem) {
list.push(subItem)
}
} else {
list.push(item)
}
}
}
return list
}, [])
useEffect(() => {
const handlerStartRoute = (url) => {
setRoutingItem(pageList.find(page => page.route === url))
setRouting(true)
}
const handlerCompleteRoute = () => {
setRouting(false)
}
router.events.on('routeChangeStart', handlerStartRoute)
router.events.on('routeChangeComplete', handlerCompleteRoute)
return () => {
router.events.off('routeChangeStart', handlerStartRoute)
router.events.off('routeChangeComplete', handlerCompleteRoute)
}
}, [router]);
return (
<FullPageLayout sx={{mt: 3, position: 'relative'}}>
<LoadingHardPage icon={routingItem?.icon}
label={routingItem?.key ? `${t('routing_to')} ${t(routingItem?.key)}` : ''}
loading={routing} width={100} height={100}
sx={{position: "absolute", bgcolor: "#fffc"}}>
<RolePermissionMiddleware requiredPermissions={props.permissions}>
<BreadCrumbs isVisible={true}/>
{props.children}
</RolePermissionMiddleware>
</LoadingHardPage>
</FullPageLayout>
)
}
export default Content

View File

@@ -1,5 +1,5 @@
import {Divider, List} from "@mui/material";
import {Fragment, useEffect, useReducer, useRef, useState} from "react";
import {Fragment, useEffect, useReducer, useState} from "react";
import SidebarListItem from "./SidebarListItem";
import sidebarMenu from "@/core/data/sidebarMenu";
import {useRouter} from "next/router";
@@ -10,20 +10,41 @@ function reducer(state, action) {
case "COLLAPSE_MENU":
return state.map((itemsArr) =>
itemsArr.map((item) =>
action.key === item.key ? { ...item, showSubItem: !item.showSubItem } : item
action.key === item.key ? {...item, showSubItem: !item.showSubItem} : item
)
);
case "SELECTED":
return state.map((itemsArr) =>
itemsArr.map((item) => {
return item.type === "page"
? { ...item, selected: action.route === item.route }
? {...item, selected: action.route === item.route, routing: false}
: item.subItem && Array.isArray(item.subItem)
? {
...item,
subItem: item.subItem.map((subitem) => ({
...subitem,
selected: subitem.route === action.route,
routing: false
})),
showSubItem: item.subItem.some(
(subitem) => subitem.selected
),
}
: item;
})
);
case "SELECTING":
return state.map((itemsArr) =>
itemsArr.map((item) => {
return item.type === "page"
? {...item, selected: action.route === item.route, routing: action.route === item.route}
: item.subItem && Array.isArray(item.subItem)
? {
...item,
subItem: item.subItem.map((subitem) => ({
...subitem,
selected: subitem.route === action.route,
routing: subitem.route === action.route
})),
showSubItem: item.subItem.some(
(subitem) => subitem.selected
@@ -46,7 +67,16 @@ export default function SidebarList({handleDrawerToggle}) {
useEffect(() => {
dispatch({type: "SELECTED", route: router.pathname});
setSelectedKey(router.pathname);
}, [router.pathname]);
const handlerStartRoute = (url) => {
dispatch({type: "SELECTING", route: url});
}
router.events.on('routeChangeStart', handlerStartRoute)
return () => {
router.events.off('routeChangeStart', handlerStartRoute)
}
}, [router]);
useEffect(() => {
@@ -57,7 +87,7 @@ export default function SidebarList({handleDrawerToggle}) {
}, [selectedKey]);
return (
<List sx={{ overflow: "scroll"}}>
<List sx={{overflow: "scroll"}}>
{itemMenu.map((itemArr, index) => (
<Fragment key={index}>
{itemArr.map((item) =>

View File

@@ -1,7 +1,16 @@
import {NextLinkComposed} from "@/core/components/LinkRouting";
import ExpandLess from "@mui/icons-material/ExpandLess";
import ExpandMore from "@mui/icons-material/ExpandMore";
import {Badge, IconButton, ListItem, ListItemButton, ListItemIcon, ListItemText, Typography,} from "@mui/material";
import {
Badge,
CircularProgress,
IconButton,
ListItem,
ListItemButton,
ListItemIcon,
ListItemText,
Typography,
} from "@mui/material";
import {useTranslations} from "next-intl";
import {Fragment} from "react";
import SidebarListSubItem from "./SidebarListSubItem";
@@ -9,7 +18,7 @@ import useNotification from "@/lib/app/hooks/useNotification";
const SidebarListItem = ({item, dispatch, handleDrawerToggle}) => {
const t = useTranslations();
const { notification_count } = useNotification();
const {notification_count} = useNotification();
const hasSubItems = item.type === "menu" && item.subItem && item.subItem.length > 0;
const renderBadge = () => {
return !hasSubItems ? (
@@ -40,7 +49,7 @@ const SidebarListItem = ({item, dispatch, handleDrawerToggle}) => {
})}
onClick={() => {
if (hasSubItems) {
dispatch({ type: "COLLAPSE_MENU", key: item.key });
dispatch({type: "COLLAPSE_MENU", key: item.key});
}
}}
sx={{
@@ -53,10 +62,13 @@ const SidebarListItem = ({item, dispatch, handleDrawerToggle}) => {
minWidth: 0,
justifyContent: "center",
color: "primary.main",
width: 40,
height: 24,
pr: 2,
}}
>
{item.icon}
{item.routing ?
<CircularProgress size={24} color="inherit"/> : item.icon}
</ListItemIcon>
<ListItemText
primary={t(item.key)}
@@ -69,7 +81,7 @@ const SidebarListItem = ({item, dispatch, handleDrawerToggle}) => {
}
/>
{hasSubItems && (item.showSubItem ? <ExpandLess /> : <ExpandMore />)}
{hasSubItems && (item.showSubItem ? <ExpandLess/> : <ExpandMore/>)}
</ListItemButton>
</ListItem>
{item.subItem && (

View File

@@ -1,6 +1,7 @@
import {NextLinkComposed} from "@/core/components/LinkRouting";
import {
Badge,
CircularProgress,
Collapse,
IconButton,
List,
@@ -13,7 +14,7 @@ import {
import {useTranslations} from "next-intl";
import useNotification from "@/lib/app/hooks/useNotification";
const SidebarListSubItem = ({item, handleDrawerToggle }) => {
const SidebarListSubItem = ({item, handleDrawerToggle}) => {
const t = useTranslations();
const {notification_count} = useNotification();
const renderBadge = (subitem) => (
@@ -50,10 +51,13 @@ const SidebarListSubItem = ({item, handleDrawerToggle }) => {
sx={{
minWidth: 0,
justifyContent: "center",
width: 40,
height: 24,
pr: 2,
}}
>
{subitem.icon}
{subitem.routing ?
<CircularProgress size={24} color="inherit"/> : subitem.icon}
</ListItemIcon>
<ListItemText primary={t(subitem.key)} secondary={
subitem.secondary !== undefined ? (

View File

@@ -1,47 +1,54 @@
import {Box, Drawer} from "@mui/material";
import {Box, Drawer, useMediaQuery} from "@mui/material";
import SidebarDrawer from "./SidebarDrawer";
import {useTheme} from "@mui/material/styles";
const Sidebar = (props) => {
const theme = useTheme();
const isUpSm = useMediaQuery((theme.breakpoints.up('sm')))
return (
<Box
component="nav"
sx={{width: {md: props.drawerWidth}, flexShrink: {sm: 0}}}
aria-label="mailbox folders"
>
<Drawer
data-testid="mobile-drawer"
container={props.container}
variant="temporary"
open={props.mobileOpen}
onClose={props.handleDrawerToggle}
ModalProps={{
keepMounted: true,
}}
sx={{
display: {xs: "block", md: "none"},
"& .MuiDrawer-paper": {
boxSizing: "border-box",
width: props.drawerWidth,
overflow: "hidden",
},
}}
>
<SidebarDrawer handleDrawerToggle={props.handleDrawerToggle}/>
</Drawer>
<Drawer
variant="permanent"
sx={{
display: {xs: "none", md: "block"},
"& .MuiDrawer-paper": {
boxSizing: "border-box",
width: props.drawerWidth,
overflow: "hidden",
},
}}
open
>
<SidebarDrawer handleDrawerToggle={props.handleDrawerToggle}/>
</Drawer>
{isUpSm ? (
<Drawer
variant="permanent"
sx={{
display: {xs: "none", md: "block"},
"& .MuiDrawer-paper": {
boxSizing: "border-box",
width: props.drawerWidth,
overflow: "hidden",
},
}}
open
>
<SidebarDrawer handleDrawerToggle={props.handleDrawerToggle}/>
</Drawer>
) : (
<Drawer
data-testid="mobile-drawer"
container={props.container}
variant="temporary"
open={props.mobileOpen}
onClose={props.handleDrawerToggle}
ModalProps={{
keepMounted: true,
}}
sx={{
display: {xs: "block", md: "none"},
"& .MuiDrawer-paper": {
boxSizing: "border-box",
width: props.drawerWidth,
overflow: "hidden",
},
}}
>
<SidebarDrawer handleDrawerToggle={props.handleDrawerToggle}/>
</Drawer>
)}
</Box>
);
};

View File

@@ -1,10 +1,9 @@
import FullPageLayout from "@/layouts/FullPageLayout";
import {Toolbar} from "@mui/material";
import {useState} from "react";
import RolePermissionMiddleware from "@/middlewares/RolePermission";
import Header from "@/components/layouts/Dashboard/Header";
import Sidebar from "@/components/layouts/Dashboard/Sidebar";
import BreadCrumbs from "@/components/layouts/Dashboard/Breadcrumbs";
import Content from "@/components/layouts/Dashboard/Content";
const drawerWidth = 240;
@@ -34,12 +33,7 @@ const Dashboard = (props) => {
sx={{flexGrow: 1, width: {sm: `calc(100% - ${drawerWidth}px)`}}}
>
<Toolbar/>
<FullPageLayout sx={{mt: 3}}>
<RolePermissionMiddleware requiredPermissions={props.permissions}>
<BreadCrumbs isVisible={true}/>
{props.children}
</RolePermissionMiddleware>
</FullPageLayout>
<Content {...props}/>
</FullPageLayout>
</FullPageLayout>
)

View File

@@ -1,5 +1,5 @@
import SvgLoading from "@/core/components/svgs/SvgLoading";
import {Backdrop, Box} from "@mui/material";
import {Backdrop, Box, Stack, Typography} from "@mui/material";
import {styled} from "@mui/material/styles";
const LoadingImage = styled(Box)({
@@ -10,7 +10,7 @@ const LoadingImage = styled(Box)({
},
"50%": {
// opacity: 1,
transform: "scale(2)",
transform: "scale(0.5)",
},
"100%": {
// opacity: 0,
@@ -20,20 +20,29 @@ const LoadingImage = styled(Box)({
animation: "load 2s infinite",
});
const LoadingHardPage = ({children, loading}) => {
const LoadingHardPage = ({children, loading, sx = {}, icon = null, width = 200, height = 200, label = ''}) => {
return (
<>
<Backdrop
sx={{bgcolor: "#fff", zIndex: (theme) => theme.zIndex.modal + 1}}
sx={{bgcolor: "#fff", zIndex: (theme) => theme.zIndex.modal + 1, ...sx}}
open={loading}
>
<LoadingImage
width={100}
height={100}
>
<SvgLoading width={100}
height={100}/>
</LoadingImage>
<Stack alignItems={'center'} spacing={2}>
<LoadingImage
width={width}
height={height}
>
{icon ? (
<Box sx={{color: "primary.main", width: width, height: height}}>
{icon}
</Box>
) : (
<SvgLoading width={width}
height={height}/>
)}
</LoadingImage>
<Typography variant={'body2'} sx={{color: "primary.main"}}>{label}</Typography>
</Stack>
</Backdrop>
{children}
</>

View File

@@ -8,7 +8,7 @@ const sidebarMenu = [
key: "sidebar.dashboard",
type: "page",
route: "/dashboard",
icon: <SpaceDashboardIcon />,
icon: <SpaceDashboardIcon sx={{width: 'inherit', height: 'inherit'}}/>,
selected: false,
permission: "all",
},
@@ -17,16 +17,16 @@ const sidebarMenu = [
type: "page",
name: "expert_management",
route: "/dashboard/expert-management",
icon: <ManageAccountsIcon/>,
icon: <ManageAccountsIcon sx={{width: 'inherit', height: 'inherit'}}/>,
selected: false,
permission: "all",
},
{
key: "sidebar.role-management",
name : "role_management",
name: "role_management",
type: "page",
route: "/dashboard/role-management",
icon: <AccessibilityIcon />,
icon: <AccessibilityIcon sx={{width: 'inherit', height: 'inherit'}}/>,
selected: false,
permission: "manage_roles",
},

View File

@@ -2,7 +2,7 @@ import DashboardLayout from "@/layouts/DashboardLayout";
import {Fragment} from "react";
const layoutList = {
DashboardLayout
DashboardLayout,
}
const Layout = ({layout, children}) => {