}
+ />
+ );
+};
+export default CityController;
diff --git a/src/components/infrastructure/tollHouse/Form/CreateTollHouse/CreateTollHouseContent.jsx b/src/components/infrastructure/tollHouse/Form/CreateTollHouse/CreateTollHouseContent.jsx
new file mode 100644
index 0000000..f56c8ca
--- /dev/null
+++ b/src/components/infrastructure/tollHouse/Form/CreateTollHouse/CreateTollHouseContent.jsx
@@ -0,0 +1,192 @@
+import StyledForm from "@/core/components/StyledForm";
+import { Box, Button, DialogActions, DialogContent, Tab, Tabs } from "@mui/material";
+import ExitToAppIcon from "@mui/icons-material/ExitToApp";
+import KeyboardDoubleArrowRightIcon from "@mui/icons-material/KeyboardDoubleArrowRight";
+import BeenhereIcon from "@mui/icons-material/Beenhere";
+import OtherHousesIcon from "@mui/icons-material/OtherHouses";
+import LocalShippingIcon from "@mui/icons-material/LocalShipping";
+import EditLocationAltIcon from "@mui/icons-material/EditLocationAlt";
+import React, { useState } from "react";
+import { useForm } from "react-hook-form";
+import { yupResolver } from "@hookform/resolvers/yup";
+import { mixed, number, object, string } from "yup";
+import TollHouseInfo from "./TollHouseInfo";
+import KeyboardDoubleArrowLeftIcon from "@mui/icons-material/KeyboardDoubleArrowLeft";
+import MachineInfo from "@/components/infrastructure/tollHouse/Form/CreateTollHouse/MachineInfo";
+import TollHouseLocation from "@/components/infrastructure/tollHouse/Form/CreateTollHouse/TollHouseLocation";
+
+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("وارد کردن ماشین آلات سنگین الزامیست!"),
+ overview_files_1: mixed().nullable().required("لطفا نمای کلی از راهداری را بارگذاری کنید!"),
+ overview_files_2: mixed().nullable().required("لطفا آشیانه ماشین آلات را بارگذاری کنید!"),
+ overview_files_3: mixed().nullable().required("لطفا انباری شن و ماسه را بارگذاری کنید!"),
+ overview_files_4: mixed().nullable().required("لطفا محیط کاری را بارگذاری کنید!"),
+ overview_files_5: mixed().nullable().required("لطفا سایر تصاویر را بارگذاری کنید!"),
+ start_point: mixed()
+ .test("start-point-required", "لطفاً محل پروژه را مشخص کنید!", function (value) {
+ return !!value;
+ })
+ .required("لطفاً محل پروژه را مشخص کنید!"),
+});
+const CreateTollHouseContent = ({ setOpen, SubmitCreateTollHouse, defaultValues }) => {
+ const [tabState, setTabState] = useState(0);
+ const handleClose = () => {
+ setOpen(false);
+ };
+
+ const handleChangeTab = (event, newValue) => {
+ setTabState(newValue);
+ };
+ const handleNext = async () => {
+ let fieldsToValidate = [];
+ if (tabState === 0) {
+ fieldsToValidate = [
+ "name",
+ "province_id",
+ "city_id",
+ "status",
+ "type",
+ "phone",
+ "team_num",
+ "staff_num",
+ "responsible_name",
+ "responsible_mobile",
+ "successor_name",
+ "successor_mobile",
+ ];
+ } else if (tabState === 1) {
+ fieldsToValidate = [
+ "machines_light",
+ "machines_sheavy",
+ "machines_heavy",
+ "overview_files_1",
+ "overview_files_2",
+ "overview_files_3",
+ "overview_files_4",
+ "overview_files_5",
+ ];
+ } else if (tabState === 2) {
+ fieldsToValidate = ["start_point"];
+ }
+ const isValid = await trigger(fieldsToValidate);
+ if (isValid) {
+ setTabState(tabState + 1);
+ }
+ };
+ const handlePrev = () => {
+ if (tabState === 0) {
+ handleClose();
+ } else {
+ setTabState(tabState - 1);
+ }
+ };
+ const {
+ control,
+ handleSubmit,
+ trigger,
+ setValue,
+ formState: { isSubmitting, errors },
+ } = useForm({
+ defaultValues,
+ resolver: yupResolver(validationSchema),
+ mode: "onBlur",
+ });
+ const onSubmitBase = async (data) => {
+ await SubmitCreateTollHouse(data);
+ };
+ return (
+
+
+ } label="اطلاعات راهدارخانه" />
+ } label="ماشین آلات" />
+ } label="موقعیت" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ : }
+ >
+ {tabState === 0 ? "بستن" : "مرحله قبل"}
+
+ {tabState !== 2 && (
+ }
+ >
+ مرحله بعد
+
+ )}
+ {tabState === 2 && (
+ }
+ >
+ {isSubmitting ? "در حال ثبت راهدارخانه" : "ثبت راهدارخانه"}
+
+ )}
+
+
+ );
+};
+export default CreateTollHouseContent;
diff --git a/src/components/infrastructure/tollHouse/Form/CreateTollHouse/ImageUpload.jsx b/src/components/infrastructure/tollHouse/Form/CreateTollHouse/ImageUpload.jsx
new file mode 100644
index 0000000..5186a4b
--- /dev/null
+++ b/src/components/infrastructure/tollHouse/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/infrastructure/tollHouse/Form/CreateTollHouse/LogesticController.jsx b/src/components/infrastructure/tollHouse/Form/CreateTollHouse/LogesticController.jsx
new file mode 100644
index 0000000..513abbf
--- /dev/null
+++ b/src/components/infrastructure/tollHouse/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/infrastructure/tollHouse/Form/CreateTollHouse/MachineInfo.jsx b/src/components/infrastructure/tollHouse/Form/CreateTollHouse/MachineInfo.jsx
new file mode 100644
index 0000000..44d3fea
--- /dev/null
+++ b/src/components/infrastructure/tollHouse/Form/CreateTollHouse/MachineInfo.jsx
@@ -0,0 +1,199 @@
+import { Grid, Stack, TextField } from "@mui/material";
+import { Controller } from "react-hook-form";
+import PersianTextField from "@/core/components/PersianTextField";
+import ImageUpload from "@/components/dashboard/damages/operator/Actions/create/Forms/ImageUpload";
+
+const MachineInfo = ({ control }) => {
+ 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);
+ }}
+ />
+ )}
+ />
+
+
+
+
+
+
+ (
+
+ )}
+ />
+
+
+ (
+
+ )}
+ />
+
+
+
+
+
+
+ (
+
+ )}
+ />
+
+
+ (
+
+ )}
+ />
+
+
+
+
+
+
+ (
+
+ )}
+ />
+
+
+
+
+ );
+};
+export default MachineInfo;
diff --git a/src/components/infrastructure/tollHouse/Form/CreateTollHouse/TollHouseInfo.jsx b/src/components/infrastructure/tollHouse/Form/CreateTollHouse/TollHouseInfo.jsx
new file mode 100644
index 0000000..d06a823
--- /dev/null
+++ b/src/components/infrastructure/tollHouse/Form/CreateTollHouse/TollHouseInfo.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 TollHouseInfo = ({ 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 TollHouseInfo;
diff --git a/src/components/infrastructure/tollHouse/Form/CreateTollHouse/TollHouseLocation.jsx b/src/components/infrastructure/tollHouse/Form/CreateTollHouse/TollHouseLocation.jsx
new file mode 100644
index 0000000..98d36a7
--- /dev/null
+++ b/src/components/infrastructure/tollHouse/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/infrastructure/tollHouse/Form/CreateTollHouse/index.jsx b/src/components/infrastructure/tollHouse/Form/CreateTollHouse/index.jsx
new file mode 100644
index 0000000..621f4e6
--- /dev/null
+++ b/src/components/infrastructure/tollHouse/Form/CreateTollHouse/index.jsx
@@ -0,0 +1,140 @@
+import { Button, Dialog, IconButton, useMediaQuery } from "@mui/material";
+import { useState } from "react";
+import CreateTollHouseContent from "./CreateTollHouseContent";
+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);
+ 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: "",
+ };
+ return (
+ <>
+ {isMobile ? (
+
+
+
+ ) : (
+ }
+ onClick={handleOpen}
+ >
+ راهدارخانه جدید
+
+ )}
+
+ >
+ );
+};
+export default CreateTollHouse;
diff --git a/src/components/infrastructure/tollHouse/Form/Edit/EditController.jsx b/src/components/infrastructure/tollHouse/Form/Edit/EditController.jsx
new file mode 100644
index 0000000..774e5cb
--- /dev/null
+++ b/src/components/infrastructure/tollHouse/Form/Edit/EditController.jsx
@@ -0,0 +1,107 @@
+import useRequest from "@/lib/hooks/useRequest";
+import { useEffect, useState } from "react";
+import { GET_TOLL_HOUSE_DETAILS, UPDATE_TOLL_HOUSE_ITEM } from "@/core/utils/routes";
+import DialogLoading from "@/core/components/DialogLoading";
+import CreateTollHouseContent from "../CreateTollHouse/CreateTollHouseContent";
+import { usePermissions } from "@/lib/hooks/usePermissions";
+
+const EditController = ({ rowId, mutate, setOpenEditDialog }) => {
+ const { data: userPermissions } = usePermissions();
+ const hasCreateProvincePermission = userPermissions.some((item) => ["add-receipt"].includes(item));
+ const requestServer = useRequest();
+ const [tollHouseItemDetails, setTollHouseItemDetails] = useState(null);
+ const [tollHouseItemDetailsLoading, setTollHouseItemDetailsLoading] = useState(false);
+
+ useEffect(() => {
+ setTollHouseItemDetailsLoading(true);
+ requestServer(`${GET_TOLL_HOUSE_DETAILS}/${rowId}`, "get")
+ .then((response) => {
+ setTollHouseItemDetails(response.data.data);
+ setTollHouseItemDetailsLoading(false);
+ })
+ .catch((e) => {
+ setTollHouseItemDetailsLoading(false);
+ });
+ }, [rowId]);
+
+ const defaultData = {
+ phone: tollHouseItemDetails?.phone || "",
+ is_province: !hasCreateProvincePermission,
+ province_id: tollHouseItemDetails?.province_id || null,
+ city_id: tollHouseItemDetails?.city_id || null,
+ start_point: { lat: tollHouseItemDetails?.lat || "", lng: tollHouseItemDetails?.lng || "" },
+ name: tollHouseItemDetails?.name || "",
+ status: tollHouseItemDetails?.status || "",
+ type: tollHouseItemDetails?.type || "",
+ team_num: tollHouseItemDetails?.team_num || "",
+ staff_num: tollHouseItemDetails?.staff_num || "",
+ responsible_name: tollHouseItemDetails?.responsible_name || "",
+ responsible_mobile: tollHouseItemDetails?.responsible_mobile || "",
+ successor_name: tollHouseItemDetails?.successor_name || "",
+ successor_mobile: tollHouseItemDetails?.successor_mobile || "",
+ machines_light: tollHouseItemDetails?.machines_light || "",
+ machines_sheavy: tollHouseItemDetails?.machines_sheavy || "",
+ machines_heavy: tollHouseItemDetails?.machines_heavy || "",
+ overview_files_1: tollHouseItemDetails?.files[0]?.path || null,
+ overview_files_2: tollHouseItemDetails?.files[1]?.path || null,
+ overview_files_3: tollHouseItemDetails?.files[2]?.path || null,
+ overview_files_4: tollHouseItemDetails?.files[3]?.path || null,
+ overview_files_5: tollHouseItemDetails?.files[4]?.path || null,
+ };
+ const HandleSubmit = 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",
+ ];
+ 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}`]);
+ }
+ }
+ formData.append("lat", result.start_point.lat);
+ formData.append("lng", result.start_point.lng);
+
+ await requestServer(`${UPDATE_TOLL_HOUSE_ITEM}/${rowId}`, "post", {
+ data: formData,
+ notificationSuccess: true,
+ })
+ .then(() => {
+ mutate();
+ setOpenEditDialog(false);
+ })
+ .catch(() => {});
+ };
+ return (
+ <>
+ {tollHouseItemDetailsLoading ? (
+
+ ) : (
+
+ )}
+ >
+ );
+};
+export default EditController;
diff --git a/src/components/infrastructure/tollHouse/Form/Edit/index.jsx b/src/components/infrastructure/tollHouse/Form/Edit/index.jsx
new file mode 100644
index 0000000..53be78c
--- /dev/null
+++ b/src/components/infrastructure/tollHouse/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/infrastructure/tollHouse/RowActions/DeleteDialog/DeleteContent.jsx b/src/components/infrastructure/tollHouse/RowActions/DeleteDialog/DeleteContent.jsx
new file mode 100644
index 0000000..3f3095a
--- /dev/null
+++ b/src/components/infrastructure/tollHouse/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/infrastructure/tollHouse/RowActions/DeleteDialog/index.jsx b/src/components/infrastructure/tollHouse/RowActions/DeleteDialog/index.jsx
new file mode 100644
index 0000000..54278bd
--- /dev/null
+++ b/src/components/infrastructure/tollHouse/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/infrastructure/tollHouse/RowActions/ImageForm/ImageFormContent.jsx b/src/components/infrastructure/tollHouse/RowActions/ImageForm/ImageFormContent.jsx
new file mode 100644
index 0000000..c8f22d0
--- /dev/null
+++ b/src/components/infrastructure/tollHouse/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/infrastructure/tollHouse/RowActions/ImageForm/ImagesContent.jsx b/src/components/infrastructure/tollHouse/RowActions/ImageForm/ImagesContent.jsx
new file mode 100644
index 0000000..4b66734
--- /dev/null
+++ b/src/components/infrastructure/tollHouse/RowActions/ImageForm/ImagesContent.jsx
@@ -0,0 +1,38 @@
+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 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/infrastructure/tollHouse/RowActions/ImageForm/index.jsx b/src/components/infrastructure/tollHouse/RowActions/ImageForm/index.jsx
new file mode 100644
index 0000000..6ac8d25
--- /dev/null
+++ b/src/components/infrastructure/tollHouse/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/infrastructure/tollHouse/RowActions/LocationForm/LocationFormContent.jsx b/src/components/infrastructure/tollHouse/RowActions/LocationForm/LocationFormContent.jsx
new file mode 100644
index 0000000..14b3db3
--- /dev/null
+++ b/src/components/infrastructure/tollHouse/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/infrastructure/tollHouse/RowActions/LocationForm/index.jsx b/src/components/infrastructure/tollHouse/RowActions/LocationForm/index.jsx
new file mode 100644
index 0000000..f578ca8
--- /dev/null
+++ b/src/components/infrastructure/tollHouse/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/infrastructure/tollHouse/RowActions/index.jsx b/src/components/infrastructure/tollHouse/RowActions/index.jsx
new file mode 100644
index 0000000..8801bda
--- /dev/null
+++ b/src/components/infrastructure/tollHouse/RowActions/index.jsx
@@ -0,0 +1,21 @@
+import { Box } from "@mui/material";
+import EditForm from "../Form/Edit";
+import DeleteDialog from "./DeleteDialog";
+import { usePermissions } from "@/lib/hooks/usePermissions";
+
+const RowActions = ({ row, mutate }) => {
+ const { data: userPermissions } = usePermissions();
+ const hasEditPermission = userPermissions.some((item) =>
+ ["edit-tollhouse", "edit-tollhouse-province"].includes(item)
+ );
+ const hasDeletePermission = userPermissions.some((item) =>
+ ["delete-tollhouse", "delete-tollhouse-province"].includes(item)
+ );
+ return (
+
+ {hasEditPermission && }
+ {hasDeletePermission && }
+
+ );
+};
+export default RowActions;
diff --git a/src/components/infrastructure/tollHouse/TollHouseList.jsx b/src/components/infrastructure/tollHouse/TollHouseList.jsx
new file mode 100644
index 0000000..2b0a39c
--- /dev/null
+++ b/src/components/infrastructure/tollHouse/TollHouseList.jsx
@@ -0,0 +1,412 @@
+"use client";
+import { useEffect, useMemo, useState } from "react";
+import { Box, Stack, Typography } from "@mui/material";
+import DataTableWithAuth from "@/core/components/DataTableWithAuth";
+import Toolbar from "./Toolbar";
+import { GET_TOLL_HOUSE_LIST } from "@/core/utils/routes";
+import moment from "jalali-moment";
+import RowActions from "./RowActions";
+import useProvinces from "@/lib/hooks/useProvince";
+import CustomSelectByDependency from "@/core/components/DataTable/filter/fieldsType/CustomSelectByDependency";
+import useEdaratLists from "@/lib/hooks/useEdaratLists";
+import { usePermissions } from "@/lib/hooks/usePermissions";
+import LocationForm from "./RowActions/LocationForm";
+import ImageDialog from "./RowActions/ImageForm";
+
+const statusOptions = [
+ { value: "", label: "همه وضعیت ها" },
+ { value: 0, label: "غیرفعال" },
+ { value: 1, label: "فعال" },
+];
+const typeOptions = [
+ { value: "", label: "همه نوع ها" },
+ { value: 0, label: "فصلی" },
+ { value: 1, label: "داعمی" },
+ { value: 2, label: "موقت" },
+];
+const TollHouseList = () => {
+ const { data: userPermissions } = usePermissions();
+ const hasProvincePermission = userPermissions.some((item) => ["add-tollhouse"].includes(item));
+
+ const columns = useMemo(() => {
+ 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: "tollHouseInfo",
+ enableColumnFilter: false,
+ enableSorting: false,
+ datatype: "text",
+ grow: false,
+ size: 50,
+ columns: [
+ {
+ accessorKey: "name",
+ header: "نام راهدارخانه",
+ id: "name",
+ enableColumnFilter: true,
+ datatype: "text",
+ filterMode: "equals",
+ sortDescFirst: false,
+ columnFilterModeOptions: ["equals", "contains"],
+ grow: false,
+ size: 100,
+ },
+ {
+ accessorKey: "province_id",
+ header: "استان",
+ id: "province_id",
+ enableColumnFilter: true,
+ datatype: "numeric",
+ filterMode: "equals",
+ grow: false,
+ size: 130,
+ ColumnSelectComponent: (props) => {
+ const { provinces, errorProvinces, loadingProvinces } = useProvinces();
+ const getColumnSelectOptions = useMemo(() => {
+ if (loadingProvinces) {
+ return [{ value: "loading", label: "در حال بارگذاری..." }];
+ }
+ if (errorProvinces) {
+ return [{ value: "error", label: "خطا در بارگذاری" }];
+ }
+ return [
+ { value: "", label: "کل کشور" },
+ ...provinces.map((province) => ({
+ value: province.id,
+ label: province.name_fa,
+ })),
+ ];
+ }, [provinces, errorProvinces, loadingProvinces]);
+ return (
+
+ );
+ },
+ Cell: ({ row }) => <>{row.original.province_name}>,
+ },
+ {
+ accessorKey: "city_id",
+ header: "شهرستان", // Office
+ id: "city_id",
+ enableColumnFilter: true,
+ datatype: "numeric",
+ filterMode: "equals",
+ dependencyId: "province_id",
+ grow: false,
+ size: 120,
+ ColumnSelectComponent: (props) => {
+ const { edaratList, loadingEdaratList, errorEdaratList } = useEdaratLists(
+ props.dependencyFieldValue.value
+ );
+ const [prevDependency, setPrevDependency] = useState(props.dependencyFieldValue.value);
+
+ const getColumnSelectOptions = useMemo(() => {
+ if (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 (prevDependency === props.dependencyFieldValue?.value) return;
+ props.handleChange({ ...props.filterParameters, value: "" });
+ setPrevDependency(props.dependencyFieldValue?.value);
+ }, [props.dependencyFieldValue?.value]);
+ 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 || ""};
+ },
+ },
+ {
+ accessorKey: "type",
+ header: "نوع",
+ id: "type",
+ enableColumnFilter: true,
+ datatype: "numeric",
+ filterMode: "equals",
+ grow: false,
+ size: 100,
+ columnSelectOption: () => {
+ return typeOptions.map((type) => ({
+ value: type.value,
+ label: type.label,
+ }));
+ },
+ Cell: ({ row }) => {
+ return {row.original.type_fa};
+ },
+ },
+ {
+ accessorKey: "phone",
+ header: "تلفن راهدارخانه",
+ id: "phone",
+ enableColumnFilter: false,
+ enableSorting: true,
+ datatype: "text",
+ filterMode: "equals",
+ columnFilterModeOptions: ["equals", "contains"],
+ grow: false,
+ size: 120,
+ },
+ {
+ accessorKey: "team_num",
+ header: "تعداد اکیپ",
+ id: "team_num",
+ enableColumnFilter: false,
+ enableSorting: true,
+ datatype: "text",
+ filterMode: "equals",
+ columnFilterModeOptions: ["equals", "contains"],
+ grow: false,
+ size: 120,
+ },
+ {
+ accessorKey: "staff_num",
+ header: "تعداد پرسنل",
+ id: "staff_num",
+ enableColumnFilter: false,
+ enableSorting: true,
+ datatype: "text",
+ filterMode: "equals",
+ columnFilterModeOptions: ["equals", "contains"],
+ grow: false,
+ size: 120,
+ },
+ {
+ accessorKey: "responsible_name",
+ header: "مسئول",
+ id: "responsible_name",
+ enableColumnFilter: false,
+ enableSorting: true,
+ datatype: "text",
+ filterMode: "equals",
+ columnFilterModeOptions: ["equals", "contains"],
+ grow: false,
+ size: 120,
+ },
+ {
+ accessorKey: "responsible_mobile",
+ header: "شماره مسئول",
+ id: "responsible_mobile",
+ enableColumnFilter: false,
+ enableSorting: true,
+ datatype: "text",
+ filterMode: "equals",
+ columnFilterModeOptions: ["equals", "contains"],
+ grow: false,
+ size: 120,
+ },
+ {
+ accessorKey: "successor_name",
+ header: "جانشین",
+ id: "successor_name",
+ enableColumnFilter: false,
+ enableSorting: true,
+ datatype: "text",
+ filterMode: "equals",
+ columnFilterModeOptions: ["equals", "contains"],
+ grow: false,
+ size: 120,
+ },
+ {
+ accessorKey: "successor_mobile",
+ header: "شماره جانشین",
+ id: "successor_mobile",
+ enableColumnFilter: false,
+ enableSorting: true,
+ datatype: "text",
+ filterMode: "equals",
+ columnFilterModeOptions: ["equals", "contains"],
+ grow: false,
+ size: 120,
+ },
+ ],
+ },
+ {
+ header: "ماشین آلات",
+ id: "machines",
+ enableColumnFilter: false,
+ enableSorting: false,
+ datatype: "text",
+ grow: false,
+ size: 50,
+ columns: [
+ {
+ accessorKey: "machines_light",
+ header: "ماشین آلات سبک",
+ id: "machines_light",
+ enableColumnFilter: false,
+ enableSorting: true,
+ datatype: "text",
+ filterMode: "equals",
+ columnFilterModeOptions: ["equals", "contains"],
+ grow: false,
+ size: 100,
+ },
+ {
+ accessorKey: "machines_sheavy",
+ header: "ماشین آلات نیمه سنگین",
+ id: "machines_sheavy",
+ enableColumnFilter: false,
+ enableSorting: true,
+ datatype: "text",
+ filterMode: "equals",
+ columnFilterModeOptions: ["equals", "contains"],
+ grow: false,
+ size: 100,
+ },
+ {
+ accessorKey: "machines_heavy",
+ header: "ماشین آلات سنگین",
+ id: "machines_heavy",
+ enableColumnFilter: false,
+ enableSorting: true,
+ datatype: "text",
+ filterMode: "equals",
+ columnFilterModeOptions: ["equals", "contains"],
+ grow: false,
+ size: 100,
+ },
+ {
+ header: "تصاویر",
+ id: "images",
+ enableColumnFilter: false,
+ enableSorting: false,
+ datatype: "array",
+ grow: false,
+ size: 100,
+ muiTableBodyCellProps: {
+ sx: {
+ borderLeft: "1px solid #e1e1e1",
+ py: 0,
+ "&:first-of-type": {
+ borderLeft: "unset",
+ },
+ },
+ },
+ Cell: ({ renderedCellValue, row }) => {
+ return (
+
+
+
+ );
+ },
+ },
+ ],
+ },
+ {
+ header: "موقعیت", // Location
+ id: "location",
+ enableColumnFilter: false,
+ enableSorting: false,
+ datatype: "array",
+ grow: false,
+ size: 100,
+ muiTableBodyCellProps: {
+ sx: {
+ borderLeft: "1px solid #e1e1e1",
+ py: 0,
+ "&:first-of-type": {
+ borderLeft: "unset",
+ },
+ },
+ },
+ Cell: ({ renderedCellValue, row }) => {
+ return (
+
+
+
+ );
+ },
+ },
+ {
+ accessorFn: (row) => moment(row.created_at).locale("fa").format("HH:mm | yyyy/MM/DD"),
+ header: "تاریخ ثبت",
+ id: "created_at",
+ enableColumnFilter: true,
+ datatype: "date",
+ filterMode: "between",
+ grow: false,
+ size: 100,
+ },
+ ];
+ }, []);
+
+ return (
+ <>
+
+
+
+ >
+ );
+};
+export default TollHouseList;
diff --git a/src/components/infrastructure/tollHouse/Toolbar.jsx b/src/components/infrastructure/tollHouse/Toolbar.jsx
new file mode 100644
index 0000000..d8ada27
--- /dev/null
+++ b/src/components/infrastructure/tollHouse/Toolbar.jsx
@@ -0,0 +1,10 @@
+import { Box } from "@mui/material";
+import CreateTollHouse from "./Form/CreateTollHouse";
+import { usePermissions } from "@/lib/hooks/usePermissions";
+
+const Toolbar = ({ table, filterData, mutate }) => {
+ const { data: userPermissions } = usePermissions();
+ const hasAddPermission = userPermissions.some((item) => ["add-tollhouse", "add-tollhouse-province"].includes(item));
+ return {hasAddPermission && };
+};
+export default Toolbar;
diff --git a/src/components/infrastructure/tollHouse/index.jsx b/src/components/infrastructure/tollHouse/index.jsx
new file mode 100644
index 0000000..c1d4172
--- /dev/null
+++ b/src/components/infrastructure/tollHouse/index.jsx
@@ -0,0 +1,14 @@
+"use client";
+import PageTitle from "@/core/components/PageTitle";
+import { Stack } from "@mui/material";
+import TollHouseList from "./TollHouseList";
+
+const TollHousePage = () => {
+ return (
+
+
+
+
+ );
+};
+export default TollHousePage;
diff --git a/src/core/utils/headerMenu.js b/src/core/utils/headerMenu.js
index a3ac3c9..dc58a18 100644
--- a/src/core/utils/headerMenu.js
+++ b/src/core/utils/headerMenu.js
@@ -37,7 +37,7 @@ export const headerMenu = [
},
{
title: "راهدارخانه ها",
- href: api + "/v2/rahdari_points",
+ href: "/infrastructure/toll-house",
},
],
[
diff --git a/src/core/utils/routes.js b/src/core/utils/routes.js
index b91186b..d97dba7 100644
--- a/src/core/utils/routes.js
+++ b/src/core/utils/routes.js
@@ -9,7 +9,7 @@ export const GET_PERMISSIONS_ROUTE = api + "/webapi/user/get-permission";
export const GET_SIDEBAR_BADGE_ROUTE = api + "/api/v3/notifications";
export const REFER_ADMIN_PROVINCE = "/v3/api/fake-submit";
export const REFER_ADMIN_CITY = "/v3/api/fake-submit";
-export const GET_CITY_LISTS = "/v3/api/fake-cities";
+export const GET_CITY_LISTS = api + "/public/contents/provinces";
export const GET_PROVINCE_LISTS = api + "/public/contents/provinces";
export const GET_PREV_STATE_OPINION = "/v3/api/fake-prev-state-opinion";
export const SUBMIT_ROAD_SAFETY_FORM = "/v3/api/fake-submit";
@@ -154,3 +154,11 @@ export const GET_USER_ACTIVITIES_LIST = api + "/api/v3/log_list";
export const CREATE_USER_ACTIVITY = api + "/api/v3/log_list";
export const UPDATE_USER_ACTIVITY = api + "/api/v3/log_list";
export const DELETE_USER_ACTIVITIES = api + "/api/v3/log_list";
+
+// toll-house
+export const GET_TOLL_HOUSE_LIST = api + "/api/v3/road_maintenance_station";
+export const CREATE_TOLL_HOUSE = api + "/api/v3/road_maintenance_station";
+export const GET_TOLL_HOUSE_DETAILS = api + "/api/v3/road_maintenance_station";
+export const DELETE_TOLL_HOUSE_ITEM = api + "/api/v3/road_maintenance_station";
+export const UPDATE_TOLL_HOUSE_ITEM = api + "/api/v3/road_maintenance_station";
+export const GET_TOLL_HOUSE_IMAGES = api + "/api/v3/road_maintenance_station/images";
diff --git a/src/lib/hooks/useCities.js b/src/lib/hooks/useCities.js
index 6ac535d..dfaadbb 100644
--- a/src/lib/hooks/useCities.js
+++ b/src/lib/hooks/useCities.js
@@ -1,30 +1,59 @@
-import { useState, useEffect } from "react";
+import { useEffect, useReducer } from "react";
import { GET_CITY_LISTS } from "@/core/utils/routes";
import useRequest from "@/lib/hooks/useRequest";
+const ACTIONS = {
+ LOADING: "loading",
+ SUCCESS: "success",
+ ERROR: "error",
+ RESET: "reset",
+};
-const useCities = () => {
+const initialState = {
+ data: [],
+ loading: true,
+ error: null,
+};
+function reducer(state, action) {
+ switch (action.type) {
+ case ACTIONS.LOADING:
+ return { ...state, loading: true, error: null };
+ case ACTIONS.SUCCESS:
+ return { ...state, loading: false, data: action.payload };
+ case ACTIONS.ERROR:
+ return { ...state, loading: false, error: action.payload };
+ case ACTIONS.RESET:
+ return { ...initialState, loading: false };
+ default:
+ return state;
+ }
+}
+const useCities = (id) => {
const requestServer = useRequest();
- const [cities, setCities] = useState([]);
- const [loadingCities, setLoadingCities] = useState(true);
- const [errorCities, setErrorCities] = useState(null);
+ const [state, dispatch] = useReducer(reducer, initialState);
useEffect(() => {
- const fetchCities = async () => {
+ if (!id) {
+ dispatch({ type: ACTIONS.RESET });
+ return;
+ }
+ const fetchCityLists = async () => {
+ dispatch({ type: ACTIONS.LOADING });
try {
- const response = await requestServer(`${GET_CITY_LISTS}`);
- setCities(response.data.data);
- setLoadingCities(false);
- } catch (e) {
- console.error("Error fetching cities:", e);
- setErrorCities(e);
- setLoadingCities(false);
+ const response = await requestServer(`${GET_CITY_LISTS}/${id}`);
+ dispatch({ type: ACTIONS.SUCCESS, payload: response.data.data });
+ } catch (error) {
+ dispatch({ type: ACTIONS.ERROR, payload: error });
}
};
- fetchCities();
- }, []);
+ fetchCityLists();
+ }, [id]);
- return { cities, loadingCities, errorCities };
+ return {
+ cityList: state.data,
+ loadingCityList: state.loading,
+ errorCityList: state.error,
+ };
};
export default useCities;