CFE-3 Merging

This commit is contained in:
AmirHossein Mahmoodi
2023-10-18 11:36:09 +03:30
71 changed files with 2452 additions and 390 deletions

View File

@@ -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,

View File

@@ -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

View 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;

View 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;

View 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;

View File

@@ -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();
});
});
});

View File

@@ -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" },
});
});
});
});

View File

@@ -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();
})
});
});
});

View File

@@ -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]);

View File

@@ -5,7 +5,6 @@ const StyledFab = styled(Fab)`
bottom: 16px;
/* @noflip */
left: 16px;
z-index: 1500;
`;
export default StyledFab;

View File

@@ -1,5 +1,5 @@
import {styled} from "@mui/material";
import {Form} from "formik";
import {styled} from "@mui/material/styles";
const StyledForm = styled(Form)``;

View File

@@ -1,4 +1,4 @@
import {useTheme} from "@mui/material";
import {useTheme} from "@mui/material/styles";
const Svg403 = ({width, height}) => {
const theme = useTheme()

View File

@@ -1,4 +1,4 @@
import {useTheme} from "@mui/material";
import {useTheme} from "@mui/material/styles";
const Svg404 = ({width, height}) => {
const theme = useTheme()

View File

@@ -1,4 +1,4 @@
import {useTheme} from "@mui/material";
import {useTheme} from "@mui/material/styles";
const SvgChangePassword = ({width, height}) => {
const theme = useTheme()

View File

@@ -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">

View File

@@ -1,4 +1,4 @@
import {useTheme} from "@mui/material";
import {useTheme} from "@mui/material/styles";
const SvgLoading = ({width, height}) => {
const theme = useTheme()

View File

@@ -1,4 +1,4 @@
import {useTheme} from "@mui/material";
import {useTheme} from "@mui/material/styles";
const SvgLogin = ({width, height}) => {
const theme = useTheme()

View File

@@ -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

View File

@@ -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",
},
],
];