added the construction and aviation map stuff as well as the site api and model
This commit is contained in:
parent
02cee8623a
commit
bcacf761c8
20 changed files with 2549 additions and 9 deletions
119
src/maps/mapDrawers/GateDrawer.tsx
Normal file
119
src/maps/mapDrawers/GateDrawer.tsx
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
import DisplayDrawer from "common/DisplayDrawer";
|
||||
import { Gate as IGate } from "models/Gate";
|
||||
import Gate from "pages/Gate";
|
||||
import { useGateAPI, useSnackbar } from "providers";
|
||||
import React, { useEffect, useState } from "react";
|
||||
|
||||
interface Props {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
gates: Map<string, IGate>;
|
||||
selectedGate: string;
|
||||
moveMap: (longitude: number, latitude: number) => void;
|
||||
removeMarker: (key: string) => void;
|
||||
}
|
||||
|
||||
export default function GateDrawer(props: Props) {
|
||||
const { open, onClose, gates, selectedGate, moveMap, removeMarker } = props;
|
||||
const [gate, setGate] = useState(IGate.create());
|
||||
const gateAPI = useGateAPI();
|
||||
const { openSnack } = useSnackbar();
|
||||
|
||||
useEffect(() => {
|
||||
let g = gates.get(selectedGate);
|
||||
if (g) {
|
||||
setGate(g);
|
||||
}
|
||||
}, [selectedGate, gates]);
|
||||
|
||||
const closeDrawer = () => {
|
||||
onClose();
|
||||
};
|
||||
|
||||
const displayNext = () => {
|
||||
let gateArr = Array.from(gates.values());
|
||||
let index = gateArr.indexOf(gate);
|
||||
let found = false;
|
||||
do {
|
||||
if (index === gateArr.length - 1) {
|
||||
index = 0;
|
||||
} else {
|
||||
index++;
|
||||
}
|
||||
let next = gateArr[index];
|
||||
if (
|
||||
next.longitude() !== 0 &&
|
||||
next.longitude() !== 0 &&
|
||||
next.latitude() !== undefined &&
|
||||
next.longitude() !== undefined
|
||||
) {
|
||||
setGate(next);
|
||||
moveMap(next.longitude(), next.latitude());
|
||||
found = true;
|
||||
}
|
||||
} while (!found);
|
||||
};
|
||||
|
||||
const displayPrev = () => {
|
||||
let gateArr = Array.from(gates.values());
|
||||
let index = gateArr.indexOf(gate);
|
||||
let found = false;
|
||||
do {
|
||||
if (index === 0) {
|
||||
index = gateArr.length - 1;
|
||||
} else {
|
||||
index--;
|
||||
}
|
||||
let next = gateArr[index];
|
||||
if (
|
||||
next.longitude() !== 0 &&
|
||||
next.latitude() !== 0 &&
|
||||
next.latitude() !== undefined &&
|
||||
next.longitude() !== undefined
|
||||
) {
|
||||
setGate(next);
|
||||
moveMap(next.longitude(), next.latitude());
|
||||
found = true;
|
||||
}
|
||||
} while (!found);
|
||||
};
|
||||
|
||||
const drawerBody = () => {
|
||||
return <Gate gateKey={gate.key} useMobile />;
|
||||
};
|
||||
|
||||
/**
|
||||
* function to remove the marker and coordinates from the object
|
||||
*/
|
||||
const remove = () => {
|
||||
//set the long/lat of the yard to 0 and call an update
|
||||
let settings = gate.settings;
|
||||
settings.longitude = 0;
|
||||
settings.latitude = 0;
|
||||
gateAPI
|
||||
.updateGate(gate.key, gate.name, settings)
|
||||
.then(resp => {
|
||||
openSnack("Marker Removed");
|
||||
//then use the removeMarker prop function to update the markers in the parent map
|
||||
removeMarker(gate.key);
|
||||
})
|
||||
.catch(err => {
|
||||
openSnack("Failed to remove marker");
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<DisplayDrawer
|
||||
displayNext={displayNext}
|
||||
displayPrev={displayPrev}
|
||||
drawerBody={drawerBody()}
|
||||
removeElement={remove}
|
||||
onClose={closeDrawer}
|
||||
open={open}
|
||||
title={gate.name}
|
||||
width={"40vw"}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
144
src/maps/mapDrawers/GroupDrawer.tsx
Normal file
144
src/maps/mapDrawers/GroupDrawer.tsx
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
import { Box } from "@mui/material";
|
||||
import DisplayDrawer from "common/DisplayDrawer";
|
||||
//import DeviceCardList from "device/DeviceCardList";
|
||||
import { useGroupAPI, useSnackbar } from "hooks";
|
||||
import { Group } from "models";
|
||||
import { pond } from "protobuf-ts/pond";
|
||||
import React, { useEffect, useState, useCallback } from "react";
|
||||
import { or } from "utils";
|
||||
|
||||
interface Props {
|
||||
open: boolean;
|
||||
groups: Map<string, Group>;
|
||||
selectedGroup: string;
|
||||
onClose: () => void;
|
||||
removeMarker: (key: string) => void;
|
||||
moveMap: (long: number, lat: number) => void;
|
||||
}
|
||||
|
||||
export default function GroupDrawer(props: Props) {
|
||||
const { open, groups, selectedGroup, onClose, moveMap, removeMarker } = props;
|
||||
const [group, setGroup] = useState<Group>(Group.create());
|
||||
const groupAPI = useGroupAPI();
|
||||
const { openSnack } = useSnackbar();
|
||||
const [grpDevs, setGrpDevs] = useState<pond.ComprehensiveDevice[]>([]);
|
||||
|
||||
const loadDevs = useCallback(() => {
|
||||
groupAPI
|
||||
.listGroupDevices(group.id(), 100, 0, "asc", undefined, true)
|
||||
.then(resp => {
|
||||
const groupDevices: pond.ComprehensiveDevice[] = or(
|
||||
resp.data.comprehensiveDevices,
|
||||
[]
|
||||
).map(d => pond.ComprehensiveDevice.fromObject(d));
|
||||
setGrpDevs(groupDevices);
|
||||
})
|
||||
.catch(err => {
|
||||
openSnack("Failed to get devices for group");
|
||||
});
|
||||
}, [group, groupAPI, openSnack]);
|
||||
|
||||
useEffect(() => {
|
||||
loadDevs();
|
||||
}, [loadDevs]);
|
||||
|
||||
useEffect(() => {
|
||||
let g = groups.get(selectedGroup);
|
||||
if (g) {
|
||||
setGroup(g);
|
||||
}
|
||||
}, [selectedGroup, groups]);
|
||||
|
||||
const closeDrawer = () => {
|
||||
onClose();
|
||||
};
|
||||
|
||||
const displayNext = () => {
|
||||
let groupArr = Array.from(groups.values());
|
||||
let index = groupArr.indexOf(group);
|
||||
let found = false;
|
||||
do {
|
||||
if (index === groupArr.length - 1) {
|
||||
index = 0;
|
||||
} else {
|
||||
index++;
|
||||
}
|
||||
let next = groupArr[index];
|
||||
if (
|
||||
next.settings.longitude !== 0 &&
|
||||
next.settings.longitude !== 0 &&
|
||||
next.settings.latitude !== undefined &&
|
||||
next.settings.longitude !== undefined
|
||||
) {
|
||||
setGroup(next);
|
||||
moveMap(next.settings.longitude, next.settings.latitude);
|
||||
found = true;
|
||||
}
|
||||
} while (!found);
|
||||
};
|
||||
|
||||
const displayPrev = () => {
|
||||
let groupArr = Array.from(groups.values());
|
||||
let index = groupArr.indexOf(group);
|
||||
let found = false;
|
||||
do {
|
||||
if (index === 0) {
|
||||
index = groupArr.length - 1;
|
||||
} else {
|
||||
index--;
|
||||
}
|
||||
let next = groupArr[index];
|
||||
if (
|
||||
next.settings.longitude !== 0 &&
|
||||
next.settings.longitude !== 0 &&
|
||||
next.settings.latitude !== undefined &&
|
||||
next.settings.longitude !== undefined
|
||||
) {
|
||||
setGroup(next);
|
||||
moveMap(next.settings.longitude, next.settings.latitude);
|
||||
found = true;
|
||||
}
|
||||
} while (!found);
|
||||
};
|
||||
|
||||
/**
|
||||
* function to remove the marker and coordinates from the object
|
||||
*/
|
||||
const remove = () => {
|
||||
//set the long/lat of the yard to 0 and call an update
|
||||
let settings = group.settings;
|
||||
settings.longitude = 0;
|
||||
settings.latitude = 0;
|
||||
groupAPI
|
||||
.updateGroup(group.id(), settings)
|
||||
.then(resp => {
|
||||
openSnack("Marker Removed");
|
||||
//then use the removeMarker prop function to update the markers in the parent map
|
||||
removeMarker(group.id().toString());
|
||||
})
|
||||
.catch(err => {
|
||||
openSnack("Failed to remove marker");
|
||||
});
|
||||
};
|
||||
|
||||
const drawerBody = () => {
|
||||
return (<Box>Use the responsive table here</Box>)
|
||||
//return <DeviceCardList devices={grpDevs} refreshCallback={loadDevs} showMobile={true} />;
|
||||
};
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<DisplayDrawer
|
||||
open={open}
|
||||
onClose={closeDrawer}
|
||||
displayNext={displayNext}
|
||||
displayPrev={displayPrev}
|
||||
title={group.name()}
|
||||
width={"40vw"}
|
||||
drawerBody={drawerBody()}
|
||||
//updateElement={update}
|
||||
removeElement={remove}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
221
src/maps/mapDrawers/SiteDrawer.tsx
Normal file
221
src/maps/mapDrawers/SiteDrawer.tsx
Normal file
|
|
@ -0,0 +1,221 @@
|
|||
import { Box, Typography } from "@mui/material";
|
||||
import DeviceLinkDrawer from "common/DeviceLinkDrawer";
|
||||
import DisplayDrawer from "common/DisplayDrawer";
|
||||
import MapMarkerSettings from "maps/MapMarkerSettings";
|
||||
import { Device, Site } from "models";
|
||||
//import { ObjectHeater } from "models/ObjectHeater";
|
||||
//import ObjectHeaterCard from "objectHeater/ObjectHeaterCard";
|
||||
import { pond } from "protobuf-ts/pond";
|
||||
import { /*useObjectHeaterAPI,*/ useSiteAPI, useSnackbar } from "providers";
|
||||
import React, { useEffect, useState } from "react";
|
||||
|
||||
interface Props {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
selectedSite: string;
|
||||
sites: Map<string, Site>;
|
||||
heaters: Map<string, pond.ObjectHeaterData[]>;
|
||||
removeMarker: (key: string) => void;
|
||||
updateMarker: (key: string, newSettings: pond.SiteSettings) => void;
|
||||
moveMap: (long: number, lat: number) => void;
|
||||
reLoadSites: () => void;
|
||||
}
|
||||
|
||||
export default function SiteDrawer(props: Props) {
|
||||
const {
|
||||
open,
|
||||
onClose,
|
||||
selectedSite,
|
||||
sites,
|
||||
removeMarker,
|
||||
moveMap,
|
||||
heaters,
|
||||
reLoadSites,
|
||||
updateMarker
|
||||
} = props;
|
||||
const [site, setSite] = useState<Site>(Site.create());
|
||||
const siteAPI = useSiteAPI();
|
||||
//const objectHeaterAPI = useObjectHeaterAPI();
|
||||
const { openSnack } = useSnackbar();
|
||||
const [linkDrawer, setLinkDrawer] = useState(false);
|
||||
const [linkedDevices, setLinkedDevices] = useState<Map<string, pond.ComprehensiveDevice>>(
|
||||
new Map<string, pond.ComprehensiveDevice>()
|
||||
);
|
||||
//const [selectedHeater, setSelectedHeater] = useState<ObjectHeater>();
|
||||
const [openMarkerSettings, setOpenMarkerSettings] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
let s = sites.get(selectedSite);
|
||||
if (s) {
|
||||
setSite(s);
|
||||
}
|
||||
}, [selectedSite, sites]);
|
||||
|
||||
const closeDrawer = () => {
|
||||
onClose();
|
||||
};
|
||||
|
||||
const displayNext = () => {
|
||||
let devArr = Array.from(sites.values());
|
||||
let index = devArr.indexOf(site);
|
||||
let found = false;
|
||||
do {
|
||||
if (index === devArr.length - 1) {
|
||||
index = 0;
|
||||
} else {
|
||||
index++;
|
||||
}
|
||||
let nextSite = devArr[index];
|
||||
if (
|
||||
nextSite.location().longitude !== 0 &&
|
||||
nextSite.location().longitude !== 0 &&
|
||||
nextSite.location().latitude !== undefined &&
|
||||
nextSite.location().longitude !== undefined
|
||||
) {
|
||||
setSite(nextSite);
|
||||
moveMap(nextSite.location().longitude, nextSite.location().latitude);
|
||||
found = true;
|
||||
}
|
||||
} while (!found);
|
||||
};
|
||||
|
||||
const displayPrev = () => {
|
||||
let devArr = Array.from(sites.values());
|
||||
let index = devArr.indexOf(site);
|
||||
let found = false;
|
||||
do {
|
||||
if (index === 0) {
|
||||
index = devArr.length - 1;
|
||||
} else {
|
||||
index--;
|
||||
}
|
||||
let nextSite = devArr[index];
|
||||
if (
|
||||
nextSite.location().longitude !== 0 &&
|
||||
nextSite.location().longitude !== 0 &&
|
||||
nextSite.location().latitude !== undefined &&
|
||||
nextSite.location().longitude !== undefined
|
||||
) {
|
||||
setSite(nextSite);
|
||||
moveMap(nextSite.location().longitude, nextSite.location().latitude);
|
||||
found = true;
|
||||
}
|
||||
} while (!found);
|
||||
};
|
||||
|
||||
// const deviceLinkDrawer = () => {
|
||||
// return (
|
||||
// <DeviceLinkDrawer
|
||||
// open={linkDrawer}
|
||||
// close={() => {
|
||||
// setLinkDrawer(false);
|
||||
// }}
|
||||
// linkedDevices={linkedDevices}
|
||||
// updateLinkedDevices={(dev, linked) => {
|
||||
// let deviceID = dev.device?.settings?.deviceId;
|
||||
// if (deviceID) {
|
||||
// if (linked && selectedHeater) {
|
||||
// objectHeaterAPI
|
||||
// .updateLink(selectedHeater.key, "objectHeater", deviceID.toString(), "device", [
|
||||
// "read",
|
||||
// "write",
|
||||
// "grant",
|
||||
// "revoke"
|
||||
// ])
|
||||
// .then(resp => {
|
||||
// setLinkDrawer(false);
|
||||
// reLoadSites();
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
// }}
|
||||
// />
|
||||
// );
|
||||
// };
|
||||
|
||||
const drawerBody = () => {
|
||||
//let siteHeaters = heaters.get(site.key());
|
||||
return (
|
||||
<Box padding={2}>
|
||||
{/* {siteHeaters ? (
|
||||
siteHeaters.map(h => (
|
||||
<ObjectHeaterCard
|
||||
key={h.heater?.key}
|
||||
heaterData={h}
|
||||
linkDevice={(heater, linkedDevices) => {
|
||||
setSelectedHeater(heater);
|
||||
setLinkDrawer(true);
|
||||
let d = new Map<string, pond.ComprehensiveDevice>();
|
||||
linkedDevices.forEach(compDev => {
|
||||
let device = Device.any(compDev.device);
|
||||
d.set(device.id().toString(), compDev);
|
||||
});
|
||||
setLinkedDevices(d);
|
||||
}}
|
||||
/>
|
||||
))
|
||||
) : ( */}
|
||||
<Typography>No Heaters Found</Typography>
|
||||
{/* )} */}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* function to remove the marker and coordinates from the object
|
||||
*/
|
||||
const remove = () => {
|
||||
siteAPI
|
||||
.removeSite(site.key())
|
||||
.then(resp => {
|
||||
openSnack("Site Deleted");
|
||||
//then use the removeMarker prop function to update the markers in the parent map
|
||||
removeMarker(site.key());
|
||||
})
|
||||
.catch(err => {
|
||||
openSnack("Failed to remove marker");
|
||||
});
|
||||
};
|
||||
|
||||
const update = () => {
|
||||
setOpenMarkerSettings(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
{/* {deviceLinkDrawer()} */}
|
||||
<DisplayDrawer
|
||||
open={open}
|
||||
onClose={closeDrawer}
|
||||
displayNext={displayNext}
|
||||
displayPrev={displayPrev}
|
||||
title={site.siteName()}
|
||||
width={"40vw"}
|
||||
drawerBody={drawerBody()}
|
||||
updateElement={update}
|
||||
removeElement={remove}
|
||||
/>
|
||||
<MapMarkerSettings
|
||||
close={() => {
|
||||
setOpenMarkerSettings(false);
|
||||
}}
|
||||
open={openMarkerSettings}
|
||||
colourControl
|
||||
theme={site.settings.theme ?? pond.ObjectTheme.create()}
|
||||
updateObject={newTheme => {
|
||||
let settings = site.settings;
|
||||
settings.theme = newTheme;
|
||||
siteAPI
|
||||
.updateSite(site.key(), settings)
|
||||
.then(resp => {
|
||||
openSnack("marker settings updated");
|
||||
updateMarker(site.key(), settings);
|
||||
})
|
||||
.catch(() => {
|
||||
openSnack("failed to update marker settings");
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
119
src/maps/mapDrawers/TerminalDrawer.tsx
Normal file
119
src/maps/mapDrawers/TerminalDrawer.tsx
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
import DisplayDrawer from "common/DisplayDrawer";
|
||||
import { Terminal as ITerminal } from "models/Terminal";
|
||||
import Terminal from "pages/Terminals";
|
||||
import { useSnackbar, useTerminalAPI } from "providers";
|
||||
import React, { useState, useEffect } from "react";
|
||||
|
||||
interface Props {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
terminals: Map<string, ITerminal>;
|
||||
selectedKey: string;
|
||||
moveMap: (longitude: number, latitude: number) => void;
|
||||
removeMarker: (key: string) => void;
|
||||
}
|
||||
|
||||
export default function TerminalDrawer(props: Props) {
|
||||
const { terminals, selectedKey, open, onClose, moveMap, removeMarker } = props;
|
||||
const [terminal, setTerminal] = useState<ITerminal>(ITerminal.create());
|
||||
const terminalAPI = useTerminalAPI();
|
||||
const { openSnack } = useSnackbar();
|
||||
|
||||
useEffect(() => {
|
||||
let t = terminals.get(selectedKey);
|
||||
if (t) {
|
||||
setTerminal(t);
|
||||
}
|
||||
}, [selectedKey, terminals]);
|
||||
|
||||
const closeDrawer = () => {
|
||||
onClose();
|
||||
};
|
||||
|
||||
const displayNext = () => {
|
||||
let termArr = Array.from(terminals.values());
|
||||
let index = termArr.indexOf(terminal);
|
||||
let found = false;
|
||||
do {
|
||||
if (index === termArr.length - 1) {
|
||||
index = 0;
|
||||
} else {
|
||||
index++;
|
||||
}
|
||||
let next = termArr[index];
|
||||
if (
|
||||
next.longitude() !== 0 &&
|
||||
next.longitude() !== 0 &&
|
||||
next.latitude() !== undefined &&
|
||||
next.longitude() !== undefined
|
||||
) {
|
||||
setTerminal(next);
|
||||
moveMap(next.longitude(), next.latitude());
|
||||
found = true;
|
||||
}
|
||||
} while (!found);
|
||||
};
|
||||
|
||||
const displayPrev = () => {
|
||||
let termArr = Array.from(terminals.values());
|
||||
let index = termArr.indexOf(terminal);
|
||||
let found = false;
|
||||
do {
|
||||
if (index === 0) {
|
||||
index = termArr.length - 1;
|
||||
} else {
|
||||
index--;
|
||||
}
|
||||
let next = termArr[index];
|
||||
if (
|
||||
next.longitude() !== 0 &&
|
||||
next.latitude() !== 0 &&
|
||||
next.latitude() !== undefined &&
|
||||
next.longitude() !== undefined
|
||||
) {
|
||||
setTerminal(next);
|
||||
moveMap(next.longitude(), next.latitude());
|
||||
found = true;
|
||||
}
|
||||
} while (!found);
|
||||
};
|
||||
|
||||
const drawerBody = () => {
|
||||
return <Terminal terminal={terminal} />;
|
||||
};
|
||||
|
||||
/**
|
||||
* function to remove the marker and coordinates from the object
|
||||
*/
|
||||
const remove = () => {
|
||||
//set the long/lat of the yard to 0 and call an update
|
||||
let settings = terminal.settings;
|
||||
settings.longitude = 0;
|
||||
settings.latitude = 0;
|
||||
terminalAPI
|
||||
.updateTerminal(terminal.key, terminal.name, settings)
|
||||
.then(resp => {
|
||||
openSnack("Marker Removed");
|
||||
//then use the removeMarker prop function to update the markers in the parent map
|
||||
removeMarker(terminal.key);
|
||||
})
|
||||
.catch(err => {
|
||||
openSnack("Failed to remove marker");
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<DisplayDrawer
|
||||
open={open}
|
||||
onClose={closeDrawer}
|
||||
title={terminal.name}
|
||||
width="40vw"
|
||||
displayNext={displayNext}
|
||||
displayPrev={displayPrev}
|
||||
drawerBody={drawerBody()}
|
||||
removeElement={remove}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
468
src/maps/mapObjectControllers/AviationMapController.tsx
Normal file
468
src/maps/mapObjectControllers/AviationMapController.tsx
Normal file
|
|
@ -0,0 +1,468 @@
|
|||
import {
|
||||
Box,
|
||||
Button,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
TextField,
|
||||
Autocomplete
|
||||
} from "@mui/material";
|
||||
//import { Autocomplete } from "@material-ui/lab";
|
||||
import ResponsiveDialog from "common/ResponsiveDialog";
|
||||
import { useMobile } from "hooks";
|
||||
import { clone } from "lodash";
|
||||
import MapBase, { ViewData } from "maps/MapBase";
|
||||
//import { GeocoderObject } from "maps/mapControllers/Geocoder";
|
||||
import { Result } from "@mapbox/mapbox-gl-geocoder";
|
||||
import GateDrawer from "maps/mapDrawers/GateDrawer";
|
||||
import TerminalDrawer from "maps/mapDrawers/TerminalDrawer";
|
||||
import { MarkerData } from "maps/mapMarkers/Markers";
|
||||
import AviationMapTools from "maps/mapObjectTools/aviationMapTools";
|
||||
import { Gate } from "models/Gate";
|
||||
import { Terminal } from "models/Terminal";
|
||||
import OmniAirDeviceIcon from "products/AviationIcons/OmniAirDeviceIcon";
|
||||
import PlaneIcon from "products/AviationIcons/PlaneIcon";
|
||||
import { pond } from "protobuf-ts/pond";
|
||||
import { useGateAPI, useGlobalState, useSnackbar, useTerminalAPI } from "providers";
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
|
||||
interface Props {
|
||||
startingView?: ViewData;
|
||||
}
|
||||
|
||||
export default function AviationMapController(props: Props) {
|
||||
const [{ user }] = useGlobalState();
|
||||
const transDuration = 3000;
|
||||
const [currentView, setCurrentView] = useState(
|
||||
props.startingView
|
||||
? props.startingView
|
||||
: {
|
||||
latitude: 52.1579,
|
||||
longitude: -106.6702,
|
||||
zoom: user.settings.mapZoom
|
||||
}
|
||||
);
|
||||
const terminalAPI = useTerminalAPI();
|
||||
const gateAPI = useGateAPI();
|
||||
const [terminals, setTerminals] = useState<Map<string, Terminal>>(new Map());
|
||||
const [gates, setGates] = useState<Map<string, Gate>>(new Map());
|
||||
const [terminalOptions, setTerminalOptions] = useState<Terminal[]>([]);
|
||||
const [gateOptions, setGateOptions] = useState<Gate[]>([]);
|
||||
const [terminalMarkers, setTerminalMarkers] = useState<Map<string, MarkerData>>(new Map());
|
||||
const [gateMarkers, setGateMarkers] = useState<Map<string, MarkerData>>(new Map());
|
||||
const [markers, setMarkers] = useState<MarkerData[]>([]);
|
||||
const { openSnack } = useSnackbar();
|
||||
const [gateDrawerOpen, setGateDrawerOpen] = useState(false);
|
||||
const [terminalDrawerOpen, setTerminalDrawerOpen] = useState(false);
|
||||
const [objectKey, setObjectKey] = useState("");
|
||||
const [endTime, setEndTime] = useState(0);
|
||||
const isMobile = useMobile();
|
||||
const [markerType, setMarkerType] = useState(pond.ObjectType.OBJECT_TYPE_UNKNOWN);
|
||||
const [markerDisplay, setMarkerDisplay] = useState(false);
|
||||
const [openMarkerDialog, setOpenMarkerDialog] = useState(false);
|
||||
const [selectKey, setSelectKey] = useState("");
|
||||
const [newLong, setNewLong] = useState(0);
|
||||
const [newLat, setNewLat] = useState(0);
|
||||
const zoomLevels = {
|
||||
far: 14,
|
||||
close: 16
|
||||
};
|
||||
const [customSearchEntries, setCustomSearchEntries] = useState<Result[]>([]);
|
||||
const [terminalSearchEntries, setTerminalSearchEntries] = useState<Result[]>([]);
|
||||
const [gateSearchEntries, setGateSearchEntries] = useState<Result[]>([]);
|
||||
|
||||
const closeDrawers = () => {
|
||||
setTerminalDrawerOpen(false);
|
||||
setGateDrawerOpen(false);
|
||||
};
|
||||
|
||||
const markerClick = (key: string, long: number, lat: number, isMobile: boolean) => {
|
||||
closeDrawers();
|
||||
clickDelay();
|
||||
setObjectKey(key);
|
||||
moveMap(lat, long, zoomLevels.close, isMobile);
|
||||
};
|
||||
|
||||
//this click delay is implemented to fix an issue with touch screens that caused the drawer to close immediately after opening
|
||||
const clickDelay = () => {
|
||||
let endTime = new Date().valueOf() + 1000;
|
||||
setEndTime(endTime);
|
||||
};
|
||||
|
||||
const updateTerminalMarkers = (
|
||||
terminal: Terminal,
|
||||
longitude: number,
|
||||
latitude: number,
|
||||
newMarker?: boolean
|
||||
) => {
|
||||
let settings = terminal.settings;
|
||||
settings.longitude = longitude;
|
||||
settings.latitude = latitude;
|
||||
terminalAPI
|
||||
.updateTerminal(terminal.key, terminal.name, settings)
|
||||
.then(resp => {
|
||||
openSnack("Terminal Location Updated");
|
||||
if (newMarker) {
|
||||
//create new marker
|
||||
let termMarker = terminal.getMarkerData(
|
||||
(e, i, isMobile) => {
|
||||
markerClick(terminal.key, terminal.longitude(), terminal.latitude(), isMobile);
|
||||
setTerminalDrawerOpen(true);
|
||||
},
|
||||
location => {
|
||||
updateTerminalMarkers(terminal, location.longitude, location.latitude);
|
||||
}
|
||||
);
|
||||
termMarker.markerIcon = <PlaneIcon width={50 * 0.6} height={50 * 0.6} />;
|
||||
//clone data to update the state with and put the marker into the clone
|
||||
let cloneData = clone(terminalMarkers);
|
||||
cloneData.set(terminal.key, termMarker);
|
||||
setTerminalMarkers(cloneData);
|
||||
//remove from options
|
||||
if (terminalOptions.includes(terminal)) {
|
||||
terminalOptions.splice(terminalOptions.indexOf(terminal), 1);
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
openSnack("Failed to update location");
|
||||
});
|
||||
};
|
||||
|
||||
const updateGateMarkers = (
|
||||
gate: Gate,
|
||||
longitude: number,
|
||||
latitude: number,
|
||||
newMarker?: boolean
|
||||
) => {
|
||||
let settings = gate.settings;
|
||||
settings.longitude = longitude;
|
||||
settings.latitude = latitude;
|
||||
gateAPI
|
||||
.updateGate(gate.key, gate.name, settings)
|
||||
.then(resp => {
|
||||
openSnack("Gate Location Updated");
|
||||
if (newMarker) {
|
||||
let marker = gate.getMarkerData(
|
||||
(e, i, isMobile) => {
|
||||
markerClick(gate.key, gate.longitude(), gate.latitude(), isMobile);
|
||||
setGateDrawerOpen(true);
|
||||
},
|
||||
location => {
|
||||
updateGateMarkers(gate, location.longitude, location.latitude);
|
||||
}
|
||||
);
|
||||
marker.markerIcon = <OmniAirDeviceIcon size={50 * 0.6} />;
|
||||
let cloneData = clone(gateMarkers);
|
||||
cloneData.set(gate.key, marker);
|
||||
setGateMarkers(cloneData);
|
||||
//remove from options
|
||||
if (gateOptions.includes(gate)) {
|
||||
gateOptions.splice(gateOptions.indexOf(gate), 1);
|
||||
}
|
||||
} else {
|
||||
//change the click function in the markers data to use the new long and lat
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
openSnack("Failed to update location");
|
||||
});
|
||||
};
|
||||
|
||||
const loadTerminals = useCallback(() => {
|
||||
terminalAPI.listTerminals(0, 0, undefined, undefined, undefined).then(resp => {
|
||||
let terminalMap: Map<string, Terminal> = new Map();
|
||||
let terminalMarkers: Map<string, MarkerData> = new Map();
|
||||
let terminalOps: Terminal[] = [];
|
||||
let searchEntries: Result[] = [];
|
||||
resp.data.terminals.forEach(term => {
|
||||
let terminal = Terminal.create(term);
|
||||
terminalMap.set(terminal.key, terminal);
|
||||
if (
|
||||
terminal.longitude() &&
|
||||
terminal.longitude() !== 0 &&
|
||||
terminal.latitude() &&
|
||||
terminal.latitude() !== 0
|
||||
) {
|
||||
//make marker
|
||||
let termMarker = terminal.getMarkerData(
|
||||
(e, i, isMobile) => {
|
||||
markerClick(terminal.key, terminal.longitude(), terminal.latitude(), isMobile);
|
||||
setTerminalDrawerOpen(true);
|
||||
},
|
||||
location => {
|
||||
updateTerminalMarkers(terminal, location.longitude, location.latitude);
|
||||
}
|
||||
);
|
||||
termMarker.markerIcon = <PlaneIcon width={50 * 0.6} height={50 * 0.6} />;
|
||||
terminalMarkers.set(terminal.key, termMarker);
|
||||
searchEntries.push({
|
||||
id: terminal.key,
|
||||
center: [terminal.longitude(), terminal.latitude()],
|
||||
place_name: terminal.name,
|
||||
place_type: ["terminal"]
|
||||
} as Result);
|
||||
} else {
|
||||
//no location was set so add to the options
|
||||
terminalOps.push(terminal);
|
||||
}
|
||||
});
|
||||
setTerminals(terminalMap);
|
||||
setTerminalMarkers(terminalMarkers);
|
||||
setTerminalSearchEntries(searchEntries);
|
||||
setTerminalOptions(terminalOps);
|
||||
});
|
||||
}, [terminalAPI]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
const loadGates = useCallback(() => {
|
||||
gateAPI.listGates(0, 0, undefined, undefined, undefined, true).then(resp => {
|
||||
let gates: Map<string, Gate> = new Map();
|
||||
let gateOptions: Gate[] = [];
|
||||
let gateMarkers: Map<string, MarkerData> = new Map();
|
||||
let searchEntries: Result[] = [];
|
||||
resp.data.gates.forEach(g => {
|
||||
let gate = Gate.create(g);
|
||||
gates.set(gate.key, gate);
|
||||
if (
|
||||
gate.longitude() &&
|
||||
gate.longitude() !== 0 &&
|
||||
gate.latitude() &&
|
||||
gate.latitude() !== 0
|
||||
) {
|
||||
let marker = gate.getMarkerData(
|
||||
(e, i, isMobile) => {
|
||||
markerClick(gate.key, gate.longitude(), gate.latitude(), isMobile);
|
||||
setGateDrawerOpen(true);
|
||||
},
|
||||
location => {
|
||||
updateGateMarkers(gate, location.longitude, location.latitude);
|
||||
}
|
||||
);
|
||||
marker.markerIcon = <OmniAirDeviceIcon size={50 * 0.6} />;
|
||||
gateMarkers.set(gate.key, marker);
|
||||
searchEntries.push({
|
||||
id: gate.key,
|
||||
center: [gate.longitude(), gate.latitude()],
|
||||
place_name: gate.name,
|
||||
place_type: ["gate"]
|
||||
} as Result);
|
||||
} else {
|
||||
gateOptions.push(gate);
|
||||
}
|
||||
});
|
||||
setGates(gates);
|
||||
setGateOptions(gateOptions);
|
||||
setGateMarkers(gateMarkers);
|
||||
setGateSearchEntries(searchEntries);
|
||||
});
|
||||
}, [gateAPI]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
useEffect(() => {
|
||||
loadTerminals();
|
||||
loadGates();
|
||||
}, [loadTerminals, loadGates]);
|
||||
|
||||
useEffect(() => {
|
||||
let tArr = Array.from(terminalMarkers.values());
|
||||
let gArr = Array.from(gateMarkers.values());
|
||||
setMarkers(tArr.concat(gArr));
|
||||
}, [terminalMarkers, gateMarkers]);
|
||||
|
||||
useEffect(() => {
|
||||
setCustomSearchEntries(terminalSearchEntries.concat(gateSearchEntries));
|
||||
}, [terminalSearchEntries, gateSearchEntries]);
|
||||
|
||||
const moveMap = (
|
||||
lat: number,
|
||||
long: number,
|
||||
zoom: number,
|
||||
mobileOffset?: boolean,
|
||||
center?: boolean
|
||||
) => {
|
||||
let xOff = !mobileOffset ? -250 : undefined;
|
||||
let yOff = mobileOffset ? -150 : undefined;
|
||||
|
||||
setCurrentView({
|
||||
latitude: lat,
|
||||
longitude: long,
|
||||
zoom: zoom,
|
||||
transitionDuration: transDuration,
|
||||
xOffset: center ? 0 : xOff,
|
||||
yOffset: center ? 0 : yOff
|
||||
});
|
||||
};
|
||||
|
||||
const closeNewMarkerDialog = () => {
|
||||
if (new Date().valueOf() > endTime) {
|
||||
setOpenMarkerDialog(false);
|
||||
setSelectKey("");
|
||||
}
|
||||
};
|
||||
|
||||
const setMarker = () => {
|
||||
if (markerType === pond.ObjectType.OBJECT_TYPE_TERMINAL) {
|
||||
let terminal = terminals.get(selectKey);
|
||||
if (terminal) {
|
||||
updateTerminalMarkers(terminal, newLong, newLat, true);
|
||||
}
|
||||
}
|
||||
if (markerType === pond.ObjectType.OBJECT_TYPE_GATE) {
|
||||
let gate = gates.get(selectKey);
|
||||
if (gate) {
|
||||
updateGateMarkers(gate, newLong, newLat, true);
|
||||
}
|
||||
}
|
||||
setOpenMarkerDialog(false);
|
||||
};
|
||||
|
||||
const markerDialog = () => {
|
||||
return (
|
||||
<ResponsiveDialog
|
||||
open={openMarkerDialog}
|
||||
onClose={() => {
|
||||
setOpenMarkerDialog(false);
|
||||
}}>
|
||||
<DialogTitle>Place Marker</DialogTitle>
|
||||
<DialogContent>
|
||||
<Box width={300}>
|
||||
{markerType === pond.ObjectType.OBJECT_TYPE_TERMINAL && (
|
||||
<Autocomplete
|
||||
id="autoTerminalList"
|
||||
options={terminalOptions}
|
||||
getOptionLabel={(option: Terminal) => option.name}
|
||||
fullWidth
|
||||
onChange={(e, val) => {
|
||||
val && setSelectKey(val.key);
|
||||
}}
|
||||
renderInput={params => <TextField {...params} label="Terminal" />}
|
||||
/>
|
||||
)}
|
||||
{markerType === pond.ObjectType.OBJECT_TYPE_GATE && (
|
||||
<Autocomplete
|
||||
id="autoGateList"
|
||||
options={gateOptions}
|
||||
getOptionLabel={(option: Gate) => option.name}
|
||||
fullWidth
|
||||
onChange={(e, val) => {
|
||||
val && setSelectKey(val.key);
|
||||
}}
|
||||
renderInput={params => <TextField {...params} label="Gate" />}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Box>
|
||||
<Button onClick={closeNewMarkerDialog}>Cancel</Button>
|
||||
<Button onClick={setMarker}>Plot New Marker</Button>
|
||||
</Box>
|
||||
</DialogActions>
|
||||
</ResponsiveDialog>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
{markerDialog()}
|
||||
<MapBase
|
||||
displayMarkerTitles={markerDisplay}
|
||||
customSearchEntries={customSearchEntries}
|
||||
geocoderResultFunction={result => {
|
||||
//open drawer
|
||||
setObjectKey(result.id?.toString() ?? "");
|
||||
if (result.place_type.includes("terminal")) {
|
||||
setTerminalDrawerOpen(true);
|
||||
} else if (result.place_type.includes("gate")) {
|
||||
setGateDrawerOpen(true);
|
||||
}
|
||||
}}
|
||||
geocoderTransitionFunction={result => {
|
||||
//override the geocoders transition with our own
|
||||
let long = result.center[0];
|
||||
let lat = result.center[1];
|
||||
if (long && lat) {
|
||||
let centered = true;
|
||||
let zoom = zoomLevels.far;
|
||||
if (result.place_type.includes("terminal")) {
|
||||
centered = false;
|
||||
} else if (result.place_type.includes("gate")) {
|
||||
zoom = zoomLevels.close;
|
||||
centered = false;
|
||||
}
|
||||
moveMap(lat, long, zoom, isMobile, centered);
|
||||
}
|
||||
}}
|
||||
markerData={markers}
|
||||
placingMarker={markerType !== pond.ObjectType.OBJECT_TYPE_UNKNOWN}
|
||||
mapTools={
|
||||
<AviationMapTools
|
||||
toggleMarkerType={setMarkerType}
|
||||
toggleMarkerDisplay={setMarkerDisplay}
|
||||
/>
|
||||
}
|
||||
mapClick={e => {
|
||||
setNewLong(e.lngLat.lng);
|
||||
setNewLat(e.lngLat.lat);
|
||||
if (markerType !== pond.ObjectType.OBJECT_TYPE_UNKNOWN) {
|
||||
setOpenMarkerDialog(true);
|
||||
}
|
||||
}}
|
||||
currentView={currentView}
|
||||
/>
|
||||
<GateDrawer
|
||||
gates={gates}
|
||||
moveMap={(long, lat) => {
|
||||
moveMap(lat, long, zoomLevels.close, isMobile);
|
||||
}}
|
||||
open={gateDrawerOpen}
|
||||
onClose={() => {
|
||||
if (new Date().valueOf() > endTime) {
|
||||
setObjectKey("");
|
||||
setGateDrawerOpen(false);
|
||||
}
|
||||
}}
|
||||
selectedGate={objectKey}
|
||||
removeMarker={key => {
|
||||
if (new Date().valueOf() > endTime) {
|
||||
//use the key to remove it from the map of yard markers which should cause the markerData array to update
|
||||
let newMarkers = clone(gateMarkers);
|
||||
newMarkers.delete(key);
|
||||
setGateMarkers(newMarkers);
|
||||
//add it back to the options for terminals
|
||||
let gate = gates.get(key);
|
||||
if (gate) {
|
||||
gateOptions.push(gate);
|
||||
}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<TerminalDrawer
|
||||
terminals={terminals}
|
||||
moveMap={(long, lat) => {
|
||||
moveMap(lat, long, zoomLevels.close, isMobile);
|
||||
}}
|
||||
open={terminalDrawerOpen}
|
||||
onClose={() => {
|
||||
if (new Date().valueOf() > endTime) {
|
||||
setObjectKey("");
|
||||
setTerminalDrawerOpen(false);
|
||||
}
|
||||
}}
|
||||
selectedKey={objectKey}
|
||||
removeMarker={key => {
|
||||
if (new Date().valueOf() > endTime) {
|
||||
//use the key to remove it from the map of yard markers which should cause the markerData array to update
|
||||
let newMarkers = clone(terminalMarkers);
|
||||
newMarkers.delete(key);
|
||||
setTerminalMarkers(newMarkers);
|
||||
//add it back to the options for terminals
|
||||
let terminal = terminals.get(key);
|
||||
if (terminal) {
|
||||
terminalOptions.push(terminal);
|
||||
}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
757
src/maps/mapObjectControllers/ConstructionMapController.tsx
Normal file
757
src/maps/mapObjectControllers/ConstructionMapController.tsx
Normal file
|
|
@ -0,0 +1,757 @@
|
|||
import {
|
||||
TextField,
|
||||
DialogContent,
|
||||
DialogActions,
|
||||
Button,
|
||||
Box,
|
||||
DialogTitle,
|
||||
Autocomplete
|
||||
} from "@mui/material";
|
||||
import React, { useCallback, useState } from "react";
|
||||
import "mapbox-gl/dist/mapbox-gl.css";
|
||||
import { pond } from "protobuf-ts/pond";
|
||||
import { useDeviceAPI, useGroupAPI, useMobile, useSnackbar } from "hooks";
|
||||
import { useEffect } from "react";
|
||||
import { useGlobalState, useSiteAPI } from "providers";
|
||||
import ResponsiveDialog from "common/ResponsiveDialog";
|
||||
import { Device, Group, Site } from "models";
|
||||
// import GroupSettingsIcon from "@material-ui/icons/Settings";
|
||||
import MapBase, { ViewData } from "maps/MapBase";
|
||||
import ConstructionMapTools from "maps/mapObjectTools/constructionMapTools";
|
||||
import { MarkerData } from "maps/mapMarkers/Markers";
|
||||
import { clone } from "lodash";
|
||||
import NexusSTIcon from "products/Construction/NexusSTIcon";
|
||||
import DeviceDrawer from "maps/mapDrawers/DeviceDrawer";
|
||||
//import { Autocomplete } from "@material-ui/lab";
|
||||
import JobsiteIcon from "products/Construction/JobSiteIcon";
|
||||
import SiteDrawer from "maps/mapDrawers/SiteDrawer";
|
||||
import DeviceGroupIcon from "products/Construction/DeviceGroupIcon";
|
||||
import GroupDrawer from "maps/mapDrawers/GroupDrawer";
|
||||
import { Result } from "@mapbox/mapbox-gl-geocoder";
|
||||
//import { GeocoderObject } from "maps/mapControllers/Geocoder";
|
||||
|
||||
interface Props {
|
||||
setSiteCallback?: React.Dispatch<React.SetStateAction<pond.SiteSettings | undefined>>;
|
||||
disableDrawer?: boolean;
|
||||
startingView?: ViewData;
|
||||
}
|
||||
|
||||
export default function ConstructionMapController(props: Props) {
|
||||
const transDuration = 3000;
|
||||
const [{ user, as }] = useGlobalState();
|
||||
const [currentView, setCurrentView] = useState<ViewData>(
|
||||
props.startingView
|
||||
? props.startingView
|
||||
: {
|
||||
latitude: 52.1579,
|
||||
longitude: -106.6702,
|
||||
zoom: user.settings.mapZoom
|
||||
}
|
||||
);
|
||||
const [newLong, setNewLong] = useState(0);
|
||||
const [newLat, setNewLat] = useState(0);
|
||||
const [siteName, setSiteName] = useState("");
|
||||
const siteAPI = useSiteAPI();
|
||||
const [openSiteDrawer, setOpenSiteDrawer] = useState(false);
|
||||
const [SiteDialog, setSiteDialog] = useState(false);
|
||||
const [sites, setSites] = useState<Map<string, Site>>(new Map());
|
||||
const { openSnack } = useSnackbar();
|
||||
const deviceAPI = useDeviceAPI();
|
||||
const [loadingSites, setLoadingSites] = useState(false);
|
||||
const [loadingDevices, setLoadingDevices] = useState(false);
|
||||
//map that is the site id as the key with and array of heater data objects (heaters for that site)
|
||||
const [heaterMap, setHeaterMap] = useState<Map<string, pond.ObjectHeaterData[]>>(
|
||||
new Map<string, pond.ObjectHeaterData[]>()
|
||||
);
|
||||
const groupAPI = useGroupAPI();
|
||||
const [groups, setGroups] = useState<Map<string, Group>>(new Map());
|
||||
const [openMarkerDialog, setOpenMarkerDialog] = useState(false);
|
||||
const [selectKey, setSelectKey] = useState("");
|
||||
const [endTime, setEndTime] = useState(0);
|
||||
const [devOps, setDevOps] = useState<Device[]>([]);
|
||||
const [groupOps, setGroupOps] = useState<Group[]>([]);
|
||||
const [devices, setDevices] = useState<Map<string, Device>>(new Map<string, Device>());
|
||||
const [openDevDrawer, setOpenDevDrawer] = useState(false);
|
||||
const [openGroupDrawer, setOpenGroupDrawer] = useState(false);
|
||||
const [markerType, setMarkerType] = useState<pond.ObjectType>(0);
|
||||
const [devMarkerData, setDevMarkerData] = useState<Map<string, MarkerData>>(new Map());
|
||||
const [siteMarkerData, setSiteMarkerData] = useState<Map<string, MarkerData>>(new Map());
|
||||
const [groupMarkerData, setGroupMarkerData] = useState<Map<string, MarkerData>>(new Map());
|
||||
const [objectKey, setObjectKey] = useState<string>("");
|
||||
const isMobile = useMobile();
|
||||
const [markers, setMarkers] = useState<MarkerData[]>([]);
|
||||
const [displayMarkerTitle, setDisplayMarkerTitle] = useState(false);
|
||||
const zoomLevels = {
|
||||
far: 14,
|
||||
close: 18
|
||||
};
|
||||
const [customGeoSearchEntries, setCustomGeoSearchEntries] = useState<Result[]>([]);
|
||||
const [siteSearchEntries, setSiteSearchEntries] = useState<Result[]>([]);
|
||||
const [deviceSearchEntries, setDeviceSearchEntries] = useState<Result[]>([]);
|
||||
const [groupSearchEntries, setGroupSearchEntries] = useState<Result[]>([]);
|
||||
|
||||
//this click delay is implemented to fix an issue with touch screens that caused the drawer to close immediately after opening
|
||||
const clickDelay = () => {
|
||||
let endTime = new Date().valueOf() + 1000;
|
||||
setEndTime(endTime);
|
||||
};
|
||||
|
||||
const closeDrawers = () => {
|
||||
setOpenDevDrawer(false);
|
||||
setOpenSiteDrawer(false);
|
||||
};
|
||||
|
||||
//makes sure all of the Fields are filled out on the first form of the dialog otherwise the next button is disabled
|
||||
const formComplete = () => {
|
||||
if (siteName.length < 1) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
//save the new site to the database
|
||||
const saveSite = () => {
|
||||
let newSite = Site.create();
|
||||
newSite.settings.userId = user.id();
|
||||
newSite.settings.siteName = siteName;
|
||||
newSite.settings.objectKey = user.id();
|
||||
newSite.settings.longitude = newLong;
|
||||
newSite.settings.latitude = newLat;
|
||||
|
||||
siteAPI
|
||||
.addSite(newSite.settings)
|
||||
.then(resp => {
|
||||
openSnack("Added new Site Location");
|
||||
loadSites();
|
||||
})
|
||||
.catch(err => {
|
||||
openSnack("Failed to add site");
|
||||
});
|
||||
setSiteDialog(false);
|
||||
};
|
||||
|
||||
//load the sites from the database and set the markers for each of them
|
||||
const loadSites = useCallback(() => {
|
||||
if (loadingSites) return;
|
||||
setLoadingSites(true);
|
||||
let s: Map<string, Site> = new Map();
|
||||
let sm: Map<string, MarkerData> = new Map();
|
||||
let hMap: Map<string, pond.ObjectHeaterData[]> = new Map<string, pond.ObjectHeaterData[]>();
|
||||
let searchEntries: Result[] = [];
|
||||
siteAPI
|
||||
.listSitesPageData(50, 0, "asc", "key", undefined, as)
|
||||
.then(resp => {
|
||||
resp.data.sites.forEach(site => {
|
||||
if (!site) return;
|
||||
let newSite: Site = Site.any(site.site);
|
||||
s.set(newSite.key(), newSite);
|
||||
//if there is no long/lat on the site give it a default of saskatoon
|
||||
if (!newSite.longitude() || !newSite.latitude()) {
|
||||
newSite.settings.latitude = 52.1579;
|
||||
newSite.settings.longitude = -106.6702;
|
||||
}
|
||||
let siteMarker: MarkerData = {
|
||||
colour: newSite.settings.theme?.color ?? "blue",
|
||||
longitude: newSite.longitude(),
|
||||
latitude: newSite.latitude(),
|
||||
title: newSite.siteName(),
|
||||
markerIcon: <JobsiteIcon type="light" size={50 * 0.6} />,
|
||||
details: [newSite.siteName()],
|
||||
clickFunc: (e, i, isMobile) => {
|
||||
closeDrawers();
|
||||
setObjectKey(newSite.key());
|
||||
setOpenSiteDrawer(true);
|
||||
moveMap(newSite.latitude(), newSite.longitude(), zoomLevels.far, isMobile);
|
||||
},
|
||||
updateFunc: location => {
|
||||
if (newSite.settings) {
|
||||
updateSiteMarkers(newSite, location.longitude, location.latitude);
|
||||
}
|
||||
}
|
||||
};
|
||||
sm.set(newSite.key(), siteMarker);
|
||||
hMap.set(newSite.key(), site.heaterData);
|
||||
searchEntries.push({
|
||||
id: newSite.key(),
|
||||
center: [newSite.longitude(), newSite.latitude()],
|
||||
place_name: newSite.siteName(),
|
||||
place_type: ["site"]
|
||||
} as Result);
|
||||
});
|
||||
setSiteMarkerData(sm);
|
||||
setSites(s);
|
||||
setLoadingSites(false);
|
||||
setHeaterMap(hMap);
|
||||
setSiteSearchEntries(searchEntries);
|
||||
})
|
||||
.catch(err => {
|
||||
openSnack("Failed to load sites");
|
||||
});
|
||||
}, [siteAPI, openSnack, as]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
const loadDevices = useCallback(() => {
|
||||
if (loadingDevices) return;
|
||||
setLoadingDevices(true);
|
||||
deviceAPI
|
||||
.list(500, 0, "asc", "name", undefined, undefined, undefined, undefined, undefined, true)
|
||||
.then(resp => {
|
||||
let devOps: Device[] = []; //only devices that are not mapped
|
||||
let deviceMap: Map<string, Device> = new Map<string, Device>(); // all devices
|
||||
let m = clone(devMarkerData);
|
||||
let searchEntries: Result[] = [];
|
||||
resp.data.comprehensiveDevices.forEach(cd => {
|
||||
if (cd.device?.settings?.deviceId) {
|
||||
let dev = Device.any(cd.device);
|
||||
deviceMap.set(dev.id().toString(), dev);
|
||||
let devLoc = dev.location();
|
||||
if (
|
||||
devLoc.longitude &&
|
||||
devLoc.longitude !== 0 &&
|
||||
devLoc.latitude &&
|
||||
devLoc.latitude !== 0
|
||||
) {
|
||||
let devMarker: MarkerData = {
|
||||
colour: dev.settings.theme?.color || "blue",
|
||||
title: dev.name(),
|
||||
longitude: devLoc.longitude,
|
||||
latitude: devLoc.latitude,
|
||||
markerIcon: <NexusSTIcon size={50 * 0.6} type="light" />,
|
||||
details: [dev.name()],
|
||||
clickFunc: (e, i, isMobile) => {
|
||||
closeDrawers();
|
||||
clickDelay();
|
||||
setObjectKey(dev.id().toString());
|
||||
setOpenDevDrawer(true);
|
||||
moveMap(devLoc.latitude, devLoc.longitude, zoomLevels.far, isMobile);
|
||||
},
|
||||
updateFunc: location => {
|
||||
if (dev.settings) {
|
||||
updateDeviceMarkers(dev, location.longitude, location.latitude);
|
||||
}
|
||||
}
|
||||
};
|
||||
m.set(dev.id().toString(), devMarker);
|
||||
searchEntries.push({
|
||||
id: dev.id().toString(),
|
||||
center: [devLoc.longitude, devLoc.latitude],
|
||||
place_name: dev.name(),
|
||||
place_type: ["device"]
|
||||
} as Result);
|
||||
} else {
|
||||
devOps.push(dev);
|
||||
}
|
||||
}
|
||||
});
|
||||
setDevices(deviceMap);
|
||||
setDevOps(devOps);
|
||||
setLoadingDevices(false);
|
||||
setDevMarkerData(m);
|
||||
setDeviceSearchEntries(searchEntries);
|
||||
})
|
||||
.catch(err => {
|
||||
openSnack("Failed to Load Devices");
|
||||
});
|
||||
}, [deviceAPI, openSnack]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
const loadGroups = useCallback(() => {
|
||||
groupAPI
|
||||
.listGroups(50, 0)
|
||||
.then(resp => {
|
||||
let gMap: Map<string, Group> = new Map();
|
||||
let groupMarkers: Map<string, MarkerData> = new Map();
|
||||
let groupOps: Group[] = [];
|
||||
let searchEntries: Result[] = [];
|
||||
resp.data.groups.forEach((g: pond.Group) => {
|
||||
let group = Group.any(g);
|
||||
gMap.set(group.id().toString(), group);
|
||||
if (
|
||||
group.settings.longitude &&
|
||||
group.settings.longitude !== 0 &&
|
||||
group.settings.latitude &&
|
||||
group.settings.latitude !== 0
|
||||
) {
|
||||
let marker: MarkerData = {
|
||||
colour: "blue",
|
||||
longitude: group.settings.longitude,
|
||||
latitude: group.settings.latitude,
|
||||
title: group.name(),
|
||||
markerIcon: <DeviceGroupIcon type="light" size={50 * 0.6} />,
|
||||
details: [group.name()],
|
||||
clickFunc: (e, i, isMobile) => {
|
||||
closeDrawers();
|
||||
clickDelay();
|
||||
setObjectKey(group.id().toString());
|
||||
setOpenGroupDrawer(true);
|
||||
moveMap(
|
||||
group.settings.latitude,
|
||||
group.settings.longitude,
|
||||
zoomLevels.far,
|
||||
isMobile
|
||||
);
|
||||
},
|
||||
updateFunc: location => {
|
||||
if (group) {
|
||||
if (group.settings) {
|
||||
let dataClone = clone(groupMarkerData);
|
||||
updateGroupMarkers(group, location.longitude, location.latitude);
|
||||
marker.longitude = location.longitude;
|
||||
marker.latitude = location.latitude;
|
||||
dataClone.set(group.id().toString(), marker);
|
||||
setGroupMarkerData(dataClone);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
groupMarkers.set(group.id().toString(), marker);
|
||||
searchEntries.push({
|
||||
id: group.id().toString(),
|
||||
center: [group.settings.longitude, group.settings.latitude],
|
||||
place_name: group.name(),
|
||||
place_type: ["group"]
|
||||
} as Result);
|
||||
} else {
|
||||
groupOps.push(group);
|
||||
}
|
||||
});
|
||||
setGroups(gMap);
|
||||
setGroupMarkerData(groupMarkers);
|
||||
setGroupOps(groupOps);
|
||||
setGroupSearchEntries(searchEntries);
|
||||
})
|
||||
.catch(err => {
|
||||
//openSnack("Failed to load Groups"); //backend function errors when there are no groups
|
||||
});
|
||||
}, [groupAPI]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
useEffect(() => {
|
||||
loadSites();
|
||||
loadDevices();
|
||||
loadGroups();
|
||||
}, [loadSites, loadDevices, loadGroups, as]);
|
||||
|
||||
//assemble the different objects markerdata into a single array to pass to the map
|
||||
useEffect(() => {
|
||||
let dArray = Array.from(devMarkerData.values());
|
||||
let sArray = Array.from(siteMarkerData.values());
|
||||
let gArray = Array.from(groupMarkerData.values());
|
||||
setMarkers(dArray.concat(sArray, gArray));
|
||||
}, [devMarkerData, siteMarkerData, groupMarkerData]);
|
||||
|
||||
//assemble the different object search entries into one array to pass to the map
|
||||
useEffect(() => {
|
||||
setCustomGeoSearchEntries(siteSearchEntries.concat(deviceSearchEntries, groupSearchEntries));
|
||||
}, [deviceSearchEntries, siteSearchEntries, groupSearchEntries]);
|
||||
|
||||
//the entry fields for the first part of the stepper
|
||||
const siteDetailsForm = () => {
|
||||
return (
|
||||
<Box>
|
||||
<TextField
|
||||
value={siteName}
|
||||
color="primary"
|
||||
autoFocus
|
||||
margin="dense"
|
||||
id="name"
|
||||
label="Site Name"
|
||||
type="text"
|
||||
fullWidth
|
||||
onChange={e => setSiteName(e.target.value)}></TextField>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
//dialog the renders the forms for creating a new site
|
||||
const dialogForSite = () => {
|
||||
return (
|
||||
<ResponsiveDialog
|
||||
fullWidth
|
||||
fullScreen={false}
|
||||
open={SiteDialog}
|
||||
onClose={() => {
|
||||
setSiteDialog(false);
|
||||
}}>
|
||||
<DialogTitle>Enter Name of Site</DialogTitle>
|
||||
<DialogContent>{siteDetailsForm()}</DialogContent>
|
||||
<DialogActions>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setSiteDialog(false);
|
||||
}}
|
||||
color="primary">
|
||||
Cancel
|
||||
</Button>
|
||||
<Button onClick={saveSite} color="primary" disabled={!formComplete()}>
|
||||
Add Site
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</ResponsiveDialog>
|
||||
);
|
||||
};
|
||||
|
||||
const markerDialog = () => {
|
||||
return (
|
||||
<ResponsiveDialog
|
||||
open={openMarkerDialog}
|
||||
onClose={() => {
|
||||
setOpenMarkerDialog(false);
|
||||
}}>
|
||||
<DialogTitle>Place Marker</DialogTitle>
|
||||
<DialogContent>
|
||||
<Box width={300}>
|
||||
{markerType === pond.ObjectType.OBJECT_TYPE_DEVICE && (
|
||||
<Autocomplete
|
||||
id="autoYardList"
|
||||
options={devOps}
|
||||
getOptionLabel={(option: Device) => option.name()}
|
||||
fullWidth
|
||||
onChange={(e, val) => {
|
||||
val && setSelectKey(val.id().toString());
|
||||
}}
|
||||
renderInput={params => <TextField {...params} label="Device" />}
|
||||
/>
|
||||
)}
|
||||
{markerType === pond.ObjectType.OBJECT_TYPE_GROUP && (
|
||||
<Autocomplete
|
||||
id="autoYardList"
|
||||
options={groupOps}
|
||||
getOptionLabel={(option: Group) => option.name()}
|
||||
fullWidth
|
||||
onChange={(e, val) => {
|
||||
val && setSelectKey(val.id().toString());
|
||||
}}
|
||||
renderInput={params => <TextField {...params} label="Group" />}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Box>
|
||||
<Button onClick={closeNewMarkerDialog}>Cancel</Button>
|
||||
<Button onClick={setMarker}>Plot New Marker</Button>
|
||||
</Box>
|
||||
</DialogActions>
|
||||
</ResponsiveDialog>
|
||||
);
|
||||
};
|
||||
|
||||
const setMarker = () => {
|
||||
if (markerType === pond.ObjectType.OBJECT_TYPE_DEVICE) {
|
||||
let dev = devices.get(selectKey);
|
||||
if (dev) {
|
||||
let key = dev.id().toString();
|
||||
let location = dev.location();
|
||||
let newDevMarker: MarkerData = {
|
||||
colour: dev.settings.theme?.color || "blue",
|
||||
title: dev.name(),
|
||||
longitude: newLong,
|
||||
latitude: newLat,
|
||||
markerIcon: <NexusSTIcon size={50 * 0.6} type="light" />,
|
||||
details: [dev.name()],
|
||||
clickFunc: (e, i, isMobile) => {
|
||||
closeDrawers();
|
||||
clickDelay();
|
||||
setObjectKey(key);
|
||||
setOpenDevDrawer(true);
|
||||
moveMap(location.latitude, location.longitude, zoomLevels.far, isMobile);
|
||||
},
|
||||
updateFunc: location => {
|
||||
if (dev) {
|
||||
if (dev.settings) {
|
||||
let dataClone = clone(devMarkerData);
|
||||
updateDeviceMarkers(dev, location.longitude, location.latitude);
|
||||
newDevMarker.longitude = location.longitude;
|
||||
newDevMarker.latitude = location.latitude;
|
||||
dataClone.set(key, newDevMarker);
|
||||
setDevMarkerData(dataClone);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
let devMarkers = clone(devMarkerData);
|
||||
devMarkers.set(key, newDevMarker);
|
||||
setDevMarkerData(devMarkers);
|
||||
updateDeviceMarkers(dev, newLong, newLat);
|
||||
}
|
||||
}
|
||||
if (markerType === pond.ObjectType.OBJECT_TYPE_GROUP) {
|
||||
let group = groups.get(selectKey);
|
||||
if (group) {
|
||||
let key = group.id().toString();
|
||||
let long = group.settings.longitude;
|
||||
let lat = group.settings.latitude;
|
||||
let newMarker: MarkerData = {
|
||||
colour: "blue",
|
||||
title: group.name(),
|
||||
longitude: newLong,
|
||||
latitude: newLat,
|
||||
markerIcon: <DeviceGroupIcon type="light" size={50 * 0.6} />,
|
||||
details: [group.name()],
|
||||
clickFunc: (e, i, isMobile) => {
|
||||
closeDrawers();
|
||||
clickDelay();
|
||||
setObjectKey(key);
|
||||
setOpenGroupDrawer(true);
|
||||
moveMap(lat, long, zoomLevels.far, isMobile);
|
||||
},
|
||||
updateFunc: location => {
|
||||
if (group) {
|
||||
if (group.settings) {
|
||||
let dataClone = clone(groupMarkerData);
|
||||
updateGroupMarkers(group, location.longitude, location.latitude);
|
||||
newMarker.longitude = location.longitude;
|
||||
newMarker.latitude = location.latitude;
|
||||
dataClone.set(key, newMarker);
|
||||
setGroupMarkerData(dataClone);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
let groupMarkers = clone(groupMarkerData);
|
||||
groupMarkers.set(key, newMarker);
|
||||
setGroupMarkerData(groupMarkers);
|
||||
updateGroupMarkers(group, newLong, newLat);
|
||||
}
|
||||
}
|
||||
closeNewMarkerDialog();
|
||||
};
|
||||
|
||||
const closeNewMarkerDialog = () => {
|
||||
if (new Date().valueOf() > endTime) {
|
||||
setOpenMarkerDialog(false);
|
||||
setSelectKey("");
|
||||
}
|
||||
};
|
||||
|
||||
const updateDeviceMarkers = (device: Device, long: number, lat: number) => {
|
||||
device.settings.longitude = long;
|
||||
device.settings.latitude = lat;
|
||||
deviceAPI
|
||||
.update(device.id(), device.settings)
|
||||
.then(resp => {
|
||||
openSnack("Device location Updated");
|
||||
if (devOps.includes(device)) {
|
||||
devOps.splice(devOps.indexOf(device), 1);
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
openSnack("Could not update Device Location");
|
||||
});
|
||||
};
|
||||
|
||||
const updateGroupMarkers = (group: Group, long: number, lat: number) => {
|
||||
group.settings.longitude = long;
|
||||
group.settings.latitude = lat;
|
||||
groupAPI
|
||||
.updateGroup(group.id(), group.settings)
|
||||
.then(resp => {
|
||||
openSnack("Group location Updated");
|
||||
if (groupOps.includes(group)) {
|
||||
groupOps.splice(groupOps.indexOf(group), 1);
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
openSnack("Could not update Group Location");
|
||||
});
|
||||
};
|
||||
|
||||
const updateSiteMarkers = (site: Site, long: number, lat: number) => {
|
||||
site.settings.longitude = long;
|
||||
site.settings.latitude = lat;
|
||||
siteAPI
|
||||
.updateSite(site.key(), site.settings)
|
||||
.then(resp => {
|
||||
openSnack("Site location updated");
|
||||
})
|
||||
.catch(err => {
|
||||
openSnack("Could not update site location");
|
||||
});
|
||||
};
|
||||
|
||||
const moveMap = (
|
||||
lat: number,
|
||||
long: number,
|
||||
zoom: number,
|
||||
mobileOffset?: boolean,
|
||||
center?: boolean
|
||||
) => {
|
||||
let xOff = !mobileOffset ? -250 : undefined;
|
||||
let yOff = mobileOffset ? -150 : undefined;
|
||||
|
||||
setCurrentView({
|
||||
latitude: lat,
|
||||
longitude: long,
|
||||
zoom: zoom,
|
||||
transitionDuration: transDuration,
|
||||
xOffset: center ? 0 : xOff,
|
||||
yOffset: center ? 0 : yOff
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Box style={{ height: "100%" }}>
|
||||
<MapBase
|
||||
placingMarker={markerType !== pond.ObjectType.OBJECT_TYPE_UNKNOWN}
|
||||
displayMarkerTitles={displayMarkerTitle}
|
||||
customSearchEntries={customGeoSearchEntries}
|
||||
geocoderResultFunction={result => {
|
||||
//open drawer
|
||||
setObjectKey(result.id?.toString() ?? "");
|
||||
if (result.place_type.includes("site")) {
|
||||
setOpenSiteDrawer(true);
|
||||
} else if (result.place_type.includes("device")) {
|
||||
setOpenDevDrawer(true);
|
||||
} else if (result.place_type.includes("group")) {
|
||||
setOpenGroupDrawer(true);
|
||||
}
|
||||
}}
|
||||
geocoderTransitionFunction={result => {
|
||||
//override the geocoders transition with our own
|
||||
let long = result.center[0];
|
||||
let lat = result.center[1];
|
||||
if (long && lat) {
|
||||
let centered = true;
|
||||
let zoom = zoomLevels.far;
|
||||
if (
|
||||
result.place_type.includes("site") ||
|
||||
result.place_type.includes("device") ||
|
||||
result.place_type.includes("group")
|
||||
) {
|
||||
centered = false;
|
||||
}
|
||||
moveMap(lat, long, zoom, isMobile, centered);
|
||||
}
|
||||
}}
|
||||
mapTools={
|
||||
<ConstructionMapTools
|
||||
toggleMarkerType={setMarkerType}
|
||||
toggleMarkerDisplay={setDisplayMarkerTitle}
|
||||
/>
|
||||
}
|
||||
mapClick={e => {
|
||||
if (markerType !== pond.ObjectType.OBJECT_TYPE_UNKNOWN) {
|
||||
setNewLong(e.lngLat.lng);
|
||||
setNewLat(e.lngLat.lat);
|
||||
if (markerType === pond.ObjectType.OBJECT_TYPE_SITE) {
|
||||
setSiteDialog(true);
|
||||
} else {
|
||||
setOpenMarkerDialog(true);
|
||||
}
|
||||
}
|
||||
}}
|
||||
currentView={currentView}
|
||||
markerData={markers}
|
||||
/>
|
||||
{dialogForSite()}
|
||||
{markerDialog()}
|
||||
{objectKey !== "" && (
|
||||
<React.Fragment>
|
||||
<DeviceDrawer
|
||||
open={openDevDrawer}
|
||||
onClose={() => {
|
||||
if (new Date().valueOf() > endTime) {
|
||||
setOpenDevDrawer(false);
|
||||
setObjectKey("");
|
||||
}
|
||||
}}
|
||||
selectedDevice={objectKey}
|
||||
devices={devices}
|
||||
removeMarker={key => {
|
||||
if (new Date().valueOf() > endTime) {
|
||||
//use the key to remove it from the map of yard markers which should cause the markerData array to update
|
||||
let newMarkers = clone(devMarkerData);
|
||||
newMarkers.delete(key);
|
||||
setDevMarkerData(newMarkers);
|
||||
//adds it back to the options
|
||||
let dev = devices.get(key);
|
||||
if (dev) {
|
||||
devOps.push(dev);
|
||||
}
|
||||
}
|
||||
}}
|
||||
updateMarker={(key, objectSettings) => {
|
||||
if (new Date().valueOf() > endTime) {
|
||||
let newMarkers = clone(devMarkerData);
|
||||
let toupdate = newMarkers.get(key);
|
||||
if (toupdate) {
|
||||
if (objectSettings.theme) {
|
||||
toupdate.colour = objectSettings.theme.color;
|
||||
}
|
||||
}
|
||||
setDevMarkerData(newMarkers);
|
||||
}
|
||||
}}
|
||||
moveMap={(long, lat) => {
|
||||
moveMap(lat, long, zoomLevels.far, isMobile);
|
||||
}}
|
||||
/>
|
||||
<SiteDrawer
|
||||
open={openSiteDrawer}
|
||||
sites={sites}
|
||||
heaters={heaterMap}
|
||||
selectedSite={objectKey}
|
||||
onClose={() => {
|
||||
if (new Date().valueOf() > endTime) {
|
||||
setOpenSiteDrawer(false);
|
||||
setObjectKey("");
|
||||
}
|
||||
}}
|
||||
moveMap={(long, lat) => {
|
||||
moveMap(lat, long, zoomLevels.far, isMobile);
|
||||
}}
|
||||
removeMarker={key => {
|
||||
if (new Date().valueOf() > endTime) {
|
||||
//use the key to remove it from the map of yard markers which should cause the markerData array to update
|
||||
let newMarkers = clone(siteMarkerData);
|
||||
newMarkers.delete(key);
|
||||
setSiteMarkerData(newMarkers);
|
||||
}
|
||||
}}
|
||||
updateMarker={(key, objectSettings) => {
|
||||
if (new Date().valueOf() > endTime) {
|
||||
let newMarkers = clone(siteMarkerData);
|
||||
let toupdate = newMarkers.get(key);
|
||||
if (toupdate) {
|
||||
if (objectSettings.theme) {
|
||||
toupdate.colour = objectSettings.theme.color;
|
||||
}
|
||||
}
|
||||
setSiteMarkerData(newMarkers);
|
||||
}
|
||||
}}
|
||||
reLoadSites={() => {
|
||||
loadSites();
|
||||
}}
|
||||
/>
|
||||
<GroupDrawer
|
||||
open={openGroupDrawer}
|
||||
groups={groups}
|
||||
moveMap={(long, lat) => {
|
||||
moveMap(lat, long, zoomLevels.far, isMobile);
|
||||
}}
|
||||
onClose={() => {
|
||||
if (new Date().valueOf() > endTime) {
|
||||
setOpenGroupDrawer(false);
|
||||
setObjectKey("");
|
||||
}
|
||||
}}
|
||||
removeMarker={key => {
|
||||
if (new Date().valueOf() > endTime) {
|
||||
//use the key to remove it from the map of yard markers which should cause the markerData array to update
|
||||
let newMarkers = clone(groupMarkerData);
|
||||
newMarkers.delete(key);
|
||||
setGroupMarkerData(newMarkers);
|
||||
//adds it back to the options
|
||||
let group = groups.get(key);
|
||||
if (group) {
|
||||
groupOps.push(group);
|
||||
}
|
||||
}
|
||||
}}
|
||||
selectedGroup={objectKey}
|
||||
/>
|
||||
</React.Fragment>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
136
src/maps/mapObjectTools/aviationMapTools.tsx
Normal file
136
src/maps/mapObjectTools/aviationMapTools.tsx
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
import {
|
||||
Box,
|
||||
Grid2 as Grid,
|
||||
IconButton,
|
||||
List,
|
||||
ListItem,
|
||||
ListItemIcon,
|
||||
Theme,
|
||||
Typography,
|
||||
} from "@mui/material";
|
||||
import FieldNamesIcon from "products/AgIcons/FieldNames";
|
||||
import AddPlaneIcon from "products/AviationIcons/AddPlaneIcon";
|
||||
import OmniAirDeviceIcon from "products/AviationIcons/OmniAirDeviceIcon";
|
||||
import VisibilityOffIcon from "@mui/icons-material/VisibilityOff";
|
||||
import PlaneIcon from "products/AviationIcons/PlaneIcon";
|
||||
import { pond } from "protobuf-ts/pond";
|
||||
import React, { useState } from "react";
|
||||
import { makeStyles } from "@mui/styles";
|
||||
|
||||
const useStyles = makeStyles((theme: Theme) => ({
|
||||
button: {
|
||||
background: theme.palette.background.default,
|
||||
margin: 5,
|
||||
boxShadow: "4px 4px 10px black",
|
||||
width: 50,
|
||||
height: 50
|
||||
},
|
||||
list: {
|
||||
background: theme.palette.background.default,
|
||||
opacity: 0.75,
|
||||
borderRadius: 15,
|
||||
marginTop: 10,
|
||||
boxShadow: "4px 4px 10px black"
|
||||
},
|
||||
listItems: {
|
||||
borderRadius: "3rem"
|
||||
},
|
||||
liFont: {
|
||||
fontWeight: 700,
|
||||
marginLeft: -15
|
||||
}
|
||||
}));
|
||||
|
||||
interface Props {
|
||||
toggleMarkerType: (type: pond.ObjectType) => void;
|
||||
toggleMarkerDisplay: (display: boolean) => void;
|
||||
}
|
||||
|
||||
export default function AviationMapTools(props: Props) {
|
||||
const classes = useStyles();
|
||||
const { toggleMarkerType, toggleMarkerDisplay } = props;
|
||||
const [airportMenu, setAirportMenu] = useState<"block" | "none">("none");
|
||||
const [siteButtonHovered, setSiteButtonHovered] = useState<boolean>(false);
|
||||
const [placeTerminal, setPlaceTerminal] = useState(false);
|
||||
const [placeGate, setPlaceGate] = useState(false);
|
||||
const [markerDisplay, setMarkerDisplay] = useState(false);
|
||||
|
||||
const toggleAirportMenu = () => {
|
||||
if (airportMenu === "none") {
|
||||
setAirportMenu("block");
|
||||
} else {
|
||||
setAirportMenu("none");
|
||||
setPlaceTerminal(false);
|
||||
setPlaceGate(false);
|
||||
toggleMarkerType(pond.ObjectType.OBJECT_TYPE_UNKNOWN);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Box style={{ position: "absolute", top: 5, left: 5, zIndex: 300 }}>
|
||||
<Grid container direction="column">
|
||||
<Grid>
|
||||
<IconButton
|
||||
className={classes.button}
|
||||
title={"Terminal"}
|
||||
onClick={toggleAirportMenu}
|
||||
onMouseOver={() => setSiteButtonHovered(true)}
|
||||
onMouseOut={() => setSiteButtonHovered(false)}
|
||||
style={
|
||||
airportMenu === "block"
|
||||
? { background: "#004f9b" }
|
||||
: siteButtonHovered
|
||||
? { background: "grey" }
|
||||
: {}
|
||||
}>
|
||||
<PlaneIcon width={35} height={35} />
|
||||
</IconButton>
|
||||
</Grid>
|
||||
<Grid style={{ display: airportMenu }}>
|
||||
<List className={classes.list}>
|
||||
<ListItem
|
||||
id="addSite"
|
||||
onClick={() => {
|
||||
setPlaceTerminal(true);
|
||||
setPlaceGate(false);
|
||||
toggleMarkerType(pond.ObjectType.OBJECT_TYPE_TERMINAL);
|
||||
}}
|
||||
className={classes.listItems}
|
||||
style={placeTerminal ? { background: "#004f9b" } : {}}>
|
||||
<ListItemIcon>
|
||||
<AddPlaneIcon height={35} width={35} />
|
||||
</ListItemIcon>
|
||||
<Typography className={classes.liFont}>Add Terminal</Typography>
|
||||
</ListItem>
|
||||
<ListItem
|
||||
id="addGate"
|
||||
onClick={() => {
|
||||
setPlaceGate(true);
|
||||
setPlaceTerminal(false);
|
||||
toggleMarkerType(pond.ObjectType.OBJECT_TYPE_GATE);
|
||||
}}
|
||||
className={classes.listItems}
|
||||
style={placeGate ? { background: "#004f9b" } : {}}>
|
||||
<ListItemIcon>
|
||||
<OmniAirDeviceIcon size={35} />
|
||||
</ListItemIcon>
|
||||
<Typography className={classes.liFont}>Add Gate</Typography>
|
||||
</ListItem>
|
||||
<ListItem
|
||||
id="showSiteNames"
|
||||
onClick={() => {
|
||||
setMarkerDisplay(!markerDisplay);
|
||||
toggleMarkerDisplay(!markerDisplay);
|
||||
}}
|
||||
className={classes.listItems}>
|
||||
<ListItemIcon>
|
||||
{markerDisplay ? <VisibilityOffIcon /> : <FieldNamesIcon />}
|
||||
</ListItemIcon>
|
||||
<Typography className={classes.liFont}>Show Marker Names</Typography>
|
||||
</ListItem>
|
||||
</List>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
238
src/maps/mapObjectTools/constructionMapTools.tsx
Normal file
238
src/maps/mapObjectTools/constructionMapTools.tsx
Normal file
|
|
@ -0,0 +1,238 @@
|
|||
import {
|
||||
//Box,
|
||||
Grid2 as Grid,
|
||||
IconButton,
|
||||
List,
|
||||
ListItem,
|
||||
ListItemIcon,
|
||||
Theme,
|
||||
Typography
|
||||
} from "@mui/material";
|
||||
import { Device } from "models";
|
||||
import FieldNamesIcon from "products/AgIcons/FieldNames";
|
||||
import DeviceGroupIcon from "products/Construction/DeviceGroupIcon";
|
||||
import JobSiteIcon from "products/Construction/JobSiteIcon";
|
||||
import NexusSTIcon from "products/Construction/NexusSTIcon";
|
||||
import VisibilityOffIcon from "@mui/icons-material/VisibilityOff";
|
||||
|
||||
//import { useDeviceAPI, useGlobalState } from "providers";
|
||||
// import { downloadJSON } from "utils";
|
||||
import { useState } from "react";
|
||||
import { pond } from "protobuf-ts/pond";
|
||||
import { makeStyles } from "@mui/styles";
|
||||
|
||||
const useStyles = makeStyles((theme: Theme) => ({
|
||||
button: {
|
||||
background: theme.palette.background.default,
|
||||
margin: 5,
|
||||
boxShadow: "4px 4px 10px black",
|
||||
width: 50,
|
||||
height: 50
|
||||
},
|
||||
list: {
|
||||
background: theme.palette.background.default,
|
||||
opacity: 0.75,
|
||||
borderRadius: 15,
|
||||
marginTop: 10,
|
||||
boxShadow: "4px 4px 10px black"
|
||||
},
|
||||
listItems: {
|
||||
borderRadius: "3rem"
|
||||
},
|
||||
liFont: {
|
||||
fontWeight: 700,
|
||||
marginLeft: -15
|
||||
}
|
||||
}));
|
||||
|
||||
interface Props {
|
||||
toggleMarkerType: (type: pond.ObjectType) => void;
|
||||
devices?: Device[];
|
||||
toggleMarkerDisplay: (display: boolean) => void;
|
||||
}
|
||||
|
||||
export default function ConstructionMapTools(props: Props) {
|
||||
const { toggleMarkerType, toggleMarkerDisplay } = props;
|
||||
//const [{ user }] = useGlobalState();
|
||||
const classes = useStyles();
|
||||
const [siteControls, setSiteControls] = useState<"block" | "none">("none");
|
||||
const [deviceControls, setDeviceControls] = useState<"block" | "none">("none");
|
||||
const [placeDevice, setPlaceDevice] = useState<boolean>(false);
|
||||
const [placeGroup, setPlaceGroup] = useState<boolean>(false);
|
||||
const [placeSite, setPlaceSite] = useState<boolean>(false);
|
||||
const [siteButtonHovered, setSiteButtonHovered] = useState<boolean>(false);
|
||||
const [devButtonHovered, setDevButtonHovered] = useState<boolean>(false);
|
||||
const [markerDisplay, setMarkerDisplay] = useState(false);
|
||||
//const deviceAPI = useDeviceAPI();
|
||||
|
||||
const toggleSiteControls = () => {
|
||||
if (siteControls === "none") {
|
||||
setSiteControls("block");
|
||||
setDeviceControls("none");
|
||||
setPlaceDevice(false);
|
||||
setPlaceGroup(false);
|
||||
} else {
|
||||
setSiteControls("none");
|
||||
setPlaceSite(false);
|
||||
toggleMarkerType(pond.ObjectType.OBJECT_TYPE_UNKNOWN);
|
||||
}
|
||||
};
|
||||
|
||||
const toggleDeviceControls = () => {
|
||||
if (deviceControls === "none") {
|
||||
setDeviceControls("block");
|
||||
setSiteControls("none");
|
||||
setPlaceSite(false);
|
||||
} else {
|
||||
setDeviceControls("none");
|
||||
setPlaceDevice(false);
|
||||
setPlaceGroup(false);
|
||||
toggleMarkerType(pond.ObjectType.OBJECT_TYPE_UNKNOWN);
|
||||
}
|
||||
};
|
||||
|
||||
// const getDeviceGeoJson = () => {
|
||||
// let ids: number[] = [];
|
||||
// if (props.devices) {
|
||||
// props.devices.forEach(dev => ids.push(dev.id()));
|
||||
// deviceAPI.getMultiGeoJson(ids).then(resp => {
|
||||
// downloadJSON(resp.data, "allDevicesGeoJSON.json");
|
||||
// });
|
||||
// }
|
||||
// };
|
||||
|
||||
return (
|
||||
<Grid container direction="column">
|
||||
<Grid>
|
||||
<Grid container direction="row">
|
||||
{" "}
|
||||
{/* row that has the buttons for controlling the display of the controls */}
|
||||
<Grid>
|
||||
<IconButton
|
||||
className={classes.button}
|
||||
title={"Sites"}
|
||||
onClick={toggleSiteControls}
|
||||
onMouseOver={() => setSiteButtonHovered(true)}
|
||||
onMouseOut={() => setSiteButtonHovered(false)}
|
||||
style={
|
||||
siteControls === "block"
|
||||
? { background: "blue" }
|
||||
: siteButtonHovered
|
||||
? { background: "grey" }
|
||||
: {}
|
||||
}>
|
||||
{siteControls === "block" ? <JobSiteIcon type="light" /> : <JobSiteIcon />}
|
||||
</IconButton>
|
||||
</Grid>
|
||||
<Grid>
|
||||
<IconButton
|
||||
className={classes.button}
|
||||
title={"Devices"}
|
||||
onClick={toggleDeviceControls}
|
||||
onMouseOver={() => setDevButtonHovered(true)}
|
||||
onMouseOut={() => setDevButtonHovered(false)}
|
||||
style={
|
||||
deviceControls === "block"
|
||||
? { background: "blue" }
|
||||
: devButtonHovered
|
||||
? { background: "grey" }
|
||||
: {}
|
||||
}>
|
||||
{deviceControls === "block" ? <NexusSTIcon type="light" /> : <NexusSTIcon />}
|
||||
</IconButton>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid>
|
||||
<Grid container direction="row">
|
||||
{" "}
|
||||
{/* row that contains the controls */}
|
||||
<Grid style={{ display: siteControls }}>
|
||||
<List className={classes.list}>
|
||||
<ListItem
|
||||
id="addSite"
|
||||
onClick={() => {
|
||||
toggleMarkerType(pond.ObjectType.OBJECT_TYPE_SITE);
|
||||
setPlaceSite(true);
|
||||
}}
|
||||
className={classes.listItems}
|
||||
style={placeSite ? { background: "blue" } : {}}>
|
||||
<ListItemIcon>
|
||||
<JobSiteIcon />
|
||||
</ListItemIcon>
|
||||
<Typography className={classes.liFont}>Add Site</Typography>
|
||||
</ListItem>
|
||||
<ListItem
|
||||
id="showSiteNames"
|
||||
onClick={() => {
|
||||
setMarkerDisplay(!markerDisplay);
|
||||
toggleMarkerDisplay(!markerDisplay);
|
||||
}}
|
||||
className={classes.listItems}>
|
||||
<ListItemIcon>
|
||||
{markerDisplay ? <VisibilityOffIcon /> : <FieldNamesIcon />}
|
||||
</ListItemIcon>
|
||||
<Typography className={classes.liFont}>Show Marker Names</Typography>
|
||||
</ListItem>
|
||||
</List>
|
||||
</Grid>
|
||||
<Grid style={{ display: deviceControls }}>
|
||||
<List className={classes.list} style={{ marginLeft: 60 }}>
|
||||
<ListItem
|
||||
id="addDevice"
|
||||
onClick={() => {
|
||||
setPlaceDevice(true);
|
||||
toggleMarkerType(pond.ObjectType.OBJECT_TYPE_DEVICE);
|
||||
setPlaceGroup(false);
|
||||
}}
|
||||
className={classes.listItems}
|
||||
style={placeDevice ? { background: "blue" } : {}}>
|
||||
<ListItemIcon>
|
||||
<NexusSTIcon />
|
||||
</ListItemIcon>
|
||||
<Typography className={classes.liFont}>Add Device</Typography>
|
||||
</ListItem>
|
||||
<ListItem
|
||||
id="addGroup"
|
||||
onClick={() => {
|
||||
setPlaceGroup(true);
|
||||
toggleMarkerType(pond.ObjectType.OBJECT_TYPE_GROUP);
|
||||
setPlaceDevice(false);
|
||||
}}
|
||||
className={classes.listItems}
|
||||
style={placeGroup ? { background: "blue" } : {}}>
|
||||
<ListItemIcon>
|
||||
<DeviceGroupIcon />
|
||||
</ListItemIcon>
|
||||
<Typography className={classes.liFont}>Add Group</Typography>
|
||||
</ListItem>
|
||||
<ListItem
|
||||
id="showGroupNames"
|
||||
onClick={() => {
|
||||
setMarkerDisplay(!markerDisplay);
|
||||
toggleMarkerDisplay(!markerDisplay);
|
||||
}}
|
||||
className={classes.listItems}>
|
||||
<ListItemIcon>
|
||||
{markerDisplay ? <VisibilityOffIcon /> : <FieldNamesIcon />}
|
||||
</ListItemIcon>
|
||||
<Typography className={classes.liFont}>Show Marker Names</Typography>
|
||||
</ListItem>
|
||||
{/* TODO: Create flag for the GeoJson data for device from map */}
|
||||
{/* {user.hasAdmin() && (
|
||||
<ListItem
|
||||
id="exportGeoJSON"
|
||||
button
|
||||
onClick={() => getDeviceGeoJson()}
|
||||
className={classes.listItems}>
|
||||
<ListItemIcon></ListItemIcon>
|
||||
<Typography className={classes.liFont}>Export GeoJSON for devices</Typography>
|
||||
</ListItem>
|
||||
)} */}
|
||||
</List>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue