CFE-3 Merging
This commit is contained in:
@@ -9,54 +9,45 @@ import useRequest from "@/lib/app/hooks/useRequest";
|
||||
|
||||
function DataTable(props) {
|
||||
const requestServer = useRequest({auth: true})
|
||||
const fetcher = (...args) => {
|
||||
return requestServer(args, 'get', {
|
||||
pending: false,
|
||||
success: {notification: {show: false}}
|
||||
}).then((response) => {
|
||||
setRowCount(response.data.meta.totalRowCount);
|
||||
return response.data.data;
|
||||
}).catch(() => {
|
||||
})
|
||||
};
|
||||
const t = useTranslations();
|
||||
const {languageApp, languageList} = useLanguage();
|
||||
const [columnFilters, setColumnFilters] = useState([]);
|
||||
const [sorting, setSorting] = useState([]);
|
||||
const [pagination, setPagination] = useState({pageIndex: 0, pageSize: 10});
|
||||
const [rowCount, setRowCount] = useState(0);
|
||||
const [columnFilterFns, setColumnFilterFns] = useState(() => {
|
||||
let output = {};
|
||||
const list = props.columns.map((item) =>
|
||||
item.enableColumnFilter ? {[item.id]: item.filterFn} : {[item.id]: ""}
|
||||
);
|
||||
for (var key in list) {
|
||||
var nestedObj = list[key];
|
||||
for (var nestedKey in nestedObj) {
|
||||
for (const key in list) {
|
||||
const nestedObj = list[key];
|
||||
for (const nestedKey in nestedObj) {
|
||||
output[nestedKey] = nestedObj[nestedKey];
|
||||
}
|
||||
}
|
||||
return output;
|
||||
});
|
||||
|
||||
const [updateTime, setupdateTime] = useState(
|
||||
const [updateTime, setUpdateTime] = useState(
|
||||
moment().format("HH:mm | jYYYY/jM/jD")
|
||||
);
|
||||
|
||||
const tableLocalization = useMemo(
|
||||
() =>
|
||||
languageList.find((item) => item.key == languageApp).tableLocalization,
|
||||
languageList.find((item) => item.key === languageApp).tableLocalization,
|
||||
[languageApp, languageList]
|
||||
);
|
||||
|
||||
const fetchUrl = useMemo(() => {
|
||||
const url = new URL(props.tableUrl);
|
||||
url.searchParams.set(
|
||||
const params = new URLSearchParams();
|
||||
params.set(
|
||||
"start",
|
||||
`${pagination.pageIndex * pagination.pageSize}`
|
||||
);
|
||||
const filters = columnFilters.map((filter) => {
|
||||
let datatype;
|
||||
for (const i in props.columns) {
|
||||
if (props.columns[i].id == filter.id) {
|
||||
if (props.columns[i].id === filter.id) {
|
||||
datatype = props.columns[i].datatype;
|
||||
}
|
||||
}
|
||||
@@ -66,32 +57,40 @@ function DataTable(props) {
|
||||
datatype: datatype,
|
||||
};
|
||||
});
|
||||
url.searchParams.set("size", pagination.pageSize);
|
||||
url.searchParams.set("filters", JSON.stringify(filters ?? []));
|
||||
url.searchParams.set("sorting", JSON.stringify(sorting ?? []));
|
||||
return url;
|
||||
params.set("size", pagination.pageSize);
|
||||
params.set("filters", JSON.stringify(filters ?? []));
|
||||
params.set("sorting", JSON.stringify(sorting ?? []));
|
||||
return `${props.tableUrl}?${params}`;
|
||||
}, [
|
||||
props.tableUrl,
|
||||
columnFilters,
|
||||
columnFilterFns,
|
||||
pagination,
|
||||
sorting,
|
||||
props.columns,
|
||||
]);
|
||||
|
||||
const {data, isValidating, mutate} = useSWR(fetchUrl, fetcher, {
|
||||
revalidateIfStale: false,
|
||||
revalidateOnFocus: false,
|
||||
revalidateOnReconnect: false
|
||||
});
|
||||
const {data, isValidating, mutate} = useSWR(fetchUrl, (...args) =>
|
||||
requestServer(args, 'get', {
|
||||
pending: false,
|
||||
success: {notification: {show: false}}
|
||||
}).then((response) => response.data).catch(() => {
|
||||
})
|
||||
, {
|
||||
revalidateIfStale: false,
|
||||
revalidateOnFocus: false,
|
||||
revalidateOnReconnect: true,
|
||||
keepPreviousData: true
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setupdateTime(moment().format("HH:mm | jYYYY/jM/jD"));
|
||||
setUpdateTime(moment().format("HH:mm | jYYYY/jM/jD"));
|
||||
}, [isValidating, languageApp]);
|
||||
|
||||
|
||||
return (
|
||||
<MaterialReactTable
|
||||
localization={tableLocalization}
|
||||
data={data ?? []}
|
||||
data={data?.data ?? []}
|
||||
manualFiltering
|
||||
manualPagination
|
||||
manualSorting
|
||||
@@ -122,7 +121,7 @@ function DataTable(props) {
|
||||
}}
|
||||
enableColumnFilterModes
|
||||
muiTablePaperProps={{elevation: 0}}
|
||||
rowCount={rowCount}
|
||||
rowCount={data?.meta.totalRowCount}
|
||||
onColumnFilterFnsChange={setColumnFilterFns}
|
||||
onColumnFiltersChange={setColumnFilters}
|
||||
onPaginationChange={setPagination}
|
||||
@@ -131,8 +130,8 @@ function DataTable(props) {
|
||||
renderTopToolbarCustomActions={({table}) => (
|
||||
<>
|
||||
{props.enableCustomToolbar /* send condition */
|
||||
? (<props.CustomToolbar fetchUrl={fetchUrl} mutate={mutate}/>) /* send component */
|
||||
: <span></span>}
|
||||
? <props.CustomToolbar fetchUrl={fetchUrl} mutate={mutate}/> /* send component */
|
||||
: "<span></span>"}
|
||||
</>
|
||||
)}
|
||||
renderBottomToolbarCustomActions={({table}) => (
|
||||
@@ -156,6 +155,7 @@ function DataTable(props) {
|
||||
</>
|
||||
)}
|
||||
state={{
|
||||
showProgressBars: isValidating,
|
||||
columnFilters,
|
||||
columnFilterFns,
|
||||
pagination,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {Backdrop, Box, styled} from "@mui/material";
|
||||
import SvgLoading from "@/core/components/svgs/SvgLoading";
|
||||
import {Backdrop, Box} from "@mui/material";
|
||||
import {styled} from "@mui/material/styles";
|
||||
|
||||
const LoadingImage = styled(Box)({
|
||||
"@keyframes load": {
|
||||
@@ -23,7 +24,7 @@ const LoadingHardPage = ({children, loading}) => {
|
||||
return (
|
||||
<>
|
||||
<Backdrop
|
||||
sx={{bgcolor: "#fff", zIndex: (theme) => theme.zIndex.drawer + 1}}
|
||||
sx={{bgcolor: "#fff", zIndex: (theme) => theme.zIndex.modal + 1}}
|
||||
open={loading}
|
||||
>
|
||||
<LoadingImage
|
||||
|
||||
33
src/core/components/Middleware/RolePermissionComponent.jsx
Normal file
33
src/core/components/Middleware/RolePermissionComponent.jsx
Normal file
@@ -0,0 +1,33 @@
|
||||
import { Button, Typography } from "@mui/material";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { NextLinkComposed } from "@/core/components/LinkRouting";
|
||||
import Message from "@/core/components/Messages";
|
||||
|
||||
const RolePermissionComponent = () => {
|
||||
const t = useTranslations();
|
||||
|
||||
return (
|
||||
<Message
|
||||
text={
|
||||
<Typography sx={{ textAlign: "center" }}>
|
||||
{t("Permission.typography_you_dont_have_access")}
|
||||
</Typography>
|
||||
}
|
||||
actions={
|
||||
<>
|
||||
<Button
|
||||
variant="contained"
|
||||
component={NextLinkComposed}
|
||||
to={{
|
||||
pathname: "/dashboard",
|
||||
}}
|
||||
>
|
||||
{t("Permission.button_back_dashboard")}
|
||||
</Button>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default RolePermissionComponent;
|
||||
36
src/core/components/Middleware/WithAuthComponent.jsx
Normal file
36
src/core/components/Middleware/WithAuthComponent.jsx
Normal file
@@ -0,0 +1,36 @@
|
||||
import { Button, Typography } from "@mui/material";
|
||||
import { useRouter } from "next/router";
|
||||
import Message from "@/core/components/Messages";
|
||||
import { NextLinkComposed } from "@/core/components/LinkRouting";
|
||||
import {useTranslations} from "next-intl";
|
||||
|
||||
const WithAuthComponent = () => {
|
||||
const router = useRouter();
|
||||
const t = useTranslations();
|
||||
|
||||
return (
|
||||
<Message
|
||||
text={
|
||||
<Typography sx={{ textAlign: "center" }}>
|
||||
{t("Authorization.typography_your_access_to_this_page_has_expired_Please_login_again")}
|
||||
</Typography>
|
||||
}
|
||||
actions={
|
||||
<>
|
||||
<Button
|
||||
variant="contained"
|
||||
component={NextLinkComposed}
|
||||
to={{
|
||||
pathname: "/login-expert",
|
||||
query: {back_url: encodeURIComponent(router.asPath)},
|
||||
}}
|
||||
>
|
||||
{t("login")}
|
||||
</Button>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default WithAuthComponent;
|
||||
25
src/core/components/Middleware/WithoutAuthComponent.jsx
Normal file
25
src/core/components/Middleware/WithoutAuthComponent.jsx
Normal file
@@ -0,0 +1,25 @@
|
||||
import {Button, Stack, Typography} from "@mui/material";
|
||||
import Message from "@/core/components/Messages";
|
||||
import {useTranslations} from "next-intl";
|
||||
const WithoutAuthComponent = ({ backUrlDecodedPath }) => {
|
||||
const t = useTranslations();
|
||||
|
||||
return (
|
||||
<Message
|
||||
text={
|
||||
<Stack alignItems="center" spacing={2}>
|
||||
<Typography sx={{ textAlign: "center" }}>
|
||||
{t("Authorization.typography_your_login_is_valid_and_you_do_not_need_to_login_again")}
|
||||
</Typography>
|
||||
<Typography>
|
||||
{t("Authorization.typography_redirect_to")}{" "}
|
||||
{backUrlDecodedPath
|
||||
? t("Authorization.typography_routing_previuos_page")
|
||||
: t("Authorization.typography_routing_dashbaord_page")}
|
||||
</Typography>
|
||||
</Stack>
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
export default WithoutAuthComponent;
|
||||
@@ -0,0 +1,23 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import MockAppWithProviders from "../../../../../mocks/AppWithProvider";
|
||||
import RolePermissionComponent from "@/core/components/Middleware/RolePermissionComponent";
|
||||
|
||||
describe('RolePermissionComponent From RolePermissionComponent page', () => {
|
||||
describe('Rendering', () => {
|
||||
it('should render the error message when user dont have permission', () => {
|
||||
render(<MockAppWithProviders><RolePermissionComponent /></MockAppWithProviders>);
|
||||
|
||||
expect(screen.queryByText('شما دسترسی لازم برای مشاهده این صفحه را ندارید!')).toBeInTheDocument();
|
||||
|
||||
});
|
||||
|
||||
it('should render the back dashboard button when user dont have permission', () => {
|
||||
render(<MockAppWithProviders><RolePermissionComponent /></MockAppWithProviders>);
|
||||
|
||||
const backButton = screen.queryByText('بازگشت به صفحه اصلی');
|
||||
|
||||
expect(backButton).toBeInTheDocument();
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,39 @@
|
||||
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
|
||||
import MockAppWithProviders from "../../../../../mocks/AppWithProvider";
|
||||
import WithAuthComponent from "@/core/components/Middleware/WithAuthComponent";
|
||||
import mockRouter from 'next-router-mock';
|
||||
|
||||
describe('WithAuthComponent From WithAuthMiddleware', () => {
|
||||
describe('Rendering', () => {
|
||||
it('should see expired text in render component', () => {
|
||||
render(<MockAppWithProviders><WithAuthComponent /></MockAppWithProviders>);
|
||||
|
||||
const authText = screen.queryByText('دسترسی شما منقضی شده است لطفا دوباره ورود نمایید.');
|
||||
|
||||
expect(authText).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should see login button in render component', () => {
|
||||
render(<MockAppWithProviders><WithAuthComponent /></MockAppWithProviders>);
|
||||
|
||||
const loginButton = screen.queryByText('ورود');
|
||||
|
||||
expect(loginButton).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
describe('behavior', () => {
|
||||
it('should see if router updated in login button', () => {
|
||||
|
||||
mockRouter.query.back_url = 'back_url'
|
||||
render(<MockAppWithProviders><WithAuthComponent /></MockAppWithProviders>);
|
||||
|
||||
const loginButton = screen.queryByText('ورود');
|
||||
|
||||
fireEvent.click(loginButton);
|
||||
|
||||
expect(mockRouter).toMatchObject({
|
||||
query: { back_url: "back_url" },
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,37 @@
|
||||
import {render, screen, waitFor} from '@testing-library/react';
|
||||
import WithoutAuthComponent from "@/core/components/Middleware/WithoutAuthComponent";
|
||||
import MockAppWithProviders from "../../../../../mocks/AppWithProvider";
|
||||
import WithoutAuthMiddleware from "@/middlewares/WithoutAuth";
|
||||
|
||||
describe('WithoutAuthComponent From WithoutAuthMiddleware', () => {
|
||||
describe('Rendering', () => {
|
||||
it('should render the message with correct text when backUrlDecodedPath is not provided', async () => {
|
||||
|
||||
localStorage.setItem("_token", 'token');
|
||||
|
||||
render(<MockAppWithProviders><WithoutAuthMiddleware /></MockAppWithProviders>);
|
||||
|
||||
await waitFor(()=>{
|
||||
expect(screen.queryByText('شما دسترسی لازم به این صفحه را دارید نیاز به ورود مجدد نیست.')).toBeInTheDocument();
|
||||
|
||||
expect(screen.queryByText('درحال رفتن به صفحه داشبورد...')).toBeInTheDocument();
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
it('should render the message with a different text when backUrlDecodedPath is provided', async() => {
|
||||
render(<MockAppWithProviders><WithoutAuthComponent backUrlDecodedPath="/dashboard/change-password" /></MockAppWithProviders>);
|
||||
|
||||
localStorage.setItem("_token", 'token');
|
||||
|
||||
render(<MockAppWithProviders><WithoutAuthMiddleware /></MockAppWithProviders>);
|
||||
|
||||
await waitFor(()=>{
|
||||
|
||||
expect(screen.queryByText('درحال رفتن به صفحه قبل...')).toBeInTheDocument();
|
||||
})
|
||||
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -6,13 +6,13 @@ import WifiOffIcon from '@mui/icons-material/WifiOff';
|
||||
import {useTranslations} from "next-intl";
|
||||
|
||||
const NetworkComponent = () => {
|
||||
const toastId = useRef(null);
|
||||
const networkToastId = useRef(null);
|
||||
const network = useNetwork()
|
||||
const t = useTranslations()
|
||||
|
||||
useEffect(() => {
|
||||
if (network.online) {
|
||||
toast.update(toastId.current, {
|
||||
toast.update(networkToastId.current, {
|
||||
type: toast.TYPE.SUCCESS,
|
||||
render: t('online_message'),
|
||||
autoClose: 2000,
|
||||
@@ -22,8 +22,7 @@ const NetworkComponent = () => {
|
||||
});
|
||||
return
|
||||
}
|
||||
toast.dismiss()
|
||||
toastId.current = toast.warn(t('offline_message'), {
|
||||
networkToastId.current = toast.warn(t('offline_message'), {
|
||||
autoClose: false, closeButton: false, closeOnClick: false, icon: <WifiOffIcon/>
|
||||
})
|
||||
}, [network.online]);
|
||||
|
||||
@@ -5,7 +5,6 @@ const StyledFab = styled(Fab)`
|
||||
bottom: 16px;
|
||||
/* @noflip */
|
||||
left: 16px;
|
||||
z-index: 1500;
|
||||
`;
|
||||
|
||||
export default StyledFab;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {styled} from "@mui/material";
|
||||
import {Form} from "formik";
|
||||
import {styled} from "@mui/material/styles";
|
||||
|
||||
const StyledForm = styled(Form)``;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {useTheme} from "@mui/material";
|
||||
import {useTheme} from "@mui/material/styles";
|
||||
|
||||
const Svg403 = ({width, height}) => {
|
||||
const theme = useTheme()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {useTheme} from "@mui/material";
|
||||
import {useTheme} from "@mui/material/styles";
|
||||
|
||||
const Svg404 = ({width, height}) => {
|
||||
const theme = useTheme()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {useTheme} from "@mui/material";
|
||||
import {useTheme} from "@mui/material/styles";
|
||||
|
||||
const SvgChangePassword = ({width, height}) => {
|
||||
const theme = useTheme()
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import {useTheme} from "@mui/material";
|
||||
import {useTheme} from "@mui/material/styles";
|
||||
|
||||
const SvgDashboard = ({width, height}) => {
|
||||
const theme = useTheme()
|
||||
const fillColor = theme.palette.primary.main
|
||||
|
||||
|
||||
return (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" data-name="Layer 1" width={width} height={height}
|
||||
viewBox="0 0 1029.56255 548.69495">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {useTheme} from "@mui/material";
|
||||
import {useTheme} from "@mui/material/styles";
|
||||
|
||||
const SvgLoading = ({width, height}) => {
|
||||
const theme = useTheme()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {useTheme} from "@mui/material";
|
||||
import {useTheme} from "@mui/material/styles";
|
||||
|
||||
const SvgLogin = ({width, height}) => {
|
||||
const theme = useTheme()
|
||||
|
||||
@@ -4,6 +4,10 @@ const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL;
|
||||
export const GET_USER_TOKEN = BASE_URL + "/api/auth/login";
|
||||
//end login
|
||||
|
||||
//edit profile
|
||||
export const UPDATE_AVATAR = BASE_URL + "";
|
||||
//end edit profile
|
||||
|
||||
// role list
|
||||
export const GET_ROLE_LIST = BASE_URL + "/api/roles/list";
|
||||
// role list
|
||||
@@ -13,13 +17,32 @@ export const GET_PROVINCE_LIST = BASE_URL + "/api/provinces";
|
||||
// province list
|
||||
|
||||
//change password
|
||||
export const SET_USER_PASSWORD = BASE_URL + "/dashboard/profile/change_password";
|
||||
export const SET_USER_PASSWORD = BASE_URL + "/api/profile/change_password";
|
||||
//end change password
|
||||
|
||||
//user data
|
||||
export const GET_USER_ROUTE = BASE_URL + "/api/profile/info";
|
||||
//user data
|
||||
|
||||
//sidebar notification
|
||||
export const GET_SIDEBAR_NOTIFICATION = BASE_URL + "/dashboard/notification"
|
||||
//sidebar notification
|
||||
|
||||
|
||||
// role management
|
||||
export const GET_ROLE_MANAGEMENT =
|
||||
BASE_URL + "/api/roles"
|
||||
export const ADD_ROLE_MANAGEMENT =
|
||||
BASE_URL + "/api/roles"
|
||||
export const UPDATE_ROLE_MANAGEMENT =
|
||||
BASE_URL + "/api/roles"
|
||||
export const DELETE_ROLE_MANAGEMENT =
|
||||
BASE_URL + "/api/roles"
|
||||
|
||||
export const GET_PERMISSIONS_LIST =
|
||||
BASE_URL + "/api/permissions/list"
|
||||
//role management
|
||||
|
||||
//expert management
|
||||
export const EXPERT_MANAGEMENT = BASE_URL + "/api/users";
|
||||
//expert management
|
||||
@@ -1,4 +1,5 @@
|
||||
import SpaceDashboardIcon from "@mui/icons-material/SpaceDashboard";
|
||||
import AccessibilityIcon from '@mui/icons-material/Accessibility';
|
||||
import ManageAccountsIcon from '@mui/icons-material/ManageAccounts';
|
||||
|
||||
const sidebarMenu = [
|
||||
@@ -7,7 +8,7 @@ const sidebarMenu = [
|
||||
key: "sidebar.dashboard",
|
||||
type: "page",
|
||||
route: "/dashboard",
|
||||
icon: <SpaceDashboardIcon/>,
|
||||
icon: <SpaceDashboardIcon />,
|
||||
selected: false,
|
||||
permission: "all",
|
||||
},
|
||||
@@ -20,6 +21,15 @@ const sidebarMenu = [
|
||||
selected: false,
|
||||
permission: "all",
|
||||
},
|
||||
{
|
||||
key: "sidebar.role-management",
|
||||
name : "role_management",
|
||||
type: "page",
|
||||
route: "/dashboard/role-management",
|
||||
icon: <AccessibilityIcon />,
|
||||
selected: false,
|
||||
permission: "manage_roles",
|
||||
},
|
||||
],
|
||||
];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user