XPlane objecte können wegebt und per rechts-click gelöscht werden
This commit is contained in:
@@ -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) {
|
||||||
|
|||||||
@@ -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: [
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user