use witel webapp builder

This commit is contained in:
AmirHossein Mahmoodi
2023-12-10 15:08:10 +03:30
parent a4bcd15b41
commit 03ed807eaf
74 changed files with 188 additions and 2132 deletions

View File

@@ -3,7 +3,7 @@
const nextConfig = { const nextConfig = {
reactStrictMode: true, reactStrictMode: true,
images: { images: {
domains: ['loan.witel.ir', /* other domains */], domains: [process.env.NEXT_PUBLIC_BASE_URL],
}, },
i18n: { i18n: {
defaultLocale: 'fa', defaultLocale: 'fa',

View File

@@ -16,6 +16,7 @@
"@mui/icons-material": "^5.11.16", "@mui/icons-material": "^5.11.16",
"@mui/lab": "^5.0.0-alpha.152", "@mui/lab": "^5.0.0-alpha.152",
"@mui/material": "^5.12.0", "@mui/material": "^5.12.0",
"@witel/webapp-builder": "^1.0.2",
"axios": "^1.4.0", "axios": "^1.4.0",
"colord": "^2.9.3", "colord": "^2.9.3",
"eslint": "8.36.0", "eslint": "8.36.0",

2
public/global.scss Normal file
View File

@@ -0,0 +1,2 @@
@import "fontiran";
@import 'react-toastify/dist/ReactToastify.css';

View File

@@ -1,5 +1,5 @@
{ {
"short_name": "Loan Facilities Dashboard", "short_name": "Loan Facilities",
"name": "Loan Facilities User Dashboard", "name": "Loan Facilities User Dashboard",
"description": "Loan Facilities", "description": "Loan Facilities",
"version": "1.0.0", "version": "1.0.0",

View File

@@ -1,7 +1,5 @@
import DashboardLayouts from "@/layouts/dashboardLayouts";
const FirstComponent = () => { const FirstComponent = () => {
return <DashboardLayouts></DashboardLayouts>; return <></>;
}; };
export default FirstComponent; export default FirstComponent;

View File

@@ -1,10 +1,8 @@
import CenterLayout from "@/layouts/CenterLayout";
import FullPageLayout from "@/layouts/FullPageLayout";
import {Button, Typography} from "@mui/material"; import {Button, Typography} from "@mui/material";
import {NextLinkComposed} from "@/core/components/LinkRouting";
import {useTranslations} from "next-intl"; import {useTranslations} from "next-intl";
import TitlePage from "@/core/components/TitlePage"; import TitlePage from "@/core/components/TitlePage";
import Svg403 from "@/core/components/svgs/Svg403"; import Svg403 from "@/core/components/svgs/Svg403";
import {CenterLayout, FullPageLayout, NextLinkComposed} from "@witel/webapp-builder";
const UnAuthorizedComponent = () => { const UnAuthorizedComponent = () => {
const t = useTranslations(); const t = useTranslations();

View File

@@ -1,10 +1,8 @@
import CenterLayout from "@/layouts/CenterLayout";
import FullPageLayout from "@/layouts/FullPageLayout";
import {Button, Typography} from "@mui/material"; import {Button, Typography} from "@mui/material";
import {NextLinkComposed} from "@/core/components/LinkRouting";
import {useTranslations} from "next-intl"; import {useTranslations} from "next-intl";
import TitlePage from "@/core/components/TitlePage"; import TitlePage from "@/core/components/TitlePage";
import Svg404 from "@/core/components/svgs/Svg404"; import Svg404 from "@/core/components/svgs/Svg404";
import {CenterLayout, FullPageLayout, NextLinkComposed} from "@witel/webapp-builder";
const NotFoundComponent = () => { const NotFoundComponent = () => {
const t = useTranslations(); const t = useTranslations();

View File

@@ -1,10 +1,8 @@
import CenterLayout from "@/layouts/CenterLayout";
import FullPageLayout from "@/layouts/FullPageLayout";
import {Button, Typography} from "@mui/material"; import {Button, Typography} from "@mui/material";
import {NextLinkComposed} from "@/core/components/LinkRouting";
import {useTranslations} from "next-intl"; import {useTranslations} from "next-intl";
import TitlePage from "@/core/components/TitlePage"; import TitlePage from "@/core/components/TitlePage";
import Svg500 from "@/core/components/svgs/Svg500"; import Svg500 from "@/core/components/svgs/Svg500";
import {CenterLayout, FullPageLayout, NextLinkComposed} from "@witel/webapp-builder";
const ServerErrorComponent = () => { const ServerErrorComponent = () => {
const t = useTranslations(); const t = useTranslations();

View File

@@ -1,6 +1,6 @@
import {Box, Button, Container, Grid, Stack, Typography} from "@mui/material"; import {Box, Button, Container, Grid, Stack, Typography} from "@mui/material";
import LinkRouting from "@/core/components/LinkRouting";
import {useTranslations} from "next-intl"; import {useTranslations} from "next-intl";
import {LinkRouting} from "@witel/webapp-builder";
const FooterDetails = () => { const FooterDetails = () => {
const t = useTranslations(); const t = useTranslations();
@@ -10,7 +10,8 @@ const FooterDetails = () => {
<Grid container sx={{py: 2}}> <Grid container sx={{py: 2}}>
<Grid item xs={12} md={8}> <Grid item xs={12} md={8}>
<Button disabled> <Button disabled>
<Typography color={'white'}>کلیه حقوق محفوظ و متعلق به سازمان راهداری و حمل و نقل جاده ای می باشد</Typography> <Typography color={'white'}>کلیه حقوق محفوظ و متعلق به سازمان راهداری و حمل و نقل جاده ای می
باشد</Typography>
</Button> </Button>
</Grid> </Grid>
<Grid item xs={12} md={4}> <Grid item xs={12} md={4}>

View File

@@ -5,7 +5,7 @@ const HeaderDetails = () => {
<Box> <Box>
<Container sx={{py: 3}} maxWidth="xl"> <Container sx={{py: 3}} maxWidth="xl">
<Grid container> <Grid container>
<Grid item sx={12} md={8}> <Grid item xs={12} md={8}>
<Button startIcon={<img src='/icons/headerLogo.png' alt='icon'/>}/> <Button startIcon={<img src='/icons/headerLogo.png' alt='icon'/>}/>
</Grid> </Grid>
</Grid> </Grid>

View File

@@ -1,6 +1,7 @@
import {Box, Button, Chip, Container, Grid, Link, Stack, Typography} from "@mui/material"; import {Box, Button, Chip, Container, Grid, Link, Typography} from "@mui/material";
import {useTranslations} from "next-intl"; import {useTranslations} from "next-intl";
import DownloadIcon from '@mui/icons-material/Download'; import DownloadIcon from '@mui/icons-material/Download';
const LoanCondition = () => { const LoanCondition = () => {
const t = useTranslations(); const t = useTranslations();
return ( return (
@@ -15,16 +16,19 @@ const LoanCondition = () => {
<Typography variant={'h5'}> <Typography variant={'h5'}>
قوانین دریافت وام قوانین دریافت وام
</Typography> </Typography>
<Grid item sx={{my : 2}} spacing={2}> <Grid item sx={{my: 2}}>
<Chip sx={{m: 1}} label="1 . فقط به زوجین ایرانی تعلق می‌گیرد"/> <Chip sx={{m: 1}} label="1 . فقط به زوجین ایرانی تعلق می‌گیرد"/>
<Chip sx={{m: 1}} label="2 . فقط به زوجین ایرانی تعلق می‌گیرد"/> <Chip sx={{m: 1}} label="2 . فقط به زوجین ایرانی تعلق می‌گیرد"/>
<Chip sx={{m: 1}} label="3 . فقط به زوجین ایرانی تعلق می‌گیرد"/> <Chip sx={{m: 1}} label="3 . فقط به زوجین ایرانی تعلق می‌گیرد"/>
<Chip sx={{m: 1}} label="4 . فقط به زوجین ایرانی تعلق می‌گیرد"/> <Chip sx={{m: 1}} label="4 . فقط به زوجین ایرانی تعلق می‌گیرد"/>
</Grid> </Grid>
<Typography textAlign={'justify'}> این یکی از محبوبترین انواع وام در ایران است. دلیل این محبوبیت سرعت آن در پرداخت و سود بسیار پایین آن است. <Typography textAlign={'justify'}> این یکی از محبوبترین انواع وام در ایران است. دلیل این
محبوبیت سرعت آن در پرداخت و سود بسیار پایین آن است.
تنها یک شرط برای اخذ این وام وجود دارد و آن این است که باید به تازگی ازدواج کرده باشید. تنها یک شرط برای اخذ این وام وجود دارد و آن این است که باید به تازگی ازدواج کرده باشید.
همچنین فقط یک بار از این وام میتوانید استفاده کنید. یعنی این وام فقط یک بار به هر فرد تعلق میگیرد. همچنین فقط یک بار از این وام میتوانید استفاده کنید. یعنی این وام فقط یک بار به هر فرد
محل تامین بودجه آن حسابهای قرض الحسنهایست که افراد در بانکها باز میکنند و مرجع اصلی آرائه آن، بانک مرکزی است. تعلق میگیرد.
محل تامین بودجه آن حسابهای قرض الحسنهایست که افراد در بانکها باز میکنند و مرجع اصلی
آرائه آن، بانک مرکزی است.
</Typography> </Typography>
</Box> </Box>
</Grid> </Grid>

View File

@@ -1,5 +1,4 @@
import {Box, Container, Grid, Typography} from "@mui/material"; import {Box, Container, Grid, Typography} from "@mui/material";
import {Timeline, TimelineConnector, TimelineContent, TimelineDot, TimelineItem, TimelineSeparator} from "@mui/lab";
import {useTranslations} from "next-intl"; import {useTranslations} from "next-intl";
import TimeLineDetails from "@/components/first/LoanDescription/TimeLineDetails"; import TimeLineDetails from "@/components/first/LoanDescription/TimeLineDetails";
@@ -14,12 +13,21 @@ const LoanDescription = () => {
<Grid sx={{py: 2}} container spacing={2}> <Grid sx={{py: 2}} container spacing={2}>
<Grid item xs={12} md={7}> <Grid item xs={12} md={7}>
<Typography variant={'subtitle1'} textAlign={'justify'}> <Typography variant={'subtitle1'} textAlign={'justify'}>
وام خرید کالای مهر ایران یا کالا کارت با هدف خرید مایحتاج خانه و کالاهای ضروری زندگی داده میشود. سقف کالا کارت ۱۵۰ میلیون تومان و سود آن ۴ درصد است. وام خرید کالای مهر ایران یا کالا کارت با هدف خرید مایحتاج خانه و کالاهای ضروری زندگی داده
میشود. سقف کالا کارت ۱۵۰ میلیون تومان و سود آن ۴ درصد است.
با دریافت کالا کارت میتوانید وسایل موردنیازتان را از فروشگاههای طرف قرارداد با دریافت کالا کارت میتوانید وسایل موردنیازتان را از فروشگاههای طرف قرارداد
این فروشگاهها با نام گلدیران در شهرهای تهران، شیراز، اهواز، نور، اصفهان، تبریز، ساری و رشت فعالیت میکنند. این فروشگاهها با نام گلدیران در شهرهای تهران، شیراز، اهواز، نور، اصفهان، تبریز، ساری و رشت
طبق قوانین جدید و در راستای حمایت از طرح افزایش جمیعت، مبلغ آن برای زوجینی که مرد زیر ۲۵ سال و زن زیر ۲۳ سال داشته باشد، <Typography sx={{color : "secondary.main"}} variant={'h5'} fontWeight={'bold'} component={'span'}>440</Typography> میلیون یا ۲۲۰ میلیون به ازای هر نفر است. فعالیت میکنند.
طبق قوانین جدید و در راستای حمایت از طرح افزایش جمیعت، مبلغ آن برای زوجینی که مرد زیر ۲۵ سال
و زن زیر ۲۳ سال داشته باشد، <Typography sx={{color: "secondary.main"}} variant={'h5'}
fontWeight={'bold'}
component={'span'}>440</Typography> میلیون یا ۲۲۰
میلیون به ازای هر نفر است.
در این قانون هریک از زوجین که شرایط آنها در این قانون نگنجد، در این قانون هریک از زوجین که شرایط آنها در این قانون نگنجد،
مبلغ تغییر کرده و به زوج <Typography sx={{color : "secondary.main"}} variant={'h5'} fontWeight={'bold'} component={'span'}>400</Typography> میلیون وام تعلق خواهد گرفت. مبلغ تغییر کرده و به زوج <Typography sx={{color: "secondary.main"}} variant={'h5'}
fontWeight={'bold'}
component={'span'}>400</Typography> میلیون وام تعلق
خواهد گرفت.
حداکثر زمان بازپرداخت وام خرید کالا ۲۴ ماه است و برای گرفتن آن به یک ضامن رسمی احتیاج دارید. حداکثر زمان بازپرداخت وام خرید کالا ۲۴ ماه است و برای گرفتن آن به یک ضامن رسمی احتیاج دارید.
</Typography> </Typography>
</Grid> </Grid>

View File

@@ -1,6 +1,6 @@
import {Box, Container, Grid, Stack, Typography} from "@mui/material"; import {Box, Container, Grid, Stack, Typography} from "@mui/material";
import LinkRouting from "@/core/components/LinkRouting";
import {useTranslations} from "next-intl"; import {useTranslations} from "next-intl";
import {LinkRouting} from "@witel/webapp-builder";
const LoanRegister = () => { const LoanRegister = () => {
const t = useTranslations(); const t = useTranslations();
@@ -15,20 +15,31 @@ const LoanRegister = () => {
<Grid item xs={12} md={6}> <Grid item xs={12} md={6}>
<Box sx={{paddingBottom: 4}}> <Box sx={{paddingBottom: 4}}>
<Typography> <Typography>
برای استفاده از تسهیلات این سامانه و دریافت وام می بایست ابتدا وارد سامانه شوید و یا اگر ثبت نام نکرده اید ثبت نام کنید برای استفاده از تسهیلات این سامانه و دریافت وام می بایست ابتدا وارد سامانه شوید و یا
اگر ثبت نام نکرده اید ثبت نام کنید
</Typography> </Typography>
</Box> </Box>
</Grid> </Grid>
<Grid item xs={12} md={6}> <Grid item xs={12} md={6}>
<Stack spacing={3} alignItems={'center'} justifyContent={'center'}> <Stack spacing={3} alignItems={'center'} justifyContent={'center'}>
<Container maxWidth={'xs'} sx={{backgroundColor : "primary.main", textAlign : "center", borderRadius : 2, padding : 1}}> <Container maxWidth={'xs'} sx={{
backgroundColor: "primary.main",
textAlign: "center",
borderRadius: 2,
padding: 1
}}>
<LinkRouting underline="none" color="inherit" href={'/login'}> <LinkRouting underline="none" color="inherit" href={'/login'}>
<Typography variant="h6"> <Typography variant="h6">
{t("firstPage.login_button")} {t("firstPage.login_button")}
</Typography> </Typography>
</LinkRouting> </LinkRouting>
</Container> </Container>
<Container maxWidth={'xs'} sx={{backgroundColor : "primary.main", textAlign : "center", borderRadius : 2 ,padding : 1}}> <Container maxWidth={'xs'} sx={{
backgroundColor: "primary.main",
textAlign: "center",
borderRadius: 2,
padding: 1
}}>
<LinkRouting underline="none" color="inherit" href={'/register'}> <LinkRouting underline="none" color="inherit" href={'/register'}>
<Typography variant="h6"> <Typography variant="h6">
{t("firstPage.register_button")} {t("firstPage.register_button")}

View File

@@ -1,4 +1,3 @@
import FullPageLayout from "@/layouts/FullPageLayout";
import {Stack} from "@mui/material"; import {Stack} from "@mui/material";
import {useTranslations} from "next-intl"; import {useTranslations} from "next-intl";
import LoanRegister from "@/components/first/LoanRegister"; import LoanRegister from "@/components/first/LoanRegister";
@@ -6,6 +5,7 @@ import LoanCondition from "@/components/first/LoanCondition";
import FooterDetails from "@/components/first/FooterDetails"; import FooterDetails from "@/components/first/FooterDetails";
import HeaderDetails from "@/components/first/HeaderDetails"; import HeaderDetails from "@/components/first/HeaderDetails";
import LoanDescription from "@/components/first/LoanDescription"; import LoanDescription from "@/components/first/LoanDescription";
import {FullPageLayout} from "@witel/webapp-builder";
const FirstComponent = () => { const FirstComponent = () => {
const t = useTranslations(); const t = useTranslations();

View File

@@ -1,116 +0,0 @@
import MuiLink from "@mui/material/Link";
import {styled} from "@mui/material/styles";
import clsx from "clsx";
import NextLink from "next/link";
import {useRouter} from "next/router";
import * as React from "react";
// Add support for the sx prop for consistency with the other branches.
const Anchor = styled("a")({});
export const NextLinkComposed = React.forwardRef(function NextLinkComposed(
props,
ref
) {
const {
to,
linkAs,
replace,
scroll,
shallow,
prefetch,
legacyBehavior = true,
locale,
...other
} = props;
return (
<NextLink
href={to}
prefetch={prefetch}
as={linkAs}
replace={replace}
scroll={scroll}
shallow={shallow}
passHref
locale={locale}
legacyBehavior={legacyBehavior}
>
<Anchor ref={ref} {...other} />
</NextLink>
);
});
// A styled version of the Next.js Link component:
// https://nextjs.org/docs/api-reference/next/link
const LinkRouting = React.forwardRef(function Link(props, ref) {
const {
activeClassName = "active",
as,
className: classNameProps,
href,
legacyBehavior,
linkAs: linkAsProp,
locale,
noLinkStyle,
prefetch,
replace,
role, // Link don't have roles.
scroll,
shallow,
...other
} = props;
const router = useRouter();
const pathname = typeof href === "string" ? href : href.pathname;
const className = clsx(classNameProps, {
[activeClassName]: router.pathname === pathname && activeClassName,
});
const isExternal =
typeof href === "string" &&
(href.indexOf("http") === 0 || href.indexOf("mailto:") === 0);
if (isExternal) {
if (noLinkStyle) {
return <Anchor className={className} href={href} ref={ref} {...other} />;
}
return <MuiLink className={className} href={href} ref={ref} {...other} />;
}
const linkAs = linkAsProp || as;
const nextjsProps = {
to: href,
linkAs,
replace,
scroll,
shallow,
prefetch,
legacyBehavior,
locale,
};
if (noLinkStyle) {
return (
<NextLinkComposed
className={className}
ref={ref}
{...nextjsProps}
{...other}
/>
);
}
return (
<MuiLink
component={NextLinkComposed}
className={className}
ref={ref}
{...nextjsProps}
{...other}
/>
);
});
export default LinkRouting;

View File

@@ -1,44 +0,0 @@
import {Backdrop, Box, Fade, styled} from "@mui/material";
import SvgLoading from "@/core/components/svgs/SvgLoading";
const LoadingImage = styled(Box)({
"@keyframes load": {
"0%": {
// opacity: 0,
transform: "scale(1)",
},
"50%": {
// opacity: 1,
transform: "scale(2)",
},
"100%": {
// opacity: 0,
transform: "scale(1)",
},
},
animation: "load 2s infinite",
});
const LoadingHardPage = ({children, loading}) => {
return (
<>
<Backdrop
sx={{bgcolor: "#fff", zIndex: (theme) => theme.zIndex.drawer + 1}}
open={loading}
>
<Fade in={true}>
<LoadingImage
width={100}
height={100}
>
<SvgLoading width={100}
height={100}/>
</LoadingImage>
</Fade>
</Backdrop>
{children}
</>
);
};
export default LoadingHardPage;

View File

@@ -1,18 +0,0 @@
import CenterLayout from "@/layouts/CenterLayout";
import FullPageLayout from "@/layouts/FullPageLayout";
import SvgLoading from "@/core/components/svgs/SvgLoading";
const Message = ({text, actions}) => {
return (
<FullPageLayout sx={{p: 1}}>
<CenterLayout spacing={3}>
<SvgLoading width={100}
height={100}/>
{text}
{actions}
</CenterLayout>
</FullPageLayout>
);
};
export default Message;

View File

@@ -1,34 +0,0 @@
import useNetwork from "@/lib/app/hooks/useNetwork";
import {useEffect, useRef} from "react";
import {toast} from "react-toastify";
import WifiIcon from '@mui/icons-material/Wifi';
import WifiOffIcon from '@mui/icons-material/WifiOff';
import {useTranslations} from "next-intl";
const NetworkComponent = () => {
const toastId = useRef(null);
const network = useNetwork()
const t = useTranslations()
useEffect(() => {
if (network.online) {
toast.update(toastId.current, {
type: toast.TYPE.SUCCESS,
render: t('online_message'),
autoClose: 2000,
closeButton: true,
closeOnClick: true,
icon: <WifiIcon/>
});
return
}
toast.dismiss()
toastId.current = toast.warn(t('offline_message'), {
autoClose: false, closeButton: false, closeOnClick: false, icon: <WifiOffIcon/>
})
}, [network.online]);
return ''
}
export default NetworkComponent

View File

@@ -1,8 +0,0 @@
import {NoSsr} from "@mui/material";
const NoSsrHandler = ({isBot, children}) => {
if (isBot) return children;
return <NoSsr>{children}</NoSsr>;
};
export default NoSsrHandler;

View File

@@ -1,39 +0,0 @@
import DangerousIcon from "@mui/icons-material/Dangerous";
import {Box, Typography} from "@mui/material";
import {toast} from "react-toastify";
const ErrorNotification = (t, status, message) => {
toast(
() => (
<>
<Box
sx={{
display: "flex",
flexDirection: "column",
alignItems: "start",
justifyContent: "start",
}}
>
<Box sx={{display: "flex", alignItems: "center"}}>
<DangerousIcon color="error" sx={{mr: 1.6}}/>
<Box sx={{display: "flex", flexDirection: "column"}}>
<Typography color="error" variant="button">
{t("notifications.error")} ({t("notifications.code")}: {status})
</Typography>
<Typography variant="caption">
{message || t("notifications.error_static_text")}
</Typography>
</Box>
</Box>
</Box>
</>
),
{
autoClose: false,
closeOnClick: false,
draggable: false,
}
);
};
export default ErrorNotification;

View File

@@ -1,12 +0,0 @@
import {toast} from "react-toastify";
const PendingNotification = (t) => {
toast(t("notifications.pending"), {
autoClose: false,
closeButton: false,
closeOnClick: false,
draggable: false,
});
};
export default PendingNotification;

View File

@@ -1,42 +0,0 @@
import BeenhereIcon from "@mui/icons-material/Beenhere";
import {Box, Typography} from "@mui/material";
import {toast} from "react-toastify";
const SuccessNotification = (t, status) => {
toast(
() => (
<>
<Box
sx={{
display: "flex",
flexDirection: "column",
alignItems: "start",
justifyContent: "start",
}}
>
<Box sx={{display: "flex", alignItems: "center"}}>
<BeenhereIcon color="success" sx={{mr: 1.6}}/>
<Box sx={{display: "flex", flexDirection: "column"}}>
<Typography color="success.main" variant="button">
{t("notifications.success")} ({t("notifications.code")}:{" "}
{status})
</Typography>
<Typography variant="caption">
{t("notifications.success_static_text")}
</Typography>
</Box>
</Box>
</Box>
</>
),
{
autoClose: 3000,
hideProgressBar: true,
pauseOnHover: true,
closeOnClick: false,
draggable: true,
}
);
};
export default SuccessNotification;

View File

@@ -1,40 +0,0 @@
import ReportIcon from "@mui/icons-material/Report";
import {Box, Typography} from "@mui/material";
import {toast} from "react-toastify";
const WarningNotification = (t, status) => {
toast(
() => (
<>
<Box
sx={{
display: "flex",
flexDirection: "column",
alignItems: "start",
justifyContent: "start",
}}
>
<Box sx={{display: "flex", alignItems: "center"}}>
<ReportIcon color="warning" sx={{mr: 1.6}}/>
<Box sx={{display: "flex", flexDirection: "column"}}>
<Typography color="warning.main" variant="button">
{t("notifications.warning")} ({t("notifications.code")}:{" "}
{status})
</Typography>
<Typography variant="caption">
{t("notifications.warning_static_text")}
</Typography>
</Box>
</Box>
</Box>
</>
),
{
autoClose: false,
closeOnClick: false,
draggable: false,
}
);
};
export default WarningNotification;

View File

@@ -1,54 +0,0 @@
import {toast} from "react-toastify";
import ErrorNotification from "./ErrorNotification";
import SuccessNotification from "./SuccessNotification";
import WarningNotification from "./WarningNotification";
const Notifications = async (t, response) => {
const {status, data} = response != undefined ? response : ""
toast.dismiss();
switch (status) {
case 200:
SuccessNotification(t, status);
break;
case 400:
ErrorNotification(t, status);
break;
case 401:
ErrorNotification(t, status);
break;
case 403:
ErrorNotification(t, status);
break;
case 422:
ErrorNotification(t, status, data.message);
break;
case 500:
WarningNotification(t, status);
break;
case 503:
WarningNotification(t, status);
break;
case 504:
WarningNotification(t, status);
break;
default:
toast(t("pending"), {
autoClose: false,
closeOnClick: false,
draggable: false,
});
break;
}
};
export default Notifications;
/*
usage document
** for pending use ( Notifications( t, undefined) ) this before your request.
** for success use ( Notifications( t, response) ) this inside .then() of your request.
** for Error and Warning use ( Notifications( t, error.response) ) this inside .catche() of your request.
end usage document
*/

View File

@@ -5,7 +5,8 @@ export const LOGIN = BASE_URL + "/login";
// export const REGISTER_SEND_OTP_TOKEN = BASE_URL + "/register/send_otp"; // export const REGISTER_SEND_OTP_TOKEN = BASE_URL + "/register/send_otp";
export const SEND_OTP_TOKEN = BASE_URL + "/otp"; export const SEND_OTP_TOKEN = BASE_URL + "/otp";
export const REGISTER = BASE_URL + "/register"; export const REGISTER = BASE_URL + "/register";
export const USER_INFO = BASE_URL + "/profile/info"; export const GET_USER = BASE_URL + "/profile/info";
export const GET_SIDEBAR_NOTIFICATION = "";
export const SHOW_LOAN_REQUEST_NAVGAN = BASE_URL + "/navgan/loan/index"; export const SHOW_LOAN_REQUEST_NAVGAN = BASE_URL + "/navgan/loan/index";
export const SEND_LOAN_REQUEST_NAVGAN = BASE_URL + "/navgan/loan/store"; export const SEND_LOAN_REQUEST_NAVGAN = BASE_URL + "/navgan/loan/store";
export const SHOW_LOAN_REQUEST_WELFARE = BASE_URL + "/refahi/loan/index"; export const SHOW_LOAN_REQUEST_WELFARE = BASE_URL + "/refahi/loan/index";

View File

@@ -0,0 +1,14 @@
import {FA_DATATABLE_LOCALIZATION} from "&/locales/fa/datatable";
const languageList = [
{
key: "fa",
dir: 'rtl',
name: "فارسی",
fontFamily: `IRANSans, sans-serif`,
tableLocalization: FA_DATATABLE_LOCALIZATION,
chartLocalization: null
}
];
export default languageList;

View File

@@ -9,7 +9,6 @@ const sidebarMenu = [
type: "page", type: "page",
route: "/dashboard", route: "/dashboard",
icon: <SpaceDashboardIcon/>, icon: <SpaceDashboardIcon/>,
userType: 0,
selected: false, selected: false,
permission_name: "all" permission_name: "all"
}, },
@@ -18,7 +17,6 @@ const sidebarMenu = [
type: "page", type: "page",
route: "/dashboard/navgan/add-request-loan", route: "/dashboard/navgan/add-request-loan",
icon: <DataSaverOnIcon/>, icon: <DataSaverOnIcon/>,
userType: 1,
selected: false, selected: false,
permission_name: "can_request_a_new_loan" permission_name: "can_request_a_new_loan"
}, },
@@ -27,25 +25,6 @@ const sidebarMenu = [
type: "page", type: "page",
route: "/dashboard/navgan/followUp-loan", route: "/dashboard/navgan/followUp-loan",
icon: <BookmarkAddedIcon/>, icon: <BookmarkAddedIcon/>,
userType: 1,
selected: false,
permission_name: "all"
},
{
key: "sidebar.add-request-loan",
type: "page",
route: "/dashboard/refahi/add-request-loan",
icon: <DataSaverOnIcon/>,
userType: 2,
selected: false,
permission_name: "can_request_a_new_loan"
},
{
key: "sidebar.followUp-loan",
type: "page",
route: "/dashboard/refahi/followUp-loan",
icon: <BookmarkAddedIcon/>,
userType: 2,
selected: false, selected: false,
permission_name: "all" permission_name: "all"
}, },

View File

@@ -1,38 +0,0 @@
import createCache from "@emotion/cache";
import {prefixer} from "stylis";
import stylisRTLPlugin from "stylis-plugin-rtl";
const isBrowser = typeof document !== "undefined";
export const createEmotionCacheLtr = () => {
let insertionPoint;
if (isBrowser) {
const emotionInsertionPoint = document.querySelector(
'meta[name="emotion-insertion-point"]'
);
insertionPoint = emotionInsertionPoint ?? undefined;
}
return createCache({
key: "mui-style",
insertionPoint,
});
};
export const createEmotionCacheRtl = () => {
let insertionPoint;
if (isBrowser) {
const emotionInsertionPoint = document.querySelector(
'meta[name="emotion-insertion-point"]'
);
insertionPoint = emotionInsertionPoint ?? undefined;
}
return createCache({
key: "muirtl",
stylisPlugins: [prefixer, stylisRTLPlugin],
insertionPoint,
});
};

View File

@@ -1,62 +0,0 @@
import ErrorNotification from "@/core/components/notifications/ErrorNotification";
import WarningNotification from "@/core/components/notifications/WarningNotification";
import {toast} from "react-toastify";
export const errorSetting = (t, notification) => {
if (notification) toast.dismiss();
}
export const errorRequest = (t, notification) => {
if (notification) toast.dismiss();
}
export const errorResponse = (response, clearToken, t, notification) => {
if (notification) toast.dismiss();
if (isServerError(response.status)) {
errorServer(response, t, notification)
} else if (isClientError(response.status)) {
errorClient(response, clearToken, t, notification)
}
}
const errorServer = (response, t, notification) => {
if (notification) WarningNotification(t, response.status);
}
const errorClient = (response, clearToken, t, notification) => {
switch (response.status) {
case 401:
clearToken()
if (notification) ErrorNotification(t, response.status)
break;
case 422:
if ('type' in response.data) {
errorLogic(response, t, notification)
break;
}
errorValidation(response, t, notification)
break;
case 429:
if (notification) ErrorNotification(t, response.status)
break
default:
if (notification) ErrorNotification(t, response.status)
break
}
}
const isServerError = status => status >= 500 && status <= 599;
const isClientError = status => status >= 400 && status <= 499;
const errorLogic = (response, t, notification) => {
if (notification) ErrorNotification(t, response.status, response.data.message)
}
const errorValidation = (response, t, notification) => {
if (notification) {
const errorsMap = Object.keys(response.data.errors)
const errorsArray = response.data.errors
errorsMap.map((item, index) => {
ErrorNotification(t, response.status, errorsArray[item][0]);
})
}
}

View File

@@ -1,9 +0,0 @@
import SuccessNotification from "@/core/components/notifications/SuccessNotification";
import {toast} from "react-toastify";
export const successRequest = (response, t, options) => {
if (options.notification && options.success.notification.show) {
toast.dismiss();
SuccessNotification(t, response.status)
}
}

View File

@@ -1,56 +0,0 @@
import theme from "@/core/utils/theme";
import useLanguage from "@/lib/app/hooks/useLanguage";
import useLoading from "@/lib/app/hooks/useLoading";
import useUser from "@/lib/app/hooks/useUser";
import NextNProgress from "nextjs-progressbar";
import {useEffect} from "react";
import {ToastContainer} from "react-toastify";
import useDirection from "@/lib/app/hooks/useDirection";
import "react-toastify/dist/ReactToastify.css";
import NetworkComponent from "@/core/components/NetworkComponent";
import GlobalHead from "@/core/components/GlobalHead";
function AppLayout({children, isBot}) {
const {languageIsReady} = useLanguage();
const {setLoadingPage} = useLoading();
const {userChangedLanguage, token, isAuth} = useUser();
const {directionApp} = useDirection();
useEffect(() => {
if (languageIsReady) {
if (token) {
if (isAuth) {
if (userChangedLanguage) {
setLoadingPage(true);
return;
}
setLoadingPage(false);
return;
}
setLoadingPage(true);
return;
}
setLoadingPage(false);
return;
}
setLoadingPage(true);
}, [languageIsReady, token, isAuth, userChangedLanguage]);
if (!isBot) {
if (userChangedLanguage) return;
if (!languageIsReady) return;
}
return (<>
<GlobalHead/>
<NextNProgress
color={theme.palette.secondary.dark}
options={{showSpinner: false}}
/>
<ToastContainer position={directionApp === "ltr" ? "top-left" : "top-right"} rtl={directionApp === 'rtl'}/>
<NetworkComponent/>
{children}
</>);
}
export default AppLayout;

View File

@@ -1,18 +0,0 @@
import {Fade, Stack} from "@mui/material";
const CenterLayout = (props) => {
return (
<Fade in={true}>
<Stack
alignItems="center"
justifyContent="center"
spacing={props?.spacing}
sx={{flex: 1, ...props?.sx, py: 3}}
>
{props.children}
</Stack>
</Fade>
);
};
export default CenterLayout;

View File

@@ -1,21 +0,0 @@
import {Stack} from "@mui/material";
const FullPageLayout = (props) => {
return (
<Stack
spacing={props?.spacing}
direction={props?.direction}
sx={{
width: "100%",
height: "100%",
overflowY: "scroll",
overflowX: "scroll",
...props?.sx,
}}
>
{props.children}
</Stack>
);
};
export default FullPageLayout;

View File

@@ -1,75 +0,0 @@
import NoSsrHandler from "@/core/components/NoSsrHandler";
import {createEmotionCacheRtl} from "@/core/utils/createEmotionCache";
import themeRtl from "@/core/utils/theme-rtl";
import {CacheProvider} from "@emotion/react";
import {GlobalStyles} from "@mui/material";
import CssBaseline from "@mui/material/CssBaseline";
import {ThemeProvider} from "@mui/material/styles";
import Head from "next/head";
const clientSideEmotionCacheRtl = createEmotionCacheRtl();
const MuiLayout = ({children, isBot}) => {
const emotionCache = clientSideEmotionCacheRtl;
const theme = themeRtl;
return (
<NoSsrHandler isBot={isBot}>
<CacheProvider value={emotionCache}>
<Head>
<meta name="viewport" content="initial-scale=1, width=device-width"/>
</Head>
<ThemeProvider theme={theme}>
<GlobalStyles
styles={{
"*:not(.MuiTableContainer-root)::-webkit-scrollbar": {
display: "none",
},
"*::-webkit-scrollbar": {
height: "8px",
},
"*::-webkit-scrollbar-thumb": {
background: `${theme.palette.primary.light}80`,
borderRadius: "3px",
},
"*:not(.MuiTableContainer-root)": {
scrollbarWidth: "thin",
scrollbarColor: "transparent transparent",
},
"*": {
scrollbarWidth: "thin",
scrollbarColor: `${theme.palette.primary.light}80 transparent`,
},
"*::-moz-scrollbar-thumb": {
backgroundColor: `${theme.palette.primary.light}80`,
},
[`@media (max-width: ${theme.breakpoints.values.sm}px)`]: {
"*::-webkit-scrollbar": {
height: "4px",
},
},
body: {
width: "100vw",
height: "100vh",
},
"#__next": {
width: "100%",
height: "100%",
},
}}
/>
<CssBaseline/>
{children}
</ThemeProvider>
</CacheProvider>
</NoSsrHandler>
);
};
export default MuiLayout;

View File

@@ -1,25 +0,0 @@
import {Box, Fade} from "@mui/material";
const ScrollableLayout = (props) => {
const overflowY = props?.y || "hidden";
const overflowX = props?.x || "hidden";
return (
<Fade in={true}>
<Box
spacing={props?.spacing}
sx={{
width: "auto",
height: "auto",
overflowY: overflowY,
overflowX: overflowX,
...props?.sx,
}}
>
{props.children}
</Box>
</Fade>
);
};
export default ScrollableLayout;

15
src/layouts/dashboard.jsx Normal file
View File

@@ -0,0 +1,15 @@
import {DashboardLayout as DashboardLayoutPure} from "@witel/webapp-builder";
import headerProfileItems from "@/core/data/headerProfileItems";
import sidebarMenu from "@/core/data/sidebarMenu";
import {GET_SIDEBAR_NOTIFICATION} from "@/core/data/apiRoutes";
const DashboardLayout = (props) => {
return (
<>
<DashboardLayoutPure headerProfileItems={headerProfileItems} sidebarMenu={sidebarMenu}
urlNotification={GET_SIDEBAR_NOTIFICATION} {...props} />
</>
)
}
export default DashboardLayout

View File

@@ -1,19 +0,0 @@
import LinkRouting from "@/core/components/LinkRouting";
import {Typography} from "@mui/material";
import {useTranslations} from "next-intl";
export default function BreadcrumbItem(props) {
const t = useTranslations();
const isLast = props.index === props.RouterArray.length - 1;
const url = `/${props.RouterArray.slice(0, props.index + 1).join("/")}`;
return isLast ? (
<Typography variant="body2" color="primary">
{t("sidebar." + props.label)}
</Typography>
) : (
<LinkRouting underline="hover" color="inherit" passHref href={url}>
<Typography variant="body2">{t("sidebar." + props.label)}</Typography>
</LinkRouting>
);
}

View File

@@ -1,62 +0,0 @@
import {useRouter} from "next/router";
import {Box, Breadcrumbs} from "@mui/material";
import {useTheme} from "@mui/material/styles";
import {NavigateBefore, NavigateNext} from "@mui/icons-material";
import BreadcrumbItem from "./BreadcrumbItem";
import useUser from "@/lib/app/hooks/useUser";
const BreadCrumbs = (props) => {
const {isVisible} = props;
const theme = useTheme();
const router = useRouter();
const {user} = useUser();
if (!isVisible) {
return null;
}
const {pathname} = router;
const RouterArray = pathname.split("/").filter((segment) => segment !== "");
// every part of your router you want to remove add it to this array
const segmentsToRemove = ["navgan", "refahi", "[id]"];
segmentsToRemove.forEach((segmentToRemove) => {
const index = RouterArray.indexOf(segmentToRemove);
if (index !== -1) {
RouterArray.splice(index, 1);
}
});
if (RouterArray.length === 1) {
return null;
}
return (
<Box p={3} component="span">
<Breadcrumbs
maxItems={2}
separator={
theme.direction === "ltr" ? (
<NavigateNext fontSize="small"/>
) : (
<NavigateBefore fontSize="small"/>
)
}
aria-label="breadcrumb"
>
{RouterArray.map((segment, index) => (
<BreadcrumbItem
RouterArray={RouterArray}
label={segment}
key={segment}
index={index}
/>
))}
</Breadcrumbs>
</Box>
);
};
export default BreadCrumbs;

View File

@@ -1,15 +0,0 @@
import {Avatar, Stack} from "@mui/material";
import useUser from "@/lib/app/hooks/useUser";
export default function ProfileData() {
const {user} = useUser();
return (
<Stack alignItems="center" spacing={2} sx={{p: 3}}>
<Avatar
sx={{width: "80px", height: "80px"}}
alt="User Image"
src={user.user_avatar}
/>
</Stack>
);
}

View File

@@ -1,62 +0,0 @@
import {Avatar, IconButton, Menu, Tooltip} from "@mui/material";
import {useState} from "react";
import ProfileData from "./ProfileData";
import ProfileOptions from "./ProfileOptions";
import {useTranslations} from "next-intl";
import useUser from "@/lib/app/hooks/useUser";
function ProfileMenu() {
const t = useTranslations();
const [anchorElUser, setAnchorElUser] = useState(null);
const {user} = useUser();
const handleOpenUserMenu = (event) => {
setAnchorElUser(event.currentTarget);
};
const handleCloseUserMenu = () => {
setAnchorElUser(null);
};
return (
<>
<Tooltip title={t("header.open_profile")} arrow>
<IconButton onClick={handleOpenUserMenu} sx={{p: 0}}>
<Avatar
sx={{
width: 24,
height: 24,
backgroundColor: "#fff",
color: "primary.main",
}}
alt="User Image"
src={user.user_avatar}
/>
</IconButton>
</Tooltip>
<Menu
MenuListProps={{sx: {py: 0}}}
sx={{
mt: 6,
}}
id="menu-appbar"
anchorEl={anchorElUser}
anchorOrigin={{
vertical: "top",
horizontal: "right",
}}
keepMounted
transformOrigin={{
vertical: "top",
horizontal: "right",
}}
open={Boolean(anchorElUser)}
onClose={handleCloseUserMenu}
>
<ProfileData/>
<ProfileOptions handleCloseUserMenu={handleCloseUserMenu}/>
</Menu>
</>
);
}
export default ProfileMenu;

View File

@@ -1,49 +0,0 @@
import {Box, Button, MenuItem, Typography} from "@mui/material";
import MeetingRoomIcon from "@mui/icons-material/MeetingRoom";
import useUser from "@/lib/app/hooks/useUser";
import {useTranslations} from "next-intl";
export default function ProfileOptionLogOut({handleCloseUserMenu}) {
const t = useTranslations();
const {clearToken} = useUser();
const handleClickLogOut = () => {
handleCloseUserMenu();
clearToken();
};
return (
<>
<MenuItem
component={Button}
to={{
pathname: "/dashboard/logout",
}}
sx={{
display: "flex",
justifyContent: "center",
borderTop: 1,
px: 3,
py: 1.5,
borderColor: "#e1e1e1",
textTransform: "unset",
}}
onClick={handleClickLogOut}
>
<Box sx={{display: "flex", alignItems: "center", flex: 1}}>
<Box
sx={{
display: "flex",
alignItems: "center",
color: "primary.main",
pr: 2,
}}
>
<MeetingRoomIcon/>
</Box>
<Typography sx={{flex: 1}} textAlign="start">
{t("header.logout")}
</Typography>
</Box>
</MenuItem>
</>
);
}

View File

@@ -1,49 +0,0 @@
import {Box, MenuItem, Typography} from "@mui/material";
import {NextLinkComposed} from "@/core/components/LinkRouting";
import {useTranslations} from "next-intl";
import headerProfileItems from "@/core/data/headerProfileItems";
import ProfileOptionLogOut from "./ProfileOptionLogOut";
export default function ProfileOptions({handleCloseUserMenu}) {
const t = useTranslations();
return (
<>
{headerProfileItems.map((profile_item) => (
<MenuItem
component={NextLinkComposed}
to={{
pathname: profile_item.route,
}}
sx={{
display: "flex",
justifyContent: "center",
borderTop: 1,
px: 3,
py: 1.5,
borderColor: "#e1e1e1",
}}
key={profile_item.key}
onClick={handleCloseUserMenu}
>
<Box sx={{display: "flex", alignItems: "center", flex: 1}}>
<Box
sx={{
display: "flex",
alignItems: "center",
color: "primary.main",
pr: 2,
}}
>
{profile_item.icon}
</Box>
<Typography sx={{flex: 1}} textAlign="start">
{t(profile_item.name)}
</Typography>
</Box>
</MenuItem>
))}
<ProfileOptionLogOut handleCloseUserMenu={handleCloseUserMenu}/>
</>
);
}

View File

@@ -1,72 +0,0 @@
import MenuIcon from "@mui/icons-material/Menu";
import {AppBar, Box, Container, CssBaseline, IconButton, Stack, Toolbar, useTheme} from "@mui/material";
import ProfileMenu from "./ProfileMenu";
function Header({drawerWidth, handleDrawerToggle}) {
const theme = useTheme();
return (
<>
<CssBaseline/>
<AppBar
position="fixed"
sx={{
width: {md: `calc(100% - ${drawerWidth}px)`},
ml: {md: `${drawerWidth}px`},
}}
>
<Container maxWidth="xxl">
<Toolbar
disableGutters
sx={{
display: "flex",
}}
>
<Stack direction="row" justifyContent="flex-start" sx={{flex: 1}}>
<IconButton
color="inherit"
aria-label="open drawer"
onClick={handleDrawerToggle}
edge="start"
sx={{display: {md: "none"}, m: 0}}
>
<MenuIcon/>
</IconButton>
</Stack>
<Stack
direction="row"
justifyContent="center"
sx={{
flex: 1,
position: "relative",
...theme.mixins.toolbar,
}}
>
<Box
sx={{
position: "relative",
my: 1,
width: 56,
height: 56,
"@media (min-width:600px)": {maxWidth: 64, maxHeight: 64},
"@media (min-width:0px)": {
"@media (orientation: landscape)": {
width: 48,
height: 48,
},
},
}}
>
</Box>
</Stack>
<Stack direction="row" justifyContent="flex-end" sx={{flex: 1}}>
<ProfileMenu/>
</Stack>
</Toolbar>
</Container>
</AppBar>
</>
);
}
export default Header;

View File

@@ -1,49 +0,0 @@
import {useState} from "react";
import FullPageLayout from "../FullPageLayout";
import Header from "./header";
import Sidebar from "./sidebar";
import {Toolbar} from "@mui/material";
import BreadCrumbs from "./breadcrumbs";
import {ToastContainer} from "react-toastify";
import "react-toastify/dist/ReactToastify.css";
import useDirection from "@/lib/app/hooks/useDirection";
const drawerWidth = 240;
const DashboardLayouts = (props) => {
const {directionApp} = useDirection();
const {window} = props;
const [mobileOpen, setMobileOpen] = useState(false);
const container =
window !== undefined ? () => window().document.body : undefined;
const handleDrawerToggle = () => {
setMobileOpen(!mobileOpen);
};
return (
<FullPageLayout direction="row">
<Header
handleDrawerToggle={handleDrawerToggle}
drawerWidth={drawerWidth}
/>
<Sidebar
container={container}
mobileOpen={mobileOpen}
handleDrawerToggle={handleDrawerToggle}
drawerWidth={drawerWidth}
/>
<FullPageLayout
component="main"
sx={{flexGrow: 1, width: {sm: `calc(100% - ${drawerWidth}px)`}}}
>
<Toolbar/>
<FullPageLayout sx={{mt: 3}}>
<BreadCrumbs isVisible={true}/>
{props.children}
</FullPageLayout>
</FullPageLayout>
</FullPageLayout>
);
};
export default DashboardLayouts;

View File

@@ -1,26 +0,0 @@
import useUser from "@/lib/app/hooks/useUser";
import {Divider, Stack, Toolbar, Typography} from "@mui/material";
import {useTranslations} from "next-intl";
import SidebarList from "./SidebarList";
const SidebarDrawer = ({handleDrawerToggle}) => {
const {user} = useUser();
const t = useTranslations();
return (
<>
<Toolbar>
<Stack>
<Typography variant="h6" sx={{color: "primary.main"}}>
{t("app_short_name")}
</Typography>
<Typography variant="caption">{user.type_name}</Typography>
</Stack>
</Toolbar>
<Divider/>
<SidebarList handleDrawerToggle={handleDrawerToggle}/>
</>
);
};
export default SidebarDrawer;

View File

@@ -1,69 +0,0 @@
import {Divider, List} from "@mui/material";
import {Fragment, useEffect, useReducer} from "react";
import SidebarListItem from "./SidebarListItem";
import sidebarMenu from "@/core/data/sidebarMenu";
import {useRouter} from "next/router";
import useUser from "@/lib/app/hooks/useUser";
function reducer(state, action) {
switch (action.type) {
case "COLLAPSE_MENU":
return state.map((itemsArr) =>
itemsArr.map((item) =>
action.key == item.key
? {...item, showSubItem: !item.showSubItem}
: item
)
);
case "SELECTED":
return state.map((itemsArr) =>
itemsArr.map((item) =>
action.route == item.route
? {...item, selected: true}
: {...item, selected: false}
)
);
default:
throw new Error();
}
}
export default function SidebarList({handleDrawerToggle}) {
const [itemMenu, dispatch] = useReducer(reducer, sidebarMenu);
const {user} = useUser();
// activate current page in menu
const router = useRouter();
useEffect(() => {
dispatch({type: "SELECTED", route: router.pathname});
}, [router.pathname]);
return (
<List>
{itemMenu.map((itemArr, index) => (
<Fragment key={index}>
{itemArr.map((item) => {
if (
(item.userType === user.type_id || item.userType === 0) &&
(user[item.permission_name] || item.permission_name === "all")
) {
return (
<SidebarListItem
item={item}
dispatch={dispatch}
key={item.key}
handleDrawerToggle={handleDrawerToggle}
/>
);
} else {
return null;
}
})}
<Divider/>
</Fragment>
)
)}
</List>
)
;
}

View File

@@ -1,59 +0,0 @@
import {NextLinkComposed} from "@/core/components/LinkRouting";
import ExpandLess from "@mui/icons-material/ExpandLess";
import ExpandMore from "@mui/icons-material/ExpandMore";
import {ListItem, ListItemButton, ListItemIcon, ListItemText,} from "@mui/material";
import {useTranslations} from "next-intl";
import {Fragment} from "react";
import SidebarListSubItem from "./SidebarListSubItem";
const SidebarListItem = ({item, dispatch, handleDrawerToggle}) => {
const t = useTranslations();
return (
<Fragment key={item.key}>
<ListItem disablePadding sx={{display: "block"}}>
<ListItemButton
selected={item.selected}
{...(item.type == "page" && {
component: NextLinkComposed,
to: {
pathname: item.route,
},
})}
onClick={() => {
if (item.type == "menu") {
dispatch({type: "COLLAPSE_MENU", key: item.key});
}
handleDrawerToggle();
}}
sx={{
minHeight: 48,
}}
>
<ListItemIcon
sx={{
minWidth: 0,
justifyContent: "center",
color: "primary.main",
pr: 2,
}}
>
{item.icon}
</ListItemIcon>
<ListItemText primary={t(item.key)}/>
{item.type == "menu" &&
(item.showSubItem ? <ExpandLess/> : <ExpandMore/>)}
</ListItemButton>
</ListItem>
{item.subItem && (
<SidebarListSubItem
item={item}
handleDrawerToggle={handleDrawerToggle}
/>
)}
</Fragment>
);
};
export default SidebarListItem;

View File

@@ -1,48 +0,0 @@
import {NextLinkComposed} from "@/core/components/LinkRouting";
import InboxIcon from "@mui/icons-material/MoveToInbox";
import {Collapse, List, ListItemButton, ListItemIcon, ListItemText,} from "@mui/material";
import {useTranslations} from "next-intl";
import {Fragment} from "react";
const SidebarListSubItem = ({item, handleDrawerToggle}) => {
const t = useTranslations();
return (
<Collapse in={item.showSubItem} timeout="auto" unmountOnExit>
<List component="div" disablePadding sx={{bgcolor: "#f6f6f6"}}>
{item.subItem.map((subitem, index) => (
<Fragment key={subitem.key}>
<ListItemButton
component={NextLinkComposed}
to={{
pathname: subitem.route,
}}
sx={{
minHeight: 48,
}}
onClick={(event) => {
if (item.type == "menu") {
dispatch({type: "COLLAPSE_MENU", key: item.key});
}
handleDrawerToggle();
}}
>
<ListItemIcon
sx={{
minWidth: 0,
justifyContent: "center",
pr: 2,
}}
>
<InboxIcon/>
</ListItemIcon>
<ListItemText primary={t(subitem.key)}/>
</ListItemButton>
</Fragment>
))}
</List>
</Collapse>
);
};
export default SidebarListSubItem;

View File

@@ -1,46 +0,0 @@
import {Box, Drawer} from "@mui/material";
import SidebarDrawer from "./SidebarDrawer";
const Sidebar = (props) => {
return (
<Box
component="nav"
sx={{width: {md: props.drawerWidth}, flexShrink: {sm: 0}}}
aria-label="mailbox folders"
>
<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,
},
}}
>
<SidebarDrawer handleDrawerToggle={props.handleDrawerToggle}/>
</Drawer>
<Drawer
variant="permanent"
sx={{
display: {xs: "none", md: "block"},
"& .MuiDrawer-paper": {
boxSizing: "border-box",
width: props.drawerWidth,
},
}}
open
>
<SidebarDrawer handleDrawerToggle={props.handleDrawerToggle}/>
</Drawer>
</Box>
);
};
export default Sidebar;

16
src/layouts/index.jsx Normal file
View File

@@ -0,0 +1,16 @@
import {Fragment} from "react";
const layoutList = (await import('./list'))
const Layout = ({layout, children}) => {
const Component = layoutList[layout?.name] || Fragment
const props = layout?.props || {}
return (
<Component {...props}>
{children}
</Component>
)
}
export default Layout

3
src/layouts/list.jsx Normal file
View File

@@ -0,0 +1,3 @@
import DashboardLayout from './dashboard'
export {DashboardLayout}

View File

@@ -1,99 +0,0 @@
import {FA_DATATABLE_LOCALIZATION} from "&/locales/fa/datatable";
import {useRouter} from "next/router";
import {createContext, useEffect, useState} from "react";
import useUser from "../hooks/useUser";
export const LanguageContext = createContext();
export const LanguageProvider = ({children}) => {
const router = useRouter();
const languageList = [
{
key: "fa",
dir: "rtl",
name: "فارسی",
fontFamily: `IRANSans, sans-serif`,
tableLocalization: FA_DATATABLE_LOCALIZATION,
}
];
const {user, userChangedLanguage, changeLanguageState} = useUser();
const [languageIsReady, setLanguageIsReady] = useState(false);
const [languageApp, setLanguageApp] = useState();
const [directionApp, setDirectionApp] = useState(
process.env.NEXT_PUBLIC_DEFAULT_DIRECTION
);
useEffect(() => {
const lang = localStorage.getItem("_language");
if (!lang && !languageApp) {
setLanguageApp(process.env.NEXT_PUBLIC_DEFAULT_LANGUAGE);
} else if (lang) {
setLanguageApp(lang);
}
}, []);
useEffect(() => {
if (!languageApp) return;
const lang = localStorage.getItem("_language");
if (!lang) {
localStorage.setItem("_language", languageApp);
} else {
if (languageApp != lang) {
localStorage.setItem("_language", languageApp);
}
}
}, [languageApp]);
useEffect(() => {
if (!router.isReady) return;
if (user.user_language) {
if (user.user_language != languageApp) {
setLanguageApp(user.user_language);
return;
}
}
if (languageApp != router.locale) {
router.replace(
{pathname: router.pathname, query: router.query},
router.asPath,
{
locale: languageApp,
}
);
return;
}
for (const lang of languageList) {
if (languageApp != lang.key) continue;
setDirectionApp(lang.dir);
document.dir = lang.dir;
}
const timer = setTimeout(() => {
changeLanguageState(false);
setLanguageIsReady(true);
}, 1000);
return () => {
clearTimeout(timer);
};
}, [router.locale, router.isReady, userChangedLanguage, languageApp]);
return (
<LanguageContext.Provider
value={{
languageApp,
setLanguageApp,
directionApp,
languageIsReady,
setLanguageIsReady,
languageList,
}}
>
{children}
</LanguageContext.Provider>
);
};

View File

@@ -1,14 +0,0 @@
import LoadingHardPage from "@/core/components/LoadingHardPage";
import {createContext, useState} from "react";
export const LoadingContext = createContext();
export const LoadingProvider = ({children}) => {
const [loadingPage, setLoadingPage] = useState(false);
return (
<LoadingContext.Provider value={{loadingPage, setLoadingPage}}>
<LoadingHardPage loading={loadingPage}/>
{children}
</LoadingContext.Provider>
);
};

View File

@@ -1,133 +0,0 @@
import {USER_INFO} from "@/core/data/apiRoutes";
import axios from "axios";
import {createContext, useCallback, useEffect, useReducer} from "react";
import {errorRequest, errorResponse, errorSetting} from "@/core/utils/errorHandler";
const initialUser = {
isAuth: false,
userChangedLanguage: false,
token: null,
user: {},
};
const reducer = (state, action) => {
switch (action.type) {
case "CLEAR_USER":
return {...state, user: {}};
case "CHANGE_USER":
return {...state, user: action.user};
case "CHANGE_USER_LANGUAGE":
return {
...state,
user: {...state.user, user_language: action.language},
};
case "CHANGE_AUTH_STATE":
return {...state, isAuth: action.isAuth};
case "CHANGE_LANGUAGE_STATE":
return {...state, userChangedLanguage: action.userChangedLanguage};
case "CLEAR_TOKEN":
localStorage.removeItem("_token");
return {...state, token: null};
case "SET_TOKEN":
localStorage.setItem("_token", action.token);
return {...state, token: action.token};
default:
return state;
}
};
export const UserContext = createContext();
export const UserProvider = ({children}) => {
const [state, dispatch] = useReducer(reducer, initialUser);
const clearUser = useCallback(() => {
dispatch({type: "CLEAR_USER"});
}, []);
const changeUser = useCallback((user) => {
dispatch({type: "CHANGE_USER", user});
}, []);
const changeUserLanguage = useCallback(/* for multi language app */);
const changeAuthState = useCallback((isAuth) => {
dispatch({type: "CHANGE_AUTH_STATE", isAuth});
}, []);
const changeLanguageState = useCallback((userChangedLanguage) => {
dispatch({type: "CHANGE_LANGUAGE_STATE", userChangedLanguage});
}, []);
const clearToken = useCallback(() => {
dispatch({type: "CLEAR_TOKEN"});
}, []);
const setToken = useCallback((token) => {
dispatch({type: "SET_TOKEN", token});
}, []);
const getUser = useCallback(
(callback = () => {
}) => {
axios
.get(USER_INFO, {
headers: {authorization: `Bearer ${state.token}`},
})
.then(({data}) => {
if (typeof callback === "function") callback(data);
})
.catch(error => {
if (error.response) {
errorResponse(error.response, clearToken, null, false)
} else if (error.request) {
errorRequest(null, false)
} else {
errorSetting(null, false)
}
})
},
[state.token]
);
useEffect(() => {
const localToken = localStorage.getItem("_token");
if (localToken) dispatch({type: "SET_TOKEN", token: localToken});
}, []);
useEffect(() => {
if (!state.token) {
clearUser();
changeAuthState(false);
changeLanguageState(false);
return;
}
getUser((data) => {
changeUser(data);
changeAuthState(true);
changeLanguageState(true);
});
}, [state.token]);
return (
<UserContext.Provider
value={{
isAuth: state.isAuth,
userChangedLanguage: state.userChangedLanguage,
token: state.token,
user: state.user,
getUser,
clearUser,
changeUser,
changeUserLanguage,
changeAuthState,
changeLanguageState,
clearToken,
setToken,
}}
>
{children}
</UserContext.Provider>
);
};

View File

@@ -1,10 +0,0 @@
import {useContext} from "react";
import {LanguageContext} from "../contexts/language";
const useDirection = () => {
const {directionApp} = useContext(LanguageContext);
return {directionApp};
};
export default useDirection;

View File

@@ -1,28 +0,0 @@
import {useContext} from "react";
import {LanguageContext} from "../contexts/language";
import useUser from "./useUser";
const useLanguage = () => {
const {isAuth, changeUserLanguage} = useUser();
const {
languageApp,
setLanguageApp,
languageIsReady,
setLanguageIsReady,
languageList,
} = useContext(LanguageContext);
const changeLanguage = (lang) => {
if (lang == languageApp) return;
setLanguageIsReady(false);
setLanguageApp(lang);
if (isAuth) {
changeUserLanguage(lang);
}
};
return {languageApp, changeLanguage, languageIsReady, languageList};
};
export default useLanguage;

View File

@@ -1,10 +0,0 @@
import {useContext} from "react";
import {LoadingContext} from "../contexts/loading";
const useLoading = () => {
const {setLoadingPage} = useContext(LoadingContext);
return {setLoadingPage};
};
export default useLoading;

View File

@@ -1,58 +0,0 @@
import {useEffect, useState} from "react";
function getNetworkConnection() {
return (
navigator.connection ||
navigator.mozConnection ||
navigator.webkitConnection ||
null
);
}
function getNetworkConnectionInfo() {
const connection = getNetworkConnection();
if (!connection) {
return {};
}
return {
rtt: connection.rtt,
type: connection.type,
saveData: connection.saveData,
downLink: connection.downLink,
downLinkMax: connection.downLinkMax,
effectiveType: connection.effectiveType,
};
}
function useNetwork() {
const [state, setState] = useState(() => {
return {
online: navigator.onLine,
};
});
useEffect(() => {
const handleOnline = () => {
setState((prevState) => ({
...prevState,
online: true,
}));
};
const handleOffline = () => {
setState((prevState) => ({
...prevState,
online: false,
}));
};
window.addEventListener("online", handleOnline);
window.addEventListener("offline", handleOffline);
return () => {
window.removeEventListener("online", handleOnline);
window.removeEventListener("offline", handleOffline);
};
}, []);
return state;
}
export default useNetwork;

View File

@@ -1,66 +0,0 @@
import axios from "axios";
import {successRequest} from "@/core/utils/succesHandler";
import PendingNotification from "@/core/components/notifications/PendingNotification";
import {useTranslations} from "next-intl";
import useUser from "@/lib/app/hooks/useUser";
import {errorRequest, errorResponse, errorSetting} from "@/core/utils/errorHandler";
import useNetwork from "@/lib/app/hooks/useNetwork";
const defaultOptions = {
auth: false, data: {}, requestOptions: {
headers: {}
}, notification: true, pending: true, success: {
notification: {
show: true,
},
}, failed: {
notification: {
show: true,
},
},
}
const useRequest = (initOptions) => {
const network = useNetwork()
const t = useTranslations()
const {token, clearToken} = useUser()
let _options = {...defaultOptions, ...initOptions}
function requestServer(url = '', method = 'get', options) {
_options = {..._options, ...options}
if (_options.auth) _options = {
..._options, requestOptions: {
..._options.requestOptions,
headers: {..._options.requestOptions.headers, authorization: `Bearer ${token}`}
}
}
return new Promise((resolve, reject) => {
if (!network.online) {
reject()
return
}
if (_options.notification && _options.failed.notification.show && _options.pending) PendingNotification(t)
axios({
url: url, method: method, data: _options.data, ..._options.requestOptions
})
.then(response => {
successRequest(response, t, _options)
resolve(response)
})
.catch(error => {
if (error.response) {
errorResponse(error.response, clearToken, t, _options.notification && _options.failed.notification.show)
} else if (error.request) {
errorRequest(t, _options.notification && _options.failed.notification.show)
} else {
errorSetting(t, _options.notification && _options.failed.notification.show)
}
reject(error)
})
});
}
return requestServer
}
export default useRequest

View File

@@ -1,36 +0,0 @@
import {useContext} from "react";
import {UserContext} from "../contexts/user";
const useUser = () => {
const {
isAuth,
userChangedLanguage,
token,
user,
getUser,
clearUser,
changeUser,
changeUserLanguage,
changeAuthState,
changeLanguageState,
clearToken,
setToken,
} = useContext(UserContext);
return {
isAuth,
userChangedLanguage,
token,
user,
getUser,
clearUser,
changeUser,
changeUserLanguage,
changeAuthState,
changeLanguageState,
clearToken,
setToken,
};
};
export default useUser;

View File

@@ -1,41 +0,0 @@
import {NextLinkComposed} from "@/core/components/LinkRouting";
import Message from "@/core/components/Messages";
import useUser from "@/lib/app/hooks/useUser";
import {Button, Typography} from "@mui/material";
import {useTranslations} from "next-intl";
const UserTypeMiddleware = ({children, user_type}) => {
const {user} = useUser();
const t = useTranslations();
const userPermission = user.type_id === user_type;
if (!userPermission) {
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>
</>
}
/>
);
}
return <>{children}</>;
};
export default UserTypeMiddleware;

View File

@@ -1,41 +0,0 @@
import {NextLinkComposed} from "@/core/components/LinkRouting";
import Message from "@/core/components/Messages";
import useUser from "@/lib/app/hooks/useUser";
import {Button, Typography} from "@mui/material";
import {useTranslations} from "next-intl";
import {useRouter} from "next/router";
const WithAuthMiddleware = ({children}) => {
const {isAuth} = useUser();
const t = useTranslations();
const router = useRouter();
if (!isAuth)
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",
}}
>
{t("login")}
</Button>
</>
}
/>
);
return <>{children}</>;
};
export default WithAuthMiddleware;

View File

@@ -1,56 +0,0 @@
import Message from "@/core/components/Messages";
import useUser from "@/lib/app/hooks/useUser";
import {Stack, Typography} from "@mui/material";
import {useTranslations} from "next-intl";
import {useSearchParams} from "next/navigation";
import {useRouter} from "next/router";
import {useEffect} from "react";
const WithoutAuthMiddleware = ({children}) => {
const {isAuth} = useUser();
const t = useTranslations();
const router = useRouter();
// gettin url query
const searchParams = useSearchParams();
const backUrlDecodedPath = searchParams.get("back_url");
useEffect(() => {
if (!isAuth) return;
const timer = setTimeout(() => {
router.replace(
backUrlDecodedPath
? decodeURIComponent(backUrlDecodedPath)
: "/dashboard"
);
}, 2000);
return () => {
clearTimeout(timer);
};
}, [isAuth]);
if (isAuth)
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>
}
/>
);
return <>{children}</>;
};
export default WithoutAuthMiddleware;

View File

@@ -1,25 +1,41 @@
import "&/fontiran.scss"; import "&/global.scss";
import AppLayout from "@/layouts/AppLayout";
import MuiLayout from "@/layouts/MuiLayout";
import {LanguageProvider} from "@/lib/app/contexts/language";
import {LoadingProvider} from "@/lib/app/contexts/loading";
import {UserProvider} from "@/lib/app/contexts/user";
import "moment/locale/fa"; import "moment/locale/fa";
import {
AppLayout,
LanguageProvider,
LoadingProvider,
MuiLayout,
NetworkProvider,
ToastProvider,
UserProvider
} from "@witel/webapp-builder";
import {GET_USER} from "@/core/data/apiRoutes";
import languageList from "@/core/data/languageList";
import {NextIntlProvider} from "next-intl"; import {NextIntlProvider} from "next-intl";
import themeRtl from "@/core/utils/theme-rtl";
import TitlePage from "@/core/components/TitlePage"; import TitlePage from "@/core/components/TitlePage";
import GlobalHead from "@/core/components/GlobalHead";
import Layout from "@/layouts";
const App = ({Component, pageProps}) => { const App = ({Component, pageProps}) => {
return ( return (
<> <>
<UserProvider> <UserProvider urlGetUser={GET_USER} schemaGetUser={(data) => data}>
<LanguageProvider> <LanguageProvider defaultLanguage={process.env.NEXT_PUBLIC_DEFAULT_LANGUAGE}
<NextIntlProvider messages={pageProps.messages || {}}> languageList={languageList}>
<MuiLayout isBot={pageProps.isBot}> <NextIntlProvider locale={pageProps.locale} messages={pageProps.messages || {}}>
<MuiLayout themes={{rtl: themeRtl}} isBot={pageProps.isBot}>
{pageProps.messages ? <TitlePage text={pageProps.title}/> : ''} {pageProps.messages ? <TitlePage text={pageProps.title}/> : ''}
<LoadingProvider> <LoadingProvider>
<AppLayout isBot={pageProps.isBot}> <ToastProvider>
<AppLayout headComponent={GlobalHead} isBot={pageProps.isBot}>
<NetworkProvider>
<Layout layout={pageProps.layout}>
<Component {...pageProps} /> <Component {...pageProps} />
</Layout>
</NetworkProvider>
</AppLayout> </AppLayout>
</ToastProvider>
</LoadingProvider> </LoadingProvider>
</MuiLayout> </MuiLayout>
</NextIntlProvider> </NextIntlProvider>

View File

@@ -1,7 +1,8 @@
import {createEmotionCacheRtl} from "@/core/utils/createEmotionCache";
import theme from "@/core/utils/theme"; import theme from "@/core/utils/theme";
import createEmotionServer from "@emotion/server/create-instance"; import createEmotionServer from "@emotion/server/create-instance";
import Document, {Head, Html, Main, NextScript} from "next/document"; import Document, {Head, Html, Main, NextScript} from "next/document";
import {createEmotionCacheLtr, createEmotionCacheRtl} from "@witel/webapp-builder";
import languageList from "@/core/data/languageList";
export default function MyDocument(props) { export default function MyDocument(props) {
const {emotionStyleTags} = props; const {emotionStyleTags} = props;
@@ -26,10 +27,13 @@ export default function MyDocument(props) {
MyDocument.getInitialProps = async (ctx) => { MyDocument.getInitialProps = async (ctx) => {
const originalRenderPage = ctx.renderPage; const originalRenderPage = ctx.renderPage;
let cache; let cache;
switch (ctx.locale) { for (const lang of languageList) {
case "fa": if (ctx.locale !== lang.key) continue;
if (lang.dir === 'rtl') {
cache = createEmotionCacheRtl(); cache = createEmotionCacheRtl();
break; } else {
cache = createEmotionCacheLtr();
}
} }
const {extractCriticalToChunks} = createEmotionServer(cache); const {extractCriticalToChunks} = createEmotionServer(cache);

View File

@@ -1,12 +1,9 @@
import FirstComponent from "@/components/dashboard/first"; import FirstComponent from "@/components/dashboard/first";
import WithAuthMiddleware from "@/middlewares/WithAuth";
import {parse} from "next-useragent"; import {parse} from "next-useragent";
export default function Dashboard() { export default function Dashboard() {
return ( return (
<WithAuthMiddleware>
<FirstComponent/> <FirstComponent/>
</WithAuthMiddleware>
); );
} }
@@ -17,6 +14,8 @@ export async function getServerSideProps({req, locale}) {
messages: (await import(`&/locales/${locale}/app.json`)).default, messages: (await import(`&/locales/${locale}/app.json`)).default,
title: "Dashboard.dashboard_page", title: "Dashboard.dashboard_page",
isBot, isBot,
locale,
layout: {name: 'DashboardLayout'}
}, },
}; };
} }

View File

@@ -1,16 +1,9 @@
import WithAuthMiddleware from "@/middlewares/WithAuth";
import {parse} from "next-useragent"; import {parse} from "next-useragent";
import UserTypeMiddleware from "@/middlewares/UserType";
import LoanRequestComponent from "@/components/dashboard/navgan/add-request-loan"; import LoanRequestComponent from "@/components/dashboard/navgan/add-request-loan";
const user_type = 1;
export default function AddLoanRequest() { export default function AddLoanRequest() {
return ( return (
<WithAuthMiddleware>
<UserTypeMiddleware user_type={user_type}>
<LoanRequestComponent/> <LoanRequestComponent/>
</UserTypeMiddleware>
</WithAuthMiddleware>
); );
} }
@@ -21,6 +14,8 @@ export async function getServerSideProps({req, locale}) {
messages: (await import(`&/locales/${locale}/app.json`)).default, messages: (await import(`&/locales/${locale}/app.json`)).default,
title: "LoanRequest.loan_request_page", title: "LoanRequest.loan_request_page",
isBot, isBot,
locale,
layout: {name: 'DashboardLayout'}
}, },
}; };
} }

View File

@@ -1,16 +1,9 @@
import WithAuthMiddleware from "@/middlewares/WithAuth";
import {parse} from "next-useragent"; import {parse} from "next-useragent";
import UserTypeMiddleware from "@/middlewares/UserType";
import LoanFollowUpComponent from "@/components/dashboard/navgan/followUp-loan"; import LoanFollowUpComponent from "@/components/dashboard/navgan/followUp-loan";
const user_type = 1;
export default function FollowUpLoan() { export default function FollowUpLoan() {
return ( return (
<WithAuthMiddleware>
<UserTypeMiddleware user_type={user_type}>
<LoanFollowUpComponent/> <LoanFollowUpComponent/>
</UserTypeMiddleware>
</WithAuthMiddleware>
); );
} }
@@ -21,6 +14,8 @@ export async function getServerSideProps({req, locale}) {
messages: (await import(`&/locales/${locale}/app.json`)).default, messages: (await import(`&/locales/${locale}/app.json`)).default,
title: "LoanRequest.loan_request_page", title: "LoanRequest.loan_request_page",
isBot, isBot,
locale,
layout: {name: 'DashboardLayout'}
}, },
}; };
} }

View File

@@ -1,24 +0,0 @@
import WithAuthMiddleware from "@/middlewares/WithAuth";
import {parse} from "next-useragent";
const user_type = 2;
export default function AddLoanRequest() {
return (
<WithAuthMiddleware>
{/*<UserTypeMiddleware user_type={user_type}>*/}
{/* <LoanRequestComponent/>*/}
{/*</UserTypeMiddleware>*/}
</WithAuthMiddleware>
);
}
export async function getServerSideProps({req, locale}) {
const {isBot} = parse(req.headers["user-agent"]);
return {
props: {
messages: (await import(`&/locales/${locale}/app.json`)).default,
title: "LoanRequest.loan_request_page",
isBot,
},
};
}

View File

@@ -1,24 +0,0 @@
import WithAuthMiddleware from "@/middlewares/WithAuth";
import {parse} from "next-useragent";
const user_type = 2;
export default function EditLoanRequest() {
return (
<WithAuthMiddleware>
{/*<UserTypeMiddleware user_type={user_type}>*/}
{/* <LoanRequestComponent/>*/}
{/*</UserTypeMiddleware>*/}
</WithAuthMiddleware>
);
}
export async function getServerSideProps({req, locale}) {
const {isBot} = parse(req.headers["user-agent"]);
return {
props: {
messages: (await import(`&/locales/${locale}/app.json`)).default,
title: "EditLoanRequest.edit_loan_request_page",
isBot,
},
};
}

View File

@@ -1,24 +0,0 @@
import WithAuthMiddleware from "@/middlewares/WithAuth";
import {parse} from "next-useragent";
const user_type = 2;
export default function FollowUpLoan() {
return (
<WithAuthMiddleware>
{/*<UserTypeMiddleware user_type={user_type}>*/}
{/* <LoanFollowUpComponent/>*/}
{/*</UserTypeMiddleware>*/}
</WithAuthMiddleware>
);
}
export async function getServerSideProps({req, locale}) {
const {isBot} = parse(req.headers["user-agent"]);
return {
props: {
messages: (await import(`&/locales/${locale}/app.json`)).default,
title: "LoanFollowUp.loan_follow_up_page",
isBot,
},
};
}

View File

@@ -1,10 +1,10 @@
import LoginComponent from "@/components/login"; import LoginComponent from "@/components/login";
import WithoutAuthMiddleware from "@/middlewares/WithoutAuth";
import {parse} from "next-useragent"; import {parse} from "next-useragent";
import {WithoutAuthMiddleware} from "@witel/webapp-builder";
export default function Login() { export default function Login() {
return ( return (
<WithoutAuthMiddleware> <WithoutAuthMiddleware defaultUrl={'/'}>
<LoginComponent/> <LoginComponent/>
</WithoutAuthMiddleware> </WithoutAuthMiddleware>
); );

View File

@@ -1,10 +1,10 @@
import RegisterComponent from "@/components/register"; import RegisterComponent from "@/components/register";
import WithoutAuthMiddleware from "@/middlewares/WithoutAuth";
import {parse} from "next-useragent"; import {parse} from "next-useragent";
import {WithoutAuthMiddleware} from "@witel/webapp-builder";
export default function Register() { export default function Register() {
return ( return (
<WithoutAuthMiddleware> <WithoutAuthMiddleware defaultUrl={'/'}>
<RegisterComponent/> <RegisterComponent/>
</WithoutAuthMiddleware> </WithoutAuthMiddleware>
); );