From 63ec584aca243c3a5d9144608fcbbd8b8f1a5c82 Mon Sep 17 00:00:00 2001 From: baslani Date: Wed, 10 Jun 2026 16:30:08 +0330 Subject: [PATCH 1/3] [wip] implementing machines cartable --- jsconfig.json | 38 +-- .../dashboard/machines/loading.jsx | 7 + .../dashboard/machines/page.jsx | 19 ++ .../Form/CreateTollHouse/CityContent.jsx | 33 ++ .../Form/CreateTollHouse/CityController.jsx | 14 + .../CreateTollHouse/EditMachineContent.jsx | 145 ++++++++ .../Form/CreateTollHouse/ImageUpload.jsx | 77 +++++ .../CreateTollHouse/LogesticController.jsx | 42 +++ .../Form/CreateTollHouse/MachineInfo.jsx | 311 ++++++++++++++++++ .../CreateTollHouse/TollHouseLocation.jsx | 20 ++ .../machines/Form/CreateTollHouse/index.jsx | 154 +++++++++ .../machines/Form/Edit/EditController.jsx | 77 +++++ .../dashboard/machines/Form/Edit/index.jsx | 59 ++++ .../dashboard/machines/MachinesList.jsx | 242 ++++++++++++++ .../RowActions/DeleteDialog/DeleteContent.jsx | 39 +++ .../RowActions/DeleteDialog/index.jsx | 34 ++ .../RowActions/ImageForm/ImageFormContent.jsx | 45 +++ .../RowActions/ImageForm/ImagesContent.jsx | 39 +++ .../machines/RowActions/ImageForm/index.jsx | 43 +++ .../LocationForm/LocationFormContent.jsx | 35 ++ .../RowActions/LocationForm/index.jsx | 38 +++ .../dashboard/machines/RowActions/index.jsx | 16 + .../machines/ShowProjectArea/ShowBound.jsx | 29 ++ .../machines/ShowProjectArea/index.jsx | 65 ++++ src/components/dashboard/machines/Toolbar.jsx | 6 + src/components/dashboard/machines/index.jsx | 14 + src/core/utils/pageMenu.js | 16 +- src/core/utils/pageMenuDev.js | 10 +- src/core/utils/routes.js | 5 + 29 files changed, 1640 insertions(+), 32 deletions(-) create mode 100644 src/app/(withAuth)/(dashboardLayout)/dashboard/machines/loading.jsx create mode 100644 src/app/(withAuth)/(dashboardLayout)/dashboard/machines/page.jsx create mode 100644 src/components/dashboard/machines/Form/CreateTollHouse/CityContent.jsx create mode 100644 src/components/dashboard/machines/Form/CreateTollHouse/CityController.jsx create mode 100644 src/components/dashboard/machines/Form/CreateTollHouse/EditMachineContent.jsx create mode 100644 src/components/dashboard/machines/Form/CreateTollHouse/ImageUpload.jsx create mode 100644 src/components/dashboard/machines/Form/CreateTollHouse/LogesticController.jsx create mode 100644 src/components/dashboard/machines/Form/CreateTollHouse/MachineInfo.jsx create mode 100644 src/components/dashboard/machines/Form/CreateTollHouse/TollHouseLocation.jsx create mode 100644 src/components/dashboard/machines/Form/CreateTollHouse/index.jsx create mode 100644 src/components/dashboard/machines/Form/Edit/EditController.jsx create mode 100644 src/components/dashboard/machines/Form/Edit/index.jsx create mode 100644 src/components/dashboard/machines/MachinesList.jsx create mode 100644 src/components/dashboard/machines/RowActions/DeleteDialog/DeleteContent.jsx create mode 100644 src/components/dashboard/machines/RowActions/DeleteDialog/index.jsx create mode 100644 src/components/dashboard/machines/RowActions/ImageForm/ImageFormContent.jsx create mode 100644 src/components/dashboard/machines/RowActions/ImageForm/ImagesContent.jsx create mode 100644 src/components/dashboard/machines/RowActions/ImageForm/index.jsx create mode 100644 src/components/dashboard/machines/RowActions/LocationForm/LocationFormContent.jsx create mode 100644 src/components/dashboard/machines/RowActions/LocationForm/index.jsx create mode 100644 src/components/dashboard/machines/RowActions/index.jsx create mode 100644 src/components/dashboard/machines/ShowProjectArea/ShowBound.jsx create mode 100644 src/components/dashboard/machines/ShowProjectArea/index.jsx create mode 100644 src/components/dashboard/machines/Toolbar.jsx create mode 100644 src/components/dashboard/machines/index.jsx diff --git a/jsconfig.json b/jsconfig.json index b51472a..c136578 100644 --- a/jsconfig.json +++ b/jsconfig.json @@ -1,27 +1,27 @@ // javascript for production -{ - "compilerOptions": { - "paths": { - "@/*": ["./src/*"], - "^/*": ["./public/*"] - } - } -} - -// typescript for local development // { // "compilerOptions": { -// "jsx": "react-jsx", -// "noUnusedLocals": true, -// "noImplicitAny": false, -// "checkJs": true, -// "allowJs": true, -// "strict": true, -// "baseUrl": ".", // "paths": { // "@/*": ["./src/*"], // "^/*": ["./public/*"] // } -// }, -// "exclude": ["node_modules"] +// } // } + +// typescript for local development +{ + "compilerOptions": { + "jsx": "react-jsx", + "noUnusedLocals": true, + "noImplicitAny": false, + "checkJs": true, + "allowJs": true, + "strict": true, + "baseUrl": ".", + "paths": { + "@/*": ["./src/*"], + "^/*": ["./public/*"] + } + }, + "exclude": ["node_modules"] +} diff --git a/src/app/(withAuth)/(dashboardLayout)/dashboard/machines/loading.jsx b/src/app/(withAuth)/(dashboardLayout)/dashboard/machines/loading.jsx new file mode 100644 index 0000000..4535a8d --- /dev/null +++ b/src/app/(withAuth)/(dashboardLayout)/dashboard/machines/loading.jsx @@ -0,0 +1,7 @@ +"use client"; +import PageLoading from "@/core/components/PageLoading"; + +const Loading = () => { + return ; +}; +export default Loading; diff --git a/src/app/(withAuth)/(dashboardLayout)/dashboard/machines/page.jsx b/src/app/(withAuth)/(dashboardLayout)/dashboard/machines/page.jsx new file mode 100644 index 0000000..53e7040 --- /dev/null +++ b/src/app/(withAuth)/(dashboardLayout)/dashboard/machines/page.jsx @@ -0,0 +1,19 @@ +import WithPermission from "@/core/middlewares/withPermission"; +import MachinesPage from "@/components/dashboard/machines" + +export const metadata = { + title: "کارتابل ماشین‌ها", +}; +const Page = () => { + return ( + <> + + + + + ); +}; + +export default Page; diff --git a/src/components/dashboard/machines/Form/CreateTollHouse/CityContent.jsx b/src/components/dashboard/machines/Form/CreateTollHouse/CityContent.jsx new file mode 100644 index 0000000..3069208 --- /dev/null +++ b/src/components/dashboard/machines/Form/CreateTollHouse/CityContent.jsx @@ -0,0 +1,33 @@ +import SelectBox from "@/core/components/SelectBox"; +import { useEffect, useState } from "react"; +import useCities from "@/lib/hooks/useCities"; + +const CityContent = ({ provinceID, field, fieldState: { error } }) => { + const { cityList, loadingCityList, errorCityList } = useCities(provinceID); + const [prevDependency, setPrevDependency] = useState(provinceID); + + useEffect(() => { + if (prevDependency === provinceID) return; + field.onChange(null); + setPrevDependency(provinceID); + }, [provinceID]); + + return ( + + ); +}; +export default CityContent; diff --git a/src/components/dashboard/machines/Form/CreateTollHouse/CityController.jsx b/src/components/dashboard/machines/Form/CreateTollHouse/CityController.jsx new file mode 100644 index 0000000..72de542 --- /dev/null +++ b/src/components/dashboard/machines/Form/CreateTollHouse/CityController.jsx @@ -0,0 +1,14 @@ +import { Controller, useWatch } from "react-hook-form"; +import CityContent from "./CityContent"; + +const CityController = ({ control }) => { + const provinceID = useWatch({ control, name: "province_id" }); + return ( + } + /> + ); +}; +export default CityController; diff --git a/src/components/dashboard/machines/Form/CreateTollHouse/EditMachineContent.jsx b/src/components/dashboard/machines/Form/CreateTollHouse/EditMachineContent.jsx new file mode 100644 index 0000000..7128b06 --- /dev/null +++ b/src/components/dashboard/machines/Form/CreateTollHouse/EditMachineContent.jsx @@ -0,0 +1,145 @@ +import StyledForm from "@/core/components/StyledForm"; +import { yupResolver } from "@hookform/resolvers/yup"; +import BeenhereIcon from "@mui/icons-material/Beenhere"; +import ExitToAppIcon from "@mui/icons-material/ExitToApp"; +import KeyboardDoubleArrowLeftIcon from "@mui/icons-material/KeyboardDoubleArrowLeft"; +import KeyboardDoubleArrowRightIcon from "@mui/icons-material/KeyboardDoubleArrowRight"; +import OtherHousesIcon from "@mui/icons-material/OtherHouses"; +import { Box, Button, DialogActions, DialogContent, Tab, Tabs } from "@mui/material"; +import { useState } from "react"; +import { useForm } from "react-hook-form"; +import { number, object, string } from "yup"; +import MachineInfo from "./MachineInfo"; + +function TabPanel(props) { + const { children, value, index } = props; + return ( + + ); +} + +const validationSchema = object({ + name: string().required("وارد کردن نام راهدارخانه الزامیست!"), + status: string().required("وارد کردن وضعیت الزامیست!"), + type: string().required("وارد کردن نوع الزامیست!"), + phone: string().required("وارد کردن تلفن راهدارخانه الزامیست!"), + team_num: string().required("وارد کردن تعداد اکیپ الزامیست!"), + staff_num: string().required("وارد کردن تعداد پرسنل الزامیست!"), + responsible_name: string().required("وارد کردن نام مسئول الزامیست!"), + responsible_mobile: string() + .required("وارد کردن تلفن مسئول الزامیست!") + .matches(/^09\d{9}$/, "شماره تلفن مسئول باید با ۰۹ شروع شده و ۱۱ رقم باشد!"), + successor_name: string().required("وارد کردن نام جانشین الزامیست!"), + successor_mobile: string() + .required("وارد کردن تلفن جانشین الزامیست!") + .matches(/^09\d{9}$/, "شماره تلفن جانشین باید با ۰۹ شروع شده و ۱۱ رقم باشد!"), + province_id: number().required("وارد کردن استان الزامیست!"), + city_id: number().required("وارد کردن شهرستان الزامیست!"), + machines_light: string().required("وارد کردن ماشین آلات سبک الزامیست!"), + machines_sheavy: string().required("وارد کردن ماشین آلات نیمه سنگین الزامیست!"), + machines_heavy: string().required("وارد کردن ماشین آلات سنگین الزامیست!"), +}); +const EditMachineContent = ({ setOpen, SubmitCreateTollHouse, defaultValues }) => { + const [tabState, setTabState] = useState(0); + const handleClose = () => { + setOpen(false); + }; + + const handleChangeTab = (event, newValue) => { + setTabState(newValue); + }; + const handleNext = async () => { + const fieldsToValidate = [ + "status", + "province_id", + "city_id", + "station_id", + ]; + const isValid = await trigger(fieldsToValidate); + if (isValid) { + setTabState(tabState + 1); + } + }; + const handlePrev = () => { + if (tabState === 0) { + handleClose(); + } else { + setTabState(tabState - 1); + } + }; + const { + control, + handleSubmit, + trigger, + formState: { isSubmitting }, + } = useForm({ + defaultValues, + resolver: yupResolver(validationSchema), + mode: "all", + }); + const onSubmitBase = async (data) => { + await SubmitCreateTollHouse(data); + }; + return ( + + + } label="اطلاعات راهدارخانه" /> + + + + + + + + + + + {tabState !== 3 && ( + + )} + {tabState === 3 && ( + + )} + + + ); +}; +export default EditMachineContent; diff --git a/src/components/dashboard/machines/Form/CreateTollHouse/ImageUpload.jsx b/src/components/dashboard/machines/Form/CreateTollHouse/ImageUpload.jsx new file mode 100644 index 0000000..033a6f1 --- /dev/null +++ b/src/components/dashboard/machines/Form/CreateTollHouse/ImageUpload.jsx @@ -0,0 +1,77 @@ +import { FormControl, FormHelperText } from "@mui/material"; +import UploadSystem from "@/core/components/UploadSystem"; +import React, { useEffect, useState } from "react"; + +const ImageUpload = ({ name, value, onChange, error, title }) => { + const [beforeImg, setBeforeImg] = useState(value ? value : null); + const [beforeFileType, setBeforeFileType] = useState(value ? "image/" : null); + const [beforeFileName, setBeforeFileName] = useState(null); + const [showBeforeImage, setShowBeforeImage] = useState(!value); + + useEffect(() => { + if (value) { + setShowBeforeImage(false); + if (typeof value === "string") { + setBeforeImg(value); + setBeforeFileType("image/"); + } else if (value instanceof File) { + setBeforeImg(URL.createObjectURL(value)); + setBeforeFileType(value.type); + } + } + }, [value]); + + const handleFileChange = (event) => { + const uploadedFile = event.target?.files?.[0]; + if (uploadedFile) { + const fileType = event.target?.files?.[0].type; + const fileName = event.target?.files?.[0].name; + setBeforeImg(URL.createObjectURL(uploadedFile)); + setBeforeFileType(fileType); + setBeforeFileName(fileName); + onChange(uploadedFile); + setShowBeforeImage(false); + } + }; + + return ( + + + {title} + + + {error ? error.message : null} + + ); +}; +export default ImageUpload; diff --git a/src/components/dashboard/machines/Form/CreateTollHouse/LogesticController.jsx b/src/components/dashboard/machines/Form/CreateTollHouse/LogesticController.jsx new file mode 100644 index 0000000..513abbf --- /dev/null +++ b/src/components/dashboard/machines/Form/CreateTollHouse/LogesticController.jsx @@ -0,0 +1,42 @@ +import { Controller, useWatch } from "react-hook-form"; +import SelectBox from "@/core/components/SelectBox"; +import useProvinces from "@/lib/hooks/useProvince"; +import { Grid } from "@mui/material"; +import CityController from "./CityController"; + +const LogesticController = ({ control }) => { + const { provinces, errorProvinces, loadingProvinces } = useProvinces(); + const isProvince = useWatch({ control, name: "is_province" }); + return ( + + {!isProvince ? ( + + { + return ( + + ); + }} + /> + + ) : null} + + + + + ); +}; +export default LogesticController; diff --git a/src/components/dashboard/machines/Form/CreateTollHouse/MachineInfo.jsx b/src/components/dashboard/machines/Form/CreateTollHouse/MachineInfo.jsx new file mode 100644 index 0000000..6fca8a4 --- /dev/null +++ b/src/components/dashboard/machines/Form/CreateTollHouse/MachineInfo.jsx @@ -0,0 +1,311 @@ +import { Grid, Stack, TextField } from "@mui/material"; +import { Controller } from "react-hook-form"; +import SelectBox from "@/core/components/SelectBox"; +import useProvince from "@/lib/hooks/useProvince"; +import PersianTextField from "@/core/components/PersianTextField"; +import CityController from "./CityController"; +import LogesticController from "./LogesticController"; + +const MachineInfo = ({ control }) => { + const { provinces, loadingProvinces, errorProvinces } = useProvince(); + return ( + + + + + ( + + )} + /> + + + ( + { + const inputValue = event.target.value; + if (isNaN(Number(inputValue))) { + return; + } + onChange(inputValue); + }} + /> + )} + /> + + + + + + + + + + { + return ( + + ); + }} + /> + + + { + return ( + + ); + }} + /> + + + + + + + ( + { + const inputValue = event.target.value; + if (isNaN(Number(inputValue))) { + return; + } + onChange(inputValue); + }} + /> + )} + /> + + + ( + { + const inputValue = event.target.value; + if (isNaN(Number(inputValue))) { + return; + } + onChange(inputValue); + }} + /> + )} + /> + + + + + + + ( + + )} + /> + + + ( + { + const inputValue = event.target.value; + if (isNaN(Number(inputValue))) { + return; + } + onChange(inputValue); + }} + /> + )} + /> + + + + + + + ( + + )} + /> + + + ( + { + const inputValue = event.target.value; + if (isNaN(Number(inputValue))) { + return; + } + onChange(inputValue); + }} + /> + )} + /> + + + + + ); +}; +export default MachineInfo; diff --git a/src/components/dashboard/machines/Form/CreateTollHouse/TollHouseLocation.jsx b/src/components/dashboard/machines/Form/CreateTollHouse/TollHouseLocation.jsx new file mode 100644 index 0000000..98d36a7 --- /dev/null +++ b/src/components/dashboard/machines/Form/CreateTollHouse/TollHouseLocation.jsx @@ -0,0 +1,20 @@ +import MapInfoOneMarker from "@/core/components/MapInfoOneMarker"; +import { Stack } from "@mui/material"; +import { useWatch } from "react-hook-form"; + +const TollHouseLocation = ({ setValue, errors, control }) => { + const StartPoint = useWatch({ control, name: "start_point" }); + return ( + <> + + + + + ); +}; +export default TollHouseLocation; diff --git a/src/components/dashboard/machines/Form/CreateTollHouse/index.jsx b/src/components/dashboard/machines/Form/CreateTollHouse/index.jsx new file mode 100644 index 0000000..3649dcd --- /dev/null +++ b/src/components/dashboard/machines/Form/CreateTollHouse/index.jsx @@ -0,0 +1,154 @@ +import { Button, Dialog, IconButton, useMediaQuery } from "@mui/material"; +import { useState } from "react"; +import CreateTollHouseContent from "./EditMachineContent"; +import { AddCircle } from "@mui/icons-material"; +import { useTheme } from "@emotion/react"; +import { CREATE_TOLL_HOUSE } from "@/core/utils/routes"; +import useRequest from "@/lib/hooks/useRequest"; +import CloseIcon from "@mui/icons-material/Close"; +import { useAuth } from "@/lib/contexts/auth"; +import { usePermissions } from "@/lib/hooks/usePermissions"; + +const CreateTollHouse = ({ mutate }) => { + const requestServer = useRequest({ notificationSuccess: true }); + const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down("sm")); + const [open, setOpen] = useState(false); + const { user } = useAuth(); + const { data: userPermissions } = usePermissions(); + const hasCreateProvincePermission = userPermissions.some((item) => ["add-tollhouse"].includes(item)); + const handleOpen = () => { + setOpen(true); + }; + const SubmitCreateTollHouse = async (result) => { + const fields = [ + "province_id", + "city_id", + "name", + "status", + "type", + "team_num", + "staff_num", + "phone", + "responsible_name", + "responsible_mobile", + "successor_name", + "successor_mobile", + "machines_light", + "machines_sheavy", + "machines_heavy", + "overview_files_1", + "overview_files_2", + "overview_files_3", + "overview_files_4", + "overview_files_5", + ]; + const formData = new FormData(); + fields.forEach((field) => { + if (result[field] !== undefined && result[field] !== null) { + formData.append(field, result[field]); + } + }); + formData.append("lat", result.start_point.lat); + formData.append("lng", result.start_point.lng); + formData.append("area[type]", "polygon"); + result.area.map((point, pointIndex) => { + formData.append(`area[coordinates][${pointIndex}][0]`, point.lon); + formData.append(`area[coordinates][${pointIndex}][1]`, point.lat); + }); + const repeat_first = result.area.length; + formData.append(`area[coordinates][${repeat_first}][0]`, result.area[0].lon); + formData.append(`area[coordinates][${repeat_first}][1]`, result.area[0].lat); + await requestServer(`${CREATE_TOLL_HOUSE}`, "post", { + data: formData, + }) + .then(() => { + mutate(); + setOpen(false); + }) + .catch(() => {}); + }; + const defaultValues = { + name: "", + is_province: !hasCreateProvincePermission, + province_id: !hasCreateProvincePermission ? user.province_id : null, + city_id: null, + status: "", + type: "", + phone: "", + team_num: "", + staff_num: "", + responsible_name: "", + responsible_mobile: "", + successor_name: "", + successor_mobile: "", + machines_light: "", + machines_sheavy: "", + machines_heavy: "", + overview_files_1: null, + overview_files_2: null, + overview_files_3: null, + overview_files_4: null, + overview_files_5: null, + start_point: "", + area: [ + { lat: "", lon: "" }, + { lat: "", lon: "" }, + { lat: "", lon: "" }, + { lat: "", lon: "" }, + ], + }; + return ( + <> + {isMobile ? ( + + + + ) : ( + + )} + setOpen(false)} + PaperProps={{ + sx: { + boxShadow: "rgba(60, 64, 67, 0.3) 0px 1px 16px 0px, rgba(60, 64, 67, 0.15) 0px 1px 10px 0px", + }, + }} + maxWidth={"md"} + scroll="paper" + > + setOpen(false)} + sx={(theme) => ({ + position: "absolute", + right: 8, + top: 8, + zIndex: 50, + color: theme.palette.grey[500], + })} + > + + + {open && ( + + )} + + + ); +}; +export default CreateTollHouse; diff --git a/src/components/dashboard/machines/Form/Edit/EditController.jsx b/src/components/dashboard/machines/Form/Edit/EditController.jsx new file mode 100644 index 0000000..a5f9c7f --- /dev/null +++ b/src/components/dashboard/machines/Form/Edit/EditController.jsx @@ -0,0 +1,77 @@ +import DialogLoading from "@/core/components/DialogLoading"; +import { GET_TOLL_HOUSE_DETAILS, UPDATE_TOLL_HOUSE_ITEM } from "@/core/utils/routes"; +import { usePermissions } from "@/lib/hooks/usePermissions"; +import useRequest from "@/lib/hooks/useRequest"; +import { useEffect, useState } from "react"; +import EditMachineContent from "../CreateTollHouse/EditMachineContent"; + +const EditController = ({ rowId, mutate, setOpenEditDialog }) => { + const { data: userPermissions } = usePermissions(); + const hasCreateProvincePermission = userPermissions.some((item) => ["update-machine"].includes(item)); + const requestServer = useRequest(); + const [MachineItemDetails, setMachineItemDetails] = useState(null); + const [machineItemDetailsLoading, setMachineItemDetailsLoading] = useState(false); + + useEffect(() => { + setMachineItemDetailsLoading(true); + requestServer(`${GET_TOLL_HOUSE_DETAILS}/${rowId}`, "get") + .then((response) => { + setMachineItemDetails(response.data.data); + setMachineItemDetailsLoading(false); + }) + .catch((e) => { + setMachineItemDetailsLoading(false); + }); + }, [rowId]); + + const defaultData = { + is_province: !hasCreateProvincePermission, + province_id: MachineItemDetails?.province_id || null, + city_id: MachineItemDetails?.city_id || null, + status: MachineItemDetails?.status || "", + station_id: MachineItemDetails?.station_id || "", + }; + const HandleSubmit = async (result) => { + const fields = [ + "status", + "province_id", + "city_id", + "station_id", + ]; + const formData = new FormData(); + fields.forEach((field) => { + if (result[field] !== undefined && result[field] !== null) { + formData.append(field, result[field]); + } + }); + for (let i = 1; i <= 5; i++) { + if (result[`overview_files_${i}`] !== defaultData[`overview_files_${i}`]) { + formData.append(`overview_files_${i}`, result[`overview_files_${i}`]); + } + } + + await requestServer(`${UPDATE_TOLL_HOUSE_ITEM}/${rowId}`, "post", { + data: formData, + notificationSuccess: true, + }) + .then(() => { + mutate(); + setOpenEditDialog(false); + }) + .catch(() => {}); + }; + return ( + <> + {machineItemDetailsLoading ? ( + + ) : ( + + )} + + ); +}; +export default EditController; diff --git a/src/components/dashboard/machines/Form/Edit/index.jsx b/src/components/dashboard/machines/Form/Edit/index.jsx new file mode 100644 index 0000000..53be78c --- /dev/null +++ b/src/components/dashboard/machines/Form/Edit/index.jsx @@ -0,0 +1,59 @@ +import { Dialog, IconButton, Tooltip } from "@mui/material"; +import { useState } from "react"; +import BorderColorIcon from "@mui/icons-material/BorderColor"; +import EditController from "./EditController"; +import CloseIcon from "@mui/icons-material/Close"; + +const EditForm = ({ row, mutate, rowId }) => { + const [openEditDialog, setOpenEditDialog] = useState(false); + + return ( + <> + + { + setOpenEditDialog(true); + }} + > + + + + + setOpenEditDialog(false)} + sx={(theme) => ({ + position: "absolute", + right: 8, + top: 8, + zIndex: 50, + color: theme.palette.grey[500], + })} + > + + + {openEditDialog && ( + + )} + + + ); +}; +export default EditForm; diff --git a/src/components/dashboard/machines/MachinesList.jsx b/src/components/dashboard/machines/MachinesList.jsx new file mode 100644 index 0000000..bcfb104 --- /dev/null +++ b/src/components/dashboard/machines/MachinesList.jsx @@ -0,0 +1,242 @@ +"use client"; +import CustomSelectByDependency from "@/core/components/DataTable/filter/fieldsType/CustomSelectByDependency"; +import DataTableWithAuth from "@/core/components/DataTableWithAuth"; +import { GET_MACHINES_LIST } from "@/core/utils/routes"; +import { useAuth } from "@/lib/contexts/auth"; +import useEdaratLists from "@/lib/hooks/useEdaratLists"; +import { usePermissions } from "@/lib/hooks/usePermissions"; +import useProvinces from "@/lib/hooks/useProvince"; +import { Box, Typography } from "@mui/material"; +import { useEffect, useMemo, useState } from "react"; +import RowActions from "./RowActions"; +import Toolbar from "./Toolbar"; + +const statusOptions = [ + { value: "", label: "همه وضعیت ها" }, + { value: 1, label: "آماده" }, + { value: 2, label: "رزرو" }, + { value: 3, label: "در ماموریت" }, + { value: 4, label: "خارج از رده" }, + { value: 5, label: "در تعمیر" }, + { value: 6, label: "فروخته شده" }, + { value: 7, label: "نیاز به تعمیر" }, +]; + +const MachinesList = () => { + const { data: userPermissions } = usePermissions(); + const hasCountryPermission = userPermissions?.includes("show-tollhouse"); + const { user } = useAuth(); + + const columns = useMemo(() => { + const provinceColumn = { + accessorKey: "province_id", + header: "استان", + id: "province_id", + enableColumnFilter: true, + datatype: "numeric", + filterMode: "equals", + grow: false, + size: 130, + ColumnSelectComponent: (props) => { + const { provinces, errorProvinces, loadingProvinces } = useProvinces(); + const getColumnSelectOptions = useMemo(() => { + if (loadingProvinces) { + return [{ value: "loading", label: "در حال بارگذاری..." }]; + } + if (errorProvinces) { + return [{ value: "error", label: "خطا در بارگذاری" }]; + } + return [ + { value: "", label: "کل کشور" }, + ...provinces.map((province) => ({ + value: province.id, + label: province.name_fa, + })), + ]; + }, [provinces, errorProvinces, loadingProvinces]); + return ( + + ); + }, + Cell: ({ row }) => <>{row.original.province_name}, + }; + return [ + { + accessorKey: "id", + header: "کد یکتا", // Unique Code + id: "id", + enableColumnFilter: true, + datatype: "text", + filterMode: "equals", + sortDescFirst: false, + columnFilterModeOptions: ["equals", "contains"], + grow: false, + size: 100, + }, + + { + header: "اطلاعات ماشین‌ها", + id: "machines_info", + enableColumnFilter: false, + enableSorting: false, + datatype: "text", + grow: false, + size: 50, + columns: [ + { + accessorKey: "car_name", + header: "نام ماشین", + id: "car_name", + enableColumnFilter: true, + datatype: "text", + filterMode: "equals", + sortDescFirst: false, + columnFilterModeOptions: ["equals", "contains"], + grow: false, + size: 100, + }, + { + accessorKey: "car_type", + header: "نوع ماشین", + id: "car_type", + enableColumnFilter: true, + datatype: "text", + filterMode: "equals", + sortDescFirst: false, + columnFilterModeOptions: ["equals", "contains"], + grow: false, + size: 100, + }, + { + accessorKey: "machine_code", + header: "کد ماشین", + id: "machine_code", + enableColumnFilter: true, + datatype: "text", + filterMode: "equals", + sortDescFirst: false, + columnFilterModeOptions: ["equals", "contains"], + grow: false, + size: 100, + }, + { + accessorKey: "plak_number", + header: "پلاک", + id: "plak_number", + enableColumnFilter: true, + datatype: "text", + filterMode: "equals", + sortDescFirst: false, + columnFilterModeOptions: ["equals", "contains"], + grow: false, + size: 100, + }, + ...(hasCountryPermission ? [provinceColumn] : []), + { + accessorKey: "city_id", + header: "شهرستان", // Office + id: "city_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: ({ row }) => <>{row.original.city_name}, + }, + { + accessorKey: "status", + header: "وضعیت", + id: "status", + enableColumnFilter: true, + datatype: "numeric", + filterMode: "equals", + grow: false, + size: 100, + columnSelectOption: () => { + return statusOptions.map((status) => ({ + value: status.value, + label: status.label, + })); + }, + Cell: ({ row }) => { + const status = statusOptions.find((option) => option.value == row.original.status); + return {status?.label || ""}; + }, + }, + + ], + }, + ]; + }, []); + + return ( + <> + + + + + ); +}; +export default MachinesList; diff --git a/src/components/dashboard/machines/RowActions/DeleteDialog/DeleteContent.jsx b/src/components/dashboard/machines/RowActions/DeleteDialog/DeleteContent.jsx new file mode 100644 index 0000000..3f3095a --- /dev/null +++ b/src/components/dashboard/machines/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_TOLL_HOUSE_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_TOLL_HOUSE_ITEM}/${rowId}`, "delete") + .then(() => { + mutate(); + setOpenDeleteDialog(false); + setSubmitting(false); + }) + .catch(() => { + setSubmitting(false); + }); + }; + return ( + <> + + + آیا از حذف راهدارخانه اطمینان دارید؟ + + + + + + + + ); +}; +export default DeleteContent; diff --git a/src/components/dashboard/machines/RowActions/DeleteDialog/index.jsx b/src/components/dashboard/machines/RowActions/DeleteDialog/index.jsx new file mode 100644 index 0000000..54278bd --- /dev/null +++ b/src/components/dashboard/machines/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/machines/RowActions/ImageForm/ImageFormContent.jsx b/src/components/dashboard/machines/RowActions/ImageForm/ImageFormContent.jsx new file mode 100644 index 0000000..c8f22d0 --- /dev/null +++ b/src/components/dashboard/machines/RowActions/ImageForm/ImageFormContent.jsx @@ -0,0 +1,45 @@ +import { Box, Stack, Typography, useTheme } from "@mui/material"; +import Image from "next/image"; + +const ImageFormContent = ({ image, title }) => { + const theme = useTheme(); + return ( + + + {title} + + + Image + + + ); +}; +export default ImageFormContent; diff --git a/src/components/dashboard/machines/RowActions/ImageForm/ImagesContent.jsx b/src/components/dashboard/machines/RowActions/ImageForm/ImagesContent.jsx new file mode 100644 index 0000000..6e22c9a --- /dev/null +++ b/src/components/dashboard/machines/RowActions/ImageForm/ImagesContent.jsx @@ -0,0 +1,39 @@ +import { useEffect, useState } from "react"; +import ImageFormContent from "./ImageFormContent"; +import DialogLoading from "@/core/components/DialogLoading"; +import useRequest from "@/lib/hooks/useRequest"; +import { GET_TOLL_HOUSE_IMAGES } from "@/core/utils/routes"; +import { Typography } from "@mui/material"; +const titles = ["نمای کلی از راهداری", "آشیانه ماشین آلات", "انباری شن و ماسه", "محیط کاری", "سایر تصاویر"]; + +const ImagesContent = ({ rowId }) => { + const requestServer = useRequest(); + const [tollHouseImages, setTollHouseImages] = useState(null); + const [tollHouseImagesLoading, setTollHouseImagesLoading] = useState(false); + useEffect(() => { + setTollHouseImagesLoading(true); + requestServer(`${GET_TOLL_HOUSE_IMAGES}/${rowId}`, "get") + .then((response) => { + setTollHouseImages(response.data.data); + setTollHouseImagesLoading(false); + }) + .catch((e) => { + setTollHouseImagesLoading(false); + }); + }, [rowId]); + + return ( + <> + {tollHouseImagesLoading ? ( + + ) : tollHouseImages ? ( + tollHouseImages.map((image, index) => { + return ; + }) + ) : ( + تصویری در سامانه یافت نشد + )} + + ); +}; +export default ImagesContent; diff --git a/src/components/dashboard/machines/RowActions/ImageForm/index.jsx b/src/components/dashboard/machines/RowActions/ImageForm/index.jsx new file mode 100644 index 0000000..6ac8d25 --- /dev/null +++ b/src/components/dashboard/machines/RowActions/ImageForm/index.jsx @@ -0,0 +1,43 @@ +import PhotoLibraryIcon from "@mui/icons-material/PhotoLibrary"; +import { Button, Dialog, DialogActions, DialogContent, DialogTitle, IconButton, Tooltip } from "@mui/material"; +import { useEffect, useState } from "react"; +import ImagesContent from "./ImagesContent"; +import useRequest from "@/lib/hooks/useRequest"; +import { GET_TOLL_HOUSE_IMAGES } from "@/core/utils/routes"; +import DialogLoading from "@/core/components/DialogLoading"; + +const ImageDialog = ({ rowId }) => { + const [openImageDialog, setOpenImageDialog] = useState(false); + + return ( + <> + + setOpenImageDialog(true)}> + + + + setOpenImageDialog(false)} + PaperProps={{ + sx: { + boxShadow: "rgba(60, 64, 67, 0.3) 0px 1px 16px 0px, rgba(60, 64, 67, 0.15) 0px 1px 10px 0px", + }, + }} + maxWidth={"sm"} + scroll="paper" + > + تصاویر + {openImageDialog && } + + + + + + ); +}; + +export default ImageDialog; diff --git a/src/components/dashboard/machines/RowActions/LocationForm/LocationFormContent.jsx b/src/components/dashboard/machines/RowActions/LocationForm/LocationFormContent.jsx new file mode 100644 index 0000000..14b3db3 --- /dev/null +++ b/src/components/dashboard/machines/RowActions/LocationForm/LocationFormContent.jsx @@ -0,0 +1,35 @@ +"use client"; +import { Box, Button, DialogActions, DialogContent, Stack } from "@mui/material"; +import React from "react"; +import dynamic from "next/dynamic"; +import MapLoading from "@/core/components/MapLayer/Loading"; +import ShowLocationMarker from "@/core/components/ShowLocationMarker"; +const MapLayer = dynamic(() => import("@/core/components/MapLayer"), { + loading: () => , + ssr: false, +}); + +const LocationFormContent = ({ setOpenLocationDialog, start_lat, start_lng, end_lat, end_lng }) => { + return ( + <> + + + + + + + + + + + + ); +}; +export default LocationFormContent; diff --git a/src/components/dashboard/machines/RowActions/LocationForm/index.jsx b/src/components/dashboard/machines/RowActions/LocationForm/index.jsx new file mode 100644 index 0000000..f578ca8 --- /dev/null +++ b/src/components/dashboard/machines/RowActions/LocationForm/index.jsx @@ -0,0 +1,38 @@ +import React, { useState } from "react"; +import { Tooltip, IconButton, Dialog, DialogTitle } from "@mui/material"; +import ExploreIcon from "@mui/icons-material/Explore"; +import LocationFormContent from "./LocationFormContent"; +const LocationForm = ({ start_lat, start_lng, end_lat, end_lng }) => { + const [openLocationDialog, setOpenLocationDialog] = useState(false); + + return ( + <> + + setOpenLocationDialog(true)}> + + + + setOpenLocationDialog(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"} + > + موقعیت + + + + ); +}; +export default LocationForm; diff --git a/src/components/dashboard/machines/RowActions/index.jsx b/src/components/dashboard/machines/RowActions/index.jsx new file mode 100644 index 0000000..8feb437 --- /dev/null +++ b/src/components/dashboard/machines/RowActions/index.jsx @@ -0,0 +1,16 @@ +import { usePermissions } from "@/lib/hooks/usePermissions"; +import { Box } from "@mui/material"; +import EditForm from "../Form/Edit"; + +const RowActions = ({ row, mutate }) => { + const { data: userPermissions } = usePermissions(); + const hasEditPermission = userPermissions.some((item) => + ["update-machine"].includes(item) + ); + return ( + + {hasEditPermission && } + + ); +}; +export default RowActions; diff --git a/src/components/dashboard/machines/ShowProjectArea/ShowBound.jsx b/src/components/dashboard/machines/ShowProjectArea/ShowBound.jsx new file mode 100644 index 0000000..24db476 --- /dev/null +++ b/src/components/dashboard/machines/ShowProjectArea/ShowBound.jsx @@ -0,0 +1,29 @@ +import { useEffect, useRef } from "react"; +import { FeatureGroup, useMap } from "react-leaflet"; + +const ShowBound = ({ bound }) => { + const map = useMap(); + const featureRef = useRef(null); + + const safeFitBounds = (layer) => { + if (!layer || !map) return; + try { + const latLngs = layer.getLatLngs(); + map.fitBounds(latLngs, { + paddingTopLeft: [20, 20], + paddingBottomRight: [20, 20], + }); + } catch (e) { + console.warn("fitBounds failed:", e); + } + }; + + useEffect(() => { + bound?.editing?.disable(); + featureRef.current.addLayer(bound); + safeFitBounds(bound); + }, []); + + return ; +}; +export default ShowBound; diff --git a/src/components/dashboard/machines/ShowProjectArea/index.jsx b/src/components/dashboard/machines/ShowProjectArea/index.jsx new file mode 100644 index 0000000..250cd52 --- /dev/null +++ b/src/components/dashboard/machines/ShowProjectArea/index.jsx @@ -0,0 +1,65 @@ +const { IconButton, Tooltip, Box, Dialog, DialogTitle, Typography, DialogContent } = require("@mui/material"); +import MapLayer from "@/core/components/MapLayer"; +import CloseIcon from "@mui/icons-material/Close"; +import LayersIcon from "@mui/icons-material/Layers"; +import L from "leaflet"; +import { useMemo, useState } from "react"; +import ShowBound from "./ShowBound"; + +const ShowProjectArea = ({ primaryArea }) => { + const [openShowAreaDialog, setOpenShowAreaDialog] = useState(false); + const bound = useMemo(() => { + const coordinates = Object.values(primaryArea.coordinates); + + const latLngCoords = coordinates.map(([lat, lng]) => [parseFloat(lng), parseFloat(lat)]); + + return primaryArea.type === "polygon" + ? L.polygon(latLngCoords, { color: "#ff5555" }) + : L.polyline(latLngCoords); + }, [primaryArea]); + + return ( + + + setOpenShowAreaDialog(true)}> + + + + setOpenShowAreaDialog(false)} + PaperProps={{ + sx: { + transition: "all .3s", + boxShadow: "rgba(60, 64, 67, 0.3) 0px 1px 16px 0px, rgba(60, 64, 67, 0.15) 0px 1px 10px 0px", + borderRadius: 2, + padding: 1, + }, + }} + maxWidth="sm" + fullWidth + dir="rtl" + > + + + محدوده طرح + + setOpenShowAreaDialog(false)} size="small"> + + + + + + + + + + + + + + + ); +}; + +export default ShowProjectArea; diff --git a/src/components/dashboard/machines/Toolbar.jsx b/src/components/dashboard/machines/Toolbar.jsx new file mode 100644 index 0000000..ae86ec0 --- /dev/null +++ b/src/components/dashboard/machines/Toolbar.jsx @@ -0,0 +1,6 @@ +import { Box } from "@mui/material"; + +const Toolbar = ({ table, filterData, mutate }) => { + return machines toolbar; +}; +export default Toolbar; diff --git a/src/components/dashboard/machines/index.jsx b/src/components/dashboard/machines/index.jsx new file mode 100644 index 0000000..ed3db7c --- /dev/null +++ b/src/components/dashboard/machines/index.jsx @@ -0,0 +1,14 @@ +"use client"; +import PageTitle from "@/core/components/PageTitle"; +import { Stack } from "@mui/material"; +import MachinesList from "./MachinesList"; + +const MachinesPage = () => { + return ( + + + + + ); +}; +export default MachinesPage; diff --git a/src/core/utils/pageMenu.js b/src/core/utils/pageMenu.js index a770353..99366c4 100644 --- a/src/core/utils/pageMenu.js +++ b/src/core/utils/pageMenu.js @@ -1,3 +1,4 @@ +import { ReportProblem, RvHookup } from "@mui/icons-material"; import AccountTreeIcon from "@mui/icons-material/AccountTree"; import AdminPanelSettingsIcon from "@mui/icons-material/AdminPanelSettings"; import AssessmentIcon from "@mui/icons-material/Assessment"; @@ -5,7 +6,9 @@ import AssignmentLateIcon from "@mui/icons-material/AssignmentLate"; import AssistantIcon from "@mui/icons-material/Assistant"; import BallotIcon from "@mui/icons-material/Ballot"; import BiotechIcon from "@mui/icons-material/Biotech"; +import ContactPageIcon from "@mui/icons-material/ContactPage"; import CottageIcon from "@mui/icons-material/Cottage"; +import DirectionsBusIcon from "@mui/icons-material/DirectionsBus"; import DoorbellIcon from "@mui/icons-material/Doorbell"; import DriveEtaIcon from "@mui/icons-material/DriveEta"; import ElectricBoltIcon from "@mui/icons-material/ElectricBolt"; @@ -23,17 +26,14 @@ import MapIcon from "@mui/icons-material/Map"; import MapsHomeWorkIcon from "@mui/icons-material/MapsHomeWork"; import Mediation from "@mui/icons-material/Mediation"; import PsychologyAltIcon from "@mui/icons-material/PsychologyAlt"; +import RemoveRoadIcon from "@mui/icons-material/RemoveRoad"; import ReportIcon from "@mui/icons-material/Report"; import RouteIcon from "@mui/icons-material/Route"; import ScienceIcon from "@mui/icons-material/Science"; import SecurityIcon from "@mui/icons-material/Security"; import SpaceDashboardIcon from "@mui/icons-material/SpaceDashboard"; import SpeedIcon from "@mui/icons-material/Speed"; -import ContactPageIcon from "@mui/icons-material/ContactPage"; -import DirectionsBusIcon from "@mui/icons-material/DirectionsBus"; import SupervisedUserCircleIcon from "@mui/icons-material/SupervisedUserCircle"; -import RemoveRoadIcon from "@mui/icons-material/RemoveRoad"; -import { ReportProblem, RvHookup } from "@mui/icons-material"; export const pageMenu = [ { @@ -685,12 +685,12 @@ export const pageMenu = [ permissions: ["manage-damage"], }, { - id: "CarDetails", - label: "اطلاعات خودرو", + id: "machines", + label: "ماشین‌ها", type: "page", - route: "/dashboard/car-details", + route: "/dashboard/machines", icon: , - permissions: [""], + permissions: ["update-machine"], }, ], }, diff --git a/src/core/utils/pageMenuDev.js b/src/core/utils/pageMenuDev.js index e0f742d..08aaf62 100644 --- a/src/core/utils/pageMenuDev.js +++ b/src/core/utils/pageMenuDev.js @@ -682,13 +682,13 @@ export const pageMenuDev = [ icon: , permissions: ["manage-damage"], }, - { - id: "CarDetails", - label: "اطلاعات خودرو", + { + id: "machines", + label: "ماشین‌ها", type: "page", - route: "/dashboard/car-details", + route: "/dashboard/machines", icon: , - permissions: ["show-rahdaran"], + permissions: ["update-machine"], }, ], }, diff --git a/src/core/utils/routes.js b/src/core/utils/routes.js index a2ea39f..4de64fe 100644 --- a/src/core/utils/routes.js +++ b/src/core/utils/routes.js @@ -277,3 +277,8 @@ export const CONFIRM_VERIFY_LICENSE_FORM = api + "/api/v3/harim/technical_deputy // History export const GET_HISTORY_LIST = api + "/api/v3/harim/detail/histories"; export const GET_REQUEST_INQUIRY_PRIVACY = api + "/api/v3/harim/technical_deputy"; + + +// Machines +export const GET_MACHINES_LIST = api + "/api/v3/cmms_machines" +export const UPDATE_CMMS_MACHINES = api + "/api/v3/cmms_machines" \ No newline at end of file From 5f88728d674537b2c528d7ab0518a2fbfcc9e207 Mon Sep 17 00:00:00 2001 From: baslani Date: Sat, 13 Jun 2026 10:57:36 +0330 Subject: [PATCH 2/3] implemented machines cartable --- .../Form/CreateTollHouse/CityContent.jsx | 33 -- .../Form/CreateTollHouse/CityController.jsx | 14 - .../CreateTollHouse/EditMachineContent.jsx | 145 -------- .../Form/CreateTollHouse/ImageUpload.jsx | 77 ----- .../CreateTollHouse/LogesticController.jsx | 42 --- .../Form/CreateTollHouse/MachineInfo.jsx | 311 ------------------ .../CreateTollHouse/TollHouseLocation.jsx | 20 -- .../machines/Form/CreateTollHouse/index.jsx | 154 --------- .../machines/Form/Edit/EditController.jsx | 77 ----- .../dashboard/machines/MachinesList.jsx | 112 +------ .../machines/RowActions/Edit/Form/index.jsx | 281 ++++++++++++++++ .../{Form => RowActions}/Edit/index.jsx | 33 +- .../RowActions/ImageForm/ImageFormContent.jsx | 45 --- .../RowActions/ImageForm/ImagesContent.jsx | 39 --- .../machines/RowActions/ImageForm/index.jsx | 43 --- .../LocationForm/LocationFormContent.jsx | 35 -- .../RowActions/LocationForm/index.jsx | 38 --- .../dashboard/machines/RowActions/index.jsx | 8 +- src/components/dashboard/machines/Toolbar.jsx | 6 - src/core/utils/routes.js | 9 +- 20 files changed, 301 insertions(+), 1221 deletions(-) delete mode 100644 src/components/dashboard/machines/Form/CreateTollHouse/CityContent.jsx delete mode 100644 src/components/dashboard/machines/Form/CreateTollHouse/CityController.jsx delete mode 100644 src/components/dashboard/machines/Form/CreateTollHouse/EditMachineContent.jsx delete mode 100644 src/components/dashboard/machines/Form/CreateTollHouse/ImageUpload.jsx delete mode 100644 src/components/dashboard/machines/Form/CreateTollHouse/LogesticController.jsx delete mode 100644 src/components/dashboard/machines/Form/CreateTollHouse/MachineInfo.jsx delete mode 100644 src/components/dashboard/machines/Form/CreateTollHouse/TollHouseLocation.jsx delete mode 100644 src/components/dashboard/machines/Form/CreateTollHouse/index.jsx delete mode 100644 src/components/dashboard/machines/Form/Edit/EditController.jsx create mode 100644 src/components/dashboard/machines/RowActions/Edit/Form/index.jsx rename src/components/dashboard/machines/{Form => RowActions}/Edit/index.jsx (54%) delete mode 100644 src/components/dashboard/machines/RowActions/ImageForm/ImageFormContent.jsx delete mode 100644 src/components/dashboard/machines/RowActions/ImageForm/ImagesContent.jsx delete mode 100644 src/components/dashboard/machines/RowActions/ImageForm/index.jsx delete mode 100644 src/components/dashboard/machines/RowActions/LocationForm/LocationFormContent.jsx delete mode 100644 src/components/dashboard/machines/RowActions/LocationForm/index.jsx delete mode 100644 src/components/dashboard/machines/Toolbar.jsx diff --git a/src/components/dashboard/machines/Form/CreateTollHouse/CityContent.jsx b/src/components/dashboard/machines/Form/CreateTollHouse/CityContent.jsx deleted file mode 100644 index 3069208..0000000 --- a/src/components/dashboard/machines/Form/CreateTollHouse/CityContent.jsx +++ /dev/null @@ -1,33 +0,0 @@ -import SelectBox from "@/core/components/SelectBox"; -import { useEffect, useState } from "react"; -import useCities from "@/lib/hooks/useCities"; - -const CityContent = ({ provinceID, field, fieldState: { error } }) => { - const { cityList, loadingCityList, errorCityList } = useCities(provinceID); - const [prevDependency, setPrevDependency] = useState(provinceID); - - useEffect(() => { - if (prevDependency === provinceID) return; - field.onChange(null); - setPrevDependency(provinceID); - }, [provinceID]); - - return ( - - ); -}; -export default CityContent; diff --git a/src/components/dashboard/machines/Form/CreateTollHouse/CityController.jsx b/src/components/dashboard/machines/Form/CreateTollHouse/CityController.jsx deleted file mode 100644 index 72de542..0000000 --- a/src/components/dashboard/machines/Form/CreateTollHouse/CityController.jsx +++ /dev/null @@ -1,14 +0,0 @@ -import { Controller, useWatch } from "react-hook-form"; -import CityContent from "./CityContent"; - -const CityController = ({ control }) => { - const provinceID = useWatch({ control, name: "province_id" }); - return ( - } - /> - ); -}; -export default CityController; diff --git a/src/components/dashboard/machines/Form/CreateTollHouse/EditMachineContent.jsx b/src/components/dashboard/machines/Form/CreateTollHouse/EditMachineContent.jsx deleted file mode 100644 index 7128b06..0000000 --- a/src/components/dashboard/machines/Form/CreateTollHouse/EditMachineContent.jsx +++ /dev/null @@ -1,145 +0,0 @@ -import StyledForm from "@/core/components/StyledForm"; -import { yupResolver } from "@hookform/resolvers/yup"; -import BeenhereIcon from "@mui/icons-material/Beenhere"; -import ExitToAppIcon from "@mui/icons-material/ExitToApp"; -import KeyboardDoubleArrowLeftIcon from "@mui/icons-material/KeyboardDoubleArrowLeft"; -import KeyboardDoubleArrowRightIcon from "@mui/icons-material/KeyboardDoubleArrowRight"; -import OtherHousesIcon from "@mui/icons-material/OtherHouses"; -import { Box, Button, DialogActions, DialogContent, Tab, Tabs } from "@mui/material"; -import { useState } from "react"; -import { useForm } from "react-hook-form"; -import { number, object, string } from "yup"; -import MachineInfo from "./MachineInfo"; - -function TabPanel(props) { - const { children, value, index } = props; - return ( - - ); -} - -const validationSchema = object({ - name: string().required("وارد کردن نام راهدارخانه الزامیست!"), - status: string().required("وارد کردن وضعیت الزامیست!"), - type: string().required("وارد کردن نوع الزامیست!"), - phone: string().required("وارد کردن تلفن راهدارخانه الزامیست!"), - team_num: string().required("وارد کردن تعداد اکیپ الزامیست!"), - staff_num: string().required("وارد کردن تعداد پرسنل الزامیست!"), - responsible_name: string().required("وارد کردن نام مسئول الزامیست!"), - responsible_mobile: string() - .required("وارد کردن تلفن مسئول الزامیست!") - .matches(/^09\d{9}$/, "شماره تلفن مسئول باید با ۰۹ شروع شده و ۱۱ رقم باشد!"), - successor_name: string().required("وارد کردن نام جانشین الزامیست!"), - successor_mobile: string() - .required("وارد کردن تلفن جانشین الزامیست!") - .matches(/^09\d{9}$/, "شماره تلفن جانشین باید با ۰۹ شروع شده و ۱۱ رقم باشد!"), - province_id: number().required("وارد کردن استان الزامیست!"), - city_id: number().required("وارد کردن شهرستان الزامیست!"), - machines_light: string().required("وارد کردن ماشین آلات سبک الزامیست!"), - machines_sheavy: string().required("وارد کردن ماشین آلات نیمه سنگین الزامیست!"), - machines_heavy: string().required("وارد کردن ماشین آلات سنگین الزامیست!"), -}); -const EditMachineContent = ({ setOpen, SubmitCreateTollHouse, defaultValues }) => { - const [tabState, setTabState] = useState(0); - const handleClose = () => { - setOpen(false); - }; - - const handleChangeTab = (event, newValue) => { - setTabState(newValue); - }; - const handleNext = async () => { - const fieldsToValidate = [ - "status", - "province_id", - "city_id", - "station_id", - ]; - const isValid = await trigger(fieldsToValidate); - if (isValid) { - setTabState(tabState + 1); - } - }; - const handlePrev = () => { - if (tabState === 0) { - handleClose(); - } else { - setTabState(tabState - 1); - } - }; - const { - control, - handleSubmit, - trigger, - formState: { isSubmitting }, - } = useForm({ - defaultValues, - resolver: yupResolver(validationSchema), - mode: "all", - }); - const onSubmitBase = async (data) => { - await SubmitCreateTollHouse(data); - }; - return ( - - - } label="اطلاعات راهدارخانه" /> - - - - - - - - - - - {tabState !== 3 && ( - - )} - {tabState === 3 && ( - - )} - - - ); -}; -export default EditMachineContent; diff --git a/src/components/dashboard/machines/Form/CreateTollHouse/ImageUpload.jsx b/src/components/dashboard/machines/Form/CreateTollHouse/ImageUpload.jsx deleted file mode 100644 index 033a6f1..0000000 --- a/src/components/dashboard/machines/Form/CreateTollHouse/ImageUpload.jsx +++ /dev/null @@ -1,77 +0,0 @@ -import { FormControl, FormHelperText } from "@mui/material"; -import UploadSystem from "@/core/components/UploadSystem"; -import React, { useEffect, useState } from "react"; - -const ImageUpload = ({ name, value, onChange, error, title }) => { - const [beforeImg, setBeforeImg] = useState(value ? value : null); - const [beforeFileType, setBeforeFileType] = useState(value ? "image/" : null); - const [beforeFileName, setBeforeFileName] = useState(null); - const [showBeforeImage, setShowBeforeImage] = useState(!value); - - useEffect(() => { - if (value) { - setShowBeforeImage(false); - if (typeof value === "string") { - setBeforeImg(value); - setBeforeFileType("image/"); - } else if (value instanceof File) { - setBeforeImg(URL.createObjectURL(value)); - setBeforeFileType(value.type); - } - } - }, [value]); - - const handleFileChange = (event) => { - const uploadedFile = event.target?.files?.[0]; - if (uploadedFile) { - const fileType = event.target?.files?.[0].type; - const fileName = event.target?.files?.[0].name; - setBeforeImg(URL.createObjectURL(uploadedFile)); - setBeforeFileType(fileType); - setBeforeFileName(fileName); - onChange(uploadedFile); - setShowBeforeImage(false); - } - }; - - return ( - - - {title} - - - {error ? error.message : null} - - ); -}; -export default ImageUpload; diff --git a/src/components/dashboard/machines/Form/CreateTollHouse/LogesticController.jsx b/src/components/dashboard/machines/Form/CreateTollHouse/LogesticController.jsx deleted file mode 100644 index 513abbf..0000000 --- a/src/components/dashboard/machines/Form/CreateTollHouse/LogesticController.jsx +++ /dev/null @@ -1,42 +0,0 @@ -import { Controller, useWatch } from "react-hook-form"; -import SelectBox from "@/core/components/SelectBox"; -import useProvinces from "@/lib/hooks/useProvince"; -import { Grid } from "@mui/material"; -import CityController from "./CityController"; - -const LogesticController = ({ control }) => { - const { provinces, errorProvinces, loadingProvinces } = useProvinces(); - const isProvince = useWatch({ control, name: "is_province" }); - return ( - - {!isProvince ? ( - - { - return ( - - ); - }} - /> - - ) : null} - - - - - ); -}; -export default LogesticController; diff --git a/src/components/dashboard/machines/Form/CreateTollHouse/MachineInfo.jsx b/src/components/dashboard/machines/Form/CreateTollHouse/MachineInfo.jsx deleted file mode 100644 index 6fca8a4..0000000 --- a/src/components/dashboard/machines/Form/CreateTollHouse/MachineInfo.jsx +++ /dev/null @@ -1,311 +0,0 @@ -import { Grid, Stack, TextField } from "@mui/material"; -import { Controller } from "react-hook-form"; -import SelectBox from "@/core/components/SelectBox"; -import useProvince from "@/lib/hooks/useProvince"; -import PersianTextField from "@/core/components/PersianTextField"; -import CityController from "./CityController"; -import LogesticController from "./LogesticController"; - -const MachineInfo = ({ control }) => { - const { provinces, loadingProvinces, errorProvinces } = useProvince(); - return ( - - - - - ( - - )} - /> - - - ( - { - const inputValue = event.target.value; - if (isNaN(Number(inputValue))) { - return; - } - onChange(inputValue); - }} - /> - )} - /> - - - - - - - - - - { - return ( - - ); - }} - /> - - - { - return ( - - ); - }} - /> - - - - - - - ( - { - const inputValue = event.target.value; - if (isNaN(Number(inputValue))) { - return; - } - onChange(inputValue); - }} - /> - )} - /> - - - ( - { - const inputValue = event.target.value; - if (isNaN(Number(inputValue))) { - return; - } - onChange(inputValue); - }} - /> - )} - /> - - - - - - - ( - - )} - /> - - - ( - { - const inputValue = event.target.value; - if (isNaN(Number(inputValue))) { - return; - } - onChange(inputValue); - }} - /> - )} - /> - - - - - - - ( - - )} - /> - - - ( - { - const inputValue = event.target.value; - if (isNaN(Number(inputValue))) { - return; - } - onChange(inputValue); - }} - /> - )} - /> - - - - - ); -}; -export default MachineInfo; diff --git a/src/components/dashboard/machines/Form/CreateTollHouse/TollHouseLocation.jsx b/src/components/dashboard/machines/Form/CreateTollHouse/TollHouseLocation.jsx deleted file mode 100644 index 98d36a7..0000000 --- a/src/components/dashboard/machines/Form/CreateTollHouse/TollHouseLocation.jsx +++ /dev/null @@ -1,20 +0,0 @@ -import MapInfoOneMarker from "@/core/components/MapInfoOneMarker"; -import { Stack } from "@mui/material"; -import { useWatch } from "react-hook-form"; - -const TollHouseLocation = ({ setValue, errors, control }) => { - const StartPoint = useWatch({ control, name: "start_point" }); - return ( - <> - - - - - ); -}; -export default TollHouseLocation; diff --git a/src/components/dashboard/machines/Form/CreateTollHouse/index.jsx b/src/components/dashboard/machines/Form/CreateTollHouse/index.jsx deleted file mode 100644 index 3649dcd..0000000 --- a/src/components/dashboard/machines/Form/CreateTollHouse/index.jsx +++ /dev/null @@ -1,154 +0,0 @@ -import { Button, Dialog, IconButton, useMediaQuery } from "@mui/material"; -import { useState } from "react"; -import CreateTollHouseContent from "./EditMachineContent"; -import { AddCircle } from "@mui/icons-material"; -import { useTheme } from "@emotion/react"; -import { CREATE_TOLL_HOUSE } from "@/core/utils/routes"; -import useRequest from "@/lib/hooks/useRequest"; -import CloseIcon from "@mui/icons-material/Close"; -import { useAuth } from "@/lib/contexts/auth"; -import { usePermissions } from "@/lib/hooks/usePermissions"; - -const CreateTollHouse = ({ mutate }) => { - const requestServer = useRequest({ notificationSuccess: true }); - const theme = useTheme(); - const isMobile = useMediaQuery(theme.breakpoints.down("sm")); - const [open, setOpen] = useState(false); - const { user } = useAuth(); - const { data: userPermissions } = usePermissions(); - const hasCreateProvincePermission = userPermissions.some((item) => ["add-tollhouse"].includes(item)); - const handleOpen = () => { - setOpen(true); - }; - const SubmitCreateTollHouse = async (result) => { - const fields = [ - "province_id", - "city_id", - "name", - "status", - "type", - "team_num", - "staff_num", - "phone", - "responsible_name", - "responsible_mobile", - "successor_name", - "successor_mobile", - "machines_light", - "machines_sheavy", - "machines_heavy", - "overview_files_1", - "overview_files_2", - "overview_files_3", - "overview_files_4", - "overview_files_5", - ]; - const formData = new FormData(); - fields.forEach((field) => { - if (result[field] !== undefined && result[field] !== null) { - formData.append(field, result[field]); - } - }); - formData.append("lat", result.start_point.lat); - formData.append("lng", result.start_point.lng); - formData.append("area[type]", "polygon"); - result.area.map((point, pointIndex) => { - formData.append(`area[coordinates][${pointIndex}][0]`, point.lon); - formData.append(`area[coordinates][${pointIndex}][1]`, point.lat); - }); - const repeat_first = result.area.length; - formData.append(`area[coordinates][${repeat_first}][0]`, result.area[0].lon); - formData.append(`area[coordinates][${repeat_first}][1]`, result.area[0].lat); - await requestServer(`${CREATE_TOLL_HOUSE}`, "post", { - data: formData, - }) - .then(() => { - mutate(); - setOpen(false); - }) - .catch(() => {}); - }; - const defaultValues = { - name: "", - is_province: !hasCreateProvincePermission, - province_id: !hasCreateProvincePermission ? user.province_id : null, - city_id: null, - status: "", - type: "", - phone: "", - team_num: "", - staff_num: "", - responsible_name: "", - responsible_mobile: "", - successor_name: "", - successor_mobile: "", - machines_light: "", - machines_sheavy: "", - machines_heavy: "", - overview_files_1: null, - overview_files_2: null, - overview_files_3: null, - overview_files_4: null, - overview_files_5: null, - start_point: "", - area: [ - { lat: "", lon: "" }, - { lat: "", lon: "" }, - { lat: "", lon: "" }, - { lat: "", lon: "" }, - ], - }; - return ( - <> - {isMobile ? ( - - - - ) : ( - - )} - setOpen(false)} - PaperProps={{ - sx: { - boxShadow: "rgba(60, 64, 67, 0.3) 0px 1px 16px 0px, rgba(60, 64, 67, 0.15) 0px 1px 10px 0px", - }, - }} - maxWidth={"md"} - scroll="paper" - > - setOpen(false)} - sx={(theme) => ({ - position: "absolute", - right: 8, - top: 8, - zIndex: 50, - color: theme.palette.grey[500], - })} - > - - - {open && ( - - )} - - - ); -}; -export default CreateTollHouse; diff --git a/src/components/dashboard/machines/Form/Edit/EditController.jsx b/src/components/dashboard/machines/Form/Edit/EditController.jsx deleted file mode 100644 index a5f9c7f..0000000 --- a/src/components/dashboard/machines/Form/Edit/EditController.jsx +++ /dev/null @@ -1,77 +0,0 @@ -import DialogLoading from "@/core/components/DialogLoading"; -import { GET_TOLL_HOUSE_DETAILS, UPDATE_TOLL_HOUSE_ITEM } from "@/core/utils/routes"; -import { usePermissions } from "@/lib/hooks/usePermissions"; -import useRequest from "@/lib/hooks/useRequest"; -import { useEffect, useState } from "react"; -import EditMachineContent from "../CreateTollHouse/EditMachineContent"; - -const EditController = ({ rowId, mutate, setOpenEditDialog }) => { - const { data: userPermissions } = usePermissions(); - const hasCreateProvincePermission = userPermissions.some((item) => ["update-machine"].includes(item)); - const requestServer = useRequest(); - const [MachineItemDetails, setMachineItemDetails] = useState(null); - const [machineItemDetailsLoading, setMachineItemDetailsLoading] = useState(false); - - useEffect(() => { - setMachineItemDetailsLoading(true); - requestServer(`${GET_TOLL_HOUSE_DETAILS}/${rowId}`, "get") - .then((response) => { - setMachineItemDetails(response.data.data); - setMachineItemDetailsLoading(false); - }) - .catch((e) => { - setMachineItemDetailsLoading(false); - }); - }, [rowId]); - - const defaultData = { - is_province: !hasCreateProvincePermission, - province_id: MachineItemDetails?.province_id || null, - city_id: MachineItemDetails?.city_id || null, - status: MachineItemDetails?.status || "", - station_id: MachineItemDetails?.station_id || "", - }; - const HandleSubmit = async (result) => { - const fields = [ - "status", - "province_id", - "city_id", - "station_id", - ]; - const formData = new FormData(); - fields.forEach((field) => { - if (result[field] !== undefined && result[field] !== null) { - formData.append(field, result[field]); - } - }); - for (let i = 1; i <= 5; i++) { - if (result[`overview_files_${i}`] !== defaultData[`overview_files_${i}`]) { - formData.append(`overview_files_${i}`, result[`overview_files_${i}`]); - } - } - - await requestServer(`${UPDATE_TOLL_HOUSE_ITEM}/${rowId}`, "post", { - data: formData, - notificationSuccess: true, - }) - .then(() => { - mutate(); - setOpenEditDialog(false); - }) - .catch(() => {}); - }; - return ( - <> - {machineItemDetailsLoading ? ( - - ) : ( - - )} - - ); -}; -export default EditController; diff --git a/src/components/dashboard/machines/MachinesList.jsx b/src/components/dashboard/machines/MachinesList.jsx index bcfb104..880f0cb 100644 --- a/src/components/dashboard/machines/MachinesList.jsx +++ b/src/components/dashboard/machines/MachinesList.jsx @@ -1,15 +1,9 @@ "use client"; -import CustomSelectByDependency from "@/core/components/DataTable/filter/fieldsType/CustomSelectByDependency"; import DataTableWithAuth from "@/core/components/DataTableWithAuth"; import { GET_MACHINES_LIST } from "@/core/utils/routes"; -import { useAuth } from "@/lib/contexts/auth"; -import useEdaratLists from "@/lib/hooks/useEdaratLists"; -import { usePermissions } from "@/lib/hooks/usePermissions"; -import useProvinces from "@/lib/hooks/useProvince"; import { Box, Typography } from "@mui/material"; -import { useEffect, useMemo, useState } from "react"; +import { useMemo } from "react"; import RowActions from "./RowActions"; -import Toolbar from "./Toolbar"; const statusOptions = [ { value: "", label: "همه وضعیت ها" }, @@ -23,47 +17,7 @@ const statusOptions = [ ]; const MachinesList = () => { - const { data: userPermissions } = usePermissions(); - const hasCountryPermission = userPermissions?.includes("show-tollhouse"); - const { user } = useAuth(); - const columns = useMemo(() => { - const provinceColumn = { - accessorKey: "province_id", - header: "استان", - id: "province_id", - enableColumnFilter: true, - datatype: "numeric", - filterMode: "equals", - grow: false, - size: 130, - ColumnSelectComponent: (props) => { - const { provinces, errorProvinces, loadingProvinces } = useProvinces(); - const getColumnSelectOptions = useMemo(() => { - if (loadingProvinces) { - return [{ value: "loading", label: "در حال بارگذاری..." }]; - } - if (errorProvinces) { - return [{ value: "error", label: "خطا در بارگذاری" }]; - } - return [ - { value: "", label: "کل کشور" }, - ...provinces.map((province) => ({ - value: province.id, - label: province.name_fa, - })), - ]; - }, [provinces, errorProvinces, loadingProvinces]); - return ( - - ); - }, - Cell: ({ row }) => <>{row.original.province_name}, - }; return [ { accessorKey: "id", @@ -75,7 +29,7 @@ const MachinesList = () => { sortDescFirst: false, columnFilterModeOptions: ["equals", "contains"], grow: false, - size: 100, + size: 50, }, { @@ -85,7 +39,6 @@ const MachinesList = () => { enableSorting: false, datatype: "text", grow: false, - size: 50, columns: [ { accessorKey: "car_name", @@ -135,65 +88,6 @@ const MachinesList = () => { grow: false, size: 100, }, - ...(hasCountryPermission ? [provinceColumn] : []), - { - accessorKey: "city_id", - header: "شهرستان", // Office - id: "city_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: ({ row }) => <>{row.original.city_name}, - }, { accessorKey: "status", header: "وضعیت", @@ -214,7 +108,6 @@ const MachinesList = () => { return {status?.label || ""}; }, }, - ], }, ]; @@ -230,7 +123,6 @@ const MachinesList = () => { page_name={"machines"} table_name={"machinesList"} sorting={[{ id: "id", desc: true }]} - TableToolbar={Toolbar} enableRowActions positionActionsColumn={"first"} RowActions={RowActions} diff --git a/src/components/dashboard/machines/RowActions/Edit/Form/index.jsx b/src/components/dashboard/machines/RowActions/Edit/Form/index.jsx new file mode 100644 index 0000000..6cfcd9d --- /dev/null +++ b/src/components/dashboard/machines/RowActions/Edit/Form/index.jsx @@ -0,0 +1,281 @@ +import StyledForm from "@/core/components/StyledForm"; +import { + GET_CITIES_LIST, + GET_PROVINCES_LIST, + GET_STATIONS_LIST, + UPDATE_MACHINE_STATUS_AND_LOCATION, +} from "@/core/utils/routes"; +import useRequest from "@/lib/hooks/useRequest"; +import { yupResolver } from "@hookform/resolvers/yup"; +import { Beenhere, ExitToApp } from "@mui/icons-material"; +import { + Button, + CircularProgress, + DialogActions, + DialogContent, + DialogTitle, + FormControl, + InputLabel, + MenuItem, + Select, + Stack, +} from "@mui/material"; +import { DialogHeader } from "next/dist/client/components/react-dev-overlay/internal/components/Dialog"; +import { useEffect, useState } from "react"; +import { Controller, useForm, useWatch } from "react-hook-form"; +import { number, object } from "yup"; + +const statusOptions = [ + { value: 1, label: "آماده" }, + { value: 2, label: "رزرو" }, + { value: 3, label: "در ماموریت" }, + { value: 4, label: "خارج از رده" }, + { value: 5, label: "در تعمیر" }, + { value: 6, label: "فروخته شده" }, + { value: 7, label: "نیاز به تعمیر" }, +]; + +const validationSchema = object({ + status: number().required("انتخاب وضعیت الزامی است"), + province_id: number().required("انتخاب استان الزامی است"), + city_id: number().required("انتخاب شهر الزامی است"), + station_id: number().required("انتخاب راهداری الزامی است"), +}); + +const EditFormContent = ({ row, rowId, mutate, setOpen }) => { + const requestServer = useRequest(); + + const [provinces, setProvinces] = useState([]); + const [cities, setCities] = useState([]); + const [stations, setStations] = useState([]); + + const [loadingProvinces, setLoadingProvinces] = useState(false); + const [loadingCities, setLoadingCities] = useState(false); + const [loadingStations, setLoadingStations] = useState(false); + + const { + control, + handleSubmit, + setValue, + formState: { isSubmitting }, + } = useForm({ + defaultValues: { + status: row.original.status, + province_id: row.original.province_id, + city_id: row.original.city_id, + station_id: row.original.station_id, + }, + resolver: yupResolver(validationSchema), + mode: "all", + }); + + const provinceId = useWatch({ + control, + name: "province_id", + }); + + const cityId = useWatch({ + control, + name: "city_id", + }); + + const loadProvinces = async () => { + try { + setLoadingProvinces(true); + const response = await requestServer(GET_PROVINCES_LIST, "get"); + setProvinces(response.data?.data || []); + } finally { + setLoadingProvinces(false); + } + }; + + const loadCities = async (provinceId) => { + if (!provinceId) return; + + try { + setLoadingCities(true); + const response = await requestServer(`${GET_CITIES_LIST}?province_id=${provinceId}`, "get"); + setCities(response.data?.data || []); + } finally { + setLoadingCities(false); + } + }; + + const loadStations = async (cityId) => { + if (!cityId) return; + + try { + setLoadingStations(true); + const response = await requestServer(`${GET_STATIONS_LIST}/search_by_city?city_id=${cityId}`, "get"); + setStations(response.data?.data || []); + } finally { + setLoadingStations(false); + } + }; + + useEffect(() => { + loadProvinces(); + }, []); + + /** + * Initial edit mode loading + */ + useEffect(() => { + if (row.original.province_id) { + loadCities(row.original.province_id); + } + + if (row.original.city_id) { + loadStations(row.original.city_id); + } + }, []); + + useEffect(() => { + if (!provinceId) return; + + loadCities(provinceId); + + if (provinceId !== row.original.province_id) { + setValue("city_id", ""); + setValue("station_id", ""); + setStations([]); + } + }, [provinceId]); + + useEffect(() => { + if (!cityId) return; + + loadStations(cityId); + + if (cityId !== row.original.city_id) { + setValue("station_id", ""); + } + }, [cityId]); + + const onSubmit = async (data) => { + const formData = new FormData(); + + formData.append("status", String(data.status)); + formData.append("province_id", String(data.province_id)); + formData.append("city_id", String(data.city_id)); + formData.append("station_id", String(data.station_id)); + + try { + await requestServer(`${UPDATE_MACHINE_STATUS_AND_LOCATION}/${rowId}`, "post", { + data: formData, + }); + + mutate(); + setOpen(false); + } catch (error) {} + }; + + return ( + <> + + ویرایش ماشین + + + + + + ( + + استان + + + + )} + /> + + ( + + شهر + + + + )} + /> + + ( + + راهداری + + + + )} + /> + ( + + وضعیت + + + + )} + /> + + {(loadingCities || loadingStations) && ( + + + + )} + + + + + + + + + + + ); +}; + +export default EditFormContent; diff --git a/src/components/dashboard/machines/Form/Edit/index.jsx b/src/components/dashboard/machines/RowActions/Edit/index.jsx similarity index 54% rename from src/components/dashboard/machines/Form/Edit/index.jsx rename to src/components/dashboard/machines/RowActions/Edit/index.jsx index 53be78c..f3cec95 100644 --- a/src/components/dashboard/machines/Form/Edit/index.jsx +++ b/src/components/dashboard/machines/RowActions/Edit/index.jsx @@ -1,15 +1,15 @@ +import { SwapHoriz } from "@mui/icons-material"; +import CloseIcon from "@mui/icons-material/Close"; import { Dialog, IconButton, Tooltip } from "@mui/material"; import { useState } from "react"; -import BorderColorIcon from "@mui/icons-material/BorderColor"; -import EditController from "./EditController"; -import CloseIcon from "@mui/icons-material/Close"; +import EditFormContent from "./Form"; -const EditForm = ({ row, mutate, rowId }) => { +const Edit = ({ row, mutate, rowId }) => { const [openEditDialog, setOpenEditDialog] = useState(false); return ( <> - + { setOpenEditDialog(true); }} > - + - + setOpenEditDialog(false)} @@ -44,16 +35,10 @@ const EditForm = ({ row, mutate, rowId }) => { {openEditDialog && ( - + )} ); }; -export default EditForm; +export default Edit; diff --git a/src/components/dashboard/machines/RowActions/ImageForm/ImageFormContent.jsx b/src/components/dashboard/machines/RowActions/ImageForm/ImageFormContent.jsx deleted file mode 100644 index c8f22d0..0000000 --- a/src/components/dashboard/machines/RowActions/ImageForm/ImageFormContent.jsx +++ /dev/null @@ -1,45 +0,0 @@ -import { Box, Stack, Typography, useTheme } from "@mui/material"; -import Image from "next/image"; - -const ImageFormContent = ({ image, title }) => { - const theme = useTheme(); - return ( - - - {title} - - - Image - - - ); -}; -export default ImageFormContent; diff --git a/src/components/dashboard/machines/RowActions/ImageForm/ImagesContent.jsx b/src/components/dashboard/machines/RowActions/ImageForm/ImagesContent.jsx deleted file mode 100644 index 6e22c9a..0000000 --- a/src/components/dashboard/machines/RowActions/ImageForm/ImagesContent.jsx +++ /dev/null @@ -1,39 +0,0 @@ -import { useEffect, useState } from "react"; -import ImageFormContent from "./ImageFormContent"; -import DialogLoading from "@/core/components/DialogLoading"; -import useRequest from "@/lib/hooks/useRequest"; -import { GET_TOLL_HOUSE_IMAGES } from "@/core/utils/routes"; -import { Typography } from "@mui/material"; -const titles = ["نمای کلی از راهداری", "آشیانه ماشین آلات", "انباری شن و ماسه", "محیط کاری", "سایر تصاویر"]; - -const ImagesContent = ({ rowId }) => { - const requestServer = useRequest(); - const [tollHouseImages, setTollHouseImages] = useState(null); - const [tollHouseImagesLoading, setTollHouseImagesLoading] = useState(false); - useEffect(() => { - setTollHouseImagesLoading(true); - requestServer(`${GET_TOLL_HOUSE_IMAGES}/${rowId}`, "get") - .then((response) => { - setTollHouseImages(response.data.data); - setTollHouseImagesLoading(false); - }) - .catch((e) => { - setTollHouseImagesLoading(false); - }); - }, [rowId]); - - return ( - <> - {tollHouseImagesLoading ? ( - - ) : tollHouseImages ? ( - tollHouseImages.map((image, index) => { - return ; - }) - ) : ( - تصویری در سامانه یافت نشد - )} - - ); -}; -export default ImagesContent; diff --git a/src/components/dashboard/machines/RowActions/ImageForm/index.jsx b/src/components/dashboard/machines/RowActions/ImageForm/index.jsx deleted file mode 100644 index 6ac8d25..0000000 --- a/src/components/dashboard/machines/RowActions/ImageForm/index.jsx +++ /dev/null @@ -1,43 +0,0 @@ -import PhotoLibraryIcon from "@mui/icons-material/PhotoLibrary"; -import { Button, Dialog, DialogActions, DialogContent, DialogTitle, IconButton, Tooltip } from "@mui/material"; -import { useEffect, useState } from "react"; -import ImagesContent from "./ImagesContent"; -import useRequest from "@/lib/hooks/useRequest"; -import { GET_TOLL_HOUSE_IMAGES } from "@/core/utils/routes"; -import DialogLoading from "@/core/components/DialogLoading"; - -const ImageDialog = ({ rowId }) => { - const [openImageDialog, setOpenImageDialog] = useState(false); - - return ( - <> - - setOpenImageDialog(true)}> - - - - setOpenImageDialog(false)} - PaperProps={{ - sx: { - boxShadow: "rgba(60, 64, 67, 0.3) 0px 1px 16px 0px, rgba(60, 64, 67, 0.15) 0px 1px 10px 0px", - }, - }} - maxWidth={"sm"} - scroll="paper" - > - تصاویر - {openImageDialog && } - - - - - - ); -}; - -export default ImageDialog; diff --git a/src/components/dashboard/machines/RowActions/LocationForm/LocationFormContent.jsx b/src/components/dashboard/machines/RowActions/LocationForm/LocationFormContent.jsx deleted file mode 100644 index 14b3db3..0000000 --- a/src/components/dashboard/machines/RowActions/LocationForm/LocationFormContent.jsx +++ /dev/null @@ -1,35 +0,0 @@ -"use client"; -import { Box, Button, DialogActions, DialogContent, Stack } from "@mui/material"; -import React from "react"; -import dynamic from "next/dynamic"; -import MapLoading from "@/core/components/MapLayer/Loading"; -import ShowLocationMarker from "@/core/components/ShowLocationMarker"; -const MapLayer = dynamic(() => import("@/core/components/MapLayer"), { - loading: () => , - ssr: false, -}); - -const LocationFormContent = ({ setOpenLocationDialog, start_lat, start_lng, end_lat, end_lng }) => { - return ( - <> - - - - - - - - - - - - ); -}; -export default LocationFormContent; diff --git a/src/components/dashboard/machines/RowActions/LocationForm/index.jsx b/src/components/dashboard/machines/RowActions/LocationForm/index.jsx deleted file mode 100644 index f578ca8..0000000 --- a/src/components/dashboard/machines/RowActions/LocationForm/index.jsx +++ /dev/null @@ -1,38 +0,0 @@ -import React, { useState } from "react"; -import { Tooltip, IconButton, Dialog, DialogTitle } from "@mui/material"; -import ExploreIcon from "@mui/icons-material/Explore"; -import LocationFormContent from "./LocationFormContent"; -const LocationForm = ({ start_lat, start_lng, end_lat, end_lng }) => { - const [openLocationDialog, setOpenLocationDialog] = useState(false); - - return ( - <> - - setOpenLocationDialog(true)}> - - - - setOpenLocationDialog(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"} - > - موقعیت - - - - ); -}; -export default LocationForm; diff --git a/src/components/dashboard/machines/RowActions/index.jsx b/src/components/dashboard/machines/RowActions/index.jsx index 8feb437..526fe10 100644 --- a/src/components/dashboard/machines/RowActions/index.jsx +++ b/src/components/dashboard/machines/RowActions/index.jsx @@ -1,15 +1,13 @@ import { usePermissions } from "@/lib/hooks/usePermissions"; import { Box } from "@mui/material"; -import EditForm from "../Form/Edit"; +import Edit from "./Edit"; const RowActions = ({ row, mutate }) => { const { data: userPermissions } = usePermissions(); - const hasEditPermission = userPermissions.some((item) => - ["update-machine"].includes(item) - ); + const hasEditPermission = userPermissions.some((item) => ["update-machine"].includes(item)); return ( - {hasEditPermission && } + {hasEditPermission && } ); }; diff --git a/src/components/dashboard/machines/Toolbar.jsx b/src/components/dashboard/machines/Toolbar.jsx deleted file mode 100644 index ae86ec0..0000000 --- a/src/components/dashboard/machines/Toolbar.jsx +++ /dev/null @@ -1,6 +0,0 @@ -import { Box } from "@mui/material"; - -const Toolbar = ({ table, filterData, mutate }) => { - return machines toolbar; -}; -export default Toolbar; diff --git a/src/core/utils/routes.js b/src/core/utils/routes.js index 4de64fe..c970699 100644 --- a/src/core/utils/routes.js +++ b/src/core/utils/routes.js @@ -278,7 +278,10 @@ export const CONFIRM_VERIFY_LICENSE_FORM = api + "/api/v3/harim/technical_deputy export const GET_HISTORY_LIST = api + "/api/v3/harim/detail/histories"; export const GET_REQUEST_INQUIRY_PRIVACY = api + "/api/v3/harim/technical_deputy"; - // Machines -export const GET_MACHINES_LIST = api + "/api/v3/cmms_machines" -export const UPDATE_CMMS_MACHINES = api + "/api/v3/cmms_machines" \ No newline at end of file +export const GET_MACHINES_LIST = api + "/api/v3/cmms_machines"; +export const UPDATE_CMMS_MACHINES = api + "/api/v3/cmms_machines"; +export const GET_PROVINCES_LIST = api + "/api/v3/provinces/list"; +export const GET_CITIES_LIST = api + "/api/v3/cities/list"; +export const GET_STATIONS_LIST = api + "/api/v3/road_maintenance_station"; +export const UPDATE_MACHINE_STATUS_AND_LOCATION = api + "/api/v3/cmms_machines"; From 01bb171ebbe6e54070c1ea0b88944eaf60ef2c80 Mon Sep 17 00:00:00 2001 From: baslani Date: Sat, 13 Jun 2026 11:01:50 +0330 Subject: [PATCH 3/3] build & format --- jsconfig.json | 38 +++++++++---------- .../dashboard/machines/page.jsx | 6 +-- src/core/utils/pageMenuDev.js | 2 +- 3 files changed, 22 insertions(+), 24 deletions(-) diff --git a/jsconfig.json b/jsconfig.json index c136578..b51472a 100644 --- a/jsconfig.json +++ b/jsconfig.json @@ -1,27 +1,27 @@ // javascript for production -// { -// "compilerOptions": { -// "paths": { -// "@/*": ["./src/*"], -// "^/*": ["./public/*"] -// } -// } -// } - -// typescript for local development { "compilerOptions": { - "jsx": "react-jsx", - "noUnusedLocals": true, - "noImplicitAny": false, - "checkJs": true, - "allowJs": true, - "strict": true, - "baseUrl": ".", "paths": { "@/*": ["./src/*"], "^/*": ["./public/*"] } - }, - "exclude": ["node_modules"] + } } + +// typescript for local development +// { +// "compilerOptions": { +// "jsx": "react-jsx", +// "noUnusedLocals": true, +// "noImplicitAny": false, +// "checkJs": true, +// "allowJs": true, +// "strict": true, +// "baseUrl": ".", +// "paths": { +// "@/*": ["./src/*"], +// "^/*": ["./public/*"] +// } +// }, +// "exclude": ["node_modules"] +// } diff --git a/src/app/(withAuth)/(dashboardLayout)/dashboard/machines/page.jsx b/src/app/(withAuth)/(dashboardLayout)/dashboard/machines/page.jsx index 53e7040..35c27ad 100644 --- a/src/app/(withAuth)/(dashboardLayout)/dashboard/machines/page.jsx +++ b/src/app/(withAuth)/(dashboardLayout)/dashboard/machines/page.jsx @@ -1,5 +1,5 @@ import WithPermission from "@/core/middlewares/withPermission"; -import MachinesPage from "@/components/dashboard/machines" +import MachinesPage from "@/components/dashboard/machines"; export const metadata = { title: "کارتابل ماشین‌ها", @@ -7,9 +7,7 @@ export const metadata = { const Page = () => { return ( <> - + diff --git a/src/core/utils/pageMenuDev.js b/src/core/utils/pageMenuDev.js index 08aaf62..4bc0665 100644 --- a/src/core/utils/pageMenuDev.js +++ b/src/core/utils/pageMenuDev.js @@ -682,7 +682,7 @@ export const pageMenuDev = [ icon: , permissions: ["manage-damage"], }, - { + { id: "machines", label: "ماشین‌ها", type: "page",