cleaned code, added hpg validation in Mission Marker
This commit is contained in:
@@ -235,6 +235,7 @@ router.post("/:id/validate-hpg", async (req, res) => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
); */
|
); */
|
||||||
|
// TODO: remove this after testing
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
io.to(`user:${req.user?.id}`).emit("notification", {
|
io.to(`user:${req.user?.id}`).emit("notification", {
|
||||||
type: "hpg-validation",
|
type: "hpg-validation",
|
||||||
|
|||||||
@@ -4,13 +4,7 @@ import { Server, Socket } from "socket.io";
|
|||||||
|
|
||||||
export const handleConnectPilot =
|
export const handleConnectPilot =
|
||||||
(socket: Socket, io: Server) =>
|
(socket: Socket, io: Server) =>
|
||||||
async ({
|
async ({ logoffTime, stationId }: { logoffTime: string; stationId: string }) => {
|
||||||
logoffTime,
|
|
||||||
stationId,
|
|
||||||
}: {
|
|
||||||
logoffTime: string;
|
|
||||||
stationId: string;
|
|
||||||
}) => {
|
|
||||||
try {
|
try {
|
||||||
const user = socket.data.user; // User ID aus dem JWT-Token
|
const user = socket.data.user; // User ID aus dem JWT-Token
|
||||||
const userId = socket.data.user.id; // User ID aus dem JWT-Token
|
const userId = socket.data.user.id; // User ID aus dem JWT-Token
|
||||||
@@ -32,9 +26,7 @@ export const handleConnectPilot =
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (existingConnection) {
|
if (existingConnection) {
|
||||||
await io
|
await io.to(`user:${user.id}`).emit("force-disconnect", "double-connection");
|
||||||
.to(`user:${user.id}`)
|
|
||||||
.emit("force-disconnect", "double-connection");
|
|
||||||
await prisma.connectedAircraft.updateMany({
|
await prisma.connectedAircraft.updateMany({
|
||||||
where: {
|
where: {
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
@@ -72,6 +64,7 @@ export const handleConnectPilot =
|
|||||||
stationId: parseInt(stationId),
|
stationId: parseInt(stationId),
|
||||||
posLat: 51.45,
|
posLat: 51.45,
|
||||||
posLng: 9.77,
|
posLng: 9.77,
|
||||||
|
posH145active: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -2,29 +2,11 @@ import { Marker, useMap } from "react-leaflet";
|
|||||||
import { DivIcon, Marker as LMarker, Popup as LPopup } from "leaflet";
|
import { DivIcon, 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 {
|
import { Fragment, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||||
Fragment,
|
|
||||||
useCallback,
|
|
||||||
useEffect,
|
|
||||||
useMemo,
|
|
||||||
useRef,
|
|
||||||
useState,
|
|
||||||
} from "react";
|
|
||||||
import { cn } from "helpers/cn";
|
import { cn } from "helpers/cn";
|
||||||
import {
|
import { ClipboardList, Cross, House, Minimize2, SmartphoneNfc, PencilLine } from "lucide-react";
|
||||||
ClipboardList,
|
import { calculateAnchor, SmartPopup, useSmartPopup } from "_components/SmartPopup";
|
||||||
Cross,
|
import { Mission, MissionState } from "@repo/db";
|
||||||
House,
|
|
||||||
Minimize2,
|
|
||||||
SmartphoneNfc,
|
|
||||||
PencilLine,
|
|
||||||
} from "lucide-react";
|
|
||||||
import {
|
|
||||||
calculateAnchor,
|
|
||||||
SmartPopup,
|
|
||||||
useSmartPopup,
|
|
||||||
} from "_components/SmartPopup";
|
|
||||||
import { HpgValidationState, Mission, MissionState } from "@repo/db";
|
|
||||||
import Einsatzdetails, {
|
import Einsatzdetails, {
|
||||||
FMSStatusHistory,
|
FMSStatusHistory,
|
||||||
Patientdetails,
|
Patientdetails,
|
||||||
@@ -33,14 +15,15 @@ import Einsatzdetails, {
|
|||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import { getMissionsAPI } from "querys/missions";
|
import { getMissionsAPI } from "querys/missions";
|
||||||
import { useDispatchConnectionStore } from "_store/dispatch/connectionStore";
|
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> =
|
export const MISSION_STATUS_COLORS: Record<MissionState | "attention", string> = {
|
||||||
{
|
draft: "#0092b8",
|
||||||
draft: "#0092b8",
|
running: "#155dfc",
|
||||||
running: "#155dfc",
|
finished: "#155dfc",
|
||||||
finished: "#155dfc",
|
attention: "rgb(186,105,0)",
|
||||||
attention: "rgb(186,105,0)",
|
};
|
||||||
};
|
|
||||||
|
|
||||||
export const MISSION_STATUS_TEXT_COLORS: Record<MissionState, string> = {
|
export const MISSION_STATUS_TEXT_COLORS: Record<MissionState, string> = {
|
||||||
draft: "#00d3f2",
|
draft: "#00d3f2",
|
||||||
@@ -48,12 +31,17 @@ export const MISSION_STATUS_TEXT_COLORS: Record<MissionState, string> = {
|
|||||||
finished: "#50a2ff",
|
finished: "#50a2ff",
|
||||||
};
|
};
|
||||||
|
|
||||||
const MissionPopupContent = ({ mission }: { mission: Mission }) => {
|
const MissionPopupContent = ({
|
||||||
|
mission,
|
||||||
|
hpgNeedsAttention,
|
||||||
|
}: {
|
||||||
|
mission: Mission;
|
||||||
|
hpgNeedsAttention?: boolean;
|
||||||
|
}) => {
|
||||||
const { setEditingMission } = usePannelStore();
|
const { setEditingMission } = usePannelStore();
|
||||||
const setMissionMarker = useMapStore((state) => state.setOpenMissionMarker);
|
const setMissionMarker = useMapStore((state) => state.setOpenMissionMarker);
|
||||||
const currentTab = useMapStore(
|
const currentTab = useMapStore(
|
||||||
(state) =>
|
(state) => state.openMissionMarker.find((m) => m.id === mission.id)?.tab ?? "home",
|
||||||
state.openMissionMarker.find((m) => m.id === mission.id)?.tab ?? "home",
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleTabChange = useCallback(
|
const handleTabChange = useCallback(
|
||||||
@@ -75,7 +63,7 @@ const MissionPopupContent = ({ mission }: { mission: Mission }) => {
|
|||||||
const renderTabContent = useMemo(() => {
|
const renderTabContent = useMemo(() => {
|
||||||
switch (currentTab) {
|
switch (currentTab) {
|
||||||
case "home":
|
case "home":
|
||||||
return <Einsatzdetails mission={mission} />;
|
return <Einsatzdetails mission={mission} hpgNeedsAttention={hpgNeedsAttention} />;
|
||||||
case "details":
|
case "details":
|
||||||
return <Rettungsmittel mission={mission} />;
|
return <Rettungsmittel mission={mission} />;
|
||||||
case "patient":
|
case "patient":
|
||||||
@@ -87,9 +75,7 @@ const MissionPopupContent = ({ mission }: { mission: Mission }) => {
|
|||||||
}
|
}
|
||||||
}, [currentTab, mission]);
|
}, [currentTab, mission]);
|
||||||
|
|
||||||
const setOpenMissionMarker = useMapStore(
|
const setOpenMissionMarker = useMapStore((state) => state.setOpenMissionMarker);
|
||||||
(state) => state.setOpenMissionMarker,
|
|
||||||
);
|
|
||||||
const { anchor } = useSmartPopup();
|
const { anchor } = useSmartPopup();
|
||||||
const { setMissionFormValues, setOpen } = usePannelStore((state) => state);
|
const { setMissionFormValues, setOpen } = usePannelStore((state) => state);
|
||||||
|
|
||||||
@@ -224,9 +210,17 @@ const MissionMarker = ({ mission }: { mission: Mission }) => {
|
|||||||
const markerRef = useRef<LMarker>(null);
|
const markerRef = useRef<LMarker>(null);
|
||||||
const popupRef = useRef<LPopup>(null);
|
const popupRef = useRef<LPopup>(null);
|
||||||
|
|
||||||
const { openMissionMarker, setOpenMissionMarker } = useMapStore(
|
const { data: aircrafts } = useQuery({
|
||||||
(store) => store,
|
queryKey: ["aircrafts"],
|
||||||
);
|
queryFn: getConnectedAircraftsAPI,
|
||||||
|
refetchInterval: 10000,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { openMissionMarker, setOpenMissionMarker } = useMapStore((store) => store);
|
||||||
|
|
||||||
|
const needsAction =
|
||||||
|
HPGValidationRequired(mission.missionStationIds, aircrafts, mission.hpgMissionString) &&
|
||||||
|
mission.hpgValidationState !== "VALID";
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleClick = () => {
|
const handleClick = () => {
|
||||||
@@ -255,15 +249,12 @@ const MissionMarker = ({ mission }: { mission: Mission }) => {
|
|||||||
};
|
};
|
||||||
}, [mission.id, openMissionMarker, setOpenMissionMarker]);
|
}, [mission.id, openMissionMarker, setOpenMissionMarker]);
|
||||||
|
|
||||||
const [anchor, setAnchor] = useState<
|
const [anchor, setAnchor] = useState<"topleft" | "topright" | "bottomleft" | "bottomright">(
|
||||||
"topleft" | "topright" | "bottomleft" | "bottomright"
|
"topleft",
|
||||||
>("topleft");
|
);
|
||||||
|
|
||||||
const handleConflict = useCallback(() => {
|
const handleConflict = useCallback(() => {
|
||||||
const newAnchor = calculateAnchor(
|
const newAnchor = calculateAnchor(`mission-${mission.id.toString()}`, "marker");
|
||||||
`mission-${mission.id.toString()}`,
|
|
||||||
"marker",
|
|
||||||
);
|
|
||||||
setAnchor(newAnchor);
|
setAnchor(newAnchor);
|
||||||
}, [mission.id]);
|
}, [mission.id]);
|
||||||
|
|
||||||
@@ -292,15 +283,13 @@ const MissionMarker = ({ mission }: { mission: Mission }) => {
|
|||||||
mission: Mission,
|
mission: Mission,
|
||||||
anchor: "topleft" | "topright" | "bottomleft" | "bottomright",
|
anchor: "topleft" | "topright" | "bottomleft" | "bottomright",
|
||||||
) => {
|
) => {
|
||||||
const markerColor =
|
console.log(
|
||||||
mission.hpgValidationState ===
|
HPGValidationRequired(mission.missionStationIds, aircrafts, mission.hpgMissionString),
|
||||||
(HpgValidationState.POSITION_AMANDED ||
|
);
|
||||||
HpgValidationState.INVALID ||
|
|
||||||
HpgValidationState.HPG_DISCONNECT ||
|
const markerColor = needsAction
|
||||||
HpgValidationState.HPG_BUSY ||
|
? MISSION_STATUS_COLORS["attention"]
|
||||||
HpgValidationState.HPG_INVALID_MISSION)
|
: MISSION_STATUS_COLORS[mission.state];
|
||||||
? MISSION_STATUS_COLORS["attention"]
|
|
||||||
: MISSION_STATUS_COLORS[mission.state];
|
|
||||||
return `<div
|
return `<div
|
||||||
id="marker-mission-${mission.id}"
|
id="marker-mission-${mission.id}"
|
||||||
class="${cn(
|
class="${cn(
|
||||||
@@ -369,7 +358,7 @@ const MissionMarker = ({ mission }: { mission: Mission }) => {
|
|||||||
className="w-[502px]"
|
className="w-[502px]"
|
||||||
>
|
>
|
||||||
<div style={{ height: "auto", maxHeight: "90vh", overflowY: "auto" }}>
|
<div style={{ height: "auto", maxHeight: "90vh", overflowY: "auto" }}>
|
||||||
<MissionPopupContent mission={mission} />
|
<MissionPopupContent mission={mission} hpgNeedsAttention={needsAction} />
|
||||||
</div>
|
</div>
|
||||||
</SmartPopup>
|
</SmartPopup>
|
||||||
)}
|
)}
|
||||||
@@ -378,8 +367,7 @@ const MissionMarker = ({ mission }: { mission: Mission }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const MissionLayer = () => {
|
export const MissionLayer = () => {
|
||||||
const dispatcherConnected =
|
const dispatcherConnected = useDispatchConnectionStore((s) => s.status) === "connected";
|
||||||
useDispatchConnectionStore((s) => s.status) === "connected";
|
|
||||||
const { data: missions = [] } = useQuery({
|
const { data: missions = [] } = useQuery({
|
||||||
queryKey: ["missions"],
|
queryKey: ["missions"],
|
||||||
queryFn: () =>
|
queryFn: () =>
|
||||||
|
|||||||
@@ -31,16 +31,18 @@ import {
|
|||||||
import { usePannelStore } from "_store/pannelStore";
|
import { usePannelStore } from "_store/pannelStore";
|
||||||
import { useSession } from "next-auth/react";
|
import { useSession } from "next-auth/react";
|
||||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||||
import {
|
import { deleteMissionAPI, editMissionAPI, sendMissionAPI } from "querys/missions";
|
||||||
deleteMissionAPI,
|
|
||||||
editMissionAPI,
|
|
||||||
sendMissionAPI,
|
|
||||||
} from "querys/missions";
|
|
||||||
import { getConnectedAircraftsAPI } from "querys/aircrafts";
|
import { getConnectedAircraftsAPI } from "querys/aircrafts";
|
||||||
import { getStationsAPI } from "querys/stations";
|
import { getStationsAPI } from "querys/stations";
|
||||||
import { useDispatchConnectionStore } from "_store/dispatch/connectionStore";
|
import { useDispatchConnectionStore } from "_store/dispatch/connectionStore";
|
||||||
|
|
||||||
const Einsatzdetails = ({ mission }: { mission: Mission }) => {
|
const Einsatzdetails = ({
|
||||||
|
mission,
|
||||||
|
hpgNeedsAttention,
|
||||||
|
}: {
|
||||||
|
mission: Mission;
|
||||||
|
hpgNeedsAttention?: boolean;
|
||||||
|
}) => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const deleteMissionMutation = useMutation({
|
const deleteMissionMutation = useMutation({
|
||||||
mutationKey: ["missions"],
|
mutationKey: ["missions"],
|
||||||
@@ -67,13 +69,8 @@ const Einsatzdetails = ({ mission }: { mission: Mission }) => {
|
|||||||
});
|
});
|
||||||
const editMissionMutation = useMutation({
|
const editMissionMutation = useMutation({
|
||||||
mutationKey: ["missions"],
|
mutationKey: ["missions"],
|
||||||
mutationFn: ({
|
mutationFn: ({ id, mission }: { id: number; mission: Prisma.MissionUpdateInput }) =>
|
||||||
id,
|
editMissionAPI(id, mission),
|
||||||
mission,
|
|
||||||
}: {
|
|
||||||
id: number;
|
|
||||||
mission: Prisma.MissionUpdateInput;
|
|
||||||
}) => editMissionAPI(id, mission),
|
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
toast.success("Gespeichert");
|
toast.success("Gespeichert");
|
||||||
queryClient.invalidateQueries({
|
queryClient.invalidateQueries({
|
||||||
@@ -81,9 +78,9 @@ const Einsatzdetails = ({ mission }: { mission: Mission }) => {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const dispatcherConnected =
|
const dispatcherConnected = useDispatchConnectionStore((s) => s.status) === "connected";
|
||||||
useDispatchConnectionStore((s) => s.status) === "connected";
|
|
||||||
const { setMissionFormValues, setOpen } = usePannelStore((state) => state);
|
const { setMissionFormValues, setOpen } = usePannelStore((state) => state);
|
||||||
|
const [ignoreHpg, setIgnoreHpg] = useState(false);
|
||||||
return (
|
return (
|
||||||
<div className="p-4 text-base-content">
|
<div className="p-4 text-base-content">
|
||||||
<div className="flex items-center justify-between mb-3">
|
<div className="flex items-center justify-between mb-3">
|
||||||
@@ -170,6 +167,8 @@ const Einsatzdetails = ({ mission }: { mission: Mission }) => {
|
|||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
className="checkbox checkbox-sm checkbox-primary"
|
className="checkbox checkbox-sm checkbox-primary"
|
||||||
|
checked={ignoreHpg}
|
||||||
|
onChange={(e) => setIgnoreHpg(e.target.checked)}
|
||||||
/>
|
/>
|
||||||
<span className="label-text font-semibold leading-6">
|
<span className="label-text font-semibold leading-6">
|
||||||
Ohne HPG-Mission alarmieren
|
Ohne HPG-Mission alarmieren
|
||||||
@@ -178,9 +177,7 @@ const Einsatzdetails = ({ mission }: { mission: Mission }) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center gap-2 w-full">
|
<div className="flex items-center gap-2 w-full">
|
||||||
{(mission.hpgValidationState === HpgValidationState.VALID ||
|
{(!hpgNeedsAttention || ignoreHpg) && (
|
||||||
mission.hpgValidationState ===
|
|
||||||
HpgValidationState.NOT_VALIDATED) && (
|
|
||||||
<button
|
<button
|
||||||
className="btn btn-sm btn-info btn-outline flex-3"
|
className="btn btn-sm btn-info btn-outline flex-3"
|
||||||
onClick={() => sendAlertMutation.mutate(mission.id)}
|
onClick={() => sendAlertMutation.mutate(mission.id)}
|
||||||
@@ -190,45 +187,31 @@ const Einsatzdetails = ({ mission }: { mission: Mission }) => {
|
|||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
{(mission.hpgValidationState === HpgValidationState.PENDING ||
|
{hpgNeedsAttention && (
|
||||||
mission.hpgValidationState === HpgValidationState.HPG_BUSY ||
|
<button
|
||||||
mission.hpgValidationState ===
|
className="btn btn-sm btn-info btn-outline flex-3"
|
||||||
HpgValidationState.HPG_DISCONNECT ||
|
onClick={() => sendAlertMutation.mutate(mission.id)}
|
||||||
mission.hpgValidationState === HpgValidationState.INVALID ||
|
disabled
|
||||||
HpgValidationState.HPG_INVALID_MISSION) &&
|
>
|
||||||
mission.hpgValidationState !== HpgValidationState.NOT_VALIDATED &&
|
<span className="flex items-center gap-2">
|
||||||
mission.hpgValidationState !==
|
{mission.hpgValidationState === HpgValidationState.PENDING && (
|
||||||
HpgValidationState.POSITION_AMANDED &&
|
<div>
|
||||||
mission.hpgValidationState !== HpgValidationState.VALID && (
|
<span className="loading loading-spinner loading-md"></span> HPG-Validierung
|
||||||
<button
|
läuft...
|
||||||
className="btn btn-sm btn-info btn-outline flex-3"
|
</div>
|
||||||
onClick={() => sendAlertMutation.mutate(mission.id)}
|
)}
|
||||||
disabled
|
{mission.hpgValidationState === HpgValidationState.HPG_BUSY && "HPG-Client busy"}
|
||||||
>
|
{mission.hpgValidationState === HpgValidationState.HPG_DISCONNECT &&
|
||||||
<span className="flex items-center gap-2">
|
"HPG-Client nicht verbunden"}
|
||||||
{mission.hpgValidationState ===
|
{mission.hpgValidationState === HpgValidationState.INVALID &&
|
||||||
HpgValidationState.PENDING && (
|
"HPG-Client fehlerhaft"}
|
||||||
<div>
|
{mission.hpgValidationState === HpgValidationState.HPG_INVALID_MISSION &&
|
||||||
<span className="loading loading-spinner loading-md"></span>{" "}
|
"Fehlerhafte HPG-Mission"}
|
||||||
HPG-Validierung läuft...
|
</span>
|
||||||
</div>
|
</button>
|
||||||
)}
|
)}
|
||||||
{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 ===
|
{mission.hpgValidationState === HpgValidationState.POSITION_AMANDED && (
|
||||||
HpgValidationState.POSITION_AMANDED && (
|
|
||||||
<button
|
<button
|
||||||
className="btn btn-sm btn-warning btn-outline flex-3"
|
className="btn btn-sm btn-warning btn-outline flex-3"
|
||||||
onClick={() => sendAlertMutation.mutate(mission.id)}
|
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">
|
<h2 className="flex items-center gap-2 text-lg font-bold mb-3">
|
||||||
<User /> Patientendetails
|
<User /> Patientendetails
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-base-content font-semibold">
|
<p className="text-base-content font-semibold">{mission.missionPatientInfo}</p>
|
||||||
{mission.missionPatientInfo}
|
|
||||||
</p>
|
|
||||||
<div className="divider my-2" />
|
<div className="divider my-2" />
|
||||||
<h2 className="flex items-center gap-2 text-lg font-bold mb-3">
|
<h2 className="flex items-center gap-2 text-lg font-bold mb-3">
|
||||||
<Cross /> Einsatzinformationen
|
<Cross /> Einsatzinformationen
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-base-content font-semibold">
|
<p className="text-base-content font-semibold">{mission.missionAdditionalInfo}</p>
|
||||||
{mission.missionAdditionalInfo}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -303,13 +282,8 @@ const Rettungsmittel = ({ mission }: { mission: Mission }) => {
|
|||||||
});
|
});
|
||||||
const updateMissionMutation = useMutation({
|
const updateMissionMutation = useMutation({
|
||||||
mutationKey: ["missions", "stations-mission", mission.id],
|
mutationKey: ["missions", "stations-mission", mission.id],
|
||||||
mutationFn: ({
|
mutationFn: ({ id, missionEdit }: { id: number; missionEdit: Prisma.MissionUpdateInput }) =>
|
||||||
id,
|
editMissionAPI(id, missionEdit),
|
||||||
missionEdit,
|
|
||||||
}: {
|
|
||||||
id: number;
|
|
||||||
missionEdit: Prisma.MissionUpdateInput;
|
|
||||||
}) => editMissionAPI(id, missionEdit),
|
|
||||||
|
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
@@ -323,17 +297,15 @@ const Rettungsmittel = ({ mission }: { mission: Mission }) => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const { data: missionStations, refetch: refetchMissionStationIds } = useQuery(
|
const { data: missionStations, refetch: refetchMissionStationIds } = useQuery({
|
||||||
{
|
queryKey: ["stations-mission", mission.id],
|
||||||
queryKey: ["stations-mission", mission.id],
|
queryFn: () =>
|
||||||
queryFn: () =>
|
getStationsAPI({
|
||||||
getStationsAPI({
|
id: {
|
||||||
id: {
|
in: mission.missionStationIds,
|
||||||
in: mission.missionStationIds,
|
},
|
||||||
},
|
}),
|
||||||
}),
|
});
|
||||||
},
|
|
||||||
);
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
refetchMissionStationIds();
|
refetchMissionStationIds();
|
||||||
}, [mission.missionStationIds, refetchMissionStationIds]);
|
}, [mission.missionStationIds, refetchMissionStationIds]);
|
||||||
@@ -426,22 +398,14 @@ const Rettungsmittel = ({ mission }: { mission: Mission }) => {
|
|||||||
<select
|
<select
|
||||||
className="select select-sm select-primary select-bordered flex-1"
|
className="select select-sm select-primary select-bordered flex-1"
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const selected = allStations?.find(
|
const selected = allStations?.find((s) => s.id.toString() === e.target.value);
|
||||||
(s) => s.id.toString() === e.target.value,
|
|
||||||
);
|
|
||||||
if (selected) {
|
if (selected) {
|
||||||
setSelectedStation(selected);
|
setSelectedStation(selected);
|
||||||
} else {
|
} else {
|
||||||
setSelectedStation(
|
setSelectedStation(e.target.value as "ambulance" | "police" | "firebrigade");
|
||||||
e.target.value as "ambulance" | "police" | "firebrigade",
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
value={
|
value={typeof selectedStation === "string" ? selectedStation : selectedStation?.id}
|
||||||
typeof selectedStation === "string"
|
|
||||||
? selectedStation
|
|
||||||
: selectedStation?.id
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
{allStations
|
{allStations
|
||||||
?.filter((s) => !mission.missionStationIds.includes(s.id))
|
?.filter((s) => !mission.missionStationIds.includes(s.id))
|
||||||
@@ -498,17 +462,11 @@ const FMSStatusHistory = ({ mission }: { mission: Mission }) => {
|
|||||||
const [note, setNote] = useState("");
|
const [note, setNote] = useState("");
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
const dispatcherConnected =
|
const dispatcherConnected = useDispatchConnectionStore((s) => s.status) === "connected";
|
||||||
useDispatchConnectionStore((s) => s.status) === "connected";
|
|
||||||
|
|
||||||
const editMissionMutation = useMutation({
|
const editMissionMutation = useMutation({
|
||||||
mutationFn: ({
|
mutationFn: ({ id, mission }: { id: number; mission: Partial<Prisma.MissionUpdateInput> }) =>
|
||||||
id,
|
editMissionAPI(id, mission),
|
||||||
mission,
|
|
||||||
}: {
|
|
||||||
id: number;
|
|
||||||
mission: Partial<Prisma.MissionUpdateInput>;
|
|
||||||
}) => editMissionAPI(id, mission),
|
|
||||||
mutationKey: ["missions"],
|
mutationKey: ["missions"],
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
queryClient.invalidateQueries({ queryKey: ["missions"] });
|
queryClient.invalidateQueries({ queryKey: ["missions"] });
|
||||||
@@ -608,9 +566,7 @@ const FMSStatusHistory = ({ mission }: { mission: Mission }) => {
|
|||||||
>
|
>
|
||||||
{entry.data.newFMSstatus}
|
{entry.data.newFMSstatus}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-base-content">
|
<span className="text-base-content">{entry.data.station.bosCallsign}</span>
|
||||||
{entry.data.station.bosCallsign}
|
|
||||||
</span>
|
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
if (entry.type === "message-log" || entry.type === "sds-log")
|
if (entry.type === "message-log" || entry.type === "sds-log")
|
||||||
@@ -651,9 +607,7 @@ const FMSStatusHistory = ({ mission }: { mission: Mission }) => {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-base-content">
|
<span className="text-base-content">{entry.data.message}</span>
|
||||||
{entry.data.message}
|
|
||||||
</span>
|
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -5,10 +5,7 @@ import { zodResolver } from "@hookform/resolvers/zod";
|
|||||||
import { BellRing, BookmarkPlus } from "lucide-react";
|
import { BellRing, BookmarkPlus } from "lucide-react";
|
||||||
import { Select } from "_components/Select";
|
import { Select } from "_components/Select";
|
||||||
import { KEYWORD_CATEGORY, missionType, Prisma } from "@repo/db";
|
import { KEYWORD_CATEGORY, missionType, Prisma } from "@repo/db";
|
||||||
import {
|
import { MissionOptionalDefaults, MissionOptionalDefaultsSchema } from "@repo/db/zod";
|
||||||
MissionOptionalDefaults,
|
|
||||||
MissionOptionalDefaultsSchema,
|
|
||||||
} from "@repo/db/zod";
|
|
||||||
import { usePannelStore } from "_store/pannelStore";
|
import { usePannelStore } from "_store/pannelStore";
|
||||||
import { useSession } from "next-auth/react";
|
import { useSession } from "next-auth/react";
|
||||||
import { toast } from "react-hot-toast";
|
import { toast } from "react-hot-toast";
|
||||||
@@ -23,10 +20,10 @@ import { getKeywordsAPI } from "querys/keywords";
|
|||||||
import { getStationsAPI } from "querys/stations";
|
import { getStationsAPI } from "querys/stations";
|
||||||
import { useMapStore } from "_store/mapStore";
|
import { useMapStore } from "_store/mapStore";
|
||||||
import { getConnectedAircraftsAPI } from "querys/aircrafts";
|
import { getConnectedAircraftsAPI } from "querys/aircrafts";
|
||||||
|
import { HPGValidationRequired } from "helpers/hpgValidationRequired";
|
||||||
|
|
||||||
export const MissionForm = () => {
|
export const MissionForm = () => {
|
||||||
const { isEditingMission, editingMissionId, setEditingMission } =
|
const { isEditingMission, editingMissionId, setEditingMission } = usePannelStore();
|
||||||
usePannelStore();
|
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const setSeachOSMElements = useMapStore((s) => s.setSearchElements);
|
const setSeachOSMElements = useMapStore((s) => s.setSearchElements);
|
||||||
|
|
||||||
@@ -57,13 +54,8 @@ export const MissionForm = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const editMissionMutation = useMutation({
|
const editMissionMutation = useMutation({
|
||||||
mutationFn: ({
|
mutationFn: ({ id, mission }: { id: number; mission: Partial<Prisma.MissionUpdateInput> }) =>
|
||||||
id,
|
editMissionAPI(id, mission),
|
||||||
mission,
|
|
||||||
}: {
|
|
||||||
id: number;
|
|
||||||
mission: Partial<Prisma.MissionUpdateInput>;
|
|
||||||
}) => editMissionAPI(id, mission),
|
|
||||||
mutationKey: ["missions"],
|
mutationKey: ["missions"],
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
queryClient.invalidateQueries({
|
queryClient.invalidateQueries({
|
||||||
@@ -113,11 +105,11 @@ export const MissionForm = () => {
|
|||||||
});
|
});
|
||||||
const { missionFormValues, setOpen } = usePannelStore((state) => state);
|
const { missionFormValues, setOpen } = usePannelStore((state) => state);
|
||||||
|
|
||||||
const validationRequired = /* form.watch("missionStationIds")?.some((id) => {
|
const validationRequired = HPGValidationRequired(
|
||||||
const aircraft = aircrafts?.find((a) => a.stationId === id);
|
form.watch("missionStationIds"),
|
||||||
|
aircrafts,
|
||||||
return aircraft?.posH145active;
|
form.watch("hpgMissionString"),
|
||||||
}) && form.watch("hpgMissionString")?.length !== 0; */ true;
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (session.data?.user.id) {
|
if (session.data?.user.id) {
|
||||||
@@ -159,8 +151,7 @@ export const MissionForm = () => {
|
|||||||
disabled
|
disabled
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{(form.formState.errors.addressLat ||
|
{(form.formState.errors.addressLat || form.formState.errors.addressLng) && (
|
||||||
form.formState.errors.addressLng) && (
|
|
||||||
<p className="text-error">
|
<p className="text-error">
|
||||||
Bitte wähle eine Postion übder das Context-Menu über der Karte aus.
|
Bitte wähle eine Postion übder das Context-Menu über der Karte aus.
|
||||||
</p>
|
</p>
|
||||||
@@ -236,10 +227,7 @@ export const MissionForm = () => {
|
|||||||
{...form.register("missionKeywordCategory")}
|
{...form.register("missionKeywordCategory")}
|
||||||
className="select select-primary select-bordered w-full mb-4"
|
className="select select-primary select-bordered w-full mb-4"
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
form.setValue(
|
form.setValue("missionKeywordCategory", e.target.value as string);
|
||||||
"missionKeywordCategory",
|
|
||||||
e.target.value as string,
|
|
||||||
);
|
|
||||||
form.setValue("missionKeywordName", null);
|
form.setValue("missionKeywordName", null);
|
||||||
form.setValue("missionKeywordAbbreviation", "");
|
form.setValue("missionKeywordAbbreviation", "");
|
||||||
form.setValue("hpgMissionString", "");
|
form.setValue("hpgMissionString", "");
|
||||||
@@ -259,14 +247,9 @@ export const MissionForm = () => {
|
|||||||
{...form.register("missionKeywordAbbreviation")}
|
{...form.register("missionKeywordAbbreviation")}
|
||||||
className="select select-primary select-bordered w-full mb-4"
|
className="select select-primary select-bordered w-full mb-4"
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const keyword = keywords?.find(
|
const keyword = keywords?.find((k) => k.abreviation === e.target.value);
|
||||||
(k) => k.abreviation === e.target.value,
|
|
||||||
);
|
|
||||||
form.setValue("missionKeywordName", keyword?.name || null);
|
form.setValue("missionKeywordName", keyword?.name || null);
|
||||||
form.setValue(
|
form.setValue("missionKeywordAbbreviation", keyword?.abreviation || null);
|
||||||
"missionKeywordAbbreviation",
|
|
||||||
keyword?.abreviation || null,
|
|
||||||
);
|
|
||||||
form.setValue("hpgMissionString", "default");
|
form.setValue("hpgMissionString", "default");
|
||||||
}}
|
}}
|
||||||
defaultValue="default"
|
defaultValue="default"
|
||||||
@@ -276,9 +259,7 @@ export const MissionForm = () => {
|
|||||||
</option>
|
</option>
|
||||||
{keywords &&
|
{keywords &&
|
||||||
keywords
|
keywords
|
||||||
.filter(
|
.filter((k) => k.category === form.watch("missionKeywordCategory"))
|
||||||
(k) => k.category === form.watch("missionKeywordCategory"),
|
|
||||||
)
|
|
||||||
.map((keyword) => (
|
.map((keyword) => (
|
||||||
<option key={keyword.id} value={keyword.abreviation}>
|
<option key={keyword.id} value={keyword.abreviation}>
|
||||||
{keyword.name}
|
{keyword.name}
|
||||||
@@ -308,9 +289,7 @@ export const MissionForm = () => {
|
|||||||
})}
|
})}
|
||||||
</select>
|
</select>
|
||||||
{validationRequired && (
|
{validationRequired && (
|
||||||
<p className="text-sm text-warning">
|
<p className="text-sm text-warning">Szenario wird vor Alarmierung HPG-Validiert.</p>
|
||||||
Szenario wird vor Alarmierung HPG-Validiert.
|
|
||||||
</p>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
@@ -339,8 +318,7 @@ export const MissionForm = () => {
|
|||||||
|
|
||||||
{missionFormValues?.addressOSMways?.length && (
|
{missionFormValues?.addressOSMways?.length && (
|
||||||
<p className="text-sm text-info">
|
<p className="text-sm text-info">
|
||||||
In diesem Einsatz gibt es {missionFormValues?.addressOSMways?.length}{" "}
|
In diesem Einsatz gibt es {missionFormValues?.addressOSMways?.length} Gebäude
|
||||||
Gebäude
|
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -350,37 +328,33 @@ export const MissionForm = () => {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="btn btn-primary flex-1"
|
className="btn btn-primary flex-1"
|
||||||
onClick={form.handleSubmit(
|
onClick={form.handleSubmit(async (mission: MissionOptionalDefaults) => {
|
||||||
async (mission: MissionOptionalDefaults) => {
|
try {
|
||||||
try {
|
const hpgSzenario = mission.hpgMissionString?.split(":")[0];
|
||||||
const hpgSzenario = mission.hpgMissionString?.split(":")[0];
|
const newMission = await editMissionMutation.mutateAsync({
|
||||||
const newMission = await editMissionMutation.mutateAsync({
|
id: Number(editingMissionId),
|
||||||
id: Number(editingMissionId),
|
mission: {
|
||||||
mission: {
|
...(mission as unknown as Prisma.MissionCreateInput),
|
||||||
...(mission as unknown as Prisma.MissionCreateInput),
|
missionAdditionalInfo:
|
||||||
missionAdditionalInfo:
|
!mission.missionAdditionalInfo.length && hpgSzenario
|
||||||
!mission.missionAdditionalInfo.length && hpgSzenario
|
? `HPG-Szenario: ${hpgSzenario}`
|
||||||
? `HPG-Szenario: ${hpgSzenario}`
|
: mission.missionAdditionalInfo,
|
||||||
: mission.missionAdditionalInfo,
|
},
|
||||||
},
|
});
|
||||||
});
|
if (validationRequired) {
|
||||||
if (validationRequired) {
|
await startHpgValidation(newMission.id);
|
||||||
await startHpgValidation(newMission.id);
|
|
||||||
}
|
|
||||||
toast.success(
|
|
||||||
`Einsatz ${newMission.id} erfolgreich aktualisiert`,
|
|
||||||
);
|
|
||||||
setSeachOSMElements([]); // Reset search elements
|
|
||||||
setEditingMission(false, null); // Reset editing state
|
|
||||||
form.reset(); // Reset the form
|
|
||||||
setOpen(false);
|
|
||||||
} catch (error) {
|
|
||||||
toast.error(
|
|
||||||
`Fehler beim Aktualisieren des Einsatzes: ${(error as Error).message}`,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
},
|
toast.success(`Einsatz ${newMission.id} erfolgreich aktualisiert`);
|
||||||
)}
|
setSeachOSMElements([]); // Reset search elements
|
||||||
|
setEditingMission(false, null); // Reset editing state
|
||||||
|
form.reset(); // Reset the form
|
||||||
|
setOpen(false);
|
||||||
|
} catch (error) {
|
||||||
|
toast.error(
|
||||||
|
`Fehler beim Aktualisieren des Einsatzes: ${(error as Error).message}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})}
|
||||||
>
|
>
|
||||||
Änderungen speichern
|
Änderungen speichern
|
||||||
</button>
|
</button>
|
||||||
@@ -389,66 +363,54 @@ export const MissionForm = () => {
|
|||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
className="btn btn-warning"
|
className="btn btn-warning"
|
||||||
onClick={form.handleSubmit(
|
onClick={form.handleSubmit(async (mission: MissionOptionalDefaults) => {
|
||||||
async (mission: MissionOptionalDefaults) => {
|
try {
|
||||||
try {
|
const hpgSzenario = mission.hpgMissionString?.split(":")[0];
|
||||||
const hpgSzenario =
|
const newMission = await createMissionMutation.mutateAsync({
|
||||||
mission.hpgMissionString?.split(":")[0];
|
...(mission as unknown as Prisma.MissionCreateInput),
|
||||||
const newMission =
|
missionAdditionalInfo:
|
||||||
await createMissionMutation.mutateAsync({
|
!mission.missionAdditionalInfo.length && hpgSzenario
|
||||||
...(mission as unknown as Prisma.MissionCreateInput),
|
? `HPG-Szenario: ${hpgSzenario}`
|
||||||
missionAdditionalInfo:
|
: mission.missionAdditionalInfo,
|
||||||
!mission.missionAdditionalInfo.length && hpgSzenario
|
});
|
||||||
? `HPG-Szenario: ${hpgSzenario}`
|
if (validationRequired) {
|
||||||
: mission.missionAdditionalInfo,
|
await startHpgValidation(newMission.id, {
|
||||||
});
|
alertWhenValid: true,
|
||||||
if (validationRequired) {
|
});
|
||||||
await startHpgValidation(newMission.id, {
|
} else {
|
||||||
alertWhenValid: true,
|
await sendAlertMutation.mutateAsync(newMission.id);
|
||||||
});
|
|
||||||
} else {
|
|
||||||
await sendAlertMutation.mutateAsync(newMission.id);
|
|
||||||
}
|
|
||||||
setSeachOSMElements([]); // Reset search elements
|
|
||||||
setOpen(false);
|
|
||||||
} catch (error) {
|
|
||||||
toast.error(
|
|
||||||
`Fehler beim Erstellen des Einsatzes: ${(error as Error).message}`,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
},
|
setSeachOSMElements([]); // Reset search elements
|
||||||
)}
|
setOpen(false);
|
||||||
|
} catch (error) {
|
||||||
|
toast.error(`Fehler beim Erstellen des Einsatzes: ${(error as Error).message}`);
|
||||||
|
}
|
||||||
|
})}
|
||||||
>
|
>
|
||||||
<BellRing className="h-4 w-4" /> Alarmieren
|
<BellRing className="h-4 w-4" /> Alarmieren
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
className="btn btn-primary flex-1"
|
className="btn btn-primary flex-1"
|
||||||
onClick={form.handleSubmit(
|
onClick={form.handleSubmit(async (mission: MissionOptionalDefaults) => {
|
||||||
async (mission: MissionOptionalDefaults) => {
|
try {
|
||||||
try {
|
const hpgSzenario = mission.hpgMissionString?.split(":")[0];
|
||||||
const hpgSzenario =
|
const newMission = await createMissionMutation.mutateAsync({
|
||||||
mission.hpgMissionString?.split(":")[0];
|
...(mission as unknown as Prisma.MissionCreateInput),
|
||||||
const newMission =
|
missionAdditionalInfo:
|
||||||
await createMissionMutation.mutateAsync({
|
!mission.missionAdditionalInfo.length && hpgSzenario
|
||||||
...(mission as unknown as Prisma.MissionCreateInput),
|
? `HPG-Szenario: ${hpgSzenario}`
|
||||||
missionAdditionalInfo:
|
: mission.missionAdditionalInfo,
|
||||||
!mission.missionAdditionalInfo.length && hpgSzenario
|
});
|
||||||
? `HPG-Szenario: ${hpgSzenario}`
|
setSeachOSMElements([]); // Reset search elements
|
||||||
: mission.missionAdditionalInfo,
|
await startHpgValidation(newMission.id);
|
||||||
});
|
toast.success(`Einsatz ${newMission.publicId} erstellt`);
|
||||||
setSeachOSMElements([]); // Reset search elements
|
form.reset();
|
||||||
await startHpgValidation(newMission.id);
|
setOpen(false);
|
||||||
toast.success(`Einsatz ${newMission.publicId} erstellt`);
|
} catch (error) {
|
||||||
form.reset();
|
toast.error(`Fehler beim Erstellen des Einsatzes: ${(error as Error).message}`);
|
||||||
setOpen(false);
|
}
|
||||||
} catch (error) {
|
})}
|
||||||
toast.error(
|
|
||||||
`Fehler beim Erstellen des Einsatzes: ${(error as Error).message}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)}
|
|
||||||
>
|
>
|
||||||
<BookmarkPlus className="h-5 w-5" /> Einsatz vorbereiten
|
<BookmarkPlus className="h-5 w-5" /> Einsatz vorbereiten
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
15
apps/dispatch/app/helpers/hpgValidationRequired.ts
Normal file
15
apps/dispatch/app/helpers/hpgValidationRequired.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { ConnectedAircraft } from "@repo/db";
|
||||||
|
|
||||||
|
export const HPGValidationRequired = (
|
||||||
|
missionStationIds?: number[],
|
||||||
|
aircrafts?: ConnectedAircraft[],
|
||||||
|
hpgMissionString?: string | null,
|
||||||
|
) => {
|
||||||
|
return (
|
||||||
|
missionStationIds?.some((id) => {
|
||||||
|
const aircraft = aircrafts?.find((a) => a.stationId === id);
|
||||||
|
|
||||||
|
return aircraft?.posH145active;
|
||||||
|
}) && hpgMissionString?.length !== 0
|
||||||
|
);
|
||||||
|
};
|
||||||
Binary file not shown.
Reference in New Issue
Block a user