diff --git a/src/components/dashboard/inquiryPrivacy/generalManager/zaminGov/TaskList.jsx b/src/components/dashboard/inquiryPrivacy/generalManager/zaminGov/TaskList.jsx
index 92f500b..dcb9084 100644
--- a/src/components/dashboard/inquiryPrivacy/generalManager/zaminGov/TaskList.jsx
+++ b/src/components/dashboard/inquiryPrivacy/generalManager/zaminGov/TaskList.jsx
@@ -14,7 +14,7 @@ const TaskList = () => {
id: "id",
enableColumnFilter: false,
datatype: "text",
- filterFn: "notEquals",
+ filterMode: "notEquals",
},
{
accessorKey: "aplication_number",
@@ -22,7 +22,7 @@ const TaskList = () => {
id: "aplication_number",
enableColumnFilter: false,
datatype: "text",
- filterFn: "notEquals",
+ filterMode: "notEquals",
},
{
accessorKey: "application_date",
@@ -30,7 +30,7 @@ const TaskList = () => {
id: "application_date",
enableColumnFilter: false,
datatype: "text",
- filterFn: "equals",
+ filterMode: "equals",
},
{
accessorKey: "province",
@@ -38,7 +38,7 @@ const TaskList = () => {
id: "province",
enableColumnFilter: false,
datatype: "text",
- filterFn: "equals",
+ filterMode: "equals",
},
{
accessorKey: "township",
@@ -46,7 +46,7 @@ const TaskList = () => {
id: "township",
enableColumnFilter: false,
datatype: "text",
- filterFn: "equals",
+ filterMode: "equals",
},
{
accessorKey: "city",
@@ -54,7 +54,7 @@ const TaskList = () => {
id: "city",
enableColumnFilter: false,
datatype: "text",
- filterFn: "equals",
+ filterMode: "equals",
},
{
accessorKey: "county",
@@ -62,7 +62,7 @@ const TaskList = () => {
id: "county",
enableColumnFilter: false,
datatype: "text",
- filterFn: "equals",
+ filterMode: "equals",
},
{
accessorKey: "village",
@@ -70,7 +70,7 @@ const TaskList = () => {
id: "village",
enableColumnFilter: false,
datatype: "text",
- filterFn: "equals",
+ filterMode: "equals",
},
{
accessorKey: "land_area",
@@ -78,7 +78,7 @@ const TaskList = () => {
id: "land_area",
enableColumnFilter: false,
datatype: "text",
- filterFn: "equals",
+ filterMode: "equals",
},
{
accessorKey: "design_area",
@@ -86,7 +86,7 @@ const TaskList = () => {
id: "design_area",
enableColumnFilter: false,
datatype: "text",
- filterFn: "equals",
+ filterMode: "equals",
},
{
accessorKey: "organization",
@@ -94,7 +94,7 @@ const TaskList = () => {
id: "organization",
enableColumnFilter: false,
datatype: "text",
- filterFn: "equals",
+ filterMode: "equals",
},
{
accessorKey: "design_group",
@@ -102,7 +102,7 @@ const TaskList = () => {
id: "design_group",
enableColumnFilter: false,
datatype: "text",
- filterFn: "equals",
+ filterMode: "equals",
},
{
accessorKey: "design_title",
@@ -110,7 +110,7 @@ const TaskList = () => {
id: "design_title",
enableColumnFilter: false,
datatype: "text",
- filterFn: "equals",
+ filterMode: "equals",
},
{
accessorKey: "state",
@@ -118,7 +118,7 @@ const TaskList = () => {
id: "state",
enableColumnFilter: false,
datatype: "text",
- filterFn: "equals",
+ filterMode: "equals",
},
],
[]
diff --git a/src/components/dashboard/inquiryPrivacy/provinceAdmin/zaminGov/RowActions/Refer/ReferFormContext.jsx b/src/components/dashboard/inquiryPrivacy/provinceAdmin/zaminGov/RowActions/Refer/ReferFormContext.jsx
index e02512e..1284aa2 100644
--- a/src/components/dashboard/inquiryPrivacy/provinceAdmin/zaminGov/RowActions/Refer/ReferFormContext.jsx
+++ b/src/components/dashboard/inquiryPrivacy/provinceAdmin/zaminGov/RowActions/Refer/ReferFormContext.jsx
@@ -31,7 +31,7 @@ const ReferFormContext = ({ setOpenReferDialog, rowId, mutate }) => {
province: "",
};
const { provinces, loadingProvinces, errorProvinces } = useProvinces();
- const requestServer = useRequest({ notification: { show: true, success: true } });
+ const requestServer = useRequest({ notificationSuccess: true });
const {
control,
register,
diff --git a/src/components/dashboard/inquiryPrivacy/provinceAdmin/zaminGov/RowActions/RoadSafetyForm/RoadSaftyFormContext.jsx b/src/components/dashboard/inquiryPrivacy/provinceAdmin/zaminGov/RowActions/RoadSafetyForm/RoadSaftyFormContext.jsx
index 861400a..05cb6e0 100644
--- a/src/components/dashboard/inquiryPrivacy/provinceAdmin/zaminGov/RowActions/RoadSafetyForm/RoadSaftyFormContext.jsx
+++ b/src/components/dashboard/inquiryPrivacy/provinceAdmin/zaminGov/RowActions/RoadSafetyForm/RoadSaftyFormContext.jsx
@@ -41,7 +41,7 @@ const RoadSafetyFormContext = ({ rowId, mutate, setOpenRoadSafetyForm }) => {
description: "",
RadioGroup: "",
};
- const requestServer = useRequest({ notification: { success: true, show: true } });
+ const requestServer = useRequest({ notificationSuccess: true });
const {
register,
handleSubmit,
diff --git a/src/components/dashboard/inquiryPrivacy/provinceAdmin/zaminGov/TaskList.jsx b/src/components/dashboard/inquiryPrivacy/provinceAdmin/zaminGov/TaskList.jsx
index 550e152..d059812 100644
--- a/src/components/dashboard/inquiryPrivacy/provinceAdmin/zaminGov/TaskList.jsx
+++ b/src/components/dashboard/inquiryPrivacy/provinceAdmin/zaminGov/TaskList.jsx
@@ -14,7 +14,7 @@ const TaskList = () => {
id: "id",
enableColumnFilter: false,
datatype: "text",
- filterFn: "notEquals",
+ filterMode: "notEquals",
},
{
accessorKey: "aplication_number",
@@ -22,7 +22,7 @@ const TaskList = () => {
id: "aplication_number",
enableColumnFilter: false,
datatype: "text",
- filterFn: "notEquals",
+ filterMode: "notEquals",
},
{
accessorKey: "application_date",
@@ -30,7 +30,7 @@ const TaskList = () => {
id: "application_date",
enableColumnFilter: false,
datatype: "text",
- filterFn: "equals",
+ filterMode: "equals",
},
{
accessorKey: "province",
@@ -38,7 +38,7 @@ const TaskList = () => {
id: "province",
enableColumnFilter: false,
datatype: "text",
- filterFn: "equals",
+ filterMode: "equals",
},
{
accessorKey: "township",
@@ -46,7 +46,7 @@ const TaskList = () => {
id: "township",
enableColumnFilter: false,
datatype: "text",
- filterFn: "equals",
+ filterMode: "equals",
},
{
accessorKey: "city",
@@ -54,7 +54,7 @@ const TaskList = () => {
id: "city",
enableColumnFilter: false,
datatype: "text",
- filterFn: "equals",
+ filterMode: "equals",
},
{
accessorKey: "county",
@@ -62,7 +62,7 @@ const TaskList = () => {
id: "county",
enableColumnFilter: false,
datatype: "text",
- filterFn: "equals",
+ filterMode: "equals",
},
{
accessorKey: "village",
@@ -70,7 +70,7 @@ const TaskList = () => {
id: "village",
enableColumnFilter: false,
datatype: "text",
- filterFn: "equals",
+ filterMode: "equals",
},
{
accessorKey: "land_area",
@@ -78,7 +78,7 @@ const TaskList = () => {
id: "land_area",
enableColumnFilter: false,
datatype: "text",
- filterFn: "equals",
+ filterMode: "equals",
},
{
accessorKey: "design_area",
@@ -86,7 +86,7 @@ const TaskList = () => {
id: "design_area",
enableColumnFilter: false,
datatype: "text",
- filterFn: "equals",
+ filterMode: "equals",
},
{
accessorKey: "organization",
@@ -94,7 +94,7 @@ const TaskList = () => {
id: "organization",
enableColumnFilter: false,
datatype: "text",
- filterFn: "equals",
+ filterMode: "equals",
},
{
accessorKey: "design_group",
@@ -102,7 +102,7 @@ const TaskList = () => {
id: "design_group",
enableColumnFilter: false,
datatype: "text",
- filterFn: "equals",
+ filterMode: "equals",
},
{
accessorKey: "design_title",
@@ -110,7 +110,7 @@ const TaskList = () => {
id: "design_title",
enableColumnFilter: false,
datatype: "text",
- filterFn: "equals",
+ filterMode: "equals",
},
{
accessorKey: "state",
@@ -118,7 +118,7 @@ const TaskList = () => {
id: "state",
enableColumnFilter: false,
datatype: "text",
- filterFn: "equals",
+ filterMode: "equals",
},
],
[]
diff --git a/src/components/dashboard/roadItems/operator/Actions/Create/Forms/CreateFormContent.jsx b/src/components/dashboard/roadItems/operator/Actions/Create/Forms/CreateFormContent.jsx
new file mode 100644
index 0000000..e05531b
--- /dev/null
+++ b/src/components/dashboard/roadItems/operator/Actions/Create/Forms/CreateFormContent.jsx
@@ -0,0 +1,225 @@
+import React, { useState } from "react";
+import { useForm } from "react-hook-form";
+import { yupResolver } from "@hookform/resolvers/yup";
+import { array, mixed, object, string } from "yup";
+import { useTheme } from "@emotion/react";
+import useRequest from "@/lib/hooks/useRequest";
+import { Box, Button, DialogActions, DialogContent, Tab, Tabs, useMediaQuery } from "@mui/material";
+import StyledForm from "@/core/components/StyledForm";
+import InsertDriveFileIcon from "@mui/icons-material/InsertDriveFile";
+import FileCopyIcon from "@mui/icons-material/FileCopy";
+import InfoIcon from "@mui/icons-material/Info";
+import ExitToAppIcon from "@mui/icons-material/ExitToApp";
+import KeyboardDoubleArrowRightIcon from "@mui/icons-material/KeyboardDoubleArrowRight";
+import BeenhereIcon from "@mui/icons-material/Beenhere";
+import GetItemsForm from "@/components/dashboard/roadItems/operator/Actions/Create/Forms/GetItemsForm";
+import GetSubItemsForm from "@/components/dashboard/roadItems/operator/Actions/Create/Forms/GetSubItemsForm";
+import GetItemInfo from "@/components/dashboard/roadItems/operator/Actions/Create/Forms/GetItemInfo";
+import { CREATE_ROAD_ITEMS } from "@/core/utils/routes";
+
+function TabPanel(props) {
+ const { children, value, index } = props;
+ return (
+
+ {value === index && {children}}
+
+ );
+}
+
+const defaultValues = {
+ itemId: null,
+ itemSubId: null,
+ amount: "",
+ action_date: "",
+ start_date: "",
+ before_image: null,
+ after_image: null,
+ cmms_machine_id: null,
+ rahdaran_id: null,
+ start_point: "",
+ end_point: "",
+};
+const validationSchema = object({
+ itemId: string().required("نوع آیتم را مشخص کنید!"),
+ itemSubId: string().required("موضوع مشاهده شده را مشخص کنید!"),
+ amount: string().required("وارد کردن مقدار الزامیست!"),
+ cmms_machine_id: object().required("وارد کردن کد خودرو الزامیست!"),
+ rahdaran_id: array().required("وارد کردن کد راهداران الزامیست!").min(1, "حداقل یک کد راهدار باید وارد شود!"), // بررسی حداقل یک آیتم,
+ action_date: string().required("لطفا زمان فعالیت را انتخاب کنید!"),
+ start_date: string().required("لطفاً تاریخ شروع فعالیت را انتخاب کنید!"),
+ before_image: mixed()
+ .nullable()
+ .test("before-image-required", "لطفا عکس قبل از اقدام را بارگذاری کنید!", function (value) {
+ const { subItemsList } = this.options.context;
+ const needsImage = subItemsList?.needs_image === 1;
+ if (needsImage) {
+ return !!value;
+ }
+ return true;
+ }),
+ after_image: mixed()
+ .nullable()
+ .test("after-image-required", "لطفا عکس بعد از اقدام را بارگذاری کنید!", function (value) {
+ const { subItemsList } = this.options.context;
+ const needsImage = subItemsList?.needs_image === 1;
+ if (needsImage) {
+ return !!value;
+ }
+ return true;
+ }),
+ start_point: mixed()
+ .test("start-point-required", "لطفاً نقطه شروع را مشخص کنید!", function (value) {
+ return !!value; // چک میکند که مقدار موجود است
+ })
+ .required("لطفاً نقطه شروع را مشخص کنید!"),
+ end_point: mixed().test("end-point-required", "لطفاً نقطه پایان را مشخص کنید!", function (value) {
+ const { subItemsList } = this.options.context;
+ const needsEndPoint = subItemsList?.needs_end_point === 1;
+ if (needsEndPoint) {
+ return !!value; // چک میکند که مقدار موجود است
+ }
+ return true;
+ }),
+});
+
+const CreateFormContent = ({ setOpen, mutate, rowId }) => {
+ const [tabState, setTabState] = useState(0);
+ const [itemsList, setItemsList] = useState();
+ const [subItemsList, setSubItemsList] = useState([]);
+ const theme = useTheme();
+ const requestServer = useRequest({ notificationSuccess: true });
+ const isMobile = useMediaQuery(theme.breakpoints.down("sm"));
+ const handleClose = () => {
+ setOpen(false);
+ };
+
+ const handleChangeTab = (event, newValue) => {
+ setTabState(newValue);
+ };
+
+ const handlePrev = () => {
+ if (tabState === 0) {
+ handleClose();
+ } else {
+ setTabState(tabState - 1);
+ }
+ };
+ const {
+ control,
+ watch,
+ getValues,
+ register,
+ handleSubmit,
+ setValue,
+ formState: { errors, isSubmitting },
+ } = useForm({
+ defaultValues,
+ resolver: yupResolver(validationSchema),
+ mode: "onBlur",
+ context: { subItemsList },
+ });
+
+ const onSubmit = async (data) => {
+ console.log(data);
+ let endPoint;
+ let startPoint = `${data.start_point.lat},${data.start_point.lng}`;
+ data.end_point !== "" && (endPoint = `${data.end_point.lat},${data.end_point.lng}`);
+ const formData = new FormData();
+ data.rahdaran_id.map((rahdar, index) => formData.append(`rahdaran_id[${index}]`, rahdar.id));
+ formData.append("item_id", data.itemId);
+ formData.append("sub_item_id", data.itemSubId);
+ formData.append("amount", data.amount);
+ formData.append("activity_time", data.action_date);
+ formData.append("activity_date", data.start_date);
+ formData.append("cmms_machine_id", data.cmms_machine_id.id);
+ data.before_image !== null && formData.append("before_image", data.before_image);
+ data.after_image !== null && formData.append("after_image", data.after_image);
+ data.start_point !== "" && formData.append("start_point", startPoint);
+ data.end_point !== "" && formData.append("end_point", endPoint);
+ requestServer(CREATE_ROAD_ITEMS, "post", {
+ data: formData,
+ })
+ .then((response) => {
+ mutate();
+ setOpen(false);
+ })
+ .catch((error) => {});
+ };
+ return (
+
+
+ } label="انتخاب آیتم" />
+ } label="انتخاب موضوع مشاهده شده" />
+ } label="اطلاعات فعالیت" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ : }
+ >
+ {tabState === 0 ? "بستن" : "مرحله قبل"}
+
+ {tabState === 2 && (
+ }
+ >
+ {isSubmitting ? "در حال ثبت فعالیت" : "ثبت فعالیت"}
+
+ )}
+
+
+ );
+};
+export default CreateFormContent;
diff --git a/src/components/dashboard/roadItems/operator/Actions/Create/Forms/GetItemInfo.jsx b/src/components/dashboard/roadItems/operator/Actions/Create/Forms/GetItemInfo.jsx
new file mode 100644
index 0000000..54885c1
--- /dev/null
+++ b/src/components/dashboard/roadItems/operator/Actions/Create/Forms/GetItemInfo.jsx
@@ -0,0 +1,111 @@
+import { Stack, TextField, Grid } from "@mui/material";
+import ImageUpload from "./ImageUpload";
+import MuiDatePicker from "@/core/components/MuiDatePicker";
+import MuiTimePicker from "@/core/components/MuiTimePicker";
+import MapInfoTwoMarker from "@/core/components/MapInfoTwoMarker";
+import MapInfoOneMarker from "@/core/components/MapInfoOneMarker";
+import { Controller } from "react-hook-form";
+import CarCode from "@/core/components/CarCode";
+import RahdarCode from "@/core/components/RahdarCode";
+import PreviousStatesInfo from "./PreviousStatesInfo";
+
+const GetItemInfo = ({ register, itemsList, subItemsList, control, setValue, errors, watch, getValues }) => {
+ return (
+
+
+
+
+
+ {
+ if (!isNaN(event.target.value)) {
+ setValue("amount", event.target.value);
+ } else {
+ setValue("amount", watch("amount"));
+ }
+ }}
+ helperText={errors.amount ? errors.amount.message : null}
+ variant="outlined"
+ fullWidth
+ />
+
+
+ {
+ return (
+ field.onChange(value || null)}
+ error={error}
+ />
+ );
+ }}
+ name={"cmms_machine_id"}
+ />
+
+
+ {
+ return (
+ field.onChange(value || [])}
+ error={error}
+ />
+ );
+ }}
+ name={"rahdaran_id"}
+ />
+
+
+ {subItemsList.needs_image ? (
+
+ ) : null}
+
+
+
+
+
+
+
+
+
+
+
+
+ {subItemsList?.needs_end_point === 1 ? (
+
+ ) : (
+
+ )}
+
+
+ );
+};
+export default GetItemInfo;
diff --git a/src/components/dashboard/roadItems/operator/Actions/Create/Forms/GetItemsForm.jsx b/src/components/dashboard/roadItems/operator/Actions/Create/Forms/GetItemsForm.jsx
new file mode 100644
index 0000000..0396b71
--- /dev/null
+++ b/src/components/dashboard/roadItems/operator/Actions/Create/Forms/GetItemsForm.jsx
@@ -0,0 +1,64 @@
+import React from "react";
+import { Card, CardActionArea, Grid, LinearProgress, Typography } from "@mui/material";
+import Image from "next/image";
+import useRoadItemGetItems from "@/lib/hooks/useRoadItemGetItems";
+
+const GetItemsForm = ({ setValue, setItemsList, tabState, setTabState }) => {
+ const { itemsList, loadingItemsList, errorItemsList } = useRoadItemGetItems();
+
+ return (
+ <>
+ {errorItemsList ? (
+
+ خطا در دریافت لیست آیتم ها
+
+ ) : loadingItemsList ? (
+
+ ) : (
+
+ {itemsList.map((item) => (
+
+ {
+ setValue("itemId", item.id);
+ setItemsList(item.name);
+ setTabState(tabState + 1);
+ }}
+ >
+
+
+ {item.name}
+
+
+
+ ))}
+
+ )}
+ >
+ );
+};
+export default GetItemsForm;
diff --git a/src/components/dashboard/roadItems/operator/Actions/Create/Forms/GetSubItemsForm.jsx b/src/components/dashboard/roadItems/operator/Actions/Create/Forms/GetSubItemsForm.jsx
new file mode 100644
index 0000000..8b36594
--- /dev/null
+++ b/src/components/dashboard/roadItems/operator/Actions/Create/Forms/GetSubItemsForm.jsx
@@ -0,0 +1,53 @@
+import React from "react";
+import { Card, CardActionArea, Grid, LinearProgress, Typography } from "@mui/material";
+import useRoadItemGetSubItems from "@/lib/hooks/useRoadItemGetISubtems";
+
+const GetSubItemsForm = ({ setValue, watch, setSubItemsList, tabState, setTabState }) => {
+ const { subItemsList, loadingSubItemsList, errorSubItemsList } = useRoadItemGetSubItems(watch("itemId"));
+
+ return (
+ <>
+ {errorSubItemsList ? (
+
+ خطا در دریافت موضوع مشاهده شده
+
+ ) : loadingSubItemsList ? (
+
+ ) : (
+
+ {subItemsList.map((item) => (
+
+ {
+ setValue("itemSubId", item.sub_item);
+ setSubItemsList(item);
+ setTabState(tabState + 1);
+ }}
+ >
+
+ {item.name}
+
+
+
+ ))}
+
+ )}
+ >
+ );
+};
+export default GetSubItemsForm;
diff --git a/src/components/dashboard/roadItems/operator/Actions/Create/Forms/ImageUpload.jsx b/src/components/dashboard/roadItems/operator/Actions/Create/Forms/ImageUpload.jsx
new file mode 100644
index 0000000..3efd1d9
--- /dev/null
+++ b/src/components/dashboard/roadItems/operator/Actions/Create/Forms/ImageUpload.jsx
@@ -0,0 +1,131 @@
+import { FormControl, FormHelperText, Grid } from "@mui/material";
+import UploadSystem from "@/core/components/UploadSystem";
+import { Controller } from "react-hook-form";
+import React, { useEffect, useState } from "react";
+
+const ImageUpload = ({ control, setValue, errors, getValues, beforeImage = null, afterImage = null }) => {
+ const [beforeImg, setBeforeImg] = useState(beforeImage ? beforeImage : null);
+ const [beforeFileType, setBeforeFileType] = useState(beforeImage ? "image/" : null);
+ const [beforeFileName, setBeforeFileName] = useState(null);
+ const [showBeforeImage, setShowBeforeImage] = useState(!beforeImage);
+
+ const [afterImg, setAfterImg] = useState(afterImage ? afterImage : null);
+ const [afterFileType, setAfterFileType] = useState(afterImage ? "image/" : null);
+ const [afterFileName, setAfterFileName] = useState(null);
+ const [showAfterImage, setShowAfterImage] = useState(!afterImage);
+
+ useEffect(() => {
+ if (getValues("before_image")) {
+ setShowBeforeImage(false);
+ setBeforeImg(getValues("before_image"));
+ setBeforeFileType("image/");
+ }
+ if (getValues("after_image")) {
+ setShowAfterImage(false);
+ setAfterImg(getValues("after_image"));
+ setAfterFileType("image/");
+ }
+ }, [getValues]);
+
+ const handleBeforeFileChange = (event) => {
+ const uploadedFile = event.target?.files?.[0];
+ if (uploadedFile) {
+ const fileType = event.target?.files?.[0].type;
+ const fileName = event.target?.files?.[0].name;
+ setBeforeImg(URL.createObjectURL(uploadedFile));
+ setBeforeFileType(fileType);
+ setBeforeFileName(fileName);
+ setValue("before_image", uploadedFile);
+ setShowBeforeImage(false);
+ }
+ };
+ const handleAfterFileChange = (event) => {
+ const uploadedFile = event.target?.files?.[0];
+ if (uploadedFile) {
+ const fileType = event.target?.files?.[0].type;
+ const fileName = event.target?.files?.[0].name;
+ setAfterImg(URL.createObjectURL(uploadedFile));
+ setAfterFileType(fileType);
+ setAfterFileName(fileName);
+ setValue("after_image", uploadedFile);
+ setShowAfterImage(false);
+ }
+ };
+ return (
+
+
+ {
+ return (
+
+
+ عکس قبل از اقدام
+
+
+
+ {errors.before_image ? errors.before_image.message : null}
+
+
+ );
+ }}
+ />
+
+
+ {
+ return (
+
+
+ عکس بعد از اقدام
+
+
+
+ {errors.after_image ? errors.after_image.message : null}
+
+
+ );
+ }}
+ />
+
+
+ );
+};
+export default ImageUpload;
diff --git a/src/components/dashboard/roadItems/operator/Actions/Create/Forms/PreviousStatesInfo.jsx b/src/components/dashboard/roadItems/operator/Actions/Create/Forms/PreviousStatesInfo.jsx
new file mode 100644
index 0000000..6f3cc86
--- /dev/null
+++ b/src/components/dashboard/roadItems/operator/Actions/Create/Forms/PreviousStatesInfo.jsx
@@ -0,0 +1,43 @@
+import { Box, Chip, Divider, Grid, Typography } from "@mui/material";
+import InsertDriveFileIcon from "@mui/icons-material/InsertDriveFile";
+import FileCopyIcon from "@mui/icons-material/FileCopy";
+
+const PreviousStatesInfo = ({ itemsList, subItemsList }) => {
+ return (
+
+
+
+ }
+ label={
+
+ آیتم انتخاب شده
+
+ }
+ />
+
+
+
+ {itemsList || "هیچ آیتمی انتخاب نشده است"}
+
+
+
+
+ }
+ label={
+
+ موضوع مشاهدهشده
+
+ }
+ />
+
+
+
+ {subItemsList?.name || "هیچ موضوعی انتخاب نشده است"}
+
+
+
+ );
+};
+export default PreviousStatesInfo;
diff --git a/src/components/dashboard/roadItems/operator/Actions/Create/Forms/index.jsx b/src/components/dashboard/roadItems/operator/Actions/Create/Forms/index.jsx
new file mode 100644
index 0000000..52c2d42
--- /dev/null
+++ b/src/components/dashboard/roadItems/operator/Actions/Create/Forms/index.jsx
@@ -0,0 +1,12 @@
+"use client";
+import { Dialog } from "@mui/material";
+import CreateFormContent from "@/components/dashboard/roadItems/operator/Actions/Create/Forms/CreateFormContent";
+
+const OperatorCreateForm = ({ open, setOpen, mutate, rowId }) => {
+ return (
+
+ );
+};
+export default OperatorCreateForm;
diff --git a/src/components/dashboard/roadItems/operator/Actions/Create/index.jsx b/src/components/dashboard/roadItems/operator/Actions/Create/index.jsx
new file mode 100644
index 0000000..ce764e7
--- /dev/null
+++ b/src/components/dashboard/roadItems/operator/Actions/Create/index.jsx
@@ -0,0 +1,38 @@
+"use client";
+import { Button, IconButton, useMediaQuery } from "@mui/material";
+import { useTheme } from "@emotion/react";
+import { useState } from "react";
+import { AddCircle } from "@mui/icons-material";
+import OperatorCreateForm from "./Forms";
+
+const OperatorCreate = ({ mutate }) => {
+ const theme = useTheme();
+ const isMobile = useMediaQuery(theme.breakpoints.down("sm"));
+ const [open, setOpen] = useState(false);
+
+ const handleOpen = () => {
+ setOpen(true);
+ };
+
+ return (
+ <>
+ {isMobile ? (
+
+
+
+ ) : (
+ }
+ onClick={handleOpen}
+ >
+ ثبت فعالیت
+
+ )}
+ {open && }
+ >
+ );
+};
+export default OperatorCreate;
diff --git a/src/components/dashboard/roadItems/operator/ExcelPrint/index.jsx b/src/components/dashboard/roadItems/operator/ExcelPrint/index.jsx
new file mode 100644
index 0000000..605018e
--- /dev/null
+++ b/src/components/dashboard/roadItems/operator/ExcelPrint/index.jsx
@@ -0,0 +1,71 @@
+"use client";
+import { Button, CircularProgress, IconButton, useMediaQuery } from "@mui/material";
+import { useMemo, useState } from "react";
+import moment from "jalali-moment";
+import FileSaver from "file-saver";
+import DescriptionIcon from "@mui/icons-material/Description";
+import useRequest from "@/lib/hooks/useRequest";
+import { EXPORT_ROAD_ITEMS_OPERATOR_LIST } from "@/core/utils/routes";
+import { useTheme } from "@emotion/react";
+
+const PrintExcel = ({ table, filterData }) => {
+ const theme = useTheme();
+ const isMobile = useMediaQuery(theme.breakpoints.down("sm"));
+ const [loading, setLoading] = useState(false);
+ const requestServer = useRequest({ notificationSuccess: true });
+ const activeFilters = Object.entries(filterData).reduce((acc, [key, filter]) => {
+ if (filter.value) {
+ // Check if there's an active filter
+ acc.push({ id: key, value: filter.value });
+ }
+ return acc;
+ }, []);
+
+ const filterParams = useMemo(() => {
+ const params = new URLSearchParams();
+ activeFilters.length > 0 &&
+ activeFilters.map((filter, index) => {
+ params.set(`${filter.id}`, filter.value);
+ });
+ return params;
+ }, [activeFilters]);
+
+ const clickHandler = () => {
+ setLoading(true);
+ requestServer(`${EXPORT_ROAD_ITEMS_OPERATOR_LIST}?${filterParams}`, "get", {
+ requestOptions: { responseType: "blob" },
+ })
+ .then((response) => {
+ const filename = `خروجی کارتابل فعالیت روزانه تاریخ_${moment().format("jYYYY_jMM_jDD")}.xlsx`;
+ FileSaver.saveAs(response.data, filename);
+ })
+ .catch(() => { })
+ .finally(() => {
+ setLoading(false);
+ });
+ };
+
+ return (
+ <>
+ {isMobile ? (
+
+
+
+ ) : (
+ : }
+ onClick={clickHandler}
+ >
+ خروجی اکسل
+
+ )}
+ >
+ );
+};
+
+export default PrintExcel;
diff --git a/src/components/dashboard/roadItems/operator/OperatorList.jsx b/src/components/dashboard/roadItems/operator/OperatorList.jsx
new file mode 100644
index 0000000..aed25de
--- /dev/null
+++ b/src/components/dashboard/roadItems/operator/OperatorList.jsx
@@ -0,0 +1,220 @@
+"use client";
+import { useMemo } from "react";
+import { Box, Stack, Typography } from "@mui/material";
+import DataTableWithAuth from "@/core/components/DataTableWithAuth";
+import Toolbar from "./Toolbar";
+import { GET_ROAD_ITEMS_OPERATOR_LIST } from "@/core/utils/routes";
+import moment from "jalali-moment";
+import RowActions from "./RowActions";
+import LocationForm from "./RowActions/LocationForm";
+import ImageDialog from "./RowActions/ImageForm";
+import RahdaranDialog from "./RowActions/RahdaranForm";
+
+const OperatorList = () => {
+ const columns = useMemo(
+ () => [
+ {
+ accessorKey: "id",
+ header: "کد یکتا",
+ id: "id",
+ enableColumnFilter: true,
+ datatype: "text",
+ filterMode: "equals",
+ columnFilterModeOptions: ["equals", "contains"],
+ },
+ {
+ accessorKey: "item_fa",
+ header: "آیتم فعالیت",
+ id: "item_fa",
+ enableColumnFilter: true,
+ datatype: "text",
+ filterMode: "equals",
+ columnFilterModeOptions: ["equals", "contains"],
+ },
+ {
+ accessorKey: "sub_item_fa",
+ header: "موضوع مشاهده شده", // Observed Subject
+ id: "sub_item_fa",
+ enableColumnFilter: true,
+ datatype: "text",
+ filterMode: "equals",
+ columnFilterModeOptions: ["equals", "contains"],
+ },
+ {
+ accessorKey: "value",
+ header: "مقدار", // Value
+ id: "value",
+ enableColumnFilter: false,
+ datatype: "text",
+ filterMode: "equals",
+ columnFilterModeOptions: ["equals", "contains"],
+ Cell: ({ renderedCellValue, row }) => {
+ return (
+
+ {row.original.sub_item_data}
+ {`(${row.original.unit_fa})`}
+
+ );
+ },
+ },
+ {
+ accessorKey: "files",
+ header: "تصاویر", // Images
+ id: "files",
+ enableColumnFilter: false,
+ enableSorting: false,
+ datatype: "array",
+ muiTableBodyCellProps: {
+ sx: {
+ borderLeft: "1px solid #e1e1e1",
+ py: 0,
+ "&:first-of-type": {
+ borderLeft: "unset",
+ },
+ },
+ },
+ Cell: ({ renderedCellValue }) => {
+ if (renderedCellValue.length > 0) {
+ return (
+
+
+
+ );
+ }
+ return (
+
+ بدون تصویر
+
+ );
+ },
+ },
+ {
+ accessorKey: "location",
+ header: "موقعیت", // Location
+ id: "location",
+ enableColumnFilter: false,
+ enableSorting: false,
+ datatype: "array",
+ muiTableBodyCellProps: {
+ sx: {
+ borderLeft: "1px solid #e1e1e1",
+ py: 0,
+ "&:first-of-type": {
+ borderLeft: "unset",
+ },
+ },
+ },
+ Cell: ({ renderedCellValue, row }) => {
+ return (
+
+
+
+ );
+ },
+ },
+ {
+ accessorKey: "cmms_machine_code",
+ header: "کد خودرو", // Car ID
+ id: "cmms_machine_code",
+ enableColumnFilter: true,
+ datatype: "text",
+ filterMode: "equals",
+ enableSorting: false,
+ columnFilterModeOptions: ["equals", "contains"],
+ },
+ {
+ accessorKey: "rahdaran",
+ header: "راهداران",
+ id: "rahdaran",
+ enableColumnFilter: false,
+ enableSorting: false,
+ datatype: "array",
+ muiTableBodyCellProps: {
+ sx: {
+ borderLeft: "1px solid #e1e1e1",
+ py: 0,
+ "&:first-of-type": {
+ borderLeft: "unset",
+ },
+ },
+ },
+ Cell: ({ renderedCellValue }) => {
+ if (renderedCellValue) {
+ return (
+
+
+
+ );
+ }
+ return بدون شخص;
+ },
+ },
+ {
+ accessorFn: (row) => moment(row.created_at).locale("fa").format("HH:mm | yyyy/MM/DD"),
+ header: "تاریخ ثبت", // Register Date
+ id: "created_at",
+ enableColumnFilter: true,
+ datatype: "date",
+ filterMode: "equals",
+ columnFilterModeOptions: ["equals", "contains"],
+ },
+ {
+ accessorFn: (row) => moment(row.activity_date_time).locale("fa").format("HH:mm | yyyy/MM/DD"),
+ header: "تاریخ فعالیت", // Start Date
+ id: "activity_date_time",
+ enableColumnFilter: true,
+ datatype: "date",
+ filterMode: "equals",
+ columnFilterModeOptions: ["equals", "contains"],
+ },
+ {
+ accessorKey: "status_fa",
+ header: "وضعیت", // Status
+ id: "status_fa",
+ enableColumnFilter: true,
+ datatype: "text",
+ filterMode: "equals",
+ sortDescFirst: true,
+ columnFilterModeOptions: ["equals", "contains"],
+ },
+ {
+ accessorKey: "supervisor_description",
+ header: "توضیحات", // Description
+ id: "supervisor_description",
+ enableColumnFilter: false,
+ datatype: "text",
+ filterMode: "equals",
+ columnFilterModeOptions: ["equals", "contains"],
+ },
+ ],
+ []
+ );
+
+ return (
+ <>
+
+
+
+ >
+ );
+};
+export default OperatorList;
diff --git a/src/components/dashboard/roadItems/operator/RowActions/EditForm/EditFormContent.jsx b/src/components/dashboard/roadItems/operator/RowActions/EditForm/EditFormContent.jsx
new file mode 100644
index 0000000..460d64d
--- /dev/null
+++ b/src/components/dashboard/roadItems/operator/RowActions/EditForm/EditFormContent.jsx
@@ -0,0 +1,211 @@
+import { Button, DialogActions, DialogContent, Stack, TextField } from "@mui/material";
+import useRequest from "@/lib/hooks/useRequest";
+import { Controller, useForm } from "react-hook-form";
+import { yupResolver } from "@hookform/resolvers/yup";
+import { array, mixed, object, string } from "yup";
+import StyledForm from "@/core/components/StyledForm";
+import React from "react";
+import ImageUpload from "@/components/dashboard/roadItems/operator/Actions/Create/Forms/ImageUpload";
+import MapInfoTwoMarker from "@/core/components/MapInfoTwoMarker";
+import MapInfoOneMarker from "@/core/components/MapInfoOneMarker";
+import { UPDATE_ROAD_ITEMS } from "@/core/utils/routes";
+import CarCode from "@/core/components/CarCode";
+import RahdarCode from "@/core/components/RahdarCode";
+import useRoadItemGetISubtems from "@/lib/hooks/useRoadItemGetISubtems";
+
+const validationSchema = object({
+ amount: string().required("وارد کردن مقدار الزامیست!"),
+ cmms_machine_id: object().required("وارد کردن کد خودرو الزامیست!"),
+ rahdaran_id: array().required("وارد کردن کد راهداران الزامیست!").min(1, "حداقل یک کد راهدار باید وارد شود!"),
+ before_image: mixed()
+ .nullable()
+ .test("before-image-required", "لطفا عکس قبل از اقدام را بارگذاری کنید!", function (value) {
+ const { subItem } = this.options.context;
+ const needsImage = subItem?.needs_image === 1;
+ if (needsImage) {
+ return !!value;
+ }
+ return true;
+ }),
+ after_image: mixed()
+ .nullable()
+ .test("after-image-required", "لطفا عکس بعد از اقدام را بارگذاری کنید!", function (value) {
+ const { subItem } = this.options.context;
+ const needsImage = subItem?.needs_image === 1;
+ if (needsImage) {
+ return !!value;
+ }
+ return true;
+ }),
+ start_point: mixed()
+ .test("start-point-required", "لطفاً نقطه شروع را مشخص کنید!", function (value) {
+ return !!value; // چک میکند که مقدار موجود است
+ })
+ .required("لطفاً نقطه شروع را مشخص کنید!"),
+ end_point: mixed().test("end-point-required", "لطفاً نقطه پایان را مشخص کنید!", function (value) {
+ const { subItem } = this.options.context;
+ const needsEndPoint = subItem?.needs_end_point === 1;
+ if (needsEndPoint) {
+ return !!value; // چک میکند که مقدار موجود است
+ }
+ return true;
+ }),
+});
+
+const EditFormContent = ({ row, mutate, setOpenEditDialog, rowId, subItem }) => {
+ const defaultValues = {
+ before_image: row.original?.files[0]?.full_path_for_fast_react || null,
+ after_image: row.original?.files[1]?.full_path_for_fast_react || null,
+ amount: row.original.sub_item_data || null,
+ start_point: { lat: row.original.start_lat || "", lng: row.original.start_lng || "" },
+ end_point: { lat: row.original.end_lat || "", lng: row.original.end_lng || "" },
+ cmms_machine_id: row.original.cmms_machine,
+ rahdaran_id: row.original.rahdaran || null,
+ };
+ const requestServer = useRequest();
+
+ const {
+ control,
+ getValues,
+ watch,
+ register,
+ handleSubmit,
+ setValue,
+ formState: { errors, isSubmitting },
+ } = useForm({
+ defaultValues,
+ resolver: yupResolver(validationSchema),
+ mode: "onBlur",
+ context: { subItem },
+ });
+
+ const onSubmit = async (data) => {
+ let endPoint;
+ let startPoint = `${data.start_point.lat},${data.start_point.lng}`;
+ subItem.needs_end_point === 1 && (endPoint = `${data.end_point.lat},${data.end_point.lng}`);
+ const formData = new FormData();
+ subItem.needs_image === 1 &&
+ data.before_image !== defaultValues.before_image &&
+ formData.append("before_image", data.before_image);
+ subItem.needs_image === 1 &&
+ data.after_image !== defaultValues.after_image &&
+ formData.append("after_image", data.after_image);
+ formData.append("amount", data.amount);
+ formData.append("cmms_machine_id", data.cmms_machine_id.id);
+ data.rahdaran_id.map((rahdar, index) => formData.append(`rahdaran_id[${index}]`, rahdar.id));
+ formData.append("start_point", startPoint);
+ subItem.needs_end_point === 1 && formData.append("end_point", endPoint);
+
+ requestServer(`${UPDATE_ROAD_ITEMS}/${rowId}`, "post", {
+ notificationSuccess: true,
+ data: formData,
+ })
+ .then((res) => {
+ mutate();
+ setOpenEditDialog(false);
+ })
+ .catch((err) => { });
+ };
+ return (
+ <>
+
+
+
+
+ {subItem.needs_image === 1 ? (
+
+ ) : null}
+
+
+ {
+ if (!isNaN(event.target.value)) {
+ setValue("amount", event.target.value);
+ } else {
+ setValue("amount", watch("amount"));
+ }
+ }}
+ error={!!errors.amount}
+ helperText={errors.amount ? errors.amount.message : null}
+ variant="outlined"
+ fullWidth
+ />
+
+
+ {
+ return (
+ field.onChange(value || "")}
+ inputValueDefault={row.original.cmms_machine_code}
+ error={error} // اگر خطا وجود داشته باشد
+ />
+ );
+ }}
+ name={"cmms_machine_id"}
+ />
+
+
+ {
+ return (
+ field.onChange(value || [])}
+ error={error}
+ />
+ );
+ }}
+ name={"rahdaran_id"}
+ />
+
+
+ {subItem.needs_end_point === 1 ? (
+
+ ) : (
+
+ )}
+
+
+
+
+
+
+
+
+ >
+ );
+};
+export default EditFormContent;
diff --git a/src/components/dashboard/roadItems/operator/RowActions/EditForm/index.jsx b/src/components/dashboard/roadItems/operator/RowActions/EditForm/index.jsx
new file mode 100644
index 0000000..92652e9
--- /dev/null
+++ b/src/components/dashboard/roadItems/operator/RowActions/EditForm/index.jsx
@@ -0,0 +1,54 @@
+import { Dialog, DialogTitle, IconButton, LinearProgress, Tooltip, Typography } from "@mui/material";
+import { useState } from "react";
+import BorderColorIcon from "@mui/icons-material/BorderColor";
+import EditFormContent from "@/components/dashboard/roadItems/operator/RowActions/EditForm/EditFormContent";
+import useRoadItemGetISubtems from "@/lib/hooks/useRoadItemGetISubtems";
+
+const EditForm = ({ row, mutate, rowId }) => {
+ const [openEditDialog, setOpenEditDialog] = useState(false);
+ const { subItemsList, loadingSubItemsList, errorSubItemsList } = useRoadItemGetISubtems(row.original?.item);
+ const subItem = subItemsList.find((subItem) => subItem.sub_item === row.original?.sub_item);
+ return (
+ <>
+
+ {
+ setOpenEditDialog(true);
+ }}
+ >
+
+
+
+
+ >
+ );
+};
+export default EditForm;
diff --git a/src/components/dashboard/roadItems/operator/RowActions/ImageForm/ImageFormContent.jsx b/src/components/dashboard/roadItems/operator/RowActions/ImageForm/ImageFormContent.jsx
new file mode 100644
index 0000000..c9f4e2e
--- /dev/null
+++ b/src/components/dashboard/roadItems/operator/RowActions/ImageForm/ImageFormContent.jsx
@@ -0,0 +1,41 @@
+import { DialogContent, Paper, Stack, Typography, useTheme } from "@mui/material";
+
+const ImageFormContent = ({ image, title }) => {
+ const theme = useTheme();
+ return (
+
+
+
+
+ {title}
+
+
+
+
+
+ );
+};
+export default ImageFormContent;
diff --git a/src/components/dashboard/roadItems/operator/RowActions/ImageForm/index.jsx b/src/components/dashboard/roadItems/operator/RowActions/ImageForm/index.jsx
new file mode 100644
index 0000000..8bdb7e9
--- /dev/null
+++ b/src/components/dashboard/roadItems/operator/RowActions/ImageForm/index.jsx
@@ -0,0 +1,41 @@
+import { Button, Dialog, DialogActions, DialogTitle, IconButton, Tooltip } from "@mui/material";
+import { useState } from "react";
+import PhotoLibraryIcon from "@mui/icons-material/PhotoLibrary";
+import ImageFormContent from "./ImageFormContent";
+
+const ImageDialog = ({ images }) => {
+ const [openImageDialog, setOpenImageDialog] = useState(false);
+
+ return (
+ <>
+
+ setOpenImageDialog(true)}>
+
+
+
+
+ >
+ );
+};
+
+export default ImageDialog;
diff --git a/src/components/dashboard/roadItems/operator/RowActions/LocationForm/LocationFormContent.jsx b/src/components/dashboard/roadItems/operator/RowActions/LocationForm/LocationFormContent.jsx
new file mode 100644
index 0000000..6c17f3a
--- /dev/null
+++ b/src/components/dashboard/roadItems/operator/RowActions/LocationForm/LocationFormContent.jsx
@@ -0,0 +1,36 @@
+"use client";
+import { Box, Button, DialogActions, DialogContent, Stack } from "@mui/material";
+import React from "react";
+import dynamic from "next/dynamic";
+import MapLoading from "@/core/components/MapLayer/Loading";
+import ShowLocationMarker from "@/core/components/ShowLocationMarker";
+const MapLayer = dynamic(() => import("@/core/components/MapLayer"), {
+ loading: () => ,
+ ssr: false,
+});
+
+const LocationFormContent = ({ setOpenLocationDialog, start_lat, start_lng, end_lat, end_lng }) => {
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+};
+
+export default LocationFormContent;
diff --git a/src/components/dashboard/roadItems/operator/RowActions/LocationForm/index.jsx b/src/components/dashboard/roadItems/operator/RowActions/LocationForm/index.jsx
new file mode 100644
index 0000000..f578ca8
--- /dev/null
+++ b/src/components/dashboard/roadItems/operator/RowActions/LocationForm/index.jsx
@@ -0,0 +1,38 @@
+import React, { useState } from "react";
+import { Tooltip, IconButton, Dialog, DialogTitle } from "@mui/material";
+import ExploreIcon from "@mui/icons-material/Explore";
+import LocationFormContent from "./LocationFormContent";
+const LocationForm = ({ start_lat, start_lng, end_lat, end_lng }) => {
+ const [openLocationDialog, setOpenLocationDialog] = useState(false);
+
+ return (
+ <>
+
+ setOpenLocationDialog(true)}>
+
+
+
+
+ >
+ );
+};
+export default LocationForm;
diff --git a/src/components/dashboard/roadItems/operator/RowActions/RahdaranForm/RahdaranContent.jsx b/src/components/dashboard/roadItems/operator/RowActions/RahdaranForm/RahdaranContent.jsx
new file mode 100644
index 0000000..d1e9b15
--- /dev/null
+++ b/src/components/dashboard/roadItems/operator/RowActions/RahdaranForm/RahdaranContent.jsx
@@ -0,0 +1,48 @@
+import { DialogContent, Paper, Table, TableBody, TableCell, TableContainer, TableHead, TableRow } from "@mui/material";
+
+const RahdaranContent = ({ rahdarLists }) => {
+ return (
+ <>
+
+
+
+
+
+
+ کد راهدار
+ نام
+
+
+
+ {rahdarLists.map((rahdar) => {
+ return (
+
+ {rahdar.code}
+ {rahdar.name}
+
+ );
+ })}
+
+
+
+
+
+ >
+ );
+};
+export default RahdaranContent;
diff --git a/src/components/dashboard/roadItems/operator/RowActions/RahdaranForm/index.jsx b/src/components/dashboard/roadItems/operator/RowActions/RahdaranForm/index.jsx
new file mode 100644
index 0000000..2c73d80
--- /dev/null
+++ b/src/components/dashboard/roadItems/operator/RowActions/RahdaranForm/index.jsx
@@ -0,0 +1,38 @@
+import { Button, Dialog, DialogActions, DialogTitle, IconButton, Tooltip } from "@mui/material";
+import GroupsIcon from "@mui/icons-material/Groups";
+import { useState } from "react";
+import RahdaranContent from "./RahdaranContent";
+
+const RahdaranDialog = ({ rahdarLists }) => {
+ const [openRahdaranDialog, setOpenRahdaranDialog] = useState(false);
+ return (
+ <>
+
+ setOpenRahdaranDialog(true)}>
+
+
+
+
+ >
+ );
+};
+export default RahdaranDialog;
diff --git a/src/components/dashboard/roadItems/operator/RowActions/index.jsx b/src/components/dashboard/roadItems/operator/RowActions/index.jsx
new file mode 100644
index 0000000..c5051d8
--- /dev/null
+++ b/src/components/dashboard/roadItems/operator/RowActions/index.jsx
@@ -0,0 +1,11 @@
+import { Box } from "@mui/material";
+import EditForm from "./EditForm";
+
+const RowActions = ({ row, mutate }) => {
+ return (
+
+ {row.original.status === 2 && }
+
+ );
+};
+export default RowActions;
diff --git a/src/components/dashboard/roadItems/operator/Toolbar.jsx b/src/components/dashboard/roadItems/operator/Toolbar.jsx
new file mode 100644
index 0000000..c98ae05
--- /dev/null
+++ b/src/components/dashboard/roadItems/operator/Toolbar.jsx
@@ -0,0 +1,13 @@
+import PrintExcel from "./ExcelPrint";
+import OperatorCreate from "./Actions/Create";
+import { Stack } from "@mui/material";
+
+const Toolbar = ({ table, filterData, mutate }) => {
+ return (
+
+
+
+
+ );
+};
+export default Toolbar;
diff --git a/src/components/dashboard/roadItems/operator/index.jsx b/src/components/dashboard/roadItems/operator/index.jsx
new file mode 100644
index 0000000..a2b328c
--- /dev/null
+++ b/src/components/dashboard/roadItems/operator/index.jsx
@@ -0,0 +1,14 @@
+"use client";
+import PageTitle from "@/core/components/PageTitle";
+import { Stack } from "@mui/material";
+import OperatorList from "./OperatorList";
+
+const OperatorPage = () => {
+ return (
+
+
+
+
+ );
+};
+export default OperatorPage;
diff --git a/src/components/dashboard/roadItems/supervisor/ExcelPrint/index.jsx b/src/components/dashboard/roadItems/supervisor/ExcelPrint/index.jsx
new file mode 100644
index 0000000..1cc0b2c
--- /dev/null
+++ b/src/components/dashboard/roadItems/supervisor/ExcelPrint/index.jsx
@@ -0,0 +1,70 @@
+import { Button, CircularProgress, IconButton, useMediaQuery } from "@mui/material";
+import { useMemo, useState } from "react";
+import moment from "jalali-moment";
+import FileSaver from "file-saver";
+import DescriptionIcon from "@mui/icons-material/Description";
+import useRequest from "@/lib/hooks/useRequest";
+import { EXPORT_ROAD_ITEMS_SUPERVISOR_LIST } from "@/core/utils/routes";
+import { useTheme } from "@emotion/react";
+
+const PrintExcel = ({ table, filterData }) => {
+ const [loading, setLoading] = useState(false);
+ const theme = useTheme();
+ const isMobile = useMediaQuery(theme.breakpoints.down("sm"));
+ const requestServer = useRequest();
+ const activeFilters = Object.entries(filterData).reduce((acc, [key, filter]) => {
+ if (filter.value) {
+ // Check if there's an active filter
+ acc.push({ id: key, value: filter.value });
+ }
+ return acc;
+ }, []);
+
+ const filterParams = useMemo(() => {
+ const params = new URLSearchParams();
+ activeFilters.length > 0 &&
+ activeFilters.map((filter, index) => {
+ params.set(`${filter.id}`, filter.value);
+ });
+ return params;
+ }, [activeFilters]);
+
+ const clickHandler = () => {
+ setLoading(true);
+ requestServer(`${EXPORT_ROAD_ITEMS_SUPERVISOR_LIST}?${filterParams}`, "get", {
+ requestOptions: { responseType: "blob" },
+ })
+ .then((response) => {
+ const filename = `خروجی کارتابل فعالیت روزانه تاریخ_${moment().format("jYYYY_jMM_jDD")}.xlsx`;
+ FileSaver.saveAs(response.data, filename);
+ })
+ .catch(() => {})
+ .finally(() => {
+ setLoading(false);
+ });
+ };
+
+ return (
+ <>
+ {isMobile ? (
+
+
+
+ ) : (
+ : }
+ onClick={clickHandler}
+ >
+ خروجی اکسل
+
+ )}
+ >
+ );
+};
+
+export default PrintExcel;
diff --git a/src/components/dashboard/roadItems/supervisor/RowActions/ConfirmForm/ConfirmContent.jsx b/src/components/dashboard/roadItems/supervisor/RowActions/ConfirmForm/ConfirmContent.jsx
new file mode 100644
index 0000000..f4be809
--- /dev/null
+++ b/src/components/dashboard/roadItems/supervisor/RowActions/ConfirmForm/ConfirmContent.jsx
@@ -0,0 +1,69 @@
+import { Button, DialogActions, DialogContent, Stack, TextField } from "@mui/material";
+import { useForm } from "react-hook-form";
+import useRequest from "@/lib/hooks/useRequest";
+import { VERIFY_BY_SUPERVISOR } from "@/core/utils/routes";
+
+const ConfirmContent = ({ rowId, mutate, setOpenConfirmDialog }) => {
+ const requestServer = useRequest({ notificationSuccess: true });
+
+ const {
+ register,
+ handleSubmit,
+ formState: { isSubmitting },
+ reset,
+ } = useForm({
+ defaultValues: {
+ description: "",
+ },
+ });
+
+ const onSubmit = async (data) => {
+ const formData = new FormData();
+ data.description !== "" && formData.append("description", data.description);
+ formData.append("verify", 1);
+ requestServer(`${VERIFY_BY_SUPERVISOR}/${rowId}`, "post", {
+ data: formData,
+ })
+ .then(() => {
+ mutate();
+ setOpenConfirmDialog(false);
+ })
+ .catch(() => { });
+ };
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+};
+
+export default ConfirmContent;
diff --git a/src/components/dashboard/roadItems/supervisor/RowActions/ConfirmForm/index.jsx b/src/components/dashboard/roadItems/supervisor/RowActions/ConfirmForm/index.jsx
new file mode 100644
index 0000000..a542a30
--- /dev/null
+++ b/src/components/dashboard/roadItems/supervisor/RowActions/ConfirmForm/index.jsx
@@ -0,0 +1,29 @@
+import { Dialog, DialogTitle, IconButton, Tooltip } from "@mui/material";
+import { useState } from "react";
+import ConfirmContent from "./ConfirmContent";
+import DoneIcon from "@mui/icons-material/Done";
+const ConfirmForm = ({ rowId, mutate }) => {
+ const [openConfirmDialog, setOpenConfirmDialog] = useState(false);
+ return (
+ <>
+
+ setOpenConfirmDialog(true)}>
+
+
+
+
+ >
+ );
+};
+export default ConfirmForm;
diff --git a/src/components/dashboard/roadItems/supervisor/RowActions/DeleteForm/DeleteContent.jsx b/src/components/dashboard/roadItems/supervisor/RowActions/DeleteForm/DeleteContent.jsx
new file mode 100644
index 0000000..18198a8
--- /dev/null
+++ b/src/components/dashboard/roadItems/supervisor/RowActions/DeleteForm/DeleteContent.jsx
@@ -0,0 +1,42 @@
+import { Button, DialogActions, DialogContent, Stack, Typography } from "@mui/material";
+import React, { useState } from "react";
+import useRequest from "@/lib/hooks/useRequest";
+import { DELETE_BY_SUPERVISOR } from "@/core/utils/routes";
+
+const DeleteContent = ({ rowId, mutate, setOpenDeleteDialog }) => {
+ const [submitting, setSubmitting] = useState(false);
+ const requestServer = useRequest({ notificationSuccess: true });
+ const handleClick = () => {
+ setSubmitting(true);
+ requestServer(`${DELETE_BY_SUPERVISOR}/${rowId}`, "post")
+ .then(() => {
+ mutate();
+ setOpenDeleteDialog(false);
+ setSubmitting(false);
+ })
+ .catch(() => {
+ setSubmitting(false);
+ });
+ };
+ return (
+ <>
+
+
+
+ آیا از حذف فعالیت اطمینان دارید؟
+
+
+
+
+
+
+
+ >
+ );
+};
+
+export default DeleteContent;
diff --git a/src/components/dashboard/roadItems/supervisor/RowActions/DeleteForm/index.jsx b/src/components/dashboard/roadItems/supervisor/RowActions/DeleteForm/index.jsx
new file mode 100644
index 0000000..c8c8375
--- /dev/null
+++ b/src/components/dashboard/roadItems/supervisor/RowActions/DeleteForm/index.jsx
@@ -0,0 +1,33 @@
+import React, { useState } from "react";
+import { Tooltip, IconButton, Dialog, DialogTitle } from "@mui/material";
+import DeleteIcon from "@mui/icons-material/Delete";
+import DeleteContent from "./DeleteContent";
+import useRequest from "@/lib/hooks/useRequest";
+const DeleteForm = ({ rowId, mutate }) => {
+ const [openDeleteDialog, setOpenDeleteDialog] = useState(false);
+
+ return (
+ <>
+
+ setOpenDeleteDialog(true)}>
+
+
+
+
+ >
+ );
+};
+export default DeleteForm;
diff --git a/src/components/dashboard/roadItems/supervisor/RowActions/ImageForm/ImageFormContent.jsx b/src/components/dashboard/roadItems/supervisor/RowActions/ImageForm/ImageFormContent.jsx
new file mode 100644
index 0000000..c9f4e2e
--- /dev/null
+++ b/src/components/dashboard/roadItems/supervisor/RowActions/ImageForm/ImageFormContent.jsx
@@ -0,0 +1,41 @@
+import { DialogContent, Paper, Stack, Typography, useTheme } from "@mui/material";
+
+const ImageFormContent = ({ image, title }) => {
+ const theme = useTheme();
+ return (
+
+
+
+
+ {title}
+
+
+
+
+
+ );
+};
+export default ImageFormContent;
diff --git a/src/components/dashboard/roadItems/supervisor/RowActions/ImageForm/index.jsx b/src/components/dashboard/roadItems/supervisor/RowActions/ImageForm/index.jsx
new file mode 100644
index 0000000..0e70c1d
--- /dev/null
+++ b/src/components/dashboard/roadItems/supervisor/RowActions/ImageForm/index.jsx
@@ -0,0 +1,41 @@
+import { Button, Dialog, DialogActions, DialogTitle, IconButton, Tooltip } from "@mui/material";
+import { useState } from "react";
+import PhotoLibraryIcon from "@mui/icons-material/PhotoLibrary";
+import ImageFormContent from "./ImageFormContent";
+
+const ImageDialog = ({ images }) => {
+ const [openImageDialog, setOpenImageDialog] = useState(false);
+
+ return (
+ <>
+
+ setOpenImageDialog(true)}>
+
+
+
+
+ >
+ );
+};
+
+export default ImageDialog;
diff --git a/src/components/dashboard/roadItems/supervisor/RowActions/LocationForm/LocationFormContent.jsx b/src/components/dashboard/roadItems/supervisor/RowActions/LocationForm/LocationFormContent.jsx
new file mode 100644
index 0000000..6c17f3a
--- /dev/null
+++ b/src/components/dashboard/roadItems/supervisor/RowActions/LocationForm/LocationFormContent.jsx
@@ -0,0 +1,36 @@
+"use client";
+import { Box, Button, DialogActions, DialogContent, Stack } from "@mui/material";
+import React from "react";
+import dynamic from "next/dynamic";
+import MapLoading from "@/core/components/MapLayer/Loading";
+import ShowLocationMarker from "@/core/components/ShowLocationMarker";
+const MapLayer = dynamic(() => import("@/core/components/MapLayer"), {
+ loading: () => ,
+ ssr: false,
+});
+
+const LocationFormContent = ({ setOpenLocationDialog, start_lat, start_lng, end_lat, end_lng }) => {
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+};
+
+export default LocationFormContent;
diff --git a/src/components/dashboard/roadItems/supervisor/RowActions/LocationForm/index.jsx b/src/components/dashboard/roadItems/supervisor/RowActions/LocationForm/index.jsx
new file mode 100644
index 0000000..f578ca8
--- /dev/null
+++ b/src/components/dashboard/roadItems/supervisor/RowActions/LocationForm/index.jsx
@@ -0,0 +1,38 @@
+import React, { useState } from "react";
+import { Tooltip, IconButton, Dialog, DialogTitle } from "@mui/material";
+import ExploreIcon from "@mui/icons-material/Explore";
+import LocationFormContent from "./LocationFormContent";
+const LocationForm = ({ start_lat, start_lng, end_lat, end_lng }) => {
+ const [openLocationDialog, setOpenLocationDialog] = useState(false);
+
+ return (
+ <>
+
+ setOpenLocationDialog(true)}>
+
+
+
+
+ >
+ );
+};
+export default LocationForm;
diff --git a/src/components/dashboard/roadItems/supervisor/RowActions/RahdaranForm/RahdaranContent.jsx b/src/components/dashboard/roadItems/supervisor/RowActions/RahdaranForm/RahdaranContent.jsx
new file mode 100644
index 0000000..d1e9b15
--- /dev/null
+++ b/src/components/dashboard/roadItems/supervisor/RowActions/RahdaranForm/RahdaranContent.jsx
@@ -0,0 +1,48 @@
+import { DialogContent, Paper, Table, TableBody, TableCell, TableContainer, TableHead, TableRow } from "@mui/material";
+
+const RahdaranContent = ({ rahdarLists }) => {
+ return (
+ <>
+
+
+
+
+
+
+ کد راهدار
+ نام
+
+
+
+ {rahdarLists.map((rahdar) => {
+ return (
+
+ {rahdar.code}
+ {rahdar.name}
+
+ );
+ })}
+
+
+
+
+
+ >
+ );
+};
+export default RahdaranContent;
diff --git a/src/components/dashboard/roadItems/supervisor/RowActions/RahdaranForm/index.jsx b/src/components/dashboard/roadItems/supervisor/RowActions/RahdaranForm/index.jsx
new file mode 100644
index 0000000..2c73d80
--- /dev/null
+++ b/src/components/dashboard/roadItems/supervisor/RowActions/RahdaranForm/index.jsx
@@ -0,0 +1,38 @@
+import { Button, Dialog, DialogActions, DialogTitle, IconButton, Tooltip } from "@mui/material";
+import GroupsIcon from "@mui/icons-material/Groups";
+import { useState } from "react";
+import RahdaranContent from "./RahdaranContent";
+
+const RahdaranDialog = ({ rahdarLists }) => {
+ const [openRahdaranDialog, setOpenRahdaranDialog] = useState(false);
+ return (
+ <>
+
+ setOpenRahdaranDialog(true)}>
+
+
+
+
+ >
+ );
+};
+export default RahdaranDialog;
diff --git a/src/components/dashboard/roadItems/supervisor/RowActions/RejectForm/RejectContent.jsx b/src/components/dashboard/roadItems/supervisor/RowActions/RejectForm/RejectContent.jsx
new file mode 100644
index 0000000..81cc311
--- /dev/null
+++ b/src/components/dashboard/roadItems/supervisor/RowActions/RejectForm/RejectContent.jsx
@@ -0,0 +1,81 @@
+import { Button, DialogActions, DialogContent, Stack, TextField } from "@mui/material";
+import { useForm } from "react-hook-form";
+import { yupResolver } from "@hookform/resolvers/yup";
+import * as Yup from "yup";
+import useRequest from "@/lib/hooks/useRequest";
+import { REJECT_BY_SUPERVISOR } from "@/core/utils/routes";
+
+const RejectContent = ({ rowId, mutate, setOpenRejectDialog }) => {
+ const requestServer = useRequest({ notificationSuccess: true });
+
+ const validationSchema = Yup.object().shape({
+ description: Yup.string().required("توضیحات الزامیست!!!"),
+ });
+
+ const {
+ register,
+ handleSubmit,
+ formState: { errors, isSubmitting },
+ reset,
+ } = useForm({
+ defaultValues: {
+ description: "",
+ },
+ resolver: yupResolver(validationSchema),
+ mode: "onBlur",
+ });
+
+ const onSubmit = async (data) => {
+ const formData = new FormData();
+ formData.append("description", data.description);
+ formData.append("verify", 2);
+ requestServer(`${REJECT_BY_SUPERVISOR}/${rowId}`, "post", {
+ data: formData,
+ })
+ .then(() => {
+ mutate();
+ setOpenRejectDialog(false);
+ })
+ .catch(() => { })
+ .finally(() => {
+ reset();
+ });
+ };
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+};
+export default RejectContent;
diff --git a/src/components/dashboard/roadItems/supervisor/RowActions/RejectForm/index.jsx b/src/components/dashboard/roadItems/supervisor/RowActions/RejectForm/index.jsx
new file mode 100644
index 0000000..e05288c
--- /dev/null
+++ b/src/components/dashboard/roadItems/supervisor/RowActions/RejectForm/index.jsx
@@ -0,0 +1,29 @@
+import { Dialog, DialogTitle, IconButton, Tooltip } from "@mui/material";
+import { useState } from "react";
+import RejectContent from "./RejectContent";
+import ClearIcon from "@mui/icons-material/Clear";
+const RejectForm = ({ rowId, mutate }) => {
+ const [openRejectDialog, setOpenRejectDialog] = useState(false);
+ return (
+ <>
+
+ setOpenRejectDialog(true)}>
+
+
+
+
+ >
+ );
+};
+export default RejectForm;
diff --git a/src/components/dashboard/roadItems/supervisor/RowActions/RestoreForm/RestoreContent.jsx b/src/components/dashboard/roadItems/supervisor/RowActions/RestoreForm/RestoreContent.jsx
new file mode 100644
index 0000000..3155406
--- /dev/null
+++ b/src/components/dashboard/roadItems/supervisor/RowActions/RestoreForm/RestoreContent.jsx
@@ -0,0 +1,42 @@
+import { Button, DialogActions, DialogContent, Stack, Typography } from "@mui/material";
+import React, { useState } from "react";
+import useRequest from "@/lib/hooks/useRequest";
+import { RESTORE_BY_SUPERVISOR } from "@/core/utils/routes";
+
+const RestoreContent = ({ rowId, mutate, setOpenRestoreDialog }) => {
+ const [submitting, setSubmitting] = useState(false);
+ const requestServer = useRequest({ notificationSuccess: true });
+ const handleClick = () => {
+ setSubmitting(true);
+ requestServer(`${RESTORE_BY_SUPERVISOR}/${rowId}`, "post")
+ .then(() => {
+ mutate();
+ setOpenRestoreDialog(false);
+ setSubmitting(false);
+ })
+ .catch(() => {
+ setSubmitting(false);
+ });
+ };
+ return (
+ <>
+
+
+
+ آیا از بازگردانی فعالیت اطمینان دارید؟
+
+
+
+
+
+
+
+ >
+ );
+};
+
+export default RestoreContent;
diff --git a/src/components/dashboard/roadItems/supervisor/RowActions/RestoreForm/index.jsx b/src/components/dashboard/roadItems/supervisor/RowActions/RestoreForm/index.jsx
new file mode 100644
index 0000000..2632c20
--- /dev/null
+++ b/src/components/dashboard/roadItems/supervisor/RowActions/RestoreForm/index.jsx
@@ -0,0 +1,32 @@
+import React, { useState } from "react";
+import { Tooltip, IconButton, Dialog, DialogTitle } from "@mui/material";
+import ReplyIcon from "@mui/icons-material/Reply";
+import RestoreContent from "./RestoreContent";
+const RestoreForm = ({ rowId, mutate }) => {
+ const [openRestoreDialog, setOpenRestoreDialog] = useState(false);
+
+ return (
+ <>
+
+ setOpenRestoreDialog(true)}>
+
+
+
+
+ >
+ );
+};
+export default RestoreForm;
diff --git a/src/components/dashboard/roadItems/supervisor/RowActions/index.jsx b/src/components/dashboard/roadItems/supervisor/RowActions/index.jsx
new file mode 100644
index 0000000..b18a1da
--- /dev/null
+++ b/src/components/dashboard/roadItems/supervisor/RowActions/index.jsx
@@ -0,0 +1,27 @@
+import { Box } from "@mui/material";
+import DeleteForm from "./DeleteForm";
+import RejectForm from "./RejectForm";
+import ConfirmForm from "./ConfirmForm";
+import RestoreForm from "./RestoreForm";
+import { usePermissions } from "@/lib/hooks/usePermissions";
+
+const RowActions = ({ row, mutate }) => {
+ const { data: userPermissions } = usePermissions();
+ const hasDeletePermission = userPermissions.includes("delete-road-item");
+ const hasRestorePermission = userPermissions.includes("restore-road-item");
+ return (
+
+ {row.original.status === 0 && row.original.can_supervise === 1 && (
+
+ )}
+ {row.original.status === 0 && row.original.can_supervise === 1 && (
+
+ )}
+ {hasRestorePermission && row.original.status !== 0 && (
+
+ )}
+ {hasDeletePermission && }
+
+ );
+};
+export default RowActions;
diff --git a/src/components/dashboard/roadItems/supervisor/SupervisorList.jsx b/src/components/dashboard/roadItems/supervisor/SupervisorList.jsx
new file mode 100644
index 0000000..1166ed6
--- /dev/null
+++ b/src/components/dashboard/roadItems/supervisor/SupervisorList.jsx
@@ -0,0 +1,239 @@
+import { useMemo } from "react";
+import { Box, Stack, Typography } from "@mui/material";
+import DataTableWithAuth from "@/core/components/DataTableWithAuth";
+import Toolbar from "./Toolbar";
+import { GET_ROAD_ITEMS_SUPERVISOR_LIST } from "@/core/utils/routes";
+import moment from "jalali-moment";
+import RowActions from "./RowActions";
+import LocationForm from "./RowActions/LocationForm";
+import ImageDialog from "./RowActions/ImageForm";
+import RahdaranDialog from "./RowActions/RahdaranForm";
+
+const SupervisorList = () => {
+ const columns = useMemo(
+ () => [
+ {
+ accessorKey: "id",
+ header: "کد یکتا", // Unique Code
+ id: "id",
+ enableColumnFilter: true,
+ datatype: "text",
+ filterMode: "equals",
+ sortDescFirst: false,
+ columnFilterModeOptions: ["equals", "contains"],
+ },
+ {
+ accessorKey: "province_fa",
+ header: "استان", // Province
+ id: "province_fa",
+ enableColumnFilter: true,
+ datatype: "text",
+ filterMode: "equals",
+ columnFilterModeOptions: ["equals", "contains"],
+ },
+ {
+ accessorKey: "edarat_name",
+ header: "اداره", // Office
+ id: "edarat_name",
+ enableColumnFilter: true,
+ datatype: "text",
+ filterMode: "equals",
+ columnFilterModeOptions: ["equals", "contains"],
+ },
+ {
+ accessorKey: "item_fa",
+ header: "آیتم فعالیت",
+ id: "item_fa",
+ enableColumnFilter: true,
+ datatype: "text",
+ filterMode: "equals",
+ columnFilterModeOptions: ["equals", "contains"],
+ },
+ {
+ accessorKey: "sub_item_fa",
+ header: "موضوع مشاهده شده", // Observed Subject
+ id: "sub_item_fa",
+ enableColumnFilter: true,
+ datatype: "text",
+ filterMode: "equals",
+ columnFilterModeOptions: ["equals", "contains"],
+ },
+ {
+ accessorKey: "value",
+ header: "مقدار", // Value
+ id: "value",
+ enableColumnFilter: false,
+ datatype: "text",
+ filterMode: "equals",
+ columnFilterModeOptions: ["equals", "contains"],
+ Cell: ({ renderedCellValue, row }) => {
+ return (
+
+ {row.original.sub_item_data}
+ {`(${row.original.unit_fa})`}
+
+ );
+ },
+ },
+ {
+ accessorKey: "files",
+ header: "تصاویر", // Images
+ id: "files",
+ enableColumnFilter: false,
+ enableSorting: false,
+ datatype: "array",
+ muiTableBodyCellProps: {
+ sx: {
+ borderLeft: "1px solid #e1e1e1",
+ py: 0,
+ "&:first-of-type": {
+ borderLeft: "unset",
+ },
+ },
+ },
+ Cell: ({ renderedCellValue }) => {
+ if (renderedCellValue.length > 0) {
+ return (
+
+
+
+ );
+ }
+ return (
+
+ بدون تصویر
+
+ );
+ },
+ },
+ {
+ accessorKey: "location",
+ header: "موقعیت", // Location
+ id: "location",
+ enableColumnFilter: false,
+ enableSorting: false,
+ datatype: "array",
+ muiTableBodyCellProps: {
+ sx: {
+ borderLeft: "1px solid #e1e1e1",
+ py: 0,
+ "&:first-of-type": {
+ borderLeft: "unset",
+ },
+ },
+ },
+ Cell: ({ renderedCellValue, row }) => {
+ return (
+
+
+
+ );
+ },
+ },
+ {
+ accessorKey: "cmms_machine_code",
+ header: "کد خودرو", // Car ID
+ id: "cmms_machine_code",
+ enableColumnFilter: true,
+ datatype: "text",
+ filterMode: "equals",
+ enableSorting: false,
+ columnFilterModeOptions: ["equals", "contains"],
+ },
+ {
+ accessorKey: "rahdaran",
+ header: "راهداران",
+ id: "rahdaran",
+ enableColumnFilter: false,
+ enableSorting: false,
+ datatype: "array",
+ muiTableBodyCellProps: {
+ sx: {
+ borderLeft: "1px solid #e1e1e1",
+ py: 0,
+ "&:first-of-type": {
+ borderLeft: "unset",
+ },
+ },
+ },
+ Cell: ({ renderedCellValue }) => {
+ if (renderedCellValue) {
+ return (
+
+
+
+ );
+ }
+ return بدون شخص;
+ },
+ },
+ {
+ accessorFn: (row) => moment(row.created_at).locale("fa").format("HH:mm | yyyy/MM/DD"),
+ header: "تاریخ ثبت", // Register Date
+ id: "created_at",
+ enableColumnFilter: true,
+ datatype: "date",
+ filterMode: "equals",
+ columnFilterModeOptions: ["equals", "contains"],
+ },
+ {
+ accessorFn: (row) => moment(row.activity_date_time).locale("fa").format("HH:mm | yyyy/MM/DD"),
+ header: "تاریخ فعالیت", // Start Date
+ id: "activity_date_time",
+ enableColumnFilter: true,
+ datatype: "date",
+ filterMode: "equals",
+ columnFilterModeOptions: ["equals", "contains"],
+ },
+ {
+ accessorKey: "status_fa",
+ header: "وضعیت", // Status
+ id: "status_fa",
+ enableColumnFilter: true,
+ datatype: "text",
+ filterMode: "equals",
+ sortDescFirst: true,
+ columnFilterModeOptions: ["equals", "contains"],
+ },
+ {
+ accessorKey: "supervisor_description",
+ header: "توضیحات", // Description
+ id: "supervisor_description",
+ enableColumnFilter: false,
+ datatype: "text",
+ filterMode: "equals",
+ columnFilterModeOptions: ["equals", "contains"],
+ },
+ ],
+ []
+ );
+
+ return (
+ <>
+
+
+
+ >
+ );
+};
+
+export default SupervisorList;
diff --git a/src/components/dashboard/roadItems/supervisor/Toolbar.jsx b/src/components/dashboard/roadItems/supervisor/Toolbar.jsx
new file mode 100644
index 0000000..32c4395
--- /dev/null
+++ b/src/components/dashboard/roadItems/supervisor/Toolbar.jsx
@@ -0,0 +1,10 @@
+import PrintExcel from "./ExcelPrint";
+
+const Toolbar = ({ table, filterData }) => {
+ return (
+ <>
+
+ >
+ );
+};
+export default Toolbar;
diff --git a/src/components/dashboard/roadItems/supervisor/index.jsx b/src/components/dashboard/roadItems/supervisor/index.jsx
new file mode 100644
index 0000000..3fa490b
--- /dev/null
+++ b/src/components/dashboard/roadItems/supervisor/index.jsx
@@ -0,0 +1,14 @@
+"use client";
+import PageTitle from "@/core/components/PageTitle";
+import { Stack } from "@mui/material";
+import SupervisorList from "./SupervisorList";
+
+const SupervisorPage = () => {
+ return (
+
+
+
+
+ );
+};
+export default SupervisorPage;
diff --git a/src/components/dashboard/roadPatrols/operator/ExcelPrint/index.jsx b/src/components/dashboard/roadPatrols/operator/ExcelPrint/index.jsx
index 9148331..54df4da 100644
--- a/src/components/dashboard/roadPatrols/operator/ExcelPrint/index.jsx
+++ b/src/components/dashboard/roadPatrols/operator/ExcelPrint/index.jsx
@@ -1,18 +1,18 @@
-import {Button, CircularProgress} from "@mui/material";
-import {useMemo, useState} from "react";
+import { Button, CircularProgress } from "@mui/material";
+import { useMemo, useState } from "react";
import moment from "jalali-moment";
import FileSaver from "file-saver";
import DescriptionIcon from "@mui/icons-material/Description";
import useRequest from "@/lib/hooks/useRequest";
-import {EXPORT_OPERATOR_LIST} from "@/core/utils/routes";
+import { EXPORT_ROAD_PATROL_OPERATOR_LIST } from "@/core/utils/routes";
-const PrintExcel = ({table, filterData}) => {
+const PrintExcel = ({ table, filterData }) => {
const [loading, setLoading] = useState(false);
const requestServer = useRequest();
const activeFilters = Object.entries(filterData).reduce((acc, [key, filter]) => {
if (filter.value) {
// Check if there's an active filter
- acc.push({id: key, value: filter.value});
+ acc.push({ id: key, value: filter.value });
}
return acc;
}, []);
@@ -20,23 +20,22 @@ const PrintExcel = ({table, filterData}) => {
const filterParams = useMemo(() => {
const params = new URLSearchParams();
activeFilters.length > 0 &&
- activeFilters.map((filter, index) => {
- params.set(`${filter.id}`, filter.value);
- });
+ activeFilters.map((filter, index) => {
+ params.set(`${filter.id}`, filter.value);
+ });
return params;
}, [activeFilters]);
const clickHandler = () => {
setLoading(true);
- requestServer(`${EXPORT_OPERATOR_LIST}?${filterParams}`, "get", {
- requestOptions: {responseType: "blob"},
+ requestServer(`${EXPORT_ROAD_PATROL_OPERATOR_LIST}?${filterParams}`, "get", {
+ requestOptions: { responseType: "blob" },
})
.then((response) => {
const filename = `خروجی کارتابل عملیات تاریخ_${moment().format("jYYYY_jMM_jDD")}.xlsx`;
FileSaver.saveAs(response.data, filename);
})
- .catch(() => {
- })
+ .catch(() => {})
.finally(() => {
setLoading(false);
});
@@ -46,9 +45,10 @@ const PrintExcel = ({table, filterData}) => {
: }
+ sx={{ textTransform: "unset", alignSelf: "center" }}
+ startIcon={loading ? : }
onClick={clickHandler}
>
خروجی اکسل
diff --git a/src/components/dashboard/roadPatrols/operator/OperatorList.jsx b/src/components/dashboard/roadPatrols/operator/OperatorList.jsx
index 292f197..05f083b 100644
--- a/src/components/dashboard/roadPatrols/operator/OperatorList.jsx
+++ b/src/components/dashboard/roadPatrols/operator/OperatorList.jsx
@@ -3,7 +3,7 @@ import { useMemo } from "react";
import { Box, Typography } from "@mui/material";
import DataTableWithAuth from "@/core/components/DataTableWithAuth";
import Toolbar from "./Toolbar";
-import { GET_OPERATOR_LIST } from "@/core/utils/routes";
+import { GET_ROAD_PATROL_OPERATOR_LIST } from "@/core/utils/routes";
import moment from "jalali-moment";
import RowActions from "./RowActions";
@@ -16,7 +16,7 @@ const OperatorList = () => {
id: "id",
enableColumnFilter: true,
datatype: "text",
- filterFn: "equals",
+ filterMode: "equals",
columnFilterModeOptions: ["equals", "contains"],
},
{
@@ -25,7 +25,7 @@ const OperatorList = () => {
id: "operator_name",
enableColumnFilter: true,
datatype: "text",
- filterFn: "equals",
+ filterMode: "equals",
columnFilterModeOptions: ["equals", "contains"],
},
{
@@ -34,7 +34,7 @@ const OperatorList = () => {
id: "phone_number",
enableColumnFilter: true,
datatype: "text",
- filterFn: "equals",
+ filterMode: "equals",
columnFilterModeOptions: ["equals", "contains"],
},
{
@@ -43,7 +43,7 @@ const OperatorList = () => {
id: "car_id",
enableColumnFilter: true,
datatype: "text",
- filterFn: "equals",
+ filterMode: "equals",
columnFilterModeOptions: ["equals", "contains"],
},
{
@@ -52,7 +52,7 @@ const OperatorList = () => {
id: "start_date",
enableColumnFilter: true,
datatype: "date",
- filterFn: "equals",
+ filterMode: "equals",
columnFilterModeOptions: ["equals", "contains"],
Cell: ({ renderedCellValue }) => (
@@ -66,7 +66,7 @@ const OperatorList = () => {
id: "end_date",
enableColumnFilter: true,
datatype: "date",
- filterFn: "equals",
+ filterMode: "equals",
columnFilterModeOptions: ["equals", "contains"],
Cell: ({ renderedCellValue }) => (
@@ -80,7 +80,7 @@ const OperatorList = () => {
id: "register_date",
enableColumnFilter: true,
datatype: "date",
- filterFn: "equals",
+ filterMode: "equals",
columnFilterModeOptions: ["equals", "contains"],
Cell: ({ renderedCellValue }) => (
@@ -99,7 +99,7 @@ const OperatorList = () => {
table_title={"عملیات"}
need_filter={true}
columns={columns}
- table_url={GET_OPERATOR_LIST}
+ table_url={GET_ROAD_PATROL_OPERATOR_LIST}
page_name={"operator"}
table_name={"operatorList"}
TableToolbar={Toolbar}
diff --git a/src/components/dashboard/roadPatrols/supervisor/ExcelPrint/index.jsx b/src/components/dashboard/roadPatrols/supervisor/ExcelPrint/index.jsx
index 49c05eb..e5ba95a 100644
--- a/src/components/dashboard/roadPatrols/supervisor/ExcelPrint/index.jsx
+++ b/src/components/dashboard/roadPatrols/supervisor/ExcelPrint/index.jsx
@@ -4,7 +4,7 @@ import moment from "jalali-moment";
import FileSaver from "file-saver";
import DescriptionIcon from "@mui/icons-material/Description";
import useRequest from "@/lib/hooks/useRequest";
-import { EXPORT_SUPERVISOR_LIST } from "@/core/utils/routes";
+import { EXPORT_ROAD_PATROL_SUPERVISOR_LIST } from "@/core/utils/routes";
const PrintExcel = ({ table, filterData }) => {
const [loading, setLoading] = useState(false);
@@ -28,7 +28,7 @@ const PrintExcel = ({ table, filterData }) => {
const clickHandler = () => {
setLoading(true);
- requestServer(`${EXPORT_SUPERVISOR_LIST}?${filterParams}`, "get", {
+ requestServer(`${EXPORT_ROAD_PATROL_SUPERVISOR_LIST}?${filterParams}`, "get", {
requestOptions: { responseType: "blob" },
})
.then((response) => {
diff --git a/src/components/dashboard/roadPatrols/supervisor/RowActions/DeleteForm/index.jsx b/src/components/dashboard/roadPatrols/supervisor/RowActions/DeleteForm/index.jsx
index 46cad99..a7218df 100644
--- a/src/components/dashboard/roadPatrols/supervisor/RowActions/DeleteForm/index.jsx
+++ b/src/components/dashboard/roadPatrols/supervisor/RowActions/DeleteForm/index.jsx
@@ -1,19 +1,7 @@
import React, { useState } from "react";
-import {
- Tooltip,
- IconButton,
- Dialog,
- DialogTitle,
- DialogContent,
- DialogActions,
- Button,
- Box,
- Typography,
-} from "@mui/material";
+import { Tooltip, IconButton, Dialog, DialogTitle } from "@mui/material";
import DeleteIcon from "@mui/icons-material/Delete";
-import CloseIcon from "@mui/icons-material/Close";
-import DeleteContent from "@/components/dashboard/roadPatrols/supervisor/RowActions/DeleteForm/DeleteContent";
-
+import DeleteContent from "./DeleteContent";
const DeleteForm = ({ rowId, mutate }) => {
const [openDeleteDialog, setOpenDeleteDialog] = useState(false);
@@ -41,5 +29,4 @@ const DeleteForm = ({ rowId, mutate }) => {
>
);
};
-
export default DeleteForm;
diff --git a/src/components/dashboard/roadPatrols/supervisor/SupervisorList.jsx b/src/components/dashboard/roadPatrols/supervisor/SupervisorList.jsx
index 9478939..a5e15f4 100644
--- a/src/components/dashboard/roadPatrols/supervisor/SupervisorList.jsx
+++ b/src/components/dashboard/roadPatrols/supervisor/SupervisorList.jsx
@@ -1,15 +1,15 @@
"use client";
-import {useMemo} from "react";
-import {Box, Typography} from "@mui/material";
+import { useMemo } from "react";
+import { Box, Typography } from "@mui/material";
import DataTableWithAuth from "@/core/components/DataTableWithAuth";
import Toolbar from "./Toolbar";
-import {GET_SUPERVISOR_LIST} from "@/core/utils/routes";
+import { GET_ROAD_PATROL_SUPERVISOR_LIST } from "@/core/utils/routes";
import moment from "jalali-moment";
import RowActions from "./RowActions";
import useProvinces from "@/lib/hooks/useProvince";
-const SuperviserList = () => {
- const {provinces, errorProvinces, loadingProvinces} = useProvinces();
+const OperatorList = () => {
+ const { provinces, errorProvinces, loadingProvinces } = useProvinces();
const citiesListApi = "https://rms.rmto.ir/public/contents/edarate_shahri_by_province";
const columns = useMemo(
() => [
@@ -19,7 +19,7 @@ const SuperviserList = () => {
id: "id",
enableColumnFilter: true,
datatype: "text",
- filterFn: "equals",
+ filterMode: "equals",
columnFilterModeOptions: ["equals", "contains"],
},
{
@@ -28,14 +28,14 @@ const SuperviserList = () => {
id: "province",
enableColumnFilter: true,
datatype: "numeric",
- filterFn: "equals",
+ filterMode: "equals",
columnSelectOption: () => {
if (loadingProvinces) {
- return [{value: "", label: "در حال بارگذاری..."}];
+ return [{ value: "", label: "در حال بارگذاری..." }];
}
if (errorProvinces) {
- return [{value: "", label: "خطا در بارگذاری"}];
+ return [{ value: "", label: "خطا در بارگذاری" }];
}
return provinces.map((province) => ({
@@ -50,14 +50,14 @@ const SuperviserList = () => {
id: "office",
enableColumnFilter: true,
datatype: "numeric",
- filterFn: "equals",
+ filterMode: "equals",
dependencyId: "province",
columnSelectOption: (dependencyField) => {
if (dependencyField.value === "") {
- return [{value: "", label: "ابتدا استان را انتخاب کنید"}];
+ return [{ value: "", label: "ابتدا استان را انتخاب کنید" }];
}
- return [{value: dependencyField.value, label: dependencyField.value}];
+ return [{ value: dependencyField.value, label: dependencyField.value }];
},
},
{
@@ -66,7 +66,7 @@ const SuperviserList = () => {
id: "operator_name",
enableColumnFilter: true,
datatype: "text",
- filterFn: "equals",
+ filterMode: "equals",
columnFilterModeOptions: ["equals", "contains"],
},
{
@@ -75,7 +75,7 @@ const SuperviserList = () => {
id: "phone_number",
enableColumnFilter: true,
datatype: "text",
- filterFn: "equals",
+ filterMode: "equals",
columnFilterModeOptions: ["equals", "contains"],
},
{
@@ -84,7 +84,7 @@ const SuperviserList = () => {
id: "car_id",
enableColumnFilter: true,
datatype: "text",
- filterFn: "equals",
+ filterMode: "equals",
columnFilterModeOptions: ["equals", "contains"],
},
{
@@ -93,9 +93,9 @@ const SuperviserList = () => {
id: "start_date",
enableColumnFilter: true,
datatype: "date",
- filterFn: "equals",
+ filterMode: "equals",
columnFilterModeOptions: ["equals", "contains"],
- Cell: ({renderedCellValue}) => (
+ Cell: ({ renderedCellValue }) => (
{renderedCellValue ? moment(renderedCellValue).locale("fa").format("YYYY/MM/DD") : "-"}
@@ -107,9 +107,9 @@ const SuperviserList = () => {
id: "end_date",
enableColumnFilter: true,
datatype: "date",
- filterFn: "equals",
+ filterMode: "equals",
columnFilterModeOptions: ["equals", "contains"],
- Cell: ({renderedCellValue}) => (
+ Cell: ({ renderedCellValue }) => (
{renderedCellValue ? moment(renderedCellValue).locale("fa").format("YYYY/MM/DD") : "-"}
@@ -121,9 +121,9 @@ const SuperviserList = () => {
id: "register_date",
enableColumnFilter: true,
datatype: "date",
- filterFn: "equals",
+ filterMode: "equals",
columnFilterModeOptions: ["equals", "contains"],
- Cell: ({renderedCellValue}) => (
+ Cell: ({ renderedCellValue }) => (
{renderedCellValue ? moment(renderedCellValue).locale("fa").format("YYYY/MM/DD") : "-"}
@@ -135,12 +135,12 @@ const SuperviserList = () => {
return (
<>
-
+
{
>
);
};
-export default SuperviserList;
+export default OperatorList;
diff --git a/src/core/components/CarCode.jsx b/src/core/components/CarCode.jsx
index 503a826..32a4c20 100644
--- a/src/core/components/CarCode.jsx
+++ b/src/core/components/CarCode.jsx
@@ -1,28 +1,26 @@
-"use client";
-
-import {Autocomplete, CircularProgress, TextField} from "@mui/material";
-import {useEffect, useState} from "react";
+import { Autocomplete, CircularProgress, TextField } from "@mui/material";
+import { useEffect, useState } from "react";
import useRequest from "@/lib/hooks/useRequest";
-import {debounce} from "@mui/material/utils";
+import { debounce } from "@mui/material/utils";
+import { GET_CAR_LIST_SEARCH } from "@/core/utils/routes";
-const CarCode = ({carCode, setCarCode}) => {
- const [inputValue, setInputValue] = useState('');
+const CarCode = ({ carCode, setCarCode, inputValueDefault = "", error }) => {
+ const [inputValue, setInputValue] = useState(inputValueDefault);
const [options, setOptions] = useState([]);
const [loading, setLoading] = useState(false);
const requestServer = useRequest();
-
const fetchCarCodes = (inputValue, controller) => {
const debouncer = debounce((query) => {
- if (query.length < 2) {
+ if (!query || query?.length < 3) {
setOptions([]);
return;
}
setLoading(true);
- requestServer(`car-code/api?${query}`, "get", {
- requestOptions: {signal: controller.signal}
+ requestServer(`${GET_CAR_LIST_SEARCH}?machine_code=${query}`, "get", {
+ requestOptions: { signal: controller.signal },
})
.then((response) => {
- setOptions(response || []);
+ setOptions(response.data.data || []);
})
.catch(() => {
setOptions([]);
@@ -30,9 +28,9 @@ const CarCode = ({carCode, setCarCode}) => {
.finally(() => {
setLoading(false);
});
- }, 500)
- debouncer(inputValue)
- }
+ }, 500);
+ debouncer(inputValue);
+ };
useEffect(() => {
const controller = new AbortController();
@@ -45,34 +43,35 @@ const CarCode = ({carCode, setCarCode}) => {
value={carCode}
size="small"
onChange={(event, newValue) => {
- setCarCode(newValue);
+ setCarCode(newValue || null); // Set an empty string if no value is selected
}}
inputValue={inputValue}
onInputChange={(event, newInputValue) => {
- setInputValue(newInputValue);
+ setInputValue(newInputValue || ""); // Prevent inputValue from being null or undefined
}}
options={options}
- getOptionLabel={(option) =>
- typeof option === 'string' ? option : option.name || option.code
- }
+ getOptionLabel={(option) => (typeof option === "string" ? option : option.machine_code)}
+ isOptionEqualToValue={(option, value) => {
+ if (!value) return false; // Handle null/undefined values
+ if (value === "") return option.machine_code === ""; // Handle empty string case
+ return option.machine_code === (value?.machine_code || value);
+ }}
loading={loading}
loadingText="درحال جستجوی کد خودرو"
- noOptionsText={
- inputValue.length < 2
- ? "حداقل دو رقم از کد خودرو را وارد کنید"
- : "کد خودرویی یافت نشد"
- }
- sx={{width: 300}}
+ noOptionsText={inputValue?.length < 3 ? "حداقل سه رقم از کد خودرو را وارد کنید" : "کد خودرویی یافت نشد"}
+ fullWidth
renderInput={(params) => (
- {loading ? : null}
+ {loading ? : null}
{params.InputProps.endAdornment}
>
),
@@ -82,5 +81,4 @@ const CarCode = ({carCode, setCarCode}) => {
/>
);
};
-
-export default CarCode;
\ No newline at end of file
+export default CarCode;
diff --git a/src/core/components/DataTable/Main.js b/src/core/components/DataTable/Main.js
index cdb3671..c6d82d5 100644
--- a/src/core/components/DataTable/Main.js
+++ b/src/core/components/DataTable/Main.js
@@ -39,7 +39,13 @@ const DataTable_Main = (props) => {
}
return value === "" || value === null || value === undefined;
};
- const filteredFilterData = Object.values(filterData).filter((filter) => !isValueEmpty(filter.value));
+
+ const filteredFilterData = Object.values(filterData)
+ .filter((filter) => !isValueEmpty(filter.value))
+ .map(({ filterMode, ...rest }) => ({
+ ...rest,
+ fn: filterMode,
+ }));
const params = new URLSearchParams();
params.set("start", `${pagination.pageIndex * pagination.pageSize}`);
diff --git a/src/core/components/DataTable/filter/FilterBody.jsx b/src/core/components/DataTable/filter/FilterBody.jsx
index c981b93..d0eb7cf 100644
--- a/src/core/components/DataTable/filter/FilterBody.jsx
+++ b/src/core/components/DataTable/filter/FilterBody.jsx
@@ -1,13 +1,14 @@
"use client";
-import { Box, Button, Drawer, styled } from "@mui/material";
-import { Form, Formik, getIn } from "formik";
-import useTableSetting from "@/lib/hooks/useTableSetting";
-import { useEffect, useState } from "react";
-import FilterHeader from "@/core/components/DataTable/filter/FilterHeader";
-import * as Yup from "yup";
import FilterBodyField from "@/core/components/DataTable/filter/FilterBodyField";
+import FilterHeader from "@/core/components/DataTable/filter/FilterHeader";
import useDataTable from "@/lib/hooks/useDataTable";
+import { yupResolver } from "@hookform/resolvers/yup";
+import { Box, Button, Drawer, styled } from "@mui/material";
+import { useEffect, useState } from "react";
+import { Controller, useForm, useWatch } from "react-hook-form";
+import * as Yup from "yup";
+import FilterBodyController from "./FilterBodyController";
const ScrollBox = styled(Box)({
flexGrow: 1,
@@ -26,21 +27,15 @@ const ScrollBox = styled(Box)({
},
});
-function FilterBody({ columns, drawerState, setDrawerState, user_id, page_name, table_name }) {
- const { settingStore } = useTableSetting();
- const [initialValues, setInitialValues] = useState({});
- const [validationSchema, setValidationSchema] = useState({});
+function FilterBody({ columns, drawerState, setDrawerState }) {
const { filterData, setFilterData } = useDataTable();
+ const [validationSchema, setValidationSchema] = useState(Yup.object({}));
useEffect(() => {
- setInitialValues(filterData);
- }, [columns, settingStore, user_id, page_name, table_name, filterData]);
-
- useEffect(() => {
- const generatedSchema = Yup.object().shape(
- Object.keys(initialValues).reduce((acc, key) => {
- const initialValue = initialValues[key];
- if (initialValue.filterFn === "between") {
+ const schema = Yup.object(
+ Object.keys(filterData).reduce((acc, key) => {
+ const initialValue = filterData[key];
+ if (initialValue.filterMode === "between") {
acc[key] = Yup.object().shape({
value: Yup.array()
.of(Yup.string().nullable())
@@ -68,13 +63,23 @@ function FilterBody({ columns, drawerState, setDrawerState, user_id, page_name,
}, {})
);
- setValidationSchema(generatedSchema);
- }, [initialValues]);
+ setValidationSchema(schema);
+ }, [filterData]);
- const handleSubmit = (values, { setSubmitting }) => {
+ const {
+ handleSubmit,
+ control,
+ reset,
+ formState: { errors, isDirty, isValid },
+ } = useForm({
+ defaultValues: filterData,
+ resolver: yupResolver(validationSchema),
+ mode: "onChange",
+ });
+
+ const onSubmit = (data) => {
setDrawerState(false);
- setFilterData(values);
- setSubmitting(false);
+ setFilterData(data);
};
return (
@@ -84,72 +89,49 @@ function FilterBody({ columns, drawerState, setDrawerState, user_id, page_name,
sx={{ overflowY: "hidden", display: "flex", flexDirection: "column", height: "100%", zIndex: "1300" }}
>
- {Object.keys(initialValues).length > 0 && (
+ {Object.keys(filterData).length > 0 && (
-
- {({
- values,
- handleChange,
- handleBlur,
- setFieldValue,
- errors,
- touched,
- isSubmitting,
- isValid,
- dirty,
- resetForm,
- }) => (
-
- )}
-
+
)}
diff --git a/src/core/components/DataTable/filter/FilterBodyController.jsx b/src/core/components/DataTable/filter/FilterBodyController.jsx
new file mode 100644
index 0000000..df3ba62
--- /dev/null
+++ b/src/core/components/DataTable/filter/FilterBodyController.jsx
@@ -0,0 +1,25 @@
+import { Controller, useWatch } from "react-hook-form";
+import FilterBodyField from "./FilterBodyField";
+
+const FilterBodyController = ({ column, control, reset, errors }) => {
+ const dependencyField = useWatch({ control, name: column?.dependencyId });
+
+ return (
+ (
+ reset()}
+ errors={errors}
+ />
+ )}
+ />
+ );
+};
+export default FilterBodyController;
diff --git a/src/core/components/DataTable/filter/FilterBodyField.jsx b/src/core/components/DataTable/filter/FilterBodyField.jsx
index 2755326..4783f13 100644
--- a/src/core/components/DataTable/filter/FilterBodyField.jsx
+++ b/src/core/components/DataTable/filter/FilterBodyField.jsx
@@ -1,11 +1,11 @@
-import { useState } from "react";
import FilterOptionList from "@/core/components/DataTable/filter/FilterOptionList";
+import CustomDatePicker from "@/core/components/DataTable/filter/fieldsType/CustomDate/CustomDatePicker";
+import CustomDatePickerRange from "@/core/components/DataTable/filter/fieldsType/CustomDate/CustomDatePickerRange";
import CustomSelect from "@/core/components/DataTable/filter/fieldsType/CustomSelect";
+import CustomSelectMultiple from "@/core/components/DataTable/filter/fieldsType/CustomSelectMultiple";
import CustomTextField from "@/core/components/DataTable/filter/fieldsType/CustomTextField";
import CustomTextFieldRange from "@/core/components/DataTable/filter/fieldsType/CustomTextFieldRange";
-import CustomDatePickerRange from "@/core/components/DataTable/filter/fieldsType/CustomDate/CustomDatePickerRange";
-import CustomDatePicker from "@/core/components/DataTable/filter/fieldsType/CustomDate/CustomDatePicker";
-import CustomSelectMultiple from "@/core/components/DataTable/filter/fieldsType/CustomSelectMultiple";
+import { useState } from "react";
const columnFilterModeOptionFa = {
equals: "برابر",
@@ -24,82 +24,71 @@ function FilterBodyField({
handleBlur,
dependencyFieldValue,
setFieldValue,
- errors,
- touched,
resetForm,
+ errors,
}) {
const [anchorEl, setAnchorEl] = useState(null);
- const defaultFilterTranslation = columnFilterModeOptionFa[filterParameters.filterFn] || filterParameters.filterFn;
+ const defaultFilterTranslation =
+ columnFilterModeOptionFa[filterParameters.filterMode] || filterParameters.filterMode;
const handleOpenFilterBox = (event) => {
setAnchorEl(event.currentTarget);
};
+
+ const renderField = () => {
+ const commonProps = {
+ column,
+ filterParameters,
+ defaultFilterTranslation,
+ handleOpenFilterBox,
+ dependencyFieldValue,
+ setFieldValue,
+ handleChange,
+ handleBlur,
+ errors,
+ };
+
+ switch (filterParameters.datatype) {
+ case "numeric":
+ if (filterParameters.filterMode === "between") {
+ return ;
+ }
+ if (filterParameters.filterMode === "equals") {
+ return ;
+ }
+ break;
+
+ case "date":
+ if (filterParameters.filterMode === "equals") {
+ return ;
+ }
+ if (filterParameters.filterMode === "between") {
+ return ;
+ }
+ break;
+
+ case "array":
+ if (filterParameters.filterMode === "equals") {
+ return ;
+ }
+ break;
+
+ default:
+ return ;
+ }
+ };
+
return (
<>
- {filterParameters.datatype === "numeric" && filterParameters.filterFn === "between" ? (
-
- ) : filterParameters.datatype === "numeric" && filterParameters.filterFn === "equals" ? (
-
- ) : filterParameters.datatype === "date" && filterParameters.filterFn === "equals" ? (
-
- ) : filterParameters.datatype === "date" && filterParameters.filterFn === "between" ? (
-
- ) : filterParameters.datatype === "array" && filterParameters.filterFn === "equals" ? (
-
- ) : (
- setFieldValue(`${column.id}.value`, e.target.value)}
- />
- )}
+ {renderField()}
{Array.isArray(column.columnFilterModeOptions) && (
{
- setFieldValue(`${column.id}.value`, filterOption[index] === "between" ? ["", ""] : "");
- setFieldValue(`${column.id}.filterFn`, filterOption[index]);
+ handleChange({
+ ...filterParameters,
+ value: filterOption[index] === "between" ? ["", ""] : "",
+ filterMode: filterOption[index],
+ });
setSelectedFilter(filterOption[index]);
setAnchorEl(null);
};
diff --git a/src/core/components/DataTable/filter/fieldsType/CustomDate/CustomDatePicker.jsx b/src/core/components/DataTable/filter/fieldsType/CustomDate/CustomDatePicker.jsx
index fe5d2e0..e207cfb 100644
--- a/src/core/components/DataTable/filter/fieldsType/CustomDate/CustomDatePicker.jsx
+++ b/src/core/components/DataTable/filter/fieldsType/CustomDate/CustomDatePicker.jsx
@@ -2,12 +2,14 @@ import React from "react";
import MuiDatePicker from "@/core/components/DataTable/filter/fieldsType/CustomDate/MuiDatePicker";
import { Typography } from "@mui/material";
-function CustomDatePicker({ column, filterParameters, defaultFilterTranslation, setFieldValue }) {
+function CustomDatePicker({ column, filterParameters, defaultFilterTranslation, handleChange }) {
return (
{
+ handleChange({ ...filterParameters, value: formattedDate });
+ }}
placeholder={column.header}
helperText={
diff --git a/src/core/components/DataTable/filter/fieldsType/CustomDate/CustomDatePickerRange.jsx b/src/core/components/DataTable/filter/fieldsType/CustomDate/CustomDatePickerRange.jsx
index 116af77..4839f77 100644
--- a/src/core/components/DataTable/filter/fieldsType/CustomDate/CustomDatePickerRange.jsx
+++ b/src/core/components/DataTable/filter/fieldsType/CustomDate/CustomDatePickerRange.jsx
@@ -4,13 +4,15 @@ import React from "react";
import { Box, Typography } from "@mui/material";
import MuiDatePicker from "@/core/components/DataTable/filter/fieldsType/CustomDate/MuiDatePicker";
-function CustomDatePickerRange({ column, filterParameters, defaultFilterTranslation, setFieldValue, errors, touched }) {
+function CustomDatePickerRange({ column, filterParameters, defaultFilterTranslation, handleChange, errors }) {
return (
{
+ handleChange({ ...filterParameters, value: [formattedDate, filterParameters.value[1]] });
+ }}
maxDate={filterParameters.value[1]}
placeholder={`از تاریخ`}
helperText={
@@ -22,10 +24,12 @@ function CustomDatePickerRange({ column, filterParameters, defaultFilterTranslat
{
+ handleChange({ ...filterParameters, value: [filterParameters.value[0], formattedDate] });
+ }}
minDate={filterParameters.value[0]}
placeholder={`تا تاریخ`}
- helperText={errors?.[`${column.id}`]?.value ? errors?.[`${column.id}`]?.value : null}
+ helperText={errors?.[`${column.id}`]?.value ? errors?.[`${column.id}`]?.value.message : null}
error={Boolean(errors?.[`${column.id}`]?.value)}
/>
diff --git a/src/core/components/DataTable/filter/fieldsType/CustomDate/MuiDatePicker.jsx b/src/core/components/DataTable/filter/fieldsType/CustomDate/MuiDatePicker.jsx
index 22eb5f9..9cfa8c5 100644
--- a/src/core/components/DataTable/filter/fieldsType/CustomDate/MuiDatePicker.jsx
+++ b/src/core/components/DataTable/filter/fieldsType/CustomDate/MuiDatePicker.jsx
@@ -18,6 +18,7 @@ function MuiDatePicker({ value, setFieldValue, name, minDate, maxDate, helperTex
sx={{ width: "100%" }}
id={name}
name={name}
+ closeOnSelect={true}
aria-describedby="component-helper-text"
onChange={(value) => {
const date = new Date(value);
diff --git a/src/core/components/DataTable/filter/fieldsType/CustomSelect.jsx b/src/core/components/DataTable/filter/fieldsType/CustomSelect.jsx
index 6f471ce..274a58b 100644
--- a/src/core/components/DataTable/filter/fieldsType/CustomSelect.jsx
+++ b/src/core/components/DataTable/filter/fieldsType/CustomSelect.jsx
@@ -1,9 +1,22 @@
"use client";
import { FormControl, FormHelperText, InputLabel, MenuItem, OutlinedInput, Select, Typography } from "@mui/material";
+import { useEffect, useMemo } from "react";
-function CustomSelect({ column, dependencyFieldValue, filterParameters, defaultFilterTranslation, setFieldValue }) {
- const columnSelectOption = column.columnSelectOption(dependencyFieldValue);
+function CustomSelect({ column, dependencyFieldValue, filterParameters, defaultFilterTranslation, handleChange }) {
+ const columnSelectOption = useMemo(() => {
+ return column?.columnSelectOption?.(dependencyFieldValue) || [];
+ }, [dependencyFieldValue]);
+
+ useEffect(() => {
+ const isValidValue = columnSelectOption.some((option) => option.value === filterParameters.value);
+ if (!isValidValue) {
+ handleChange({ ...filterParameters, value: "" });
+ }
+ }, [columnSelectOption, filterParameters.value, handleChange]);
+
+ const isValidValue = columnSelectOption.some((option) => option.value === filterParameters.value);
+ const selectedValue = isValidValue ? filterParameters.value : "";
return (
@@ -12,11 +25,9 @@ function CustomSelect({ column, dependencyFieldValue, filterParameters, defaultF
labelId={`label${column.id}`}
id={column.id}
name={`${column.id}.value`}
- value={filterParameters.value}
+ value={selectedValue}
input={}
- onChange={(e) => {
- setFieldValue(`${column.id}.value`, e.target.value);
- }}
+ onChange={(e) => handleChange({ ...filterParameters, value: e.target.value })}
>
{columnSelectOption.map((option) => (
diff --git a/src/core/components/DataTable/filter/fieldsType/CustomSelectMultiple.jsx b/src/core/components/DataTable/filter/fieldsType/CustomSelectMultiple.jsx
index 130f98f..22ff8aa 100644
--- a/src/core/components/DataTable/filter/fieldsType/CustomSelectMultiple.jsx
+++ b/src/core/components/DataTable/filter/fieldsType/CustomSelectMultiple.jsx
@@ -12,7 +12,7 @@ import {
Typography,
} from "@mui/material";
-function CustomSelectMultiple({ column, filterParameters, defaultFilterTranslation, setFieldValue }) {
+function CustomSelectMultiple({ column, filterParameters, defaultFilterTranslation, handleChange }) {
const columnSelectOption = column.columnSelectOption;
const getLabelForValue = (value) => {
@@ -30,9 +30,7 @@ function CustomSelectMultiple({ column, filterParameters, defaultFilterTranslati
id={column.id}
value={filterParameters.value}
multiple
- onChange={(e) => {
- setFieldValue(`${column.id}.value`, e.target.value);
- }}
+ onChange={(e) => handleChange({ ...filterParameters, value: e.target.value })}
renderValue={(selected) => (
{selected.map((value) => (
diff --git a/src/core/components/DataTable/filter/fieldsType/CustomTextField.jsx b/src/core/components/DataTable/filter/fieldsType/CustomTextField.jsx
index 05b44ce..619237a 100644
--- a/src/core/components/DataTable/filter/fieldsType/CustomTextField.jsx
+++ b/src/core/components/DataTable/filter/fieldsType/CustomTextField.jsx
@@ -3,11 +3,11 @@ import FilterListIcon from "@mui/icons-material/FilterList";
function CustomTextField({
column,
- filterParameters,
defaultFilterTranslation,
+ filterParameters,
handleOpenFilterBox,
- handleChange,
handleBlur,
+ handleChange,
}) {
return (
handleChange({ ...filterParameters, value: e.target.value })}
onBlur={handleBlur}
fullWidth
helperText={
diff --git a/src/core/components/DataTable/filter/fieldsType/CustomTextFieldRange.jsx b/src/core/components/DataTable/filter/fieldsType/CustomTextFieldRange.jsx
index fea4169..20afcbd 100644
--- a/src/core/components/DataTable/filter/fieldsType/CustomTextFieldRange.jsx
+++ b/src/core/components/DataTable/filter/fieldsType/CustomTextFieldRange.jsx
@@ -3,20 +3,21 @@ import FilterListIcon from "@mui/icons-material/FilterList";
function CustomTextFieldRange({
column,
- filterParameters,
defaultFilterTranslation,
handleOpenFilterBox,
handleChange,
+ filterParameters,
handleBlur,
errors,
- touched,
}) {
return (
+ handleChange({ ...filterParameters, value: [e.target.value, filterParameters.value[1]] })
+ }
onBlur={handleBlur}
label={از {column.header}}
value={filterParameters.value[0]}
@@ -34,10 +35,12 @@ function CustomTextFieldRange({
+ handleChange({ ...filterParameters, value: [filterParameters.value[0], e.target.value] })
+ }
onBlur={handleBlur}
- error={touched?.[`${column.id}`]?.value && Boolean(errors?.[`${column.id}`]?.value)}
- helperText={touched?.[`${column.id}`]?.value ? errors?.[`${column.id}`]?.value : null}
+ error={Boolean(errors?.[`${column.id}`]?.value)}
+ helperText={errors?.[`${column.id}`]?.value ? errors?.[`${column.id}`]?.value.message : null}
label={تا {column.header}}
value={filterParameters.value[1]}
fullWidth
diff --git a/src/core/components/DataTable/reset/ResetStorage.jsx b/src/core/components/DataTable/reset/ResetStorage.jsx
index 9f6abd5..8e83d3f 100644
--- a/src/core/components/DataTable/reset/ResetStorage.jsx
+++ b/src/core/components/DataTable/reset/ResetStorage.jsx
@@ -1,11 +1,12 @@
"use client";
-
-import { IconButton, Tooltip } from "@mui/material";
+import { IconButton, Tooltip, Badge } from "@mui/material";
import AutorenewIcon from "@mui/icons-material/Autorenew";
import useTableSetting from "@/lib/hooks/useTableSetting";
+import useDataTable from "@/lib/hooks/useDataTable";
function ResetStorage({ user_id, page_name, table_name }) {
const { resetAction } = useTableSetting();
+ const { isDirty } = useDataTable();
const reset = () => {
resetAction(user_id, page_name, table_name);
};
@@ -13,7 +14,20 @@ function ResetStorage({ user_id, page_name, table_name }) {
return (
-
+ {isDirty ? (
+
+
+
+ ) : (
+
+ )}
);
diff --git a/src/core/components/LoadingHardPage.jsx b/src/core/components/LoadingHardPage.jsx
index a4da80d..89e5f17 100644
--- a/src/core/components/LoadingHardPage.jsx
+++ b/src/core/components/LoadingHardPage.jsx
@@ -1,4 +1,4 @@
-'use client'
+"use client";
import { Backdrop, Box, Stack, styled, Typography, useTheme } from "@mui/material";
import SvgLoading from "@/core/components/svgs/SvgLoading";
import SvgError from "@/core/components/svgs/SvgError";
diff --git a/src/core/components/MapInfoOneMarker.jsx b/src/core/components/MapInfoOneMarker.jsx
new file mode 100644
index 0000000..7132d5d
--- /dev/null
+++ b/src/core/components/MapInfoOneMarker.jsx
@@ -0,0 +1,137 @@
+import React, { useEffect, useRef, useState } from "react";
+import { Marker, useMapEvents } from "react-leaflet";
+import "leaflet/dist/leaflet.css";
+import L from "leaflet";
+import { Box, Button, Typography } from "@mui/material";
+import dynamic from "next/dynamic";
+import MapLoading from "@/core/components/MapLayer/Loading";
+import HereIcon from "@/assets/images/examine_marker_active.png";
+
+const MapLayer = dynamic(() => import("@/core/components/MapLayer"), {
+ loading: () => ,
+ ssr: false,
+});
+
+const createCustomIcon = (size, iconUrl, labelText) => {
+ if (labelText) {
+ return L.divIcon({
+ html: `
+
+

+
+ ${labelText}
+
+
+ `,
+ iconSize: size,
+ iconAnchor: [size[0] / 2, size[1]],
+ popupAnchor: [0, -size[1]],
+ });
+ }
+
+ return L.icon({
+ iconUrl: iconUrl,
+ iconSize: size,
+ iconAnchor: [size[0] / 2, size[1]],
+ popupAnchor: [0, -size[1]],
+ });
+};
+
+const MapInteraction = ({ setValue, startLat, startLng }) => {
+ const [isMarkerLocked, setIsMarkerLocked] = useState(!!(startLat && startLng)); // وضعیت قفل مارکر
+ const [markerPosition, setMarkerPosition] = useState(
+ startLat && startLng ? { lat: startLat, lng: startLng } : null
+ );
+ const markerRef = useRef();
+
+ const map = useMapEvents({
+ move(e) {
+ if (!isMarkerLocked && markerRef.current) {
+ markerRef.current.setLatLng(e.target.getCenter());
+ }
+ },
+ zoom(e) {
+ if (!isMarkerLocked && markerRef.current) {
+ markerRef.current.setLatLng(e.target.getCenter());
+ }
+ },
+ });
+
+ useEffect(() => {
+ if (startLat && startLng) {
+ const position = { lat: startLat, lng: startLng };
+ map.setView(position, 15);
+ }
+ }, [startLat, startLng, map]);
+
+ const handleMarkerClick = () => {
+ if (!isMarkerLocked) {
+ const center = map.getCenter();
+ setValue("start_point", { lat: center.lat.toString(), lng: center.lng.toString() });
+ setMarkerPosition({ lat: center.lat, lng: center.lng }); // بهروزرسانی موقعیت مارکر
+ setIsMarkerLocked(true);
+ }
+ };
+
+ const handleUnlockMarker = () => {
+ setValue("start_point", null); // حذف مقدار قبلی
+ setIsMarkerLocked(false); // باز کردن قفل مارکر
+ setMarkerPosition(null); // تنظیم مارکر به مرکز نقشه
+ };
+
+ return (
+ <>
+
+ {isMarkerLocked && (
+
+
+
+ )}
+ >
+ );
+};
+
+const MapInfoOneMarker = ({ setValue, errors, StartPoint = null }) => {
+ return (
+
+
+
+
+
+
+
+ {errors.start_point && (
+
+ {errors.start_point.message}
+
+ )}
+
+
+ );
+};
+
+export default MapInfoOneMarker;
diff --git a/src/core/components/MapInfoTwoMarker.jsx b/src/core/components/MapInfoTwoMarker.jsx
new file mode 100644
index 0000000..f4bd24b
--- /dev/null
+++ b/src/core/components/MapInfoTwoMarker.jsx
@@ -0,0 +1,191 @@
+import React, { useEffect, useRef, useState } from "react";
+import { Marker, useMapEvents } from "react-leaflet";
+import "leaflet/dist/leaflet.css";
+import L from "leaflet";
+import { Box, Button, Typography } from "@mui/material";
+import dynamic from "next/dynamic";
+import MapLoading from "@/core/components/MapLayer/Loading";
+import EndIcon from "@/assets/images/examine_marker_active.png";
+import StartIcon from "@/assets/images/examine_marker.png";
+
+const MapLayer = dynamic(() => import("@/core/components/MapLayer"), {
+ loading: () => ,
+ ssr: false,
+});
+const createCustomIcon = (size, iconUrl, labelText) => {
+ if (labelText) {
+ return L.divIcon({
+ html: `
+
+

+
+ ${labelText}
+
+
+ `,
+ iconSize: size,
+ iconAnchor: [size[0] / 2, size[1]],
+ popupAnchor: [0, -size[1]],
+ });
+ }
+
+ return L.icon({
+ iconUrl: iconUrl,
+ iconSize: size,
+ iconAnchor: [size[0] / 2, size[1]],
+ popupAnchor: [0, -size[1]],
+ });
+};
+
+const MapInteraction = ({ setValue, startLat, startLng, endLat, endLng }) => {
+ const [isStartLocked, setIsStartLocked] = useState(!!(startLat && startLng)); // وضعیت قفل نقطه آغاز
+ const [isEndLocked, setIsEndLocked] = useState(!!(endLat && endLng)); // وضعیت قفل نقطه پایان
+ const [startPosition, setStartPosition] = useState(startLat && startLng ? { lat: startLat, lng: startLng } : null);
+ const [endPosition, setEndPosition] = useState(endLat && endLng ? { lat: endLat, lng: endLng } : null);
+ const startRef = useRef();
+ const endRef = useRef();
+
+ const map = useMapEvents({
+ move(e) {
+ if (!isStartLocked && startRef.current) {
+ startRef.current.setLatLng(e.target.getCenter());
+ } else if (isStartLocked && !isEndLocked && endRef.current) {
+ endRef.current.setLatLng(e.target.getCenter());
+ }
+ },
+ zoom(e) {
+ if (!isStartLocked && startRef.current) {
+ startRef.current.setLatLng(e.target.getCenter());
+ } else if (isStartLocked && !isEndLocked && endRef.current) {
+ endRef.current.setLatLng(e.target.getCenter());
+ }
+ },
+ });
+
+ useEffect(() => {
+ if (startLat && endLng) {
+ map.fitBounds(
+ [
+ { lat: startLat, lng: startLng },
+ { lat: endLat, lng: endLng },
+ ],
+ { paddingTopLeft: [16, 16], paddingBottomRight: [16, 130] }
+ );
+ }
+ }, [startLat, map, endLng]);
+
+ const handleUnlockStart = () => {
+ setIsStartLocked(false);
+ setStartPosition(null);
+ setValue("start_point", null);
+ };
+
+ const handleUnlockEnd = () => {
+ setIsEndLocked(false);
+ setIsStartLocked(false);
+ setEndPosition(null);
+ setStartPosition(null);
+ setValue("end_point", "");
+ setValue("start_point", null);
+ };
+
+ return (
+ <>
+ {
+ if (!isStartLocked) {
+ const center = map.getCenter();
+ setValue("start_point", { lat: center.lat.toString(), lng: center.lng.toString() });
+ setStartPosition({ lat: center.lat, lng: center.lng });
+ setIsStartLocked(true);
+ }
+ },
+ }}
+ />
+ {isStartLocked && !isEndLocked && (
+
+
+
+ )}
+ {isStartLocked && (
+ {
+ if (!isEndLocked) {
+ const center = map.getCenter();
+ setValue("end_point", { lat: center.lat.toString(), lng: center.lng.toString() });
+ setEndPosition({ lat: center.lat, lng: center.lng });
+ setIsEndLocked(true);
+ }
+ },
+ }}
+ />
+ )}
+ {isEndLocked && (
+
+
+
+ )}
+ >
+ );
+};
+
+const MapInfoTwoMarker = ({ setValue, errors, StartPoint = null, EndPoint = null }) => {
+ return (
+
+
+
+
+
+
+
+ {errors.start_point && (
+
+ {errors.start_point.message}
+
+ )}
+ {errors.end_point && (
+
+ {errors.end_point.message}
+
+ )}
+
+
+ );
+};
+export default MapInfoTwoMarker;
diff --git a/src/core/components/MuiDatePicker.jsx b/src/core/components/MuiDatePicker.jsx
index 35bfe01..78ffe58 100644
--- a/src/core/components/MuiDatePicker.jsx
+++ b/src/core/components/MuiDatePicker.jsx
@@ -1,53 +1,70 @@
-import {LocalizationProvider, MobileDateTimePicker} from "@mui/x-date-pickers";
-import {AdapterDateFnsJalali} from "@mui/x-date-pickers/AdapterDateFnsJalali";
-import moment from "jalali-moment";
-import {faIR} from "@mui/x-date-pickers/locales";
-import {FormControl, IconButton, InputAdornment, Stack} from "@mui/material";
+import React from "react";
+import { LocalizationProvider, MobileDatePicker } from "@mui/x-date-pickers";
+import { AdapterDateFnsJalali } from "@mui/x-date-pickers/AdapterDateFnsJalali";
+import { faIR } from "@mui/x-date-pickers/locales";
+import { Box, FormHelperText, IconButton, InputAdornment } from "@mui/material";
import ClearIcon from "@mui/icons-material/Clear";
+import moment from "jalali-moment";
-export default function PickerWithButtonField({formik, name, value, error, touched, disabled}) {
+function MuiDatePicker({ value, setFieldValue, name, minDate, maxDate, helperText, placeholder, error }) {
return (
-
-
-
- {
- const formattedDate = moment(newValue).locale("en").format("YYYY-MM-DD HH:mm");
- formik.setFieldValue(name, formattedDate);
- }}
- slotProps={{
- textField: {
- placeholder:
- name === "start_date" ? "تاریخ شروع را وارد کنید" : "تاریخ اتمام را وارد کنید",
- variant: "outlined",
- disabled,
- fullWidth: true,
- error: !!error,
- helperText: touched && error,
- size: "small",
- InputProps: {
- endAdornment: (
-
- formik.setFieldValue(name, null)}
- >
-
-
-
- ),
- },
+
+
+ {
+ const date = new Date(value);
+ const formattedDate = moment(date).locale("en").format("YYYY-MM-DD");
+ setFieldValue(name, formattedDate);
+ }}
+ minDate={minDate ? new Date(minDate) : null}
+ maxDate={maxDate ? new Date(maxDate) : null}
+ slotProps={{
+ textField: {
+ error: error,
+ size: "small",
+ placeholder: placeholder,
+ InputProps: {
+ endAdornment: (
+
+ {
+ event.stopPropagation();
+ setFieldValue(name, "");
+ }}
+ sx={{
+ color: "#bfbfbf",
+ "&:hover": {
+ backgroundColor: "rgba(189, 189, 189, 0.1)",
+ color: "#363434",
+ },
+ }}
+ >
+
+
+
+ ),
},
- }}
- />
-
-
-
+ },
+ }}
+ />
+
+ {helperText ? helperText : ""}
+
+
+
);
}
+
+export default MuiDatePicker;
diff --git a/src/core/components/MuiTimePicker.jsx b/src/core/components/MuiTimePicker.jsx
new file mode 100644
index 0000000..35532fd
--- /dev/null
+++ b/src/core/components/MuiTimePicker.jsx
@@ -0,0 +1,75 @@
+import React from "react";
+import { LocalizationProvider, MobileTimePicker } from "@mui/x-date-pickers";
+import { AdapterDateFnsJalali } from "@mui/x-date-pickers/AdapterDateFnsJalali";
+import { faIR } from "@mui/x-date-pickers/locales";
+import { Box, FormHelperText, IconButton, InputAdornment } from "@mui/material";
+import ClearIcon from "@mui/icons-material/Clear";
+import { parseISO, format } from "date-fns";
+
+function MuiTimePicker({ value, setFieldValue, name, minTime, maxTime, helperText, placeholder, error }) {
+ // Ensure value, minTime, maxTime are Date objects
+ const parsedValue = value ? (typeof value === "string" ? parseISO(value) : value) : null;
+ const parsedMinTime = minTime ? (typeof minTime === "string" ? parseISO(minTime) : minTime) : null;
+ const parsedMaxTime = maxTime && (typeof maxTime === "string" ? parseISO(maxTime) : null);
+
+ return (
+
+
+ {
+ const formattedTime = newValue ? format(new Date(newValue), "HH:mm") : "";
+ setFieldValue(name, formattedTime);
+ }}
+ minTime={parsedMinTime}
+ maxTime={parsedMaxTime}
+ slotProps={{
+ textField: {
+ placeholder: placeholder,
+ size: "small",
+ error: error,
+ InputProps: {
+ endAdornment: (
+
+ {
+ event.stopPropagation();
+ setFieldValue(name, "");
+ }}
+ sx={{
+ color: "#bfbfbf",
+ "&:hover": {
+ backgroundColor: "rgba(189, 189, 189, 0.1)",
+ color: "#363434",
+ },
+ }}
+ >
+
+
+
+ ),
+ },
+ },
+ }}
+ />
+
+ {helperText ? helperText : ""}
+
+
+
+ );
+}
+
+export default MuiTimePicker;
diff --git a/src/core/components/PageLoading.jsx b/src/core/components/PageLoading.jsx
index 757d609..c5b2159 100644
--- a/src/core/components/PageLoading.jsx
+++ b/src/core/components/PageLoading.jsx
@@ -1,10 +1,12 @@
-'use client'
-import { Paper } from "@mui/material"
-import LoadingHardPage from "./LoadingHardPage"
+"use client";
+import { Paper } from "@mui/material";
+import LoadingHardPage from "./LoadingHardPage";
const PageLoading = () => {
- return
-
-
-}
-export default PageLoading
\ No newline at end of file
+ return (
+
+
+
+ );
+};
+export default PageLoading;
diff --git a/src/core/components/RahdarCode.jsx b/src/core/components/RahdarCode.jsx
new file mode 100644
index 0000000..9dfeeb7
--- /dev/null
+++ b/src/core/components/RahdarCode.jsx
@@ -0,0 +1,90 @@
+"use client";
+import { Autocomplete, CircularProgress, TextField } from "@mui/material";
+import { useEffect, useState } from "react";
+import useRequest from "@/lib/hooks/useRequest";
+import { debounce } from "@mui/material/utils";
+import { GET_RAHDARANS_LIST_SEARCH } from "@/core/utils/routes";
+
+const RahdarCode = ({ rahdarsCode, setRahdarsCode, error }) => {
+ const [inputValue, setInputValue] = useState(""); // مدیریت مقدار تایپشده
+ const [options, setOptions] = useState([]);
+ const [loading, setLoading] = useState(false);
+ const requestServer = useRequest();
+
+ const fetchRahdarCodes = (inputValue, controller) => {
+ const debouncer = debounce((query) => {
+ if ((query || "").length < 3) {
+ setOptions([]);
+ return;
+ }
+ setLoading(true);
+ requestServer(`${GET_RAHDARANS_LIST_SEARCH}?code=${query}`, "get", {
+ requestOptions: { signal: controller.signal },
+ })
+ .then((response) => {
+ setOptions(response.data.data || []);
+ })
+ .catch(() => {
+ setOptions([]);
+ })
+ .finally(() => {
+ setLoading(false);
+ });
+ }, 500);
+ debouncer(inputValue);
+ };
+
+ useEffect(() => {
+ const controller = new AbortController();
+ fetchRahdarCodes(inputValue, controller);
+ return () => controller.abort();
+ }, [inputValue]);
+
+ return (
+ {
+ setRahdarsCode(newValue || []); // بهروزرسانی موارد انتخابشده
+ }}
+ inputValue={inputValue} // مقدار تایپشده
+ onInputChange={(event, newInputValue) => {
+ setInputValue(newInputValue || ""); // بهروزرسانی مقدار تایپشده
+ }}
+ options={options} // گزینههای موجود
+ getOptionLabel={(option) => (typeof option === "string" ? option : `${option.code} - ${option.name}`)}
+ isOptionEqualToValue={(option, value) => {
+ if (!value) return false;
+ if (value === "") return option.code === "";
+ return option.code === (value?.code || value);
+ }}
+ loading={loading}
+ loadingText="درحال جستجوی کد راهدار"
+ noOptionsText={
+ (inputValue || "").length < 3 ? "حداقل سه رقم از کد راهدار را وارد کنید" : "کد راهداری یافت نشد"
+ }
+ fullWidth
+ renderInput={(params) => (
+
+ {loading ? : null}
+ {params.InputProps.endAdornment}
+ >
+ ),
+ }}
+ />
+ )}
+ />
+ );
+};
+
+export default RahdarCode;
diff --git a/src/core/components/ShowLocationMarker.jsx b/src/core/components/ShowLocationMarker.jsx
new file mode 100644
index 0000000..e30a4e4
--- /dev/null
+++ b/src/core/components/ShowLocationMarker.jsx
@@ -0,0 +1,181 @@
+"use client";
+
+import { Marker, useMap } from "react-leaflet";
+import { useEffect } from "react";
+import L from "leaflet";
+import AzmayeshActiveIcon from "@/assets/images/examine_marker_active.png";
+import { Box, FormControl, InputAdornment, InputLabel, OutlinedInput, Stack, useMediaQuery } from "@mui/material";
+import VerifiedIcon from "@mui/icons-material/Verified";
+import { useTheme } from "@emotion/react";
+
+const ShowLocationMarker = ({ start_lat, start_lng, end_lat, end_lng }) => {
+ const map = useMap();
+ const theme = useTheme();
+ const isMobile = useMediaQuery(theme.breakpoints.down("sm"));
+
+ const defaultIconSize = [35, 35];
+
+ const createCustomIcon = (size, iconUrl) => {
+ return L.icon({
+ iconUrl: iconUrl,
+ iconSize: size,
+ iconAnchor: [size[0] / 2, size[1]],
+ popupAnchor: [0, -size[1]],
+ });
+ };
+
+ useEffect(() => {
+ if (start_lat && end_lng) {
+ map.fitBounds(
+ [
+ { lat: start_lat, lng: start_lng },
+ { lat: end_lat, lng: end_lng },
+ ],
+ { paddingTopLeft: [16, 16], paddingBottomRight: [16, 130] }
+ );
+ } else if (start_lat) {
+ map.setView({ lat: start_lat, lng: start_lng }, 15);
+ }
+ }, [start_lat, start_lng, map, end_lng, end_lat]);
+
+ return (
+ <>
+ {start_lat && start_lng && (
+
+ )}
+ {end_lat && end_lng && (
+
+ )}
+
+
+ {start_lat && start_lng && (
+
+
+
+ {end_lat && end_lng ? "طول جغرافیایی شروع" : "طول جغرافیایی"}
+
+
+
+
+ }
+ label={end_lat && end_lng ? "طول جغرافیایی شروع" : "طول جغرافیایی"}
+ />
+
+
+
+ {end_lat && end_lng ? "عرض جغرافیایی شروع" : "عرض جغرافیایی"}
+
+
+
+
+ }
+ label={end_lat && end_lng ? "عرض جغرافیایی شروع" : "عرض جغرافیایی"}
+ />
+
+
+ )}
+ {end_lat && end_lng && (
+
+
+
+ طول جغرافیایی پایان
+
+
+
+
+ }
+ label="طول جغرافیایی پایان"
+ />
+
+
+
+ عرض جغرافیایی پایان
+
+
+
+
+ }
+ label="عرض جغرافیایی پایان"
+ />
+
+
+ )}
+
+
+ >
+ );
+};
+
+export default ShowLocationMarker;
diff --git a/src/core/components/Toasts/error.jsx b/src/core/components/Toasts/error.jsx
index e93138b..dbc25e8 100644
--- a/src/core/components/Toasts/error.jsx
+++ b/src/core/components/Toasts/error.jsx
@@ -61,6 +61,35 @@ export const errorUnauthorizedToast = (toastContainer) =>
draggable: true,
}
);
+export const errorAccessDeniedToast = (message, toastContainer) =>
+ toast.error(
+ () => (
+
+
+
+
+ {message || "Access Denied"}
+
+
+
+ ),
+ {
+ icon: false,
+ containerId: toastContainer,
+ autoClose: 3000,
+ hideProgressBar: true,
+ pauseOnHover: true,
+ closeOnClick: false,
+ draggable: true,
+ }
+ );
export const errorLogicToast = (message, toastContainer) =>
toast.error(
diff --git a/src/core/components/UploadSystem.jsx b/src/core/components/UploadSystem.jsx
new file mode 100644
index 0000000..98ac8c3
--- /dev/null
+++ b/src/core/components/UploadSystem.jsx
@@ -0,0 +1,94 @@
+import { Box, Button, Paper, Typography } from "@mui/material";
+import AddIcon from "@mui/icons-material/Add";
+import DeleteForeverIcon from "@mui/icons-material/DeleteForever";
+import { useRef } from "react";
+
+const UploadSystem = ({ selectedImage, handleUploadChange, imageSize, fileType, showAddIcon }) => {
+ const fileInputRef = useRef(null);
+
+ const handleClick = () => {
+ fileInputRef.current.click();
+ };
+
+ return (
+
+ {showAddIcon ? (
+
+
+
+ فرمت قابل قبول : png, jpg
+
+ حداکثر 3Mb
+
+
+ ) : (
+ <>
+ {fileType && fileType.startsWith("image/") && (
+
+
+
+ )}
+ >
+ )}
+
+
+ );
+};
+export default UploadSystem;
diff --git a/src/core/utils/errorResponse.js b/src/core/utils/errorResponse.js
index c71bfed..5de1e77 100644
--- a/src/core/utils/errorResponse.js
+++ b/src/core/utils/errorResponse.js
@@ -1,6 +1,7 @@
"use client";
import { toast } from "react-toastify";
import {
+ errorAccessDeniedToast,
errorClientToast,
errorLogicToast,
errorServerToast,
@@ -22,6 +23,9 @@ const errorClient = (response, notification, toastContainer, logout) => {
logout();
if (notification) errorUnauthorizedToast(toastContainer);
break;
+ case 403:
+ if (notification) errorAccessDeniedToast(response.data.message, toastContainer);
+ break;
case 422:
if ("type" in response.data) {
if (Array.isArray(response.data.message)) {
diff --git a/src/core/utils/pageMenu.js b/src/core/utils/pageMenu.js
index 5fd062d..97e416d 100644
--- a/src/core/utils/pageMenu.js
+++ b/src/core/utils/pageMenu.js
@@ -108,7 +108,7 @@ export const pageMenu = [
id: "roadItemManagmentSupervisorCartable",
label: "کارتابل",
type: "page",
- route: process.env.NEXT_PUBLIC_API_URL + "/v2/road_items/supervisor/cartable",
+ route: "/dashboard/road-items/supervisor",
permissions: [
"show-road-item-supervise-cartable",
"show-road-item-supervise-cartable-province",
@@ -135,7 +135,7 @@ export const pageMenu = [
id: "roadItemManagmentOparationCartable",
label: "کارتابل",
type: "page",
- route: process.env.NEXT_PUBLIC_API_URL + "/v2/road_items/operator/cartable",
+ route: "/dashboard/road-items/operator",
permissions: ["create-road-item"],
},
],
diff --git a/src/core/utils/routes.js b/src/core/utils/routes.js
index 1cd95f7..9548f91 100644
--- a/src/core/utils/routes.js
+++ b/src/core/utils/routes.js
@@ -27,11 +27,25 @@ export const DELETE_SAMPLE_LIST = api + "/api/v3/azmayesh_samples/delete";
export const DELETE_AZMAYESH_TYPE_LIST = api + "/api/v3/azmayesh_types/delete";
export const ADD_AZMAYESH_TYPE = api + "/api/v3/azmayesh_types/store";
export const UPDATE_AZMAYESH_TYPE = api + "/api/v3/azmayesh_types/update";
-export const GET_OPERATOR_LIST = "/v3/api/fake-operator-list";
-export const EXPORT_OPERATOR_LIST = "https://rms.rmto.ir/v2/road_patrols/operator/cartable/report";
-export const GET_SUPERVISOR_LIST = "/v3/api/fake-supervisor-list";
-export const EXPORT_SUPERVISOR_LIST = "https://rms.rmto.ir/v2/road_patrols/supervisor/cartable/report";
-////**** mohammad check here ****////
-export const GET_OTP_TOKEN = "https://rms.rmto.ir/v2/get_otp_token";
-export const VERIFY_OTP = "https://rms.rmto.ir/v2/verify_otp";
+//road patrol
+export const GET_ROAD_PATROL_OPERATOR_LIST = "/v3/api/fake-road-patrol/operator";
+export const EXPORT_ROAD_PATROL_OPERATOR_LIST = "https://rms.rmto.ir/v2/road_patrols/operator/cartable/report";
+export const GET_ROAD_PATROL_SUPERVISOR_LIST = "/v3/api/fake-road-patrol/supervisor";
+export const EXPORT_ROAD_PATROL_SUPERVISOR_LIST = "https://rms.rmto.ir/v2/road_patrols/supervisor/cartable/report";
+
+// road items
+export const GET_ROAD_ITEMS_SUPERVISOR_LIST = api + "/api/v3/road_items/supervisor_index";
+export const EXPORT_ROAD_ITEMS_SUPERVISOR_LIST = api + "/api/v3/road_items/supervisor_report";
+export const GET_ROAD_ITEMS_OPERATOR_LIST = api + "/api/v3/road_items/operator_index";
+export const EXPORT_ROAD_ITEMS_OPERATOR_LIST = api + "/api/v3/road_items/operator_report";
+export const GET_ROAD_ITEMS_ITEM = "https://rms.witel.ir/v2/items";
+export const GET_ROAD_ITEMS_SUB_ITEM = "https://rms.witel.ir/v2/sub_items";
+export const CREATE_ROAD_ITEMS = api + "/api/v3/road_items/store";
+export const UPDATE_ROAD_ITEMS = api + "/api/v3/road_items/update";
+export const VERIFY_BY_SUPERVISOR = api + "/api/v3/road_items/verify_by_supervisor";
+export const REJECT_BY_SUPERVISOR = api + "/api/v3/road_items/verify_by_supervisor";
+export const DELETE_BY_SUPERVISOR = api + "/api/v3/road_items/delete";
+export const RESTORE_BY_SUPERVISOR = api + "/api/v3/road_items/restore";
+export const GET_CAR_LIST_SEARCH = api + "/api/v3/cmms_machines/search";
+export const GET_RAHDARANS_LIST_SEARCH = api + "/api/v3/rahdaran/search";
diff --git a/src/lib/contexts/DataTable.js b/src/lib/contexts/DataTable.js
index 3d69afc..ec22020 100644
--- a/src/lib/contexts/DataTable.js
+++ b/src/lib/contexts/DataTable.js
@@ -17,6 +17,16 @@ const DataTableProvider = ({ children, user_id, page_name, table_name, columns,
const [sortData, setSortData] = useState(initialSort || []);
const [initHide, setInitHide] = useState({});
const [hideData, setHideData] = useState({});
+ const [isDirty, setIsDirty] = useState(false);
+
+ useEffect(() => {
+ const hasConflict =
+ JSON.stringify(hideData) !== JSON.stringify(initHide) ||
+ JSON.stringify(sortData) !== JSON.stringify(initSort) ||
+ JSON.stringify(filterData) !== JSON.stringify(initFilter);
+
+ setIsDirty(hasConflict);
+ }, [hideData, sortData, filterData, initHide, initSort, initFilter]);
useEffect(() => {
if (!settingStore) return;
@@ -29,34 +39,27 @@ const DataTableProvider = ({ children, user_id, page_name, table_name, columns,
acc[column.id] = {
id: column.id,
value: storeFilter?.value || [],
- filterFn: storeFilter?.filterFn || column._filterFn,
+ filterMode: storeFilter?.filterMode || column.filterMode,
datatype: column.datatype,
};
- } else if (column._filterFn === "between") {
+ } else if (column.filterMode === "between") {
acc[column.id] = {
id: column.id,
value: storeFilter?.value || ["", ""],
- filterFn: storeFilter?.filterFn || column._filterFn,
+ filterMode: storeFilter?.filterMode || column.filterMode,
datatype: column.datatype,
};
} else {
acc[column.id] = {
id: column.id,
value: storeFilter?.value || "",
- filterFn: storeFilter?.filterFn || column._filterFn,
+ filterMode: storeFilter?.filterMode || column.filterMode,
datatype: column.datatype,
};
}
return acc;
}, {});
- setFilterData(filterValues);
- setInitFilter(filterValues);
- setSortData(settingStore?.[user_id]?.[page_name]?.[table_name]?.["sorts"] || initialSort);
- setInitSort(settingStore?.[user_id]?.[page_name]?.[table_name]?.["sorts"] || initialSort);
- setInitStates(true);
- }, [settingStore, isInitStates]);
- useEffect(() => {
const getHideValues = (columns) => {
return columns.reduce((acc, column) => {
const columnId = column.id;
@@ -74,7 +77,12 @@ const DataTableProvider = ({ children, user_id, page_name, table_name, columns,
const hideValues = getHideValues(columns);
setHideData(hideValues);
setInitHide(hideValues);
- }, [settingStore, isInitStates]);
+ setFilterData(filterValues);
+ setInitFilter(filterValues);
+ setSortData(settingStore?.[user_id]?.[page_name]?.[table_name]?.["sorts"] || initialSort);
+ setInitSort(settingStore?.[user_id]?.[page_name]?.[table_name]?.["sorts"] || initialSort);
+ setInitStates(true);
+ }, [settingStore]);
useEffect(() => {
if (
@@ -127,7 +135,9 @@ const DataTableProvider = ({ children, user_id, page_name, table_name, columns,
if (!isInitStates) return ;
return (
-
+
{children}
);
diff --git a/src/lib/hooks/useDataTable.js b/src/lib/hooks/useDataTable.js
index 9bef124..9026caa 100644
--- a/src/lib/hooks/useDataTable.js
+++ b/src/lib/hooks/useDataTable.js
@@ -2,8 +2,9 @@ import { useContext } from "react";
import { DataTableContext } from "@/lib/contexts/DataTable";
const useTableSetting = () => {
- const { filterData, setFilterData, sortData, setSortData, hideData, setHideData } = useContext(DataTableContext);
- return { filterData, setFilterData, sortData, setSortData, hideData, setHideData };
+ const { filterData, setFilterData, sortData, setSortData, hideData, setHideData, isDirty } =
+ useContext(DataTableContext);
+ return { filterData, setFilterData, sortData, setSortData, hideData, setHideData, isDirty };
};
export default useTableSetting;
diff --git a/src/lib/hooks/useRequest.js b/src/lib/hooks/useRequest.js
index 1efa7de..08b963b 100644
--- a/src/lib/hooks/useRequest.js
+++ b/src/lib/hooks/useRequest.js
@@ -8,11 +8,9 @@ const defaultOptions = {
requestOptions: {
headers: {},
},
- notification: {
- show: true,
- success: false,
- failed: true,
- },
+ notificationShow: true,
+ notificationSuccess: false,
+ notificationFailed: true
};
const useRequest = (initOptions) => {
@@ -30,13 +28,13 @@ const useRequest = (initOptions) => {
withCredentials: true,
...mergedOptions.requestOptions,
});
- successRequest(mergedOptions.notification.show && mergedOptions.notification.success, "request_data");
+ successRequest(mergedOptions.notificationShow && mergedOptions.notificationSuccess, "request_data");
return response;
} catch (error) {
if (error.response) {
errorResponse(
error.response,
- mergedOptions.notification.show && mergedOptions.notification.failed,
+ mergedOptions.notificationShow && mergedOptions.notificationFailed,
"request_data",
logout
);
diff --git a/src/lib/hooks/useRoadItemGetISubtems.js b/src/lib/hooks/useRoadItemGetISubtems.js
new file mode 100644
index 0000000..f315fd3
--- /dev/null
+++ b/src/lib/hooks/useRoadItemGetISubtems.js
@@ -0,0 +1,29 @@
+import { useEffect, useState } from "react";
+import { GET_ROAD_ITEMS_SUB_ITEM } from "@/core/utils/routes";
+import useRequest from "@/lib/hooks/useRequest";
+
+const useRoadItemGetItems = (id) => {
+ const requestServer = useRequest();
+ const [subItemsList, setSubItemsList] = useState([]);
+ const [loadingSubItemsList, setLoadingSubItemsList] = useState(true);
+ const [errorSubItemsList, setErrorSubItemsList] = useState(null);
+
+ useEffect(() => {
+ const fetchSubItems = async () => {
+ try {
+ const response = await requestServer(`${GET_ROAD_ITEMS_SUB_ITEM}/${id}`);
+ setSubItemsList(response.data.data);
+ setLoadingSubItemsList(false);
+ } catch (e) {
+ setErrorSubItemsList(e);
+ setLoadingSubItemsList(false);
+ }
+ };
+
+ fetchSubItems();
+ }, []);
+
+ return { subItemsList, loadingSubItemsList, errorSubItemsList };
+};
+
+export default useRoadItemGetItems;
diff --git a/src/lib/hooks/useRoadItemGetItems.js b/src/lib/hooks/useRoadItemGetItems.js
new file mode 100644
index 0000000..523f7c9
--- /dev/null
+++ b/src/lib/hooks/useRoadItemGetItems.js
@@ -0,0 +1,29 @@
+import { useEffect, useState } from "react";
+import { GET_ROAD_ITEMS_ITEM } from "@/core/utils/routes";
+import useRequest from "@/lib/hooks/useRequest";
+
+const useRoadItemGetItems = () => {
+ const requestServer = useRequest();
+ const [itemsList, setItemsList] = useState([]);
+ const [loadingItemsList, setLoadingItemsList] = useState(true);
+ const [errorItemsList, setErrorItemsList] = useState(null);
+
+ useEffect(() => {
+ const fetchItems = async () => {
+ try {
+ const response = await requestServer(`${GET_ROAD_ITEMS_ITEM}`);
+ setItemsList(response.data.data);
+ setLoadingItemsList(false);
+ } catch (e) {
+ setErrorItemsList(e);
+ setLoadingItemsList(false);
+ }
+ };
+
+ fetchItems();
+ }, []);
+
+ return { itemsList, loadingItemsList, errorItemsList };
+};
+
+export default useRoadItemGetItems;