diff --git a/.env.example b/.env.example index 37aa8cf..1328a44 100644 --- a/.env.example +++ b/.env.example @@ -1,4 +1,4 @@ HOST="rms.witel.ir" -NEXT_PUBLIC_VERSION="1.7.0" +NEXT_PUBLIC_VERSION="1.7.3" NEXT_PUBLIC_API_URL="https://rms.witel.ir" NEXT_PUBLIC_MAPTILE_ENDPOINT="https://rmsmap.rmto.ir/141map" \ No newline at end of file diff --git a/src/components/dashboard/rahdaran/Actions/Create/Form/CreateFormContent.jsx b/src/components/dashboard/rahdaran/Actions/Create/Form/CreateFormContent.jsx index 74a2e76..ff8be50 100644 --- a/src/components/dashboard/rahdaran/Actions/Create/Form/CreateFormContent.jsx +++ b/src/components/dashboard/rahdaran/Actions/Create/Form/CreateFormContent.jsx @@ -3,16 +3,19 @@ import StyledForm from "@/core/components/StyledForm"; import validateNationalCode from "@/core/utils/nationalCodeValidation"; import { yupResolver } from "@hookform/resolvers/yup"; import { Beenhere, ExitToApp } from "@mui/icons-material"; -import { Button, DialogActions, DialogContent, Grid, Stack } from "@mui/material"; +import { Button, DialogActions, DialogContent, Grid, Stack, TextField } from "@mui/material"; import { Controller, useForm } from "react-hook-form"; import { object, string } from "yup"; const validationSchema = object({ - name: string().required("وارد کردن نام فارسی الزامیست!"), + name: string().required("وارد کردن نام و نام خانوادگی الزامیست!"), code: string() .required("لطفا کد ملی را وارد کنید!!!") .test("max", "کد ملی باید شامل 10 رقم باشد", (value) => value.toString().length === 10) .test("validation", "کد ملی صحیح نمی باشد", (value) => validateNationalCode(value)), + phone_number: string() + .matches(/^09\d{9}$/, "شماره موبایل باید با 09 شروع شده و 11 رقم باشد") + .required("لطفا تلفن همراه را وارد کنید!!!"), }); const CreateFormContent = ({ setOpen, defaultValues, onSubmitBase }) => { @@ -44,10 +47,10 @@ const CreateFormContent = ({ setOpen, defaultValues, onSubmitBase }) => { { + + + + ( + { + 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 }} + /> + )} + /> + + + diff --git a/src/components/dashboard/rahdaran/Actions/Create/Form/index.jsx b/src/components/dashboard/rahdaran/Actions/Create/Form/index.jsx index 5cb292d..fbc264f 100644 --- a/src/components/dashboard/rahdaran/Actions/Create/Form/index.jsx +++ b/src/components/dashboard/rahdaran/Actions/Create/Form/index.jsx @@ -10,11 +10,13 @@ const CreateForm = ({ open, setOpen, mutate }) => { const defaultValues = { name: "", code: "", + phone_number: "", }; const onSubmit = async (result) => { const formData = new FormData(); formData.append("name", result.name); formData.append("code", result.code); + formData.append("mobile", result.phone_number); await requestServer(CREATE_RAHDARAN_ITEM, "post", { data: formData, diff --git a/src/components/dashboard/rahdaran/Actions/Edit/EditController.jsx b/src/components/dashboard/rahdaran/Actions/Edit/EditController.jsx index 61bb336..6fc5d76 100644 --- a/src/components/dashboard/rahdaran/Actions/Edit/EditController.jsx +++ b/src/components/dashboard/rahdaran/Actions/Edit/EditController.jsx @@ -10,11 +10,13 @@ const EditController = ({ rowId, mutate, setOpen, row }) => { const defaultData = { name: row.original?.name || "", code: row.original?.code || "", + phone_number: row.original?.mobile || "", }; const handleSubmit = async (result) => { const formData = new FormData(); formData.append("name", result.name); formData.append("code", result.code); + formData.append("mobile", result.phone_number); await requestServer(`${UPDATE_RAHDARAN_ITEM}/${rowId}`, "post", { data: formData, diff --git a/src/components/dashboard/rahdaran/RahdaranList.jsx b/src/components/dashboard/rahdaran/RahdaranList.jsx index 90df9e9..7fe1ffb 100644 --- a/src/components/dashboard/rahdaran/RahdaranList.jsx +++ b/src/components/dashboard/rahdaran/RahdaranList.jsx @@ -43,6 +43,17 @@ const RahdaranList = () => { grow: false, size: 100, }, + { + accessorKey: "mobile", + header: "شماره همراه", + id: "mobile", + enableColumnFilter: true, + datatype: "text", + filterMode: "equals", + columnFilterModeOptions: ["equals", "contains"], + grow: false, + size: 100, + }, ], [] ); 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 293c6a7..7476478 100644 --- a/src/components/dashboard/roadItems/operator/Actions/Create/Forms/CreateFormContent.jsx +++ b/src/components/dashboard/roadItems/operator/Actions/Create/Forms/CreateFormContent.jsx @@ -1,20 +1,20 @@ -import React, { useState } from "react"; -import { useForm } from "react-hook-form"; -import { yupResolver } from "@hookform/resolvers/yup"; -import { array, mixed, number, object, string } from "yup"; -import { useTheme } from "@emotion/react"; -import { Box, Button, DialogActions, DialogContent, Tab, Tabs, useMediaQuery } from "@mui/material"; -import StyledForm from "@/core/components/StyledForm"; -import InsertDriveFileIcon from "@mui/icons-material/InsertDriveFile"; -import FileCopyIcon from "@mui/icons-material/FileCopy"; -import InfoIcon from "@mui/icons-material/Info"; -import ExitToAppIcon from "@mui/icons-material/ExitToApp"; -import KeyboardDoubleArrowRightIcon from "@mui/icons-material/KeyboardDoubleArrowRight"; -import BeenhereIcon from "@mui/icons-material/Beenhere"; +import GetItemInfo from "@/components/dashboard/roadItems/operator/Actions/Create/Forms/GetItemInfo"; import GetItemsForm from "@/components/dashboard/roadItems/operator/Actions/Create/Forms/GetItemsForm"; import GetSubItemsForm from "@/components/dashboard/roadItems/operator/Actions/Create/Forms/GetSubItemsForm"; -import GetItemInfo from "@/components/dashboard/roadItems/operator/Actions/Create/Forms/GetItemInfo"; +import StyledForm from "@/core/components/StyledForm"; +import { useTheme } from "@emotion/react"; +import { yupResolver } from "@hookform/resolvers/yup"; +import BeenhereIcon from "@mui/icons-material/Beenhere"; +import ExitToAppIcon from "@mui/icons-material/ExitToApp"; +import FileCopyIcon from "@mui/icons-material/FileCopy"; +import InfoIcon from "@mui/icons-material/Info"; +import InsertDriveFileIcon from "@mui/icons-material/InsertDriveFile"; +import KeyboardDoubleArrowRightIcon from "@mui/icons-material/KeyboardDoubleArrowRight"; +import { Box, Button, DialogActions, DialogContent, Tab, Tabs, useMediaQuery } from "@mui/material"; import { format } from "date-fns"; +import { useState } from "react"; +import { useForm } from "react-hook-form"; +import { mixed, number, object, string } from "yup"; function TabPanel(props) { const { children, value, index } = props; @@ -29,24 +29,7 @@ const validationSchema = object({ item_id: number().required("نوع آیتم را مشخص کنید!"), sub_item_id: number().required("اقدام انجام شده را مشخص کنید!"), amount: string().required("وارد کردن مقدار الزامیست!"), - cmms_machines: array() - .test("cmms-machines-conditional", "وارد کردن کد خودرو الزامیست!", function (value) { - const { is_gasht } = this.options.context; - if (is_gasht) { - return true; // اگر is_gasht true باشد، این فیلد نیاز به اعتبارسنجی ندارد - } - return Array.isArray(value) && value.length > 0; // حداقل یک کد باید وجود داشته باشد - }) - .min(1, "حداقل یک کد خودرو باید وارد شود!"), // پیام خطا برای زمانی که شرط تست برقرار باشد - rahdaran_id: array() - .test("rahdaran-id-conditional", "وارد کردن کد راهداران الزامیست!", function (value) { - const { is_gasht } = this.options.context; - if (is_gasht) { - return true; // اگر is_gasht true باشد، این فیلد نیاز به اعتبارسنجی ندارد - } - return Array.isArray(value) && value.length > 0; // حداقل یک کد باید وجود داشته باشد - }) - .min(1, "حداقل یک کد راهدار باید وارد شود!"), // پیام خطا برای زمانی که شرط تست برقرار باشد + mission_id: object().required("ماموریت را مشخص کنید!"), activity_time: string().test("activity-time-conditional", "لطفا زمان فعالیت را انتخاب کنید!", function (value) { const { is_gasht } = this.options.context; // دسترسی به context if (is_gasht) { @@ -110,10 +93,9 @@ const CreateFormContent = ({ setOpen, onSubmit, is_gasht = false }) => { start_point: "", end_point: "", ...(!is_gasht && { + mission_id: null, activity_time: "", activity_date: "", - cmms_machines: [], - rahdaran_id: [], }), }; @@ -139,8 +121,6 @@ const CreateFormContent = ({ setOpen, onSubmit, is_gasht = false }) => { "activity_date", "before_image", "after_image", - "cmms_machines", - "rahdaran_id", "start_point", "end_point", ]; @@ -195,20 +175,9 @@ const CreateFormContent = ({ setOpen, onSubmit, is_gasht = false }) => { } if (!is_gasht) { - result.rahdaran_id.forEach((rahdar, index) => { - result[`rahdaran_id[${index}]`] = rahdar.id; - }); - delete result.rahdaran_id; - - result.cmms_machines.forEach((cmms_machine, index) => { - result[`machines_id[${index}]`] = cmms_machine.id; - }); - delete result.cmms_machines; - result[`activity_time`] = format(new Date(result.activity_time), "HH:mm"); + result.mission_id = result.mission_id.id; } else { - delete result.rahdaran_id; - delete result.cmms_machines; delete result.activity_date; delete result.activity_time; } diff --git a/src/components/dashboard/roadItems/operator/Actions/Create/Forms/ImageUpload.jsx b/src/components/dashboard/roadItems/operator/Actions/Create/Forms/GetItemInfo/ImageUpload.jsx similarity index 97% rename from src/components/dashboard/roadItems/operator/Actions/Create/Forms/ImageUpload.jsx rename to src/components/dashboard/roadItems/operator/Actions/Create/Forms/GetItemInfo/ImageUpload.jsx index 6e33e99..8c23c76 100644 --- a/src/components/dashboard/roadItems/operator/Actions/Create/Forms/ImageUpload.jsx +++ b/src/components/dashboard/roadItems/operator/Actions/Create/Forms/GetItemInfo/ImageUpload.jsx @@ -1,173 +1,173 @@ -import { FormControl, FormHelperText, Grid } from "@mui/material"; -import UploadSystem from "@/core/components/UploadSystem"; -import { Controller } from "react-hook-form"; -import React, { useEffect, useState } from "react"; - -const ImageUpload = ({ control, setValue, errors, getValues, beforeImage = null, afterImage = null }) => { - const [beforeImg, setBeforeImg] = useState(beforeImage ? beforeImage : null); - const [beforeFileType, setBeforeFileType] = useState(beforeImage ? "image/" : null); - const [beforeFileName, setBeforeFileName] = useState(null); - const [showBeforeImage, setShowBeforeImage] = useState(!beforeImage); - - const [afterImg, setAfterImg] = useState(afterImage ? afterImage : null); - const [afterFileType, setAfterFileType] = useState(afterImage ? "image/" : null); - const [afterFileName, setAfterFileName] = useState(null); - const [showAfterImage, setShowAfterImage] = useState(!afterImage); - - useEffect(() => { - const beforeImage = getValues("before_image"); - const afterImage = getValues("after_image"); - - if (beforeImage) { - setShowBeforeImage(false); - - if (typeof beforeImage === "string") { - setBeforeImg(beforeImage); - setBeforeFileType("image/"); - } else if (beforeImage instanceof File) { - setBeforeImg(URL.createObjectURL(beforeImage)); - setBeforeFileType(beforeImage.type); - } - } - - if (afterImage) { - setShowAfterImage(false); - - if (typeof afterImage === "string") { - setAfterImg(afterImage); - setAfterFileType("image/"); - } else if (afterImage instanceof File) { - setAfterImg(URL.createObjectURL(afterImage)); - setAfterFileType(afterImage.type); - } - } - }, [getValues]); - - const handleBeforeFileChange = (event) => { - const uploadedFile = event.target?.files?.[0]; - if (uploadedFile) { - const fileType = event.target?.files?.[0].type; - const fileName = event.target?.files?.[0].name; - setBeforeImg(URL.createObjectURL(uploadedFile)); - setBeforeFileType(fileType); - setBeforeFileName(fileName); - setValue("before_image", uploadedFile); - setShowBeforeImage(false); - } - }; - const handleAfterFileChange = (event) => { - const uploadedFile = event.target?.files?.[0]; - if (uploadedFile) { - const fileType = event.target?.files?.[0].type; - const fileName = event.target?.files?.[0].name; - setAfterImg(URL.createObjectURL(uploadedFile)); - setAfterFileType(fileType); - setAfterFileName(fileName); - setValue("after_image", uploadedFile); - setShowAfterImage(false); - } - }; - return ( - - - { - return ( - - - عکس قبل از اقدام - - - - {errors.before_image ? errors.before_image.message : null} - - - ); - }} - /> - - - { - return ( - - - عکس بعد از اقدام - - - - {errors.after_image ? errors.after_image.message : null} - - - ); - }} - /> - - - ); -}; -export default ImageUpload; +import { FormControl, FormHelperText, Grid } from "@mui/material"; +import UploadSystem from "@/core/components/UploadSystem"; +import { Controller } from "react-hook-form"; +import React, { useEffect, useState } from "react"; + +const ImageUpload = ({ control, setValue, errors, getValues, beforeImage = null, afterImage = null }) => { + const [beforeImg, setBeforeImg] = useState(beforeImage ? beforeImage : null); + const [beforeFileType, setBeforeFileType] = useState(beforeImage ? "image/" : null); + const [beforeFileName, setBeforeFileName] = useState(null); + const [showBeforeImage, setShowBeforeImage] = useState(!beforeImage); + + const [afterImg, setAfterImg] = useState(afterImage ? afterImage : null); + const [afterFileType, setAfterFileType] = useState(afterImage ? "image/" : null); + const [afterFileName, setAfterFileName] = useState(null); + const [showAfterImage, setShowAfterImage] = useState(!afterImage); + + useEffect(() => { + const beforeImage = getValues("before_image"); + const afterImage = getValues("after_image"); + + if (beforeImage) { + setShowBeforeImage(false); + + if (typeof beforeImage === "string") { + setBeforeImg(beforeImage); + setBeforeFileType("image/"); + } else if (beforeImage instanceof File) { + setBeforeImg(URL.createObjectURL(beforeImage)); + setBeforeFileType(beforeImage.type); + } + } + + if (afterImage) { + setShowAfterImage(false); + + if (typeof afterImage === "string") { + setAfterImg(afterImage); + setAfterFileType("image/"); + } else if (afterImage instanceof File) { + setAfterImg(URL.createObjectURL(afterImage)); + setAfterFileType(afterImage.type); + } + } + }, [getValues]); + + const handleBeforeFileChange = (event) => { + const uploadedFile = event.target?.files?.[0]; + if (uploadedFile) { + const fileType = event.target?.files?.[0].type; + const fileName = event.target?.files?.[0].name; + setBeforeImg(URL.createObjectURL(uploadedFile)); + setBeforeFileType(fileType); + setBeforeFileName(fileName); + setValue("before_image", uploadedFile); + setShowBeforeImage(false); + } + }; + const handleAfterFileChange = (event) => { + const uploadedFile = event.target?.files?.[0]; + if (uploadedFile) { + const fileType = event.target?.files?.[0].type; + const fileName = event.target?.files?.[0].name; + setAfterImg(URL.createObjectURL(uploadedFile)); + setAfterFileType(fileType); + setAfterFileName(fileName); + setValue("after_image", uploadedFile); + setShowAfterImage(false); + } + }; + return ( + + + { + return ( + + + عکس قبل از اقدام + + + + {errors.before_image ? errors.before_image.message : null} + + + ); + }} + /> + + + { + return ( + + + عکس بعد از اقدام + + + + {errors.after_image ? errors.after_image.message : null} + + + ); + }} + /> + + + ); +}; +export default ImageUpload; diff --git a/src/components/dashboard/roadItems/operator/Actions/Create/Forms/GetItemInfo/MissionsDialog/MissionsList/RowActions/Allocate/index.jsx b/src/components/dashboard/roadItems/operator/Actions/Create/Forms/GetItemInfo/MissionsDialog/MissionsList/RowActions/Allocate/index.jsx new file mode 100644 index 0000000..bb44050 --- /dev/null +++ b/src/components/dashboard/roadItems/operator/Actions/Create/Forms/GetItemInfo/MissionsDialog/MissionsList/RowActions/Allocate/index.jsx @@ -0,0 +1,23 @@ +import { Done } from "@mui/icons-material"; +import { Button } from "@mui/material"; + +const Allocate = ({ row, setMission, setOpenDialog }) => { + const handleClick = () => { + setMission(row.original); + setOpenDialog(false); + }; + + return ( + <> + + + ); +}; +export default Allocate; diff --git a/src/components/dashboard/roadItems/operator/Actions/Create/Forms/GetItemInfo/MissionsDialog/MissionsList/RowActions/index.jsx b/src/components/dashboard/roadItems/operator/Actions/Create/Forms/GetItemInfo/MissionsDialog/MissionsList/RowActions/index.jsx new file mode 100644 index 0000000..31e8824 --- /dev/null +++ b/src/components/dashboard/roadItems/operator/Actions/Create/Forms/GetItemInfo/MissionsDialog/MissionsList/RowActions/index.jsx @@ -0,0 +1,6 @@ +import Allocate from "./Allocate"; + +const RowActions = ({ row, setMission, setOpenDialog }) => { + return ; +}; +export default RowActions; diff --git a/src/components/dashboard/roadItems/operator/Actions/Create/Forms/GetItemInfo/MissionsDialog/MissionsList/index.jsx b/src/components/dashboard/roadItems/operator/Actions/Create/Forms/GetItemInfo/MissionsDialog/MissionsList/index.jsx new file mode 100644 index 0000000..a66e705 --- /dev/null +++ b/src/components/dashboard/roadItems/operator/Actions/Create/Forms/GetItemInfo/MissionsDialog/MissionsList/index.jsx @@ -0,0 +1,111 @@ +import DataTableWithAuth from "@/core/components/DataTableWithAuth"; +import { GET_ROAD_MISSIONS_OPERATOR_LIST } from "@/core/utils/routes"; +import { Box } from "@mui/material"; +import { useMemo } from "react"; +import RowActions from "./RowActions"; +import moment from "jalali-moment"; + +const MissionList = ({ setMission, setOpenDialog }) => { + const columns = useMemo( + () => [ + { + accessorKey: "id", + header: "کد یکتا", + id: "id", + enableColumnFilter: true, + datatype: "text", + filterMode: "equals", + columnFilterModeOptions: ["equals", "contains"], + grow: false, + size: 100, + }, + { + accessorKey: "explanation", + header: "موضوع", + id: "explanation", + enableColumnFilter: false, + datatype: "numeric", + filterMode: "equals", + grow: false, + size: 100, + }, + { + accessorKey: "zone", + header: "محدوده", + id: "zone", + enableColumnFilter: true, + datatype: "numeric", + filterMode: "equals", + sortDescFirst: true, + grow: false, + size: 100, + columnSelectOption: () => { + return missionRegions.map((region) => ({ + value: region.id, + label: region.name_fa, + })); + }, + Cell: ({ row }) => row.original.zone_fa, + }, + { + accessorFn: (row) => + row.type == 1 + ? moment(row.start_date).locale("fa").format("HH:mm | yyyy/MM/DD") + : moment(row.start_date).locale("fa").format("yyyy/MM/DD"), + header: "تاریخ شروع", + id: "start_date", + enableColumnFilter: true, + datatype: "date", + filterMode: "between", + grow: false, + size: 100, + }, + { + accessorFn: (row) => + row.type == 1 + ? moment(row.end_date).locale("fa").format("HH:mm | yyyy/MM/DD") + : moment(row.end_date).locale("fa").format("yyyy/MM/DD"), + header: "تاریخ پایان", + id: "end_date", + enableColumnFilter: true, + datatype: "date", + filterMode: "between", + grow: false, + size: 100, + }, + { + accessorKey: "end_point", + header: "مقصد", + id: "end_point", + enableColumnFilter: false, + datatype: "numeric", + filterMode: "equals", + grow: false, + size: 100, + }, + ], + [] + ); + + return ( + <> + + ( + + )} + /> + + + ); +}; +export default MissionList; diff --git a/src/components/dashboard/roadItems/operator/Actions/Create/Forms/GetItemInfo/MissionsDialog/index.jsx b/src/components/dashboard/roadItems/operator/Actions/Create/Forms/GetItemInfo/MissionsDialog/index.jsx new file mode 100644 index 0000000..e3efea4 --- /dev/null +++ b/src/components/dashboard/roadItems/operator/Actions/Create/Forms/GetItemInfo/MissionsDialog/index.jsx @@ -0,0 +1,38 @@ +import { Close, Edit } from "@mui/icons-material"; +import { Button, Dialog, IconButton } from "@mui/material"; +import { useState } from "react"; +import MissionList from "./MissionsList"; + +const MissionsDialog = ({ setMission, mode }) => { + const [open, setOpen] = useState(false); + return ( + <> + {mode == "edit" ? ( + setOpen(true)}> + + + ) : ( + + )} + + setOpen(false)} + sx={(theme) => ({ + position: "absolute", + right: 8, + top: 8, + zIndex: 50, + color: theme.palette.grey[500], + })} + > + + + {open && } + + + ); +}; +export default MissionsDialog; diff --git a/src/components/dashboard/roadItems/operator/Actions/Create/Forms/PreviousStatesInfo.jsx b/src/components/dashboard/roadItems/operator/Actions/Create/Forms/GetItemInfo/PreviousStatesInfo.jsx similarity index 97% rename from src/components/dashboard/roadItems/operator/Actions/Create/Forms/PreviousStatesInfo.jsx rename to src/components/dashboard/roadItems/operator/Actions/Create/Forms/GetItemInfo/PreviousStatesInfo.jsx index a944ccd..51fac42 100644 --- a/src/components/dashboard/roadItems/operator/Actions/Create/Forms/PreviousStatesInfo.jsx +++ b/src/components/dashboard/roadItems/operator/Actions/Create/Forms/GetItemInfo/PreviousStatesInfo.jsx @@ -1,43 +1,43 @@ -import { Box, Chip, Divider, Grid, Typography } from "@mui/material"; -import InsertDriveFileIcon from "@mui/icons-material/InsertDriveFile"; -import FileCopyIcon from "@mui/icons-material/FileCopy"; - -const PreviousStatesInfo = ({ itemsList, subItemsList }) => { - return ( - - - - } - label={ - - آیتم انتخاب ‌شده - - } - /> - - - - {itemsList.name || "هیچ آیتمی انتخاب نشده است"} - - - - - } - label={ - - اقدام انجام شده - - } - /> - - - - {subItemsList?.name || "هیچ اقدامی انتخاب نشده است"} - - - - ); -}; -export default PreviousStatesInfo; +import { Box, Chip, Divider, Grid, Typography } from "@mui/material"; +import InsertDriveFileIcon from "@mui/icons-material/InsertDriveFile"; +import FileCopyIcon from "@mui/icons-material/FileCopy"; + +const PreviousStatesInfo = ({ itemsList, subItemsList }) => { + return ( + + + + } + label={ + + آیتم انتخاب ‌شده + + } + /> + + + + {itemsList.name || "هیچ آیتمی انتخاب نشده است"} + + + + + } + label={ + + اقدام انجام شده + + } + /> + + + + {subItemsList?.name || "هیچ اقدامی انتخاب نشده است"} + + + + ); +}; +export default PreviousStatesInfo; diff --git a/src/components/dashboard/roadItems/operator/Actions/Create/Forms/GetItemInfo.jsx b/src/components/dashboard/roadItems/operator/Actions/Create/Forms/GetItemInfo/index.jsx similarity index 70% rename from src/components/dashboard/roadItems/operator/Actions/Create/Forms/GetItemInfo.jsx rename to src/components/dashboard/roadItems/operator/Actions/Create/Forms/GetItemInfo/index.jsx index 82d5cca..6aab3ec 100644 --- a/src/components/dashboard/roadItems/operator/Actions/Create/Forms/GetItemInfo.jsx +++ b/src/components/dashboard/roadItems/operator/Actions/Create/Forms/GetItemInfo/index.jsx @@ -1,124 +1,109 @@ -import { Grid, Stack } from "@mui/material"; -import ImageUpload from "./ImageUpload"; -import MuiDatePicker from "@/core/components/MuiDatePicker"; -import MuiTimePicker from "@/core/components/MuiTimePicker"; -import MapInfoTwoMarker from "@/core/components/MapInfoTwoMarker"; -import MapInfoOneMarker from "@/core/components/MapInfoOneMarker"; -import { Controller } from "react-hook-form"; -import CarCode from "@/core/components/CarCode"; -import RahdarCode from "@/core/components/RahdarCode"; -import PreviousStatesInfo from "./PreviousStatesInfo"; -import NumberField from "@/core/components/NumberField"; - -const GetItemInfo = ({ register, itemsList, subItemsList, control, setValue, errors, watch, getValues, is_gasht }) => { - return ( - - - - - - { - if (!isNaN(event.target.value)) { - setValue("amount", event.target.value); - } else { - setValue("amount", watch("amount")); - } - }} - helperText={errors.amount ? errors.amount.message : null} - variant="outlined" - fullWidth - /> - - {!is_gasht && ( - - { - return ( - field.onChange(value || [])} - error={error} - multiple={true} - /> - ); - }} - name={"cmms_machines"} - /> - - )} - {!is_gasht && ( - - { - return ( - field.onChange(value || [])} - error={error} - /> - ); - }} - name={"rahdaran_id"} - /> - - )} - - {subItemsList.needs_image ? ( - - ) : null} - - {!is_gasht ? ( - - - - - - - - - - - ) : null} - - {subItemsList?.needs_end_point === 1 ? ( - - ) : ( - - )} - - - ); -}; -export default GetItemInfo; +import MapInfoOneMarker from "@/core/components/MapInfoOneMarker"; +import MapInfoTwoMarker from "@/core/components/MapInfoTwoMarker"; +import MuiDatePicker from "@/core/components/MuiDatePicker"; +import MuiTimePicker from "@/core/components/MuiTimePicker"; +import NumberField from "@/core/components/NumberField"; +import { Chip, Divider, FormHelperText, Grid, Stack, Typography } from "@mui/material"; +import ImageUpload from "./ImageUpload"; +import MissionsDialog from "./MissionsDialog"; +import PreviousStatesInfo from "./PreviousStatesInfo"; +import { Controller } from "react-hook-form"; + +const GetItemInfo = ({ register, itemsList, subItemsList, control, setValue, errors, watch, getValues, is_gasht }) => { + return ( + + + + + { + return value ? ( + + + + + onChange(m)} mode={"edit"} /> + + {error ? error.message : null} + + ) : ( + + onChange(m)} /> + {error ? error.message : null} + + ); + }} + /> + + { + if (!isNaN(event.target.value)) { + setValue("amount", event.target.value); + } else { + setValue("amount", watch("amount")); + } + }} + helperText={errors.amount ? errors.amount.message : null} + variant="outlined" + fullWidth + /> + + {!is_gasht ? ( + + + + + + + + + + + ) : null} + + {subItemsList.needs_image ? ( + + ) : null} + + + {subItemsList?.needs_end_point === 1 ? ( + + ) : ( + + )} + + + ); +}; +export default GetItemInfo; diff --git a/src/components/dashboard/roadItems/operator/OperatorList.jsx b/src/components/dashboard/roadItems/operator/OperatorList.jsx index 06b98ff..7f862e4 100644 --- a/src/components/dashboard/roadItems/operator/OperatorList.jsx +++ b/src/components/dashboard/roadItems/operator/OperatorList.jsx @@ -219,60 +219,6 @@ const OperatorList = () => { ); }, }, - { - header: "خودرو ها", - id: "cmmsMachines__machine_code", - enableColumnFilter: true, - datatype: "text", - filterMode: "contains", - enableSorting: false, - columnFilterModeOptions: ["equals", "contains"], - grow: false, - size: 100, - muiTableBodyCellProps: { - sx: { - borderLeft: "1px solid #e1e1e1", - py: 0, - "&:first-of-type": { - borderLeft: "unset", - }, - }, - }, - Cell: ({ renderedCellValue, row }) => { - return ( - - - - ); - }, - }, - { - header: "راهداران", - id: "rahdaran__code", - enableColumnFilter: true, - enableSorting: false, - datatype: "text", - filterMode: "contains", - columnFilterModeOptions: ["equals", "contains"], - grow: false, - size: 100, - muiTableBodyCellProps: { - sx: { - borderLeft: "1px solid #e1e1e1", - py: 0, - "&:first-of-type": { - borderLeft: "unset", - }, - }, - }, - Cell: ({ renderedCellValue, row }) => { - return ( - - - - ); - }, - }, { accessorFn: (row) => moment(row.activity_date_time).locale("fa").format("HH:mm | yyyy/MM/DD"), header: "تاریخ فعالیت", // Start Date diff --git a/src/components/dashboard/roadItems/operator/RowActions/EditForm/EditFormCreate.jsx b/src/components/dashboard/roadItems/operator/RowActions/EditForm/EditFormCreate.jsx index 4004086..5a24c01 100644 --- a/src/components/dashboard/roadItems/operator/RowActions/EditForm/EditFormCreate.jsx +++ b/src/components/dashboard/roadItems/operator/RowActions/EditForm/EditFormCreate.jsx @@ -1,36 +1,15 @@ -import { Button, DialogActions, DialogContent, Stack } from "@mui/material"; -import ImageUpload from "@/components/dashboard/roadItems/operator/Actions/Create/Forms/ImageUpload"; -import NumberField from "@/core/components/NumberField"; -import { Controller, useForm } from "react-hook-form"; -import CarCode from "@/core/components/CarCode"; -import RahdarCode from "@/core/components/RahdarCode"; -import MapInfoTwoMarker from "@/core/components/MapInfoTwoMarker"; import MapInfoOneMarker from "@/core/components/MapInfoOneMarker"; +import MapInfoTwoMarker from "@/core/components/MapInfoTwoMarker"; +import NumberField from "@/core/components/NumberField"; import StyledForm from "@/core/components/StyledForm"; -import React from "react"; import { yupResolver } from "@hookform/resolvers/yup"; -import { array, mixed, object, string } from "yup"; +import { Button, DialogActions, DialogContent, Stack } from "@mui/material"; +import { useForm } from "react-hook-form"; +import { mixed, object, string } from "yup"; +import ImageUpload from "../../Actions/ObservedGashtCreate/RowActions/ImageUpload"; const validationSchema = object({ amount: string().required("وارد کردن مقدار الزامیست!"), - cmms_machines: array() - .test("cmms-machines-conditional", "وارد کردن کد خودرو الزامیست!", function (value) { - const { is_gasht } = this.options.context; - if (is_gasht) { - return true; // اگر is_gasht true باشد، این فیلد نیاز به اعتبارسنجی ندارد - } - return Array.isArray(value) && value.length > 0; // حداقل یک کد باید وجود داشته باشد - }) - .min(1, "حداقل یک کد خودرو باید وارد شود!"), // پیام خطا برای زمانی که شرط تست برقرار باشد - rahdaran_id: array() - .test("rahdaran-id-conditional", "وارد کردن کد راهداران الزامیست!", function (value) { - const { is_gasht } = this.options.context; - if (is_gasht) { - return true; // اگر is_gasht true باشد، این فیلد نیاز به اعتبارسنجی ندارد - } - return Array.isArray(value) && value.length > 0; // حداقل یک کد باید وجود داشته باشد - }) - .min(1, "حداقل یک کد راهدار باید وارد شود!"), before_image: mixed() .nullable() .test("before-image-required", "لطفا عکس قبل از اقدام را بارگذاری کنید!", function (value) { @@ -124,42 +103,6 @@ const EditFormCreate = ({ defaultData, subItem, onSubmitBase, defaultValues, set fullWidth /> - {!is_gasht && ( - - { - return ( - field.onChange(value || [])} - inputValueDefault={defaultData?.cmms_machines} - multiple={true} - error={error} // اگر خطا وجود داشته باشد - /> - ); - }} - name={"cmms_machines"} - /> - - )} - {!is_gasht && ( - - { - return ( - field.onChange(value || [])} - error={error} - /> - ); - }} - name={"rahdaran_id"} - /> - - )} {subItem.needs_end_point === 1 ? ( { - + {/* */} ); }; diff --git a/src/components/dashboard/roadItems/supervisor/SupervisorList.jsx b/src/components/dashboard/roadItems/supervisor/SupervisorList.jsx index 807f417..75005b7 100644 --- a/src/components/dashboard/roadItems/supervisor/SupervisorList.jsx +++ b/src/components/dashboard/roadItems/supervisor/SupervisorList.jsx @@ -316,60 +316,6 @@ const SupervisorList = () => { ); }, }, - { - header: "خودرو ها", - id: "cmmsMachines__machine_code", - enableColumnFilter: true, - datatype: "text", - filterMode: "contains", - enableSorting: false, - columnFilterModeOptions: ["equals", "contains"], - grow: false, - size: 100, - muiTableBodyCellProps: { - sx: { - borderLeft: "1px solid #e1e1e1", - py: 0, - "&:first-of-type": { - borderLeft: "unset", - }, - }, - }, - Cell: ({ renderedCellValue, row }) => { - return ( - - - - ); - }, - }, - { - header: "راهداران", - id: "rahdaran__code", - enableColumnFilter: true, - enableSorting: false, - datatype: "text", - filterMode: "contains", - columnFilterModeOptions: ["equals", "contains"], - grow: false, - size: 100, - muiTableBodyCellProps: { - sx: { - borderLeft: "1px solid #e1e1e1", - py: 0, - "&:first-of-type": { - borderLeft: "unset", - }, - }, - }, - Cell: ({ renderedCellValue, row }) => { - return ( - - - - ); - }, - }, { accessorFn: (row) => moment(row.activity_date_time).locale("fa").format("HH:mm | yyyy/MM/DD"), header: "تاریخ فعالیت", // Start Date diff --git a/src/components/dashboard/roadMissions/control/ControlList.jsx b/src/components/dashboard/roadMissions/control/ControlList.jsx index 18e757a..be2443d 100644 --- a/src/components/dashboard/roadMissions/control/ControlList.jsx +++ b/src/components/dashboard/roadMissions/control/ControlList.jsx @@ -102,6 +102,17 @@ const ControlList = () => { ); }, }, + { + accessorFn: (row) => + row.start_time ? moment(row.start_time).locale("fa").format("HH:mm | yyyy/MM/DD") : null, + header: "تاریخ خروج", + id: "start_time", + enableColumnFilter: true, + datatype: "date", + filterMode: "between", + grow: false, + size: 100, + }, ], [] ); diff --git a/src/components/dashboard/roadMissions/operator/Actions/Create/Form/GetDateTime/MissionDates/index.jsx b/src/components/dashboard/roadMissions/operator/Actions/Create/Form/GetDateTime/MissionDates/index.jsx index a2b5c07..8414a10 100644 --- a/src/components/dashboard/roadMissions/operator/Actions/Create/Form/GetDateTime/MissionDates/index.jsx +++ b/src/components/dashboard/roadMissions/operator/Actions/Create/Form/GetDateTime/MissionDates/index.jsx @@ -1,95 +1,119 @@ import MuiDatePicker from "@/core/components/MuiDatePicker"; import MuiTimePicker from "@/core/components/MuiTimePicker"; +import { makeDateTime } from "@/core/utils/makeDateTime"; import { Grid } from "@mui/material"; import { Controller, useWatch } from "react-hook-form"; const MissionDates = ({ control }) => { const type = useWatch({ control, name: "type" }); + const start_date = useWatch({ control, name: "start_date" }); + const end_date = useWatch({ control, name: "end_date" }); + const start_time = useWatch({ control, name: "start_time" }); + const end_time = useWatch({ control, name: "end_time" }); + + const startDateTime = makeDateTime(start_date, start_time); + const endDateTime = makeDateTime(end_date, end_time); + const now = new Date(); + return ( - <> - - { - return ( - field.onChange(value || [])} - helperText={error ? error.message : null} - /> - ); - }} - /> - - {type == 1 && ( + type && ( + <> { return ( - field.onChange(value || null)} + disableFuture={false} + minDate={now} + maxDate={end_date ?? null} + placeholder={"تاریخ شروع ماموریت را وارد کنید"} + label={"تاریخ شروع ماموریت"} + setFieldValue={(name, value) => field.onChange(value || [])} helperText={error ? error.message : null} /> ); }} /> - )} - - { - return ( - field.onChange(value || [])} - helperText={error ? error.message : null} - /> - ); - }} - /> - - {type == 1 && ( + {type == 1 && ( + + { + return ( + field.onChange(value || null)} + helperText={error ? error.message : null} + /> + ); + }} + /> + + )} { return ( - field.onChange(value || null)} + disableFuture={false} + minDate={start_date ? start_date : now} + placeholder={"تاریخ پایان ماموریت را وارد کنید"} + label={"تاریخ پایان ماموریت"} + setFieldValue={(name, value) => field.onChange(value || [])} helperText={error ? error.message : null} /> ); }} /> - )} - + {type == 1 && ( + + { + return ( + field.onChange(value || null)} + helperText={error ? error.message : null} + /> + ); + }} + /> + + )} + + ) ); }; export default MissionDates; diff --git a/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/Area/MapControlPolygon/DrawBound/index.jsx b/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/Area/MapControlPolygon/DrawBound/index.jsx new file mode 100644 index 0000000..a2134c5 --- /dev/null +++ b/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/Area/MapControlPolygon/DrawBound/index.jsx @@ -0,0 +1,129 @@ +import React, { useCallback, useEffect, useRef, useState } from "react"; +import L from "leaflet"; +import { FeatureGroup, useMap } from "react-leaflet"; +import "leaflet-draw"; + +const DrawBound = ({ control, controlDispach, bound, setBound }) => { + const map = useMap(); + const featureRef = useRef(null); + const [area, setArea] = useState(); + const drawControlRef = useRef(null); + + const safeFlyToBounds = (layer) => { + if (!layer || !map) return; + try { + const latLngs = layer.getLatLngs(); + map.flyToBounds(latLngs, { + paddingTopLeft: [20, 35], + paddingBottomRight: [20, 55], + }); + } catch (e) { + console.warn("flyToBounds failed:", e); + } + }; + + const safeFitBounds = (layer) => { + if (!layer || !map) return; + try { + const latLngs = layer.getLatLngs(); + map.fitBounds(latLngs, { + paddingTopLeft: [20, 35], + paddingBottomRight: [20, 55], + }); + } catch (e) { + console.warn("fitBounds failed:", e); + } + }; + + const bindEditEvent = (layer) => { + if (!layer) return; + layer.on("edit", function (e) { + const editedLayer = e.target; + setArea(editedLayer); + setBound(editedLayer); + safeFlyToBounds(editedLayer); + }); + }; + + const handlerCreatedBound = useCallback((event) => { + const { layer } = event; + layer.editing.enable(); + featureRef.current.addLayer(layer); // 🟢 اول اضافه به نقشه + setArea(layer); + setBound(layer); + safeFlyToBounds(layer); + bindEditEvent(layer); + controlDispach({ type: "SET_STATUS", status: 2 }); + }, []); + + useEffect(() => { + if ( + control.status === 1 && + drawControlRef.current && + drawControlRef.current._toolbars?.draw?._modes?.polygon?.handler + ) { + drawControlRef.current._toolbars.draw._modes.polygon.handler.enable(); + } + }, [control.status]); + + useEffect(() => { + if (control.status === 0 && area && featureRef.current) { + featureRef.current.removeLayer(area); + setArea(null); + setBound(null); + } + }, [control.status, area]); + + useEffect(() => { + if (control.status === 2 && bound && featureRef.current) { + setArea(bound); + featureRef.current.addLayer(bound); + bound.editing.enable(); + safeFitBounds(bound); + bindEditEvent(bound); + } + }, [control.status, bound]); + + useEffect(() => { + if (!featureRef.current) return; + + L.drawLocal.draw.handlers.polygon.tooltip.start = "برای شروع ترسیم محدوده، روی نقشه کلیک کنید"; + L.drawLocal.draw.handlers.polygon.tooltip.cont = "برای ادامه ترسیم، نقاط بعدی را کلیک کنید"; + L.drawLocal.draw.handlers.polygon.tooltip.end = "برای بستن محدوده، روی نقطه‌ی شروع کلیک کنید"; + + const drawControl = new L.Control.Draw({ + draw: { + polygon: { + shapeOptions: { + color: "#3388ff", + weight: 3, + clickable: true, + }, + }, + polyline: false, + rectangle: false, + circle: false, + marker: false, + circlemarker: false, + }, + edit: { + featureGroup: featureRef.current, + edit: true, + remove: false, + }, + }); + + drawControlRef.current = drawControl; + map.addControl(drawControl); + map.on(L.Draw.Event.CREATED, handlerCreatedBound); + + return () => { + map.off(L.Draw.Event.CREATED, handlerCreatedBound); + map.removeControl(drawControl); + }; + }, [map, handlerCreatedBound]); + + return ; +}; + +export default DrawBound; diff --git a/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/Area/MapControlPolygon/index.jsx b/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/Area/MapControlPolygon/index.jsx new file mode 100644 index 0000000..186a0b6 --- /dev/null +++ b/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/Area/MapControlPolygon/index.jsx @@ -0,0 +1,116 @@ +import { Delete, Route } from "@mui/icons-material"; +import { Box, Button, Stack, Typography } from "@mui/material"; +import { useReducer } from "react"; +import DrawBound from "./DrawBound"; + +const statusType = [ + { + id: 0, + message: "برای آغاز ترسیم محدوده، کلیک کنید!", + buttons: [ + { + label: "شروع ترسیم محدوده", + key: "start", + color: "primary", + icon: , + onclick: (controlDispach) => { + controlDispach({ type: "SET_STATUS", status: 1 }); + }, + }, + ], + }, + { + id: 1, + message: "محدوده‌ی موردنظر را با کلیک روی نقشه مشخص کنید. برای اتمام، روی نقطه‌ی ابتدایی کلیک کنید!", + buttons: [], + }, + { + id: 2, + message: "برای اصلاح محدوده، گوشه‌ها را بکشید. برای ترسیم دوباره، آن را حذف کنید", + buttons: [ + { + label: "حذف", + key: "end", + color: "error", + icon: , + onclick: (controlDispach) => { + controlDispach({ type: "SET_STATUS", status: 0 }); + }, + }, + ], + }, +]; + +const createInitialState = (bound) => { + if (bound) { + return { status: 2 }; + } + return { status: 0 }; +}; + +const reducer = (state, action) => { + switch (action.type) { + case "SET_STATUS": + return { ...state, status: action.status }; + default: + return state; + } +}; + +const MapControlPolygon = ({ bound, setBound }) => { + const [control, controlDispach] = useReducer(reducer, bound, createInitialState); + + return ( + <> + + + theme.palette.info.main, + borderBottomLeftRadius: 8, + borderBottomRightRadius: 8, + py: 0.5, + px: 2, + }} + > + + {statusType.find((st) => st.id == control.status).message} + + + + + + {statusType + .find((st) => st.id == control.status) + .buttons.map((button) => ( + + ))} + + + + ); +}; +export default MapControlPolygon; diff --git a/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/Area/MapControlPolyline/DrawBound/index.jsx b/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/Area/MapControlPolyline/DrawBound/index.jsx new file mode 100644 index 0000000..682be24 --- /dev/null +++ b/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/Area/MapControlPolyline/DrawBound/index.jsx @@ -0,0 +1,129 @@ +import React, { useCallback, useEffect, useRef, useState } from "react"; +import L from "leaflet"; +import { FeatureGroup, useMap } from "react-leaflet"; +import "leaflet-draw"; + +const DrawBound = ({ control, controlDispach, bound, setBound }) => { + const map = useMap(); + const featureRef = useRef(null); + const [area, setArea] = useState(); + const drawControlRef = useRef(null); + + const safeFlyToBounds = (layer) => { + if (!layer || !map) return; + try { + const latLngs = layer.getLatLngs(); + map.flyToBounds(latLngs, { + paddingTopLeft: [20, 35], + paddingBottomRight: [20, 55], + }); + } catch (e) { + console.warn("flyToBounds failed:", e); + } + }; + + const safeFitBounds = (layer) => { + if (!layer || !map) return; + try { + const latLngs = layer.getLatLngs(); + map.fitBounds(latLngs, { + paddingTopLeft: [20, 35], + paddingBottomRight: [20, 55], + }); + } catch (e) { + console.warn("fitBounds failed:", e); + } + }; + + const bindEditEvent = (layer) => { + if (!layer) return; + layer.on("edit", function (e) { + const editedLayer = e.target; + setArea(editedLayer); + setBound(editedLayer); + safeFlyToBounds(editedLayer); + }); + }; + + const handlerCreatedBound = useCallback((event) => { + const { layer } = event; + layer.editing.enable(); + featureRef.current.addLayer(layer); // 🟢 اول اضافه به نقشه + setArea(layer); + setBound(layer); + safeFlyToBounds(layer); + bindEditEvent(layer); + controlDispach({ type: "SET_STATUS", status: 2 }); + }, []); + + useEffect(() => { + if ( + control.status === 1 && + drawControlRef.current && + drawControlRef.current._toolbars?.draw?._modes?.polyline?.handler + ) { + drawControlRef.current._toolbars.draw._modes.polyline.handler.enable(); + } + }, [control.status]); + + useEffect(() => { + if (control.status === 0 && area && featureRef.current) { + featureRef.current.removeLayer(area); + setArea(null); + setBound(null); + } + }, [control.status, area]); + + useEffect(() => { + if (control.status === 2 && bound && featureRef.current) { + setArea(bound); + featureRef.current.addLayer(bound); + bound.editing.enable(); + safeFitBounds(bound); + bindEditEvent(bound); + } + }, [control.status, bound]); + + useEffect(() => { + if (!featureRef.current) return; + + L.drawLocal.draw.handlers.polyline.tooltip.start = "برای شروع ترسیم مسیر، روی نقشه کلیک کنید"; + L.drawLocal.draw.handlers.polyline.tooltip.cont = "برای ادامه ترسیم، نقاط بعدی را کلیک کنید"; + L.drawLocal.draw.handlers.polyline.tooltip.end = "برای بستن مسیر، روی نقطه پایانی کلیک کنید"; + + const drawControl = new L.Control.Draw({ + draw: { + polyline: { + shapeOptions: { + color: "#3388ff", + weight: 3, + clickable: true, + }, + }, + polygon: false, + rectangle: false, + circle: false, + marker: false, + circlemarker: false, + }, + edit: { + featureGroup: featureRef.current, + edit: true, + remove: false, + }, + }); + + drawControlRef.current = drawControl; + map.addControl(drawControl); + map.on(L.Draw.Event.CREATED, handlerCreatedBound); + + return () => { + map.off(L.Draw.Event.CREATED, handlerCreatedBound); + map.removeControl(drawControl); + }; + }, [map, handlerCreatedBound]); + + return ; +}; + +export default DrawBound; diff --git a/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/Area/MapControlPolyline/index.jsx b/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/Area/MapControlPolyline/index.jsx new file mode 100644 index 0000000..edd6cac --- /dev/null +++ b/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/Area/MapControlPolyline/index.jsx @@ -0,0 +1,116 @@ +import { Delete, Route } from "@mui/icons-material"; +import { Box, Button, Stack, Typography } from "@mui/material"; +import { useReducer } from "react"; +import DrawBound from "./DrawBound"; + +const statusType = [ + { + id: 0, + message: "برای آغاز ترسیم مسیر، کلیک کنید!", + buttons: [ + { + label: "شروع ترسیم مسیر", + key: "start", + color: "primary", + icon: , + onclick: (controlDispach) => { + controlDispach({ type: "SET_STATUS", status: 1 }); + }, + }, + ], + }, + { + id: 1, + message: "مسیر موردنظر را با کلیک روی نقشه مشخص کنید. برای اتمام، روی نقطه‌ی پایانی کلیک کنید!", + buttons: [], + }, + { + id: 2, + message: "برای اصلاح مسیر، گوشه‌ها را بکشید. برای ترسیم دوباره، آن را حذف کنید", + buttons: [ + { + label: "حذف", + key: "end", + color: "error", + icon: , + onclick: (controlDispach) => { + controlDispach({ type: "SET_STATUS", status: 0 }); + }, + }, + ], + }, +]; + +const createInitialState = (bound) => { + if (bound) { + return { status: 2 }; + } + return { status: 0 }; +}; + +const reducer = (state, action) => { + switch (action.type) { + case "SET_STATUS": + return { ...state, status: action.status }; + default: + return state; + } +}; + +const MapControlPolyline = ({ bound, setBound }) => { + const [control, controlDispach] = useReducer(reducer, bound, createInitialState); + + return ( + <> + + + theme.palette.info.main, + borderBottomLeftRadius: 8, + borderBottomRightRadius: 8, + py: 0.5, + px: 2, + }} + > + + {statusType.find((st) => st.id == control.status).message} + + + + + + {statusType + .find((st) => st.id == control.status) + .buttons.map((button) => ( + + ))} + + + + ); +}; +export default MapControlPolyline; diff --git a/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/Area/SelectBoundType/index.jsx b/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/Area/SelectBoundType/index.jsx new file mode 100644 index 0000000..a69a0ba --- /dev/null +++ b/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/Area/SelectBoundType/index.jsx @@ -0,0 +1,25 @@ +import SelectBox from "@/core/components/SelectBox"; + +export const boundTypes = [ + { id: "polygon", name_fa: "محدوده" }, + { id: "polyline", name_fa: "مسیر" }, +]; + +const SelectBoundType = ({ boundType, setBoundType, setBound, setAllData }) => { + return ( + <> + { + setBound(null); + setBoundType(newValue); + setAllData({ bound_type: newValue }); + }} + /> + + ); +}; +export default SelectBoundType; diff --git a/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/Area/index.jsx b/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/Area/index.jsx new file mode 100644 index 0000000..91dd0d3 --- /dev/null +++ b/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/Area/index.jsx @@ -0,0 +1,54 @@ +import MapLoading from "@/core/components/MapLayer/Loading"; +import { Box, Button, DialogActions, DialogContent, Stack } from "@mui/material"; +import dynamic from "next/dynamic"; +import { useCallback, useState } from "react"; +import MapControlPolygon from "./MapControlPolygon"; +import MapControlPolyline from "./MapControlPolyline"; +import SelectBoundType from "./SelectBoundType"; +const MapLayer = dynamic(() => import("@/core/components/MapLayer"), { + loading: () => , + ssr: false, +}); + +const Area = ({ allData, setAllData, handlePrev, setTabState }) => { + const [bound, setBound] = useState(allData.bound); + const [boundType, setBoundType] = useState(allData.bound_type); + + const handleNext = useCallback(() => { + setAllData({ bound: bound }); + setTabState((s) => s + 1); + }, [bound]); + + return ( + <> + + + + + + {boundType == "polygon" ? ( + + ) : ( + + )} + + + + + + + + + + ); +}; +export default Area; diff --git a/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/GetDateTime/MissionDates/index.jsx b/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/GetDateTime/MissionDates/index.jsx new file mode 100644 index 0000000..c002df3 --- /dev/null +++ b/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/GetDateTime/MissionDates/index.jsx @@ -0,0 +1,119 @@ +import MuiDatePicker from "@/core/components/MuiDatePicker"; +import MuiTimePicker from "@/core/components/MuiTimePicker"; +import { makeDateTime } from "@/core/utils/makeDateTime"; +import { Grid } from "@mui/material"; +import { Controller, useWatch } from "react-hook-form"; + +const MissionDates = ({ control }) => { + const start_date = useWatch({ control, name: "start_date" }); + const end_date = useWatch({ control, name: "end_date" }); + const start_time = useWatch({ control, name: "start_time" }); + const end_time = useWatch({ control, name: "end_time" }); + + const startDateTime = makeDateTime(start_date, start_time); + const endDateTime = makeDateTime(end_date, end_time); + const now = new Date(); + + return ( + <> + + ( + field.onChange(value || null)} + helperText={error ? error.message : null} + /> + )} + /> + + + { + return ( + field.onChange(value || null)} + helperText={error ? error.message : null} + /> + ); + }} + /> + + + ( + field.onChange(value || null)} + helperText={error ? error.message : null} + /> + )} + /> + + + { + return ( + field.onChange(value || null)} + helperText={error ? error.message : null} + /> + ); + }} + /> + + + ); +}; + +export default MissionDates; diff --git a/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/GetDateTime/index.jsx b/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/GetDateTime/index.jsx new file mode 100644 index 0000000..9e35f41 --- /dev/null +++ b/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/GetDateTime/index.jsx @@ -0,0 +1,59 @@ +import SelectBox from "@/core/components/SelectBox"; +import StyledForm from "@/core/components/StyledForm"; +import { missionTypes } from "@/core/utils/missionTypes"; +import { yupResolver } from "@hookform/resolvers/yup"; +import { Box, Button, DialogActions, DialogContent, Grid, Stack } from "@mui/material"; +import moment from "jalali-moment"; +import { Controller, useForm } from "react-hook-form"; +import { object, string } from "yup"; +import MissionDates from "./MissionDates"; + +const validationSchema = object({ + start_date: string().required("تاریخ شروع ماموریت را مشخص کنید!"), + start_time: string().required("زمان شروع ماموریت را مشخص کنید!"), + end_date: string().required("تاریخ پایان ماموریت را مشخص کنید!"), + end_time: string().required("زمان شروع ماموریت را مشخص کنید!"), +}); + +const GetDateTime = ({ allData, setAllData, handlePrev, setTabState }) => { + const defaultValues = { + start_date: allData.start_date, + start_time: allData.start_time ? moment(allData.start_time).toDate() : null, + end_date: allData.end_date, + end_time: allData.end_time ? moment(allData.end_time).toDate() : null, + }; + + const { control, handleSubmit } = useForm({ + defaultValues, + resolver: yupResolver(validationSchema), + mode: "all", + }); + + const handleNext = (data) => { + setAllData(data); + setTabState((s) => s + 1); + }; + + return ( + + + + + + + + + + + + + + + + ); +}; +export default GetDateTime; diff --git a/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/GetItemInfo/FastReactCode/Dialog/List/RowActions/SelectId/index.jsx b/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/GetItemInfo/FastReactCode/Dialog/List/RowActions/SelectId/index.jsx new file mode 100644 index 0000000..1e7e881 --- /dev/null +++ b/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/GetItemInfo/FastReactCode/Dialog/List/RowActions/SelectId/index.jsx @@ -0,0 +1,20 @@ +import { Done } from "@mui/icons-material"; +import { IconButton, Tooltip } from "@mui/material"; + +const SelectId = ({ row, onChange, setOpenFastReactDialog }) => { + const handleClick = () => { + onChange(row.original.id); + setOpenFastReactDialog(false); + }; + + return ( + <> + + + + + + + ); +}; +export default SelectId; diff --git a/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/GetItemInfo/FastReactCode/Dialog/List/RowActions/index.jsx b/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/GetItemInfo/FastReactCode/Dialog/List/RowActions/index.jsx new file mode 100644 index 0000000..3d0b958 --- /dev/null +++ b/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/GetItemInfo/FastReactCode/Dialog/List/RowActions/index.jsx @@ -0,0 +1,10 @@ +import SelectId from "./SelectId"; + +const RowActions = ({ row, onChange, setOpenFastReactDialog }) => { + return ( + <> + + + ); +}; +export default RowActions; diff --git a/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/GetItemInfo/FastReactCode/Dialog/List/index.jsx b/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/GetItemInfo/FastReactCode/Dialog/List/index.jsx new file mode 100644 index 0000000..82a0472 --- /dev/null +++ b/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/GetItemInfo/FastReactCode/Dialog/List/index.jsx @@ -0,0 +1,274 @@ +import DataTableWithAuth from "@/core/components/DataTableWithAuth"; +import { GET_FAST_REACT_COMPLAINTS } from "@/core/utils/routes"; +import { Box, Stack, Typography } from "@mui/material"; +import { useMemo } from "react"; +import RowActions from "./RowActions"; +import { usePermissions } from "@/lib/hooks/usePermissions"; +import { useAuth } from "@/lib/contexts/auth"; +import CustomSelectByDependency from "@/core/components/DataTable/filter/fieldsType/CustomSelectByDependency"; +import DescriptionForm from "@/components/dashboard/fastReact/complaintList/RowActions/DescriptionDialog"; +import LocationForm from "@/components/dashboard/fastReact/complaintList/RowActions/LocationDialog"; +import moment from "jalali-moment"; +import useProvinces from "@/lib/hooks/useProvince"; +import useEdaratLists from "@/lib/hooks/useEdaratLists"; + +const FastReactList = ({ onChange, setOpenFastReactDialog }) => { + 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).locale("fa").format("HH:mm | yyyy/MM/DD"), + header: "تاریخ ثبت", + id: "StartTime_DateTime", + enableColumnFilter: true, + datatype: "date", + filterMode: "between", + grow: false, + size: 100, + }, + ], + }, + ]; + }, []); + + return ( + <> + + ( + + )} + /> + + + ); +}; +export default FastReactList; diff --git a/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/GetItemInfo/FastReactCode/Dialog/index.jsx b/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/GetItemInfo/FastReactCode/Dialog/index.jsx new file mode 100644 index 0000000..013f73f --- /dev/null +++ b/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/GetItemInfo/FastReactCode/Dialog/index.jsx @@ -0,0 +1,49 @@ +import { Close, Edit } from "@mui/icons-material"; +import { Button, Chip, Dialog, Divider, IconButton } from "@mui/material"; +import FastReactList from "./List"; +import { useState, forwardRef } from "react"; + +const FastReactDialog = forwardRef(({ onChange, value }, ref) => { + const [openFastReactDialog, setOpenFastReactDialog] = useState(false); + + return ( + <> + + {value === "" ? ( + + ) : ( + } + onDelete={() => setOpenFastReactDialog(true)} + /> + )} + + + setOpenFastReactDialog(false)} + sx={(theme) => ({ + position: "absolute", + right: 8, + top: 8, + zIndex: 50, + color: theme.palette.grey[500], + })} + > + + + {openFastReactDialog && ( + + )} + + + ); +}); + +FastReactDialog.displayName = "FastReactDialog"; + +export default FastReactDialog; diff --git a/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/GetItemInfo/FastReactCode/index.jsx b/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/GetItemInfo/FastReactCode/index.jsx new file mode 100644 index 0000000..1545046 --- /dev/null +++ b/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/GetItemInfo/FastReactCode/index.jsx @@ -0,0 +1,22 @@ +import { Divider, Stack } from "@mui/material"; +import { Controller, useWatch } from "react-hook-form"; +import FastReactDialog from "./Dialog"; + +const FastReactCode = ({ control }) => { + const category_id = useWatch({ control, name: "category_id" }); + + if (category_id != 3) return null; + + return ( + ( + + + + )} + /> + ); +}; +export default FastReactCode; diff --git a/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/GetItemInfo/index.jsx b/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/GetItemInfo/index.jsx new file mode 100644 index 0000000..3b3b513 --- /dev/null +++ b/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/GetItemInfo/index.jsx @@ -0,0 +1,153 @@ +import PersianTextField from "@/core/components/PersianTextField"; +import SelectBox from "@/core/components/SelectBox"; +import StyledForm from "@/core/components/StyledForm"; +import { missionCategoryTypes } from "@/core/utils/missionCategoryTypes"; +import { missionRegions } from "@/core/utils/missionRegions"; +import { yupResolver } from "@hookform/resolvers/yup"; +import { ExitToApp } from "@mui/icons-material"; +import { Box, Button, DialogActions, DialogContent, Grid, Stack } from "@mui/material"; +import { Controller, useForm } from "react-hook-form"; +import { object, string } from "yup"; +import FastReactCode from "./FastReactCode"; + +const validationSchema = object({ + explanation: string().required("موضوع را مشخص کنید!"), + end_point: string().required("مقصد را مشخص کنید!"), + region: string().required("محور ماموریت را مشخص کنید!"), + category_id: string().required("نوع ماموریت را مشخص کنید!"), + road_observed_id: string().when("category_id", { + is: "3", + then: (schema) => schema.required("کد یکتا شکایت را مشخص کنید!"), + otherwise: (schema) => schema.notRequired(), + }), +}); + +const GetItemInfo = ({ allData, setAllData, handlePrev, setTabState }) => { + const defaultValues = { + explanation: allData.explanation, + end_point: allData.end_point, + region: allData.region, + category_id: allData.category_id, + road_observed_id: allData.road_observed_id, + }; + + const { control, handleSubmit } = useForm({ + defaultValues, + resolver: yupResolver(validationSchema), + mode: "all", + }); + + const handleNext = (data) => { + setAllData(data); + setTabState((s) => s + 1); + }; + + return ( + + + + + + + ( + + )} + /> + + + ( + + )} + /> + + + ( + + )} + /> + + + + + + { + return ( + + ); + }} + /> + + + + + + + + + + + ); +}; +export default GetItemInfo; diff --git a/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/MachineAndDriver/DriversDialog/DriversSearch/Form/index.jsx b/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/MachineAndDriver/DriversDialog/DriversSearch/Form/index.jsx new file mode 100644 index 0000000..93bb1d6 --- /dev/null +++ b/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/MachineAndDriver/DriversDialog/DriversSearch/Form/index.jsx @@ -0,0 +1,61 @@ +import RahdarNameOrCode from "@/core/components/RahdarNameOrCode"; +import StyledForm from "@/core/components/StyledForm"; +import { yupResolver } from "@hookform/resolvers/yup"; +import { Done } from "@mui/icons-material"; +import { Button, Stack } from "@mui/material"; +import { Controller, useForm } from "react-hook-form"; +import { object } from "yup"; + +const schema = object().shape({ + rahdar: object().required("همراه الزامی است."), +}); +const DriverForm = ({ setDriver, setOpenDriversDialog }) => { + const defaultValues = { + rahdar: null, + }; + + const { + control, + handleSubmit, + formState: { isSubmitting, isValid }, + } = useForm({ defaultValues, resolver: yupResolver(schema), mode: "all" }); + + const submit = (data) => { + setDriver(data.rahdar); + setOpenDriversDialog(false); + }; + + return ( + + + + { + return ( + field.onChange(value)} + error={error} + multiple={false} + /> + ); + }} + name={"rahdar"} + /> + + + + + ); +}; +export default DriverForm; diff --git a/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/MachineAndDriver/DriversDialog/DriversSearch/index.jsx b/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/MachineAndDriver/DriversDialog/DriversSearch/index.jsx new file mode 100644 index 0000000..b7136c9 --- /dev/null +++ b/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/MachineAndDriver/DriversDialog/DriversSearch/index.jsx @@ -0,0 +1,11 @@ +import { DialogContent } from "@mui/material"; +import DriverForm from "./Form"; + +const DriversSearch = ({ setDriver, setOpenDriversDialog }) => { + return ( + + + + ); +}; +export default DriversSearch; diff --git a/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/MachineAndDriver/DriversDialog/index.jsx b/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/MachineAndDriver/DriversDialog/index.jsx new file mode 100644 index 0000000..3c1d406 --- /dev/null +++ b/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/MachineAndDriver/DriversDialog/index.jsx @@ -0,0 +1,41 @@ +import { Close, Edit } from "@mui/icons-material"; +import { Button, Dialog, DialogTitle, IconButton } from "@mui/material"; +import { useState } from "react"; +import DriversSearch from "./DriversSearch"; + +const DriversDialog = ({ setDriver, mode }) => { + const [openDriversDialog, setOpenDriversDialog] = useState(false); + return ( + <> + {mode == "edit" ? ( + setOpenDriversDialog(true)} size="small"> + + + ) : ( + + )} + + setOpenDriversDialog(false)} + sx={(theme) => ({ + position: "absolute", + right: 8, + top: 8, + zIndex: 50, + color: theme.palette.grey[500], + })} + > + + + انتخاب راننده + {openDriversDialog && ( + + )} + + + ); +}; +export default DriversDialog; diff --git a/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/MachineAndDriver/MachinesDialog/MachineList/RowActions/Allocate/index.jsx b/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/MachineAndDriver/MachinesDialog/MachineList/RowActions/Allocate/index.jsx new file mode 100644 index 0000000..f03579a --- /dev/null +++ b/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/MachineAndDriver/MachinesDialog/MachineList/RowActions/Allocate/index.jsx @@ -0,0 +1,20 @@ +import { Done } from "@mui/icons-material"; +import { IconButton, Tooltip } from "@mui/material"; + +const Allocate = ({ row, setMachine, setOpenMachinesDialog }) => { + const handleClick = () => { + setMachine(row.original); + setOpenMachinesDialog(false); + }; + + return ( + <> + + + + + + + ); +}; +export default Allocate; diff --git a/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/MachineAndDriver/MachinesDialog/MachineList/RowActions/index.jsx b/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/MachineAndDriver/MachinesDialog/MachineList/RowActions/index.jsx new file mode 100644 index 0000000..bcfcc03 --- /dev/null +++ b/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/MachineAndDriver/MachinesDialog/MachineList/RowActions/index.jsx @@ -0,0 +1,6 @@ +import Allocate from "./Allocate"; + +const RowActions = ({ row, setMachine, setOpenMachinesDialog }) => { + return ; +}; +export default RowActions; diff --git a/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/MachineAndDriver/MachinesDialog/MachineList/index.jsx b/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/MachineAndDriver/MachinesDialog/MachineList/index.jsx new file mode 100644 index 0000000..e070b01 --- /dev/null +++ b/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/MachineAndDriver/MachinesDialog/MachineList/index.jsx @@ -0,0 +1,68 @@ +import DataTableWithAuth from "@/core/components/DataTableWithAuth"; +import { Box } from "@mui/material"; +import { useMemo } from "react"; +import RowActions from "./RowActions"; +import { GET_MACHINES_TABLE_LIST } from "@/core/utils/routes"; + +const MachinesList = ({ machineType, setMachine, setOpenMachinesDialog }) => { + const columns = useMemo( + () => [ + { + accessorKey: "machine_code", + header: "کد خودرو", + id: "machine_code", + enableColumnFilter: true, + datatype: "text", + filterMode: "equals", + columnFilterModeOptions: ["equals", "contains"], + grow: false, + size: 100, + }, + { + accessorKey: "car_name", + header: "نام خودرو", + id: "car_name", + enableColumnFilter: true, + datatype: "text", + filterMode: "equals", + columnFilterModeOptions: ["equals", "contains"], + grow: false, + size: 100, + }, + { + accessorKey: "car_type", + header: "نوع خودرو", + id: "car_type", + enableColumnFilter: false, + datatype: "text", + filterMode: "equals", + columnFilterModeOptions: ["equals", "contains"], + grow: false, + size: 100, + }, + ], + [] + ); + + return ( + <> + + ( + + )} + /> + + + ); +}; +export default MachinesList; diff --git a/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/MachineAndDriver/MachinesDialog/index.jsx b/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/MachineAndDriver/MachinesDialog/index.jsx new file mode 100644 index 0000000..8750142 --- /dev/null +++ b/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/MachineAndDriver/MachinesDialog/index.jsx @@ -0,0 +1,40 @@ +import { Close, Edit } from "@mui/icons-material"; +import { Button, Dialog, IconButton } from "@mui/material"; +import { useState } from "react"; +import MachinesList from "./MachineList"; + +const MachinesDialog = ({ setMachine, mode }) => { + const [openMachinesDialog, setOpenMachinesDialog] = useState(false); + return ( + <> + {mode == "edit" ? ( + setOpenMachinesDialog(true)} size="small"> + + + ) : ( + + )} + + setOpenMachinesDialog(false)} + sx={(theme) => ({ + position: "absolute", + right: 8, + top: 8, + zIndex: 50, + color: theme.palette.grey[500], + })} + > + + + {openMachinesDialog && ( + + )} + + + ); +}; +export default MachinesDialog; diff --git a/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/MachineAndDriver/index.jsx b/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/MachineAndDriver/index.jsx new file mode 100644 index 0000000..24bfb15 --- /dev/null +++ b/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/MachineAndDriver/index.jsx @@ -0,0 +1,77 @@ +import { Box, Button, Chip, DialogActions, DialogContent, Divider, Stack, Typography } from "@mui/material"; +import DriversDialog from "./DriversDialog"; +import { useState } from "react"; +import MachinesDialog from "./MachinesDialog"; + +const MachineAndDriver = ({ setTabState, allData, setAllData, handlePrev }) => { + const [machine, setMachine] = useState(allData.machine); + const [driver, setDriver] = useState(allData.driver); + + const handleNext = (data) => { + setAllData(data); + setTabState((s) => s + 1); + }; + + return ( + <> + + + + + {machine ? ( + <> + خودرو + + + + + + ) : ( + + )} + + {machine && ( + + {driver ? ( + <> + راننده + + + + + + ) : ( + + )} + + )} + + + + + + + + + ); +}; +export default MachineAndDriver; diff --git a/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/Rahdaran/Form/index.jsx b/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/Rahdaran/Form/index.jsx new file mode 100644 index 0000000..a4449fe --- /dev/null +++ b/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/Rahdaran/Form/index.jsx @@ -0,0 +1,66 @@ +import RahdarNameOrCode from "@/core/components/RahdarNameOrCode"; +import StyledForm from "@/core/components/StyledForm"; +import { yupResolver } from "@hookform/resolvers/yup"; +import { Add } from "@mui/icons-material"; +import { Button, Stack } from "@mui/material"; +import { Controller, useForm } from "react-hook-form"; +import { object } from "yup"; + +const schema = object().shape({ + rahdar: object().required("همراه الزامی است."), +}); +const RahdaranForm = ({ setRahdaran }) => { + const defaultValues = { + rahdar: null, + }; + + const { + control, + handleSubmit, + reset, + formState: { isSubmitting, isValid }, + } = useForm({ defaultValues, resolver: yupResolver(schema), mode: "all" }); + + const submit = (data) => { + setRahdaran((prev) => { + const alreadyExists = prev.some((r) => r.id === data.rahdar.id); + if (alreadyExists) return prev; + return [...prev, data.rahdar]; + }); + reset(); + }; + + return ( + + + + { + return ( + field.onChange(value)} + error={error} + multiple={false} + /> + ); + }} + name={"rahdar"} + /> + + + + + ); +}; +export default RahdaranForm; diff --git a/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/Rahdaran/List/index.jsx b/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/Rahdaran/List/index.jsx new file mode 100644 index 0000000..ebc4edc --- /dev/null +++ b/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/Rahdaran/List/index.jsx @@ -0,0 +1,56 @@ +import { AccountCircle, Delete } from "@mui/icons-material"; +import { Card, Collapse, IconButton, Stack, Typography } from "@mui/material"; +import { TransitionGroup } from "react-transition-group"; + +const RahdaranList = ({ rahdaran, setRahdaran }) => { + const handleRemove = (index) => { + setRahdaran((prev) => prev.filter((_, i) => i !== index)); + }; + + return ( + + + {rahdaran.map((rahdar, index) => ( + + + + + + + نام و نام خانوادگی: + {rahdar.name} + + + کدملی: + {rahdar.code} + + + + handleRemove(index)}> + + + + + ))} + {rahdaran.length == 0 && ( + + همراهی ثبت نشده است + + )} + + + ); +}; +export default RahdaranList; diff --git a/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/Rahdaran/index.jsx b/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/Rahdaran/index.jsx new file mode 100644 index 0000000..af01fe6 --- /dev/null +++ b/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/Rahdaran/index.jsx @@ -0,0 +1,36 @@ +import { Box, Button, Chip, DialogActions, DialogContent, Divider } from "@mui/material"; +import { useCallback, useState } from "react"; +import RahdaranForm from "./Form"; +import RahdaranList from "./List"; + +const Rahdaran = ({ allData, setAllData, setTabState, handlePrev }) => { + const [rahdaran, setRahdaran] = useState(allData.rahdaran); + + const handleNext = useCallback(() => { + setAllData({ rahdaran: rahdaran }); + setTabState((s) => s + 1); + }, [rahdaran]); + + return ( + <> + + + + + + + + + + + + + + + ); +}; +export default Rahdaran; diff --git a/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/Verify/index.jsx b/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/Verify/index.jsx new file mode 100644 index 0000000..0393018 --- /dev/null +++ b/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/Verify/index.jsx @@ -0,0 +1,130 @@ +import MapLoading from "@/core/components/MapLayer/Loading"; +import { missionCategoryTypes } from "@/core/utils/missionCategoryTypes"; +import { missionRegions } from "@/core/utils/missionRegions"; +import { Box, Button, Chip, DialogActions, DialogContent, Divider, Stack, Typography } from "@mui/material"; +import moment from "jalali-moment"; +import dynamic from "next/dynamic"; +import { useCallback } from "react"; +import ShowBound from "../../../showBound"; +const MapLayer = dynamic(() => import("@/core/components/MapLayer"), { + loading: () => , + ssr: false, +}); + +const Verify = ({ allData, handlePrev, submitForm, submitting }) => { + const handleNext = useCallback(() => { + submitForm(allData); + }, [allData]); + + return ( + <> + + + + + + + + + + + + + + موضوع ماموریت + + + + + محدوده + + r.id == allData.region).name_fa} /> + + + نوع ماموریت + + t.id == allData.category_id).name_fa} + /> + + {allData.category_id == 3 && ( + + کد شکایت + + + + )} + + مقصد + + + + + تاریخ شروع ماموریت + + + + + تاریخ پایان ماموریت + + + + + + + + + + خودرو + + + + + راننده + + + + + + + + + {allData.rahdaran.length != 0 ? ( + allData.rahdaran.map((rahdar) => ( + + {rahdar.name} + + + + )) + ) : ( + + همراهی ثبت نشده است + + )} + + + + + + + + + + ); +}; +export default Verify; diff --git a/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/index.jsx b/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/index.jsx new file mode 100644 index 0000000..00f9ef1 --- /dev/null +++ b/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/index.jsx @@ -0,0 +1,125 @@ +import { AccessTime, Engineering, InsertDriveFile, LocalShipping, Route, Verified } from "@mui/icons-material"; +import { Box, Tab, Tabs } from "@mui/material"; +import { useReducer, useState } from "react"; +import Area from "./Area"; +import GetDateTime from "./GetDateTime"; +import GetItemInfo from "./GetItemInfo"; +import Rahdaran from "./Rahdaran"; +import Verify from "./Verify"; +import MachineAndDriver from "./MachineAndDriver"; + +function TabPanel(props) { + const { children, value, index } = props; + return ( + + ); +} + +const reducer = (state, action) => { + switch (action.type) { + case "changeData": + return { ...state, ...action.data }; + default: + return state; + } +}; + +const CreateForm = ({ defaultValues, submitForm, setOpen, submitting }) => { + const [allData, dispatch] = useReducer(reducer, defaultValues); + const [tabState, setTabState] = useState(0); + const handleClose = () => { + setOpen(false); + }; + const handleChangeTab = (event, newValue) => { + setTabState(newValue); + }; + + const handlePrev = () => { + if (tabState === 0) { + handleClose(); + } else { + setTabState((t) => t - 1); + } + }; + + return ( + <> + + } label="مشخصات" /> + } label="زمانبندی" /> + } label="منطقه عملیاتی" /> + } label="خودرو و راننده" /> + } label="همراهان" /> + } label="بررسی نهایی" /> + + + { + dispatch({ type: "changeData", data }); + }} + handlePrev={handlePrev} + setTabState={setTabState} + /> + + + { + dispatch({ type: "changeData", data }); + }} + handlePrev={handlePrev} + setTabState={setTabState} + /> + + + { + dispatch({ type: "changeData", data }); + }} + handlePrev={handlePrev} + setTabState={setTabState} + /> + + + { + dispatch({ type: "changeData", data }); + }} + handlePrev={handlePrev} + setTabState={setTabState} + /> + + + { + dispatch({ type: "changeData", data }); + }} + handlePrev={handlePrev} + setTabState={setTabState} + /> + + + + + + ); +}; +export default CreateForm; diff --git a/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/index.jsx b/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/index.jsx new file mode 100644 index 0000000..ab64c85 --- /dev/null +++ b/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/index.jsx @@ -0,0 +1,121 @@ +import { REQUEST_MISSION_WITHOUT_PROCESS } from "@/core/utils/routes"; +import useRequest from "@/lib/hooks/useRequest"; +import { AddCircle, AddCircleOutline, Close } from "@mui/icons-material"; +import { Button, Dialog, IconButton, useMediaQuery, useTheme } from "@mui/material"; +import moment from "jalali-moment"; +import { useState } from "react"; +import CreateForm from "./Form"; + +const CreateWithoutProcess = ({ mutate }) => { + const [submitting, setSubmitting] = useState(false); + const requestServer = useRequest({ notificationSuccess: true }); + const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down("sm")); + const [open, setOpen] = useState(false); + + const handleOpen = () => { + setOpen(true); + }; + + const submitForm = async (result) => { + setSubmitting(true); + const bound = result.bound.getLatLngs(); + let area = + result.bound_type == "polygon" + ? bound.map((ring) => ring.map((latlng) => [latlng.lat, latlng.lng]))[0] + : bound.map((latlng) => [latlng.lat, latlng.lng]); + + await requestServer(REQUEST_MISSION_WITHOUT_PROCESS, "post", { + data: { + explanation: result.explanation, + category_id: result.category_id, + ...(result.category_id == 3 + ? { + road_observed_id: result.road_observed_id, + } + : {}), + area: { + type: result.bound_type, + coordinates: area, + }, + ...(result.rahdaran.length != 0 ? { rahdaran: result.rahdaran.map((r) => r.id) } : {}), + zone: result.region, + end_point: result.end_point, + start_date: `${result.start_date} ${moment(result.start_time).format("HH:mm")}`, + end_date: `${result.end_date} ${moment(result.end_time).format("HH:mm")}`, + machines: [result.machine.id], + driver: result.driver.id, + }, + hasSidebarUpdate: true, + }) + .then((response) => { + mutate(); + setOpen(false); + }) + .catch((error) => {}) + .finally(() => { + setSubmitting(false); + }); + }; + + return ( + <> + {isMobile ? ( + + + + ) : ( + + )} + + setOpen(false)} + sx={(theme) => ({ + position: "absolute", + right: 8, + top: 8, + zIndex: 50, + color: theme.palette.grey[500], + })} + > + + + {open && ( + + )} + + + ); +}; +export default CreateWithoutProcess; diff --git a/src/components/dashboard/roadMissions/operator/OperatorList.jsx b/src/components/dashboard/roadMissions/operator/OperatorList.jsx index df9528e..9625838 100644 --- a/src/components/dashboard/roadMissions/operator/OperatorList.jsx +++ b/src/components/dashboard/roadMissions/operator/OperatorList.jsx @@ -227,6 +227,28 @@ const OperatorList = () => { ); }, }, + { + accessorFn: (row) => + row.start_time ? moment(row.start_time).locale("fa").format("HH:mm | yyyy/MM/DD") : null, + header: "تاریخ خروج", + id: "start_time", + enableColumnFilter: true, + datatype: "date", + filterMode: "between", + grow: false, + size: 100, + }, + { + accessorFn: (row) => + row.finish_time ? moment(row.finish_time).locale("fa").format("HH:mm | yyyy/MM/DD") : null, + header: "تاریخ ورود", + id: "finish_time", + enableColumnFilter: true, + datatype: "date", + filterMode: "between", + grow: false, + size: 100, + }, { accessorFn: (row) => moment(row.request_date).locale("fa").format("HH:mm | yyyy/MM/DD"), header: "تاریخ ثبت", diff --git a/src/components/dashboard/roadMissions/operator/Toolbar.jsx b/src/components/dashboard/roadMissions/operator/Toolbar.jsx index 5f49db7..9ddee3a 100644 --- a/src/components/dashboard/roadMissions/operator/Toolbar.jsx +++ b/src/components/dashboard/roadMissions/operator/Toolbar.jsx @@ -1,10 +1,12 @@ import { Stack } from "@mui/material"; import Create from "./Actions/Create"; +import CreateWithoutProcess from "./Actions/CreateWithoutProcess"; const Toolbar = ({ table, filterData, mutate }) => { return ( + ); }; diff --git a/src/components/dashboard/roadMissions/transportation/RowActions/Allocation/Form/index.jsx b/src/components/dashboard/roadMissions/transportation/RowActions/Allocation/Form/index.jsx index e3e9c3b..e06d06b 100644 --- a/src/components/dashboard/roadMissions/transportation/RowActions/Allocation/Form/index.jsx +++ b/src/components/dashboard/roadMissions/transportation/RowActions/Allocation/Form/index.jsx @@ -93,7 +93,7 @@ const AllocationForm = ({ row, mutate, setOpenAllocationDialog }) => { { return ( - + {/* */} ); }; diff --git a/src/components/infrastructure/tollHouse/Form/CreateTollHouse/CreateTollHouseContent.jsx b/src/components/infrastructure/tollHouse/Form/CreateTollHouse/CreateTollHouseContent.jsx index bb3cb1b..a19dfa5 100644 --- a/src/components/infrastructure/tollHouse/Form/CreateTollHouse/CreateTollHouseContent.jsx +++ b/src/components/infrastructure/tollHouse/Form/CreateTollHouse/CreateTollHouseContent.jsx @@ -7,13 +7,15 @@ import OtherHousesIcon from "@mui/icons-material/OtherHouses"; import LocalShippingIcon from "@mui/icons-material/LocalShipping"; import EditLocationAltIcon from "@mui/icons-material/EditLocationAlt"; import React, { useState } from "react"; -import { useForm } from "react-hook-form"; +import { Controller, useForm } from "react-hook-form"; import { yupResolver } from "@hookform/resolvers/yup"; -import { mixed, number, object, string } from "yup"; +import { array, mixed, number, object, string } from "yup"; import TollHouseInfo from "./TollHouseInfo"; import KeyboardDoubleArrowLeftIcon from "@mui/icons-material/KeyboardDoubleArrowLeft"; import MachineInfo from "@/components/infrastructure/tollHouse/Form/CreateTollHouse/MachineInfo"; import TollHouseLocation from "@/components/infrastructure/tollHouse/Form/CreateTollHouse/TollHouseLocation"; +import { Route } from "@mui/icons-material"; +import TollHouseArea from "./TollHouseArea"; function TabPanel(props) { const { children, value, index } = props; @@ -54,6 +56,23 @@ const validationSchema = object({ return !!value; }) .required("لطفاً محل پروژه را مشخص کنید!"), + area: array() + .of( + object().shape({ + lat: number() + .typeError("عرض جغرافیایی باید یک عدد معتبر باشد") + .required("عرض جغرافیایی الزامی است") + .min(25, "عرض جغرافیایی نمی‌تواند کمتر از 25 باشد") + .max(40, "عرض جغرافیایی نمی‌تواند بیشتر از 40 باشد"), + lon: number("وارد کردن طول جغرافیایی الزامیست") + .typeError("طول جغرافیایی باید یک عدد معتبر باشد") + .required("طول جغرافیایی الزامی است") + .min(44, "طول جغرافیایی نمی‌تواند کمتر از 44 باشد") + .max(64, "طول جغرافیایی نمی‌تواند بیشتر از 64 باشد"), + }) + ) + .min(4, "حداقل باید ۴ نقطه وجود داشته باشد") + .required("محدوده الزامی است"), }); const CreateTollHouseContent = ({ setOpen, SubmitCreateTollHouse, defaultValues }) => { const [tabState, setTabState] = useState(0); @@ -137,8 +156,9 @@ const CreateTollHouseContent = ({ setOpen, SubmitCreateTollHouse, defaultValues }} > } label="اطلاعات راهدارخانه" /> - } label="ماشین آلات" /> - } label="موقعیت" /> + } label="ماشین آلات" /> + } label="موقعیت" /> + } label="محدوده" /> @@ -151,6 +171,15 @@ const CreateTollHouseContent = ({ setOpen, SubmitCreateTollHouse, defaultValues + + ( + + )} + /> + @@ -163,7 +192,7 @@ const CreateTollHouseContent = ({ setOpen, SubmitCreateTollHouse, defaultValues > {tabState === 0 ? "بستن" : "مرحله قبل"} - {tabState !== 2 && ( + {tabState !== 3 && ( + + + + + ); +}; +export default TollHouseArea; diff --git a/src/components/infrastructure/tollHouse/Form/CreateTollHouse/TollHouseInfo.jsx b/src/components/infrastructure/tollHouse/Form/CreateTollHouse/TollHouseInfo.jsx index 22e3fdc..dc56843 100644 --- a/src/components/infrastructure/tollHouse/Form/CreateTollHouse/TollHouseInfo.jsx +++ b/src/components/infrastructure/tollHouse/Form/CreateTollHouse/TollHouseInfo.jsx @@ -106,9 +106,9 @@ const TollHouseInfo = ({ control }) => { value={field.value} label="نوع" selectors={[ - { id: 0, name: "داعمی" }, - { id: 1, name: "موقت" }, - { id: 2, name: "فصلی" }, + { id: 1, name: "دائمی" }, + { id: 2, name: "موقت" }, + { id: 3, name: "فصلی" }, ]} schema={{ name: "name", value: "id" }} error={error} diff --git a/src/components/infrastructure/tollHouse/Form/CreateTollHouse/index.jsx b/src/components/infrastructure/tollHouse/Form/CreateTollHouse/index.jsx index 7dbe3d5..a9cb7cf 100644 --- a/src/components/infrastructure/tollHouse/Form/CreateTollHouse/index.jsx +++ b/src/components/infrastructure/tollHouse/Form/CreateTollHouse/index.jsx @@ -51,6 +51,12 @@ const CreateTollHouse = ({ mutate }) => { }); formData.append("lat", result.start_point.lat); formData.append("lng", result.start_point.lng); + formData.append("area[type]", "polygon"); + result.area.map((point, pointIndex) => { + formData.append(`area[coordinates][${pointIndex}][0]`, point.lat); + formData.append(`area[coordinates][${pointIndex}][1]`, point.lon); + }); + await requestServer(`${CREATE_TOLL_HOUSE}`, "post", { data: formData, }) @@ -83,6 +89,12 @@ const CreateTollHouse = ({ mutate }) => { overview_files_4: null, overview_files_5: null, start_point: "", + area: [ + { lat: "", lon: "" }, + { lat: "", lon: "" }, + { lat: "", lon: "" }, + { lat: "", lon: "" }, + ], }; return ( <> diff --git a/src/components/infrastructure/tollHouse/Form/Edit/EditController.jsx b/src/components/infrastructure/tollHouse/Form/Edit/EditController.jsx index a9d4281..58b108b 100644 --- a/src/components/infrastructure/tollHouse/Form/Edit/EditController.jsx +++ b/src/components/infrastructure/tollHouse/Form/Edit/EditController.jsx @@ -47,6 +47,12 @@ const EditController = ({ rowId, mutate, setOpenEditDialog }) => { overview_files_3: tollHouseItemDetails?.files[2]?.path || null, overview_files_4: tollHouseItemDetails?.files[3]?.path || null, overview_files_5: tollHouseItemDetails?.files[4]?.path || null, + area: tollHouseItemDetails?.area?.coordinates.map(([lat, lon]) => ({ lat, lon })) || [ + { lat: "", lon: "" }, + { lat: "", lon: "" }, + { lat: "", lon: "" }, + { lat: "", lon: "" }, + ], }; const HandleSubmit = async (result) => { const fields = [ @@ -79,6 +85,11 @@ const EditController = ({ rowId, mutate, setOpenEditDialog }) => { } formData.append("lat", result.start_point.lat); formData.append("lng", result.start_point.lng); + formData.append("area[type]", "polygon"); + result.area.map((point, pointIndex) => { + formData.append(`area[coordinates][${pointIndex}][0]`, point.lat); + formData.append(`area[coordinates][${pointIndex}][1]`, point.lon); + }); await requestServer(`${UPDATE_TOLL_HOUSE_ITEM}/${rowId}`, "post", { data: formData, diff --git a/src/components/infrastructure/tollHouse/TollHouseList.jsx b/src/components/infrastructure/tollHouse/TollHouseList.jsx index 113dcbc..4ef1674 100644 --- a/src/components/infrastructure/tollHouse/TollHouseList.jsx +++ b/src/components/infrastructure/tollHouse/TollHouseList.jsx @@ -12,6 +12,7 @@ import useEdaratLists from "@/lib/hooks/useEdaratLists"; import { usePermissions } from "@/lib/hooks/usePermissions"; import LocationForm from "./RowActions/LocationForm"; import ImageDialog from "./RowActions/ImageForm"; +import { useAuth } from "@/lib/contexts/auth"; const statusOptions = [ { value: "", label: "همه وضعیت ها" }, @@ -21,14 +22,51 @@ const statusOptions = [ const typeOptions = [ { value: "", label: "همه نوع ها" }, { value: 0, label: "فصلی" }, - { value: 1, label: "داعمی" }, + { value: 1, label: "دائمی" }, { value: 2, label: "موقت" }, ]; const TollHouseList = () => { const { data: userPermissions } = usePermissions(); - const hasProvincePermission = userPermissions.some((item) => ["add-tollhouse"].includes(item)); + const hasCountryPermission = userPermissions?.includes("show-tollhouse"); + const { user } = useAuth(); const columns = useMemo(() => { + const provinceColumn = { + 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_name}, + }; return [ { accessorKey: "id", @@ -64,42 +102,7 @@ const TollHouseList = () => { grow: false, size: 100, }, - { - 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_name}, - }, + ...(hasCountryPermission ? [provinceColumn] : []), { accessorKey: "city_id", header: "شهرستان", // Office @@ -107,17 +110,19 @@ const TollHouseList = () => { enableColumnFilter: true, datatype: "numeric", filterMode: "equals", - dependencyId: "province_id", + dependencyId: hasCountryPermission ? "province_id" : null, grow: false, size: 120, ColumnSelectComponent: (props) => { const { edaratList, loadingEdaratList, errorEdaratList } = useEdaratLists( - props.dependencyFieldValue.value + hasCountryPermission ? props.dependencyFieldValue.value : user.province_id + ); + const [prevDependency, setPrevDependency] = useState( + hasCountryPermission ? props.dependencyFieldValue.value : user.province_id ); - const [prevDependency, setPrevDependency] = useState(props.dependencyFieldValue.value); const getColumnSelectOptions = useMemo(() => { - if (props.dependencyFieldValue.value === "") { + if (hasCountryPermission && props.dependencyFieldValue.value === "") { return [{ value: "empty", label: "ابتدا استان را انتخاب کنید" }]; } if (loadingEdaratList) { @@ -135,10 +140,11 @@ const TollHouseList = () => { ]; }, [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]); + }, [props.dependencyFieldValue?.value, hasCountryPermission]); return ( { return {row.original.type_fa}; }, }, + { + header: "موقعیت", // Location + 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: ({ renderedCellValue, row }) => { + return ( + + + + ); + }, + }, { accessorKey: "phone", header: "تلفن راهدارخانه", @@ -352,31 +383,6 @@ const TollHouseList = () => { }, ], }, - { - header: "موقعیت", // Location - 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: ({ renderedCellValue, row }) => { - return ( - - - - ); - }, - }, { accessorFn: (row) => moment(row.created_at).locale("fa").format("HH:mm | yyyy/MM/DD"), header: "تاریخ ثبت", @@ -402,7 +408,7 @@ const TollHouseList = () => { sorting={[{ id: "id", desc: true }]} TableToolbar={Toolbar} enableRowActions - positionActionsColumn={"last"} + positionActionsColumn={"first"} RowActions={RowActions} /> diff --git a/src/core/components/MuiDatePicker.jsx b/src/core/components/MuiDatePicker.jsx index cb2abc5..7c8dddd 100644 --- a/src/core/components/MuiDatePicker.jsx +++ b/src/core/components/MuiDatePicker.jsx @@ -6,7 +6,18 @@ import { Box, FormHelperText, IconButton, InputAdornment } from "@mui/material"; import ClearIcon from "@mui/icons-material/Clear"; import moment from "jalali-moment"; -function MuiDatePicker({ value, setFieldValue, name, minDate, maxDate, helperText, placeholder, error, label }) { +function MuiDatePicker({ + value, + disableFuture = true, + setFieldValue, + name, + minDate, + maxDate, + helperText, + placeholder, + error, + label, +}) { return ( <> @@ -20,7 +31,7 @@ function MuiDatePicker({ value, setFieldValue, name, minDate, maxDate, helperTex id={name} name={name} closeOnSelect - disableFuture + disableFuture={disableFuture} label={label} aria-describedby="component-helper-text" onChange={(value) => { diff --git a/src/core/utils/machineTypes.js b/src/core/utils/machineTypes.js index e2188bc..5a5ae78 100644 --- a/src/core/utils/machineTypes.js +++ b/src/core/utils/machineTypes.js @@ -1,4 +1,119 @@ export const machineType = [ + { id: "آسفالت تراش", name_fa: "آسفالت تراش" }, + { id: "اتوبوس", name_fa: "اتوبوس" }, + { id: "اتومیکسر", name_fa: "اتومیکسر" }, + { id: "آمبولانس", name_fa: "آمبولانس" }, + { id: "اسپریدر کششی", name_fa: "اسپریدر کششی" }, + { id: "اسنوتراک", name_fa: "اسنوتراک" }, + { id: "بتونیر", name_fa: "بتونیر" }, + { id: "برفخور", name_fa: "برفخور" }, + { id: "برفخور لودری", name_fa: "برفخور لودری" }, + { id: "بلدوزر", name_fa: "بلدوزر" }, + { id: "بیل بکهو", name_fa: "بیل بکهو" }, + { id: "بیل مکانیکی", name_fa: "بیل مکانیکی" }, + { id: "بیل مکانیکی چرخ لاستیکی", name_fa: "بیل مکانیکی چرخ لاستیکی" }, + { id: "تانکر آبرسان", name_fa: "تانکر آبرسان" }, + { id: "تانکر آب", name_fa: "تانکر آب" }, + { id: "تانکر سوخت", name_fa: "تانکر سوخت" }, + { id: "تانکر قیر", name_fa: "تانکر قیر" }, + { id: "تراکتور", name_fa: "تراکتور" }, + { id: "تراگریدر", name_fa: "تراگریدر" }, + { id: "تریلر آبپاش", name_fa: "تریلر آبپاش" }, + { id: "تریلر بونکر", name_fa: "تریلر بونکر" }, + { id: "تریلر حمل قیر", name_fa: "تریلر حمل قیر" }, + { id: "تریلر سوخت رسان", name_fa: "تریلر سوخت رسان" }, + { id: "تریلر کارواش", name_fa: "تریلر کارواش" }, + { id: "تریلر کمپرسی", name_fa: "تریلر کمپرسی" }, + { id: "تیغ برفروب کامیونی", name_fa: "تیغ برفروب کامیونی" }, + { id: "تیغ برفروب لودری", name_fa: "تیغ برفروب لودری" }, + { id: "تیغه تراکتوری", name_fa: "تیغه تراکتوری" }, + { id: "جرثقیل", name_fa: "جرثقیل" }, + { id: "چکش هیدرولیکی", name_fa: "چکش هیدرولیکی" }, + { id: "رله تانک قیر", name_fa: "رله تانک قیر" }, + { id: "سواری بیابانی", name_fa: "سواری بیابانی" }, + { id: "سواری شهری", name_fa: "سواری شهری" }, + { id: "سنگ شکن", name_fa: "سنگ شکن" }, + { id: "سنگ شکن ثابت پارکر", name_fa: "سنگ شکن ثابت پارکر" }, + { id: "سنگ شکن سیار پارکر", name_fa: "سنگ شکن سیار پارکر" }, + { id: "سنگ شکن تلمیت", name_fa: "سنگ شکن تلمیت" }, + { id: "ضربه گیر پشت کامیونی", name_fa: "ضربه گیر پشت کامیونی" }, + { id: "غلتک", name_fa: "غلتک" }, + { id: "غلتک استاتیک", name_fa: "غلتک استاتیک" }, + { id: "غلتک پاچه بزی", name_fa: "غلتک پاچه بزی" }, + { id: "غلتک چرخ آهنی", name_fa: "غلتک چرخ آهنی" }, + { id: "غلتک چرخ لاستیکی", name_fa: "غلتک چرخ لاستیکی" }, + { id: "غلتک دستی", name_fa: "غلتک دستی" }, + { id: "غلتک کششی", name_fa: "غلتک کششی" }, + { id: "غلتک ویبره", name_fa: "غلتک ویبره" }, + { id: "غلطک دستی", name_fa: "غلطک دستی" }, + { id: "غلطک کششی", name_fa: "غلطک کششی" }, + { id: "فینیشر", name_fa: "فینیشر" }, + { id: "قیرپاش کششی", name_fa: "قیرپاش کششی" }, + { id: "کانتینر", name_fa: "کانتینر" }, + { id: "کانکس", name_fa: "کانکس" }, + { id: "کارخانه آسفالت", name_fa: "کارخانه آسفالت" }, + { id: "کارواش ثابت", name_fa: "کارواش ثابت" }, + { id: "کامیون آتشنشانی", name_fa: "کامیون آتشنشانی" }, + { id: "کامیون آب نمک پاش", name_fa: "کامیون آب نمک پاش" }, + { id: "کامیون آبپاش", name_fa: "کامیون آبپاش" }, + { id: "کامیون بالابر", name_fa: "کامیون بالابر" }, + { id: "کامیون باری", name_fa: "کامیون باری" }, + { id: "کامیون برفخور", name_fa: "کامیون برفخور" }, + { id: "کامیون برفروب", name_fa: "کامیون برفروب" }, + { id: "کامیون جاروب", name_fa: "کامیون جاروب" }, + { id: "کامیون جرثقیل", name_fa: "کامیون جرثقیل" }, + { id: "کامیون خط کش", name_fa: "کامیون خط کش" }, + { id: "کامیون فلاسک آسفالت", name_fa: "کامیون فلاسک آسفالت" }, + { id: "کامیون قیرپاش", name_fa: "کامیون قیرپاش" }, + { id: "کامیون کفی", name_fa: "کامیون کفی" }, + { id: "کامیون کمپرسی", name_fa: "کامیون کمپرسی" }, + { id: "کامیون کشنده", name_fa: "کامیون کشنده" }, + { id: "کامیون گاردریل شور", name_fa: "کامیون گاردریل شور" }, + { id: "کامیون میکسر", name_fa: "کامیون میکسر" }, + { id: "کامیون نمکپاش", name_fa: "کامیون نمکپاش" }, + { id: "کامیون کارواش", name_fa: "کامیون کارواش" }, + { id: "کامیونت", name_fa: "کامیونت" }, + { id: "کامیونت آتشنشانی", name_fa: "کامیونت آتشنشانی" }, + { id: "کامیونت بالابر", name_fa: "کامیونت بالابر" }, + { id: "کامیونت باری", name_fa: "کامیونت باری" }, + { id: "کامیونت تانکر آّب", name_fa: "کامیونت تانکر آّب" }, + { id: "کامیونت جرثقیل", name_fa: "کامیونت جرثقیل" }, + { id: "کامیونت جاروب", name_fa: "کامیونت جاروب" }, + { id: "کامیونت خط کش", name_fa: "کامیونت خط کش" }, + { id: "کامیونت سوخت رسان", name_fa: "کامیونت سوخت رسان" }, + { id: "کامیونت عملیات", name_fa: "کامیونت عملیات" }, + { id: "کامیونت کمپرسی", name_fa: "کامیونت کمپرسی" }, + { id: "کفی", name_fa: "کفی" }, + { id: "کمرشکن", name_fa: "کمرشکن" }, + { id: "کمپرسور", name_fa: "کمپرسور" }, + { id: "کوبیت ثابت", name_fa: "کوبیت ثابت" }, { id: "گریدر", name_fa: "گریدر" }, + { id: "لکه گیر آسفالت", name_fa: "لکه گیر آسفالت" }, { id: "لودر", name_fa: "لودر" }, + { id: "لیفتراک", name_fa: "لیفتراک" }, + { id: "ماسه ساز", name_fa: "ماسه ساز" }, + { id: "مینی بوس", name_fa: "مینی بوس" }, + { id: "مینی بیل", name_fa: "مینی بیل" }, + { id: "مینی لودر", name_fa: "مینی لودر" }, + { id: "مولد برق", name_fa: "مولد برق" }, + { id: "موتور جوش", name_fa: "موتور جوش" }, + { id: "موتور کارواش", name_fa: "موتور کارواش" }, + { id: "موتورسیکلت", name_fa: "موتورسیکلت" }, + { id: "نمک پاش", name_fa: "نمک پاش" }, + { id: "نمک پاش ثابت", name_fa: "نمک پاش ثابت" }, + { id: "وانت آتشنشانی", name_fa: "وانت آتشنشانی" }, + { id: "وانت تک کابین", name_fa: "وانت تک کابین" }, + { id: "وانت تک کابین بیابانی", name_fa: "وانت تک کابین بیابانی" }, + { id: "وانت تک کابین شهری", name_fa: "وانت تک کابین شهری" }, + { id: "وانت دو کابین شهری", name_fa: "وانت دو کابین شهری" }, + { id: "وانت دوکابین بیابانی", name_fa: "وانت دوکابین بیابانی" }, + { id: "ون", name_fa: "ون" }, + { id: "ویبراتور بتون", name_fa: "ویبراتور بتون" }, + { id: "پمپ کف کش", name_fa: "پمپ کف کش" }, + { id: "تراک میکسر", name_fa: "تراک میکسر" }, + { id: "ترانس جوش", name_fa: "ترانس جوش" }, + { id: "تجهیزات ثابت", name_fa: "تجهیزات ثابت" }, + { id: "دریل واگن", name_fa: "دریل واگن" }, + { id: "کامیون سرویس سیار", name_fa: "کامیون سرویس سیار" }, + { id: "میکسر آب نمک", name_fa: "میکسر آب نمک" }, ]; diff --git a/src/core/utils/makeDateTime.js b/src/core/utils/makeDateTime.js new file mode 100644 index 0000000..645a93c --- /dev/null +++ b/src/core/utils/makeDateTime.js @@ -0,0 +1,9 @@ +export const makeDateTime = (date, time) => { + if (!date) return null; + const d = new Date(date); + if (time) { + const t = new Date(time); + d.setHours(t.getHours(), t.getMinutes(), 0, 0); + } + return d; +}; diff --git a/src/core/utils/missionCategoryTypes.js b/src/core/utils/missionCategoryTypes.js index d0711ad..10ee11b 100644 --- a/src/core/utils/missionCategoryTypes.js +++ b/src/core/utils/missionCategoryTypes.js @@ -1,5 +1,5 @@ export const missionCategoryTypes = [ { id: 1, name_fa: "فعالیت روزانه" }, { id: 2, name_fa: "گشت راهداری" }, - { id: 3, name_fa: "واکنش سریع" }, + // { id: 3, name_fa: "واکنش سریع" }, ]; diff --git a/src/core/utils/pageMenuDev.js b/src/core/utils/pageMenuDev.js index 118dacb..b4ea525 100644 --- a/src/core/utils/pageMenuDev.js +++ b/src/core/utils/pageMenuDev.js @@ -580,7 +580,7 @@ export const pageMenuDev = [ type: "page", route: "/dashboard/rahdaran", icon: , - permissions: ["all"], + permissions: ["show-rahdaran"], }, { id: "adminManagement", diff --git a/src/core/utils/routes.js b/src/core/utils/routes.js index da56c9e..75eb864 100644 --- a/src/core/utils/routes.js +++ b/src/core/utils/routes.js @@ -214,6 +214,7 @@ export const GET_ROAD_MISSIONS_OPERATOR_LIST = api + "/api/v3/missions/request_p export const GET_ROAD_MISSIONS_TRANSPORTATION_LIST = api + "/api/v3/missions/transportation_unit"; export const GET_ROAD_MISSIONS_CONTROL_LIST = api + "/api/v3/missions/control_unit"; export const REQUEST_MISSION = api + "/api/v3/missions/request_portal"; +export const REQUEST_MISSION_WITHOUT_PROCESS = api + "/api/v3/missions/request_portal/no_process"; export const UPDATE_REQUEST_MISSION = api + "/api/v3/missions/request_portal"; export const DELETE_REQUEST_MISSION = api + "/api/v3/missions/request_portal"; export const GET_RAHDARAN_BY_ID = api + "/api/v3/missions/details/rahdaran";