This commit is contained in:
PxlLoewe
2025-07-24 16:13:17 -07:00
9 changed files with 249 additions and 104 deletions

View File

@@ -8,6 +8,8 @@ import { editUserAPI, getUserAPI } from "_querys/user";
import { useSession } from "next-auth/react"; import { useSession } from "next-auth/react";
import { useAudioStore } from "_store/audioStore"; import { useAudioStore } from "_store/audioStore";
import toast from "react-hot-toast"; import toast from "react-hot-toast";
import { useMapStore } from "_store/mapStore";
import { set } from "date-fns";
export const SettingsBtn = () => { export const SettingsBtn = () => {
const session = useSession(); const session = useSession();
@@ -41,6 +43,7 @@ export const SettingsBtn = () => {
}); });
const { setSettings: setAudioSettings } = useAudioStore((state) => state); const { setSettings: setAudioSettings } = useAudioStore((state) => state);
const { setUserSettings: setUserSettings } = useMapStore((state) => state);
useEffect(() => { useEffect(() => {
if (user) { if (user) {
@@ -56,8 +59,11 @@ export const SettingsBtn = () => {
radioVolume: user.settingsRadioVolume || 0.8, radioVolume: user.settingsRadioVolume || 0.8,
autoCloseMapPopup: user.settingsAutoCloseMapPopup || false, autoCloseMapPopup: user.settingsAutoCloseMapPopup || false,
}); });
setUserSettings({
settingsAutoCloseMapPopup: user.settingsAutoCloseMapPopup || false,
});
} }
}, [user, setSettings, setAudioSettings]); }, [user, setSettings, setAudioSettings, setUserSettings]);
const setSettingsPartial = (newSettings: Partial<typeof settings>) => { const setSettingsPartial = (newSettings: Partial<typeof settings>) => {
setSettings((prev) => ({ setSettings((prev) => ({
@@ -178,15 +184,19 @@ export const SettingsBtn = () => {
</div> </div>
<div className="flex w-full justify-center"> <div className="flex w-full justify-center">
<div className="divider w-full"> <div className="divider w-full">Disponenten Einstellungen</div>
<div> </div>
<legend className="fieldset-legend">Login options</legend>
<label className="label"> <div className="flex w-full items-center gap-2">
<input type="checkbox" defaultChecked className="toggle" /> <input
Remember me type="checkbox"
</label> className="toggle"
</div> checked={settings.autoCloseMapPopup}
</div> onChange={(e) => {
setSettingsPartial({ autoCloseMapPopup: e.target.checked });
}}
/>
Popups automatisch schließen
</div> </div>
<div className="modal-action flex justify-between"> <div className="modal-action flex justify-between">
@@ -213,6 +223,7 @@ export const SettingsBtn = () => {
settingsMicDevice: settings.micDeviceId, settingsMicDevice: settings.micDeviceId,
settingsMicVolume: settings.micVolume, settingsMicVolume: settings.micVolume,
settingsRadioVolume: settings.radioVolume, settingsRadioVolume: settings.radioVolume,
settingsAutoCloseMapPopup: settings.autoCloseMapPopup,
}, },
}); });
setAudioSettings({ setAudioSettings({
@@ -220,6 +231,9 @@ export const SettingsBtn = () => {
micVolume: settings.micVolume, micVolume: settings.micVolume,
radioVolume: settings.radioVolume, radioVolume: settings.radioVolume,
}); });
setUserSettings({
settingsAutoCloseMapPopup: settings.autoCloseMapPopup,
});
modalRef.current?.close(); modalRef.current?.close();
toast.success("Einstellungen gespeichert"); toast.success("Einstellungen gespeichert");
}} }}

View File

@@ -15,10 +15,19 @@ export const HPGnotificationToast = ({
}) => { }) => {
const handleClick = () => { const handleClick = () => {
toast.dismiss(t.id); toast.dismiss(t.id);
mapStore.setOpenMissionMarker({
open: [{ id: event.data.mission.id, tab: "home" }], if (mapStore.userSettings.settingsAutoCloseMapPopup) {
close: [], mapStore.setOpenMissionMarker({
}); open: [{ id: event.data.mission.id, tab: "home" }],
close: mapStore.openMissionMarker?.map((m) => m.id) || [],
});
} else {
mapStore.setOpenMissionMarker({
open: [{ id: event.data.mission.id, tab: "home" }],
close: [],
});
}
mapStore.setMap({ mapStore.setMap({
center: [event.data.mission.addressLat, event.data.mission.addressLng], center: [event.data.mission.addressLat, event.data.mission.addressLng],
zoom: 14, zoom: 14,
@@ -29,7 +38,7 @@ export const HPGnotificationToast = ({
return ( return (
<BaseNotification icon={<Cross />} className="flex flex-row"> <BaseNotification icon={<Cross />} className="flex flex-row">
<div className="flex-1"> <div className="flex-1">
<h1 className="text-red-500 font-bold">HPG validierung fehlgeschlagen</h1> <h1 className="font-bold text-red-500">HPG validierung fehlgeschlagen</h1>
<p>{event.message}</p> <p>{event.message}</p>
</div> </div>
<div className="ml-11"> <div className="ml-11">
@@ -43,7 +52,7 @@ export const HPGnotificationToast = ({
return ( return (
<BaseNotification icon={<Check />} className="flex flex-row"> <BaseNotification icon={<Check />} className="flex flex-row">
<div className="flex-1"> <div className="flex-1">
<h1 className="text-green-600 font-bold">HPG validierung erfolgreich</h1> <h1 className="font-bold text-green-600">HPG validierung erfolgreich</h1>
<p className="text-sm">{event.message}</p> <p className="text-sm">{event.message}</p>
</div> </div>
<div className="ml-11"> <div className="ml-11">

View File

@@ -65,15 +65,27 @@ export const MissionAutoCloseToast = ({
lng: mission.addressLng, lng: mission.addressLng,
}, },
}); });
mapStore.setOpenMissionMarker({ if (mapStore.userSettings.settingsAutoCloseMapPopup) {
open: [ mapStore.setOpenMissionMarker({
{ open: [
id: mission.id, {
tab: "home", id: mission.id,
}, tab: "home",
], },
close: [], ],
}); close: mapStore.openMissionMarker?.map((m) => m.id) || [],
});
} else {
mapStore.setOpenMissionMarker({
open: [
{
id: mission.id,
tab: "home",
},
],
close: [],
});
}
toast.dismiss(t.id); toast.dismiss(t.id);
}} }}
> >

View File

@@ -47,7 +47,10 @@ export const StatusToast = ({ event, t }: { event: StationStatus; t: Toast }) =>
} }
}, []); }, []);
const [aircraftDataAcurate, setAircraftDataAccurate] = useState(false); const [aircraftDataAcurate, setAircraftDataAccurate] = useState(false);
const mapStore = useMapStore((s) => s); //const mapStore = useMapStore((s) => s);
const { openAircraftMarker, setOpenAircraftMarker, userSettings, setMap } = useMapStore(
(store) => store,
);
const { data: connectedAircrafts } = useQuery({ const { data: connectedAircrafts } = useQuery({
queryKey: ["aircrafts"], queryKey: ["aircrafts"],
@@ -129,11 +132,18 @@ export const StatusToast = ({ event, t }: { event: StationStatus; t: Toast }) =>
className="mr-1 cursor-pointer font-bold underline" className="mr-1 cursor-pointer font-bold underline"
onClick={() => { onClick={() => {
if (!connectedAircraft.posLat || !connectedAircraft.posLng) return; if (!connectedAircraft.posLat || !connectedAircraft.posLng) return;
mapStore.setOpenAircraftMarker({ if (userSettings.settingsAutoCloseMapPopup) {
open: [{ id: connectedAircraft.id, tab: "fms" }], setOpenAircraftMarker({
close: [], open: [{ id: connectedAircraft.id, tab: "fms" }],
}); close: openAircraftMarker?.map((m) => m.id) || [],
mapStore.setMap({ });
} else {
setOpenAircraftMarker({
open: [{ id: connectedAircraft.id, tab: "fms" }],
close: [],
});
}
setMap({
center: [connectedAircraft.posLat, connectedAircraft.posLng], center: [connectedAircraft.posLat, connectedAircraft.posLng],
zoom: 14, zoom: 14,
}); });

View File

@@ -54,7 +54,14 @@ export const SituationBoard = () => {
queryKey: ["aircrafts"], queryKey: ["aircrafts"],
queryFn: () => getConnectedAircraftsAPI(), queryFn: () => getConnectedAircraftsAPI(),
}); });
const { setOpenAircraftMarker, setOpenMissionMarker, setMap } = useMapStore((state) => state); const {
setOpenAircraftMarker,
setOpenMissionMarker,
setMap,
userSettings,
openAircraftMarker,
openMissionMarker,
} = useMapStore((state) => state);
return ( return (
<div className={cn("dropdown dropdown-top", situationTabOpen && "dropdown-open")}> <div className={cn("dropdown dropdown-top", situationTabOpen && "dropdown-open")}>
@@ -112,15 +119,27 @@ export const SituationBoard = () => {
mission.state === "draft" && "missionListItem", mission.state === "draft" && "missionListItem",
)} )}
onDoubleClick={() => { onDoubleClick={() => {
setOpenMissionMarker({ if (userSettings.settingsAutoCloseMapPopup) {
open: [ setOpenMissionMarker({
{ open: [
id: mission.id, {
tab: "home", id: mission.id,
}, tab: "home",
], },
close: [], ],
}); close: openMissionMarker?.map((m) => m.id) || [],
});
} else {
setOpenMissionMarker({
open: [
{
id: mission.id,
tab: "home",
},
],
close: [],
});
}
setMap({ setMap({
center: { center: {
lat: mission.addressLat, lat: mission.addressLat,
@@ -161,45 +180,57 @@ export const SituationBoard = () => {
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{connectedAircrafts?.map((station) => ( {connectedAircrafts?.map((aircraft) => (
<tr <tr
className="cursor-pointer" className="cursor-pointer"
key={station.id} key={aircraft.id}
onDoubleClick={() => { onDoubleClick={() => {
setOpenAircraftMarker({ if (userSettings.settingsAutoCloseMapPopup) {
open: [ setOpenAircraftMarker({
{ open: [
id: station.id, {
tab: "home", id: aircraft.id,
}, tab: "home",
], },
close: [], ],
}); close: openAircraftMarker?.map((m) => m.id) || [],
if (station.posLat === null || station.posLng === null) return; });
} else {
setOpenAircraftMarker({
open: [
{
id: aircraft.id,
tab: "home",
},
],
close: [],
});
}
if (aircraft.posLat === null || aircraft.posLng === null) return;
setMap({ setMap({
center: { center: {
lat: station.posLat, lat: aircraft.posLat,
lng: station.posLng, lng: aircraft.posLng,
}, },
zoom: 14, zoom: 14,
}); });
}} }}
> >
<td>{station.Station.bosCallsignShort}</td> <td>{aircraft.Station.bosCallsignShort}</td>
<td <td
className="font-lg text-center font-semibold" className="font-lg text-center font-semibold"
style={{ style={{
color: FMS_STATUS_TEXT_COLORS[station.fmsStatus], color: FMS_STATUS_TEXT_COLORS[aircraft.fmsStatus],
backgroundColor: FMS_STATUS_COLORS[station.fmsStatus], backgroundColor: FMS_STATUS_COLORS[aircraft.fmsStatus],
}} }}
> >
{station.fmsStatus} {aircraft.fmsStatus}
</td> </td>
<td className="whitespace-nowrap"> <td className="whitespace-nowrap">
{station.posLng || !station.posLat ? ( {aircraft.posLng || !aircraft.posLat ? (
<>{findLeitstelleForPosition(station.posLng!, station.posLat!)}</> <>{findLeitstelleForPosition(aircraft.posLng!, aircraft.posLat!)}</>
) : ( ) : (
station.Station.bosRadioArea aircraft.Station.bosRadioArea
)} )}
</td> </td>
</tr> </tr>

View File

@@ -17,6 +17,7 @@ import { getConnectedAircraftPositionLogAPI, getConnectedAircraftsAPI } from "_q
import { getMissionsAPI } from "_querys/missions"; import { getMissionsAPI } from "_querys/missions";
import { FMS_STATUS_COLORS, FMS_STATUS_TEXT_COLORS } from "_helpers/fmsStatusColors"; import { FMS_STATUS_COLORS, FMS_STATUS_TEXT_COLORS } from "_helpers/fmsStatusColors";
import { usePilotConnectionStore } from "_store/pilot/connectionStore"; import { usePilotConnectionStore } from "_store/pilot/connectionStore";
import { useSession } from "next-auth/react";
const AircraftPopupContent = ({ const AircraftPopupContent = ({
aircraft, aircraft,
@@ -72,7 +73,7 @@ const AircraftPopupContent = ({
} }
}, [currentTab, aircraft, mission]); }, [currentTab, aircraft, mission]);
const { setOpenAircraftMarker, setMap } = useMapStore((state) => state); const { setOpenAircraftMarker, setMap, openAircraftMarker } = useMapStore((state) => state);
const { anchor } = useSmartPopup(); const { anchor } = useSmartPopup();
return ( return (
<> <>
@@ -229,7 +230,7 @@ const AircraftMarker = ({ aircraft }: { aircraft: ConnectedAircraft & { Station:
const markerRef = useRef<LMarker>(null); const markerRef = useRef<LMarker>(null);
const popupRef = useRef<LPopup>(null); const popupRef = useRef<LPopup>(null);
const { openAircraftMarker, setOpenAircraftMarker } = useMapStore((store) => store); const { openAircraftMarker, setOpenAircraftMarker, userSettings } = useMapStore((store) => store);
const { data: positionLog } = useQuery({ const { data: positionLog } = useQuery({
queryKey: ["positionlog", aircraft.id], queryKey: ["positionlog", aircraft.id],
queryFn: () => queryFn: () =>
@@ -249,15 +250,27 @@ const AircraftMarker = ({ aircraft }: { aircraft: ConnectedAircraft & { Station:
close: [aircraft.id], close: [aircraft.id],
}); });
} else { } else {
setOpenAircraftMarker({ if (userSettings.settingsAutoCloseMapPopup) {
open: [ setOpenAircraftMarker({
{ open: [
id: aircraft.id, {
tab: "home", id: aircraft.id,
}, tab: "home",
], },
close: [], ],
}); close: openAircraftMarker?.map((m) => m.id) || [],
});
} else {
setOpenAircraftMarker({
open: [
{
id: aircraft.id,
tab: "home",
},
],
close: [],
});
}
} }
}; };
const marker = markerRef.current; const marker = markerRef.current;
@@ -265,7 +278,7 @@ const AircraftMarker = ({ aircraft }: { aircraft: ConnectedAircraft & { Station:
return () => { return () => {
marker?.off("click", handleClick); marker?.off("click", handleClick);
}; };
}, [aircraft.id, openAircraftMarker, setOpenAircraftMarker]); }, [aircraft.id, openAircraftMarker, setOpenAircraftMarker, userSettings]);
const [anchor, setAnchor] = useState<"topleft" | "topright" | "bottomleft" | "bottomright">( const [anchor, setAnchor] = useState<"topleft" | "topright" | "bottomleft" | "bottomright">(
"topleft", "topleft",

View File

@@ -230,7 +230,7 @@ const MissionMarker = ({
refetchInterval: 10000, refetchInterval: 10000,
}); });
const { openMissionMarker, setOpenMissionMarker } = useMapStore((store) => store); const { openMissionMarker, setOpenMissionMarker, userSettings } = useMapStore((store) => store);
const needsAction = const needsAction =
HPGValidationRequired(mission.missionStationIds, aircrafts, mission.hpgMissionString) && HPGValidationRequired(mission.missionStationIds, aircrafts, mission.hpgMissionString) &&
@@ -246,15 +246,27 @@ const MissionMarker = ({
close: [mission.id], close: [mission.id],
}); });
} else { } else {
setOpenMissionMarker({ if (userSettings.settingsAutoCloseMapPopup) {
open: [ setOpenMissionMarker({
{ open: [
id: mission.id, {
tab: "home", id: mission.id,
}, tab: "home",
], },
close: [], ],
}); close: openMissionMarker?.map((m) => m.id) || [],
});
} else {
setOpenMissionMarker({
open: [
{
id: mission.id,
tab: "home",
},
],
close: [],
});
}
} }
}; };
const markerCopy = markerRef.current; const markerCopy = markerRef.current;
@@ -262,7 +274,7 @@ const MissionMarker = ({
return () => { return () => {
markerCopy?.off("click", handleClick); markerCopy?.off("click", handleClick);
}; };
}, [mission.id, openMissionMarker, setOpenMissionMarker]); }, [mission.id, openMissionMarker, setOpenMissionMarker, userSettings]);
const [anchor, setAnchor] = useState<"topleft" | "topright" | "bottomleft" | "bottomright">( const [anchor, setAnchor] = useState<"topleft" | "topright" | "bottomleft" | "bottomright">(
"topleft", "topleft",

View File

@@ -21,7 +21,13 @@ const PopupContent = ({
missions: Mission[]; missions: Mission[];
}) => { }) => {
const { anchor } = useSmartPopup(); const { anchor } = useSmartPopup();
const { setOpenAircraftMarker, setOpenMissionMarker } = useMapStore((state) => state); const {
setOpenAircraftMarker,
setOpenMissionMarker,
openAircraftMarker,
openMissionMarker,
userSettings,
} = useMapStore((state) => state);
const map = useMap(); const map = useMap();
let borderColor = ""; let borderColor = "";
@@ -77,15 +83,27 @@ const PopupContent = ({
<span <span
className="mx-2 my-0.5 flex-1 cursor-pointer" className="mx-2 my-0.5 flex-1 cursor-pointer"
onClick={() => { onClick={() => {
setOpenMissionMarker({ if (userSettings.settingsAutoCloseMapPopup) {
open: [ setOpenMissionMarker({
{ open: [
id: mission.id, {
tab: "home", id: mission.id,
}, tab: "home",
], },
close: [], ],
}); close: openMissionMarker?.map((m) => m.id) || [],
});
} else {
setOpenMissionMarker({
open: [
{
id: mission.id,
tab: "home",
},
],
close: [],
});
}
map.setView([mission.addressLat, mission.addressLng], 12, { map.setView([mission.addressLat, mission.addressLng], 12, {
animate: true, animate: true,
}); });
@@ -104,15 +122,27 @@ const PopupContent = ({
backgroundColor: FMS_STATUS_COLORS[aircraft.fmsStatus], backgroundColor: FMS_STATUS_COLORS[aircraft.fmsStatus],
}} }}
onClick={() => { onClick={() => {
setOpenAircraftMarker({ if (userSettings.settingsAutoCloseMapPopup) {
open: [ setOpenAircraftMarker({
{ open: [
id: aircraft.id, {
tab: "aircraft", id: aircraft.id,
}, tab: "home",
], },
close: [], ],
}); close: openAircraftMarker?.map((m) => m.id) || [],
});
} else {
setOpenAircraftMarker({
open: [
{
id: aircraft.id,
tab: "home",
},
],
close: [],
});
}
map.setView([aircraft.posLat!, aircraft.posLng!], 12, { map.setView([aircraft.posLat!, aircraft.posLng!], 12, {
animate: true, animate: true,
}); });

View File

@@ -39,6 +39,10 @@ export interface MapStore {
[aircraftId: string]: "home" | "fms" | "aircraft" | "mission" | "chat"; [aircraftId: string]: "home" | "fms" | "aircraft" | "mission" | "chat";
}; };
setAircraftTab: (aircraftId: number, tab: MapStore["aircraftTabs"][number]) => void; setAircraftTab: (aircraftId: number, tab: MapStore["aircraftTabs"][number]) => void;
userSettings: {
settingsAutoCloseMapPopup: boolean;
};
setUserSettings: (settings: Partial<MapStore["userSettings"]>) => void;
} }
export const useMapStore = create<MapStore>((set, get) => ({ export const useMapStore = create<MapStore>((set, get) => ({
@@ -102,4 +106,14 @@ export const useMapStore = create<MapStore>((set, get) => ({
}, },
})), })),
missionTabs: {}, missionTabs: {},
userSettings: {
settingsAutoCloseMapPopup: false,
},
setUserSettings: (settings) =>
set((state) => ({
userSettings: {
...state.userSettings,
...settings,
},
})),
})); }));