Position übernehmen beim bearbeiten von Einsätzen ändert nun die Position von Einsätzen

This commit is contained in:
PxlLoewe
2025-06-17 21:00:21 -07:00
parent d39039e149
commit fe77ef2c03
7 changed files with 54 additions and 47 deletions

View File

@@ -1,5 +1,5 @@
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
import { OSMWay } from "@repo/db"; import { OSMWay, Prisma } from "@repo/db";
import { useDispatchConnectionStore } from "_store/dispatch/connectionStore"; import { useDispatchConnectionStore } from "_store/dispatch/connectionStore";
import { useMapStore } from "_store/mapStore"; import { useMapStore } from "_store/mapStore";
import { usePannelStore } from "_store/pannelStore"; import { usePannelStore } from "_store/pannelStore";
@@ -8,6 +8,8 @@ import { getOsmAddress } from "_querys/osm";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import toast from "react-hot-toast"; import toast from "react-hot-toast";
import { Popup, useMap } from "react-leaflet"; import { Popup, useMap } from "react-leaflet";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { editMissionAPI } from "_querys/missions";
export const ContextMenu = () => { export const ContextMenu = () => {
const map = useMap(); const map = useMap();
@@ -19,9 +21,8 @@ export const ContextMenu = () => {
setSearchPopup, setSearchPopup,
toggleSearchElementSelection, toggleSearchElementSelection,
} = useMapStore(); } = useMapStore();
const { missionFormValues, setMissionFormValues, setOpen, isOpen } = usePannelStore( const { missionFormValues, setMissionFormValues, setOpen, isOpen, editingMissionId } =
(state) => state, usePannelStore((state) => state);
);
const [showRulerOptions, setShowRulerOptions] = useState(false); const [showRulerOptions, setShowRulerOptions] = useState(false);
const [rulerHover, setRulerHover] = useState(false); const [rulerHover, setRulerHover] = useState(false);
const [rulerOptionsHover, setRulerOptionsHover] = useState(false); const [rulerOptionsHover, setRulerOptionsHover] = useState(false);
@@ -54,7 +55,7 @@ export const ContextMenu = () => {
const einsatzBtnText = missionFormValues && isOpen ? "Position übernehmen" : "Einsatz erstellen"; const einsatzBtnText = missionFormValues && isOpen ? "Position übernehmen" : "Einsatz erstellen";
const addOSMobjects = async () => { const addOSMobjects = async (ignorePreviosSelected?: boolean) => {
const res = await fetch( const res = await fetch(
`https://overpass-api.de/api/interpreter?data=${encodeURIComponent(` `https://overpass-api.de/api/interpreter?data=${encodeURIComponent(`
[out:json]; [out:json];
@@ -73,7 +74,7 @@ export const ContextMenu = () => {
.map((e: any) => { .map((e: any) => {
const elementInMap = searchElements.find((el) => el.wayID === e.id); const elementInMap = searchElements.find((el) => el.wayID === e.id);
return { return {
isSelected: elementInMap?.isSelected ?? false, isSelected: ignorePreviosSelected ? false : (elementInMap?.isSelected ?? false),
wayID: e.id, wayID: e.id,
nodes: e.nodes.map((nodeId: string) => { nodes: e.nodes.map((nodeId: string) => {
const node = data.elements.find((element: any) => element.id === nodeId); const node = data.elements.find((element: any) => element.id === nodeId);
@@ -111,7 +112,7 @@ export const ContextMenu = () => {
style={{ transform: "translateX(-50%)" }} style={{ transform: "translateX(-50%)" }}
onClick={async () => { onClick={async () => {
const { parsed } = await getOsmAddress(contextMenu.lat, contextMenu.lng); const { parsed } = await getOsmAddress(contextMenu.lat, contextMenu.lng);
const objects = await addOSMobjects(); const objects = await addOSMobjects(true);
const closestToContext = objects.reduce((prev, curr) => { const closestToContext = objects.reduce((prev, curr) => {
const prevLat = prev.nodes?.[0]?.lat ?? 0; const prevLat = prev.nodes?.[0]?.lat ?? 0;
const prevLon = prev.nodes?.[0]?.lon ?? 0; const prevLon = prev.nodes?.[0]?.lon ?? 0;
@@ -135,10 +136,11 @@ export const ContextMenu = () => {
); );
if (closestToContext) { if (closestToContext) {
toggleSearchElementSelection(closestToContext.wayID); toggleSearchElementSelection(closestToContext.wayID, true);
} }
setMissionFormValues({ setMissionFormValues({
...missionFormValues,
...parsed, ...parsed,
state: "draft", state: "draft",
addressLat: contextMenu.lat, addressLat: contextMenu.lat,

View File

@@ -1,5 +1,5 @@
import { Marker, useMap } from "react-leaflet"; import { Marker, useMap } from "react-leaflet";
import { DivIcon, Marker as LMarker, Popup as LPopup } from "leaflet"; import { DivIcon, LatLngExpression, Marker as LMarker, Popup as LPopup } from "leaflet";
import { useMapStore } from "_store/mapStore"; import { useMapStore } from "_store/mapStore";
import { usePannelStore } from "_store/pannelStore"; import { usePannelStore } from "_store/pannelStore";
import { Fragment, useCallback, useEffect, useMemo, useRef, useState } from "react"; import { Fragment, useCallback, useEffect, useMemo, useRef, useState } from "react";
@@ -175,7 +175,7 @@ const MissionPopupContent = ({
hpgLocationLat: mission.hpgLocationLat ?? undefined, hpgLocationLat: mission.hpgLocationLat ?? undefined,
hpgLocationLng: mission.hpgLocationLng ?? undefined, hpgLocationLng: mission.hpgLocationLng ?? undefined,
}); });
setEditingMission(true, mission.id); setEditingMission(mission.id);
setOpen(true); setOpen(true);
}} }}
> >
@@ -208,6 +208,7 @@ const MissionPopupContent = ({
const MissionMarker = ({ mission }: { mission: Mission }) => { const MissionMarker = ({ mission }: { mission: Mission }) => {
const map = useMap(); const map = useMap();
const [hideMarker, setHideMarker] = useState(false); const [hideMarker, setHideMarker] = useState(false);
const { editingMissionId, missionFormValues } = usePannelStore((state) => state);
const markerRef = useRef<LMarker>(null); const markerRef = useRef<LMarker>(null);
const popupRef = useRef<LPopup>(null); const popupRef = useRef<LPopup>(null);
@@ -329,11 +330,28 @@ const MissionMarker = ({ mission }: { mission: Mission }) => {
</div>`; </div>`;
}; };
const markerPosition = useMemo<LatLngExpression>(() => {
return [
editingMissionId === mission.id && missionFormValues?.addressLat
? missionFormValues.addressLat
: mission.addressLat,
editingMissionId === mission.id && missionFormValues?.addressLng
? missionFormValues.addressLng
: mission.addressLng,
];
}, [
editingMissionId,
mission.addressLat,
mission.addressLng,
missionFormValues?.addressLat,
missionFormValues?.addressLng,
]);
return ( return (
<Fragment key={mission.id}> <Fragment key={mission.id}>
<Marker <Marker
ref={markerRef} ref={markerRef}
position={[mission.addressLat, mission.addressLng]} position={markerPosition}
icon={ icon={
new DivIcon({ new DivIcon({
iconAnchor: [0, 0], iconAnchor: [0, 0],
@@ -348,7 +366,7 @@ const MissionMarker = ({ mission }: { mission: Mission }) => {
}} }}
id={`mission-${mission.id.toString()}`} id={`mission-${mission.id.toString()}`}
ref={popupRef} ref={popupRef}
position={[mission.addressLat, mission.addressLng]} position={markerPosition}
autoClose={false} autoClose={false}
closeOnClick={false} closeOnClick={false}
autoPan={false} autoPan={false}

View File

@@ -1,5 +1,5 @@
import { useMapStore } from "_store/mapStore"; import { useMapStore } from "_store/mapStore";
import { Marker, Polygon, Popup } from "react-leaflet"; import { Polygon } from "react-leaflet";
import { useQuery } from "@tanstack/react-query"; import { useQuery } from "@tanstack/react-query";
import { getMissionsAPI } from "_querys/missions"; import { getMissionsAPI } from "_querys/missions";
import { usePannelStore } from "_store/pannelStore"; import { usePannelStore } from "_store/pannelStore";
@@ -26,14 +26,7 @@ export const SearchElements = () => {
useEffect(() => { useEffect(() => {
if (pannelOpen) { if (pannelOpen) {
const missionEdited = missions?.find((m) => m.id === editingMissionId); // OSM Elements wirden in ContextMenu.tsx gesetzt
if (missionEdited) {
const elements = missionEdited.addressOSMways.map((e) => ({
...(e as unknown as OSMWay),
isSelected: true,
}));
setSearchElements(elements);
}
} else { } else {
const openMissions = openMissionMarker.map((m) => { const openMissions = openMissionMarker.map((m) => {
const mission = missions?.find((mi) => mi.id === m.id); const mission = missions?.find((mi) => mi.id === m.id);

View File

@@ -26,7 +26,7 @@ export interface MapStore {
}) => void; }) => void;
searchElements: OSMWay[]; searchElements: OSMWay[];
setSearchElements: (elements: MapStore["searchElements"]) => void; setSearchElements: (elements: MapStore["searchElements"]) => void;
toggleSearchElementSelection: (elementId: number) => void; toggleSearchElementSelection: (elementId: number, forceState?: boolean) => void;
setContextMenu: (popup: MapStore["contextMenu"]) => void; setContextMenu: (popup: MapStore["contextMenu"]) => void;
searchPopup: { searchPopup: {
lat: number; lat: number;
@@ -83,11 +83,11 @@ export const useMapStore = create<MapStore>((set, get) => ({
set(() => ({ set(() => ({
searchElements: elements, searchElements: elements,
})), })),
toggleSearchElementSelection: (elementId) => { toggleSearchElementSelection: (elementId, forceState) => {
const searchElements = get().searchElements; const searchElements = get().searchElements;
const element = searchElements.find((e) => e.wayID === elementId); const element = searchElements.find((e) => e.wayID === elementId);
if (!element) return; if (!element) return;
element.isSelected = !element.isSelected; element.isSelected = forceState ? forceState : !element.isSelected;
set(() => ({ set(() => ({
searchElements: [...searchElements], searchElements: [...searchElements],
})); }));

View File

@@ -7,9 +7,8 @@ interface PannelStore {
setOpen: (isOpen: boolean) => void; setOpen: (isOpen: boolean) => void;
missionFormValues?: Partial<MissionOptionalDefaults>; missionFormValues?: Partial<MissionOptionalDefaults>;
setMissionFormValues: (values: Partial<MissionOptionalDefaults>) => void; setMissionFormValues: (values: Partial<MissionOptionalDefaults>) => void;
isEditingMission: boolean;
editingMissionId: number | null; editingMissionId: number | null;
setEditingMission: (isEditing: boolean, missionId: number | null) => void; setEditingMission: (missionId: number | null) => void;
} }
export const usePannelStore = create<PannelStore>((set) => ({ export const usePannelStore = create<PannelStore>((set) => ({
@@ -17,8 +16,6 @@ export const usePannelStore = create<PannelStore>((set) => ({
setOpen: (isOpen) => set({ isOpen }), setOpen: (isOpen) => set({ isOpen }),
missionFormValues: undefined, missionFormValues: undefined,
setMissionFormValues: (values) => set({ missionFormValues: values }), setMissionFormValues: (values) => set({ missionFormValues: values }),
isEditingMission: false,
editingMissionId: null, editingMissionId: null,
setEditingMission: (isEditing, missionId) => setEditingMission: (missionId) => set({ editingMissionId: missionId }),
set({ isEditingMission: isEditing, editingMissionId: missionId }),
})); }));

View File

@@ -30,7 +30,7 @@ import { AxiosError } from "axios";
import { cn } from "_helpers/cn"; import { cn } from "_helpers/cn";
export const MissionForm = () => { export const MissionForm = () => {
const { isEditingMission, editingMissionId, setEditingMission } = usePannelStore(); const { editingMissionId, setEditingMission } = usePannelStore();
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const { setSearchElements, searchElements, setContextMenu } = useMapStore((s) => s); const { setSearchElements, searchElements, setContextMenu } = useMapStore((s) => s);
@@ -130,7 +130,9 @@ export const MissionForm = () => {
"addressOSMways", "addressOSMways",
searchElements.filter((e) => e.isSelected) as unknown as JsonValueType[], searchElements.filter((e) => e.isSelected) as unknown as JsonValueType[],
); );
}, [searchElements, form]); }, [searchElements, form, missionFormValues]);
console.log("addressOSMways", form.watch("addressOSMways"));
useEffect(() => { useEffect(() => {
if (missionFormValues) { if (missionFormValues) {
@@ -139,6 +141,7 @@ export const MissionForm = () => {
return; return;
} }
for (const key in missionFormValues) { for (const key in missionFormValues) {
if (key === "addressOSMways") continue; // Skip addressOSMways as it is handled separately
form.setValue( form.setValue(
key as keyof MissionOptionalDefaults, key as keyof MissionOptionalDefaults,
missionFormValues[key as keyof MissionOptionalDefaults], missionFormValues[key as keyof MissionOptionalDefaults],
@@ -416,16 +419,17 @@ export const MissionForm = () => {
<div className="form-control min-h-[140px]"> <div className="form-control min-h-[140px]">
<div className="flex gap-2"> <div className="flex gap-2">
{isEditingMission && editingMissionId ? ( {editingMissionId ? (
<button <button
type="button" type="button"
className="btn btn-primary flex-1" className="btn btn-primary flex-1"
onClick={form.handleSubmit(async (mission: MissionOptionalDefaults) => { onClick={form.handleSubmit(async (mission: MissionOptionalDefaults) => {
try { try {
console.log("Saving mission", mission.addressOSMways);
const newMission = await saveMission(mission); const newMission = await saveMission(mission);
toast.success(`Einsatz ${newMission.id} erfolgreich aktualisiert`); toast.success(`Einsatz ${newMission.id} erfolgreich aktualisiert`);
setSearchElements([]); // Reset search elements setSearchElements([]); // Reset search elements
setEditingMission(false, null); // Reset editing state setEditingMission(null); // Reset editing state
form.reset(); // Reset the form form.reset(); // Reset the form
setOpen(false); setOpen(false);
} catch (error) { } catch (error) {

View File

@@ -8,7 +8,7 @@ import { getMissionsAPI } from "_querys/missions";
export const Pannel = () => { export const Pannel = () => {
const { setOpen, setMissionFormValues } = usePannelStore(); const { setOpen, setMissionFormValues } = usePannelStore();
const { isEditingMission, setEditingMission, missionFormValues } = usePannelStore(); const { editingMissionId, setEditingMission, missionFormValues } = usePannelStore();
const missions = useQuery({ const missions = useQuery({
queryKey: ["missions"], queryKey: ["missions"],
queryFn: () => queryFn: () =>
@@ -18,36 +18,29 @@ export const Pannel = () => {
}); });
useEffect(() => { useEffect(() => {
if (isEditingMission && missionFormValues) { if (editingMissionId && missionFormValues) {
const mission = missions.data?.find((mission) => mission.id === missionFormValues.id); const mission = missions.data?.find((mission) => mission.id === missionFormValues.id);
if (!mission) { if (!mission) {
setEditingMission(false, null); setEditingMission(null);
setMissionFormValues({}); setMissionFormValues({});
setOpen(false); setOpen(false);
} }
} }
}, [ }, [missions, setMissionFormValues, setEditingMission, setOpen, missionFormValues]);
isEditingMission,
missions,
setMissionFormValues,
setEditingMission,
setOpen,
missionFormValues,
]);
return ( return (
<div className={cn("flex-1 max-w-[600px] z-9999999")}> <div className={cn("flex-1 max-w-[600px] z-9999999")}>
<div className="bg-base-100 min-h-screen h-full max-h-screen w-full overflow-auto"> <div className="bg-base-100 min-h-screen h-full max-h-screen w-full overflow-auto">
<div className="flex flex-row justify-between items-center p-4"> <div className="flex flex-row justify-between items-center p-4">
<h1 className="text-xl font-bold flex items-center gap-2"> <h1 className="text-xl font-bold flex items-center gap-2">
<Rss /> {isEditingMission ? "Einsatz bearbeiten" : "Neuer Einsatz"} <Rss /> {editingMissionId ? "Einsatz bearbeiten" : "Neuer Einsatz"}
</h1> </h1>
<div> <div>
<button <button
className="btn btn-ghost btn-sm mr-2 btn-warning" className="btn btn-ghost btn-sm mr-2 btn-warning"
onClick={() => { onClick={() => {
setMissionFormValues({}); setMissionFormValues({});
setEditingMission(false, null); setEditingMission(null);
}} }}
> >
<Trash2Icon size={18} /> <Trash2Icon size={18} />
@@ -56,7 +49,7 @@ export const Pannel = () => {
className="btn" className="btn"
onClick={() => { onClick={() => {
setOpen(false); setOpen(false);
setEditingMission(false, null); setEditingMission(null);
setMissionFormValues({}); setMissionFormValues({});
}} }}
> >