From 0f50bf1db8eb5b687f2fbb53f1a572cd8c9e6821 Mon Sep 17 00:00:00 2001 From: PxlLoewe <72106766+PxlLoewe@users.noreply.github.com> Date: Tue, 3 Jun 2025 15:50:20 -0700 Subject: [PATCH] improved OSM Element selection --- apps/dispatch-server/routes/mission.ts | 11 ++ .../app/_components/map/ContextMenu.tsx | 16 +- .../app/_components/map/MissionMarkers.tsx | 2 +- .../app/_components/map/SearchElements.tsx | 151 +++++------------- .../map/_components/MissionMarkerTabs.tsx | 7 +- apps/dispatch/app/_store/mapStore.ts | 20 ++- apps/dispatch/app/_store/pannelStore.ts | 4 +- .../_components/pannel/MissionForm.tsx | 60 ++++--- .../public/HPG_Solid_Transparent_180x.avif | Bin 0 -> 9015 bytes packages/database/prisma/json/OSMway.ts | 1 + 10 files changed, 118 insertions(+), 154 deletions(-) create mode 100644 apps/dispatch/public/HPG_Solid_Transparent_180x.avif diff --git a/apps/dispatch-server/routes/mission.ts b/apps/dispatch-server/routes/mission.ts index 52ce77c7..a52f0737 100644 --- a/apps/dispatch-server/routes/mission.ts +++ b/apps/dispatch-server/routes/mission.ts @@ -318,6 +318,17 @@ router.post("/:id/validate-hpg", async (req, res) => { }); return; } + + const newMission = await prisma.mission.update({ + where: { + id: Number(id), + }, + data: { + hpgValidationState: "PENDING", + }, + }); + io.to("dispatchers").emit("update-mission", newMission); + res.json({ message: "HPG validierung gestartet", }); diff --git a/apps/dispatch/app/_components/map/ContextMenu.tsx b/apps/dispatch/app/_components/map/ContextMenu.tsx index f4d34ee6..cedbb47d 100644 --- a/apps/dispatch/app/_components/map/ContextMenu.tsx +++ b/apps/dispatch/app/_components/map/ContextMenu.tsx @@ -11,7 +11,14 @@ import { Popup, useMap } from "react-leaflet"; export const ContextMenu = () => { const map = useMap(); - const { contextMenu, setContextMenu, setSearchElements, setSearchPopup } = useMapStore(); + const { + contextMenu, + searchElements, + setContextMenu, + setSearchElements, + setSearchPopup, + toggleSearchElementSelection, + } = useMapStore(); const { missionFormValues, setMissionFormValues, setOpen, isOpen } = usePannelStore( (state) => state, ); @@ -64,7 +71,9 @@ export const ContextMenu = () => { const parsed: OSMWay[] = data.elements .filter((e: any) => e.type === "way") .map((e: any) => { + const elementInMap = searchElements.find((el) => el.wayID === e.id); return { + isSelected: elementInMap?.isSelected ?? false, wayID: e.id, nodes: e.nodes.map((nodeId: string) => { const node = data.elements.find((element: any) => element.id === nodeId); @@ -125,12 +134,15 @@ export const ContextMenu = () => { nodeWay.push([node.lat, node.lon]), ); + if (closestToContext) { + toggleSearchElementSelection(closestToContext.wayID); + } + setMissionFormValues({ ...parsed, state: "draft", addressLat: contextMenu.lat, addressLng: contextMenu.lng, - addressOSMways: [closestToContext], }); map.setView([contextMenu.lat, contextMenu.lng], 18, { diff --git a/apps/dispatch/app/_components/map/MissionMarkers.tsx b/apps/dispatch/app/_components/map/MissionMarkers.tsx index b57e3977..ed42336a 100644 --- a/apps/dispatch/app/_components/map/MissionMarkers.tsx +++ b/apps/dispatch/app/_components/map/MissionMarkers.tsx @@ -175,7 +175,7 @@ const MissionPopupContent = ({ hpgLocationLat: mission.hpgLocationLat ?? undefined, hpgLocationLng: mission.hpgLocationLng ?? undefined, }); - setEditingMission(true, String(mission.id)); + setEditingMission(true, mission.id); setOpen(true); }} > diff --git a/apps/dispatch/app/_components/map/SearchElements.tsx b/apps/dispatch/app/_components/map/SearchElements.tsx index 916c281c..eec7f043 100644 --- a/apps/dispatch/app/_components/map/SearchElements.tsx +++ b/apps/dispatch/app/_components/map/SearchElements.tsx @@ -1,18 +1,15 @@ import { useMapStore } from "_store/mapStore"; -import { Marker as LMarker } from "leaflet"; -import { Fragment, useEffect, useRef } from "react"; import { Marker, Polygon, Popup } from "react-leaflet"; -import L from "leaflet"; import { useQuery } from "@tanstack/react-query"; import { getMissionsAPI } from "_querys/missions"; -import { OSMWay } from "@repo/db"; import { usePannelStore } from "_store/pannelStore"; +import { OSMWay } from "@repo/db"; +import { useEffect } from "react"; export const SearchElements = () => { - const { searchElements, searchPopup, setSearchPopup, setContextMenu, openMissionMarker } = - useMapStore(); - const { missionFormValues, setMissionFormValues } = usePannelStore((state) => state); - const missions = useQuery({ + const { searchElements, openMissionMarker, setSearchElements } = useMapStore(); + const { isOpen: pannelOpen, editingMissionId } = usePannelStore((state) => state); + const { data: missions } = useQuery({ queryKey: ["missions"], queryFn: () => getMissionsAPI({ @@ -26,131 +23,59 @@ export const SearchElements = () => { ], }), }); - const poppupRef = useRef(null); - const searchPopupElement = searchElements.find( - (element) => element.wayID === searchPopup?.elementId, - ); - const SearchElement = ({ - element, - isActive = false, - }: { - element: (typeof searchElements)[1]; - isActive?: boolean; - }) => { - const ref = useRef(null); - - useEffect(() => { - if (ref.current) { - ref.current.on("click", () => { - const center = ref.current?.getBounds().getCenter(); - if (center && searchPopup?.elementId !== element.wayID) { - setSearchPopup({ - lat: center.lat, - lng: center.lng, - elementId: element.wayID, - }); - } else { - setSearchPopup(null); - } - setContextMenu(null); - }); + useEffect(() => { + if (pannelOpen) { + const missionEdited = missions?.find((m) => m.id === editingMissionId); + console.log("Mission Edited", missionEdited, editingMissionId, missions); + if (missionEdited) { + const elements = missionEdited.addressOSMways.map((e) => ({ + ...(e as unknown as OSMWay), + isSelected: true, + })); + setSearchElements(elements); + } else { + setSearchElements([]); } - }, [element.wayID]); + } else { + const openMissions = openMissionMarker.map((m) => { + const mission = missions?.find((mi) => mi.id === m.id); + return mission; + }); + const elements = openMissions + .filter((m) => m?.addressOSMways) + .flatMap((m) => + m?.addressOSMways.map((e) => ({ + ...(e as unknown as OSMWay), + isSelected: true, + })), + ); + setSearchElements(elements.filter((e) => !!e)); + } + }, [openMissionMarker, pannelOpen, missions]); + const SearchElement = ({ element }: { element: OSMWay }) => { + const { toggleSearchElementSelection } = useMapStore(); if (!element.nodes) return null; return ( [node.lat, node.lon])} - color={searchPopup?.elementId === element.wayID || isActive ? "#ff4500" : "#46b7a3"} + color={element.isSelected ? "#ff4500" : "#46b7a3"} eventHandlers={{ click: () => { - const addressOSMways = missionFormValues?.addressOSMways || []; - - addressOSMways.push(JSON.parse(JSON.stringify(element))); - - setMissionFormValues({ - ...missionFormValues, - addressOSMways, - }); + if (!pannelOpen) return; + toggleSearchElementSelection(element.wayID); }, }} - ref={ref} /> ); }; - const SearchElementPopup = ({ element }: { element: (typeof searchElements)[1] }) => { - if (!searchPopup) return null; - return ( - -
-

- {element.tags?.building === "yes" ? "Gebäude" : element.tags?.building} - {!element.tags?.building && "unbekannt"} -

-

- {element.tags?.["addr:street"]} {element.tags?.["addr:housenumber"]} -

-

- {element.tags?.["addr:suburb"]} {element.tags?.["addr:postcode"]} -

-
- -
-
-
- ); - }; - return ( <> - {openMissionMarker.map(({ id }) => { - const mission = missions.data?.find((m) => m.id === id); - if (!mission) return null; - return ( - - {(mission.addressOSMways as (OSMWay | null)[]) - .filter((element): element is OSMWay => element !== null) - .map((element: OSMWay, i) => ( - - ))} - - ); - })} {searchElements.map((element, i) => { - if ( - missions.data?.some( - (mission) => - (mission.addressOSMways as (OSMWay | null)[]) - .filter((e): e is OSMWay => e !== null) - .some((e) => e.wayID === element.wayID) && - openMissionMarker.some((m) => m.id === mission.id), - ) - ) - return null; return ; })} - {searchPopup && ( - - {!searchPopupElement &&
} -
- )} - {searchPopupElement && } ); }; diff --git a/apps/dispatch/app/_components/map/_components/MissionMarkerTabs.tsx b/apps/dispatch/app/_components/map/_components/MissionMarkerTabs.tsx index 9d8185c7..c8299554 100644 --- a/apps/dispatch/app/_components/map/_components/MissionMarkerTabs.tsx +++ b/apps/dispatch/app/_components/map/_components/MissionMarkerTabs.tsx @@ -89,6 +89,7 @@ const Einsatzdetails = ({ }); }, }); + const dispatcherConnected = useDispatchConnectionStore((s) => s.status) === "connected"; const { setMissionFormValues, setOpen, setEditingMission } = usePannelStore((state) => state); const [ignoreHpg, setIgnoreHpg] = useState(false); @@ -191,11 +192,7 @@ const Einsatzdetails = ({
- {HPGValidationRequired( - mission.missionStationIds, - aircrafts, - mission.hpgMissionString, - ) && ( + {hpgNeedsAttention && (