From a4a03120739d7a8438c37ce436dbc92c9610b0e9 Mon Sep 17 00:00:00 2001 From: AminGhasempoor Date: Sun, 28 Dec 2025 11:10:07 +0330 Subject: [PATCH] edit missions pages --- .../dashboard/road-missions/control/page.js | 2 +- .../road-missions/transportation/page.js | 2 +- .../dashboard/rahdaran/RahdaranList.jsx | 62 +- .../roadItems/supervisor/SupervisorList.jsx | 4 +- .../roadMissions/control/ControlList.jsx | 106 ++- .../MachineAndDriver/MachinesDialog/index.jsx | 27 +- .../roadMissions/operator/OperatorList.jsx | 617 ++++++++++-------- .../transportation/TransportationList.jsx | 364 +++++++---- .../CreateViolation/Form/CreateForm.jsx | 54 ++ .../Actions/CreateViolation/Form/index.jsx | 39 ++ .../Actions/CreateViolation/index.jsx | 50 ++ .../Actions/EditViolation/EditController.jsx | 51 ++ .../EditViolation/EditViolationForm.jsx | 95 +++ .../EditViolation/MissionDates/index.jsx | 99 +++ .../Actions/EditViolation/index.jsx | 51 ++ .../violations/RowActions/index.jsx | 10 +- .../roadMissions/violations/Toolbar.jsx | 11 + .../violations/ViolationsList.jsx | 2 + src/core/utils/routes.js | 2 + 19 files changed, 1169 insertions(+), 479 deletions(-) create mode 100644 src/components/dashboard/roadMissions/violations/Actions/CreateViolation/Form/CreateForm.jsx create mode 100644 src/components/dashboard/roadMissions/violations/Actions/CreateViolation/Form/index.jsx create mode 100644 src/components/dashboard/roadMissions/violations/Actions/CreateViolation/index.jsx create mode 100644 src/components/dashboard/roadMissions/violations/Actions/EditViolation/EditController.jsx create mode 100644 src/components/dashboard/roadMissions/violations/Actions/EditViolation/EditViolationForm.jsx create mode 100644 src/components/dashboard/roadMissions/violations/Actions/EditViolation/MissionDates/index.jsx create mode 100644 src/components/dashboard/roadMissions/violations/Actions/EditViolation/index.jsx create mode 100644 src/components/dashboard/roadMissions/violations/Toolbar.jsx diff --git a/src/app/(withAuth)/(dashboardLayout)/dashboard/road-missions/control/page.js b/src/app/(withAuth)/(dashboardLayout)/dashboard/road-missions/control/page.js index 708e125..a172e18 100644 --- a/src/app/(withAuth)/(dashboardLayout)/dashboard/road-missions/control/page.js +++ b/src/app/(withAuth)/(dashboardLayout)/dashboard/road-missions/control/page.js @@ -9,7 +9,7 @@ const Page = () => { <> { <> { const { data: userPermissions } = usePermissions(); - const { user } = useAuth(); const hasCountryPermission = userPermissions?.includes("manage-rahdaran-country"); const columns = useMemo(() => { const provinceColumn = { @@ -66,63 +63,6 @@ const RahdaranList = () => { size: 100, }, ...(hasCountryPermission ? [provinceColumn] : []), - { - header: "اداره", - id: "edareh_shahri_id", - enableColumnFilter: true, - datatype: "numeric", - filterMode: "equals", - dependencyId: hasCountryPermission ? "province_id" : null, - grow: false, - size: 120, - ColumnSelectComponent: (props) => { - const { edaratList, loadingEdaratList, errorEdaratList } = useEdaratLists( - hasCountryPermission ? props.dependencyFieldValue.value : user.province_id - ); - const [prevDependency, setPrevDependency] = useState( - hasCountryPermission ? props.dependencyFieldValue.value : user.province_id - ); - - const getColumnSelectOptions = useMemo(() => { - if (hasCountryPermission && props.dependencyFieldValue.value === "") { - return [{ value: "empty", label: "ابتدا استان را انتخاب کنید" }]; - } - if (loadingEdaratList) { - return [{ value: "loading", label: "در حال بارگذاری..." }]; - } - if (errorEdaratList) { - return [{ value: "error", label: "خطا در بارگذاری" }]; - } - return [ - { value: "", label: "کل ادارات" }, - ...edaratList.map((edare) => ({ - value: edare.id, - label: edare.name_fa, - })), - ]; - }, [edaratList, loadingEdaratList, errorEdaratList]); - useEffect(() => { - if (hasCountryPermission) return; - if (prevDependency === props.dependencyFieldValue?.value) return; - props.handleChange({ ...props.filterParameters, value: "" }); - setPrevDependency(props.dependencyFieldValue?.value); - }, [props.dependencyFieldValue?.value, hasCountryPermission]); - return ( - - ); - }, - Cell: ({ renderedCellValue, row }) => <>{row.original.edareh_shahri_name}, - }, { accessorKey: "name", header: "نام و نام خانوادگی", diff --git a/src/components/dashboard/roadItems/supervisor/SupervisorList.jsx b/src/components/dashboard/roadItems/supervisor/SupervisorList.jsx index 69cc288..73e504c 100644 --- a/src/components/dashboard/roadItems/supervisor/SupervisorList.jsx +++ b/src/components/dashboard/roadItems/supervisor/SupervisorList.jsx @@ -12,8 +12,6 @@ import { Box, Stack, Typography } from "@mui/material"; import moment from "jalali-moment"; import { useEffect, useMemo, useState } from "react"; import ImageDialog from "../operator/RowActions/ImageForm"; -import MachinesCodeDialog from "../operator/RowActions/MachinesCodeForm"; -import RahdaranDialog from "../operator/RowActions/RahdaranForm"; import RowActions from "./RowActions"; import LocationForm from "./RowActions/LocationForm"; import Toolbar from "./Toolbar"; @@ -364,7 +362,7 @@ const SupervisorList = () => { label: status.label, })); }, - Cell: ({ renderedCellValue, row }) => <>{row.original.status_fa}, + Cell: ({ row }) => <>{row.original.status_fa}, }, ]; }, [hasCountryPermission]); diff --git a/src/components/dashboard/roadMissions/control/ControlList.jsx b/src/components/dashboard/roadMissions/control/ControlList.jsx index be2443d..b746a8d 100644 --- a/src/components/dashboard/roadMissions/control/ControlList.jsx +++ b/src/components/dashboard/roadMissions/control/ControlList.jsx @@ -7,10 +7,50 @@ import { useMemo } from "react"; import MachinesDialog from "./RowActions/Machines"; import RowActions from "./RowActions"; import RahdaranDialog from "./RowActions/Rahdaran"; +import useProvinces from "@/lib/hooks/useProvince"; +import CustomSelectByDependency from "@/core/components/DataTable/filter/fieldsType/CustomSelectByDependency"; +import useCities from "@/lib/hooks/useCities"; const ControlList = () => { - const columns = useMemo( - () => [ + const columns = useMemo(() => { + const dynamicColumns = { + header: "استان", + id: "province_id", + enableColumnFilter: true, + datatype: "numeric", + filterMode: "equals", + grow: false, + size: 130, + ColumnSelectComponent: (props) => { + const { provinces, errorProvinces, loadingProvinces } = useProvinces(); + const getColumnSelectOptions = useMemo(() => { + if (loadingProvinces) { + return [{ value: "loading", label: "در حال بارگذاری..." }]; + } + if (errorProvinces) { + return [{ value: "error", label: "خطا در بارگذاری" }]; + } + return [ + { value: "", label: "کل کشور" }, + ...provinces.map((province) => ({ + value: province.id, + label: province.name_fa, + })), + ]; + }, [provinces, errorProvinces, loadingProvinces]); + return ( + + ); + }, + Cell: ({ renderedCellValue, row }) => { + return <>{row.original.province_name}; + }, + }; + return [ { accessorKey: "id", header: "کد یکتا", @@ -22,6 +62,63 @@ const ControlList = () => { grow: false, size: 100, }, + ...[dynamicColumns], + { + header: "شهر", + id: "city_id", + enableColumnFilter: true, + datatype: "numeric", + filterMode: "equals", + dependencyId: "province_id", + grow: false, + size: 120, + ColumnSelectComponent: (props) => { + const { cityList, loadingCityList, errorCityList } = useCities(props.dependencyFieldValue.value); + + const getColumnSelectOptions = useMemo(() => { + if (props.dependencyFieldValue.value === "") { + return [{ value: "empty", label: "ابتدا استان را انتخاب کنید" }]; + } + if (loadingCityList) { + return [{ value: "loading", label: "در حال بارگذاری..." }]; + } + if (errorCityList) { + return [{ value: "error", label: "خطا در بارگذاری" }]; + } + return [ + { value: "", label: "کل ادارات" }, + ...cityList.map((edare) => ({ + value: edare.id, + label: edare.name_fa, + })), + ]; + }, [cityList, loadingCityList, errorCityList]); + return ( + + ); + }, + Cell: ({ row }) => <>{row.original.city_name}, + }, + { + accessorKey: "station_name", + header: "راهدارخانه", + id: "station_name", + enableColumnFilter: false, + datatype: "text", + filterMode: "equals", + grow: false, + size: 100, + }, { accessorFn: (row) => row.type == 1 @@ -113,9 +210,8 @@ const ControlList = () => { grow: false, size: 100, }, - ], - [] - ); + ]; + }, []); return ( <> diff --git a/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/MachineAndDriver/MachinesDialog/index.jsx b/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/MachineAndDriver/MachinesDialog/index.jsx index b85b6f6..e121e29 100644 --- a/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/MachineAndDriver/MachinesDialog/index.jsx +++ b/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/MachineAndDriver/MachinesDialog/index.jsx @@ -2,6 +2,8 @@ import LtrTextField from "@/core/components/LtrTextField"; import { Close, Edit } from "@mui/icons-material"; import { Button, Dialog, IconButton, Stack } from "@mui/material"; import { useState } from "react"; +import { Controller } from "react-hook-form"; +import CarCode from "@/core/components/CarCode"; const MachinesDialog = ({ setMachine, mode, machineCode, setShowMachineCodeDialog }) => { const [openMachinesDialog, setOpenMachinesDialog] = useState(false); @@ -33,23 +35,14 @@ const MachinesDialog = ({ setMachine, mode, machineCode, setShowMachineCodeDialo {openMachinesDialog && ( - { - setMachine(event.target.value); - }} - variant="outlined" - fullWidth - /> + + { + setMachine(value.machine_code); + }} + /> + + + + + + ); +}; +export default CreateViolationForm \ No newline at end of file diff --git a/src/components/dashboard/roadMissions/violations/Actions/CreateViolation/Form/index.jsx b/src/components/dashboard/roadMissions/violations/Actions/CreateViolation/Form/index.jsx new file mode 100644 index 0000000..260b0d9 --- /dev/null +++ b/src/components/dashboard/roadMissions/violations/Actions/CreateViolation/Form/index.jsx @@ -0,0 +1,39 @@ +import { DialogTitle } from "@mui/material"; +import { DialogHeader } from "next/dist/client/components/react-dev-overlay/internal/components/Dialog"; +import useRequest from "@/lib/hooks/useRequest"; +import { CREATE_VIOLATION } from "@/core/utils/routes"; +import CreateViolationForm from "@/components/dashboard/roadMissions/violations/Actions/CreateViolation/Form/CreateForm"; + +const ViolationForm = ({ setOpenMachinesDialog, mutate, openMachinesDialog }) => { + const requestServer = useRequest({ notificationSuccess: true }); + + const defaultValues = { + machinesId: null, + }; + + const submitForm = async (result) => { + await requestServer(CREATE_VIOLATION, "post", { + data: { + machine_code: result.machinesId.machine_code, + }, + }) + .then((response) => { + setOpenMachinesDialog(false); + mutate(); + }) + .catch((error) => {}); + }; + return ( + <> + + ثبت تخلف + + + + ); +}; +export default ViolationForm; diff --git a/src/components/dashboard/roadMissions/violations/Actions/CreateViolation/index.jsx b/src/components/dashboard/roadMissions/violations/Actions/CreateViolation/index.jsx new file mode 100644 index 0000000..eb431f4 --- /dev/null +++ b/src/components/dashboard/roadMissions/violations/Actions/CreateViolation/index.jsx @@ -0,0 +1,50 @@ +import { AddCircle, Close } from "@mui/icons-material"; +import { Button, Dialog, IconButton, Tooltip } from "@mui/material"; +import { useState } from "react"; +import ViolationForm from "./Form"; + + +const CreateViolation = ({ mutate }) => { + const [openMachinesDialog, setOpenMachinesDialog] = useState(false); + + return ( + <> + + + + + + setOpenMachinesDialog(false)} + sx={(theme) => ({ + position: "absolute", + right: 8, + top: 8, + zIndex: 50, + color: theme.palette.grey[500], + })} + > + + + + {openMachinesDialog && ( + + )} + + + ); +}; +export default CreateViolation; diff --git a/src/components/dashboard/roadMissions/violations/Actions/EditViolation/EditController.jsx b/src/components/dashboard/roadMissions/violations/Actions/EditViolation/EditController.jsx new file mode 100644 index 0000000..97ae1e2 --- /dev/null +++ b/src/components/dashboard/roadMissions/violations/Actions/EditViolation/EditController.jsx @@ -0,0 +1,51 @@ +import { EDIT_VIOLATION } 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 EditViolationForm from "./EditViolationForm"; +import moment from "jalali-moment"; + +const EditController = ({ rowId, mutate, setOpen }) => { + const requestServer = useRequest({ notificationSuccess: true }); + + const defaultData = { + start_date: null, + start_time: null, + end_date: null, + end_time: null, + }; + const handleSubmit = async (result) => { + const payload = {}; + + if (result.start_date && result.start_time) { + payload.enter_time = `${result.start_date} ${moment(result.start_time).format("HH:mm")}`; + } + + if (result.end_date && result.end_time) { + payload.exit_time = `${result.end_date} ${moment(result.end_time).format("HH:mm")}`; + } + + await requestServer(`${EDIT_VIOLATION}/${rowId}`, "post", { + data: payload, + }) + .then(() => { + mutate(); + setOpen(false); + }) + .catch(() => {}); + }; + + return ( + <> + + ثبت تردد + + + + ); +}; +export default EditController; diff --git a/src/components/dashboard/roadMissions/violations/Actions/EditViolation/EditViolationForm.jsx b/src/components/dashboard/roadMissions/violations/Actions/EditViolation/EditViolationForm.jsx new file mode 100644 index 0000000..0c07e7b --- /dev/null +++ b/src/components/dashboard/roadMissions/violations/Actions/EditViolation/EditViolationForm.jsx @@ -0,0 +1,95 @@ +import { useForm } from "react-hook-form"; +import { yupResolver } from "@hookform/resolvers/yup"; +import StyledForm from "@/core/components/StyledForm"; +import { Box, Button, DialogActions, DialogContent, Grid, Stack } from "@mui/material"; +import MissionDates from "./MissionDates"; +import * as yup from "yup"; + +export const schema = yup + .object({ + start_date: yup.string().nullable(), + start_time: yup.string().nullable(), + end_date: yup.string().nullable(), + end_time: yup.string().nullable(), + }) + .test("valid-dates", function (values) { + if (!values) return false; + + const hasStartDate = !!values.start_date; + const hasStartTime = !!values.start_time; + const hasEndDate = !!values.end_date; + const hasEndTime = !!values.end_time; + + if (hasStartDate !== hasStartTime) { + return this.createError({ + message: "تاریخ و زمان شروع باید هر دو وارد شوند", + }); + } + + if (hasEndDate !== hasEndTime) { + return this.createError({ + message: "تاریخ و زمان پایان باید هر دو وارد شوند", + }); + } + + if (!((hasStartDate && hasStartTime) || (hasEndDate && hasEndTime))) { + return this.createError({ + message: "حداقل تاریخ و زمان شروع یا پایان باید مشخص شود", + }); + } + + return true; + }); + +const EditViolationForm = ({ setOpenMachinesDialog, defaultValues, onSubmitBase }) => { + const { + control, + handleSubmit, + formState: { isSubmitting, errors }, + } = useForm({ defaultValues, resolver: yupResolver(schema), mode: "all" }); + + const handleOnSubmit = async (data) => { + await onSubmitBase(data); + }; + return ( + <> + + + + + + + + {errors?.[""]?.message && ( + + {errors?.[""]?.message} + + )} + + + + + + + + + + ); +}; +export default EditViolationForm \ No newline at end of file diff --git a/src/components/dashboard/roadMissions/violations/Actions/EditViolation/MissionDates/index.jsx b/src/components/dashboard/roadMissions/violations/Actions/EditViolation/MissionDates/index.jsx new file mode 100644 index 0000000..225ab8f --- /dev/null +++ b/src/components/dashboard/roadMissions/violations/Actions/EditViolation/MissionDates/index.jsx @@ -0,0 +1,99 @@ +import MuiDatePicker from "@/core/components/MuiDatePicker"; +import MuiTimePicker from "@/core/components/MuiTimePicker"; +import { makeDateTime } from "@/core/utils/makeDateTime"; +import { Grid } from "@mui/material"; +import { Controller, useWatch } from "react-hook-form"; + +const MissionDates = ({ control }) => { + const start_date = useWatch({ control, name: "start_date" }); + const end_date = useWatch({ control, name: "end_date" }); + const now = new Date(); + + return ( + <> + + { + return ( + field.onChange(value || [])} + helperText={error ? error.message : null} + /> + ); + }} + /> + + + { + return ( + field.onChange(value || null)} + helperText={error ? error.message : null} + /> + ); + }} + /> + + + { + return ( + field.onChange(value || [])} + helperText={error ? error.message : null} + /> + ); + }} + /> + + + { + return ( + field.onChange(value || null)} + helperText={error ? error.message : null} + /> + ); + }} + /> + + + ); +}; +export default MissionDates; diff --git a/src/components/dashboard/roadMissions/violations/Actions/EditViolation/index.jsx b/src/components/dashboard/roadMissions/violations/Actions/EditViolation/index.jsx new file mode 100644 index 0000000..506d325 --- /dev/null +++ b/src/components/dashboard/roadMissions/violations/Actions/EditViolation/index.jsx @@ -0,0 +1,51 @@ +import CloseIcon from "@mui/icons-material/Close"; +import { Dialog, IconButton, Tooltip } from "@mui/material"; +import { useState } from "react"; +import BookmarkAddIcon from "@mui/icons-material/BookmarkAdd"; +import EditController from "./EditController"; + +const EditViolation = ({ 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 EditViolation; diff --git a/src/components/dashboard/roadMissions/violations/RowActions/index.jsx b/src/components/dashboard/roadMissions/violations/RowActions/index.jsx index 0451883..01c8254 100644 --- a/src/components/dashboard/roadMissions/violations/RowActions/index.jsx +++ b/src/components/dashboard/roadMissions/violations/RowActions/index.jsx @@ -1,7 +1,15 @@ import CreateWithoutProcess from "../Actions/CreateWithoutProcess"; +import EditViolation from "../Actions/EditViolation"; const RowActions = ({ row, mutate }) => { const canEdit = row.original.status === 0 && row.original.type === 1; - return canEdit && ; + return ( + <> + {!row.original.mileage && + } + {canEdit && } + + ); + }; export default RowActions; diff --git a/src/components/dashboard/roadMissions/violations/Toolbar.jsx b/src/components/dashboard/roadMissions/violations/Toolbar.jsx new file mode 100644 index 0000000..1edd6cd --- /dev/null +++ b/src/components/dashboard/roadMissions/violations/Toolbar.jsx @@ -0,0 +1,11 @@ +import { Stack } from "@mui/material"; +import CreateViolation from "./Actions/CreateViolation"; + +const Toolbar = ({ mutate }) => { + return ( + + + + ); +}; +export default Toolbar; diff --git a/src/components/dashboard/roadMissions/violations/ViolationsList.jsx b/src/components/dashboard/roadMissions/violations/ViolationsList.jsx index f7a3719..dfbc013 100644 --- a/src/components/dashboard/roadMissions/violations/ViolationsList.jsx +++ b/src/components/dashboard/roadMissions/violations/ViolationsList.jsx @@ -5,6 +5,7 @@ import { Box } from "@mui/material"; import moment from "jalali-moment"; import { useMemo } from "react"; import RowActions from "./RowActions"; +import Toolbar from "./Toolbar"; const ViolationsList = () => { const columns = useMemo( @@ -68,6 +69,7 @@ const ViolationsList = () => {