XPlane objecte können wegebt und per rechts-click gelöscht werden

This commit is contained in:
PxlLoewe
2025-10-28 01:52:55 +01:00
parent 5af68b8a70
commit da9b957fcf
3 changed files with 95 additions and 11 deletions

View File

@@ -117,7 +117,7 @@ export const MissionForm = () => {
resolver: zodResolver(MissionOptionalDefaultsSchema), resolver: zodResolver(MissionOptionalDefaultsSchema),
defaultValues: defaultFormValues, defaultValues: defaultFormValues,
}); });
const { missionFormValues, setOpen } = usePannelStore((state) => state); const { missionFormValues, setOpen, setMissionFormValues } = usePannelStore((state) => state);
const validationRequired = const validationRequired =
HPGValidationRequired( HPGValidationRequired(
@@ -156,6 +156,21 @@ export const MissionForm = () => {
} }
}, [missionFormValues, form, defaultFormValues]); }, [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 ( const saveMission = async (
mission: MissionOptionalDefaults, mission: MissionOptionalDefaults,
{ alertWhenValid = false, createNewMission = false } = {}, { alertWhenValid = false, createNewMission = false } = {},
@@ -449,7 +464,11 @@ export const MissionForm = () => {
setSearchElements([]); // Reset search elements setSearchElements([]); // Reset search elements
setEditingMission(null); setEditingMission(null);
setContextMenu(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(); form.reset();
setOpen(false); setOpen(false);
} catch (error) { } catch (error) {
@@ -474,7 +493,11 @@ export const MissionForm = () => {
setSearchElements([]); // Reset search elements setSearchElements([]); // Reset search elements
setContextMenu(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(); form.reset();
setOpen(false); setOpen(false);
} catch (error) { } catch (error) {

View File

@@ -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" 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" data-tip="Rettungswagen platzieren"
onClick={() => { onClick={() => {
console.log("Add Ambulance");
setMissionFormValues({ setMissionFormValues({
...missionFormValues, ...missionFormValues,
xPlaneObjects: [ xPlaneObjects: [

View File

@@ -1,6 +1,6 @@
"use client"; "use client";
import { usePannelStore } from "_store/pannelStore"; import { usePannelStore } from "_store/pannelStore";
import { Marker } from "react-leaflet"; import { Marker, useMap } from "react-leaflet";
import L from "leaflet"; import L from "leaflet";
import { useQuery } from "@tanstack/react-query"; import { useQuery } from "@tanstack/react-query";
import { getMissionsAPI } from "_querys/missions"; import { getMissionsAPI } from "_querys/missions";
@@ -8,12 +8,13 @@ import { HPGValidationRequired } from "_helpers/hpgValidationRequired";
import { getConnectedAircraftsAPI } from "_querys/aircrafts"; import { getConnectedAircraftsAPI } from "_querys/aircrafts";
import { useMapStore } from "_store/mapStore"; import { useMapStore } from "_store/mapStore";
import { useDispatchConnectionStore } from "_store/dispatch/connectionStore"; import { useDispatchConnectionStore } from "_store/dispatch/connectionStore";
import { is } from "date-fns/locale";
import { XplaneObject } from "@repo/db"; import { XplaneObject } from "@repo/db";
import { useEffect, useState } from "react";
export const MapAdditionals = () => { export const MapAdditionals = () => {
const { isOpen, missionFormValues } = usePannelStore((state) => state); const { isOpen, missionFormValues, setMissionFormValues } = usePannelStore((state) => state);
const dispatcherConnectionState = useDispatchConnectionStore((state) => state.status); const dispatcherConnectionState = useDispatchConnectionStore((state) => state.status);
const { openMissionMarker } = useMapStore((state) => state);
const { data: missions = [] } = useQuery({ const { data: missions = [] } = useQuery({
queryKey: ["missions"], queryKey: ["missions"],
@@ -23,13 +24,28 @@ export const MapAdditionals = () => {
}), }),
refetchInterval: 10_000, refetchInterval: 10_000,
}); });
const mapStore = useMapStore((state) => state); const { setOpenMissionMarker } = useMapStore((state) => state);
const [showDetailedAdditionals, setShowDetailedAdditionals] = useState(false);
const { data: aircrafts } = useQuery({ const { data: aircrafts } = useQuery({
queryKey: ["aircrafts"], queryKey: ["aircrafts"],
queryFn: () => getConnectedAircraftsAPI(), queryFn: () => getConnectedAircraftsAPI(),
refetchInterval: 10000, 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( const markersNeedingAttention = missions.filter(
(m) => (m) =>
@@ -39,9 +55,11 @@ export const MapAdditionals = () => {
m.hpgLocationLat && m.hpgLocationLat &&
dispatcherConnectionState === "connected" && dispatcherConnectionState === "connected" &&
m.hpgLocationLng && m.hpgLocationLng &&
mapStore.openMissionMarker.find((openMission) => openMission.id === m.id), openMissionMarker.find((openMission) => openMission.id === m.id),
); );
console.log("markersNeedingAttention", showDetailedAdditionals);
return ( return (
<> <>
{missionFormValues?.addressLat && missionFormValues?.addressLng && isOpen && ( {missionFormValues?.addressLat && missionFormValues?.addressLng && isOpen && (
@@ -55,6 +73,24 @@ export const MapAdditionals = () => {
interactive={false} 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) => (
<Marker
key={`${mission.id}-additional-${index}`}
position={[obj.lat, obj.lon]}
icon={L.icon({
iconUrl: `/icons/${obj.objectName}.png`,
iconSize: [40, 40],
iconAnchor: [20, 35],
})}
interactive={false}
/>
));
})}
{isOpen && {isOpen &&
missionFormValues?.xPlaneObjects && missionFormValues?.xPlaneObjects &&
(missionFormValues.xPlaneObjects as unknown as XplaneObject[]).map((obj, index) => ( (missionFormValues.xPlaneObjects as unknown as XplaneObject[]).map((obj, index) => (
@@ -66,7 +102,33 @@ export const MapAdditionals = () => {
iconSize: [40, 40], iconSize: [40, 40],
iconAnchor: [20, 35], 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) => ( {markersNeedingAttention.map((mission) => (
@@ -80,7 +142,7 @@ export const MapAdditionals = () => {
})} })}
eventHandlers={{ eventHandlers={{
click: () => click: () =>
mapStore.setOpenMissionMarker({ setOpenMissionMarker({
open: [ open: [
{ {
id: mission.id, id: mission.id,