Fixed StationSelectBug, Fixed HPGNotificationGuf, see #45

This commit is contained in:
PxlLoewe
2025-07-06 12:53:04 -07:00
parent 690f4876d6
commit 9b954e4053
8 changed files with 60 additions and 37 deletions

View File

@@ -257,6 +257,9 @@ router.post("/:id/hpg-validation-result", async (req, res) => {
type: "hpg-validation",
status: "success",
message: `HPG Validierung erfolgreich`,
data: {
mission: newMission,
},
} as NotificationPayload);
if (result.alertWhenValid) {
@@ -268,6 +271,9 @@ router.post("/:id/hpg-validation-result", async (req, res) => {
type: "hpg-validation",
status: "failed",
message: result.state,
data: {
mission: newMission,
},
} as NotificationPayload);
}
res.json({

View File

@@ -5,10 +5,10 @@ import { Select } from "_components/Select";
import { getConnectedAircraftsAPI } from "_querys/aircrafts";
import { getStationsAPI } from "_querys/stations";
import { Ambulance, FireExtinguisher, Radio, Siren } from "lucide-react";
import { useState } from "react";
import { useEffect, useState } from "react";
import { FieldValues } from "react-hook-form";
type MissionStationsSelectProps<T extends FieldValues> = {
type MissionStationsSelectProps = {
selectedStations?: number[];
className?: string;
menuPlacement?: "top" | "bottom" | "auto"; // Added menuPlacement prop for better control
@@ -27,7 +27,7 @@ type MissionStationsSelectProps<T extends FieldValues> = {
isMulti?: boolean;
};
export function StationsSelect<T extends FieldValues>({
export function StationsSelect({
onChange,
selectedStations,
vehicleStates,
@@ -35,7 +35,7 @@ export function StationsSelect<T extends FieldValues>({
isMulti = true,
menuPlacement = "bottom",
filterSelected = false,
}: MissionStationsSelectProps<T>) {
}: MissionStationsSelectProps) {
const { data: connectedAircrafts } = useQuery({
queryKey: ["aircrafts"],
queryFn: () => getConnectedAircraftsAPI(),
@@ -47,11 +47,21 @@ export function StationsSelect<T extends FieldValues>({
const [value, setValue] = useState<string[]>(selectedStations?.map((id) => String(id)) || []);
useEffect(() => {
setValue([
...(selectedStations || []).map((id) => String(id)),
...(vehicleStates.hpgAmbulanceState !== HpgState.NOT_REQUESTED ? ["RTW"] : []),
...(vehicleStates.hpgFireEngineState !== HpgState.NOT_REQUESTED ? ["FW"] : []),
...(vehicleStates.hpgPoliceState !== HpgState.NOT_REQUESTED ? ["POL"] : []),
]);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [selectedStations, vehicleStates]);
// Helper to check if a station is a vehicle and its state is NOT_REQUESTED
const stationsOptions = [
...(stations?.map((station) => ({
label: station.bosCallsign,
value: station.id,
value: String(station.id),
type: "station" as const,
isOnline: !!connectedAircrafts?.find((a) => a.stationId === station.id),
})) || []),
@@ -78,7 +88,7 @@ export function StationsSelect<T extends FieldValues>({
if (!filterSelected) return true; // If filterSelected is false, include all stations
// Filter out selected stations if filterSelectedStations is true
if (s.type === "station") {
return !selectedStations?.includes(s.value);
return !selectedStations?.map(String)?.includes(s.value);
}
// If the station is a vehicle, we need to check its state
if (s.type === "vehicle") {

View File

@@ -21,7 +21,6 @@ import {
startHpgValidation,
} from "_querys/missions";
import { getKeywordsAPI } from "_querys/keywords";
import { getStationsAPI } from "_querys/stations";
import { useMapStore } from "_store/mapStore";
import { getConnectedAircraftsAPI } from "_querys/aircrafts";
import { HPGValidationRequired } from "_helpers/hpgValidationRequired";
@@ -197,7 +196,7 @@ export const MissionForm = () => {
}
return newMission;
};
console.log(form.formState.errors);
console.log(form.watch("missionStationIds"));
return (
<form className="space-y-4">
{/* Koorinaten Section */}
@@ -282,12 +281,12 @@ export const MissionForm = () => {
className="select select-primary select-bordered w-full mb-4"
onChange={(e) => {
form.setValue("type", e.target.value as missionType);
if (e.target.value === "sekundär") {
form.setValue("missionKeywordName", KEYWORD_CATEGORY.Z_SONSTIGES);
form.setValue("missionKeywordAbbreviation", "VL_S");
form.setValue("hpgMissionString", "Verlegung:4_1_1_1-4_1");
if (e.target.value === "primary") {
form.setValue("missionKeywordName", null as any);
form.setValue("missionKeywordAbbreviation", null as any);
form.setValue("hpgMissionString", null);
} else {
form.setValue("missionKeywordCategory", "V_VERLEGUNG");
form.setValue("missionKeywordCategory", KEYWORD_CATEGORY.V_VERLEGUNG);
form.setValue("missionKeywordAbbreviation", null as any);
form.setValue("hpgMissionString", null);
}

View File

@@ -16,15 +16,15 @@ const Map = dynamic(() => import("_components/map/Map"), {
ssr: false,
});
const DispatchPage = () => {
const PilotPage = () => {
const { connectedAircraft, status } = usePilotConnectionStore((state) => state);
const { data: ownAircraftArray = [] } = useQuery({
queryKey: ["aircrafts", connectedAircraft?.id],
queryKey: ["own-aircraft", connectedAircraft?.id],
queryFn: () =>
getAircraftsAPI({
id: connectedAircraft?.id,
}),
refetchInterval: 1000,
refetchInterval: 10000,
});
const ownAircraft = ownAircraftArray[0];
const simulatorConnected = ownAircraft ? checkSimulatorConnected(ownAircraft) : false;
@@ -73,6 +73,6 @@ const DispatchPage = () => {
);
};
DispatchPage.displayName = "DispatchPage";
PilotPage.displayName = "DispatchPage";
export default DispatchPage;
export default PilotPage;

View File

@@ -59,6 +59,7 @@ export function QueryProvider({ children }: { children: ReactNode }) {
const handleNotification = (notification: NotificationPayload) => {
switch (notification.type) {
case "hpg-validation":
console.log("hpg-validation notification received", notification);
toast.custom(
(t) => <HPGnotificationToast event={notification} mapStore={mapStore} t={t} />,
{

View File

@@ -110,7 +110,7 @@ const AircraftPopupContent = ({
/>
<div>
<div
className="flex gap-[2px] text-white pb-0.5"
className="flex gap-[2px] text-white pb-0.5 overflow-auto"
style={{
backgroundColor: `${FMS_STATUS_TEXT_COLORS[aircraft.fmsStatus]}`,
}}
@@ -152,21 +152,26 @@ const AircraftPopupContent = ({
{aircraft.fmsStatus}
</div>
<div
className="w-100 cursor-pointer px-2"
className="cursor-pointer px-2 min-w-[130px]"
style={{
backgroundColor: `${FMS_STATUS_COLORS[aircraft.fmsStatus]}`,
borderBottom:
currentTab === "aircraft"
? `5px solid ${FMS_STATUS_TEXT_COLORS[aircraft.fmsStatus]}`
: "5px solid transparent",
whiteSpace: "nowrap",
}}
onClick={() => handleTabChange("aircraft")}
>
<span className="text-white text-base font-medium">
{aircraft.Station.bosCallsign}
<span className="text-white text-base font-medium truncate">
{aircraft.Station.bosCallsign.length > 20
? aircraft.Station.bosCallsignShort
: aircraft.Station.bosCallsign}
</span>
<span className="text-sm text-gray-400">
<br />
{aircraft.Station.bosUse}
{aircraft.Station.bosUse === "DUAL_USE" && "(dual use)"}
{aircraft.Station.bosUse === "PRIMARY" && "(primär)"}
{aircraft.Station.bosUse === "SECONDARY" && "(sekundär)"}
</span>
</div>
<div
@@ -184,7 +189,7 @@ const AircraftPopupContent = ({
<span className="text-white text-base font-medium">Einsatz</span>
<br />
<span className="text-white text-sm font-medium">
{mission?.publicId || "Kein aktiver Einsatz"}
{mission?.publicId || "kein Einsatz"}
</span>
</div>
<div

View File

@@ -73,7 +73,6 @@ const StationsLayer = ({ attribution }: { attribution: Control.Attribution }) =>
queryKey: ["stations"],
queryFn: () => getStationsAPI(),
});
console.log("StationsLayer: stations", stations);
const [selectedStations, setSelectedStations] = useState<Station["id"][]>([]);
const attributionText = "";
@@ -306,14 +305,6 @@ const WindfarmOutlineLayer = () => {
export const BaseMaps = () => {
const map = useMap();
const isPannelOpen = usePannelStore((state) => state.isOpen);
useEffect(() => {
setTimeout(() => {
map.invalidateSize();
}, 600);
}, [isPannelOpen]);
return (
<LayersControl position="topleft">
<LayersControl.Overlay name={"Leitstellenbereiche"}>

View File

@@ -3,6 +3,11 @@ import { Prisma, prisma } from "@repo/db";
export async function GET(request: NextRequest): Promise<NextResponse> {
try {
const config = await prisma.config.findFirst({
orderBy: {
createdAt: "desc",
},
});
const filter = JSON.parse(
new URL(request.url).searchParams.get("filter") || "{}",
) as Prisma.ConnectedAircraftWhereInput;
@@ -16,9 +21,15 @@ export async function GET(request: NextRequest): Promise<NextResponse> {
Station: true,
},
});
return NextResponse.json(connectedAircraft, {
return NextResponse.json(
connectedAircraft.map((a) => ({
...a,
posH145active: config?.disableHPG ? false : a.posH145active,
})),
{
status: 200,
});
},
);
} catch (error) {
console.error(error);
return NextResponse.json({ error: "Failed to fetch Aircrafts" }, { status: 500 });