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/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 3cf2a09..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 @@ -87,21 +87,21 @@ export default function Example() { height: "400px", }} > - + - {/* saved markers */} - {origin && } + {/* saved markers */} + {origin && } - {destination && } + {destination && } - {/* route line */} - {origin && destination && } + {/* route line */} + {origin && destination && } {/* fixed center pin */} {step !== "done" && } -
+
+
+ {isDest && ( +
+ + +
+ )} + + ); +}; + +export default DataBox; \ No newline at end of file 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 index f7e6f44..c27cb73 100644 --- 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 @@ -1,46 +1,202 @@ -import { Marker, useMapEvents } from "react-leaflet"; +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 { useRef } from "react"; - +import PrevDestIcon from "@/assets/images/prev-destination-icon.svg"; +import DestIcon from "@/assets/images/destination-icon.svg"; +import { useRef, useState } from "react"; +import DataBox from "./DataBox"; const Routing = () => { - const mapPrevSourceMarker = useRef(); - 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 mapPrevSourceMarker = useRef(); + const mapPrevDestMarker = useRef(); + 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 map = useMapEvents({ - move(e) { - mapPrevSourceMarker.current.setLatLng(e.target.getCenter()); - }, - movestart() { - mapPrevSourceMarker.current.setIcon(createCustomIcon([45, 45], PrevSourceIcon.src)); - }, - moveend() { - mapPrevSourceMarker.current.setIcon(createCustomIcon(defaultIconSize, PrevSourceIcon.src)); - }, - }); + const createCustomIcon = (size, iconUrl) => { + return L.icon({ + iconUrl: iconUrl, + iconSize: size, + iconAnchor: [size[0] / 2, size[1]], + popupAnchor: [0, -size[1]], + }); + }; - const handleMarkerClick = () => { - console.log(mapPrevSourceMarker.current.getLatLng().lat); - }; + const defaultIcon = createCustomIcon( + defaultIconSize, + isSource && editingTarget !== "source" ? SourceIcon.src : PrevSourceIcon.src + ); - return ( - - ) -} + const prevDestIcon = createCustomIcon( + defaultIconSize, + isDest && editingTarget !== "dest" ? DestIcon.src : PrevDestIcon.src + ); -export default Routing \ No newline at end of file + 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)); + } + }, + }); + + const fetchRoute = async (source, dest) => { + const locations = [ + { 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 }), + }); + const data = await response.json(); + + const shape = data.trip.legs[0].shape; + const decoded = polyline.decode(shape, 6); + setRouteCoords(decoded); + setSelectedRoute({ index: "main", coords: decoded }); + + if (data.alternates && 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(); + console.log("Source:", clickedPosition.lat, clickedPosition.lng); + 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 }); + map.flyToBounds(coords, { padding: [50, 50], duration: 0.3 }); + console.log("Selected route:", index, coords); + }; + + return ( + <> + + + {isSource && ( + + )} + {alternateCoords.map((coords, index) => ( + handleSelectRoute(index, coords) }} + /> + ))} + {routeCoords.length > 0 && ( + handleSelectRoute("main", routeCoords) }} + /> + )} + + ); +}; + +export default Routing; \ No newline at end of file diff --git a/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/Area/index.jsx b/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/Area/index.jsx index 38ec045..a985737 100644 --- a/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/Area/index.jsx +++ b/src/components/dashboard/roadMissions/operator/Actions/CreateWithoutProcess/Form/Area/index.jsx @@ -24,7 +24,7 @@ const Area = ({ allData, setAllData, handlePrev, setTabState }) => { - + diff --git a/src/components/dashboard/roadMissions/reports/taradodReports/Search/index.jsx b/src/components/dashboard/roadMissions/reports/taradodReports/Search/index.jsx index 2b0d128..f29d302 100644 --- a/src/components/dashboard/roadMissions/reports/taradodReports/Search/index.jsx +++ b/src/components/dashboard/roadMissions/reports/taradodReports/Search/index.jsx @@ -5,10 +5,7 @@ const SearchReportList = ({ handleSubmit, onSearchSubmit, control, hasProvincesP return ( <> - + ); diff --git a/src/components/dashboard/roadMissions/reports/violationsReports/Search/index.jsx b/src/components/dashboard/roadMissions/reports/violationsReports/Search/index.jsx index 2b0d128..f29d302 100644 --- a/src/components/dashboard/roadMissions/reports/violationsReports/Search/index.jsx +++ b/src/components/dashboard/roadMissions/reports/violationsReports/Search/index.jsx @@ -5,10 +5,7 @@ const SearchReportList = ({ handleSubmit, onSearchSubmit, control, hasProvincesP return ( <> - + ); diff --git a/src/core/utils/routes.js b/src/core/utils/routes.js index c1656f7..a2ea39f 100644 --- a/src/core/utils/routes.js +++ b/src/core/utils/routes.js @@ -47,10 +47,8 @@ export const GET_COUNTRY_ACTIVITY_REPORT = api + "/api/v3/missions/report/countr export const EXPORT_COUNTRY_ROAD_PATROL_REPORTS = api + "/api/v3/road_patrols/report/country_activity_excel"; export const EXPORT_PROVINCE_ROAD_PATROL_REPORTS = api + "/api/v3/road_patrols/report/province_activity_excel"; -export const GET_MISSIONS_REPORT_TARADOD_COUNTRY_ACTIVITY = - api + "/api/v3/missions/report_taradod/country_activity"; -export const GET_MISSIONS_REPORT_TARADOD_PROVINCE_ACTIVITY = - api + "/api/v3/missions/report_taradod/province_activity"; +export const GET_MISSIONS_REPORT_TARADOD_COUNTRY_ACTIVITY = api + "/api/v3/missions/report_taradod/country_activity"; +export const GET_MISSIONS_REPORT_TARADOD_PROVINCE_ACTIVITY = api + "/api/v3/missions/report_taradod/province_activity"; export const EXPORT_MISSIONS_REPORT_TARADOD_COUNTRY_EXCEL_ACTIVITY = api + "/api/v3/missions/report_taradod/country_excel_activity"; export const EXPORT_MISSIONS_REPORT_TARADOD_PROVINCE_EXCEL_ACTIVITY =