This commit is contained in:
PxlLoewe
2025-07-24 14:31:34 -07:00
parent 0db96a0e2a
commit 792c4d5cb5
5 changed files with 68 additions and 33 deletions

View File

@@ -113,6 +113,7 @@ export const SmartPopup = (
); );
const handleConflict = useCallback(() => { const handleConflict = useCallback(() => {
console.log("handleConflict in smartMarker", id, options);
const newAnchor = calculateAnchor(id, "popup", options); const newAnchor = calculateAnchor(id, "popup", options);
setAnchor(newAnchor); setAnchor(newAnchor);
}, [id, options]); }, [id, options]);
@@ -139,10 +140,10 @@ export const SmartPopup = (
<Popup {...props} className={cn("relative", wrapperClassName)}> <Popup {...props} className={cn("relative", wrapperClassName)}>
<div <div
className={cn( className={cn(
"pointer-events-auto bg-base-100 relative", "bg-base-100 pointer-events-auto relative",
anchor.includes("right") && "-translate-x-full", anchor.includes("right") && "-translate-x-full",
anchor.includes("bottom") && "-translate-y-full", anchor.includes("bottom") && "-translate-y-full",
!showContent && "opacity-0 pointer-events-none", !showContent && "pointer-events-none opacity-0",
className, className,
)} )}
> >
@@ -150,7 +151,7 @@ export const SmartPopup = (
data-id={id} data-id={id}
id={`popup-domain-${id}`} id={`popup-domain-${id}`}
className={cn( className={cn(
"map-collision absolute w-[200%] h-[200%] top-0 left-0 transform pointer-events-none", "map-collision pointer-events-none absolute left-0 top-0 h-[200%] w-[200%] transform",
anchor.includes("left") && "-translate-x-1/2", anchor.includes("left") && "-translate-x-1/2",
anchor.includes("top") && "-translate-y-1/2", anchor.includes("top") && "-translate-y-1/2",
)} )}

View File

@@ -272,7 +272,9 @@ const AircraftMarker = ({ aircraft }: { aircraft: ConnectedAircraft & { Station:
); );
const handleConflict = useCallback(() => { const handleConflict = useCallback(() => {
const newAnchor = calculateAnchor(`aircraft-${aircraft.id}`, "marker"); const newAnchor = calculateAnchor(`aircraft-${aircraft.id}`, "marker", {
ignoreCluster: true,
});
setAnchor(newAnchor); setAnchor(newAnchor);
}, [aircraft.id]); }, [aircraft.id]);

View File

@@ -20,9 +20,12 @@ export const ContextMenu = () => {
setSearchPopup, setSearchPopup,
toggleSearchElementSelection, toggleSearchElementSelection,
} = useMapStore(); } = useMapStore();
const { missionFormValues, setMissionFormValues, setOpen, isOpen } = usePannelStore( const {
(state) => state, missionFormValues,
); setMissionFormValues,
setOpen,
isOpen: isPannelOpen,
} = usePannelStore((state) => state);
const [showRulerOptions, setShowRulerOptions] = useState(false); const [showRulerOptions, setShowRulerOptions] = useState(false);
const [rulerHover, setRulerHover] = useState(false); const [rulerHover, setRulerHover] = useState(false);
const [rulerOptionsHover, setRulerOptionsHover] = useState(false); const [rulerOptionsHover, setRulerOptionsHover] = useState(false);
@@ -53,7 +56,8 @@ export const ContextMenu = () => {
if (!contextMenu || !dispatcherConnected) return null; if (!contextMenu || !dispatcherConnected) return null;
const missionBtnText = missionFormValues && isOpen ? "Position übernehmen" : "Einsatz erstellen"; const missionBtnText =
missionFormValues && isPannelOpen ? "Position übernehmen" : "Einsatz erstellen";
const addOSMobjects = async (ignorePreviosSelected?: boolean) => { const addOSMobjects = async (ignorePreviosSelected?: boolean) => {
const res = await fetch( const res = await fetch(
@@ -101,13 +105,13 @@ export const ContextMenu = () => {
autoPan={false} autoPan={false}
> >
<div <div
className="absolute opacity-100 pointer-events-none p-3 flex items-center justify-center" className="pointer-events-none absolute flex items-center justify-center p-3 opacity-100"
style={{ left: "-13px", top: "-13px" }} style={{ left: "-13px", top: "-13px" }}
> >
<div className="relative w-38 h-38 flex items-center justify-center -translate-x-1/2 -translate-y-1/2 pointer-events-none"> <div className="w-38 h-38 pointer-events-none relative flex -translate-x-1/2 -translate-y-1/2 items-center justify-center">
{/* Top Button */} {/* Top Button */}
<button <button
className="btn btn-circle bg-rescuetrack w-10 h-10 absolute left-1/2 top-0 pointer-events-auto opacity-80 tooltip tooltip-top tooltip-accent" className="btn btn-circle bg-rescuetrack tooltip tooltip-top tooltip-accent pointer-events-auto absolute left-1/2 top-0 h-10 w-10 opacity-80"
data-tip={missionBtnText} data-tip={missionBtnText}
style={{ transform: "translateX(-50%)" }} style={{ transform: "translateX(-50%)" }}
onClick={async () => { onClick={async () => {
@@ -131,17 +135,18 @@ export const ContextMenu = () => {
if (closestObject) { if (closestObject) {
toggleSearchElementSelection(closestObject.wayID, true); toggleSearchElementSelection(closestObject.wayID, true);
} }
if (isPannelOpen) {
map.setView([contextMenu.lat, contextMenu.lng], 18, { map.setView([contextMenu.lat, contextMenu.lng], 18, {
animate: true, animate: true,
}); });
}
}} }}
> >
<MapPinned size={20} /> <MapPinned size={20} />
</button> </button>
{/* Left Button */} {/* Left Button */}
<button <button
className="btn btn-circle bg-rescuetrack w-10 h-10 absolute top-1/2 left-0 pointer-events-auto opacity-80" className="btn btn-circle bg-rescuetrack pointer-events-auto absolute left-0 top-1/2 h-10 w-10 opacity-80"
style={{ transform: "translateY(-50%)" }} style={{ transform: "translateY(-50%)" }}
onMouseEnter={() => setRulerHover(true)} onMouseEnter={() => setRulerHover(true)}
onMouseLeave={() => setRulerHover(false)} onMouseLeave={() => setRulerHover(false)}
@@ -151,7 +156,7 @@ export const ContextMenu = () => {
</button> </button>
{/* Bottom Button */} {/* Bottom Button */}
<button <button
className="btn btn-circle bg-rescuetrack w-10 h-10 absolute left-1/2 bottom-0 pointer-events-auto opacity-80 tooltip tooltip-bottom tooltip-accent" className="btn btn-circle bg-rescuetrack tooltip tooltip-bottom tooltip-accent pointer-events-auto absolute bottom-0 left-1/2 h-10 w-10 opacity-80"
data-tip="Koordinaten kopieren" data-tip="Koordinaten kopieren"
style={{ transform: "translateX(-50%)" }} style={{ transform: "translateX(-50%)" }}
onClick={async () => { onClick={async () => {
@@ -164,7 +169,7 @@ export const ContextMenu = () => {
</button> </button>
{/* Right Button (original Search button) */} {/* Right Button (original Search button) */}
<button <button
className="btn btn-circle bg-rescuetrack w-10 h-10 absolute top-1/2 right-0 pointer-events-auto opacity-80 tooltip tooltip-right tooltip-accent" className="btn btn-circle bg-rescuetrack tooltip tooltip-right tooltip-accent pointer-events-auto absolute right-0 top-1/2 h-10 w-10 opacity-80"
data-tip="Gebäude suchen" data-tip="Gebäude suchen"
style={{ transform: "translateY(-50%)" }} style={{ transform: "translateY(-50%)" }}
onClick={async () => { onClick={async () => {
@@ -176,7 +181,7 @@ export const ContextMenu = () => {
{/* Ruler Options - shown when Ruler button is hovered or options are hovered */} {/* Ruler Options - shown when Ruler button is hovered or options are hovered */}
{showRulerOptions && ( {showRulerOptions && (
<div <div
className="absolute flex flex-col items-center pointer-events-auto" className="pointer-events-auto absolute flex flex-col items-center"
style={{ style={{
left: "-100px", // position to the right of the left button left: "-100px", // position to the right of the left button
top: "50%", top: "50%",
@@ -200,7 +205,7 @@ export const ContextMenu = () => {
}} }}
> >
<button <button
className="btn btn-circle bg-rescuetrack w-10 h-10 mb-2 opacity-80 tooltip tooltip-left tooltip-accent" className="btn btn-circle bg-rescuetrack tooltip tooltip-left tooltip-accent mb-2 h-10 w-10 opacity-80"
data-tip="Strecke Messen" data-tip="Strecke Messen"
style={{ style={{
transform: "translateX(100%)", transform: "translateX(100%)",
@@ -212,7 +217,7 @@ export const ContextMenu = () => {
<RulerDimensionLine size={20} /> <RulerDimensionLine size={20} />
</button> </button>
<button <button
className="btn btn-circle bg-rescuetrack w-10 h-10 mb-2 opacity-80 tooltip tooltip-left tooltip-accent" className="btn btn-circle bg-rescuetrack tooltip tooltip-left tooltip-accent mb-2 h-10 w-10 opacity-80"
data-tip="Radius Messen" data-tip="Radius Messen"
onClick={() => { onClick={() => {
/* ... */ /* ... */
@@ -221,7 +226,7 @@ export const ContextMenu = () => {
<Radius size={20} /> <Radius size={20} />
</button> </button>
<button <button
className="btn btn-circle bg-rescuetrack w-10 h-10 opacity-80 tooltip tooltip-left tooltip-accent" className="btn btn-circle bg-rescuetrack tooltip tooltip-left tooltip-accent h-10 w-10 opacity-80"
data-tip="Fläche Messen" data-tip="Fläche Messen"
style={{ style={{
transform: "translateX(100%)", transform: "translateX(100%)",

View File

@@ -209,7 +209,15 @@ const MissionPopupContent = ({
); );
}; };
const MissionMarker = ({ mission }: { mission: Mission }) => { const MissionMarker = ({
mission,
options,
}: {
mission: Mission;
options: {
hideDetailedKeyword?: boolean;
};
}) => {
const map = useMap(); const map = useMap();
const [hideMarker, setHideMarker] = useState(false); const [hideMarker, setHideMarker] = useState(false);
const { editingMissionId, missionFormValues } = usePannelStore((state) => state); const { editingMissionId, missionFormValues } = usePannelStore((state) => state);
@@ -261,7 +269,9 @@ const MissionMarker = ({ mission }: { mission: Mission }) => {
); );
const handleConflict = useCallback(() => { const handleConflict = useCallback(() => {
const newAnchor = calculateAnchor(`mission-${mission.id.toString()}`, "marker"); const newAnchor = calculateAnchor(`mission-${mission.id.toString()}`, "marker", {
ignoreCluster: true,
});
setAnchor(newAnchor); setAnchor(newAnchor);
}, [mission.id]); }, [mission.id]);
@@ -318,7 +328,7 @@ const MissionMarker = ({ mission }: { mission: Mission }) => {
" "
></div> ></div>
<span class="text-white text-[15px] text-nowrap"> <span class="text-white text-[15px] text-nowrap">
${mission.missionKeywordAbbreviation} ${mission.missionKeywordName} ${mission.missionKeywordAbbreviation} ${options.hideDetailedKeyword ? "" : mission.missionKeywordName}
</span> </span>
<div <div
data-anchor-lat="${mission.addressLat}" data-anchor-lat="${mission.addressLat}"
@@ -396,6 +406,11 @@ export const MissionLayer = () => {
selectedStation, selectedStation,
} = usePilotConnectionStore((state) => state); } = usePilotConnectionStore((state) => state);
const { data: aircrafts = [] } = useQuery({
queryKey: ["aircrafts"],
queryFn: () => getConnectedAircraftsAPI(),
refetchInterval: 10_000,
});
const { data: missions = [] } = useQuery({ const { data: missions = [] } = useQuery({
queryKey: ["missions"], queryKey: ["missions"],
queryFn: () => queryFn: () =>
@@ -426,7 +441,15 @@ export const MissionLayer = () => {
return ( return (
<> <>
{filteredMissions.map((mission) => { {filteredMissions.map((mission) => {
return <MissionMarker key={mission.id} mission={mission as Mission} />; return (
<MissionMarker
key={mission.id}
mission={mission as Mission}
options={{
hideDetailedKeyword: missions.length + aircrafts.length > 10,
}}
/>
);
})} })}
</> </>
); );

View File

@@ -42,10 +42,10 @@ const PopupContent = ({
return ( return (
<> <>
<div className="relative flex flex-col text-white min-w-[200px]"> <div className="relative flex min-w-fit flex-col text-white">
<div <div
className={cn( className={cn(
"absolute w-[calc(100%+2px)] h-4 z-99 pointer-events-none", "z-99 pointer-events-none absolute h-4 w-[calc(100%+2px)]",
anchor.includes("left") ? "-left-[2px]" : "-right-[2px]", anchor.includes("left") ? "-left-[2px]" : "-right-[2px]",
anchor.includes("top") ? "-top-[2px]" : "-bottom-[2px]", anchor.includes("top") ? "-top-[2px]" : "-bottom-[2px]",
)} )}
@@ -68,7 +68,7 @@ const PopupContent = ({
return ( return (
<div <div
key={mission.id} key={mission.id}
className={cn("relative inline-flex items-center gap-2 text-nowrap w-full")} className={cn("relative inline-flex w-full items-center gap-2 text-nowrap")}
style={{ style={{
backgroundColor: markerColor, backgroundColor: markerColor,
cursor: "pointer", cursor: "pointer",
@@ -99,7 +99,7 @@ const PopupContent = ({
{aircrafts.map((aircraft) => ( {aircrafts.map((aircraft) => (
<div <div
key={aircraft.id} key={aircraft.id}
className="relative w-auto inline-flex items-center gap-2 text-nowrap cursor-pointer" className="relative inline-flex w-auto cursor-pointer items-center gap-2 text-nowrap px-2"
style={{ style={{
backgroundColor: FMS_STATUS_COLORS[aircraft.fmsStatus], backgroundColor: FMS_STATUS_COLORS[aircraft.fmsStatus],
}} }}
@@ -119,14 +119,18 @@ const PopupContent = ({
}} }}
> >
<span <span
className="mx-2 my-0.5 text-gt font-bold" className="text-gt my-0.5 font-bold"
style={{ style={{
color: FMS_STATUS_TEXT_COLORS[aircraft.fmsStatus], color: FMS_STATUS_TEXT_COLORS[aircraft.fmsStatus],
}} }}
> >
{aircraft.fmsStatus} {aircraft.fmsStatus}
</span> </span>
<span>{aircraft.Station.bosCallsign}</span> <span>
{aircraft.Station.bosCallsign.length > 15
? aircraft.Station.locationStateShort
: aircraft.Station.bosCallsign}
</span>
</div> </div>
))} ))}
</div> </div>
@@ -299,7 +303,7 @@ export const MarkerCluster = () => {
position={[c.lat, c.lng]} position={[c.lat, c.lng]}
autoPan={false} autoPan={false}
autoClose={false} autoClose={false}
className="w-[202px]" className="min-w-fit"
> >
<PopupContent aircrafts={c.aircrafts} missions={c.missions} /> <PopupContent aircrafts={c.aircrafts} missions={c.missions} />
</SmartPopup> </SmartPopup>