diff --git a/.env.local.example b/.env.local.example
index e69de29..c93a078 100644
--- a/.env.local.example
+++ b/.env.local.example
@@ -0,0 +1,2 @@
+NEXT_PUBLIC_VERSION="0.2.0"
+NEXT_PUBLIC_API_URL="https://rms.witel.ir"
\ No newline at end of file
diff --git a/.eslintrc.json b/.eslintrc.json
index bffb357..121cefd 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -1,3 +1,3 @@
{
- "extends": "next/core-web-vitals"
+ "extends": ["next/core-web-vitals","prettier"]
}
diff --git a/.prettierrc b/.prettierrc
index fe94791..7eeebb6 100644
--- a/.prettierrc
+++ b/.prettierrc
@@ -1,12 +1,12 @@
{
- "printWidth": 120,
- "tabWidth": 4,
- "useTabs": false,
- "semi": true,
- "singleQuote": false,
- "trailingComma": "es5",
- "bracketSpacing": true,
- "jsxBracketSameLine": false,
- "arrowParens": "always",
- "endOfLine": "crlf"
+ "printWidth": 120,
+ "tabWidth": 4,
+ "useTabs": false,
+ "semi": true,
+ "singleQuote": false,
+ "trailingComma": "es5",
+ "bracketSpacing": true,
+ "jsxBracketSameLine": false,
+ "arrowParens": "always",
+ "endOfLine": "crlf"
}
\ No newline at end of file
diff --git a/next.config.mjs b/next.config.mjs
index bb49d74..72cfa4a 100644
--- a/next.config.mjs
+++ b/next.config.mjs
@@ -3,6 +3,9 @@ const isProd = process.env.NODE_ENV === 'production'
/** @type {import('next').NextConfig} */
const nextConfig = {
assetPrefix: isProd ? '/v3' : undefined,
+ images: {
+ path: isProd ? '/v3/_next/image' : undefined,
+ }
};
export default nextConfig;
diff --git a/package.json b/package.json
index 7662699..7bec569 100644
--- a/package.json
+++ b/package.json
@@ -7,7 +7,8 @@
"build": "next build",
"start": "next start",
"lint": "next lint",
- "format": "prettier --write \"src/**/*.{js,jsx,ts,tsx,html,css,scss}\""
+ "format": "prettier --write \"src/**/*.{js,jsx,ts,tsx,html,css,scss}\"",
+ "build:publish": "npm i && npm run build && pm2 restart ecosystem.config.js"
},
"dependencies": {
"@emotion/cache": "^11.11.0",
@@ -17,6 +18,7 @@
"@mui/material": "^5.15.6",
"@mui/material-nextjs": "^5.15.6",
"@mui/x-date-pickers": "^6.19.5",
+ "axios": "^1.7.2",
"date-fns": "^3.3.1",
"date-fns-jalali": "^2.13.0-0",
"dayjs": "^1.11.10",
@@ -36,6 +38,7 @@
"devDependencies": {
"eslint": "^8",
"eslint-config-next": "14.1.0",
+ "eslint-config-prettier": "^9.1.0",
"prettier": "^3.3.2"
}
}
diff --git a/public/global.scss b/public/global.scss
index e3d2485..d1d10e3 100644
--- a/public/global.scss
+++ b/public/global.scss
@@ -1,6 +1,8 @@
@import "fontiran";
+@import "react-toastify/dist/ReactToastify.css";
.filter-toast {
- box-shadow: rgba(50, 50, 93, 0.25) 0px 13px 27px -5px, rgba(0, 0, 0, 0.3) 0px 8px 16px -8px;
+ box-shadow: rgba(50, 50, 93, 0.25) 0px 13px 27px -5px,
+ rgba(0, 0, 0, 0.3) 0px 8px 16px -8px;
background-color: #d0dfe8;
-}
\ No newline at end of file
+}
diff --git a/public/images/headerLogo.png b/public/images/headerLogo.png
new file mode 100644
index 0000000..86dbfcd
Binary files /dev/null and b/public/images/headerLogo.png differ
diff --git a/public/images/rmsLogo.png b/public/images/rmsLogo.png
new file mode 100644
index 0000000..8340d5a
Binary files /dev/null and b/public/images/rmsLogo.png differ
diff --git a/src/app/(withAuth)/dashboard/inquiry_privacy/fencing/page.js b/src/app/(withAuth)/dashboard/inquiry_privacy/fencing/page.js
new file mode 100644
index 0000000..0a1b9ec
--- /dev/null
+++ b/src/app/(withAuth)/dashboard/inquiry_privacy/fencing/page.js
@@ -0,0 +1,10 @@
+import InquiryPrivacyFencingPage from "@/components/dashboard/inquiryPrivacyFencing";
+
+const Page = () => {
+
+ return (
+
+ );
+};
+
+export default Page;
\ No newline at end of file
diff --git a/src/app/(withAuth)/dashboard/page.js b/src/app/(withAuth)/dashboard/page.js
index 25989ef..c43c1ae 100644
--- a/src/app/(withAuth)/dashboard/page.js
+++ b/src/app/(withAuth)/dashboard/page.js
@@ -1,5 +1,9 @@
const Page = () => {
- return
dashboard
;
+
+ return (
+ <>
+ >
+ );
};
export default Page;
diff --git a/src/app/(withAuth)/dashboard/template.js b/src/app/(withAuth)/dashboard/template.js
deleted file mode 100644
index 28b4ea0..0000000
--- a/src/app/(withAuth)/dashboard/template.js
+++ /dev/null
@@ -1,17 +0,0 @@
-import { Box, Stack } from "@mui/material";
-import HeaderWithLogo from "@/components/layouts/dashboard/headerWithLogo";
-import HeaderWithSidebar from "@/components/layouts/dashboard/headerWithSidebar";
-import "react-toastify/dist/ReactToastify.css";
-
-const Template = ({ children }) => {
- return (
-
-
-
- {children}
-
-
- );
-};
-
-export default Template;
diff --git a/src/app/(withAuth)/layout.js b/src/app/(withAuth)/layout.js
new file mode 100644
index 0000000..400bbc9
--- /dev/null
+++ b/src/app/(withAuth)/layout.js
@@ -0,0 +1,12 @@
+import WithAuthMiddleware from "@/core/middlewares/withAuth";
+import { AuthProvider } from "@/lib/contexts/auth";
+
+const Layout = ({ children }) => {
+ return (
+
+ {children}
+
+ );
+};
+
+export default Layout;
diff --git a/src/app/default.js b/src/app/default.js
new file mode 100644
index 0000000..d157a71
--- /dev/null
+++ b/src/app/default.js
@@ -0,0 +1,3 @@
+export default function Default() {
+ return null;
+}
diff --git a/src/app/lfwd/page.js b/src/app/lfwd/page.js
new file mode 100644
index 0000000..b3456a3
--- /dev/null
+++ b/src/app/lfwd/page.js
@@ -0,0 +1,57 @@
+"use client";
+
+import { GET_LOGIN_ROUTE } from "@/core/utils/routes";
+import useRequest from "@/lib/hooks/useRequest";
+import { Button, Stack, Typography, Zoom } from "@mui/material";
+import Link from "next/link";
+import { useEffect, useState } from "react";
+
+const Page = ({ searchParams }) => {
+ const { username } = searchParams;
+ const [login, setLogin] = useState(0);
+ const request = useRequest();
+
+ useEffect(() => {
+ const login = async () => {
+ try {
+ await request(`${GET_LOGIN_ROUTE}?username=${username}`);
+ setLogin(1);
+ } catch (error) {
+ setLogin(2);
+ }
+ };
+
+ login();
+ }, []);
+
+ const retry = async () => {
+ setLogin(0);
+ try {
+ await request(`${GET_LOGIN_ROUTE}?username=${username}`);
+ setLogin(1);
+ } catch (error) {
+ setLogin(2);
+ }
+ };
+
+ return (
+
+
+ {login === 0
+ ? "درحال دریافت مجوز برای ارتباط با سرور..."
+ : login === 1
+ ? "ارتباط با سرور برقرار شد."
+ : "ارتباط با سرور برقرار نشد. مشکلی وجود دارد."}
+
+
+
+
+
+
+
+
+ );
+};
+export default Page;
diff --git a/src/app/page.js b/src/app/page.js
index e4c74e8..2997c35 100644
--- a/src/app/page.js
+++ b/src/app/page.js
@@ -1,8 +1,8 @@
-"use client";
-import HomeComponent from "@/components/home";
+import { redirect } from "next/navigation";
function Page() {
- return ;
+ redirect("/dashboard", "replace");
+ return null;
}
export default Page;
diff --git a/src/app/template.js b/src/app/template.js
index 9cf7484..3ffc656 100644
--- a/src/app/template.js
+++ b/src/app/template.js
@@ -2,7 +2,6 @@ import cacheProviderRtl from "@/core/utils/cacheRtl";
import { CssBaseline, ThemeProvider } from "@mui/material";
import theme from "@/core/utils/theme";
import { AppRouterCacheProvider } from "@mui/material-nextjs/v14-appRouter";
-import "react-toastify/dist/ReactToastify.css";
import "^/global.scss";
import { ToastContainer } from "react-toastify";
@@ -12,7 +11,8 @@ const Template = ({ children }) => {
{children}
-
+
+
);
diff --git a/src/components/dashboard/inquiryPrivacyFencing/index.jsx b/src/components/dashboard/inquiryPrivacyFencing/index.jsx
new file mode 100644
index 0000000..539885d
--- /dev/null
+++ b/src/components/dashboard/inquiryPrivacyFencing/index.jsx
@@ -0,0 +1,4 @@
+const InquiryPrivacyFencingPage = () => {
+ return <>>
+}
+export default InquiryPrivacyFencingPage
\ No newline at end of file
diff --git a/src/components/layouts/dashboard/headerWithLogo/HaederBottom/index.jsx b/src/components/layouts/dashboard/headerWithLogo/HaederBottom/index.jsx
new file mode 100644
index 0000000..4f0b159
--- /dev/null
+++ b/src/components/layouts/dashboard/headerWithLogo/HaederBottom/index.jsx
@@ -0,0 +1,27 @@
+"use client";
+import { Stack, Typography } from "@mui/material";
+import Image from "next/image";
+
+const HeaderBottom = () => {
+ return (
+ theme.palette.primary2.main,
+ }}
+ spacing={4}
+ alignItems={"center"}
+ direction={"row"}
+ >
+
+
+ theme.palette.primary2.contrastText }}>
+ سامانه جامع راهداری
+
+ theme.palette.primary2.contrastText }}>rms.rmto.ir
+
+
+ );
+};
+export default HeaderBottom;
diff --git a/src/components/layouts/dashboard/headerWithLogo/HaederTop/index.jsx b/src/components/layouts/dashboard/headerWithLogo/HaederTop/index.jsx
new file mode 100644
index 0000000..6899881
--- /dev/null
+++ b/src/components/layouts/dashboard/headerWithLogo/HaederTop/index.jsx
@@ -0,0 +1,11 @@
+import { Stack } from "@mui/material";
+import Image from "next/image";
+
+const HeaderTop = () => {
+ return (
+
+
+
+ );
+};
+export default HeaderTop;
diff --git a/src/components/layouts/dashboard/headerWithLogo/index.jsx b/src/components/layouts/dashboard/headerWithLogo/index.jsx
index ed083a4..16ef4b2 100644
--- a/src/components/layouts/dashboard/headerWithLogo/index.jsx
+++ b/src/components/layouts/dashboard/headerWithLogo/index.jsx
@@ -1,10 +1,12 @@
import { Box, Stack } from "@mui/material";
+import HeaderTop from "./HaederTop";
+import HeaderBottom from "./HaederBottom";
const HeaderWithLogo = () => {
return (
- header1
- header2
+
+
);
};
diff --git a/src/components/layouts/dashboard/headerWithSidebar/HeaderMenu/index.jsx b/src/components/layouts/dashboard/headerWithSidebar/HeaderMenu/index.jsx
new file mode 100644
index 0000000..c705242
--- /dev/null
+++ b/src/components/layouts/dashboard/headerWithSidebar/HeaderMenu/index.jsx
@@ -0,0 +1,4 @@
+const HeaderMenu = () => {
+ return <>منو>;
+};
+export default HeaderMenu;
diff --git a/src/components/layouts/dashboard/headerWithSidebar/index.jsx b/src/components/layouts/dashboard/headerWithSidebar/index.jsx
index 1774a85..c8febb6 100644
--- a/src/components/layouts/dashboard/headerWithSidebar/index.jsx
+++ b/src/components/layouts/dashboard/headerWithSidebar/index.jsx
@@ -1,12 +1,13 @@
"use client";
-import { useState } from "react";
-import { Box, Drawer, IconButton, Paper, styled, Toolbar, Typography } from "@mui/material";
-import MuiAppBar from "@mui/material/AppBar";
-import MenuIcon from "@mui/icons-material/Menu";
-import ChevronRightIcon from "@mui/icons-material/ChevronRight";
import SidebarMenu from "@/core/components/SidebarMenu";
+import ChevronRightIcon from "@mui/icons-material/ChevronRight";
+import MenuIcon from "@mui/icons-material/Menu";
+import { Box, Drawer, IconButton, Paper, styled, Toolbar } from "@mui/material";
+import MuiAppBar from "@mui/material/AppBar";
+import { useState } from "react";
+import HeaderMenu from "./HeaderMenu";
-const drawerWidth = 260;
+const drawerWidth = 300;
const Main = styled(Paper, { shouldForwardProp: (prop) => prop !== "open" })(({ theme, open }) => ({
flexGrow: 1,
@@ -44,25 +45,24 @@ const AppBar = styled(MuiAppBar, {
const DrawerHeader = styled("div")(({ theme }) => ({
display: "flex",
alignItems: "center",
- padding: theme.spacing(0, 1), // necessary for content to be below app bar
+ padding: theme.spacing(0, 1),
...theme.mixins.toolbar,
justifyContent: "flex-end",
}));
const HeaderWithSidebar = ({ children }) => {
- const [open, setOpen] = useState(false);
+ const [open, setOpen] = useState(true);
const handleDrawerOpen = () => {
setOpen(true);
};
-
const handleDrawerClose = () => {
setOpen(false);
};
return (
-
+
{
>
-
- menu
-
+
+
+
{
return (
<>
-
+
{
dispatch({ type: "COLLAPSE_MENU", id: menuItem.id });
}}
@@ -25,21 +24,28 @@ const SidebarListItems = ({ menuItem, dispatch }) => {
color: "primary.main",
width: 40,
height: 24,
- pr: 2,
}}
>
{menuItem.icon}
-
+
{menuItem.hasSubitems ? menuItem.showSubitems ? : : null}
- {menuItem.hasSubitems
- ? menuItem.Subitems.map((subitem, index) => {
- return ;
- })
- : null}
+ {menuItem.hasSubitems ? (
+
+ {menuItem.Subitems.map((subitem, index) => {
+ return ;
+ })}
+
+ ) : null}
>
);
diff --git a/src/core/components/SidebarMenu.jsx b/src/core/components/SidebarMenu.jsx
index 55cc9b9..30a9297 100644
--- a/src/core/components/SidebarMenu.jsx
+++ b/src/core/components/SidebarMenu.jsx
@@ -1,221 +1,86 @@
"use client";
-import { List } from "@mui/material";
import SidebarListItems from "@/core/components/SidebarListItems";
-import { useEffect, useReducer, useState } from "react";
+import { usePermissions } from "@/lib/hooks/usePermissions";
+import { List } from "@mui/material";
import { usePathname } from "next/navigation";
-import SecurityIcon from "@mui/icons-material/Security";
-import SpaceDashboardIcon from "@mui/icons-material/SpaceDashboard";
-import AssessmentIcon from "@mui/icons-material/Assessment";
-import AirlineSeatReclineNormalIcon from "@mui/icons-material/AirlineSeatReclineNormal";
-import RepartitionIcon from "@mui/icons-material/Repartition";
+import { useEffect, useReducer, useState } from "react";
+import { filterMenuItems } from "../utils/filterMenuItems";
+import { pageMenu } from "../utils/pageMenu";
-const data = [
- {
- id: "hello",
- type: "menu",
- route: "/hello",
- icon: ,
- selected: false,
- Subitems: [
- {
- id: "amin",
- route: "/s",
- icon: ,
- firstName: "amin",
- lastName: "ali",
- Subitems: [
- {
- id: "shahrokh",
- shahrokh: "shahrokh",
- type: "page",
- selected: false,
- route: "/dashbssoard",
- icon: ,
- },
- {
- id: "shasdasdsahrokh",
- shahrokh: "shahroasdasdkh",
- type: "page",
- selected: false,
- route: "/dashboardsd",
- icon: ,
- },
- ],
- hasSubitems: true,
- showSubitems: false,
- type: "menu",
- selected: false,
- },
- {
- id: "amir",
- route: "/dashboarsad",
- icon: ,
- firstName: "amir",
- lastName: "akbar",
- hasSubitems: false,
- showSubitems: false,
- selected: false,
- type: "page",
- },
- ],
- hasSubitems: true,
- showSubitems: false,
- },
- {
- id: "hi",
- type: "menu",
- icon: ,
- route: "/hi",
- selected: false,
- Subitems: [
- {
- id: "ali",
- route: "/dashboardss",
- firstName: "ali",
- lastName: "ali",
- icon: ,
- hasSubitems: false,
- showSubitems: false,
- type: "page",
- selected: false,
- },
- {
- id: "akbar",
- route: "/akbar",
- firstName: "akbar",
- icon: ,
- lastName: "akbar",
- hasSubitems: false,
- showSubitems: false,
- type: "page",
- selected: false,
- },
- ],
- hasSubitems: true,
- showSubitems: false,
- },
- {
- id: "hoo",
- type: "page",
- route: "/dashboard",
- icon: ,
- selected: false,
- hasSubitems: false,
- showSubitems: false,
- },
-];
+function selectPage(item, route) {
+ if (item.type === "page") {
+ return {
+ ...item,
+ selected: item.route === route,
+ showSubitems: item.route === route,
+ };
+ } else if (item.Subitems && Array.isArray(item.Subitems)) {
+ const updatedSubitems = item.Subitems.map(subitem => selectPage(subitem, route));
+ return {
+ ...item,
+ Subitems: updatedSubitems,
+ showSubitems: updatedSubitems.some(subitem => subitem.showSubitems || subitem.route === route),
+ };
+ }
+ return item;
+}
function reducer(state, action) {
switch (action.type) {
+ case "UPDATE_MENU":
+ const _permissions = action.permissions || []
+ const filteredPageMenu = filterMenuItems(state, ['all', ..._permissions])
+ return filteredPageMenu
case "COLLAPSE_MENU":
return state.map((item) =>
action.id == item.id
? {
- ...item,
- showSubitems: !item.showSubitems,
- }
+ ...item,
+ showSubitems: !item.showSubitems,
+ }
: item
);
case "COLLAPSE_SUB_ITEMS":
return state.map((item) => {
return item.hasSubitems
? {
- ...item,
- Subitems: item.Subitems.map((subitem) =>
- action.id === subitem.id
- ? {
- ...subitem,
- showSubitems: !subitem.showSubitems,
- }
- : subitem
- ),
- }
+ ...item,
+ Subitems: item.Subitems.map((subitem) =>
+ action.id === subitem.id
+ ? {
+ ...subitem,
+ showSubitems: !subitem.showSubitems,
+ }
+ : subitem
+ ),
+ }
: item;
});
case "COLLAPSE_SUB_SECOND_ITEMS":
return state.map((item) => {
return item.hasSubitems
? {
- ...item,
- Subitems: item.Subitems.map((subitem) => {
- return subitem.hasSubitems
- ? {
- ...subitem,
- Subitems: subitem.Subitems.map((secondSubitem) =>
- action.id === secondSubitem.id
- ? {
- ...secondSubitem,
- showSubitems: !secondSubitem.showSubitems,
- }
- : secondSubitem
- ),
- }
- : subitem;
- }),
- }
+ ...item,
+ Subitems: item.Subitems.map((subitem) => {
+ return subitem.hasSubitems
+ ? {
+ ...subitem,
+ Subitems: subitem.Subitems.map((secondSubitem) =>
+ action.id === secondSubitem.id
+ ? {
+ ...secondSubitem,
+ showSubitems: !secondSubitem.showSubitems,
+ }
+ : secondSubitem
+ ),
+ }
+ : subitem;
+ }),
+ }
: item;
});
case "SELECTED":
- return state.map((item) => {
- return item.type === "page"
- ? {
- ...item,
- selected: action.route === item.route,
- showSubitems: item.route === action.route,
- }
- : item.Subitems && Array.isArray(item.Subitems)
- ? {
- ...item,
- Subitems: item.Subitems.map((subitem) => {
- return subitem.type === "page"
- ? {
- ...subitem,
- selected: action.route === subitem.route,
- showSubitems: subitem.route === action.route,
- }
- : subitem.Subitems && Array.isArray(subitem.Subitems)
- ? {
- ...subitem,
- Subitems: subitem.Subitems.map((secondSubItem) => ({
- ...secondSubItem,
- selected: secondSubItem.route === action.route,
- })),
- showSubitems: subitem.Subitems.some(
- (secondSubItem) => secondSubItem.route === action.route
- ),
- }
- : subitem;
- }),
- showSubitems: item.Subitems.some((subitem) => subitem.showSubitems),
- }
- : item;
- });
- // case "SELECTED":
- // return state.map((item) => {
- // return item.type === "page" ? {
- // ...item,
- // selected: action.route === item.route
- // } : item.Subitems && Array.isArray(item.Subitems) ? {
- // ...item,
- // Subitems: item.Subitems.map((subitem) => {
- // const updatedSubitem = subitem.type === "page" ? {
- // ...subitem,
- // selected: action.route === subitem.route
- // } : subitem.Subitems && Array.isArray(subitem.Subitems) ? {
- // ...subitem,
- // Subitems: subitem.Subitems.map((secondSubItem) => ({
- // ...secondSubItem,
- // selected: secondSubItem.route === action.route
- // })),
- // showSubitems: subitem.Subitems.some((secondSubItem) => secondSubItem.route === action.route),
- // } : subitem;
- //
- // console.log("Updated subitem:", updatedSubitem); // Log the updated subitem
- // return updatedSubitem;
- // }),
- // showSubitems: item.Subitems.some((subitem) => subitem.showSubitems),
- // } : item
- // });
+ return state.map(item => selectPage(item, action.route));
default:
throw new Error();
@@ -223,13 +88,21 @@ function reducer(state, action) {
}
const SidebarMenu = () => {
- const [menuItems, dispatch] = useReducer(reducer, data);
+ const { data: userPermissions } = usePermissions()
+ const [menuItems, dispatch] = useReducer(reducer, pageMenu);
const pathname = usePathname();
const [selectedKey, setSelectedKey] = useState(null);
+
useEffect(() => {
dispatch({ type: "SELECTED", route: pathname });
setSelectedKey(pathname);
- }, [pathname]);
+ }, [userPermissions, pathname]);
+
+ useEffect(() => {
+ if (!userPermissions) return
+ dispatch({ type: 'UPDATE_MENU', permissions: userPermissions });
+ }, [userPermissions])
+
useEffect(() => {
selectedKey &&
@@ -240,11 +113,13 @@ const SidebarMenu = () => {
}, [selectedKey]);
return (
-
- {menuItems.map((menuItem) => {
- return ;
- })}
-
+ userPermissions && (
+
+ {menuItems.map((menuItem) => {
+ return ;
+ })}
+
+ )
);
};
export default SidebarMenu;
diff --git a/src/core/components/SidebarSubitems.jsx b/src/core/components/SidebarSubitems.jsx
index 18c1016..69418ea 100644
--- a/src/core/components/SidebarSubitems.jsx
+++ b/src/core/components/SidebarSubitems.jsx
@@ -1,82 +1,50 @@
-import { Collapse, List, ListItem, ListItemButton, ListItemIcon, ListItemText } from "@mui/material";
import { ExpandLess, ExpandMore } from "@mui/icons-material";
+import { Collapse, List, ListItem, ListItemButton, ListItemIcon, ListItemText } from "@mui/material";
+import Link from "next/link";
const SidebarSubitems = ({ subitem, dispatch }) => {
return (
<>
-
-
-
+ {
+ dispatch({ type: "COLLAPSE_SUB_ITEMS", id: subitem.id });
+ }}
+ >
+ {
- dispatch({ type: "COLLAPSE_SUB_ITEMS", id: subitem.id });
+ minWidth: 0,
+ justifyContent: "center",
+ width: 40,
+ height: 24,
}}
>
-
- {subitem.icon}
-
-
- {subitem.hasSubitems ? subitem.showSubitems ? : : null}
-
-
-
- {subitem.hasSubitems
- ? subitem.Subitems.map((subSubitem, index) => {
- return (
-
-
-
- {
- dispatch({ type: "COLLAPSE_SUB_SECOND_ITEMS", id: subSubitem.id });
- }}
- selected={subSubitem.selected}
- sx={{
- "&.Mui-selected": {
- backgroundColor: "rgba(0, 0, 0, 0.05)",
- },
- }}
- >
-
- {subSubitem.icon}
-
-
-
-
-
-
- );
- })
- : null}
+ {subitem.icon}
+
+
+ {subitem.hasSubitems ? subitem.showSubitems ? : : null}
+
+
+
+ {subitem.hasSubitems ? (
+
+ {subitem.Subitems.map((subSubitem, index) => {
+ return ;
+ })}
+
+ ) : null}
+
>
);
};
diff --git a/src/core/components/Toasts/error.jsx b/src/core/components/Toasts/error.jsx
new file mode 100644
index 0000000..e93138b
--- /dev/null
+++ b/src/core/components/Toasts/error.jsx
@@ -0,0 +1,187 @@
+import { toast } from "react-toastify";
+import { Box, Typography } from "@mui/material";
+import { Dangerous } from "@mui/icons-material";
+
+export const errorServerToast = (toastContainer) =>
+ toast.error(
+ () => (
+
+
+
+
+ {"The request failed due to an internal error."}
+
+
+
+ ),
+ {
+ icon: false,
+ containerId: toastContainer,
+ autoClose: 5000,
+ hideProgressBar: true,
+ pauseOnHover: true,
+ closeOnClick: false,
+ draggable: true,
+ }
+ );
+
+export const errorUnauthorizedToast = (toastContainer) =>
+ toast.error(
+ () => (
+
+
+
+
+ {"The user is not authorized to make the request."}
+
+
+
+ ),
+ {
+ icon: false,
+ containerId: toastContainer,
+ autoClose: 3000,
+ hideProgressBar: true,
+ pauseOnHover: true,
+ closeOnClick: false,
+ draggable: true,
+ }
+ );
+
+export const errorLogicToast = (message, toastContainer) =>
+ toast.error(
+ () => (
+
+
+
+
+
+ {message ||
+ "The request was well-formed but was unable to be followed due to semantic errors."}
+
+
+
+
+ ),
+ {
+ icon: false,
+ containerId: toastContainer,
+ autoClose: false,
+ closeOnClick: false,
+ draggable: false,
+ }
+ );
+
+export const errorValidationToast = (message, toastContainer) =>
+ toast.error(
+ () => (
+
+
+
+
+
+ {message ||
+ "The request was well-formed but was unable to be followed due to semantic errors."}
+
+
+
+
+ ),
+ {
+ icon: false,
+ containerId: toastContainer,
+ autoClose: false,
+ closeOnClick: false,
+ draggable: false,
+ }
+ );
+
+export const errorTooManyToast = (toastContainer) =>
+ toast.error(
+ () => (
+
+
+
+
+
+ {"Too many requests have been sent within a given time span."}
+
+
+
+
+ ),
+ {
+ icon: false,
+ containerId: toastContainer,
+ autoClose: false,
+ closeOnClick: false,
+ draggable: false,
+ }
+ );
+
+export const errorClientToast = (toastContainer) =>
+ toast.error(
+ () => (
+
+
+
+
+
+ {
+ "The API request is invalid or improperly formed. Consequently, the API server could not understand the request."
+ }
+
+
+
+
+ ),
+ {
+ icon: false,
+ containerId: toastContainer,
+ autoClose: false,
+ closeOnClick: false,
+ draggable: false,
+ }
+ );
diff --git a/src/core/components/Toasts/success.jsx b/src/core/components/Toasts/success.jsx
new file mode 100644
index 0000000..36023c6
--- /dev/null
+++ b/src/core/components/Toasts/success.jsx
@@ -0,0 +1,33 @@
+import { toast } from "react-toastify";
+import { Box, Typography } from "@mui/material";
+import { Beenhere } from "@mui/icons-material";
+
+export const successToast = (toastContainer) =>
+ toast.success(
+ () => (
+
+
+
+
+ {"Your operation was successful"}
+
+
+
+ ),
+ {
+ icon: false,
+ containerId: toastContainer,
+ autoClose: 3000,
+ hideProgressBar: true,
+ pauseOnHover: true,
+ closeOnClick: false,
+ draggable: true,
+ }
+ );
diff --git a/src/core/middlewares/withAuth.js b/src/core/middlewares/withAuth.js
new file mode 100644
index 0000000..216b55b
--- /dev/null
+++ b/src/core/middlewares/withAuth.js
@@ -0,0 +1,22 @@
+"use client";
+import { useAuth } from "@/lib/contexts/auth";
+import { usePathname, useRouter } from "next/navigation";
+import { useEffect } from "react";
+
+function WithAuthMiddleware({ children }) {
+ const router = useRouter();
+ const pathName = usePathname();
+ const { isAuth, initAuthState } = useAuth();
+
+ useEffect(() => {
+ if (!initAuthState) return;
+ if (!isAuth) {
+ router.replace(`${process.env.NEXT_PUBLIC_API_URL}/login?_back=${encodeURIComponent(pathName)}`);
+ }
+ }, [isAuth, initAuthState]);
+
+ if (!initAuthState || !isAuth) return null;
+ return <>{children}>;
+}
+
+export default WithAuthMiddleware;
diff --git a/src/core/utils/errorResponse.js b/src/core/utils/errorResponse.js
new file mode 100644
index 0000000..47317bd
--- /dev/null
+++ b/src/core/utils/errorResponse.js
@@ -0,0 +1,60 @@
+"use client";
+import { toast } from "react-toastify";
+import {
+ errorClientToast,
+ errorLogicToast,
+ errorServerToast,
+ errorTooManyToast,
+ errorUnauthorizedToast,
+ errorValidationToast,
+} from "@/core/components/Toasts/error";
+
+const isServerError = (status) => status >= 500 && status <= 599;
+const isClientError = (status) => status >= 400 && status <= 499;
+
+const errorServer = (response, notification, toastContainer) => {
+ if (notification) errorServerToast(toastContainer);
+};
+
+const errorClient = (response, notification, toastContainer) => {
+ switch (response.status) {
+ case 401:
+ if (notification) errorUnauthorizedToast(toastContainer);
+ break;
+ case 422:
+ if ("type" in response.data) {
+ if (Array.isArray(response.data.message)) {
+ response.data.message.map((item) => {
+ if (notification) errorLogicToast(item, toastContainer);
+ });
+ } else {
+ if (notification) errorLogicToast(response.data.message, toastContainer);
+ }
+ break;
+ }
+ if (notification) {
+ const errorsMap = Object.keys(response.data.errors);
+ const errorsArray = response.data.errors;
+
+ errorsMap.map((item, index) => {
+ errorValidationToast(errorsArray[item][0], toastContainer);
+ });
+ }
+ break;
+ case 429:
+ if (notification) errorTooManyToast(toastContainer);
+ break;
+ default:
+ if (notification) errorClientToast(toastContainer);
+ break;
+ }
+};
+
+export const errorResponse = (response, notification, toastContainer) => {
+ if (notification) toast.dismiss({ container: toastContainer });
+ if (isServerError(response.status)) {
+ errorServer(response, notification, toastContainer);
+ } else if (isClientError(response.status)) {
+ errorClient(response, notification, toastContainer);
+ }
+};
diff --git a/src/core/utils/filterMenuItems.js b/src/core/utils/filterMenuItems.js
new file mode 100644
index 0000000..7bceeb5
--- /dev/null
+++ b/src/core/utils/filterMenuItems.js
@@ -0,0 +1,17 @@
+export function filterMenuItems(items, _permissions = []) {
+ return items.reduce((acc, item) => {
+ if (item.permissions) {
+ if (item.permissions.some(permission => _permissions.includes(permission))) {
+ acc.push(item);
+ }
+ } else if (item.hasSubitems) {
+ const filteredSubItems = filterMenuItems(item.Subitems, _permissions);
+ if (filteredSubItems.length > 0) {
+ acc.push({ ...item, Subitems: filteredSubItems });
+ }
+ } else {
+ acc.push(item);
+ }
+ return acc;
+ }, []);
+}
\ No newline at end of file
diff --git a/src/core/utils/pageMenu.js b/src/core/utils/pageMenu.js
new file mode 100644
index 0000000..0129a78
--- /dev/null
+++ b/src/core/utils/pageMenu.js
@@ -0,0 +1,409 @@
+import { Security } from "@mui/icons-material";
+const prefix = process.env.NODE_ENV === 'production' ? '/v3' : ''
+
+export const pageMenu = [
+ {
+ id: "dashboard",
+ label: "پیشخوان",
+ type: "page",
+ route: prefix + "/dashboard",
+ icon: ,
+ permissions: ["all"]
+ },
+ {
+ id: "userManagement",
+ label: "مدیریت کاربران",
+ type: "page",
+ route: process.env.NEXT_PUBLIC_API_URL + "/v2/user_management",
+ icon: ,
+ permissions: ['full-user-management', 'limited-user-management']
+ },
+ {
+ id: "projectsManagment",
+ label: "پروژه های راهداری",
+ type: "menu",
+ icon: ,
+ hasSubitems: true,
+ Subitems: [
+ {
+ id: "projectsManagmentFinance",
+ label: "ثبت قرارداد و پروژه",
+ type: "page",
+ route: process.env.NEXT_PUBLIC_API_URL + "/v2/rahdari_projects/finance",
+ icon: ,
+ permissions: ['show-contract', 'show-contract-province'],
+ },
+ {
+ id: "projectsManagmentList",
+ label: "لیست پروژه ها",
+ type: "page",
+ route: process.env.NEXT_PUBLIC_API_URL + "/v2/rahdari_projects/projects_list",
+ icon: ,
+ permissions: ["all"]
+ },
+ {
+ id: "projectsManagmentProposal",
+ label: "پروژه های پیشنهادی",
+ type: "page",
+ route: process.env.NEXT_PUBLIC_API_URL + "/v2/rahdari_projects/proposal",
+ icon: ,
+ permissions: ['show-proposal', 'show-proposal-province']
+ },
+ {
+ id: "projectsManagmentLawmaker",
+ label: "درخواست نمایندگان",
+ type: "page",
+ route: process.env.NEXT_PUBLIC_API_URL + "/v2/rahdari_projects/lawmakers",
+ icon: ,
+ permissions: ['show-lawmaker', 'show-lawmaker-province']
+ },
+ {
+ id: "projectsManagmentMap",
+ label: "پراکندگی بر روی نقشه",
+ type: "page",
+ route: process.env.NEXT_PUBLIC_API_URL + "/v2/map?type=rahdari_projects",
+ icon: ,
+ permissions: ["all"]
+ },
+ ],
+ },
+ {
+ id: "roadItemManagment",
+ label: "فعالیت های روزانه",
+ type: "menu",
+ icon: ,
+ hasSubitems: true,
+ Subitems: [
+ {
+ id: "roadItemManagmentSupervisor",
+ label: "ارزیابی",
+ type: "menu",
+ icon: ,
+ hasSubitems: true,
+ Subitems: [
+ {
+ id: "roadItemManagmentSupervisorCartable",
+ label: "کارتابل",
+ type: "page",
+ route: process.env.NEXT_PUBLIC_API_URL + "/v2/road_items/supervisor/cartable",
+ permissions: ['show-road-item-supervise-cartable', 'show-road-item-supervise-cartable-province']
+ },
+ ],
+ },
+ {
+ id: "roadItemManagmentOparation",
+ label: "عملیات",
+ type: "menu",
+ icon: ,
+ hasSubitems: true,
+ Subitems: [
+ {
+ id: "roadItemManagmentOparationCreate",
+ label: "ثبت فعالیت",
+ type: "page",
+ route: process.env.NEXT_PUBLIC_API_URL + "/v2/road_items/operator/create",
+ permissions: ['create-road-item']
+ },
+ {
+ id: "roadItemManagmentOparationCartable",
+ label: "کارتابل",
+ type: "page",
+ route: process.env.NEXT_PUBLIC_API_URL + "/v2/road_items/operator/cartable",
+ permissions: ['create-road-item']
+ },
+ ],
+ },
+ {
+ id: "roadItemManagmentMap",
+ label: "پراکندگی بر روی نقشه",
+ type: "page",
+ route: process.env.NEXT_PUBLIC_API_URL + "/v2/map?type=road_items",
+ icon: ,
+ permissions: ['show-road-item-supervise-cartable', 'show-road-item-supervise-cartable-province', 'create-road-item']
+ },
+ {
+ id: "roadItemManagmentReport",
+ label: "گزارش ها",
+ type: "page",
+ route: process.env.NEXT_PUBLIC_API_URL + "/v2/road_items/report",
+ icon: ,
+ permissions: ['show-road-item-supervise-cartable', 'show-road-item-supervise-cartable-province', 'create-road-item']
+ },
+ ],
+ },
+ {
+ id: "roadPatrolManagment",
+ label: "گشت راهداری و ترابری",
+ type: "menu",
+ icon: ,
+ hasSubitems: true,
+ Subitems: [
+ {
+ id: "roadPatrolManagmentSupervisor",
+ label: "ارزیابی",
+ type: "menu",
+ icon: ,
+ hasSubitems: true,
+ Subitems: [
+ {
+ id: "roadPatrolManagmentSupervisorCartable",
+ label: "کارتابل",
+ type: "page",
+ route: process.env.NEXT_PUBLIC_API_URL + "/road_patrols/supervisor/cartable",
+ permissions: ['show-road-patrol-supervise-cartable', 'show-road-patrol-supervise-cartable-province']
+ },
+ ],
+ },
+ {
+ id: "roadPatrolManagmentOparation",
+ label: "عملیات",
+ type: "menu",
+ icon: ,
+ hasSubitems: true,
+ Subitems: [
+ {
+ id: "roadPatrolManagmentOparationCreate",
+ label: "ثبت اقدام",
+ type: "page",
+ route: process.env.NEXT_PUBLIC_API_URL + "/v2/road_patrols/operator/create",
+ permissions: ['add-road-patrol']
+ },
+ {
+ id: "roadPatrolManagmentOparationCartable",
+ label: "کارتابل",
+ type: "page",
+ route: process.env.NEXT_PUBLIC_API_URL + "/v2/road_patrols/operator/cartable",
+ permissions: ['add-road-patrol']
+ },
+ ],
+ },
+ {
+ id: "roadPatrolManagmentMap",
+ label: "پراکندگی بر روی نقشه",
+ type: "page",
+ route: process.env.NEXT_PUBLIC_API_URL + "/v2/map?type=road_patrols",
+ icon: ,
+ permissions: ['add-road-patrol', 'show-road-patrol-supervise-cartable', 'show-road-patrol-supervise-cartable-province']
+ },
+ {
+ id: "roadPatrolManagmentReport",
+ label: "گزارش ها",
+ type: "page",
+ route: process.env.NEXT_PUBLIC_API_URL + "/v2/road_patrols/report",
+ icon: ,
+ permissions: ['add-road-patrol', 'show-road-patrol-supervise-cartable', 'show-road-patrol-supervise-cartable-province']
+ },
+ ],
+ },
+ {
+ id: "inquiryPrivacyManagment",
+ label: "استعلام حریم راه",
+ type: "menu",
+ icon: ,
+ hasSubitems: true,
+ Subitems: [
+ {
+ id: "inquiryPrivacyManagmentFencing",
+ label: "احداث و بهره برداری دیوارکشی و مستحدثات",
+ type: "page",
+ route: prefix + "/dashboard/inquiry_privacy/fencing",
+ permissions: ['all']
+ },
+ {
+ id: "inquiryPrivacyManagmentRoad",
+ label: "احداث راه دسترسی اختصاصی",
+ type: "page",
+ route: prefix + "/dashboard/inquiry_privacy/road",
+ permissions: ['all']
+ },
+ {
+ id: "inquiryPrivacyManagmentInfrastructure",
+ label: "احداث و بهره برداری تاسیسات زیربنایی",
+ type: "page",
+ route: prefix + "/dashboard/inquiry_privacy/infrastructure",
+ permissions: ['all']
+ },
+ ]
+ },
+ {
+ id: "safetyAndPrivacyManagment",
+ label: "نگهداری حریم راه",
+ type: "menu",
+ icon: ,
+ hasSubitems: true,
+ Subitems: [
+ {
+ id: "safetyAndPrivacyManagmentOparation",
+ label: "عملیات",
+ type: "menu",
+ icon: ,
+ hasSubitems: true,
+ Subitems: [
+ {
+ id: "safetyAndPrivacyManagmentOparationCreate",
+ label: "ثبت فعالیت",
+ type: "page",
+ route: process.env.NEXT_PUBLIC_API_URL + "/safety_and_privacy/operator/first_step",
+ permissions: ['add-safety-and-privacy']
+ },
+ {
+ id: "safetyAndPrivacyManagmentOparationCartable",
+ label: "کارتابل",
+ type: "page",
+ route: process.env.NEXT_PUBLIC_API_URL + "/v2/safety_and_privacy/operator/cartable",
+ permissions: ['show-safety-and-privacy-operator-cartable', 'show-safety-and-privacy-operator-cartable-province', 'show-safety-and-privacy-operator-cartable-edarate-shahri']
+ },
+ ],
+ },
+ {
+ id: "safetyAndPrivacyManagmentMap",
+ label: "پراکندگی بر روی نقشه",
+ type: "page",
+ route: process.env.NEXT_PUBLIC_API_URL + "/v2/map?type=safety_and_privacy",
+ icon: ,
+ permissions: ['show-safety-and-privacy-operator-cartable', 'show-safety-and-privacy-operator-cartable-province',
+ 'show-safety-and-privacy-operator-cartable-edarate-shahri', 'add-safety-and-privacy']
+ },
+ {
+ id: "safetyAndPrivacyManagmentReport",
+ label: "گزارش ها",
+ type: "page",
+ route: process.env.NEXT_PUBLIC_API_URL + "/v2/safety_and_privacy/report",
+ icon: ,
+ permissions: ['show-safety-and-privacy-operator-cartable', 'show-safety-and-privacy-operator-cartable-province',
+ 'show-safety-and-privacy-operator-cartable-edarate-shahri', 'add-safety-and-privacy']
+ },
+ ],
+ },
+ {
+ id: "roadObservationsManagment",
+ label: "واکنش سریع",
+ type: "menu",
+ icon: ,
+ hasSubitems: true,
+ Subitems: [
+ {
+ id: "roadObservationsManagmentSupervisor",
+ label: "ارزیابی",
+ type: "menu",
+ icon: ,
+ hasSubitems: true,
+ Subitems: [
+ {
+ id: "roadObservationsManagmentSupervisorCartable",
+ label: "کارتابل",
+ type: "page",
+ route: process.env.NEXT_PUBLIC_API_URL + "/v2/road_observations/supervisor/cartable",
+ permissions: ['supervise-fast-react', 'supervise-fast-react-province']
+ },
+ ],
+ },
+ {
+ id: "roadObservationsManagmentList",
+ label: "رسیدگی به شکایات",
+ type: "page",
+ route: process.env.NEXT_PUBLIC_API_URL + "/v2/road_observations",
+ icon: ,
+ permissions: ['show-fast-react', 'show-fast-react-province', 'show-fast-react-edarate-shahri']
+ },
+ {
+ id: "roadObservationsManagmentOparation",
+ label: "عملیات",
+ type: "menu",
+ icon: ,
+ hasSubitems: true,
+ Subitems: [
+ {
+ id: "roadObservationsManagmentOparationCartable",
+ label: "کارتابل",
+ type: "page",
+ route: process.env.NEXT_PUBLIC_API_URL + "/v2/road_observations/operator/cartable",
+ permissions: ['show-fast-react', 'show-fast-react-province', 'show-fast-react-edarate-shahri']
+ },
+ ],
+ },
+ {
+ id: "roadObservationsManagmentMap",
+ label: "پراکندگی بر روی نقشه",
+ type: "page",
+ route: process.env.NEXT_PUBLIC_API_URL + "/v2/map?type=road_observations",
+ icon: ,
+ permissions: ["all"]
+ },
+ {
+ id: "roadObservationsManagmentReport",
+ label: "گزارش ها",
+ type: "page",
+ route: process.env.NEXT_PUBLIC_API_URL + "/v2/road_observations/report/index",
+ icon: ,
+ permissions: ["all"]
+ },
+ ],
+ },
+ {
+ id: "winterCampManagment",
+ label: "قرارگاه زمستانی",
+ type: "menu",
+ icon: ,
+ hasSubitems: true,
+ Subitems: [
+ {
+ id: "winterCampManagmentBlocking",
+ label: "محور های انسدادی",
+ type: "page",
+ route: process.env.NEXT_PUBLIC_API_URL + "/v2/winter_camp",
+ icon: ,
+ permissions: ['show-camp', 'show-camp-province']
+ },
+ {
+ id: "winterCampManagmentFirstRing",
+ label: "محور های حلقه اول",
+ type: "page",
+ route: process.env.NEXT_PUBLIC_API_URL + "/v2/map?type=camp",
+ icon: ,
+ permissions: ['show-camp', 'show-camp-province']
+ },
+ {
+ id: "winterCampManagmentReport",
+ label: "گزارش ها",
+ type: "page",
+ route: process.env.NEXT_PUBLIC_API_URL + "/v2/map?type=campNew",
+ icon: ,
+ permissions: ['show-camp', 'show-camp-province']
+ },
+ ],
+ },
+ {
+ id: "receiptManagment",
+ label: "خسارات وارده بر ابنیه فنی و تاسیسات راه",
+ type: "menu",
+ icon: ,
+ hasSubitems: true,
+ Subitems: [
+ {
+ id: "receiptManagmentOparation",
+ label: "عملیات",
+ type: "menu",
+ icon: ,
+ hasSubitems: true,
+ Subitems: [
+ {
+ id: "receiptManagmentOparationCartable",
+ label: "کارتابل",
+ type: "page",
+ route: process.env.NEXT_PUBLIC_API_URL + "/v2/receipt",
+ permissions: ["all"]
+ },
+ ],
+ },
+ {
+ id: "receiptManagmentReport",
+ label: "گزارش ها",
+ type: "page",
+ route: process.env.NEXT_PUBLIC_API_URL + "/v2/receipt/report",
+ icon: ,
+ permissions: ["all"]
+ },
+ ],
+ },
+];
diff --git a/src/core/utils/routes.js b/src/core/utils/routes.js
new file mode 100644
index 0000000..81ac016
--- /dev/null
+++ b/src/core/utils/routes.js
@@ -0,0 +1,6 @@
+const api = process.env.NEXT_PUBLIC_API_URL;
+
+export const GET_USER_ROUTE = api + "/webapi/user/get-permission";
+export const GET_LOGIN_ROUTE = api + "/login_dev";
+
+export const GET_PERMISSIONS_ROUTE = api + "/webapi/user/get-permission";
\ No newline at end of file
diff --git a/src/core/utils/successRequest.js b/src/core/utils/successRequest.js
new file mode 100644
index 0000000..b06d83d
--- /dev/null
+++ b/src/core/utils/successRequest.js
@@ -0,0 +1,10 @@
+"use client";
+import { toast } from "react-toastify";
+import { successToast } from "@/core/components/Toasts/success";
+
+export const successRequest = (notification, toastContainer) => {
+ if (notification) {
+ toast.dismiss({ container: toastContainer });
+ successToast(toastContainer);
+ }
+};
diff --git a/src/core/utils/theme.js b/src/core/utils/theme.js
index efe56eb..f32031b 100644
--- a/src/core/utils/theme.js
+++ b/src/core/utils/theme.js
@@ -7,6 +7,16 @@ const theme = createTheme({
fontFamily: `IRANSansFaNum, sans-serif`,
fontSize: 12,
},
+ palette: {
+ primary: {
+ main: "#2070af",
+ contrastText: "#fff",
+ },
+ primary2: {
+ main: "#015688",
+ contrastText: "#fff",
+ },
+ },
});
export default theme;
diff --git a/src/lib/contexts/DataTable.js b/src/lib/contexts/DataTable.js
new file mode 100644
index 0000000..fb03fd7
--- /dev/null
+++ b/src/lib/contexts/DataTable.js
@@ -0,0 +1,11 @@
+import { createContext, useState } from "react";
+
+export const DataTableContext = createContext();
+
+const DataTableProvider = ({ children }) => {
+ const [filterData, setFilterData] = useState({});
+
+ return {children};
+};
+
+export default DataTableProvider;
diff --git a/src/lib/contexts/auth.js b/src/lib/contexts/auth.js
new file mode 100644
index 0000000..f2df0e6
--- /dev/null
+++ b/src/lib/contexts/auth.js
@@ -0,0 +1,82 @@
+"use client";
+import { createContext, useCallback, useContext, useEffect, useReducer } from "react";
+import useRequest from "../hooks/useRequest";
+import { GET_USER_ROUTE } from "@/core/utils/routes";
+
+const initAuth = {
+ initAuthState: false,
+ isAuth: false,
+ user: {},
+};
+
+const authReducer = (state, action) => {
+ switch (action.type) {
+ case "CLEAR_USER":
+ return { ...state, user: {} };
+ case "CHANGE_USER":
+ return { ...state, user: action.user };
+ case "CHANGE_AUTH_STATE":
+ return { ...state, isAuth: action.isAuth };
+ case "CHANGE_INIT_AUTH":
+ return { ...state, initAuthState: action.initAuthState };
+ default:
+ return state;
+ }
+};
+
+const AuthContext = createContext();
+
+export const AuthProvider = ({ children }) => {
+ const [state, dispatch] = useReducer(authReducer, initAuth);
+ const request = useRequest();
+
+ const clearUser = useCallback(() => {
+ dispatch({ type: "CLEAR_USER" });
+ }, []);
+
+ const changeUser = useCallback((user) => {
+ dispatch({ type: "CHANGE_USER", user });
+ }, []);
+
+ const changeAuthState = useCallback((isAuth) => {
+ dispatch({ type: "CHANGE_AUTH_STATE", isAuth });
+ }, []);
+
+ const changeInitAuth = useCallback((initAuthState) => {
+ dispatch({ type: "CHANGE_INIT_AUTH", initAuthState });
+ }, []);
+
+ const getUser = useCallback(async () => {
+ try {
+ const { data } = await request(GET_USER_ROUTE, "get");
+ changeUser(data);
+ changeAuthState(true);
+ changeInitAuth(true);
+ } catch (error) {
+ if (error.response && error.response.status === 401) {
+ clearUser();
+ changeAuthState(false);
+ changeInitAuth(true);
+ }
+ }
+ }, [clearUser, changeUser, changeAuthState, changeInitAuth]);
+
+ useEffect(() => {
+ getUser();
+ }, []);
+
+ return (
+
+ {children}
+
+ );
+};
+
+export const useAuth = () => useContext(AuthContext);
diff --git a/src/lib/contexts/tableSetting.jsx b/src/lib/contexts/tableSetting.js
similarity index 100%
rename from src/lib/contexts/tableSetting.jsx
rename to src/lib/contexts/tableSetting.js
diff --git a/src/lib/hooks/useDataTable.js b/src/lib/hooks/useDataTable.js
new file mode 100644
index 0000000..e396908
--- /dev/null
+++ b/src/lib/hooks/useDataTable.js
@@ -0,0 +1,9 @@
+import { useContext } from "react";
+import { DataTableContext } from "@/lib/contexts/DataTable";
+
+const useTableSetting = () => {
+ const { filterData, setFilterData } = useContext(DataTableContext);
+ return { filterData, setFilterData };
+};
+
+export default useTableSetting;
diff --git a/src/lib/hooks/usePermissions.js b/src/lib/hooks/usePermissions.js
new file mode 100644
index 0000000..4f54b41
--- /dev/null
+++ b/src/lib/hooks/usePermissions.js
@@ -0,0 +1,18 @@
+import { GET_PERMISSIONS_ROUTE } from "@/core/utils/routes";
+import useSWR from "swr";
+import useRequest from "./useRequest";
+
+export const usePermissions = () => {
+ const request = useRequest()
+
+ const fetcher = async (url) => {
+ try {
+ const response = await request(url)
+ return response.data.data
+ } catch (error) {
+ throw new Error();
+ }
+ };
+
+ return useSWR(GET_PERMISSIONS_ROUTE, fetcher, { keepPreviousData: true });
+}
\ No newline at end of file
diff --git a/src/lib/hooks/useRequest.js b/src/lib/hooks/useRequest.js
new file mode 100644
index 0000000..2e35512
--- /dev/null
+++ b/src/lib/hooks/useRequest.js
@@ -0,0 +1,46 @@
+import { errorResponse } from "@/core/utils/errorResponse";
+import { successRequest } from "@/core/utils/successRequest";
+import axios from "axios";
+
+const defaultOptions = {
+ data: {},
+ requestOptions: {
+ headers: {},
+ },
+ notification: {
+ show: true,
+ success: false,
+ failed: true,
+ },
+};
+
+const useRequest = (initOptions) => {
+ const _options = Object.assign({}, defaultOptions, initOptions);
+
+ return async (url = "", method = "get", options) => {
+ const mergedOptions = Object.assign({}, _options, options);
+
+ try {
+ const response = await axios({
+ url,
+ method,
+ data: method === "get" ? null : mergedOptions.data,
+ withCredentials: true,
+ ...mergedOptions.requestOptions,
+ });
+ successRequest(mergedOptions.notification.show && mergedOptions.notification.success, "request_data");
+ return response;
+ } catch (error) {
+ if (error.response) {
+ errorResponse(
+ error.response,
+ mergedOptions.notification.show && mergedOptions.notification.failed,
+ "request_data"
+ );
+ }
+ throw error;
+ }
+ };
+};
+
+export default useRequest;
diff --git a/src/lib/hooks/useTableSetting.js b/src/lib/hooks/useTableSetting.js
new file mode 100644
index 0000000..6588446
--- /dev/null
+++ b/src/lib/hooks/useTableSetting.js
@@ -0,0 +1,9 @@
+import { useContext } from "react";
+import { TableSettingContext } from "../contexts/tableSetting";
+
+const useTableSetting = () => {
+ const { settingStore, hideAction, sortAction, filterAction, refactorAction } = useContext(TableSettingContext);
+ return { settingStore, hideAction, sortAction, filterAction, refactorAction };
+};
+
+export default useTableSetting;