cleaned code, added hpg validation in Mission Marker
This commit is contained in:
@@ -2,29 +2,11 @@ import { Marker, useMap } from "react-leaflet";
|
||||
import { DivIcon, Marker as LMarker, Popup as LPopup } from "leaflet";
|
||||
import { useMapStore } from "_store/mapStore";
|
||||
import { usePannelStore } from "_store/pannelStore";
|
||||
import {
|
||||
Fragment,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import { Fragment, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { cn } from "helpers/cn";
|
||||
import {
|
||||
ClipboardList,
|
||||
Cross,
|
||||
House,
|
||||
Minimize2,
|
||||
SmartphoneNfc,
|
||||
PencilLine,
|
||||
} from "lucide-react";
|
||||
import {
|
||||
calculateAnchor,
|
||||
SmartPopup,
|
||||
useSmartPopup,
|
||||
} from "_components/SmartPopup";
|
||||
import { HpgValidationState, Mission, MissionState } from "@repo/db";
|
||||
import { ClipboardList, Cross, House, Minimize2, SmartphoneNfc, PencilLine } from "lucide-react";
|
||||
import { calculateAnchor, SmartPopup, useSmartPopup } from "_components/SmartPopup";
|
||||
import { Mission, MissionState } from "@repo/db";
|
||||
import Einsatzdetails, {
|
||||
FMSStatusHistory,
|
||||
Patientdetails,
|
||||
@@ -33,14 +15,15 @@ import Einsatzdetails, {
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { getMissionsAPI } from "querys/missions";
|
||||
import { useDispatchConnectionStore } from "_store/dispatch/connectionStore";
|
||||
import { HPGValidationRequired } from "helpers/hpgValidationRequired";
|
||||
import { getConnectedAircraftsAPI } from "querys/aircrafts";
|
||||
|
||||
export const MISSION_STATUS_COLORS: Record<MissionState | "attention", string> =
|
||||
{
|
||||
draft: "#0092b8",
|
||||
running: "#155dfc",
|
||||
finished: "#155dfc",
|
||||
attention: "rgb(186,105,0)",
|
||||
};
|
||||
export const MISSION_STATUS_COLORS: Record<MissionState | "attention", string> = {
|
||||
draft: "#0092b8",
|
||||
running: "#155dfc",
|
||||
finished: "#155dfc",
|
||||
attention: "rgb(186,105,0)",
|
||||
};
|
||||
|
||||
export const MISSION_STATUS_TEXT_COLORS: Record<MissionState, string> = {
|
||||
draft: "#00d3f2",
|
||||
@@ -48,12 +31,17 @@ export const MISSION_STATUS_TEXT_COLORS: Record<MissionState, string> = {
|
||||
finished: "#50a2ff",
|
||||
};
|
||||
|
||||
const MissionPopupContent = ({ mission }: { mission: Mission }) => {
|
||||
const MissionPopupContent = ({
|
||||
mission,
|
||||
hpgNeedsAttention,
|
||||
}: {
|
||||
mission: Mission;
|
||||
hpgNeedsAttention?: boolean;
|
||||
}) => {
|
||||
const { setEditingMission } = usePannelStore();
|
||||
const setMissionMarker = useMapStore((state) => state.setOpenMissionMarker);
|
||||
const currentTab = useMapStore(
|
||||
(state) =>
|
||||
state.openMissionMarker.find((m) => m.id === mission.id)?.tab ?? "home",
|
||||
(state) => state.openMissionMarker.find((m) => m.id === mission.id)?.tab ?? "home",
|
||||
);
|
||||
|
||||
const handleTabChange = useCallback(
|
||||
@@ -75,7 +63,7 @@ const MissionPopupContent = ({ mission }: { mission: Mission }) => {
|
||||
const renderTabContent = useMemo(() => {
|
||||
switch (currentTab) {
|
||||
case "home":
|
||||
return <Einsatzdetails mission={mission} />;
|
||||
return <Einsatzdetails mission={mission} hpgNeedsAttention={hpgNeedsAttention} />;
|
||||
case "details":
|
||||
return <Rettungsmittel mission={mission} />;
|
||||
case "patient":
|
||||
@@ -87,9 +75,7 @@ const MissionPopupContent = ({ mission }: { mission: Mission }) => {
|
||||
}
|
||||
}, [currentTab, mission]);
|
||||
|
||||
const setOpenMissionMarker = useMapStore(
|
||||
(state) => state.setOpenMissionMarker,
|
||||
);
|
||||
const setOpenMissionMarker = useMapStore((state) => state.setOpenMissionMarker);
|
||||
const { anchor } = useSmartPopup();
|
||||
const { setMissionFormValues, setOpen } = usePannelStore((state) => state);
|
||||
|
||||
@@ -224,9 +210,17 @@ const MissionMarker = ({ mission }: { mission: Mission }) => {
|
||||
const markerRef = useRef<LMarker>(null);
|
||||
const popupRef = useRef<LPopup>(null);
|
||||
|
||||
const { openMissionMarker, setOpenMissionMarker } = useMapStore(
|
||||
(store) => store,
|
||||
);
|
||||
const { data: aircrafts } = useQuery({
|
||||
queryKey: ["aircrafts"],
|
||||
queryFn: getConnectedAircraftsAPI,
|
||||
refetchInterval: 10000,
|
||||
});
|
||||
|
||||
const { openMissionMarker, setOpenMissionMarker } = useMapStore((store) => store);
|
||||
|
||||
const needsAction =
|
||||
HPGValidationRequired(mission.missionStationIds, aircrafts, mission.hpgMissionString) &&
|
||||
mission.hpgValidationState !== "VALID";
|
||||
|
||||
useEffect(() => {
|
||||
const handleClick = () => {
|
||||
@@ -255,15 +249,12 @@ const MissionMarker = ({ mission }: { mission: Mission }) => {
|
||||
};
|
||||
}, [mission.id, openMissionMarker, setOpenMissionMarker]);
|
||||
|
||||
const [anchor, setAnchor] = useState<
|
||||
"topleft" | "topright" | "bottomleft" | "bottomright"
|
||||
>("topleft");
|
||||
const [anchor, setAnchor] = useState<"topleft" | "topright" | "bottomleft" | "bottomright">(
|
||||
"topleft",
|
||||
);
|
||||
|
||||
const handleConflict = useCallback(() => {
|
||||
const newAnchor = calculateAnchor(
|
||||
`mission-${mission.id.toString()}`,
|
||||
"marker",
|
||||
);
|
||||
const newAnchor = calculateAnchor(`mission-${mission.id.toString()}`, "marker");
|
||||
setAnchor(newAnchor);
|
||||
}, [mission.id]);
|
||||
|
||||
@@ -292,15 +283,13 @@ const MissionMarker = ({ mission }: { mission: Mission }) => {
|
||||
mission: Mission,
|
||||
anchor: "topleft" | "topright" | "bottomleft" | "bottomright",
|
||||
) => {
|
||||
const markerColor =
|
||||
mission.hpgValidationState ===
|
||||
(HpgValidationState.POSITION_AMANDED ||
|
||||
HpgValidationState.INVALID ||
|
||||
HpgValidationState.HPG_DISCONNECT ||
|
||||
HpgValidationState.HPG_BUSY ||
|
||||
HpgValidationState.HPG_INVALID_MISSION)
|
||||
? MISSION_STATUS_COLORS["attention"]
|
||||
: MISSION_STATUS_COLORS[mission.state];
|
||||
console.log(
|
||||
HPGValidationRequired(mission.missionStationIds, aircrafts, mission.hpgMissionString),
|
||||
);
|
||||
|
||||
const markerColor = needsAction
|
||||
? MISSION_STATUS_COLORS["attention"]
|
||||
: MISSION_STATUS_COLORS[mission.state];
|
||||
return `<div
|
||||
id="marker-mission-${mission.id}"
|
||||
class="${cn(
|
||||
@@ -369,7 +358,7 @@ const MissionMarker = ({ mission }: { mission: Mission }) => {
|
||||
className="w-[502px]"
|
||||
>
|
||||
<div style={{ height: "auto", maxHeight: "90vh", overflowY: "auto" }}>
|
||||
<MissionPopupContent mission={mission} />
|
||||
<MissionPopupContent mission={mission} hpgNeedsAttention={needsAction} />
|
||||
</div>
|
||||
</SmartPopup>
|
||||
)}
|
||||
@@ -378,8 +367,7 @@ const MissionMarker = ({ mission }: { mission: Mission }) => {
|
||||
};
|
||||
|
||||
export const MissionLayer = () => {
|
||||
const dispatcherConnected =
|
||||
useDispatchConnectionStore((s) => s.status) === "connected";
|
||||
const dispatcherConnected = useDispatchConnectionStore((s) => s.status) === "connected";
|
||||
const { data: missions = [] } = useQuery({
|
||||
queryKey: ["missions"],
|
||||
queryFn: () =>
|
||||
|
||||
@@ -31,16 +31,18 @@ import {
|
||||
import { usePannelStore } from "_store/pannelStore";
|
||||
import { useSession } from "next-auth/react";
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import {
|
||||
deleteMissionAPI,
|
||||
editMissionAPI,
|
||||
sendMissionAPI,
|
||||
} from "querys/missions";
|
||||
import { deleteMissionAPI, editMissionAPI, sendMissionAPI } from "querys/missions";
|
||||
import { getConnectedAircraftsAPI } from "querys/aircrafts";
|
||||
import { getStationsAPI } from "querys/stations";
|
||||
import { useDispatchConnectionStore } from "_store/dispatch/connectionStore";
|
||||
|
||||
const Einsatzdetails = ({ mission }: { mission: Mission }) => {
|
||||
const Einsatzdetails = ({
|
||||
mission,
|
||||
hpgNeedsAttention,
|
||||
}: {
|
||||
mission: Mission;
|
||||
hpgNeedsAttention?: boolean;
|
||||
}) => {
|
||||
const queryClient = useQueryClient();
|
||||
const deleteMissionMutation = useMutation({
|
||||
mutationKey: ["missions"],
|
||||
@@ -67,13 +69,8 @@ const Einsatzdetails = ({ mission }: { mission: Mission }) => {
|
||||
});
|
||||
const editMissionMutation = useMutation({
|
||||
mutationKey: ["missions"],
|
||||
mutationFn: ({
|
||||
id,
|
||||
mission,
|
||||
}: {
|
||||
id: number;
|
||||
mission: Prisma.MissionUpdateInput;
|
||||
}) => editMissionAPI(id, mission),
|
||||
mutationFn: ({ id, mission }: { id: number; mission: Prisma.MissionUpdateInput }) =>
|
||||
editMissionAPI(id, mission),
|
||||
onSuccess: () => {
|
||||
toast.success("Gespeichert");
|
||||
queryClient.invalidateQueries({
|
||||
@@ -81,9 +78,9 @@ const Einsatzdetails = ({ mission }: { mission: Mission }) => {
|
||||
});
|
||||
},
|
||||
});
|
||||
const dispatcherConnected =
|
||||
useDispatchConnectionStore((s) => s.status) === "connected";
|
||||
const dispatcherConnected = useDispatchConnectionStore((s) => s.status) === "connected";
|
||||
const { setMissionFormValues, setOpen } = usePannelStore((state) => state);
|
||||
const [ignoreHpg, setIgnoreHpg] = useState(false);
|
||||
return (
|
||||
<div className="p-4 text-base-content">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
@@ -170,6 +167,8 @@ const Einsatzdetails = ({ mission }: { mission: Mission }) => {
|
||||
<input
|
||||
type="checkbox"
|
||||
className="checkbox checkbox-sm checkbox-primary"
|
||||
checked={ignoreHpg}
|
||||
onChange={(e) => setIgnoreHpg(e.target.checked)}
|
||||
/>
|
||||
<span className="label-text font-semibold leading-6">
|
||||
Ohne HPG-Mission alarmieren
|
||||
@@ -178,9 +177,7 @@ const Einsatzdetails = ({ mission }: { mission: Mission }) => {
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2 w-full">
|
||||
{(mission.hpgValidationState === HpgValidationState.VALID ||
|
||||
mission.hpgValidationState ===
|
||||
HpgValidationState.NOT_VALIDATED) && (
|
||||
{(!hpgNeedsAttention || ignoreHpg) && (
|
||||
<button
|
||||
className="btn btn-sm btn-info btn-outline flex-3"
|
||||
onClick={() => sendAlertMutation.mutate(mission.id)}
|
||||
@@ -190,45 +187,31 @@ const Einsatzdetails = ({ mission }: { mission: Mission }) => {
|
||||
</span>
|
||||
</button>
|
||||
)}
|
||||
{(mission.hpgValidationState === HpgValidationState.PENDING ||
|
||||
mission.hpgValidationState === HpgValidationState.HPG_BUSY ||
|
||||
mission.hpgValidationState ===
|
||||
HpgValidationState.HPG_DISCONNECT ||
|
||||
mission.hpgValidationState === HpgValidationState.INVALID ||
|
||||
HpgValidationState.HPG_INVALID_MISSION) &&
|
||||
mission.hpgValidationState !== HpgValidationState.NOT_VALIDATED &&
|
||||
mission.hpgValidationState !==
|
||||
HpgValidationState.POSITION_AMANDED &&
|
||||
mission.hpgValidationState !== HpgValidationState.VALID && (
|
||||
<button
|
||||
className="btn btn-sm btn-info btn-outline flex-3"
|
||||
onClick={() => sendAlertMutation.mutate(mission.id)}
|
||||
disabled
|
||||
>
|
||||
<span className="flex items-center gap-2">
|
||||
{mission.hpgValidationState ===
|
||||
HpgValidationState.PENDING && (
|
||||
<div>
|
||||
<span className="loading loading-spinner loading-md"></span>{" "}
|
||||
HPG-Validierung läuft...
|
||||
</div>
|
||||
)}
|
||||
{mission.hpgValidationState ===
|
||||
HpgValidationState.HPG_BUSY && "HPG-Client busy"}
|
||||
{mission.hpgValidationState ===
|
||||
HpgValidationState.HPG_DISCONNECT &&
|
||||
"HPG-Client nicht verbunden"}
|
||||
{mission.hpgValidationState ===
|
||||
HpgValidationState.INVALID && "HPG-Client fehlerhaft"}
|
||||
{mission.hpgValidationState ===
|
||||
HpgValidationState.HPG_INVALID_MISSION &&
|
||||
"Fehlerhafte HPG-Mission"}
|
||||
</span>
|
||||
</button>
|
||||
)}
|
||||
{hpgNeedsAttention && (
|
||||
<button
|
||||
className="btn btn-sm btn-info btn-outline flex-3"
|
||||
onClick={() => sendAlertMutation.mutate(mission.id)}
|
||||
disabled
|
||||
>
|
||||
<span className="flex items-center gap-2">
|
||||
{mission.hpgValidationState === HpgValidationState.PENDING && (
|
||||
<div>
|
||||
<span className="loading loading-spinner loading-md"></span> HPG-Validierung
|
||||
läuft...
|
||||
</div>
|
||||
)}
|
||||
{mission.hpgValidationState === HpgValidationState.HPG_BUSY && "HPG-Client busy"}
|
||||
{mission.hpgValidationState === HpgValidationState.HPG_DISCONNECT &&
|
||||
"HPG-Client nicht verbunden"}
|
||||
{mission.hpgValidationState === HpgValidationState.INVALID &&
|
||||
"HPG-Client fehlerhaft"}
|
||||
{mission.hpgValidationState === HpgValidationState.HPG_INVALID_MISSION &&
|
||||
"Fehlerhafte HPG-Mission"}
|
||||
</span>
|
||||
</button>
|
||||
)}
|
||||
|
||||
{mission.hpgValidationState ===
|
||||
HpgValidationState.POSITION_AMANDED && (
|
||||
{mission.hpgValidationState === HpgValidationState.POSITION_AMANDED && (
|
||||
<button
|
||||
className="btn btn-sm btn-warning btn-outline flex-3"
|
||||
onClick={() => sendAlertMutation.mutate(mission.id)}
|
||||
@@ -278,16 +261,12 @@ const Patientdetails = ({ mission }: { mission: Mission }) => {
|
||||
<h2 className="flex items-center gap-2 text-lg font-bold mb-3">
|
||||
<User /> Patientendetails
|
||||
</h2>
|
||||
<p className="text-base-content font-semibold">
|
||||
{mission.missionPatientInfo}
|
||||
</p>
|
||||
<p className="text-base-content font-semibold">{mission.missionPatientInfo}</p>
|
||||
<div className="divider my-2" />
|
||||
<h2 className="flex items-center gap-2 text-lg font-bold mb-3">
|
||||
<Cross /> Einsatzinformationen
|
||||
</h2>
|
||||
<p className="text-base-content font-semibold">
|
||||
{mission.missionAdditionalInfo}
|
||||
</p>
|
||||
<p className="text-base-content font-semibold">{mission.missionAdditionalInfo}</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -303,13 +282,8 @@ const Rettungsmittel = ({ mission }: { mission: Mission }) => {
|
||||
});
|
||||
const updateMissionMutation = useMutation({
|
||||
mutationKey: ["missions", "stations-mission", mission.id],
|
||||
mutationFn: ({
|
||||
id,
|
||||
missionEdit,
|
||||
}: {
|
||||
id: number;
|
||||
missionEdit: Prisma.MissionUpdateInput;
|
||||
}) => editMissionAPI(id, missionEdit),
|
||||
mutationFn: ({ id, missionEdit }: { id: number; missionEdit: Prisma.MissionUpdateInput }) =>
|
||||
editMissionAPI(id, missionEdit),
|
||||
|
||||
onError: (error) => {
|
||||
console.error(error);
|
||||
@@ -323,17 +297,15 @@ const Rettungsmittel = ({ mission }: { mission: Mission }) => {
|
||||
},
|
||||
});
|
||||
|
||||
const { data: missionStations, refetch: refetchMissionStationIds } = useQuery(
|
||||
{
|
||||
queryKey: ["stations-mission", mission.id],
|
||||
queryFn: () =>
|
||||
getStationsAPI({
|
||||
id: {
|
||||
in: mission.missionStationIds,
|
||||
},
|
||||
}),
|
||||
},
|
||||
);
|
||||
const { data: missionStations, refetch: refetchMissionStationIds } = useQuery({
|
||||
queryKey: ["stations-mission", mission.id],
|
||||
queryFn: () =>
|
||||
getStationsAPI({
|
||||
id: {
|
||||
in: mission.missionStationIds,
|
||||
},
|
||||
}),
|
||||
});
|
||||
useEffect(() => {
|
||||
refetchMissionStationIds();
|
||||
}, [mission.missionStationIds, refetchMissionStationIds]);
|
||||
@@ -426,22 +398,14 @@ const Rettungsmittel = ({ mission }: { mission: Mission }) => {
|
||||
<select
|
||||
className="select select-sm select-primary select-bordered flex-1"
|
||||
onChange={(e) => {
|
||||
const selected = allStations?.find(
|
||||
(s) => s.id.toString() === e.target.value,
|
||||
);
|
||||
const selected = allStations?.find((s) => s.id.toString() === e.target.value);
|
||||
if (selected) {
|
||||
setSelectedStation(selected);
|
||||
} else {
|
||||
setSelectedStation(
|
||||
e.target.value as "ambulance" | "police" | "firebrigade",
|
||||
);
|
||||
setSelectedStation(e.target.value as "ambulance" | "police" | "firebrigade");
|
||||
}
|
||||
}}
|
||||
value={
|
||||
typeof selectedStation === "string"
|
||||
? selectedStation
|
||||
: selectedStation?.id
|
||||
}
|
||||
value={typeof selectedStation === "string" ? selectedStation : selectedStation?.id}
|
||||
>
|
||||
{allStations
|
||||
?.filter((s) => !mission.missionStationIds.includes(s.id))
|
||||
@@ -498,17 +462,11 @@ const FMSStatusHistory = ({ mission }: { mission: Mission }) => {
|
||||
const [note, setNote] = useState("");
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const dispatcherConnected =
|
||||
useDispatchConnectionStore((s) => s.status) === "connected";
|
||||
const dispatcherConnected = useDispatchConnectionStore((s) => s.status) === "connected";
|
||||
|
||||
const editMissionMutation = useMutation({
|
||||
mutationFn: ({
|
||||
id,
|
||||
mission,
|
||||
}: {
|
||||
id: number;
|
||||
mission: Partial<Prisma.MissionUpdateInput>;
|
||||
}) => editMissionAPI(id, mission),
|
||||
mutationFn: ({ id, mission }: { id: number; mission: Partial<Prisma.MissionUpdateInput> }) =>
|
||||
editMissionAPI(id, mission),
|
||||
mutationKey: ["missions"],
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ["missions"] });
|
||||
@@ -608,9 +566,7 @@ const FMSStatusHistory = ({ mission }: { mission: Mission }) => {
|
||||
>
|
||||
{entry.data.newFMSstatus}
|
||||
</span>
|
||||
<span className="text-base-content">
|
||||
{entry.data.station.bosCallsign}
|
||||
</span>
|
||||
<span className="text-base-content">{entry.data.station.bosCallsign}</span>
|
||||
</li>
|
||||
);
|
||||
if (entry.type === "message-log" || entry.type === "sds-log")
|
||||
@@ -651,9 +607,7 @@ const FMSStatusHistory = ({ mission }: { mission: Mission }) => {
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
<span className="text-base-content">
|
||||
{entry.data.message}
|
||||
</span>
|
||||
<span className="text-base-content">{entry.data.message}</span>
|
||||
</li>
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user