Fix Pilot-conenction

This commit is contained in:
PxlLoewe
2025-07-10 10:19:36 -07:00
parent 3b1ceb8f8c
commit b9eef5252e
6 changed files with 135 additions and 102 deletions

View File

@@ -3,11 +3,10 @@
import { useSession } from "next-auth/react";
import { useDispatchConnectionStore } from "../../../../../_store/dispatch/connectionStore";
import { useEffect, useRef, useState } from "react";
import { toast } from "react-hot-toast";
import { useMutation } from "@tanstack/react-query";
import { Prisma } from "@repo/db";
import { changeDispatcherAPI } from "_querys/dispatcher";
import { getNextDateWithTime } from "@repo/shared-components";
import { Button, getNextDateWithTime } from "@repo/shared-components";
export const ConnectionBtn = () => {
const modalRef = useRef<HTMLDialogElement>(null);
@@ -20,44 +19,19 @@ export const ConnectionBtn = () => {
mutationFn: ({ id, data }: { id: number; data: Prisma.ConnectedDispatcherUpdateInput }) =>
changeDispatcherAPI(id, data),
});
const [logoffDebounce, setLogoffDebounce] = useState<NodeJS.Timeout | null>(null);
const session = useSession();
const uid = session.data?.user?.id;
// useEffect für die Logoff-Zeit
const [logoffHours, logoffMinutes] = form.logoffTime?.split(":").map(Number) || [];
useEffect(() => {
if (!logoffHours || !logoffMinutes) return;
if (logoffDebounce) clearTimeout(logoffDebounce);
const timeout = setTimeout(async () => {
if (!logoffHours || !logoffMinutes || !connection.connectedDispatcher) return;
await changeDispatcherMutation.mutateAsync({
id: connection.connectedDispatcher?.id,
data: {
esimatedLogoutTime:
logoffHours && logoffMinutes ? getNextDateWithTime(logoffHours, logoffMinutes) : null,
},
});
toast.success("Änderung gespeichert!");
modalRef.current?.close();
}, 2000);
setLogoffDebounce(timeout);
// Cleanup function
return () => {
if (logoffDebounce) clearTimeout(logoffDebounce);
};
}, [form.logoffTime, connection.connectedDispatcher]);
useEffect(() => {
// Disconnect the socket when the component unmounts
return () => {
connection.disconnect();
};
}, [connection.disconnect]);
if (!uid) return null;
return (
<div className="rounded-box bg-base-200 flex justify-center items-center gap-2 p-1">
@@ -122,16 +96,36 @@ export const ConnectionBtn = () => {
<form method="dialog" className="w-full flex justify-between">
<button className="btn btn-soft">Zurück</button>
{connection.status == "connected" ? (
<button
className="btn btn-soft btn-error"
type="submit"
onSubmit={() => false}
onClick={() => {
connection.disconnect();
}}
>
Verbindung Trennen
</button>
<>
<Button
className="btn"
onClick={async () => {
if (!connection.connectedDispatcher?.id) return;
await changeDispatcherMutation.mutateAsync({
id: connection.connectedDispatcher?.id,
data: {
esimatedLogoutTime:
logoffHours && logoffMinutes
? getNextDateWithTime(logoffHours, logoffMinutes)
: null,
},
});
modalRef.current?.close();
}}
>
Logoff-Zeit speichern
</Button>
<button
className="btn btn-soft btn-error"
type="submit"
onSubmit={() => false}
onClick={() => {
connection.disconnect();
}}
>
Verbindung Trennen
</button>
</>
) : (
<button
type="submit"

View File

@@ -4,10 +4,9 @@ import { usePilotConnectionStore } from "_store/pilot/connectionStore";
import { useEffect, useRef, useState } from "react";
import { useMutation, useQuery } from "@tanstack/react-query";
import { getStationsAPI } from "_querys/stations";
import toast from "react-hot-toast";
import { editConnectedAircraftAPI, getConnectedAircraftsAPI } from "_querys/aircrafts";
import { Prisma } from "@repo/db";
import { getNextDateWithTime } from "@repo/shared-components";
import { Button, getNextDateWithTime } from "@repo/shared-components";
import { Select } from "_components/Select";
import { Radio } from "lucide-react";
@@ -23,7 +22,6 @@ export const ConnectionBtn = () => {
selectedStationId: null,
debugPosition: false,
});
const [logoffDebounce, setLogoffDebounce] = useState<NodeJS.Timeout | null>(null);
const { data: stations } = useQuery({
queryKey: ["stations"],
@@ -53,7 +51,8 @@ export const ConnectionBtn = () => {
return () => {
connection.disconnect();
};
}, [connection, connection.disconnect]);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [connection.disconnect]);
const [logoffHours, logoffMinutes] = form.logoffTime?.split(":").map(Number) || [];
@@ -62,32 +61,6 @@ export const ConnectionBtn = () => {
queryFn: () => getConnectedAircraftsAPI(),
});
useEffect(() => {
if (!logoffHours || !logoffMinutes || !connection.connectedAircraft) return;
if (logoffDebounce) clearTimeout(logoffDebounce);
const timeout = setTimeout(async () => {
if (!connection.connectedAircraft?.id) return;
await aircraftMutation.mutateAsync({
sessionId: connection.connectedAircraft.id,
change: {
esimatedLogoutTime:
logoffHours && logoffMinutes ? getNextDateWithTime(logoffHours, logoffMinutes) : null,
},
});
modalRef.current?.close();
toast.success("Änderung gespeichert!");
}, 2000);
setLogoffDebounce(timeout);
// Cleanup function to clear timeout
return () => {
if (logoffDebounce) clearTimeout(logoffDebounce);
};
}, [logoffHours, logoffMinutes, connection.connectedAircraft, aircraftMutation, logoffDebounce]);
const session = useSession();
const uid = session.data?.user?.id;
if (!uid) return null;
@@ -185,45 +158,66 @@ export const ConnectionBtn = () => {
)}
</fieldset>
{session.data?.user.permissions.includes("ADMIN_STATION") && (
<fieldset className="fieldset bg-base-100 border-base-300 rounded-box w-full border p-4">
<legend className="fieldset-legend">Debug-optionen</legend>
<label className="label">
<input
checked={form.debugPosition}
onChange={(e) => setForm({ ...form, debugPosition: e.target.checked })}
type="checkbox"
className="checkbox"
/>
Zufalls Position für 2h anzeigen
</label>
</fieldset>
)}
{session.data?.user.permissions.includes("ADMIN_STATION") &&
connection.status === "disconnected" && (
<fieldset className="fieldset bg-base-100 border-base-300 rounded-box w-full border p-4">
<legend className="fieldset-legend">Debug-optionen</legend>
<label className="label">
<input
checked={form.debugPosition}
onChange={(e) => setForm({ ...form, debugPosition: e.target.checked })}
type="checkbox"
className="checkbox"
/>
Zufalls Position für 2h anzeigen
</label>
</fieldset>
)}
<div className="modal-action flex justify-between w-full">
<form method="dialog" className="w-full flex justify-between">
<button className="btn btn-soft">Zurück</button>
{connection.status == "connected" ? (
<button
className="btn btn-soft btn-error"
type="submit"
onSubmit={() => false}
onClick={() => {
connection.disconnect();
}}
>
Verbindung Trennen
</button>
<>
<Button
className="btn"
onClick={async () => {
if (!connection.connectedAircraft) return;
await aircraftMutation.mutateAsync({
sessionId: connection.connectedAircraft.id,
change: {
esimatedLogoutTime:
logoffHours && logoffMinutes
? getNextDateWithTime(logoffHours, logoffMinutes)
: null,
},
});
modalRef.current?.close();
}}
>
Logoff-Zeit speichern
</Button>
<button
className="btn btn-soft btn-error"
type="submit"
onSubmit={() => false}
onClick={() => {
connection.disconnect();
}}
>
Verbindung Trennen
</button>
</>
) : (
<button
<Button
type="submit"
onSubmit={() => false}
onClick={() => {
onClick={async () => {
const selectedStation = stations?.find(
(station) =>
station.id === parseInt(form.selectedStationId?.toString() || ""),
);
if (selectedStation) {
connection.connect(
await connection.connect(
uid,
form.selectedStationId?.toString() || "",
form.logoffTime || "",
@@ -232,11 +226,12 @@ export const ConnectionBtn = () => {
form.debugPosition,
);
}
modalRef.current?.close();
}}
className="btn btn-soft btn-info"
>
{connection.status == "disconnected" ? "Verbinden" : connection.status}
</button>
</Button>
)}
</form>
</div>

View File

@@ -0,0 +1,43 @@
"use client";
import React, {
ButtonHTMLAttributes,
DetailedHTMLProps,
useEffect,
useState,
forwardRef,
} from "react";
import { cn } from "@repo/shared-components";
export const Button = forwardRef<
HTMLButtonElement,
DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement> & {
isLoading?: boolean;
}
>(({ isLoading, ...props }, ref) => {
const [isLoadingState, setIsLoadingState] = useState(isLoading);
useEffect(() => {
setIsLoadingState(isLoading);
}, [isLoading]);
return (
<button
{...props}
ref={ref}
className={cn("btn", props.className)}
disabled={isLoadingState || props.disabled}
onClick={async (e) => {
if (props.onClick) {
setIsLoadingState(true);
await props.onClick(e);
setIsLoadingState(false);
}
}}
>
{isLoadingState && <span className="loading loading-spinner loading-sm"></span>}
{props.children}
</button>
);
});
Button.displayName = "Button";

View File

@@ -1,3 +1,4 @@
export * from "./Badge";
export * from "./PenaltyDropdown";
export * from "./Maintenance";
export * from "./Button";

View File

@@ -15,7 +15,7 @@
"tailwind-merge": "^3.3.0"
},
"devDependencies": {
"@types/react": "^19.1.6",
"@types/react": "^19.1.8",
"@types/react-dom": "^19.1.5",
"react": "^19.1.0",
"react-dom": "^19.1.0"

10
pnpm-lock.yaml generated
View File

@@ -630,11 +630,11 @@ importers:
version: 3.3.0
devDependencies:
'@types/react':
specifier: ^19.1.6
version: 19.1.6
specifier: ^19.1.8
version: 19.1.8
'@types/react-dom':
specifier: ^19.1.5
version: 19.1.5(@types/react@19.1.6)
version: 19.1.5(@types/react@19.1.8)
react:
specifier: ^19.1.0
version: 19.1.0
@@ -8668,9 +8668,9 @@ snapshots:
'@types/range-parser@1.2.7': {}
'@types/react-dom@19.1.5(@types/react@19.1.6)':
'@types/react-dom@19.1.5(@types/react@19.1.8)':
dependencies:
'@types/react': 19.1.6
'@types/react': 19.1.8
'@types/react-dom@19.1.6(@types/react@19.1.8)':
dependencies: