From fb84d5a188accd305f3d3921fea1f91c8223a7a2 Mon Sep 17 00:00:00 2001 From: baslani Date: Sun, 13 Jul 2025 16:51:43 +0330 Subject: [PATCH 1/4] implemented rahdaran's page --- .../dashboard/rahdaran/page.js | 10 ++ .../dashboard/road-missions/operator/page.js | 8 +- .../Actions/Create/Form/CreateFormContent.jsx | 105 ++++++++++++++++++ .../rahdaran/Actions/Create/Form/index.jsx | 57 ++++++++++ .../rahdaran/Actions/Create/index.jsx | 36 ++++++ .../rahdaran/Actions/Edit/EditController.jsx | 37 ++++++ .../dashboard/rahdaran/Actions/Edit/index.jsx | 51 +++++++++ .../dashboard/rahdaran/RahdaranList.jsx | 69 ++++++++++++ .../RowActions/DeleteDialog/DeleteContent.jsx | 39 +++++++ .../RowActions/DeleteDialog/index.jsx | 34 ++++++ .../dashboard/rahdaran/RowActions/index.jsx | 12 ++ src/components/dashboard/rahdaran/Toolbar.jsx | 9 ++ src/components/dashboard/rahdaran/index.jsx | 13 +++ .../operator/Actions/Create/index.jsx | 14 +-- .../RowActions/Edit/EditController/index.jsx | 14 +-- .../NotificationDesign/AskForKeepData.jsx | 2 +- src/core/utils/pageMenu.js | 6 +- src/core/utils/pageMenuDev.js | 57 ++++++---- src/core/utils/routes.js | 6 + 19 files changed, 540 insertions(+), 39 deletions(-) create mode 100644 src/app/(withAuth)/(dashboardLayout)/dashboard/rahdaran/page.js create mode 100644 src/components/dashboard/rahdaran/Actions/Create/Form/CreateFormContent.jsx create mode 100644 src/components/dashboard/rahdaran/Actions/Create/Form/index.jsx create mode 100644 src/components/dashboard/rahdaran/Actions/Create/index.jsx create mode 100644 src/components/dashboard/rahdaran/Actions/Edit/EditController.jsx create mode 100644 src/components/dashboard/rahdaran/Actions/Edit/index.jsx create mode 100644 src/components/dashboard/rahdaran/RahdaranList.jsx create mode 100644 src/components/dashboard/rahdaran/RowActions/DeleteDialog/DeleteContent.jsx create mode 100644 src/components/dashboard/rahdaran/RowActions/DeleteDialog/index.jsx create mode 100644 src/components/dashboard/rahdaran/RowActions/index.jsx create mode 100644 src/components/dashboard/rahdaran/Toolbar.jsx create mode 100644 src/components/dashboard/rahdaran/index.jsx diff --git a/src/app/(withAuth)/(dashboardLayout)/dashboard/rahdaran/page.js b/src/app/(withAuth)/(dashboardLayout)/dashboard/rahdaran/page.js new file mode 100644 index 0000000..67311d9 --- /dev/null +++ b/src/app/(withAuth)/(dashboardLayout)/dashboard/rahdaran/page.js @@ -0,0 +1,10 @@ +import RahdaranTablePage from "@/components/dashboard/rahdaran"; +import WithPermission from "@/core/middlewares/withPermission"; + +export default function page() { + return ( + + + + ); +} diff --git a/src/app/(withAuth)/(dashboardLayout)/dashboard/road-missions/operator/page.js b/src/app/(withAuth)/(dashboardLayout)/dashboard/road-missions/operator/page.js index a788616..417937a 100644 --- a/src/app/(withAuth)/(dashboardLayout)/dashboard/road-missions/operator/page.js +++ b/src/app/(withAuth)/(dashboardLayout)/dashboard/road-missions/operator/page.js @@ -7,7 +7,13 @@ export const metadata = { const Page = () => { return ( <> - + {/* */} diff --git a/src/components/dashboard/rahdaran/Actions/Create/Form/CreateFormContent.jsx b/src/components/dashboard/rahdaran/Actions/Create/Form/CreateFormContent.jsx new file mode 100644 index 0000000..74a2e76 --- /dev/null +++ b/src/components/dashboard/rahdaran/Actions/Create/Form/CreateFormContent.jsx @@ -0,0 +1,105 @@ +import PersianTextField from "@/core/components/PersianTextField"; +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 { Controller, useForm } from "react-hook-form"; +import { object, string } from "yup"; + +const validationSchema = object({ + name: string().required("وارد کردن نام فارسی الزامیست!"), + code: string() + .required("لطفا کد ملی را وارد کنید!!!") + .test("max", "کد ملی باید شامل 10 رقم باشد", (value) => value.toString().length === 10) + .test("validation", "کد ملی صحیح نمی باشد", (value) => validateNationalCode(value)), +}); + +const CreateFormContent = ({ setOpen, defaultValues, onSubmitBase }) => { + const { + control, + handleSubmit, + formState: { isSubmitting }, + } = useForm({ + defaultValues, + resolver: yupResolver(validationSchema), + mode: "all", + }); + const handleOnSubmit = async (data) => { + await onSubmitBase(data); + }; + + return ( + <> + + + + + + + ( + + )} + /> + + + + + + + ( + + )} + /> + + + + + + + + + + + + ); +}; +export default CreateFormContent; diff --git a/src/components/dashboard/rahdaran/Actions/Create/Form/index.jsx b/src/components/dashboard/rahdaran/Actions/Create/Form/index.jsx new file mode 100644 index 0000000..5cb292d --- /dev/null +++ b/src/components/dashboard/rahdaran/Actions/Create/Form/index.jsx @@ -0,0 +1,57 @@ +import { Dialog, DialogTitle, IconButton } from "@mui/material"; +import CreateFormContent from "./CreateFormContent"; +import CloseIcon from "@mui/icons-material/Close"; +import useRequest from "@/lib/hooks/useRequest"; +import { CREATE_RAHDARAN_ITEM } from "@/core/utils/routes"; +import { DialogHeader } from "next/dist/client/components/react-dev-overlay/internal/components/Dialog"; + +const CreateForm = ({ open, setOpen, mutate }) => { + const requestServer = useRequest({ notificationSuccess: true }); + const defaultValues = { + name: "", + code: "", + }; + const onSubmit = async (result) => { + const formData = new FormData(); + formData.append("name", result.name); + formData.append("code", result.code); + + await requestServer(CREATE_RAHDARAN_ITEM, "post", { + data: formData, + }) + .then(() => { + mutate(); + setOpen(false); + }) + .catch(() => {}); + }; + return ( + + setOpen(false)} + sx={(theme) => ({ + position: "absolute", + right: 8, + top: 8, + zIndex: 50, + color: theme.palette.grey[500], + })} + > + + + + ثبت راهدار جدید + + {open && ( + + )} + + ); +}; +export default CreateForm; diff --git a/src/components/dashboard/rahdaran/Actions/Create/index.jsx b/src/components/dashboard/rahdaran/Actions/Create/index.jsx new file mode 100644 index 0000000..8417fd5 --- /dev/null +++ b/src/components/dashboard/rahdaran/Actions/Create/index.jsx @@ -0,0 +1,36 @@ +import { AddCircle } from "@mui/icons-material"; +import { Button, IconButton, useMediaQuery, useTheme } from "@mui/material"; +import CreateForm from "./Form"; +import { useState } from "react"; + +const CreateRahdar = ({ mutate }) => { + const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down("sm")); + const [open, setOpen] = useState(false); + + const handleOpen = () => { + setOpen(true); + }; + + return ( + <> + {isMobile ? ( + + + + ) : ( + + )} + + + ); +}; +export default CreateRahdar; diff --git a/src/components/dashboard/rahdaran/Actions/Edit/EditController.jsx b/src/components/dashboard/rahdaran/Actions/Edit/EditController.jsx new file mode 100644 index 0000000..61bb336 --- /dev/null +++ b/src/components/dashboard/rahdaran/Actions/Edit/EditController.jsx @@ -0,0 +1,37 @@ +import { UPDATE_RAHDARAN_ITEM } from "@/core/utils/routes"; +import useRequest from "@/lib/hooks/useRequest"; +import { DialogTitle } from "@mui/material"; +import { DialogHeader } from "next/dist/client/components/react-dev-overlay/internal/components/Dialog"; +import CreateFormContent from "../Create/Form/CreateFormContent"; + +const EditController = ({ rowId, mutate, setOpen, row }) => { + const requestServer = useRequest({ notificationSuccess: true }); + + const defaultData = { + name: row.original?.name || "", + code: row.original?.code || "", + }; + const handleSubmit = async (result) => { + const formData = new FormData(); + formData.append("name", result.name); + formData.append("code", result.code); + + await requestServer(`${UPDATE_RAHDARAN_ITEM}/${rowId}`, "post", { + data: formData, + }) + .then(() => { + mutate(); + setOpen(false); + }) + .catch(() => {}); + }; + return ( + <> + + ویرایش راهدار + + + + ); +}; +export default EditController; diff --git a/src/components/dashboard/rahdaran/Actions/Edit/index.jsx b/src/components/dashboard/rahdaran/Actions/Edit/index.jsx new file mode 100644 index 0000000..2a47c4c --- /dev/null +++ b/src/components/dashboard/rahdaran/Actions/Edit/index.jsx @@ -0,0 +1,51 @@ +import BorderColorIcon from "@mui/icons-material/BorderColor"; +import CloseIcon from "@mui/icons-material/Close"; +import { Dialog, IconButton, Tooltip } from "@mui/material"; +import { useState } from "react"; +import EditController from "./EditController"; + +const EditRahdar = ({ mutate, row, rowId }) => { + const [open, setOpen] = useState(false); + + return ( + <> + + { + setOpen(true); + }} + > + + + + + setOpen(false)} + sx={(theme) => ({ + position: "absolute", + right: 8, + top: 8, + zIndex: 50, + color: theme.palette.grey[500], + })} + > + + + {open && } + + + ); +}; +export default EditRahdar; diff --git a/src/components/dashboard/rahdaran/RahdaranList.jsx b/src/components/dashboard/rahdaran/RahdaranList.jsx new file mode 100644 index 0000000..90df9e9 --- /dev/null +++ b/src/components/dashboard/rahdaran/RahdaranList.jsx @@ -0,0 +1,69 @@ +"use client"; +import DataTableWithAuth from "@/core/components/DataTableWithAuth"; +import { GET_RAHDARAN_TABLE_LIST } from "@/core/utils/routes"; +import { Box } from "@mui/material"; +import { useMemo } from "react"; +import RowActions from "./RowActions"; +import Toolbar from "./Toolbar"; + +const RahdaranList = () => { + const columns = useMemo( + () => [ + { + accessorKey: "id", + header: "کد یکتا", + id: "id", + enableColumnFilter: true, + datatype: "text", + filterMode: "equals", + sortDescFirst: false, + columnFilterModeOptions: ["equals", "contains"], + grow: false, + size: 100, + }, + { + accessorKey: "name", + header: "نام و نام خانوادگی", + id: "name", + enableColumnFilter: true, + datatype: "text", + filterMode: "equals", + columnFilterModeOptions: ["equals", "contains"], + grow: false, + size: 100, + }, + { + accessorKey: "code", + header: "کد ملی", + id: "code", + enableColumnFilter: true, + datatype: "text", + filterMode: "equals", + columnFilterModeOptions: ["equals", "contains"], + grow: false, + size: 100, + }, + ], + [] + ); + + return ( + <> + + + + + ); +}; +export default RahdaranList; diff --git a/src/components/dashboard/rahdaran/RowActions/DeleteDialog/DeleteContent.jsx b/src/components/dashboard/rahdaran/RowActions/DeleteDialog/DeleteContent.jsx new file mode 100644 index 0000000..ef1c300 --- /dev/null +++ b/src/components/dashboard/rahdaran/RowActions/DeleteDialog/DeleteContent.jsx @@ -0,0 +1,39 @@ +import { Button, DialogActions, DialogContent, Stack, Typography } from "@mui/material"; +import React, { useState } from "react"; +import useRequest from "@/lib/hooks/useRequest"; +import { DELETE_RAHDARAN_ITEM } from "@/core/utils/routes"; + +const DeleteContent = ({ rowId, mutate, setOpenDeleteDialog }) => { + const [submitting, setSubmitting] = useState(false); + const requestServer = useRequest({ notificationSuccess: true }); + const handleClick = () => { + setSubmitting(true); + requestServer(`${DELETE_RAHDARAN_ITEM}/${rowId}`, "delete") + .then(() => { + mutate(); + setSubmitting(false); + setOpenDeleteDialog(false); + }) + .catch(() => { + setSubmitting(false); + }); + }; + return ( + <> + + + آیا از حذف راهدار اطمینان دارید؟ + + + + + + + + ); +}; +export default DeleteContent; diff --git a/src/components/dashboard/rahdaran/RowActions/DeleteDialog/index.jsx b/src/components/dashboard/rahdaran/RowActions/DeleteDialog/index.jsx new file mode 100644 index 0000000..3cf143b --- /dev/null +++ b/src/components/dashboard/rahdaran/RowActions/DeleteDialog/index.jsx @@ -0,0 +1,34 @@ +import DeleteIcon from "@mui/icons-material/Delete"; +import { Dialog, DialogTitle, IconButton, Tooltip } from "@mui/material"; +import { useState } from "react"; +import DeleteContent from "./DeleteContent"; +const DeleteDialog = ({ rowId, mutate }) => { + const [openDeleteDialog, setOpenDeleteDialog] = useState(false); + + return ( + <> + + setOpenDeleteDialog(true)}> + + + + setOpenDeleteDialog(false)} + PaperProps={{ + sx: { + boxShadow: "rgba(60, 64, 67, 0.3) 0px 1px 16px 0px, rgba(60, 64, 67, 0.15) 0px 1px 10px 0px", + }, + }} + dir="rtl" + maxWidth={"md"} + > + حذف راهدار + {openDeleteDialog && ( + + )} + + + ); +}; +export default DeleteDialog; diff --git a/src/components/dashboard/rahdaran/RowActions/index.jsx b/src/components/dashboard/rahdaran/RowActions/index.jsx new file mode 100644 index 0000000..88602aa --- /dev/null +++ b/src/components/dashboard/rahdaran/RowActions/index.jsx @@ -0,0 +1,12 @@ +import { Box } from "@mui/material"; +import EditRahdar from "../Actions/Edit"; +import DeleteDialog from "./DeleteDialog"; +const RowActions = ({ row, mutate }) => { + return ( + + + + + ); +}; +export default RowActions; diff --git a/src/components/dashboard/rahdaran/Toolbar.jsx b/src/components/dashboard/rahdaran/Toolbar.jsx new file mode 100644 index 0000000..18b0315 --- /dev/null +++ b/src/components/dashboard/rahdaran/Toolbar.jsx @@ -0,0 +1,9 @@ +import CreateRahdar from "./Actions/Create"; +const Toolbar = ({ mutate }) => { + return ( + <> + + + ); +}; +export default Toolbar; diff --git a/src/components/dashboard/rahdaran/index.jsx b/src/components/dashboard/rahdaran/index.jsx new file mode 100644 index 0000000..bfcce4d --- /dev/null +++ b/src/components/dashboard/rahdaran/index.jsx @@ -0,0 +1,13 @@ +import PageTitle from "@/core/components/PageTitle"; +import { Stack } from "@mui/material"; +import RahdaranList from "./RahdaranList"; + +const RahdaranTablePage = () => { + return ( + + + + + ); +}; +export default RahdaranTablePage; diff --git a/src/components/dashboard/roadMissions/operator/Actions/Create/index.jsx b/src/components/dashboard/roadMissions/operator/Actions/Create/index.jsx index 236e38c..27d3e0c 100644 --- a/src/components/dashboard/roadMissions/operator/Actions/Create/index.jsx +++ b/src/components/dashboard/roadMissions/operator/Actions/Create/index.jsx @@ -32,13 +32,13 @@ const Create = ({ mutate }) => { end_point: result.end_point, ...(result.type == 1 ? { - start_date: `${result.start_date} ${moment(result.start_time).format("HH:mm")}`, - end_date: `${result.end_date} ${moment(result.end_time).format("HH:mm")}`, - } + start_date: `${result.start_date} ${moment(result.start_time).format("HH:mm")}`, + end_date: `${result.end_date} ${moment(result.end_time).format("HH:mm")}`, + } : { - start_date: result.start_date, - end_date: result.end_date, - }), + start_date: result.start_date, + end_date: result.end_date, + }), }, hasSidebarUpdate: true, }) @@ -46,7 +46,7 @@ const Create = ({ mutate }) => { mutate(); setOpen(false); }) - .catch((error) => { }) + .catch((error) => {}) .finally(() => { setSubmitting(false); }); diff --git a/src/components/dashboard/roadMissions/operator/RowActions/Edit/EditController/index.jsx b/src/components/dashboard/roadMissions/operator/RowActions/Edit/EditController/index.jsx index 612bac4..6fd0c7f 100644 --- a/src/components/dashboard/roadMissions/operator/RowActions/Edit/EditController/index.jsx +++ b/src/components/dashboard/roadMissions/operator/RowActions/Edit/EditController/index.jsx @@ -40,13 +40,13 @@ const EditController = ({ row, mutate, openEditDialog, setOpenEditDialog }) => { end_point: result.end_point, ...(result.type == 1 ? { - start_date: `${result.start_date} ${moment(result.start_time).format("HH:mm")}`, - end_date: `${result.end_date} ${moment(result.end_time).format("HH:mm")}`, - } + start_date: `${result.start_date} ${moment(result.start_time).format("HH:mm")}`, + end_date: `${result.end_date} ${moment(result.end_time).format("HH:mm")}`, + } : { - start_date: result.start_date, - end_date: result.end_date, - }), + start_date: result.start_date, + end_date: result.end_date, + }), }, hasSidebarUpdate: true, notificationSuccess: true, @@ -55,7 +55,7 @@ const EditController = ({ row, mutate, openEditDialog, setOpenEditDialog }) => { mutate(); setOpenEditDialog(false); }) - .catch((error) => { }) + .catch((error) => {}) .finally(() => { setSubmitting(false); }); diff --git a/src/core/components/NotificationDesign/AskForKeepData.jsx b/src/core/components/NotificationDesign/AskForKeepData.jsx index e6fa9f3..7bedb50 100644 --- a/src/core/components/NotificationDesign/AskForKeepData.jsx +++ b/src/core/components/NotificationDesign/AskForKeepData.jsx @@ -13,7 +13,7 @@ function filterFalseValues(obj) { for (const [key, value] of Object.entries(obj)) { if (value === false) { result[key] = false; - } else if (typeof value === 'object' && value !== null && !Array.isArray(value)) { + } else if (typeof value === "object" && value !== null && !Array.isArray(value)) { const nested = filterFalseValues(value); if (Object.keys(nested).length > 0) { result[key] = nested; diff --git a/src/core/utils/pageMenu.js b/src/core/utils/pageMenu.js index 7a3c1ea..acffb1d 100644 --- a/src/core/utils/pageMenu.js +++ b/src/core/utils/pageMenu.js @@ -103,7 +103,11 @@ export const pageMenu = [ label: "کارتابل", type: "page", route: "/dashboard/road-missions/operator", - permissions: ["manage-request-portal-country", "manage-request-portal-province", "manage-request-portal-city"], + permissions: [ + "manage-request-portal-country", + "manage-request-portal-province", + "manage-request-portal-city", + ], }, { id: "roadMissionsManagemnetNaqlie", diff --git a/src/core/utils/pageMenuDev.js b/src/core/utils/pageMenuDev.js index 74977f9..63c4ac1 100644 --- a/src/core/utils/pageMenuDev.js +++ b/src/core/utils/pageMenuDev.js @@ -1,28 +1,29 @@ -import SpaceDashboardIcon from "@mui/icons-material/SpaceDashboard"; -import GroupsIcon from "@mui/icons-material/Groups"; +import { Mediation } from "@mui/icons-material"; import AccountTreeIcon from "@mui/icons-material/AccountTree"; -import FactCheckIcon from "@mui/icons-material/FactCheck"; -import FireTruckIcon from "@mui/icons-material/FireTruck"; -import RouteIcon from "@mui/icons-material/Route"; -import DoorbellIcon from "@mui/icons-material/Doorbell"; -import ElectricBoltIcon from "@mui/icons-material/ElectricBolt"; -import CottageIcon from "@mui/icons-material/Cottage"; -import GppMaybeIcon from "@mui/icons-material/GppMaybe"; -import GavelIcon from "@mui/icons-material/Gavel"; -import BallotIcon from "@mui/icons-material/Ballot"; -import AssistantIcon from "@mui/icons-material/Assistant"; import AdminPanelSettingsIcon from "@mui/icons-material/AdminPanelSettings"; -import MapIcon from "@mui/icons-material/Map"; -import SpeedIcon from "@mui/icons-material/Speed"; -import EngineeringIcon from "@mui/icons-material/Engineering"; import AssessmentIcon from "@mui/icons-material/Assessment"; import AssignmentLateIcon from "@mui/icons-material/AssignmentLate"; -import LockPersonIcon from "@mui/icons-material/LockPerson"; -import LineAxisIcon from "@mui/icons-material/LineAxis"; -import ScienceIcon from "@mui/icons-material/Science"; -import DriveEtaIcon from "@mui/icons-material/DriveEta"; +import AssistantIcon from "@mui/icons-material/Assistant"; +import BallotIcon from "@mui/icons-material/Ballot"; import BiotechIcon from "@mui/icons-material/Biotech"; -import { Mediation } from "@mui/icons-material"; +import CottageIcon from "@mui/icons-material/Cottage"; +import DoorbellIcon from "@mui/icons-material/Doorbell"; +import DriveEtaIcon from "@mui/icons-material/DriveEta"; +import ElectricBoltIcon from "@mui/icons-material/ElectricBolt"; +import EngineeringIcon from "@mui/icons-material/Engineering"; +import FactCheckIcon from "@mui/icons-material/FactCheck"; +import FireTruckIcon from "@mui/icons-material/FireTruck"; +import GavelIcon from "@mui/icons-material/Gavel"; +import GppMaybeIcon from "@mui/icons-material/GppMaybe"; +import GroupsIcon from "@mui/icons-material/Groups"; +import HandymanIcon from "@mui/icons-material/Handyman"; +import LineAxisIcon from "@mui/icons-material/LineAxis"; +import LockPersonIcon from "@mui/icons-material/LockPerson"; +import MapIcon from "@mui/icons-material/Map"; +import RouteIcon from "@mui/icons-material/Route"; +import ScienceIcon from "@mui/icons-material/Science"; +import SpaceDashboardIcon from "@mui/icons-material/SpaceDashboard"; +import SpeedIcon from "@mui/icons-material/Speed"; export const pageMenuDev = [ { @@ -102,7 +103,11 @@ export const pageMenuDev = [ label: "کارتابل", type: "page", route: "/dashboard/road-missions/operator", - permissions: ["manage-request-portal-country", "manage-request-portal-province", "manage-request-portal-city"], + permissions: [ + "manage-request-portal-country", + "manage-request-portal-province", + "manage-request-portal-city", + ], }, { id: "roadMissionsManagemnetNaqlie", @@ -535,6 +540,14 @@ export const pageMenuDev = [ icon: , permissions: ["azmayesh-type-management"], }, + { + id: "rahdaran", + label: "راهداران", + type: "page", + route: "/dashboard/rahdaran", + icon: , + permissions: ["all"], + }, { id: "adminManagement", label: "مدیریت سامانه", @@ -572,7 +585,7 @@ export const pageMenuDev = [ type: "page", route: "/dashboard/car-details", icon: , - permissions: [""], + permissions: ["show-rahdaran"], }, ], }, diff --git a/src/core/utils/routes.js b/src/core/utils/routes.js index 741127c..d65335c 100644 --- a/src/core/utils/routes.js +++ b/src/core/utils/routes.js @@ -191,3 +191,9 @@ export const START_MISSION = api + "/api/v3/missions/control_unit/start"; export const FINISH_MISSION = api + "/api/v3/missions/control_unit/finish"; export const GET_MACHINES_TABLE_LIST = api + "/api/v3/cmms_machines"; + +// rahdaran +export const GET_RAHDARAN_TABLE_LIST = api + "/api/v3/rahdaran"; +export const DELETE_RAHDARAN_ITEM = api + "/api/v3/rahdaran"; +export const UPDATE_RAHDARAN_ITEM = api + "/api/v3/rahdaran"; +export const CREATE_RAHDARAN_ITEM = api + "/api/v3/rahdaran"; From ae06be8f02bb6dcf7699a6eac9cf8ba31e0409cc Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Tue, 22 Jul 2025 13:08:53 +0000 Subject: [PATCH 2/4] Feature/amiriis qr for factor --- package.json | 1 + .../Form/CreateFactor/CreateFactorContent.jsx | 67 +++++++++++++++---- .../headerWithLogo/HaederBottom/index.jsx | 23 +++---- .../dashboard/headerWithSidebar/index.jsx | 4 +- 4 files changed, 68 insertions(+), 27 deletions(-) diff --git a/package.json b/package.json index 45edef7..a4532cb 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "react-leaflet": "^4.2.1", "react-leaflet-draw": "^0.20.4", "react-leaflet-markercluster": "^4.2.1", + "react-qr-code": "^2.0.18", "react-toastify": "^10.0.5", "sass": "^1.75.0", "sharp": "^0.33.4", diff --git a/src/components/dashboard/damages/operator/Form/CreateFactor/CreateFactorContent.jsx b/src/components/dashboard/damages/operator/Form/CreateFactor/CreateFactorContent.jsx index b538199..fe8380b 100644 --- a/src/components/dashboard/damages/operator/Form/CreateFactor/CreateFactorContent.jsx +++ b/src/components/dashboard/damages/operator/Form/CreateFactor/CreateFactorContent.jsx @@ -17,6 +17,7 @@ import { Typography, } from "@mui/material"; import { useReducer, useState } from "react"; +import QRCode from "react-qr-code"; const initialState = { submittingCreateFactor: false, @@ -147,15 +148,53 @@ const CreateFactorContent = ({ row, mutate, rowId, setOpenCreateFactorDialog }) ریال - {!factorCreated && ( + {factorCreated ? ( + + + + {row.original.is_foreign ? ( + <> + + می‌توانید برای پرداخت، بارکد را اسکن کنید + + + ) : ( + <> + + لینک پرداخت به شماره همراه مورد نظر ارسال شد + + + همچنین می‌توانید برای پرداخت، بارکد را اسکن کنید + + + )} + + + + + + + ) : ( پس از صدور فاکتور، امکان ویرایش مبالغ بیمه و داغی وجود نخواهد داشت - - لینک پرداخت فاکتور به شماره {row.original?.driver_phone_number} ارسال می شود. - + {!row.original.is_foreign && ( + + لینک پرداخت فاکتور به شماره {row.original?.driver_phone_number} ارسال می شود. + + )} )} @@ -172,15 +211,17 @@ const CreateFactorContent = ({ row, mutate, rowId, setOpenCreateFactorDialog }) > بستن - + {!row.original.is_foreign && ( + + )} + + + + ); +}; +export default MachinesDialog; diff --git a/src/components/dashboard/roadMissions/control/RowActions/Rahdaran/RahdaranContent.jsx b/src/components/dashboard/roadMissions/control/RowActions/Rahdaran/RahdaranContent.jsx new file mode 100644 index 0000000..46001aa --- /dev/null +++ b/src/components/dashboard/roadMissions/control/RowActions/Rahdaran/RahdaranContent.jsx @@ -0,0 +1,74 @@ +import DialogLoading from "@/core/components/DialogLoading"; +import { GET_RAHDARAN_BY_ID } from "@/core/utils/routes"; +import useRequest from "@/lib/hooks/useRequest"; +import { + DialogContent, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + Typography, +} from "@mui/material"; +import { useEffect, useState } from "react"; + +const RahdaranContent = ({ rowId }) => { + const [loading, setLoading] = useState(true); + const [data, setData] = useState(null); + const request = useRequest(); + + useEffect(() => { + const fetchData = async () => { + try { + setLoading(true); + const response = await request(`${GET_RAHDARAN_BY_ID}/${rowId}`); + setData(response.data.data); + } catch (error) { + } finally { + setLoading(false); + } + }; + + fetchData(); + }, [rowId]); + + return ( + <> + + {loading ? ( + + ) : data ? ( + + + + + کد ملی + نام + نقش + + + + {data.map((rahdar) => { + return ( + + {rahdar.code} + {rahdar.name} + {rahdar.is_driver ? "راننده" : "همراه"} + + ); + })} + +
+
+ ) : ( + اطلاعات راهداران در سامانه یافت نشد + )} +
+ + ); +}; +export default RahdaranContent; diff --git a/src/components/dashboard/roadMissions/control/RowActions/Rahdaran/index.jsx b/src/components/dashboard/roadMissions/control/RowActions/Rahdaran/index.jsx new file mode 100644 index 0000000..b27009d --- /dev/null +++ b/src/components/dashboard/roadMissions/control/RowActions/Rahdaran/index.jsx @@ -0,0 +1,38 @@ +import { Button, Dialog, DialogActions, DialogTitle, IconButton, Tooltip } from "@mui/material"; +import GroupsIcon from "@mui/icons-material/Groups"; +import { useState } from "react"; +import RahdaranContent from "./RahdaranContent"; + +const RahdaranDialog = ({ rowId }) => { + const [openRahdaranDialog, setOpenRahdaranDialog] = useState(false); + return ( + <> + + setOpenRahdaranDialog(true)}> + + + + setOpenRahdaranDialog(false)} + PaperProps={{ + sx: { + boxShadow: "rgba(60, 64, 67, 0.3) 0px 1px 16px 0px, rgba(60, 64, 67, 0.15) 0px 1px 10px 0px", + }, + }} + dir="rtl" + maxWidth={"sm"} + > + لیست افراد + {openRahdaranDialog && } + + + + + + ); +}; +export default RahdaranDialog; diff --git a/src/components/dashboard/roadMissions/control/RowActions/StartMission/StartMissionContent.jsx b/src/components/dashboard/roadMissions/control/RowActions/StartMission/StartMissionContent.jsx index 6aa5e78..c7f516d 100644 --- a/src/components/dashboard/roadMissions/control/RowActions/StartMission/StartMissionContent.jsx +++ b/src/components/dashboard/roadMissions/control/RowActions/StartMission/StartMissionContent.jsx @@ -1,38 +1,87 @@ +import LtrTextField from "@/core/components/LtrTextField"; import { START_MISSION } from "@/core/utils/routes"; import useRequest from "@/lib/hooks/useRequest"; +import { yupResolver } from "@hookform/resolvers/yup"; import { Button, DialogActions, DialogContent, Stack, Typography } from "@mui/material"; -import { useState } from "react"; +import { Controller, useForm } from "react-hook-form"; +import { object, string } from "yup"; const StartMissionContent = ({ rowId, mutate, setOpenStartMissionDialog }) => { - const [submitting, setSubmitting] = useState(false); const requestServer = useRequest({ notificationSuccess: true }); - const handleClick = () => { - setSubmitting(true); + + const validationSchema = object().shape({ + code: string().length(6, "کد خروج باید 6 رقم باشد").required("لطفاً کد خروج را وارد کنید."), + }); + + const { + control, + handleSubmit, + formState: { isSubmitting }, + } = useForm({ + defaultValues: { + code: "", + }, + resolver: yupResolver(validationSchema), + mode: "all", + }); + + const onSubmit = (values) => { requestServer(`${START_MISSION}/${rowId}`, "post", { + data: { + code: values.code, + }, hasSidebarUpdate: true, }) .then(() => { mutate(); setOpenStartMissionDialog(false); - setSubmitting(false); }) - .catch(() => { - setSubmitting(false); - }); + .catch(() => {}); }; + return ( <> - + آیا از خروج خودرو و شروع ماموریت اطمینان دارید؟ + + ( + { + if (isNaN(Number(e.target.value))) return; + e.target.value.length <= 6 ? field.onChange(e.target.value) : null; + }} + type="tel" + autoComplete="off" + fullWidth + variant="outlined" + InputLabelProps={{ shrink: true }} + /> + )} + /> + - - diff --git a/src/components/dashboard/roadMissions/operator/Actions/Create/Form/Area/MapControl/DrawBound/index.jsx b/src/components/dashboard/roadMissions/operator/Actions/Create/Form/Area/MapControlPolygon/DrawBound/index.jsx similarity index 90% rename from src/components/dashboard/roadMissions/operator/Actions/Create/Form/Area/MapControl/DrawBound/index.jsx rename to src/components/dashboard/roadMissions/operator/Actions/Create/Form/Area/MapControlPolygon/DrawBound/index.jsx index 639f8fb..a2134c5 100644 --- a/src/components/dashboard/roadMissions/operator/Actions/Create/Form/Area/MapControl/DrawBound/index.jsx +++ b/src/components/dashboard/roadMissions/operator/Actions/Create/Form/Area/MapControlPolygon/DrawBound/index.jsx @@ -1,116 +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 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(); - safeFlyToBounds(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; +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/Create/Form/Area/MapControl/index.jsx b/src/components/dashboard/roadMissions/operator/Actions/Create/Form/Area/MapControlPolygon/index.jsx similarity index 93% rename from src/components/dashboard/roadMissions/operator/Actions/Create/Form/Area/MapControl/index.jsx rename to src/components/dashboard/roadMissions/operator/Actions/Create/Form/Area/MapControlPolygon/index.jsx index bdd1c97..186a0b6 100644 --- a/src/components/dashboard/roadMissions/operator/Actions/Create/Form/Area/MapControl/index.jsx +++ b/src/components/dashboard/roadMissions/operator/Actions/Create/Form/Area/MapControlPolygon/index.jsx @@ -1,116 +1,116 @@ -import { Box, Button, Stack, Typography } from "@mui/material"; -import DrawBound from "./DrawBound"; -import { Delete, Edit, Route } from "@mui/icons-material"; -import { useReducer } from "react"; - -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 MapControl = ({ 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 MapControl; +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/Create/Form/Area/MapControlPolyline/DrawBound/index.jsx b/src/components/dashboard/roadMissions/operator/Actions/Create/Form/Area/MapControlPolyline/DrawBound/index.jsx new file mode 100644 index 0000000..682be24 --- /dev/null +++ b/src/components/dashboard/roadMissions/operator/Actions/Create/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/Create/Form/Area/MapControlPolyline/index.jsx b/src/components/dashboard/roadMissions/operator/Actions/Create/Form/Area/MapControlPolyline/index.jsx new file mode 100644 index 0000000..edd6cac --- /dev/null +++ b/src/components/dashboard/roadMissions/operator/Actions/Create/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/Create/Form/Area/SelectBoundType/index.jsx b/src/components/dashboard/roadMissions/operator/Actions/Create/Form/Area/SelectBoundType/index.jsx new file mode 100644 index 0000000..a69a0ba --- /dev/null +++ b/src/components/dashboard/roadMissions/operator/Actions/Create/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/Create/Form/Area/index.jsx b/src/components/dashboard/roadMissions/operator/Actions/Create/Form/Area/index.jsx index aaa229c..3d666fd 100644 --- a/src/components/dashboard/roadMissions/operator/Actions/Create/Form/Area/index.jsx +++ b/src/components/dashboard/roadMissions/operator/Actions/Create/Form/Area/index.jsx @@ -1,8 +1,10 @@ import MapLoading from "@/core/components/MapLayer/Loading"; -import { Box, Button, DialogActions, DialogContent } from "@mui/material"; +import { Box, Button, DialogActions, DialogContent, Stack } from "@mui/material"; import dynamic from "next/dynamic"; -import MapControl from "./MapControl"; 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, @@ -10,6 +12,7 @@ const MapLayer = dynamic(() => import("@/core/components/MapLayer"), { 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 }); @@ -18,12 +21,24 @@ const Area = ({ allData, setAllData, handlePrev, setTabState }) => { return ( <> - - - - - - + + + + + + {boundType == "polygon" ? ( + + ) : ( + + )} + + + + + + + ); +}; +export default GetDateTime; diff --git a/src/components/dashboard/roadMissions/operator/Actions/Create/Form/GetItemInfo/FastReactCode/Dialog/List/RowActions/SelectId/index.jsx b/src/components/dashboard/roadMissions/operator/Actions/Create/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/Create/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/Create/Form/GetItemInfo/FastReactCode/Dialog/List/RowActions/index.jsx b/src/components/dashboard/roadMissions/operator/Actions/Create/Form/GetItemInfo/FastReactCode/Dialog/List/RowActions/index.jsx new file mode 100644 index 0000000..3d0b958 --- /dev/null +++ b/src/components/dashboard/roadMissions/operator/Actions/Create/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/Create/Form/GetItemInfo/FastReactCode/Dialog/List/index.jsx b/src/components/dashboard/roadMissions/operator/Actions/Create/Form/GetItemInfo/FastReactCode/Dialog/List/index.jsx new file mode 100644 index 0000000..82a0472 --- /dev/null +++ b/src/components/dashboard/roadMissions/operator/Actions/Create/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/Create/Form/GetItemInfo/FastReactCode/Dialog/index.jsx b/src/components/dashboard/roadMissions/operator/Actions/Create/Form/GetItemInfo/FastReactCode/Dialog/index.jsx new file mode 100644 index 0000000..013f73f --- /dev/null +++ b/src/components/dashboard/roadMissions/operator/Actions/Create/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/Create/Form/GetItemInfo/FastReactCode/index.jsx b/src/components/dashboard/roadMissions/operator/Actions/Create/Form/GetItemInfo/FastReactCode/index.jsx new file mode 100644 index 0000000..1545046 --- /dev/null +++ b/src/components/dashboard/roadMissions/operator/Actions/Create/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/Create/Form/GetItemInfo/index.jsx b/src/components/dashboard/roadMissions/operator/Actions/Create/Form/GetItemInfo/index.jsx index 91dc5cf..b49ede0 100644 --- a/src/components/dashboard/roadMissions/operator/Actions/Create/Form/GetItemInfo/index.jsx +++ b/src/components/dashboard/roadMissions/operator/Actions/Create/Form/GetItemInfo/index.jsx @@ -1,41 +1,37 @@ import PersianTextField from "@/core/components/PersianTextField"; import SelectBox from "@/core/components/SelectBox"; import StyledForm from "@/core/components/StyledForm"; +import { machineType } from "@/core/utils/machineTypes"; +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 MissionDates from "./MissionDates"; -import moment from "jalali-moment"; +import { array, object, string } from "yup"; +import FastReactCode from "./FastReactCode"; const validationSchema = object({ - type: string().required("نوع ماموریت را مشخص کنید!"), - start_date: string().required("تاریخ شروع ماموریت را مشخص کنید!"), - start_time: string().when("type", { - is: "1", - then: (schema) => schema.required("زمان شروع ماموریت را مشخص کنید!"), - otherwise: (schema) => schema.notRequired(), - }), - end_date: string().required("تاریخ پایان ماموریت را مشخص کنید!"), - end_time: string().when("type", { - is: "1", - then: (schema) => schema.required("زمان شروع ماموریت را مشخص کنید!"), - otherwise: (schema) => schema.notRequired(), - }), + explanation: string().required("موضوع را مشخص کنید!"), end_point: string().required("مقصد را مشخص کنید!"), region: string().required("محور ماموریت را مشخص کنید!"), + category_id: string().required("نوع ماموریت را مشخص کنید!"), + requested_machines: array().min(1, "نوع خودرو را مشخص کنید!").required("نوع خودرو را مشخص کنید!"), + road_observed_id: string().when("category_id", { + is: "3", + then: (schema) => schema.required("کد یکتا شکایت را مشخص کنید!"), + otherwise: (schema) => schema.notRequired(), + }), }); -const GetItemInfo = ({ types, regions, allData, setAllData, handlePrev, setTabState }) => { +const GetItemInfo = ({ allData, setAllData, handlePrev, setTabState }) => { const defaultValues = { - type: allData.type, - 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, + explanation: allData.explanation, end_point: allData.end_point, region: allData.region, + requested_machines: allData.requested_machines, + category_id: allData.category_id, + road_observed_id: allData.road_observed_id, }; const { control, handleSubmit } = useForm({ @@ -58,16 +54,20 @@ const GetItemInfo = ({ types, regions, allData, setAllData, handlePrev, setTabSt ( - )} /> @@ -79,8 +79,8 @@ const GetItemInfo = ({ types, regions, allData, setAllData, handlePrev, setTabSt render={({ field, fieldState: { error } }) => ( - - + + ( + + )} + /> + + + + + + ( + field.onChange([newValue])} + helperText={error?.message} + /> + )} + /> + + { const defaultValues = { @@ -38,9 +38,10 @@ const RahdaranForm = ({ setRahdaran }) => { control={control} render={({ field, fieldState: { error } }) => { return ( - field.onChange(value)} + field.onChange(value)} error={error} multiple={false} /> @@ -56,7 +57,7 @@ const RahdaranForm = ({ setRahdaran }) => { disabled={isSubmitting || !isValid} startIcon={} > - افزودن راهدار + افزودن همراه diff --git a/src/components/dashboard/roadMissions/operator/Actions/Create/Form/Rahdaran/List/index.jsx b/src/components/dashboard/roadMissions/operator/Actions/Create/Form/Rahdaran/List/index.jsx index 133b82c..be31897 100644 --- a/src/components/dashboard/roadMissions/operator/Actions/Create/Form/Rahdaran/List/index.jsx +++ b/src/components/dashboard/roadMissions/operator/Actions/Create/Form/Rahdaran/List/index.jsx @@ -33,7 +33,7 @@ const RahdaranList = ({ rahdaran, setRahdaran }) => { {rahdar.name} - کد راهدار: + کدملی: {rahdar.code} @@ -46,7 +46,7 @@ const RahdaranList = ({ rahdaran, setRahdaran }) => { ))} {rahdaran.length == 0 && ( - راهداری ثبت نشده است + همراهی ثبت نشده است )} diff --git a/src/components/dashboard/roadMissions/operator/Actions/Create/Form/Rahdaran/index.jsx b/src/components/dashboard/roadMissions/operator/Actions/Create/Form/Rahdaran/index.jsx index 5e1221a..b530af4 100644 --- a/src/components/dashboard/roadMissions/operator/Actions/Create/Form/Rahdaran/index.jsx +++ b/src/components/dashboard/roadMissions/operator/Actions/Create/Form/Rahdaran/index.jsx @@ -17,7 +17,7 @@ const Rahdaran = ({ allData, setAllData, setTabState, handlePrev }) => { - + @@ -26,8 +26,8 @@ const Rahdaran = ({ allData, setAllData, setTabState, handlePrev }) => { - diff --git a/src/components/dashboard/roadMissions/operator/Actions/Create/Form/RequestMachines/Form/index.jsx b/src/components/dashboard/roadMissions/operator/Actions/Create/Form/RequestMachines/Form/index.jsx deleted file mode 100644 index 7d80762..0000000 --- a/src/components/dashboard/roadMissions/operator/Actions/Create/Form/RequestMachines/Form/index.jsx +++ /dev/null @@ -1,89 +0,0 @@ -import LtrTextField from "@/core/components/LtrTextField"; -import SelectBox from "@/core/components/SelectBox"; -import StyledForm from "@/core/components/StyledForm"; -import { machineType } from "@/core/utils/machineTypes"; -import { yupResolver } from "@hookform/resolvers/yup"; -import { Add } from "@mui/icons-material"; -import { Button, Grid, Stack } from "@mui/material"; -import moment from "jalali-moment"; -import { Controller, useForm } from "react-hook-form"; -import { object, string } from "yup"; - -const validationSchema = object({ - type: string().required("نوع خودرو را مشخص کنید!"), - count: string().required("تعداد خودرو را مشخص کنید!"), -}); - -const RequestMachinesForm = ({ requests, setRequests }) => { - const usedTypeIds = requests.map((r) => r.type); - const filteredMachineTypes = machineType.filter((mt) => !usedTypeIds.includes(mt.id)); - - const defaultValues = { - type: "", - count: "", - }; - - const { control, handleSubmit, reset } = useForm({ - defaultValues, - resolver: yupResolver(validationSchema), - mode: "all", - }); - - const submit = (data) => { - setRequests((r) => [...r, { id: moment().valueOf(), ...data }]); - reset(); - }; - return ( - - - - - ( - - )} - /> - - - ( - { - if (isNaN(Number(e.target.value))) return; - field.onChange(e.target.value); - }} - size="small" - error={!!error} - helperText={!!error && error.message} - type="tel" - label="تعداد خودرو" - /> - )} - /> - - - - - - - - ); -}; -export default RequestMachinesForm; diff --git a/src/components/dashboard/roadMissions/operator/Actions/Create/Form/RequestMachines/List/index.jsx b/src/components/dashboard/roadMissions/operator/Actions/Create/Form/RequestMachines/List/index.jsx deleted file mode 100644 index 44ce785..0000000 --- a/src/components/dashboard/roadMissions/operator/Actions/Create/Form/RequestMachines/List/index.jsx +++ /dev/null @@ -1,59 +0,0 @@ -import { machineType } from "@/core/utils/machineTypes"; -import { Delete, FireTruck } from "@mui/icons-material"; -import { Card, Collapse, IconButton, Stack, Typography } from "@mui/material"; -import { TransitionGroup } from "react-transition-group"; - -const RequestMachinesList = ({ requests, setRequests }) => { - const handleRemove = (index) => { - setRequests((prev) => prev.filter((_, i) => i !== index)); - }; - - return ( - - - {requests.map((request, index) => ( - - - - - - - نوع خودرو: - - {machineType.find((t) => t.id == request.type)?.name_fa || ""} - - - - تعداد: - {request.count} - - - - handleRemove(index)}> - - - - - ))} - {requests.length == 0 && ( - - درخواستی ثبت نشده است - - )} - - - ); -}; -export default RequestMachinesList; diff --git a/src/components/dashboard/roadMissions/operator/Actions/Create/Form/RequestMachines/index.jsx b/src/components/dashboard/roadMissions/operator/Actions/Create/Form/RequestMachines/index.jsx deleted file mode 100644 index 28f6ed0..0000000 --- a/src/components/dashboard/roadMissions/operator/Actions/Create/Form/RequestMachines/index.jsx +++ /dev/null @@ -1,36 +0,0 @@ -import { Box, Button, Chip, DialogActions, DialogContent, Divider } from "@mui/material"; -import { useCallback, useState } from "react"; -import RequestMachinesForm from "./Form"; -import RequestMachinesList from "./List"; - -const RequestMachines = ({ allData, setAllData, handlePrev, setTabState }) => { - const [requests, setRequests] = useState(allData.requested_machines); - - const handleNext = useCallback(() => { - setAllData({ requested_machines: requests }); - setTabState((s) => s + 1); - }, [requests]); - - return ( - <> - - - - - - - - - - - - - - - ); -}; -export default RequestMachines; diff --git a/src/components/dashboard/roadMissions/operator/Actions/Create/Form/Verify/index.jsx b/src/components/dashboard/roadMissions/operator/Actions/Create/Form/Verify/index.jsx index 41a652a..57515c4 100644 --- a/src/components/dashboard/roadMissions/operator/Actions/Create/Form/Verify/index.jsx +++ b/src/components/dashboard/roadMissions/operator/Actions/Create/Form/Verify/index.jsx @@ -1,16 +1,19 @@ import MapLoading from "@/core/components/MapLayer/Loading"; +import { machineType } from "@/core/utils/machineTypes"; +import { missionCategoryTypes } from "@/core/utils/missionCategoryTypes"; +import { missionRegions } from "@/core/utils/missionRegions"; +import { missionTypes } from "@/core/utils/missionTypes"; 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 moment from "jalali-moment"; import ShowBound from "../../../showBound"; -import { machineType } from "@/core/utils/machineTypes"; const MapLayer = dynamic(() => import("@/core/components/MapLayer"), { loading: () => , ssr: false, }); -const Verify = ({ types, regions, allData, handlePrev, submitForm, submitting }) => { +const Verify = ({ allData, handlePrev, submitForm, submitting }) => { const handleNext = useCallback(() => { submitForm(allData); }, [allData]); @@ -30,14 +33,47 @@ const Verify = ({ types, regions, allData, handlePrev, submitForm, submitting }) - نوع ماموریت + موضوع ماموریت - t.id == allData.type).name_fa} /> + - محدوده ماموریت + محدوده - r.id == allData.region).name_fa} /> + r.id == allData.region).name_fa} /> + + + نوع ماموریت + + t.id == allData.category_id).name_fa} + /> + + {allData.category_id == 3 && ( + + کد شکایت + + + + )} + + نوع خودرو + + mt.id == allData.requested_machines[0]).name_fa} + /> + + + مقصد + + + + + نوع زمانبندی + + t.id == allData.type).name_fa} /> تاریخ شروع ماموریت @@ -69,37 +105,24 @@ const Verify = ({ types, regions, allData, handlePrev, submitForm, submitting }) /> )} - - مقصد - - - - + - {allData.requested_machines.map((request) => ( - - - {machineType.find((mt) => mt.id == request.type).name_fa} - - - + {allData.rahdaran.length != 0 ? ( + allData.rahdaran.map((rahdar) => ( + + {rahdar.name} + + + + )) + ) : ( + + همراهی ثبت نشده است - ))} - - - - - - {allData.rahdaran.map((rahdar) => ( - - {rahdar.name} - - - - ))} + )} diff --git a/src/components/dashboard/roadMissions/operator/Actions/Create/Form/index.jsx b/src/components/dashboard/roadMissions/operator/Actions/Create/Form/index.jsx index 37063c2..efdaaf7 100644 --- a/src/components/dashboard/roadMissions/operator/Actions/Create/Form/index.jsx +++ b/src/components/dashboard/roadMissions/operator/Actions/Create/Form/index.jsx @@ -1,23 +1,12 @@ -import { Engineering, InsertDriveFile, Route, TaxiAlert, Verified } from "@mui/icons-material"; +import { AccessTime, Engineering, InsertDriveFile, Route, Verified } from "@mui/icons-material"; import { Box, Tab, Tabs } from "@mui/material"; import { useReducer, useState } from "react"; -import GetItemInfo from "./GetItemInfo"; -import RequestMachines from "./RequestMachines"; -import Rahdaran from "./Rahdaran"; import Area from "./Area"; +import GetDateTime from "./GetDateTime"; +import GetItemInfo from "./GetItemInfo"; +import Rahdaran from "./Rahdaran"; import Verify from "./Verify"; -const types = [ - { id: 1, name_fa: "ساعتی" }, - { id: 2, name_fa: "روزانه" }, -]; - -const regions = [ - { id: 1, name_fa: "داخل شهر" }, - { id: 2, name_fa: "بیرون شهر - داخل محدوده" }, - { id: 3, name_fa: "بیرون شهر - خارج محدوده" }, -]; - function TabPanel(props) { const { children, value, index } = props; return ( @@ -70,15 +59,13 @@ const CreateForm = ({ defaultValues, submitForm, setOpen, submitting }) => { }} > } label="مشخصات" /> - } label="محدوده" /> - } label="خودرو ها" /> - } label="راهداران" /> + } label="زمانبندی" /> + } label="منطقه عملیاتی" /> + } label="همراهان" /> } label="بررسی نهایی" /> { dispatch({ type: "changeData", data }); @@ -88,7 +75,7 @@ const CreateForm = ({ defaultValues, submitForm, setOpen, submitting }) => { /> - { dispatch({ type: "changeData", data }); @@ -98,7 +85,7 @@ const CreateForm = ({ defaultValues, submitForm, setOpen, submitting }) => { /> - { dispatch({ type: "changeData", data }); @@ -118,14 +105,7 @@ const CreateForm = ({ defaultValues, submitForm, setOpen, submitting }) => { /> - + ); diff --git a/src/components/dashboard/roadMissions/operator/Actions/Create/index.jsx b/src/components/dashboard/roadMissions/operator/Actions/Create/index.jsx index 27d3e0c..e48ab26 100644 --- a/src/components/dashboard/roadMissions/operator/Actions/Create/index.jsx +++ b/src/components/dashboard/roadMissions/operator/Actions/Create/index.jsx @@ -20,12 +20,25 @@ const Create = ({ mutate }) => { const submitForm = async (result) => { setSubmitting(true); const bound = result.bound.getLatLngs(); - let area = bound.map((ring) => ring.map((latlng) => [latlng.lat, latlng.lng])); + 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, "post", { data: { - area: area[0], - rahdaran: result.rahdaran.map((r) => r.id), + 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) } : {}), requested_machines: result.requested_machines, type: result.type, zone: result.region, @@ -86,9 +99,13 @@ const Create = ({ mutate }) => { {open && ( { - const bound = useMemo(() => L.polygon([...area]), [area]); + const bound = useMemo( + () => (area.type == "polygon" ? L.polygon([...area.coordinates]) : L.polyline([...area.coordinates])), + [area] + ); const [openAreaDialog, setOpenAreaDialog] = useState(false); return ( <> - + setOpenAreaDialog(true)}> @@ -33,7 +36,7 @@ const ShowArea = ({ area }) => { > - محدوده + منطقه عملیاتی setOpenAreaDialog(false)} size="small"> diff --git a/src/components/dashboard/roadMissions/operator/OperatorList.jsx b/src/components/dashboard/roadMissions/operator/OperatorList.jsx index ae85aca..df9528e 100644 --- a/src/components/dashboard/roadMissions/operator/OperatorList.jsx +++ b/src/components/dashboard/roadMissions/operator/OperatorList.jsx @@ -1,17 +1,18 @@ "use client"; import DataTableWithAuth from "@/core/components/DataTableWithAuth"; +import { missionCategoryTypes } from "@/core/utils/missionCategoryTypes"; +import { missionRegions } from "@/core/utils/missionRegions"; +import { missionTypes } from "@/core/utils/missionTypes"; import { GET_ROAD_MISSIONS_OPERATOR_LIST } from "@/core/utils/routes"; import { Box, Stack } from "@mui/material"; import moment from "jalali-moment"; import { useMemo } from "react"; import ShowArea from "./Actions/showArea"; import RowActions from "./RowActions"; +import MachinesDialog from "./RowActions/Machines"; import Toolbar from "./Toolbar"; +import RahdaranDialog from "./RowActions/Rahdaran"; -const typeOptions = [ - { value: 1, label: "روزانه" }, - { value: 2, label: "ساعتی" }, -]; const OperatorList = () => { const columns = useMemo( () => [ @@ -27,8 +28,74 @@ const OperatorList = () => { size: 100, }, { - accessorKey: "type", + accessorKey: "code", + header: "کد خروج", + id: "code", + enableColumnFilter: false, + datatype: "numeric", + filterMode: "equals", + grow: false, + size: 100, + }, + { + accessorKey: "state_name", + header: "وضعیت", + id: "state_name", + enableColumnFilter: false, + datatype: "numeric", + filterMode: "equals", + grow: false, + size: 100, + }, + { + accessorKey: "explanation", + header: "موضوع", + id: "explanation", + enableColumnFilter: false, + datatype: "numeric", + filterMode: "equals", + grow: false, + size: 100, + }, + { + accessorKey: "category_id", header: "نوع", + id: "category_id", + enableColumnFilter: true, + datatype: "numeric", + filterMode: "equals", + sortDescFirst: true, + grow: false, + size: 100, + columnSelectOption: () => { + return missionCategoryTypes.map((category) => ({ + value: category.id, + label: category.name_fa, + })); + }, + Cell: ({ row }) => row.original.category_name, + }, + { + 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, + }, + { + accessorKey: "type", + header: "نوع زمانبندی", id: "type", enableColumnFilter: true, datatype: "numeric", @@ -37,15 +104,18 @@ const OperatorList = () => { grow: false, size: 100, columnSelectOption: () => { - return typeOptions.map((type) => ({ - value: type.value, - label: type.label, + return missionTypes.map((type) => ({ + value: type.id, + label: type.name_fa, })); }, Cell: ({ row }) => row.original.type_fa, }, { - accessorFn: (row) => moment(row.start_date).locale("fa").format("HH:mm | yyyy/MM/DD"), + 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, @@ -55,7 +125,10 @@ const OperatorList = () => { size: 100, }, { - accessorFn: (row) => moment(row.end_date).locale("fa").format("HH:mm | yyyy/MM/DD"), + 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, @@ -75,7 +148,7 @@ const OperatorList = () => { size: 100, }, { - header: "محدوده", + header: "منطقه عملیاتی", id: "area", enableColumnFilter: false, datatype: "numeric", @@ -101,12 +174,66 @@ const OperatorList = () => { ), }, { - accessorKey: "state_name", - header: "وضعیت", - id: "state_name", - enableColumnFilter: false, - datatype: "numeric", - filterMode: "equals", + 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.request_date).locale("fa").format("HH:mm | yyyy/MM/DD"), + header: "تاریخ ثبت", + id: "request_date", + enableColumnFilter: true, + datatype: "date", + filterMode: "between", grow: false, size: 100, }, diff --git a/src/components/dashboard/roadMissions/operator/RowActions/Edit/EditController/index.jsx b/src/components/dashboard/roadMissions/operator/RowActions/Edit/EditController/index.jsx index 6fd0c7f..1fdda15 100644 --- a/src/components/dashboard/roadMissions/operator/RowActions/Edit/EditController/index.jsx +++ b/src/components/dashboard/roadMissions/operator/RowActions/Edit/EditController/index.jsx @@ -7,7 +7,13 @@ import L from "leaflet"; import { GET_RAHDARAN_BY_ID, UPDATE_REQUEST_MISSION } from "@/core/utils/routes"; const EditController = ({ row, mutate, openEditDialog, setOpenEditDialog }) => { - const bound = useMemo(() => L.polygon([...row.original.area]), [row.original.area]); + const bound = useMemo( + () => + row.original.area.type == "polygon" + ? L.polygon([...row.original.area.coordinates]) + : L.polyline([...row.original.area.coordinates]), + [row.original.area] + ); const [submitting, setSubmitting] = useState(false); const [rahdaran, setRahdaran] = useState(null); const [rahdaranLoading, setRahdaranLoading] = useState(false); @@ -32,8 +38,18 @@ const EditController = ({ row, mutate, openEditDialog, setOpenEditDialog }) => { await requestServer(`${UPDATE_REQUEST_MISSION}/${row.original.id}`, "post", { data: { - area: area[0], - rahdaran: result.rahdaran.map((r) => r.id), + 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[0], + }, + ...(result.rahdaran.length != 0 ? { rahdaran: result.rahdaran.map((r) => r.id) } : {}), requested_machines: result.requested_machines, type: result.type, zone: result.region, @@ -68,9 +84,13 @@ const EditController = ({ row, mutate, openEditDialog, setOpenEditDialog }) => { ) : ( { + const [loading, setLoading] = useState(true); + const [data, setData] = useState(null); + const request = useRequest(); + + useEffect(() => { + const fetchData = async () => { + try { + setLoading(true); + const response = await request(`${GET_MACHINES_BY_ID}/${rowId}`); + setData(response.data.data); + } catch (error) { + } finally { + setLoading(false); + } + }; + + fetchData(); + }, [rowId]); + + return ( + <> + + {loading ? ( + + ) : data ? ( + + + + + کد ماشین + نام خودرو + + + + {data.map((machine) => { + return ( + + {machine.machine_code} + {machine.car_name} + + ); + })} + +
+
+ ) : ( + اطلاعات خودرویی در سامانه یافت نشد + )} +
+ + ); +}; +export default MachinesContent; diff --git a/src/components/dashboard/roadMissions/operator/RowActions/Machines/index.jsx b/src/components/dashboard/roadMissions/operator/RowActions/Machines/index.jsx new file mode 100644 index 0000000..395b276 --- /dev/null +++ b/src/components/dashboard/roadMissions/operator/RowActions/Machines/index.jsx @@ -0,0 +1,38 @@ +import { Button, Dialog, DialogActions, DialogTitle, IconButton, Tooltip } from "@mui/material"; +import DirectionsCarIcon from "@mui/icons-material/DirectionsCar"; +import { useState } from "react"; +import MachinesContent from "./MachinesContent"; + +const MachinesDialog = ({ rowId }) => { + const [openMachinesDialog, setOpenMachinesDialog] = useState(false); + return ( + <> + + setOpenMachinesDialog(true)}> + + + + setOpenMachinesDialog(false)} + PaperProps={{ + sx: { + boxShadow: "rgba(60, 64, 67, 0.3) 0px 1px 16px 0px, rgba(60, 64, 67, 0.15) 0px 1px 10px 0px", + }, + }} + dir="rtl" + maxWidth={"sm"} + > + اطلاعات خودرو + {openMachinesDialog && } + + + + + + ); +}; +export default MachinesDialog; diff --git a/src/components/dashboard/roadMissions/operator/RowActions/Rahdaran/RahdaranContent.jsx b/src/components/dashboard/roadMissions/operator/RowActions/Rahdaran/RahdaranContent.jsx new file mode 100644 index 0000000..46001aa --- /dev/null +++ b/src/components/dashboard/roadMissions/operator/RowActions/Rahdaran/RahdaranContent.jsx @@ -0,0 +1,74 @@ +import DialogLoading from "@/core/components/DialogLoading"; +import { GET_RAHDARAN_BY_ID } from "@/core/utils/routes"; +import useRequest from "@/lib/hooks/useRequest"; +import { + DialogContent, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + Typography, +} from "@mui/material"; +import { useEffect, useState } from "react"; + +const RahdaranContent = ({ rowId }) => { + const [loading, setLoading] = useState(true); + const [data, setData] = useState(null); + const request = useRequest(); + + useEffect(() => { + const fetchData = async () => { + try { + setLoading(true); + const response = await request(`${GET_RAHDARAN_BY_ID}/${rowId}`); + setData(response.data.data); + } catch (error) { + } finally { + setLoading(false); + } + }; + + fetchData(); + }, [rowId]); + + return ( + <> + + {loading ? ( + + ) : data ? ( + + + + + کد ملی + نام + نقش + + + + {data.map((rahdar) => { + return ( + + {rahdar.code} + {rahdar.name} + {rahdar.is_driver ? "راننده" : "همراه"} + + ); + })} + +
+
+ ) : ( + اطلاعات راهداران در سامانه یافت نشد + )} +
+ + ); +}; +export default RahdaranContent; diff --git a/src/components/dashboard/roadMissions/operator/RowActions/Rahdaran/index.jsx b/src/components/dashboard/roadMissions/operator/RowActions/Rahdaran/index.jsx new file mode 100644 index 0000000..b27009d --- /dev/null +++ b/src/components/dashboard/roadMissions/operator/RowActions/Rahdaran/index.jsx @@ -0,0 +1,38 @@ +import { Button, Dialog, DialogActions, DialogTitle, IconButton, Tooltip } from "@mui/material"; +import GroupsIcon from "@mui/icons-material/Groups"; +import { useState } from "react"; +import RahdaranContent from "./RahdaranContent"; + +const RahdaranDialog = ({ rowId }) => { + const [openRahdaranDialog, setOpenRahdaranDialog] = useState(false); + return ( + <> + + setOpenRahdaranDialog(true)}> + + + + setOpenRahdaranDialog(false)} + PaperProps={{ + sx: { + boxShadow: "rgba(60, 64, 67, 0.3) 0px 1px 16px 0px, rgba(60, 64, 67, 0.15) 0px 1px 10px 0px", + }, + }} + dir="rtl" + maxWidth={"sm"} + > + لیست افراد + {openRahdaranDialog && } + + + + + + ); +}; +export default RahdaranDialog; diff --git a/src/components/dashboard/roadMissions/operator/RowActions/index.jsx b/src/components/dashboard/roadMissions/operator/RowActions/index.jsx index a4c3c39..b5659da 100644 --- a/src/components/dashboard/roadMissions/operator/RowActions/index.jsx +++ b/src/components/dashboard/roadMissions/operator/RowActions/index.jsx @@ -1,11 +1,14 @@ import { Box } from "@mui/material"; import Edit from "./Edit"; import Delete from "./Delete"; +import { useAuth } from "@/lib/contexts/auth"; const RowActions = ({ row, mutate }) => { + const { user } = useAuth(); + return ( - {row.original.state_id == 5 && ( + {row.original.state_id == 5 && user.id == row.original.user_id && ( <> diff --git a/src/components/dashboard/roadMissions/transportation/Actions/showArea/index.jsx b/src/components/dashboard/roadMissions/transportation/Actions/showArea/index.jsx index 4232c07..be4ee7a 100644 --- a/src/components/dashboard/roadMissions/transportation/Actions/showArea/index.jsx +++ b/src/components/dashboard/roadMissions/transportation/Actions/showArea/index.jsx @@ -6,12 +6,15 @@ import ShowBound from "../showBound"; import L from "leaflet"; const ShowArea = ({ area }) => { - const bound = useMemo(() => L.polygon([...area]), [area]); + const bound = useMemo( + () => (area.type == "polygon" ? L.polygon([...area.coordinates]) : L.polyline([...area.coordinates])), + [area] + ); const [openAreaDialog, setOpenAreaDialog] = useState(false); return ( <> - + setOpenAreaDialog(true)}> @@ -33,7 +36,7 @@ const ShowArea = ({ area }) => { > - محدوده + منطقه عملیاتی setOpenAreaDialog(false)} size="small"> diff --git a/src/components/dashboard/roadMissions/transportation/RowActions/Allocation/Form/DriversDialog/DriversSearch/Form/index.jsx b/src/components/dashboard/roadMissions/transportation/RowActions/Allocation/Form/DriversDialog/DriversSearch/Form/index.jsx new file mode 100644 index 0000000..93bb1d6 --- /dev/null +++ b/src/components/dashboard/roadMissions/transportation/RowActions/Allocation/Form/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/transportation/RowActions/Allocation/Form/DriversDialog/DriversSearch/index.jsx b/src/components/dashboard/roadMissions/transportation/RowActions/Allocation/Form/DriversDialog/DriversSearch/index.jsx new file mode 100644 index 0000000..b7136c9 --- /dev/null +++ b/src/components/dashboard/roadMissions/transportation/RowActions/Allocation/Form/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/transportation/RowActions/Allocation/Form/DriversDialog/index.jsx b/src/components/dashboard/roadMissions/transportation/RowActions/Allocation/Form/DriversDialog/index.jsx new file mode 100644 index 0000000..3c1d406 --- /dev/null +++ b/src/components/dashboard/roadMissions/transportation/RowActions/Allocation/Form/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/transportation/RowActions/Allocation/Form/MachinesDialog/MachineList/RowActions/Allocate/index.jsx b/src/components/dashboard/roadMissions/transportation/RowActions/Allocation/Form/MachinesDialog/MachineList/RowActions/Allocate/index.jsx index e26344e..9718815 100644 --- a/src/components/dashboard/roadMissions/transportation/RowActions/Allocation/Form/MachinesDialog/MachineList/RowActions/Allocate/index.jsx +++ b/src/components/dashboard/roadMissions/transportation/RowActions/Allocation/Form/MachinesDialog/MachineList/RowActions/Allocate/index.jsx @@ -1,13 +1,9 @@ import { Done } from "@mui/icons-material"; import { IconButton, Tooltip } from "@mui/material"; -const Allocate = ({ row, request, dispatch, setOpenMachinesDialog }) => { +const Allocate = ({ row, setMachine, setOpenMachinesDialog }) => { const handleClick = () => { - dispatch({ - type: "CHANGE_MACHINE", - id: request.id, - machine: row.original, - }); + setMachine(row.original); setOpenMachinesDialog(false); }; diff --git a/src/components/dashboard/roadMissions/transportation/RowActions/Allocation/Form/MachinesDialog/MachineList/RowActions/index.jsx b/src/components/dashboard/roadMissions/transportation/RowActions/Allocation/Form/MachinesDialog/MachineList/RowActions/index.jsx index ee1b9d2..ba0a20d 100644 --- a/src/components/dashboard/roadMissions/transportation/RowActions/Allocation/Form/MachinesDialog/MachineList/RowActions/index.jsx +++ b/src/components/dashboard/roadMissions/transportation/RowActions/Allocation/Form/MachinesDialog/MachineList/RowActions/index.jsx @@ -1,6 +1,6 @@ import Allocate from "./Allocate"; -const RowActions = ({ row, request, dispatch, setOpenMachinesDialog }) => { - return ; +const RowActions = ({ row, setMachine, setOpenMachinesDialog }) => { + return ; }; export default RowActions; diff --git a/src/components/dashboard/roadMissions/transportation/RowActions/Allocation/Form/MachinesDialog/MachineList/index.jsx b/src/components/dashboard/roadMissions/transportation/RowActions/Allocation/Form/MachinesDialog/MachineList/index.jsx index fd7a643..07e8983 100644 --- a/src/components/dashboard/roadMissions/transportation/RowActions/Allocation/Form/MachinesDialog/MachineList/index.jsx +++ b/src/components/dashboard/roadMissions/transportation/RowActions/Allocation/Form/MachinesDialog/MachineList/index.jsx @@ -4,7 +4,7 @@ import { useMemo } from "react"; import RowActions from "./RowActions"; import { GET_MACHINES_TABLE_LIST } from "@/core/utils/routes"; -const MachinesList = ({ request, dispatch, setOpenMachinesDialog }) => { +const MachinesList = ({ machineType, setMachine, setOpenMachinesDialog }) => { const columns = useMemo( () => [ { @@ -40,6 +40,7 @@ const MachinesList = ({ request, dispatch, setOpenMachinesDialog }) => { need_filter={true} table_title="لیست خودرو ها" columns={columns} + specialFilter={[{ value: machineType, datatype: "text", id: "car_type", fn: "equals" }]} sorting={[{ id: "id", desc: true }]} table_url={GET_MACHINES_TABLE_LIST} page_name={"roadMissionsTransportation"} @@ -47,12 +48,7 @@ const MachinesList = ({ request, dispatch, setOpenMachinesDialog }) => { enableRowActions positionActionsColumn={"first"} RowActions={(props) => ( - + )} /> diff --git a/src/components/dashboard/roadMissions/transportation/RowActions/Allocation/Form/MachinesDialog/index.jsx b/src/components/dashboard/roadMissions/transportation/RowActions/Allocation/Form/MachinesDialog/index.jsx index 5bdd6d8..0265264 100644 --- a/src/components/dashboard/roadMissions/transportation/RowActions/Allocation/Form/MachinesDialog/index.jsx +++ b/src/components/dashboard/roadMissions/transportation/RowActions/Allocation/Form/MachinesDialog/index.jsx @@ -3,7 +3,7 @@ import { Button, Dialog, IconButton } from "@mui/material"; import { useState } from "react"; import MachinesList from "./MachineList"; -const MachinesDialog = ({ request, dispatch, mode }) => { +const MachinesDialog = ({ machineType, setMachine, mode }) => { const [openMachinesDialog, setOpenMachinesDialog] = useState(false); return ( <> @@ -12,11 +12,11 @@ const MachinesDialog = ({ request, dispatch, mode }) => {
) : ( - )} - + setOpenMachinesDialog(false)} @@ -31,7 +31,11 @@ const MachinesDialog = ({ request, dispatch, mode }) => { {openMachinesDialog && ( - + )} diff --git a/src/components/dashboard/roadMissions/transportation/RowActions/Allocation/Form/Reject/index.jsx b/src/components/dashboard/roadMissions/transportation/RowActions/Allocation/Form/Reject/index.jsx index ee48c0a..9a6aa3a 100644 --- a/src/components/dashboard/roadMissions/transportation/RowActions/Allocation/Form/Reject/index.jsx +++ b/src/components/dashboard/roadMissions/transportation/RowActions/Allocation/Form/Reject/index.jsx @@ -10,7 +10,7 @@ const Reject = ({ isSubmitting, rowId, mutate, setOpenAllocationDialog }) => { - + setOpenRejectDialog(false)} 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 929b43c..e3e9c3b 100644 --- a/src/components/dashboard/roadMissions/transportation/RowActions/Allocation/Form/index.jsx +++ b/src/components/dashboard/roadMissions/transportation/RowActions/Allocation/Form/index.jsx @@ -1,44 +1,34 @@ -import { machineType } from "@/core/utils/machineTypes"; -import { Alert, Box, Button, Chip, DialogActions, DialogContent, Divider, Typography } from "@mui/material"; -import { Stack } from "@mui/system"; -import { useReducer, useState } from "react"; -import MachinesDialog from "./MachinesDialog"; -import useRequest from "@/lib/hooks/useRequest"; +import MapLayer from "@/core/components/MapLayer"; import { ALLOCATE_REQUEST_MISSION } from "@/core/utils/routes"; +import useRequest from "@/lib/hooks/useRequest"; +import { Box, Button, Chip, DialogActions, DialogContent, Divider, Paper, Typography } from "@mui/material"; +import { Stack } from "@mui/system"; +import { useMemo, useState } from "react"; +import ShowBound from "../../../Actions/showBound"; import Reject from "./Reject"; - -const initValue = (requested_machines) => { - let arr = []; - let counter = 0; - for (let i = 0; i < requested_machines.length; i++) { - const request = requested_machines[i]; - for (let j = 0; j < request.count; j++) { - counter++; - arr.push({ id: counter, type: request.type, machine: null, position: j + 1 }); - } - } - return arr; -}; - -const reducer = (state, action) => { - switch (action.type) { - case "CHANGE_MACHINE": - return state.map((s) => (s.id == action.id ? { ...s, machine: action.machine } : s)); - default: - return state; - } -}; - -const AllocationForm = ({ rowId, mutate, setOpenAllocationDialog, requested_machines }) => { +import { missionRegions } from "@/core/utils/missionRegions"; +import { machineType } from "@/core/utils/machineTypes"; +import { missionTypes } from "@/core/utils/missionTypes"; +import moment from "jalali-moment"; +import MachinesDialog from "./MachinesDialog"; +import DriversDialog from "./DriversDialog"; +const AllocationForm = ({ row, mutate, setOpenAllocationDialog }) => { + const bound = useMemo( + () => + row.area.type == "polygon" ? L.polygon([...row.area.coordinates]) : L.polyline([...row.area.coordinates]), + [row.area] + ); const requestServer = useRequest({ notificationSuccess: true }); - const [requests, dispatch] = useReducer(reducer, requested_machines, initValue); + const [machine, setMachine] = useState(); + const [driver, setDriver] = useState(); const [isSubmitting, setIsSubmitting] = useState(false); - const handleSubmit = async (_requests) => { + const handleSubmit = async (_machine, _driver) => { setIsSubmitting(true); - await requestServer(`${ALLOCATE_REQUEST_MISSION}/${rowId}`, "post", { + await requestServer(`${ALLOCATE_REQUEST_MISSION}/${row.id}`, "post", { data: { - machines: _requests.map((r) => r.machine.id), + machines: [_machine.id], + driver: _driver.id, }, hasSidebarUpdate: true, }) @@ -55,36 +45,129 @@ const AllocationForm = ({ rowId, mutate, setOpenAllocationDialog, requested_mach return ( <> - - {requests.map((request) => ( - - - {machineType.find((mt) => mt.id == request.type).name_fa} {request.position} - - - {!request.machine && ( - + + + + + + + نوع خودرو درخواستی + + + mt.id == row.requested_machines[0]).name_fa} + /> + + + {machine ? ( + <> + خودرو + + + + + + ) : ( + + )} + + {machine && ( + + {driver ? ( + <> + راننده + + + + + + ) : ( + + )} + )} + + + + + - {request.machine ? ( - <> + + موضوع ماموریت + + + + + محدوده + + r.id == row.zone).name_fa} /> + + + مقصد + + + + + نوع زمانبندی + + t.id == row.type).name_fa} /> + + + تاریخ شروع ماموریت + + {row.type == 1 ? ( - - - ) : ( - - )} + ) : ( + + )} + + + تاریخ پایان ماموریت + + {row.type == 1 ? ( + + ) : ( + + )} + - ))} - {requests.some((r) => !r.machine) && ( - - برای تایید و تخصیص باید همه خودرو ها انتخاب شده باشند! - - )} - + + + + + + + @@ -98,17 +181,17 @@ const AllocationForm = ({ rowId, mutate, setOpenAllocationDialog, requested_mach