Files
var-monorepo/apps/dispatch/app/_components/customToasts/StationStatusToast.tsx
2025-07-27 16:27:58 -07:00

186 lines
5.3 KiB
TypeScript

import { Prisma, StationStatus } from "@repo/db";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { BaseNotification } from "_components/customToasts/BaseNotification";
import { FMS_STATUS_COLORS } from "_helpers/fmsStatusColors";
import { editConnectedAircraftAPI, getConnectedAircraftsAPI } from "_querys/aircrafts";
import { getLivekitRooms } from "_querys/livekit";
import { getStationsAPI } from "_querys/stations";
import { useAudioStore } from "_store/audioStore";
import { useMapStore } from "_store/mapStore";
import { X } from "lucide-react";
import { useEffect, useRef, useState } from "react";
import { Toast, toast } from "react-hot-toast";
export const QUICK_RESPONSE: Record<string, string[]> = {
"5": ["J", "o"],
"9": ["u", "o"],
"0": ["J"],
};
export const StatusToast = ({ event, t }: { event: StationStatus; t: Toast }) => {
const status0Sounds = useRef<HTMLAudioElement | null>(null);
const status5Sounds = useRef<HTMLAudioElement | null>(null);
const status9Sounds = useRef<HTMLAudioElement | null>(null);
const { data: livekitRooms } = useQuery({
queryKey: ["livekit-rooms"],
queryFn: () => getLivekitRooms(),
refetchInterval: 5000,
});
const audioRoom = useAudioStore((s) => s.room?.name);
const participants =
livekitRooms?.flatMap((room) =>
room.participants.map((p) => ({
...p,
roomName: room.room.name,
})),
) || [];
const livekitUser = participants.find((p) => p.attributes.userId === event.data?.userId);
useEffect(() => {
if (typeof window !== "undefined") {
status0Sounds.current = new Audio("/sounds/status-0.mp3");
status5Sounds.current = new Audio("/sounds/status-5.mp3");
status9Sounds.current = new Audio("/sounds/status-9.mp3");
}
}, []);
const [aircraftDataAcurate, setAircraftDataAccurate] = useState(false);
//const mapStore = useMapStore((s) => s);
const { setOpenAircraftMarker, setMap } = useMapStore((store) => store);
const { data: connectedAircrafts } = useQuery({
queryKey: ["aircrafts"],
queryFn: () => getConnectedAircraftsAPI(),
refetchInterval: 10000,
initialData: [],
});
const { data: stations } = useQuery({
queryKey: ["stations"],
queryFn: () => getStationsAPI(),
});
const connectedAircraft = connectedAircrafts?.find((a) => a.id === event.data?.aircraftId);
const station = stations?.find((s) => s.id === event.data?.stationId);
const queryClient = useQueryClient();
const changeAircraftMutation = useMutation({
mutationFn: async ({
id,
update,
}: {
id: number;
update: Prisma.ConnectedAircraftUpdateInput;
}) => {
await editConnectedAircraftAPI(id, update);
queryClient.invalidateQueries({
queryKey: ["aircrafts"],
});
},
});
useEffect(() => {
if (event.status !== connectedAircraft?.fmsStatus && aircraftDataAcurate) {
toast.remove(t.id);
} else if (event.status == connectedAircraft?.fmsStatus && !aircraftDataAcurate) {
setAircraftDataAccurate(true);
}
}, [aircraftDataAcurate, connectedAircraft, event.status, t.id]);
useEffect(() => {
let soundRef: React.RefObject<HTMLAudioElement | null> | null = null;
switch (event.status) {
case "0":
soundRef = status0Sounds;
break;
case "5":
soundRef = status5Sounds;
break;
case "9":
soundRef = status9Sounds;
break;
default:
soundRef = null;
}
if (audioRoom !== livekitUser?.roomName) {
toast.remove(t.id);
return;
}
if (soundRef?.current) {
soundRef.current.currentTime = 0;
soundRef.current.volume = 0.7;
soundRef.current.play().catch(() => {});
}
return () => {
if (soundRef?.current) {
soundRef.current.pause();
soundRef.current.currentTime = 0;
}
};
}, [event.status, livekitUser?.roomName, audioRoom, t.id]);
if (!connectedAircraft || !station) return null;
return (
<BaseNotification>
<div className="flex flex-row items-center gap-14">
<p>
<span
className="mr-1 cursor-pointer font-bold underline"
onClick={() => {
if (!connectedAircraft.posLat || !connectedAircraft.posLng) return;
setOpenAircraftMarker({
open: [{ id: connectedAircraft.id, tab: "fms" }],
close: [],
});
setMap({
center: [connectedAircraft.posLat, connectedAircraft.posLng],
zoom: 14,
});
}}
>
{station.bosCallsign}
</span>
sendet Status {event.status}
</p>
<div className="flex items-center gap-2">
{QUICK_RESPONSE[String(event.status)]?.map((status) => (
<button
key={status}
className={
"flex min-h-10 min-w-10 cursor-pointer items-center justify-center text-lg font-bold"
}
style={{
backgroundColor: FMS_STATUS_COLORS[status],
color: "white",
}}
onClick={async () => {
if (!event.data?.aircraftId) {
toast.remove(t.id);
toast.error("Keine Flugzeug-ID gefunden");
return;
}
await changeAircraftMutation.mutateAsync({
id: event.data?.aircraftId,
update: {
fmsStatus: status,
},
});
toast.remove(t.id);
toast.success(`Status auf ${status} geändert`);
}}
>
{status}
</button>
))}
<button className="btn btn-ghost btn-sm" onClick={() => toast.remove(t.id)}>
<X size={16} />
</button>
</div>
</div>
</BaseNotification>
);
};