}
+ />
+ );
+};
+export default CityController;
diff --git a/src/components/dashboard/damages/operator/Actions/create/Forms/CreateFormContent.jsx b/src/components/dashboard/damages/operator/Actions/create/Forms/CreateFormContent.jsx
new file mode 100644
index 0000000..11ec3b4
--- /dev/null
+++ b/src/components/dashboard/damages/operator/Actions/create/Forms/CreateFormContent.jsx
@@ -0,0 +1,239 @@
+import React, { useState } from "react";
+import { Controller, useForm } from "react-hook-form";
+import { yupResolver } from "@hookform/resolvers/yup";
+import { array, mixed, number, object, string } from "yup";
+import { Box, Button, DialogActions, DialogContent, Tab, Tabs } from "@mui/material";
+import StyledForm from "@/core/components/StyledForm";
+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 MinorCrashIcon from "@mui/icons-material/MinorCrash";
+import DamageInfo from "./DamageInfo";
+import KeyboardDoubleArrowLeftIcon from "@mui/icons-material/KeyboardDoubleArrowLeft";
+import DamageReport from "./DamageReport";
+import DamageItem from "./DamageItem";
+import validateNationalCode from "@/core/utils/nationalCodeValidation";
+import AssessmentIcon from "@mui/icons-material/Assessment";
+
+function TabPanel(props) {
+ const { children, value, index } = props;
+ return (
+
+ {value === index && {children}}
+
+ );
+}
+
+const validationSchema = object({
+ accident_date: string().required("لطفا تاریخ تصادف را وارد کنید!!!"),
+ accident_time: string().required("لطفا زمان تصادف را وارد کنید!!!"),
+ accident_type: string().required("لطفا نوع تصادف را وارد کنید!!!"),
+ province_id: number().required("لطفا استان را وارد کنید!!!"),
+ city_id: number().required("لطفا شهرستان را وارد کنید!!!"),
+ axis_name: string().required("لطفا نام محور را وارد کنید!!!"),
+ driver_name: string().required("لطفا نام و نام خانوادگی را وارد کنید!!!"),
+ phone_number: string()
+ .matches(/^09\d{9}$/, "شماره موبایل باید با 09 شروع شده و 11 رقم باشد")
+ .required("لطفا تلفن همراه را وارد کنید!!!"),
+ national_code: mixed()
+ .test("max", "کد ملی باید شامل 10 رقم باشد", (value) => value.toString().length === 10)
+ .test("validation", "کد ملی صحیح نمی باشد", (value) => validateNationalCode(value))
+ .required("لطفا کد ملی را وارد کنید!!!"),
+ start_point: mixed()
+ .test("start-point-required", "لطفاً نقطه تصادف را مشخص کنید!", function (value) {
+ return !!value;
+ })
+ .required("لطفاً نقطه تصادف را مشخص کنید!"),
+ plate_part1: mixed()
+ .test("max", "2رقم", (value) => value.toString().length === 2)
+ .required("plate_part1 الزامیست"),
+ plate_part2: mixed().required("plate_part2 الزامیست"),
+ plate_part3: mixed()
+ .test("max", "3رقم", (value) => value.toString().length === 3)
+ .required("plate_part3الزامیست"),
+ plate_part4: mixed()
+ .test("max", "2رقم", (value) => value.toString().length === 2)
+ .required("plate_part4الزامیست"),
+ damage_picture1: mixed().nullable().required("لطفا عکس تصادف را بارگذاری کنید!"),
+ damage_picture2: mixed().nullable().required("لطفا عکس تصادف را بارگذاری کنید!"),
+ police_serial: string().when("radio_button", {
+ is: "police_file_checkbox", // زمانی که گزینه "police_file_checkbox" انتخاب شده باشد
+ then: (schema) => schema.required("شماره کروکی یا نامه پلیس راه الزامی است"),
+ }),
+ police_file_date: string().when("radio_button", {
+ is: "police_file_checkbox", // زمانی که گزینه "police_file_checkbox" انتخاب شده باشد
+ then: (schema) => schema.required("تاریخ کروکی یا نامه پلیس راه الزامی است"),
+ }),
+ police_file: mixed().when("radio_button", {
+ is: "police_file_checkbox", // زمانی که گزینه "police_file_checkbox" انتخاب شده باشد
+ then: (schema) => schema.nullable().required("تصویر کروکی یا نامه پلیس راه الزامی است"),
+ otherwise: (schema) => schema.notRequired(),
+ }),
+ items_damage: array().min(1, "حداقل یک آیتم خسارت ضروریست!!!"),
+});
+
+const CreateFormContent = ({ setOpen, SubmitDamage, defaultData }) => {
+ const defaultValues = {
+ accident_date: defaultData.accident_date,
+ accident_time: defaultData.accident_time,
+ accident_type: defaultData.accident_type,
+ province_id: defaultData.province_id,
+ city_id: defaultData.city_id,
+ axis_name: defaultData.axis_name,
+ driver_name: defaultData.driver_name,
+ phone_number: defaultData.phone_number,
+ national_code: defaultData.national_code,
+ plate_part1: defaultData.plate_part1,
+ plate_part2: defaultData.plate_part2,
+ plate_part3: defaultData.plate_part3,
+ plate_part4: defaultData.plate_part4,
+ radio_button: defaultData.radio_button,
+ damage_picture1: defaultData.damage_picture1,
+ damage_picture2: defaultData.damage_picture2,
+ report_base: defaultData.report_base,
+ police_file_checkbox: defaultData.police_file_checkbox,
+ police_file: defaultData.police_file,
+ police_file_date: defaultData.police_file_date,
+ police_serial: defaultData.police_serial,
+ start_point: defaultData.start_point,
+ items_damage: defaultData.items_damage,
+ };
+
+ const [tabState, setTabState] = useState(0);
+ const handleClose = () => setOpen(false);
+ const handleChangeTab = (event, newValue) => setTabState(newValue);
+ const handlePrev = () => {
+ if (tabState === 0) {
+ handleClose();
+ } else {
+ setTabState(tabState - 1);
+ }
+ };
+ const {
+ control,
+ handleSubmit,
+ setValue,
+ trigger,
+ formState: { errors, isSubmitting, isValid },
+ } = useForm({
+ defaultValues,
+ resolver: yupResolver(validationSchema),
+ mode: "all",
+ });
+ const onSubmitBase = async (data) => {
+ await SubmitDamage(data);
+ };
+ const handleNext = async () => {
+ let fieldsToValidate = [];
+ if (tabState === 0) {
+ fieldsToValidate = [
+ "phone_number",
+ "start_point",
+ "plate_part1",
+ "plate_part2",
+ "plate_part3",
+ "plate_part4",
+ "accident_date",
+ "accident_type",
+ "national_code",
+ "accident_time",
+ "province_id",
+ "city_id",
+ "axis_name",
+ "driver_name",
+ "damage_picture1",
+ "damage_picture2",
+ ];
+ } else if (tabState === 1) {
+ fieldsToValidate = ["report_base", "police_file", "police_file_date", "police_serial"];
+ } else if (tabState === 2) {
+ fieldsToValidate = ["items_damage"];
+ }
+ const isValid = await trigger(fieldsToValidate);
+ if (isValid) {
+ setTabState(tabState + 1);
+ }
+ };
+ return (
+
+
+ } label="اطلاعات تصادف" />
+ } label="گزارش خسارت" />
+ } label="آیتم خسارت" />
+
+
+
+
+
+
+
+
+
+
+ {
+ return (
+
+ );
+ }}
+ />
+
+
+
+
+ : }
+ >
+ {tabState === 0 ? "بستن" : "مرحله قبل"}
+
+ {tabState !== 2 ? (
+ }
+ >
+ مرحله بعد
+
+ ) : (
+ }
+ >
+ {!isValid ? "حداقل یک آیتم خسارت ضروریست" : isSubmitting ? "در حال ثبت خسارت" : "ثبت خسارت"}
+
+ )}
+
+
+ );
+};
+export default CreateFormContent;
diff --git a/src/components/dashboard/damages/operator/Actions/create/Forms/DamageAmount.jsx b/src/components/dashboard/damages/operator/Actions/create/Forms/DamageAmount.jsx
new file mode 100644
index 0000000..35eb93f
--- /dev/null
+++ b/src/components/dashboard/damages/operator/Actions/create/Forms/DamageAmount.jsx
@@ -0,0 +1,56 @@
+import { Controller, useWatch } from "react-hook-form";
+import { Grid } from "@mui/material";
+import NumberField from "@/core/components/NumberField";
+import { useEffect } from "react";
+
+const DamageAmount = ({ control, damageItemList, setValue }) => {
+ const itemDamage = useWatch({ control, name: "id" });
+ const itemValue = useWatch({ control, name: "value" });
+ const itemDamageId = damageItemList.find((damageItem) => damageItem.id === itemDamage);
+ const isDisabled = itemDamageId?.base_price ? itemDamageId?.base_price !== 0 : false;
+ const calculatedValue = itemValue * (itemDamageId?.base_price ? itemDamageId?.base_price : 0);
+ useEffect(() => {
+ if (isDisabled) {
+ setValue("amount", calculatedValue);
+ }
+ }, [itemValue, itemDamageId, isDisabled, setValue]);
+ return (
+
+ {
+ return (
+ {
+ const inputValue = event.target.value;
+ if (isNaN(Number(inputValue))) {
+ return;
+ }
+ if (!isDisabled) {
+ field.onChange(inputValue);
+ }
+ }}
+ label={`هزینه خسارت (ریال)`}
+ placeholder="هزینه خسارت را وارد کنید"
+ fullWidth
+ size="small"
+ disabled={isDisabled || itemDamage === null}
+ error={error}
+ helperText={error?.message}
+ />
+ );
+ }}
+ />
+
+ );
+};
+export default DamageAmount;
diff --git a/src/components/dashboard/damages/operator/Actions/create/Forms/DamageInfo.jsx b/src/components/dashboard/damages/operator/Actions/create/Forms/DamageInfo.jsx
new file mode 100644
index 0000000..797610c
--- /dev/null
+++ b/src/components/dashboard/damages/operator/Actions/create/Forms/DamageInfo.jsx
@@ -0,0 +1,280 @@
+import { Controller, useWatch } from "react-hook-form";
+import { Grid, Stack, TextField } from "@mui/material";
+import MuiDatePicker from "@/core/components/MuiDatePicker";
+import MuiTimePicker from "@/core/components/MuiTimePicker";
+import SelectBox from "@/core/components/SelectBox";
+import useProvinces from "@/lib/hooks/useProvince";
+import PlateNumber from "@/core/components/PlateNumber";
+import ImageUpload from "./ImageUpload";
+import MapInfoOneMarker from "@/core/components/MapInfoOneMarker";
+import CityController from "@/components/dashboard/damages/operator/Actions/create/Forms/CityController";
+import PersianTextField from "@/core/components/PersianTextField";
+
+const DamageInfo = ({ control, setValue, errors }) => {
+ const StartPoint = useWatch({ control, name: "start_point" });
+ const { provinces, errorProvinces, loadingProvinces } = useProvinces();
+
+ return (
+
+
+
+ {
+ return (
+
+ );
+ }}
+ />
+
+
+
+
+
+ (
+
+ )}
+ />
+
+
+
+
+ (
+
+ )}
+ />
+
+
+ (
+ {
+ const inputValue = event.target.value;
+ if (isNaN(Number(inputValue))) {
+ return;
+ }
+ field.onChange(inputValue);
+ }}
+ label="کد ملی راننده"
+ placeholder={"کد ملی راننده را وارد کنید"}
+ fullWidth
+ size="small"
+ error={error}
+ helperText={error?.message}
+ InputLabelProps={{ shrink: true }}
+ />
+ )}
+ />
+
+
+
+
+ (
+ {
+ const inputValue = event.target.value;
+ if (isNaN(Number(inputValue))) {
+ return;
+ }
+ field.onChange(inputValue);
+ }}
+ label="شماره موبایل راننده"
+ placeholder={"شماره موبایل راننده را وارد کنید"}
+ fullWidth
+ size="small"
+ error={error}
+ helperText={error?.message}
+ InputLabelProps={{ shrink: true }}
+ />
+ )}
+ />
+
+
+
+
+
+
+
+ {
+ return (
+ field.onChange(value || [])}
+ helperText={error ? error.message : null}
+ />
+ );
+ }}
+ name={"accident_date"}
+ />
+
+
+ {
+ return (
+ field.onChange(value || null)}
+ helperText={error ? error.message : null}
+ />
+ );
+ }}
+ name={"accident_time"}
+ />
+
+
+ {
+ return (
+
+ );
+ }}
+ />
+
+
+
+
+ (
+
+ )}
+ />
+
+
+ (
+
+ )}
+ />
+
+
+
+
+
+
+ );
+};
+export default DamageInfo;
diff --git a/src/components/dashboard/damages/operator/Actions/create/Forms/DamageItem.jsx b/src/components/dashboard/damages/operator/Actions/create/Forms/DamageItem.jsx
new file mode 100644
index 0000000..e52c280
--- /dev/null
+++ b/src/components/dashboard/damages/operator/Actions/create/Forms/DamageItem.jsx
@@ -0,0 +1,167 @@
+import {
+ Autocomplete,
+ Box,
+ Button,
+ Chip,
+ Divider,
+ Fade,
+ Grid,
+ Skeleton,
+ Stack,
+ TextField,
+ Typography,
+} from "@mui/material";
+import { Controller, useForm, useWatch } from "react-hook-form";
+import LinearProgress from "@mui/material/LinearProgress";
+import { useEffect, useState } from "react";
+import DamageItemInfo from "./DamageItemInfo";
+import { number, object } from "yup";
+import useDamageItemList from "@/lib/hooks/useDamageItemList";
+import DamagePrice from "./DamagePrice";
+import { yupResolver } from "@hookform/resolvers/yup";
+
+const schema = object({
+ id: number().required("لطفا نوع خسارت را وارد کنید!!!"),
+ value: number().required("لطفا میزان خسارت را وارد کنید!!!"),
+ amount: number().required("لطفا هزینه خسارت را وارد کنید!!!"),
+});
+const defaultValues = {
+ id: null,
+ value: null,
+ amount: null,
+};
+const DamageItem = ({ baseOnChange, baseDamageItems }) => {
+ const flattenBaseDamageItems = (items) => {
+ return items.map((item) => ({
+ id: item.id,
+ title: item.title,
+ unit: item.unit,
+ base_price: item.base_price,
+ status: item.status,
+ until_27_shahrivar: item.until_27_shahrivar,
+ ...item.pivot, // اضافه کردن مقادیر موجود در pivot به سطح بالا
+ }));
+ };
+ const [selectedDamageItemList, setSelectedDamageItemList] = useState(
+ baseDamageItems.length !== 0 ? flattenBaseDamageItems(baseDamageItems) : []
+ );
+ const { damageItemList, loadingDamageItemList, errorDamageItemList } = useDamageItemList();
+ const { control, handleSubmit, reset, setValue } = useForm({
+ defaultValues,
+ resolver: yupResolver(schema),
+ mode: "onBlur",
+ });
+ const deleteDamageItem = (id) => {
+ setSelectedDamageItemList((prev) => prev.filter((DamageItem) => DamageItem.id !== id));
+ };
+ useEffect(() => {
+ baseOnChange(selectedDamageItemList);
+ }, [selectedDamageItemList]);
+ const handleCreateDamage = (data) => {
+ setSelectedDamageItemList((prev) => {
+ const isDuplicate = prev.some((item) => item.id === data.id);
+ if (!isDuplicate) {
+ return [...prev, data];
+ } else {
+ return prev;
+ }
+ });
+ reset();
+ };
+
+ return (
+ <>
+
+ {loadingDamageItemList ? (
+
+
+
+ ) : (
+ <>
+
+ (
+ option.title}
+ isOptionEqualToValue={(option, value) => option.id === value.id}
+ loading={loadingDamageItemList}
+ renderInput={(params) => (
+
+ )}
+ onChange={(_, value) => field.onChange(value?.id)}
+ value={damageItemList.find((item) => item.id === field.value) || null}
+ />
+ )}
+ />
+
+
+
+
+
+
+
+
+
+ >
+ )}
+
+
+
+
+ {selectedDamageItemList.length === 0 && (
+
+
+ ابتدا اطلاعات بالا را تکمیل نمایید
+
+
+ )}
+
+
+ {loadingDamageItemList ? (
+
+
+
+ ) : (
+ selectedDamageItemList.length !== 0 &&
+ selectedDamageItemList.map((selectedDamageItem, index) => (
+
+
+
+ ))
+ )}
+
+
+ >
+ );
+};
+export default DamageItem;
diff --git a/src/components/dashboard/damages/operator/Actions/create/Forms/DamageItemInfo.jsx b/src/components/dashboard/damages/operator/Actions/create/Forms/DamageItemInfo.jsx
new file mode 100644
index 0000000..ca95574
--- /dev/null
+++ b/src/components/dashboard/damages/operator/Actions/create/Forms/DamageItemInfo.jsx
@@ -0,0 +1,60 @@
+import { Box, Card, IconButton, Stack, Typography } from "@mui/material";
+import DeleteIcon from "@mui/icons-material/Delete";
+import React from "react";
+import MinorCrashIcon from "@mui/icons-material/MinorCrash";
+
+const DamageItemInfo = ({ deleteDamageItem, selectedDamageItem, damageItemList }) => {
+ const itemDamageId = damageItemList.find((item) => item.id == selectedDamageItem.id);
+ return (
+
+
+
+
+
+ {itemDamageId.title}
+
+
+
+ میزان خسارت ({itemDamageId.unit}) :{" "}
+
+ {(selectedDamageItem.value / 1).toLocaleString()}
+
+
+
+
+ هزینه خسارت (ریال) :{" "}
+
+ {(selectedDamageItem.amount / 1).toLocaleString()}
+
+
+
+
+
+
+ deleteDamageItem(selectedDamageItem.id)}>
+
+
+
+
+ );
+};
+export default DamageItemInfo;
diff --git a/src/components/dashboard/damages/operator/Actions/create/Forms/DamagePrice.jsx b/src/components/dashboard/damages/operator/Actions/create/Forms/DamagePrice.jsx
new file mode 100644
index 0000000..96ff80a
--- /dev/null
+++ b/src/components/dashboard/damages/operator/Actions/create/Forms/DamagePrice.jsx
@@ -0,0 +1,48 @@
+import { Controller, useWatch } from "react-hook-form";
+import { Grid, TextField } from "@mui/material";
+import DamageAmount from "./DamageAmount";
+
+const DamagePrice = ({ damageItemList, control, setValue }) => {
+ const itemDamage = useWatch({ control, name: "id" });
+ const itemDamageId = damageItemList.find((damageItem) => damageItem.id === itemDamage);
+
+ return (
+ <>
+
+ {
+ return (
+ {
+ const inputValue = event.target.value;
+ if (isNaN(Number(inputValue))) {
+ return;
+ }
+ field.onChange(inputValue);
+ }}
+ label={`میزان خسارت ${itemDamageId?.unit ? `(${itemDamageId?.unit})` : ""}`}
+ fullWidth
+ size="small"
+ error={error}
+ helperText={error?.message}
+ />
+ );
+ }}
+ />
+
+
+ >
+ );
+};
+export default DamagePrice;
diff --git a/src/components/dashboard/damages/operator/Actions/create/Forms/DamageReport.jsx b/src/components/dashboard/damages/operator/Actions/create/Forms/DamageReport.jsx
new file mode 100644
index 0000000..d994aff
--- /dev/null
+++ b/src/components/dashboard/damages/operator/Actions/create/Forms/DamageReport.jsx
@@ -0,0 +1,51 @@
+import { FormControlLabel, RadioGroup, Stack } from "@mui/material";
+import { Controller, useWatch } from "react-hook-form";
+import { useState } from "react";
+import PoliceFileReport from "./PoliceFileReport";
+import Radio from "@mui/material/Radio";
+
+const DamageReport = ({ control }) => {
+ const watchedStatus = useWatch({ control, name: "radio_button" });
+ const [selectedOption, setSelectedOption] = useState(watchedStatus || null);
+ // const [selectedOption, setSelectedOption] = useState("report_base");
+
+ const handleOptionChange = (e) => {
+ setSelectedOption(e.target.value);
+ };
+
+ return (
+ <>
+
+
+ }
+ />
+ }
+ label="بازدید عوامل و کارشناسان راهداری"
+ />
+ }
+ />
+ }
+ label="کروکی یا نامه پلیس راه (معرفی فرد به اداره)"
+ />
+
+
+ {selectedOption === "police_file_checkbox" && }
+ >
+ );
+};
+export default DamageReport;
diff --git a/src/components/dashboard/damages/operator/Actions/create/Forms/ImageUpload.jsx b/src/components/dashboard/damages/operator/Actions/create/Forms/ImageUpload.jsx
new file mode 100644
index 0000000..5186a4b
--- /dev/null
+++ b/src/components/dashboard/damages/operator/Actions/create/Forms/ImageUpload.jsx
@@ -0,0 +1,77 @@
+import { FormControl, FormHelperText } from "@mui/material";
+import UploadSystem from "@/core/components/UploadSystem";
+import React, { useEffect, useState } from "react";
+
+const ImageUpload = ({ name, value, onChange, error, title }) => {
+ const [beforeImg, setBeforeImg] = useState(value ? value : null);
+ const [beforeFileType, setBeforeFileType] = useState(value ? "image/" : null);
+ const [beforeFileName, setBeforeFileName] = useState(null);
+ const [showBeforeImage, setShowBeforeImage] = useState(!value);
+
+ useEffect(() => {
+ if (value) {
+ setShowBeforeImage(false);
+ if (typeof value === "string") {
+ setBeforeImg(value);
+ setBeforeFileType("image/");
+ } else if (value instanceof File) {
+ setBeforeImg(URL.createObjectURL(value));
+ setBeforeFileType(value.type);
+ }
+ }
+ }, [value]);
+
+ const handleFileChange = (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);
+ onChange(uploadedFile);
+ setShowBeforeImage(false);
+ }
+ };
+
+ return (
+
+
+ {title}
+
+
+ {error ? error.message : null}
+
+ );
+};
+export default ImageUpload;
diff --git a/src/components/dashboard/damages/operator/Actions/create/Forms/PoliceFileReport.jsx b/src/components/dashboard/damages/operator/Actions/create/Forms/PoliceFileReport.jsx
new file mode 100644
index 0000000..64f1067
--- /dev/null
+++ b/src/components/dashboard/damages/operator/Actions/create/Forms/PoliceFileReport.jsx
@@ -0,0 +1,67 @@
+import { Controller } from "react-hook-form";
+import { Grid, Stack, TextField } from "@mui/material";
+import MuiDatePicker from "@/core/components/MuiDatePicker";
+import ImageUpload from "./ImageUpload";
+
+const PoliceFileReport = ({ control }) => {
+ return (
+
+
+
+
+ (
+
+ )}
+ />
+
+
+ {
+ return (
+ field.onChange(value || [])}
+ helperText={error ? error.message : null}
+ />
+ );
+ }}
+ name={"police_file_date"}
+ />
+
+
+
+ {
+ return (
+
+ );
+ }}
+ />
+
+ );
+};
+export default PoliceFileReport;
diff --git a/src/components/dashboard/damages/operator/Actions/create/Forms/index.jsx b/src/components/dashboard/damages/operator/Actions/create/Forms/index.jsx
new file mode 100644
index 0000000..ab50184
--- /dev/null
+++ b/src/components/dashboard/damages/operator/Actions/create/Forms/index.jsx
@@ -0,0 +1,102 @@
+"use client";
+import { Dialog, IconButton } from "@mui/material";
+import CreateFormContent from "./CreateFormContent";
+import { format } from "date-fns";
+import { CREATE_DAMAGE } from "@/core/utils/routes";
+import useRequest from "@/lib/hooks/useRequest";
+import CloseIcon from "@mui/icons-material/Close";
+
+const OperatorCreateForm = ({ open, setOpen, mutate }) => {
+ const requestServer = useRequest({ notificationSuccess: true });
+ const SubmitCreateDamage = async (result) => {
+ const PlateNumber = `${result.plate_part1}-${result.plate_part2}-${result.plate_part3}-${result.plate_part4}`;
+ const formData = new FormData();
+ formData.append("accident_date", result.accident_date);
+ formData.append("accident_time", format(new Date(result.accident_time), "HH:mm"));
+ formData.append("accident_type", result.accident_type);
+ formData.append("province_id", result.province_id);
+ formData.append("city_id", result.city_id);
+ formData.append("axis_name", result.axis_name);
+ formData.append("driver_name", result.driver_name);
+ formData.append("driver_phone_number", result.phone_number);
+ formData.append("driver_national_code", result.national_code);
+ formData.append("plaque", PlateNumber);
+ formData.append("damage_picture1", result.damage_picture1);
+ formData.append("damage_picture2", result.damage_picture2);
+ formData.append("lat", result.start_point.lat);
+ formData.append("lng", result.start_point.lng);
+ result.items_damage.map((item, index) => {
+ formData.append(`damage_items[${index}][id]`, item.id);
+ formData.append(`damage_items[${index}][value]`, item.value);
+ formData.append(`damage_items[${index}][amount]`, item.amount);
+ });
+ if (result.radio_button === "report_base") {
+ formData.append("report_base", 1);
+ }
+ if (result.radio_button === "police_file_checkbox") {
+ formData.append("report_base", 0);
+ formData.append("police_file", result.police_file);
+ formData.append("police_file_date", result.police_file_date);
+ formData.append("police_serial", result.police_serial);
+ }
+ await requestServer(`${CREATE_DAMAGE}`, "post", {
+ data: formData,
+ })
+ .then(() => {
+ mutate();
+ setOpen(false);
+ })
+ .catch(() => {});
+ };
+ const defaultData = {
+ accident_date: "",
+ accident_time: "",
+ accident_type: "",
+ province_id: null,
+ city_id: null,
+ axis_name: "",
+ driver_name: "",
+ phone_number: "",
+ national_code: "",
+ plate_part1: "",
+ plate_part2: "الف",
+ plate_part3: "",
+ plate_part4: "",
+ radio_button: "report_base",
+ damage_picture1: null,
+ damage_picture2: null,
+ report_base: "",
+ police_file_checkbox: "",
+ police_file: null,
+ police_file_date: "",
+ police_serial: "",
+ start_point: "",
+ items_damage: [],
+ };
+ return (
+
+ );
+};
+export default OperatorCreateForm;
diff --git a/src/components/dashboard/damages/operator/Actions/create/index.jsx b/src/components/dashboard/damages/operator/Actions/create/index.jsx
new file mode 100644
index 0000000..5816c12
--- /dev/null
+++ b/src/components/dashboard/damages/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}
+ >
+ ثبت خسارت
+
+ )}
+
+ >
+ );
+};
+export default OperatorCreate;
diff --git a/src/components/dashboard/damages/operator/ExcelPrint/index.jsx b/src/components/dashboard/damages/operator/ExcelPrint/index.jsx
new file mode 100644
index 0000000..f38d451
--- /dev/null
+++ b/src/components/dashboard/damages/operator/ExcelPrint/index.jsx
@@ -0,0 +1,75 @@
+"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 { useTheme } from "@emotion/react";
+import DataTableFilterDataStructure from "@/core/utils/DataTableFilterDataStructure";
+import isArrayEmpty from "@/core/utils/isArrayEmpty";
+import { EXPORT_DAMAGES_OPERATOR_LIST } from "@/core/utils/routes";
+
+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 && (!Array.isArray(filter.value) || filter.value.some((item) => item.trim() !== ""))) {
+ acc.push({ id: key, value: filter.value });
+ }
+ return acc;
+ }, []);
+
+ const filterParams = useMemo(() => {
+ const params = new URLSearchParams();
+ if (activeFilters.length > 0) {
+ activeFilters.map((filter, index) => {
+ const filteredFilterData = DataTableFilterDataStructure(filterData, isArrayEmpty);
+ params.set("filters", JSON.stringify(filteredFilterData.length === 0 ? [] : filteredFilterData));
+ });
+ } else {
+ params.set("filters", JSON.stringify([]));
+ }
+ return params;
+ }, [activeFilters]);
+
+ const clickHandler = () => {
+ setLoading(true);
+ requestServer(`${EXPORT_DAMAGES_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/damages/operator/Form/CreateFactor/CreateFactorContent.jsx b/src/components/dashboard/damages/operator/Form/CreateFactor/CreateFactorContent.jsx
new file mode 100644
index 0000000..961f59c
--- /dev/null
+++ b/src/components/dashboard/damages/operator/Form/CreateFactor/CreateFactorContent.jsx
@@ -0,0 +1,215 @@
+import { CHECK_PAYMENT_STATUS, CREATE_FACTOR_DAMAGE, SEND_SMS_AGAIN } from "@/core/utils/routes";
+import useRequest from "@/lib/hooks/useRequest";
+import PaymentIcon from "@mui/icons-material/Payment";
+import ReceiptLongIcon from "@mui/icons-material/ReceiptLong";
+import RequestQuoteIcon from "@mui/icons-material/RequestQuote";
+import WebAssetIcon from "@mui/icons-material/WebAsset";
+import {
+ Alert,
+ Box,
+ Button,
+ Chip,
+ DialogActions,
+ DialogContent,
+ Divider,
+ Grid,
+ Stack,
+ Typography,
+} from "@mui/material";
+import { useReducer, useState } from "react";
+
+const initialState = {
+ submittingCreateFactor: false,
+ submittingCheckFactor: false,
+ submittingSms: false,
+};
+
+const reducer = (state, action) => {
+ switch (action.type) {
+ case "SET_SUBMITTING_CREATE_FACTOR":
+ return { ...state, submittingCreateFactor: action.payload };
+ case "SET_SUBMITTING_CHECK_FACTOR":
+ return { ...state, submittingCheckFactor: action.payload };
+ case "SET_SUBMITTING_SMS":
+ return { ...state, submittingSms: action.payload };
+ default:
+ return state;
+ }
+};
+const CreateFactorContent = ({ row, mutate, rowId, setOpenCreateFactorDialog }) => {
+ const [state, dispatch] = useReducer(reducer, initialState);
+ const requestServer = useRequest({ notificationSuccess: true });
+ const [factorCreated, setFactorCreated] = useState(row.original?.status === 3);
+ const handleCreateFactor = () => {
+ dispatch({ type: "SET_SUBMITTING_CREATE_FACTOR", payload: true });
+ requestServer(`${CREATE_FACTOR_DAMAGE}/${rowId}`, "get")
+ .then(() => {
+ mutate();
+ dispatch({ type: "SET_SUBMITTING_CREATE_FACTOR", payload: false });
+ setFactorCreated(true);
+ })
+ .catch(() => {
+ dispatch({ type: "SET_SUBMITTING_CREATE_FACTOR", payload: false });
+ });
+ };
+ const handleCheckPaymentStatus = () => {
+ dispatch({ type: "SET_SUBMITTING_CHECK_FACTOR", payload: true });
+ requestServer(`${CHECK_PAYMENT_STATUS}/${rowId}`, "get")
+ .then(() => {
+ setOpenCreateFactorDialog(false);
+ mutate();
+ dispatch({ type: "SET_SUBMITTING_CHECK_FACTOR", payload: false });
+ })
+ .catch(() => {
+ dispatch({ type: "SET_SUBMITTING_CHECK_FACTOR", payload: false });
+ });
+ };
+ const handleSendSMS = () => {
+ dispatch({ type: "SET_SUBMITTING_SMS", payload: true });
+ requestServer(`${SEND_SMS_AGAIN}/${rowId}`, "get")
+ .then(() => {
+ dispatch({ type: "SET_SUBMITTING_SMS", payload: false });
+ })
+ .catch(() => {
+ dispatch({ type: "SET_SUBMITTING_SMS", payload: false });
+ });
+ };
+
+ return (
+ <>
+
+
+
+
+ }
+ label={
+
+ مبلغ کسری بیمه
+
+ }
+ />
+
+
+
+ {(row.original?.deposit_insurance_amount / 1).toLocaleString() || "0"} ریال
+
+
+
+
+ }
+ label={
+
+ مبلغ کسری داغی
+
+ }
+ />
+
+
+
+ {(row.original?.deposit_daghi_amount / 1).toLocaleString() || "0"} ریال
+
+
+
+
+ }
+ label={
+
+ مبلغ کل خسارت برآورد شده
+
+ }
+ />
+
+
+
+ {(row.original?.sum / 1).toLocaleString() || "0"} ریال
+
+
+
+
+ }
+ label={
+
+ مبلغ قابل پرداخت
+
+ }
+ />
+
+
+
+ {(row.original?.sum / 1).toLocaleString() || "0"} ریال
+
+
+ {!factorCreated && (
+
+
+
+ بعد از صدور فاکتور، ویرایش اطلاعات کاربر و مبالغ امکانپذیر نخواهد بود.
+
+
+ لینک پرداخت فاکتور به شماره {row.original?.driver_phone_number} ارسال می شود.
+
+
+
+ )}
+
+
+
+ {factorCreated ? (
+ <>
+
+
+
+ >
+ ) : (
+ <>
+
+
+ >
+ )}
+
+ >
+ );
+};
+export default CreateFactorContent;
diff --git a/src/components/dashboard/damages/operator/Form/CreateFactor/index.jsx b/src/components/dashboard/damages/operator/Form/CreateFactor/index.jsx
new file mode 100644
index 0000000..86804a7
--- /dev/null
+++ b/src/components/dashboard/damages/operator/Form/CreateFactor/index.jsx
@@ -0,0 +1,43 @@
+import { Dialog, DialogTitle, IconButton, Tooltip } from "@mui/material";
+import { useState } from "react";
+import ReceiptLongIcon from "@mui/icons-material/ReceiptLong";
+import CreateFactorContent from "./CreateFactorContent";
+
+const CreateFactor = ({ row, rowId, mutate }) => {
+ const [openCreateFactorDialog, setOpenCreateFactorDialog] = useState(false);
+
+ return (
+ <>
+
+ setOpenCreateFactorDialog(true)}>
+
+
+
+
+ >
+ );
+};
+export default CreateFactor;
diff --git a/src/components/dashboard/damages/operator/Form/EditForm/EditController.jsx b/src/components/dashboard/damages/operator/Form/EditForm/EditController.jsx
new file mode 100644
index 0000000..f545cc6
--- /dev/null
+++ b/src/components/dashboard/damages/operator/Form/EditForm/EditController.jsx
@@ -0,0 +1,113 @@
+import useRequest from "@/lib/hooks/useRequest";
+import CreateFormContent from "@/components/dashboard/damages/operator/Actions/create/Forms/CreateFormContent";
+import { useEffect, useState } from "react";
+import { GET_DAMAGE_ITEM_DETAILS, UPDATE_DAMAGE_ITEM } from "@/core/utils/routes";
+import DialogLoading from "@/core/components/DialogLoading";
+import { format } from "date-fns";
+import moment from "jalali-moment";
+
+const EditController = ({ rowId, mutate, setOpenEditDialog }) => {
+ const requestServer = useRequest();
+ const [damageItemDetails, setDamageItemDetails] = useState(null);
+ const [damageItemDetailsLoading, setDamageItemDetailsLoading] = useState(false);
+
+ useEffect(() => {
+ setDamageItemDetailsLoading(true);
+ requestServer(`${GET_DAMAGE_ITEM_DETAILS}/${rowId}`, "get")
+ .then((response) => {
+ setDamageItemDetails(response.data.data);
+ setDamageItemDetailsLoading(false);
+ })
+ .catch((e) => {
+ setDamageItemDetailsLoading(false);
+ });
+ }, [rowId]);
+
+ const today = new Date().toISOString().split("T")[0];
+ const plaqueNumber = damageItemDetails?.plaque.split("-") || [];
+ const defaultData = {
+ damage_picture1: damageItemDetails?.damage_picture1 || null,
+ damage_picture2: damageItemDetails?.damage_picture2 || null,
+ accident_date: new Date(damageItemDetails?.accident_date) || "",
+ accident_time: new Date(`${today} ${damageItemDetails?.accident_time || ""}`),
+ accident_type: damageItemDetails?.accident_type || "",
+ axis_name: damageItemDetails?.axis_name || "",
+ driver_name: damageItemDetails?.driver_name || "",
+ police_file: damageItemDetails?.police_file || null,
+ police_file_date: damageItemDetails?.police_file_date || "",
+ police_serial: damageItemDetails?.police_serial || "",
+ items_damage: damageItemDetails?.damages || [],
+ phone_number: damageItemDetails?.driver_phone_number || "",
+ national_code: damageItemDetails?.driver_national_code || "",
+ plate_part1: plaqueNumber[0] || "",
+ plate_part2: plaqueNumber[1] || "",
+ plate_part3: plaqueNumber[2] || "",
+ plate_part4: plaqueNumber[3] || "",
+ radio_button: damageItemDetails?.report_base === 1 ? "report_base" : "police_file_checkbox",
+ report_base: damageItemDetails?.report_base,
+ province_id: damageItemDetails?.province_id || null,
+ city_id: damageItemDetails?.city_id || null,
+ start_point: { lat: damageItemDetails?.lat || "", lng: damageItemDetails?.lng || "" },
+ };
+ const HandleSubmit = async (result) => {
+ const PlateNumber = `${result.plate_part1}-${result.plate_part2}-${result.plate_part3}-${result.plate_part4}`;
+ const formData = new FormData();
+ const accidentDate =
+ result.accident_date == defaultData.accident_date
+ ? moment(result.accident_date).locale("en").format("YYYY-MM-DD")
+ : result.accident_date;
+ formData.append("accident_date", accidentDate);
+ formData.append("accident_time", format(new Date(result.accident_time), "HH:mm"));
+ formData.append("accident_type", result.accident_type);
+ formData.append("province_id", result.province_id);
+ formData.append("city_id", result.city_id);
+ formData.append("axis_name", result.axis_name);
+ formData.append("driver_name", result.driver_name);
+ formData.append("driver_phone_number", result.phone_number);
+ formData.append("driver_national_code", result.national_code);
+ formData.append("plaque", PlateNumber);
+ result.damage_picture1 !== defaultData.damage_picture1 &&
+ formData.append("damage_picture1", result.damage_picture1);
+ result.damage_picture2 !== defaultData.damage_picture2 &&
+ formData.append("damage_picture2", result.damage_picture2);
+ formData.append("lat", result.start_point.lat);
+ formData.append("lng", result.start_point.lng);
+ result.items_damage.map((item, index) => {
+ formData.append(`damage_items[${index}][id]`, item.id);
+ formData.append(`damage_items[${index}][value]`, item.value);
+ formData.append(`damage_items[${index}][amount]`, item.amount);
+ });
+ if (result.radio_button === "report_base") {
+ formData.append("report_base", 1);
+ }
+ if (result.radio_button === "police_file_checkbox") {
+ formData.append("report_base", 0);
+ result.police_file !== defaultData.police_file && formData.append("police_file", result.police_file);
+ formData.append("police_file_date", result.police_file_date);
+ formData.append("police_serial", result.police_serial);
+ }
+ await requestServer(`${UPDATE_DAMAGE_ITEM}/${rowId}`, "post", {
+ data: formData,
+ })
+ .then(() => {
+ mutate();
+ setOpenEditDialog(false);
+ })
+ .catch(() => {});
+ };
+ return (
+ <>
+ {damageItemDetailsLoading ? (
+
+ ) : (
+
+ )}
+ >
+ );
+};
+export default EditController;
diff --git a/src/components/dashboard/damages/operator/Form/EditForm/index.jsx b/src/components/dashboard/damages/operator/Form/EditForm/index.jsx
new file mode 100644
index 0000000..e608b32
--- /dev/null
+++ b/src/components/dashboard/damages/operator/Form/EditForm/index.jsx
@@ -0,0 +1,59 @@
+import { Dialog, DialogTitle, IconButton, Tooltip } from "@mui/material";
+import { useState } from "react";
+import BorderColorIcon from "@mui/icons-material/BorderColor";
+import EditController from "./EditController";
+import CloseIcon from "@mui/icons-material/Close";
+
+const EditForm = ({ row, mutate, rowId }) => {
+ const [openEditDialog, setOpenEditDialog] = useState(false);
+
+ return (
+ <>
+
+ {
+ setOpenEditDialog(true);
+ }}
+ >
+
+
+
+
+ >
+ );
+};
+export default EditForm;
diff --git a/src/components/dashboard/damages/operator/Form/RegisterInsurance/DaghiFile.jsx b/src/components/dashboard/damages/operator/Form/RegisterInsurance/DaghiFile.jsx
new file mode 100644
index 0000000..9f9670a
--- /dev/null
+++ b/src/components/dashboard/damages/operator/Form/RegisterInsurance/DaghiFile.jsx
@@ -0,0 +1,48 @@
+import { Grid, Stack, TextField } from "@mui/material";
+import { Controller } from "react-hook-form";
+import ImageUpload from "./ImageUpload";
+import NumberField from "@/core/components/NumberField";
+
+const DaghiFile = ({ control }) => {
+ return (
+
+
+
+
+ (
+
+ )}
+ />
+
+
+
+ (
+
+ )}
+ />
+
+ );
+};
+export default DaghiFile;
diff --git a/src/components/dashboard/damages/operator/Form/RegisterInsurance/DaghiPayment.jsx b/src/components/dashboard/damages/operator/Form/RegisterInsurance/DaghiPayment.jsx
new file mode 100644
index 0000000..91c4430
--- /dev/null
+++ b/src/components/dashboard/damages/operator/Form/RegisterInsurance/DaghiPayment.jsx
@@ -0,0 +1,53 @@
+import { useState } from "react";
+import { FormControlLabel, RadioGroup, Stack } from "@mui/material";
+import { Controller, useWatch } from "react-hook-form";
+import Radio from "@mui/material/Radio";
+import DaghiFile from "./DaghiFile";
+
+const DaghiPayment = ({ control }) => {
+ const watchedStatus = useWatch({ control, name: "deposit_daghi_status" });
+ const [selectedOption, setSelectedOption] = useState(watchedStatus || null);
+
+ const handleOptionChange = (e) => {
+ setSelectedOption(e.target.value);
+ };
+
+ return (
+ <>
+
+
+ }
+ />
+ }
+ label="صورتجلسه تحویل داغی به انبار ندارد"
+ />
+ }
+ />
+ }
+ label="صورتجلسه تحویل داغی به انبار دارد"
+ />
+
+
+
+ {selectedOption === "has_daghi" && }
+ >
+ );
+};
+export default DaghiPayment;
diff --git a/src/components/dashboard/damages/operator/Form/RegisterInsurance/ImageUpload.jsx b/src/components/dashboard/damages/operator/Form/RegisterInsurance/ImageUpload.jsx
new file mode 100644
index 0000000..107d640
--- /dev/null
+++ b/src/components/dashboard/damages/operator/Form/RegisterInsurance/ImageUpload.jsx
@@ -0,0 +1,77 @@
+import { FormControl, FormHelperText } from "@mui/material";
+import UploadSystem from "@/core/components/UploadSystem";
+import React, { useEffect, useState } from "react";
+
+const ImageUpload = ({ name, value, onChange, error, beforeImage = null, title }) => {
+ const [beforeImg, setBeforeImg] = useState(beforeImage ? beforeImage : null);
+ const [beforeFileType, setBeforeFileType] = useState(beforeImage ? "image/" : null);
+ const [beforeFileName, setBeforeFileName] = useState(null);
+ const [showBeforeImage, setShowBeforeImage] = useState(!beforeImage);
+
+ useEffect(() => {
+ if (value) {
+ setShowBeforeImage(false);
+ if (typeof value === "string") {
+ setBeforeImg(value);
+ setBeforeFileType("image/");
+ } else if (value instanceof File) {
+ setBeforeImg(URL.createObjectURL(value));
+ setBeforeFileType(value.type);
+ }
+ }
+ }, [value]);
+
+ const handleFileChange = (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);
+ onChange(uploadedFile);
+ setShowBeforeImage(false);
+ }
+ };
+
+ return (
+
+
+ {title}
+
+
+ {error ? error.message : null}
+
+ );
+};
+export default ImageUpload;
diff --git a/src/components/dashboard/damages/operator/Form/RegisterInsurance/InsuranceFile.jsx b/src/components/dashboard/damages/operator/Form/RegisterInsurance/InsuranceFile.jsx
new file mode 100644
index 0000000..da88acd
--- /dev/null
+++ b/src/components/dashboard/damages/operator/Form/RegisterInsurance/InsuranceFile.jsx
@@ -0,0 +1,48 @@
+import NumberField from "@/core/components/NumberField";
+import { Grid, Stack } from "@mui/material";
+import { Controller } from "react-hook-form";
+import ImageUpload from "./ImageUpload";
+
+const InsuranceFile = ({ control }) => {
+ return (
+
+
+
+
+ (
+
+ )}
+ />
+
+
+
+ (
+
+ )}
+ />
+
+ );
+};
+export default InsuranceFile;
diff --git a/src/components/dashboard/damages/operator/Form/RegisterInsurance/InsurancePayment.jsx b/src/components/dashboard/damages/operator/Form/RegisterInsurance/InsurancePayment.jsx
new file mode 100644
index 0000000..50ee912
--- /dev/null
+++ b/src/components/dashboard/damages/operator/Form/RegisterInsurance/InsurancePayment.jsx
@@ -0,0 +1,52 @@
+import { FormControlLabel, FormHelperText, RadioGroup, Stack } from "@mui/material";
+import { Controller, useWatch } from "react-hook-form";
+import Radio from "@mui/material/Radio";
+import { useState } from "react";
+import InsuranceFile from "./InsuranceFile";
+
+const InsurancePayment = ({ control }) => {
+ const watchedStatus = useWatch({ control, name: "deposit_insurance_status" });
+ const [selectedOption, setSelectedOption] = useState(watchedStatus || null);
+
+ const handleOptionChange = (e) => {
+ setSelectedOption(e.target.value);
+ };
+
+ return (
+ <>
+
+
+ }
+ />
+ }
+ label="فیش واریزی (بیمه) ندارد"
+ />
+ }
+ />
+ }
+ label="فیش واریزی (بیمه) دارد"
+ />
+
+
+ {selectedOption === "has_insurance" && }
+ >
+ );
+};
+export default InsurancePayment;
diff --git a/src/components/dashboard/damages/operator/Form/RegisterInsurance/RegisterInsuranceContent.jsx b/src/components/dashboard/damages/operator/Form/RegisterInsurance/RegisterInsuranceContent.jsx
new file mode 100644
index 0000000..b33b433
--- /dev/null
+++ b/src/components/dashboard/damages/operator/Form/RegisterInsurance/RegisterInsuranceContent.jsx
@@ -0,0 +1,203 @@
+import { Box, Button, DialogActions, DialogContent, Tab, Tabs, Typography } from "@mui/material";
+import ExitToAppIcon from "@mui/icons-material/ExitToApp";
+import KeyboardDoubleArrowRightIcon from "@mui/icons-material/KeyboardDoubleArrowRight";
+import KeyboardDoubleArrowLeftIcon from "@mui/icons-material/KeyboardDoubleArrowLeft";
+import LocalPostOfficeIcon from "@mui/icons-material/LocalPostOffice";
+import BeenhereIcon from "@mui/icons-material/Beenhere";
+import StyledForm from "@/core/components/StyledForm";
+import PaymentIcon from "@mui/icons-material/Payment";
+import React, { useState } from "react";
+import { useForm } from "react-hook-form";
+import { yupResolver } from "@hookform/resolvers/yup";
+import { mixed, object, string } from "yup";
+import InsurancePayment from "./InsurancePayment";
+import DaghiPayment from "./DaghiPayment";
+import { CONFIRM_PAYMENT_INFO } from "@/core/utils/routes";
+import useRequest from "@/lib/hooks/useRequest";
+function TabPanel(props) {
+ const { children, value, index } = props;
+ return (
+
+ {value === index && {children}}
+
+ );
+}
+const validationSchema = object({
+ deposit_insurance_status: string().required("انتخاب یک گزینه الزامیست."),
+ deposit_daghi_status: string().required("انتخاب یک گزینه الزامیست."),
+ deposit_insurance_amount: string().when("deposit_insurance_status", {
+ is: "has_insurance",
+ then: (schema) => schema.required("مبلغ بیمه الزامی است."),
+ otherwise: (schema) => schema.notRequired(),
+ }),
+ deposit_insurance_image: mixed().when("deposit_insurance_status", {
+ is: "has_insurance",
+ then: (schema) => schema.required("تصویر فیش واریزی بیمه الزامی است."),
+ otherwise: (schema) => schema.notRequired(),
+ }),
+ deposit_daghi_amount: string()
+ .when("deposit_daghi_status", {
+ is: "has_daghi",
+ then: (schema) => schema.required("مبلغ داغی الزامی است."),
+ otherwise: (schema) => schema.notRequired(),
+ })
+ .test("max-percentage", "مبلغ داغی نباید بیشتر از ۳۰ درصد مبلغ کل خسارت باشد.", function (value) {
+ const sum = this.options.context.sum || 0;
+ if (!value || isNaN(value) || !sum) return true;
+ return parseFloat(value) <= sum * 0.3;
+ }),
+ deposit_daghi_image: mixed().when("deposit_daghi_status", {
+ is: "has_daghi",
+ then: (schema) => schema.required("تصویر صورتجلسه تحویل داغی به انبار الزامی است."),
+ otherwise: (schema) => schema.notRequired(),
+ }),
+}).test("total-amount-limit", "مجموع مبلغ بیمه و داغی نباید از کل مبلغ خسارت بیشتر باشد.", function (values) {
+ const sum = this.options.context.sum || 0;
+ const depositInsuranceAmount = parseFloat(values.deposit_insurance_amount) || 0;
+ const depositDaghiAmount = parseFloat(values.deposit_daghi_amount) || 0;
+ return depositInsuranceAmount + depositDaghiAmount <= sum;
+});
+
+const RegisterInsuranceContent = ({ setOpenRegisterInsuranceDialog, row, mutate, rowId }) => {
+ const [tabState, setTabState] = useState(0);
+ const requestServer = useRequest({ notificationSuccess: true });
+ const handleClose = () => setOpenRegisterInsuranceDialog(false);
+ const handleChangeTab = (event, newValue) => setTabState(newValue);
+
+ const defaultValues = {
+ deposit_insurance_status: "no_insurance",
+ deposit_insurance_amount: "",
+ deposit_insurance_image: null,
+ deposit_daghi_status: "no_daghi",
+ deposit_daghi_amount: "",
+ deposit_daghi_image: null,
+ };
+ const handlePrev = () => {
+ if (tabState === 0) {
+ handleClose();
+ } else {
+ setTabState(tabState - 1);
+ }
+ };
+ const handleNext = async () => {
+ let fieldsToValidate = [];
+ if (tabState === 0) {
+ fieldsToValidate = ["deposit_insurance_status", "deposit_insurance_amount", "deposit_insurance_image"];
+ }
+ if (tabState === 1) {
+ fieldsToValidate = ["deposit_daghi_status", "deposit_daghi_amount", "deposit_daghi_image"];
+ }
+
+ const isValid = await trigger(fieldsToValidate);
+ if (isValid) {
+ setTabState(tabState + 1);
+ }
+ };
+
+ const {
+ control,
+ handleSubmit,
+ trigger,
+ formState: { isSubmitting, errors },
+ } = useForm({
+ defaultValues,
+ resolver: yupResolver(validationSchema),
+ mode: "all",
+ context: { sum: row?.original?.sum },
+ });
+
+ const onSubmitBase = async (data) => {
+ const formData = new FormData();
+ const depositDaghiStatus = data.deposit_daghi_status === "no_daghi" ? 0 : 1;
+ const depositInsuranceStatus = data.deposit_insurance_status === "no_insurance" ? 0 : 1;
+
+ if (data.deposit_daghi_status === "has_daghi") {
+ formData.append("deposit_daghi_status", depositDaghiStatus);
+ formData.append("deposit_daghi_amount", data.deposit_daghi_amount);
+ formData.append("deposit_daghi_image", data.deposit_daghi_image);
+ }
+ if (data.deposit_insurance_status === "has_insurance") {
+ formData.append("deposit_insurance_status", depositInsuranceStatus);
+ formData.append("deposit_insurance_amount", data.deposit_insurance_amount);
+ formData.append("deposit_insurance_image", data.deposit_insurance_image);
+ }
+ try {
+ await requestServer(`${CONFIRM_PAYMENT_INFO}/${rowId}`, "post", {
+ data: formData,
+ });
+ mutate();
+ setOpenRegisterInsuranceDialog(false);
+ } catch (error) {}
+ };
+ return (
+
+
+ } label="فیش واریزی بیمه" />
+ } label="صورتجلسه تحویل داغی به انبار" />
+
+
+
+
+
+
+
+
+
+
+
+ {errors[""]?.message && (
+
+ {errors[""].message}
+
+ )}
+
+ : }
+ >
+ {tabState === 0 ? "بستن" : "مرحله قبل"}
+
+ {tabState !== 1 && (
+ }
+ >
+ مرحله بعد
+
+ )}
+ {tabState === 1 && (
+ }
+ >
+ {isSubmitting ? "در حال ثبت" : "ثبت"}
+
+ )}
+
+
+ );
+};
+export default RegisterInsuranceContent;
diff --git a/src/components/dashboard/damages/operator/Form/RegisterInsurance/index.jsx b/src/components/dashboard/damages/operator/Form/RegisterInsurance/index.jsx
new file mode 100644
index 0000000..9a4e05e
--- /dev/null
+++ b/src/components/dashboard/damages/operator/Form/RegisterInsurance/index.jsx
@@ -0,0 +1,53 @@
+import { Dialog, IconButton, Tooltip } from "@mui/material";
+import { useState } from "react";
+import ReceiptIcon from "@mui/icons-material/Receipt";
+import RegisterInsuranceContent from "./RegisterInsuranceContent";
+import CloseIcon from "@mui/icons-material/Close";
+
+const RegisterInsurance = ({ row, mutate, rowId }) => {
+ const [openRegisterInsuranceDialog, setOpenRegisterInsuranceDialog] = useState(false);
+
+ return (
+ <>
+
+ setOpenRegisterInsuranceDialog(true)}>
+
+
+
+
+ >
+ );
+};
+export default RegisterInsurance;
diff --git a/src/components/dashboard/damages/operator/OperatorList.jsx b/src/components/dashboard/damages/operator/OperatorList.jsx
new file mode 100644
index 0000000..15907c0
--- /dev/null
+++ b/src/components/dashboard/damages/operator/OperatorList.jsx
@@ -0,0 +1,466 @@
+"use client";
+import { useEffect, useMemo, useState } from "react";
+import { Box, Stack, Typography } from "@mui/material";
+import DataTableWithAuth from "@/core/components/DataTableWithAuth";
+import Toolbar from "./Toolbar";
+import { GET_TECHNICAL_DAMAGE_OPERATOR_LIST } from "@/core/utils/routes";
+import moment from "jalali-moment";
+import RowActions from "./RowActions";
+import useProvinces from "@/lib/hooks/useProvince";
+import CustomSelectByDependency from "@/core/components/DataTable/filter/fieldsType/CustomSelectByDependency";
+import useEdaratLists from "@/lib/hooks/useEdaratLists";
+import ImageDialog from "./RowActions/ImageDialog";
+import LocationForm from "./RowActions/LocationForm";
+import ShowPlate from "./RowActions/ShowPlate";
+import DamageItemDialog from "./RowActions/DamageItemDialog";
+import DamageReportDialog from "./RowActions/DamageReport";
+
+const OperatorList = () => {
+ const statusOptions = [
+ { value: "", label: "همه وضعیت ها" },
+ { value: 0, label: "بدون اقدام" },
+ { value: 1, label: "صدور نامه بیمه و کارشناسی داغی" },
+ { value: 2, label: "فیش ها ثبت شده است" },
+ { value: 3, label: "فاکتور صادر شده است" },
+ { value: 4, label: "فاکتور پرداخت شده است" },
+ { value: 5, label: "نامه پلیس راه صادر شده است" },
+ ];
+ const columns = useMemo(() => {
+ return [
+ {
+ accessorKey: "id",
+ header: "کد یکتا", // Unique Code
+ id: "id",
+ enableColumnFilter: true,
+ datatype: "text",
+ filterMode: "equals",
+ sortDescFirst: false,
+ columnFilterModeOptions: ["equals", "contains"],
+ grow: false,
+ size: 100,
+ },
+ {
+ header: "اطلاعات تصادف",
+ id: "accidentInfo",
+ enableColumnFilter: false,
+ enableSorting: false,
+ grow: false,
+ size: 50,
+ columns: [
+ {
+ accessorKey: "province_id",
+ header: "استان",
+ id: "province_id",
+ enableColumnFilter: true,
+ datatype: "numeric",
+ filterMode: "equals",
+ grow: false,
+ size: 130,
+ ColumnSelectComponent: (props) => {
+ const { provinces, errorProvinces, loadingProvinces } = useProvinces();
+ const getColumnSelectOptions = useMemo(() => {
+ if (loadingProvinces) {
+ return [{ value: "loading", label: "در حال بارگذاری..." }];
+ }
+ if (errorProvinces) {
+ return [{ value: "error", label: "خطا در بارگذاری" }];
+ }
+ return [
+ { value: "", label: "کل کشور" },
+ ...provinces.map((province) => ({
+ value: province.id,
+ label: province.name_fa,
+ })),
+ ];
+ }, [provinces, errorProvinces, loadingProvinces]);
+ return (
+
+ );
+ },
+ Cell: ({ row }) => <>{row.original.province_fa}>,
+ },
+ {
+ accessorKey: "city_id",
+ header: "شهرستان", // Office
+ id: "city_id",
+ enableColumnFilter: true,
+ datatype: "numeric",
+ filterMode: "equals",
+ dependencyId: "province_id",
+ grow: false,
+ size: 120,
+ ColumnSelectComponent: (props) => {
+ const { edaratList, loadingEdaratList, errorEdaratList } = useEdaratLists(
+ props.dependencyFieldValue.value
+ );
+ const [prevDependency, setPrevDependency] = useState(props.dependencyFieldValue.value);
+
+ const getColumnSelectOptions = useMemo(() => {
+ if (props.dependencyFieldValue.value === "") {
+ return [{ value: "empty", label: "ابتدا استان را انتخاب کنید" }];
+ }
+ if (loadingEdaratList) {
+ return [{ value: "loading", label: "در حال بارگذاری..." }];
+ }
+ if (errorEdaratList) {
+ return [{ value: "error", label: "خطا در بارگذاری" }];
+ }
+ return [
+ { value: "", label: "کل شهر ها" },
+ ...edaratList.map((edare) => ({
+ value: edare.id,
+ label: edare.name_fa,
+ })),
+ ];
+ }, [edaratList, loadingEdaratList, errorEdaratList]);
+ useEffect(() => {
+ if (prevDependency === props.dependencyFieldValue?.value) return;
+ props.handleChange({ ...props.filterParameters, value: "" });
+ setPrevDependency(props.dependencyFieldValue?.value);
+ }, [props.dependencyFieldValue?.value]);
+ return (
+
+ );
+ },
+ Cell: ({ row }) => <>{row.original.city_fa}>,
+ },
+ {
+ accessorKey: "axis_name",
+ header: "نام محور",
+ id: "axis_name",
+ enableColumnFilter: true,
+ enableSorting: true,
+ datatype: "text",
+ filterMode: "equals",
+ columnFilterModeOptions: ["equals", "contains"],
+ grow: false,
+ size: 120,
+ },
+ {
+ accessorKey: "driver_name",
+ header: "نام راننده",
+ id: "driver_name",
+ enableColumnFilter: true,
+ enableSorting: true,
+ datatype: "text",
+ filterMode: "equals",
+ columnFilterModeOptions: ["equals", "contains"],
+ grow: false,
+ size: 120,
+ },
+ {
+ accessorKey: "driver_national_code",
+ header: "کد ملی راننده",
+ id: "driver_national_code",
+ enableColumnFilter: true,
+ enableSorting: true,
+ datatype: "text",
+ filterMode: "equals",
+ columnFilterModeOptions: ["equals", "contains"],
+ grow: false,
+ size: 120,
+ },
+ {
+ accessorKey: "driver_phone_number",
+ header: "شماره موبایل راننده",
+ id: "driver_phone_number",
+ enableColumnFilter: true,
+ enableSorting: true,
+ datatype: "text",
+ filterMode: "equals",
+ columnFilterModeOptions: ["equals", "contains"],
+ grow: false,
+ size: 120,
+ },
+ {
+ accessorKey: "plaque",
+ header: "پلاک",
+ id: "plaque",
+ enableColumnFilter: false,
+ datatype: "text",
+ filterMode: "equals",
+ enableSorting: false,
+ grow: false,
+ size: 100,
+ Cell: ({ renderedCellValue }) => ,
+ },
+ {
+ accessorKey: "accident_type",
+ header: "نوع تصادف", // Value
+ id: "accident_type",
+ enableColumnFilter: true,
+ enableSorting: true,
+ datatype: "text",
+ filterMode: "equals",
+ columnFilterModeOptions: ["equals", "contains"],
+ grow: false,
+ size: 120,
+ Cell: ({ row }) => <>{row.original.accident_type_fa}>,
+ },
+ {
+ accessorFn: (row) => {
+ const formattedDate = moment(row.accident_date, "YYYY-MM-DD"); // تنظیم فرمت صحیح تاریخ
+ const formattedTime = row.accident_time || "00:00:00"; // در صورت خالی بودن مقدار، مقدار پیشفرض داده شود
+ return moment(`${formattedDate.format("YYYY-MM-DD")}T${formattedTime}`)
+ .locale("fa")
+ .format("HH:mm | YYYY/MM/DD");
+ },
+ header: "تاریخ تصادف",
+ id: "accident_date",
+ enableColumnFilter: true,
+ datatype: "date",
+ filterMode: "between",
+ grow: false,
+ size: 100,
+ },
+ {
+ accessorKey: "files",
+ header: "تصاویر",
+ id: "files",
+ enableColumnFilter: false,
+ enableSorting: false,
+ datatype: "array",
+ grow: false,
+ size: 100,
+ muiTableBodyCellProps: {
+ sx: {
+ borderLeft: "1px solid #e1e1e1",
+ py: 0,
+ "&:first-of-type": {
+ borderLeft: "unset",
+ },
+ },
+ },
+ Cell: ({ row }) => {
+ if (row.original.damage_picture1) {
+ const imagesList = [row.original.damage_picture1, row.original.damage_picture2];
+ return (
+
+
+
+ );
+ }
+ return (
+
+ بدون تصویر
+
+ );
+ },
+ },
+ {
+ accessorKey: "location",
+ header: "موقعیت",
+ id: "location",
+ enableColumnFilter: false,
+ enableSorting: false,
+ datatype: "array",
+ grow: false,
+ size: 100,
+ muiTableBodyCellProps: {
+ sx: {
+ borderLeft: "1px solid #e1e1e1",
+ py: 0,
+ "&:first-of-type": {
+ borderLeft: "unset",
+ },
+ },
+ },
+ Cell: ({ row }) => {
+ return (
+
+
+
+ );
+ },
+ },
+ ],
+ },
+ {
+ header: "اطلاعات خسارت",
+ id: "damagesInfo",
+ enableColumnFilter: false,
+ enableSorting: false,
+ grow: false,
+ size: 50,
+ columns: [
+ {
+ header: "گزارش خسارت",
+ id: "damageReport",
+ enableColumnFilter: false,
+ enableSorting: false,
+ datatype: "array",
+ grow: false,
+ size: 100,
+ muiTableBodyCellProps: {
+ sx: {
+ borderLeft: "1px solid #e1e1e1",
+ py: 0,
+ "&:first-of-type": {
+ borderLeft: "unset",
+ },
+ },
+ },
+ Cell: ({ row }) => {
+ return (
+
+
+
+ );
+ },
+ },
+ {
+ header: "آیتم های خسارت",
+ id: "damageItems",
+ enableColumnFilter: false,
+ enableSorting: false,
+ datatype: "array",
+ grow: false,
+ size: 100,
+ muiTableBodyCellProps: {
+ sx: {
+ borderLeft: "1px solid #e1e1e1",
+ py: 0,
+ "&:first-of-type": {
+ borderLeft: "unset",
+ },
+ },
+ },
+ Cell: ({ row }) => {
+ return (
+
+
+
+ );
+ },
+ },
+ {
+ accessorKey: "sum",
+ header: "مبلغ کل خسارت (ریال)",
+ id: "sum",
+ enableColumnFilter: true,
+ datatype: "text",
+ filterMode: "equals",
+ columnFilterModeOptions: ["equals", "contains"],
+ sortDescFirst: true,
+ grow: false,
+ size: 100,
+ Cell: ({ row }) => (
+
+ {(row.original.sum / 1).toLocaleString()}
+
+ ),
+ },
+ ],
+ },
+ {
+ header: "اطلاعات واریز",
+ id: "items",
+ enableColumnFilter: false,
+ enableSorting: false,
+ grow: false,
+ size: 50,
+ columns: [
+ {
+ header: "مبلغ فیش (ریال)",
+ id: "final_amount",
+ enableColumnFilter: false,
+ grow: false,
+ size: 100,
+ Cell: ({ row }) => (
+
+ {(row.original.final_amount / 1).toLocaleString()}
+
+ ),
+ },
+ {
+ header: "مبلغ بیمه (ریال)",
+ id: "deposit_insurance_amount",
+ enableColumnFilter: false,
+ grow: false,
+ size: 100,
+ Cell: ({ row }) => (
+
+ {(row.original.deposit_insurance_amount / 1).toLocaleString()}
+
+ ),
+ },
+ {
+ header: "مبلغ داغی (ریال)",
+ id: "deposit_daghi_amount",
+ enableColumnFilter: false,
+ grow: false,
+ size: 100,
+ Cell: ({ row }) => (
+
+ {(row.original.deposit_daghi_amount / 1).toLocaleString()}
+
+ ),
+ },
+ ],
+ },
+ {
+ accessorFn: (row) => moment(row.created_at).locale("fa").format("HH:mm | yyyy/MM/DD"),
+ header: "تاریخ ثبت",
+ id: "created_at",
+ enableColumnFilter: true,
+ datatype: "date",
+ filterMode: "between",
+ grow: false,
+ size: 100,
+ },
+ {
+ accessorKey: "status",
+ header: "وضعیت",
+ id: "status",
+ enableColumnFilter: true,
+ datatype: "numeric",
+ filterMode: "equals",
+ grow: false,
+ size: 100,
+ columnSelectOption: () => {
+ return statusOptions.map((status) => ({
+ value: status.value,
+ label: status.label,
+ }));
+ },
+ Cell: ({ row }) => {
+ return {row.original.status_fa};
+ },
+ },
+ ];
+ }, []);
+
+ return (
+ <>
+
+
+
+ >
+ );
+};
+export default OperatorList;
diff --git a/src/components/dashboard/damages/operator/RowActions/DamageItemDialog/DamageItemContent.jsx b/src/components/dashboard/damages/operator/RowActions/DamageItemDialog/DamageItemContent.jsx
new file mode 100644
index 0000000..e9a76e5
--- /dev/null
+++ b/src/components/dashboard/damages/operator/RowActions/DamageItemDialog/DamageItemContent.jsx
@@ -0,0 +1,76 @@
+import DialogLoading from "@/core/components/DialogLoading";
+import { GET_DAMAGE_ITEM_DETAILS, GET_RAHDARAN_ROAD_ITEM } from "@/core/utils/routes";
+import useRequest from "@/lib/hooks/useRequest";
+import {
+ DialogContent,
+ Table,
+ TableBody,
+ TableCell,
+ TableContainer,
+ TableHead,
+ TableRow,
+ Typography,
+} from "@mui/material";
+import { useEffect, useState } from "react";
+
+const DamageItemContent = ({ rowId }) => {
+ const [loading, setLoading] = useState(true);
+ const [data, setData] = useState(null);
+ const request = useRequest();
+
+ useEffect(() => {
+ const fetchData = async () => {
+ try {
+ setLoading(true);
+ const response = await request(`${GET_DAMAGE_ITEM_DETAILS}/${rowId}`);
+ setData(response.data.data.damages);
+ } catch (error) {
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ fetchData();
+ }, [rowId]);
+
+ return (
+ <>
+
+ {loading ? (
+
+ ) : data ? (
+
+
+
+
+ نوع خسارت
+ میزان خسارت
+ هزینه خسارت (ریال)
+
+
+
+ {data.map((item) => {
+ return (
+
+ {item?.title}
+
+ {(item?.pivot?.value / 1).toLocaleString()} ({item?.pivot?.unit})
+
+ {(item?.pivot?.amount / 1).toLocaleString()}
+
+ );
+ })}
+
+
+
+ ) : (
+ اطلاعات یافت نشد
+ )}
+
+ >
+ );
+};
+export default DamageItemContent;
diff --git a/src/components/dashboard/damages/operator/RowActions/DamageItemDialog/index.jsx b/src/components/dashboard/damages/operator/RowActions/DamageItemDialog/index.jsx
new file mode 100644
index 0000000..6164de2
--- /dev/null
+++ b/src/components/dashboard/damages/operator/RowActions/DamageItemDialog/index.jsx
@@ -0,0 +1,43 @@
+import { Button, Dialog, DialogActions, DialogTitle, IconButton, Tooltip } from "@mui/material";
+import MinorCrashIcon from "@mui/icons-material/MinorCrash";
+import { useState } from "react";
+import DamageItemContent from "./DamageItemContent";
+
+const DamageItemDialog = ({ rowId }) => {
+ const [openDamageItemDialog, setOpenDamageItemDialog] = useState(false);
+ return (
+ <>
+
+ setOpenDamageItemDialog(true)}>
+
+
+
+
+ >
+ );
+};
+export default DamageItemDialog;
diff --git a/src/components/dashboard/damages/operator/RowActions/DamageReport/DamageReportContent.jsx b/src/components/dashboard/damages/operator/RowActions/DamageReport/DamageReportContent.jsx
new file mode 100644
index 0000000..3876c57
--- /dev/null
+++ b/src/components/dashboard/damages/operator/RowActions/DamageReport/DamageReportContent.jsx
@@ -0,0 +1,103 @@
+import { Box, Button, Chip, DialogContent, Divider, Grid, Typography } from "@mui/material";
+import PaymentIcon from "@mui/icons-material/Payment";
+import moment from "jalali-moment";
+import DownloadIcon from "@mui/icons-material/Download";
+import FormatListNumberedIcon from "@mui/icons-material/FormatListNumbered";
+import EventNoteIcon from "@mui/icons-material/EventNote";
+import ImageIcon from "@mui/icons-material/Image";
+
+const DamageReportContent = ({ damageReportDetails }) => {
+ return (
+ <>
+
+
+
+
+ }
+ label={
+
+ نوع گزارش
+
+ }
+ />
+
+
+
+ {damageReportDetails?.report_base === 1
+ ? "بازدید عوامل و کارشناسان راهداری"
+ : "کروکی یا نامه پلیس راه"}
+
+
+ {damageReportDetails?.report_base === 0 && (
+ <>
+
+
+ }
+ label={
+
+ شماره کروکی یا نامه پلیس راه
+
+ }
+ />
+
+
+
+ {damageReportDetails?.police_serial}
+
+
+
+
+ }
+ label={
+
+ تاریخ کروکی یا نامه پلیس راه
+
+ }
+ />
+
+
+
+ {moment(damageReportDetails?.police_file_date, "YYYY-MM-DD HH:mm:ss")
+ .locale("fa")
+ .format("YYYY/MM/DD")}
+
+
+
+
+ }
+ label={
+
+ تصویر کروکی یا نامه پلیس راه
+
+ }
+ />
+
+
+ }
+ onClick={() => {
+ if (damageReportDetails?.police_file) {
+ window.open(damageReportDetails.police_file, "_blank");
+ } else {
+ alert("فایلی برای دانلود موجود نیست.");
+ }
+ }}
+ >
+ دانلود فایل
+
+
+ >
+ )}
+
+
+ >
+ );
+};
+export default DamageReportContent;
diff --git a/src/components/dashboard/damages/operator/RowActions/DamageReport/DamageReportController.jsx b/src/components/dashboard/damages/operator/RowActions/DamageReport/DamageReportController.jsx
new file mode 100644
index 0000000..402911f
--- /dev/null
+++ b/src/components/dashboard/damages/operator/RowActions/DamageReport/DamageReportController.jsx
@@ -0,0 +1,34 @@
+import useRequest from "@/lib/hooks/useRequest";
+import { useEffect, useState } from "react";
+import { GET_DAMAGE_ITEM_DETAILS } from "@/core/utils/routes";
+import DialogLoading from "@/core/components/DialogLoading";
+import DamageReportContent from "./DamageReportContent";
+
+const DamageReportController = ({ rowId }) => {
+ const requestServer = useRequest();
+ const [damageReportDetails, setDamageReportDetails] = useState(null);
+ const [damageItemDetailsLoading, setDamageItemDetailsLoading] = useState(false);
+
+ useEffect(() => {
+ setDamageItemDetailsLoading(true);
+ requestServer(`${GET_DAMAGE_ITEM_DETAILS}/${rowId}`, "get")
+ .then((response) => {
+ setDamageReportDetails(response.data.data);
+ setDamageItemDetailsLoading(false);
+ })
+ .catch((e) => {
+ setDamageItemDetailsLoading(false);
+ });
+ }, [rowId]);
+
+ return (
+ <>
+ {damageItemDetailsLoading ? (
+
+ ) : (
+
+ )}
+ >
+ );
+};
+export default DamageReportController;
diff --git a/src/components/dashboard/damages/operator/RowActions/DamageReport/index.jsx b/src/components/dashboard/damages/operator/RowActions/DamageReport/index.jsx
new file mode 100644
index 0000000..be283ab
--- /dev/null
+++ b/src/components/dashboard/damages/operator/RowActions/DamageReport/index.jsx
@@ -0,0 +1,38 @@
+import React, { useState } from "react";
+import { Tooltip, IconButton, Dialog, DialogTitle, DialogActions, Button } from "@mui/material";
+import AssessmentIcon from "@mui/icons-material/Assessment";
+import DamageReportController from "./DamageReportController";
+const DamageReportDialog = ({ rowId }) => {
+ const [openDamageReportDialog, setOpenDamageReportDialog] = useState(false);
+
+ return (
+ <>
+
+ setOpenDamageReportDialog(true)}>
+
+
+
+
+ >
+ );
+};
+export default DamageReportDialog;
diff --git a/src/components/dashboard/damages/operator/RowActions/DeleteDialog/DeleteContent.jsx b/src/components/dashboard/damages/operator/RowActions/DeleteDialog/DeleteContent.jsx
new file mode 100644
index 0000000..28791c7
--- /dev/null
+++ b/src/components/dashboard/damages/operator/RowActions/DeleteDialog/DeleteContent.jsx
@@ -0,0 +1,39 @@
+import { Button, DialogActions, DialogContent, Stack, Typography } from "@mui/material";
+import React, { useState } from "react";
+import useRequest from "@/lib/hooks/useRequest";
+import { DELETE_DAMAGE_ITEM } 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_DAMAGE_ITEM}/${rowId}`, "delete")
+ .then(() => {
+ mutate();
+ setOpenDeleteDialog(false);
+ setSubmitting(false);
+ })
+ .catch(() => {
+ setSubmitting(false);
+ });
+ };
+ return (
+ <>
+
+
+ آیا از حذف فعالیت اطمینان دارید؟
+
+
+
+
+
+
+ >
+ );
+};
+export default DeleteContent;
diff --git a/src/components/dashboard/damages/operator/RowActions/DeleteDialog/index.jsx b/src/components/dashboard/damages/operator/RowActions/DeleteDialog/index.jsx
new file mode 100644
index 0000000..145e323
--- /dev/null
+++ b/src/components/dashboard/damages/operator/RowActions/DeleteDialog/index.jsx
@@ -0,0 +1,34 @@
+import DeleteIcon from "@mui/icons-material/Delete";
+import { Dialog, DialogTitle, IconButton, Tooltip } from "@mui/material";
+import { useState } from "react";
+import DeleteContent from "./DeleteContent";
+const DeleteDialog = ({ rowId, mutate }) => {
+ const [openDeleteDialog, setOpenDeleteDialog] = useState(false);
+
+ return (
+ <>
+
+ setOpenDeleteDialog(true)}>
+
+
+
+
+ >
+ );
+};
+export default DeleteDialog;
diff --git a/src/components/dashboard/damages/operator/RowActions/ImageDialog/ImageFormContent.jsx b/src/components/dashboard/damages/operator/RowActions/ImageDialog/ImageFormContent.jsx
new file mode 100644
index 0000000..c8f22d0
--- /dev/null
+++ b/src/components/dashboard/damages/operator/RowActions/ImageDialog/ImageFormContent.jsx
@@ -0,0 +1,45 @@
+import { Box, Stack, Typography, useTheme } from "@mui/material";
+import Image from "next/image";
+
+const ImageFormContent = ({ image, title }) => {
+ const theme = useTheme();
+ return (
+
+
+ {title}
+
+
+
+
+
+ );
+};
+export default ImageFormContent;
diff --git a/src/components/dashboard/damages/operator/RowActions/ImageDialog/index.jsx b/src/components/dashboard/damages/operator/RowActions/ImageDialog/index.jsx
new file mode 100644
index 0000000..74ade1d
--- /dev/null
+++ b/src/components/dashboard/damages/operator/RowActions/ImageDialog/index.jsx
@@ -0,0 +1,44 @@
+import { Button, Dialog, DialogActions, DialogContent, DialogTitle, IconButton, Paper, 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/damages/operator/RowActions/LocationForm/LocationFormContent.jsx b/src/components/dashboard/damages/operator/RowActions/LocationForm/LocationFormContent.jsx
new file mode 100644
index 0000000..14b3db3
--- /dev/null
+++ b/src/components/dashboard/damages/operator/RowActions/LocationForm/LocationFormContent.jsx
@@ -0,0 +1,35 @@
+"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/damages/operator/RowActions/LocationForm/index.jsx b/src/components/dashboard/damages/operator/RowActions/LocationForm/index.jsx
new file mode 100644
index 0000000..d614215
--- /dev/null
+++ b/src/components/dashboard/damages/operator/RowActions/LocationForm/index.jsx
@@ -0,0 +1,40 @@
+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/damages/operator/RowActions/PoliceRahLetter/PoliceRahLetterContent.jsx b/src/components/dashboard/damages/operator/RowActions/PoliceRahLetter/PoliceRahLetterContent.jsx
new file mode 100644
index 0000000..a4d4b63
--- /dev/null
+++ b/src/components/dashboard/damages/operator/RowActions/PoliceRahLetter/PoliceRahLetterContent.jsx
@@ -0,0 +1,40 @@
+import { Button, DialogActions, DialogContent, Stack, Typography } from "@mui/material";
+import React, { useState } from "react";
+import useRequest from "@/lib/hooks/useRequest";
+import { GET_POLICERAH, GET_POLICERAH_PAGE } from "@/core/utils/routes";
+
+const PoliceRahLetterContent = ({ setOpenPoliceRahDialog, rowId, mutate }) => {
+ const [submitting, setSubmitting] = useState(false);
+ const requestServer = useRequest();
+ const handleClick = () => {
+ setSubmitting(true);
+ requestServer(`${GET_POLICERAH}/${rowId}`, "get")
+ .then(() => {
+ mutate();
+ window.open(`${GET_POLICERAH_PAGE}/${rowId}`, "_blank");
+ setSubmitting(false);
+ setOpenPoliceRahDialog(false);
+ })
+ .catch(() => {
+ setSubmitting(false);
+ });
+ };
+ return (
+ <>
+
+
+ آیا از صدور نامه به پلیس راه اطمینان دارید؟
+
+
+
+
+
+
+ >
+ );
+};
+export default PoliceRahLetterContent;
diff --git a/src/components/dashboard/damages/operator/RowActions/PoliceRahLetter/index.jsx b/src/components/dashboard/damages/operator/RowActions/PoliceRahLetter/index.jsx
new file mode 100644
index 0000000..ab064d9
--- /dev/null
+++ b/src/components/dashboard/damages/operator/RowActions/PoliceRahLetter/index.jsx
@@ -0,0 +1,38 @@
+import { Dialog, DialogTitle, IconButton, Tooltip } from "@mui/material";
+import GarageIcon from "@mui/icons-material/Garage";
+import React, { useState } from "react";
+import PoliceRahLetterContent from "./PoliceRahLetterContent";
+
+const PoliceRahLetter = ({ rowId, mutate }) => {
+ const [openPoliceRahDialog, setOpenPoliceRahDialog] = useState(false);
+ return (
+ <>
+
+ setOpenPoliceRahDialog(true)}>
+
+
+
+
+ >
+ );
+};
+export default PoliceRahLetter;
diff --git a/src/components/dashboard/damages/operator/RowActions/SendToInsurance/SendToInsuranceContent.jsx b/src/components/dashboard/damages/operator/RowActions/SendToInsurance/SendToInsuranceContent.jsx
new file mode 100644
index 0000000..620049a
--- /dev/null
+++ b/src/components/dashboard/damages/operator/RowActions/SendToInsurance/SendToInsuranceContent.jsx
@@ -0,0 +1,40 @@
+import { Button, DialogActions, DialogContent, Stack, Typography } from "@mui/material";
+import React, { useState } from "react";
+import useRequest from "@/lib/hooks/useRequest";
+import { GET_INSURANCE, GET_INSURANCE_PAGE } from "@/core/utils/routes";
+
+const SendToInsuranceContent = ({ setOpenSendInsuranceDialog, rowId, mutate }) => {
+ const [submitting, setSubmitting] = useState(false);
+ const requestServer = useRequest();
+ const handleClick = () => {
+ setSubmitting(true);
+ requestServer(`${GET_INSURANCE}/${rowId}`, "get")
+ .then(() => {
+ mutate();
+ window.open(`${GET_INSURANCE_PAGE}/${rowId}`, "_blank");
+ setSubmitting(false);
+ setOpenSendInsuranceDialog(false);
+ })
+ .catch(() => {
+ setSubmitting(false);
+ });
+ };
+ return (
+ <>
+
+
+ آیا از صدور نامه بیمه اطمینان دارید؟
+
+
+
+
+
+
+ >
+ );
+};
+export default SendToInsuranceContent;
diff --git a/src/components/dashboard/damages/operator/RowActions/SendToInsurance/index.jsx b/src/components/dashboard/damages/operator/RowActions/SendToInsurance/index.jsx
new file mode 100644
index 0000000..d1d578a
--- /dev/null
+++ b/src/components/dashboard/damages/operator/RowActions/SendToInsurance/index.jsx
@@ -0,0 +1,38 @@
+import { Dialog, DialogTitle, IconButton, Tooltip } from "@mui/material";
+import EmailIcon from "@mui/icons-material/Email";
+import React, { useState } from "react";
+import SendToInsuranceContent from "./SendToInsuranceContent";
+
+const SendToInsurance = ({ rowId, mutate }) => {
+ const [openSendInsuranceDialog, setOpenSendInsuranceDialog] = useState(false);
+ return (
+ <>
+
+ setOpenSendInsuranceDialog(true)}>
+
+
+
+
+ >
+ );
+};
+export default SendToInsurance;
diff --git a/src/components/dashboard/damages/operator/RowActions/ShowPlate/index.jsx b/src/components/dashboard/damages/operator/RowActions/ShowPlate/index.jsx
new file mode 100644
index 0000000..af1f495
--- /dev/null
+++ b/src/components/dashboard/damages/operator/RowActions/ShowPlate/index.jsx
@@ -0,0 +1,38 @@
+import { Stack } from "@mui/material";
+
+const ShowPlate = ({ plate }) => {
+ return (
+
+
+ {plate.split("-")[3]}
+
+
+
+ {plate.split("-")[2]}
+
+
+ {plate.split("-")[1]}
+
+
+ {plate.split("-")[0]}
+
+
+
+ );
+};
+export default ShowPlate;
diff --git a/src/components/dashboard/damages/operator/RowActions/index.jsx b/src/components/dashboard/damages/operator/RowActions/index.jsx
new file mode 100644
index 0000000..f146c12
--- /dev/null
+++ b/src/components/dashboard/damages/operator/RowActions/index.jsx
@@ -0,0 +1,33 @@
+import { Box } from "@mui/material";
+import RegisterInsurance from "../Form/RegisterInsurance";
+import CreateFactor from "../Form/CreateFactor";
+import DeleteDialog from "./DeleteDialog";
+import SendToInsurance from "./SendToInsurance";
+import PoliceRahLetter from "./PoliceRahLetter";
+import EditForm from "../Form/EditForm";
+import { usePermissions } from "@/lib/hooks/usePermissions";
+
+const RowActions = ({ row, mutate }) => {
+ const { data: userPermissions } = usePermissions();
+ const hasDeletePermission = userPermissions.some((item) =>
+ ["delete-receipt", "delete-receipt-province"].includes(item)
+ );
+ const hasEditPermission = userPermissions.some((item) => ["edit-receipt", "edit-receipt-province"].includes(item));
+ return (
+
+ {[4, 5].includes(row.original?.status) && }
+ {[2, 3].includes(row.original?.status) && (
+
+ )}
+ {[1, 2].includes(row.original?.status) && (
+
+ )}
+ {[0, 1, 2].includes(row.original?.status) && }
+ {hasEditPermission && [0, 1, 2].includes(row.original?.status) && (
+
+ )}
+ {hasDeletePermission && }
+
+ );
+};
+export default RowActions;
diff --git a/src/components/dashboard/damages/operator/Toolbar.jsx b/src/components/dashboard/damages/operator/Toolbar.jsx
new file mode 100644
index 0000000..3024621
--- /dev/null
+++ b/src/components/dashboard/damages/operator/Toolbar.jsx
@@ -0,0 +1,16 @@
+import { Box } from "@mui/material";
+import OperatorCreate from "./Actions/create";
+import PrintExcel from "./ExcelPrint";
+import { usePermissions } from "@/lib/hooks/usePermissions";
+
+const Toolbar = ({ table, filterData, mutate }) => {
+ const { data: userPermissions } = usePermissions();
+ const hasAddPermission = userPermissions.some((item) => ["add-receipt", "add-receipt-province"].includes(item));
+ return (
+
+
+ {hasAddPermission && }
+
+ );
+};
+export default Toolbar;
diff --git a/src/components/dashboard/damages/operator/index.jsx b/src/components/dashboard/damages/operator/index.jsx
new file mode 100644
index 0000000..4936664
--- /dev/null
+++ b/src/components/dashboard/damages/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/reports/ExcelPrint/index.jsx b/src/components/dashboard/damages/report/ExcelPrint/index.jsx
similarity index 80%
rename from src/components/dashboard/roadItems/reports/ExcelPrint/index.jsx
rename to src/components/dashboard/damages/report/ExcelPrint/index.jsx
index eb86d27..2922a18 100644
--- a/src/components/dashboard/roadItems/reports/ExcelPrint/index.jsx
+++ b/src/components/dashboard/damages/report/ExcelPrint/index.jsx
@@ -1,12 +1,12 @@
"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 { EXPORT_COUNTRY_RECEIPT_REPORT, EXPORT_PROVINCE_RECEIPT_REPORT } from "@/core/utils/routes";
import useRequest from "@/lib/hooks/useRequest";
-import { EXPORT_ROAD_ITEMS_OPERATOR_LIST } from "@/core/utils/routes";
import { useTheme } from "@emotion/react";
+import DescriptionIcon from "@mui/icons-material/Description";
+import { Button, CircularProgress, IconButton, useMediaQuery } from "@mui/material";
+import FileSaver from "file-saver";
+import moment from "jalali-moment";
+import { useMemo, useState } from "react";
const PrintExcel = ({ table, filterData }) => {
const theme = useTheme();
@@ -15,8 +15,7 @@ const PrintExcel = ({ table, filterData }) => {
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 });
+ acc.push({ id: filter.id, value: filter.value });
}
return acc;
}, []);
@@ -32,11 +31,14 @@ const PrintExcel = ({ table, filterData }) => {
const clickHandler = () => {
setLoading(true);
- requestServer(`${EXPORT_ROAD_ITEMS_OPERATOR_LIST}?${filterParams}`, "get", {
+ const CountryOrProvince = activeFilters.find((item) => item.id === "province_id");
+ const requestUrl =
+ CountryOrProvince.value === "-1" ? EXPORT_COUNTRY_RECEIPT_REPORT : EXPORT_PROVINCE_RECEIPT_REPORT;
+ requestServer(`${requestUrl}?${filterParams}`, "get", {
requestOptions: { responseType: "blob" },
})
.then((response) => {
- const filename = `خروجی کارتابل گزارشات فعالیت روزانه تاریخ_${moment().format("jYYYY_jMM_jDD")}.xlsx`;
+ const filename = `خروجی کارتابل گزارشات خسارات وارده بر ابنیه فنی تاریخ_${moment().format("jYYYY_jMM_jDD")}.xlsx`;
FileSaver.saveAs(response.data, filename);
})
.catch(() => {})
diff --git a/src/components/dashboard/damages/report/ReportLists.jsx b/src/components/dashboard/damages/report/ReportLists.jsx
new file mode 100644
index 0000000..c43c9bb
--- /dev/null
+++ b/src/components/dashboard/damages/report/ReportLists.jsx
@@ -0,0 +1,241 @@
+"use client";
+import DataTableWithAuth from "@/core/components/DataTableWithAuth";
+import { Box } from "@mui/material";
+import { useMemo } from "react";
+import Toolbar from "./Toolbar";
+
+const ReportLists = ({ data, specialFilter }) => {
+ const columns = useMemo(() => {
+ return [
+ {
+ accessorKey: "edare_name",
+ header: "اداره",
+ id: "edare_name",
+ enableColumnFilter: false,
+ enableSorting: false,
+ datatype: "text",
+ grow: false,
+ size: 50,
+ },
+ {
+ accessorKey: "tedade",
+ header: "تعداد کل",
+ id: "tedade",
+ enableColumnFilter: false,
+ enableSorting: false,
+ datatype: "text",
+ grow: false,
+ size: 50,
+ muiTableBodyCellProps: {
+ sx: {
+ textAlign: "center",
+ borderLeft: "1px solid #e1e1e1",
+ py: 0,
+ "&:first-of-type": {
+ borderLeft: "unset",
+ },
+ },
+ },
+ Cell: ({ renderedCellValue }) => {
+ return (renderedCellValue / 1).toLocaleString();
+ },
+ },
+ {
+ header: "تعداد نحوه شناسایی خسارت",
+ enableColumnFilter: false,
+ enableSorting: false,
+ datatype: "text",
+ grow: false,
+ size: 50,
+ columns: [
+ {
+ accessorKey: "police_rah",
+ header: "گزارش پلیس راه",
+ id: "police_rah",
+ enableColumnFilter: false,
+ enableSorting: false,
+ datatype: "text",
+ grow: false,
+ size: 50,
+ muiTableBodyCellProps: {
+ sx: {
+ textAlign: "center",
+ borderLeft: "1px solid #e1e1e1",
+ py: 0,
+ "&:first-of-type": {
+ borderLeft: "unset",
+ },
+ },
+ },
+ Cell: ({ renderedCellValue }) => {
+ return (renderedCellValue / 1).toLocaleString();
+ },
+ },
+ {
+ accessorKey: "gozaresh_gasht",
+ header: "گزارش گشت راهداری",
+ id: "gozaresh_gasht",
+ enableColumnFilter: false,
+ enableSorting: false,
+ datatype: "text",
+ grow: false,
+ size: 50,
+ muiTableBodyCellProps: {
+ sx: {
+ textAlign: "center",
+ borderLeft: "1px solid #e1e1e1",
+ py: 0,
+ "&:first-of-type": {
+ borderLeft: "unset",
+ },
+ },
+ },
+ Cell: ({ renderedCellValue }) => {
+ return (renderedCellValue / 1).toLocaleString();
+ },
+ },
+ ],
+ },
+ {
+ accessorKey: "kol_sabt_shode",
+ header: "مبلغ کل خسارات ثبت شده (ریال)",
+ id: "kol_sabt_shode",
+ enableColumnFilter: false,
+ enableSorting: false,
+ datatype: "text",
+ grow: false,
+ size: 50,
+ muiTableBodyCellProps: {
+ sx: {
+ textAlign: "center",
+ borderLeft: "1px solid #e1e1e1",
+ py: 0,
+ "&:first-of-type": {
+ borderLeft: "unset",
+ },
+ },
+ },
+ Cell: ({ renderedCellValue }) => {
+ return (renderedCellValue / 1).toLocaleString();
+ },
+ },
+ {
+ accessorKey: "vasel_shode",
+ header: "کل مبلغ وصول شده (ریال)",
+ id: "vasel_shode",
+ enableColumnFilter: false,
+ enableSorting: false,
+ datatype: "text",
+ grow: false,
+ size: 50,
+ muiTableBodyCellProps: {
+ sx: {
+ textAlign: "center",
+ borderLeft: "1px solid #e1e1e1",
+ py: 0,
+ "&:first-of-type": {
+ borderLeft: "unset",
+ },
+ },
+ },
+ Cell: ({ renderedCellValue }) => {
+ return (renderedCellValue / 1).toLocaleString();
+ },
+ },
+ {
+ header: "مبلغ وصول شده به تفکیک نوع پرداخت (ریال)",
+ enableColumnFilter: false,
+ enableSorting: false,
+ datatype: "text",
+ grow: false,
+ size: 50,
+ columns: [
+ {
+ accessorKey: "vasel_shode_final",
+ header: "فیش",
+ id: "vasel_shode_final",
+ enableColumnFilter: false,
+ enableSorting: false,
+ datatype: "text",
+ grow: false,
+ size: 50,
+ muiTableBodyCellProps: {
+ sx: {
+ textAlign: "center",
+ borderLeft: "1px solid #e1e1e1",
+ py: 0,
+ "&:first-of-type": {
+ borderLeft: "unset",
+ },
+ },
+ },
+ Cell: ({ renderedCellValue }) => {
+ return (renderedCellValue / 1).toLocaleString();
+ },
+ },
+ {
+ accessorKey: "vasel_shode_bimeh",
+ header: "بیمه",
+ id: "vasel_shode_bimeh",
+ enableColumnFilter: false,
+ enableSorting: false,
+ datatype: "text",
+ grow: false,
+ size: 50,
+ muiTableBodyCellProps: {
+ sx: {
+ textAlign: "center",
+ borderLeft: "1px solid #e1e1e1",
+ py: 0,
+ "&:first-of-type": {
+ borderLeft: "unset",
+ },
+ },
+ },
+ Cell: ({ renderedCellValue }) => {
+ return (renderedCellValue / 1).toLocaleString();
+ },
+ },
+ {
+ accessorKey: "vasel_shode_daghi",
+ header: "داغی",
+ id: "vasel_shode_daghi",
+ enableColumnFilter: false,
+ enableSorting: false,
+ datatype: "text",
+ grow: false,
+ size: 50,
+ muiTableBodyCellProps: {
+ sx: {
+ textAlign: "center",
+ borderLeft: "1px solid #e1e1e1",
+ py: 0,
+ "&:first-of-type": {
+ borderLeft: "unset",
+ },
+ },
+ },
+ Cell: ({ renderedCellValue }) => {
+ return (renderedCellValue / 1).toLocaleString();
+ },
+ },
+ ],
+ },
+ ];
+ }, []);
+
+ return (
+
+
+
+ );
+};
+
+export default ReportLists;
diff --git a/src/components/dashboard/damages/report/Search/FromDateController.jsx b/src/components/dashboard/damages/report/Search/FromDateController.jsx
new file mode 100644
index 0000000..02d92bd
--- /dev/null
+++ b/src/components/dashboard/damages/report/Search/FromDateController.jsx
@@ -0,0 +1,28 @@
+import { Controller } from "react-hook-form";
+import MuiDatePicker from "@/core/components/MuiDatePicker";
+import React from "react";
+import moment from "jalali-moment";
+
+const FromDateController = ({ control }) => {
+ return (
+ (
+ {
+ const formattedDate = moment(new Date(newValue)).locale("en").format("YYYY-MM-DD");
+ onChange(formattedDate); // Update the field value in react-hook-form
+ }}
+ helperText={error ? error.message : null}
+ />
+ )}
+ />
+ );
+};
+export default FromDateController;
diff --git a/src/components/dashboard/damages/report/Search/SearchReportField.jsx b/src/components/dashboard/damages/report/Search/SearchReportField.jsx
new file mode 100644
index 0000000..ab8442c
--- /dev/null
+++ b/src/components/dashboard/damages/report/Search/SearchReportField.jsx
@@ -0,0 +1,45 @@
+import { Button, Grid, LinearProgress, Typography } from "@mui/material";
+import SearchIcon from "@mui/icons-material/Search";
+import React from "react";
+import FromDateController from "./FromDateController";
+import ToDateController from "./ToDateController";
+import SelectProvince from "./SelectProvince";
+import { Controller } from "react-hook-form";
+
+const SearchReportField = ({ control, hasProvincesPermission }) => {
+ return (
+
+
+
+
+
+
+
+ {hasProvincesPermission && (
+
+
+
+ )}
+
+ (
+ }
+ fullWidth
+ >
+ {isSubmitting ? "درحال دریافت اطلاعات" : "جستجو"}
+
+ )}
+ />
+
+
+ );
+};
+
+export default SearchReportField;
diff --git a/src/components/dashboard/damages/report/Search/SelectProvince.jsx b/src/components/dashboard/damages/report/Search/SelectProvince.jsx
new file mode 100644
index 0000000..b1d8a39
--- /dev/null
+++ b/src/components/dashboard/damages/report/Search/SelectProvince.jsx
@@ -0,0 +1,47 @@
+import { Controller } from "react-hook-form";
+import MuiDatePicker from "@/core/components/MuiDatePicker";
+import React from "react";
+import { FormControl, FormHelperText, InputLabel, LinearProgress, MenuItem, Select, Typography } from "@mui/material";
+import useProvinces from "@/lib/hooks/useProvince";
+
+const SelectProvince = ({ control }) => {
+ const { provinces, loadingProvinces, errorProvinces } = useProvinces();
+ return (
+ (
+
+ استان
+
+ {error && {error.message}}
+
+ )}
+ />
+ );
+};
+export default SelectProvince;
diff --git a/src/components/dashboard/damages/report/Search/ToDateController.jsx b/src/components/dashboard/damages/report/Search/ToDateController.jsx
new file mode 100644
index 0000000..4e41124
--- /dev/null
+++ b/src/components/dashboard/damages/report/Search/ToDateController.jsx
@@ -0,0 +1,30 @@
+import { Controller, useWatch } from "react-hook-form";
+import MuiDatePicker from "@/core/components/MuiDatePicker";
+import React from "react";
+import moment from "jalali-moment";
+
+const ToDateController = ({ control }) => {
+ const minDate = useWatch({ control, name: "from_date" });
+ return (
+ (
+ {
+ const formattedDate = moment(new Date(newValue)).locale("en").format("YYYY-MM-DD");
+ onChange(formattedDate); // Update the field value in react-hook-form
+ }}
+ helperText={error ? error.message : null}
+ />
+ )}
+ />
+ );
+};
+export default ToDateController;
diff --git a/src/components/dashboard/damages/report/Search/index.jsx b/src/components/dashboard/damages/report/Search/index.jsx
new file mode 100644
index 0000000..f29d302
--- /dev/null
+++ b/src/components/dashboard/damages/report/Search/index.jsx
@@ -0,0 +1,13 @@
+import StyledForm from "@/core/components/StyledForm";
+import SearchReportField from "./SearchReportField";
+
+const SearchReportList = ({ handleSubmit, onSearchSubmit, control, hasProvincesPermission }) => {
+ return (
+ <>
+
+
+
+ >
+ );
+};
+export default SearchReportList;
diff --git a/src/components/dashboard/damages/report/Toolbar.jsx b/src/components/dashboard/damages/report/Toolbar.jsx
new file mode 100644
index 0000000..7dd930f
--- /dev/null
+++ b/src/components/dashboard/damages/report/Toolbar.jsx
@@ -0,0 +1,11 @@
+import PrintExcel from "./ExcelPrint";
+import { Box } from "@mui/material";
+
+const Toolbar = ({ table, filterData, mutate }) => {
+ return (
+
+
+
+ );
+};
+export default Toolbar;
diff --git a/src/components/dashboard/damages/report/index.jsx b/src/components/dashboard/damages/report/index.jsx
new file mode 100644
index 0000000..6e2092d
--- /dev/null
+++ b/src/components/dashboard/damages/report/index.jsx
@@ -0,0 +1,137 @@
+"use client";
+import PageTitle from "@/core/components/PageTitle";
+import { GET_PROVINCE_RECEIPT_REPORT, GET_COUNTRY_RECEIPT_REPORT } from "@/core/utils/routes";
+import { useAuth } from "@/lib/contexts/auth";
+import { usePermissions } from "@/lib/hooks/usePermissions";
+import useRequest from "@/lib/hooks/useRequest";
+import { yupResolver } from "@hookform/resolvers/yup";
+import { LinearProgress, Stack } from "@mui/material";
+import moment from "jalali-moment";
+import { useEffect, useState } from "react";
+import { useForm } from "react-hook-form";
+import * as yup from "yup";
+import SearchReportList from "./Search";
+import ReportLists from "./ReportLists";
+
+const DamagesReportPage = () => {
+ const [data, setData] = useState(null);
+ const { data: userPermissions } = usePermissions();
+ const { user } = useAuth();
+ const requestServer = useRequest();
+ const hasProvincesPermission = userPermissions?.includes("show-receipt");
+
+ const defaultValues = {
+ from_date: moment(new Date()).format("YYYY-MM-DD"),
+ date_to: moment(new Date()).format("YYYY-MM-DD"),
+ province_id: hasProvincesPermission ? "-1" : user.province_id,
+ };
+ const defaultFilter = [
+ { value: `${defaultValues.province_id}`, datatype: "numeric", id: "province_id", fn: "equals" },
+ { value: `${defaultValues.from_date}`, datatype: "date", id: "from_date", fn: "between" },
+ { value: `${defaultValues.date_to}`, datatype: "date", id: "date_to", fn: "between" },
+ ];
+ const [specialFilter, setSpecialFilter] = useState(defaultFilter);
+
+ const onSearchSubmit = async (data) => {
+ const params = new URLSearchParams();
+ params.set("from_date", `${data.from_date}`);
+ params.set("date_to", `${data.date_to}`);
+ setSpecialFilter([
+ { value: `${data.province_id}`, datatype: "numeric", id: "province_id", fn: "equals" },
+ { value: `${data.from_date}`, datatype: "date", id: "from_date", fn: "between" },
+ { value: `${data.date_to}`, datatype: "date", id: "date_to", fn: "between" },
+ ]);
+ if (data.province_id === "-1") {
+ try {
+ const response = await requestServer(`${GET_COUNTRY_RECEIPT_REPORT}?${params}`);
+ const result = response.data.data;
+ const nationalReportForItem = result.activities.find((report) => report.province_id == -1);
+ const nationalReport = {
+ edare_name: "کل کشور",
+ ...nationalReportForItem,
+ };
+
+ setData({
+ data: [
+ nationalReport,
+ ...result.provinces.map((province) => {
+ const filteredReports = result.activities.find(
+ (report) => report.province_id == province.id
+ );
+ return {
+ ...filteredReports,
+ edare_name: province.name_fa,
+ };
+ }),
+ ],
+ filters: data,
+ });
+ } catch (e) {
+ console.log(e);
+ }
+ } else {
+ try {
+ params.set("province_id", `${data.province_id}`);
+ const response = await requestServer(`${GET_PROVINCE_RECEIPT_REPORT}?${params}`);
+ const result = response.data.data;
+ const nationalReportForItem = result.activities.find((report) => report.city_id == "-1");
+ const nationalReport = {
+ ...nationalReportForItem,
+ edare_name: "کل استان",
+ };
+ setData({
+ data: [
+ nationalReport,
+ ...result.cities.map((edare) => {
+ const filteredReports = result.activities.find((report) => report.city_id == edare.id);
+ return {
+ edare_name: edare.name_fa,
+ ...filteredReports,
+ };
+ }),
+ ],
+ filters: data,
+ });
+ } catch (e) {}
+ }
+ };
+
+ useEffect(() => {
+ if (!userPermissions) return;
+ onSearchSubmit(defaultValues);
+ }, [userPermissions]);
+
+ const validationSchema = yup.object().shape({
+ from_date: yup
+ .string()
+ .required("تاریخ شروع الزامی است!")
+ .matches(/^\d{4}-\d{2}-\d{2}$/, "تاریخ شروع الزامی است!")
+ .test("is-valid-date", "تاریخ شروع معتبر نیست!", (value) => moment(value, "YYYY-MM-DD", true).isValid()),
+ date_to: yup
+ .string()
+ .required("تاریخ پایان الزامی است!")
+ .matches(/^\d{4}-\d{2}-\d{2}$/, "تاریخ پایان الزامی است!")
+ .test("is-valid-date", "تاریخ پایان معتبر نیست!", (value) => moment(value, "YYYY-MM-DD", true).isValid()),
+ province_id: yup.string().nullable(),
+ });
+
+ const { control, handleSubmit } = useForm({
+ defaultValues,
+ resolver: yupResolver(validationSchema),
+ mode: "onBlur",
+ });
+
+ return (
+
+
+
+ {!data ? : }
+
+ );
+};
+export default DamagesReportPage;
diff --git a/src/components/dashboard/fastReact/complaintList/ComplaintListTable.jsx b/src/components/dashboard/fastReact/complaintList/ComplaintListTable.jsx
new file mode 100644
index 0000000..90102fc
--- /dev/null
+++ b/src/components/dashboard/fastReact/complaintList/ComplaintListTable.jsx
@@ -0,0 +1,281 @@
+import { useEffect, useMemo, useState } from "react";
+import { Box, Button, Dialog, DialogActions, Stack, Typography } from "@mui/material";
+import DataTableWithAuth from "@/core/components/DataTableWithAuth";
+import Toolbar from "./Toolbar";
+import RowActions from "./RowActions";
+import { GET_FAST_REACT_COMPLAINTS } from "@/core/utils/routes";
+import useProvinces from "@/lib/hooks/useProvince";
+import CustomSelectByDependency from "@/core/components/DataTable/filter/fieldsType/CustomSelectByDependency";
+import { usePermissions } from "@/lib/hooks/usePermissions";
+import { useAuth } from "@/lib/contexts/auth";
+import useEdaratLists from "@/lib/hooks/useEdaratLists";
+import DescriptionForm from "./RowActions/DescriptionDialog";
+import LocationForm from "./RowActions/LocationDialog";
+import moment from "jalali-moment";
+
+const ComplaintListTable = ({ open, setOpen, mutate }) => {
+ const { data: userPermissions } = usePermissions();
+ const hasCountryPermission = userPermissions?.includes("show-fast-react");
+ const { user } = useAuth();
+ const columns = useMemo(() => {
+ const dynamicColumns = {
+ header: "استان",
+ id: "road_observeds__province_id",
+ enableColumnFilter: true,
+ datatype: "numeric",
+ filterMode: "equals",
+ grow: false,
+ size: 130,
+ ColumnSelectComponent: (props) => {
+ const { provinces, errorProvinces, loadingProvinces } = useProvinces();
+ const getColumnSelectOptions = useMemo(() => {
+ if (loadingProvinces) {
+ return [{ value: "loading", label: "در حال بارگذاری..." }];
+ }
+ if (errorProvinces) {
+ return [{ value: "error", label: "خطا در بارگذاری" }];
+ }
+ return [
+ { value: "", label: "کل کشور" },
+ ...provinces.map((province) => ({
+ value: province.id,
+ label: province.name_fa,
+ })),
+ ];
+ }, [provinces, errorProvinces, loadingProvinces]);
+ return (
+
+ );
+ },
+ Cell: ({ row }) => <>{row.original.province_fa}>,
+ };
+ return [
+ {
+ accessorKey: "id",
+ header: "کد یکتا",
+ id: "road_observeds__id",
+ enableColumnFilter: true,
+ datatype: "text",
+ filterMode: "equals",
+ sortDescFirst: false,
+ columnFilterModeOptions: ["equals", "contains"],
+ grow: false,
+ size: 100,
+ },
+ ...(hasCountryPermission ? [dynamicColumns] : []),
+ {
+ header: "اداره",
+ id: "road_observeds__edarate_shahri_id",
+ enableColumnFilter: true,
+ datatype: "numeric",
+ filterMode: "equals",
+ dependencyId: hasCountryPermission ? "road_observeds__province_id" : null,
+ grow: false,
+ size: 120,
+ ColumnSelectComponent: (props) => {
+ const { edaratList, loadingEdaratList, errorEdaratList } = useEdaratLists(
+ hasCountryPermission ? props.dependencyFieldValue.value : user.province_id
+ );
+ const [prevDependency, setPrevDependency] = useState(
+ hasCountryPermission ? props.dependencyFieldValue.value : user.province_id
+ );
+
+ const getColumnSelectOptions = useMemo(() => {
+ if (hasCountryPermission && props.dependencyFieldValue.value === "") {
+ return [{ value: "empty", label: "ابتدا استان را انتخاب کنید" }];
+ }
+ if (loadingEdaratList) {
+ return [{ value: "loading", label: "در حال بارگذاری..." }];
+ }
+ if (errorEdaratList) {
+ return [{ value: "error", label: "خطا در بارگذاری" }];
+ }
+ return [
+ { value: "", label: "کل ادارات" },
+ ...edaratList.map((edare) => ({
+ value: edare.id,
+ label: edare.name_fa,
+ })),
+ ];
+ }, [edaratList, loadingEdaratList, errorEdaratList]);
+ useEffect(() => {
+ if (hasCountryPermission) return;
+ if (prevDependency === props.dependencyFieldValue?.value) return;
+ props.handleChange({ ...props.filterParameters, value: "" });
+ setPrevDependency(props.dependencyFieldValue?.value);
+ }, [props.dependencyFieldValue?.value, hasCountryPermission]);
+ return (
+
+ );
+ },
+ Cell: ({ row }) => <>{row.original.edarate_shahri_name_fa}>,
+ },
+ {
+ header: "اطلاعات ثبت شده در سامانه سوانح",
+ id: "fkInfo",
+ enableColumnFilter: false,
+ enableSorting: false,
+ grow: false,
+ size: 50,
+ columns: [
+ {
+ accessorKey: "fk_RegisteredEventMessage",
+ header: "کد سوانح",
+ id: "fk_RegisteredEventMessage",
+ enableColumnFilter: true,
+ datatype: "text",
+ filterMode: "equals",
+ sortDescFirst: false,
+ columnFilterModeOptions: ["equals", "contains"],
+ grow: false,
+ size: 100,
+ },
+ {
+ accessorKey: "Title",
+ header: "موضوع گزارش",
+ id: "Title",
+ enableColumnFilter: false,
+ datatype: "text",
+ filterMode: "equals",
+ sortDescFirst: false,
+ columnFilterModeOptions: ["equals", "contains"],
+ grow: false,
+ size: 100,
+ },
+ {
+ accessorKey: "FeatureTypeTitle",
+ header: "نوع گزارش",
+ id: "FeatureTypeTitle",
+ enableColumnFilter: false,
+ datatype: "text",
+ filterMode: "equals",
+ sortDescFirst: false,
+ columnFilterModeOptions: ["equals", "contains"],
+ grow: false,
+ size: 100,
+ },
+ {
+ accessorKey: "Description",
+ header: "توضیح گزارش",
+ id: "Description",
+ enableColumnFilter: false,
+ datatype: "text",
+ filterMode: "equals",
+ sortDescFirst: false,
+ columnFilterModeOptions: ["equals", "contains"],
+ grow: false,
+ size: 100,
+ muiTableBodyCellProps: {
+ sx: {
+ borderLeft: "1px solid #e1e1e1",
+ py: 0,
+ "&:first-of-type": {
+ borderLeft: "unset",
+ },
+ },
+ },
+ Cell: ({ renderedCellValue }) => {
+ if (renderedCellValue) {
+ return (
+
+
+
+ );
+ }
+ return بدون توضیحات;
+ },
+ },
+ {
+ header: "موقعیت",
+ id: "location",
+ enableColumnFilter: false,
+ enableSorting: false,
+ datatype: "array",
+ grow: false,
+ size: 100,
+ muiTableBodyCellProps: {
+ sx: {
+ borderLeft: "1px solid #e1e1e1",
+ py: 0,
+ "&:first-of-type": {
+ borderLeft: "unset",
+ },
+ },
+ },
+ Cell: ({ row }) => {
+ return (
+
+
+
+ );
+ },
+ },
+ {
+ accessorKey: "MobileForSendEventSms",
+ header: "شماره تماس گیرنده",
+ id: "MobileForSendEventSms",
+ enableColumnFilter: false,
+ datatype: "text",
+ filterMode: "equals",
+ sortDescFirst: false,
+ columnFilterModeOptions: ["equals", "contains"],
+ grow: false,
+ size: 100,
+ },
+ {
+ accessorFn: (row) => moment(row.StartTime_DateTime, "M/D/YYYY h:mm:ss A").locale("fa").format("HH:mm | yyyy/MM/DD"),
+ header: "تاریخ ثبت",
+ id: "StartTime_DateTime",
+ enableColumnFilter: false,
+ datatype: "date",
+ filterMode: "between",
+ grow: false,
+ size: 100,
+ },
+ ]
+ }
+ ];
+ }, []);
+
+ return (
+
+ );
+};
+export default ComplaintListTable;
diff --git a/src/components/dashboard/fastReact/complaintList/ExcelPrint/index.jsx b/src/components/dashboard/fastReact/complaintList/ExcelPrint/index.jsx
new file mode 100644
index 0000000..05f3362
--- /dev/null
+++ b/src/components/dashboard/fastReact/complaintList/ExcelPrint/index.jsx
@@ -0,0 +1,74 @@
+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_FAST_REACT_COMPLAINT_LIST } from "@/core/utils/routes";
+import { useTheme } from "@emotion/react";
+import DataTableFilterDataStructure from "@/core/utils/DataTableFilterDataStructure";
+import isArrayEmpty from "@/core/utils/isArrayEmpty";
+
+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 && (!Array.isArray(filter.value) || filter.value.some((item) => item.trim() !== ""))) {
+ acc.push({ id: key, value: filter.value });
+ }
+ return acc;
+ }, []);
+
+ const filterParams = useMemo(() => {
+ const params = new URLSearchParams();
+ if (activeFilters.length > 0) {
+ activeFilters.map((filter, index) => {
+ const filteredFilterData = DataTableFilterDataStructure(filterData, isArrayEmpty);
+ params.set("filters", JSON.stringify(filteredFilterData.length === 0 ? [] : filteredFilterData));
+ });
+ } else {
+ params.set("filters", JSON.stringify([]));
+ }
+ return params;
+ }, [activeFilters]);
+
+ const clickHandler = () => {
+ setLoading(true);
+ requestServer(`${EXPORT_FAST_REACT_COMPLAINT_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/fastReact/complaintList/Form/registerAction/ImageUpload.jsx b/src/components/dashboard/fastReact/complaintList/Form/registerAction/ImageUpload.jsx
new file mode 100644
index 0000000..5186a4b
--- /dev/null
+++ b/src/components/dashboard/fastReact/complaintList/Form/registerAction/ImageUpload.jsx
@@ -0,0 +1,77 @@
+import { FormControl, FormHelperText } from "@mui/material";
+import UploadSystem from "@/core/components/UploadSystem";
+import React, { useEffect, useState } from "react";
+
+const ImageUpload = ({ name, value, onChange, error, title }) => {
+ const [beforeImg, setBeforeImg] = useState(value ? value : null);
+ const [beforeFileType, setBeforeFileType] = useState(value ? "image/" : null);
+ const [beforeFileName, setBeforeFileName] = useState(null);
+ const [showBeforeImage, setShowBeforeImage] = useState(!value);
+
+ useEffect(() => {
+ if (value) {
+ setShowBeforeImage(false);
+ if (typeof value === "string") {
+ setBeforeImg(value);
+ setBeforeFileType("image/");
+ } else if (value instanceof File) {
+ setBeforeImg(URL.createObjectURL(value));
+ setBeforeFileType(value.type);
+ }
+ }
+ }, [value]);
+
+ const handleFileChange = (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);
+ onChange(uploadedFile);
+ setShowBeforeImage(false);
+ }
+ };
+
+ return (
+
+
+ {title}
+
+
+ {error ? error.message : null}
+
+ );
+};
+export default ImageUpload;
diff --git a/src/components/dashboard/fastReact/complaintList/Form/registerAction/RegisterActionContent.jsx b/src/components/dashboard/fastReact/complaintList/Form/registerAction/RegisterActionContent.jsx
new file mode 100644
index 0000000..34d4130
--- /dev/null
+++ b/src/components/dashboard/fastReact/complaintList/Form/registerAction/RegisterActionContent.jsx
@@ -0,0 +1,106 @@
+import { Button, DialogActions, DialogContent, FormControlLabel, RadioGroup, Stack } from "@mui/material";
+import { Controller, useForm } from "react-hook-form";
+import Radio from "@mui/material/Radio";
+import StyledForm from "@/core/components/StyledForm";
+import { useState } from "react";
+import RegisterActionDone from "./RegisterActionDone";
+import RegisterActionUndone from "./RegisterActionUndone";
+import { yupResolver } from "@hookform/resolvers/yup";
+import { mixed, object, string } from "yup";
+import BeenhereIcon from "@mui/icons-material/Beenhere";
+
+const RegisterActionContent = ({ setOpen, defaultValues, onBaseSubmit }) => {
+ const [selectedOption, setSelectedOption] = useState(defaultValues?.rms_status || "1");
+
+ const handleOptionChange = (e) => {
+ setSelectedOption(e.target.value);
+ };
+ const validationSchema = object({
+ description: string().required("توضیحات الزامی است"),
+ start_point: mixed().when("rms_status", {
+ is: "1",
+ then: (schema) => schema.required("مکان اقدام الزامی است"),
+ otherwise: (schema) => schema.notRequired(),
+ }),
+ image_before_1: mixed().when("rms_status", {
+ is: "1",
+ then: (schema) => schema.required("تصویر قبل از اقدام الزامی است"),
+ otherwise: (schema) => schema.notRequired(),
+ }),
+ image_after_1: mixed().when("rms_status", {
+ is: "1",
+ then: (schema) => schema.required("تصویر بعد از اقدام الزامی است"),
+ otherwise: (schema) => schema.notRequired(),
+ }),
+ });
+
+ const {
+ control,
+ handleSubmit,
+ setValue,
+ formState: { isSubmitting, errors },
+ } = useForm({
+ defaultValues,
+ resolver: yupResolver(validationSchema),
+ mode: "onChange",
+ });
+
+ const onSubmit = async (data) => {
+ await onBaseSubmit(data);
+ };
+ return (
+
+
+
+
+ }
+ />
+ }
+ label="اقدام انجام شد"
+ />
+ }
+ />
+ }
+ label="اقدام انجام نشد"
+ />
+
+
+ {selectedOption === "1" ? (
+
+ ) : (
+
+ )}
+
+
+
+ }
+ color={"primary"}
+ variant={"contained"}
+ type={"submit"}
+ >
+ {isSubmitting ? "درحال ثبت اقدام..." : "ثبت اقدام"}
+
+
+
+ );
+};
+export default RegisterActionContent;
diff --git a/src/components/dashboard/fastReact/complaintList/Form/registerAction/RegisterActionDone.jsx b/src/components/dashboard/fastReact/complaintList/Form/registerAction/RegisterActionDone.jsx
new file mode 100644
index 0000000..567832d
--- /dev/null
+++ b/src/components/dashboard/fastReact/complaintList/Form/registerAction/RegisterActionDone.jsx
@@ -0,0 +1,86 @@
+import { Controller, useWatch } from "react-hook-form";
+import { Grid, Stack, TextField } from "@mui/material";
+import ImageUpload from "./ImageUpload";
+import MapInfoOneMarker from "@/core/components/MapInfoOneMarker";
+
+const RegisterActionDone = ({ control, setValue, errors }) => {
+ const StartPoint = useWatch({ control, name: "start_point" });
+ return (
+
+
+
+
+ {
+ return (
+
+ );
+ }}
+ />
+
+
+
+
+
+
+ (
+
+ )}
+ />
+
+
+ (
+
+ )}
+ />
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+export default RegisterActionDone;
diff --git a/src/components/dashboard/fastReact/complaintList/Form/registerAction/RegisterActionUndone.jsx b/src/components/dashboard/fastReact/complaintList/Form/registerAction/RegisterActionUndone.jsx
new file mode 100644
index 0000000..dd2b43b
--- /dev/null
+++ b/src/components/dashboard/fastReact/complaintList/Form/registerAction/RegisterActionUndone.jsx
@@ -0,0 +1,37 @@
+import { Grid, Stack, TextField } from "@mui/material";
+import { Controller } from "react-hook-form";
+
+const RegisterActionUndone = ({ control }) => {
+ return (
+
+
+
+
+ {
+ return (
+
+ );
+ }}
+ />
+
+
+
+
+ );
+};
+export default RegisterActionUndone;
diff --git a/src/components/dashboard/fastReact/complaintList/Form/registerAction/index.jsx b/src/components/dashboard/fastReact/complaintList/Form/registerAction/index.jsx
new file mode 100644
index 0000000..7362f4b
--- /dev/null
+++ b/src/components/dashboard/fastReact/complaintList/Form/registerAction/index.jsx
@@ -0,0 +1,66 @@
+import { Dialog, DialogTitle, IconButton, Tooltip } from "@mui/material";
+import { useState } from "react";
+import RegisterActionContent from "./RegisterActionContent";
+import AppRegistrationIcon from "@mui/icons-material/AppRegistration";
+import useRequest from "@/lib/hooks/useRequest";
+import { REGISTER_COMPLAINTS_LIST } from "@/core/utils/routes";
+const RegisterAction = ({ rowId, mutate }) => {
+ const requestServer = useRequest({ notificationSuccess: true });
+ const [openRegisterActionDialog, setOpenRegisterActionDialog] = useState(false);
+ const defaultValues = {
+ rms_status: "1",
+ description: "",
+ start_point: "",
+ image_before_1: null,
+ image_after_1: null,
+ };
+ const onBaseSubmit = async (result) => {
+ console.log(result);
+ const formData = new FormData();
+ const rmsLatLng = `${result.start_point.lat},${result.start_point.lng}`;
+ if (result.rms_status === "1") {
+ formData.append("rms_description", result.description);
+ formData.append("rms_status", 1);
+ formData.append("start_point", rmsLatLng);
+ formData.append("image_before", result.image_before_1);
+ formData.append("image_after", result.image_after_1);
+ } else {
+ formData.append("rms_description", result.description);
+ formData.append("rms_status", 2);
+ }
+ await requestServer(`${REGISTER_COMPLAINTS_LIST}/${rowId}`, "post", {
+ data: formData,
+ })
+ .then((res) => {
+ mutate();
+ setOpenRegisterActionDialog(false);
+ })
+ .catch(() => {});
+ };
+ return (
+ <>
+
+ setOpenRegisterActionDialog(true)}>
+
+
+
+
+ >
+ );
+};
+export default RegisterAction;
diff --git a/src/components/dashboard/fastReact/complaintList/RowActions/DescriptionDialog/index.jsx b/src/components/dashboard/fastReact/complaintList/RowActions/DescriptionDialog/index.jsx
new file mode 100644
index 0000000..4d80ac4
--- /dev/null
+++ b/src/components/dashboard/fastReact/complaintList/RowActions/DescriptionDialog/index.jsx
@@ -0,0 +1,50 @@
+import { Dialog, DialogContent, DialogTitle, IconButton, Tooltip, Typography, Card, CardContent } from "@mui/material";
+import { useState } from "react";
+import RemoveRedEyeIcon from "@mui/icons-material/RemoveRedEye";
+import CloseIcon from "@mui/icons-material/Close";
+
+const DescriptionForm = ({ description, title, icon: IconComponent = RemoveRedEyeIcon }) => {
+ const [openOfficerDescriptionDialog, setOpenOfficerDescriptionDialog] = useState(false);
+
+ return (
+ <>
+
+ setOpenOfficerDescriptionDialog(true)}>
+
+
+
+
+ >
+ );
+};
+export default DescriptionForm;
diff --git a/src/components/dashboard/fastReact/complaintList/RowActions/LocationDialog/LocationFormContent.jsx b/src/components/dashboard/fastReact/complaintList/RowActions/LocationDialog/LocationFormContent.jsx
new file mode 100644
index 0000000..94f091d
--- /dev/null
+++ b/src/components/dashboard/fastReact/complaintList/RowActions/LocationDialog/LocationFormContent.jsx
@@ -0,0 +1,30 @@
+"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 }) => {
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+};
+export default LocationFormContent;
diff --git a/src/components/dashboard/fastReact/complaintList/RowActions/LocationDialog/index.jsx b/src/components/dashboard/fastReact/complaintList/RowActions/LocationDialog/index.jsx
new file mode 100644
index 0000000..09df6ad
--- /dev/null
+++ b/src/components/dashboard/fastReact/complaintList/RowActions/LocationDialog/index.jsx
@@ -0,0 +1,36 @@
+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 }) => {
+ const [openLocationDialog, setOpenLocationDialog] = useState(false);
+
+ return (
+ <>
+
+ setOpenLocationDialog(true)}>
+
+
+
+
+ >
+ );
+};
+export default LocationForm;
diff --git a/src/components/dashboard/fastReact/complaintList/RowActions/Refer/EdarateShahriField.jsx b/src/components/dashboard/fastReact/complaintList/RowActions/Refer/EdarateShahriField.jsx
new file mode 100644
index 0000000..f44d8e1
--- /dev/null
+++ b/src/components/dashboard/fastReact/complaintList/RowActions/Refer/EdarateShahriField.jsx
@@ -0,0 +1,70 @@
+"use client";
+import useEdaratLists from "@/lib/hooks/useEdaratLists";
+import { FormControl, FormHelperText, InputLabel, MenuItem, OutlinedInput, Select } from "@mui/material";
+import { useEffect, useMemo, useState } from "react";
+import { useWatch } from "react-hook-form";
+
+const EdarateShahriField = ({ control, field, fieldState: { error } }) => {
+ const dependencyField = useWatch({ control, name: "province_id" });
+ return (
+
+ );
+};
+
+const EdarehShahriController = ({ value, onChange, dependencyField, error }) => {
+ const { edaratList, loadingEdaratList, errorEdaratList } = useEdaratLists(dependencyField);
+ const [prevDependency, setPrevDependency] = useState(dependencyField);
+
+ const columnSelectOption = useMemo(() => {
+ if (dependencyField === "") {
+ return [{ value: "empty", label: "ابتدا استان را انتخاب کنید" }];
+ }
+ if (loadingEdaratList) {
+ return [{ value: "loading", label: "در حال بارگذاری..." }];
+ }
+ if (errorEdaratList) {
+ return [{ value: "error", label: "خطا در بارگذاری" }];
+ }
+ return edaratList.map((edare) => ({
+ value: edare.id,
+ label: edare.name_fa,
+ }));
+ }, [edaratList, loadingEdaratList, errorEdaratList]);
+
+ useEffect(() => {
+ if (prevDependency === dependencyField) return;
+ onChange("");
+ setPrevDependency(dependencyField);
+ }, [dependencyField]);
+
+ return (
+
+
+ اداره
+
+ }
+ size="small"
+ onChange={(e) => onChange(e.target.value)}
+ displayEmpty
+ >
+ {columnSelectOption.map((option) => (
+
+ ))}
+
+ {error && {error.message}}
+
+ );
+};
+export default EdarateShahriField;
diff --git a/src/components/dashboard/fastReact/complaintList/RowActions/Refer/ProvinceField.jsx b/src/components/dashboard/fastReact/complaintList/RowActions/Refer/ProvinceField.jsx
new file mode 100644
index 0000000..455339e
--- /dev/null
+++ b/src/components/dashboard/fastReact/complaintList/RowActions/Refer/ProvinceField.jsx
@@ -0,0 +1,33 @@
+import useProvinces from "@/lib/hooks/useProvince";
+import { FormControl, FormHelperText, InputLabel, MenuItem, Select } from "@mui/material";
+
+const ProvinceField = ({ field, fieldState: { error } }) => {
+ const { provinces, loadingProvinces, errorProvinces } = useProvinces();
+ return (
+
+ استان
+
+ {error && {error.message}}
+
+ );
+};
+export default ProvinceField;
diff --git a/src/components/dashboard/fastReact/complaintList/RowActions/Refer/ReferContent.jsx b/src/components/dashboard/fastReact/complaintList/RowActions/Refer/ReferContent.jsx
new file mode 100644
index 0000000..59774e6
--- /dev/null
+++ b/src/components/dashboard/fastReact/complaintList/RowActions/Refer/ReferContent.jsx
@@ -0,0 +1,101 @@
+import { REFER_FAST_REACT } from "@/core/utils/routes";
+import useRequest from "@/lib/hooks/useRequest";
+import { yupResolver } from "@hookform/resolvers/yup";
+import { Button, DialogActions, DialogContent, Stack, TextField } from "@mui/material";
+import { Controller, useForm } from "react-hook-form";
+import * as Yup from "yup";
+import EdarateShahriField from "./EdarateShahriField";
+import ProvinceField from "./ProvinceField";
+
+const ReferContent = ({ rowId, mutate, setOpenReferDialog }) => {
+ const requestServer = useRequest({ notificationSuccess: true });
+
+ const validationSchema = Yup.object().shape({
+ province_id: Yup.string().required("لطفاً استان را وارد کنید."),
+ edarat_shahri_id: Yup.string().required("لطفاً اداره را وارد کنید."),
+ description: Yup.string().required("لطفاً توضیحات را وارد کنید."),
+ });
+
+ const {
+ control,
+ register,
+ handleSubmit,
+ formState: { errors, isSubmitting },
+ } = useForm({
+ defaultValues: {
+ province_id: "",
+ edarat_shahri_id: "",
+ description: "",
+ },
+ resolver: yupResolver(validationSchema),
+ mode: "onBlur",
+ });
+
+ const onSubmit = async (data) => {
+ const formData = new FormData();
+ formData.append("province_id", data.province_id);
+ formData.append("edarate_shahri_id", data.edarat_shahri_id);
+ formData.append("refer_description", data.description);
+ await requestServer(`${REFER_FAST_REACT}/${rowId}`, "post", {
+ data: formData,
+ })
+ .then(() => {
+ mutate();
+ setOpenReferDialog(false);
+ })
+ .catch(() => {});
+ };
+
+ return (
+ <>
+
+
+ {
+ return ;
+ }}
+ />
+ {
+ return ;
+ }}
+ />
+
+
+
+
+
+
+
+
+
+ >
+ );
+};
+export default ReferContent;
diff --git a/src/components/dashboard/fastReact/complaintList/RowActions/Refer/index.jsx b/src/components/dashboard/fastReact/complaintList/RowActions/Refer/index.jsx
new file mode 100644
index 0000000..8dc9875
--- /dev/null
+++ b/src/components/dashboard/fastReact/complaintList/RowActions/Refer/index.jsx
@@ -0,0 +1,33 @@
+import SettingsBackupRestoreIcon from "@mui/icons-material/SettingsBackupRestore";
+import { Dialog, DialogTitle, IconButton, Tooltip } from "@mui/material";
+import { useState } from "react";
+import ReferContent from "./ReferContent";
+
+const Refer = ({ rowId, mutate }) => {
+ const [openReferDialog, setOpenReferDialog] = useState(false);
+ return (
+ <>
+
+ setOpenReferDialog(true)}>
+
+
+
+
+ >
+ );
+};
+export default Refer;
diff --git a/src/components/dashboard/fastReact/complaintList/RowActions/ReferList/ReferListContent.jsx b/src/components/dashboard/fastReact/complaintList/RowActions/ReferList/ReferListContent.jsx
new file mode 100644
index 0000000..31a1e4c
--- /dev/null
+++ b/src/components/dashboard/fastReact/complaintList/RowActions/ReferList/ReferListContent.jsx
@@ -0,0 +1,81 @@
+import DialogLoading from "@/core/components/DialogLoading";
+import { GET_FAST_REACT_REFER_LIST } from "@/core/utils/routes";
+import useRequest from "@/lib/hooks/useRequest";
+import {
+ DialogContent,
+ Table,
+ TableBody,
+ TableCell,
+ TableContainer,
+ TableHead,
+ TableRow,
+ Typography,
+} from "@mui/material";
+import moment from "jalali-moment";
+import { useEffect, useState } from "react";
+
+const ReferListContent = ({ rowId }) => {
+ const [loading, setLoading] = useState(true);
+ const [data, setData] = useState(null);
+ const request = useRequest();
+
+ useEffect(() => {
+ const fetchData = async () => {
+ try {
+ setLoading(true);
+ const response = await request(`${GET_FAST_REACT_REFER_LIST}/${rowId}`);
+ setData(response.data.data);
+ } catch (error) {
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ fetchData();
+ }, [rowId]);
+
+ return (
+ <>
+
+ {loading ? (
+
+ ) : data.length !== 0 ? (
+
+
+
+
+ کاربر
+ از اداره
+ از اداره
+ تاریخ
+ توضیحات
+
+
+
+ {data.map((refer, index) => {
+ return (
+
+ {refer.user}
+ {`${refer.from_province} | ${refer.from_edareh}`}
+ {`${refer.to_province} | ${refer.to_edareh}`}
+
+ {moment(refer.created_at).locale("fa").format("HH:mm | yyyy/MM/DD")}
+
+ {refer.description}
+
+ );
+ })}
+
+
+
+ ) : (
+ ارجاعی در سامانه یافت نشد
+ )}
+
+ >
+ );
+};
+export default ReferListContent;
diff --git a/src/components/dashboard/fastReact/complaintList/RowActions/ReferList/index.jsx b/src/components/dashboard/fastReact/complaintList/RowActions/ReferList/index.jsx
new file mode 100644
index 0000000..619bd98
--- /dev/null
+++ b/src/components/dashboard/fastReact/complaintList/RowActions/ReferList/index.jsx
@@ -0,0 +1,43 @@
+import { Button, Dialog, DialogActions, DialogTitle, IconButton, Tooltip } from "@mui/material";
+import ListAltIcon from "@mui/icons-material/ListAlt";
+import ReferListContent from "./ReferListContent";
+import { useState } from "react";
+
+const ReferList = ({ rowId }) => {
+ const [openReferListDialog, setOpenReferListDialog] = useState(false);
+ return (
+ <>
+
+ setOpenReferListDialog(true)}>
+
+
+
+
+ >
+ );
+};
+export default ReferList;
diff --git a/src/components/dashboard/fastReact/complaintList/RowActions/index.jsx b/src/components/dashboard/fastReact/complaintList/RowActions/index.jsx
new file mode 100644
index 0000000..e9972aa
--- /dev/null
+++ b/src/components/dashboard/fastReact/complaintList/RowActions/index.jsx
@@ -0,0 +1,20 @@
+import { Box } from "@mui/material";
+import RegisterAction from "../Form/registerAction";
+import ReferList from "./ReferList";
+import Refer from "./Refer";
+import { usePermissions } from "@/lib/hooks/usePermissions";
+
+const RowActions = ({ row, mutate }) => {
+ const { data: userPermissions } = usePermissions();
+ const hasActionPermission = userPermissions.includes("show-fast-react-edarate-shahri")
+ return (
+
+ {hasActionPermission && (
+
+ )}
+
+
+
+ );
+};
+export default RowActions;
diff --git a/src/components/dashboard/fastReact/complaintList/Toolbar.jsx b/src/components/dashboard/fastReact/complaintList/Toolbar.jsx
new file mode 100644
index 0000000..22d8bd3
--- /dev/null
+++ b/src/components/dashboard/fastReact/complaintList/Toolbar.jsx
@@ -0,0 +1,11 @@
+import { Stack } from "@mui/material";
+import PrintExcel from "./ExcelPrint";
+
+const Toolbar = ({ table, filterData, mutate }) => {
+ return (
+
+
+
+ );
+};
+export default Toolbar;
diff --git a/src/components/dashboard/fastReact/complaintList/index.jsx b/src/components/dashboard/fastReact/complaintList/index.jsx
new file mode 100644
index 0000000..5c3edc6
--- /dev/null
+++ b/src/components/dashboard/fastReact/complaintList/index.jsx
@@ -0,0 +1,36 @@
+import { Button, IconButton, useMediaQuery } from "@mui/material";
+import ChecklistIcon from "@mui/icons-material/Checklist";
+import { useTheme } from "@emotion/react";
+import { useState } from "react";
+import ComplaintListTable from "./ComplaintListTable";
+
+const ComplaintList = ({ mutate }) => {
+ const theme = useTheme();
+ const isMobile = useMediaQuery(theme.breakpoints.down("sm"));
+ const [open, setOpen] = useState(false);
+
+ const handleOpen = () => {
+ setOpen(true);
+ };
+ return (
+ <>
+ {isMobile ? (
+
+
+
+ ) : (
+ }
+ onClick={handleOpen}
+ >
+ لیست شکایات
+
+ )}
+
+ >
+ );
+};
+export default ComplaintList;
diff --git a/src/components/dashboard/fastReact/operator/ExcelPrint/index.jsx b/src/components/dashboard/fastReact/operator/ExcelPrint/index.jsx
new file mode 100644
index 0000000..c1e2bea
--- /dev/null
+++ b/src/components/dashboard/fastReact/operator/ExcelPrint/index.jsx
@@ -0,0 +1,75 @@
+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_FAST_REACT_OPERATOR_LIST } from "@/core/utils/routes";
+import { useTheme } from "@emotion/react";
+import DataTableFilterDataStructure from "@/core/utils/DataTableFilterDataStructure";
+import isArrayEmpty from "@/core/utils/isArrayEmpty";
+
+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 && (!Array.isArray(filter.value) || filter.value.some((item) => item.trim() !== ""))) {
+ acc.push({ id: key, value: filter.value });
+ }
+ return acc;
+ }, []);
+
+ const filterParams = useMemo(() => {
+ const params = new URLSearchParams();
+ if (activeFilters.length > 0) {
+ activeFilters.map((filter, index) => {
+ const filteredFilterData = DataTableFilterDataStructure(filterData, isArrayEmpty);
+ params.set("filters", JSON.stringify(filteredFilterData.length === 0 ? [] : filteredFilterData));
+ });
+ } else {
+ params.set("filters", JSON.stringify([]));
+ }
+ return params;
+ }, [activeFilters]);
+
+ const clickHandler = () => {
+ setLoading(true);
+ requestServer(`${EXPORT_FAST_REACT_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/fastReact/operator/Form/EditForm/EditController.jsx b/src/components/dashboard/fastReact/operator/Form/EditForm/EditController.jsx
new file mode 100644
index 0000000..5025d54
--- /dev/null
+++ b/src/components/dashboard/fastReact/operator/Form/EditForm/EditController.jsx
@@ -0,0 +1,72 @@
+import useRequest from "@/lib/hooks/useRequest";
+import { useEffect, useState } from "react";
+import { GET_FAST_REACT_ITEM_DETAIL, UPDATE_FAST_REACT } from "@/core/utils/routes";
+import DialogLoading from "@/core/components/DialogLoading";
+import RegisterActionContent from "@/components/dashboard/fastReact/complaintList/Form/registerAction/RegisterActionContent";
+
+const EditController = ({ rowId, mutate, setOpenEditDialog }) => {
+ const requestServer = useRequest();
+ const [fastReactItemDetails, setFastReactItemDetails] = useState(null);
+ const [fastReactItemDetailsLoading, setFastReactItemDetailsLoading] = useState(false);
+
+ useEffect(() => {
+ setFastReactItemDetailsLoading(true);
+ requestServer(`${GET_FAST_REACT_ITEM_DETAIL}/${rowId}`, "get")
+ .then((response) => {
+ setFastReactItemDetails(response.data.data);
+ setFastReactItemDetailsLoading(false);
+ })
+ .catch((e) => {
+ setFastReactItemDetailsLoading(false);
+ });
+ }, [rowId]);
+ const startPoint = fastReactItemDetails?.rms_start_latlng
+ ? { lat: fastReactItemDetails?.rms_start_latlng[0], lng: fastReactItemDetails?.rms_start_latlng[1] }
+ : { lat: "", lng: "" };
+ const defaultData = {
+ rms_status: `${fastReactItemDetails?.rms_status || "1"}`,
+ description: fastReactItemDetails?.rms_description || "",
+ start_point: startPoint,
+ image_before_1: fastReactItemDetails?.image_before || null,
+ image_after_1: fastReactItemDetails?.image_after || null,
+ };
+ const HandleSubmit = async (result) => {
+ console.log("result", result);
+ const formData = new FormData();
+ const rmsLatLng = `${result.start_point.lat},${result.start_point.lng}`;
+ if (result.rms_status === "1") {
+ formData.append("rms_description", result.description);
+ formData.append("rms_status", 1);
+ formData.append("start_point", rmsLatLng);
+ result.image_before_1 !== defaultData.image_before_1 &&
+ formData.append("image_before", result.image_before_1);
+ result.image_after_1 !== defaultData.image_after_1 && formData.append("image_after", result.image_after_1);
+ } else {
+ formData.append("rms_description", result.description);
+ formData.append("rms_status", 2);
+ }
+
+ await requestServer(`${UPDATE_FAST_REACT}/${rowId}`, "post", {
+ data: formData,
+ })
+ .then(() => {
+ mutate();
+ setOpenEditDialog(false);
+ })
+ .catch(() => {});
+ };
+ return (
+ <>
+ {fastReactItemDetailsLoading ? (
+
+ ) : (
+
+ )}
+ >
+ );
+};
+export default EditController;
diff --git a/src/components/dashboard/fastReact/operator/Form/EditForm/index.jsx b/src/components/dashboard/fastReact/operator/Form/EditForm/index.jsx
new file mode 100644
index 0000000..0af8244
--- /dev/null
+++ b/src/components/dashboard/fastReact/operator/Form/EditForm/index.jsx
@@ -0,0 +1,58 @@
+import BorderColorIcon from "@mui/icons-material/BorderColor";
+import CloseIcon from "@mui/icons-material/Close";
+import { Dialog, IconButton, Tooltip } from "@mui/material";
+import { useState } from "react";
+import EditController from "./EditController";
+
+const EditForm = ({ row, mutate, rowId }) => {
+ const [openEditDialog, setOpenEditDialog] = useState(false);
+
+ return (
+ <>
+
+ {
+ setOpenEditDialog(true);
+ }}
+ >
+
+
+
+
+ >
+ );
+};
+export default EditForm;
diff --git a/src/components/dashboard/fastReact/operator/OperatorList.jsx b/src/components/dashboard/fastReact/operator/OperatorList.jsx
new file mode 100644
index 0000000..3ff010b
--- /dev/null
+++ b/src/components/dashboard/fastReact/operator/OperatorList.jsx
@@ -0,0 +1,323 @@
+import DataTableWithAuth from "@/core/components/DataTableWithAuth";
+import { GET_FAST_REACT_OPERATOR } from "@/core/utils/routes";
+import EngineeringIcon from "@mui/icons-material/Engineering";
+import ThreePIcon from "@mui/icons-material/ThreeP";
+import { Box, Stack, Typography } from "@mui/material";
+import moment from "jalali-moment";
+import { useMemo } from "react";
+import RowActions from "./RowActions";
+import DescriptionForm from "./RowActions/DescriptionDialog";
+import ImageDialog from "./RowActions/ImageDialog";
+import LocationForm from "./RowActions/LocationDialog";
+import Toolbar from "./Toolbar";
+
+const OperatorList = () => {
+ const statusOptions = [
+ { value: "", label: "همه وضعیت ها" },
+ { value: 0, label: "درحال بررسی" },
+ { value: 1, label: "تایید شده" },
+ { value: 2, label: "عدم تایید" },
+ ];
+
+ const columns = useMemo(
+ () => [
+ {
+ accessorKey: "id",
+ header: "کد یکتا",
+ id: "road_observeds__id",
+ enableColumnFilter: true,
+ datatype: "text",
+ filterMode: "equals",
+ sortDescFirst: false,
+ columnFilterModeOptions: ["equals", "contains"],
+ grow: false,
+ size: 100,
+ },
+ {
+ header: "اطلاعات ثبت شده در سامانه سوانح",
+ id: "fkInfo",
+ enableColumnFilter: false,
+ enableSorting: false,
+ grow: false,
+ size: 50,
+ columns: [
+ {
+ accessorKey: "fk_RegisteredEventMessage",
+ header: "کد سوانح",
+ id: "fk_RegisteredEventMessage",
+ enableColumnFilter: true,
+ datatype: "text",
+ filterMode: "equals",
+ sortDescFirst: false,
+ columnFilterModeOptions: ["equals", "contains"],
+ grow: false,
+ size: 100,
+ },
+ {
+ accessorKey: "Title",
+ header: "موضوع گزارش",
+ id: "Title",
+ enableColumnFilter: false,
+ datatype: "text",
+ filterMode: "equals",
+ sortDescFirst: false,
+ columnFilterModeOptions: ["equals", "contains"],
+ grow: false,
+ size: 100,
+ },
+ {
+ accessorKey: "FeatureTypeTitle",
+ header: "نوع گزارش",
+ id: "FeatureTypeTitle",
+ enableColumnFilter: false,
+ datatype: "text",
+ filterMode: "equals",
+ sortDescFirst: false,
+ columnFilterModeOptions: ["equals", "contains"],
+ grow: false,
+ size: 100,
+ },
+ {
+ accessorKey: "Description",
+ header: "توضیح گزارش",
+ id: "Description",
+ enableColumnFilter: false,
+ datatype: "text",
+ filterMode: "equals",
+ sortDescFirst: false,
+ columnFilterModeOptions: ["equals", "contains"],
+ grow: false,
+ size: 100,
+ muiTableBodyCellProps: {
+ sx: {
+ borderLeft: "1px solid #e1e1e1",
+ py: 0,
+ "&:first-of-type": {
+ borderLeft: "unset",
+ },
+ },
+ },
+ Cell: ({ renderedCellValue }) => {
+ if (renderedCellValue) {
+ return (
+
+
+
+ );
+ }
+ return بدون توضیحات;
+ },
+ },
+ {
+ header: "موقعیت",
+ id: "location",
+ enableColumnFilter: false,
+ enableSorting: false,
+ datatype: "array",
+ grow: false,
+ size: 100,
+ muiTableBodyCellProps: {
+ sx: {
+ borderLeft: "1px solid #e1e1e1",
+ py: 0,
+ "&:first-of-type": {
+ borderLeft: "unset",
+ },
+ },
+ },
+ Cell: ({ row }) => {
+ if (!row.original.rms_start_latlng) return <>بدون موقعیت>;
+ return (
+
+
+
+ );
+ },
+ },
+ {
+ header: "تصاویر",
+ id: "images",
+ enableColumnFilter: false,
+ enableSorting: false,
+ datatype: "array",
+ grow: false,
+ size: 100,
+ muiTableBodyCellProps: {
+ sx: {
+ borderLeft: "1px solid #e1e1e1",
+ py: 0,
+ "&:first-of-type": {
+ borderLeft: "unset",
+ },
+ },
+ },
+ Cell: ({ row }) => {
+ if (!row.original?.image_before && !row.original?.image_after) return <>بدون تصویر>;
+ return (
+
+ {row.original?.image_before && row.original?.image_after && (
+
+ )}
+
+ );
+ },
+ },
+ {
+ accessorKey: "MobileForSendEventSms",
+ header: "شماره تماس گیرنده",
+ id: "MobileForSendEventSms",
+ enableColumnFilter: false,
+ datatype: "text",
+ filterMode: "equals",
+ sortDescFirst: false,
+ columnFilterModeOptions: ["equals", "contains"],
+ grow: false,
+ size: 100,
+ },
+ {
+ accessorFn: (row) => moment(row.StartTime_DateTime, "M/D/YYYY h:mm:ss A").locale("fa").format("HH:mm | yyyy/MM/DD"),
+ header: "تاریخ ثبت",
+ id: "StartTime_DateTime",
+ enableColumnFilter: false,
+ datatype: "date",
+ filterMode: "between",
+ grow: false,
+ size: 100,
+ },
+ ]
+ },
+ {
+ accessorFn: (row) => {
+ return row.rms_last_activity
+ ? moment(row?.rms_last_activity).locale("fa").format("HH:mm | yyyy/MM/DD")
+ : "بدون تاریخ";
+ },
+ header: "تاریخ اقدام",
+ id: "rms_last_activity",
+ enableColumnFilter: true,
+ datatype: "date",
+ filterMode: "between",
+ grow: false,
+ size: 100,
+ },
+ {
+ accessorKey: "rms_description",
+ header: "توضیح مامور",
+ id: "rms_description",
+ enableColumnFilter: false,
+ datatype: "text",
+ filterMode: "equals",
+ sortDescFirst: false,
+ columnFilterModeOptions: ["equals", "contains"],
+ grow: false,
+ size: 100,
+ muiTableBodyCellProps: {
+ sx: {
+ borderLeft: "1px solid #e1e1e1",
+ py: 0,
+ "&:first-of-type": {
+ borderLeft: "unset",
+ },
+ },
+ },
+ Cell: ({ renderedCellValue }) => {
+ if (renderedCellValue) {
+ return (
+
+
+
+ );
+ }
+ return بدون توضیحات;
+ },
+ },
+ {
+ accessorKey: "supervisor_description",
+ header: "توضیح کارشناس",
+ id: "supervisor_description",
+ enableColumnFilter: false,
+ datatype: "text",
+ filterMode: "equals",
+ sortDescFirst: false,
+ columnFilterModeOptions: ["equals", "contains"],
+ grow: false,
+ size: 100,
+ muiTableBodyCellProps: {
+ sx: {
+ borderLeft: "1px solid #e1e1e1",
+ py: 0,
+ "&:first-of-type": {
+ borderLeft: "unset",
+ },
+ },
+ },
+ Cell: ({ renderedCellValue }) => {
+ if (renderedCellValue) {
+ return (
+
+
+
+ );
+ }
+ return بدون توضیحات;
+ },
+ },
+ {
+ accessorKey: "status",
+ header: "وضعیت",
+ id: "status",
+ enableColumnFilter: true,
+ datatype: "numeric",
+ filterMode: "equals",
+ sortDescFirst: true,
+ grow: false,
+ size: 100,
+ columnSelectOption: () => {
+ return statusOptions.map((status) => ({
+ value: status.value,
+ label: status.label,
+ }));
+ },
+ Cell: ({ row }) => <>{row.original.status_fa}>,
+ },
+ ],
+ []
+ );
+
+ return (
+ <>
+
+
+
+ >
+ );
+};
+export default OperatorList;
diff --git a/src/components/dashboard/fastReact/operator/RowActions/DescriptionDialog/index.jsx b/src/components/dashboard/fastReact/operator/RowActions/DescriptionDialog/index.jsx
new file mode 100644
index 0000000..4d80ac4
--- /dev/null
+++ b/src/components/dashboard/fastReact/operator/RowActions/DescriptionDialog/index.jsx
@@ -0,0 +1,50 @@
+import { Dialog, DialogContent, DialogTitle, IconButton, Tooltip, Typography, Card, CardContent } from "@mui/material";
+import { useState } from "react";
+import RemoveRedEyeIcon from "@mui/icons-material/RemoveRedEye";
+import CloseIcon from "@mui/icons-material/Close";
+
+const DescriptionForm = ({ description, title, icon: IconComponent = RemoveRedEyeIcon }) => {
+ const [openOfficerDescriptionDialog, setOpenOfficerDescriptionDialog] = useState(false);
+
+ return (
+ <>
+
+ setOpenOfficerDescriptionDialog(true)}>
+
+
+
+
+ >
+ );
+};
+export default DescriptionForm;
diff --git a/src/components/dashboard/fastReact/operator/RowActions/ImageDialog/ImageFormContent.jsx b/src/components/dashboard/fastReact/operator/RowActions/ImageDialog/ImageFormContent.jsx
new file mode 100644
index 0000000..c8f22d0
--- /dev/null
+++ b/src/components/dashboard/fastReact/operator/RowActions/ImageDialog/ImageFormContent.jsx
@@ -0,0 +1,45 @@
+import { Box, Stack, Typography, useTheme } from "@mui/material";
+import Image from "next/image";
+
+const ImageFormContent = ({ image, title }) => {
+ const theme = useTheme();
+ return (
+
+
+ {title}
+
+
+
+
+
+ );
+};
+export default ImageFormContent;
diff --git a/src/components/dashboard/fastReact/operator/RowActions/ImageDialog/index.jsx b/src/components/dashboard/fastReact/operator/RowActions/ImageDialog/index.jsx
new file mode 100644
index 0000000..de3b76a
--- /dev/null
+++ b/src/components/dashboard/fastReact/operator/RowActions/ImageDialog/index.jsx
@@ -0,0 +1,46 @@
+import PhotoLibraryIcon from "@mui/icons-material/PhotoLibrary";
+import { Button, Dialog, DialogActions, DialogContent, DialogTitle, IconButton, Tooltip } from "@mui/material";
+import { useState } from "react";
+import ImageFormContent from "./ImageFormContent";
+
+const ImageDialog = ({ image_before, image_after }) => {
+ const [openImageDialog, setOpenImageDialog] = useState(false);
+
+ return (
+ <>
+
+ setOpenImageDialog(true)}>
+
+
+
+
+ >
+ );
+};
+export default ImageDialog;
diff --git a/src/components/dashboard/fastReact/operator/RowActions/LocationDialog/LocationFormContent.jsx b/src/components/dashboard/fastReact/operator/RowActions/LocationDialog/LocationFormContent.jsx
new file mode 100644
index 0000000..94f091d
--- /dev/null
+++ b/src/components/dashboard/fastReact/operator/RowActions/LocationDialog/LocationFormContent.jsx
@@ -0,0 +1,30 @@
+"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 }) => {
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+};
+export default LocationFormContent;
diff --git a/src/components/dashboard/fastReact/operator/RowActions/LocationDialog/index.jsx b/src/components/dashboard/fastReact/operator/RowActions/LocationDialog/index.jsx
new file mode 100644
index 0000000..09df6ad
--- /dev/null
+++ b/src/components/dashboard/fastReact/operator/RowActions/LocationDialog/index.jsx
@@ -0,0 +1,36 @@
+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 }) => {
+ const [openLocationDialog, setOpenLocationDialog] = useState(false);
+
+ return (
+ <>
+
+ setOpenLocationDialog(true)}>
+
+
+
+
+ >
+ );
+};
+export default LocationForm;
diff --git a/src/components/dashboard/fastReact/operator/RowActions/index.jsx b/src/components/dashboard/fastReact/operator/RowActions/index.jsx
new file mode 100644
index 0000000..5b632d6
--- /dev/null
+++ b/src/components/dashboard/fastReact/operator/RowActions/index.jsx
@@ -0,0 +1,13 @@
+import { Box } from "@mui/material";
+import EditForm from "../Form/EditForm";
+
+const RowActions = ({ row, mutate }) => {
+ return (
+
+ {row.original.status === 2 && (
+
+ )}
+
+ );
+};
+export default RowActions;
diff --git a/src/components/dashboard/fastReact/operator/Toolbar.jsx b/src/components/dashboard/fastReact/operator/Toolbar.jsx
new file mode 100644
index 0000000..479ae4e
--- /dev/null
+++ b/src/components/dashboard/fastReact/operator/Toolbar.jsx
@@ -0,0 +1,13 @@
+import PrintExcel from "./ExcelPrint";
+import ComplaintList from "../complaintList";
+import { Stack } from "@mui/material";
+
+const Toolbar = ({ table, filterData, mutate }) => {
+ return (
+
+
+
+
+ );
+};
+export default Toolbar;
diff --git a/src/components/dashboard/fastReact/operator/index.jsx b/src/components/dashboard/fastReact/operator/index.jsx
new file mode 100644
index 0000000..a2b328c
--- /dev/null
+++ b/src/components/dashboard/fastReact/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/fastReact/supervisor/ExcelPrint/index.jsx b/src/components/dashboard/fastReact/supervisor/ExcelPrint/index.jsx
new file mode 100644
index 0000000..120a8f0
--- /dev/null
+++ b/src/components/dashboard/fastReact/supervisor/ExcelPrint/index.jsx
@@ -0,0 +1,75 @@
+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_FAST_REACT_SUPERVISOR_LIST } from "@/core/utils/routes";
+import { useTheme } from "@emotion/react";
+import DataTableFilterDataStructure from "@/core/utils/DataTableFilterDataStructure";
+import isArrayEmpty from "@/core/utils/isArrayEmpty";
+
+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 && (!Array.isArray(filter.value) || filter.value.some((item) => item.trim() !== ""))) {
+ acc.push({ id: key, value: filter.value });
+ }
+ return acc;
+ }, []);
+
+ const filterParams = useMemo(() => {
+ const params = new URLSearchParams();
+ if (activeFilters.length > 0) {
+ activeFilters.map((filter, index) => {
+ const filteredFilterData = DataTableFilterDataStructure(filterData, isArrayEmpty);
+ params.set("filters", JSON.stringify(filteredFilterData.length === 0 ? [] : filteredFilterData));
+ });
+ } else {
+ params.set("filters", JSON.stringify([]));
+ }
+ return params;
+ }, [activeFilters]);
+
+ const clickHandler = () => {
+ setLoading(true);
+ requestServer(`${EXPORT_FAST_REACT_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/fastReact/supervisor/RowActions/ConfirmDialog/ConfirmContent.jsx b/src/components/dashboard/fastReact/supervisor/RowActions/ConfirmDialog/ConfirmContent.jsx
new file mode 100644
index 0000000..5944db3
--- /dev/null
+++ b/src/components/dashboard/fastReact/supervisor/RowActions/ConfirmDialog/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_FAST_REACT_SUPERVISOR } from "@/core/utils/routes";
+
+const ConfirmContent = ({ rowId, mutate, setOpenConfirmDialog }) => {
+ const requestServer = useRequest({ notificationSuccess: true });
+
+ const {
+ register,
+ handleSubmit,
+ formState: { isSubmitting },
+ } = 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_FAST_REACT_SUPERVISOR}/${rowId}`, "post", {
+ data: formData,
+ })
+ .then(() => {
+ mutate();
+ setOpenConfirmDialog(false);
+ })
+ .catch(() => {});
+ };
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+};
+export default ConfirmContent;
diff --git a/src/components/dashboard/fastReact/supervisor/RowActions/ConfirmDialog/index.jsx b/src/components/dashboard/fastReact/supervisor/RowActions/ConfirmDialog/index.jsx
new file mode 100644
index 0000000..10e6048
--- /dev/null
+++ b/src/components/dashboard/fastReact/supervisor/RowActions/ConfirmDialog/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/fastReact/supervisor/RowActions/DescriptionDialog/index.jsx b/src/components/dashboard/fastReact/supervisor/RowActions/DescriptionDialog/index.jsx
new file mode 100644
index 0000000..4d80ac4
--- /dev/null
+++ b/src/components/dashboard/fastReact/supervisor/RowActions/DescriptionDialog/index.jsx
@@ -0,0 +1,50 @@
+import { Dialog, DialogContent, DialogTitle, IconButton, Tooltip, Typography, Card, CardContent } from "@mui/material";
+import { useState } from "react";
+import RemoveRedEyeIcon from "@mui/icons-material/RemoveRedEye";
+import CloseIcon from "@mui/icons-material/Close";
+
+const DescriptionForm = ({ description, title, icon: IconComponent = RemoveRedEyeIcon }) => {
+ const [openOfficerDescriptionDialog, setOpenOfficerDescriptionDialog] = useState(false);
+
+ return (
+ <>
+
+ setOpenOfficerDescriptionDialog(true)}>
+
+
+
+
+ >
+ );
+};
+export default DescriptionForm;
diff --git a/src/components/dashboard/fastReact/supervisor/RowActions/ImageDialog/ImageFormContent.jsx b/src/components/dashboard/fastReact/supervisor/RowActions/ImageDialog/ImageFormContent.jsx
new file mode 100644
index 0000000..c8f22d0
--- /dev/null
+++ b/src/components/dashboard/fastReact/supervisor/RowActions/ImageDialog/ImageFormContent.jsx
@@ -0,0 +1,45 @@
+import { Box, Stack, Typography, useTheme } from "@mui/material";
+import Image from "next/image";
+
+const ImageFormContent = ({ image, title }) => {
+ const theme = useTheme();
+ return (
+
+
+ {title}
+
+
+
+
+
+ );
+};
+export default ImageFormContent;
diff --git a/src/components/dashboard/fastReact/supervisor/RowActions/ImageDialog/index.jsx b/src/components/dashboard/fastReact/supervisor/RowActions/ImageDialog/index.jsx
new file mode 100644
index 0000000..de3b76a
--- /dev/null
+++ b/src/components/dashboard/fastReact/supervisor/RowActions/ImageDialog/index.jsx
@@ -0,0 +1,46 @@
+import PhotoLibraryIcon from "@mui/icons-material/PhotoLibrary";
+import { Button, Dialog, DialogActions, DialogContent, DialogTitle, IconButton, Tooltip } from "@mui/material";
+import { useState } from "react";
+import ImageFormContent from "./ImageFormContent";
+
+const ImageDialog = ({ image_before, image_after }) => {
+ const [openImageDialog, setOpenImageDialog] = useState(false);
+
+ return (
+ <>
+
+ setOpenImageDialog(true)}>
+
+
+
+
+ >
+ );
+};
+export default ImageDialog;
diff --git a/src/components/dashboard/fastReact/supervisor/RowActions/LocationDialog/LocationFormContent.jsx b/src/components/dashboard/fastReact/supervisor/RowActions/LocationDialog/LocationFormContent.jsx
new file mode 100644
index 0000000..94f091d
--- /dev/null
+++ b/src/components/dashboard/fastReact/supervisor/RowActions/LocationDialog/LocationFormContent.jsx
@@ -0,0 +1,30 @@
+"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 }) => {
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+};
+export default LocationFormContent;
diff --git a/src/components/dashboard/fastReact/supervisor/RowActions/LocationDialog/index.jsx b/src/components/dashboard/fastReact/supervisor/RowActions/LocationDialog/index.jsx
new file mode 100644
index 0000000..09df6ad
--- /dev/null
+++ b/src/components/dashboard/fastReact/supervisor/RowActions/LocationDialog/index.jsx
@@ -0,0 +1,36 @@
+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 }) => {
+ const [openLocationDialog, setOpenLocationDialog] = useState(false);
+
+ return (
+ <>
+
+ setOpenLocationDialog(true)}>
+
+
+
+
+ >
+ );
+};
+export default LocationForm;
diff --git a/src/components/dashboard/fastReact/supervisor/RowActions/RejectDialog/RejectFormContent.jsx b/src/components/dashboard/fastReact/supervisor/RowActions/RejectDialog/RejectFormContent.jsx
new file mode 100644
index 0000000..5c51886
--- /dev/null
+++ b/src/components/dashboard/fastReact/supervisor/RowActions/RejectDialog/RejectFormContent.jsx
@@ -0,0 +1,83 @@
+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_FAST_REACT_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_FAST_REACT_SUPERVISOR}/${rowId}`, "post", {
+ data: formData,
+ })
+ .then(() => {
+ mutate();
+ setOpenRejectDialog(false);
+ })
+ .catch(() => {})
+ .finally(() => {
+ reset();
+ });
+ };
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+};
+export default RejectContent;
diff --git a/src/components/dashboard/fastReact/supervisor/RowActions/RejectDialog/index.jsx b/src/components/dashboard/fastReact/supervisor/RowActions/RejectDialog/index.jsx
new file mode 100644
index 0000000..5d754a5
--- /dev/null
+++ b/src/components/dashboard/fastReact/supervisor/RowActions/RejectDialog/index.jsx
@@ -0,0 +1,29 @@
+import { Dialog, DialogTitle, IconButton, Tooltip } from "@mui/material";
+import { useState } from "react";
+import ClearIcon from "@mui/icons-material/Clear";
+import RejectContent from "./RejectFormContent";
+const RejectForm = ({ rowId, mutate }) => {
+ const [openRejectDialog, setOpenRejectDialog] = useState(false);
+ return (
+ <>
+
+ setOpenRejectDialog(true)}>
+
+
+
+
+ >
+ );
+};
+export default RejectForm;
diff --git a/src/components/dashboard/fastReact/supervisor/RowActions/RestoreForm/RestoreContent.jsx b/src/components/dashboard/fastReact/supervisor/RowActions/RestoreForm/RestoreContent.jsx
new file mode 100644
index 0000000..06c86a6
--- /dev/null
+++ b/src/components/dashboard/fastReact/supervisor/RowActions/RestoreForm/RestoreContent.jsx
@@ -0,0 +1,42 @@
+import { RESTORE_FAST_REACT } from "@/core/utils/routes";
+import useRequest from "@/lib/hooks/useRequest";
+import { Button, DialogActions, DialogContent, Stack, Typography } from "@mui/material";
+import { useState } from "react";
+
+const RestoreContent = ({ rowId, mutate, setOpenRestoreDialog }) => {
+ const [submitting, setSubmitting] = useState(false);
+ const requestServer = useRequest({ notificationSuccess: true });
+ const handleClick = () => {
+ setSubmitting(true);
+ requestServer(`${RESTORE_FAST_REACT}/${rowId}`, "post")
+ .then(() => {
+ mutate();
+ setOpenRestoreDialog(false);
+ setSubmitting(false);
+ })
+ .catch(() => {
+ setSubmitting(false);
+ });
+ };
+ return (
+ <>
+
+
+
+ آیا از بازگردانی فعالیت اطمینان دارید؟
+
+
+
+
+
+
+
+ >
+ );
+};
+
+export default RestoreContent;
diff --git a/src/components/dashboard/fastReact/supervisor/RowActions/RestoreForm/index.jsx b/src/components/dashboard/fastReact/supervisor/RowActions/RestoreForm/index.jsx
new file mode 100644
index 0000000..2632c20
--- /dev/null
+++ b/src/components/dashboard/fastReact/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/fastReact/supervisor/RowActions/index.jsx b/src/components/dashboard/fastReact/supervisor/RowActions/index.jsx
new file mode 100644
index 0000000..f9947ab
--- /dev/null
+++ b/src/components/dashboard/fastReact/supervisor/RowActions/index.jsx
@@ -0,0 +1,25 @@
+import { Box } from "@mui/material";
+import ConfirmForm from "./ConfirmDialog";
+import RejectForm from "./RejectDialog";
+import { usePermissions } from "@/lib/hooks/usePermissions";
+import RestoreForm from "./RestoreForm";
+
+const RowActions = ({ row, mutate }) => {
+ const { data: userPermissions } = usePermissions();
+ const hasSupervisePermission = userPermissions.some((item) =>
+ ["supervise-fast-react", "supervise-fast-react-province"].includes(item)
+ );
+ const hasRestorePermission = userPermissions.includes("restore-fast-react");
+ return (
+
+ {hasSupervisePermission && row.original?.status === 0 && (
+
+ )}
+ {hasSupervisePermission && row.original?.status === 0 && (
+
+ )}
+ {hasRestorePermission && row.original?.status !== 0 && }
+
+ );
+};
+export default RowActions;
diff --git a/src/components/dashboard/fastReact/supervisor/SupervisorList.jsx b/src/components/dashboard/fastReact/supervisor/SupervisorList.jsx
new file mode 100644
index 0000000..89eb53c
--- /dev/null
+++ b/src/components/dashboard/fastReact/supervisor/SupervisorList.jsx
@@ -0,0 +1,423 @@
+import { useEffect, useMemo, useState } from "react";
+import { Box, Stack, Typography } from "@mui/material";
+import DataTableWithAuth from "@/core/components/DataTableWithAuth";
+import Toolbar from "./Toolbar";
+import moment from "jalali-moment";
+import RowActions from "./RowActions";
+import { GET_FAST_REACT_SUPERVISOR } from "@/core/utils/routes";
+import useProvinces from "@/lib/hooks/useProvince";
+import CustomSelectByDependency from "@/core/components/DataTable/filter/fieldsType/CustomSelectByDependency";
+import { usePermissions } from "@/lib/hooks/usePermissions";
+import { useAuth } from "@/lib/contexts/auth";
+import useEdaratLists from "@/lib/hooks/useEdaratLists";
+import DescriptionForm from "./RowActions/DescriptionDialog";
+import LocationForm from "./RowActions/LocationDialog";
+import EngineeringIcon from "@mui/icons-material/Engineering";
+import ThreePIcon from "@mui/icons-material/ThreeP";
+import ImageDialog from "./RowActions/ImageDialog";
+
+const SupervisorList = () => {
+ const { data: userPermissions } = usePermissions();
+ const hasCountryPermission = userPermissions?.includes("show-fast-react");
+ const { user } = useAuth();
+ const statusOptions = [
+ { value: "", label: "همه وضعیت ها" },
+ { value: 0, label: "درحال بررسی" },
+ { value: 1, label: "تایید شده" },
+ { value: 2, label: "عدم تایید" },
+ ];
+
+ const columns = useMemo(() => {
+ const dynamicColumns = {
+ header: "استان",
+ id: "road_observeds__province_id",
+ enableColumnFilter: true,
+ datatype: "numeric",
+ filterMode: "equals",
+ grow: false,
+ size: 130,
+ ColumnSelectComponent: (props) => {
+ const { provinces, errorProvinces, loadingProvinces } = useProvinces();
+ const getColumnSelectOptions = useMemo(() => {
+ if (loadingProvinces) {
+ return [{ value: "loading", label: "در حال بارگذاری..." }];
+ }
+ if (errorProvinces) {
+ return [{ value: "error", label: "خطا در بارگذاری" }];
+ }
+ return [
+ { value: "", label: "کل کشور" },
+ ...provinces.map((province) => ({
+ value: province.id,
+ label: province.name_fa,
+ })),
+ ];
+ }, [provinces, errorProvinces, loadingProvinces]);
+ return (
+
+ );
+ },
+ Cell: ({ row }) => <>{row.original.province_fa}>,
+ };
+ return [
+ {
+ accessorKey: "id",
+ header: "کد یکتا",
+ id: "road_observeds__id",
+ enableColumnFilter: true,
+ datatype: "text",
+ filterMode: "equals",
+ sortDescFirst: false,
+ columnFilterModeOptions: ["equals", "contains"],
+ grow: false,
+ size: 100,
+ },
+ ...(hasCountryPermission ? [dynamicColumns] : []),
+ {
+ header: "اداره",
+ id: "edarate_shahri_id",
+ enableColumnFilter: true,
+ datatype: "numeric",
+ filterMode: "equals",
+ dependencyId: hasCountryPermission ? "road_observeds__province_id" : null,
+ grow: false,
+ size: 120,
+ ColumnSelectComponent: (props) => {
+ const { edaratList, loadingEdaratList, errorEdaratList } = useEdaratLists(
+ hasCountryPermission ? props.dependencyFieldValue.value : user.province_id
+ );
+ const [prevDependency, setPrevDependency] = useState(
+ hasCountryPermission ? props.dependencyFieldValue.value : user.province_id
+ );
+
+ const getColumnSelectOptions = useMemo(() => {
+ if (hasCountryPermission && props.dependencyFieldValue.value === "") {
+ return [{ value: "empty", label: "ابتدا استان را انتخاب کنید" }];
+ }
+ if (loadingEdaratList) {
+ return [{ value: "loading", label: "در حال بارگذاری..." }];
+ }
+ if (errorEdaratList) {
+ return [{ value: "error", label: "خطا در بارگذاری" }];
+ }
+ return [
+ { value: "", label: "کل ادارات" },
+ ...edaratList.map((edare) => ({
+ value: edare.id,
+ label: edare.name_fa,
+ })),
+ ];
+ }, [edaratList, loadingEdaratList, errorEdaratList]);
+ useEffect(() => {
+ if (hasCountryPermission) return;
+ if (prevDependency === props.dependencyFieldValue?.value) return;
+ props.handleChange({ ...props.filterParameters, value: "" });
+ setPrevDependency(props.dependencyFieldValue?.value);
+ }, [props.dependencyFieldValue?.value, hasCountryPermission]);
+ return (
+
+ );
+ },
+ Cell: ({ renderedCellValue, row }) => <>{row.original.name_fa}>,
+ },
+ {
+ header: "اطلاعات ثبت شده در سامانه سوانح",
+ id: "fkInfo",
+ enableColumnFilter: false,
+ enableSorting: false,
+ grow: false,
+ size: 50,
+ columns: [
+ {
+ accessorKey: "fk_RegisteredEventMessage",
+ header: "کد سوانح",
+ id: "fk_RegisteredEventMessage",
+ enableColumnFilter: true,
+ datatype: "text",
+ filterMode: "equals",
+ sortDescFirst: false,
+ columnFilterModeOptions: ["equals", "contains"],
+ grow: false,
+ size: 100,
+ },
+ {
+ accessorKey: "Title",
+ header: "موضوع گزارش",
+ id: "Title",
+ enableColumnFilter: false,
+ datatype: "text",
+ filterMode: "equals",
+ sortDescFirst: false,
+ columnFilterModeOptions: ["equals", "contains"],
+ grow: false,
+ size: 100,
+ },
+ {
+ accessorKey: "FeatureTypeTitle",
+ header: "نوع گزارش",
+ id: "FeatureTypeTitle",
+ enableColumnFilter: false,
+ datatype: "text",
+ filterMode: "equals",
+ sortDescFirst: false,
+ columnFilterModeOptions: ["equals", "contains"],
+ grow: false,
+ size: 100,
+ },
+ {
+ accessorKey: "Description",
+ header: "توضیح گزارش",
+ id: "Description",
+ enableColumnFilter: false,
+ datatype: "text",
+ filterMode: "equals",
+ sortDescFirst: false,
+ columnFilterModeOptions: ["equals", "contains"],
+ grow: false,
+ size: 100,
+ muiTableBodyCellProps: {
+ sx: {
+ borderLeft: "1px solid #e1e1e1",
+ py: 0,
+ "&:first-of-type": {
+ borderLeft: "unset",
+ },
+ },
+ },
+ Cell: ({ renderedCellValue }) => {
+ if (renderedCellValue) {
+ return (
+
+
+
+ );
+ }
+ return بدون توضیحات;
+ },
+ },
+ {
+ header: "موقعیت",
+ id: "location",
+ enableColumnFilter: false,
+ enableSorting: false,
+ datatype: "array",
+ grow: false,
+ size: 100,
+ muiTableBodyCellProps: {
+ sx: {
+ borderLeft: "1px solid #e1e1e1",
+ py: 0,
+ "&:first-of-type": {
+ borderLeft: "unset",
+ },
+ },
+ },
+ Cell: ({ row }) => {
+ if (!row.original.rms_start_latlng) return <>بدون موقعیت>;
+ return (
+
+
+
+ );
+ },
+ },
+ {
+ header: "تصاویر",
+ id: "images",
+ enableColumnFilter: false,
+ enableSorting: false,
+ datatype: "array",
+ grow: false,
+ size: 100,
+ muiTableBodyCellProps: {
+ sx: {
+ borderLeft: "1px solid #e1e1e1",
+ py: 0,
+ "&:first-of-type": {
+ borderLeft: "unset",
+ },
+ },
+ },
+ Cell: ({ row }) => {
+ if (!row.original?.image_before && !row.original?.image_after) return <>بدون تصویر>;
+ return (
+
+ {row.original?.image_before && row.original?.image_after && (
+
+ )}
+
+ );
+ },
+ },
+ {
+ accessorKey: "MobileForSendEventSms",
+ header: "شماره تماس گیرنده",
+ id: "MobileForSendEventSms",
+ enableColumnFilter: false,
+ datatype: "text",
+ filterMode: "equals",
+ sortDescFirst: false,
+ columnFilterModeOptions: ["equals", "contains"],
+ grow: false,
+ size: 100,
+ },
+ {
+ accessorFn: (row) => moment(row.StartTime_DateTime, "M/D/YYYY h:mm:ss A").locale("fa").format("HH:mm | yyyy/MM/DD"),
+ header: "تاریخ ثبت",
+ id: "StartTime_DateTime",
+ enableColumnFilter: false,
+ datatype: "date",
+ filterMode: "between",
+ grow: false,
+ size: 100,
+ },
+ ]
+ },
+ {
+ accessorFn: (row) => {
+ return row.rms_last_activity
+ ? moment(row?.rms_last_activity).locale("fa").format("HH:mm | yyyy/MM/DD")
+ : "بدون تاریخ";
+ },
+ header: "تاریخ اقدام",
+ id: "rms_last_activity",
+ enableColumnFilter: true,
+ datatype: "date",
+ filterMode: "between",
+ grow: false,
+ size: 100,
+ },
+ {
+ accessorKey: "rms_description",
+ header: "توضیح مامور",
+ id: "rms_description",
+ enableColumnFilter: false,
+ datatype: "text",
+ filterMode: "equals",
+ sortDescFirst: false,
+ columnFilterModeOptions: ["equals", "contains"],
+ grow: false,
+ size: 100,
+ muiTableBodyCellProps: {
+ sx: {
+ borderLeft: "1px solid #e1e1e1",
+ py: 0,
+ "&:first-of-type": {
+ borderLeft: "unset",
+ },
+ },
+ },
+ Cell: ({ renderedCellValue }) => {
+ if (renderedCellValue) {
+ return (
+
+
+
+ );
+ }
+ return بدون توضیحات;
+ },
+ },
+ {
+ accessorKey: "supervisor_description",
+ header: "توضیح کارشناس",
+ id: "supervisor_description",
+ enableColumnFilter: false,
+ datatype: "text",
+ filterMode: "equals",
+ sortDescFirst: false,
+ columnFilterModeOptions: ["equals", "contains"],
+ grow: false,
+ size: 100,
+ muiTableBodyCellProps: {
+ sx: {
+ borderLeft: "1px solid #e1e1e1",
+ py: 0,
+ "&:first-of-type": {
+ borderLeft: "unset",
+ },
+ },
+ },
+ Cell: ({ renderedCellValue }) => {
+ if (renderedCellValue) {
+ return (
+
+
+
+ );
+ }
+ return بدون توضیحات;
+ },
+ },
+ {
+ accessorKey: "status",
+ header: "وضعیت",
+ id: "status",
+ enableColumnFilter: true,
+ datatype: "numeric",
+ filterMode: "equals",
+ sortDescFirst: true,
+ grow: false,
+ size: 100,
+ columnSelectOption: () => {
+ return statusOptions.map((status) => ({
+ value: status.value,
+ label: status.label,
+ }));
+ },
+ Cell: ({ row }) => <>{row.original.status_fa}>,
+ },
+ ];
+ }, []);
+
+ return (
+ <>
+
+
+
+ >
+ );
+};
+export default SupervisorList;
diff --git a/src/components/dashboard/fastReact/supervisor/Toolbar.jsx b/src/components/dashboard/fastReact/supervisor/Toolbar.jsx
new file mode 100644
index 0000000..479ae4e
--- /dev/null
+++ b/src/components/dashboard/fastReact/supervisor/Toolbar.jsx
@@ -0,0 +1,13 @@
+import PrintExcel from "./ExcelPrint";
+import ComplaintList from "../complaintList";
+import { Stack } from "@mui/material";
+
+const Toolbar = ({ table, filterData, mutate }) => {
+ return (
+
+
+
+
+ );
+};
+export default Toolbar;
diff --git a/src/components/dashboard/fastReact/supervisor/index.jsx b/src/components/dashboard/fastReact/supervisor/index.jsx
new file mode 100644
index 0000000..3fa490b
--- /dev/null
+++ b/src/components/dashboard/fastReact/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/inquiryPrivacy/provinceAdmin/zaminGov/RowActions/MapModify/PolygonForDesign.jsx b/src/components/dashboard/inquiryPrivacy/provinceAdmin/zaminGov/RowActions/MapModify/PolygonForDesign.jsx
index cd75f8d..4e1bb43 100644
--- a/src/components/dashboard/inquiryPrivacy/provinceAdmin/zaminGov/RowActions/MapModify/PolygonForDesign.jsx
+++ b/src/components/dashboard/inquiryPrivacy/provinceAdmin/zaminGov/RowActions/MapModify/PolygonForDesign.jsx
@@ -1,7 +1,5 @@
"use client";
-import "leaflet-draw/dist/leaflet.draw.css";
-import "@/assets/scss/map-styles.scss";
import { FeatureGroup, useMap } from "react-leaflet";
import { EditControl } from "react-leaflet-draw";
import { useEffect, useRef, useState } from "react";
diff --git a/src/components/dashboard/roadItems/operator/Actions/Create/Forms/CreateFormContent.jsx b/src/components/dashboard/roadItems/operator/Actions/Create/Forms/CreateFormContent.jsx
index cfe6c09..62bc6a8 100644
--- a/src/components/dashboard/roadItems/operator/Actions/Create/Forms/CreateFormContent.jsx
+++ b/src/components/dashboard/roadItems/operator/Actions/Create/Forms/CreateFormContent.jsx
@@ -249,7 +249,6 @@ const CreateFormContent = ({ setOpen, onSubmit, is_gasht = false }) => {
diff --git a/src/components/dashboard/roadItems/operator/Actions/Create/Forms/index.jsx b/src/components/dashboard/roadItems/operator/Actions/Create/Forms/index.jsx
index c9921bf..edcc098 100644
--- a/src/components/dashboard/roadItems/operator/Actions/Create/Forms/index.jsx
+++ b/src/components/dashboard/roadItems/operator/Actions/Create/Forms/index.jsx
@@ -1,10 +1,11 @@
"use client";
-import { Dialog } from "@mui/material";
+import { Dialog, IconButton } from "@mui/material";
import CreateFormContent from "@/components/dashboard/roadItems/operator/Actions/Create/Forms/CreateFormContent";
import { CREATE_ROAD_ITEMS } from "@/core/utils/routes";
import useRequest from "@/lib/hooks/useRequest";
+import CloseIcon from "@mui/icons-material/Close";
-const OperatorCreateForm = ({ open, setOpen, mutate, rowId }) => {
+const OperatorCreateForm = ({ open, setOpen, mutate }) => {
const requestServer = useRequest({ notificationSuccess: true });
const HandleSubmit = async ({ result }) => {
const formData = new FormData();
@@ -22,6 +23,19 @@ const OperatorCreateForm = ({ open, setOpen, mutate, rowId }) => {
};
return (
);
diff --git a/src/components/dashboard/roadItems/operator/Actions/ObservedGashtCreate/GashtCreate.jsx b/src/components/dashboard/roadItems/operator/Actions/ObservedGashtCreate/GashtCreate.jsx
index 968d907..4784f26 100644
--- a/src/components/dashboard/roadItems/operator/Actions/ObservedGashtCreate/GashtCreate.jsx
+++ b/src/components/dashboard/roadItems/operator/Actions/ObservedGashtCreate/GashtCreate.jsx
@@ -1,11 +1,25 @@
-import { Dialog } from "@mui/material";
+import { Dialog, IconButton } from "@mui/material";
import GashtCreateContent from "./GashtCreateContent";
import { useState } from "react";
+import CloseIcon from "@mui/icons-material/Close";
const GashtCreate = ({ open, setOpen, mutate, rowId }) => {
const [tabState, setTabState] = useState(0);
return (
+ />
+ ) : (
+
+ تمامی اطلاعات یافت نشد!
+
);
};
export default EditController;
diff --git a/src/components/dashboard/roadItems/operator/RowActions/EditForm/EditFormContent.jsx b/src/components/dashboard/roadItems/operator/RowActions/EditForm/EditFormContent.jsx
index d8b8dc3..c49d50d 100644
--- a/src/components/dashboard/roadItems/operator/RowActions/EditForm/EditFormContent.jsx
+++ b/src/components/dashboard/roadItems/operator/RowActions/EditForm/EditFormContent.jsx
@@ -1,7 +1,6 @@
-import { LinearProgress, Typography } from "@mui/material";
-import React from "react";
-import useRoadItemGetISubtems from "@/lib/hooks/useRoadItemGetISubtems";
import EditFormCreate from "@/components/dashboard/roadItems/operator/RowActions/EditForm/EditFormCreate";
+import useRoadItemGetISubtems from "@/lib/hooks/useRoadItemGetISubtems";
+import { LinearProgress, Typography } from "@mui/material";
const EditFormContent = ({ setOpenEditDialog, onSubmit, defaultData, is_gasht }) => {
const { subItemsList, loadingSubItemsList, errorSubItemsList } = useRoadItemGetISubtems(defaultData?.item_id);
diff --git a/src/components/dashboard/roadItems/operator/RowActions/EditForm/index.jsx b/src/components/dashboard/roadItems/operator/RowActions/EditForm/index.jsx
index 9302918..4548f15 100644
--- a/src/components/dashboard/roadItems/operator/RowActions/EditForm/index.jsx
+++ b/src/components/dashboard/roadItems/operator/RowActions/EditForm/index.jsx
@@ -2,6 +2,7 @@ import { Dialog, DialogTitle, IconButton, Tooltip } from "@mui/material";
import { useState } from "react";
import BorderColorIcon from "@mui/icons-material/BorderColor";
import EditController from "./EditController";
+import CloseIcon from "@mui/icons-material/Close";
const EditForm = ({ row, mutate, rowId }) => {
const [openEditDialog, setOpenEditDialog] = useState(false);
@@ -19,13 +20,40 @@ const EditForm = ({ row, mutate, rowId }) => {
-
+
+ ویرایش اطلاعات
+ setOpenEditDialog(false)}
+ sx={(theme) => ({
+ position: "absolute",
+ right: 8,
+ top: 8,
+ zIndex: 50,
+ color: theme.palette.grey[500],
+ })}
+ >
+
+
+ {openEditDialog && (
+
+ )}
+
>
);
};
diff --git a/src/components/dashboard/roadItems/operator/RowActions/ImageForm/ImageFormContent.jsx b/src/components/dashboard/roadItems/operator/RowActions/ImageForm/ImageFormContent.jsx
index 9aaa92e..c8f22d0 100644
--- a/src/components/dashboard/roadItems/operator/RowActions/ImageForm/ImageFormContent.jsx
+++ b/src/components/dashboard/roadItems/operator/RowActions/ImageForm/ImageFormContent.jsx
@@ -1,4 +1,4 @@
-import { Box, DialogContent, Paper, Stack, Typography, useTheme } from "@mui/material";
+import { Box, Stack, Typography, useTheme } from "@mui/material";
import Image from "next/image";
const ImageFormContent = ({ image, title }) => {
@@ -29,6 +29,7 @@ const ImageFormContent = ({ image, title }) => {
src={image}
alt="Image"
fill={true}
+ sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
loading="lazy"
style={{
objectFit: "contain",
diff --git a/src/components/dashboard/roadItems/operator/RowActions/ImageForm/ImagesContent.jsx b/src/components/dashboard/roadItems/operator/RowActions/ImageForm/ImagesContent.jsx
new file mode 100644
index 0000000..6414e24
--- /dev/null
+++ b/src/components/dashboard/roadItems/operator/RowActions/ImageForm/ImagesContent.jsx
@@ -0,0 +1,43 @@
+import { useEffect, useState } from "react";
+import ImageFormContent from "./ImageFormContent";
+import DialogLoading from "@/core/components/DialogLoading";
+import useRequest from "@/lib/hooks/useRequest";
+import { GET_IMAGES_ROAD_ITEM } from "@/core/utils/routes";
+import { Typography } from "@mui/material";
+
+const ImagesContent = ({ rowId }) => {
+ const [loading, setLoading] = useState(true);
+ const [data, setData] = useState(null);
+ const request = useRequest();
+
+ useEffect(() => {
+ const fetchData = async () => {
+ try {
+ setLoading(true);
+ const response = await request(`${GET_IMAGES_ROAD_ITEM}/${rowId}`);
+ setData(response.data.data);
+ } catch (error) {
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ fetchData();
+ }, [rowId]);
+
+ return (
+ <>
+ {loading ? (
+
+ ) : data ? (
+ <>
+
+
+ >
+ ) : (
+ تصویری در سامانه یافت نشد
+ )}
+ >
+ );
+};
+export default ImagesContent;
diff --git a/src/components/dashboard/roadItems/operator/RowActions/ImageForm/index.jsx b/src/components/dashboard/roadItems/operator/RowActions/ImageForm/index.jsx
index 130f443..08e9cd9 100644
--- a/src/components/dashboard/roadItems/operator/RowActions/ImageForm/index.jsx
+++ b/src/components/dashboard/roadItems/operator/RowActions/ImageForm/index.jsx
@@ -1,9 +1,9 @@
-import { Button, Dialog, DialogActions, DialogContent, DialogTitle, IconButton, Paper, Tooltip } from "@mui/material";
-import { useState } from "react";
import PhotoLibraryIcon from "@mui/icons-material/PhotoLibrary";
-import ImageFormContent from "./ImageFormContent";
+import { Button, Dialog, DialogActions, DialogContent, DialogTitle, IconButton, Tooltip } from "@mui/material";
+import { useState } from "react";
+import ImagesContent from "./ImagesContent";
-const ImageDialog = ({ images }) => {
+const ImageDialog = ({ rowId }) => {
const [openImageDialog, setOpenImageDialog] = useState(false);
return (
@@ -26,12 +26,7 @@ const ImageDialog = ({ images }) => {
scroll="paper"
>
تصاویر
-
-
-
-
-
-
+ {openImageDialog && }
+ )}
+ >
+ );
+};
+export default PrintExcel;
diff --git a/src/components/dashboard/roadItems/reports/SubItemsReports/Search/FromDateController.jsx b/src/components/dashboard/roadItems/reports/SubItemsReports/Search/FromDateController.jsx
index 2cfa8af..2e23def 100644
--- a/src/components/dashboard/roadItems/reports/SubItemsReports/Search/FromDateController.jsx
+++ b/src/components/dashboard/roadItems/reports/SubItemsReports/Search/FromDateController.jsx
@@ -13,6 +13,7 @@ const FromDateController = ({ control }) => {
name="from_date"
error={!!error}
value={value}
+ label={"تاریخ فعالیت (از تاریخ)"}
placeholder={"از تاریخ"}
setFieldValue={(name, newValue) => {
const formattedDate = moment(new Date(newValue)).locale("en").format("YYYY-MM-DD");
diff --git a/src/components/dashboard/roadItems/reports/SubItemsReports/Search/SelectItems.jsx b/src/components/dashboard/roadItems/reports/SubItemsReports/Search/SelectItems.jsx
index 57bcc86..db71a8a 100644
--- a/src/components/dashboard/roadItems/reports/SubItemsReports/Search/SelectItems.jsx
+++ b/src/components/dashboard/roadItems/reports/SubItemsReports/Search/SelectItems.jsx
@@ -12,13 +12,13 @@ const SelectProvince = ({ control }) => {
control={control}
render={({ field, fieldState: { error } }) => (
- ایتم
+ آیتم
+
+
+
+ {subItemsList?.needs_end_point === 1 ? (
+
+ ) : (
+
+ )}
+
+
+ );
+};
+export default GetObservedItemInfo;
diff --git a/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/ModalActionsDuringPatrol.jsx b/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/ModalActionsDuringPatrol.jsx
index 073fd9e..97be471 100644
--- a/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/ModalActionsDuringPatrol.jsx
+++ b/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/ModalActionsDuringPatrol.jsx
@@ -3,7 +3,7 @@ import { Dialog } from "@mui/material";
const ModalActionsDuringPatrol = ({ open, setOpen, setActionsList }) => {
const HandleSubmit = async (data) => {
- setActionsList((prev) => [...prev, data]);
+ setActionsList((prev) => [...prev, { ...data, instant_action: 1 }]);
setOpen(false);
};
diff --git a/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/ModalObservedItemPatrol.jsx b/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/ModalObservedItemPatrol.jsx
new file mode 100644
index 0000000..9a6c0fc
--- /dev/null
+++ b/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/ModalObservedItemPatrol.jsx
@@ -0,0 +1,16 @@
+import { Dialog } from "@mui/material";
+import CreateFormContentObservedItem from "./CreateFormContentObservedItem";
+
+const ModalObservedItemPatrol = ({ open, setOpen, setActionsList }) => {
+ const HandleSubmit = async (data) => {
+ setActionsList((prev) => [...prev, { ...data, instant_action: 0 }]);
+ setOpen(false);
+ };
+
+ return (
+
+
+
+ );
+};
+export default ModalObservedItemPatrol;
diff --git a/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/ObservedInfo.jsx b/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/ObservedInfo.jsx
new file mode 100644
index 0000000..16b9953
--- /dev/null
+++ b/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/ObservedInfo.jsx
@@ -0,0 +1,37 @@
+"use client";
+
+import DeleteIcon from "@mui/icons-material/Delete";
+import VisibilityIcon from "@mui/icons-material/Visibility";
+import { Box, Card, CardActions, CardHeader, Chip, IconButton, Typography } from "@mui/material";
+
+const ObservedInfo = ({ action, deleteAction }) => {
+ return (
+
+ }
+ title={
+
+ {action.data.item_name}
+
+ }
+ subheader={action.data.sub_item_name}
+ />
+
+
+
+
+
+
+
+
+ );
+};
+
+export default ObservedInfo;
diff --git a/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/PatrolDetailInfo.jsx b/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/PatrolDetailInfo.jsx
index 91cc822..14fd172 100644
--- a/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/PatrolDetailInfo.jsx
+++ b/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/PatrolDetailInfo.jsx
@@ -4,7 +4,7 @@ import MapLoading from "@/core/components/MapLayer/Loading";
import ShowPlak from "@/core/components/ShowPlak";
import { formatSecondsToHHMMSS } from "@/core/utils/formatSecondsToHHMMSS";
import AddRoadIcon from "@mui/icons-material/AddRoad";
-import BadgeIcon from '@mui/icons-material/Badge';
+import BadgeIcon from "@mui/icons-material/Badge";
import DirectionsCarFilledIcon from "@mui/icons-material/DirectionsCarFilled";
import ElectricCarIcon from "@mui/icons-material/ElectricCar";
import LocalGasStationIcon from "@mui/icons-material/LocalGasStation";
@@ -12,16 +12,7 @@ import QrCode2Icon from "@mui/icons-material/QrCode2";
import QueryBuilderIcon from "@mui/icons-material/QueryBuilder";
import ShareLocationIcon from "@mui/icons-material/ShareLocation";
import WatchLaterIcon from "@mui/icons-material/WatchLater";
-import {
- Box,
- Card,
- CardContent,
- Chip,
- Divider,
- Fade,
- Stack,
- Typography
-} from "@mui/material";
+import { Box, Card, CardContent, Chip, Divider, Fade, Stack, Typography } from "@mui/material";
import moment from "jalali-moment";
import dynamic from "next/dynamic";
import React, { useEffect } from "react";
diff --git a/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/PreviousStatesInfoInObservedItem.jsx b/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/PreviousStatesInfoInObservedItem.jsx
new file mode 100644
index 0000000..7476631
--- /dev/null
+++ b/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/PreviousStatesInfoInObservedItem.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 PreviousStatesInfoInObservedItem = ({ itemsList, subItemsList }) => {
+ return (
+
+
+
+ }
+ label={
+
+ آیتم انتخاب شده
+
+ }
+ />
+
+
+
+ {itemsList.name || "هیچ آیتمی انتخاب نشده است"}
+
+
+
+
+ }
+ label={
+
+ اقدام مشاهده شده
+
+ }
+ />
+
+
+
+ {subItemsList?.name || "هیچ اقدامی انتخاب نشده است"}
+
+
+
+ );
+};
+export default PreviousStatesInfoInObservedItem;
diff --git a/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/index.jsx b/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/index.jsx
index 7769896..c19d1e1 100644
--- a/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/index.jsx
+++ b/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/index.jsx
@@ -1,11 +1,25 @@
"use client";
-import { Dialog } from "@mui/material";
+import { Dialog, IconButton } from "@mui/material";
import PatrolForms from "./PatrolForms";
+import CloseIcon from "@mui/icons-material/Close";
const CreatePatrol = ({ open, setOpen, mutate }) => {
return (
+ setOpen(false)}
+ sx={(theme) => ({
+ position: "absolute",
+ right: 8,
+ top: 8,
+ zIndex: 50,
+ color: theme.palette.grey[500],
+ })}
+ >
+
+
{open && }
);
diff --git a/src/components/dashboard/roadPatrols/operator/OperatorList.jsx b/src/components/dashboard/roadPatrols/operator/OperatorList.jsx
index f9e144d..52bb4b4 100644
--- a/src/components/dashboard/roadPatrols/operator/OperatorList.jsx
+++ b/src/components/dashboard/roadPatrols/operator/OperatorList.jsx
@@ -7,9 +7,10 @@ import { GET_ROAD_PATROL_OPERATOR_LIST } from "@/core/utils/routes";
import moment from "jalali-moment";
import RowActions from "./RowActions";
import ReportForm from "./RowActions/ReportForm";
-import RahdaranDialog from "./RowActions/RahdaranForm";
+import RahdaranDialog from "./RowActions/RahdaranForm"
import MachinePerformanceForm from "./RowActions/MachinePerformanceForm";
+
const OperatorList = () => {
const columns = useMemo(
() => [
diff --git a/src/components/dashboard/roadPatrols/operator/RowActions/MachinePerformanceForm/MachinePerformanceContent.jsx b/src/components/dashboard/roadPatrols/operator/RowActions/MachinePerformanceForm/MachinePerformanceContent.jsx
index c16b10d..ead48ff 100644
--- a/src/components/dashboard/roadPatrols/operator/RowActions/MachinePerformanceForm/MachinePerformanceContent.jsx
+++ b/src/components/dashboard/roadPatrols/operator/RowActions/MachinePerformanceForm/MachinePerformanceContent.jsx
@@ -2,11 +2,11 @@ import { Box, Chip, DialogContent, Divider, Grid, Stack, Typography } from "@mui
import React, { useEffect, useState, useRef } from "react";
import { Marker, useMap } from "react-leaflet";
import AzmayeshActiveIcon from "@/assets/images/examine_marker_active.png";
-import StopsInfo from "@/components/dashboard/roadPatrols/operator/RowActions/MachinePerformanceForm/StopsInfo";
import MapLayer from "@/core/components/MapLayer";
import PinDropIcon from "@mui/icons-material/PinDrop";
import AccessAlarmsIcon from "@mui/icons-material/AccessAlarms";
import { formatSecondsToHHMMSS } from "@/core/utils/formatSecondsToHHMMSS";
+import StopsInfo from "./StopsInfo";
const defaultIconSize = [35, 35];
const MachinePerformanceContent = ({ machinesLists }) => {
const [selectedPoint, setSelectedPoint] = useState(null);
@@ -44,7 +44,7 @@ const MachinePerformanceContent = ({ machinesLists }) => {
return (
<>
-
+
{
-
+
{
+ const [loading, setLoading] = useState(true);
+ const [data, setData] = useState(null);
+ const request = useRequest();
+
+ useEffect(() => {
+ const fetchData = async () => {
+ try {
+ setLoading(true);
+ const response = await request(`${GET_CMMS_MACHINE_ROAD_PATROL}/${row.getValue('id')}`);
+ setData(response.data.data);
+ } catch (error) {
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ fetchData();
+ }, []);
+
+ return (
+ <>
+
+ {loading ? (
+
+ ) : data && row.original?.stop_points?.length > 0 ? (
+
+ ) : (
+ عملکرد خودرویی در سامانه یافت نشد
+ )}
+
+ >
+ )
+}
+export default MachinePerformanceFetch
\ No newline at end of file
diff --git a/src/components/dashboard/roadPatrols/operator/RowActions/MachinePerformanceForm/StopsInfo.jsx b/src/components/dashboard/roadPatrols/operator/RowActions/MachinePerformanceForm/StopsInfo.jsx
index 115a79d..d8e44cb 100644
--- a/src/components/dashboard/roadPatrols/operator/RowActions/MachinePerformanceForm/StopsInfo.jsx
+++ b/src/components/dashboard/roadPatrols/operator/RowActions/MachinePerformanceForm/StopsInfo.jsx
@@ -5,13 +5,18 @@ import LocalGasStationIcon from "@mui/icons-material/LocalGasStation";
import QrCode2Icon from "@mui/icons-material/QrCode2";
import AddRoadIcon from "@mui/icons-material/AddRoad";
import DirectionsCarFilledIcon from "@mui/icons-material/DirectionsCarFilled";
-import BadgeIcon from '@mui/icons-material/Badge';
+import BadgeIcon from "@mui/icons-material/Badge";
import { formatSecondsToHHMMSS } from "@/core/utils/formatSecondsToHHMMSS";
import ShowPlak from "@/core/components/ShowPlak";
const StopsInfo = ({ staticData }) => {
return (
-
+
{
const [openMachinesCodeDialog, setOpenMachinesCodeDialog] = useState(false);
- const machineData = {
- cmms_machine_code: row.original?.cmms_machines[0],
- resultCode: 0,
- mileage: row.original?.distance,
- accOnDuration: row.original?.vehicle_runtime,
- fuelConsumption: row.original?.fuel_consumption,
- stopPoints: row.original?.stop_points,
- };
return (
<>
@@ -33,7 +25,9 @@ const MachinePerformanceForm = ({ row }) => {
maxWidth={"md"}
>
عملکرد خودرو
-
+ {openMachinesCodeDialog && (
+
+ )}
setOpenMachinesCodeDialog(false)}
diff --git a/src/components/dashboard/roadPatrols/operator/Toolbar.jsx b/src/components/dashboard/roadPatrols/operator/Toolbar.jsx
index 9df8491..d10e83d 100644
--- a/src/components/dashboard/roadPatrols/operator/Toolbar.jsx
+++ b/src/components/dashboard/roadPatrols/operator/Toolbar.jsx
@@ -6,7 +6,7 @@ const Toolbar = ({ table, filterData, mutate }) => {
return (
- {/**/}
+
);
};
diff --git a/src/components/dashboard/roadPatrols/reports/ExcelPrint/index.jsx b/src/components/dashboard/roadPatrols/reports/ExcelPrint/index.jsx
index 414a9cb..192d4fb 100644
--- a/src/components/dashboard/roadPatrols/reports/ExcelPrint/index.jsx
+++ b/src/components/dashboard/roadPatrols/reports/ExcelPrint/index.jsx
@@ -1,16 +1,21 @@
-import { Button, CircularProgress } from "@mui/material";
+"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 { useTheme } from "@emotion/react";
+import { EXPORT_COUNTRY_ROAD_PATROL_REPORTS, EXPORT_PROVINCE_ROAD_PATROL_REPORTS } from "@/core/utils/routes";
const PrintExcel = ({ table, filterData }) => {
+ const theme = useTheme();
+ const isMobile = useMediaQuery(theme.breakpoints.down("sm"));
const [loading, setLoading] = useState(false);
- const requestServer = useRequest();
+ const requestServer = useRequest({ notificationSuccess: true });
const activeFilters = Object.entries(filterData).reduce((acc, [key, filter]) => {
- if (filter) {
- acc.push({ id: key, value: filter });
+ if (filter.value) {
+ acc.push({ id: filter.id, value: filter.value });
}
return acc;
}, []);
@@ -25,31 +30,43 @@ const PrintExcel = ({ table, filterData }) => {
}, [activeFilters]);
const clickHandler = () => {
- // setLoading(true);
- // 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(() => {})
- // .finally(() => {
- // setLoading(false);
- // });
+ setLoading(true);
+ const CountryOrProvince = activeFilters.find((item) => item.id === "province_id");
+ const requestUrl =
+ CountryOrProvince.value === "-1" ? EXPORT_COUNTRY_ROAD_PATROL_REPORTS : EXPORT_PROVINCE_ROAD_PATROL_REPORTS;
+ requestServer(`${requestUrl}?${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 (
- : }
- onClick={clickHandler}
- >
- خروجی اکسل
-
+ <>
+ {isMobile ? (
+
+
+
+ ) : (
+ : }
+ onClick={clickHandler}
+ >
+ خروجی اکسل
+
+ )}
+ >
);
};
export default PrintExcel;
diff --git a/src/components/dashboard/roadPatrols/reports/ReportLists.jsx b/src/components/dashboard/roadPatrols/reports/ReportLists.jsx
index 4a3d8e5..76f6cef 100644
--- a/src/components/dashboard/roadPatrols/reports/ReportLists.jsx
+++ b/src/components/dashboard/roadPatrols/reports/ReportLists.jsx
@@ -4,7 +4,7 @@ import { Box } from "@mui/material";
import { useMemo } from "react";
import Toolbar from "./Toolbar";
-const ReportLists = ({ data }) => {
+const ReportLists = ({ data, specialFilter }) => {
const columns = useMemo(() => {
return [
{
@@ -195,12 +195,12 @@ const ReportLists = ({ data }) => {
return (
);
diff --git a/src/components/dashboard/roadPatrols/reports/Search/FromDateController.jsx b/src/components/dashboard/roadPatrols/reports/Search/FromDateController.jsx
index 2cfa8af..73b095f 100644
--- a/src/components/dashboard/roadPatrols/reports/Search/FromDateController.jsx
+++ b/src/components/dashboard/roadPatrols/reports/Search/FromDateController.jsx
@@ -13,6 +13,7 @@ const FromDateController = ({ control }) => {
name="from_date"
error={!!error}
value={value}
+ label={"تاریخ شروع (از تاریخ)"}
placeholder={"از تاریخ"}
setFieldValue={(name, newValue) => {
const formattedDate = moment(new Date(newValue)).locale("en").format("YYYY-MM-DD");
diff --git a/src/components/dashboard/roadPatrols/reports/Search/ToDateController.jsx b/src/components/dashboard/roadPatrols/reports/Search/ToDateController.jsx
index 003ac20..d102c13 100644
--- a/src/components/dashboard/roadPatrols/reports/Search/ToDateController.jsx
+++ b/src/components/dashboard/roadPatrols/reports/Search/ToDateController.jsx
@@ -15,6 +15,7 @@ const ToDateController = ({ control }) => {
minDate={minDate}
error={!!error}
value={value}
+ label={"تاریخ شروع (تا تاریخ)"}
placeholder={"تا تاریخ"}
setFieldValue={(name, newValue) => {
const formattedDate = moment(new Date(newValue)).locale("en").format("YYYY-MM-DD");
diff --git a/src/components/dashboard/roadPatrols/reports/Toolbar.jsx b/src/components/dashboard/roadPatrols/reports/Toolbar.jsx
index 5dc6e42..7dd930f 100644
--- a/src/components/dashboard/roadPatrols/reports/Toolbar.jsx
+++ b/src/components/dashboard/roadPatrols/reports/Toolbar.jsx
@@ -2,6 +2,10 @@ import PrintExcel from "./ExcelPrint";
import { Box } from "@mui/material";
const Toolbar = ({ table, filterData, mutate }) => {
- return {/**/};
+ return (
+
+
+
+ );
};
export default Toolbar;
diff --git a/src/components/dashboard/roadPatrols/reports/index.jsx b/src/components/dashboard/roadPatrols/reports/index.jsx
index 12743aa..ae2ec32 100644
--- a/src/components/dashboard/roadPatrols/reports/index.jsx
+++ b/src/components/dashboard/roadPatrols/reports/index.jsx
@@ -18,18 +18,29 @@ const ReportPage = () => {
const { data: userPermissions } = usePermissions();
const { user } = useAuth();
const requestServer = useRequest();
- const hasProvincesPermission = userPermissions.includes("show-road-patrol-supervise-cartable");
+ const hasProvincesPermission = userPermissions?.includes("show-road-patrol-supervise-cartable");
const defaultValues = {
from_date: moment(new Date()).format("YYYY-MM-DD"),
date_to: moment(new Date()).format("YYYY-MM-DD"),
province_id: hasProvincesPermission ? "-1" : user.province_id,
};
+ const defaultFilter = [
+ { value: `${defaultValues.province_id}`, datatype: "numeric", id: "province_id", fn: "equals" },
+ { value: `${defaultValues.from_date}`, datatype: "date", id: "from_date", fn: "between" },
+ { value: `${defaultValues.date_to}`, datatype: "date", id: "date_to", fn: "between" },
+ ];
+ const [specialFilter, setSpecialFilter] = useState(defaultFilter);
const onSearchSubmit = async (data) => {
const params = new URLSearchParams();
params.set("from_date", `${data.from_date}`);
params.set("date_to", `${data.date_to}`);
+ setSpecialFilter([
+ { value: `${data.province_id}`, datatype: "numeric", id: "province_id", fn: "equals" },
+ { value: `${data.from_date}`, datatype: "date", id: "from_date", fn: "between" },
+ { value: `${data.date_to}`, datatype: "date", id: "date_to", fn: "between" },
+ ]);
if (data.province_id === "-1") {
try {
const response = await requestServer(`${GET_PROVINCE_ACTIVITY_REPORT}?${params}`);
@@ -84,8 +95,9 @@ const ReportPage = () => {
};
useEffect(() => {
+ if (!userPermissions) return;
onSearchSubmit(defaultValues);
- }, []);
+ }, [userPermissions]);
const validationSchema = yup.object().shape({
from_date: yup
@@ -108,14 +120,14 @@ const ReportPage = () => {
});
return (
-
+
- {!data ? : }
+ {!data ? : }
);
};
diff --git a/src/components/dashboard/roadPatrols/supervisor/ExcelPrint/index.jsx b/src/components/dashboard/roadPatrols/supervisor/ExcelPrint/index.jsx
index e5ba95a..ba176bb 100644
--- a/src/components/dashboard/roadPatrols/supervisor/ExcelPrint/index.jsx
+++ b/src/components/dashboard/roadPatrols/supervisor/ExcelPrint/index.jsx
@@ -1,17 +1,21 @@
-import { Button, CircularProgress } from "@mui/material";
+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_PATROL_SUPERVISOR_LIST } from "@/core/utils/routes";
+import DataTableFilterDataStructure from "@/core/utils/DataTableFilterDataStructure";
+import isArrayEmpty from "@/core/utils/isArrayEmpty";
+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();
const activeFilters = Object.entries(filterData).reduce((acc, [key, filter]) => {
- if (filter.value) {
- // Check if there's an active filter
+ if (filter.value && (!Array.isArray(filter.value) || filter.value.some((item) => item.trim() !== ""))) {
acc.push({ id: key, value: filter.value });
}
return acc;
@@ -19,10 +23,14 @@ const PrintExcel = ({ table, filterData }) => {
const filterParams = useMemo(() => {
const params = new URLSearchParams();
- activeFilters.length > 0 &&
+ if (activeFilters.length > 0) {
activeFilters.map((filter, index) => {
- params.set(`${filter.id}`, filter.value);
+ const filteredFilterData = DataTableFilterDataStructure(filterData, isArrayEmpty);
+ params.set("filters", JSON.stringify(filteredFilterData.length === 0 ? [] : filteredFilterData));
});
+ } else {
+ params.set("filters", JSON.stringify([]));
+ }
return params;
}, [activeFilters]);
@@ -32,7 +40,7 @@ const PrintExcel = ({ table, filterData }) => {
requestOptions: { responseType: "blob" },
})
.then((response) => {
- const filename = `خروجی کارتابل ارزیابی تاریخ_${moment().format("jYYYY_jMM_jDD")}.xlsx`;
+ const filename = `خروجی کارتابل ارزیابی گشت راهداری تاریخ_${moment().format("jYYYY_jMM_jDD")}.xlsx`;
FileSaver.saveAs(response.data, filename);
})
.catch(() => {})
@@ -42,17 +50,25 @@ const PrintExcel = ({ table, filterData }) => {
};
return (
- : }
- onClick={clickHandler}
- >
- خروجی اکسل
-
+ <>
+ {isMobile ? (
+
+
+
+ ) : (
+ : }
+ onClick={clickHandler}
+ >
+ خروجی اکسل
+
+ )}
+ >
);
};
diff --git a/src/components/dashboard/roadPatrols/supervisor/RowActions/MachinePerformanceForm/MachinePerformanceContent.jsx b/src/components/dashboard/roadPatrols/supervisor/RowActions/MachinePerformanceForm/MachinePerformanceContent.jsx
index 93c0874..ead48ff 100644
--- a/src/components/dashboard/roadPatrols/supervisor/RowActions/MachinePerformanceForm/MachinePerformanceContent.jsx
+++ b/src/components/dashboard/roadPatrols/supervisor/RowActions/MachinePerformanceForm/MachinePerformanceContent.jsx
@@ -2,11 +2,11 @@ import { Box, Chip, DialogContent, Divider, Grid, Stack, Typography } from "@mui
import React, { useEffect, useState, useRef } from "react";
import { Marker, useMap } from "react-leaflet";
import AzmayeshActiveIcon from "@/assets/images/examine_marker_active.png";
-import StopsInfo from "@/components/dashboard/roadPatrols/operator/RowActions/MachinePerformanceForm/StopsInfo";
import MapLayer from "@/core/components/MapLayer";
import PinDropIcon from "@mui/icons-material/PinDrop";
import AccessAlarmsIcon from "@mui/icons-material/AccessAlarms";
import { formatSecondsToHHMMSS } from "@/core/utils/formatSecondsToHHMMSS";
+import StopsInfo from "./StopsInfo";
const defaultIconSize = [35, 35];
const MachinePerformanceContent = ({ machinesLists }) => {
const [selectedPoint, setSelectedPoint] = useState(null);
@@ -44,7 +44,7 @@ const MachinePerformanceContent = ({ machinesLists }) => {
return (
<>
-
+
{
-
+
{
>
);
};
-export default MachinePerformanceContent;
\ No newline at end of file
+export default MachinePerformanceContent;
diff --git a/src/components/dashboard/roadPatrols/supervisor/RowActions/MachinePerformanceForm/MachinesCodeFetch.jsx b/src/components/dashboard/roadPatrols/supervisor/RowActions/MachinePerformanceForm/MachinesCodeFetch.jsx
new file mode 100644
index 0000000..0eb47d2
--- /dev/null
+++ b/src/components/dashboard/roadPatrols/supervisor/RowActions/MachinePerformanceForm/MachinesCodeFetch.jsx
@@ -0,0 +1,49 @@
+import DialogLoading from "@/core/components/DialogLoading";
+import { GET_CMMS_MACHINE_ROAD_PATROL } from "@/core/utils/routes";
+import useRequest from "@/lib/hooks/useRequest";
+import { DialogContent, Typography } from "@mui/material";
+import { useEffect, useState } from "react";
+import MachinePerformanceContent from "./MachinePerformanceContent";
+
+const MachinePerformanceFetch = ({ row }) => {
+ const [loading, setLoading] = useState(true);
+ const [data, setData] = useState(null);
+ const request = useRequest();
+
+ useEffect(() => {
+ const fetchData = async () => {
+ try {
+ setLoading(true);
+ const response = await request(`${GET_CMMS_MACHINE_ROAD_PATROL}/${row.getValue('id')}`);
+ setData(response.data.data);
+ } catch (error) {
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ fetchData();
+ }, []);
+
+ return (
+ <>
+
+ {loading ? (
+
+ ) : data && row.original?.stop_points?.length > 0 ? (
+
+ ) : (
+ عملکرد خودرویی در سامانه یافت نشد
+ )}
+
+ >
+ )
+}
+export default MachinePerformanceFetch
\ No newline at end of file
diff --git a/src/components/dashboard/roadPatrols/supervisor/RowActions/MachinePerformanceForm/StopsInfo.jsx b/src/components/dashboard/roadPatrols/supervisor/RowActions/MachinePerformanceForm/StopsInfo.jsx
index 0415b7c..d8e44cb 100644
--- a/src/components/dashboard/roadPatrols/supervisor/RowActions/MachinePerformanceForm/StopsInfo.jsx
+++ b/src/components/dashboard/roadPatrols/supervisor/RowActions/MachinePerformanceForm/StopsInfo.jsx
@@ -1,17 +1,22 @@
import { Box, Chip, Divider, Grid, Typography } from "@mui/material";
import AccessAlarmsIcon from "@mui/icons-material/AccessAlarms";
-import PinDropIcon from "@mui/icons-material/PinDrop";
+
import LocalGasStationIcon from "@mui/icons-material/LocalGasStation";
import QrCode2Icon from "@mui/icons-material/QrCode2";
import AddRoadIcon from "@mui/icons-material/AddRoad";
import DirectionsCarFilledIcon from "@mui/icons-material/DirectionsCarFilled";
-import BadgeIcon from '@mui/icons-material/Badge';
+import BadgeIcon from "@mui/icons-material/Badge";
import { formatSecondsToHHMMSS } from "@/core/utils/formatSecondsToHHMMSS";
import ShowPlak from "@/core/components/ShowPlak";
-const StopsInfo = ({ selectedPoint, staticData }) => {
+const StopsInfo = ({ staticData }) => {
return (
-
+
{
{formatSecondsToHHMMSS(staticData?.accOnDuration)}
-
-
- }
- label={
-
- طول جغرافیایی
-
- }
- />
-
-
-
- {selectedPoint ? selectedPoint.longitude : "نقطه مورد نظر را انتخاب کنید"}
-
-
-
-
- }
- label={
-
- عرض جغرافیایی
-
- }
- />
-
-
-
- {selectedPoint ? selectedPoint.latitude : "نقطه مورد نظر را انتخاب کنید"}
-
-
-
-
- }
- label={
-
- مدت زمان توقف
-
- }
- />
-
-
-
- {selectedPoint
- ? `${formatSecondsToHHMMSS(selectedPoint.duration)} ثانیه`
- : "نقطه مورد نظر را انتخاب کنید"}
-
-
);
};
-export default StopsInfo;
\ No newline at end of file
+export default StopsInfo;
diff --git a/src/components/dashboard/roadPatrols/supervisor/RowActions/MachinePerformanceForm/index.jsx b/src/components/dashboard/roadPatrols/supervisor/RowActions/MachinePerformanceForm/index.jsx
index 3eb7b87..c8dcb9e 100644
--- a/src/components/dashboard/roadPatrols/supervisor/RowActions/MachinePerformanceForm/index.jsx
+++ b/src/components/dashboard/roadPatrols/supervisor/RowActions/MachinePerformanceForm/index.jsx
@@ -1,18 +1,10 @@
-import { Button, Dialog, DialogActions, DialogTitle, IconButton, Tooltip } from "@mui/material";
import DirectionsCarIcon from "@mui/icons-material/DirectionsCar";
-import MachinePerformanceContent from "./MachinePerformanceContent";
+import { Button, Dialog, DialogActions, DialogTitle, IconButton, Tooltip } from "@mui/material";
import { useState } from "react";
+import MachinePerformanceFetch from "./MachinesCodeFetch";
const MachinePerformanceForm = ({ row }) => {
const [openMachinesCodeDialog, setOpenMachinesCodeDialog] = useState(false);
- const machineData = {
- cmms_machine_code: row.original?.cmms_machines[0],
- resultCode: 0,
- mileage: row.original?.distance,
- accOnDuration: row.original?.vehicle_runtime,
- fuelConsumption: row.original?.fuel_consumption,
- stopPoints: row.original?.stop_points,
- };
return (
<>
@@ -33,7 +25,9 @@ const MachinePerformanceForm = ({ row }) => {
maxWidth={"md"}
>
عملکرد خودرو
-
+ {openMachinesCodeDialog && (
+
+ )}
setOpenMachinesCodeDialog(false)}
@@ -48,4 +42,4 @@ const MachinePerformanceForm = ({ row }) => {
>
);
};
-export default MachinePerformanceForm;
\ No newline at end of file
+export default MachinePerformanceForm;
diff --git a/src/components/dashboard/roadPatrols/supervisor/RowActions/index.jsx b/src/components/dashboard/roadPatrols/supervisor/RowActions/index.jsx
index 0b3d0a0..6672411 100644
--- a/src/components/dashboard/roadPatrols/supervisor/RowActions/index.jsx
+++ b/src/components/dashboard/roadPatrols/supervisor/RowActions/index.jsx
@@ -4,8 +4,8 @@ import { usePermissions } from "@/lib/hooks/usePermissions";
const RowActions = ({ row, mutate }) => {
const { data: userPermissions } = usePermissions();
- const hasPartialDeletePermission = userPermissions.includes("partial-delete-road-patrol");
- const hasCompleteDeletePermission = userPermissions.includes("complete-delete-road-patrol");
+ const hasPartialDeletePermission = userPermissions?.includes("partial-delete-road-patrol");
+ const hasCompleteDeletePermission = userPermissions?.includes("complete-delete-road-patrol");
return (
{hasPartialDeletePermission | hasCompleteDeletePermission && (
diff --git a/src/components/dashboard/roadPatrols/supervisor/SupervisorList.jsx b/src/components/dashboard/roadPatrols/supervisor/SupervisorList.jsx
index b3a0b02..495452e 100644
--- a/src/components/dashboard/roadPatrols/supervisor/SupervisorList.jsx
+++ b/src/components/dashboard/roadPatrols/supervisor/SupervisorList.jsx
@@ -12,13 +12,13 @@ import useEdaratLists from "@/lib/hooks/useEdaratLists";
import OfficerDescriptionForm from "./RowActions/OfficerDescription";
import ReportForm from "./RowActions/ReportForm";
import RahdaranDialog from "./RowActions/RahdaranForm";
-import MachinePerformanceForm from "./RowActions/MachinePerformanceForm";
import { usePermissions } from "@/lib/hooks/usePermissions";
import { useAuth } from "@/lib/contexts/auth";
+import MachinePerformanceForm from "./RowActions/MachinePerformanceForm";
const SupervisorList = () => {
const { data: userPermissions } = usePermissions();
- const hasCountryPermission = userPermissions.includes("show-road-patrol-supervise-cartable");
+ const hasCountryPermission = userPermissions?.includes("show-road-patrol-supervise-cartable");
const { user } = useAuth();
const columns = useMemo(() => {
const dynamicColumns = {
@@ -119,8 +119,8 @@ const SupervisorList = () => {
props.dependencyFieldValue?.value === ""
? "empty"
: loadingEdaratList
- ? "loading"
- : props.filterParameters.value
+ ? "loading"
+ : props.filterParameters.value
}
columnSelectOption={getColumnSelectOptions}
/>
@@ -257,7 +257,7 @@ const SupervisorList = () => {
),
},
];
- }, []);
+ }, [hasCountryPermission]);
return (
<>
diff --git a/src/components/dashboard/roadPatrols/supervisor/Toolbar.jsx b/src/components/dashboard/roadPatrols/supervisor/Toolbar.jsx
index 47cb9a9..32c4395 100644
--- a/src/components/dashboard/roadPatrols/supervisor/Toolbar.jsx
+++ b/src/components/dashboard/roadPatrols/supervisor/Toolbar.jsx
@@ -1,6 +1,10 @@
import PrintExcel from "./ExcelPrint";
const Toolbar = ({ table, filterData }) => {
- return <>{/**/}>;
+ return (
+ <>
+
+ >
+ );
};
export default Toolbar;
diff --git a/src/core/components/DataTable/Main.js b/src/core/components/DataTable/Main.js
index eb11e54..e9a3a37 100644
--- a/src/core/components/DataTable/Main.js
+++ b/src/core/components/DataTable/Main.js
@@ -7,6 +7,8 @@ import { useCallback, useMemo, useState } from "react";
import useRequest from "@/lib/hooks/useRequest";
import useSWR from "swr";
import { flattenObjectOfObjects } from "@/core/utils/flattenObjectOfObjects";
+import DataTableFilterDataStructure from "@/core/utils/DataTableFilterDataStructure";
+import isArrayEmpty from "@/core/utils/isArrayEmpty";
const DataTable_Main = (props) => {
const request = useRequest();
@@ -37,26 +39,13 @@ const DataTable_Main = (props) => {
const params = new URLSearchParams();
params.set("start", `${pagination.pageIndex * pagination.pageSize}`);
params.set("size", pagination.pageSize);
- const isValueEmpty = (value) => {
- if (Array.isArray(value)) {
- return value.length === 0 || value.every((v) => v === "");
- }
- return value === "" || value === null || value === undefined;
- };
if (specialFilter) {
const filteredSpecialFilterData = Object.values(specialFilter).filter(
- (filter) => !isValueEmpty(filter.value)
+ (filter) => !isArrayEmpty(filter.value)
);
params.set("filters", JSON.stringify(filteredSpecialFilterData || []));
} else {
- const filteredFilterData = Object.values(filterData)
- .filter((filter) => !isValueEmpty(filter.value))
- .map(({ filterMode, id, ...rest }) => ({
- ...rest,
- id: id.replace(/__/g, "."),
- fn: filterMode,
- }));
-
+ const filteredFilterData = DataTableFilterDataStructure(filterData, isArrayEmpty);
params.set("filters", JSON.stringify(filteredFilterData.length === 0 ? [] : filteredFilterData));
}
params.set("sorting", JSON.stringify(sortData));
diff --git a/src/core/components/DataTable/filter/FilterBody.jsx b/src/core/components/DataTable/filter/FilterBody.jsx
index ab12820..461eb01 100644
--- a/src/core/components/DataTable/filter/FilterBody.jsx
+++ b/src/core/components/DataTable/filter/FilterBody.jsx
@@ -1,32 +1,15 @@
"use client";
-import FilterBodyField from "@/core/components/DataTable/filter/FilterBodyField";
+import FilterBodyControllerWithDependency from "@/core/components/DataTable/filter/FilterBodyControllerWithDependency";
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 { Box, Button } from "@mui/material";
import { useEffect, useState } from "react";
-import { Controller, useForm, useWatch } from "react-hook-form";
+import { useForm } from "react-hook-form";
import * as Yup from "yup";
+import ScrollBox from "../../ScrollBox";
import FilterBodyController from "./FilterBodyController";
-import FilterBodyControllerWithDependency from "@/core/components/DataTable/filter/FilterBodyControllerWithDependency";
-
-const ScrollBox = styled(Box)({
- flexGrow: 1,
- overflowY: "scroll",
- maxWidth: "450px",
- "::-webkit-scrollbar": {
- width: "5px",
- },
- "::-webkit-scrollbar-track": {
- boxShadow: "inset 0 0 5px #fff",
- borderRadius: "5px",
- },
- "::-webkit-scrollbar-thumb": {
- background: "#155175",
- borderRadius: "0px",
- },
-});
function FilterBody({ columns, drawerState, setDrawerState }) {
const { filterData, setFilterData } = useDataTable();
@@ -89,7 +72,7 @@ function FilterBody({ columns, drawerState, setDrawerState }) {
{Object.keys(filterData).length > 0 && (