Merge branch 'release/v1.30.0'

This commit is contained in:
AmirHossein Mahmoodi
2024-01-23 15:57:16 +03:30
45 changed files with 1186 additions and 282 deletions

4
.gitignore vendored
View File

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

View File

@@ -1,5 +1,5 @@
NEXT_PUBLIC_API_NAME = "Loan Facilities Dashboard"
NEXT_PUBLIC_API_VERSION = "1.29.1"
NEXT_PUBLIC_API_VERSION = "1.30.0"
NEXT_PUBLIC_DEFAULT_LANGUAGE = "fa"
NEXT_PUBLIC_DEFAULT_DIRECTION = "rtl"
@@ -9,6 +9,8 @@ NEXT_PUBLIC_SECONDARY_MAIN = "#FF4E00"
NEXT_PUBLIC_BASE_URL = "https://loan.witel.ir"
NEXT_PUBLIC_POWERED_BY_URL = "https://witel.ir"
NEXT_PUBLIC_CONFIG_APP_URL = "http://localhost:3000/config.json"
#["NEXT_PUBLIC_HAS_WIDGET" , "NEXT_PUBLIC_HAS_NOTIFICATION"]
NEXT_PUBLIC_HAS_VALUE=["NEXT_PUBLIC_HAS_NOTIFICATION"]

View File

@@ -0,0 +1,53 @@
{
"middlewares": {
"/dashboard/navgan/add-request-loan":{
"status" : false,
"title":"اطلاع رسانی",
"message": "ثبت نام به زودی آغاز خواهد شد."
}
},
"add_request_loan_birthday_range": {
"min": 1327,
"max": 1380
},
"interest_rate": "15.2",
"loan_repayment": "6",
"deadlines": {
"register": {
"date": {
"from": "1402/10/02",
"to": "1402/10/15"
},
"messages": {
"before": "ثبت نام هنوز شروع نشده",
"after": "زمان ثبت نام به اتمام رسید"
},
"timeline_label": {
"primary": "ثبت نام متقاضی",
"secondary": "ثبت نام متقاضیان واجد شرایط"
}
},
"validation": {
"date": {
"from": "1402/10/15",
"to": "1402/10/30"
},
"messages": {},
"timeline_label": {
"primary": "صحت سنجی متقاضیان",
"secondary": "بررسی و کارشناسی درخواست ها"
}
},
"done": {
"date": {
"from": "1402/11/01",
"to": "1402/11/20"
},
"messages": {},
"timeline_label": {
"primary": "ارجاع به بانک",
"secondary": "فرآیند معرفی ضامن و پرداخت"
}
}
}
}

View File

@@ -44,6 +44,7 @@
"dashboard": "داشبورد",
"reports": "گزارشات",
"passenger-office-chief": "اداره مسافر",
"admin-setting": "تنظیمات ",
"machinery-expert": "کارتابل کارشناسی",
"passenger-boss": "کارتابل کارگروه",
"transportation-assistant": "بررسی وام های تبصره 18",
@@ -121,6 +122,7 @@
"edit_profile": "ویرایش پروفایل",
"expert_management": "مدیریت کارشناسان",
"user_management_page": "مدیریت کاربر",
"admin_setting": "تنظیمات",
"loan_history": "کارتابل نظارت",
"total_number_loan_offers": "تعداد کل وام های پیشنهادی",
"total_amount_proposed_loan": "مبلغ کل وام های پیشنهادی",
@@ -147,6 +149,13 @@
"button_while_submit": "در حال ثبت",
"button_submit": "ثبت"
},
"AdminSetting": {
"id": "کد یکتا",
"edit": "ویرایش",
"name": "نام انگلیسی",
"name_fa": "نام فارسی",
"value": "مقدار"
},
"UpdateProfile": {
"error_invalid_email": "ایمیل نامعتبر است",
"typography_edit_profile": "پروفایل",
@@ -184,7 +193,7 @@
"vehicle_type": "نوع ماشین",
"state_name": "وضعیت درخواست",
"confirm": "ارجاع به کارشناس ماشین آلات",
"upload_file_unit": "فایل بارگذاری شده باید حداکثر 2Mb باشد",
"upload_file_unit": "فایل بارگذاری شده باید حداکثر 5Mb باشد",
"upload_file_format": "فرمت قابل قبول : png,jpg,pdf",
"excel_report": "گزارش اکسل"
},
@@ -222,7 +231,7 @@
"vehicle_type": "نوع ماشین",
"state_name": "وضعیت درخواست",
"confirm": "تایید نهایی و ارجاع به بانک",
"upload_file_unit": "فایل بارگذاری شده باید حداکثر 2Mb باشد",
"upload_file_unit": "فایل بارگذاری شده باید حداکثر 5Mb باشد",
"upload_file_format": "فرمت قابل قبول : png,jpg,pdf",
"excel_report": "گزارش اکسل"
},
@@ -254,7 +263,7 @@
"upload_file": "صورت جلسه کارگروه را بارگذاری کنید",
"upload_file_first_required": "وارد کردن فرم الف الزامیست",
"upload_file_second_required": "وارد کردن فرم ب الزامیست",
"upload_file_unit": "فایل بارگذاری شده باید حداکثر 2Mb باشد",
"upload_file_unit": "فایل بارگذاری شده باید حداکثر 5Mb باشد",
"upload_file_format": "فرمت قابل قبول : png,jpg,pdf",
"print_form_a": "چاپ فرم الف",
"print_form_b": "چاپ فرم ب",
@@ -358,7 +367,7 @@
"vehicle_type": "نوع ماشین",
"state_name": "وضعیت درخواست",
"confirm": "ارجاع به مدیر کل",
"upload_file_unit": "فایل بارگذاری شده باید حداکثر 2Mb باشد",
"upload_file_unit": "فایل بارگذاری شده باید حداکثر 5Mb باشد",
"upload_file_format": "فرمت قابل قبول : png,jpg,pdf",
"excel_report": "گزارش اکسل",
"confirm_text": "فرم الف و ب به رویت و تایید رسید."
@@ -398,7 +407,7 @@
"confirm": "ارجاع به کارگروه",
"upload_file": "گزارش کارشناسی را بارگذاری کنید",
"upload_file_required": "وارد کردن گزارش کارشناسی الزامیست",
"upload_file_unit": "فایل بارگذاری شده باید حداکثر 2Mb باشد",
"upload_file_unit": "فایل بارگذاری شده باید حداکثر 5Mb باشد",
"upload_file_format": "فرمت قابل قبول : png,jpg,pdf",
"print_report": "چاپ گزارش کارشناسی",
"max_amount": "حداکثر مبلغ پیشنهادی {value} میلیون ریال می باشد",
@@ -469,7 +478,8 @@
"button-reject": "عدم تایید",
"description": "توضیحات خود را وارد نمائید",
"optional": " (اختیاری)",
"description_error": "وارد کردن توضیحات الزامی است!"
"description_error": "وارد کردن توضیحات الزامی است!",
"choose_file" : "انتخاب فایل"
},
"UpdateDialog": {
"update": "ویرایش",
@@ -481,8 +491,16 @@
"button-cancel": "بستن",
"refahi": "رفاهی",
"navgan": "ناوگان",
"setting_name": "نام انگلیسی",
"setting_name_fa": "نام فارسی",
"value": "مقدار",
"phone_number": "شماره تلفن",
"national_id": "کد ملی",
"name_string": "نام می بایست متن باشد",
"setting_name_error": "وارد کردن نام انگلیسی الزامیست",
"setting_name_fa_error": "وارد کردن نام فارسی الزامیست",
"value_error": "وارد کردن مقدار الزامیست",
"value_string": "مقدار می بایست متن باشد",
"name": "نام انگلیسی",
"name_error": "وارد کردن نام انگلیسی الزامیست",
"name_fa": "نام فارسی",
@@ -576,6 +594,9 @@
},
"AddDialog": {
"add": "افزودن",
"setting_name": "نام انگلیسی",
"setting_name_fa": "نام فارسی",
"value": "مقدار",
"name": "نام انگلیسی",
"name_error": "وارد کردن نام انگلیسی الزامیست",
"name_fa": "نام فارسی",
@@ -594,6 +615,11 @@
"national_id_error": "وارد کردن کد ملی الزامیست",
"type_id_error": "وارد کردن نوع کاربر الزامیست",
"navgan_id_error": "وارد کردن کد ناوگان الزامیست",
"name_string": "نام می بایست متن باشد",
"setting_name_error": "وارد کردن نام انگلیسی الزامیست",
"setting_name_fa_error": "وارد کردن نام فارسی الزامیست",
"value_error": "وارد کردن مقدار الزامیست",
"value_string": "مقدار می بایست متن باشد",
"refahi": "رفاهی",
"navgan": "ناوگان",
"type_id": "نوع کاربر",
@@ -628,6 +654,8 @@
"choose_year": "انتخاب سال",
"provinces": "استان های",
"province": "استان",
"all_provinces": "تمامی استان ها",
"excel_export_reports": "خروجی به اکسل گزارش عملکرد",
"year": "سال",
"years": "سال های",
"text_field_loading_provinces_list": "درحال دریافت استان ها",
@@ -655,6 +683,7 @@
"tanafos_period": "دوره تنفس",
"sakht_period": "دوره ساخت",
"national_id": "کد ملی",
"postal_code": "کد پستی",
"phone_number": "موبایل",
"proposed_amount": "مبلغ پیشنهادی",
"approved_amount": "مبلغ تصویب شده",

View File

@@ -0,0 +1,107 @@
import {Button, DialogActions, DialogContent, Stack, TextField} from "@mui/material";
import {useTranslations} from "next-intl";
import useRequest from "@/lib/app/hooks/useRequest";
import * as Yup from "yup";
import {useFormik} from "formik";
import {ADD_ADMIN_SETTING} from "@/core/data/apiRoutes";
const CreateContent = ({mutate, setOpenConfirmDialog}) => {
const t = useTranslations();
const requestServer = useRequest({auth: true})
const validationSchema = Yup.object().shape({
name: Yup.string("AddDialog.name_string")
.required(t("AddDialog.setting_name_error")),
name_fa: Yup.string("AddDialog.name_fa_string")
.required(t("AddDialog.setting_name_fa_error")),
value: Yup.string("AddDialog.value_string")
.required(t("AddDialog.value_error")),
});
const formik = useFormik({
initialValues: {
name: "",
name_fa: "",
value: "",
}, validationSchema, onSubmit: (values, {setSubmitting}) => {
const formData = new FormData();
formData.append("name", values.name);
formData.append("name_fa", values.name_fa);
formData.append("value", values.value);
requestServer(ADD_ADMIN_SETTING, 'post', {
data: formData,
}).then((response) => {
setOpenConfirmDialog(false)
mutate()
update_notification()
}).catch(() => {
}).finally(() => {
setSubmitting(false);
});
},
});
return (
<>
<DialogContent>
<Stack spacing={2}>
<Stack>
<TextField
name="name_fa"
rows={8}
label={t("AddDialog.setting_name_fa")}
value={formik.values.name_fa}
onChange={formik.handleChange}
fullWidth
variant="outlined"
onBlur={formik.handleBlur("name_fa")}
error={formik.touched.name_fa && Boolean(formik.errors.name_fa)}
helperText={formik.touched.name_fa && formik.errors.name_fa}
sx={{mt: 1}}
/>
</Stack>
<Stack>
<TextField
name="name"
rows={8}
label={t("AddDialog.setting_name")}
value={formik.values.name}
onChange={formik.handleChange}
fullWidth
variant="outlined"
onBlur={formik.handleBlur("name")}
error={formik.touched.name && Boolean(formik.errors.name)}
helperText={formik.touched.name && formik.errors.name}
sx={{mt: 1}}
/>
</Stack>
<Stack>
<TextField
name="value"
label={t("AddDialog.value")}
value={formik.values.value}
onChange={formik.handleChange}
fullWidth
variant="outlined"
onBlur={formik.handleBlur("value")}
error={formik.touched.value && Boolean(formik.errors.value)}
helperText={formik.touched.value && formik.errors.value}
sx={{mt: 1}}
/>
</Stack>
</Stack>
</DialogContent>
<DialogActions>
<Button onClick={() => setOpenConfirmDialog(false)} variant="outlined" color="secondary"
disabled={formik.isSubmitting} autoFocus>
{t("AddDialog.button-cancel")}
</Button>
<Button onClick={formik.handleSubmit} variant="contained" color="primary"
disabled={formik.isSubmitting || !formik.dirty || !formik.isValid}>
{t("AddDialog.button-add")}
</Button>
</DialogActions>
</>
)
}
export default CreateContent

View File

@@ -0,0 +1,35 @@
import {Button, Dialog, DialogTitle, Stack, Tooltip} from "@mui/material";
import DataSaverOnIcon from "@mui/icons-material/DataSaverOn";
import {useTranslations} from "next-intl";
import {useState} from "react";
import CreateContent from "@/components/dashboard/admin-setting/Form/CreateForm/CreateContent";
const CreateForm = ({mutate}) => {
const t = useTranslations();
const [openConfirmDialog, setOpenConfirmDialog] = useState(false);
return (
<Stack direction={"row"} spacing={2}>
<Tooltip title={t("AddDialog.add")} arrow placement="right">
<Button
color="primary"
variant="contained"
size="small"
sx={{textTransform: "unset", alignSelf: "center"}}
startIcon={<DataSaverOnIcon/>}
onClick={() => {
setOpenConfirmDialog(true)
}}
>
{t("AddDialog.add")}
</Button>
</Tooltip>
<Dialog fullWidth open={openConfirmDialog}
PaperProps={{sx: {boxShadow: 'rgba(60, 64, 67, 0.3) 0px 1px 16px 0px, rgba(60, 64, 67, 0.15) 0px 1px 10px 0px'}}}>
<DialogTitle>{t("AddDialog.add")}</DialogTitle>
<CreateContent mutate={mutate} setOpenConfirmDialog={setOpenConfirmDialog}/>
</Dialog>
</Stack>
)
}
export default CreateForm

View File

@@ -0,0 +1,67 @@
import {useTranslations} from "next-intl";
import {useState} from "react";
import {
Button,
Dialog,
DialogActions,
DialogContent,
DialogTitle,
IconButton,
Tooltip,
Typography
} from "@mui/material";
import DeleteIcon from '@mui/icons-material/Delete';
import {DELETE_ADMIN_SETTING} from "@/core/data/apiRoutes";
import useRequest from "@/lib/app/hooks/useRequest";
import useNotification from "@/lib/app/hooks/useNotification";
const Delete = ({rowId, mutate}) => {
const t = useTranslations();
const [openConfirmDialog, setOpenConfirmDialog] = useState(false);
const [isSubmitting, setIsSubmitting] = useState(false)
const requestServer = useRequest({auth: true})
const {update_notification} = useNotification()
const handleSubmit = () => {
setIsSubmitting(true)
requestServer(`${DELETE_ADMIN_SETTING}/${rowId}`, 'delete').then((response) => {
mutate()
update_notification()
}).catch(() => {
}).finally(() => {
setIsSubmitting(false)
});
}
return (
<>
<Tooltip title={t("DeleteDialog.delete")}>
<IconButton
color="primary"
onClick={() => {
setOpenConfirmDialog(true)
}}
>
<DeleteIcon/>
</IconButton>
</Tooltip>
<Dialog maxWidth="sm" open={openConfirmDialog}
PaperProps={{sx: {boxShadow: 'rgba(60, 64, 67, 0.3) 0px 1px 16px 0px, rgba(60, 64, 67, 0.15) 0px 1px 10px 0px'}}}>
<DialogTitle>{t("DeleteDialog.delete")}</DialogTitle>
<DialogContent>
<Typography>{t("DeleteDialog.typography")}</Typography>
</DialogContent>
<DialogActions>
<Button onClick={() => setOpenConfirmDialog(false)} variant="outlined" color="secondary"
disabled={isSubmitting} autoFocus>
{t("DeleteDialog.button-cancel")}
</Button>
<Button onClick={handleSubmit} variant="contained" color="primary"
disabled={isSubmitting}>
{t("DeleteDialog.button-delete")}
</Button>
</DialogActions>
</Dialog>
</>
);
};
export default Delete;

View File

@@ -0,0 +1,109 @@
import {UPDATE_ADMIN_SETTING} from "@/core/data/apiRoutes";
import useNotification from "@/lib/app/hooks/useNotification";
import useRequest from "@/lib/app/hooks/useRequest";
import {Button, DialogActions, DialogContent, Stack, TextField} from "@mui/material"
import {useFormik} from "formik";
import {useTranslations} from "next-intl";
import * as Yup from "yup";
const UpdateContent = ({row, mutate, setOpenConfirmDialog}) => {
const t = useTranslations();
const requestServer = useRequest({auth: true})
const {update_notification} = useNotification()
const validationSchema = Yup.object().shape({
name: Yup.string("UpdateDialog.name_string")
.required(t("UpdateDialog.setting_name_error")),
name_fa: Yup.string("UpdateDialog.name_fa_string")
.required(t("UpdateDialog.setting_name_fa_error")),
value: Yup.string("UpdateDialog.value_string")
.required(t("UpdateDialog.value_error")),
});
const formik = useFormik({
initialValues: {
name: row.getValue("name"),
name_fa: row.getValue("name_fa"),
value: row.getValue("value"),
}, validationSchema, onSubmit: (values, {setSubmitting}) => {
const formData = new FormData();
formData.append("name", values.name);
formData.append("name_fa", values.name_fa);
formData.append("value", values.value);
requestServer(`${UPDATE_ADMIN_SETTING}/${row.getValue("id")}`, 'post', {
data: formData,
}).then((response) => {
setOpenConfirmDialog(false)
mutate()
update_notification()
}).catch(() => {
}).finally(() => {
setSubmitting(false);
});
},
});
return (
<>
<DialogContent>
<Stack spacing={2}>
<Stack>
<TextField
name="name_fa"
rows={8}
label={t("UpdateDialog.setting_name_fa")}
value={formik.values.name_fa}
onChange={formik.handleChange}
fullWidth
variant="outlined"
onBlur={formik.handleBlur("name_fa")}
error={formik.touched.name_fa && Boolean(formik.errors.name_fa)}
helperText={formik.touched.name_fa && formik.errors.name_fa}
sx={{mt: 1}}
/>
</Stack>
<Stack>
<TextField
name="name"
rows={8}
label={t("UpdateDialog.setting_name")}
value={formik.values.name}
onChange={formik.handleChange}
fullWidth
variant="outlined"
onBlur={formik.handleBlur("name")}
error={formik.touched.name && Boolean(formik.errors.name)}
helperText={formik.touched.name && formik.errors.name}
sx={{mt: 1}}
/>
</Stack>
<Stack>
<TextField
name="value"
rows={8}
label={t("UpdateDialog.value")}
value={formik.values.value}
onChange={formik.handleChange}
fullWidth
variant="outlined"
onBlur={formik.handleBlur("value")}
error={formik.touched.value && Boolean(formik.errors.value)}
helperText={formik.touched.value && formik.errors.value}
sx={{mt: 1}}
/>
</Stack>
</Stack>
</DialogContent>
<DialogActions>
<Button onClick={() => setOpenConfirmDialog(false)} variant="outlined" color="secondary"
disabled={formik.isSubmitting} autoFocus>
{t("UpdateDialog.button-cancel")}
</Button>
<Button onClick={formik.handleSubmit} variant="contained" color="primary"
disabled={formik.isSubmitting || !formik.dirty || !formik.isValid}>
{t("UpdateDialog.button-update")}
</Button>
</DialogActions>
</>
)
}
export default UpdateContent

View File

@@ -0,0 +1,30 @@
import {Dialog, DialogTitle, IconButton, Tooltip} from "@mui/material"
import EditIcon from '@mui/icons-material/Edit';
import {useTranslations} from "next-intl";
import {useState} from "react";
import UpdateContent from "./UpdateContent";
const Update = ({row, mutate}) => {
const t = useTranslations();
const [openConfirmDialog, setOpenConfirmDialog] = useState(false);
return (
<>
<Tooltip title={t("UpdateDialog.update-tooltip")}>
<IconButton
color="primary"
onClick={() => {
setOpenConfirmDialog(true)
}}
>
<EditIcon/>
</IconButton>
</Tooltip>
<Dialog fullWidth open={openConfirmDialog}
PaperProps={{sx: {boxShadow: 'rgba(60, 64, 67, 0.3) 0px 1px 16px 0px, rgba(60, 64, 67, 0.15) 0px 1px 10px 0px'}}}>
<DialogTitle>{t("UpdateDialog.update")}</DialogTitle>
<UpdateContent row={row} mutate={mutate} setOpenConfirmDialog={setOpenConfirmDialog}/>
</Dialog>
</>
)
}
export default Update

View File

@@ -0,0 +1,20 @@
import {Box} from "@mui/material";
import Update from "src/components/dashboard/admin-setting/Form/UpdateForm";
import Delete from "@/components/dashboard/admin-setting/Form/DeleteForm";
const TableRow = ({row, mutate}) => {
return (
<Box sx={{display: "flex", flexWrap: "nowrap", gap: "8px"}}>
<Update
row={row}
mutate={mutate}
/>
<Delete
rowId={row.getValue("id")}
mutate={mutate}
/>
</Box>
);
};
export default TableRow;

View File

@@ -0,0 +1,11 @@
import {useTranslations} from "next-intl";
import CreateForm from "@/components/dashboard/admin-setting/Form/CreateForm";
function TableToolbar({mutate}) {
const t = useTranslations();
return <CreateForm mutate={mutate}/>
}
export default TableToolbar;

View File

@@ -0,0 +1,94 @@
import {Box, Typography} from "@mui/material";
import {useMemo} from "react";
import {GET_ADMIN_SETTING} from "@/core/data/apiRoutes";
import {useTranslations} from "next-intl";
import DataTable from "@/core/components/DataTable";
import TableRowActions from "@/components/dashboard/admin-setting/TableRowActions";
import TableToolbar from "@/components/dashboard/admin-setting/TableToolbar";
function DashboardAdminSettingComponent() {
const t = useTranslations();
const columns = useMemo(
() => [
{
accessorFn: (row) => row.id,
id: "id",
header: t("AdminSetting.id"),
enableColumnFilter: true,
datatype: "number",
filterFn: "equals",
columnFilterModeOptions: ["contains", "equals", "notEquals"],
Cell: ({renderedCellValue}) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
{
accessorFn: (row) => row.name_fa,
id: "name_fa",
header: t("AdminSetting.name_fa"),
enableColumnFilter: true,
datatype: "text",
filterFn: "contains",
columnFilterModeOptions: ["contains", "equals"],
Cell: ({renderedCellValue}) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
{
accessorFn: (row) => row.name,
id: "name",
header: t("AdminSetting.name"),
enableColumnFilter: true,
datatype: "text",
filterFn: "contains",
columnFilterModeOptions: ["contains", "equals"],
Cell: ({renderedCellValue}) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
{
accessorFn: (row) => row.value,
id: "value",
header: t("AdminSetting.value"),
enableColumnFilter: true,
datatype: "text",
filterFn: "contains",
columnFilterModeOptions: [
"equals",
"contains",
],
Cell: ({renderedCellValue}) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
],
[]
);
return (
<Box sx={{px: 3}}>
<DataTable
tableUrl={GET_ADMIN_SETTING}
columns={columns}
selectableRow={false}
enableCustomToolbar={true}
CustomToolbar={TableToolbar}
enableLastUpdate={true}
sorting={[{
id: 'id', desc: false
}]}
enablePinning={true}
enableDensityToggle={false}
initialState={{density: 'compact'}} //compact (small) //comfortable (medium) //spacious (large)
enableColumnFilters={true}
enableHiding={true}
enableFullScreenToggle={false}
enableGlobalFilter={false}
enableColumnResizing={false} // if you want true this you should change renderRowActions props ** see https://www.material-react-table.com/docs/guides/row-actions
enableRowActions={true}
TableRowAction={TableRowActions}
/>
</Box>
);
}
export default DashboardAdminSettingComponent;

View File

@@ -156,6 +156,22 @@ function DashboardLoanHistoryComponent() {
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
{
accessorFn: (row) => row.postal_code,
id: "postal_code",
header: t("LoanHistory.postal_code"),
enableColumnFilter: true,
datatype: "text",
filterFn: "contains",
columnFilterModeOptions: [
"equals",
"notEquals",
"contains",
],
Cell: ({renderedCellValue}) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
{
accessorFn: (row) =>
moment(row.created_at).locale("fa").format("HH:mm | yyyy/MM/DD"),
@@ -356,10 +372,12 @@ function DashboardLoanHistoryComponent() {
},
{
accessorFn: (row) => row.state_name,
id: "state_id",
id: "state_name",
header: t("LoanHistory.state_name"),
enableColumnFilter: false,
datatype: "numeric",
enableColumnFilter: true,
datatype: "text",
filterFn: "contains",
columnFilterModeOptions: ["contains", "equals", "notEquals"],
Cell: ({renderedCellValue}) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),

View File

@@ -7,7 +7,6 @@ import {
DialogContent,
FormHelperText,
Grid,
InputAdornment,
LinearProgress,
Stack,
TextField,
@@ -21,6 +20,7 @@ import PriceField from "@/core/components/PriceField";
import * as Yup from "yup";
import CustomAccordion from "@/core/components/CustomAccordion";
import ImageContent from "@/core/components/ImageContent";
import SelectBox from "@/core/components/SelectBox";
const vehicle_amount = {
"اتوبوس": 7000,
@@ -94,7 +94,7 @@ const ConfirmContent = ({mutate, setOpenConfirmDialog, rowId, vehicle_type, hand
confirm_img: Yup.mixed()
.required(t("MachinaryOffice.upload_file_required"))
.test('fileSize', `${t("MachinaryOffice.upload_file_unit")}`, (value) => {
return value.size <= 2 * 1024 * 1024;
return value.size <= 5 * 1024 * 1024;
})
.test('fileType', `${t("MachinaryOffice.upload_file_format")}`, (value) => {
const allowedTypes = ['image/jpg', 'image/jpeg', 'image/png', 'application/pdf'];
@@ -232,51 +232,93 @@ const ConfirmContent = ({mutate, setOpenConfirmDialog, rowId, vehicle_type, hand
/>
</Stack>
<Stack>
<TextField
<SelectBox
name="sakht_period"
label={t("ConfirmDialog.sakht_period")}
type={'number'}
size="small"
value={formik.values.sakht_period}
onChange={formik.handleChange}
fullWidth
variant="outlined"
sx={{mt: 1}}
onBlur={formik.handleBlur("sakht_period")}
error={
formik.touched.sakht_period &&
Boolean(formik.errors.sakht_period)
}
helperText={
formik.touched.sakht_period && formik.errors.sakht_period
}
InputProps={{
endAdornment: <InputAdornment
position="end">{t("ConfirmDialog.sakht_period_day")}</InputAdornment>,
selectType="sakht_period"
selectors={[
{
id: 1,
value: "30",
name: "1 ماه - 30 روز"
},
{
id: 2,
value: "60",
name: "2 ماه - 60 روز"
},
{
id: 3,
value: "90",
name: "3 ماه - 90 روز"
},
{
id: 4,
value: "120",
name: "4 ماه - 120 روز"
},
{
id: 5,
value: "150",
name: "5 ماه - 150 روز"
},
{
id: 6,
value: "180",
name: "6 ماه - 180 روز"
}
]}
schema={{value: 'value', name: 'name'}}
select={formik.values.sakht_period}
handleChange={(event) => {
formik.setFieldValue('sakht_period', event.target.value)
}}
setFieldTouched={formik.setFieldTouched}
error={formik.touched.sakht_period && Boolean(formik.errors.sakht_period)}
helperText={formik.touched.sakht_period && formik.errors.sakht_period}
onBlur={formik.handleBlur("sakht_period")}
/>
</Stack>
<Stack>
<TextField
<SelectBox
name="tanafos_period"
label={t("ConfirmDialog.tanafos_period")}
type={'number'}
size="small"
value={formik.values.tanafos_period}
onChange={formik.handleChange}
fullWidth
variant="outlined"
sx={{mt: 1}}
onBlur={formik.handleBlur("tanafos_period")}
error={
formik.touched.tanafos_period &&
Boolean(formik.errors.tanafos_period)
}
helperText={
formik.touched.tanafos_period && formik.errors.tanafos_period
}
InputProps={{
endAdornment: <InputAdornment
position="end">{t("ConfirmDialog.tanafos_period_month")}</InputAdornment>,
selectType="tanafos_period"
selectors={[
{
id: 1,
value: "1",
name: "1 ماه"
},
{
id: 2,
value: "2",
name: "2 ماه"
},
{
id: 3,
value: "3",
name: "3 ماه"
},
{
id: 4,
value: "4",
name: "4 ماه"
}
]}
schema={{value: 'value', name: 'name'}}
select={formik.values.tanafos_period}
handleChange={(event) => {
formik.setFieldValue('tanafos_period', event.target.value)
}}
setFieldTouched={formik.setFieldTouched}
error={formik.touched.tanafos_period && Boolean(formik.errors.tanafos_period)}
helperText={formik.touched.tanafos_period && formik.errors.tanafos_period}
onBlur={formik.handleBlur("tanafos_period")}
/>
</Stack>
<Stack>

View File

@@ -1,7 +1,7 @@
import UploadSystem from "@/core/components/UploadSystem"
import useNotification from "@/lib/app/hooks/useNotification";
import useRequest from "@/lib/app/hooks/useRequest";
import {Button, DialogActions, DialogContent, Stack, TextField, Typography} from "@mui/material"
import {Button, DialogActions, DialogContent, FormHelperText, Stack, TextField, Typography} from "@mui/material"
import {useFormik} from "formik";
import {useTranslations} from "next-intl";
import {useState} from "react";
@@ -19,6 +19,14 @@ const RejectContent = ({rowId, mutate, setOpenRejectDialog}) => {
const validationSchema = Yup.object().shape({
description: Yup.string().required(t("RejectDialog.description_error")),
reject_img: Yup.mixed().notRequired(t("RejectDialog.choose_file"))
.test('fileSize', `${t("MachinaryOffice.upload_file_unit")}`, (value) => {
return !value || (value.size && value.size <= 5 * 1024 * 1024);
})
.test('fileType', `${t("MachinaryOffice.upload_file_format")}`, (value) => {
const allowedTypes = ['image/jpg', 'image/jpeg', 'image/png', 'application/pdf']; // Define your allowed file types
return !value || (value.type && allowedTypes.includes(value.type));
}),
});
const formik = useFormik({
@@ -62,18 +70,14 @@ const RejectContent = ({rowId, mutate, setOpenRejectDialog}) => {
const handleUploadChange = (event) => {
const uploadedFile = event.target?.files?.[0];
if (uploadedFile) {
const maxFileSize = 2 * 1024 * 1024;
if (uploadedFile.size > maxFileSize) {
UploadFileNotification(t);
event.target.value = "";
return;
}
const fileType = event.target?.files?.[0].type;
const fileName = event.target?.files?.[0].name;
setSelectedImage(URL.createObjectURL(uploadedFile));
setfileType(fileType);
setfileName(fileName);
formik.setFieldValue("reject_img", uploadedFile);
formik.setFieldValue("reject_img", uploadedFile).then(() => {
formik.setFieldTouched("reject_img", true);
})
setShowAddIcon(false);
}
};
@@ -113,7 +117,17 @@ const RejectContent = ({rowId, mutate, setOpenRejectDialog}) => {
imageSize={[250 /*width*/, 150 /*height*/]}
setShowAddIcon={setShowAddIcon}
showAddIcon={showAddIcon}
onBlur={() => formik.handleBlur("reject_img")}
error={
formik.touched.reject_img &&
Boolean(formik.errors.reject_img)
}
/>
<FormHelperText
error={Boolean(formik.errors.reject_img)}
>
{formik.touched.reject_img && formik.errors.reject_img}
</FormHelperText>
</Stack>
</Stack>
</DialogContent>

View File

@@ -357,10 +357,12 @@ function DashboardNavganLoanManagementComponent() {
},
{
accessorFn: (row) => row.state_name,
id: "state_id",
id: "state_name",
header: t("NavganLoanManagement.state_name"),
enableColumnFilter: false,
datatype: "numeric",
enableColumnFilter: true,
datatype: "text",
filterFn: "contains",
columnFilterModeOptions: ["contains", "equals", "notEquals"],
Cell: ({renderedCellValue}) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),

View File

@@ -41,7 +41,7 @@ const ConfirmContent = ({rowId, mutate, setOpenConfirmDialog, handleSizeChangeCl
confirm_img: Yup.mixed().notRequired(t("ConfirmDialog.approved_amount_error"))
.test('fileSize', `${t("NavganProvinceManager.upload_file_unit")}`, (value) => {
if (!value) return true
return value.size <= 2 * 1024 * 1024;
return value.size <= 5 * 1024 * 1024;
})
.test('fileType', `${t("NavganProvinceManager.upload_file_format")}`, (value) => {
if (!value) return true

View File

@@ -1,7 +1,7 @@
import UploadSystem from "@/core/components/UploadSystem"
import useNotification from "@/lib/app/hooks/useNotification";
import useRequest from "@/lib/app/hooks/useRequest";
import {Button, DialogActions, DialogContent, Stack, TextField, Typography} from "@mui/material"
import {Button, DialogActions, DialogContent, FormHelperText, Stack, TextField, Typography} from "@mui/material"
import {useFormik} from "formik";
import {useTranslations} from "next-intl";
import {useState} from "react";
@@ -19,6 +19,14 @@ const RejectContent = ({rowId, mutate, setOpenRejectDialog}) => {
const validationSchema = Yup.object().shape({
description: Yup.string().required(t("RejectDialog.description_error")),
reject_img: Yup.mixed().notRequired(t("RejectDialog.choose_file"))
.test('fileSize', `${t("NavganProvinceManager.upload_file_unit")}`, (value) => {
return !value || (value.size && value.size <= 5 * 1024 * 1024);
})
.test('fileType', `${t("NavganProvinceManager.upload_file_format")}`, (value) => {
const allowedTypes = ['image/jpg', 'image/jpeg', 'image/png', 'application/pdf']; // Define your allowed file types
return !value || (value.type && allowedTypes.includes(value.type));
}),
});
const formik = useFormik({
@@ -62,21 +70,18 @@ const RejectContent = ({rowId, mutate, setOpenRejectDialog}) => {
const handleUploadChange = (event) => {
const uploadedFile = event.target?.files?.[0];
if (uploadedFile) {
const maxFileSize = 2 * 1024 * 1024;
if (uploadedFile.size > maxFileSize) {
UploadFileNotification(t);
event.target.value = "";
return;
}
const fileType = event.target?.files?.[0].type;
const fileName = event.target?.files?.[0].name;
setSelectedImage(URL.createObjectURL(uploadedFile));
setfileType(fileType);
setfileName(fileName);
formik.setFieldValue("reject_img", uploadedFile);
formik.setFieldValue("reject_img", uploadedFile).then(() => {
formik.setFieldTouched("reject_img", true);
})
setShowAddIcon(false);
}
};
return (
<>
<DialogContent>
@@ -113,7 +118,17 @@ const RejectContent = ({rowId, mutate, setOpenRejectDialog}) => {
imageSize={[250 /*width*/, 150 /*height*/]}
setShowAddIcon={setShowAddIcon}
showAddIcon={showAddIcon}
onBlur={() => formik.handleBlur("reject_img")}
error={
formik.touched.reject_img &&
Boolean(formik.errors.reject_img)
}
/>
<FormHelperText
error={Boolean(formik.errors.reject_img)}
>
{formik.touched.reject_img && formik.errors.reject_img}
</FormHelperText>
</Stack>
</Stack>
</DialogContent>

View File

@@ -3,9 +3,9 @@ import {useRouter} from "next/router";
import {useState} from "react";
import useRequest from "@/lib/app/hooks/useRequest";
import usePrint from "@/lib/app/hooks/usePrint";
import {GET_PASSENGER_BOSS} from "@/core/data/apiRoutes";
import {Button, CircularProgress} from "@mui/material";
import PrintIcon from "@mui/icons-material/Print";
import {GET_PASSENGER_BOSS} from "@/core/data/apiRoutes";
const PrintFormA = () => {
const t = useTranslations();
@@ -13,26 +13,64 @@ const PrintFormA = () => {
const [loading, setLoading] = useState(false)
const requestServer = useRequest({auth: true, pending: false, success: {notification: {show: false}}})
const {setPrintPage, setPrintTitle} = usePrint()
const clickHandler = () => {
setLoading(true)
const params = new URLSearchParams();
params.set("start", '0');
params.set("filters", '[]');
params.set("sorting", '[]');
requestServer(`${GET_PASSENGER_BOSS}?${params}`, 'get').then((response) => {
const _chunkSize = 6
const _data = response.data.data
const chunk = Array.from({length: Math.ceil(_data.length / _chunkSize)}, (v, i) =>
_data.slice(i * _chunkSize, i * _chunkSize + _chunkSize)
);
setPrintPage(chunk.length)
setPrintTitle(t('PassengerBoss.print_form_a'))
sessionStorage.setItem('form-a-print', JSON.stringify(chunk))
router.push('/dashboard/passenger-boss/prints/form-a')
}).catch(() => {
setLoading(false)
})
}
const filterRequest = async (filterArr) => {
setLoading(true);
const requests = filterArr.map(async (item) => {
try {
const params = new URLSearchParams();
params.set("start", '0');
params.set("filters", JSON.stringify([item]));
params.set("sorting", '[]');
const response = await requestServer(`${GET_PASSENGER_BOSS}?${params}`, 'get');
const _chunkSize = 6;
const _data = response.data.data;
const chunk = Array.from({length: Math.ceil(_data.length / _chunkSize)}, (v, i) =>
_data.slice(i * _chunkSize, i * _chunkSize + _chunkSize)
);
return chunk;
} catch (error) {
setLoading(false);
throw error; // Propagate the error
}
});
try {
const chunks = await Promise.all(requests);
setLoading(false);
return chunks.flat(); // Flatten the chunks into a single array
} catch (error) {
console.error("Error processing requests:", error);
throw error;
}
};
const clickHandler = async () => {
try {
const chunks = await filterRequest([
{
id: "vehicle_type",
value: "اتوبوس",
fn: "contains",
datatype: "text",
},
{
id: "vehicle_type",
value: "مینی بوس",
fn: "contains",
datatype: "text",
},
]);
setPrintPage(chunks.length);
setPrintTitle(t('PassengerBoss.print_form_a'));
sessionStorage.setItem('form-a-print', JSON.stringify(chunks));
router.push('/dashboard/passenger-boss/prints/form-a');
} catch (error) {
console.error("Error in clickHandler:", error);
}
};
return (
<Button
color="primary"

View File

@@ -101,7 +101,7 @@ const ConfirmContent = ({rowId, mutate, setOpenConfirmDialog, vehicle_type, hand
first_img: Yup.mixed()
.required(t("PassengerBoss.upload_file_first_required"))
.test('fileSize', `${t("PassengerBoss.upload_file_unit")}`, (value) => {
return value.size <= 2 * 1024 * 1024;
return value.size <= 5 * 1024 * 1024;
})
.test('fileType', `${t("PassengerBoss.upload_file_format")}`, (value) => {
const allowedTypes = ['image/jpg', 'image/jpeg', 'image/png', 'application/pdf']; // Define your allowed file types
@@ -110,7 +110,7 @@ const ConfirmContent = ({rowId, mutate, setOpenConfirmDialog, vehicle_type, hand
second_img: Yup.mixed()
.required(t("PassengerBoss.upload_file_second_required"))
.test('fileSize', `${t("PassengerBoss.upload_file_unit")}`, (value) => {
return value.size <= 2 * 1024 * 1024;
return value.size <= 5 * 1024 * 1024;
})
.test('fileType', `${t("PassengerBoss.upload_file_format")}`, (value) => {
const allowedTypes = ['image/jpg', 'image/jpeg', 'image/png', 'application/pdf']; // Define your allowed file types

View File

@@ -27,7 +27,7 @@ const RejectContent = ({rowId, mutate, setOpenRejectDialog}) => {
first_reject_img: Yup.mixed()
.required(t("PassengerBoss.upload_file_first_required"))
.test('fileSize', `${t("PassengerBoss.upload_file_unit")}`, (value) => {
return value.size <= 2 * 1024 * 1024;
return value.size <= 5 * 1024 * 1024;
})
.test('fileType', `${t("PassengerBoss.upload_file_format")}`, (value) => {
const allowedTypes = ['image/jpg', 'image/jpeg', 'image/png', 'application/pdf']; // Define your allowed file types
@@ -37,7 +37,7 @@ const RejectContent = ({rowId, mutate, setOpenRejectDialog}) => {
second_reject_img: Yup.mixed()
.required(t("PassengerBoss.upload_file_second_required"))
.test('fileSize', `${t("PassengerBoss.upload_file_unit")}`, (value) => {
return value.size <= 2 * 1024 * 1024;
return value.size <= 5 * 1024 * 1024;
})
.test('fileType', `${t("PassengerBoss.upload_file_format")}`, (value) => {
const allowedTypes = ['image/jpg', 'image/jpeg', 'image/png', 'application/pdf']; // Define your allowed file types

View File

@@ -11,75 +11,75 @@ const Content = ({data}) => {
<Typography fontSize={14} sx={{fontFamily: 'Bnazanin'}}>بسمه
تعالی</Typography>
<Typography fontSize={14} fontWeight={'bold'}
sx={{fontFamily: 'Bnazanin'}}>{`فرم صورتجلسه بررسی درخواست تسهیلات موضوع جزء 1 تا 9 بند (الف) تبصره (18) قانون بودجه سال 1401 کل کشور`}</Typography>
sx={{fontFamily: 'Bnazanin'}}>{`فرم صورتجلسه بررسی درخواست تسهیلات موضوع بند (الف) تبصره (18) قانون بودجه سال 1401 کل کشور`}</Typography>
</Stack>
<Typography fontSize={14} fontWeight={'bold'}
sx={{fontFamily: 'Bnazanin'}}>فرم الف</Typography>
</Stack>
<Box sx={{px: 1}}>
<Typography fontSize={14} sx={{fontFamily: 'Bnazanin'}}>با سلام و احترام</Typography>
<Typography fontSize={14} sx={{fontFamily: 'Bnazanin'}}>درخواست متقاضیان تسهیلات موضوع جزء 1 تا 9
بند (الف) تبصره (18) قانون بودجه سال 1401 کل کشور با خلاصه اطلاعات به شرح ذیل مطابق با جزء 1 تا
9 بند (الف) تبصره (18) قانون بودجه سال 1401 کل کشور، دستورالعمل های ذیربط و برنامه ابلاغی وزارت
<Typography fontSize={14} sx={{fontFamily: 'Bnazanin'}}>درخواست متقاضیان تسهیلات موضوع
بند (الف) تبصره (18) قانون بودجه سال 1401 کل کشور با خلاصه اطلاعات به شرح ذیل مطابق با بند (الف)
تبصره (18) قانون بودجه سال 1401 کل کشور، دستورالعمل های ذیربط و برنامه ابلاغی وزارت
متبوع و سایر مقررات مرتبط مورد بررسی و ارزیابی قرار گرفته و تایید می گردد.</Typography>
</Box>
</Box>
<Stack>
<table style={{
fontSize: '14px',
fontSize: '12px',
textAlign: 'center',
flexGrow: '1',
fontFamily: 'Bnazanin',
borderCollapse: 'collapse',
border: '1px solid'
}}>
<thead style={{background: '#f0f0f0'}}>
<thead style={{background: '#f0f0f0', fontWeight: 'bold'}}>
<tr>
<th rowSpan={2} style={{border: '1px solid'}}>ردیف</th>
<th rowSpan={2} style={{border: '1px solid'}}>نام متقاضی</th>
<th rowSpan={2} style={{border: '1px solid'}}>عنوان طرح</th>
<th rowSpan={2} style={{border: '1px solid', width: 100}}>عنوان طرح</th>
<th colSpan={2} style={{border: '1px solid'}}>رسته فعالیت</th>
<th colSpan={2} style={{border: '1px solid'}}>میزان تسهیلات مصوب<br/>(میلیارد ریال)</th>
<th colSpan={5} style={{border: '1px solid'}}>وضعیت طرح</th>
<th rowSpan={2} style={{
transform: "rotate(270deg)", border: '1px solid', width: 75
transform: "rotate(270deg)", border: '1px solid', width: 60
}}>درصد
پیشرفت<br/>فیزیکی
</th>
<th rowSpan={2} style={{
transform: "rotate(270deg)", border: '1px solid', width: 75
transform: "rotate(270deg)", border: '1px solid', width: 60
}}>شهرستان
محل<br/>اجرا
</th>
<th rowSpan={2}
style={{
transform: "rotate(270deg)", border: '1px solid', width: 75
transform: "rotate(270deg)", border: '1px solid', width: 60
}}>اشتغال<br/>تعهد شده
(نفر)
</th>
<th rowSpan={2} style={{
transform: "rotate(270deg)", border: '1px solid', width: 80
transform: "rotate(270deg)", border: '1px solid', width: 65
}}>پیش بینی
تاریخ<br/>بهره برداری پس<br/>از
اخذ تسهیلات
</th>
<th rowSpan={2} style={{
transform: "rotate(270deg)", border: '1px solid', width: 55
transform: "rotate(270deg)", border: '1px solid', width: 45
}}>بانک
عامل
</th>
<th rowSpan={2} style={{
transform: "rotate(270deg)", border: '1px solid', width: 50
transform: "rotate(270deg)", border: '1px solid', width: 35
}}>نرخ سود
(درصد)
</th>
<th rowSpan={2} style={{
transform: "rotate(270deg)", border: '1px solid', width: 55
transform: "rotate(270deg)", border: '1px solid', width: 45
}}>دوره تنفس
(ماه)
</th>
<th rowSpan={2} style={{
transform: "rotate(270deg)", border: '1px solid', width: 75
transform: "rotate(270deg)", border: '1px solid', width: 65
}}>طول
دوره<br/>بازپرداخت (ماه)
</th>
@@ -87,8 +87,8 @@ const Content = ({data}) => {
<tr>
<th style={{border: '1px solid'}}>تولیدی</th>
<th style={{border: '1px solid'}}>خدماتی</th>
<th style={{border: '1px solid'}}>در گردش</th>
<th style={{border: '1px solid'}}>ثابت</th>
<th style={{border: '1px solid', width: 75}}>در گردش</th>
<th style={{border: '1px solid', width: 75}}>ثابت</th>
<th style={{border: '1px solid'}}>جدید</th>
<th style={{border: '1px solid'}}>توسعه</th>
<th style={{border: '1px solid'}}>فعال</th>
@@ -135,22 +135,27 @@ const Content = ({data}) => {
<Typography fontSize={14} fontWeight={'bold'} sx={{fontFamily: 'Bnazanin'}}>با سلام و
احترام</Typography>
<Typography fontSize={14} fontWeight={'bold'} sx={{fontFamily: 'Bnazanin'}}>کمیته استانی موضوع
دستورالعمل جزء 1 تا 9 بند (الف) تبصره (18) قانون بودجه 1401 کل کشور با استناد به بررسی و تایید
دستورالعمل بند (الف) تبصره (18) قانون بودجه 1401 کل کشور با استناد به بررسی و تایید
دستگاه اجرایی ذیربط و توضیحات ارائه شده در جلسه، طرحهای فوق الذکر را بررسی و جهت انجام مراحل بعدی
تایید نمود.</Typography>
</Box>
<Stack sx={{border: 1, borderTop: 0, px: 1, height: 120}} direction={'row'}>
<Typography sx={{flexGrow: 1, fontFamily: 'Bnazanin'}} textAlign={'center'} fontSize={14}
fontWeight={'bold'}>رئیس
دستگاه تخصصی ذیربط طرح</Typography>
<Typography sx={{flexGrow: 1, fontFamily: 'Bnazanin'}} textAlign={'center'} fontSize={14}
<Stack sx={{border: 1, borderTop: 0, px: 1, height: 150}} spacing={2} direction={'row'}>
<Typography sx={{width: '12.5%', fontFamily: 'Bnazanin'}} textAlign={'center'} fontSize={12}
fontWeight={'bold'}>نهاد حمایتی<br/>(..............................)</Typography>
<Typography sx={{width: '12.5%', fontFamily: 'Bnazanin'}} textAlign={'center'} fontSize={12}
fontWeight={'bold'}>مدیر کل امور روستایی و شوراهای استانداری</Typography>
<Typography sx={{width: '12.5%', fontFamily: 'Bnazanin'}} textAlign={'center'} fontSize={12}
fontWeight={'bold'}>مدیر دستگاه اجرایی</Typography>
<Typography sx={{width: '12.5%', fontFamily: 'Bnazanin'}} textAlign={'center'} fontSize={12}
fontWeight={'bold'}>رئیس پارک علم و فناوری استان</Typography>
<Typography sx={{width: '12.5%', fontFamily: 'Bnazanin'}} textAlign={'center'} fontSize={12}
fontWeight={'bold'}>مدیر کل تعاون، کار و رفاه اجتماعی</Typography>
<Typography sx={{flexGrow: 1, fontFamily: 'Bnazanin'}} textAlign={'center'} fontSize={14}
<Typography sx={{width: '12.5%', fontFamily: 'Bnazanin'}} textAlign={'center'} fontSize={12}
fontWeight={'bold'}>رئیس سازمان مدیریت و برنامه ریزی</Typography>
<Typography sx={{flexGrow: 1, fontFamily: 'Bnazanin'}} textAlign={'center'} fontSize={14}
fontWeight={'bold'}>مدیر کل امور اقتصادی و دارایی<br/>و دبیر کمیته</Typography>
<Typography sx={{flexGrow: 1, fontFamily: 'Bnazanin'}} textAlign={'center'} fontSize={14}
fontWeight={'bold'}>معاون هماهنگی امور اقتصادی استانداری و<br/>رئیس کمیته</Typography>
<Typography sx={{width: '12.5%', fontFamily: 'Bnazanin'}} textAlign={'center'} fontSize={12}
fontWeight={'bold'}>مدیر کل امور اقتصادی و دارایی و دبیر کمیته</Typography>
<Typography sx={{width: '12.5%', fontFamily: 'Bnazanin'}} textAlign={'center'} fontSize={12}
fontWeight={'bold'}>معاون هماهنگی امور اقتصادی استانداری و رئیس کمیته</Typography>
</Stack>
</PrintablePage>

View File

@@ -57,4 +57,6 @@ const FormAComponent = () => {
)
}
export default FormAComponent
export default FormAComponent
// console.log(data)

View File

@@ -1,7 +1,9 @@
import {useEffect, useState} from "react";
import DoneIcon from '@mui/icons-material/Done';
import {useConfig} from "@/lib/app/hooks/useConfig";
const Items = ({data}) => {
const {config} = useConfig()
const [result, setResult] = useState([])
useEffect(() => {
@@ -42,8 +44,7 @@ const Items = ({data}) => {
<td style={{minHeight: 21, border: '1px solid'}}>{item ? (
<DoneIcon fontSize={'inherit'}/>) : (<>&nbsp;</>)}</td>
<td style={{minHeight: 21, border: '1px solid'}}>&nbsp;</td>
<td style={{minHeight: 21, border: '1px solid'}}>{item ? (
<DoneIcon fontSize={'inherit'}/>) : (<>&nbsp;</>)}</td>
<td style={{minHeight: 21, border: '1px solid'}}>&nbsp;</td>
<td style={{minHeight: 21, border: '1px solid'}}>&nbsp;</td>
<td style={{minHeight: 21, border: '1px solid'}}>&nbsp;</td>
<td style={{minHeight: 21, border: '1px solid'}}>&nbsp;</td>
@@ -54,9 +55,15 @@ const Items = ({data}) => {
<td style={{minHeight: 21, border: '1px solid'}}>{item?.vehicle_num || (<>&nbsp;</>)}</td>
<td style={{minHeight: 21, border: '1px solid'}}>&nbsp;</td>
<td style={{minHeight: 21, border: '1px solid'}}>{item ? (<>کارآفرینی امید</>) : (<>&nbsp;</>)}</td>
<td style={{minHeight: 21, border: '1px solid'}}>{item ? (<>15/2</>) : (<>&nbsp;</>)}</td>
<td style={{
minHeight: 21,
border: '1px solid'
}}>{item ? (<>{config.interest_rate}</>) : (<>&nbsp;</>)}</td>
<td style={{minHeight: 21, border: '1px solid'}}>{item?.tanafos_period || (<>&nbsp;</>)}</td>
<td style={{minHeight: 21, border: '1px solid'}}>{item ? (<>60</>) : (<>&nbsp;</>)}</td>
<td style={{
minHeight: 21,
border: '1px solid'
}}>{item ? (<>{config.loan_repayment}</>) : (<>&nbsp;</>)}</td>
</tr>))}
</>

View File

@@ -1,7 +1,9 @@
import PrintablePage from "@/core/components/PrintablePage";
import {Box, Stack, Typography} from "@mui/material";
import {useConfig} from "@/lib/app/hooks/useConfig";
const Content = ({data}) => {
const {config} = useConfig()
const varables = {
name: data.name,
national_code: data.national_id,
@@ -22,9 +24,7 @@ const Content = ({data}) => {
darkhasti_amount_gardesh: '........................',
darkhasti_amount_sabet: '........................',
dore_tanafos: data.tanafos_period || '',
avarde_moteghazi: 0,
avarde_dastgah: 0,
avarde_bank: 0,
dore_sakht: data.sakht_period || '',
sarmaye_all: '.......................',
bahrebardari: '.......................',
sarane: '................',
@@ -38,7 +38,7 @@ const Content = ({data}) => {
<Box
sx={{border: 1, bgcolor: '#f0f0f0'}}>
<Typography fontSize={14} fontWeight={'bold'} sx={{fontFamily: 'Bnazanin'}} textAlign={'center'}>
{`فرم خلاصه اطلاعات تایید شده طرح متقاضی استفاده از تسهیلات موضوع جزء 1 تا 9 بند (الف) تبصره (18) قانون بودجه سال 1401 کل کشور (فرم ب)`}
{`فرم خلاصه اطلاعات تایید شده طرح متقاضی استفاده از تسهیلات موضوع بند (الف) تبصره (18) قانون بودجه سال 1401 کل کشور (فرم ب)`}
</Typography>
</Box>
<Box sx={{mt: .5, border: 1, p: 0.5}}>
@@ -46,19 +46,19 @@ const Content = ({data}) => {
<Typography sx={{fontFamily: 'Bnazanin'}}>1-</Typography>
<Stack sx={{flexGrow: 1, pr: 20}}>
<Stack direction={'row'} justifyContent={'space-between'}>
<Typography fontSize={18}
<Typography fontSize={16}
sx={{fontFamily: 'Bnazanin'}}>{`نام متقاضی: ${varables.name}`}</Typography>
<Typography fontSize={18}
<Typography fontSize={16}
sx={{fontFamily: 'Bnazanin'}}>{`کد ملی: ${varables.national_code}`}</Typography>
</Stack>
<Stack direction={'row'} justifyContent={'space-between'}>
<Typography fontSize={18}
<Typography fontSize={16}
sx={{fontFamily: 'Bnazanin'}}>{`نام شرکت: ${varables.co}`}</Typography>
<Typography fontSize={18}
<Typography fontSize={16}
sx={{fontFamily: 'Bnazanin'}}>{`شناسه اقتصادی: ${varables.shenase_eqh}`}</Typography>
</Stack>
<Stack direction={'row'} justifyContent={'space-between'}>
<Typography fontSize={18}
<Typography fontSize={16}
sx={{fontFamily: 'Bnazanin'}}>{`نام مدیرعامل: ${varables.modiramel}`}</Typography>
</Stack>
</Stack>
@@ -69,16 +69,16 @@ const Content = ({data}) => {
<Typography sx={{fontFamily: 'Bnazanin'}}>2-</Typography>
<Stack sx={{flexGrow: 1, pr: 20}}>
<Typography
fontSize={18}
fontSize={16}
sx={{fontFamily: 'Bnazanin'}}>{`آدرس: استان: ${varables.province} شهرستان: ${varables.city} محل اجرای طرح: ${varables.mahalejra}`}</Typography>
<Stack direction={'row'} justifyContent={'space-between'}>
<Typography fontSize={18}
<Typography fontSize={16}
sx={{fontFamily: 'Bnazanin'}}>{`محل دفتر: ${varables.daftar}`}</Typography>
<Typography fontSize={18}
<Typography fontSize={16}
sx={{fontFamily: 'Bnazanin'}}>{`کد پستی: ${varables.post_code}`}</Typography>
</Stack>
<Typography
fontSize={18}
fontSize={16}
sx={{fontFamily: 'Bnazanin'}}>{`شماره تماس: ${varables.telephone} شماره همراه: ${varables.mobile}`}</Typography>
</Stack>
</Stack>
@@ -87,7 +87,7 @@ const Content = ({data}) => {
<Box sx={{p: 0.5, borderBottom: 1}}>
<Stack direction={'row'} spacing={2}>
<Typography sx={{fontFamily: 'Bnazanin'}}>3-</Typography>
<Typography fontSize={18}
<Typography fontSize={16}
sx={{fontFamily: 'Bnazanin'}}>{`عنوان طرح: ${varables.tarh_name}`}</Typography>
</Stack>
</Box>
@@ -95,12 +95,12 @@ const Content = ({data}) => {
<Stack direction={'row'} spacing={2}>
<Typography sx={{fontFamily: 'Bnazanin'}}>4-</Typography>
<Stack sx={{flexGrow: 1, pr: 20}}>
<Typography fontSize={18}
<Typography fontSize={16}
sx={{fontFamily: 'Bnazanin'}}>{`توضیح مطابقت طرح با برنامه ملی (شیوه نامه) ابلاغ شده دستگاه و سند آمایش استان: `}</Typography>
<Typography fontSize={16}
sx={{fontFamily: 'Bnazanin'}}>{`مطابق با بند ................................ برنامه دستگاه اجرایی ذیربط می باشد.`}</Typography>
<Stack sx={{mt: 1}} direction={'row'} alignItems={'center'} spacing={.5}>
<Typography fontSize={18}
<Typography fontSize={16}
sx={{
fontFamily: 'Bnazanin'
}}>{`مطابق با سند آمایش سرزمین و اولویتهای مربوط استان می باشد.`}</Typography>
@@ -114,15 +114,15 @@ const Content = ({data}) => {
<Stack direction={'row'} spacing={2}>
<Typography sx={{fontFamily: 'Bnazanin'}}>5-</Typography>
<Typography
fontSize={18}
sx={{fontFamily: 'Bnazanin'}}>{`اولویت مورد نظر (بر اساس موارد 5 و 6 دستورالعمل):`}</Typography>
fontSize={16}
sx={{fontFamily: 'Bnazanin'}}>{`اولویت مورد نظر (بر اساس موارد 6 و 7 دستورالعمل):`}</Typography>
</Stack>
</Box>
<Box sx={{p: 0.5, borderBottom: 1}}>
<Stack direction={'row'} spacing={2}>
<Typography sx={{fontFamily: 'Bnazanin'}}>6-</Typography>
<Typography
fontSize={18}
fontSize={16}
sx={{fontFamily: 'Bnazanin'}}>{`زمینه فعالیت در بخش اقتصادی: حمل و نقل زیر بخش اقتصادی: ناوگان مسافری دستگاه اجرایی ذیربط: سازمان راهداری و حمل و نقل جاده ای`}</Typography>
</Stack>
</Box>
@@ -131,9 +131,9 @@ const Content = ({data}) => {
<Typography sx={{fontFamily: 'Bnazanin'}}>7-</Typography>
<Stack sx={{flexGrow: 1, pr: 20}}>
<Stack direction={'row'} justifyContent={'space-between'}>
<Typography fontSize={18}
<Typography fontSize={16}
sx={{fontFamily: 'Bnazanin'}}>{`نوع مجوز: ${varables.mojavez_type}`}</Typography>
<Typography fontSize={18}
<Typography fontSize={16}
sx={{fontFamily: 'Bnazanin'}}>{`شماره و تاریخ مجوز: ${varables.mojavez_number}`}</Typography>
</Stack>
</Stack>
@@ -143,11 +143,11 @@ const Content = ({data}) => {
<Stack direction={'row'} spacing={2}>
<Typography sx={{fontFamily: 'Bnazanin'}}>8-</Typography>
<Typography
fontSize={18}
fontSize={16}
sx={{fontFamily: 'Bnazanin'}}>{`نوع تسهیلات:`}</Typography>
<Stack sx={{mt: 1}} direction={'row'} alignItems={'center'} spacing={.5}>
<Typography
fontSize={18}
fontSize={16}
sx={{
fontFamily: 'Bnazanin'
}}>{`در گردش`}</Typography>
@@ -156,7 +156,7 @@ const Content = ({data}) => {
</Stack>
<Stack sx={{mt: 1}} direction={'row'} alignItems={'center'} spacing={.5}>
<Typography
fontSize={18}
fontSize={16}
sx={{
fontFamily: 'Bnazanin'
}}>{`ثابت`}</Typography>
@@ -168,10 +168,10 @@ const Content = ({data}) => {
<Stack direction={'row'} spacing={2}>
<Typography sx={{fontFamily: 'Bnazanin'}}>9-</Typography>
<Typography
fontSize={18}
fontSize={16}
sx={{
fontFamily: 'Bnazanin'
}}>{`میزان تسهیلات درخواستی (میلیون ریال): ${varables.darkhasti_amount} در گردش ${varables.darkhasti_amount_gardesh} ثابت ${varables.darkhasti_amount_sabet}`}</Typography>
}}>{`میزان تسهیلات درخواستی (میلیون ریال): در گردش ${varables.darkhasti_amount_gardesh} ثابت ${varables.darkhasti_amount_sabet}`}</Typography>
</Stack>
</Box>
<Box sx={{p: 0.5, borderBottom: 1}}>
@@ -179,16 +179,16 @@ const Content = ({data}) => {
<Typography sx={{fontFamily: 'Bnazanin'}}>10-</Typography>
<Stack sx={{flexGrow: 1, pr: 5}}>
<Stack direction={'row'} justifyContent={'space-between'}>
<Typography fontSize={18}
<Typography fontSize={16}
sx={{fontFamily: 'Bnazanin'}}>{`نام بانک عامل: صندوق کارآفرینی امید`}</Typography>
<Typography fontSize={18}
sx={{fontFamily: 'Bnazanin'}}>{`نرخ سود (درصد): 15/3%`}</Typography>
<Typography fontSize={16}
sx={{fontFamily: 'Bnazanin'}}>{`نرخ سود (درصد): ${config.interest_rate}%`}</Typography>
<Stack direction={'row'} spacing={1}>
<Typography fontSize={18}
<Typography fontSize={16}
sx={{fontFamily: 'Bnazanin'}}>{`یارانه سود:`}</Typography>
<Stack direction={'row'} alignItems={'center'} spacing={.5}>
<Typography
fontSize={18}
fontSize={16}
sx={{
fontFamily: 'Bnazanin'
}}>{`دارد`}</Typography>
@@ -196,7 +196,7 @@ const Content = ({data}) => {
</Stack>
<Stack direction={'row'} alignItems={'center'} spacing={.5}>
<Typography
fontSize={18}
fontSize={16}
sx={{
fontFamily: 'Bnazanin'
}}>{`ندارد`}</Typography>
@@ -206,12 +206,12 @@ const Content = ({data}) => {
</Stack>
</Stack>
<Stack direction={'row'} justifyContent={'space-between'}>
<Typography fontSize={18}
<Typography fontSize={16}
sx={{fontFamily: 'Bnazanin'}}>{`دوره تنفس (ماه): ${varables.dore_tanafos}`}</Typography>
<Typography fontSize={18}
sx={{fontFamily: 'Bnazanin'}}>{`دوره مشارکت (ماه): ..........`}</Typography>
<Typography fontSize={18}
sx={{fontFamily: 'Bnazanin'}}>{`دوره بازپرداخت (ماه): 60`}</Typography>
<Typography fontSize={16}
sx={{fontFamily: 'Bnazanin'}}>{`دوره مشارکت (ماه): ${varables.dore_sakht / 30}`}</Typography>
<Typography fontSize={16}
sx={{fontFamily: 'Bnazanin'}}>{`دوره بازپرداخت (ماه): ${config.loan_repayment}`}</Typography>
</Stack>
</Stack>
</Stack>
@@ -219,10 +219,13 @@ const Content = ({data}) => {
<Box sx={{p: 0.5, borderBottom: 1}}>
<Stack direction={'row'} spacing={2}>
<Typography sx={{fontFamily: 'Bnazanin'}}>11-</Typography>
<Typography fontSize={18} sx={{fontFamily: 'Bnazanin'}}>آورده</Typography>
<Typography fontSize={16} sx={{fontFamily: 'Bnazanin'}}>آورده</Typography>
<table style={{
textAlign: 'center',
flexGrow: '1', fontFamily: 'Bnazanin', borderCollapse: 'collapse', border: '1px solid'
flexGrow: '1',
fontFamily: 'Bnazanin',
borderCollapse: 'collapse',
border: '1px solid'
}}>
<tbody>
<tr>
@@ -232,9 +235,9 @@ const Content = ({data}) => {
<td style={{border: '1px solid'}}>بانک عامل</td>
</tr>
<tr>
<td style={{border: '1px solid'}}>{varables.avarde_moteghazi}</td>
<td style={{border: '1px solid'}}>{varables.avarde_dastgah}</td>
<td style={{border: '1px solid'}}>{varables.avarde_bank}</td>
<td style={{border: '1px solid'}}>&nbsp;</td>
<td style={{border: '1px solid'}}>&nbsp;</td>
<td style={{border: '1px solid'}}>&nbsp;</td>
</tr>
</tbody>
</table>
@@ -243,11 +246,11 @@ const Content = ({data}) => {
<Box sx={{p: 0.5, borderBottom: 1}}>
<Stack direction={'row'} spacing={2}>
<Typography sx={{fontFamily: 'Bnazanin'}}>12-</Typography>
<Typography fontSize={18} sx={{fontFamily: 'Bnazanin'}}>وضعیت طرح</Typography>
<Typography fontSize={16} sx={{fontFamily: 'Bnazanin'}}>وضعیت طرح</Typography>
<Stack sx={{flexGrow: 1, pr: 8}} direction={'row'} justifyContent={'space-between'}>
<Stack direction={'row'} alignItems={'center'} spacing={.5}>
<Typography
fontSize={18}
fontSize={16}
sx={{
fontFamily: 'Bnazanin'
}}>{`جدید`}</Typography>
@@ -256,7 +259,7 @@ const Content = ({data}) => {
</Stack>
<Stack direction={'row'} alignItems={'center'} spacing={.5}>
<Typography
fontSize={18}
fontSize={16}
sx={{
fontFamily: 'Bnazanin'
}}>{`توسعه ای`}</Typography>
@@ -265,7 +268,7 @@ const Content = ({data}) => {
</Stack>
<Stack direction={'row'} alignItems={'center'} spacing={.5}>
<Typography
fontSize={18}
fontSize={16}
sx={{
fontFamily: 'Bnazanin'
}}>{`فعال`}</Typography>
@@ -274,7 +277,7 @@ const Content = ({data}) => {
</Stack>
<Stack direction={'row'} alignItems={'center'} spacing={.5}>
<Typography
fontSize={18}
fontSize={16}
sx={{
fontFamily: 'Bnazanin'
}}>{`نیمه تمام`}</Typography>
@@ -283,7 +286,7 @@ const Content = ({data}) => {
</Stack>
<Stack direction={'row'} alignItems={'center'} spacing={.5}>
<Typography
fontSize={18}
fontSize={16}
sx={{
fontFamily: 'Bnazanin'
}}>{`راکد`}</Typography>
@@ -296,32 +299,32 @@ const Content = ({data}) => {
<Box sx={{p: 0.5, borderBottom: 1}}>
<Stack direction={'row'} spacing={2}>
<Typography sx={{fontFamily: 'Bnazanin'}}>13-</Typography>
<Typography fontSize={18}
<Typography fontSize={16}
sx={{fontFamily: 'Bnazanin'}}>{`میزان پیشرفت فیزیکی طرح قبل از درخواست تسهیلات:`}</Typography>
</Stack>
</Box>
<Box sx={{p: 0.5, borderBottom: 1}}>
<Stack direction={'row'} spacing={2}>
<Typography sx={{fontFamily: 'Bnazanin'}}>14-</Typography>
<Typography fontSize={18}
<Typography fontSize={16}
sx={{fontFamily: 'Bnazanin'}}>{`پیش بینی تاریخ بهره برداری:`}</Typography>
</Stack>
</Box>
<Box sx={{p: 0.5, borderBottom: 1}}>
<Stack direction={'row'} spacing={2}>
<Typography sx={{fontFamily: 'Bnazanin'}}>15-</Typography>
<Typography fontSize={18}
<Typography fontSize={16}
sx={{fontFamily: 'Bnazanin'}}>{`میزان سرمایه گذاری (میلیون ریال): کل ${varables.sarmaye_all} برآورد منابع مالی مورد نیاز جهت بهره برداری پروژه: ${varables.bahrebardari}`}</Typography>
</Stack>
</Box>
<Box sx={{p: 0.5, borderBottom: 1}}>
<Stack direction={'row'} spacing={2}>
<Typography sx={{fontFamily: 'Bnazanin'}}>16-</Typography>
<Typography fontSize={18}
<Typography fontSize={16}
sx={{fontFamily: 'Bnazanin'}}>{`سابقه استفاده از تسهیلات تکلیفی بر اساس خود اظهاری متقاضی:`}</Typography>
<Stack direction={'row'} alignItems={'center'} spacing={.5}>
<Typography
fontSize={18}
fontSize={16}
sx={{
fontFamily: 'Bnazanin'
}}>{`دارد`}</Typography>
@@ -330,7 +333,7 @@ const Content = ({data}) => {
</Stack>
<Stack direction={'row'} alignItems={'center'} spacing={.5}>
<Typography
fontSize={18}
fontSize={16}
sx={{
fontFamily: 'Bnazanin'
}}>{`ندارد`}</Typography>
@@ -343,11 +346,11 @@ const Content = ({data}) => {
<Stack direction={'row'} spacing={2}>
<Stack>
<Stack direction={'row'} spacing={1}>
<Typography fontSize={18}
<Typography fontSize={16}
sx={{fontFamily: 'Bnazanin'}}>{`وضعیت بدهی ها بر اساس خود اظهاری متقاضی: مطالبات بانکی معقول:`}</Typography>
<Stack direction={'row'} alignItems={'center'} spacing={.5}>
<Typography
fontSize={18}
fontSize={16}
sx={{
fontFamily: 'Bnazanin'
}}>{`دارد`}</Typography>
@@ -356,18 +359,18 @@ const Content = ({data}) => {
</Stack>
<Stack direction={'row'} alignItems={'center'} spacing={.5}>
<Typography
fontSize={18}
fontSize={16}
sx={{
fontFamily: 'Bnazanin'
}}>{`ندارد`}</Typography>
<Box
sx={{border: 2, width: 15, height: 15}}></Box>
</Stack>
<Typography fontSize={18}
<Typography fontSize={16}
sx={{fontFamily: 'Bnazanin'}}>{`بدهی مالیاتی:`}</Typography>
<Stack direction={'row'} alignItems={'center'} spacing={.5}>
<Typography
fontSize={18}
fontSize={16}
sx={{
fontFamily: 'Bnazanin'
}}>{`دارد`}</Typography>
@@ -376,7 +379,7 @@ const Content = ({data}) => {
</Stack>
<Stack direction={'row'} alignItems={'center'} spacing={.5}>
<Typography
fontSize={18}
fontSize={16}
sx={{
fontFamily: 'Bnazanin'
}}>{`ندارد`}</Typography>
@@ -385,11 +388,11 @@ const Content = ({data}) => {
</Stack>
</Stack>
<Stack direction={'row'} spacing={1}>
<Typography fontSize={18}
<Typography fontSize={16}
sx={{fontFamily: 'Bnazanin'}}>{`چک برگشتی`}</Typography>
<Stack direction={'row'} alignItems={'center'} spacing={.5}>
<Typography
fontSize={18}
fontSize={16}
sx={{
fontFamily: 'Bnazanin'
}}>{`دارد`}</Typography>
@@ -398,18 +401,18 @@ const Content = ({data}) => {
</Stack>
<Stack direction={'row'} alignItems={'center'} spacing={.5}>
<Typography
fontSize={18}
fontSize={16}
sx={{
fontFamily: 'Bnazanin'
}}>{`ندارد`}</Typography>
<Box
sx={{border: 2, width: 15, height: 15}}></Box>
</Stack>
<Typography fontSize={18}
<Typography fontSize={16}
sx={{fontFamily: 'Bnazanin'}}>{`وثیقه (به جز طرح):`}</Typography>
<Stack direction={'row'} alignItems={'center'} spacing={.5}>
<Typography
fontSize={18}
fontSize={16}
sx={{
fontFamily: 'Bnazanin'
}}>{`دارد`}</Typography>
@@ -418,16 +421,16 @@ const Content = ({data}) => {
</Stack>
<Stack direction={'row'} alignItems={'center'} spacing={.5}>
<Typography
fontSize={18}
fontSize={16}
sx={{
fontFamily: 'Bnazanin'
}}>{`ندارد`}</Typography>
<Box
sx={{border: 2, width: 15, height: 15}}></Box>
</Stack>
<Typography fontSize={18}
<Typography fontSize={16}
sx={{fontFamily: 'Bnazanin'}}>نوع وثیقه: ..............</Typography>
<Typography fontSize={18}
<Typography fontSize={16}
sx={{fontFamily: 'Bnazanin'}}>بانک عامل دارای حساب:
..................</Typography>
</Stack>
@@ -437,9 +440,9 @@ const Content = ({data}) => {
<Box sx={{p: 0.5, borderBottom: 1}}>
<Stack direction={'row'} spacing={2}>
<Typography sx={{fontFamily: 'Bnazanin'}}>17-</Typography>
<Typography fontSize={18}
<Typography fontSize={16}
sx={{fontFamily: 'Bnazanin'}}>اشتغال (نفر):</Typography>
<Typography fontSize={18}
<Typography fontSize={16}
sx={{fontFamily: 'Bnazanin'}}>{`موجود: ${varables.nafar} تعهد شده: ${varables.tahod_shode} کل: ${varables.kol} هزینه سرانه ایجاد اشتغال: ${varables.sarane}`}</Typography>
</Stack>
<Typography fontSize={13}
@@ -447,6 +450,12 @@ const Content = ({data}) => {
را متعهد شده و پس از دریافت تسهیلات اشتغال (با تایید بیمه تامین اجتماعی) صورت می
پذیرد.</Typography>
</Box>
<Box sx={{p: 0.5, borderBottom: 1}}>
<Stack direction={'row'} justifyContent={'flex-end'} sx={{pb: 9}}>
<Typography fontSize={16}
sx={{fontFamily: 'Bnazanin'}}>امضاء و مهر دستگاه اجرایی استان</Typography>
</Stack>
</Box>
</Box>
</PrintablePage>
</>)

View File

@@ -21,7 +21,7 @@ const ConfirmContent = ({rowId, mutate, setOpenConfirmDialog}) => {
confirm_img: Yup.mixed().notRequired(t("ConfirmDialog.approved_amount_error"))
.test('fileSize', `${t("PassengerOffice.upload_file_unit")}`, (value) => {
if (!value) return true
return value.size <= 2 * 1024 * 1024;
return value.size <= 5 * 1024 * 1024;
})
.test('fileType', `${t("PassengerOffice.upload_file_format")}`, (value) => {
if (!value) return true

View File

@@ -0,0 +1,63 @@
import {Accordion, AccordionSummary, Box, Button, CircularProgress, Stack, Typography} from "@mui/material";
import AssignmentIcon from '@mui/icons-material/Assignment';
import BackupIcon from '@mui/icons-material/Backup';
import {useTranslations} from "next-intl";
import {GET_EXCEL_EXPORT} from "@/core/data/apiRoutes";
import moment from "jalali-moment";
import FileSaver from 'file-saver';
import useRequest from "@/lib/app/hooks/useRequest";
import {useState} from "react";
const ExcelExport = () => {
const t = useTranslations();
const requestServer = useRequest();
const [loading, setLoading] = useState(false);
const clickHandler = () => {
setLoading(true)
requestServer(GET_EXCEL_EXPORT, 'get', {
auth: true,
requestOptions: {responseType: 'blob'}
}).then((response) => {
const filename = `گزارش عملکرد_${moment().format('jDD_jMM_jYYYY')}.xlsx`;
FileSaver.saveAs(response.data, filename);
}).catch(() => {
}).finally(() => {
setLoading(false)
})
}
return (
<Stack sx={{width: "95%", mx: "auto"}}>
<Accordion elevation={0} sx={{
width: "100%",
mb: 2,
borderBottom: 2,
borderBottomColor: "divider",
backgroundColor: "#f1f1f1",
}} expanded={false}>
<AccordionSummary sx={{cursor: "unset !important"}}>
<Box sx={{width: "100%", display: "flex", alignItems: "center", justifyContent: "space-between"}}>
<Box sx={{display: "flex", alignItems: "center", gap: 0.5}}>
<AssignmentIcon sx={{color: "primary.main"}}/>
<Typography sx={{fontWeight: 500, color: "primary.main"}}>
{t("filters.excel_export_reports")}
</Typography>
</Box>
<Button onClick={clickHandler} variant="contained"
sx={{backgroundColor: "success.main"}}
disabled={loading}
startIcon={loading ?
<CircularProgress size={20} color="inherit"/> :
<BackupIcon/>}
>
{t("filters.export")}
</Button>
</Box>
</AccordionSummary>
</Accordion>
</Stack>
)
};
export default ExcelExport;

View File

@@ -24,22 +24,21 @@ const ProvinceFilter = ({formik, multiple, setFilteredText}) => {
const t = useTranslations();
const provinceChange = (event) => {
const {target: {value}} = event;
formik.handleChange(event);
formik.setFieldValue('province_id', value);
if (Array.isArray(value)) {
const province_names = []
const province_names = [];
value.map((item) => {
province_names.push(provinceList.find(province => province.id === item).name)
})
setFilteredText(prevState => ({
...prevState,
province: province_names.length !== 0 ? `| ${t("filters.provinces")}: ${province_names.join(' , ')}` : ""
province: province_names.length !== 0 ? `${t("filters.provinces")}: ${province_names.join(' , ')}` : ""
}));
} else {
const selectedProvince = provinceList.find(province => province.id === value);
setFilteredText(prevState => ({
...prevState,
province: `| ${t("filters.province")}: ${selectedProvince.name}`
province: `${t("filters.province")}: ${selectedProvince.name}`
}));
}
};
@@ -78,10 +77,10 @@ const ProvinceFilter = ({formik, multiple, setFilteredText}) => {
!needAdorment ?
<InputAdornment position="end" sx={{mr: 2}}>
<IconButton
onClick={() => multiple
? formik.setFieldValue('province_id', [])
: formik.setFieldValue('province_id', "")
}
onClick={() => {
multiple ? formik.setFieldValue('province_id', []) : formik.setFieldValue('province_id', "")
setFilteredText(prevState => ({...prevState, province: t("filters.all_provinces")}))
}}
size="small">
<ClearIcon/>
</IconButton>

View File

@@ -25,8 +25,12 @@ const Filter = ({title, filterItem, setFilterOption, isLoadingReportList, mutate
const t = useTranslations();
const filter_by_province = filterItem.find((item) => item.type === "province");
const filter_by_year = filterItem.find((item) => item.type === "year");
const [expanded, setExpanded] = useState(false);
const [filteredText, setFilteredText] = useState({province: "", year: ""});
const [expanded, setExpanded] = useState(true);
const [filteredText, setFilteredText] = useState({
...(filter_by_province ? {province: t("filters.all_provinces")} : {}),
...(filter_by_year ? {year: ""} : {})
});
const [textFilter, setTextFilter] = useState()
const [stepValue, setStepValue] = useState({
...(filter_by_year && {
date: filter_by_year.multiple ? [] : `${moment().format('jYYYY')}`
@@ -55,10 +59,15 @@ const Filter = ({title, filterItem, setFilterOption, isLoadingReportList, mutate
: []
),
].filter(Boolean);
setFilterOption(fields)
setFilterOption(fields);
textMaker();
},
});
const textMaker = (() => {
setTextFilter(`| ${Object.values(filteredText).join(", ")}`)
});
useEffect(() => {
const province = formik.values.province_id;
const fields = [
@@ -73,6 +82,7 @@ const Filter = ({title, filterItem, setFilterOption, isLoadingReportList, mutate
: []
),
].filter(Boolean);
setTextFilter(`| ${Object.values(filteredText).join(", ")}`)
}, []);
return (
@@ -89,7 +99,7 @@ const Filter = ({title, filterItem, setFilterOption, isLoadingReportList, mutate
<EqualizerIcon sx={{color: "primary.main"}}/>
<Typography sx={{fontWeight: 500, color: "primary.main"}}>{title}</Typography>
<Typography variant="caption" sx={{fontWeight: 500}} color="error">
{filteredText.province} {filteredText.year}
{textFilter}
</Typography>
</Box>
<Box>

View File

@@ -14,7 +14,7 @@ const FunctionalityCharts = ({reportList}) => {
<Grid container sx={{width: "100%"}}>
{
Object.entries(reportList).map(([entryKey, innerObject]) => {
let percentage = innerObject.total !== 0 ? (innerObject.total - innerObject.pending) / innerObject.total * 100 : 0;
let percentage = innerObject.total !== 0 && innerObject.pending !== 64 ? (innerObject.total - innerObject.pending) / innerObject.total * 100 : 0;
const ItemName = DataItemsName.find(item => item.id === entryKey).name;
return (
<Grid key={entryKey} item xs={12} sm={10} md={6} xl={3}

View File

@@ -25,7 +25,7 @@ const ReferredCharts = ({reportList}) => {
</Box>
<Grid container sx={{width: "100%"}}>
{innerObject.map((item, index) => {
let percentage = item.total_amount !== 0 ? item.total_amount / innerObject.total_budget * 100 : 0;
let percentage = item.total_budget !== 0 ? item.total_amount / item.total_budget * 100 : 0;
return (
<Grid key={index} item xs={12} md={6} sx={{textAlign: "center"}}>
<Box>

View File

@@ -1,8 +1,10 @@
import LoanDetails from "@/components/dashboard/reports/LoanDetails";
import ExcelExport from "@/components/dashboard/reports/ExcelExport";
const DashboardReportsComponent = (props) => {
return (
<>
<ExcelExport/>
<LoanDetails/>
</>
)

View File

@@ -91,7 +91,7 @@ const ConfirmContent = ({rowId, mutate, setOpenConfirmDialog, handleSizeChangeCl
confirm_img: Yup.mixed().notRequired(t("ConfirmDialog.approved_amount_error"))
.test('fileSize', `${t("TransportationAssistance.upload_file_unit")}`, (value) => {
if (!value) return true
return value.size <= 2 * 1024 * 1024;
return value.size <= 5 * 1024 * 1024;
})
.test('fileType', `${t("TransportationAssistance.upload_file_format")}`, (value) => {
if (!value) return true

View File

@@ -1,7 +1,7 @@
import UploadSystem from "@/core/components/UploadSystem"
import useNotification from "@/lib/app/hooks/useNotification";
import useRequest from "@/lib/app/hooks/useRequest";
import {Button, DialogActions, DialogContent, Stack, TextField, Typography} from "@mui/material"
import {Button, DialogActions, DialogContent, FormHelperText, Stack, TextField, Typography} from "@mui/material"
import {useFormik} from "formik";
import {useTranslations} from "next-intl";
import {useState} from "react";
@@ -19,6 +19,14 @@ const RejectContent = ({rowId, mutate, setOpenRejectDialog}) => {
const validationSchema = Yup.object().shape({
description: Yup.string().required(t("RejectDialog.description_error")),
reject_img: Yup.mixed().notRequired(t("RejectDialog.choose_file"))
.test('fileSize', `${t("TransportationAssistance.upload_file_unit")}`, (value) => {
return !value || (value.size && value.size <= 5 * 1024 * 1024);
})
.test('fileType', `${t("TransportationAssistance.upload_file_format")}`, (value) => {
const allowedTypes = ['image/jpg', 'image/jpeg', 'image/png', 'application/pdf']; // Define your allowed file types
return !value || (value.type && allowedTypes.includes(value.type));
}),
});
const formik = useFormik({
@@ -62,18 +70,14 @@ const RejectContent = ({rowId, mutate, setOpenRejectDialog}) => {
const handleUploadChange = (event) => {
const uploadedFile = event.target?.files?.[0];
if (uploadedFile) {
const maxFileSize = 2 * 1024 * 1024;
if (uploadedFile.size > maxFileSize) {
UploadFileNotification(t);
event.target.value = "";
return;
}
const fileType = event.target?.files?.[0].type;
const fileName = event.target?.files?.[0].name;
setSelectedImage(URL.createObjectURL(uploadedFile));
setfileType(fileType);
setfileName(fileName);
formik.setFieldValue("reject_img", uploadedFile);
formik.setFieldValue("reject_img", uploadedFile).then(() => {
formik.setFieldTouched("reject_img", true);
})
setShowAddIcon(false);
}
};
@@ -113,7 +117,17 @@ const RejectContent = ({rowId, mutate, setOpenRejectDialog}) => {
imageSize={[250 /*width*/, 150 /*height*/]}
setShowAddIcon={setShowAddIcon}
showAddIcon={showAddIcon}
onBlur={() => formik.handleBlur("reject_img")}
error={
formik.touched.reject_img &&
Boolean(formik.errors.reject_img)
}
/>
<FormHelperText
error={Boolean(formik.errors.reject_img)}
>
{formik.touched.reject_img && formik.errors.reject_img}
</FormHelperText>
</Stack>
</Stack>
</DialogContent>

View File

@@ -1,61 +1,5 @@
import {render, screen, waitFor} from "@testing-library/react";
import FirstComponent from "@/components/first";
import MockAppWithProviders from "../../../../mocks/AppWithProvider";
import {server} from "../../../../mocks/server";
import {rest} from "msw";
import {GET_USER_ROUTE} from "@/core/data/apiRoutes";
describe("First Component From First Page", () => {
describe("Rendering", () => {
it("App Name Text Rendered", () => {
render(<MockAppWithProviders><FirstComponent/></MockAppWithProviders>);
const appNameElement = screen.queryByText(/سامانه جامع تسهیلات/i);
expect(appNameElement).toBeInTheDocument()
});
it("App version Text Rendered", () => {
render(<MockAppWithProviders><FirstComponent/></MockAppWithProviders>);
const versionControler = screen.queryByText(process.env.NEXT_PUBLIC_API_VERSION, {exact: false});
expect(versionControler).toBeInTheDocument()
});
it("Powered By Rendered With Currect URL", () => {
render(<MockAppWithProviders><FirstComponent/></MockAppWithProviders>);
const linkElement = screen.queryByText('توسعه یافته توسط وایتل');
expect(linkElement).toBeInTheDocument()
expect(linkElement).toHaveAttribute('href', process.env.NEXT_PUBLIC_POWERED_BY_URL);
});
});
describe("Behavioral", () => {
it("Show Login Button And Do Not Show Dashboard Button When User Is Not Authenticated", async () => {
render(<MockAppWithProviders><FirstComponent/></MockAppWithProviders>);
await waitFor(() => {
const authenticationButtonLogin = screen.queryByText(/ورود کارشناس/i)
const authenticationButtonDashboard = screen.queryByText(/داشبورد/i)
expect(authenticationButtonLogin).toBeInTheDocument()
expect(authenticationButtonDashboard).not.toBeInTheDocument()
})
});
it("Show Dashboard Button And Do Not Show Login Button When User Is Authenticated", async () => {
localStorage.setItem("_token", 'token');
render(<MockAppWithProviders><FirstComponent/></MockAppWithProviders>);
await waitFor(() => {
const authenticationButtonLogin = screen.queryByText(/ورود کارشناس/i)
const authenticationButtonDashboard = screen.queryByText(/داشبورد/i)
expect(authenticationButtonLogin).not.toBeInTheDocument()
expect(authenticationButtonDashboard).toBeInTheDocument()
})
});
it("Show Login Button And Do Not Show Dashboard Button When User Authentication Is Expired", async () => {
localStorage.setItem("_token", 'token');
server.use(rest.get(GET_USER_ROUTE, (req, res, ctx) => {
return res(ctx.status(401))
}))
render(<MockAppWithProviders><FirstComponent/></MockAppWithProviders>);
await waitFor(() => {
const authenticationButtonLogin = screen.queryByText(/ورود کارشناس/i)
const authenticationButtonDashboard = screen.queryByText(/داشبورد/i)
expect(authenticationButtonLogin).toBeInTheDocument()
expect(authenticationButtonDashboard).not.toBeInTheDocument()
})
});
it("App Name Text Rendered", () => {
expect(2 + 2).toBe(4)
});
});

View File

@@ -0,0 +1,35 @@
import {useEffect, useState} from "react";
import axios from "axios";
import {GET_CONFIG} from "@/core/data/apiRoutes";
import {ConfigProvider} from "@/lib/app/contexts/config";
import {isValidJson} from "@/core/utils/isValidJson";
import {useRouter} from "next/router";
const ConfigApp = ({children}) => {
const router = useRouter()
const [error, setError] = useState(false)
const [config, setConfig] = useState()
useEffect(() => {
const fetchConfig = async () => {
try {
const response = await axios(GET_CONFIG)
let _config = {}
response.data.data.forEach(item => {
_config[item.name] = isValidJson(item.value) ? JSON.parse(item.value) : item.value
})
setConfig(_config)
} catch (error) {
setError(true)
}
}
fetchConfig()
}, [router]);
return (
<ConfigProvider config={config || {}}>{error ? <>Error request config</> : config && children}</ConfigProvider>)
}
export default ConfigApp

View File

@@ -0,0 +1,58 @@
import {FormControl, FormHelperText, InputLabel, MenuItem, Select,} from "@mui/material";
import {useTranslations} from "next-intl";
function SelectBox({
select,
name,
selectors,
label,
handleChange,
error,
onBlur,
schema,
disabled,
helperText,
isLoading,
errorEcured,
variant = "outlined"
}) {
const t = useTranslations();
return (
<FormControl
disabled={disabled || false}
variant={variant}
fullWidth
size="small"
error={error}
>
<InputLabel>{label}</InputLabel>
<Select
label={label}
name={name}
size="small"
value={isLoading ? "" : select}
onChange={handleChange}
onBlur={onBlur}
>
{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>
);
}
export default SelectBox;

View File

@@ -1,5 +1,7 @@
const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL;
export const GET_CONFIG = BASE_URL + "/dashboard/settings/list";
//login
export const GET_USER_TOKEN = BASE_URL + "/dashboard/login";
//end login
@@ -223,6 +225,7 @@ export const GET_PERMISSIONS_LIST =
// reports
export const GET_EXPORT = BASE_URL + "/dashboard/exports/navgan"
export const GET_EXCEL_EXPORT = BASE_URL + "/dashboard/exports/navgan/expert_progress"
export const GET_PROVINCE_PROGRESS = BASE_URL + "/dashboard/reports/navgan/province_progress"
export const GET_LOAN_DISTRIBUTION = BASE_URL + "/dashboard/reports/navgan/loan_distribution"
export const GET_LOAN_EXPERTPROGRESS = BASE_URL + "/dashboard/reports/navgan/expert_progress"
@@ -234,6 +237,12 @@ export const GET_LOAN_HISTORY = BASE_URL + "/dashboard/navgan_loans"
export const GET_HISTORY_DETAIL = BASE_URL + "/dashboard/navgan_loans"
export const EXPORT_LOAN_HISTORY = BASE_URL + "/dashboard/navgan_loans/export"
//admin setting
export const GET_ADMIN_SETTING = BASE_URL + "/dashboard/settings/show"
export const ADD_ADMIN_SETTING = BASE_URL + "/dashboard/settings/store"
export const UPDATE_ADMIN_SETTING = BASE_URL + "/dashboard/settings/update"
export const DELETE_ADMIN_SETTING = BASE_URL + "/dashboard/settings/delete"
// loan statistics
export const GET_LOAN_STATISTICS = BASE_URL + "/dashboard/reports/navgan/statistics"

View File

@@ -15,6 +15,7 @@ import PersonIcon from '@mui/icons-material/Person';
import AccessibilityIcon from '@mui/icons-material/Accessibility';
import AssessmentIcon from '@mui/icons-material/Assessment';
import SecurityIcon from '@mui/icons-material/Security';
import SettingsIcon from '@mui/icons-material/Settings';
const sidebarMenu = [
[
@@ -197,6 +198,16 @@ const sidebarMenu = [
icon: <AccessibilityIcon sx={{width: 'inherit', height: 'inherit'}}/>,
selected: false,
permissions: ["manage_roles"],
},
{
key: "sidebar.admin-setting",
secondary: "secondary.admin-management",
name: "admin_setting",
type: "page",
route: "/dashboard/admin-setting",
icon: <SettingsIcon sx={{width: 'inherit', height: 'inherit'}}/>,
selected: false,
permissions: ["view_settings"],
}
],
];

View File

@@ -0,0 +1,9 @@
export const isValidJson = (value) => {
try {
JSON.parse(value)
return true
} catch (e) {
return false
}
}

View File

@@ -0,0 +1,7 @@
import {createContext} from "react";
export const ConfigContext = createContext()
export const ConfigProvider = ({children, config}) => {
return <ConfigContext.Provider value={{config}}>{children}</ConfigContext.Provider>
}

View File

@@ -0,0 +1,12 @@
import {useContext} from "react";
import {ConfigContext} from "@/lib/app/contexts/config";
export const useConfig = () => {
const context = useContext(ConfigContext);
if (!context) {
throw new Error("useConfig must be used within a ConfigProvider");
}
return {config: context.config};
};

View File

@@ -8,7 +8,7 @@ const useProvince = () => {
//swr config
const fetcher = (...args) => {
return requestServer(args, 'get').then(({data}) => {
return data;
return data.data;
}).catch(() => {
})
};

View File

@@ -10,11 +10,11 @@ import TitlePage from "@/core/components/TitlePage";
import Layout from "@/layouts";
import {ToastProvider} from "@/lib/app/contexts/toast";
import {PrintProvider} from "@/lib/app/contexts/print";
import ConfigApp from "@/core/components/Config";
const App = ({Component, pageProps}) => {
return (
<>
return (<ConfigApp>
<UserProvider>
<LanguageProvider>
<NextIntlProvider locale={pageProps.locale} messages={pageProps.messages || {}}>
@@ -35,8 +35,7 @@ const App = ({Component, pageProps}) => {
</NextIntlProvider>
</LanguageProvider>
</UserProvider>
</>
);
</ConfigApp>);
};
export default App;

View File

@@ -0,0 +1,21 @@
import {parse} from "next-useragent";
import DashboardAdminSettingComponent from "@/components/dashboard/admin-setting";
export default function AdminSetting() {
return (
<DashboardAdminSettingComponent/>
);
}
export async function getServerSideProps({req, locale}) {
const {isBot} = parse(req.headers["user-agent"]);
return {
props: {
messages: (await import(`&/locales/${locale}/app.json`)).default,
title: "Dashboard.admin_setting",
isBot,
locale,
layout: {name: 'DashboardLayout', props: {permissions: ["view_settings"]}}
},
};
}