diff --git a/apps/dispatch-server/routes/mission.ts b/apps/dispatch-server/routes/mission.ts index 94208122..c44c7c69 100644 --- a/apps/dispatch-server/routes/mission.ts +++ b/apps/dispatch-server/routes/mission.ts @@ -235,6 +235,7 @@ router.post("/:id/validate-hpg", async (req, res) => { } }, ); */ + // TODO: remove this after testing setTimeout(() => { io.to(`user:${req.user?.id}`).emit("notification", { type: "hpg-validation", diff --git a/apps/dispatch-server/socket-events/connect-pilot.ts b/apps/dispatch-server/socket-events/connect-pilot.ts index b07e3f91..bf8670d9 100644 --- a/apps/dispatch-server/socket-events/connect-pilot.ts +++ b/apps/dispatch-server/socket-events/connect-pilot.ts @@ -4,13 +4,7 @@ import { Server, Socket } from "socket.io"; export const handleConnectPilot = (socket: Socket, io: Server) => - async ({ - logoffTime, - stationId, - }: { - logoffTime: string; - stationId: string; - }) => { + async ({ logoffTime, stationId }: { logoffTime: string; stationId: string }) => { try { const user = socket.data.user; // 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) { - await io - .to(`user:${user.id}`) - .emit("force-disconnect", "double-connection"); + await io.to(`user:${user.id}`).emit("force-disconnect", "double-connection"); await prisma.connectedAircraft.updateMany({ where: { userId: user.id, @@ -72,6 +64,7 @@ export const handleConnectPilot = stationId: parseInt(stationId), posLat: 51.45, posLng: 9.77, + posH145active: true, }, }); diff --git a/apps/dispatch/app/_components/map/MissionMarkers.tsx b/apps/dispatch/app/_components/map/MissionMarkers.tsx index 8b0b8d6f..8b402115 100644 --- a/apps/dispatch/app/_components/map/MissionMarkers.tsx +++ b/apps/dispatch/app/_components/map/MissionMarkers.tsx @@ -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 = - { - draft: "#0092b8", - running: "#155dfc", - finished: "#155dfc", - attention: "rgb(186,105,0)", - }; +export const MISSION_STATUS_COLORS: Record = { + draft: "#0092b8", + running: "#155dfc", + finished: "#155dfc", + attention: "rgb(186,105,0)", +}; export const MISSION_STATUS_TEXT_COLORS: Record = { draft: "#00d3f2", @@ -48,12 +31,17 @@ export const MISSION_STATUS_TEXT_COLORS: Record = { 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 ; + return ; case "details": return ; 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(null); const popupRef = useRef(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 `
- +
)} @@ -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: () => diff --git a/apps/dispatch/app/_components/map/_components/MissionMarkerTabs.tsx b/apps/dispatch/app/_components/map/_components/MissionMarkerTabs.tsx index 08ca01ac..83515e1f 100644 --- a/apps/dispatch/app/_components/map/_components/MissionMarkerTabs.tsx +++ b/apps/dispatch/app/_components/map/_components/MissionMarkerTabs.tsx @@ -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 (
@@ -170,6 +167,8 @@ const Einsatzdetails = ({ mission }: { mission: Mission }) => { setIgnoreHpg(e.target.checked)} /> Ohne HPG-Mission alarmieren @@ -178,9 +177,7 @@ const Einsatzdetails = ({ mission }: { mission: Mission }) => {
- {(mission.hpgValidationState === HpgValidationState.VALID || - mission.hpgValidationState === - HpgValidationState.NOT_VALIDATED) && ( + {(!hpgNeedsAttention || ignoreHpg) && ( )} - {(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 && ( - - )} + {hpgNeedsAttention && ( + + )} - {mission.hpgValidationState === - HpgValidationState.POSITION_AMANDED && ( + {mission.hpgValidationState === HpgValidationState.POSITION_AMANDED && (
@@ -339,8 +318,7 @@ export const MissionForm = () => { {missionFormValues?.addressOSMways?.length && (

- In diesem Einsatz gibt es {missionFormValues?.addressOSMways?.length}{" "} - Gebäude + In diesem Einsatz gibt es {missionFormValues?.addressOSMways?.length} Gebäude

)} @@ -350,37 +328,33 @@ export const MissionForm = () => { @@ -389,66 +363,54 @@ export const MissionForm = () => { diff --git a/apps/dispatch/app/helpers/hpgValidationRequired.ts b/apps/dispatch/app/helpers/hpgValidationRequired.ts new file mode 100644 index 00000000..c91157d2 --- /dev/null +++ b/apps/dispatch/app/helpers/hpgValidationRequired.ts @@ -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 + ); +}; diff --git a/grafana/grafana.db b/grafana/grafana.db index 1905c35b..ab545419 100644 Binary files a/grafana/grafana.db and b/grafana/grafana.db differ