added DME, fixed sync and bugs. Rewrote setDisplay logic
This commit is contained in:
@@ -100,7 +100,7 @@ export const handleConnectPilot =
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
.catch(console.error);
|
.catch(console.error);
|
||||||
io.to("dispatchers").emit("pilots-update");
|
io.to("dispatchers").emit("update-connectedAircraft");
|
||||||
io.to("pilots").emit("pilots-update");
|
io.to("pilots").emit("pilots-update");
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -30,7 +30,10 @@ export function QueryProvider({ children }: { children: ReactNode }) {
|
|||||||
|
|
||||||
const invalidateConnectedUsers = () => {
|
const invalidateConnectedUsers = () => {
|
||||||
queryClient.invalidateQueries({
|
queryClient.invalidateQueries({
|
||||||
queryKey: ["connected-users", "aircrafts"],
|
queryKey: ["connected-users"],
|
||||||
|
});
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: ["aircrafts"],
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
26
apps/dispatch/app/_data/fmsStatusDescription.ts
Normal file
26
apps/dispatch/app/_data/fmsStatusDescription.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
export const fmsStatusDescription: { [key: string]: string } = {
|
||||||
|
NaN: "Keine Daten",
|
||||||
|
"0": "Prio. Sprechwunsch",
|
||||||
|
"1": "Frei auf Funk",
|
||||||
|
"2": "Einsatzbereit am LRZ",
|
||||||
|
"3": "Auf dem Weg",
|
||||||
|
"4": "Am Einsatzort",
|
||||||
|
"5": "Sprechwunsch",
|
||||||
|
"6": "Nicht einsatzbereit",
|
||||||
|
"7": "Patient aufgenommen",
|
||||||
|
"8": "Am Transportziel",
|
||||||
|
"9": "Fremdanmeldung",
|
||||||
|
E: "Indent/Abbruch/Einsatzbefehl abgebrochen",
|
||||||
|
C: "Anmelden zur Übernahme des Einsatzes",
|
||||||
|
F: "Kommen über Draht",
|
||||||
|
H: "Fahren auf Wache",
|
||||||
|
J: "Sprechaufforderung",
|
||||||
|
L: "Lagebericht abgeben",
|
||||||
|
P: "Einsatz mit Polizei/Pause machen",
|
||||||
|
U: "Ungültiger Status",
|
||||||
|
c: "Status korrigieren",
|
||||||
|
d: "Transportziel angeben",
|
||||||
|
h: "Zielklinik verständigt",
|
||||||
|
o: "Warten, alle Abfrageplätze belegt",
|
||||||
|
u: "Verstanden",
|
||||||
|
};
|
||||||
@@ -5,6 +5,7 @@ interface ConnectionStore {
|
|||||||
status: "connected" | "disconnected" | "connecting" | "error";
|
status: "connected" | "disconnected" | "connecting" | "error";
|
||||||
message: string;
|
message: string;
|
||||||
selectedZone: string;
|
selectedZone: string;
|
||||||
|
logoffTime: string;
|
||||||
connect: (
|
connect: (
|
||||||
uid: string,
|
uid: string,
|
||||||
selectedZone: string,
|
selectedZone: string,
|
||||||
@@ -17,6 +18,7 @@ export const useDispatchConnectionStore = create<ConnectionStore>((set) => ({
|
|||||||
status: "disconnected",
|
status: "disconnected",
|
||||||
message: "",
|
message: "",
|
||||||
selectedZone: "LST_01",
|
selectedZone: "LST_01",
|
||||||
|
logoffTime: "",
|
||||||
connect: async (uid, selectedZone, logoffTime) =>
|
connect: async (uid, selectedZone, logoffTime) =>
|
||||||
new Promise((resolve) => {
|
new Promise((resolve) => {
|
||||||
set({ status: "connecting", message: "" });
|
set({ status: "connecting", message: "" });
|
||||||
@@ -58,3 +60,12 @@ dispatchSocket.on("force-disconnect", (reason: string) => {
|
|||||||
message: reason,
|
message: reason,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
dispatchSocket.on("reconnect", () => {
|
||||||
|
const { logoffTime, selectedZone } = useDispatchConnectionStore.getState();
|
||||||
|
|
||||||
|
dispatchSocket.emit("connect-dispatch", {
|
||||||
|
logoffTime,
|
||||||
|
selectedZone,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -1,25 +1,36 @@
|
|||||||
|
import { Station } from "@repo/db";
|
||||||
|
import { fmsStatusDescription } from "_data/fmsStatusDescription";
|
||||||
import { DisplayLineProps } from "pilot/_components/mrt/Mrt";
|
import { DisplayLineProps } from "pilot/_components/mrt/Mrt";
|
||||||
import { create } from "zustand";
|
import { create } from "zustand";
|
||||||
import { syncTabs } from "zustand-sync-tabs";
|
import { syncTabs } from "zustand-sync-tabs";
|
||||||
|
|
||||||
type Page = "home" | "sending-status" | "new-status" | "error";
|
interface SetHomePageParams {
|
||||||
|
page: "home";
|
||||||
|
station: Station;
|
||||||
|
fmsStatus: string;
|
||||||
|
}
|
||||||
|
|
||||||
type PageData = {
|
interface SetSendingStatusPageParams {
|
||||||
home: undefined;
|
page: "sending-status";
|
||||||
"sending-status": undefined;
|
station: Station;
|
||||||
"new-status": undefined;
|
}
|
||||||
error: {
|
|
||||||
message: string;
|
interface SetNewStatusPageParams {
|
||||||
};
|
page: "new-status";
|
||||||
};
|
station: Station;
|
||||||
|
}
|
||||||
|
|
||||||
|
type SetPageParams =
|
||||||
|
| SetHomePageParams
|
||||||
|
| SetSendingStatusPageParams
|
||||||
|
| SetNewStatusPageParams;
|
||||||
|
|
||||||
interface MrtStore {
|
interface MrtStore {
|
||||||
page: Page;
|
page: SetPageParams["page"];
|
||||||
pageData: PageData[Page];
|
|
||||||
|
|
||||||
lines: DisplayLineProps[];
|
lines: DisplayLineProps[];
|
||||||
|
|
||||||
setPage: <P extends Page>(page: P, pageData?: PageData[P]) => void;
|
setPage: (pageData: SetPageParams) => void;
|
||||||
setLines: (lines: MrtStore["lines"]) => void;
|
setLines: (lines: MrtStore["lines"]) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,14 +41,93 @@ export const useMrtStore = create<MrtStore>(
|
|||||||
pageData: {
|
pageData: {
|
||||||
message: "",
|
message: "",
|
||||||
},
|
},
|
||||||
lines: Array.from(Array(10).keys()).map(() => ({
|
lines: [
|
||||||
textLeft: "",
|
{
|
||||||
textMid: "",
|
textLeft: "VAR.#",
|
||||||
textRight: "",
|
textSize: "2",
|
||||||
textSize: "1",
|
},
|
||||||
})),
|
{
|
||||||
|
textLeft: "No Data",
|
||||||
|
textSize: "3",
|
||||||
|
},
|
||||||
|
],
|
||||||
setLines: (lines) => set({ lines }),
|
setLines: (lines) => set({ lines }),
|
||||||
setPage: (page, pageData) => set({ page, pageData }),
|
setPage: (pageData) => {
|
||||||
|
switch (pageData.page) {
|
||||||
|
case "home": {
|
||||||
|
const { station, fmsStatus } = pageData as SetHomePageParams;
|
||||||
|
set({
|
||||||
|
page: "home",
|
||||||
|
lines: [
|
||||||
|
{
|
||||||
|
textLeft: `VAR#.${station?.bosCallsign}`,
|
||||||
|
style: { fontWeight: "bold" },
|
||||||
|
textSize: "2",
|
||||||
|
},
|
||||||
|
{ textLeft: "ILS VAR#", textSize: "3" },
|
||||||
|
{
|
||||||
|
textLeft: fmsStatus,
|
||||||
|
style: { fontWeight: "extrabold" },
|
||||||
|
textSize: "4",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
textLeft: fmsStatusDescription[fmsStatus],
|
||||||
|
textSize: "1",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "sending-status": {
|
||||||
|
const { station } = pageData as SetSendingStatusPageParams;
|
||||||
|
set({
|
||||||
|
page: "sending-status",
|
||||||
|
lines: [
|
||||||
|
{
|
||||||
|
textLeft: `VAR#.${station?.bosCallsign}`,
|
||||||
|
style: { fontWeight: "bold" },
|
||||||
|
textSize: "2",
|
||||||
|
},
|
||||||
|
{ textLeft: "ILS VAR#", textSize: "3" },
|
||||||
|
{
|
||||||
|
textMid: "sending...",
|
||||||
|
style: { fontWeight: "bold" },
|
||||||
|
textSize: "4",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
textLeft: "Status wird gesendet...",
|
||||||
|
textSize: "1",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "new-status": {
|
||||||
|
const { station } = pageData as SetNewStatusPageParams;
|
||||||
|
set({
|
||||||
|
page: "new-status",
|
||||||
|
lines: [
|
||||||
|
{
|
||||||
|
textLeft: `VAR#.${station?.bosCallsign}`,
|
||||||
|
style: { fontWeight: "bold" },
|
||||||
|
textSize: "2",
|
||||||
|
},
|
||||||
|
{ textLeft: "ILS VAR#", textSize: "3" },
|
||||||
|
{
|
||||||
|
textLeft: "new status received",
|
||||||
|
style: { fontWeight: "bold" },
|
||||||
|
textSize: "4",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
set({ page: "home" });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
name: "mrt-store", // unique name
|
name: "mrt-store", // unique name
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { create } from "zustand";
|
|||||||
import { dispatchSocket } from "../../dispatch/socket";
|
import { dispatchSocket } from "../../dispatch/socket";
|
||||||
import { ConnectedAircraft, Mission, Station } from "@repo/db";
|
import { ConnectedAircraft, Mission, Station } from "@repo/db";
|
||||||
import { pilotSocket } from "pilot/socket";
|
import { pilotSocket } from "pilot/socket";
|
||||||
import { useMrtStore } from "_store/pilot/MrtStore";
|
import { useDmeStore } from "_store/pilot/dmeStore";
|
||||||
|
|
||||||
interface ConnectionStore {
|
interface ConnectionStore {
|
||||||
status: "connected" | "disconnected" | "connecting" | "error";
|
status: "connected" | "disconnected" | "connecting" | "error";
|
||||||
@@ -75,11 +75,14 @@ pilotSocket.on("aircraft-update", (data) => {
|
|||||||
usePilotConnectionStore.setState({
|
usePilotConnectionStore.setState({
|
||||||
connectedAircraft: data,
|
connectedAircraft: data,
|
||||||
});
|
});
|
||||||
useMrtStore.getState().setLines(getNew);
|
/* useMrtStore.getState().setLines(getNew); */
|
||||||
});
|
});
|
||||||
|
|
||||||
pilotSocket.on("mission-alert", (data) => {
|
pilotSocket.on("mission-alert", (data: Mission & { Stations: Station[] }) => {
|
||||||
usePilotConnectionStore.setState({
|
usePilotConnectionStore.setState({
|
||||||
activeMission: data,
|
activeMission: data,
|
||||||
});
|
});
|
||||||
|
useDmeStore.getState().setPage({
|
||||||
|
page: "new-mission",
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
195
apps/dispatch/app/_store/pilot/dmeStore.ts
Normal file
195
apps/dispatch/app/_store/pilot/dmeStore.ts
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
import { Mission, Station, User } from "@repo/db";
|
||||||
|
import { DisplayLineProps } from "pilot/_components/dme/Dme";
|
||||||
|
import { create } from "zustand";
|
||||||
|
import { syncTabs } from "zustand-sync-tabs";
|
||||||
|
|
||||||
|
interface SetHomePageParams {
|
||||||
|
page: "home";
|
||||||
|
user: User;
|
||||||
|
station: Station;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SetErrorPageParams {
|
||||||
|
page: "error";
|
||||||
|
error: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SetNewMissionPageParams {
|
||||||
|
page: "new-mission";
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SetMissionPageParams {
|
||||||
|
page: "mission";
|
||||||
|
mission: Mission & { Stations: Station[] };
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SetAcknowledgePageParams {
|
||||||
|
page: "acknowledge";
|
||||||
|
}
|
||||||
|
|
||||||
|
type SetPageParams =
|
||||||
|
| SetHomePageParams
|
||||||
|
| SetNewMissionPageParams
|
||||||
|
| SetMissionPageParams
|
||||||
|
| SetErrorPageParams
|
||||||
|
| SetAcknowledgePageParams;
|
||||||
|
|
||||||
|
interface MrtStore {
|
||||||
|
page: SetPageParams["page"];
|
||||||
|
|
||||||
|
lines: DisplayLineProps[];
|
||||||
|
|
||||||
|
setPage: (pageData: SetPageParams) => void;
|
||||||
|
setLines: (lines: MrtStore["lines"]) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useDmeStore = create<MrtStore>(
|
||||||
|
syncTabs(
|
||||||
|
(set) => ({
|
||||||
|
page: "home",
|
||||||
|
pageData: {
|
||||||
|
message: "",
|
||||||
|
},
|
||||||
|
lines: [
|
||||||
|
{
|
||||||
|
textLeft: "VAR.#",
|
||||||
|
textSize: "2",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
textLeft: "No Data",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
setLines: (lines) => set({ lines }),
|
||||||
|
setPage: (pageData) => {
|
||||||
|
switch (pageData.page) {
|
||||||
|
case "home": {
|
||||||
|
set({
|
||||||
|
page: "home",
|
||||||
|
lines: [
|
||||||
|
{ textMid: "⠀" },
|
||||||
|
{
|
||||||
|
textMid: pageData.station.bosCallsign
|
||||||
|
? `VAR#.${pageData.station.bosCallsign}`
|
||||||
|
: "no Data",
|
||||||
|
style: { fontWeight: "bold" },
|
||||||
|
},
|
||||||
|
{ textMid: "⠀" },
|
||||||
|
{
|
||||||
|
textMid: new Date().toLocaleDateString(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
textMid: new Date().toLocaleTimeString(),
|
||||||
|
style: { fontWeight: "bold" },
|
||||||
|
},
|
||||||
|
{ textMid: "⠀" },
|
||||||
|
{
|
||||||
|
textMid: `${pageData.user.lastname} ${pageData.user.firstname}`,
|
||||||
|
},
|
||||||
|
{ textMid: "⠀" },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "new-mission": {
|
||||||
|
set({
|
||||||
|
page: "new-mission",
|
||||||
|
lines: [
|
||||||
|
{ textMid: "⠀" },
|
||||||
|
{
|
||||||
|
textMid: "new mission received",
|
||||||
|
style: { fontWeight: "bold" },
|
||||||
|
},
|
||||||
|
{ textMid: "⠀" },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "mission": {
|
||||||
|
set({
|
||||||
|
page: "mission",
|
||||||
|
lines: [
|
||||||
|
{
|
||||||
|
textLeft: `${pageData.mission.missionKeywordAbbreviation}`,
|
||||||
|
textRight: pageData.mission.Stations.map(
|
||||||
|
(s) => s.bosCallsignShort,
|
||||||
|
).join(","),
|
||||||
|
style: { fontWeight: "bold" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
textMid: `${pageData.mission.missionKeywordName}`,
|
||||||
|
style: { fontWeight: "bold" },
|
||||||
|
},
|
||||||
|
{ textLeft: `${pageData.mission.addressStreet}` },
|
||||||
|
{
|
||||||
|
textLeft: `${pageData.mission.addressZip} ${pageData.mission.addressCity}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
textMid: "Patienteninfos:",
|
||||||
|
style: { fontWeight: "bold" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
textLeft:
|
||||||
|
pageData.mission.missionPatientInfo || "keine Daten",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
textMid: "Weitere Infos:",
|
||||||
|
style: { fontWeight: "bold" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
textLeft:
|
||||||
|
pageData.mission.missionAdditionalInfo || "keine Daten",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "error": {
|
||||||
|
set({
|
||||||
|
page: "error",
|
||||||
|
lines: [
|
||||||
|
{ textMid: "Fehler:" },
|
||||||
|
{
|
||||||
|
textMid: pageData.error,
|
||||||
|
style: { fontWeight: "bold" },
|
||||||
|
},
|
||||||
|
{ textMid: "⠀" },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "acknowledge": {
|
||||||
|
set({
|
||||||
|
page: "acknowledge",
|
||||||
|
lines: [
|
||||||
|
{ textMid: "⠀" },
|
||||||
|
{
|
||||||
|
textMid: "Einsatz angenommen",
|
||||||
|
style: { fontWeight: "bold" },
|
||||||
|
},
|
||||||
|
{ textMid: "⠀" },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
set({
|
||||||
|
page: "error",
|
||||||
|
lines: [
|
||||||
|
{ textMid: "Fehler:" },
|
||||||
|
{
|
||||||
|
textMid: `Unbekannte Seite`,
|
||||||
|
style: { fontWeight: "bold" },
|
||||||
|
},
|
||||||
|
{ textMid: "⠀" },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
name: "dme-store", // unique name
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
@@ -107,7 +107,7 @@ const AircraftPopupContent = ({
|
|||||||
return missions?.find(
|
return missions?.find(
|
||||||
(m) =>
|
(m) =>
|
||||||
(m.state === "running" || m.state === "draft") &&
|
(m.state === "running" || m.state === "draft") &&
|
||||||
m.missionStationIds.includes(aircraft.Station.id.toString()),
|
m.missionStationIds.includes(aircraft.Station.id),
|
||||||
);
|
);
|
||||||
}, [missions, aircraft.Station.id]);
|
}, [missions, aircraft.Station.id]);
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import {
|
|||||||
Trash,
|
Trash,
|
||||||
User,
|
User,
|
||||||
SmartphoneNfc,
|
SmartphoneNfc,
|
||||||
|
CheckCheck,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import {
|
import {
|
||||||
getPublicUser,
|
getPublicUser,
|
||||||
@@ -23,15 +24,18 @@ import {
|
|||||||
MissionLog,
|
MissionLog,
|
||||||
MissionMessageLog,
|
MissionMessageLog,
|
||||||
Prisma,
|
Prisma,
|
||||||
|
Station,
|
||||||
} from "@repo/db";
|
} from "@repo/db";
|
||||||
import { usePannelStore } from "_store/pannelStore";
|
import { usePannelStore } from "_store/pannelStore";
|
||||||
import { useSession } from "next-auth/react";
|
import { useSession } from "next-auth/react";
|
||||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||||
import {
|
import {
|
||||||
deleteMissionAPI,
|
deleteMissionAPI,
|
||||||
editMissionAPI,
|
editMissionAPI,
|
||||||
sendMissionAPI,
|
sendMissionAPI,
|
||||||
} from "querys/missions";
|
} from "querys/missions";
|
||||||
|
import { getConnectedAircraftsAPI } from "querys/aircrafts";
|
||||||
|
import { getStationsAPI } from "querys/stations";
|
||||||
|
|
||||||
const Einsatzdetails = ({ mission }: { mission: Mission }) => {
|
const Einsatzdetails = ({ mission }: { mission: Mission }) => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
@@ -58,6 +62,22 @@ const Einsatzdetails = ({ mission }: { mission: Mission }) => {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
const editMissionMutation = useMutation({
|
||||||
|
mutationKey: ["missions"],
|
||||||
|
mutationFn: ({
|
||||||
|
id,
|
||||||
|
mission,
|
||||||
|
}: {
|
||||||
|
id: number;
|
||||||
|
mission: Prisma.MissionUpdateInput;
|
||||||
|
}) => editMissionAPI(id, mission),
|
||||||
|
onSuccess: () => {
|
||||||
|
toast.success("Gespeichert");
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: ["missions"],
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
const { setMissionFormValues, setOpen } = usePannelStore((state) => state);
|
const { setMissionFormValues, setOpen } = usePannelStore((state) => state);
|
||||||
return (
|
return (
|
||||||
<div className="p-4 text-base-content">
|
<div className="p-4 text-base-content">
|
||||||
@@ -66,28 +86,48 @@ const Einsatzdetails = ({ mission }: { mission: Mission }) => {
|
|||||||
<Flag /> Einsatzdetails
|
<Flag /> Einsatzdetails
|
||||||
</h2>
|
</h2>
|
||||||
{mission.state !== "draft" && (
|
{mission.state !== "draft" && (
|
||||||
<div
|
<div className="space-x-2">
|
||||||
className="tooltip tooltip-primary tooltip-left font-semibold"
|
<div
|
||||||
data-tip="Einsatzdaten übernehmen"
|
className="tooltip tooltip-primary tooltip-left font-semibold"
|
||||||
>
|
data-tip="Einsatzdaten übernehmen"
|
||||||
<button
|
|
||||||
className="btn btn-xs btn-primary btn-dash flex items-center gap-2"
|
|
||||||
onClick={() => {
|
|
||||||
setMissionFormValues({
|
|
||||||
...mission,
|
|
||||||
id: undefined,
|
|
||||||
hpgAmbulanceState: null,
|
|
||||||
hpgFireEngineState: null,
|
|
||||||
hpgPoliceState: null,
|
|
||||||
hpgLocationLat: undefined,
|
|
||||||
hpgLocationLng: undefined,
|
|
||||||
state: "draft",
|
|
||||||
});
|
|
||||||
setOpen(true);
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<Repeat2 size={16} />
|
<button
|
||||||
</button>
|
className="btn btn-xs btn-primary btn-dash flex items-center gap-2"
|
||||||
|
onClick={() => {
|
||||||
|
setMissionFormValues({
|
||||||
|
...mission,
|
||||||
|
id: undefined,
|
||||||
|
hpgAmbulanceState: null,
|
||||||
|
hpgFireEngineState: null,
|
||||||
|
hpgPoliceState: null,
|
||||||
|
hpgLocationLat: undefined,
|
||||||
|
hpgLocationLng: undefined,
|
||||||
|
state: "draft",
|
||||||
|
});
|
||||||
|
setOpen(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Repeat2 size={16} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="tooltip tooltip-warning tooltip-left font-semibold z-[9999]"
|
||||||
|
data-tip="Einsatz abschließen"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
className="btn btn-xs btn-warning flex items-center gap-2"
|
||||||
|
onClick={() => {
|
||||||
|
editMissionMutation.mutate({
|
||||||
|
id: mission.id,
|
||||||
|
mission: {
|
||||||
|
state: "finished",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CheckCheck size={16} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -177,46 +217,84 @@ const Patientdetails = ({ mission }: { mission: Mission }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const Rettungsmittel = ({ mission }: { mission: Mission }) => {
|
const Rettungsmittel = ({ mission }: { mission: Mission }) => {
|
||||||
/* const [stations, setStations] = useState<Station[]>([]);
|
const { data: conenctedAircrafts } = useQuery({
|
||||||
useEffect(() => {
|
queryKey: ["aircrafts"],
|
||||||
getStations().then((data) => {
|
queryFn: getConnectedAircraftsAPI,
|
||||||
setStations(data);
|
});
|
||||||
});
|
const { data: stations } = useQuery({
|
||||||
}, []); */
|
queryKey: ["mission", "stations-mission", mission.id],
|
||||||
// Mockup data
|
queryFn: () =>
|
||||||
const mockupData = [
|
getStationsAPI({
|
||||||
{ bosCallsign: "Christoph 31", FMSstatus: 2, min: 6 },
|
id: {
|
||||||
{ bosCallsign: "RTW", FMSstatus: 3, min: 2 },
|
in: mission.missionStationIds,
|
||||||
{ bosCallsign: "Polizei", FMSstatus: 4, min: 0 },
|
},
|
||||||
];
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const sendAlertMutation = useMutation({
|
||||||
|
mutationKey: ["missions"],
|
||||||
|
mutationFn: sendMissionAPI,
|
||||||
|
onError: (error) => {
|
||||||
|
console.error(error);
|
||||||
|
toast.error("Fehler beim Alarmieren");
|
||||||
|
},
|
||||||
|
onSuccess: (data) => {
|
||||||
|
toast.success(data.message);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="p-4 text-base-content">
|
<div className="p-4 text-base-content">
|
||||||
<h2 className="flex items-center gap-2 text-lg font-bold mb-3">
|
<div className="flex items-center w-full justify-between">
|
||||||
<SmartphoneNfc /> Rettungsmittel
|
<h2 className="flex items-center gap-2 text-lg font-bold mb-3">
|
||||||
</h2>
|
<SmartphoneNfc /> Rettungsmittel
|
||||||
|
</h2>
|
||||||
|
<div
|
||||||
|
className="tooltip tooltip-primary tooltip-left font-semibold"
|
||||||
|
data-tip="Einsatz erneut alarmieren"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
className="btn btn-xs btn-primary btn-outline"
|
||||||
|
onClick={() => {
|
||||||
|
sendAlertMutation.mutate(mission.id);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<BellRing size={16} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<ul className="space-y-2 max-h-[300px] overflow-y-auto overflow-x-auto">
|
<ul className="space-y-2 max-h-[300px] overflow-y-auto overflow-x-auto">
|
||||||
{mockupData.map((item, index) => (
|
{stations?.map((station, index) => {
|
||||||
<li key={index} className="flex items-center gap-2">
|
const connectedAircraft = conenctedAircrafts?.find(
|
||||||
<span
|
(aircraft) => aircraft.stationId === station.id,
|
||||||
className="font-bold text-base"
|
);
|
||||||
style={{
|
|
||||||
color: FMS_STATUS_TEXT_COLORS[item.FMSstatus],
|
return (
|
||||||
}}
|
<li key={index} className="flex items-center gap-2">
|
||||||
>
|
{connectedAircraft && (
|
||||||
{item.FMSstatus}
|
<span
|
||||||
</span>
|
className="font-bold text-base"
|
||||||
<span className="text-base-content">
|
style={{
|
||||||
<span className="font-bold">{item.bosCallsign}</span>
|
color: FMS_STATUS_TEXT_COLORS[connectedAircraft.fmsStatus],
|
||||||
{item.min > 0 && (
|
}}
|
||||||
<>
|
>
|
||||||
<br />
|
{connectedAircraft.fmsStatus}
|
||||||
Ankunft in ca. {item.min} min
|
</span>
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
</span>
|
<span className="text-base-content">
|
||||||
</li>
|
<div>
|
||||||
))}
|
<span className="font-bold">{station.bosCallsign}</span>
|
||||||
|
{/* {item.min > 0 && (
|
||||||
|
<>
|
||||||
|
<br />
|
||||||
|
Ankunft in ca. {item.min} min
|
||||||
|
</>
|
||||||
|
)} */}
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</ul>
|
</ul>
|
||||||
<div className="divider mt-0 mb-0" />
|
<div className="divider mt-0 mb-0" />
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
|
|||||||
130
apps/dispatch/app/pilot/_components/dme/Dme.tsx
Normal file
130
apps/dispatch/app/pilot/_components/dme/Dme.tsx
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
import { CSSProperties } from "react";
|
||||||
|
import sQuadImageNoReflections from "./Melder_NoReflections.png";
|
||||||
|
import sQuadReflection from "./reflektion.png";
|
||||||
|
import { useButtons } from "./useButtons";
|
||||||
|
import { useSounds } from "./useSounds";
|
||||||
|
import Image from "next/image";
|
||||||
|
import { useDmeStore } from "_store/pilot/dmeStore";
|
||||||
|
|
||||||
|
const DME_BUTTON_STYLES: CSSProperties = {
|
||||||
|
cursor: "pointer",
|
||||||
|
zIndex: "9999",
|
||||||
|
backgroundColor: "transparent",
|
||||||
|
border: "none",
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface DisplayLineProps {
|
||||||
|
style?: CSSProperties;
|
||||||
|
textLeft?: string;
|
||||||
|
textMid?: string;
|
||||||
|
textRight?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DisplayLine = ({
|
||||||
|
style = {},
|
||||||
|
textLeft,
|
||||||
|
textMid,
|
||||||
|
textRight,
|
||||||
|
}: DisplayLineProps) => {
|
||||||
|
const INNER_TEXT_PARTS: CSSProperties = {
|
||||||
|
fontFamily: "Melder",
|
||||||
|
flex: "1",
|
||||||
|
flexBasis: "auto",
|
||||||
|
overflowWrap: "break-word",
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontFamily: "Famirids",
|
||||||
|
display: "flex",
|
||||||
|
flexWrap: "wrap",
|
||||||
|
...style,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span style={INNER_TEXT_PARTS}>{textLeft}</span>
|
||||||
|
<span style={{ textAlign: "center", ...INNER_TEXT_PARTS }}>
|
||||||
|
{textMid}
|
||||||
|
</span>
|
||||||
|
<span style={{ textAlign: "end", ...INNER_TEXT_PARTS }}>{textRight}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Dme = () => {
|
||||||
|
useSounds();
|
||||||
|
const { handleButton } = useButtons();
|
||||||
|
const lines = useDmeStore((state) => state.lines);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: "grid",
|
||||||
|
aspectRatio: "1037 / 800",
|
||||||
|
color: "black",
|
||||||
|
height: "auto",
|
||||||
|
width: "auto",
|
||||||
|
maxHeight: "100%",
|
||||||
|
maxWidth: "100%",
|
||||||
|
overflow: "hidden",
|
||||||
|
gridTemplateColumns:
|
||||||
|
"25.84% 6.08% 10.99% 11.57% 11.28% 10.90% 18.71% 4.63%",
|
||||||
|
gridTemplateRows: "2.66% 9.3% 8.9% 7.2% 45.05% 27%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
src={sQuadImageNoReflections}
|
||||||
|
alt="sQuadImage"
|
||||||
|
style={{
|
||||||
|
zIndex: 0,
|
||||||
|
height: "100%",
|
||||||
|
width: "100%",
|
||||||
|
gridArea: "1 / 1 / 10 / 10",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
onClick={handleButton("main")}
|
||||||
|
style={{ gridArea: "2 / 7 / 4 / 8", ...DME_BUTTON_STYLES }}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
onClick={handleButton("menu")}
|
||||||
|
style={{ gridArea: "3 / 2 / 4 / 3", ...DME_BUTTON_STYLES }}
|
||||||
|
/>
|
||||||
|
{/* <button
|
||||||
|
onClick={handleButton('left')}
|
||||||
|
style={{ gridArea: '3 / 4 / 4 / 5', ...DME_BUTTON_STYLES }}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
onClick={handleButton('right')}
|
||||||
|
style={{ gridArea: '3 / 5 / 4 / 6', ...DME_BUTTON_STYLES }}
|
||||||
|
/> */}
|
||||||
|
<Image
|
||||||
|
src={sQuadReflection}
|
||||||
|
alt="sQuadImage"
|
||||||
|
style={{
|
||||||
|
zIndex: 1,
|
||||||
|
height: "100%",
|
||||||
|
width: "100%",
|
||||||
|
gridArea: "1 / 1 / 10 / 10",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
id="display"
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
zIndex: "2",
|
||||||
|
gridArea: "5 / 2 / 6 / 6",
|
||||||
|
padding: "5px",
|
||||||
|
overflowX: "hidden",
|
||||||
|
overflowY: "auto",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{lines.map((l, i) => {
|
||||||
|
return <DisplayLine key={`line-${i}`} {...l} />;
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
BIN
apps/dispatch/app/pilot/_components/dme/Melder_NoReflections.png
Normal file
BIN
apps/dispatch/app/pilot/_components/dme/Melder_NoReflections.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 626 KiB |
BIN
apps/dispatch/app/pilot/_components/dme/reflektion.png
Normal file
BIN
apps/dispatch/app/pilot/_components/dme/reflektion.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 167 KiB |
BIN
apps/dispatch/app/pilot/_components/dme/squad-x15.jpg
Normal file
BIN
apps/dispatch/app/pilot/_components/dme/squad-x15.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 610 KiB |
33
apps/dispatch/app/pilot/_components/dme/useButtons.ts
Normal file
33
apps/dispatch/app/pilot/_components/dme/useButtons.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import { usePilotConnectionStore } from "_store/pilot/connectionStore";
|
||||||
|
import { useDmeStore } from "_store/pilot/dmeStore";
|
||||||
|
|
||||||
|
import { useSession } from "next-auth/react";
|
||||||
|
export const useButtons = () => {
|
||||||
|
const { page, setPage } = useDmeStore((state) => state);
|
||||||
|
const user = useSession().data?.user;
|
||||||
|
const station = usePilotConnectionStore((state) => state.selectedStation);
|
||||||
|
|
||||||
|
const handleButton = (button: "main" | "menu" | "left" | "right") => () => {
|
||||||
|
switch (button) {
|
||||||
|
case "main":
|
||||||
|
if (page === "mission") {
|
||||||
|
setPage({ page: "acknowledge" });
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "menu":
|
||||||
|
console.log("home", page, { station, user });
|
||||||
|
if (station && user) {
|
||||||
|
setPage({ page: "home", station, user });
|
||||||
|
} else {
|
||||||
|
setPage({ page: "error", error: "No station or user found" });
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
setPage({ page: "error", error: "Button now allowed" });
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return { handleButton };
|
||||||
|
};
|
||||||
52
apps/dispatch/app/pilot/_components/dme/useSounds.ts
Normal file
52
apps/dispatch/app/pilot/_components/dme/useSounds.ts
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
"use client";
|
||||||
|
import { usePilotConnectionStore } from "_store/pilot/connectionStore";
|
||||||
|
import { useDmeStore } from "_store/pilot/dmeStore";
|
||||||
|
import { useEffect, useRef } from "react";
|
||||||
|
|
||||||
|
export const useSounds = () => {
|
||||||
|
const { page, setPage } = useDmeStore((state) => state);
|
||||||
|
const mission = usePilotConnectionStore((state) => state.activeMission);
|
||||||
|
|
||||||
|
const newMissionSound = useRef<HTMLAudioElement | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (typeof window !== "undefined") {
|
||||||
|
newMissionSound.current = new Audio("/sounds/Melder3.wav");
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const timeouts: NodeJS.Timeout[] = [];
|
||||||
|
|
||||||
|
if (page === "new-mission" && newMissionSound.current) {
|
||||||
|
console.log("new-mission", mission);
|
||||||
|
newMissionSound.current.currentTime = 0;
|
||||||
|
newMissionSound.current.volume = 0.3;
|
||||||
|
newMissionSound.current.play();
|
||||||
|
if (mission) {
|
||||||
|
timeouts.push(
|
||||||
|
setTimeout(() => setPage({ page: "mission", mission }), 500),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else if (page === "acknowledge") {
|
||||||
|
newMissionSound.current?.pause();
|
||||||
|
if (mission) {
|
||||||
|
timeouts.push(
|
||||||
|
setTimeout(() => setPage({ page: "mission", mission }), 500),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
timeouts.push(
|
||||||
|
setTimeout(
|
||||||
|
() => setPage({ page: "error", error: "Einsatz nicht gebunden" }),
|
||||||
|
500,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return () => {
|
||||||
|
timeouts.forEach((t) => {
|
||||||
|
clearTimeout(t);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}, [page, setPage, mission]);
|
||||||
|
};
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { CSSProperties } from "react";
|
import { CSSProperties, useEffect } from "react";
|
||||||
import MrtImage from "./MRT.png";
|
import MrtImage from "./MRT.png";
|
||||||
import { useButtons } from "./useButtons";
|
import { useButtons } from "./useButtons";
|
||||||
import { useSounds } from "./useSounds";
|
import { useSounds } from "./useSounds";
|
||||||
|
|||||||
@@ -1,115 +1,23 @@
|
|||||||
import { useSession } from "next-auth/react";
|
import { ConnectedAircraft } from "@repo/db";
|
||||||
import { usePilotConnectionStore } from "_store/pilot/connectionStore";
|
import { usePilotConnectionStore } from "_store/pilot/connectionStore";
|
||||||
import { useMrtStore } from "_store/pilot/MrtStore";
|
import { useMrtStore } from "_store/pilot/MrtStore";
|
||||||
|
import { pilotSocket } from "pilot/socket";
|
||||||
import { editConnectedAircraftAPI } from "querys/aircrafts";
|
import { editConnectedAircraftAPI } from "querys/aircrafts";
|
||||||
import { Station } from "@repo/db";
|
import { useEffect } from "react";
|
||||||
import { DisplayLineProps } from "pilot/_components/mrt/Mrt";
|
|
||||||
|
|
||||||
export const fmsStatusDescription: { [key: string]: string } = {
|
|
||||||
NaN: "Keine Daten",
|
|
||||||
"0": "Prio. Sprechwunsch",
|
|
||||||
"1": "Frei auf Funk",
|
|
||||||
"2": "Einsatzbereit am LRZ",
|
|
||||||
"3": "Auf dem Weg",
|
|
||||||
"4": "Am Einsatzort",
|
|
||||||
"5": "Sprechwunsch",
|
|
||||||
"6": "Nicht einsatzbereit",
|
|
||||||
"7": "Patient aufgenommen",
|
|
||||||
"8": "Am Transportziel",
|
|
||||||
"9": "Fremdanmeldung",
|
|
||||||
E: "Indent/Abbruch/Einsatzbefehl abgebrochen",
|
|
||||||
C: "Anmelden zur Übernahme des Einsatzes",
|
|
||||||
F: "Kommen über Draht",
|
|
||||||
H: "Fahren auf Wache",
|
|
||||||
J: "Sprechaufforderung",
|
|
||||||
L: "Lagebericht abgeben",
|
|
||||||
P: "Einsatz mit Polizei/Pause machen",
|
|
||||||
U: "Ungültiger Status",
|
|
||||||
c: "Status korrigieren",
|
|
||||||
d: "Transportziel angeben",
|
|
||||||
h: "Zielklinik verständigt",
|
|
||||||
o: "Warten, alle Abfrageplätze belegt",
|
|
||||||
u: "Verstanden",
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getSendingLines = (station: Station): DisplayLineProps[] => {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
textLeft: `VAR#.${station?.bosCallsign}123`,
|
|
||||||
style: { fontWeight: "bold" },
|
|
||||||
textSize: "2",
|
|
||||||
},
|
|
||||||
{ textLeft: "ILS VAR#", textSize: "3" },
|
|
||||||
{
|
|
||||||
textMid: "sending...",
|
|
||||||
style: { fontWeight: "bold" },
|
|
||||||
textSize: "4",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
textLeft: "Status wird gesendet...",
|
|
||||||
textSize: "1",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getHomeLines = (
|
|
||||||
station: Station,
|
|
||||||
fmsStatus: string,
|
|
||||||
): DisplayLineProps[] => {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
textLeft: `VAR#.${station?.bosCallsign}`,
|
|
||||||
style: { fontWeight: "bold" },
|
|
||||||
textSize: "2",
|
|
||||||
},
|
|
||||||
{ textLeft: "ILS VAR#", textSize: "3" },
|
|
||||||
{
|
|
||||||
textLeft: fmsStatus,
|
|
||||||
style: { fontWeight: "extrabold" },
|
|
||||||
textSize: "4",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
textLeft: fmsStatusDescription[fmsStatus],
|
|
||||||
textSize: "1",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getNewStatusLines = (
|
|
||||||
station: Station,
|
|
||||||
fmsStatus: string,
|
|
||||||
): DisplayLineProps[] => {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
textLeft: `VAR#.${station?.bosCallsign}`,
|
|
||||||
style: { fontWeight: "bold" },
|
|
||||||
textSize: "2",
|
|
||||||
},
|
|
||||||
{ textLeft: "ILS VAR#", textSize: "3" },
|
|
||||||
{
|
|
||||||
textLeft: fmsStatus,
|
|
||||||
style: { fontWeight: "extrabold" },
|
|
||||||
textSize: "4",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
textLeft: fmsStatusDescription[fmsStatus],
|
|
||||||
textSize: "1",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useButtons = () => {
|
export const useButtons = () => {
|
||||||
const user = useSession().data?.user;
|
|
||||||
const station = usePilotConnectionStore((state) => state.selectedStation);
|
const station = usePilotConnectionStore((state) => state.selectedStation);
|
||||||
const connectedAircraft = usePilotConnectionStore(
|
const connectedAircraft = usePilotConnectionStore(
|
||||||
(state) => state.connectedAircraft,
|
(state) => state.connectedAircraft,
|
||||||
);
|
);
|
||||||
|
const connectionStatus = usePilotConnectionStore((state) => state.status);
|
||||||
|
|
||||||
const { page, setLines } = useMrtStore((state) => state);
|
const { page, setPage } = useMrtStore((state) => state);
|
||||||
|
|
||||||
const handleButton =
|
const handleButton =
|
||||||
(button: "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" | "0") =>
|
(button: "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" | "0") =>
|
||||||
() => {
|
() => {
|
||||||
|
if (connectionStatus !== "connected") return;
|
||||||
if (!station) return;
|
if (!station) return;
|
||||||
if (!connectedAircraft?.id) return;
|
if (!connectedAircraft?.id) return;
|
||||||
if (
|
if (
|
||||||
@@ -125,16 +33,32 @@ export const useButtons = () => {
|
|||||||
button === "0"
|
button === "0"
|
||||||
) {
|
) {
|
||||||
if (page !== "home") return;
|
if (page !== "home") return;
|
||||||
setLines(getSendingLines(station));
|
setPage({ page: "sending-status", station });
|
||||||
|
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
await editConnectedAircraftAPI(connectedAircraft!.id, {
|
await editConnectedAircraftAPI(connectedAircraft!.id, {
|
||||||
fmsStatus: button,
|
fmsStatus: button,
|
||||||
});
|
});
|
||||||
setLines(getNewStatusLines(station, button));
|
setPage({
|
||||||
|
page: "home",
|
||||||
|
station,
|
||||||
|
fmsStatus: button,
|
||||||
|
});
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
pilotSocket.on("connect", () => {
|
||||||
|
if (!station) return;
|
||||||
|
setPage({ page: "home", fmsStatus: "6", station });
|
||||||
|
});
|
||||||
|
|
||||||
|
pilotSocket.on("aircraft-update", () => {
|
||||||
|
if (!station) return;
|
||||||
|
setPage({ page: "new-status", station });
|
||||||
|
});
|
||||||
|
}, [setPage, station]);
|
||||||
|
|
||||||
return { handleButton };
|
return { handleButton };
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,66 +1,53 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import { usePilotConnectionStore } from "_store/pilot/connectionStore";
|
import { usePilotConnectionStore } from "_store/pilot/connectionStore";
|
||||||
import { useMrtStore } from "_store/pilot/MrtStore";
|
import { useMrtStore } from "_store/pilot/MrtStore";
|
||||||
import { editConnectedAircraftAPI } from "querys/aircrafts";
|
|
||||||
import { useEffect, useRef } from "react";
|
import { useEffect, useRef } from "react";
|
||||||
|
|
||||||
const MRTstatusSound = new Audio("/sounds/MRT-status.mp3");
|
|
||||||
const MrtMessageReceivedSound = new Audio("/sounds/MRT-message-received.mp3");
|
|
||||||
|
|
||||||
export const useSounds = () => {
|
export const useSounds = () => {
|
||||||
const mrtState = useMrtStore((state) => state);
|
const mrtState = useMrtStore((state) => state);
|
||||||
const { connectedAircraft, selectedStation } = usePilotConnectionStore(
|
const { connectedAircraft, selectedStation } = usePilotConnectionStore(
|
||||||
(state) => state,
|
(state) => state,
|
||||||
);
|
);
|
||||||
|
|
||||||
const fmsStatus = connectedAircraft?.fmsStatus || "NaN";
|
const setPage = useMrtStore((state) => state.setPage);
|
||||||
|
const MRTstatusSoundRef = useRef<HTMLAudioElement>(null);
|
||||||
const previousFmsStatus = useRef(fmsStatus || "6");
|
const MrtMessageReceivedSoundRef = useRef<HTMLAudioElement>(null);
|
||||||
const timeout = useRef<NodeJS.Timeout>(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleSoundEnd = () => {
|
if (typeof window !== "undefined") {
|
||||||
mrtState.setPage("home");
|
MRTstatusSoundRef.current = new Audio("/sounds/MRT-status.mp3");
|
||||||
};
|
MrtMessageReceivedSoundRef.current = new Audio(
|
||||||
|
"/sounds/MRT-message-received.mp3",
|
||||||
|
);
|
||||||
|
MRTstatusSoundRef.current.onended = () => {
|
||||||
|
if (!selectedStation || !connectedAircraft?.fmsStatus) return;
|
||||||
|
setPage({
|
||||||
|
page: "home",
|
||||||
|
station: selectedStation,
|
||||||
|
fmsStatus: connectedAircraft?.fmsStatus,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
MrtMessageReceivedSoundRef.current.onended = () => {
|
||||||
|
if (!selectedStation || !connectedAircraft?.fmsStatus) return;
|
||||||
|
setPage({
|
||||||
|
page: "home",
|
||||||
|
station: selectedStation,
|
||||||
|
fmsStatus: connectedAircraft?.fmsStatus,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, [connectedAircraft?.fmsStatus, selectedStation, setPage]);
|
||||||
|
|
||||||
const playSound = (sound: HTMLAudioElement) => {
|
const fmsStatus = connectedAircraft?.fmsStatus || "NaN";
|
||||||
sound.play();
|
|
||||||
sound.addEventListener("ended", handleSoundEnd);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
if (!connectedAircraft) return;
|
if (!connectedAircraft) return;
|
||||||
if (mrtState.page === "new-status") {
|
if (mrtState.page === "new-status") {
|
||||||
if (fmsStatus === "J") {
|
if (fmsStatus === "J" || fmsStatus === "c") {
|
||||||
playSound(MrtMessageReceivedSound);
|
MrtMessageReceivedSoundRef.current?.play();
|
||||||
|
|
||||||
timeout.current = setTimeout(() => {
|
|
||||||
editConnectedAircraftAPI(connectedAircraft.id, {
|
|
||||||
fmsStatus: previousFmsStatus.current,
|
|
||||||
});
|
|
||||||
}, 5000);
|
|
||||||
} else if (previousFmsStatus.current !== fmsStatus) {
|
|
||||||
playSound(MRTstatusSound);
|
|
||||||
} else {
|
} else {
|
||||||
handleSoundEnd();
|
MRTstatusSoundRef.current?.play();
|
||||||
}
|
|
||||||
if (!timeout.current) {
|
|
||||||
previousFmsStatus.current = fmsStatus || "6";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return () => {
|
}, [mrtState, fmsStatus, connectedAircraft, selectedStation]);
|
||||||
if (timeout.current) clearTimeout(timeout.current);
|
|
||||||
[MRTstatusSound, MrtMessageReceivedSound].forEach((sound) => {
|
|
||||||
sound.removeEventListener("ended", handleSoundEnd);
|
|
||||||
sound.pause();
|
|
||||||
sound.currentTime = 0;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}, [
|
|
||||||
mrtState,
|
|
||||||
fmsStatus,
|
|
||||||
connectedAircraft,
|
|
||||||
selectedStation,
|
|
||||||
previousFmsStatus,
|
|
||||||
timeout,
|
|
||||||
]);
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ export const ConnectionBtn = () => {
|
|||||||
</span>
|
</span>
|
||||||
</h3>
|
</h3>
|
||||||
) : (
|
) : (
|
||||||
<h3 className="text-lg font-bold mb-5">Als Disponent anmelden</h3>
|
<h3 className="text-lg font-bold mb-5">Als Pilot anmelden</h3>
|
||||||
)}
|
)}
|
||||||
<fieldset className="fieldset w-full">
|
<fieldset className="fieldset w-full">
|
||||||
<label className="floating-label w-full text-base">
|
<label className="floating-label w-full text-base">
|
||||||
|
|||||||
@@ -4,10 +4,9 @@ import { Mrt } from "pilot/_components/mrt/Mrt";
|
|||||||
import { Chat } from "../_components/left/Chat";
|
import { Chat } from "../_components/left/Chat";
|
||||||
import { Report } from "../_components/left/Report";
|
import { Report } from "../_components/left/Report";
|
||||||
import { usePilotConnectionStore } from "_store/pilot/connectionStore";
|
import { usePilotConnectionStore } from "_store/pilot/connectionStore";
|
||||||
|
import { Dme } from "pilot/_components/dme/Dme";
|
||||||
|
|
||||||
const DispatchPage = () => {
|
const DispatchPage = () => {
|
||||||
const { activeMission } = usePilotConnectionStore();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative flex-1 flex transition-all duration-500 ease w-full">
|
<div className="relative flex-1 flex transition-all duration-500 ease w-full">
|
||||||
{/* <MapToastCard2 /> */}
|
{/* <MapToastCard2 /> */}
|
||||||
@@ -19,9 +18,9 @@ const DispatchPage = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>{JSON.stringify(activeMission)}</div>
|
<div className="flex flex-col">
|
||||||
<div>
|
|
||||||
<Mrt />
|
<Mrt />
|
||||||
|
<Dme />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
BIN
apps/dispatch/public/sounds/Melder3.wav
Normal file
BIN
apps/dispatch/public/sounds/Melder3.wav
Normal file
Binary file not shown.
Binary file not shown.
@@ -20,15 +20,18 @@ enum PERMISSION {
|
|||||||
}
|
}
|
||||||
|
|
||||||
model User {
|
model User {
|
||||||
id String @id @default(uuid())
|
id String @id @default(uuid())
|
||||||
publicId String @unique
|
publicId String @unique
|
||||||
firstname String
|
firstname String
|
||||||
lastname String
|
lastname String
|
||||||
email String @unique
|
email String @unique
|
||||||
password String
|
password String
|
||||||
vatsimCid Int? @map(name: "vatsim_cid")
|
vatsimCid Int? @map(name: "vatsim_cid")
|
||||||
moodleId Int? @map(name: "moodle_id")
|
moodleId Int? @map(name: "moodle_id")
|
||||||
emailVerified DateTime? @map(name: "email_verified")
|
emailVerified DateTime? @map(name: "email_verified")
|
||||||
|
|
||||||
|
// Settings:
|
||||||
|
|
||||||
image String?
|
image String?
|
||||||
badges BADGES[] @default([])
|
badges BADGES[] @default([])
|
||||||
permissions PERMISSION[] @default([])
|
permissions PERMISSION[] @default([])
|
||||||
|
|||||||
Reference in New Issue
Block a user