Merge branch 'release/v1.25.1'

This commit is contained in:
AmirHossein Mahmoodi
2023-12-20 11:53:03 +03:30
122 changed files with 1775 additions and 4672 deletions

4
.gitignore vendored
View File

@@ -31,4 +31,6 @@ cypress/screenshots
.env
.idea
package-lock.json
ecosystem.config
ecosystem.config
public/config.json

View File

@@ -1,5 +1,5 @@
NEXT_PUBLIC_API_NAME = "Loan Facilities Dashboard"
NEXT_PUBLIC_API_VERSION = "1.22.3"
NEXT_PUBLIC_API_VERSION = "1.25.1"
NEXT_PUBLIC_DEFAULT_LANGUAGE = "fa"
NEXT_PUBLIC_DEFAULT_DIRECTION = "rtl"

View File

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

View File

@@ -16,8 +16,11 @@
"@mui/icons-material": "^5.11.16",
"@mui/lab": "^5.0.0-alpha.152",
"@mui/material": "^5.12.0",
"@mui/x-date-pickers": "^6.18.5",
"@witel/webapp-builder": "^1.2.4",
"axios": "^1.4.0",
"colord": "^2.9.3",
"date-fns-jalali": "^2.13.0-0",
"eslint": "8.36.0",
"formik": "^2.2.9",
"fs-extra": "^11.1.1",

View File

@@ -0,0 +1,41 @@
{
"middlewares": {},
"deadlines": {
"register": {
"date": {
"from": "1402/09/01",
"to": "1402/09/10"
},
"messages": {
"before": "ثبت نام هنوز شروع نشده",
"after": "زمان ثبت نام به اتمام رسید"
},
"timeline_label": {
"primary": "ثبت نام متقاضی",
"secondary": "ثبت نام متقاضیان واجد شرایط"
}
},
"validation": {
"date": {
"from": "1402/09/15",
"to": "1402/11/10"
},
"messages": {},
"timeline_label": {
"primary": "صحت سنجی متقاضیان",
"secondary": "بررسی و کارشناسی درخواست ها"
}
},
"done": {
"date": {
"from": "1402/11/10",
"to": "1402/12/20"
},
"messages": {},
"timeline_label": {
"primary": "ارجاع به بانک",
"secondary": "فرآیند معرفی ضامن و پرداخت"
}
}
}
}

Binary file not shown.

2
public/global.scss Normal file
View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 565 KiB

View File

@@ -1,10 +1,18 @@
{
"app_name": "سامانه جامع تسهیلات",
"app_short_name": "سامانه تسهیلات",
"birthday": "تاریخ تولد",
"birthday_error": "تاریخ تولد خود را وارد کنید!",
"dashboard": "داشبورد",
"text_field_city_id": "شهر",
"text_field_error_fetching_cities": "لطفا شهر خود را وارد کنید!",
"text_field_loading_cities_list": "درحال دریافت اطلاعات شهر ها...",
"text_field_loading": "درحال دریافت اطلاعات",
"text_field_error_fetching": "خطا در دریافت اطلاعات",
"first_page": "خوش آمدید",
"powered_by_witel": "توسعه یافته توسط وایتل",
"login": "ورود",
"today": "امروز",
"pending": "درحال اجرا...",
"login_user_panel": "ورود به پنل کاربران",
"register_navy": "ثبت نام اعضای ناوگان",
@@ -18,6 +26,8 @@
"upload_file_text": "بارگذاری فایل",
"online_message": "شما به اینترنت وصل هستید",
"offline_message": "اتصال شما به اینترنت قطع شده است",
"routing_to": "در حال انتقال به صفحه",
"error_message_plate_number": "لطفا شماره پلاک خود را وارد کنید!",
"header": {
"open_profile": "پروفایل",
"edit_profile": "ویرایش پروفایل",
@@ -43,7 +53,7 @@
"button_back_dashboard": "بازگشت به صفحه اصلی"
},
"Titles": {
"title_login_page": "صفحه ورود",
"title_login_page": "ورود به سامانه",
"title_register_page": "صفحه ثبت نام",
"title_login_navy_page": "ورود اعضای ناوگان",
"title_login_welfare_services_page": "ورود اعضای خدمات رفاهی",
@@ -122,51 +132,82 @@
"loan_request_correction": "اصلاح درخواست",
"loan_request_detail": "مشاهده جزئیات",
"request_number": "درخواست شماره",
"no_request_found": "درخواستی یافت نشد"
"no_request_found": "درخواستی یافت نشد",
"loan_unique_code": "کد یکتا",
"loan_date": "تاریخ درخواست",
"loan_status": "وضعیت",
"loan_bank_branch": "اطلاعات شعبه",
"loan_details": "نمایش"
},
"LoanRequest": {
"finish_loan_request": "درخواست شما با موفقیت ثبت شد",
"button_while_submit": "درحال بارگذاری ...",
"checkbox": "بدینوسیله صحت کلیه اطلاعات مندرج در این فرم را تایید و گواهی میدهم .",
"back_to_dashboard": "پیگیری درخواست ها",
"cityList_empty": "ابتدا استان را وارد کنید",
"cityList_loading": "درحال دریافت لیست شهر ها ...",
"loan_request_page": "ثبت درخواست وام",
"personal_info": "اطلاعات شخصی",
"national_code_number": "کد ملی می بایست شامل اعداد باشد",
"national_code_max": "کد ملی می بایست 10 رقم باشد",
"shenasname_serial_max": "سریال پشت کارت ملی می بایست شامل 10 بخش باشد",
"postal_code_max": "کد پستی می بایست 10 رقم باشد",
"tel_number_max": "شماره ثابت می بایست 11 رقم باشد",
"national_code_positive": "کد ملی می بایست مثبت باشد",
"uploading_documents": "اسناد",
"error_message_name": "لطفا نام خود را وارد کنید!",
"error_message_phone_number": "لطفا شماره تلفن خود را وارد کنید!",
"error_message_plate_number": "لطفا شماره پلاک را به درستی وارد کنید!",
"error_message_occupation_id": "لطفا کد شغل را به درستی وارد کنید!",
"error_message_education_id": "لطفا تحصیلات را به درستی وارد کنید!",
"error_message_tel_number": "لطفا شماره ثابت (با پیش شماره استان) خود را وارد کنید!",
"error_message_vehicle_type": "لطفا نوع وسیله نقلیه خود را وارد کنید!",
"error_message_plate_number": "لطفا شماره پلاک خود را وارد کنید!",
"error_message_navgan_id": "لطفا کد ناوگان خود را وارد کنید!",
"error_message_navgan_id": "لطفا کد هوشمند ناوگان خود را وارد کنید!",
"error_message_province_id": "لطفا استان خود را وارد کنید!",
"error_message_national_id": "لطفا کد ملی خود را وارد کنید!",
"error_message_shenasname_id": "لطفا شماره شناسنامه خود را وارد کنید!",
"text_field_phone_number": "شماره تلفن",
"text_field_enter_your_phone_number": "شماره تلفن خود را وارد کنید",
"error_message_date_of_birth": "لطفا تاریخ تولد خود را وارد کنید!",
"error_message_shenasname_serial": "لطفا سریال پشت کارت ملی خود را وارد کنید!",
"error_message_postal_code": "لطفا کد پستی خود را وارد کنید!",
"error_message_national_code": "لطفا کد ملی خود را وارد کنید!",
"error_message_checkedBox": "لطفا کد صحت اطلاعات خود را تایید کنید!",
"error_message_navgan_type": "لطفا نوع ناوگان خود را وارد کنید!",
"error_message_address": "لطفا آدرس خود را وارد کنید!",
"text_field_tel_number": "شماره ثابت (با پیش شماره استان)",
"text_field_education_id": "تحصیلات",
"text_field_occupation_id": "شغل",
"text_field_enter_your_tel_number": "شماره ثابت (با پیش شماره استان) خود را وارد کنید",
"text_field_name": "نام",
"text_field_plate_part4": "بخش چهارم",
"text_field_plate_part3": "بخش سوم",
"text_field_plate_part2": "بخش دوم",
"text_field_plate_part1": "بخش اول",
"text_field_postal_code": "کد پستی",
"text_field_navgan_type": "نوع ناوگان",
"text_field_address": "آدرس",
"text_field_enter_your_plate_part1": "بخش اول پلاک را وارد کنید",
"text_field_enter_your_plate_part2": "بخش دوم پلاک را وارد کنید",
"text_field_enter_your_plate_part3": "بخش سوم پلاک را وارد کنید",
"text_field_enter_your_plate_part4": "بخش چهارم پلاک راوارد کنید",
"text_field_enter_your_address": "آدرس خود راوارد کنید",
"text_field_enter_your_name": "نام خود را وارد کنید",
"text_field_vehicle_type": "نام وسیله نقلیه",
"text_field_enter_your_vehicle_type": ام وسیله نقلیه خود را وارد کنید",
"text_field_enter_your_postal_code": "کد پستی خود را وارد کنید",
"text_field_vehicle_type": وع وسیله نقلیه",
"text_field_enter_your_vehicle_type": "نوع وسیله نقلیه خود را وارد کنید",
"text_field_province_id": "استان",
"text_field_loading_provinces_list": "درحال دریافت استان ها",
"text_field_loading_provinces_list": "درحال دریافت استان ها ...",
"text_field_error_fetching_provinces": "خطا در دریافت استان ها",
"text_field_enter_your_national_code": "کد ملی خود را وارد کنید",
"text_field_enter_your_province_id": "استان خود را وارد کنید",
"text_field_navgan_id": "شماره ناوگان",
"text_field_enter_your_navgan_id": "شماره ناوگان خود را وارد کنید",
"text_field_enter_your_city_id": "شهر خود را وارد کنید",
"text_field_navgan_id": "کد هوشمند ناوگان",
"text_field_enter_your_navgan_id": "کد هوشمند ناوگان خود را وارد کنید",
"text_field_national_code": "کد ملی",
"text_field_enter_your_national_id": "کد ملی خود را وارد کنید",
"text_field_shenasname_id": "شماره شناسنامه",
"text_field_enter_your_shenasname_id": "شماره شناسنامه خود را وارد کنید",
"text_field_shenasname_serial": "سریال پشت کارت ملی",
"text_field_enter_your_shenasname_serial": "سریال پشت کارت ملی خود را وارد کنید",
"text_field_plate_number": "شماره پلاک",
"text_field_enter_your_plate_number": "شماره پلاک خود را وارد کنید",
"button_submit": "ثبت درخواست",
"file_field_sherkat_naft_doc": "مجوز شرکت نفت",
"file_field_estate_doc": "سند مالکیت",
"file_field_agreement_doc": "موافقت نامه اصولی",
"error_message_sherkat_naft_doc": "مجوز شرکت نفت خود را وارد کنید",
"error_message_estate_doc": "سند مالکیت خود را وارد کنید",
"error_message_agreement_doc": "موافقت نامه اصولی خود را وارد کنید",
"file_field_shenasname_image": "تصویر شناسنامه",
"file_field_national_card_image": "تصویر کارت ملی",
"file_field_payan_khedmat_image": "تصویر کارت پایان خدمت",
"error_message_national_card_image": "تصویر کارت ملی خود را وارد کنید",
"error_message_shenasname_image": "تصویر شناسنامه خود را وارد کنید",
"error_message_payan_khedmat_image": "تصویر کارت پایان خدمت خود را وارد کنید",
"error_message_city_id": "لطفا شهر خود را وارد کنید!",
"add_loan_request_permission": "اکنون امکان ثبت درخواست جدید برای شما مقدور نیست"
},
"EditLoanRequest": {

View File

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

View File

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

View File

@@ -1,463 +1,471 @@
import Notifications from "@/core/components/notifications";
import SelectBox from "@/core/components/SelectBox";
import StyledForm from "@/core/components/StyledForm";
import UploadSystem from "@/core/components/UploadSystem";
import {GET_PROVINCE_LIST, SEND_LOAN_REQUEST_NAVGAN,} from "@/core/data/apiRoutes";
import useDirection from "@/lib/app/hooks/useDirection";
import useUser from "@/lib/app/hooks/useUser";
import DataSaverOnIcon from "@mui/icons-material/DataSaverOn";
import {Box, Button, Stack, TextField} from "@mui/material";
import axios from "axios";
import {Field, Formik} from "formik";
import {Box, Button, Checkbox, FormControlLabel, Grid, Stack, TextField, Typography} from "@mui/material";
import {useFormik} from "formik";
import {useTranslations} from "next-intl";
import {useEffect, useState} from "react";
import * as Yup from "yup";
import {CenterLayout, useRequest} from "@witel/webapp-builder";
import useProvince from "@/lib/app/hooks/useProvince";
import useCities from "@/lib/app/hooks/useCities";
import PlateNumber from "@/core/components/PlateNumber";
import MuiDatePicker from "@/core/components/MuiDatePicker";
import UseEducations from "@/lib/app/hooks/useEducations";
import useOccupations from "@/lib/app/hooks/useOccupations";
import {useState} from "react";
import DoneIcon from '@mui/icons-material/Done';
import {NextLinkComposed} from "@witel/webapp-builder/dist/utils/linkRouting";
import SvgDone from "@/core/components/svgs/SvgDone";
import {SEND_LOAN_REQUEST_NAVGAN} from "@/core/data/apiRoutes";
const AddFormComponent = () => {
const t = useTranslations();
const {directionApp} = useDirection();
const {token} = useUser();
const [finishLoanRequest, setFinishLoanRequest] = useState(false)
const [checked, setChecked] = useState(false);
const requestServer = useRequest()
const {provinceList, isLoadingProvinceList, errorProvinceList} = useProvince()
const {cityTextField, cityList, setProvinceID, isLoadingCityList} = useCities()
const {educationsList, isLoadingEducationsList, errorEducationsList} = UseEducations()
const {occupationsList, isLoadingOccupationsList, errorOccupationsList} = useOccupations()
// get province list
const [provinceList, setProvinceList] = useState([]);
useEffect(() => {
axios
.get(GET_PROVINCE_LIST, {
headers: {Authorization: `Bearer ${token}`},
})
.then(({data}) => {
const formattedData = data.map((province, index) => ({
id: index,
name: province.name,
value: province.id,
}));
setProvinceList(formattedData);
})
.catch((error) => {
Notifications(t, error.response);
});
}, []);
// end get province list
// upload files
const [selectedImageShenasname, setSelectedImageShenasname] = useState(
"/images/upload-image.svg"
);
const [fileTypeShenasname, setFileTypeShenasname] = useState(null);
const [fileNameShenasname, setFileNameShenasname] = useState(null);
const [selectedImageNationalCard, setSelectedImageNationalCard] = useState(
"/images/upload-image.svg"
);
const [fileTypeNationalCard, setFileTypeNationalCard] = useState(null);
const [fileNameNationalCard, setFileNameNationalCard] = useState(null);
const handleUploadChangeShenasname = (event, setFieldValue) => {
const uploadedFile = event.target?.files?.[0];
if (uploadedFile) {
const fileType = event.target?.files?.[0].type;
const fileName = event.target?.files?.[0].name;
setSelectedImageShenasname(URL.createObjectURL(uploadedFile));
setFileTypeShenasname(fileType);
setFileNameShenasname(fileName);
setFieldValue("shenasname_img", uploadedFile);
}
};
const handleUploadChangeNationalCard = (event, setFieldValue) => {
const uploadedFile = event.target?.files?.[0];
const fileType = event.target.files[0].type;
const fileName = event.target.files[0].name;
if (uploadedFile) {
setSelectedImageNationalCard(URL.createObjectURL(uploadedFile));
setFileTypeNationalCard(fileType);
setFileNameNationalCard(fileName);
setFieldValue("national_card_img", uploadedFile);
}
};
// end upload files
// initial values
const initialValues = {
name: "",
phone_number: "",
telephone_number: "",
vehicle_type: "",
plate_number: "",
province_id: "",
city_id: "",
navgan_id: "",
national_code: "",
shenasname_id: "",
national_card_img: null,
shenasname_img: null,
national_id: "",
national_serial_number: "",
postal_code: "",
navgan_type: "",
plate_part1: "",
plate_part2: "الف",
plate_part3: "",
plate_part4: "",
address: "",
birthday: "",
education_id: "",
occupation_id: "",
checkedBox: false
};
// end initial values
// validation
const validationSchema = Yup.object().shape({
name: Yup.string().required(t("LoanRequest.error_message_name")),
phone_number: Yup.string().required(
t("LoanRequest.error_message_phone_number")
),
telephone_number: Yup.mixed()
.test("max", `${t("LoanRequest.tel_number_max")}`, (value) => {
const stringValue = String(value);
return stringValue.length === 11
})
.required(t("LoanRequest.error_message_tel_number")),
vehicle_type: Yup.string().required(
t("LoanRequest.error_message_vehicle_type")
),
plate_number: Yup.string().required(
t("LoanRequest.error_message_plate_number")
navgan_type: Yup.string().required(
t("LoanRequest.error_message_navgan_type")
),
navgan_id: Yup.string().required(t("LoanRequest.error_message_navgan_id")),
occupation_id: Yup.string().required(
t("LoanRequest.error_message_occupation_id")
),
education_id: Yup.string().required(
t("LoanRequest.error_message_education_id")
),
address: Yup.string().required(
t("LoanRequest.error_message_address")
),
birthday: Yup.string().required(
t("LoanRequest.error_message_date_of_birth")
),
postal_code: Yup.mixed()
.test("max", `${t("LoanRequest.postal_code_max")}`, (value) => value.toString().length === 10)
.required(
t("LoanRequest.error_message_postal_code")
),
plate_part1: Yup.mixed().test("max", `${t("LoanRequest.error_message_plate_number")}`, (value) => value.toString().length === 2)
.required(
t("LoanRequest.error_message_plate_number")
),
plate_part2: Yup.mixed()
.required(
t("LoanRequest.error_message_plate_number")
),
plate_part3: Yup.mixed().test("max", `${t("LoanRequest.error_message_plate_number")}`, (value) => value.toString().length === 3)
.required(
t("LoanRequest.error_message_plate_number")
),
plate_part4: Yup.mixed().test("max", `${t("LoanRequest.error_message_plate_number")}`, (value) => value.toString().length === 2)
.required(
t("LoanRequest.error_message_plate_number")
),
navgan_id: Yup.mixed().required(t("LoanRequest.error_message_navgan_id")),
province_id: Yup.string().required(t("LoanRequest.error_message_province_id")),
national_code: Yup.string().required(
t("LoanRequest.error_message_national_code")
),
shenasname_id: Yup.string().required(
t("LoanRequest.error_message_shenasname_id")
),
city_id: Yup.string().required(t("LoanRequest.error_message_city_id")),
national_id: Yup.mixed()
.test("is-number", `${t("LoanRequest.national_code_number")}`,
(value) => !isNaN(value))
.test("positive", `${t("LoanRequest.national_code_positive")}`, (value) => value >= 0)
.test("max", `${t("LoanRequest.national_code_max")}`, (value) => value.toString().length === 10)
.required(t("LoanRequest.error_message_national_id")),
national_serial_number: Yup.string().required(t("LoanRequest.error_message_shenasname_serial")),
});
// end validation
// submit
const handleSubmit = async (values, props) => {
Notifications(t, undefined);
const formData = new FormData();
formData.append("name", values.name);
formData.append("phone_number", values.phone_number);
formData.append("vehicle_type", values.vehicle_type);
formData.append("plate_number", values.plate_number);
formData.append("province_id", values.province_id);
formData.append("navgan_id", values.navgan_id);
formData.append("national_code", values.national_code);
formData.append("shenasname_id", values.shenasname_id);
if (values.shenasname_img != null)
formData.append("shenasname_image", values.shenasname_img);
if (values.national_card_img != null)
formData.append("national_card_image", values.national_card_img);
props.setSubmitting(true)
await axios
.post(SEND_LOAN_REQUEST_NAVGAN, formData, {
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "multipart/form-data",
},
})
.then(function (response) {
Notifications(t, response);
})
.catch(function (error) {
Notifications(t, error.response);
props.setSubmitting(false);
});
await requestServer(SEND_LOAN_REQUEST_NAVGAN, "post", {
auth: true, notification: true, data: {
telephone_number: values.telephone_number,
vehicle_type: values.vehicle_type,
province_id: values.province_id,
city_id: values.city_id,
navgan_id: values.navgan_id,
national_id: values.national_id,
national_serial_number: values.national_serial_number,
postal_code: values.postal_code,
navgan_type: values.navgan_type,
plate_part1: values.plate_part1,
plate_part2: values.plate_part2,
plate_part3: values.plate_part3,
plate_part4: values.plate_part4,
address: values.address,
birthday: values.birthday.locale('en').format("YYYY-MM-DD"),
education_id: values.education_id,
occupation_id: values.occupation_id,
}
}).then(() => {
setFinishLoanRequest(true)
}).catch(() => {
}).finally(() => {
props.setSubmitting(false)
})
};
// end submit
const formik = useFormik({
initialValues, validationSchema, onSubmit: handleSubmit
})
return (
<Formik
initialValues={initialValues}
onSubmit={handleSubmit}
validationSchema={validationSchema}
>
{(props) => (
<Stack
spacing={2}
sx={{
p: 1,
width: "100%",
}}
>
<StyledForm sx={{width: "100%"}}>
<Box
sx={{
display: "flex",
flexDirection: {xs: "column", sm: "row"},
alignItems: "start",
justifyContent: "center",
width: "100%",
}}
<>
{finishLoanRequest ?
(
<CenterLayout>
<SvgDone width={200} height={200}/>
<Stack direction={'row'} spacing={0.7} sx={{mb: 4}}>
<Typography variant={'h5'}
align={'center'}>{t("LoanRequest.finish_loan_request")}</Typography>
<DoneIcon color={'success'}/>
</Stack>
<Button variant={'contained'} component={NextLinkComposed}
to={'/dashboard/navgan/followUp-loan'}>
{t("LoanRequest.back_to_dashboard")}
</Button>
</CenterLayout>
) : (
<>
<Grid container spacing={2} sx={{padding: 2}}
>
<Box
sx={{
mx: {xs: 0, sm: 2},
my: 2,
width: "100%",
}}
<Grid item xs={12} sm={4}
>
<Field
as={TextField}
<TextField
sx={{width: "100%"}}
name="name"
variant="outlined"
size="small"
label={t("LoanRequest.text_field_name")}
placeholder={t("LoanRequest.text_field_enter_your_name")}
type={"text"}
error={props.touched.name && props.errors.name ? true : false}
fullWidth
helperText={props.touched.name ? props.errors.name : null}
/>
</Box>
<Box
sx={{
mx: {xs: 0, sm: 2},
my: 2,
width: "100%",
}}
>
<Field
as={TextField}
sx={{width: "100%"}}
name="phone_number"
variant="outlined"
size="small"
label={t("LoanRequest.text_field_phone_number")}
placeholder={t(
"LoanRequest.text_field_enter_your_phone_number"
)}
type={"text"}
error={
props.touched.phone_number && props.errors.phone_number
? true
: false
}
fullWidth
helperText={
props.touched.phone_number
? props.errors.phone_number
: null
}
/>
</Box>
</Box>
<Box
sx={{
display: "flex",
flexDirection: {xs: "column", sm: "row"},
alignItems: "start",
justifyContent: "center",
width: "100%",
}}
>
<Box
sx={{
mx: {xs: 0, sm: 2},
my: 2,
width: "100%",
}}
>
<Field
as={TextField}
sx={{width: "100%"}}
name="vehicle_type"
variant="outlined"
size="small"
label={t("LoanRequest.text_field_vehicle_type")}
placeholder={t(
"LoanRequest.text_field_enter_your_vehicle_type"
)}
type={"text"}
error={
props.touched.vehicle_type && props.errors.vehicle_type
? true
: false
}
fullWidth
helperText={
props.touched.vehicle_type
? props.errors.vehicle_type
: null
}
/>
</Box>
<Box
sx={{
mx: {xs: 0, sm: 2},
my: 2,
width: "100%",
}}
>
<Field
as={TextField}
sx={{width: "100%"}}
name="plate_number"
variant="outlined"
size="small"
label={t("LoanRequest.text_field_plate_number")}
placeholder={t(
"LoanRequest.text_field_enter_your_plate_number"
)}
type={"text"}
error={
props.touched.plate_number && props.errors.plate_number
? true
: false
}
fullWidth
helperText={
props.touched.plate_number
? props.errors.plate_number
: null
}
/>
</Box>
<Box
sx={{
mx: {xs: 0, sm: 2},
my: 2,
width: "100%",
}}
>
<Field
name="province_id"
label={t("LoanRequest.text_field_province_id")} // t("LoanRequest.text_field_enter_your_province")
size="small"
selectType="province_id"
component={SelectBox}
selectors={provinceList}
select={props.values.province_id}
setFieldValue={props.setFieldValue}
setFieldTouched={props.setFieldTouched}
error={
props.touched.gender && props.errors.province_id ? true : false
}
helperText={
props.touched.gender ? props.errors.province_id : null
}
/>
</Box>
</Box>
<Box
sx={{
display: "flex",
flexDirection: {xs: "column", sm: "row"},
alignItems: "start",
justifyContent: "center",
width: "100%",
}}
>
<Box
sx={{
mx: {xs: 0, sm: 2},
my: 2,
width: "100%",
}}
>
<Field
as={TextField}
sx={{width: "100%"}}
name="navgan_id"
variant="outlined"
size="small"
label={t("LoanRequest.text_field_navgan_id")}
placeholder={t("LoanRequest.text_field_enter_your_navgan_id")}
type={"text"}
error={
props.touched.navgan_id && props.errors.navgan_id
? true
: false
}
fullWidth
helperText={
props.touched.navgan_id ? props.errors.navgan_id : null
}
/>
</Box>
<Box
sx={{
mx: {xs: 0, sm: 2},
my: 2,
width: "100%",
}}
>
<Field
as={TextField}
sx={{width: "100%"}}
name="national_code"
name="national_id"
variant="outlined"
size="small"
inputProps={{inputMode: 'numeric', pattern: '[0-9]*'}}
type={'tel'}
onChange={formik.handleChange}
label={t("LoanRequest.text_field_national_code")}
placeholder={t(
"LoanRequest.text_field_enter_your_national_code"
)}
type={"text"}
error={
props.touched.national_code && props.errors.national_code
? true
: false
}
onBlur={formik.handleBlur("national_id")}
error={formik.touched.national_id && Boolean(formik.errors.national_id)}
helperText={formik.touched.national_id && formik.errors.national_id}
fullWidth
helperText={
props.touched.national_code
? props.errors.national_code
: null
}
/>
</Box>
<Box
sx={{
mx: {xs: 0, sm: 2},
my: 2,
width: "100%",
}}
</Grid>
<Grid item xs={12} sm={4}
>
<Field
as={TextField}
<TextField
sx={{width: "100%"}}
name="shenasname_id"
name="national_serial_number"
variant="outlined"
size="small"
label={t("LoanRequest.text_field_shenasname_id")}
label={t("LoanRequest.text_field_shenasname_serial")}
placeholder={t(
"LoanRequest.text_field_enter_your_shenasname_id"
)}
type={"text"}
error={
props.touched.shenasname_id && props.errors.shenasname_id
? true
: false
}
"LoanRequest.text_field_enter_your_shenasname_serial")}
fullWidth
helperText={
props.touched.shenasname_id
? props.errors.shenasname_id
: null
}
onChange={formik.handleChange}
onBlur={formik.handleBlur("national_serial_number")}
error={formik.touched.national_serial_number && Boolean(formik.errors.national_serial_number)}
helperText={formik.touched.national_serial_number && formik.errors.national_serial_number}
/>
</Box>
</Box>
<Box
sx={{
display: "flex",
flexDirection: {xs: "column", sm: "row"},
alignItems: "center",
justifyContent: "space-around",
width: "100%",
}}
>
<UploadSystem
selectedImage={selectedImageShenasname}
handleUploadChange={(e) =>
handleUploadChangeShenasname(e, props.setFieldValue)
}
setselectedImage={setSelectedImageShenasname}
setFieldValue={props.setFieldValue}
fieldname="shenasname_img"
fileType={fileTypeShenasname}
fileName={fileNameShenasname}
imageAlt={t("app_name")}
imageSize={[300 /*width*/, 150 /*height*/]}
label={t("LoanRequest.file_field_shenasname_image")}
/>
<UploadSystem
selectedImage={selectedImageNationalCard}
handleUploadChange={(e) =>
handleUploadChangeNationalCard(e, props.setFieldValue)
}
setselectedImage={setSelectedImageNationalCard}
setFieldValue={props.setFieldValue}
fieldname="national_card_img"
fileType={fileTypeNationalCard}
fileName={fileNameNationalCard}
imageAlt={t("app_name")}
imageSize={[300 /*width*/, 150 /*height*/]}
label={t("LoanRequest.file_field_national_card_image")}
/>
</Box>
</Grid>
<Grid item xs={12} sm={4}>
<MuiDatePicker
formik={formik}
error={formik.touched.birthday && Boolean(formik.errors.birthday)}
helperText={formik.touched.birthday && formik.errors.birthday}
onBlur={formik.handleBlur("birthday")}
/>
</Grid>
</Grid>
<Grid container spacing={2} sx={{padding: 2}}>
<Grid item xs={12} sm={6}>
<TextField
sx={{width: "100%"}}
name="postal_code"
variant="outlined"
inputProps={{inputMode: 'numeric', pattern: '[0-9]*'}}
type={'tel'}
onChange={formik.handleChange}
size="small"
label={t("LoanRequest.text_field_postal_code")}
placeholder={t("LoanRequest.text_field_enter_your_postal_code")}
fullWidth
onBlur={formik.handleBlur("postal_code")}
error={formik.touched.postal_code && Boolean(formik.errors.postal_code)}
helperText={formik.touched.postal_code && formik.errors.postal_code}
/>
</Grid>
<Grid item xs={12} sm={6}>
<TextField
sx={{width: "100%"}}
name="telephone_number"
variant="outlined"
value={formik.values.telephone_number}
inputProps={{inputMode: 'numeric', pattern: '[0-9]*'}}
type={'tel'}
size="small"
onChange={(event) => {
const inputValue = event.target.value;
if (isNaN(Number(inputValue))) {
return;
}
formik.handleChange({
target: {
name: "telephone_number",
value: inputValue,
},
});
}}
label={t("LoanRequest.text_field_tel_number")}
placeholder={t(
"LoanRequest.text_field_enter_your_tel_number"
)}
fullWidth
onBlur={formik.handleBlur("telephone_number")}
error={formik.touched.telephone_number && Boolean(formik.errors.telephone_number)}
helperText={formik.touched.telephone_number && formik.errors.telephone_number}
/>
</Grid>
</Grid>
<Grid container spacing={2} sx={{padding: 2}}>
<Grid item xs={12} sm={6}>
<SelectBox
name="navgan_type"
label={t("LoanRequest.text_field_navgan_type")}
size="small"
selectType="navgan_type"
selectors={[
{
id: 1,
value: "روستایی",
name: "روستایی"
},
{
id: 2,
value: "عمومی",
name: "عمومی"
},
{
id: 3,
value: "منطقه آزاد",
name: "منطقه آزاد"
}
]}
schema={{value: 'value', name: 'name'}}
select={formik.values.navgan_type}
handleChange={(event) => {
formik.setFieldValue('navgan_type', event.target.value)
}}
setFieldTouched={formik.setFieldTouched}
error={formik.touched.navgan_type && Boolean(formik.errors.navgan_type)}
helperText={formik.touched.navgan_type && formik.errors.navgan_type}
onBlur={formik.handleBlur("navgan_type")}
/>
</Grid>
<Grid item xs={12} sm={6}>
<TextField
sx={{width: "100%"}}
name="navgan_id"
variant="outlined"
inputProps={{inputMode: 'numeric', pattern: '[0-9]*'}}
type={'tel'}
onChange={formik.handleChange}
size="small"
label={t("LoanRequest.text_field_navgan_id")}
placeholder={t("LoanRequest.text_field_enter_your_navgan_id")}
fullWidth
onBlur={formik.handleBlur("navgan_id")}
error={formik.touched.navgan_id && Boolean(formik.errors.navgan_id)}
helperText={formik.touched.navgan_id && formik.errors.navgan_id}
/>
</Grid>
</Grid>
<Grid container spacing={2} sx={{padding: 2}}>
<Grid item xs={12} sm={6}>
<SelectBox
name="province_id"
label={t("LoanRequest.text_field_province_id")}
size="small"
selectType="province_id"
isLoading={isLoadingProvinceList}
errorEcured={errorProvinceList}
selectors={provinceList}
schema={{value: 'value', name: 'name'}}
select={formik.values.province_id}
handleChange={(event) => {
formik.setFieldValue('province_id', event.target.value);
setProvinceID(event.target.value)
formik.setFieldTouched("city_id", false, false);
formik.setFieldValue("city_id", "")
}}
setFieldTouched={formik.setFieldTouched}
error={formik.touched.province_id && Boolean(formik.errors.province_id)}
helperText={formik.touched.province_id && formik.errors.province_id}
onBlur={formik.handleBlur("province_id")}
/>
</Grid>
<Grid item xs={12} sm={6}>
<SelectBox
name="city_id"
label={isLoadingCityList ? `${t("LoanRequest.cityList_loading")}` : (cityList.length === 0 ? `${t("LoanRequest.cityList_empty")}` : cityTextField)}
size="small"
selectType="city_id"
schema={{value: 'value', name: 'name'}}
disabled={cityList.length === 0}
selectors={cityList}
select={formik.values.city_id}
handleChange={(event) => formik.setFieldValue('city_id', event.target.value)}
setFieldTouched={formik.setFieldTouched}
error={formik.touched.city_id && Boolean(formik.errors.city_id)}
helperText={formik.touched.city_id && formik.errors.city_id}
onBlur={formik.handleBlur("city_id")}
/>
</Grid>
</Grid>
<Grid container spacing={2} sx={{padding: 2}}>
<Grid item xs={12} sm={6}>
<SelectBox
name="vehicle_type"
label={t("LoanRequest.text_field_vehicle_type")}
size="small"
schema={{value: 'value', name: 'name'}}
selectType="vehicle_type"
selectors={[
{
id: 0,
value: "اتوبوس",
name: "اتوبوس"
},
{
id: 1,
value: "مینی بوس",
name: "مینی بوس"
},
{
id: 2,
value: "سواری",
name: "سواری"
}
]}
select={formik.values.vehicle_type}
handleChange={(event) => formik.setFieldValue('vehicle_type', event.target.value)}
error={formik.touched.vehicle_type && Boolean(formik.errors.vehicle_type)}
helperText={formik.touched.vehicle_type && formik.errors.vehicle_type}
onBlur={formik.handleBlur("vehicle_type")}
/>
</Grid>
<Grid item xs={12} sm={6}>
<PlateNumber formik={formik}/>
</Grid>
</Grid>
<Grid container spacing={2} sx={{padding: 2}}>
<Grid item xs={12} sm={6}>
<SelectBox
name="education_id"
label={t("LoanRequest.text_field_education_id")}
size="small"
isLoading={isLoadingEducationsList}
errorEcured={errorEducationsList}
selectType="education_id"
selectors={educationsList}
select={formik.values.education_id}
schema={{value: 'id', name: 'title'}}
handleChange={(event) => {
formik.setFieldValue('education_id', event.target.value)
}}
setFieldTouched={formik.setFieldTouched}
error={formik.touched.education_id && Boolean(formik.errors.education_id)}
helperText={formik.touched.education_id && formik.errors.education_id}
onBlur={formik.handleBlur("education_id")}
/>
</Grid>
<Grid item xs={12} sm={6}>
<SelectBox
name="occupation_id"
label={t("LoanRequest.text_field_occupation_id")}
size="small"
isLoading={isLoadingOccupationsList}
errorEcured={errorOccupationsList}
selectType="occupation_id"
schema={{value: 'id', name: 'title'}}
selectors={occupationsList}
select={formik.values.occupation_id}
handleChange={(event) => {
formik.setFieldValue('occupation_id', event.target.value)
}}
setFieldTouched={formik.setFieldTouched}
error={formik.touched.occupation_id && Boolean(formik.errors.occupation_id)}
helperText={formik.touched.occupation_id && formik.errors.occupation_id}
onBlur={formik.handleBlur("occupation_id")}
/>
</Grid>
</Grid>
<Grid container spacing={2} sx={{padding: 2}}>
<Grid item xs={12}>
<TextField
multiline
rows={8}
sx={{width: "100%"}}
name="address"
variant="outlined"
size="small"
label={t("LoanRequest.text_field_address")}
placeholder={t("LoanRequest.text_field_enter_your_address")}
fullWidth
onChange={formik.handleChange}
onBlur={formik.handleBlur("address")}
error={formik.touched.address && Boolean(formik.errors.address)}
helperText={formik.touched.address && formik.errors.address}
/>
</Grid>
</Grid>
<Grid container spacing={2} sx={{padding: 2}}>
<Grid item xs={12}>
<FormControlLabel
color={'error.main'}
control={
<Checkbox
checked={formik.values.checkedBox}
onChange={(event) => {
formik.setFieldValue("checkedBox", event.target.checked)
}}
/>
}
label={`${t("LoanRequest.checkbox")}`}/>
</Grid>
</Grid>
<Box
sx={{
display: "flex",
@@ -469,20 +477,23 @@ const AddFormComponent = () => {
>
<Button
fullWidth
onClick={formik.handleSubmit}
type="submit"
variant="contained"
size="large"
sx={{mt: 2}}
sx={{my: 4}}
endIcon={<DataSaverOnIcon/>}
disabled={props.isSubmitting ? true : false}
disabled={formik.values.checkedBox ? (formik.isSubmitting || !formik.dirty || !formik.isValid) : true}
>
{t("LoanRequest.button_submit")}
{formik.isSubmitting
? t("LoanRequest.button_while_submit")
: t("LoanRequest.button_submit")}
</Button>
</Box>
</StyledForm>
</Stack>
)}
</Formik>
</>
)
}
</>
);
};

View File

@@ -1,29 +1,21 @@
import CenterLayout from "@/layouts/CenterLayout";
import DashboardLayouts from "@/layouts/dashboardLayouts";
import useUser from "@/lib/app/hooks/useUser";
import {Box, Chip, Divider} from "@mui/material";
import {useTranslations} from "next-intl";
import AddFormComponent from "@/components/dashboard/navgan/add-request-loan/forms/AddForm";
const LoanRequestComponent = () => {
const t = useTranslations();
const {user} = useUser();
return (
<DashboardLayouts>
<CenterLayout>
<Box sx={{width: "80%", my: 2}}>
<>
<Box>
<Divider>
<Chip
label={`${t("LoanRequest.loan_request_page")} | ${
user.type_name
}`}
label={`${t("LoanRequest.loan_request_page")} | ${t("user_navy")} `}
/>
</Divider>
</Box>
<AddFormComponent/>
</CenterLayout>
</DashboardLayouts>
</>
);
};

View File

@@ -1,25 +1,50 @@
import {Card, CardContent, CardHeader, Grid, Typography} from "@mui/material";
import SellIcon from '@mui/icons-material/Sell';
import {Box, Button, Card, CardContent, Grid, Typography} from "@mui/material";
import {useTranslations} from "next-intl";
import moment from "jalali-moment";
import {LinkRouting} from "@witel/webapp-builder";
import AccessTimeFilledIcon from '@mui/icons-material/AccessTimeFilled';
import CheckCircleIcon from '@mui/icons-material/CheckCircle';
import CancelIcon from '@mui/icons-material/Cancel';
const RequestCard = ({item}) => {
const t = useTranslations();
return (
<Grid item xs={12} md={6} lg={4}>
<Grid item xs={12} lg={6} xl={4}>
<Card sx={{
width: "100%",
boxShadow: "rgba(0, 0, 0, 0.15) 0px 5px 15px 0px"
}}>
<CardHeader
avatar={<SellIcon color="primary"/>}
title={`${t("LoanFollowUp.request_number")}: (${item.id})`}
subheader={item.latest_history_created_at}
/>
<CardContent sx={{pt: 0}}>
<Typography variant="button">
{item.loan_state}
<CardContent sx={{display: 'flex', flexDirection: 'column', alignItems: 'flex-start' , paddingBottom: '16px !important'}}>
<Box
sx={{
borderLeft: '6px solid',
borderColor: 'primary.main',
borderRadius: '6px 0px 0px 6px',
paddingLeft: '10px',
}}
>
<Typography variant="h6" sx={{lineHeight: 2 , fontWeight:'bold'}}>
{`${t("LoanFollowUp.loan_unique_code")}: ${item.id}`}
</Typography>
</Box>
<Typography variant="body1" sx={{lineHeight: 2 , paddingLeft: '20px'}}>
{`${t("LoanFollowUp.loan_date")}: ${moment(item.created_at).locale("fa").format("HH:mm | YYYY/MM/DD")}`}
</Typography>
<Typography variant="body1" sx={{lineHeight: 2 , paddingLeft: '20px'}}>
{`${t("LoanFollowUp.loan_bank_branch")}: ${item.branch_info === null ? '-' : item.branch_info}`}
</Typography>
<Typography variant="body1" sx={{lineHeight: 2 , paddingLeft: '20px'}}>
{`${t("LoanFollowUp.loan_status")}: `}
{item.loan_state}
{item.state_id >= 1 && item.state_id <= 6 && <AccessTimeFilledIcon sx={{verticalAlign: 'middle', marginLeft: '4px' , color: '#48a4df'}}/>}
{item.state_id === 8 && <CheckCircleIcon sx={{verticalAlign: 'middle', marginLeft: '4px', color: 'green'}}/>}
{item.state_id === 7 && <CancelIcon sx={{verticalAlign: 'middle', marginLeft: '4px', color: 'red'}}/>}
</Typography>
<Button variant="contained" color="primary" sx={{alignSelf: 'flex-end'}}>
<LinkRouting underline="none" color="inherit" href={`/dashboard/navgan/${item.id}/show`}>
{t("LoanFollowUp.loan_details")}
</LinkRouting>
</Button>
</CardContent>
</Card>
</Grid>

View File

@@ -1,59 +1,44 @@
import DashboardLayouts from "@/layouts/dashboardLayouts";
import {
Grid,
Stack,
} from "@mui/material";
import { Grid } from "@mui/material";
import {useEffect, useState} from "react";
import {SHOW_LOAN_REQUEST_NAVGAN} from "@/core/data/apiRoutes";
import useLoading from "@/lib/app/hooks/useLoading";
import useRequest from "@/lib/app/hooks/useRequest";
import moment from "jalali-moment";
import RequestCard from "@/components/dashboard/navgan/followUp-loan/RequestCard";
import {FullPageLayout , useRequest} from "@witel/webapp-builder";
const LoanFollowUpComponent = () => {
const requestServer = useRequest();
const {setLoadingPage} = useLoading();
const [isLoading, setIsLoading] = useState(false)
const [requestsList, setRequestsList] = useState([]);
// get form data
useEffect(() => {
setLoadingPage(true)
requestServer(SHOW_LOAN_REQUEST_NAVGAN, "get", {auth: true, notification: false})
setIsLoading(true)
requestServer(SHOW_LOAN_REQUEST_NAVGAN, "get", {
auth: true,
pending:false,
success:{notification:{show:false}}
})
.then(function ({data}) {
const items = data.data;
const formattedData = items.map((item, index) => ({
id: item.id,
latest_history_created_at: moment(item.latest_history_created_at).locale("fa").format("HH:mm | YYYY/MM/DD"),
state_id: item.state_id,
loan_state: item.loan_state,
}));
setRequestsList(formattedData);
setLoadingPage(false)
setRequestsList(items);
setIsLoading(false)
})
.catch(function (error) {
})
}, []);
return (
<DashboardLayouts>
<Stack
spacing={2}
sx={{
p: 4,
width: "100%",
}}
<FullPageLayout>
<Grid
container
columnSpacing={2}
rowSpacing={2}
sx={{alignItems: "start", justifyContent: "center", padding: "24px"}}
>
<Grid
container
columnSpacing={2}
rowSpacing={2}
sx={{alignItems: "start", justifyContent: "center"}}
>
{requestsList.map((item, index) => (
<RequestCard item={item} key={item.id}/>
))}
</Grid>
</Stack>
</DashboardLayouts>
{requestsList.map((item) => (
<RequestCard item={item} key={item.id}/>
))}
</Grid>
</FullPageLayout>
);
};

View File

@@ -1,32 +0,0 @@
import {Button, Typography} from "@mui/material";
import {useTranslations} from "next-intl";
import CenterLayout from "@/layouts/CenterLayout";
import {useRouter} from "next/router";
import {NextLinkComposed} from "@/core/components/LinkRouting";
const AccessibilityComponent = () => {
const t = useTranslations();
const router = useRouter();
return (
<CenterLayout>
<Typography
sx={{
fontSize: "16px",
fontWeight: 600,
my: 2
}}>
{t("LoanRequest.add_loan_request_permission")}
</Typography>
<Button color="primary" variant="contained"
component={NextLinkComposed}
to={{
pathname: "/dashboard/refahi/followUp-loan",
}}>
{t("LoanFollowUp.loan_follow_up_page")}
</Button>
</CenterLayout>
);
};
export default AccessibilityComponent;

View File

@@ -1,439 +0,0 @@
import StyledForm from "@/core/components/StyledForm";
import UploadSystem from "@/core/components/UploadSystem";
import {SEND_LOAN_REQUEST_WELFARE} from "@/core/data/apiRoutes";
import DataSaverOnIcon from "@mui/icons-material/DataSaverOn";
import {
Box,
Button,
Chip,
Divider,
FormControl,
FormHelperText,
InputLabel,
MenuItem,
Select,
Stack,
TextField
} from "@mui/material";
import {Field, Formik} from "formik";
import {useTranslations} from "next-intl";
import * as Yup from "yup";
import useRequest from "@/lib/app/hooks/useRequest";
import useProvince from "@/lib/app/hooks/useProvince";
import {useRouter} from "next/router";
const AddFormComponent = () => {
const t = useTranslations();
const requestServer = useRequest()
const {provinceList, isLoadingProvinceList, errorProvinceList} = useProvince();
const router = useRouter();
// initial values, validation and request action of form
const initialValues = {
name: "",
phone_number: "",
province_id: "",
national_id: "",
shenasname_id: "",
sherkat_naft_doc: null,
estate_doc: null,
agreement_doc: null,
national_card_image: null,
shenasname_image: null,
payan_khedmat_image: null,
};
const validationSchema = Yup.object().shape({
name: Yup.string().required(t("LoanRequest.error_message_name")),
phone_number: Yup.string().required(
t("LoanRequest.error_message_phone_number")
),
province_id: Yup.string().required(t("LoanRequest.error_message_province_id")),
national_id: Yup.string().required(
t("LoanRequest.error_message_national_id")
),
shenasname_id: Yup.string().required(
t("LoanRequest.error_message_shenasname_id")
),
sherkat_naft_doc: Yup.mixed().required(t("LoanRequest.error_message_sherkat_naft_doc")),
estate_doc: Yup.mixed().required(t("LoanRequest.error_message_estate_doc")),
agreement_doc: Yup.mixed().required(t("LoanRequest.error_message_agreement_doc")),
national_card_image: Yup.mixed().required(t("LoanRequest.error_message_national_card_image")),
shenasname_image: Yup.mixed().required(t("LoanRequest.error_message_shenasname_image")),
payan_khedmat_image: Yup.mixed().required(t("LoanRequest.error_message_payan_khedmat_image")),
});
const handleSubmit = (values, props) => {
const formData = new FormData();
formData.append("name", values.name);
formData.append("phone_number", values.phone_number);
formData.append("province_id", values.province_id);
formData.append("national_id", values.national_id);
formData.append("shenasname_id", values.shenasname_id);
if (values.sherkat_naft_doc != null)
formData.append("sherkat_naft_doc", values.sherkat_naft_doc);
if (values.estate_doc != null)
formData.append("estate_doc", values.estate_doc);
if (values.agreement_doc != null)
formData.append("agreement_doc", values.agreement_doc);
if (values.shenasname_image != null)
formData.append("shenasname_image", values.shenasname_image);
if (values.national_card_image != null)
formData.append("national_card_image", values.national_card_image);
if (values.payan_khedmat_image != null)
formData.append("payan_khedmat_image", values.payan_khedmat_image);
requestServer(SEND_LOAN_REQUEST_WELFARE, "post", {auth: true, data: formData})
.then(function (response) {
router.replace('/dashboard/refahi/followUp-loan');
})
.catch(function (error) {
})
.finally(function () {
props.setSubmitting(false);
});
};
// end initial values, validation and request action of form
return (
<Formik
initialValues={initialValues}
onSubmit={handleSubmit}
validationSchema={validationSchema}
>
{(props) => (
<Stack
spacing={2}
sx={{
p: 1,
width: "100%",
}}
>
<StyledForm sx={{width: "100%"}}>
<Box sx={{my: 1}}>
<Divider>
<Chip
label={t("LoanRequest.personal_info")}
/>
</Divider>
</Box>
<Box
sx={{
display: "flex",
flexDirection: {xs: "column", sm: "row"},
alignItems: "start",
justifyContent: "center",
width: "100%",
}}
>
<Box
sx={{
mx: {xs: 0, sm: 2},
my: 2,
width: "100%",
}}
>
<Field
as={TextField}
sx={{width: "100%"}}
name="name"
variant="outlined"
size="small"
label={t("LoanRequest.text_field_name")}
placeholder={t("LoanRequest.text_field_enter_your_name")}
type={"text"}
error={!!(props.touched.name && props.errors.name)}
fullWidth
helperText={props.touched.name ? props.errors.name : null}
/>
</Box>
<Box
sx={{
mx: {xs: 0, sm: 2},
my: 2,
width: "100%",
}}
>
<Field
as={TextField}
sx={{width: "100%"}}
name="phone_number"
variant="outlined"
size="small"
label={t("LoanRequest.text_field_phone_number")}
placeholder={t(
"LoanRequest.text_field_enter_your_phone_number"
)}
type={"text"}
error={
!!(props.touched.phone_number && props.errors.phone_number)
}
fullWidth
helperText={
props.touched.phone_number
? props.errors.phone_number
: null
}
/>
</Box>
<Box
sx={{
mx: {xs: 0, sm: 2},
mt: 0,
width: "100%",
}}
>
<FormControl
error={props.touched.province_id && !!props.errors.province_id}
sx={{width: "100%", mt: 2}}
size="small"
>
<InputLabel>{t("LoanRequest.text_field_province_id")}</InputLabel>
<Select
name="province_id"
disabled={isLoadingProvinceList || errorProvinceList}
label={t("LoanRequest.text_field_province_id")}
value={props.values.province_id}
onChange={(e) => {
props.setFieldValue("province_id", e.target.value);
}}
onBlur={props.handleBlur}
fullWidth
variant="outlined"
>
{isLoadingProvinceList ? (
<MenuItem>
{t("LoanRequest.text_field_loading_provinces_list")}
</MenuItem>
) : errorProvinceList ? (
<MenuItem>
{t("LoanRequest.text_field_error_fetching_provinces")}
</MenuItem>
) : (
provinceList.map((item) => (
<MenuItem key={item.id} value={item.id}>
{item.name}
</MenuItem>
))
)}
</Select>
<FormHelperText>
{props.touched.province_id && props.errors.province_id ? props.errors.province_id : ""}
</FormHelperText>
</FormControl>
</Box>
</Box>
<Box
sx={{
display: "flex",
flexDirection: {xs: "column", sm: "row"},
alignItems: "start",
justifyContent: "center",
width: "100%",
}}
>
<Box
sx={{
mx: {xs: 0, sm: 2},
my: 2,
width: "100%",
}}
>
<Field
as={TextField}
sx={{width: "100%"}}
name="national_id"
variant="outlined"
size="small"
label={t("LoanRequest.text_field_national_id")}
placeholder={t(
"LoanRequest.text_field_enter_your_national_id"
)}
type={"text"}
error={
!!(props.touched.national_id && props.errors.national_id)
}
fullWidth
helperText={
props.touched.national_id
? props.errors.national_id
: null
}
/>
</Box>
<Box
sx={{
mx: {xs: 0, sm: 2},
my: 2,
width: "100%",
}}
>
<Field
as={TextField}
sx={{width: "100%"}}
name="shenasname_id"
variant="outlined"
size="small"
label={t("LoanRequest.text_field_shenasname_id")}
placeholder={t(
"LoanRequest.text_field_enter_your_shenasname_id"
)}
type={"text"}
error={
!!(props.touched.shenasname_id && props.errors.shenasname_id)
}
fullWidth
helperText={
props.touched.shenasname_id
? props.errors.shenasname_id
: null
}
/>
</Box>
</Box>
<Box sx={{my: 1}}>
<Divider>
<Chip
label={t("LoanRequest.uploading_documents")}
/>
</Divider>
</Box>
<Stack spacing={{xs: 1, sm: 2}} direction="row" useFlexGap flexWrap="wrap"
sx={{justifyContent: "center"}}>
<Box sx={{
mx: {xs: 0, sm: 1},
maxWidth: "200px"
}}>
<UploadSystem
setFieldValue={props.setFieldValue}
fieldName="sherkat_naft_doc"
imageAlt={t("app_name")}
label={t("LoanRequest.file_field_sherkat_naft_doc")}
enableDelete={false}
error={
!!(props.touched.sherkat_naft_doc && props.errors.sherkat_naft_doc)
}
helperText={
props.touched.sherkat_naft_doc ? props.errors.sherkat_naft_doc : null
}
/>
</Box>
<Box sx={{
mx: {xs: 0, sm: 1},
maxWidth: "200px"
}}>
<UploadSystem
setFieldValue={props.setFieldValue}
fieldName="estate_doc"
imageAlt={t("app_name")}
label={t("LoanRequest.file_field_estate_doc")}
enableDelete={false}
error={
!!(props.touched.estate_doc && props.errors.estate_doc)
}
helperText={
props.touched.estate_doc ? props.errors.estate_doc : null
}
/>
</Box>
<Box sx={{
mx: {xs: 0, sm: 1},
maxWidth: "200px"
}}>
<UploadSystem
setFieldValue={props.setFieldValue}
fieldName="agreement_doc"
imageAlt={t("app_name")}
label={t("LoanRequest.file_field_agreement_doc")}
enableDelete={false}
error={
!!(props.touched.agreement_doc && props.errors.agreement_doc)
}
helperText={
props.touched.agreement_doc ? props.errors.agreement_doc : null
}
/>
</Box>
<Box sx={{
mx: {xs: 0, sm: 1},
maxWidth: "200px"
}}>
<UploadSystem
setFieldValue={props.setFieldValue}
fieldName="shenasname_image"
imageAlt={t("app_name")}
label={t("LoanRequest.file_field_shenasname_image")}
enableDelete={false}
error={
!!(props.touched.shenasname_image && props.errors.shenasname_image)
}
helperText={
props.touched.shenasname_image ? props.errors.shenasname_image : null
}
/>
</Box>
<Box sx={{
mx: {xs: 0, sm: 1},
maxWidth: "200px"
}}>
<UploadSystem
setFieldValue={props.setFieldValue}
fieldName="payan_khedmat_image"
imageAlt={t("app_name")}
label={t("LoanRequest.file_field_payan_khedmat_image")}
enableDelete={false}
error={
!!(props.touched.payan_khedmat_image && props.errors.payan_khedmat_image)
}
helperText={
props.touched.payan_khedmat_image ? props.errors.payan_khedmat_image : null
}
/>
</Box>
<Box sx={{
mx: {xs: 0, sm: 1},
maxWidth: "200px"
}}>
<UploadSystem
setFieldValue={props.setFieldValue}
fieldName="national_card_image"
imageAlt={t("app_name")}
label={t("LoanRequest.file_field_national_card_image")}
enableDelete={false}
error={
!!(props.touched.national_card_image && props.errors.national_card_image)
}
helperText={
props.touched.national_card_image ? props.errors.national_card_image : null
}
/>
</Box>
</Stack>
<Box
sx={{
display: "flex",
flexDirection: {xs: "column", sm: "row"},
alignItems: "center",
mx: "auto",
my: 2,
width: {xs: "100%", sm: "30%", md: "25%"},
}}
>
<Button
fullWidth
type="submit"
variant="contained"
size="large"
sx={{mt: 2}}
endIcon={<DataSaverOnIcon/>}
disabled={props.isSubmitting}
>
{t("LoanRequest.button_submit")}
</Button>
</Box>
</StyledForm>
</Stack>
)}
</Formik>
);
};
export default AddFormComponent;

View File

@@ -1,17 +0,0 @@
import DashboardLayouts from "@/layouts/dashboardLayouts";
import useUser from "@/lib/app/hooks/useUser";
import {useTranslations} from "next-intl";
import AddFormComponent from "@/components/dashboard/refahi/add-request-loan/forms/AddForm";
import AccessibilityComponent from "@/components/dashboard/refahi/add-request-loan/accessibility";
const LoanRequestComponent = () => {
const t = useTranslations();
const {user} = useUser();
return (
<DashboardLayouts>
{user.can_request_a_new_loan ? <AddFormComponent/> : <AccessibilityComponent/>}
</DashboardLayouts>
);
};
export default LoanRequestComponent;

View File

@@ -1,397 +0,0 @@
import StyledForm from "@/core/components/StyledForm";
import UploadSystem from "@/core/components/UploadSystem";
import {UPDATE_LOAN_REQUEST_WELFARE} from "@/core/data/apiRoutes";
import useDirection from "@/lib/app/hooks/useDirection";
import useUser from "@/lib/app/hooks/useUser";
import DataSaverOnIcon from "@mui/icons-material/DataSaverOn";
import {Box, Button, Chip, Divider, Stack, TextField} from "@mui/material";
import {Field, Formik} from "formik";
import {useTranslations} from "next-intl";
import * as Yup from "yup";
import SelectBox from "@/core/components/SelectBox";
import useRequest from "@/lib/app/hooks/useRequest";
import {useRouter} from "next/router";
import useLoading from "@/lib/app/hooks/useLoading";
const EditFormComponent = ({provinceList, initialValues}) => {
const t = useTranslations();
const requestServer = useRequest();
const router = useRouter();
// validation and request action of form
const validationSchema = Yup.object().shape({
name: Yup.string().required(t("LoanRequest.error_message_name")),
phone_number: Yup.string().required(
t("LoanRequest.error_message_phone_number")
),
province_id: Yup.string().required(t("LoanRequest.error_message_province_id")),
national_id: Yup.string().required(t("LoanRequest.error_message_national_id")),
shenasname_id: Yup.string().required(t("LoanRequest.error_message_shenasname_id")),
sherkat_naft_doc: Yup.mixed().required(t("LoanRequest.error_message_sherkat_naft_doc")),
estate_doc: Yup.mixed().required(t("LoanRequest.error_message_estate_doc")),
agreement_doc: Yup.mixed().required(t("LoanRequest.error_message_agreement_doc")),
national_card_image: Yup.mixed().required(t("LoanRequest.error_message_national_card_image")),
shenasname_image: Yup.mixed().required(t("LoanRequest.error_message_shenasname_image")),
payan_khedmat_image: Yup.mixed().required(t("LoanRequest.error_message_payan_khedmat_image")),
});
const handleSubmit = (values, props) => {
const formData = new FormData();
formData.append("name", values.name);
formData.append("phone_number", values.phone_number);
formData.append("province_id", values.province_id);
formData.append("national_id", values.national_id);
formData.append("shenasname_id", values.shenasname_id);
if (values.sherkat_naft_doc != initialValues.sherkat_naft_doc)
formData.append("sherkat_naft_doc", values.sherkat_naft_doc);
if (values.estate_doc != initialValues.estate_doc)
formData.append("estate_doc", values.estate_doc);
if (values.agreement_doc != initialValues.agreement_doc)
formData.append("agreement_doc", values.agreement_doc);
if (values.shenasname_image != initialValues.shenasname_image)
formData.append("shenasname_image", values.shenasname_image);
if (values.national_card_image != initialValues.national_card_image)
formData.append("national_card_image", values.national_card_image);
if (values.payan_khedmat_image != initialValues.payan_khedmat_image)
formData.append("payan_khedmat_image", values.payan_khedmat_image);
requestServer(UPDATE_LOAN_REQUEST_WELFARE + router.query.id, "post", {auth: true, data: formData})
.then(function (response) {
})
.catch(function (error) {
})
.finally(function () {
props.setSubmitting(false);
});
};
if (!initialValues) return "";
return (
<Formik
initialValues={initialValues}
onSubmit={handleSubmit}
validationSchema={validationSchema}
>
{(props) => (
<Stack
spacing={2}
sx={{
p: 1,
width: "100%",
}}
>
<StyledForm sx={{width: "100%"}}>
<Box sx={{my: 1}}>
<Divider>
<Chip
label={t("LoanRequest.personal_info")}
/>
</Divider>
</Box>
<Box
sx={{
display: "flex",
flexDirection: {xs: "column", sm: "row"},
alignItems: "start",
justifyContent: "center",
width: "100%",
}}
>
<Box
sx={{
mx: {xs: 0, sm: 2},
my: 2,
width: "100%",
}}
>
<Field
as={TextField}
sx={{width: "100%"}}
name="name"
variant="outlined"
size="small"
label={t("LoanRequest.text_field_name")}
placeholder={t("LoanRequest.text_field_enter_your_name")}
type={"text"}
error={!!(props.touched.name && props.errors.name)}
fullWidth
helperText={props.touched.name ? props.errors.name : null}
/>
</Box>
<Box
sx={{
mx: {xs: 0, sm: 2},
my: 2,
width: "100%",
}}
>
<Field
as={TextField}
sx={{width: "100%"}}
name="phone_number"
variant="outlined"
size="small"
label={t("LoanRequest.text_field_phone_number")}
placeholder={t(
"LoanRequest.text_field_enter_your_phone_number"
)}
type={"text"}
error={
!!(props.touched.phone_number && props.errors.phone_number)
}
fullWidth
helperText={
props.touched.phone_number
? props.errors.phone_number
: null
}
/>
</Box>
<Box
sx={{
mx: {xs: 0, sm: 2},
my: 2,
width: "100%",
}}
>
<Field
name="province_id"
label={t("LoanRequest.text_field_province_id")} // t("LoanRequest.text_field_enter_your_province")
size="small"
selectType="province_id"
component={SelectBox}
selectors={provinceList}
select={props.values.province_id}
setFieldValue={props.setFieldValue}
setFieldTouched={props.setFieldTouched}
error={
!!(props.touched.province_id && props.errors.province_id)
}
helperText={
props.touched.province_id ? props.errors.province_id : null
}
/>
</Box>
</Box>
<Box
sx={{
display: "flex",
flexDirection: {xs: "column", sm: "row"},
alignItems: "start",
justifyContent: "center",
width: "100%",
}}
>
<Box
sx={{
mx: {xs: 0, sm: 2},
my: 2,
width: "100%",
}}
>
<Field
as={TextField}
sx={{width: "100%"}}
name="national_id"
variant="outlined"
size="small"
label={t("LoanRequest.text_field_national_id")}
placeholder={t(
"LoanRequest.text_field_enter_your_national_id"
)}
type={"text"}
error={
!!(props.touched.national_id && props.errors.national_id)
}
fullWidth
helperText={
props.touched.national_id
? props.errors.national_id
: null
}
/>
</Box>
<Box
sx={{
mx: {xs: 0, sm: 2},
my: 2,
width: "100%",
}}
>
<Field
as={TextField}
sx={{width: "100%"}}
name="shenasname_id"
variant="outlined"
size="small"
label={t("LoanRequest.text_field_shenasname_id")}
placeholder={t(
"LoanRequest.text_field_enter_your_shenasname_id"
)}
type={"text"}
error={
!!(props.touched.shenasname_id && props.errors.shenasname_id)
}
fullWidth
helperText={
props.touched.shenasname_id
? props.errors.shenasname_id
: null
}
/>
</Box>
</Box>
<Box sx={{my: 1}}>
<Divider>
<Chip
label={t("LoanRequest.uploading_documents")}
/>
</Divider>
</Box>
<Stack spacing={{xs: 1, sm: 2}} direction="row" useFlexGap flexWrap="wrap"
sx={{justifyContent: "center"}}>
<Box sx={{
mx: {xs: 0, sm: 1},
maxWidth: "200px"
}}>
<UploadSystem
setFieldValue={props.setFieldValue}
default_image={initialValues.sherkat_naft_doc}
fieldName="sherkat_naft_doc"
imageAlt={t("LoanRequest.file_field_sherkat_naft_doc")}
label={t("LoanRequest.file_field_sherkat_naft_doc")}
enableDelete={false}
error={
!!(props.touched.sherkat_naft_doc && props.errors.sherkat_naft_doc)
}
helperText={
props.touched.sherkat_naft_doc ? props.errors.sherkat_naft_doc : null
}
/>
</Box>
<Box sx={{
mx: {xs: 0, sm: 1},
maxWidth: "200px"
}}>
<UploadSystem
setFieldValue={props.setFieldValue}
default_image={initialValues.estate_doc}
fieldName="estate_doc"
imageAlt={t("LoanRequest.file_field_estate_doc")}
label={t("LoanRequest.file_field_estate_doc")}
enableDelete={false}
error={
!!(props.touched.estate_doc && props.errors.estate_doc)
}
helperText={
props.touched.estate_doc ? props.errors.estate_doc : null
}
/>
</Box>
<Box sx={{
mx: {xs: 0, sm: 1},
maxWidth: "200px"
}}>
<UploadSystem
setFieldValue={props.setFieldValue}
default_image={initialValues.agreement_doc}
fieldName="agreement_doc"
imageAlt={t("LoanRequest.file_field_agreement_doc")}
label={t("LoanRequest.file_field_agreement_doc")}
enableDelete={false}
error={
!!(props.touched.agreement_doc && props.errors.agreement_doc)
}
helperText={
props.touched.agreement_doc ? props.errors.agreement_doc : null
}
/>
</Box>
<Box sx={{
mx: {xs: 0, sm: 1},
maxWidth: "200px"
}}>
<UploadSystem
setFieldValue={props.setFieldValue}
default_image={initialValues.shenasname_image}
fieldName="shenasname_image"
imageAlt={t("LoanRequest.file_field_shenasname_image")}
label={t("LoanRequest.file_field_shenasname_image")}
enableDelete={false}
error={
!!(props.touched.shenasname_image && props.errors.shenasname_image)
}
helperText={
props.touched.shenasname_image ? props.errors.shenasname_image : null
}
/>
</Box>
<Box sx={{
mx: {xs: 0, sm: 1},
maxWidth: "200px"
}}>
<UploadSystem
setFieldValue={props.setFieldValue}
default_image={initialValues.payan_khedmat_image}
fieldName="payan_khedmat_image"
imageAlt={t("LoanRequest.file_field_payan_khedmat_image")}
label={t("LoanRequest.file_field_payan_khedmat_image")}
enableDelete={false}
error={
!!(props.touched.payan_khedmat_image && props.errors.payan_khedmat_image)
}
helperText={
props.touched.payan_khedmat_image ? props.errors.payan_khedmat_image : null
}
/>
</Box>
<Box sx={{
mx: {xs: 0, sm: 1},
maxWidth: "200px"
}}>
<UploadSystem
setFieldValue={props.setFieldValue}
default_image={initialValues.national_card_image}
fieldName="national_card_image"
imageAlt={t("app_name")}
label={t("LoanRequest.file_field_national_card_image")}
enableDelete={false}
error={
!!(props.touched.national_card_image && props.errors.national_card_image)
}
helperText={
props.touched.national_card_image ? props.errors.national_card_image : null
}
/>
</Box>
</Stack>
<Box
sx={{
display: "flex",
flexDirection: {xs: "column", sm: "row"},
alignItems: "center",
mx: "auto",
my: 2,
width: {xs: "100%", sm: "30%", md: "25%"},
}}
>
<Button
fullWidth
type="submit"
variant="contained"
size="large"
sx={{mt: 2}}
endIcon={<DataSaverOnIcon/>}
disabled={props.isSubmitting}
>
{t("LoanRequest.button_submit")}
</Button>
</Box>
</StyledForm>
</Stack>
)}
</Formik>
);
};
export default EditFormComponent;

View File

@@ -1,81 +0,0 @@
import DashboardLayouts from "@/layouts/dashboardLayouts";
import EditFormComponent from "@/components/dashboard/refahi/edit-request-loan/forms/EditForm";
import {useEffect, useState} from "react";
import {DETAILS_LOAN_REQUEST_WELFARE, GET_PROVINCE_LIST} from "@/core/data/apiRoutes";
import useLoading from "@/lib/app/hooks/useLoading";
import useRequest from "@/lib/app/hooks/useRequest";
import {useRouter} from "next/router";
import {log} from "next/dist/server/typescript/utils";
const LoanRequestComponent = () => {
const requestServer = useRequest();
const {setLoadingPage} = useLoading();
const router = useRouter();
const [provinceList, setProvinceList] = useState(null);
const [initialValues, setInitialValues] = useState(null);
// get province list
useEffect(() => {
setLoadingPage(true);
requestServer(GET_PROVINCE_LIST, "get", {auth: true, notification: false})
.then(function ({data}) {
const formattedData = data.map((province, index) => ({
id: index,
name: province.name,
value: province.id,
}));
setProvinceList(formattedData);
}).catch(() => {
setLoadingPage(false);
});
}, []);
// get form data
useEffect(() => {
if (!provinceList) return;
setLoadingPage(true);
requestServer(DETAILS_LOAN_REQUEST_WELFARE + router.query.id, "get", {auth: true, notification: false})
.then(function ({data}) {
const item = data.data;
const formattedData = {
name: item.name,
phone_number: item.phone_number,
province_id: item.province_id,
national_id: item.national_id,
shenasname_id: item.shenasname_id,
sherkat_naft_doc: item.sherkat_naft_doc,
estate_doc: item.estate_doc,
agreement_doc: item.agreement_doc,
national_card_image: item.national_card_image,
shenasname_image: item.shenasname_image,
payan_khedmat_image: item.payan_khedmat_image,
state_id: item.state_id
};
setInitialValues(formattedData);
})
.catch(() => {
setLoadingPage(false);
});
}, [provinceList, router]);
// Redirect to /dashboard/refahi/followUp-loan if state_id is not 7
useEffect(() => {
if (!initialValues) return
if (initialValues.state_id === 7) return
router.replace('/dashboard/refahi/followUp-loan');
}, [initialValues]);
return (
<>
{initialValues && initialValues.state_id === 7 && (
<DashboardLayouts>
<EditFormComponent provinceList={provinceList} initialValues={initialValues}/>
</DashboardLayouts>
)}
</>
);
};
export default LoanRequestComponent;

View File

@@ -1,52 +0,0 @@
import {
Card,
CardContent,
CardHeader,
Grid, IconButton, Tooltip,
Typography,
} from "@mui/material";
import SellIcon from '@mui/icons-material/Sell';
import EditIcon from '@mui/icons-material/Edit';
import {useTranslations} from "next-intl";
import {NextLinkComposed} from "@/core/components/LinkRouting";
const RequestCard = ({item}) => {
const t = useTranslations();
return (
<Grid item xs={12} md={6} lg={4}>
<Card sx={{
width: "100%",
boxShadow: "rgba(0, 0, 0, 0.15) 0px 5px 15px 0px"
}}>
<CardHeader
avatar={<SellIcon color="primary"/>}
title={`${t("LoanFollowUp.request_number")}: (${item.id})`}
subheader={item.latest_history_created_at}
action={
item.state_id === 7 ? ( // Use the ternary operator or if/else inside curly braces
<Tooltip title={t("LoanFollowUp.loan_request_correction")}>
<IconButton size="small"
color="primary"
variant="outlined"
component={NextLinkComposed}
to={{
pathname: `edit-request-loan/${item.id}`,
}} aria-label="fingerprint">
<EditIcon/>
</IconButton>
</Tooltip>
) : null
}
/>
<CardContent sx={{pt: 0}}>
<Typography variant="button">
{item.loan_state}
</Typography>
</CardContent>
</Card>
</Grid>
);
};
export default RequestCard;

View File

@@ -1,54 +0,0 @@
import DashboardLayouts from "@/layouts/dashboardLayouts";
import {Grid, Stack, Typography,} from "@mui/material";
import {useEffect, useState} from "react";
import {SHOW_LOAN_REQUEST_WELFARE} from "@/core/data/apiRoutes";
import useLoading from "@/lib/app/hooks/useLoading";
import useRequest from "@/lib/app/hooks/useRequest";
import moment from "jalali-moment";
import RequestCard from "@/components/dashboard/refahi/followUp-loan/RequestCard";
import {useTranslations} from "next-intl";
const LoanFollowUpComponent = () => {
const t = useTranslations()
const requestServer = useRequest();
const {setLoadingPage} = useLoading();
const [requestsList, setRequestsList] = useState(null);
// get form data
useEffect(() => {
setLoadingPage(true)
requestServer(SHOW_LOAN_REQUEST_WELFARE, "get", {auth: true, notification: false})
.then(function ({data}) {
const items = data.data;
const formattedData = items.map((item, index) => ({
id: item.id,
latest_history_created_at: moment(item.latest_history_created_at).locale("fa").format("HH:mm | YYYY/MM/DD"),
state_id: item.state_id,
loan_state: item.loan_state,
}));
setRequestsList(formattedData);
setLoadingPage(false)
})
.catch(function (error) {
})
}, []);
return (<DashboardLayouts>
<Stack
spacing={2}
sx={{
p: 4, width: "100%",
}}
>
{requestsList == null ? '' : requestsList.length ? (<Grid
container
columnSpacing={2}
rowSpacing={2}
sx={{alignItems: "start", justifyContent: "center"}}
>
{requestsList.map((item, index) => (<RequestCard item={item} key={item.id}/>))}
</Grid>) : (<Typography sx={{textAlign: 'center'}}>{t('LoanFollowUp.no_request_found')}</Typography>)}
</Stack>
</DashboardLayouts>);
};
export default LoanFollowUpComponent;

View File

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

View File

@@ -1,23 +1,24 @@
import {Box, Button, Container, Grid, Stack, Typography} from "@mui/material";
import LinkRouting from "@/core/components/LinkRouting";
import {useTranslations} from "next-intl";
import {LinkRouting} from "@witel/webapp-builder";
const FooterDetails = () => {
const t = useTranslations();
return(
<Box sx={{backgroundColor : 'black', color : 'white'}}>
<Container sx={{py : 2}} maxWidth="xl">
<Grid container sx={{py : 2}}>
return (
<Box sx={{backgroundColor: 'black', color: 'white'}}>
<Container sx={{py: 2}} maxWidth="xl">
<Grid container sx={{py: 2}}>
<Grid item xs={12} md={8}>
<Button disabled>
<Typography color={'white'}>کلیه حقوق محفوظ و متعلق به سازمان راهداری و حمل و نقل جاده ای می باشد</Typography>
<Typography color={'white'}>کلیه حقوق محفوظ و متعلق به سازمان راهداری و حمل و نقل جاده ای می
باشد</Typography>
</Button>
</Grid>
<Grid item xs={12} md={4}>
<Stack direction="row" alignItems="center" justifyContent="center">
<img src='/icons/logoLandingPage.png' width={25} alt="Your Alt Text" />
<img src='/icons/logoLandingPage.png' width={25} alt="Your Alt Text"/>
<LinkRouting
sx={{margin: 0.5, fontSize: "20px", color : "white"}}
sx={{margin: 0.5, fontSize: "20px", color: "white"}}
href={process.env.NEXT_PUBLIC_POWERED_BY_URL}
target="_blank"
underline="hover"

View File

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

View File

@@ -1,43 +1,48 @@
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 DownloadIcon from '@mui/icons-material/Download';
const LoanCondition = () => {
const t = useTranslations();
return(
return (
<Box>
<Container sx={{py : 3}} maxWidth="xl">
<Typography sx={{my : 3}} variant={'h3'}>
تخصیص وام
</Typography>
<Container sx={{py: 3}} maxWidth="xl">
<Grid container>
<Grid item xs={12} md={8}>
<Box sx={{padding : 3}}>
<Box sx={{padding: 3}}>
<Typography variant={'h5'}>
قوانین دریافت وام
</Typography>
<Grid item sx={{my : 2}} spacing={2}>
<Chip sx={{m : 1}} label="1 . فقط به زوجین ایرانی تعلق می‌گیرد" />
<Chip sx={{m : 1}} label="2 . فقط به زوجین ایرانی تعلق می‌گیرد" />
<Chip sx={{m : 1}} label="3 . فقط به زوجین ایرانی تعلق می‌گیرد" />
<Chip sx={{m : 1}} label="4 . فقط به زوجین ایرانی تعلق می‌گیرد" />
<Grid item sx={{my: 2}}>
<Chip sx={{m: 1}}
label="1. ارایه کارت هوشمند ناوگان فعال توسط مالک ناوگان (صاحب سند و پلاک)"/>
<Chip sx={{m: 1}} label="2. ارایه حداقل 140 صورت وضعیت در سال 1400 و 1401"/>
<Chip sx={{m: 1}} label="3. نداشتن بدهی جاری"/>
</Grid>
<Typography textAlign={'justify'}> این یکی از محبوبترین انواع وام در ایران است. دلیل این محبوبیت سرعت آن در پرداخت و سود بسیار پایین آن است.
تنها یک شرط برای اخذ این وام وجود دارد و آن این است که باید به تازگی ازدواج کرده باشید.
همچنین فقط یک بار از این وام میتوانید استفاده کنید. یعنی این وام فقط یک بار به هر فرد تعلق میگیرد.
محل تامین بودجه آن حسابهای قرض الحسنهایست که افراد در بانکها باز میکنند و مرجع اصلی آرائه آن، بانک مرکزی است.
<Typography sx={{lineHeight: 2.4}} textAlign={'justify'}>
این وام برای اتوبوس تا سقف <Typography sx={{color: "secondary.main"}}
variant={'h6'}
fontWeight={'bold'}
component={'span'}>700</Typography> میلیون تومان
و برای مینی بوس تا سقف <Typography sx={{color: "secondary.main"}}
variant={'h6'}
fontWeight={'bold'}
component={'span'}>200</Typography> میلیون تومان در
نظر گرفته شده است. مبلغ نهایی با توجه به امتیاز و روند کارشناسی در ادارات مربوطه مشخص
میشود. برای اطلاع از شرایط کامل این وام فایل پیوست را مطالعه بفرمایید.
</Typography>
</Box>
</Grid>
<Grid sx={{display : 'flex' , alignItems : "center", justifyContent : "center"}} item xs={12} md={4}>
<Grid sx={{display: 'flex', alignItems: "center", justifyContent: "center"}} item xs={12} md={4}>
<Box>
<Button variant="contained" startIcon={<DownloadIcon />}>
<Button variant="contained" startIcon={<DownloadIcon/>}>
<Link
variant="subtitle1"
sx={{padding : 1}}
sx={{padding: 1}}
color={'white'}
download
underline="none"
href={'/dist/images/modalrms/Asset%2010.apk'}
href={'/files/راهنما.xlsx'}
>
دریافت فایل های راهنما
</Link>

View File

@@ -1,91 +1,52 @@
import {useTranslations} from "next-intl";
import {
Timeline,
TimelineConnector, TimelineContent,
TimelineDot,
TimelineItem,
TimelineOppositeContent,
TimelineSeparator
} from "@mui/lab";
import RepeatIcon from '@mui/icons-material/Repeat';
import FastfoodIcon from '@mui/icons-material/Fastfood';
import LaptopMacIcon from '@mui/icons-material/LaptopMac';
import CheckCircleIcon from '@mui/icons-material/CheckCircle';
import CallMadeIcon from '@mui/icons-material/CallMade';
import {CircularProgress, Typography} from "@mui/material";
import HowToRegIcon from '@mui/icons-material/HowToReg';
import DoneIcon from '@mui/icons-material/Done';
import HotelIcon from '@mui/icons-material/Hotel';
import {useEffect, useState} from "react";
import TimelineManager from "@/core/components/timelines/timelineManager";
import moment from "jalali-moment";
import {Timeline} from "@mui/lab";
import {useConfig} from "@witel/webapp-builder";
const TimeLineDetails = () => {
const t = useTranslations();
return(
const {config} = useConfig()
const [timeLineList, setTimeLineList] = useState([])
const deadlines = Object.keys(config.deadlines).map(key => ({
type: key,
...config.deadlines[key]
}));
useEffect(() => {
let tempArr = []
for (const timeLine of deadlines) {
let temp = {}
temp['label'] = timeLine.timeline_label
temp['date'] = moment(timeLine.date.from, 'jYYYY/jMM/jDD').locale('fa').format('jD jMMMM')
const fromDate = moment(timeLine.date.from, 'jYYYY/jMM/jDD')
const toDate = moment(timeLine.date.to, 'jYYYY/jMM/jDD')
const today = moment()
if (today.isBetween(fromDate, toDate, null, '[]')) {
temp['status'] = 'in progress'
} else if (today.isAfter(toDate, null)) {
temp['status'] = 'done'
} else if (today.isBefore(fromDate, null)) {
temp['status'] = 'open'
}
tempArr.push(temp)
}
setTimeLineList(tempArr)
}, []);
return (
<Timeline position="alternate">
<TimelineItem>
<TimelineOppositeContent
sx={{ m: 'auto 0' }}
align="right"
variant="body2"
color="text.secondary"
>
10 دی
</TimelineOppositeContent>
<TimelineSeparator>
<TimelineDot color={'success'}>
<DoneIcon/>
</TimelineDot>
<TimelineConnector sx={{ backgroundColor: 'green' }} />
</TimelineSeparator>
<TimelineContent sx={{ py: '12px', px: 2 }}>
<Typography variant="h6" component="span">
ثبت نام متقاضی
</Typography>
<Typography color={'gray'}>ثبت نام متقاضیان واجد شرایط</Typography>
</TimelineContent>
</TimelineItem>
<TimelineItem>
<TimelineOppositeContent
sx={{ m: 'auto 0' }}
variant="body2"
color="text.secondary"
>
10 بهمن
</TimelineOppositeContent>
<TimelineSeparator>
<TimelineConnector sx={{ backgroundColor: 'green' }} />
<TimelineDot color="primary">
<CircularProgress color={'inherit'} size={40} />
</TimelineDot>
<TimelineConnector />
</TimelineSeparator>
<TimelineContent sx={{ py: '12px', px: 2 }}>
<Typography variant="h6">
صحت سنجی متقاضیان
</Typography>
<Typography color={'gray'}>بررسی و کارشناسی درخواست ها</Typography>
</TimelineContent>
</TimelineItem>
<TimelineItem>
<TimelineOppositeContent
sx={{ m: 'auto 0' }}
variant="body2"
color="text.secondary"
>
10 اسفند
</TimelineOppositeContent>
<TimelineSeparator>
<TimelineConnector />
<TimelineDot>
<CallMadeIcon />
</TimelineDot>
</TimelineSeparator>
<TimelineContent sx={{ py: '12px', px: 2 }}>
<Typography variant="h6" component="span">
ارجاع به بانک
</Typography>
<Typography color={'gray'}>فرآیند معرفی ضامن و پرداخت</Typography>
</TimelineContent>
</TimelineItem>
{timeLineList.map((timeLine, index) => (
<TimelineManager
key={index}
status={timeLine.status}
date={timeLine.date}
primaryLabel={timeLine.label.primary}
secondaryLabel={timeLine.label.secondary}
position={index === (timeLineList.length - 1) ? 'end' : ''}
/>
))
}
</Timeline>
)
}

View File

@@ -1,31 +1,41 @@
import {Box, Container, Grid, Typography} from "@mui/material";
import {Timeline, TimelineConnector, TimelineContent, TimelineDot, TimelineItem, TimelineSeparator} from "@mui/lab";
import {useTranslations} from "next-intl";
import TimeLineDetails from "@/components/first/LoanDescription/TimeLineDetails";
const LoanDescription = () => {
const t = useTranslations();
return(
return (
<Box>
<Container sx={{py : 3}} maxWidth="xl">
<Typography sx={{my : 3}} variant={'h3'}>
توضیحات دریافت وام
<Container sx={{py: 3}} maxWidth="xl">
<Typography sx={{my: 3}} variant={'h3'}>
وام نوسازی ناوگان
</Typography>
<Grid sx={{py : 2}} container spacing={2}>
<Grid sx={{py: 2}} container spacing={2}>
<Grid item xs={12} md={7}>
<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'}>400</Typography> میلیون وام تعلق خواهد گرفت.
حداکثر زمان بازپرداخت وام خرید کالا ۲۴ ماه است و برای گرفتن آن به یک ضامن رسمی احتیاج دارید.
<Typography sx={{lineHeight: 2.4}} variant={'subtitle1'} textAlign={'justify'}>
این وام براساس <Typography sx={{color: "secondary.main"}} variant={'h5'}
fontWeight={'bold'}
component={'span'}>تبصره 18</Typography> قانون بودجه می باشد. در
قانون
بودجه این تبصره اینگونه بیان شده است:
به منظور رشد و پیشرفت استان های کشور از طریق ارتقای بهره وری و توسعه، سرمایه گذاری ها،
تکمیل واحدهای نیمه تمام و ظرفیت های خالی بنگاه های تولیدی بر پایه آمایش سرزمین و همچنین
حمایت از طرح(پروژهای) دانش بنیان و پیشران و بسط و عدالت سرزمینی منابع زیر جهت ایجاد و افزایش
تولید، اشتغال و کارآفرینی و ارتقای رشد اقتصادی اختصاص می یابد.
</Typography>
<Typography sx={{lineHeight: 2.4}} variant={'subtitle1'} textAlign={'justify'}>
در این راستا سازمان راهداری و حمل و نقل جاده ای به منظور بهبود کیفیت حمل و نقل بین شهری و
افزایش ظرفیت ترابری اقدام به اعطای وام به <Typography sx={{color: "secondary.main"}}
variant={'h5'}
fontWeight={'bold'}
component={'span'}>ناوگان های
مسافربری</Typography> برای
بازسازی و تعمیرات میکند.
</Typography>
</Grid>
<Grid item xs={12} md={5}>
<Box>
<TimeLineDetails />
<TimeLineDetails/>
</Box>
</Grid>
</Grid>

View File

@@ -1,35 +1,35 @@
import {Box, Container, Grid, Stack, Typography} from "@mui/material";
import LinkRouting from "@/core/components/LinkRouting";
import {useTranslations} from "next-intl";
import {LinkRouting} from "@witel/webapp-builder";
const LoanRegister = () => {
const t = useTranslations();
return(
<Box sx={{backgroundColor : "primary.light"}}>
<Container sx={{padding : 3, color : "white"}} maxWidth="xl">
<Typography sx={{my : 3}} variant={'h3'}>
return (
<Box sx={{backgroundSize: 'cover', backgroundImage: `url(/images/bg-firstpage-bus.png)`}}>
<Container sx={{padding: 3, color: "white"}} maxWidth="xl">
<Typography sx={{my: 3, textShadow: '1px 1px 2px #555555'}} variant={'h3'}>
{t("app_name")}
</Typography>
<Stack sx={{color : "white"}}>
<Grid sx={{py : 4}} container spacing={2}>
<Stack sx={{color: "white"}}>
<Grid sx={{py: 4}} container spacing={2}>
<Grid item xs={12} md={6}>
<Box sx={{paddingBottom : 4}}>
<Typography>
برای استفاده از تسهیلات این سامانه و دریافت وام می بایست ابتدا وارد سامانه شوید و یا اگر ثبت نام نکرده اید ثبت نام کنید
<Box sx={{paddingBottom: 4}}>
<Typography sx={{lineHeight: 2.7, textShadow: '1px 1px 2px black'}}>
وام نوسازی ناوگان به هدف رشد و پیشرفت استان های کشور از طریق ارتقای بهره وری و توسعه
اعطا میشود. برای استفاده از تسهیلات این سامانه و درخواست وام می بایست ابتدا وارد
سامانه شوید.
</Typography>
</Box>
</Grid>
<Grid item xs={12} md={6}>
<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'}>
<Typography variant="h6">
{t("firstPage.login_button")}
</Typography>
</LinkRouting>
</Container>
<Container maxWidth={'xs'} sx={{backgroundColor : "primary.main", textAlign : "center", borderRadius : 2 ,padding : 1}}>
<LinkRouting underline="none" color="inherit" href={'/register'}>
<Typography variant="h6">
{t("firstPage.register_button")}
</Typography>

View File

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

View File

@@ -1,9 +1,6 @@
import ResendToken from "@/core/components/ResendToken";
import StyledForm from "@/core/components/StyledForm";
import {LOGIN} from "@/core/data/apiRoutes";
import CenterLayout from "@/layouts/CenterLayout";
import FullPageLayout from "@/layouts/FullPageLayout";
import useUser from "@/lib/app/hooks/useUser";
import ChangeCircleIcon from "@mui/icons-material/ChangeCircle";
import LoginIcon from "@mui/icons-material/Login";
import {Box, Button, Container, Grid, Paper, Stack, TextField, Typography,} from "@mui/material";
@@ -11,8 +8,8 @@ import {Field, Formik} from "formik";
import {useTranslations} from "next-intl";
import * as Yup from "yup";
import SvgLogin from "@/core/components/svgs/SvgLogin";
import useRequest from "@/lib/app/hooks/useRequest";
import AutoSubmit from "@/core/components/AutoSubmit";
import {CenterLayout, FullPageLayout, useRequest, useUser} from "@witel/webapp-builder";
const SendTokenComponent = ({
PhoneNumber,

View File

@@ -1,17 +1,14 @@
import LinkRouting from "@/core/components/LinkRouting";
import StyledForm from "@/core/components/StyledForm";
import {SEND_OTP_TOKEN} from "@/core/data/apiRoutes";
import CenterLayout from "@/layouts/CenterLayout";
import FullPageLayout from "@/layouts/FullPageLayout";
import FingerprintIcon from "@mui/icons-material/Fingerprint";
import PersonAddIcon from "@mui/icons-material/PersonAdd";
import {Button, Container, Grid, Paper, Stack, TextField, Typography,} from "@mui/material";
import {Field, Formik} from "formik";
import {useTranslations} from "next-intl";
import {useSearchParams} from "next/navigation";
import * as Yup from "yup";
import SvgLogin from "@/core/components/svgs/SvgLogin";
import useRequest from "@/lib/app/hooks/useRequest";
import {CenterLayout, FullPageLayout, LinkRouting, useRequest} from "@witel/webapp-builder";
const SendUserDataComponent = ({
setOtpToken,
@@ -97,32 +94,16 @@ const SendUserDataComponent = ({
flexDirection: {xs: "column-reverse", sm: "row"},
}}
>
<Grid item xs={12} sm={6} sx={{pr: {xs: 0, sm: 1}}}>
<LinkRouting href={"/register"}>
<Button
fullWidth
type="button"
variant="outlined"
size="large"
endIcon={<PersonAddIcon/>}
disabled={props.isSubmitting}
>
{t("LoginPage.button_make_account")}
</Button>
</LinkRouting>
</Grid>
<Grid item xs={12} sm={6} sx={{pl: {xs: 0, sm: 1}}}>
<Button
fullWidth
type="submit"
variant="contained"
size="large"
endIcon={<FingerprintIcon/>}
disabled={props.isSubmitting}
>
{t("LoginPage.button_request_verification")}
</Button>
</Grid>
<Button
fullWidth
type="submit"
variant="contained"
size="large"
endIcon={<FingerprintIcon/>}
disabled={props.isSubmitting}
>
{t("LoginPage.button_request_verification")}
</Button>
</Grid>
</Stack>
</StyledForm>

View File

@@ -1,131 +0,0 @@
import LinkRouting from "@/core/components/LinkRouting";
import StyledForm from "@/core/components/StyledForm";
import {SEND_OTP_TOKEN} from "@/core/data/apiRoutes";
import CenterLayout from "@/layouts/CenterLayout";
import FullPageLayout from "@/layouts/FullPageLayout";
import FingerprintIcon from "@mui/icons-material/Fingerprint";
import {Button, Container, Paper, Stack, TextField, Typography,} from "@mui/material";
import {Field, Formik} from "formik";
import {useTranslations} from "next-intl";
import {useSearchParams} from "next/navigation";
import * as Yup from "yup";
import SvgLogin from "@/core/components/svgs/SvgLogin";
import useRequest from "@/lib/app/hooks/useRequest";
const RequestOtpComponent = ({
setOtpToken,
setPhoneNumber,
PhoneNumber,
setTimer,
initialTimerValue,
}) => {
const t = useTranslations();
const requestServer = useRequest();
const searchParams = useSearchParams();
const backUrlDecodedPath = searchParams.get("back_url");
const initialValues = {
phone_number: PhoneNumber,
};
const validationSchema = Yup.object().shape({
phone_number: Yup.string().required(
t("RegisterPage.error_message_phone_number")
),
});
const handleSubmit = (values, props) => {
requestServer(SEND_OTP_TOKEN, "post", {
auth: false, data: {
phone_number: values.phone_number,
}
})
.then(function (response) {
setPhoneNumber(values.phone_number);
setOtpToken(true);
setTimer(initialTimerValue);
}).catch(function (error) {
props.setSubmitting(false);
});
};
return (
<FullPageLayout sx={{p: 1}}>
<CenterLayout>
<Container maxWidth="sm">
<Paper elevation={0}>
<Formik
initialValues={initialValues}
onSubmit={handleSubmit}
validationSchema={validationSchema}
>
{(props) => (
<Stack spacing={2} sx={{p: 2}}>
<Stack
sx={{width: "100%"}}
alignItems='center'
>
<SvgLogin width={300} height={200}/>
</Stack>
<Typography margin={2} variant="h4" textAlign="center">
{t("Titles.title_register_page")}
</Typography>
<StyledForm sx={{width: "100%"}}>
<Stack spacing={3} sx={{p: 2}}>
<Field
as={TextField}
name="phone_number"
variant="outlined"
label={t("RegisterPage.text_field_phone_number")}
placeholder={t(
"RegisterPage.text_field_enter_your_phone_number"
)}
type={"text"}
error={
!!(props.touched.phone_number && props.errors.phone_number)
}
fullWidth
helperText={
props.touched.phone_number
? props.errors.phone_number
: null
}
/>
<Button
fullWidth
type="submit"
variant="contained"
size="large"
endIcon={<FingerprintIcon/>}
disabled={props.isSubmitting}
>
{t("RegisterPage.button_request_verification")}
</Button>
</Stack>
</StyledForm>
</Stack>
)}
</Formik>
</Paper>
</Container>
</CenterLayout>
<Stack direction="row" alignItems="center" justifyContent="center">
<LinkRouting
sx={{margin: 2}}
href={
backUrlDecodedPath
? decodeURIComponent(backUrlDecodedPath)
: "/register"
}
>
{t("RegisterPage.link_routing_back_to")}{" "}
{backUrlDecodedPath
? t("RegisterPage.link_routing_previuos_page")
: t("RegisterPage.link_routing_register")}
</LinkRouting>
</Stack>
</FullPageLayout>
);
};
export default RequestOtpComponent;

View File

@@ -1,90 +0,0 @@
import LinkRouting, {NextLinkComposed} from "@/core/components/LinkRouting";
import CenterLayout from "@/layouts/CenterLayout";
import FullPageLayout from "@/layouts/FullPageLayout";
import useUser from "@/lib/app/hooks/useUser";
import {Box, Button, Grid, Stack} from "@mui/material";
import {useTranslations} from "next-intl";
import {useSearchParams} from "next/navigation";
import SvgDashboard from "@/core/components/svgs/SvgDashboard";
const RegisterComponent = () => {
const t = useTranslations();
const {isAuth} = useUser();
const searchParams = useSearchParams();
const backUrlDecodedPath = searchParams.get("back_url");
return (
<FullPageLayout sx={{p: 1}}>
<CenterLayout spacing={3}>
<SvgDashboard width={300} height={200}/>
{isAuth ? (
<Button
variant="contained"
component={NextLinkComposed}
to={{
pathname: "/dashboard",
}}
>
{t("dashboard")}
</Button>
) : (
<Box>
<Grid
container
rowSpacing={{xs: 1, sm: 0}}
sx={{
flexDirection: {xs: "column", sm: "row"},
alignItems: "center",
}}
>
<Grid item xs={12} sm={6} sx={{px: {xs: 0, sm: 1}}}>
<Button
sx={{whiteSpace: "nowrap"}}
variant="contained"
size="large"
component={NextLinkComposed}
to={{
pathname: "register/navy",
}}
>
{t("register_navy")}
</Button>
</Grid>
{/*<Grid item xs={12} sm={6} sx={{px: {xs: 0, sm: 1}}}>*/}
{/* <Button*/}
{/* sx={{whiteSpace: "nowrap"}}*/}
{/* variant="contained"*/}
{/* size="large"*/}
{/* component={NextLinkComposed}*/}
{/* to={{*/}
{/* pathname: "register/welfare-services",*/}
{/* }}*/}
{/* >*/}
{/* {t("register_welfare_services")}*/}
{/* </Button>*/}
{/*</Grid>*/}
</Grid>
</Box>
)}
</CenterLayout>
<Stack direction="row" alignItems="center" justifyContent="center">
<LinkRouting
sx={{margin: 2}}
href={
backUrlDecodedPath
? decodeURIComponent(backUrlDecodedPath)
: "/"
}
>
{t("RegisterPage.link_routing_back_to")}{" "}
{backUrlDecodedPath
? t("RegisterPage.link_routing_previuos_page")
: t("LoginPage.link_routing_main_page")}
</LinkRouting>
</Stack>
</FullPageLayout>
);
};
export default RegisterComponent;

View File

@@ -1,176 +0,0 @@
import StyledForm from "@/core/components/StyledForm";
import {REGISTER} from "@/core/data/apiRoutes";
import CenterLayout from "@/layouts/CenterLayout";
import FullPageLayout from "@/layouts/FullPageLayout";
import LoginIcon from "@mui/icons-material/Login";
import ChangeCircleIcon from "@mui/icons-material/ChangeCircle";
import {Box, Button, Chip, Container, Divider, Paper, Stack, TextField, Typography,} from "@mui/material";
import {Field, Formik} from "formik";
import {useTranslations} from "next-intl";
import * as Yup from "yup";
import ResendToken from "@/core/components/ResendToken";
import useUser from "@/lib/app/hooks/useUser";
import SvgRegister from "@/core/components/svgs/SvgRegister";
import useRequest from "@/lib/app/hooks/useRequest";
import AutoSubmit from "@/core/components/AutoSubmit";
const UserRegisterComponent = ({
PhoneNumber,
setOtpToken,
initialTimerValue,
timer,
setTimer,
}) => {
const t = useTranslations();
const {setToken} = useUser();
const requestServer = useRequest();
const initialValues = {
type_id: "1",
verification_code: "",
phone_number: PhoneNumber,
national_id: "",
};
const validationSchema = Yup.object().shape({
verification_code: Yup.string().required(
t("RegisterPage.error_message_verification_code")
),
national_id: Yup.string().required(
t("RegisterPage.error_message_national_id")
),
});
const handleSubmit = (values, props) => {
requestServer(REGISTER, "post", {
auth: false, data: {
type_id: values.type_id,
national_id: values.national_id,
phone_number: values.phone_number,
verification_code: values.verification_code,
}
})
.then(function (response) {
setOtpToken(true);
setToken(response.data.token);
}).catch(function (error) {
props.setSubmitting(false);
});
};
return (
<FullPageLayout sx={{p: 1}}>
<CenterLayout>
<Container maxWidth="sm">
<Paper elevation={0}>
<Formik
initialValues={initialValues}
onSubmit={handleSubmit}
validationSchema={validationSchema}
>
{(props) => (
<Stack spacing={2} sx={{p: 2}}>
<Stack
sx={{width: "100%"}}
alignItems='center'
>
<SvgRegister width={300} height={200}/>
</Stack>
<Typography margin={2} variant="h4" textAlign="center">
{t("register_navy")}
</Typography>
<StyledForm sx={{width: "100%"}}>
<Stack spacing={3} sx={{p: 2}}>
<Box
sx={{
display: {xs: "column", sm: "flex"},
alignItems: "center",
textAlign: {xs: "center", sm: "unset"},
justifyContent: "space-between",
}}
>
<Typography variant="button" sx={{display: "block"}}>
{t("LoginPage.sent_token_to")}: {PhoneNumber}
</Typography>
<Button
size="small"
startIcon={<ChangeCircleIcon/>}
variant="outlined"
sx={{whiteSpace: "nowrap", my: {xs: 1, sm: 0}}}
onClick={() => setOtpToken(false)}
>
{t("LoginPage.change_phone_number")}
</Button>
</Box>
<Field
as={TextField}
name="verification_code"
variant="outlined"
label={t("LoginPage.text_field_verification_code")}
placeholder={t(
"LoginPage.text_field_enter_your_verification_code"
)}
type={"number"}
error={
!!(props.touched.verification_code && props.errors.verification_code)
}
fullWidth
helperText={
props.touched.verification_code
? props.errors.verification_code
: null
}
onChange={(e) => e.target.value.length <= 6 ? props.setFieldValue("verification_code", e.target.value) : ""}
/>
<AutoSubmit/>
<ResendToken
initialTimerValue={initialTimerValue}
timer={timer}
setTimer={setTimer}
PhoneNumber={PhoneNumber}
/>
<Divider>
<Chip label={t("RegisterPage.complete_information")}/>
</Divider>
<Field
as={TextField}
name="national_id"
variant="outlined"
label={t("RegisterPage.text_field_national_id")}
placeholder={t(
"RegisterPage.text_field_enter_your_national_id"
)}
type={"text"}
error={
!!(props.touched.national_id && props.errors.national_id)
}
fullWidth
helperText={
props.touched.national_id
? props.errors.national_id
: null
}
/>
<Button
fullWidth
type="submit"
variant="contained"
size="large"
endIcon={<LoginIcon/>}
disabled={props.isSubmitting}
>
{t("RegisterPage.button_submit")}
</Button>
</Stack>
</StyledForm>
</Stack>
)}
</Formik>
</Paper>
</Container>
</CenterLayout>
</FullPageLayout>
);
};
export default UserRegisterComponent;

View File

@@ -1,37 +0,0 @@
import {useState} from "react";
import RequestOtpComponent from "../RequestOtp";
import UserRegisterComponent from "./UserRegister";
const NavyComponent = () => {
const [otpToken, setOtpToken] = useState(false);
const [PhoneNumber, setPhoneNumber] = useState("");
// For Resend Token (read ResendToken Component Doc)
const initialTimerValue = 30;
const [timer, setTimer] = useState(initialTimerValue);
// End For Resend Token
if (!otpToken) {
return (
<RequestOtpComponent
setOtpToken={setOtpToken}
PhoneNumber={PhoneNumber}
setPhoneNumber={setPhoneNumber}
setTimer={setTimer}
initialTimerValue={initialTimerValue}
/>
);
} else {
return (
<UserRegisterComponent
PhoneNumber={PhoneNumber}
setOtpToken={setOtpToken}
timer={timer}
setTimer={setTimer}
initialTimerValue={initialTimerValue}
/>
);
}
};
export default NavyComponent;

View File

@@ -1,174 +0,0 @@
import StyledForm from "@/core/components/StyledForm";
import {REGISTER} from "@/core/data/apiRoutes";
import CenterLayout from "@/layouts/CenterLayout";
import FullPageLayout from "@/layouts/FullPageLayout";
import LoginIcon from "@mui/icons-material/Login";
import ChangeCircleIcon from "@mui/icons-material/ChangeCircle";
import {Box, Button, Chip, Container, Divider, Paper, Stack, TextField, Typography,} from "@mui/material";
import {Field, Formik} from "formik";
import {useTranslations} from "next-intl";
import * as Yup from "yup";
import ResendToken from "@/core/components/ResendToken";
import useUser from "@/lib/app/hooks/useUser";
import SvgRegister from "@/core/components/svgs/SvgRegister";
import useRequest from "@/lib/app/hooks/useRequest";
const UserRegisterComponent = ({
PhoneNumber,
setOtpToken,
initialTimerValue,
timer,
setTimer,
}) => {
const t = useTranslations();
const {setToken} = useUser();
const requestServer = useRequest();
const initialValues = {
type_id: "2",
verification_code: "",
phone_number: PhoneNumber,
national_id: "",
};
const validationSchema = Yup.object().shape({
verification_code: Yup.string().required(
t("RegisterPage.error_message_verification_code")
),
national_id: Yup.string().required(
t("RegisterPage.error_message_national_id")
),
});
const handleSubmit = (values, props) => {
requestServer(REGISTER, "post", {
auth: false, data: {
type_id: values.type_id,
national_id: values.national_id,
phone_number: values.phone_number,
verification_code: values.verification_code,
}
})
.then(function (response) {
setOtpToken(true);
setToken(response.data.token);
}).catch(function (error) {
props.setSubmitting(false);
});
};
return (
<FullPageLayout sx={{p: 1}}>
<CenterLayout>
<Container maxWidth="sm">
<Paper elevation={0}>
<Formik
initialValues={initialValues}
onSubmit={handleSubmit}
validationSchema={validationSchema}
>
{(props) => (
<Stack spacing={2} sx={{p: 2}}>
<Stack
sx={{width: "100%"}}
alignItems='center'
>
<SvgRegister width={300} height={200}/>
</Stack>
<Typography margin={2} variant="h4" textAlign="center">
{t("register_welfare_services")}
</Typography>
<StyledForm sx={{width: "100%"}}>
<Stack spacing={3} sx={{p: 2}}>
<Box
sx={{
display: {xs: "column", sm: "flex"},
alignItems: "center",
textAlign: {xs: "center", sm: "unset"},
justifyContent: "space-between",
}}
>
<Typography variant="button" sx={{display: "block"}}>
{t("LoginPage.sent_token_to")}: {PhoneNumber}
</Typography>
<Button
size="small"
startIcon={<ChangeCircleIcon/>}
variant="outlined"
onClick={() => setOtpToken(false)}
sx={{whiteSpace: "nowrap", my: {xs: 1, sm: 0}}}
>
{t("LoginPage.change_phone_number")}
</Button>
</Box>
<Field
as={TextField}
name="verification_code"
variant="outlined"
label={t("LoginPage.text_field_verification_code")}
placeholder={t(
"LoginPage.text_field_enter_your_verification_code"
)}
type={"text"}
error={
!!(props.touched.verification_code && props.errors.verification_code)
}
fullWidth
helperText={
props.touched.verification_code
? props.errors.verification_code
: null
}
/>
<ResendToken
initialTimerValue={initialTimerValue}
timer={timer}
setTimer={setTimer}
PhoneNumber={PhoneNumber}
disabled={props.isSubmitting}
/>
<Divider>
<Chip label={t("RegisterPage.complete_information")}/>
</Divider>
<Field
as={TextField}
name="national_id"
variant="outlined"
label={t("RegisterPage.text_field_national_id")}
placeholder={t(
"RegisterPage.text_field_enter_your_national_id"
)}
type={"text"}
error={
!!(props.touched.national_id && props.errors.national_id)
}
fullWidth
helperText={
props.touched.national_id
? props.errors.national_id
: null
}
/>
<Button
fullWidth
type="submit"
variant="contained"
size="large"
endIcon={<LoginIcon/>}
disabled={props.isSubmitting}
>
{t("RegisterPage.button_submit")}
</Button>
</Stack>
</StyledForm>
</Stack>
)}
</Formik>
</Paper>
</Container>
</CenterLayout>
</FullPageLayout>
);
};
export default UserRegisterComponent;

View File

@@ -1,37 +0,0 @@
import {useState} from "react";
import RequestOtpComponent from "../RequestOtp";
import UserRegisterComponent from "./UserRegister";
const WelfareServicesComponent = () => {
const [otpToken, setOtpToken] = useState(false);
const [PhoneNumber, setPhoneNumber] = useState("");
// For Resend Token (read ResendToken Component Doc)
const initialTimerValue = 30;
const [timer, setTimer] = useState(initialTimerValue);
// End For Resend Token
if (!otpToken) {
return (
<RequestOtpComponent
setOtpToken={setOtpToken}
PhoneNumber={PhoneNumber}
setPhoneNumber={setPhoneNumber}
setTimer={setTimer}
initialTimerValue={initialTimerValue}
/>
);
} else {
return (
<UserRegisterComponent
PhoneNumber={PhoneNumber}
setOtpToken={setOtpToken}
timer={timer}
setTimer={setTimer}
initialTimerValue={initialTimerValue}
/>
);
}
};
export default WelfareServicesComponent;

View File

@@ -39,14 +39,6 @@ const GlobalHead = () => {
/>
<link
rel="icon"
type="image/svg"
sizes="32x32"
href="/icons/favicon.png"
/>
<link
rel="icon"
type="image/svg"
sizes="16x16"
href="/icons/favicon.png"
/>
</Head>

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

@@ -0,0 +1,87 @@
import * as React from 'react';
import {useState} from 'react';
import Button from '@mui/material/Button';
import {AdapterDateFnsJalali} from "@mui/x-date-pickers/AdapterDateFnsJalali";
import {LocalizationProvider} from '@mui/x-date-pickers/LocalizationProvider';
import {DatePicker} from '@mui/x-date-pickers/DatePicker';
import {useTranslations} from "next-intl";
import moment from "jalali-moment";
import {FormControl, FormHelperText} from "@mui/material";
function ButtonField(props) {
const t = useTranslations();
const {
setOpen,
label,
id,
disabled,
InputProps: {ref} = {},
inputProps: {'aria-label': ariaLabel} = {},
} = props;
return (
<Button
sx={{width: "100%", padding: 0.8}}
variant="outlined"
color={props.slotProps.formik.touched.birthday && Boolean(props.slotProps.formik.errors.birthday) ? "error" : "primary"}
id={id}
disabled={disabled}
ref={ref}
aria-label={ariaLabel}
onClick={() => setOpen?.((prev) => !prev)}
>
{label ? `${t("birthday")}: ${label}` : `${t("birthday")}`}
</Button>
);
}
function ButtonDatePicker(props) {
const t = useTranslations();
const [open, setOpen] = useState(false);
return (
<DatePicker
slots={{field: ButtonField, ...props.slots}}
disableFuture
disabled={props.disabled}
slotProps={{
formik: props.formik,
field: {setOpen},
textField: {
helperText: props.formik.errors.birthday ? `${t("birthday_error")}` : null
}
}}
{...props}
open={open}
onClose={() => setOpen(false)}
onOpen={() => setOpen(true)}
/>
);
}
export default function PickerWithButtonField({formik, disabled}) {
return (
<FormControl
variant={"outlined"}
fullWidth
size="small"
error={true}
>
<LocalizationProvider dateAdapter={AdapterDateFnsJalali}>
<ButtonDatePicker
label={formik.values.birthday === "" ? null : formik.values.birthday
.locale("fa")
.format("YYYY/MM/DD")}
value={formik.values.birthday === "" ? null : formik.values.birthday.toDate()}
formik={formik}
disabled={disabled}
onChange={(newValue) => {
formik.setFieldValue("birthday", moment(newValue))
}}
/>
</LocalizationProvider>
<FormHelperText>{formik.touched.birthday && formik.errors.birthday}</FormHelperText>
</FormControl>
);
}

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

@@ -0,0 +1,236 @@
import {Box, Button, Drawer, FormHelperText, Stack, TextField} from "@mui/material";
import {useTranslations} from "next-intl";
import AccessibleIcon from '@mui/icons-material/Accessible';
import {useState} from "react";
const plate_words = [
{
id: 1,
value: "الف",
name: "الف"
},
{
id: 2,
value: "ب",
name: "ب"
},
{
id: 3,
value: "پ",
name: "پ"
},
{
id: 4,
value: "ت",
name: "ت"
},
{
id: 5,
value: "ث",
name: "ث"
},
{
id: 6,
value: "ج",
name: "ج"
},
{
id: 7,
value: "د",
name: "د"
},
{
id: 8,
value: "ز",
name: "ز"
},
{
id: 9,
value: "س",
name: "س"
},
{
id: 10,
value: "ش",
name: "ش"
},
{
id: 11,
value: "ص",
name: "ص"
},
{
id: 12,
value: "ط",
name: "ط"
},
{
id: 13,
value: "ع",
name: "ع"
},
{
id: 14,
value: "ف",
name: "ف"
},
{
id: 15,
value: "ق",
name: "ق"
},
{
id: 16,
value: "ک",
name: "گ"
},
{
id: 17,
value: "ل",
name: "ل"
},
{
id: 18,
value: "م",
name: "م"
},
{
id: 19,
value: "ن",
name: "ن"
},
{
id: 20,
value: "و",
name: "و"
},
{
id: 21,
value: "ه",
name: "ه"
},
{
id: 22,
value: "ی",
name: "ی"
},
{
id: 23,
value: "*",
name: <AccessibleIcon/>
},
]
const PlateNumber = ({formik, disabled}) => {
const t = useTranslations();
const [plateDrawer, setPlateDrawer] = useState(false);
const isErrorPlate = Boolean(formik.errors.plate_part1 || formik.errors.plate_part3 || formik.errors.plate_part4)
const isTouched = (formik.touched.plate_part1 || formik.touched.plate_part3 || formik.touched.plate_part4)
return (
<>
<Stack direction={'row'}
sx={{
border: 1,
overflow: 'hidden',
borderColor: (isTouched && isErrorPlate) ? 'error.main' : 'divider',
borderRadius: 1
}}>
<TextField
name={'plate_part4'}
type={'tel'}
value={formik.values.plate_part4}
disabled={disabled}
size={'small'}
placeholder={'xx'} sx={{flexGrow: 2, "& fieldset": {border: 'none'}}}
inputProps={{sx: {textAlign: 'center'}}}
onChange={(event) => event.target.value.length <= 2 ? formik.setFieldValue("plate_part4", event.target.value) : null}
onBlur={formik.handleBlur}
error={formik.touched.plate_part4 && Boolean(formik.errors.plate_part4)}
/>
<TextField
name={'plate_part3'}
type={'tel'}
value={formik.values.plate_part3}
size={'small'}
disabled={disabled}
placeholder={'xxx'}
sx={{
flexGrow: 3,
borderLeft: 1,
borderRadius: 0,
borderColor: 'divider',
"& fieldset": {border: 'none'}
}}
inputProps={{sx: {textAlign: 'center'}}}
onChange={(event) => event.target.value.length <= 3 ? formik.setFieldValue("plate_part3", event.target.value) : null}
onBlur={formik.handleBlur}
error={formik.touched.plate_part3 && Boolean(formik.errors.plate_part3)}
/>
<Box sx={{flexGrow: 1}}>
<Button
onClick={() => setPlateDrawer(true)}
sx={{height: '100%', borderRadius: 0, borderColor: 'divider'}}
size={'small'}
data-selected-word={formik.values.plate_part2}
disabled={disabled}
fullWidth
>
{plate_words.find(p => p.value === formik.values.plate_part2).name}
</Button>
</Box>
<Drawer
anchor={"bottom"}
open={plateDrawer}
onClose={() => setPlateDrawer(false)}
>
<Box sx={{
display: "flex",
flexWrap: "wrap",
mx: "auto",
my: 2,
gap: 2,
width: "60%",
justifyContent: "center"
}}>
{plate_words.map((item) => (
<Button
key={item.id}
onClick={() => {
formik.setFieldValue("plate_part2", item.value)
setPlateDrawer(false);
}}
sx={{
width: "fit-content"
}}
variant="contained"
>
{item.name}
</Button>
))}
</Box>
</Drawer>
<TextField
name={'plate_part1'}
type={'tel'}
disabled={disabled}
value={formik.values.plate_part1}
size={'small'}
placeholder={'xx'}
sx={{flexGrow: 2, "& fieldset": {border: 'none'}}}
inputProps={{sx: {textAlign: 'center'}}}
onChange={(event) => event.target.value.length <= 2 ? formik.setFieldValue("plate_part1", event.target.value) : null}
onBlur={formik.handleBlur}
error={true}
/>
<Stack sx={{width: 24, borderLeft: 1, borderColor: 'divider'}}>
<Box sx={{flexGrow: 1, bgcolor: '#1ebc1e'}}></Box>
<Box sx={{flexGrow: 1, bgcolor: 'white'}}></Box>
<Box sx={{flexGrow: 1, bgcolor: '#f52121'}}></Box>
</Stack>
</Stack>
<FormHelperText
error={(isTouched && isErrorPlate)}>{(isTouched && isErrorPlate) ? `${t("LoanRequest.error_message_plate_number")}` : null}</FormHelperText>
</>
)
}
export default PlateNumber

View File

@@ -1,11 +1,8 @@
import Notifications from "@/core/components/notifications";
import useDirection from "@/lib/app/hooks/useDirection";
import {Button} from "@mui/material";
import axios from "axios";
import {useTranslations} from "next-intl";
import {useEffect} from "react";
import {LOGIN, SEND_OTP_TOKEN} from "../data/apiRoutes";
import useRequest from "@/lib/app/hooks/useRequest";
import {useDirection, useRequest} from "@witel/webapp-builder";
const ResendToken = ({initialTimerValue, timer, setTimer, PhoneNumber, disabled}) => {
const t = useTranslations();

View File

@@ -1,48 +1,53 @@
import {FormControl, FormHelperText, InputLabel, MenuItem, Select,} from "@mui/material";
import {useTranslations} from "next-intl";
function SelectBox({
select,
selectType,
name,
selectors,
label,
setFieldValue,
setFieldTouched,
handleChange,
error,
onBlur,
schema,
disabled,
helperText,
isLoading,
errorEcured
}) {
const t = useTranslations();
const selectId = String(select);
const handleChange = (event) => {
setFieldValue(selectType, event.target.value);
};
const handleBlur = () => {
setFieldTouched(select, true);
};
return (
<FormControl
variant="outlined"
margin="normal"
disabled={disabled || false}
variant={"outlined"}
fullWidth
size="small"
error={error}
sx={{mt: 0}}
>
<InputLabel id="language-label">{label}</InputLabel>
<InputLabel>{label}</InputLabel>
<Select
labelId="language-label"
id={selectId}
name={selectId}
label={label}
name={name}
size="small"
value={select}
value={isLoading ? "" : select}
onChange={handleChange}
label="Language"
onBlur={handleBlur}
onBlur={onBlur}
>
{selectors.map((selector) => (
<MenuItem key={selector.id} value={selector.value}>
{selector.name}
{isLoading ? (
<MenuItem sx={{color: "primary.main"}}>{t("text_field_loading")}</MenuItem>
) : errorEcured ? (
<MenuItem sx={{color: "secondary.main"}}>
{t("text_field_error_fetching")}
</MenuItem>
))}
) : (
selectors.map((selector) => (
<MenuItem key={selector.id} value={selector[schema.value]}>
{selector[schema.name]}
</MenuItem>
))
)}
</Select>
<FormHelperText>{helperText}</FormHelperText>
</FormControl>

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

@@ -0,0 +1,96 @@
import {useTheme} from "@mui/material";
const SvgDone = ({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 680.83858 584.23207">
<path id="b9ccae5a-ffdd-4f5c-9c1e-05af9f0f3372-220" data-name="Path 438"
d="M310.70569,694.02818a24.21459,24.21459,0,0,0,23.38269-4.11877c8.18977-6.87441,10.758-18.196,12.8467-28.68191l6.17973-31.01657-12.9377,8.90837c-9.30465,6.40641-18.81826,13.01866-25.26011,22.29785s-9.25223,21.94707-4.07792,31.988"
transform="translate(-259.58071 -157.88396)" fill="#e6e6e6"/>
<path id="f4ad1d06-bd03-4ced-a5c4-c19a65ab4ee5-221" data-name="Path 439"
d="M312.7034,733.73874c-1.62839-11.86368-3.30382-23.88078-2.15884-35.87167,1.01467-10.64932,4.26373-21.04881,10.87831-29.57938a49.20592,49.20592,0,0,1,12.62466-11.44039c1.26215-.79648,2.42409,1.20354,1.16733,1.997a46.77949,46.77949,0,0,0-18.50446,22.32562c-4.02857,10.24607-4.67545,21.41582-3.98154,32.3003.41944,6.58218,1.31074,13.1212,2.20588,19.65251a1.19817,1.19817,0,0,1-.808,1.42251,1.16348,1.16348,0,0,1-1.42253-.808Z"
transform="translate(-259.58071 -157.88396)" fill="#f2f2f2"/>
<path id="baf785f8-b4c6-42cf-85bd-8a16037845f7-222" data-name="Path 442"
d="M324.42443,714.70229a17.82513,17.82513,0,0,0,15.53141,8.01862c7.8644-.37318,14.41806-5.85973,20.31713-11.07027l17.452-15.4088-11.54987-.55281c-8.30619-.39784-16.82672-.771-24.73813,1.79338s-15.20758,8.72639-16.654,16.91541"
transform="translate(-259.58071 -157.88396)" fill="#e6e6e6"/>
<path id="a14e4330-7125-4e03-a856-d6453c34f6cc-223" data-name="Path 443"
d="M308.10042,740.55843c7.83972-13.87142,16.93234-29.28794,33.1808-34.21552a37.02609,37.02609,0,0,1,13.95545-1.441c1.48189.128,1.11179,2.41174-.367,2.28454a34.39833,34.39833,0,0,0-22.27164,5.89215c-6.27994,4.27453-11.16975,10.21755-15.30781,16.51907-2.53511,3.86051-4.80576,7.88445-7.07642,11.903C309.48824,742.78513,307.36641,741.85759,308.10042,740.55843Z"
transform="translate(-259.58071 -157.88396)" fill="#f2f2f2"/>
<path id="ac20a106-7eb8-4a45-8835-674ef3bf3222-224" data-name="Path 141"
d="M935.3957,569.31654H503.18092a5.03014,5.03014,0,0,1-5.02359-5.02359V162.90754a5.03017,5.03017,0,0,1,5.02359-5.02358H935.3957a5.03017,5.03017,0,0,1,5.02359,5.02358V564.292a5.02922,5.02922,0,0,1-5.02359,5.02359Z"
transform="translate(-259.58071 -157.88396)" fill="#fff"/>
<path id="a8878079-c7cd-406f-a434-8b15b914b9b4-225" data-name="Path 141"
d="M935.3957,569.31654H503.18092a5.03014,5.03014,0,0,1-5.02359-5.02359V162.90754a5.03017,5.03017,0,0,1,5.02359-5.02358H935.3957a5.03017,5.03017,0,0,1,5.02359,5.02358V564.292a5.02922,5.02922,0,0,1-5.02359,5.02359ZM503.18092,159.88944a3.01808,3.01808,0,0,0-3.01152,3.01151V564.292a3.01808,3.01808,0,0,0,3.01152,3.01152H935.3957a3.01717,3.01717,0,0,0,3.01153-3.01152V162.90754a3.01809,3.01809,0,0,0-3.01153-3.01151Z"
transform="translate(-259.58071 -157.88396)" fill="#cacaca"/>
<path id="af64f961-e9a2-4c53-a333-5060c7f850d2-226" data-name="Path 142"
d="M707.41023,262.18528a3.41053,3.41053,0,0,0,0,6.82105H894.55305a3.41053,3.41053,0,0,0,0-6.82105Z"
transform="translate(-259.58071 -157.88396)" fill="#e4e4e4"/>
<path id="baad4cfb-158d-4439-9cc3-22475bf47b22-227" data-name="Path 143"
d="M707.41023,282.65037a3.41054,3.41054,0,0,0,0,6.82106h95.54019a3.41054,3.41054,0,0,0,0-6.82106Z"
transform="translate(-259.58071 -157.88396)" fill="#e4e4e4"/>
<path id="f3456279-91e5-49ad-aa43-9838b26fb6ca-228" data-name="Path 142"
d="M543.84146,392.7046a3.41054,3.41054,0,0,0,0,6.82106h350.8937a3.41054,3.41054,0,0,0,0-6.82106Z"
transform="translate(-259.58071 -157.88396)" fill="#e4e4e4"/>
<path id="a3288adf-49f8-485f-8ae9-1e4f1a13d849-229" data-name="Path 143"
d="M543.84146,413.1697a3.41054,3.41054,0,0,0,0,6.82106H803.13254a3.41054,3.41054,0,0,0,0-6.82106Z"
transform="translate(-259.58071 -157.88396)" fill="#e4e4e4"/>
<path id="e63a5b48-5a7d-40a2-b9b0-6adec326348a-230" data-name="Path 142"
d="M543.84146,433.17177a3.41054,3.41054,0,0,0,0,6.82106h350.8937a3.41054,3.41054,0,0,0,0-6.82106Z"
transform="translate(-259.58071 -157.88396)" fill="#e4e4e4"/>
<path id="a1c669b4-dfc3-4cfa-a7be-66b71399844d-231" data-name="Path 143"
d="M543.84146,453.63687a3.41054,3.41054,0,0,0,0,6.82106H803.13254a3.41054,3.41054,0,0,0,0-6.82106Z"
transform="translate(-259.58071 -157.88396)" fill="#e4e4e4"/>
<path id="bfec50d1-ffb1-4de6-a9ef-a1085e40e016-232" data-name="Path 142"
d="M543.84146,474.17177a3.41054,3.41054,0,0,0,0,6.82106h350.8937a3.41054,3.41054,0,0,0,0-6.82106Z"
transform="translate(-259.58071 -157.88396)" fill="#e4e4e4"/>
<path id="bc9696ec-ec99-41d5-9116-3ad9737a38ac-233" data-name="Path 143"
d="M543.84146,494.63687a3.41054,3.41054,0,0,0,0,6.82106H803.13254a3.41054,3.41054,0,0,0,0-6.82106Z"
transform="translate(-259.58071 -157.88396)" fill="#e4e4e4"/>
<path d="M599.41943,324.82812a49,49,0,1,1,48.99952-49A49.05567,49.05567,0,0,1,599.41943,324.82812Z"
transform="translate(-259.58071 -157.88396)" fill={fillColor}/>
<path
d="M450.67833,510.10041a12.24754,12.24754,0,0,0-14.953-11.36231l-16.19641-22.82521-16.27138,6.45945,23.32519,31.91237a12.31392,12.31392,0,0,0,24.09559-4.1843Z"
transform="translate(-259.58071 -157.88396)" fill="#a0616a"/>
<path
d="M419.11211,508.40888l-49.00774-63.57777L388.46714,387.12c1.34563-14.50936,10.425-18.56089,10.81135-18.72645l.5893-.25281,15.979,42.6119-11.73235,31.28625,28.79671,48.4319Z"
transform="translate(-259.58071 -157.88396)" fill="#3f3d56"/>
<path
d="M589.30794,312.41993a12.24758,12.24758,0,0,0-10.17219,15.78672l-21.50463,17.91269,7.69816,15.72326,30.01343-25.72272a12.31392,12.31392,0,0,0-6.03477-23.69995Z"
transform="translate(-259.58071 -157.88396)" fill="#a0616a"/>
<path
d="M590.06206,344.02244l-59.59835,53.77665-58.95815-13.84578c-14.57-.21979-19.31136-8.9587-19.50629-9.33113l-.29761-.568,41.2489-19.22578,32.0997,9.27828,46.06046-32.45509Z"
transform="translate(-259.58071 -157.88396)" fill="#3f3d56"/>
<polygon points="227.248 568.437 243.261 568.436 250.878 506.672 227.245 506.673 227.248 568.437"
fill="#a0616a"/>
<path
d="M483.39733,721.74476h50.32614a0,0,0,0,1,0,0V741.189a0,0,0,0,1,0,0h-36.207a14.11914,14.11914,0,0,1-14.11914-14.11914v-5.32505A0,0,0,0,1,483.39733,721.74476Z"
transform="translate(757.57348 1305.02654) rotate(179.99738)" fill="#2f2e41"/>
<polygon points="163.247 568.437 179.26 568.436 186.878 506.672 163.245 506.673 163.247 568.437"
fill="#a0616a"/>
<path
d="M419.397,721.74476H469.7231a0,0,0,0,1,0,0V741.189a0,0,0,0,1,0,0h-36.207A14.11914,14.11914,0,0,1,419.397,727.06981v-5.32505a0,0,0,0,1,0,0Z"
transform="translate(629.57273 1305.02946) rotate(179.99738)" fill="#2f2e41"/>
<polygon
points="157.552 342.991 158.858 434.42 160.165 554.584 188.899 551.972 203.267 386.094 221.553 551.972 251.218 551.972 254.206 384.788 243.757 348.216 157.552 342.991"
fill="#2f2e41"/>
<path
d="M473.37417,513.1531c-31.26533.00239-60.04471-14.14839-60.43319-14.34263l-.32273-.16136-2.62373-62.96637c-.76082-2.22509-15.74263-46.13091-18.28-60.08625-2.57083-14.13882,34.68842-26.54742,39.213-27.99853l1.02678-11.37405,41.75366-4.49918,5.292,14.5536,14.97942,5.6168a7.40924,7.40924,0,0,1,4.59212,8.7043l-8.32539,33.85619,20.33325,112.01266-4.37755.18946C495.709,511.39658,484.38425,513.1525,473.37417,513.1531Z"
transform="translate(-259.58071 -157.88396)" fill="#3f3d56"/>
<circle cx="454.46738" cy="294.45965" r="30.06284"
transform="matrix(0.87745, -0.47966, 0.47966, 0.87745, -345.12824, 96.19037)" fill="#a0616a"/>
<path
d="M430.1166,323.56132c5.72926,6.10289,16.36927,2.82672,17.1158-5.51069a10.07153,10.07153,0,0,0-.01268-1.94523c-.38544-3.69311-2.519-7.046-2.008-10.94542a5.73974,5.73974,0,0,1,1.05046-2.687c4.56548-6.11359,15.28263,2.73444,19.59138-2.8,2.642-3.39359-.46364-8.73664,1.56381-12.52956,2.67591-5.006,10.60183-2.53654,15.57222-5.27809,5.53017-3.05032,5.1994-11.53517,1.55907-16.6961-4.43955-6.294-12.22348-9.65241-19.91044-10.13643s-15.32094,1.59394-22.4974,4.39069c-8.15392,3.17767-16.23969,7.56925-21.25749,14.739-6.10218,8.71919-6.68942,20.44132-3.6376,30.63677C419.10222,311.0013,425.43805,318.57766,430.1166,323.56132Z"
transform="translate(-259.58071 -157.88396)" fill="#2f2e41"/>
<path d="M641.58071,741.9626h-381a1,1,0,0,1,0-2h381a1,1,0,0,1,0,2Z"
transform="translate(-259.58071 -157.88396)" fill="#cacaca"/>
<path
d="M596.58984,294.33545a3.488,3.488,0,0,1-2.38134-.93555l-16.15723-15.00732a3.49994,3.49994,0,0,1,4.76367-5.12891l13.68555,12.71192,27.07666-27.07618a3.5,3.5,0,1,1,4.94922,4.9502l-29.46094,29.46094A3.49275,3.49275,0,0,1,596.58984,294.33545Z"
transform="translate(-259.58071 -157.88396)" fill="#fff"/>
</svg>
)
}
export default SvgDone

View File

@@ -0,0 +1,91 @@
import {useTheme} from "@mui/material";
const SvgMaintenance = ({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 945 583.2243">
<path id="b0cf38c7-358d-48dc-a444-e9782686dfa5-97" data-name="Path 438"
d="M222.88482,693.5243a24.21463,24.21463,0,0,0,23.38269-4.11877c8.18977-6.87442,10.758-18.196,12.84671-28.68191l6.17972-31.01657-12.93769,8.90836c-9.30465,6.40641-18.81827,13.01866-25.26012,22.29785s-9.25222,21.94708-4.07792,31.988"
transform="translate(-127.5 -158.38785)" fill="#e6e6e6"/>
<path id="acbbaa8b-0bc2-4bc9-96d7-93dd88f90f19-98" data-name="Path 439"
d="M224.88254,733.23486c-1.6284-11.86369-3.30382-23.88079-2.15885-35.87167,1.01467-10.64933,4.26374-21.04881,10.87831-29.57938a49.20592,49.20592,0,0,1,12.62466-11.44039c1.26216-.79648,2.4241,1.20354,1.16733,1.997A46.77938,46.77938,0,0,0,228.88954,680.666c-4.02858,10.24607-4.67546,21.41582-3.98154,32.30029.41943,6.58218,1.31074,13.12121,2.20587,19.65252a1.19817,1.19817,0,0,1-.808,1.4225,1.16348,1.16348,0,0,1-1.42253-.808Z"
transform="translate(-127.5 -158.38785)" fill="#f2f2f2"/>
<path id="bed63620-84ae-4c66-a7c8-0d9b30f8c1e7-99" data-name="Path 442"
d="M236.60356,714.19841A17.82515,17.82515,0,0,0,252.135,722.217c7.8644-.37318,14.41806-5.85972,20.31713-11.07026l17.452-15.40881-11.54988-.5528c-8.30619-.39784-16.82672-.771-24.73813,1.79338s-15.20758,8.72639-16.654,16.9154"
transform="translate(-127.5 -158.38785)" fill="#e6e6e6"/>
<path id="fc4eb8ca-daad-4589-b224-2c71eec1c546-100" data-name="Path 443"
d="M220.27955,740.05454c7.83972-13.87142,16.93235-29.28794,33.1808-34.21551A37.02589,37.02589,0,0,1,267.4158,704.398c1.4819.128,1.1118,2.41174-.367,2.28454a34.39824,34.39824,0,0,0-22.27164,5.89215c-6.27994,4.27453-11.16975,10.21755-15.30781,16.51907-2.5351,3.8605-4.80576,7.88445-7.07642,11.903C221.66738,742.28125,219.54555,741.35371,220.27955,740.05454Z"
transform="translate(-127.5 -158.38785)" fill="#f2f2f2"/>
<path id="ec83fa9a-7bb4-4b90-a90b-e1cec4444963-101" data-name="Path 442"
d="M1008.35714,710.03894a17.82515,17.82515,0,0,1-17.065,3.78282c-7.50783-2.37076-12.4416-9.35006-16.813-15.89522l-12.93426-19.3574,11.30757,2.41744c8.132,1.73826,16.46493,3.55513,23.45819,8.05635s12.47224,12.32329,11.77771,20.61"
transform="translate(-127.5 -158.38785)" fill="#e6e6e6"/>
<path id="e32935a9-e392-46b4-9a67-68c65f1cc9fa-102" data-name="Path 443"
d="M1017.53074,739.20841c-4.03412-15.41439-8.88464-32.64277-23.33408-41.55944a37.026,37.026,0,0,0-13.12367-4.9599c-1.46539-.255-1.69126,2.04749-.22905,2.30247a34.39822,34.39822,0,0,1,20.02606,11.3886c4.9789,5.73758,8.18741,12.733,10.57751,19.88279,1.46425,4.38021,2.63106,8.85084,3.79926,13.31624C1015.61991,741.00647,1017.90833,740.652,1017.53074,739.20841Z"
transform="translate(-127.5 -158.38785)" fill="#f2f2f2"/>
<path
d="M858.65906,513.48057H594.33494a7.97081,7.97081,0,0,1-7.96157-7.96157V416.34942a7.97081,7.97081,0,0,1,7.96157-7.96157H858.65906a7.9707,7.9707,0,0,1,7.96157,7.96157V505.519A7.9707,7.9707,0,0,1,858.65906,513.48057Z"
transform="translate(-127.5 -158.38785)" fill="#f1f1f1"/>
<rect x="500.27353" y="273.88471" width="70.06181" height="7.96157" fill={fillColor}/>
<circle cx="662.68956" cy="278.66165" r="4.77694" fill={fillColor}/>
<circle cx="678.6127" cy="278.66165" r="4.77694" fill={fillColor}/>
<circle cx="694.53583" cy="278.66165" r="4.77694" fill={fillColor}/>
<path
d="M858.65906,626.53486H594.33494a7.97081,7.97081,0,0,1-7.96157-7.96157V529.40371a7.97081,7.97081,0,0,1,7.96157-7.96157H858.65906a7.9707,7.9707,0,0,1,7.96157,7.96157v89.16958A7.9707,7.9707,0,0,1,858.65906,626.53486Z"
transform="translate(-127.5 -158.38785)" fill="#f1f1f1"/>
<rect x="500.27353" y="386.939" width="70.06181" height="7.96157" fill={fillColor}/>
<circle cx="662.68956" cy="391.71594" r="4.77694" fill={fillColor}/>
<circle cx="678.6127" cy="391.71594" r="4.77694" fill={fillColor}/>
<circle cx="694.53583" cy="391.71594" r="4.77694" fill={fillColor}/>
<path
d="M858.65906,739.58915H594.33494a7.97081,7.97081,0,0,1-7.96157-7.96156V642.458a7.97081,7.97081,0,0,1,7.96157-7.96157H858.65906a7.9707,7.9707,0,0,1,7.96157,7.96157v89.16959A7.97069,7.97069,0,0,1,858.65906,739.58915Z"
transform="translate(-127.5 -158.38785)" fill="#f1f1f1"/>
<rect x="500.27353" y="499.99329" width="70.06181" height="7.96157" fill={fillColor}/>
<circle cx="662.68956" cy="504.77024" r="4.77694" fill={fillColor}/>
<circle cx="678.6127" cy="504.77024" r="4.77694" fill={fillColor}/>
<circle cx="694.53583" cy="504.77024" r="4.77694" fill={fillColor}/>
<path
d="M759.01179,321.31453l-.29232-.216-.007-.00528a3.57626,3.57626,0,0,0-5.00334.73888L694.21319,401.9379h-6.54562v-1.53783a1.43056,1.43056,0,0,0-1.43052-1.43052h-3.90531a1.43056,1.43056,0,0,0-1.43052,1.43052v1.53783h-2.26034v-1.53783a1.43056,1.43056,0,0,0-1.43052-1.43052h-3.9053a1.43057,1.43057,0,0,0-1.43053,1.43052v1.53783h-2.253v-1.53783a1.43057,1.43057,0,0,0-1.43053-1.43052h-3.9053a1.43056,1.43056,0,0,0-1.43052,1.43052v1.53783h-2.26034v-1.53783a1.43056,1.43056,0,0,0-1.43052-1.43052H655.259a1.43057,1.43057,0,0,0-1.43053,1.43052v1.53783h-2.253v-1.53783a1.43057,1.43057,0,0,0-1.43053-1.43052h-3.9053a1.43034,1.43034,0,0,0-1.43052,1.43052v1.53783H642.5488v-1.53783a1.43056,1.43056,0,0,0-1.43052-1.43052H637.213a1.43057,1.43057,0,0,0-1.43053,1.43052v1.53783h-2.26034v-1.53783a1.43056,1.43056,0,0,0-1.43052-1.43052h-3.9053a1.43057,1.43057,0,0,0-1.43053,1.43052v1.53783h-2.253v-1.53783a1.43056,1.43056,0,0,0-1.43052-1.43052h-3.9053a1.43057,1.43057,0,0,0-1.43053,1.43052v1.53783h-2.26034v-1.53783a1.43056,1.43056,0,0,0-1.43052-1.43052h-3.9053a1.43057,1.43057,0,0,0-1.43053,1.43052v1.53783h-2.253v-1.53783a1.43056,1.43056,0,0,0-1.43052-1.43052h-3.90531a1.43056,1.43056,0,0,0-1.43052,1.43052v1.53783H596.858a3.57618,3.57618,0,0,0-3.57631,3.57631V408.926a3.57618,3.57618,0,0,0,3.57631,3.57631h96.76151a5.32944,5.32944,0,0,0,5.21778-4.24744l.037.02751L759.74521,326.325l.00559-.00707A3.57671,3.57671,0,0,0,759.01179,321.31453Z"
transform="translate(-127.5 -158.38785)" fill="#3f3d56"/>
<polygon points="255.504 550.825 267.523 556.976 296.966 513.544 279.228 504.466 255.504 550.825"
fill="#ffb7b7"/>
<path
d="M381.94612,703.72021l23.66983,12.113.001.00049a16.94563,16.94563,0,0,1,7.3641,22.8037l-.25087.49017-38.75427-19.83265Z"
transform="translate(-127.5 -158.38785)" fill="#2f2e41"/>
<polygon points="394.317 567.652 407.819 567.652 414.242 515.575 394.315 515.575 394.317 567.652"
fill="#ffb7b7"/>
<path
d="M518.37323,721.632l26.58922-.00107h.00107a16.94563,16.94563,0,0,1,16.94473,16.94446v.55063l-43.53421.00162Z"
transform="translate(-127.5 -158.38785)" fill="#2f2e41"/>
<path
d="M458.66285,301.26058s-9.0412-6.9268-9.0412,9.99291l-1.42756,55.91271,15.94107,52.81966,9.27913-17.1307-3.80682-37.11652Z"
transform="translate(-127.5 -158.38785)" fill="#2f2e41"/>
<path
d="M555.37734,454.19165s10.48418,51.058-2.79579,92.95976l-7.33892,161.10695-26.90941-2.09684-9.43576-119.51969-9.0863-59.41037-18.87153,54.16828L418.73348,696.02681l-28.65677-22.36625s31.75629-86.64916,55.2167-104.84184l11.76615-134.011Z"
transform="translate(-127.5 -158.38785)" fill="#2f2e41"/>
<circle cx="523.48018" cy="202.31369" r="30.68251"
transform="translate(-32.63308 406.2144) rotate(-61.33685)" fill="#ffb7b7"/>
<path
d="M521.30883,208.82157c3.67807.47856,6.45254-3.28467,7.73937-6.76336s2.2673-7.5286,5.46514-9.40772c4.36891-2.56725,9.95872.52048,14.95479-.32655,5.64211-.95654,9.31053-6.93607,9.598-12.65146s-1.987-11.21239-4.21862-16.48193l-.77911,6.54884a12.98679,12.98679,0,0,0-5.67512-11.35154l1.00419,9.60941A10.199,10.199,0,0,0,537.6641,159.558l.15814,5.72564c-6.51663-.77489-13.09021-1.55087-19.62615-.96052s-13.11751,2.65828-18.0712,6.96269c-7.40994,6.43874-10.11621,17.04088-9.20765,26.81529s4.94341,18.95675,9.14865,27.827c1.058,2.23177,2.52146,4.75016,4.97491,5.03445,2.20445.25544,4.22155-1.58732,4.90683-3.698a13.40422,13.40422,0,0,0-.0595-6.58474c-.62-3.2946-1.40155-6.6621-.81863-9.96346s2.9579-6.56372,6.28582-6.96831,6.73322,3.3994,5.13321,6.34537Z"
transform="translate(-127.5 -158.38785)" fill="#2f2e41"/>
<polygon points="429.776 309.458 327.357 302.85 335.066 270.913 428.674 291.838 429.776 309.458"
fill="#cbcbcb"/>
<path
d="M494.11389,244.15824l6.424-8.91878s7.18028,2.44014,26.40983,12.01183l1.35857,8.35615L561.685,460.9092,501.01375,458.292l-16.5219-.35153-5.4068-12.12-6.66725,11.86313-16.13421-.34328-16.41693-9.51706,16.179-49.48869,5.23438-45.206-8.0895-42.58883s-10.17937-39.10848,29.027-60.19538Z"
transform="translate(-127.5 -158.38785)" fill="#2f2e41"/>
<path
d="M630.86271,399.57305a10.48605,10.48605,0,0,1-13.04108-9.40577L581.82654,380.526l14.48005-12.85707,31.9616,11.12669a10.54289,10.54289,0,0,1,2.59452,20.77747Z"
transform="translate(-127.5 -158.38785)" fill="#ffb7b7"/>
<path
d="M614.74112,394.13009a6.14221,6.14221,0,0,1-4.987.39563l-59.21317-22.47383a63.2273,63.2273,0,0,1-34.03214-28.68449l-24.6929-44.04893a19.76509,19.76509,0,1,1,29.49118-26.323L564.04672,340.737l53.7869,31.53448a6.15547,6.15547,0,0,1,2.61747,6.54608l-2.67464,11.33426a6.1431,6.1431,0,0,1-1.86626,3.15032A6.07364,6.07364,0,0,1,614.74112,394.13009Z"
transform="translate(-127.5 -158.38785)" fill="#2f2e41"/>
<path d="M1071.5,741.38785h-943a1,1,0,0,1,0-2h943a1,1,0,0,1,0,2Z" transform="translate(-127.5 -158.38785)"
fill="#cbcbcb"/>
</svg>
)
}
export default SvgMaintenance

View File

@@ -0,0 +1,35 @@
import {
TimelineConnector,
TimelineContent,
TimelineDot,
TimelineItem,
TimelineOppositeContent,
TimelineSeparator
} from "@mui/lab";
import DoneIcon from "@mui/icons-material/Done";
import {Typography} from "@mui/material";
const DoneTimeLine = ({date, primaryLabel, secondaryLabel, position}) => {
return (
<TimelineItem>
<TimelineOppositeContent
sx={{m: 'auto 0'}}
align="right"
variant="body2"
color="text.secondary"
>{date}</TimelineOppositeContent>
<TimelineSeparator>
<TimelineDot color={'success'}>
<DoneIcon/>
</TimelineDot>
{position !== 'end' && <TimelineConnector/>}
</TimelineSeparator>
<TimelineContent sx={{py: '12px', px: 2}}>
<Typography variant="h6" component="span">{primaryLabel}</Typography>
<Typography variant="body2" color={'gray'}>{secondaryLabel}</Typography>
</TimelineContent>
</TimelineItem>
)
}
export default DoneTimeLine

View File

@@ -0,0 +1,33 @@
import {
TimelineConnector,
TimelineContent,
TimelineDot,
TimelineItem,
TimelineOppositeContent,
TimelineSeparator
} from "@mui/lab";
import {CircularProgress, Typography} from "@mui/material";
const InProgressTimeLine = ({date, primaryLabel, secondaryLabel, position}) => {
return (
<TimelineItem sx={{minHeight: 90}}>
<TimelineOppositeContent
sx={{m: 'auto 0'}}
variant="body2"
color="text.secondary"
>{date}</TimelineOppositeContent>
<TimelineSeparator>
<TimelineDot color="primary">
<CircularProgress color={'inherit'} size={40}/>
</TimelineDot>
{position !== 'end' && <TimelineConnector/>}
</TimelineSeparator>
<TimelineContent sx={{py: '12px', px: 2}}>
<Typography variant="h6" component="span">{primaryLabel}</Typography>
<Typography variant="body2" color={'gray'}>{secondaryLabel}</Typography>
</TimelineContent>
</TimelineItem>
)
}
export default InProgressTimeLine

View File

@@ -0,0 +1,34 @@
import {
TimelineConnector,
TimelineContent,
TimelineDot,
TimelineItem,
TimelineOppositeContent,
TimelineSeparator
} from "@mui/lab";
import CallMadeIcon from "@mui/icons-material/CallMade";
import {Typography} from "@mui/material";
const OpenTimeLine = ({date, primaryLabel, secondaryLabel, position}) => {
return (
<TimelineItem>
<TimelineOppositeContent
sx={{m: 'auto 0'}}
variant="body2"
color="text.secondary"
>{date}</TimelineOppositeContent>
<TimelineSeparator>
<TimelineDot>
<CallMadeIcon/>
</TimelineDot>
{position !== 'end' && <TimelineConnector/>}
</TimelineSeparator>
<TimelineContent sx={{py: '12px', px: 2}}>
<Typography variant="h6" component="span">{primaryLabel}</Typography>
<Typography variant="body2" color={'gray'}>{secondaryLabel}</Typography>
</TimelineContent>
</TimelineItem>
)
}
export default OpenTimeLine

View File

@@ -0,0 +1,16 @@
import OpenTimeLine from "@/core/components/timelines/OpenTimeLine";
import InProgressTimeLine from "@/core/components/timelines/InProgressTimeLine";
import DoneTimeLine from "@/core/components/timelines/DoneTimeLine";
const TimelineManager = (props) => {
switch (props.status) {
case 'open':
return <OpenTimeLine {...props} />
case 'in progress':
return <InProgressTimeLine {...props} />
case 'done':
return <DoneTimeLine {...props} />
}
}
export default TimelineManager

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 SEND_OTP_TOKEN = BASE_URL + "/otp";
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 SEND_LOAN_REQUEST_NAVGAN = BASE_URL + "/navgan/loan/store";
export const SHOW_LOAN_REQUEST_WELFARE = BASE_URL + "/refahi/loan/index";
@@ -13,5 +14,8 @@ export const SEND_LOAN_REQUEST_WELFARE = BASE_URL + "/refahi/loan/store";
export const UPDATE_LOAN_REQUEST_WELFARE = BASE_URL + "/refahi/loan/update/";
export const DETAILS_LOAN_REQUEST_WELFARE = BASE_URL + "/refahi/loan/details/";
export const GET_PROVINCE_LIST = BASE_URL + "/provinces";
export const GET_CITIES_LIST = BASE_URL + "/cities";
export const GET_EDUCATIONS_LIST = BASE_URL + "/education";
export const GET_OCCUPATIONS_LIST = BASE_URL + "/occupations";

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

@@ -0,0 +1,37 @@
import SpaceDashboardIcon from "@mui/icons-material/SpaceDashboard";
import BookmarkAddedIcon from "@mui/icons-material/BookmarkAdded";
import DataSaverOnIcon from "@mui/icons-material/DataSaverOn";
const sidebarMenu = [
[
{
key: "sidebar.dashboard",
name: "sidebar.dashboard",
type: "page",
route: "/dashboard",
icon: <SpaceDashboardIcon sx={{width: 'inherit', height: 'inherit'}}/>,
selected: false,
permission: "all"
},
{
key: "sidebar.add-request-loan",
name: "sidebar.add-request-loan",
type: "page",
route: "/dashboard/navgan/add-request-loan",
icon: <DataSaverOnIcon sx={{width: 'inherit', height: 'inherit'}}/>,
selected: false,
permission: "can_request_navgan_loan"
},
{
key: "sidebar.followUp-loan",
name: "sidebar.followUp-loan",
type: "page",
route: "/dashboard/navgan/followUp-loan",
icon: <BookmarkAddedIcon sx={{width: 'inherit', height: 'inherit'}}/>,
selected: false,
permission: "all"
},
],
];
export default sidebarMenu;

View File

@@ -1,55 +0,0 @@
import SpaceDashboardIcon from "@mui/icons-material/SpaceDashboard";
import BookmarkAddedIcon from "@mui/icons-material/BookmarkAdded";
import DataSaverOnIcon from "@mui/icons-material/DataSaverOn";
const sidebarMenu = [
[
{
key: "sidebar.dashboard",
type: "page",
route: "/dashboard",
icon: <SpaceDashboardIcon/>,
userType: 0,
selected: false,
permission_name: "all"
},
{
key: "sidebar.add-request-loan",
type: "page",
route: "/dashboard/navgan/add-request-loan",
icon: <DataSaverOnIcon/>,
userType: 1,
selected: false,
permission_name: "can_request_a_new_loan"
},
{
key: "sidebar.followUp-loan",
type: "page",
route: "/dashboard/navgan/followUp-loan",
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,
permission_name: "all"
},
],
];
export default sidebarMenu;

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

@@ -0,0 +1,12 @@
import {parse} from "next-useragent";
export const globalServerProps = async ({req, locale}) => {
const {isBot} = parse(req.headers["user-agent"]);
return {
config: (await import(`&/config.json`)).default,
messages: (await import(`&/locales/${locale}/app.json`)).default,
isBot,
locale,
}
}

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;

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

@@ -0,0 +1,29 @@
import {DashboardLayout as DashboardLayoutPure, useUser} from "@witel/webapp-builder";
import headerProfileItems from "@/core/data/headerProfileItems";
import sidebarMenu from "@/core/data/sidebarMenu";
import {GET_SIDEBAR_NOTIFICATION} from "@/core/data/apiRoutes";
import UserInfo from "@/middlewares/UserInfo";
import {useTranslations} from "next-intl";
import moment from "jalali-moment";
const DashboardLayout = (props) => {
const {user} = useUser()
const middlewaresProps = props.middlewaresProps || {}
const t = useTranslations()
return (
<>
<DashboardLayoutPure
middlewares={{list: [UserInfo], props: {...middlewaresProps,loginUrl:'/login'}}}
user_introduction={user.phone_number}
headerProfileItems={headerProfileItems}
headerSubtitle={`${t('today')} ${moment().locale('fa').format('LL')}`}
sidebarMenu={sidebarMenu}
urlNotification={GET_SIDEBAR_NOTIFICATION}
BC_segmentsToRemove={['navgan']}
{...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

@@ -0,0 +1,38 @@
import {useEffect, useState} from "react";
import {useRequest} from "@witel/webapp-builder";
import {useTranslations} from "next-intl";
import {GET_CITIES_LIST} from "@/core/data/apiRoutes";
const useCities = () => {
const t = useTranslations();
const [provinceID, setProvinceID] = useState(null)
const [cityList, setCityList] = useState([]);
const [cityTextField, setCityTextField] = useState(t("text_field_city_id"));
const [isLoadingCityList, setIsLoadingCityList] = useState(false)
const requestServer = useRequest({auth: true, notification: false})
useEffect(() => {
if (provinceID === null) return
setIsLoadingCityList(true)
setCityTextField(t("text_field_loading_cities_list"))
requestServer(`${GET_CITIES_LIST}?province_id=${provinceID}`, "get", {auth: true, notification: false})
.then(({data}) => {
const result = data.data;
const formattedData = result.map((city, index) => ({
id: index,
name: city.name,
value: city.id,
}));
setCityList(formattedData);
setIsLoadingCityList(false)
setCityTextField(t("text_field_city_id"))
}).catch(() => {
setIsLoadingCityList(false)
setCityTextField(t("text_field_error_fetching_cities"))
});
}, [provinceID])
return {cityTextField, cityList, setProvinceID, isLoadingCityList}
}
export default useCities

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

@@ -0,0 +1,29 @@
import {GET_EDUCATIONS_LIST} from "@/core/data/apiRoutes";
import {useRequest} from "@witel/webapp-builder";
import {useEffect, useState} from "react";
const useEducations = () => {
const [isLoadingEducationsList, setIsLoadingEducationsList] = useState(false)
const [errorEducationsList, setErrorEducationsList] = useState(false)
const [educationsList, setEducationsList] = useState([])
const requestServer = useRequest({auth: true, notification: false})
useEffect(() => {
setIsLoadingEducationsList(true)
requestServer(GET_EDUCATIONS_LIST, 'get', {auth: true, notification: false}).then(({data}) => {
setIsLoadingEducationsList(false)
setEducationsList(data.data);
}).catch(() => {
setIsLoadingEducationsList(false)
setErrorEducationsList(true)
})
}, [])
return {
educationsList,
isLoadingEducationsList,
errorEducationsList
}
};
export default useEducations;

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;

Some files were not shown because too many files have changed in this diff Show More