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
|
|
@ -172,7 +172,7 @@ export default function Device(props: Props) {
|
|||
}, [load]);
|
||||
|
||||
const getOrderedComponents = () => {
|
||||
if (preferences.childDisplayOrder.length === Array.from(components.values()).length) {
|
||||
if (preferences.childDisplayOrder && preferences.childDisplayOrder.length === Array.from(components.values()).length) {
|
||||
return Array.from(components.values()).sort((a, b: Component) =>
|
||||
sortComponents(a, b, preferences.childDisplayOrder)
|
||||
);
|
||||
|
|
|
|||
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>
|
||||
);
|
||||
}
|
||||
80
src/models/Site.ts
Normal file
80
src/models/Site.ts
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
import { cloneDeep } from "lodash";
|
||||
import { pond } from "protobuf-ts/pond";
|
||||
import { or } from "utils/types";
|
||||
|
||||
export class Site {
|
||||
public settings: pond.SiteSettings = pond.SiteSettings.create();
|
||||
public status: pond.SiteStatus = pond.SiteStatus.create();
|
||||
|
||||
public static create(ps?: pond.Site): Site {
|
||||
let my = new Site();
|
||||
if (ps) {
|
||||
my.settings = pond.SiteSettings.fromObject(cloneDeep(or(ps.settings, {})));
|
||||
my.status = pond.SiteStatus.fromObject(cloneDeep(or(ps.status, {})));
|
||||
}
|
||||
return my;
|
||||
}
|
||||
|
||||
public static clone(other?: Site): Site {
|
||||
if (other) {
|
||||
return Site.create(
|
||||
pond.Site.fromObject({
|
||||
settings: cloneDeep(other.settings),
|
||||
status: cloneDeep(other.status)
|
||||
})
|
||||
);
|
||||
}
|
||||
return Site.create();
|
||||
}
|
||||
|
||||
public static any(data: any): Site {
|
||||
return Site.create(pond.Site.fromObject(cloneDeep(data)));
|
||||
}
|
||||
|
||||
public key(): string {
|
||||
return this.settings.key;
|
||||
}
|
||||
|
||||
public longitude(): number {
|
||||
return this.settings.longitude;
|
||||
}
|
||||
|
||||
public latitude(): number {
|
||||
return this.settings.latitude;
|
||||
}
|
||||
|
||||
public siteName(): string {
|
||||
return this.settings.siteName;
|
||||
}
|
||||
|
||||
public siteAddress(): string {
|
||||
return this.settings.siteAddress;
|
||||
}
|
||||
|
||||
public siteDescription(): string {
|
||||
return this.settings.siteDescription;
|
||||
}
|
||||
|
||||
public clientName(): string {
|
||||
return this.settings.clientName;
|
||||
}
|
||||
|
||||
public clientPhone(): string {
|
||||
return this.settings.clientPhone;
|
||||
}
|
||||
|
||||
public jobType(): string {
|
||||
return this.settings.jobType;
|
||||
}
|
||||
|
||||
public jobDetails(): string {
|
||||
return this.settings.jobDetails;
|
||||
}
|
||||
|
||||
public location(): pond.Location {
|
||||
let loc = pond.Location.create();
|
||||
loc.longitude = this.settings.longitude;
|
||||
loc.latitude = this.settings.latitude;
|
||||
return loc;
|
||||
}
|
||||
}
|
||||
|
|
@ -8,7 +8,7 @@ export * from "./Group";
|
|||
export * from "./Interaction";
|
||||
export * from "./Scope";
|
||||
export * from "./ShareableLink";
|
||||
// export * from "./Site";
|
||||
export * from "./Site";
|
||||
export * from "./Tag";
|
||||
// export * from "./Task";
|
||||
// export * from "./Upgrade";
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ import FieldMap from "pages/FieldMap";
|
|||
import GrainBag from "pages/grainBag";
|
||||
import Terminals from "pages/Terminals";
|
||||
import Gate from "pages/Gate";
|
||||
import AviationMap from "pages/AviationMap";
|
||||
import ConstructionSiteMap from "pages/ConstructionSiteMap";
|
||||
|
||||
const DeviceHistory = lazy(() => import("pages/DeviceHistory"));
|
||||
const DevicePage = lazy(() => import("pages/Device"));
|
||||
|
|
@ -246,7 +248,12 @@ export default function Router(props: Props) {
|
|||
{/* Page routes */}
|
||||
{/* <Route index element={<Typography>Hello!</Typography>} /> */}
|
||||
<Route path="users" element={<Users/>} />
|
||||
|
||||
{/* Map pages */}
|
||||
<Route path="visualFarm" element={<FieldMap />} />
|
||||
<Route path="aviationMap" element={<AviationMap />} />
|
||||
<Route path="constructionMap" element={<ConstructionSiteMap />} />
|
||||
|
||||
<Route path="/logout" element={<Logout />} />
|
||||
<Route path="grainbags/:bagID" element={<GrainBag />} />
|
||||
{/*
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ import MiningIcon from "products/ventilation/MiningIcon";
|
|||
import { useAuth0 } from "@auth0/auth0-react";
|
||||
import FieldsIcon from "products/AgIcons/FieldsIcon";
|
||||
import PlaneIcon from "products/AviationIcons/PlaneIcon";
|
||||
import AirportMapIcon from "products/AviationIcons/AirportMapIcon";
|
||||
|
||||
const drawerWidth = 230;
|
||||
|
||||
|
|
@ -237,6 +238,20 @@ export default function SideNavigator(props: Props) {
|
|||
</ListItemButton>
|
||||
</Tooltip>
|
||||
)}
|
||||
{(IsOmniAir || user.hasFeature("admin")) && (
|
||||
<Tooltip title="Aviation Map" placement="right">
|
||||
<ListItemButton
|
||||
id="tour-aviation-map"
|
||||
onClick={() => goTo("/aviationMap")}
|
||||
classes={getClasses("/aviationMap")}
|
||||
>
|
||||
<ListItemIcon>
|
||||
<AirportMapIcon />
|
||||
</ListItemIcon>
|
||||
{open && <ListItemText primary="Visual Farm" />}
|
||||
</ListItemButton>
|
||||
</Tooltip>
|
||||
)}
|
||||
</List>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
26
src/pages/AviationMap.tsx
Normal file
26
src/pages/AviationMap.tsx
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import AviationMapController from "maps/mapObjectControllers/AviationMapController";
|
||||
import { useLocation } from "react-router";
|
||||
import PageContainer from "./PageContainer";
|
||||
|
||||
export default function AviationMap() {
|
||||
const location = useLocation();
|
||||
|
||||
return (
|
||||
<PageContainer>
|
||||
{location.state !== undefined &&
|
||||
location.state !== null &&
|
||||
location.state.long !== undefined &&
|
||||
location.state.lat !== undefined ? (
|
||||
<AviationMapController
|
||||
startingView={{
|
||||
longitude: location.state.long,
|
||||
latitude: location.state.lat,
|
||||
zoom: 18
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<AviationMapController />
|
||||
)}
|
||||
</PageContainer>
|
||||
);
|
||||
}
|
||||
27
src/pages/ConstructionSiteMap.tsx
Normal file
27
src/pages/ConstructionSiteMap.tsx
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
import PageContainer from "./PageContainer";
|
||||
import ConstructionMapController from "maps/mapObjectControllers/ConstructionMapController";
|
||||
import { useLocation } from "react-router";
|
||||
|
||||
export default function ConstructionSiteMap() {
|
||||
const location = useLocation();
|
||||
|
||||
return (
|
||||
<PageContainer>
|
||||
{location.state !== undefined &&
|
||||
location.state !== null &&
|
||||
location.state.long !== undefined &&
|
||||
location.state.lat !== undefined ? (
|
||||
<ConstructionMapController
|
||||
startingView={{
|
||||
longitude: location.state.long,
|
||||
latitude: location.state.lat,
|
||||
zoom: 18,
|
||||
transitionDuration: 0
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<ConstructionMapController />
|
||||
)}
|
||||
</PageContainer>
|
||||
);
|
||||
}
|
||||
|
|
@ -25,7 +25,7 @@ export {
|
|||
// useMetricAPI,
|
||||
usePermissionAPI,
|
||||
// usePreferenceAPI,
|
||||
// useSiteAPI,
|
||||
useSiteAPI, //TODO: update api with resolve, reject
|
||||
useTagAPI,
|
||||
useTeamAPI,
|
||||
useImagekitAPI,
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import FieldMarkerProvider, {useFieldMarkerAPI} from "./fieldMarkerAPI";
|
|||
import HomeMarkerProvider, {useHomeMarkerAPI} from "./homeMarkerAPI";
|
||||
import HarvestPlanProvider, {useHarvestPlanAPI} from "./harvestPlanAPI";
|
||||
import TerminalProvider, {useTerminalAPI} from "./terminalAPI"
|
||||
import SiteProvider, {useSiteAPI} from "./siteAPI";
|
||||
// import NoteProvider from "providers/noteAPI";
|
||||
|
||||
export const pondURL = (partial: string, demo: boolean = false): string => {
|
||||
|
|
@ -66,7 +67,9 @@ export default function PondProvider(props: PropsWithChildren<any>) {
|
|||
<HomeMarkerProvider>
|
||||
<HarvestPlanProvider>
|
||||
<TerminalProvider>
|
||||
{children}
|
||||
<SiteProvider>
|
||||
{children}
|
||||
</SiteProvider>
|
||||
</TerminalProvider>
|
||||
</HarvestPlanProvider>
|
||||
</HomeMarkerProvider>
|
||||
|
|
@ -119,5 +122,6 @@ export {
|
|||
useFieldMarkerAPI,
|
||||
useHomeMarkerAPI,
|
||||
useHarvestPlanAPI,
|
||||
useTerminalAPI
|
||||
useTerminalAPI,
|
||||
useSiteAPI
|
||||
};
|
||||
|
|
|
|||
179
src/providers/pond/siteAPI.tsx
Normal file
179
src/providers/pond/siteAPI.tsx
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
import { AxiosResponse } from "axios";
|
||||
import { useHTTP } from "hooks";
|
||||
import { pond } from "protobuf-ts/pond";
|
||||
import { useGlobalState } from "providers/StateContainer";
|
||||
import React, { createContext, PropsWithChildren, useContext } from "react";
|
||||
import { or } from "utils";
|
||||
import { pondURL } from "./pond";
|
||||
|
||||
export interface ISiteAPIContext {
|
||||
addSite: (site: pond.SiteSettings) => Promise<any>;
|
||||
getSite: (siteID: string) => Promise<any>;
|
||||
getSitePage: (siteID: string) => Promise<any>;
|
||||
listSites: (
|
||||
limit: number,
|
||||
offset: number,
|
||||
order?: "asc" | "desc",
|
||||
orderBy?: string,
|
||||
search?: string,
|
||||
as?: string,
|
||||
asRoot?: boolean
|
||||
) => Promise<AxiosResponse<pond.ListSitesResponse>>;
|
||||
listSitesPageData: (
|
||||
limit: number,
|
||||
offset: number,
|
||||
order?: "asc" | "desc",
|
||||
orderBy?: string,
|
||||
search?: string,
|
||||
as?: string,
|
||||
asRoot?: boolean
|
||||
) => Promise<AxiosResponse<pond.ListSitesPageDataResponse>>;
|
||||
removeSite: (siteID: string) => Promise<AxiosResponse<pond.RemoveSiteResponse>>;
|
||||
updateSite: (
|
||||
key: string,
|
||||
site: pond.SiteSettings,
|
||||
asRoot?: true
|
||||
) => Promise<AxiosResponse<pond.UpdateSiteResponse>>;
|
||||
updateLink: (
|
||||
parentKey: string,
|
||||
parentType: string,
|
||||
objectID: string,
|
||||
objectType: string,
|
||||
permissions: string[]
|
||||
) => Promise<any>;
|
||||
}
|
||||
|
||||
export const SiteAPIContext = createContext<ISiteAPIContext>({} as ISiteAPIContext);
|
||||
|
||||
interface Props {}
|
||||
|
||||
export default function SiteProvider(props: PropsWithChildren<Props>) {
|
||||
const { children } = props;
|
||||
const { get, del, post, put } = useHTTP();
|
||||
const [{ as }] = useGlobalState();
|
||||
|
||||
const getSitePage = (siteID: string) => {
|
||||
if (as) return get(pondURL("/sitePage/" + siteID + "?as=" + as));
|
||||
return get(pondURL("/sitePage/" + siteID));
|
||||
};
|
||||
|
||||
const addSite = (site: pond.SiteSettings) => {
|
||||
if (as) return post(pondURL("/sites?as=" + as), site);
|
||||
return post(pondURL("/sites"), site);
|
||||
};
|
||||
|
||||
const getSite = (siteID: string) => {
|
||||
if (as) return get(pondURL("/site/" + siteID + "?as=" + as));
|
||||
return get(pondURL("/site/" + siteID));
|
||||
};
|
||||
|
||||
const removeSite = (key: string) => {
|
||||
return del<pond.RemoveSiteResponse>(pondURL("/sites/" + key));
|
||||
};
|
||||
|
||||
const listSites = (
|
||||
limit: number,
|
||||
offset: number,
|
||||
order?: "asc" | "desc",
|
||||
orderBy?: string,
|
||||
search?: string,
|
||||
as?: string,
|
||||
asRoot?: boolean
|
||||
) => {
|
||||
return get<pond.ListSitesResponse>(
|
||||
pondURL(
|
||||
"/sites" +
|
||||
"?limit=" +
|
||||
limit +
|
||||
"&offset=" +
|
||||
offset +
|
||||
("&order=" + or(order, "asc")) +
|
||||
("&by=" + or(orderBy, "key")) +
|
||||
(as ? "&as=" + as : "") +
|
||||
(asRoot ? "&asRoot=" + asRoot.toString() : "") +
|
||||
(search ? "&search=" + search : "")
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
const listSitesPageData = (
|
||||
limit: number,
|
||||
offset: number,
|
||||
order?: "asc" | "desc",
|
||||
orderBy?: string,
|
||||
search?: string,
|
||||
as?: string,
|
||||
asRoot?: boolean
|
||||
) => {
|
||||
return get<pond.ListSitesPageDataResponse>(
|
||||
pondURL(
|
||||
"/sitesWithData" +
|
||||
"?limit=" +
|
||||
limit +
|
||||
"&offset=" +
|
||||
offset +
|
||||
("&order=" + or(order, "asc")) +
|
||||
("&by=" + or(orderBy, "key")) +
|
||||
(as ? "&as=" + as : "") +
|
||||
(asRoot ? "&asRoot=" + asRoot.toString() : "") +
|
||||
(search ? "&search=" + search : "")
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
const updateSite = (key: string, site: pond.SiteSettings, asRoot?: boolean) => {
|
||||
if (as)
|
||||
return put<pond.UpdateSiteResponse>(
|
||||
pondURL(
|
||||
"/sites/" + key + (as ? "?as=" + as : "") + (asRoot ? "&asRoot=" + asRoot.toString() : "")
|
||||
),
|
||||
site
|
||||
);
|
||||
return put<pond.UpdateSiteResponse>(
|
||||
pondURL("/sites/" + key + (asRoot ? "?asRoot=" + asRoot.toString() : "")),
|
||||
site
|
||||
);
|
||||
};
|
||||
|
||||
const updateLink = (
|
||||
parentID: string,
|
||||
parentType: string,
|
||||
objectID: string,
|
||||
objectType: string,
|
||||
permissions: string[]
|
||||
) => {
|
||||
if (as)
|
||||
return post(pondURL(`/sites/` + parentID + `/link?as=${as}`), {
|
||||
Key: objectID,
|
||||
Type: objectType,
|
||||
Parent: parentID,
|
||||
ParentType: parentType,
|
||||
Permissions: permissions
|
||||
});
|
||||
return post(pondURL(`/sites/` + parentID + `/link`), {
|
||||
Key: objectID,
|
||||
Type: objectType,
|
||||
Parent: parentID,
|
||||
ParentType: parentType,
|
||||
Permissions: permissions
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<SiteAPIContext.Provider
|
||||
value={{
|
||||
addSite,
|
||||
getSite,
|
||||
listSites,
|
||||
listSitesPageData,
|
||||
removeSite,
|
||||
updateSite,
|
||||
updateLink,
|
||||
getSitePage
|
||||
}}>
|
||||
{children}
|
||||
</SiteAPIContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export const useSiteAPI = () => useContext(SiteAPIContext);
|
||||
|
|
@ -8,7 +8,7 @@ import { useState } from "react";
|
|||
import OpenWeatherMap from "openweathermap-ts";
|
||||
import { CurrentResponse, ThreeHourResponse } from "openweathermap-ts/dist/types";
|
||||
import { useEffect } from "react";
|
||||
//import WeatherIcon from "./weatherIcon";
|
||||
import WeatherIcon from "./weatherIcon";
|
||||
import { getThemeType } from "theme";
|
||||
import { useMobile, useThemeType } from "hooks";
|
||||
import WindSpeedDark from "assets/components/windSpeedDark.png";
|
||||
|
|
@ -220,10 +220,10 @@ export default function Weather(props: Props) {
|
|||
padding="5%"
|
||||
className={classes.upperSquare}
|
||||
style={{ flexDirection: isMobile ? "column" : "row" }}>
|
||||
{/* <WeatherIcon
|
||||
<WeatherIcon
|
||||
openweatherIconId={currentConditions.weather[0].id}
|
||||
size={isMobile ? 35 : 50}
|
||||
/> */}
|
||||
/>
|
||||
<Typography variant="body1" style={{ fontSize: 25 }}>
|
||||
{Math.round(celsiusConverter(currentConditions.main.temp))}℃
|
||||
</Typography>
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ interface Props {
|
|||
|
||||
//need to get Dustin/Sandaru to make some custom weather icons
|
||||
export default function WeatherIcon(props: Props) {
|
||||
const size = props.size ? props.size : 25;
|
||||
//const size = props.size ? props.size : 25;
|
||||
const iconId = props.openweatherIconId;
|
||||
|
||||
switch (iconId) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue