diff --git a/package.json b/package.json
index 8a2e759..79b3f30 100644
--- a/package.json
+++ b/package.json
@@ -15,6 +15,7 @@
"@emotion/react": "^11.11.3",
"@emotion/styled": "^11.11.0",
"@hookform/resolvers": "^3.9.0",
+ "@mapbox/polyline": "^1.2.1",
"@mui/icons-material": "^5.16.7",
"@mui/lab": "^5.0.0-alpha.173",
"@mui/material": "^5.16.7",
diff --git a/public/icons/destination-icon.svg b/public/icons/destination-icon.svg
new file mode 100644
index 0000000..ad8a633
--- /dev/null
+++ b/public/icons/destination-icon.svg
@@ -0,0 +1,18 @@
+
diff --git a/public/icons/origin-icon.svg b/public/icons/origin-icon.svg
new file mode 100644
index 0000000..295c9da
--- /dev/null
+++ b/public/icons/origin-icon.svg
@@ -0,0 +1,18 @@
+
diff --git a/src/app/(withAuth)/(dashboardLayout)/dashboard/machines/loading.jsx b/src/app/(withAuth)/(dashboardLayout)/dashboard/machines/loading.jsx
index 4535a8d..c3935ed 100644
--- a/src/app/(withAuth)/(dashboardLayout)/dashboard/machines/loading.jsx
+++ b/src/app/(withAuth)/(dashboardLayout)/dashboard/machines/loading.jsx
@@ -1,7 +1,7 @@
-"use client";
-import PageLoading from "@/core/components/PageLoading";
-
-const Loading = () => {
- return ;
-};
-export default Loading;
+"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
index 35c27ad..ad5b6c2 100644
--- a/src/app/(withAuth)/(dashboardLayout)/dashboard/machines/page.jsx
+++ b/src/app/(withAuth)/(dashboardLayout)/dashboard/machines/page.jsx
@@ -1,17 +1,17 @@
-import WithPermission from "@/core/middlewares/withPermission";
-import MachinesPage from "@/components/dashboard/machines";
-
-export const metadata = {
- title: "کارتابل ماشینها",
-};
-const Page = () => {
- return (
- <>
-
-
-
- >
- );
-};
-
-export default Page;
+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/app/(withAuth)/(dashboardLayout)/dashboard/road-missions/reports/missionsReports/page.js b/src/app/(withAuth)/(dashboardLayout)/dashboard/road-missions/reports/missionsReports/page.js
index 0fae9c1..f60b32b 100644
--- a/src/app/(withAuth)/(dashboardLayout)/dashboard/road-missions/reports/missionsReports/page.js
+++ b/src/app/(withAuth)/(dashboardLayout)/dashboard/road-missions/reports/missionsReports/page.js
@@ -1,18 +1,18 @@
-import WithPermission from "@/core/middlewares/withPermission";
-import MissionsReportPage from "@/components/dashboard/roadMissions/reports/taradodReports";
-export const metadata = {
- title: "کارتابل گزارش ترددها",
-};
-const Page = () => {
- return (
-
-
-
- );
-};
-
-export default Page;
+import WithPermission from "@/core/middlewares/withPermission";
+import MissionsReportPage from "@/components/dashboard/roadMissions/reports/taradodReports";
+export const metadata = {
+ title: "کارتابل گزارش ترددها",
+};
+const Page = () => {
+ return (
+
+
+
+ );
+};
+
+export default Page;
diff --git a/src/app/(withAuth)/(dashboardLayout)/dashboard/road-missions/reports/violationsReports/page.js b/src/app/(withAuth)/(dashboardLayout)/dashboard/road-missions/reports/violationsReports/page.js
index 6aa2483..66bc585 100644
--- a/src/app/(withAuth)/(dashboardLayout)/dashboard/road-missions/reports/violationsReports/page.js
+++ b/src/app/(withAuth)/(dashboardLayout)/dashboard/road-missions/reports/violationsReports/page.js
@@ -1,18 +1,18 @@
-import ViolationsReportsPage from "@/components/dashboard/roadMissions/reports/violationsReports";
-import WithPermission from "@/core/middlewares/withPermission";
-
-export const metadata = {
- title: "کارتابل گزارش تخلفات",
-};
-
-export default function Page() {
- return (
-
-
-
- );
-}
+import ViolationsReportsPage from "@/components/dashboard/roadMissions/reports/violationsReports";
+import WithPermission from "@/core/middlewares/withPermission";
+
+export const metadata = {
+ title: "کارتابل گزارش تخلفات",
+};
+
+export default function Page() {
+ return (
+
+
+
+ );
+}
diff --git a/src/assets/images/destination-icon.svg b/src/assets/images/destination-icon.svg
new file mode 100644
index 0000000..ad8a633
--- /dev/null
+++ b/src/assets/images/destination-icon.svg
@@ -0,0 +1,18 @@
+
diff --git a/src/assets/images/prev-destination-icon.svg b/src/assets/images/prev-destination-icon.svg
new file mode 100644
index 0000000..656eca3
--- /dev/null
+++ b/src/assets/images/prev-destination-icon.svg
@@ -0,0 +1,21 @@
+
diff --git a/src/assets/images/prev-source-icon.svg b/src/assets/images/prev-source-icon.svg
new file mode 100644
index 0000000..621e71a
--- /dev/null
+++ b/src/assets/images/prev-source-icon.svg
@@ -0,0 +1,21 @@
+
diff --git a/src/assets/images/source-icon.svg b/src/assets/images/source-icon.svg
new file mode 100644
index 0000000..295c9da
--- /dev/null
+++ b/src/assets/images/source-icon.svg
@@ -0,0 +1,18 @@
+
diff --git a/src/components/dashboard/machines/MachinesList.jsx b/src/components/dashboard/machines/MachinesList.jsx
index 880f0cb..ce787e0 100644
--- a/src/components/dashboard/machines/MachinesList.jsx
+++ b/src/components/dashboard/machines/MachinesList.jsx
@@ -1,134 +1,134 @@
-"use client";
-import DataTableWithAuth from "@/core/components/DataTableWithAuth";
-import { GET_MACHINES_LIST } from "@/core/utils/routes";
-import { Box, Typography } from "@mui/material";
-import { useMemo } from "react";
-import RowActions from "./RowActions";
-
-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 columns = useMemo(() => {
- return [
- {
- accessorKey: "id",
- header: "کد یکتا", // Unique Code
- id: "id",
- enableColumnFilter: true,
- datatype: "text",
- filterMode: "equals",
- sortDescFirst: false,
- columnFilterModeOptions: ["equals", "contains"],
- grow: false,
- size: 50,
- },
-
- {
- header: "اطلاعات ماشینها",
- id: "machines_info",
- enableColumnFilter: false,
- enableSorting: false,
- datatype: "text",
- grow: false,
- 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,
- },
- {
- 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;
+"use client";
+import DataTableWithAuth from "@/core/components/DataTableWithAuth";
+import { GET_MACHINES_LIST } from "@/core/utils/routes";
+import { Box, Typography } from "@mui/material";
+import { useMemo } from "react";
+import RowActions from "./RowActions";
+
+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 columns = useMemo(() => {
+ return [
+ {
+ accessorKey: "id",
+ header: "کد یکتا", // Unique Code
+ id: "id",
+ enableColumnFilter: true,
+ datatype: "text",
+ filterMode: "equals",
+ sortDescFirst: false,
+ columnFilterModeOptions: ["equals", "contains"],
+ grow: false,
+ size: 50,
+ },
+
+ {
+ header: "اطلاعات ماشینها",
+ id: "machines_info",
+ enableColumnFilter: false,
+ enableSorting: false,
+ datatype: "text",
+ grow: false,
+ 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,
+ },
+ {
+ 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
index 3f3095a..bf0a3c5 100644
--- a/src/components/dashboard/machines/RowActions/DeleteDialog/DeleteContent.jsx
+++ b/src/components/dashboard/machines/RowActions/DeleteDialog/DeleteContent.jsx
@@ -1,39 +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;
+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
index 54278bd..37eefdf 100644
--- a/src/components/dashboard/machines/RowActions/DeleteDialog/index.jsx
+++ b/src/components/dashboard/machines/RowActions/DeleteDialog/index.jsx
@@ -1,34 +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;
+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/Edit/Form/index.jsx b/src/components/dashboard/machines/RowActions/Edit/Form/index.jsx
index 6cfcd9d..5608c57 100644
--- a/src/components/dashboard/machines/RowActions/Edit/Form/index.jsx
+++ b/src/components/dashboard/machines/RowActions/Edit/Form/index.jsx
@@ -1,281 +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) && (
-
-
-
- )}
-
-
-
-
-
-
-
- }
- >
- {isSubmitting ? "در حال ویرایش" : "ویرایش ماشین"}
-
-
- >
- );
-};
-
-export default EditFormContent;
+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) && (
+
+
+
+ )}
+
+
+
+
+
+
+
+ }
+ >
+ {isSubmitting ? "در حال ویرایش" : "ویرایش ماشین"}
+
+
+ >
+ );
+};
+
+export default EditFormContent;
diff --git a/src/components/dashboard/machines/RowActions/Edit/index.jsx b/src/components/dashboard/machines/RowActions/Edit/index.jsx
index f3cec95..b777d54 100644
--- a/src/components/dashboard/machines/RowActions/Edit/index.jsx
+++ b/src/components/dashboard/machines/RowActions/Edit/index.jsx
@@ -1,44 +1,44 @@
-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 EditFormContent from "./Form";
-
-const Edit = ({ row, mutate, rowId }) => {
- const [openEditDialog, setOpenEditDialog] = useState(false);
-
- return (
- <>
-
- {
- setOpenEditDialog(true);
- }}
- >
-
-
-
-
- >
- );
-};
-export default Edit;
+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 EditFormContent from "./Form";
+
+const Edit = ({ row, mutate, rowId }) => {
+ const [openEditDialog, setOpenEditDialog] = useState(false);
+
+ return (
+ <>
+
+ {
+ setOpenEditDialog(true);
+ }}
+ >
+
+
+
+
+ >
+ );
+};
+export default Edit;
diff --git a/src/components/dashboard/machines/RowActions/index.jsx b/src/components/dashboard/machines/RowActions/index.jsx
index 526fe10..1e1d4eb 100644
--- a/src/components/dashboard/machines/RowActions/index.jsx
+++ b/src/components/dashboard/machines/RowActions/index.jsx
@@ -1,14 +1,14 @@
-import { usePermissions } from "@/lib/hooks/usePermissions";
-import { Box } from "@mui/material";
-import Edit from "./Edit";
-
-const RowActions = ({ row, mutate }) => {
- const { data: userPermissions } = usePermissions();
- const hasEditPermission = userPermissions.some((item) => ["update-machine"].includes(item));
- return (
-
- {hasEditPermission && }
-
- );
-};
-export default RowActions;
+import { usePermissions } from "@/lib/hooks/usePermissions";
+import { Box } from "@mui/material";
+import Edit from "./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
index 24db476..43dd0b8 100644
--- a/src/components/dashboard/machines/ShowProjectArea/ShowBound.jsx
+++ b/src/components/dashboard/machines/ShowProjectArea/ShowBound.jsx
@@ -1,29 +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;
+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
index 250cd52..29f2697 100644
--- a/src/components/dashboard/machines/ShowProjectArea/index.jsx
+++ b/src/components/dashboard/machines/ShowProjectArea/index.jsx
@@ -1,65 +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;
+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/index.jsx b/src/components/dashboard/machines/index.jsx
index ed3db7c..6074488 100644
--- a/src/components/dashboard/machines/index.jsx
+++ b/src/components/dashboard/machines/index.jsx
@@ -1,14 +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;
+"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/components/dashboard/roadMissions/operator/Actions/Create/Form/Area/MapControlPolyline/index.jsx b/src/components/dashboard/roadMissions/operator/Actions/Create/Form/Area/MapControlPolyline/index.jsx
index 8d9b5d0..0a14736 100644
--- a/src/components/dashboard/roadMissions/operator/Actions/Create/Form/Area/MapControlPolyline/index.jsx
+++ b/src/components/dashboard/roadMissions/operator/Actions/Create/Form/Area/MapControlPolyline/index.jsx
@@ -1,116 +1,113 @@
-import { Delete, Route } from "@mui/icons-material";
-import { Box, Button, Stack, Typography } from "@mui/material";
-import { useReducer } from "react";
-import DrawBound from "./DrawBound";
+"use client";
-const statusType = [
- {
- id: 0,
- message: "برای آغاز ترسیم مسیر، کلیک کنید!",
- buttons: [
- {
- label: "شروع ترسیم مسیر",
- key: "start",
- color: "primary",
- icon: ,
- onclick: (controlDispach) => {
- controlDispach({ type: "SET_STATUS", status: 1 });
- },
- },
- ],
- },
- {
- id: 1,
- message: "مسیر موردنظر را با کلیک روی نقشه مشخص کنید. برای اتمام، روی نقطهی پایانی کلیک کنید!",
- buttons: [],
- },
- {
- id: 2,
- message: "برای اصلاح مسیر، گوشهها را بکشید. برای ترسیم دوباره، آن را حذف کنید",
- buttons: [
- {
- label: "حذف",
- key: "end",
- color: "error",
- icon: ,
- onclick: (controlDispach) => {
- controlDispach({ type: "SET_STATUS", status: 0 });
- },
- },
- ],
- },
-];
+import { useEffect, useState } from "react";
+import { Marker, Polyline, useMap } from "react-leaflet";
+import L from "leaflet";
-const createInitialState = (bound) => {
- if (bound) {
- return { status: 2 };
- }
- return { status: 0 };
-};
+// ===== Icons =====
+const originIcon = new L.Icon({
+ iconUrl: "/icons/origin-icon.svg",
+ iconSize: [32, 32],
+ iconAnchor: [16, 32],
+});
-const reducer = (state, action) => {
- switch (action.type) {
- case "SET_STATUS":
- return { ...state, status: action.status };
- default:
- return state;
- }
-};
+const destinationIcon = new L.Icon({
+ iconUrl: "/icons/destination-icon.svg",
+ iconSize: [32, 32],
+ iconAnchor: [16, 32],
+});
-const MapControlPolyline = ({ bound, setBound }) => {
- const [control, controlDispach] = useReducer(reducer, bound, createInitialState);
+// ===== Track Center =====
+function MapCenterTracker({ setCenter }) {
+ const map = useMap();
+
+ useEffect(() => {
+ const updateCenter = () => {
+ setCenter(map.getCenter());
+ };
+
+ updateCenter();
+
+ map.on("move", updateCenter);
+
+ return () => {
+ map.off("move", updateCenter);
+ };
+ }, [map, setCenter]);
+
+ return null;
+}
+
+// ===== Fixed Center Marker =====
+function FixedCenterMarker() {
+ return (
+
+

+
+ );
+}
+
+export default function Example() {
+ const [center, setCenter] = useState(null);
+
+ const [origin, setOrigin] = useState(null);
+ const [destination, setDestination] = useState(null);
+
+ const [step, setStep] = useState("origin");
+
+ const handleOk = () => {
+ if (!center) return;
+
+ if (step === "origin") {
+ setOrigin(center);
+ setStep("destination");
+ return;
+ }
+
+ if (step === "destination") {
+ setDestination(center);
+ setStep("done");
+ }
+ };
return (
- <>
-
-
- theme.palette.info.main,
- borderBottomLeftRadius: 8,
- borderBottomRightRadius: 8,
- py: 0.5,
- px: 2,
- }}
- >
-
- {statusType.find((st) => st.id == control.status).message}
-
-
-
-
+
-
- {statusType
- .find((st) => st.id == control.status)
- .buttons.map((button) => (
-
- ))}
-
-
- >
+
+
+ {/* saved markers */}
+ {origin &&
}
+
+ {destination &&
}
+
+ {/* route line */}
+ {origin && destination &&
}
+
+ {/* fixed center pin */}
+ {step !== "done" &&
}
+
+
+
+
+
+
);
-};
-export default MapControlPolyline;
+}
diff --git a/src/components/dashboard/roadMissions/operator/Actions/Create/Form/Area/Routing/AlarmText.jsx b/src/components/dashboard/roadMissions/operator/Actions/Create/Form/Area/Routing/AlarmText.jsx
new file mode 100644
index 0000000..0ddf8a0
--- /dev/null
+++ b/src/components/dashboard/roadMissions/operator/Actions/Create/Form/Area/Routing/AlarmText.jsx
@@ -0,0 +1,28 @@
+import { Box, Typography } from "@mui/material";
+
+const AlarmText = ({ isSource }) => {
+ if (isSource) return null;
+ return (
+
+
+ با کلیک بر روی مارکر موقعیت را انتخاب کنید
+
+
+ );
+};
+
+export default AlarmText;
diff --git a/src/components/dashboard/roadMissions/operator/Actions/Create/Form/Area/Routing/DataBox.jsx b/src/components/dashboard/roadMissions/operator/Actions/Create/Form/Area/Routing/DataBox.jsx
new file mode 100644
index 0000000..5768f75
--- /dev/null
+++ b/src/components/dashboard/roadMissions/operator/Actions/Create/Form/Area/Routing/DataBox.jsx
@@ -0,0 +1,53 @@
+import { Box, Button, TextField } from "@mui/material";
+
+const DataBox = ({ isSource, isDest, sourcePosition, destPosition, editingTarget, onEditSource, onEditDest }) => {
+ if (!isSource) return null;
+
+ return (
+
+
+
+ {isSource && isDest && (
+
+ )}
+
+ {isDest && (
+
+
+
+
+ )}
+
+ );
+};
+
+export default DataBox;
diff --git a/src/components/dashboard/roadMissions/operator/Actions/Create/Form/Area/Routing/ShowRoute.jsx b/src/components/dashboard/roadMissions/operator/Actions/Create/Form/Area/Routing/ShowRoute.jsx
new file mode 100644
index 0000000..7d47ba8
--- /dev/null
+++ b/src/components/dashboard/roadMissions/operator/Actions/Create/Form/Area/Routing/ShowRoute.jsx
@@ -0,0 +1,46 @@
+import { useEffect, useRef } from "react";
+import { Marker, Polyline, useMap } from "react-leaflet";
+import L from "leaflet";
+import polyline from "@mapbox/polyline";
+import SourceIcon from "@/assets/images/source-icon.svg";
+import DestIcon from "@/assets/images/destination-icon.svg";
+
+const ShowRoute = ({ area }) => {
+ const map = useMap();
+ const defaultIconSize = [35, 35];
+
+ const createCustomIcon = (size, iconUrl) => {
+ return L.icon({
+ iconUrl: iconUrl,
+ iconSize: size,
+ iconAnchor: [size[0] / 2, size[1]],
+ popupAnchor: [0, -size[1]],
+ });
+ };
+
+ useEffect(() => {
+ if (coords.length > 0) {
+ map.flyToBounds(coords, { padding: [50, 50], duration: 1.5 });
+ }
+ }, [area]);
+ const sourceIcon = createCustomIcon(defaultIconSize, SourceIcon.src);
+ const destIcon = createCustomIcon(defaultIconSize, DestIcon.src);
+
+ if (!area) return null;
+ const coords = polyline.decode(area, 6);
+
+ if (coords.length === 0) return null;
+
+ const sourcePosition = coords[0];
+ const destPosition = coords[coords.length - 1];
+
+ return (
+ <>
+
+
+
+ >
+ );
+};
+
+export default ShowRoute;
diff --git a/src/components/dashboard/roadMissions/operator/Actions/Create/Form/Area/Routing/index.jsx b/src/components/dashboard/roadMissions/operator/Actions/Create/Form/Area/Routing/index.jsx
new file mode 100644
index 0000000..49c4651
--- /dev/null
+++ b/src/components/dashboard/roadMissions/operator/Actions/Create/Form/Area/Routing/index.jsx
@@ -0,0 +1,219 @@
+import { Marker, Polyline, useMapEvents } from "react-leaflet";
+import L from "leaflet";
+import polyline from "@mapbox/polyline";
+import SourceIcon from "@/assets/images/source-icon.svg";
+import PrevSourceIcon from "@/assets/images/prev-source-icon.svg";
+import PrevDestIcon from "@/assets/images/prev-destination-icon.svg";
+import DestIcon from "@/assets/images/destination-icon.svg";
+import { useEffect, useRef, useState } from "react";
+import DataBox from "./DataBox";
+import AlarmText from "./AlarmText";
+
+const Routing = ({ setArea, setLocations, locations, area }) => {
+ const mapPrevSourceMarker = useRef();
+ const mapPrevDestMarker = useRef();
+ const mapRef = useRef(null);
+ const defaultIconSize = [35, 35];
+ const [isSource, setIsSource] = useState(false);
+ const [sourcePosition, setSourcePosition] = useState(null);
+ const [isDest, setIsDest] = useState(false);
+ const [destPosition, setDestPosition] = useState(null);
+ const [routeCoords, setRouteCoords] = useState([]);
+ const [alternateCoords, setAlternateCoords] = useState([]);
+ const [editingTarget, setEditingTarget] = useState(null);
+ const [selectedRoute, setSelectedRoute] = useState(null);
+
+ const createCustomIcon = (size, iconUrl) => {
+ return L.icon({
+ iconUrl: iconUrl,
+ iconSize: size,
+ iconAnchor: [size[0] / 2, size[1]],
+ popupAnchor: [0, -size[1]],
+ });
+ };
+
+ const defaultIcon = createCustomIcon(
+ defaultIconSize,
+ isSource && editingTarget !== "source" ? SourceIcon.src : PrevSourceIcon.src
+ );
+
+ const prevDestIcon = createCustomIcon(
+ defaultIconSize,
+ isDest && editingTarget !== "dest" ? DestIcon.src : PrevDestIcon.src
+ );
+
+ const isMovingSource = !isSource || editingTarget === "source";
+ const isMovingDest = isSource && (!isDest || editingTarget === "dest");
+
+ const map = useMapEvents({
+ move(e) {
+ if (isMovingSource) mapPrevSourceMarker.current.setLatLng(e.target.getCenter());
+ if (isMovingDest && mapPrevDestMarker.current) mapPrevDestMarker.current.setLatLng(e.target.getCenter());
+ },
+ movestart() {
+ if (isMovingSource) mapPrevSourceMarker.current.setIcon(createCustomIcon([45, 45], PrevSourceIcon.src));
+ if (isMovingDest && mapPrevDestMarker.current)
+ mapPrevDestMarker.current.setIcon(createCustomIcon([45, 45], PrevDestIcon.src));
+ },
+ moveend() {
+ if (isMovingSource)
+ mapPrevSourceMarker.current.setIcon(createCustomIcon(defaultIconSize, PrevSourceIcon.src));
+ if (isMovingDest && mapPrevDestMarker.current)
+ mapPrevDestMarker.current.setIcon(createCustomIcon(defaultIconSize, PrevDestIcon.src));
+ },
+ });
+
+ useEffect(() => {
+ mapRef.current = map;
+ }, [map]);
+
+ useEffect(() => {
+ if (locations.length < 2) return;
+ const restoredSource = { lat: locations[0].lat, lng: locations[0].lon };
+ const restoredDest = { lat: locations[1].lat, lng: locations[1].lon };
+ setSourcePosition(restoredSource);
+ setDestPosition(restoredDest);
+ setIsSource(true);
+ setIsDest(true);
+ if (area) {
+ const decoded = polyline.decode(area, 6);
+ setRouteCoords(decoded);
+ setSelectedRoute({ index: "main", coords: decoded });
+ setTimeout(() => {
+ mapRef.current?.flyToBounds(decoded, { padding: [50, 50], duration: 1.5 });
+ }, 100);
+ } else {
+ fetchRoute(restoredSource, restoredDest);
+ }
+ }, []);
+
+ const fetchRoute = async (source, dest) => {
+ const locs = [
+ { lat: source.lat, lon: source.lng },
+ { lat: dest.lat, lon: dest.lng },
+ ];
+ try {
+ const response = await fetch("https://jouya.141.ir/api/router/drive", {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ locations: locs }),
+ });
+ const data = await response.json();
+ const shape = data.trip.legs[0].shape;
+ setLocations(locs);
+ setArea(shape);
+ const decoded = polyline.decode(shape, 6);
+ setRouteCoords(decoded);
+ setSelectedRoute({ index: "main", coords: decoded });
+
+ if (data.alternates?.length > 0) {
+ const decodedAlternates = data.alternates.map((alt) => polyline.decode(alt.trip.legs[0].shape, 6));
+ setAlternateCoords(decodedAlternates);
+ } else {
+ setAlternateCoords([]);
+ }
+ map.flyToBounds(decoded, { padding: [50, 50], duration: 1.5 });
+ } catch (error) {
+ console.error("Routing error:", error);
+ }
+ };
+
+ const handleMarkerClick = async () => {
+ if (!isSource || editingTarget === "source") {
+ const clickedPosition = mapPrevSourceMarker.current.getLatLng();
+ setSourcePosition(clickedPosition);
+ setIsSource(true);
+ setEditingTarget(null);
+ setRouteCoords([]);
+ setAlternateCoords([]);
+ setSelectedRoute(null);
+
+ if (editingTarget === "source" && destPosition) {
+ await fetchRoute(clickedPosition, destPosition);
+ }
+ }
+ };
+
+ const handleDestMarkerClick = async () => {
+ if (!isDest || editingTarget === "dest") {
+ const clickedPosition = mapPrevDestMarker.current.getLatLng();
+ setDestPosition(clickedPosition);
+ setIsDest(true);
+ setEditingTarget(null);
+
+ await fetchRoute(sourcePosition, clickedPosition);
+ }
+ };
+
+ const handleEditSource = () => {
+ setEditingTarget("source");
+ setRouteCoords([]);
+ setAlternateCoords([]);
+ setSelectedRoute(null);
+ };
+
+ const handleEditDest = () => {
+ setEditingTarget("dest");
+ setRouteCoords([]);
+ setAlternateCoords([]);
+ setSelectedRoute(null);
+ };
+
+ const handleSelectRoute = (index, coords) => {
+ setSelectedRoute({ index, coords });
+ const encoded = polyline.encode(coords, 6);
+ setArea(encoded);
+ map.flyToBounds(coords, { padding: [50, 50], duration: 0.3 });
+ };
+
+ return (
+ <>
+
+
+
+ {isSource && (
+
+ )}
+ {alternateCoords.map((coords, index) => (
+ handleSelectRoute(index, coords) }}
+ />
+ ))}
+ {routeCoords.length > 0 && (
+ handleSelectRoute("main", routeCoords) }}
+ />
+ )}
+ >
+ );
+};
+
+export default Routing;
diff --git a/src/components/dashboard/roadMissions/operator/Actions/Create/Form/Area/index.jsx b/src/components/dashboard/roadMissions/operator/Actions/Create/Form/Area/index.jsx
index a4240b3..200c1e0 100644
--- a/src/components/dashboard/roadMissions/operator/Actions/Create/Form/Area/index.jsx
+++ b/src/components/dashboard/roadMissions/operator/Actions/Create/Form/Area/index.jsx
@@ -2,19 +2,21 @@ import MapLoading from "@/core/components/MapLayer/Loading";
import { Box, Button, DialogActions, DialogContent, Stack } from "@mui/material";
import dynamic from "next/dynamic";
import { useCallback, useState } from "react";
-import MapControlPolyline from "./MapControlPolyline";
+import Routing from "./Routing";
const MapLayer = dynamic(() => import("@/core/components/MapLayer"), {
loading: () => ,
ssr: false,
});
const Area = ({ allData, setAllData, handlePrev, setTabState }) => {
- const [bound, setBound] = useState(allData.bound);
+ const [area, setArea] = useState(allData.area);
+ const [locations, setLocations] = useState(allData.locations);
const handleNext = useCallback(() => {
- setAllData({ bound: bound });
+ setAllData({ area: area });
+ setAllData({ locations: locations });
setTabState((s) => s + 1);
- }, [bound]);
+ }, [area]);
return (
<>
@@ -22,7 +24,7 @@ const Area = ({ allData, setAllData, handlePrev, setTabState }) => {
-
+
@@ -31,8 +33,8 @@ const Area = ({ allData, setAllData, handlePrev, setTabState }) => {
-
- )}
- >
- );
-};
-export default PrintExcel;
+"use client";
+import {
+ EXPORT_MISSIONS_REPORT_TARADOD_COUNTRY_EXCEL_ACTIVITY,
+ EXPORT_MISSIONS_REPORT_TARADOD_PROVINCE_EXCEL_ACTIVITY,
+} from "@/core/utils/routes";
+import useRequest from "@/lib/hooks/useRequest";
+import { useTheme } from "@emotion/react";
+import DescriptionIcon from "@mui/icons-material/Description";
+import { Button, CircularProgress, IconButton, useMediaQuery } from "@mui/material";
+import FileSaver from "file-saver";
+import moment from "jalali-moment";
+import { useMemo, useState } from "react";
+
+const PrintExcel = ({ table, filterData }) => {
+ const theme = useTheme();
+ const isMobile = useMediaQuery(theme.breakpoints.down("sm"));
+ const [loading, setLoading] = useState(false);
+ const requestServer = useRequest({ notificationSuccess: true });
+ const activeFilters = Object.entries(filterData).reduce((acc, [key, filter]) => {
+ if (filter.value) {
+ acc.push({ id: filter.id, value: filter.value });
+ }
+ return acc;
+ }, []);
+
+ const filterParams = useMemo(() => {
+ const params = new URLSearchParams();
+ activeFilters.length > 0 &&
+ activeFilters.map((filter, index) => {
+ params.set(`${filter.id}`, filter.value);
+ });
+ return params;
+ }, [activeFilters]);
+
+ const clickHandler = () => {
+ setLoading(true);
+ const CountryOrProvince = activeFilters.find((item) => item.id === "province_id");
+ const requestUrl =
+ CountryOrProvince.value === "-1"
+ ? EXPORT_MISSIONS_REPORT_TARADOD_COUNTRY_EXCEL_ACTIVITY
+ : EXPORT_MISSIONS_REPORT_TARADOD_PROVINCE_EXCEL_ACTIVITY;
+ requestServer(`${requestUrl}?${filterParams}`, "get", {
+ requestOptions: { responseType: "blob" },
+ })
+ .then((response) => {
+ const filename = `خروجی کارتابل گزارشات گشت راهداری تاریخ_${moment().format("jYYYY_jMM_jDD")}.xlsx`;
+ FileSaver.saveAs(response.data, filename);
+ })
+ .catch(() => {})
+ .finally(() => {
+ setLoading(false);
+ });
+ };
+
+ return (
+ <>
+ {isMobile ? (
+
+
+
+ ) : (
+ : }
+ onClick={clickHandler}
+ >
+ خروجی اکسل
+
+ )}
+ >
+ );
+};
+export default PrintExcel;
diff --git a/src/components/dashboard/roadMissions/reports/taradodReports/MissionsReportLists.jsx b/src/components/dashboard/roadMissions/reports/taradodReports/MissionsReportLists.jsx
index 12de496..a80cabb 100644
--- a/src/components/dashboard/roadMissions/reports/taradodReports/MissionsReportLists.jsx
+++ b/src/components/dashboard/roadMissions/reports/taradodReports/MissionsReportLists.jsx
@@ -1,138 +1,138 @@
-"use client";
-import DataTableWithAuth from "@/core/components/DataTableWithAuth";
-import { Box } from "@mui/material";
-import { useEffect, useMemo } from "react";
-import Toolbar from "./Toolbar";
-
-const TaradodReportLists = ({ data, specialFilter }) => {
- const columns = useMemo(() => {
- return [
- {
- accessorKey: "edare_name",
- header: "اداره کل",
- id: "edare_name",
- enableColumnFilter: false,
- enableSorting: false,
- datatype: "text",
- grow: false,
- size: 50,
- },
- {
- accessorKey: "total",
- header: "کل ماموریت ها",
- id: "total",
- enableColumnFilter: false,
- enableSorting: false,
- datatype: "numeric",
- grow: false,
- size: 50,
- },
- {
- accessorKey: "khareg_mahdode",
- header: "تردد خارج از محدوده",
- id: "khareg_mahdode",
- enableColumnFilter: false,
- enableSorting: false,
- datatype: "numeric",
- grow: false,
- size: 50,
- },
- {
- accessorKey: "durations",
- header: "جمع ساعات ماموریت",
- id: "duration",
- enableColumnFilter: false,
- enableSorting: false,
- datatype: "numeric",
- grow: false,
- size: 50,
- },
- {
- accessorKey: "gps",
- header: "جمع ساعات gps",
- id: "gps",
- enableColumnFilter: false,
- enableSorting: false,
- datatype: "numeric",
- grow: false,
- size: 50,
- },
- {
- accessorKey: "far_h",
- header: "با فرایند ساعتی",
- id: "far_h",
- enableColumnFilter: false,
- enableSorting: false,
- datatype: "numeric",
- grow: false,
- size: 50,
- },
- {
- accessorKey: "far_d",
- header: "با فرایند روزانه",
- id: "far_d",
- enableColumnFilter: false,
- enableSorting: false,
- datatype: "numeric",
- grow: false,
- size: 50,
- },
- {
- accessorKey: "un_far_h",
- header: "بدون فرایند ساعتی",
- id: "un_far_h",
- enableColumnFilter: false,
- enableSorting: false,
- datatype: "numeric",
- grow: false,
- size: 50,
- },
- {
- accessorKey: "un_far_d",
- header: "بدون فرایند روزانه",
- id: "un_far_d",
- enableColumnFilter: false,
- enableSorting: false,
- datatype: "numeric",
- grow: false,
- size: 50,
- },
- {
- accessorKey: "item",
- header: "فعالیت روزانه",
- id: "item",
- enableColumnFilter: false,
- enableSorting: false,
- datatype: "text",
- grow: false,
- size: 50,
- },
- {
- accessorKey: "patrol",
- header: "گشت راهداری",
- id: "patrol",
- enableColumnFilter: false,
- enableSorting: false,
- datatype: "text",
- grow: false,
- size: 50,
- },
- ];
- }, []);
-
- return (
-
-
-
- );
-};
-
-export default TaradodReportLists;
+"use client";
+import DataTableWithAuth from "@/core/components/DataTableWithAuth";
+import { Box } from "@mui/material";
+import { useEffect, useMemo } from "react";
+import Toolbar from "./Toolbar";
+
+const TaradodReportLists = ({ data, specialFilter }) => {
+ const columns = useMemo(() => {
+ return [
+ {
+ accessorKey: "edare_name",
+ header: "اداره کل",
+ id: "edare_name",
+ enableColumnFilter: false,
+ enableSorting: false,
+ datatype: "text",
+ grow: false,
+ size: 50,
+ },
+ {
+ accessorKey: "total",
+ header: "کل ماموریت ها",
+ id: "total",
+ enableColumnFilter: false,
+ enableSorting: false,
+ datatype: "numeric",
+ grow: false,
+ size: 50,
+ },
+ {
+ accessorKey: "khareg_mahdode",
+ header: "تردد خارج از محدوده",
+ id: "khareg_mahdode",
+ enableColumnFilter: false,
+ enableSorting: false,
+ datatype: "numeric",
+ grow: false,
+ size: 50,
+ },
+ {
+ accessorKey: "durations",
+ header: "جمع ساعات ماموریت",
+ id: "duration",
+ enableColumnFilter: false,
+ enableSorting: false,
+ datatype: "numeric",
+ grow: false,
+ size: 50,
+ },
+ {
+ accessorKey: "gps",
+ header: "جمع ساعات gps",
+ id: "gps",
+ enableColumnFilter: false,
+ enableSorting: false,
+ datatype: "numeric",
+ grow: false,
+ size: 50,
+ },
+ {
+ accessorKey: "far_h",
+ header: "با فرایند ساعتی",
+ id: "far_h",
+ enableColumnFilter: false,
+ enableSorting: false,
+ datatype: "numeric",
+ grow: false,
+ size: 50,
+ },
+ {
+ accessorKey: "far_d",
+ header: "با فرایند روزانه",
+ id: "far_d",
+ enableColumnFilter: false,
+ enableSorting: false,
+ datatype: "numeric",
+ grow: false,
+ size: 50,
+ },
+ {
+ accessorKey: "un_far_h",
+ header: "بدون فرایند ساعتی",
+ id: "un_far_h",
+ enableColumnFilter: false,
+ enableSorting: false,
+ datatype: "numeric",
+ grow: false,
+ size: 50,
+ },
+ {
+ accessorKey: "un_far_d",
+ header: "بدون فرایند روزانه",
+ id: "un_far_d",
+ enableColumnFilter: false,
+ enableSorting: false,
+ datatype: "numeric",
+ grow: false,
+ size: 50,
+ },
+ {
+ accessorKey: "item",
+ header: "فعالیت روزانه",
+ id: "item",
+ enableColumnFilter: false,
+ enableSorting: false,
+ datatype: "text",
+ grow: false,
+ size: 50,
+ },
+ {
+ accessorKey: "patrol",
+ header: "گشت راهداری",
+ id: "patrol",
+ enableColumnFilter: false,
+ enableSorting: false,
+ datatype: "text",
+ grow: false,
+ size: 50,
+ },
+ ];
+ }, []);
+
+ return (
+
+
+
+ );
+};
+
+export default TaradodReportLists;
diff --git a/src/components/dashboard/roadMissions/reports/taradodReports/Search/FromDateController.jsx b/src/components/dashboard/roadMissions/reports/taradodReports/Search/FromDateController.jsx
index 490ee7b..0a81488 100644
--- a/src/components/dashboard/roadMissions/reports/taradodReports/Search/FromDateController.jsx
+++ b/src/components/dashboard/roadMissions/reports/taradodReports/Search/FromDateController.jsx
@@ -1,27 +1,27 @@
-import MuiDatePicker from "@/core/components/MuiDatePicker";
-import moment from "jalali-moment";
-import { Controller } from "react-hook-form";
-
-const FromDateController = ({ control }) => {
- return (
- (
- {
- const formattedDate = moment(new Date(newValue)).locale("en").format("YYYY-MM-DD");
- onChange(formattedDate); // Update the field value in react-hook-form
- }}
- helperText={error ? error.message : null}
- />
- )}
- />
- );
-};
-export default FromDateController;
+import MuiDatePicker from "@/core/components/MuiDatePicker";
+import moment from "jalali-moment";
+import { Controller } from "react-hook-form";
+
+const FromDateController = ({ control }) => {
+ return (
+ (
+ {
+ const formattedDate = moment(new Date(newValue)).locale("en").format("YYYY-MM-DD");
+ onChange(formattedDate); // Update the field value in react-hook-form
+ }}
+ helperText={error ? error.message : null}
+ />
+ )}
+ />
+ );
+};
+export default FromDateController;
diff --git a/src/components/dashboard/roadMissions/reports/taradodReports/Search/SearchReportField.jsx b/src/components/dashboard/roadMissions/reports/taradodReports/Search/SearchReportField.jsx
index bde2a4c..e1b71cd 100644
--- a/src/components/dashboard/roadMissions/reports/taradodReports/Search/SearchReportField.jsx
+++ b/src/components/dashboard/roadMissions/reports/taradodReports/Search/SearchReportField.jsx
@@ -1,44 +1,44 @@
-import SearchIcon from "@mui/icons-material/Search";
-import { Button, Grid } from "@mui/material";
-import { Controller } from "react-hook-form";
-import FromDateController from "./FromDateController";
-import SelectProvince from "./SelectProvince";
-import ToDateController from "./ToDateController";
-
-const SearchReportField = ({ control, hasProvincesPermission }) => {
- return (
-
-
-
-
-
-
-
- {hasProvincesPermission && (
-
-
-
- )}
-
- (
- }
- fullWidth
- >
- {isSubmitting ? "درحال دریافت اطلاعات" : "جستجو"}
-
- )}
- />
-
-
- );
-};
-
-export default SearchReportField;
+import SearchIcon from "@mui/icons-material/Search";
+import { Button, Grid } from "@mui/material";
+import { Controller } from "react-hook-form";
+import FromDateController from "./FromDateController";
+import SelectProvince from "./SelectProvince";
+import ToDateController from "./ToDateController";
+
+const SearchReportField = ({ control, hasProvincesPermission }) => {
+ return (
+
+
+
+
+
+
+
+ {hasProvincesPermission && (
+
+
+
+ )}
+
+ (
+ }
+ fullWidth
+ >
+ {isSubmitting ? "درحال دریافت اطلاعات" : "جستجو"}
+
+ )}
+ />
+
+
+ );
+};
+
+export default SearchReportField;
diff --git a/src/components/dashboard/roadMissions/reports/taradodReports/Search/SelectProvince.jsx b/src/components/dashboard/roadMissions/reports/taradodReports/Search/SelectProvince.jsx
index 2b24d67..89395f0 100644
--- a/src/components/dashboard/roadMissions/reports/taradodReports/Search/SelectProvince.jsx
+++ b/src/components/dashboard/roadMissions/reports/taradodReports/Search/SelectProvince.jsx
@@ -1,45 +1,45 @@
-import useProvinces from "@/lib/hooks/useProvince";
-import { FormControl, FormHelperText, InputLabel, MenuItem, Select } from "@mui/material";
-import { Controller } from "react-hook-form";
-
-const SelectProvince = ({ control }) => {
- const { provinces, loadingProvinces, errorProvinces } = useProvinces();
- return (
- (
-
- استان
-
- {error && {error.message}}
-
- )}
- />
- );
-};
-export default SelectProvince;
+import useProvinces from "@/lib/hooks/useProvince";
+import { FormControl, FormHelperText, InputLabel, MenuItem, Select } from "@mui/material";
+import { Controller } from "react-hook-form";
+
+const SelectProvince = ({ control }) => {
+ const { provinces, loadingProvinces, errorProvinces } = useProvinces();
+ return (
+ (
+
+ استان
+
+ {error && {error.message}}
+
+ )}
+ />
+ );
+};
+export default SelectProvince;
diff --git a/src/components/dashboard/roadMissions/reports/taradodReports/Search/ToDateController.jsx b/src/components/dashboard/roadMissions/reports/taradodReports/Search/ToDateController.jsx
index fc4a776..ef3ff49 100644
--- a/src/components/dashboard/roadMissions/reports/taradodReports/Search/ToDateController.jsx
+++ b/src/components/dashboard/roadMissions/reports/taradodReports/Search/ToDateController.jsx
@@ -1,30 +1,30 @@
-import MuiDatePicker from "@/core/components/MuiDatePicker";
-import moment from "jalali-moment";
-import { Controller, useWatch } from "react-hook-form";
-
-const ToDateController = ({ control }) => {
- const minDate = useWatch({ control, name: "from_date" });
- return (
- (
- {
- const formattedDate = moment(new Date(newValue)).locale("en").format("YYYY-MM-DD");
- onChange(formattedDate); // Update the field value in react-hook-form
- }}
- helperText={error ? error.message : null}
- />
- )}
- />
- );
-};
-export default ToDateController;
+import MuiDatePicker from "@/core/components/MuiDatePicker";
+import moment from "jalali-moment";
+import { Controller, useWatch } from "react-hook-form";
+
+const ToDateController = ({ control }) => {
+ const minDate = useWatch({ control, name: "from_date" });
+ return (
+ (
+ {
+ const formattedDate = moment(new Date(newValue)).locale("en").format("YYYY-MM-DD");
+ onChange(formattedDate); // Update the field value in react-hook-form
+ }}
+ helperText={error ? error.message : null}
+ />
+ )}
+ />
+ );
+};
+export default ToDateController;
diff --git a/src/components/dashboard/roadMissions/reports/taradodReports/Search/index.jsx b/src/components/dashboard/roadMissions/reports/taradodReports/Search/index.jsx
index f29d302..9d504f2 100644
--- a/src/components/dashboard/roadMissions/reports/taradodReports/Search/index.jsx
+++ b/src/components/dashboard/roadMissions/reports/taradodReports/Search/index.jsx
@@ -1,13 +1,13 @@
-import StyledForm from "@/core/components/StyledForm";
-import SearchReportField from "./SearchReportField";
-
-const SearchReportList = ({ handleSubmit, onSearchSubmit, control, hasProvincesPermission }) => {
- return (
- <>
-
-
-
- >
- );
-};
-export default SearchReportList;
+import StyledForm from "@/core/components/StyledForm";
+import SearchReportField from "./SearchReportField";
+
+const SearchReportList = ({ handleSubmit, onSearchSubmit, control, hasProvincesPermission }) => {
+ return (
+ <>
+
+
+
+ >
+ );
+};
+export default SearchReportList;
diff --git a/src/components/dashboard/roadMissions/reports/taradodReports/Toolbar.jsx b/src/components/dashboard/roadMissions/reports/taradodReports/Toolbar.jsx
index 0f726c7..afc132c 100644
--- a/src/components/dashboard/roadMissions/reports/taradodReports/Toolbar.jsx
+++ b/src/components/dashboard/roadMissions/reports/taradodReports/Toolbar.jsx
@@ -1,11 +1,11 @@
-import { Box } from "@mui/material";
-import PrintExcel from "./ExcelPrint";
-
-const Toolbar = ({ table, filterData, mutate }) => {
- return (
-
-
-
- );
-};
-export default Toolbar;
+import { Box } from "@mui/material";
+import PrintExcel from "./ExcelPrint";
+
+const Toolbar = ({ table, filterData, mutate }) => {
+ return (
+
+
+
+ );
+};
+export default Toolbar;
diff --git a/src/components/dashboard/roadMissions/reports/taradodReports/index.jsx b/src/components/dashboard/roadMissions/reports/taradodReports/index.jsx
index ce41101..1af4608 100644
--- a/src/components/dashboard/roadMissions/reports/taradodReports/index.jsx
+++ b/src/components/dashboard/roadMissions/reports/taradodReports/index.jsx
@@ -1,142 +1,142 @@
-"use client";
-import PageTitle from "@/core/components/PageTitle";
-import {
- GET_MISSIONS_REPORT_TARADOD_COUNTRY_ACTIVITY,
- GET_MISSIONS_REPORT_TARADOD_PROVINCE_ACTIVITY,
-} from "@/core/utils/routes";
-import { useAuth } from "@/lib/contexts/auth";
-import { usePermissions } from "@/lib/hooks/usePermissions";
-import useRequest from "@/lib/hooks/useRequest";
-import { yupResolver } from "@hookform/resolvers/yup";
-import { LinearProgress, Stack } from "@mui/material";
-import moment from "jalali-moment";
-import { useEffect, useState } from "react";
-import { useForm } from "react-hook-form";
-import * as yup from "yup";
-import TaradodReportLists from "./MissionsReportLists";
-import SearchReportList from "./Search";
-
-const TaradodReportPage = () => {
- const [data, setData] = useState(null);
- const { data: userPermissions } = usePermissions();
- const { user } = useAuth();
- const requestServer = useRequest();
- const [provinceId, setProvinceId] = useState(user.province_id || "-1");
- const hasProvincesPermission = userPermissions?.includes("mission-report-country");
-
- const defaultValues = {
- from_date: moment(new Date()).format("YYYY-MM-DD"),
- date_to: moment(new Date()).format("YYYY-MM-DD"),
- province_id: provinceId,
- };
- const defaultFilter = [
- { value: `${defaultValues.province_id}`, datatype: "numeric", id: "province_id", fn: "equals" },
- { value: `${defaultValues.from_date}`, datatype: "date", id: "from_date", fn: "between" },
- { value: `${defaultValues.date_to}`, datatype: "date", id: "date_to", fn: "between" },
- ];
- const [specialFilter, setSpecialFilter] = useState(defaultFilter);
-
- const onSearchSubmit = async (data) => {
- const params = new URLSearchParams();
- params.set("from_date", `${data.from_date}`);
- params.set("date_to", `${data.date_to}`);
- setSpecialFilter([
- { value: `${data.province_id}`, datatype: "numeric", id: "province_id", fn: "equals" },
- { value: `${data.from_date}`, datatype: "date", id: "from_date", fn: "between" },
- { value: `${data.date_to}`, datatype: "date", id: "date_to", fn: "between" },
- ]);
- if (data.province_id === "-1") {
- try {
- // params.set("province_id", `${data.province_id}`);
- const response = await requestServer(`${GET_MISSIONS_REPORT_TARADOD_COUNTRY_ACTIVITY}?${params}`);
- const result = response.data.data;
- const nationalReportForItem = result.activities.find((report) => report.province_id === "-1");
- const nationalReport = {
- edare_name: "کل کشور",
- ...nationalReportForItem,
- };
-
- setData({
- data: [
- nationalReport,
- ...result.provinces.map((province) => {
- const filteredReports = result.activities.find(
- (report) => report.province_id == province.id
- );
- return {
- ...filteredReports,
- edare_name: province.name_fa,
- };
- }),
- ],
- filters: data,
- });
- } catch (e) {}
- } else {
- try {
- params.set("province_id", `${data.province_id}`);
- const response = await requestServer(`${GET_MISSIONS_REPORT_TARADOD_PROVINCE_ACTIVITY}?${params}`);
- const result = response.data.data;
-
- const nationalReportForItem = result.activities.find((report) => report.city_id === "-1");
-
- const nationalReport = {
- ...nationalReportForItem,
- edare_name: "کل استان",
- };
- setData({
- data: [
- nationalReport,
- ...result.city.map((city) => {
- const filteredReports = result.activities.find((report) => report.city_id == city.id);
- return {
- edare_name: city.name_fa,
- ...filteredReports,
- };
- }),
- ],
- filters: data,
- });
- } catch (e) {}
- }
- };
-
- useEffect(() => {
- if (!userPermissions) return;
- onSearchSubmit(defaultValues);
- }, [userPermissions]);
-
- const validationSchema = yup.object().shape({
- from_date: yup
- .string()
- .required("تاریخ شروع الزامی است!")
- .matches(/^\d{4}-\d{2}-\d{2}$/, "تاریخ شروع الزامی است!")
- .test("is-valid-date", "تاریخ شروع معتبر نیست!", (value) => moment(value, "YYYY-MM-DD", true).isValid()),
- date_to: yup
- .string()
- .required("تاریخ پایان الزامی است!")
- .matches(/^\d{4}-\d{2}-\d{2}$/, "تاریخ پایان الزامی است!")
- .test("is-valid-date", "تاریخ پایان معتبر نیست!", (value) => moment(value, "YYYY-MM-DD", true).isValid()),
- province_id: yup.string().nullable(),
- });
-
- const { control, handleSubmit } = useForm({
- defaultValues,
- resolver: yupResolver(validationSchema),
- mode: "all",
- });
-
- return (
-
-
-
- {!data ? : }
-
- );
-};
-export default TaradodReportPage;
+"use client";
+import PageTitle from "@/core/components/PageTitle";
+import {
+ GET_MISSIONS_REPORT_TARADOD_COUNTRY_ACTIVITY,
+ GET_MISSIONS_REPORT_TARADOD_PROVINCE_ACTIVITY,
+} from "@/core/utils/routes";
+import { useAuth } from "@/lib/contexts/auth";
+import { usePermissions } from "@/lib/hooks/usePermissions";
+import useRequest from "@/lib/hooks/useRequest";
+import { yupResolver } from "@hookform/resolvers/yup";
+import { LinearProgress, Stack } from "@mui/material";
+import moment from "jalali-moment";
+import { useEffect, useState } from "react";
+import { useForm } from "react-hook-form";
+import * as yup from "yup";
+import TaradodReportLists from "./MissionsReportLists";
+import SearchReportList from "./Search";
+
+const TaradodReportPage = () => {
+ const [data, setData] = useState(null);
+ const { data: userPermissions } = usePermissions();
+ const { user } = useAuth();
+ const requestServer = useRequest();
+ const [provinceId, setProvinceId] = useState(user.province_id || "-1");
+ const hasProvincesPermission = userPermissions?.includes("mission-report-country");
+
+ const defaultValues = {
+ from_date: moment(new Date()).format("YYYY-MM-DD"),
+ date_to: moment(new Date()).format("YYYY-MM-DD"),
+ province_id: provinceId,
+ };
+ const defaultFilter = [
+ { value: `${defaultValues.province_id}`, datatype: "numeric", id: "province_id", fn: "equals" },
+ { value: `${defaultValues.from_date}`, datatype: "date", id: "from_date", fn: "between" },
+ { value: `${defaultValues.date_to}`, datatype: "date", id: "date_to", fn: "between" },
+ ];
+ const [specialFilter, setSpecialFilter] = useState(defaultFilter);
+
+ const onSearchSubmit = async (data) => {
+ const params = new URLSearchParams();
+ params.set("from_date", `${data.from_date}`);
+ params.set("date_to", `${data.date_to}`);
+ setSpecialFilter([
+ { value: `${data.province_id}`, datatype: "numeric", id: "province_id", fn: "equals" },
+ { value: `${data.from_date}`, datatype: "date", id: "from_date", fn: "between" },
+ { value: `${data.date_to}`, datatype: "date", id: "date_to", fn: "between" },
+ ]);
+ if (data.province_id === "-1") {
+ try {
+ // params.set("province_id", `${data.province_id}`);
+ const response = await requestServer(`${GET_MISSIONS_REPORT_TARADOD_COUNTRY_ACTIVITY}?${params}`);
+ const result = response.data.data;
+ const nationalReportForItem = result.activities.find((report) => report.province_id === "-1");
+ const nationalReport = {
+ edare_name: "کل کشور",
+ ...nationalReportForItem,
+ };
+
+ setData({
+ data: [
+ nationalReport,
+ ...result.provinces.map((province) => {
+ const filteredReports = result.activities.find(
+ (report) => report.province_id == province.id
+ );
+ return {
+ ...filteredReports,
+ edare_name: province.name_fa,
+ };
+ }),
+ ],
+ filters: data,
+ });
+ } catch (e) {}
+ } else {
+ try {
+ params.set("province_id", `${data.province_id}`);
+ const response = await requestServer(`${GET_MISSIONS_REPORT_TARADOD_PROVINCE_ACTIVITY}?${params}`);
+ const result = response.data.data;
+
+ const nationalReportForItem = result.activities.find((report) => report.city_id === "-1");
+
+ const nationalReport = {
+ ...nationalReportForItem,
+ edare_name: "کل استان",
+ };
+ setData({
+ data: [
+ nationalReport,
+ ...result.city.map((city) => {
+ const filteredReports = result.activities.find((report) => report.city_id == city.id);
+ return {
+ edare_name: city.name_fa,
+ ...filteredReports,
+ };
+ }),
+ ],
+ filters: data,
+ });
+ } catch (e) {}
+ }
+ };
+
+ useEffect(() => {
+ if (!userPermissions) return;
+ onSearchSubmit(defaultValues);
+ }, [userPermissions]);
+
+ const validationSchema = yup.object().shape({
+ from_date: yup
+ .string()
+ .required("تاریخ شروع الزامی است!")
+ .matches(/^\d{4}-\d{2}-\d{2}$/, "تاریخ شروع الزامی است!")
+ .test("is-valid-date", "تاریخ شروع معتبر نیست!", (value) => moment(value, "YYYY-MM-DD", true).isValid()),
+ date_to: yup
+ .string()
+ .required("تاریخ پایان الزامی است!")
+ .matches(/^\d{4}-\d{2}-\d{2}$/, "تاریخ پایان الزامی است!")
+ .test("is-valid-date", "تاریخ پایان معتبر نیست!", (value) => moment(value, "YYYY-MM-DD", true).isValid()),
+ province_id: yup.string().nullable(),
+ });
+
+ const { control, handleSubmit } = useForm({
+ defaultValues,
+ resolver: yupResolver(validationSchema),
+ mode: "all",
+ });
+
+ return (
+
+
+
+ {!data ? : }
+
+ );
+};
+export default TaradodReportPage;
diff --git a/src/components/dashboard/roadMissions/reports/violationsReports/ExcelPrint/index.jsx b/src/components/dashboard/roadMissions/reports/violationsReports/ExcelPrint/index.jsx
index b7487f6..3968fff 100644
--- a/src/components/dashboard/roadMissions/reports/violationsReports/ExcelPrint/index.jsx
+++ b/src/components/dashboard/roadMissions/reports/violationsReports/ExcelPrint/index.jsx
@@ -1,77 +1,77 @@
-"use client";
-import {
- EXPORT_MISSIONS_REPORT_VIOLATION_COUNTRY_EXCEL_ACTIVITY,
- EXPORT_MISSIONS_REPORT_VIOLATION_PROVINCE_EXCEL_ACTIVITY,
-} from "@/core/utils/routes";
-import useRequest from "@/lib/hooks/useRequest";
-import { useTheme } from "@emotion/react";
-import DescriptionIcon from "@mui/icons-material/Description";
-import { Button, CircularProgress, IconButton, useMediaQuery } from "@mui/material";
-import FileSaver from "file-saver";
-import moment from "jalali-moment";
-import { useMemo, useState } from "react";
-
-const PrintExcel = ({ table, filterData }) => {
- const theme = useTheme();
- const isMobile = useMediaQuery(theme.breakpoints.down("sm"));
- const [loading, setLoading] = useState(false);
- const requestServer = useRequest({ notificationSuccess: true });
- const activeFilters = Object.entries(filterData).reduce((acc, [key, filter]) => {
- if (filter.value) {
- acc.push({ id: filter.id, value: filter.value });
- }
- return acc;
- }, []);
-
- const filterParams = useMemo(() => {
- const params = new URLSearchParams();
- activeFilters.length > 0 &&
- activeFilters.map((filter, index) => {
- params.set(`${filter.id}`, filter.value);
- });
- return params;
- }, [activeFilters]);
-
- const clickHandler = () => {
- setLoading(true);
- const CountryOrProvince = activeFilters.find((item) => item.id === "province_id");
- const requestUrl =
- CountryOrProvince.value === "-1"
- ? EXPORT_MISSIONS_REPORT_VIOLATION_COUNTRY_EXCEL_ACTIVITY
- : EXPORT_MISSIONS_REPORT_VIOLATION_PROVINCE_EXCEL_ACTIVITY;
- requestServer(`${requestUrl}?${filterParams}`, "get", {
- requestOptions: { responseType: "blob" },
- })
- .then((response) => {
- const filename = `خروجی کارتابل گزارشات گشت راهداری تاریخ_${moment().format("jYYYY_jMM_jDD")}.xlsx`;
- FileSaver.saveAs(response.data, filename);
- })
- .catch(() => {})
- .finally(() => {
- setLoading(false);
- });
- };
-
- return (
- <>
- {isMobile ? (
-
-
-
- ) : (
- : }
- onClick={clickHandler}
- >
- خروجی اکسل
-
- )}
- >
- );
-};
-export default PrintExcel;
+"use client";
+import {
+ EXPORT_MISSIONS_REPORT_VIOLATION_COUNTRY_EXCEL_ACTIVITY,
+ EXPORT_MISSIONS_REPORT_VIOLATION_PROVINCE_EXCEL_ACTIVITY,
+} from "@/core/utils/routes";
+import useRequest from "@/lib/hooks/useRequest";
+import { useTheme } from "@emotion/react";
+import DescriptionIcon from "@mui/icons-material/Description";
+import { Button, CircularProgress, IconButton, useMediaQuery } from "@mui/material";
+import FileSaver from "file-saver";
+import moment from "jalali-moment";
+import { useMemo, useState } from "react";
+
+const PrintExcel = ({ table, filterData }) => {
+ const theme = useTheme();
+ const isMobile = useMediaQuery(theme.breakpoints.down("sm"));
+ const [loading, setLoading] = useState(false);
+ const requestServer = useRequest({ notificationSuccess: true });
+ const activeFilters = Object.entries(filterData).reduce((acc, [key, filter]) => {
+ if (filter.value) {
+ acc.push({ id: filter.id, value: filter.value });
+ }
+ return acc;
+ }, []);
+
+ const filterParams = useMemo(() => {
+ const params = new URLSearchParams();
+ activeFilters.length > 0 &&
+ activeFilters.map((filter, index) => {
+ params.set(`${filter.id}`, filter.value);
+ });
+ return params;
+ }, [activeFilters]);
+
+ const clickHandler = () => {
+ setLoading(true);
+ const CountryOrProvince = activeFilters.find((item) => item.id === "province_id");
+ const requestUrl =
+ CountryOrProvince.value === "-1"
+ ? EXPORT_MISSIONS_REPORT_VIOLATION_COUNTRY_EXCEL_ACTIVITY
+ : EXPORT_MISSIONS_REPORT_VIOLATION_PROVINCE_EXCEL_ACTIVITY;
+ requestServer(`${requestUrl}?${filterParams}`, "get", {
+ requestOptions: { responseType: "blob" },
+ })
+ .then((response) => {
+ const filename = `خروجی کارتابل گزارشات گشت راهداری تاریخ_${moment().format("jYYYY_jMM_jDD")}.xlsx`;
+ FileSaver.saveAs(response.data, filename);
+ })
+ .catch(() => {})
+ .finally(() => {
+ setLoading(false);
+ });
+ };
+
+ return (
+ <>
+ {isMobile ? (
+
+
+
+ ) : (
+ : }
+ onClick={clickHandler}
+ >
+ خروجی اکسل
+
+ )}
+ >
+ );
+};
+export default PrintExcel;
diff --git a/src/components/dashboard/roadMissions/reports/violationsReports/Search/FromDateController.jsx b/src/components/dashboard/roadMissions/reports/violationsReports/Search/FromDateController.jsx
index 490ee7b..0a81488 100644
--- a/src/components/dashboard/roadMissions/reports/violationsReports/Search/FromDateController.jsx
+++ b/src/components/dashboard/roadMissions/reports/violationsReports/Search/FromDateController.jsx
@@ -1,27 +1,27 @@
-import MuiDatePicker from "@/core/components/MuiDatePicker";
-import moment from "jalali-moment";
-import { Controller } from "react-hook-form";
-
-const FromDateController = ({ control }) => {
- return (
- (
- {
- const formattedDate = moment(new Date(newValue)).locale("en").format("YYYY-MM-DD");
- onChange(formattedDate); // Update the field value in react-hook-form
- }}
- helperText={error ? error.message : null}
- />
- )}
- />
- );
-};
-export default FromDateController;
+import MuiDatePicker from "@/core/components/MuiDatePicker";
+import moment from "jalali-moment";
+import { Controller } from "react-hook-form";
+
+const FromDateController = ({ control }) => {
+ return (
+ (
+ {
+ const formattedDate = moment(new Date(newValue)).locale("en").format("YYYY-MM-DD");
+ onChange(formattedDate); // Update the field value in react-hook-form
+ }}
+ helperText={error ? error.message : null}
+ />
+ )}
+ />
+ );
+};
+export default FromDateController;
diff --git a/src/components/dashboard/roadMissions/reports/violationsReports/Search/SearchReportField.jsx b/src/components/dashboard/roadMissions/reports/violationsReports/Search/SearchReportField.jsx
index bde2a4c..e1b71cd 100644
--- a/src/components/dashboard/roadMissions/reports/violationsReports/Search/SearchReportField.jsx
+++ b/src/components/dashboard/roadMissions/reports/violationsReports/Search/SearchReportField.jsx
@@ -1,44 +1,44 @@
-import SearchIcon from "@mui/icons-material/Search";
-import { Button, Grid } from "@mui/material";
-import { Controller } from "react-hook-form";
-import FromDateController from "./FromDateController";
-import SelectProvince from "./SelectProvince";
-import ToDateController from "./ToDateController";
-
-const SearchReportField = ({ control, hasProvincesPermission }) => {
- return (
-
-
-
-
-
-
-
- {hasProvincesPermission && (
-
-
-
- )}
-
- (
- }
- fullWidth
- >
- {isSubmitting ? "درحال دریافت اطلاعات" : "جستجو"}
-
- )}
- />
-
-
- );
-};
-
-export default SearchReportField;
+import SearchIcon from "@mui/icons-material/Search";
+import { Button, Grid } from "@mui/material";
+import { Controller } from "react-hook-form";
+import FromDateController from "./FromDateController";
+import SelectProvince from "./SelectProvince";
+import ToDateController from "./ToDateController";
+
+const SearchReportField = ({ control, hasProvincesPermission }) => {
+ return (
+
+
+
+
+
+
+
+ {hasProvincesPermission && (
+
+
+
+ )}
+
+ (
+ }
+ fullWidth
+ >
+ {isSubmitting ? "درحال دریافت اطلاعات" : "جستجو"}
+
+ )}
+ />
+
+
+ );
+};
+
+export default SearchReportField;
diff --git a/src/components/dashboard/roadMissions/reports/violationsReports/Search/SelectProvince.jsx b/src/components/dashboard/roadMissions/reports/violationsReports/Search/SelectProvince.jsx
index 2b24d67..89395f0 100644
--- a/src/components/dashboard/roadMissions/reports/violationsReports/Search/SelectProvince.jsx
+++ b/src/components/dashboard/roadMissions/reports/violationsReports/Search/SelectProvince.jsx
@@ -1,45 +1,45 @@
-import useProvinces from "@/lib/hooks/useProvince";
-import { FormControl, FormHelperText, InputLabel, MenuItem, Select } from "@mui/material";
-import { Controller } from "react-hook-form";
-
-const SelectProvince = ({ control }) => {
- const { provinces, loadingProvinces, errorProvinces } = useProvinces();
- return (
- (
-
- استان
-
- {error && {error.message}}
-
- )}
- />
- );
-};
-export default SelectProvince;
+import useProvinces from "@/lib/hooks/useProvince";
+import { FormControl, FormHelperText, InputLabel, MenuItem, Select } from "@mui/material";
+import { Controller } from "react-hook-form";
+
+const SelectProvince = ({ control }) => {
+ const { provinces, loadingProvinces, errorProvinces } = useProvinces();
+ return (
+ (
+
+ استان
+
+ {error && {error.message}}
+
+ )}
+ />
+ );
+};
+export default SelectProvince;
diff --git a/src/components/dashboard/roadMissions/reports/violationsReports/Search/ToDateController.jsx b/src/components/dashboard/roadMissions/reports/violationsReports/Search/ToDateController.jsx
index 2dd7263..9b9ac81 100644
--- a/src/components/dashboard/roadMissions/reports/violationsReports/Search/ToDateController.jsx
+++ b/src/components/dashboard/roadMissions/reports/violationsReports/Search/ToDateController.jsx
@@ -1,30 +1,30 @@
-import MuiDatePicker from "@/core/components/MuiDatePicker";
-import moment from "jalali-moment";
-import { Controller, useWatch } from "react-hook-form";
-
-const ToDateController = ({ control }) => {
- const minDate = useWatch({ control, name: "from_date" });
- return (
- (
- {
- const formattedDate = moment(new Date(newValue)).locale("en").format("YYYY-MM-DD");
- onChange(formattedDate); // Update the field value in react-hook-form
- }}
- helperText={error ? error.message : null}
- />
- )}
- />
- );
-};
-export default ToDateController;
+import MuiDatePicker from "@/core/components/MuiDatePicker";
+import moment from "jalali-moment";
+import { Controller, useWatch } from "react-hook-form";
+
+const ToDateController = ({ control }) => {
+ const minDate = useWatch({ control, name: "from_date" });
+ return (
+ (
+ {
+ const formattedDate = moment(new Date(newValue)).locale("en").format("YYYY-MM-DD");
+ onChange(formattedDate); // Update the field value in react-hook-form
+ }}
+ helperText={error ? error.message : null}
+ />
+ )}
+ />
+ );
+};
+export default ToDateController;
diff --git a/src/components/dashboard/roadMissions/reports/violationsReports/Search/index.jsx b/src/components/dashboard/roadMissions/reports/violationsReports/Search/index.jsx
index f29d302..9d504f2 100644
--- a/src/components/dashboard/roadMissions/reports/violationsReports/Search/index.jsx
+++ b/src/components/dashboard/roadMissions/reports/violationsReports/Search/index.jsx
@@ -1,13 +1,13 @@
-import StyledForm from "@/core/components/StyledForm";
-import SearchReportField from "./SearchReportField";
-
-const SearchReportList = ({ handleSubmit, onSearchSubmit, control, hasProvincesPermission }) => {
- return (
- <>
-
-
-
- >
- );
-};
-export default SearchReportList;
+import StyledForm from "@/core/components/StyledForm";
+import SearchReportField from "./SearchReportField";
+
+const SearchReportList = ({ handleSubmit, onSearchSubmit, control, hasProvincesPermission }) => {
+ return (
+ <>
+
+
+
+ >
+ );
+};
+export default SearchReportList;
diff --git a/src/components/dashboard/roadMissions/reports/violationsReports/Toolbar.jsx b/src/components/dashboard/roadMissions/reports/violationsReports/Toolbar.jsx
index 0f726c7..afc132c 100644
--- a/src/components/dashboard/roadMissions/reports/violationsReports/Toolbar.jsx
+++ b/src/components/dashboard/roadMissions/reports/violationsReports/Toolbar.jsx
@@ -1,11 +1,11 @@
-import { Box } from "@mui/material";
-import PrintExcel from "./ExcelPrint";
-
-const Toolbar = ({ table, filterData, mutate }) => {
- return (
-
-
-
- );
-};
-export default Toolbar;
+import { Box } from "@mui/material";
+import PrintExcel from "./ExcelPrint";
+
+const Toolbar = ({ table, filterData, mutate }) => {
+ return (
+
+
+
+ );
+};
+export default Toolbar;
diff --git a/src/components/dashboard/roadMissions/reports/violationsReports/ViolationsReportsList.jsx b/src/components/dashboard/roadMissions/reports/violationsReports/ViolationsReportsList.jsx
index 60966f8..61f7282 100644
--- a/src/components/dashboard/roadMissions/reports/violationsReports/ViolationsReportsList.jsx
+++ b/src/components/dashboard/roadMissions/reports/violationsReports/ViolationsReportsList.jsx
@@ -1,98 +1,98 @@
-"use client";
-import DataTableWithAuth from "@/core/components/DataTableWithAuth";
-import { Box } from "@mui/material";
-import { useEffect, useMemo } from "react";
-import Toolbar from "./Toolbar";
-
-const ViolationsReportsList = ({ data, specialFilter }) => {
- const columns = useMemo(() => {
- return [
- {
- accessorKey: "edare_name",
- header: "اداره کل",
- id: "edare_name",
- enableColumnFilter: false,
- enableSorting: false,
- datatype: "text",
- grow: false,
- size: 50,
- },
- {
- accessorKey: "total",
- header: "کل تخلفات",
- id: "total",
- enableColumnFilter: false,
- enableSorting: false,
- datatype: "numeric",
- grow: false,
- size: 50,
- },
- {
- accessorKey: "done",
- header: "اقدام شده",
- id: "done",
- enableColumnFilter: false,
- enableSorting: false,
- datatype: "numeric",
- grow: false,
- size: 50,
- },
- {
- accessorKey: "undone",
- header: "اقدام نشده",
- id: "un_done",
- enableColumnFilter: false,
- enableSorting: false,
- datatype: "numeric",
- grow: false,
- size: 50,
- },
- {
- accessorKey: "gps",
- header: "GPS",
- id: "gps",
- enableColumnFilter: false,
- enableSorting: false,
- datatype: "numeric",
- grow: false,
- size: 50,
- },
- {
- accessorKey: "herasat",
- header: "ثبت حراست",
- id: "herasat",
- enableColumnFilter: false,
- enableSorting: false,
- datatype: "numeric",
- grow: false,
- size: 50,
- },
- {
- accessorKey: "mil_gps",
- header: "کیلومتر GPS",
- id: "mil_gps",
- enableColumnFilter: false,
- enableSorting: false,
- datatype: "numeric",
- grow: false,
- size: 50,
- },
- ];
- }, []);
-
- return (
-
-
-
- );
-};
-
-export default ViolationsReportsList;
+"use client";
+import DataTableWithAuth from "@/core/components/DataTableWithAuth";
+import { Box } from "@mui/material";
+import { useEffect, useMemo } from "react";
+import Toolbar from "./Toolbar";
+
+const ViolationsReportsList = ({ data, specialFilter }) => {
+ const columns = useMemo(() => {
+ return [
+ {
+ accessorKey: "edare_name",
+ header: "اداره کل",
+ id: "edare_name",
+ enableColumnFilter: false,
+ enableSorting: false,
+ datatype: "text",
+ grow: false,
+ size: 50,
+ },
+ {
+ accessorKey: "total",
+ header: "کل تخلفات",
+ id: "total",
+ enableColumnFilter: false,
+ enableSorting: false,
+ datatype: "numeric",
+ grow: false,
+ size: 50,
+ },
+ {
+ accessorKey: "done",
+ header: "اقدام شده",
+ id: "done",
+ enableColumnFilter: false,
+ enableSorting: false,
+ datatype: "numeric",
+ grow: false,
+ size: 50,
+ },
+ {
+ accessorKey: "undone",
+ header: "اقدام نشده",
+ id: "un_done",
+ enableColumnFilter: false,
+ enableSorting: false,
+ datatype: "numeric",
+ grow: false,
+ size: 50,
+ },
+ {
+ accessorKey: "gps",
+ header: "GPS",
+ id: "gps",
+ enableColumnFilter: false,
+ enableSorting: false,
+ datatype: "numeric",
+ grow: false,
+ size: 50,
+ },
+ {
+ accessorKey: "herasat",
+ header: "ثبت حراست",
+ id: "herasat",
+ enableColumnFilter: false,
+ enableSorting: false,
+ datatype: "numeric",
+ grow: false,
+ size: 50,
+ },
+ {
+ accessorKey: "mil_gps",
+ header: "کیلومتر GPS",
+ id: "mil_gps",
+ enableColumnFilter: false,
+ enableSorting: false,
+ datatype: "numeric",
+ grow: false,
+ size: 50,
+ },
+ ];
+ }, []);
+
+ return (
+
+
+
+ );
+};
+
+export default ViolationsReportsList;
diff --git a/src/components/dashboard/roadMissions/reports/violationsReports/index.jsx b/src/components/dashboard/roadMissions/reports/violationsReports/index.jsx
index 37d765f..d7f9a5a 100644
--- a/src/components/dashboard/roadMissions/reports/violationsReports/index.jsx
+++ b/src/components/dashboard/roadMissions/reports/violationsReports/index.jsx
@@ -1,141 +1,141 @@
-"use client";
-import PageTitle from "@/core/components/PageTitle";
-import {
- GET_MISSIONS_REPORT_VIOLATION_COUNTRY_ACTIVITY,
- GET_MISSIONS_REPORT_VIOLATION_PROVINCE_ACTIVITY,
-} from "@/core/utils/routes";
-import { useAuth } from "@/lib/contexts/auth";
-import { usePermissions } from "@/lib/hooks/usePermissions";
-import useRequest from "@/lib/hooks/useRequest";
-import { yupResolver } from "@hookform/resolvers/yup";
-import { LinearProgress, Stack } from "@mui/material";
-import moment from "jalali-moment";
-import { useEffect, useState } from "react";
-import { useForm } from "react-hook-form";
-import * as yup from "yup";
-import ViolationsReportsList from "./ViolationsReportsList";
-import SearchReportList from "./Search";
-
-const ViolationsReportsPage = () => {
- const [data, setData] = useState(null);
- const { data: userPermissions } = usePermissions();
- const { user } = useAuth();
- const requestServer = useRequest();
- const hasProvincesPermission = userPermissions?.includes("mission-report-country");
-
- const defaultValues = {
- from_date: moment(new Date()).format("YYYY-MM-DD"),
- date_to: moment(new Date()).format("YYYY-MM-DD"),
- province_id: user.province_id || "-1",
- };
- const defaultFilter = [
- { value: `${defaultValues.province_id}`, datatype: "numeric", id: "province_id", fn: "equals" },
- { value: `${defaultValues.from_date}`, datatype: "date", id: "from_date", fn: "between" },
- { value: `${defaultValues.date_to}`, datatype: "date", id: "date_to", fn: "between" },
- ];
- const [specialFilter, setSpecialFilter] = useState(defaultFilter);
-
- const onSearchSubmit = async (data) => {
- const params = new URLSearchParams();
- params.set("from_date", `${data.from_date}`);
- params.set("date_to", `${data.date_to}`);
- setSpecialFilter([
- { value: `${data.province_id}`, datatype: "numeric", id: "province_id", fn: "equals" },
- { value: `${data.from_date}`, datatype: "date", id: "from_date", fn: "between" },
- { value: `${data.date_to}`, datatype: "date", id: "date_to", fn: "between" },
- ]);
- if (data.province_id === "-1") {
- try {
- // params.set("province_id", `${data.province_id}`);
- const response = await requestServer(`${GET_MISSIONS_REPORT_VIOLATION_COUNTRY_ACTIVITY}?${params}`);
- const result = response.data.data;
- const nationalReportForItem = result.activities.find((report) => report.province_id === "-1");
- const nationalReport = {
- edare_name: "کل کشور",
- ...nationalReportForItem,
- };
-
- setData({
- data: [
- nationalReport,
- ...result.provinces.map((province) => {
- const filteredReports = result.activities.find(
- (report) => report.province_id == province.id
- );
- return {
- ...filteredReports,
- edare_name: province.name_fa,
- };
- }),
- ],
- filters: data,
- });
- } catch (e) {}
- } else {
- try {
- params.set("province_id", `${data.province_id}`);
- const response = await requestServer(`${GET_MISSIONS_REPORT_VIOLATION_PROVINCE_ACTIVITY}?${params}`);
- const result = response.data.data;
-
- const nationalReportForItem = result.activities.find((report) => report.city_id === "-1");
-
- const nationalReport = {
- ...nationalReportForItem,
- edare_name: "کل استان",
- };
- setData({
- data: [
- nationalReport,
- ...result.city.map((city) => {
- const filteredReports = result.activities.find((report) => report.city_id == city.id);
- return {
- edare_name: city.name_fa,
- ...filteredReports,
- };
- }),
- ],
- filters: data,
- });
- } catch (e) {}
- }
- };
-
- useEffect(() => {
- if (!userPermissions) return;
- onSearchSubmit(defaultValues);
- }, [userPermissions]);
-
- const validationSchema = yup.object().shape({
- from_date: yup
- .string()
- .required("تاریخ شروع الزامی است!")
- .matches(/^\d{4}-\d{2}-\d{2}$/, "تاریخ شروع الزامی است!")
- .test("is-valid-date", "تاریخ شروع معتبر نیست!", (value) => moment(value, "YYYY-MM-DD", true).isValid()),
- date_to: yup
- .string()
- .required("تاریخ پایان الزامی است!")
- .matches(/^\d{4}-\d{2}-\d{2}$/, "تاریخ پایان الزامی است!")
- .test("is-valid-date", "تاریخ پایان معتبر نیست!", (value) => moment(value, "YYYY-MM-DD", true).isValid()),
- province_id: yup.string().nullable(),
- });
-
- const { control, handleSubmit } = useForm({
- defaultValues,
- resolver: yupResolver(validationSchema),
- mode: "all",
- });
-
- return (
-
-
-
- {!data ? : }
-
- );
-};
-export default ViolationsReportsPage;
+"use client";
+import PageTitle from "@/core/components/PageTitle";
+import {
+ GET_MISSIONS_REPORT_VIOLATION_COUNTRY_ACTIVITY,
+ GET_MISSIONS_REPORT_VIOLATION_PROVINCE_ACTIVITY,
+} from "@/core/utils/routes";
+import { useAuth } from "@/lib/contexts/auth";
+import { usePermissions } from "@/lib/hooks/usePermissions";
+import useRequest from "@/lib/hooks/useRequest";
+import { yupResolver } from "@hookform/resolvers/yup";
+import { LinearProgress, Stack } from "@mui/material";
+import moment from "jalali-moment";
+import { useEffect, useState } from "react";
+import { useForm } from "react-hook-form";
+import * as yup from "yup";
+import ViolationsReportsList from "./ViolationsReportsList";
+import SearchReportList from "./Search";
+
+const ViolationsReportsPage = () => {
+ const [data, setData] = useState(null);
+ const { data: userPermissions } = usePermissions();
+ const { user } = useAuth();
+ const requestServer = useRequest();
+ const hasProvincesPermission = userPermissions?.includes("mission-report-country");
+
+ const defaultValues = {
+ from_date: moment(new Date()).format("YYYY-MM-DD"),
+ date_to: moment(new Date()).format("YYYY-MM-DD"),
+ province_id: user.province_id || "-1",
+ };
+ const defaultFilter = [
+ { value: `${defaultValues.province_id}`, datatype: "numeric", id: "province_id", fn: "equals" },
+ { value: `${defaultValues.from_date}`, datatype: "date", id: "from_date", fn: "between" },
+ { value: `${defaultValues.date_to}`, datatype: "date", id: "date_to", fn: "between" },
+ ];
+ const [specialFilter, setSpecialFilter] = useState(defaultFilter);
+
+ const onSearchSubmit = async (data) => {
+ const params = new URLSearchParams();
+ params.set("from_date", `${data.from_date}`);
+ params.set("date_to", `${data.date_to}`);
+ setSpecialFilter([
+ { value: `${data.province_id}`, datatype: "numeric", id: "province_id", fn: "equals" },
+ { value: `${data.from_date}`, datatype: "date", id: "from_date", fn: "between" },
+ { value: `${data.date_to}`, datatype: "date", id: "date_to", fn: "between" },
+ ]);
+ if (data.province_id === "-1") {
+ try {
+ // params.set("province_id", `${data.province_id}`);
+ const response = await requestServer(`${GET_MISSIONS_REPORT_VIOLATION_COUNTRY_ACTIVITY}?${params}`);
+ const result = response.data.data;
+ const nationalReportForItem = result.activities.find((report) => report.province_id === "-1");
+ const nationalReport = {
+ edare_name: "کل کشور",
+ ...nationalReportForItem,
+ };
+
+ setData({
+ data: [
+ nationalReport,
+ ...result.provinces.map((province) => {
+ const filteredReports = result.activities.find(
+ (report) => report.province_id == province.id
+ );
+ return {
+ ...filteredReports,
+ edare_name: province.name_fa,
+ };
+ }),
+ ],
+ filters: data,
+ });
+ } catch (e) {}
+ } else {
+ try {
+ params.set("province_id", `${data.province_id}`);
+ const response = await requestServer(`${GET_MISSIONS_REPORT_VIOLATION_PROVINCE_ACTIVITY}?${params}`);
+ const result = response.data.data;
+
+ const nationalReportForItem = result.activities.find((report) => report.city_id === "-1");
+
+ const nationalReport = {
+ ...nationalReportForItem,
+ edare_name: "کل استان",
+ };
+ setData({
+ data: [
+ nationalReport,
+ ...result.city.map((city) => {
+ const filteredReports = result.activities.find((report) => report.city_id == city.id);
+ return {
+ edare_name: city.name_fa,
+ ...filteredReports,
+ };
+ }),
+ ],
+ filters: data,
+ });
+ } catch (e) {}
+ }
+ };
+
+ useEffect(() => {
+ if (!userPermissions) return;
+ onSearchSubmit(defaultValues);
+ }, [userPermissions]);
+
+ const validationSchema = yup.object().shape({
+ from_date: yup
+ .string()
+ .required("تاریخ شروع الزامی است!")
+ .matches(/^\d{4}-\d{2}-\d{2}$/, "تاریخ شروع الزامی است!")
+ .test("is-valid-date", "تاریخ شروع معتبر نیست!", (value) => moment(value, "YYYY-MM-DD", true).isValid()),
+ date_to: yup
+ .string()
+ .required("تاریخ پایان الزامی است!")
+ .matches(/^\d{4}-\d{2}-\d{2}$/, "تاریخ پایان الزامی است!")
+ .test("is-valid-date", "تاریخ پایان معتبر نیست!", (value) => moment(value, "YYYY-MM-DD", true).isValid()),
+ province_id: yup.string().nullable(),
+ });
+
+ const { control, handleSubmit } = useForm({
+ defaultValues,
+ resolver: yupResolver(validationSchema),
+ mode: "all",
+ });
+
+ return (
+
+
+
+ {!data ? : }
+
+ );
+};
+export default ViolationsReportsPage;
diff --git a/src/components/dashboard/roadMissions/transportation/RowActions/Allocation/Form/index.jsx b/src/components/dashboard/roadMissions/transportation/RowActions/Allocation/Form/index.jsx
index c35018d..be001cc 100644
--- a/src/components/dashboard/roadMissions/transportation/RowActions/Allocation/Form/index.jsx
+++ b/src/components/dashboard/roadMissions/transportation/RowActions/Allocation/Form/index.jsx
@@ -14,16 +14,10 @@ import MachinesDialog from "./MachinesDialog";
import Reject from "./Reject";
import SelectBox from "@/core/components/SelectBox";
import { Controller, useForm } from "react-hook-form";
+import RouteDetail from "../../RouteDetail";
+import ShowRoute from "../../RouteDetail/ShowRoute";
const AllocationForm = ({ row, mutate, setOpenAllocationDialog }) => {
- const latLngs = useMemo(() => {
- return row.area.coordinates.map(([lng, lat]) => [lat, lng]);
- }, [row.area.coordinates]);
-
- const bound = useMemo(
- () => (row.area.type === "polygon" ? L.polygon(latLngs) : L.polyline(latLngs)),
- [row.area.type, latLngs]
- );
const requestServer = useRequest({ notificationSuccess: true });
const [machine, setMachine] = useState();
const [driver, setDriver] = useState();
@@ -180,10 +174,32 @@ const AllocationForm = ({ row, mutate, setOpenAllocationDialog }) => {
)}
-
-
-
-
+
+ {row.encoded_route ? (
+
+
+
+ ) : (
+
+ مختصات برای این ماموریت در دسترس نمیباشد
+
+ )}
diff --git a/src/components/dashboard/roadMissions/transportation/RowActions/RouteDetail/ShowRoute.jsx b/src/components/dashboard/roadMissions/transportation/RowActions/RouteDetail/ShowRoute.jsx
new file mode 100644
index 0000000..7d47ba8
--- /dev/null
+++ b/src/components/dashboard/roadMissions/transportation/RowActions/RouteDetail/ShowRoute.jsx
@@ -0,0 +1,46 @@
+import { useEffect, useRef } from "react";
+import { Marker, Polyline, useMap } from "react-leaflet";
+import L from "leaflet";
+import polyline from "@mapbox/polyline";
+import SourceIcon from "@/assets/images/source-icon.svg";
+import DestIcon from "@/assets/images/destination-icon.svg";
+
+const ShowRoute = ({ area }) => {
+ const map = useMap();
+ const defaultIconSize = [35, 35];
+
+ const createCustomIcon = (size, iconUrl) => {
+ return L.icon({
+ iconUrl: iconUrl,
+ iconSize: size,
+ iconAnchor: [size[0] / 2, size[1]],
+ popupAnchor: [0, -size[1]],
+ });
+ };
+
+ useEffect(() => {
+ if (coords.length > 0) {
+ map.flyToBounds(coords, { padding: [50, 50], duration: 1.5 });
+ }
+ }, [area]);
+ const sourceIcon = createCustomIcon(defaultIconSize, SourceIcon.src);
+ const destIcon = createCustomIcon(defaultIconSize, DestIcon.src);
+
+ if (!area) return null;
+ const coords = polyline.decode(area, 6);
+
+ if (coords.length === 0) return null;
+
+ const sourcePosition = coords[0];
+ const destPosition = coords[coords.length - 1];
+
+ return (
+ <>
+
+
+
+ >
+ );
+};
+
+export default ShowRoute;
diff --git a/src/components/dashboard/roadMissions/transportation/RowActions/RouteDetail/index.jsx b/src/components/dashboard/roadMissions/transportation/RowActions/RouteDetail/index.jsx
new file mode 100644
index 0000000..48b84a2
--- /dev/null
+++ b/src/components/dashboard/roadMissions/transportation/RowActions/RouteDetail/index.jsx
@@ -0,0 +1,57 @@
+import { Box, Button, Dialog, DialogActions, DialogTitle, IconButton, Tooltip } from "@mui/material";
+import { useState } from "react";
+import MapIcon from "@mui/icons-material/Map";
+import MapLayer from "@/core/components/MapLayer";
+import ShowRoute from "./ShowRoute";
+
+const RouteDetail = ({ encoded_route }) => {
+ const [openRouteDetailDialog, setOpenRouteDetailDialog] = useState(false);
+ return (
+ <>
+
+ setOpenRouteDetailDialog(true)}>
+
+
+
+
+ >
+ );
+};
+export default RouteDetail;
diff --git a/src/components/dashboard/roadMissions/transportation/TransportationList.jsx b/src/components/dashboard/roadMissions/transportation/TransportationList.jsx
index ab735d2..51872c9 100644
--- a/src/components/dashboard/roadMissions/transportation/TransportationList.jsx
+++ b/src/components/dashboard/roadMissions/transportation/TransportationList.jsx
@@ -4,13 +4,13 @@ import { GET_ROAD_MISSIONS_TRANSPORTATION_LIST } from "@/core/utils/routes";
import { Box, Stack } from "@mui/material";
import moment from "jalali-moment";
import { useMemo } from "react";
-import ShowArea from "./Actions/showArea";
import RowActions from "./RowActions";
import { missionRegions } from "@/core/utils/missionRegions";
import { missionTypes } from "@/core/utils/missionTypes";
import useProvinces from "@/lib/hooks/useProvince";
import CustomSelectByDependency from "@/core/components/DataTable/filter/fieldsType/CustomSelectByDependency";
import useCities from "@/lib/hooks/useCities";
+import RouteDetail from "./RowActions/RouteDetail";
const TransportationList = () => {
const columns = useMemo(() => {
@@ -230,9 +230,9 @@ const TransportationList = () => {
},
},
Cell: ({ row }) =>
- row.original.area ? (
+ row.original.encoded_route ? (
-
+
) : (
"-"
diff --git a/src/components/dashboard/roadMissions/violations/Actions/CreateWithoutProcess/Form/Area/MapControlPolygon/DrawBound/index.jsx b/src/components/dashboard/roadMissions/violations/Actions/CreateWithoutProcess/Form/Area/MapControlPolygon/DrawBound/index.jsx
deleted file mode 100644
index 5a253a9..0000000
--- a/src/components/dashboard/roadMissions/violations/Actions/CreateWithoutProcess/Form/Area/MapControlPolygon/DrawBound/index.jsx
+++ /dev/null
@@ -1,129 +0,0 @@
-import React, { useCallback, useEffect, useRef, useState } from "react";
-import L from "leaflet";
-import { FeatureGroup, useMap } from "react-leaflet";
-import "leaflet-draw";
-
-const DrawBound = ({ control, controlDispach, bound, setBound }) => {
- const map = useMap();
- const featureRef = useRef(null);
- const [area, setArea] = useState();
- const drawControlRef = useRef(null);
-
- const safeFlyToBounds = (layer) => {
- if (!layer || !map) return;
- try {
- const latLngs = layer.getLatLngs();
- map.flyToBounds(latLngs, {
- paddingTopLeft: [20, 35],
- paddingBottomRight: [20, 55],
- });
- } catch (e) {
- console.warn("flyToBounds failed:", e);
- }
- };
-
- const safeFitBounds = (layer) => {
- if (!layer || !map) return;
- try {
- const latLngs = layer.getLatLngs();
- map.fitBounds(latLngs, {
- paddingTopLeft: [20, 35],
- paddingBottomRight: [20, 55],
- });
- } catch (e) {
- console.warn("fitBounds failed:", e);
- }
- };
-
- const bindEditEvent = (layer) => {
- if (!layer) return;
- layer.on("edit", function (e) {
- const editedLayer = e.target;
- setArea(editedLayer);
- setBound(editedLayer);
- safeFlyToBounds(editedLayer);
- });
- };
-
- const handlerCreatedBound = useCallback((event) => {
- const { layer } = event;
- layer.editing.enable();
- featureRef.current.addLayer(layer); // 🟢 اول اضافه به نقشه
- setArea(layer);
- setBound(layer);
- safeFlyToBounds(layer);
- bindEditEvent(layer);
- controlDispach({ type: "SET_STATUS", status: 2 });
- }, []);
-
- useEffect(() => {
- if (
- control.status === 1 &&
- drawControlRef.current &&
- drawControlRef.current._toolbars?.draw?._modes?.polygon?.handler
- ) {
- drawControlRef.current._toolbars.draw._modes.polygon.handler.enable();
- }
- }, [control.status]);
-
- useEffect(() => {
- if (control.status === 0 && area && featureRef.current) {
- featureRef.current.removeLayer(area);
- setArea(null);
- setBound(null);
- }
- }, [control.status, area]);
-
- useEffect(() => {
- if (control.status === 2 && bound && featureRef.current) {
- setArea(bound);
- featureRef.current.addLayer(bound);
- bound.editing.enable();
- safeFitBounds(bound);
- bindEditEvent(bound);
- }
- }, [control.status, bound]);
-
- useEffect(() => {
- if (!featureRef.current) return;
-
- L.drawLocal.draw.handlers.polygon.tooltip.start = "برای شروع ترسیم محدوده، روی نقشه کلیک کنید";
- L.drawLocal.draw.handlers.polygon.tooltip.cont = "برای ادامه ترسیم، نقاط بعدی را کلیک کنید";
- L.drawLocal.draw.handlers.polygon.tooltip.end = "برای بستن محدوده، روی نقطهی شروع کلیک کنید";
-
- const drawControl = new L.Control.Draw({
- draw: {
- polygon: {
- shapeOptions: {
- color: "#3388ff",
- weight: 3,
- clickable: true,
- },
- },
- polyline: false,
- rectangle: false,
- circle: false,
- marker: false,
- circlemarker: false,
- },
- edit: {
- featureGroup: featureRef.current,
- edit: true,
- remove: false,
- },
- });
-
- drawControlRef.current = drawControl;
- map.addControl(drawControl);
- map.on(L.Draw.Event.CREATED, handlerCreatedBound);
-
- return () => {
- map.off(L.Draw.Event.CREATED, handlerCreatedBound);
- map.removeControl(drawControl);
- };
- }, [map, handlerCreatedBound]);
-
- return ;
-};
-
-export default DrawBound;
diff --git a/src/components/dashboard/roadMissions/violations/Actions/CreateWithoutProcess/Form/Area/MapControlPolygon/index.jsx b/src/components/dashboard/roadMissions/violations/Actions/CreateWithoutProcess/Form/Area/MapControlPolygon/index.jsx
deleted file mode 100644
index 718600c..0000000
--- a/src/components/dashboard/roadMissions/violations/Actions/CreateWithoutProcess/Form/Area/MapControlPolygon/index.jsx
+++ /dev/null
@@ -1,116 +0,0 @@
-import { Delete, Route } from "@mui/icons-material";
-import { Box, Button, Stack, Typography } from "@mui/material";
-import { useReducer } from "react";
-import DrawBound from "./DrawBound";
-
-const statusType = [
- {
- id: 0,
- message: "برای آغاز ترسیم محدوده، کلیک کنید!",
- buttons: [
- {
- label: "شروع ترسیم محدوده",
- key: "start",
- color: "primary",
- icon: ,
- onclick: (controlDispach) => {
- controlDispach({ type: "SET_STATUS", status: 1 });
- },
- },
- ],
- },
- {
- id: 1,
- message: "محدودهی موردنظر را با کلیک روی نقشه مشخص کنید. برای اتمام، روی نقطهی ابتدایی کلیک کنید!",
- buttons: [],
- },
- {
- id: 2,
- message: "برای اصلاح محدوده، گوشهها را بکشید. برای ترسیم دوباره، آن را حذف کنید",
- buttons: [
- {
- label: "حذف",
- key: "end",
- color: "error",
- icon: ,
- onclick: (controlDispach) => {
- controlDispach({ type: "SET_STATUS", status: 0 });
- },
- },
- ],
- },
-];
-
-const createInitialState = (bound) => {
- if (bound) {
- return { status: 2 };
- }
- return { status: 0 };
-};
-
-const reducer = (state, action) => {
- switch (action.type) {
- case "SET_STATUS":
- return { ...state, status: action.status };
- default:
- return state;
- }
-};
-
-const MapControlPolygon = ({ bound, setBound }) => {
- const [control, controlDispach] = useReducer(reducer, bound, createInitialState);
-
- return (
- <>
-
-
- theme.palette.info.main,
- borderBottomLeftRadius: 8,
- borderBottomRightRadius: 8,
- py: 0.5,
- px: 2,
- }}
- >
-
- {statusType.find((st) => st.id == control.status).message}
-
-
-
-
-
- {statusType
- .find((st) => st.id == control.status)
- .buttons.map((button) => (
- button.onclick(controlDispach)}
- color={button.color}
- startIcon={button.icon}
- >
- {button.label}
-
- ))}
-
-
- >
- );
-};
-export default MapControlPolygon;
diff --git a/src/components/dashboard/roadMissions/violations/Actions/CreateWithoutProcess/Form/Area/MapControlPolyline/DrawBound/index.jsx b/src/components/dashboard/roadMissions/violations/Actions/CreateWithoutProcess/Form/Area/MapControlPolyline/DrawBound/index.jsx
deleted file mode 100644
index 5456d12..0000000
--- a/src/components/dashboard/roadMissions/violations/Actions/CreateWithoutProcess/Form/Area/MapControlPolyline/DrawBound/index.jsx
+++ /dev/null
@@ -1,129 +0,0 @@
-import React, { useCallback, useEffect, useRef, useState } from "react";
-import L from "leaflet";
-import { FeatureGroup, useMap } from "react-leaflet";
-import "leaflet-draw";
-
-const DrawBound = ({ control, controlDispach, bound, setBound }) => {
- const map = useMap();
- const featureRef = useRef(null);
- const [area, setArea] = useState();
- const drawControlRef = useRef(null);
-
- const safeFlyToBounds = (layer) => {
- if (!layer || !map) return;
- try {
- const latLngs = layer.getLatLngs();
- map.flyToBounds(latLngs, {
- paddingTopLeft: [20, 35],
- paddingBottomRight: [20, 55],
- });
- } catch (e) {
- console.warn("flyToBounds failed:", e);
- }
- };
-
- const safeFitBounds = (layer) => {
- if (!layer || !map) return;
- try {
- const latLngs = layer.getLatLngs();
- map.fitBounds(latLngs, {
- paddingTopLeft: [20, 35],
- paddingBottomRight: [20, 55],
- });
- } catch (e) {
- console.warn("fitBounds failed:", e);
- }
- };
-
- const bindEditEvent = (layer) => {
- if (!layer) return;
- layer.on("edit", function (e) {
- const editedLayer = e.target;
- setArea(editedLayer);
- setBound(editedLayer);
- safeFlyToBounds(editedLayer);
- });
- };
-
- const handlerCreatedBound = useCallback((event) => {
- const { layer } = event;
- layer.editing.enable();
- featureRef.current.addLayer(layer); // 🟢 اول اضافه به نقشه
- setArea(layer);
- setBound(layer);
- safeFlyToBounds(layer);
- bindEditEvent(layer);
- controlDispach({ type: "SET_STATUS", status: 2 });
- }, []);
-
- useEffect(() => {
- if (
- control.status === 1 &&
- drawControlRef.current &&
- drawControlRef.current._toolbars?.draw?._modes?.polyline?.handler
- ) {
- drawControlRef.current._toolbars.draw._modes.polyline.handler.enable();
- }
- }, [control.status]);
-
- useEffect(() => {
- if (control.status === 0 && area && featureRef.current) {
- featureRef.current.removeLayer(area);
- setArea(null);
- setBound(null);
- }
- }, [control.status, area]);
-
- useEffect(() => {
- if (control.status === 2 && bound && featureRef.current) {
- setArea(bound);
- featureRef.current.addLayer(bound);
- bound.editing.enable();
- safeFitBounds(bound);
- bindEditEvent(bound);
- }
- }, [control.status, bound]);
-
- useEffect(() => {
- if (!featureRef.current) return;
-
- L.drawLocal.draw.handlers.polyline.tooltip.start = "برای شروع ترسیم مسیر، روی نقشه کلیک کنید";
- L.drawLocal.draw.handlers.polyline.tooltip.cont = "برای ادامه ترسیم، نقاط بعدی را کلیک کنید";
- L.drawLocal.draw.handlers.polyline.tooltip.end = "برای بستن مسیر، روی نقطه پایانی کلیک کنید";
-
- const drawControl = new L.Control.Draw({
- draw: {
- polyline: {
- shapeOptions: {
- color: "#3388ff",
- weight: 3,
- clickable: true,
- },
- },
- polygon: false,
- rectangle: false,
- circle: false,
- marker: false,
- circlemarker: false,
- },
- edit: {
- featureGroup: featureRef.current,
- edit: true,
- remove: false,
- },
- });
-
- drawControlRef.current = drawControl;
- map.addControl(drawControl);
- map.on(L.Draw.Event.CREATED, handlerCreatedBound);
-
- return () => {
- map.off(L.Draw.Event.CREATED, handlerCreatedBound);
- map.removeControl(drawControl);
- };
- }, [map, handlerCreatedBound]);
-
- return ;
-};
-
-export default DrawBound;
diff --git a/src/components/dashboard/roadMissions/violations/Actions/CreateWithoutProcess/Form/Area/MapControlPolyline/index.jsx b/src/components/dashboard/roadMissions/violations/Actions/CreateWithoutProcess/Form/Area/MapControlPolyline/index.jsx
deleted file mode 100644
index 8d9b5d0..0000000
--- a/src/components/dashboard/roadMissions/violations/Actions/CreateWithoutProcess/Form/Area/MapControlPolyline/index.jsx
+++ /dev/null
@@ -1,116 +0,0 @@
-import { Delete, Route } from "@mui/icons-material";
-import { Box, Button, Stack, Typography } from "@mui/material";
-import { useReducer } from "react";
-import DrawBound from "./DrawBound";
-
-const statusType = [
- {
- id: 0,
- message: "برای آغاز ترسیم مسیر، کلیک کنید!",
- buttons: [
- {
- label: "شروع ترسیم مسیر",
- key: "start",
- color: "primary",
- icon: ,
- onclick: (controlDispach) => {
- controlDispach({ type: "SET_STATUS", status: 1 });
- },
- },
- ],
- },
- {
- id: 1,
- message: "مسیر موردنظر را با کلیک روی نقشه مشخص کنید. برای اتمام، روی نقطهی پایانی کلیک کنید!",
- buttons: [],
- },
- {
- id: 2,
- message: "برای اصلاح مسیر، گوشهها را بکشید. برای ترسیم دوباره، آن را حذف کنید",
- buttons: [
- {
- label: "حذف",
- key: "end",
- color: "error",
- icon: ,
- onclick: (controlDispach) => {
- controlDispach({ type: "SET_STATUS", status: 0 });
- },
- },
- ],
- },
-];
-
-const createInitialState = (bound) => {
- if (bound) {
- return { status: 2 };
- }
- return { status: 0 };
-};
-
-const reducer = (state, action) => {
- switch (action.type) {
- case "SET_STATUS":
- return { ...state, status: action.status };
- default:
- return state;
- }
-};
-
-const MapControlPolyline = ({ bound, setBound }) => {
- const [control, controlDispach] = useReducer(reducer, bound, createInitialState);
-
- return (
- <>
-
-
- theme.palette.info.main,
- borderBottomLeftRadius: 8,
- borderBottomRightRadius: 8,
- py: 0.5,
- px: 2,
- }}
- >
-
- {statusType.find((st) => st.id == control.status).message}
-
-
-
-
-
- {statusType
- .find((st) => st.id == control.status)
- .buttons.map((button) => (
- button.onclick(controlDispach)}
- color={button.color}
- startIcon={button.icon}
- >
- {button.label}
-
- ))}
-
-
- >
- );
-};
-export default MapControlPolyline;
diff --git a/src/components/dashboard/roadMissions/violations/Actions/CreateWithoutProcess/Form/Area/Routing/AlarmText.jsx b/src/components/dashboard/roadMissions/violations/Actions/CreateWithoutProcess/Form/Area/Routing/AlarmText.jsx
new file mode 100644
index 0000000..0ddf8a0
--- /dev/null
+++ b/src/components/dashboard/roadMissions/violations/Actions/CreateWithoutProcess/Form/Area/Routing/AlarmText.jsx
@@ -0,0 +1,28 @@
+import { Box, Typography } from "@mui/material";
+
+const AlarmText = ({ isSource }) => {
+ if (isSource) return null;
+ return (
+
+
+ با کلیک بر روی مارکر موقعیت را انتخاب کنید
+
+
+ );
+};
+
+export default AlarmText;
diff --git a/src/components/dashboard/roadMissions/violations/Actions/CreateWithoutProcess/Form/Area/Routing/DataBox.jsx b/src/components/dashboard/roadMissions/violations/Actions/CreateWithoutProcess/Form/Area/Routing/DataBox.jsx
new file mode 100644
index 0000000..5768f75
--- /dev/null
+++ b/src/components/dashboard/roadMissions/violations/Actions/CreateWithoutProcess/Form/Area/Routing/DataBox.jsx
@@ -0,0 +1,53 @@
+import { Box, Button, TextField } from "@mui/material";
+
+const DataBox = ({ isSource, isDest, sourcePosition, destPosition, editingTarget, onEditSource, onEditDest }) => {
+ if (!isSource) return null;
+
+ return (
+
+
+
+ {isSource && isDest && (
+
+ ویرایش مبدا
+
+ )}
+
+ {isDest && (
+
+
+
+ ویرایش مقصد
+
+
+ )}
+
+ );
+};
+
+export default DataBox;
diff --git a/src/components/dashboard/roadMissions/violations/Actions/CreateWithoutProcess/Form/Area/Routing/ShowRoute.jsx b/src/components/dashboard/roadMissions/violations/Actions/CreateWithoutProcess/Form/Area/Routing/ShowRoute.jsx
new file mode 100644
index 0000000..6827cde
--- /dev/null
+++ b/src/components/dashboard/roadMissions/violations/Actions/CreateWithoutProcess/Form/Area/Routing/ShowRoute.jsx
@@ -0,0 +1,46 @@
+import { useEffect, useRef } from "react";
+import { Marker, Polyline, useMap } from "react-leaflet";
+import L from "leaflet";
+import polyline from "@mapbox/polyline";
+import SourceIcon from "@/assets/images/source-icon.svg";
+import DestIcon from "@/assets/images/destination-icon.svg";
+
+const ShowRoute = ({ area }) => {
+ const map = useMap();
+ const defaultIconSize = [35, 35];
+
+ const createCustomIcon = (size, iconUrl) => {
+ return L.icon({
+ iconUrl: iconUrl,
+ iconSize: size,
+ iconAnchor: [size[0] / 2, size[1]],
+ popupAnchor: [0, -size[1]],
+ });
+ };
+
+ const sourceIcon = createCustomIcon(defaultIconSize, SourceIcon.src);
+ const destIcon = createCustomIcon(defaultIconSize, DestIcon.src);
+ useEffect(() => {
+ if (coords.length > 0) {
+ map.flyToBounds(coords, { padding: [50, 50], duration: 1.5 });
+ }
+ }, [area]);
+
+ if (!area) return null;
+ const coords = polyline.decode(area, 6);
+
+ if (coords.length === 0) return null;
+
+ const sourcePosition = coords[0];
+ const destPosition = coords[coords.length - 1];
+
+ return (
+ <>
+
+
+
+ >
+ );
+};
+
+export default ShowRoute;
diff --git a/src/components/dashboard/roadMissions/violations/Actions/CreateWithoutProcess/Form/Area/Routing/index.jsx b/src/components/dashboard/roadMissions/violations/Actions/CreateWithoutProcess/Form/Area/Routing/index.jsx
new file mode 100644
index 0000000..49c4651
--- /dev/null
+++ b/src/components/dashboard/roadMissions/violations/Actions/CreateWithoutProcess/Form/Area/Routing/index.jsx
@@ -0,0 +1,219 @@
+import { Marker, Polyline, useMapEvents } from "react-leaflet";
+import L from "leaflet";
+import polyline from "@mapbox/polyline";
+import SourceIcon from "@/assets/images/source-icon.svg";
+import PrevSourceIcon from "@/assets/images/prev-source-icon.svg";
+import PrevDestIcon from "@/assets/images/prev-destination-icon.svg";
+import DestIcon from "@/assets/images/destination-icon.svg";
+import { useEffect, useRef, useState } from "react";
+import DataBox from "./DataBox";
+import AlarmText from "./AlarmText";
+
+const Routing = ({ setArea, setLocations, locations, area }) => {
+ const mapPrevSourceMarker = useRef();
+ const mapPrevDestMarker = useRef();
+ const mapRef = useRef(null);
+ const defaultIconSize = [35, 35];
+ const [isSource, setIsSource] = useState(false);
+ const [sourcePosition, setSourcePosition] = useState(null);
+ const [isDest, setIsDest] = useState(false);
+ const [destPosition, setDestPosition] = useState(null);
+ const [routeCoords, setRouteCoords] = useState([]);
+ const [alternateCoords, setAlternateCoords] = useState([]);
+ const [editingTarget, setEditingTarget] = useState(null);
+ const [selectedRoute, setSelectedRoute] = useState(null);
+
+ const createCustomIcon = (size, iconUrl) => {
+ return L.icon({
+ iconUrl: iconUrl,
+ iconSize: size,
+ iconAnchor: [size[0] / 2, size[1]],
+ popupAnchor: [0, -size[1]],
+ });
+ };
+
+ const defaultIcon = createCustomIcon(
+ defaultIconSize,
+ isSource && editingTarget !== "source" ? SourceIcon.src : PrevSourceIcon.src
+ );
+
+ const prevDestIcon = createCustomIcon(
+ defaultIconSize,
+ isDest && editingTarget !== "dest" ? DestIcon.src : PrevDestIcon.src
+ );
+
+ const isMovingSource = !isSource || editingTarget === "source";
+ const isMovingDest = isSource && (!isDest || editingTarget === "dest");
+
+ const map = useMapEvents({
+ move(e) {
+ if (isMovingSource) mapPrevSourceMarker.current.setLatLng(e.target.getCenter());
+ if (isMovingDest && mapPrevDestMarker.current) mapPrevDestMarker.current.setLatLng(e.target.getCenter());
+ },
+ movestart() {
+ if (isMovingSource) mapPrevSourceMarker.current.setIcon(createCustomIcon([45, 45], PrevSourceIcon.src));
+ if (isMovingDest && mapPrevDestMarker.current)
+ mapPrevDestMarker.current.setIcon(createCustomIcon([45, 45], PrevDestIcon.src));
+ },
+ moveend() {
+ if (isMovingSource)
+ mapPrevSourceMarker.current.setIcon(createCustomIcon(defaultIconSize, PrevSourceIcon.src));
+ if (isMovingDest && mapPrevDestMarker.current)
+ mapPrevDestMarker.current.setIcon(createCustomIcon(defaultIconSize, PrevDestIcon.src));
+ },
+ });
+
+ useEffect(() => {
+ mapRef.current = map;
+ }, [map]);
+
+ useEffect(() => {
+ if (locations.length < 2) return;
+ const restoredSource = { lat: locations[0].lat, lng: locations[0].lon };
+ const restoredDest = { lat: locations[1].lat, lng: locations[1].lon };
+ setSourcePosition(restoredSource);
+ setDestPosition(restoredDest);
+ setIsSource(true);
+ setIsDest(true);
+ if (area) {
+ const decoded = polyline.decode(area, 6);
+ setRouteCoords(decoded);
+ setSelectedRoute({ index: "main", coords: decoded });
+ setTimeout(() => {
+ mapRef.current?.flyToBounds(decoded, { padding: [50, 50], duration: 1.5 });
+ }, 100);
+ } else {
+ fetchRoute(restoredSource, restoredDest);
+ }
+ }, []);
+
+ const fetchRoute = async (source, dest) => {
+ const locs = [
+ { lat: source.lat, lon: source.lng },
+ { lat: dest.lat, lon: dest.lng },
+ ];
+ try {
+ const response = await fetch("https://jouya.141.ir/api/router/drive", {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ locations: locs }),
+ });
+ const data = await response.json();
+ const shape = data.trip.legs[0].shape;
+ setLocations(locs);
+ setArea(shape);
+ const decoded = polyline.decode(shape, 6);
+ setRouteCoords(decoded);
+ setSelectedRoute({ index: "main", coords: decoded });
+
+ if (data.alternates?.length > 0) {
+ const decodedAlternates = data.alternates.map((alt) => polyline.decode(alt.trip.legs[0].shape, 6));
+ setAlternateCoords(decodedAlternates);
+ } else {
+ setAlternateCoords([]);
+ }
+ map.flyToBounds(decoded, { padding: [50, 50], duration: 1.5 });
+ } catch (error) {
+ console.error("Routing error:", error);
+ }
+ };
+
+ const handleMarkerClick = async () => {
+ if (!isSource || editingTarget === "source") {
+ const clickedPosition = mapPrevSourceMarker.current.getLatLng();
+ setSourcePosition(clickedPosition);
+ setIsSource(true);
+ setEditingTarget(null);
+ setRouteCoords([]);
+ setAlternateCoords([]);
+ setSelectedRoute(null);
+
+ if (editingTarget === "source" && destPosition) {
+ await fetchRoute(clickedPosition, destPosition);
+ }
+ }
+ };
+
+ const handleDestMarkerClick = async () => {
+ if (!isDest || editingTarget === "dest") {
+ const clickedPosition = mapPrevDestMarker.current.getLatLng();
+ setDestPosition(clickedPosition);
+ setIsDest(true);
+ setEditingTarget(null);
+
+ await fetchRoute(sourcePosition, clickedPosition);
+ }
+ };
+
+ const handleEditSource = () => {
+ setEditingTarget("source");
+ setRouteCoords([]);
+ setAlternateCoords([]);
+ setSelectedRoute(null);
+ };
+
+ const handleEditDest = () => {
+ setEditingTarget("dest");
+ setRouteCoords([]);
+ setAlternateCoords([]);
+ setSelectedRoute(null);
+ };
+
+ const handleSelectRoute = (index, coords) => {
+ setSelectedRoute({ index, coords });
+ const encoded = polyline.encode(coords, 6);
+ setArea(encoded);
+ map.flyToBounds(coords, { padding: [50, 50], duration: 0.3 });
+ };
+
+ return (
+ <>
+
+
+
+ {isSource && (
+
+ )}
+ {alternateCoords.map((coords, index) => (
+ handleSelectRoute(index, coords) }}
+ />
+ ))}
+ {routeCoords.length > 0 && (
+ handleSelectRoute("main", routeCoords) }}
+ />
+ )}
+ >
+ );
+};
+
+export default Routing;
diff --git a/src/components/dashboard/roadMissions/violations/Actions/CreateWithoutProcess/Form/Area/SelectBoundType/index.jsx b/src/components/dashboard/roadMissions/violations/Actions/CreateWithoutProcess/Form/Area/SelectBoundType/index.jsx
deleted file mode 100644
index ebb3c69..0000000
--- a/src/components/dashboard/roadMissions/violations/Actions/CreateWithoutProcess/Form/Area/SelectBoundType/index.jsx
+++ /dev/null
@@ -1,25 +0,0 @@
-import SelectBox from "@/core/components/SelectBox";
-
-export const boundTypes = [
- { id: "polygon", name_fa: "محدوده" },
- { id: "polyline", name_fa: "مسیر" },
-];
-
-const SelectBoundType = ({ boundType, setBoundType, setBound, setAllData }) => {
- return (
- <>
- {
- setBound(null);
- setBoundType(newValue);
- setAllData({ bound_type: newValue });
- }}
- />
- >
- );
-};
-export default SelectBoundType;
diff --git a/src/components/dashboard/roadMissions/violations/Actions/CreateWithoutProcess/Form/Area/index.jsx b/src/components/dashboard/roadMissions/violations/Actions/CreateWithoutProcess/Form/Area/index.jsx
index 3d666fd..200c1e0 100644
--- a/src/components/dashboard/roadMissions/violations/Actions/CreateWithoutProcess/Form/Area/index.jsx
+++ b/src/components/dashboard/roadMissions/violations/Actions/CreateWithoutProcess/Form/Area/index.jsx
@@ -2,40 +2,29 @@ import MapLoading from "@/core/components/MapLayer/Loading";
import { Box, Button, DialogActions, DialogContent, Stack } from "@mui/material";
import dynamic from "next/dynamic";
import { useCallback, useState } from "react";
-import MapControlPolygon from "./MapControlPolygon";
-import MapControlPolyline from "./MapControlPolyline";
-import SelectBoundType from "./SelectBoundType";
+import Routing from "./Routing";
const MapLayer = dynamic(() => import("@/core/components/MapLayer"), {
loading: () => ,
ssr: false,
});
const Area = ({ allData, setAllData, handlePrev, setTabState }) => {
- const [bound, setBound] = useState(allData.bound);
- const [boundType, setBoundType] = useState(allData.bound_type);
+ const [area, setArea] = useState(allData.area);
+ const [locations, setLocations] = useState(allData.locations);
const handleNext = useCallback(() => {
- setAllData({ bound: bound });
+ setAllData({ area: area });
+ setAllData({ locations: locations });
setTabState((s) => s + 1);
- }, [bound]);
+ }, [area]);
return (
<>
-
- {boundType == "polygon" ? (
-
- ) : (
-
- )}
+
@@ -44,8 +33,8 @@ const Area = ({ allData, setAllData, handlePrev, setTabState }) => {
{"مرحله قبلی"}
-
- {!bound ? "در انتظار ترسیم" : "مرحله بعد"}
+
+ {!area ? "در انتظار ترسیم" : "مرحله بعد"}
>
diff --git a/src/components/dashboard/roadMissions/violations/Actions/CreateWithoutProcess/Form/Verify/index.jsx b/src/components/dashboard/roadMissions/violations/Actions/CreateWithoutProcess/Form/Verify/index.jsx
index 09f865f..68f99e7 100644
--- a/src/components/dashboard/roadMissions/violations/Actions/CreateWithoutProcess/Form/Verify/index.jsx
+++ b/src/components/dashboard/roadMissions/violations/Actions/CreateWithoutProcess/Form/Verify/index.jsx
@@ -5,7 +5,7 @@ import { Box, Button, Chip, DialogActions, DialogContent, Divider, Stack, Typogr
import moment from "jalali-moment";
import dynamic from "next/dynamic";
import { useCallback } from "react";
-import ShowBound from "../../../showBound";
+import ShowRoute from "../Area/Routing/ShowRoute";
const MapLayer = dynamic(() => import("@/core/components/MapLayer"), {
loading: () => ,
ssr: false,
@@ -22,6 +22,16 @@ const Verify = ({ allData, handlePrev, submitForm, submitting }) => {
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/dashboard/roadMissions/violations/Actions/CreateWithoutProcess/Form/index.jsx b/src/components/dashboard/roadMissions/violations/Actions/CreateWithoutProcess/Form/index.jsx
index 96de65d..0f9c7b2 100644
--- a/src/components/dashboard/roadMissions/violations/Actions/CreateWithoutProcess/Form/index.jsx
+++ b/src/components/dashboard/roadMissions/violations/Actions/CreateWithoutProcess/Form/index.jsx
@@ -1,4 +1,4 @@
-import { AccessTime, Engineering, InsertDriveFile, Person, Verified } from "@mui/icons-material";
+import { AccessTime, Engineering, InsertDriveFile, Person, Verified, Map } from "@mui/icons-material";
import { Box, Tab, Tabs } from "@mui/material";
import { useReducer, useState } from "react";
import GetDateTime from "./GetDateTime";
@@ -6,6 +6,7 @@ import GetItemInfo from "./GetItemInfo";
import MachineAndDriver from "./MachineAndDriver";
import Rahdaran from "./Rahdaran";
import Verify from "./Verify";
+import Area from "./Area";
function TabPanel(props) {
const { children, value, index } = props;
@@ -61,8 +62,9 @@ const CreateForm = ({ defaultValues, submitForm, setOpen, submitting }) => {
} label="مشخصات" />
} label="زمانبندی" />
} label="راننده" />
- } label="همراهان" />
- } label="بررسی نهایی" />
+ } label="مختصات" />
+ } label="همراهان" />
+ } label="بررسی نهایی" />
{
/>
- {
dispatch({ type: "changeData", data });
@@ -105,6 +107,16 @@ const CreateForm = ({ defaultValues, submitForm, setOpen, submitting }) => {
/>
+ {
+ dispatch({ type: "changeData", data });
+ }}
+ handlePrev={handlePrev}
+ setTabState={setTabState}
+ />
+
+
>
diff --git a/src/components/dashboard/roadMissions/violations/Actions/CreateWithoutProcess/index.jsx b/src/components/dashboard/roadMissions/violations/Actions/CreateWithoutProcess/index.jsx
index 0e1f42b..f2d3d09 100644
--- a/src/components/dashboard/roadMissions/violations/Actions/CreateWithoutProcess/index.jsx
+++ b/src/components/dashboard/roadMissions/violations/Actions/CreateWithoutProcess/index.jsx
@@ -28,6 +28,7 @@ const CreateWithoutProcess = ({ row, mutate }) => {
start_date: `${result.start_date} ${moment(result.start_time).format("HH:mm")}`,
end_date: `${result.end_date} ${moment(result.end_time).format("HH:mm")}`,
driver: result.driver.id,
+ encoded_route: result.area,
},
hasSidebarUpdate: true,
})
@@ -49,7 +50,7 @@ const CreateWithoutProcess = ({ row, mutate }) => {
-