diff --git a/apps/dispatch-server/routes/mission.ts b/apps/dispatch-server/routes/mission.ts index 3c6f906c..ff9705f1 100644 --- a/apps/dispatch-server/routes/mission.ts +++ b/apps/dispatch-server/routes/mission.ts @@ -87,6 +87,20 @@ router.patch("/:id", async (req, res) => { data: req.body, }); io.to("dispatchers").emit("update-mission", { updatedMission }); + if (req.body.state === "finished") { + updatedMission.missionStationUserIds?.forEach((userId) => { + io.to(`user:${userId}`).emit("notification", { + type: "mission-closed", + status: "closed", + message: `Einsatz ${updatedMission.publicId} wurde beendet`, + data: { + missionId: updatedMission.id, + publicMissionId: updatedMission.publicId, + }, + } as NotificationPayload); + }); + } + res.json(updatedMission); } catch (error) { console.error(error); diff --git a/apps/dispatch-server/socket-events/connect-pilot.ts b/apps/dispatch-server/socket-events/connect-pilot.ts index d8710639..bd9fcc85 100644 --- a/apps/dispatch-server/socket-events/connect-pilot.ts +++ b/apps/dispatch-server/socket-events/connect-pilot.ts @@ -97,6 +97,7 @@ export const handleConnectPilot = posLat: randomPos?.lat, posLng: randomPos?.lng, posXplanePluginActive: debug ? true : undefined, + posH145active: debug ? true : undefined, }, }); diff --git a/apps/dispatch/app/(app)/dispatch/_components/pannel/MissionForm.tsx b/apps/dispatch/app/(app)/dispatch/_components/pannel/MissionForm.tsx index 9fc6e487..2c53cfa6 100644 --- a/apps/dispatch/app/(app)/dispatch/_components/pannel/MissionForm.tsx +++ b/apps/dispatch/app/(app)/dispatch/_components/pannel/MissionForm.tsx @@ -119,11 +119,12 @@ export const MissionForm = () => { }); const { missionFormValues, setOpen } = usePannelStore((state) => state); - const validationRequired = HPGValidationRequired( - form.watch("missionStationIds"), - aircrafts, - form.watch("hpgMissionString"), - ); + const validationRequired = + HPGValidationRequired( + form.watch("missionStationIds"), + aircrafts, + form.watch("hpgMissionString"), + ) && !form.watch("hpgMissionString")?.startsWith("kein Szenario"); useEffect(() => { if (session.data?.user.id) { @@ -145,6 +146,7 @@ export const MissionForm = () => { return; } for (const key in missionFormValues) { + console.debug(key, missionFormValues[key as keyof MissionOptionalDefaults]); if (key === "addressOSMways") continue; // Skip addressOSMways as it is handled separately form.setValue( key as keyof MissionOptionalDefaults, @@ -370,6 +372,7 @@ export const MissionForm = () => { + {keywords && keywords .find((k) => k.name === form.watch("missionKeywordName")) @@ -416,11 +419,20 @@ export const MissionForm = () => { In diesem Einsatz gibt es {form.watch("addressOSMways").length} Gebäude

-

- In diesem Einsatz gibt es {form.watch("xPlaneObjects").length} Objekte -

+
+

+ In diesem Einsatz gibt es {form.watch("xPlaneObjects").length} Objekte +

+ +
diff --git a/apps/dispatch/app/_components/map/ContextMenu.tsx b/apps/dispatch/app/_components/map/ContextMenu.tsx index d4b70496..874ea0fd 100644 --- a/apps/dispatch/app/_components/map/ContextMenu.tsx +++ b/apps/dispatch/app/_components/map/ContextMenu.tsx @@ -3,17 +3,7 @@ import { OSMWay } from "@repo/db"; import { useDispatchConnectionStore } from "_store/dispatch/connectionStore"; import { useMapStore } from "_store/mapStore"; import { usePannelStore } from "_store/pannelStore"; -import { XplaneObject } from "@repo/db"; -import { - MapPin, - MapPinned, - Radius, - Search, - RulerDimensionLine, - Scan, - Car, - Ambulance, -} from "lucide-react"; +import { MapPin, MapPinned, Search, Car, Ambulance, Siren, Flame } from "lucide-react"; import { getOsmAddress } from "_querys/osm"; import { useEffect, useState } from "react"; import toast from "react-hot-toast"; @@ -43,15 +33,16 @@ export const ContextMenu = () => { setOpen, isOpen: isPannelOpen, } = usePannelStore((state) => state); - const [showRulerOptions, setShowRulerOptions] = useState(false); + const [showObjectOptions, setShowObjectOptions] = useState(false); const [rulerHover, setRulerHover] = useState(false); const [rulerOptionsHover, setRulerOptionsHover] = useState(false); const dispatcherConnected = useDispatchConnectionStore((s) => s.status) === "connected"; useEffect(() => { - setShowRulerOptions(rulerHover || rulerOptionsHover); - }, [rulerHover, rulerOptionsHover]); + const showObjectOptions = rulerHover || rulerOptionsHover; + setShowObjectOptions(showObjectOptions); + }, [isPannelOpen, rulerHover, rulerOptionsHover, setOpen]); useEffect(() => { const handleContextMenu = (e: any) => { @@ -198,8 +189,8 @@ export const ContextMenu = () => { > - {/* Ruler Options - shown when Ruler button is hovered or options are hovered */} - {showRulerOptions && ( + {/* XPlane Object Options - shown when Ruler button is hovered or options are hovered */} + {showObjectOptions && (
setRulerOptionsHover(true)} @@ -207,8 +198,8 @@ export const ContextMenu = () => { >
diff --git a/apps/dispatch/app/_components/map/MapAdditionals.tsx b/apps/dispatch/app/_components/map/MapAdditionals.tsx index f60a2829..6dea267e 100644 --- a/apps/dispatch/app/_components/map/MapAdditionals.tsx +++ b/apps/dispatch/app/_components/map/MapAdditionals.tsx @@ -8,6 +8,8 @@ import { HPGValidationRequired } from "_helpers/hpgValidationRequired"; import { getConnectedAircraftsAPI } from "_querys/aircrafts"; import { useMapStore } from "_store/mapStore"; import { useDispatchConnectionStore } from "_store/dispatch/connectionStore"; +import { is } from "date-fns/locale"; +import { XplaneObject } from "@repo/db"; export const MapAdditionals = () => { const { isOpen, missionFormValues } = usePannelStore((state) => state); @@ -53,6 +55,20 @@ export const MapAdditionals = () => { interactive={false} /> )} + {isOpen && + missionFormValues?.xPlaneObjects && + (missionFormValues.xPlaneObjects as unknown as XplaneObject[]).map((obj, index) => ( + + ))} {markersNeedingAttention.map((mission) => ( { return missionStationIds?.some((id) => { const aircraft = aircrafts?.find((a) => a.stationId === id); - return aircraft?.posXplanePluginActive; }); }; diff --git a/apps/dispatch/public/icons/ambulance.png b/apps/dispatch/public/icons/ambulance.png new file mode 100644 index 00000000..114a9abf Binary files /dev/null and b/apps/dispatch/public/icons/ambulance.png differ diff --git a/apps/dispatch/public/icons/fire_engine.png b/apps/dispatch/public/icons/fire_engine.png new file mode 100644 index 00000000..2f11c4e4 Binary files /dev/null and b/apps/dispatch/public/icons/fire_engine.png differ diff --git a/apps/dispatch/public/icons/police.png b/apps/dispatch/public/icons/police.png new file mode 100644 index 00000000..bd0132c4 Binary files /dev/null and b/apps/dispatch/public/icons/police.png differ diff --git a/packages/database/prisma/json/SocketEvents.ts b/packages/database/prisma/json/SocketEvents.ts index 8aa1672e..1fe7dfe7 100644 --- a/packages/database/prisma/json/SocketEvents.ts +++ b/packages/database/prisma/json/SocketEvents.ts @@ -50,9 +50,20 @@ export type MissionAutoClose = { }; }; +export type MissionClosed = { + type: "mission-closed"; + status: "closed"; + message: string; + data: { + missionId: number; + publicMissionId: string; + }; +}; + export type NotificationPayload = | ValidationFailed | ValidationSuccess | AdminMessage | StationStatus - | MissionAutoClose; + | MissionAutoClose + | MissionClosed;