@@ -435,6 +464,11 @@ const Rettungsmittel = ({ mission }: { mission: Mission }) => {
);
})}
+ {mission.hpgAmbulanceState &&
}
+ {mission.hpgFireEngineState && (
+
+ )}
+ {mission.hpgPoliceState &&
}
{dispatcherConnected && (
@@ -475,7 +509,10 @@ const Rettungsmittel = ({ mission }: { mission: Mission }) => {
className="btn btn-sm btn-primary btn-outline"
onClick={async () => {
if (typeof selectedStation === "string") {
- toast.error("Fahrzeuge werden aktuell nicht unterstützt");
+ await sendAlertMutation.mutate({
+ id: mission.id,
+ vehicleName: selectedStation,
+ });
} else {
if (!selectedStation?.id) return;
await updateMissionMutation.mutateAsync({
@@ -520,7 +557,6 @@ const FMSStatusHistory = ({ mission }: { mission: Mission }) => {
queryClient.invalidateQueries({ queryKey: ["missions"] });
},
});
- console.log(mission.missionLog);
if (!session.data?.user) return null;
return (
diff --git a/apps/dispatch/app/_store/audioStore.ts b/apps/dispatch/app/_store/audioStore.ts
index ff524c40..488e241c 100644
--- a/apps/dispatch/app/_store/audioStore.ts
+++ b/apps/dispatch/app/_store/audioStore.ts
@@ -1,6 +1,4 @@
import { PublicUser } from "@repo/db";
-import { usePilotConnectionStore } from "_store/pilot/connectionStore";
-import { channel } from "diagnostics_channel";
import { dispatchSocket } from "dispatch/socket";
import { serverApi } from "helpers/axios";
import {
@@ -17,6 +15,8 @@ import { create } from "zustand";
let interval: NodeJS.Timeout;
type TalkState = {
+ micDeviceId: string | null;
+ micVolume: number;
isTalking: boolean;
source: string;
state: "connecting" | "connected" | "disconnected" | "error";
@@ -24,7 +24,7 @@ type TalkState = {
connectionQuality: ConnectionQuality;
remoteParticipants: number;
toggleTalking: () => void;
-
+ setMic: (micDeviceId: string | null, volume: number) => void;
connect: (roomName: string) => void;
disconnect: () => void;
room: Room | null;
@@ -38,15 +38,23 @@ const getToken = async (roomName: string) => {
export const useAudioStore = create((set, get) => ({
isTalking: false,
message: null,
+ micDeviceId: null,
+ micVolume: 1,
state: "disconnected",
source: "",
remoteParticipants: 0,
connectionQuality: ConnectionQuality.Unknown,
room: null,
+ setMic: (micDeviceId, micVolume) => {
+ set({ micDeviceId, micVolume });
+ },
toggleTalking: () => {
- const { room, isTalking } = get();
+ const { room, isTalking, micDeviceId, micVolume } = get();
if (!room) return;
- room.localParticipant.setMicrophoneEnabled(!isTalking);
+ // Todo: use micVolume
+ room.localParticipant.setMicrophoneEnabled(!isTalking, {
+ deviceId: micDeviceId ?? undefined,
+ });
if (!isTalking) {
// If old status was not talking, we need to emit the PTT event
@@ -94,23 +102,19 @@ export const useAudioStore = create((set, get) => ({
handleDisconnect();
})
- .on(RoomEvent.ConnectionQualityChanged, (connectionQuality) =>
- set({ connectionQuality }),
- )
+ .on(RoomEvent.ConnectionQualityChanged, (connectionQuality) => set({ connectionQuality }))
// Track events
.on(RoomEvent.TrackSubscribed, handleTrackSubscribed)
.on(RoomEvent.TrackUnsubscribed, handleTrackUnsubscribed)
.on(RoomEvent.ActiveSpeakersChanged, handleActiveSpeakerChange)
.on(RoomEvent.LocalTrackUnpublished, handleLocalTrackUnpublished);
- await room.connect(url, token);
- console.log(room);
+ await room.connect(url, token, {});
set({ room });
interval = setInterval(() => {
set({
- remoteParticipants:
- room.numParticipants === 0 ? 0 : room.numParticipants - 1, // Unreliable and delayed
+ remoteParticipants: room.numParticipants === 0 ? 0 : room.numParticipants - 1, // Unreliable and delayed
});
}, 500);
} catch (error: Error | unknown) {
@@ -150,11 +154,7 @@ const handlePTT = (data: PTTData) => {
}
};
-const handleOtherPTT = (data: {
- publicUser: PublicUser;
- channel: string;
- source: string;
-}) => {
+const handleOtherPTT = (data: { publicUser: PublicUser; channel: string; source: string }) => {
const currentChannel = useAudioStore.getState().room?.name;
console.log("Other PTT", data);
if (data.channel === currentChannel)
diff --git a/apps/dispatch/app/_store/dispatch/connectionStore.ts b/apps/dispatch/app/_store/dispatch/connectionStore.ts
index 2219971c..d189027a 100644
--- a/apps/dispatch/app/_store/dispatch/connectionStore.ts
+++ b/apps/dispatch/app/_store/dispatch/connectionStore.ts
@@ -1,19 +1,13 @@
import { create } from "zustand";
import { dispatchSocket } from "../../dispatch/socket";
-import toast from "react-hot-toast";
-import { HPGnotificationToast } from "_components/customToasts/HPGnotification";
-import { NotificationPayload } from "@repo/db";
+import { useAudioStore } from "_store/audioStore";
interface ConnectionStore {
status: "connected" | "disconnected" | "connecting" | "error";
message: string;
selectedZone: string;
logoffTime: string;
- connect: (
- uid: string,
- selectedZone: string,
- logoffTime: string,
- ) => Promise;
+ connect: (uid: string, selectedZone: string, logoffTime: string) => Promise;
disconnect: () => void;
}
@@ -40,7 +34,7 @@ export const useDispatchConnectionStore = create((set) => ({
dispatchSocket.on("connect", () => {
const { logoffTime, selectedZone } = useDispatchConnectionStore.getState();
-
+ useAudioStore.getInitialState().connect("LST_01");
dispatchSocket.emit("connect-dispatch", {
logoffTime,
selectedZone,
diff --git a/apps/dispatch/app/_store/pilot/connectionStore.ts b/apps/dispatch/app/_store/pilot/connectionStore.ts
index 71ff874e..101fca7c 100644
--- a/apps/dispatch/app/_store/pilot/connectionStore.ts
+++ b/apps/dispatch/app/_store/pilot/connectionStore.ts
@@ -1,17 +1,10 @@
import { create } from "zustand";
import { dispatchSocket } from "../../dispatch/socket";
-import {
- ConnectedAircraft,
- Mission,
- MissionSdsLog,
- NotificationPayload,
- Station,
- User,
-} from "@repo/db";
+import { ConnectedAircraft, Mission, MissionSdsLog, Station, User } from "@repo/db";
import { pilotSocket } from "pilot/socket";
import { useDmeStore } from "_store/pilot/dmeStore";
import { useMrtStore } from "_store/pilot/MrtStore";
-import toast from "react-hot-toast";
+import { useAudioStore } from "_store/audioStore";
interface ConnectionStore {
status: "connected" | "disconnected" | "connecting" | "error";
@@ -71,6 +64,7 @@ pilotSocket.on("connect", () => {
usePilotConnectionStore.setState({ status: "connected", message: "" });
const { logoffTime, selectedStation } = usePilotConnectionStore.getState();
dispatchSocket.disconnect();
+ useAudioStore.getInitialState().connect("LST_01");
pilotSocket.emit("connect-pilot", {
logoffTime,
diff --git a/apps/dispatch/app/api/user/route.ts b/apps/dispatch/app/api/user/route.ts
new file mode 100644
index 00000000..bfa8f323
--- /dev/null
+++ b/apps/dispatch/app/api/user/route.ts
@@ -0,0 +1,49 @@
+import { NextRequest, NextResponse } from "next/server";
+import { prisma } from "@repo/db";
+
+export async function GET(req: NextRequest): Promise {
+ try {
+ const { searchParams } = new URL(req.url);
+ const id = searchParams.get("id");
+
+ if (!id) {
+ return NextResponse.json({ error: "User id is required" }, { status: 400 });
+ }
+
+ const user = await prisma.user.findUnique({
+ where: { id: id },
+ });
+
+ if (!user) {
+ return NextResponse.json({ error: "User not found" }, { status: 404 });
+ }
+
+ return NextResponse.json(user, { status: 200 });
+ } catch (error) {
+ console.error(error);
+ return NextResponse.json({ error: "Failed to fetch user" }, { status: 500 });
+ }
+}
+
+export async function POST(req: NextRequest): Promise {
+ try {
+ const { searchParams } = new URL(req.url);
+ const id = searchParams.get("id");
+
+ if (!id) {
+ return NextResponse.json({ error: "User id is required" }, { status: 400 });
+ }
+
+ const body = await req.json();
+
+ const updatedUser = await prisma.user.update({
+ where: { id: id },
+ data: body,
+ });
+
+ return NextResponse.json(updatedUser, { status: 200 });
+ } catch (error) {
+ console.error(error);
+ return NextResponse.json({ error: "Failed to update user" }, { status: 500 });
+ }
+}
diff --git a/apps/dispatch/app/dispatch/_components/navbar/Navbar.tsx b/apps/dispatch/app/dispatch/_components/navbar/Navbar.tsx
index bd3a30dc..a0ab4768 100644
--- a/apps/dispatch/app/dispatch/_components/navbar/Navbar.tsx
+++ b/apps/dispatch/app/dispatch/_components/navbar/Navbar.tsx
@@ -6,7 +6,7 @@ import { Audio } from "../../../_components/Audio";
/* import { useState } from "react"; */
import { ExitIcon, ExternalLinkIcon } from "@radix-ui/react-icons";
import Link from "next/link";
-import { SettingsBtn } from "./_components/Settings";
+import { Settings } from "_components/Settings";
export default function Navbar() {
/* const [isDark, setIsDark] = useState(false);
@@ -35,7 +35,7 @@ export default function Navbar() {
{/*
*/}
-
+
{
- const modalRef = useRef
(null);
-
- return (
-
-
-
-
-
- );
-};
-
-export const Settings = () => {
- return (
-
-
-
- );
-};
diff --git a/apps/dispatch/app/dispatch/_components/pannel/MissionForm.tsx b/apps/dispatch/app/dispatch/_components/pannel/MissionForm.tsx
index 6580d89b..4d0133a6 100644
--- a/apps/dispatch/app/dispatch/_components/pannel/MissionForm.tsx
+++ b/apps/dispatch/app/dispatch/_components/pannel/MissionForm.tsx
@@ -4,7 +4,7 @@ import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { BellRing, BookmarkPlus } from "lucide-react";
import { Select } from "_components/Select";
-import { KEYWORD_CATEGORY, missionType, Prisma } from "@repo/db";
+import { KEYWORD_CATEGORY, Mission, missionType, Prisma } from "@repo/db";
import { MissionOptionalDefaults, MissionOptionalDefaultsSchema } from "@repo/db/zod";
import { usePannelStore } from "_store/pannelStore";
import { useSession } from "next-auth/react";
@@ -21,6 +21,8 @@ import { getStationsAPI } from "querys/stations";
import { useMapStore } from "_store/mapStore";
import { getConnectedAircraftsAPI } from "querys/aircrafts";
import { HPGValidationRequired } from "helpers/hpgValidationRequired";
+import { selectRandomHPGMissionSzenery } from "helpers/selectRandomHPGMission";
+import { AxiosError } from "axios";
export const MissionForm = () => {
const { isEditingMission, editingMissionId, setEditingMission } = usePannelStore();
@@ -93,7 +95,8 @@ export const MissionForm = () => {
hpgAmbulanceState: null,
hpgPoliceState: null,
hpgMissionString: null,
-
+ hpgSelectedMissionString: null,
+ hpg: null,
missionLog: [],
}) as Partial,
[session.data?.user.id],
@@ -132,6 +135,47 @@ export const MissionForm = () => {
}
}, [missionFormValues, form, defaultFormValues]);
+ const saveMission = async (
+ mission: MissionOptionalDefaults,
+ { alertWhenValid = false, createNewMission = false } = {},
+ ) => {
+ const [hpgSzenario, hpgSzenarioCode] = mission.hpgMissionString?.split(":") || [];
+ const szenarioCode = selectRandomHPGMissionSzenery(hpgSzenarioCode || "");
+ let newMission: Mission;
+ if (createNewMission) {
+ newMission = await createMissionMutation.mutateAsync({
+ ...(mission as unknown as Prisma.MissionCreateInput),
+ missionAdditionalInfo:
+ !mission.missionAdditionalInfo.length && hpgSzenario
+ ? `HPG-Szenario: ${hpgSzenario}`
+ : mission.missionAdditionalInfo,
+ hpgSelectedMissionString: szenarioCode,
+ });
+ if (validationRequired) {
+ await startHpgValidation(newMission.id, {
+ alertWhenValid,
+ });
+ }
+ return newMission;
+ } else {
+ newMission = await editMissionMutation.mutateAsync({
+ id: Number(editingMissionId),
+ mission: {
+ ...(mission as unknown as Prisma.MissionCreateInput),
+ missionAdditionalInfo:
+ !mission.missionAdditionalInfo.length && hpgSzenario
+ ? `HPG-Szenario: ${hpgSzenario}`
+ : mission.missionAdditionalInfo,
+ hpgSelectedMissionString: szenarioCode,
+ },
+ });
+ }
+ if (validationRequired) {
+ await startHpgValidation(newMission.id, {});
+ }
+ return newMission;
+ };
+
return (