diff --git a/apps/dispatch-server/socket-events/connect-pilot.ts b/apps/dispatch-server/socket-events/connect-pilot.ts index 6e969142..2aacc23a 100644 --- a/apps/dispatch-server/socket-events/connect-pilot.ts +++ b/apps/dispatch-server/socket-events/connect-pilot.ts @@ -5,7 +5,15 @@ import { Server, Socket } from "socket.io"; export const handleConnectPilot = (socket: Socket, io: Server) => - async ({ logoffTime, stationId }: { logoffTime: string; stationId: string }) => { + async ({ + logoffTime, + stationId, + debug, + }: { + logoffTime: string; + stationId: string; + debug: boolean; + }) => { try { if (!stationId) return Error("Station ID is required"); const user: User = socket.data.user; // User ID aus dem JWT-Token @@ -78,8 +86,7 @@ export const handleConnectPilot = return { lat, lng }; } - const randomPos = - process.env.environment === "development" ? getRandomGermanPosition() : undefined; + const randomPos = debug ? getRandomGermanPosition() : undefined; const connectedAircraftEntry = await prisma.connectedAircraft.create({ data: { @@ -87,8 +94,7 @@ export const handleConnectPilot = esimatedLogoutTime: parsedLogoffDate?.toISOString() || null, userId: userId, stationId: parseInt(stationId), - lastHeartbeat: - process.env.environment === "development" ? nowPlus2h.toISOString() : undefined, + lastHeartbeat: debug ? nowPlus2h.toISOString() : undefined, posLat: randomPos?.lat, posLng: randomPos?.lng, }, diff --git a/apps/dispatch/app/(app)/dispatch/_components/pannel/MissionForm.tsx b/apps/dispatch/app/(app)/dispatch/_components/pannel/MissionForm.tsx index 34c3e734..97b2bfdc 100644 --- a/apps/dispatch/app/(app)/dispatch/_components/pannel/MissionForm.tsx +++ b/apps/dispatch/app/(app)/dispatch/_components/pannel/MissionForm.tsx @@ -41,7 +41,7 @@ export const MissionForm = () => { const { data: aircrafts } = useQuery({ queryKey: ["aircrafts"], - queryFn: getConnectedAircraftsAPI, + queryFn: () => getConnectedAircraftsAPI(), refetchInterval: 10000, }); diff --git a/apps/dispatch/app/(app)/pilot/_components/SimConnectionAlert.tsx b/apps/dispatch/app/(app)/pilot/_components/SimConnectionAlert.tsx new file mode 100644 index 00000000..644dc651 --- /dev/null +++ b/apps/dispatch/app/(app)/pilot/_components/SimConnectionAlert.tsx @@ -0,0 +1,39 @@ +import { usePilotConnectionStore } from "_store/pilot/connectionStore"; +import { AlertTriangle } from "lucide-react"; +import { useEffect, useState } from "react"; + +export const SimConnectionAlert = ({ lastUpdated }: { lastUpdated?: Date }) => { + const [lastHearbeetSeconds, setLastHearbeetSeconds] = useState(0); + const { disconnect } = usePilotConnectionStore((state) => state); + useEffect(() => { + const interval = setInterval(() => { + if (lastUpdated) { + const now = new Date(); + const diff = Math.floor((now.getTime() - new Date(lastUpdated).getTime()) / 1000); + setLastHearbeetSeconds(diff); + } else { + setLastHearbeetSeconds(0); + } + }, 1000); + + return () => clearInterval(interval); + }, [lastUpdated]); + + return ( +
+ +
+

+ Keine Simulator Verbindung{" "} + {lastHearbeetSeconds ? `seit ${lastHearbeetSeconds} Sekunden` : "gefunden"} +

+
+ Wenn dein Simulator abgestürzt ist informiere den Disponenten über den Chat links +
+
+ +
+ ); +}; diff --git a/apps/dispatch/app/(app)/pilot/_components/navbar/_components/Connection.tsx b/apps/dispatch/app/(app)/pilot/_components/navbar/_components/Connection.tsx index 42e9ca66..13ef94d3 100644 --- a/apps/dispatch/app/(app)/pilot/_components/navbar/_components/Connection.tsx +++ b/apps/dispatch/app/(app)/pilot/_components/navbar/_components/Connection.tsx @@ -7,6 +7,7 @@ import { getStationsAPI } from "_querys/stations"; import toast from "react-hot-toast"; import { editConnectedAircraftAPI } from "_querys/aircrafts"; import { Prisma } from "@repo/db"; +import { debug } from "console"; export const ConnectionBtn = () => { const modalRef = useRef(null); @@ -14,9 +15,11 @@ export const ConnectionBtn = () => { const [form, setForm] = useState<{ logoffTime: string | null; selectedStationId: number | null; + debugPosition: boolean; }>({ logoffTime: null, selectedStationId: null, + debugPosition: false, }); const [logoffDebounce, setLogoffDebounce] = useState(null); @@ -165,6 +168,21 @@ export const ConnectionBtn = () => {

Du kannst diese Zeit später noch anpassen.

)} + + {session.data?.user.permissions.includes("ADMIN_STATION") && ( +
+ Debug-optionen + +
+ )}
@@ -195,6 +213,7 @@ export const ConnectionBtn = () => { form.logoffTime || "", selectedStation, session.data!.user, + form.debugPosition, ); } }} diff --git a/apps/dispatch/app/(app)/pilot/page.tsx b/apps/dispatch/app/(app)/pilot/page.tsx index 6b97554a..3ddc0808 100644 --- a/apps/dispatch/app/(app)/pilot/page.tsx +++ b/apps/dispatch/app/(app)/pilot/page.tsx @@ -6,12 +6,28 @@ import { Report } from "../../_components/left/Report"; import { Dme } from "(app)/pilot/_components/dme/Dme"; import dynamic from "next/dynamic"; import { ConnectedDispatcher } from "tracker/_components/ConnectedDispatcher"; +import { useQuery } from "@tanstack/react-query"; +import { usePilotConnectionStore } from "_store/pilot/connectionStore"; +import { getAircraftsAPI, getConnectedAircraftsAPI } from "_querys/aircrafts"; +import { checkSimulatorConnected } from "_helpers/simulatorConnected"; +import { SimConnectionAlert } from "(app)/pilot/_components/SimConnectionAlert"; const Map = dynamic(() => import("_components/map/Map"), { ssr: false, }); const DispatchPage = () => { + const { connectedAircraft, status } = usePilotConnectionStore((state) => state); + const { data: ownAircraftArray = [] } = useQuery({ + queryKey: ["aircrafts", connectedAircraft?.id], + queryFn: () => + getAircraftsAPI({ + id: connectedAircraft?.id, + }), + refetchInterval: 1000, + }); + const ownAircraft = ownAircraftArray[0]; + const simulatorConnected = ownAircraft ? checkSimulatorConnected(ownAircraft) : false; return (
{/* */} @@ -25,7 +41,10 @@ const DispatchPage = () => {
-
+
+ {!simulatorConnected && status === "connected" && ( + + )}
diff --git a/apps/dispatch/app/_components/map/AircraftMarker.tsx b/apps/dispatch/app/_components/map/AircraftMarker.tsx index e8afbe25..34babeff 100644 --- a/apps/dispatch/app/_components/map/AircraftMarker.tsx +++ b/apps/dispatch/app/_components/map/AircraftMarker.tsx @@ -382,7 +382,7 @@ const AircraftMarker = ({ aircraft }: { aircraft: ConnectedAircraft & { Station: export const AircraftLayer = () => { const { data: aircrafts } = useQuery({ queryKey: ["aircrafts"], - queryFn: getConnectedAircraftsAPI, + queryFn: () => getConnectedAircraftsAPI(), refetchInterval: 10_000, }); diff --git a/apps/dispatch/app/_components/map/MissionMarkers.tsx b/apps/dispatch/app/_components/map/MissionMarkers.tsx index 3fb5bfb0..af549d0b 100644 --- a/apps/dispatch/app/_components/map/MissionMarkers.tsx +++ b/apps/dispatch/app/_components/map/MissionMarkers.tsx @@ -217,7 +217,7 @@ const MissionMarker = ({ mission }: { mission: Mission }) => { const { data: aircrafts } = useQuery({ queryKey: ["aircrafts"], - queryFn: getConnectedAircraftsAPI, + queryFn: () => getConnectedAircraftsAPI(), refetchInterval: 10000, }); diff --git a/apps/dispatch/app/_components/map/_components/MarkerCluster.tsx b/apps/dispatch/app/_components/map/_components/MarkerCluster.tsx index 9f894092..d63cca8e 100644 --- a/apps/dispatch/app/_components/map/_components/MarkerCluster.tsx +++ b/apps/dispatch/app/_components/map/_components/MarkerCluster.tsx @@ -139,7 +139,7 @@ export const MarkerCluster = () => { const dispatcherConnected = dispatchState.status === "connected"; const { data: aircrafts } = useQuery({ queryKey: ["aircrafts"], - queryFn: getConnectedAircraftsAPI, + queryFn: () => getConnectedAircraftsAPI(), refetchInterval: 10_000, }); diff --git a/apps/dispatch/app/_components/map/_components/MissionMarkerTabs.tsx b/apps/dispatch/app/_components/map/_components/MissionMarkerTabs.tsx index 52e1be38..fae7b8d0 100644 --- a/apps/dispatch/app/_components/map/_components/MissionMarkerTabs.tsx +++ b/apps/dispatch/app/_components/map/_components/MissionMarkerTabs.tsx @@ -363,7 +363,7 @@ const Rettungsmittel = ({ mission }: { mission: Mission }) => { ); const { data: connectedAircrafts } = useQuery({ queryKey: ["aircrafts"], - queryFn: getConnectedAircraftsAPI, + queryFn: () => getConnectedAircraftsAPI(), }); const updateMissionMutation = useMutation({ mutationKey: ["missions", "stations-mission", mission.id], diff --git a/apps/dispatch/app/_querys/aircrafts.ts b/apps/dispatch/app/_querys/aircrafts.ts index 4fe6512e..23bdabde 100644 --- a/apps/dispatch/app/_querys/aircrafts.ts +++ b/apps/dispatch/app/_querys/aircrafts.ts @@ -11,6 +11,16 @@ export const getConnectedAircraftsAPI = async () => { return res.data.filter((a) => checkSimulatorConnected(a)); }; +export const getAircraftsAPI = async (filter?: Prisma.ConnectedAircraftWhereInput) => { + const res = await axios.get<(ConnectedAircraft & { Station: Station })[]>("/api/aircrafts", { + params: { filter: JSON.stringify(filter) }, + }); + if (res.status !== 200) { + throw new Error("Failed to fetch stations"); + } + return res.data; +}; + export const editConnectedAircraftAPI = async ( id: number, mission: Prisma.ConnectedAircraftUpdateInput, diff --git a/apps/dispatch/app/_store/pilot/connectionStore.ts b/apps/dispatch/app/_store/pilot/connectionStore.ts index 1984e954..06616c0b 100644 --- a/apps/dispatch/app/_store/pilot/connectionStore.ts +++ b/apps/dispatch/app/_store/pilot/connectionStore.ts @@ -9,7 +9,7 @@ import { useAudioStore } from "_store/audioStore"; interface ConnectionStore { status: "connected" | "disconnected" | "connecting" | "error"; message: string; - + debug: boolean; logoffTime: string; selectedStation: Station | null; connectedAircraft: ConnectedAircraft | null; @@ -24,6 +24,7 @@ interface ConnectionStore { logoffTime: string, station: Station, user: User, + debug?: boolean, ) => Promise; disconnect: () => void; } @@ -35,14 +36,16 @@ export const usePilotConnectionStore = create((set) => ({ selectedStation: null, connectedAircraft: null, activeMission: null, + debug: false, - connect: async (uid, stationId, logoffTime, station, user) => + connect: async (uid, stationId, logoffTime, station, user, debug) => new Promise((resolve) => { set({ status: "connecting", message: "", selectedStation: station, logoffTime, + debug, }); pilotSocket.auth = { uid }; @@ -64,13 +67,14 @@ export const usePilotConnectionStore = create((set) => ({ pilotSocket.on("connect", () => { usePilotConnectionStore.setState({ status: "connected", message: "" }); - const { logoffTime, selectedStation } = usePilotConnectionStore.getState(); + const { logoffTime, selectedStation, debug } = usePilotConnectionStore.getState(); dispatchSocket.disconnect(); useAudioStore.getState().connect("LST_01", selectedStation?.bosCallsignShort || "pilot"); pilotSocket.emit("connect-pilot", { logoffTime, stationId: selectedStation?.id, + debug, }); }); diff --git a/apps/dispatch/app/api/aircrafts/route.ts b/apps/dispatch/app/api/aircrafts/route.ts index 39465c4b..d4d3eb92 100644 --- a/apps/dispatch/app/api/aircrafts/route.ts +++ b/apps/dispatch/app/api/aircrafts/route.ts @@ -1,7 +1,7 @@ -import { NextResponse } from "next/server"; +import { NextRequest, NextResponse } from "next/server"; import { Prisma, prisma } from "@repo/db"; -export async function GET(request: Request): Promise { +export async function GET(request: NextRequest): Promise { try { const filter = JSON.parse( new URL(request.url).searchParams.get("filter") || "{}", @@ -17,6 +17,7 @@ export async function GET(request: Request): Promise { }, }); + console.log("Filter applied:", filter, connectedAircraft); return NextResponse.json(connectedAircraft, { status: 200, });