}
+ />
+ );
+};
+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 (
+
+ {value === index && {children}}
+
+ );
+}
+
+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 === 0 ? "بستن" : "مرحله قبل"}
+
+ {tabState !== 3 && (
+ }
+ >
+ مرحله بعد
+
+ )}
+ {tabState === 3 && (
+ }
+ >
+ {isSubmitting ? "در حال ثبت راهدارخانه" : "ثبت راهدارخانه"}
+
+ )}
+
+
+ );
+};
+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 ? (
+
+
+
+ ) : (
+ }
+ onClick={handleOpen}
+ >
+ راهدارخانه جدید
+
+ )}
+
+ >
+ );
+};
+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);
+ }}
+ >
+
+
+
+
+ >
+ );
+};
+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)}>
+
+
+
+
+ >
+ );
+};
+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}
+
+
+
+
+
+ );
+};
+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)}>
+
+
+
+
+ >
+ );
+};
+
+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)}>
+
+
+
+
+ >
+ );
+};
+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)}>
+
+
+
+
+
+ );
+};
+
+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