From da9b957fcfcdac23a655c9fd3f7ba9f0c9fdb99b Mon Sep 17 00:00:00 2001 From: PxlLoewe <72106766+PxlLoewe@users.noreply.github.com> Date: Tue, 28 Oct 2025 01:52:55 +0100 Subject: [PATCH] =?UTF-8?q?XPlane=20objecte=20k=C3=B6nnen=20wegebt=20und?= =?UTF-8?q?=20per=20rechts-click=20gel=C3=B6scht=20werden?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_components/pannel/MissionForm.tsx | 29 ++++++- .../app/_components/map/ContextMenu.tsx | 1 - .../app/_components/map/MapAdditionals.tsx | 76 +++++++++++++++++-- 3 files changed, 95 insertions(+), 11 deletions(-) diff --git a/apps/dispatch/app/(app)/dispatch/_components/pannel/MissionForm.tsx b/apps/dispatch/app/(app)/dispatch/_components/pannel/MissionForm.tsx index 2c53cfa6..b5301041 100644 --- a/apps/dispatch/app/(app)/dispatch/_components/pannel/MissionForm.tsx +++ b/apps/dispatch/app/(app)/dispatch/_components/pannel/MissionForm.tsx @@ -117,7 +117,7 @@ export const MissionForm = () => { resolver: zodResolver(MissionOptionalDefaultsSchema), defaultValues: defaultFormValues, }); - const { missionFormValues, setOpen } = usePannelStore((state) => state); + const { missionFormValues, setOpen, setMissionFormValues } = usePannelStore((state) => state); const validationRequired = HPGValidationRequired( @@ -156,6 +156,21 @@ export const MissionForm = () => { } }, [missionFormValues, form, defaultFormValues]); + // Sync form state to store (avoid infinity loops by using watch) + useEffect(() => { + const subscription = form.watch((values) => { + // Only update store if values actually changed to prevent loops + const currentStoreValues = JSON.stringify(missionFormValues); + const newFormValues = JSON.stringify(values); + + if (currentStoreValues !== newFormValues) { + setMissionFormValues(values as MissionOptionalDefaults); + } + }); + + return () => subscription.unsubscribe(); + }, [form, setMissionFormValues, missionFormValues]); + const saveMission = async ( mission: MissionOptionalDefaults, { alertWhenValid = false, createNewMission = false } = {}, @@ -449,7 +464,11 @@ export const MissionForm = () => { setSearchElements([]); // Reset search elements setEditingMission(null); setContextMenu(null); - toast.success(`Einsatz ${newMission.publicId} erstellt`); + if (editingMissionId) { + toast.success(`Einsatz ${newMission.publicId} bearbeitet`); + } else { + toast.success(`Einsatz ${newMission.publicId} erstellt`); + } form.reset(); setOpen(false); } catch (error) { @@ -474,7 +493,11 @@ export const MissionForm = () => { setSearchElements([]); // Reset search elements setContextMenu(null); - toast.success(`Einsatz ${newMission.publicId} erstellt`); + if (editingMissionId) { + toast.success(`Einsatz ${newMission.publicId} bearbeitet`); + } else { + toast.success(`Einsatz ${newMission.publicId} erstellt`); + } form.reset(); setOpen(false); } catch (error) { diff --git a/apps/dispatch/app/_components/map/ContextMenu.tsx b/apps/dispatch/app/_components/map/ContextMenu.tsx index 874ea0fd..b5a1d132 100644 --- a/apps/dispatch/app/_components/map/ContextMenu.tsx +++ b/apps/dispatch/app/_components/map/ContextMenu.tsx @@ -201,7 +201,6 @@ export const ContextMenu = () => { className="btn btn-circle bg-rescuetrack tooltip tooltip-left tooltip-accent mb-2 ml-[30px] h-10 w-10 opacity-80" data-tip="Rettungswagen platzieren" onClick={() => { - console.log("Add Ambulance"); setMissionFormValues({ ...missionFormValues, xPlaneObjects: [ diff --git a/apps/dispatch/app/_components/map/MapAdditionals.tsx b/apps/dispatch/app/_components/map/MapAdditionals.tsx index 6dea267e..ff70d4f9 100644 --- a/apps/dispatch/app/_components/map/MapAdditionals.tsx +++ b/apps/dispatch/app/_components/map/MapAdditionals.tsx @@ -1,6 +1,6 @@ "use client"; import { usePannelStore } from "_store/pannelStore"; -import { Marker } from "react-leaflet"; +import { Marker, useMap } from "react-leaflet"; import L from "leaflet"; import { useQuery } from "@tanstack/react-query"; import { getMissionsAPI } from "_querys/missions"; @@ -8,12 +8,13 @@ import { HPGValidationRequired } from "_helpers/hpgValidationRequired"; import { getConnectedAircraftsAPI } from "_querys/aircrafts"; import { useMapStore } from "_store/mapStore"; import { useDispatchConnectionStore } from "_store/dispatch/connectionStore"; -import { is } from "date-fns/locale"; import { XplaneObject } from "@repo/db"; +import { useEffect, useState } from "react"; export const MapAdditionals = () => { - const { isOpen, missionFormValues } = usePannelStore((state) => state); + const { isOpen, missionFormValues, setMissionFormValues } = usePannelStore((state) => state); const dispatcherConnectionState = useDispatchConnectionStore((state) => state.status); + const { openMissionMarker } = useMapStore((state) => state); const { data: missions = [] } = useQuery({ queryKey: ["missions"], @@ -23,13 +24,28 @@ export const MapAdditionals = () => { }), refetchInterval: 10_000, }); - const mapStore = useMapStore((state) => state); + const { setOpenMissionMarker } = useMapStore((state) => state); + const [showDetailedAdditionals, setShowDetailedAdditionals] = useState(false); const { data: aircrafts } = useQuery({ queryKey: ["aircrafts"], queryFn: () => getConnectedAircraftsAPI(), refetchInterval: 10000, }); + const leafletMap = useMap(); + + useEffect(() => { + const handleZoomEnd = () => { + const currentZoom = leafletMap.getZoom(); + setShowDetailedAdditionals(currentZoom > 10); + }; + + leafletMap.on("zoomend", handleZoomEnd); + + return () => { + leafletMap.off("zoomend", handleZoomEnd); + }; + }, [leafletMap]); const markersNeedingAttention = missions.filter( (m) => @@ -39,9 +55,11 @@ export const MapAdditionals = () => { m.hpgLocationLat && dispatcherConnectionState === "connected" && m.hpgLocationLng && - mapStore.openMissionMarker.find((openMission) => openMission.id === m.id), + openMissionMarker.find((openMission) => openMission.id === m.id), ); + console.log("markersNeedingAttention", showDetailedAdditionals); + return ( <> {missionFormValues?.addressLat && missionFormValues?.addressLng && isOpen && ( @@ -55,6 +73,24 @@ export const MapAdditionals = () => { interactive={false} /> )} + {showDetailedAdditionals && + openMissionMarker.map((mission) => { + if (missionFormValues?.id === mission.id) return null; + const missionData = missions.find((m) => m.id === mission.id); + if (!missionData?.addressLat || !missionData?.addressLng) return null; + return (missionData.xPlaneObjects as unknown as XplaneObject[]).map((obj, index) => ( + + )); + })} {isOpen && missionFormValues?.xPlaneObjects && (missionFormValues.xPlaneObjects as unknown as XplaneObject[]).map((obj, index) => ( @@ -66,7 +102,33 @@ export const MapAdditionals = () => { iconSize: [40, 40], iconAnchor: [20, 35], })} - interactive={false} + draggable={true} + eventHandlers={{ + dragend: (e) => { + const marker = e.target; + const position = marker.getLatLng(); + console.log("Marker dragged to:", position); + setMissionFormValues({ + ...missionFormValues, + xPlaneObjects: (missionFormValues.xPlaneObjects as unknown as XplaneObject[]).map( + (obj, objIndex) => + objIndex === index ? { ...obj, lat: position.lat, lon: position.lng } : obj, + ), + }); + }, + + contextmenu: (e) => { + e.originalEvent.preventDefault(); + const updatedObjects = ( + missionFormValues.xPlaneObjects as unknown as XplaneObject[] + ).filter((_, objIndex) => objIndex !== index); + setMissionFormValues({ + ...missionFormValues, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + xPlaneObjects: updatedObjects as unknown as any[], + }); + }, + }} /> ))} {markersNeedingAttention.map((mission) => ( @@ -80,7 +142,7 @@ export const MapAdditionals = () => { })} eventHandlers={{ click: () => - mapStore.setOpenMissionMarker({ + setOpenMissionMarker({ open: [ { id: mission.id,