diff --git a/public/config.json.example b/public/config.json.example index ac50d76..e92ed58 100644 --- a/public/config.json.example +++ b/public/config.json.example @@ -10,6 +10,8 @@ "min": 1327, "max": 1380 }, + "interest_rate": "15.2", + "loan_repayment": "6", "deadlines": { "register": { "date": { diff --git a/public/locales/fa/app.json b/public/locales/fa/app.json index be37df8..d6792d6 100644 --- a/public/locales/fa/app.json +++ b/public/locales/fa/app.json @@ -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": "پروفایل", @@ -481,8 +490,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 +593,9 @@ }, "AddDialog": { "add": "افزودن", + "setting_name": "نام انگلیسی", + "setting_name_fa": "نام فارسی", + "value": "مقدار", "name": "نام انگلیسی", "name_error": "وارد کردن نام انگلیسی الزامیست", "name_fa": "نام فارسی", @@ -594,6 +614,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": "نوع کاربر", diff --git a/src/components/dashboard/admin-setting/Form/CreateForm/CreateContent.jsx b/src/components/dashboard/admin-setting/Form/CreateForm/CreateContent.jsx new file mode 100644 index 0000000..f10a97b --- /dev/null +++ b/src/components/dashboard/admin-setting/Form/CreateForm/CreateContent.jsx @@ -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 ( + <> + + + + + + + + + + + + + + + + + + + ) +} +export default CreateContent \ No newline at end of file diff --git a/src/components/dashboard/admin-setting/Form/CreateForm/index.jsx b/src/components/dashboard/admin-setting/Form/CreateForm/index.jsx new file mode 100644 index 0000000..9d531d2 --- /dev/null +++ b/src/components/dashboard/admin-setting/Form/CreateForm/index.jsx @@ -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 ( + + + + + + {t("AddDialog.add")} + + + + ) +} +export default CreateForm \ No newline at end of file diff --git a/src/components/dashboard/admin-setting/Form/DeleteForm.jsx b/src/components/dashboard/admin-setting/Form/DeleteForm.jsx new file mode 100644 index 0000000..b2fdead --- /dev/null +++ b/src/components/dashboard/admin-setting/Form/DeleteForm.jsx @@ -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 ( + <> + + { + setOpenConfirmDialog(true) + }} + > + + + + + {t("DeleteDialog.delete")} + + {t("DeleteDialog.typography")} + + + + + + + + ); +}; +export default Delete; \ No newline at end of file diff --git a/src/components/dashboard/admin-setting/Form/UpdateForm/UpdateContent.jsx b/src/components/dashboard/admin-setting/Form/UpdateForm/UpdateContent.jsx new file mode 100644 index 0000000..f521ced --- /dev/null +++ b/src/components/dashboard/admin-setting/Form/UpdateForm/UpdateContent.jsx @@ -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 ( + <> + + + + + + + + + + + + + + + + + + + ) +} +export default UpdateContent \ No newline at end of file diff --git a/src/components/dashboard/admin-setting/Form/UpdateForm/index.jsx b/src/components/dashboard/admin-setting/Form/UpdateForm/index.jsx new file mode 100644 index 0000000..a00cd74 --- /dev/null +++ b/src/components/dashboard/admin-setting/Form/UpdateForm/index.jsx @@ -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 ( + <> + + { + setOpenConfirmDialog(true) + }} + > + + + + + {t("UpdateDialog.update")} + + + + ) +} +export default Update \ No newline at end of file diff --git a/src/components/dashboard/admin-setting/TableRowActions.jsx b/src/components/dashboard/admin-setting/TableRowActions.jsx new file mode 100644 index 0000000..6bdd0fe --- /dev/null +++ b/src/components/dashboard/admin-setting/TableRowActions.jsx @@ -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 ( + + + + + ); +}; + +export default TableRow; diff --git a/src/components/dashboard/admin-setting/TableToolbar.jsx b/src/components/dashboard/admin-setting/TableToolbar.jsx new file mode 100644 index 0000000..007eb2f --- /dev/null +++ b/src/components/dashboard/admin-setting/TableToolbar.jsx @@ -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 + +} + +export default TableToolbar; diff --git a/src/components/dashboard/admin-setting/index.jsx b/src/components/dashboard/admin-setting/index.jsx new file mode 100644 index 0000000..685c47a --- /dev/null +++ b/src/components/dashboard/admin-setting/index.jsx @@ -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}) => ( + {renderedCellValue} + ), + }, + { + accessorFn: (row) => row.name_fa, + id: "name_fa", + header: t("AdminSetting.name_fa"), + enableColumnFilter: true, + datatype: "text", + filterFn: "contains", + columnFilterModeOptions: ["contains", "equals"], + Cell: ({renderedCellValue}) => ( + {renderedCellValue} + ), + }, + { + accessorFn: (row) => row.name, + id: "name", + header: t("AdminSetting.name"), + enableColumnFilter: true, + datatype: "text", + filterFn: "contains", + columnFilterModeOptions: ["contains", "equals"], + Cell: ({renderedCellValue}) => ( + {renderedCellValue} + ), + }, + { + accessorFn: (row) => row.value, + id: "value", + header: t("AdminSetting.value"), + enableColumnFilter: true, + datatype: "text", + filterFn: "contains", + columnFilterModeOptions: [ + "equals", + "contains", + ], + Cell: ({renderedCellValue}) => ( + {renderedCellValue} + ), + }, + ], + [] + ); + return ( + + + + ); +} + +export default DashboardAdminSettingComponent; diff --git a/src/components/dashboard/machinary-office/Form/ConfirmForm/ConfirmContent.jsx b/src/components/dashboard/machinary-office/Form/ConfirmForm/ConfirmContent.jsx index 438def6..05ad51b 100644 --- a/src/components/dashboard/machinary-office/Form/ConfirmForm/ConfirmContent.jsx +++ b/src/components/dashboard/machinary-office/Form/ConfirmForm/ConfirmContent.jsx @@ -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, @@ -232,51 +232,93 @@ const ConfirmContent = ({mutate, setOpenConfirmDialog, rowId, vehicle_type, hand /> - {t("ConfirmDialog.sakht_period_day")}, + 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")} /> - {t("ConfirmDialog.tanafos_period_month")}, + 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")} /> diff --git a/src/components/dashboard/passenger-boss/Buttons/printFormA.jsx b/src/components/dashboard/passenger-boss/Buttons/printFormA.jsx index fa1e882..6e0405e 100644 --- a/src/components/dashboard/passenger-boss/Buttons/printFormA.jsx +++ b/src/components/dashboard/passenger-boss/Buttons/printFormA.jsx @@ -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 (