diff --git a/initialize_container.sh b/initialize_container.sh
index 1a025ddb..bdfb124e 100755
--- a/initialize_container.sh
+++ b/initialize_container.sh
@@ -8,7 +8,7 @@ RESULT='window.env = {'
RESULT+='"API_URL": "'$API_URL
RESULT+='", "OAUTH_CLIENT_ID": "'$OAUTH_CLIENT_ID
RESULT+='", "OAUTH_TOKEN_URL": "'$OAUTH_TOKEN_URL
-RESULT+='", "MAP_API_URL": "'$MAP_API_URL
+RESULT+='", "MAP_API_KEY": "'$MAP_API_KEY
RESULT+='", "GEO_CODE_API": "'$GEO_CODE_API
RESULT+='", "ALERT_SOCKET_URL": "'$ALERT_SOCKET_URL
RESULT+='", "CUSTODIAN_URL": "'$CUSTODIAN_URL
diff --git a/package.json b/package.json
index a909719a..ca729a52 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "buildly-react-template",
- "version": "1.4.0",
+ "version": "1.4.1",
"description": "Frontend Template from Buildly built using the React framework",
"main": "src/index.js",
"private": true,
@@ -21,6 +21,7 @@
"@mui/material": "^5.4.2",
"@mui/styles": "^5.4.2",
"@mui/x-date-pickers": "5.0.0-beta.2",
+ "@react-google-maps/api": "^2.19.3",
"@types/react": "^16.8.18",
"@types/react-dom": "^16.8.4",
"chart.js": "^2.9.4",
@@ -154,4 +155,4 @@
],
"author": "Buildly",
"license": "ISC"
-}
+}
\ No newline at end of file
diff --git a/src/components/MapComponent/MapComponent.js b/src/components/MapComponent/MapComponent.js
index e773c6d4..9c6ce35e 100644
--- a/src/components/MapComponent/MapComponent.js
+++ b/src/components/MapComponent/MapComponent.js
@@ -1,16 +1,17 @@
+/* eslint-disable no-console */
import React, { useState, useEffect } from 'react';
-import Geocode from 'react-geocode';
import {
- withScriptjs,
- withGoogleMap,
GoogleMap,
Marker,
InfoWindow,
Polyline,
Polygon,
Circle,
-} from 'react-google-maps';
-import MarkerClusterer from 'react-google-maps/lib/components/addons/MarkerClusterer';
+ MarkerClusterer,
+ useJsApiLoader,
+ LoadScript,
+} from '@react-google-maps/api';
+import Geocode from 'react-geocode';
import _ from 'lodash';
import { Grid, useTheme } from '@mui/material';
import {
@@ -20,30 +21,39 @@ import {
Battery50 as Battery50Icon,
CalendarToday as CalendarIcon,
} from '@mui/icons-material';
-import {
- MARKER_DATA,
- getIcon,
-} from '@utils/constants';
+import { MARKER_DATA, getIcon } from '@utils/constants';
+
+const libraries = ['places', 'geometry', 'drawing'];
export const MapComponent = (props) => {
const {
+ isMarkerShown,
+ showPath,
+ screenshotMapCenter,
+ noInitialInfo,
markers,
+ zoom,
setSelectedMarker,
- geofence,
unitOfMeasure,
allMarkers,
- zoom,
- screenshotMapCenter,
- noInitialInfo,
+ geofence,
+ containerStyle,
+ setSelectedCluster,
+ selectedCluster,
} = props;
- const [center, setCenter] = useState({
- lat: 47.606209,
- lng: -122.332069,
- });
+
+ const [center, setCenter] = useState({ lat: 47.606209, lng: -122.332069 });
const [mapZoom, setMapZoom] = useState(zoom);
const [showInfoIndex, setShowInfoIndex] = useState({});
const [polygon, setPolygon] = useState({});
+ const theme = useTheme();
+
+ const { isLoaded } = useJsApiLoader({
+ googleMapsApiKey: window.env.MAP_API_KEY,
+ libraries,
+ });
+
useEffect(() => {
if (screenshotMapCenter) {
const meanCenter = { lat: _.mean(_.map(markers, 'lat')), lng: _.mean(_.map(markers, 'lng')) };
@@ -68,7 +78,13 @@ export const MapComponent = (props) => {
}
}
if (_.isEmpty(markers)) {
- if (!_.isEmpty(allMarkers)) {
+ if (!_.isEmpty(selectedCluster)) {
+ setCenter({
+ lat: selectedCluster.lat,
+ lng: selectedCluster.lng,
+ });
+ setMapZoom(18);
+ } else if (!_.isEmpty(allMarkers)) {
const allMarkerItems = [].concat(...allMarkers);
const countries = allMarkerItems.map((item) => item && item.country);
const uniqueCountries = [...new Set(countries)];
@@ -116,7 +132,6 @@ export const MapComponent = (props) => {
setCenter({ lat, lng });
},
(error) => {
- // eslint-disable-next-line no-console
console.error(error);
},
);
@@ -147,36 +162,30 @@ export const MapComponent = (props) => {
const overlapCounts = groupMarkersByLocation(_.flatten(allMarkers));
+ if (!isLoaded) return
Loading Map...
;
+
return (
-
- );
-};
-
-const RenderedMap = withScriptjs(
- withGoogleMap((props) => (
-
- {!props.isMarkerShown && props.allMarkers && !_.isEmpty(props.allMarkers)
- && _.map(props.allMarkers, (shipMarkers, idx) => (
+ zoom={mapZoom}
+ >
+ {!isMarkerShown && allMarkers && !_.isEmpty(allMarkers)
+ && _.map(allMarkers, (shipMarkers, idx) => (
{
- props.clusterClick(!_.isEmpty(shipMarkers) && _.first(shipMarkers).shipment, true);
+ setSelectedCluster(!_.isEmpty(shipMarkers) && _.first(shipMarkers));
+ setCenter({
+ lat: !_.isEmpty(shipMarkers) && _.first(shipMarkers).lat,
+ lng: !_.isEmpty(shipMarkers) && _.first(shipMarkers).lng,
+ });
+ setMapZoom(18);
}}
styles={[
{
@@ -216,13 +225,18 @@ const RenderedMap = withScriptjs(
},
]}
>
- {_.map(shipMarkers, (marker, inx) => (
-
+ {(clusterer) => _.map(shipMarkers, (marker, inx) => (
+
))}
))}
- {props.isMarkerShown && props.markers && _.map(
- props.markers,
+ {isMarkerShown && markers && _.map(
+ markers,
(mark, index) => (mark.label ? (
props.onMarkerSelect(mark)}
+ onClick={() => onMarkerSelect(mark)}
>
- {_.isEqual(props.showInfoIndex, mark) && (
- props.onMarkerSelect(null)}>
+ {_.isEqual(showInfoIndex, mark) && (
+ onMarkerSelect(null)}>
{_.isEqual(mark.label, 'Clustered')
? (
- {_.map(MARKER_DATA(props.unitOfMeasure), (item, idx) => (
+ {_.map(MARKER_DATA(unitOfMeasure), (item, idx) => (
))}
-
+
- {mark.date}
+ {mark.date}
- {mark.time}
+ {mark.time}
{mark.battery && _.gte(_.toNumber(mark.battery), 90) && (
-
+
)}
{mark.battery && _.lt(_.toNumber(mark.battery), 90) && _.gte(_.toNumber(mark.battery), 60) && (
-
+
)}
{mark.battery && _.lt(_.toNumber(mark.battery), 60) && (
-
+
)}
{!mark.battery && (
@@ -307,7 +321,7 @@ const RenderedMap = withScriptjs(
) : (
-
+
{mark.label}
)}
@@ -325,30 +339,30 @@ const RenderedMap = withScriptjs(
position={
mark.lat && mark.lng
? { lat: mark.lat, lng: mark.lng }
- : props.center
+ : center
}
onDragEnd={(e) => {
- props.onMarkerDrag(e, mark.onMarkerDrag);
+ onMarkerDrag(e, mark.onMarkerDrag);
}}
/>
)),
)}
- {props.isMarkerShown && !_.isEmpty(props.markers) && props.showPath && (
+ {isMarkerShown && markers && !_.isEmpty(markers) && showPath && (
({
+ path={_.map(markers, (marker) => ({
lat: marker.lat,
lng: marker.lng,
}))}
geodesic
options={{
- strokeColor: props.theme.palette.background.dark,
+ strokeColor: theme.palette.background.dark,
strokeOpacity: 0.75,
strokeWeight: 1,
}}
/>
)}
- {props.isMarkerShown && props.markers && !_.isEmpty(props.polygon) && _.map(
- props.markers,
+ {isMarkerShown && markers && !_.isEmpty(polygon) && _.map(
+ markers,
(mark, index) => (mark.radius ? (
-
+
{`Geofence of ${mark.radius} ${_.toLower(
- _.find(props.unitOfMeasure, (unit) => (_.toLower(unit.unit_of_measure_for) === 'distance'))
- ? _.find(props.unitOfMeasure, (unit) => (_.toLower(unit.unit_of_measure_for) === 'distance')).unit_of_measure
+ _.find(unitOfMeasure, (unit) => (_.toLower(unit.unit_of_measure_for) === 'distance'))
+ ? _.find(unitOfMeasure, (unit) => (_.toLower(unit.unit_of_measure_for) === 'distance')).unit_of_measure
: '',
)}`}
@@ -396,33 +410,33 @@ const RenderedMap = withScriptjs(
position={
mark.lat && mark.lng
? { lat: mark.lat, lng: mark.lng }
- : props.center
+ : center
}
>
-
+
Configure radius for geofence
)),
)}
- {!_.isEmpty(props.polygon) && (
+ {!_.isEmpty(polygon) && (
)}
- )),
-);
+ );
+};
export default MapComponent;
diff --git a/src/pages/Reporting/Reporting.js b/src/pages/Reporting/Reporting.js
index 8bef20fb..7fd018ba 100644
--- a/src/pages/Reporting/Reporting.js
+++ b/src/pages/Reporting/Reporting.js
@@ -250,8 +250,8 @@ const Reporting = () => {
}, [selectedShipment, markers, allGraphs, reports]);
const getShipmentValue = (value) => {
- let returnValue;
- if (selectedShipment[value] !== null) {
+ let returnValue = '';
+ if (!_.isEqual(selectedShipment[value], null)) {
if (moment(selectedShipment[value], true).isValid()) {
returnValue = moment(selectedShipment[value])
.tz(timeZone).format(`${dateFormat} ${timeFormat}`);
@@ -265,7 +265,7 @@ const Reporting = () => {
}
}
} else {
- returnValue = 'NA';
+ returnValue = 'N/A';
}
return returnValue;
};
@@ -330,9 +330,6 @@ const Reporting = () => {
const csvBody = rows.map((row) => columns.map((col, colIndex) => {
let cell = row[col.name];
- if (col.label === 'Date Time' && !_.isEmpty(cell)) {
- return escapeCSV(moment(cell).tz(timeZone).format(`${dateFormat} ${timeFormat}`));
- }
if (!row.location || row.location === 'Error retrieving address') {
row.location = 'N/A';
}
@@ -1002,12 +999,9 @@ const Reporting = () => {
screenshotMapCenter
noInitialInfo
markers={markers}
- googleMapURL={window.env.MAP_API_URL}
zoom={4}
setSelectedMarker={setSelectedMarker}
- loadingElement={
}
- containerElement={
}
- mapElement={
}
+ containerStyle={{ height: '625px' }}
unitOfMeasure={unitData}
/>
@@ -1072,10 +1066,6 @@ const Reporting = () => {
- }
- containerElement={(
-
- )}
- mapElement={
-
- }
+ containerStyle={{
+ height: '200px',
+ marginTop: isMobile() ? '10px' : '30px',
+ marginBottom: '14px',
+ }}
markers={[
{
lat: last_known_location
diff --git a/src/pages/Shipment/CreateShipment.js b/src/pages/Shipment/CreateShipment.js
index 4a8c8afd..e1c1f26a 100644
--- a/src/pages/Shipment/CreateShipment.js
+++ b/src/pages/Shipment/CreateShipment.js
@@ -1181,11 +1181,8 @@ const CreateShipment = ({ history, location }) => {
}
- containerElement={}
- mapElement={}
+ containerStyle={{ height: '300px', marginTop: '10px' }}
markers={[
{
lat: startingLocation && _.includes(startingLocation, ',') && parseFloat(startingLocation.split(',')[0]),
@@ -1255,11 +1252,8 @@ const CreateShipment = ({ history, location }) => {
}
- containerElement={}
- mapElement={}
+ containerStyle={{ height: '300px', marginTop: '10px' }}
markers={[
{
lat: endingLocation && _.includes(endingLocation, ',') && parseFloat(endingLocation.split(',')[0]),
diff --git a/src/pages/Shipment/Shipment.js b/src/pages/Shipment/Shipment.js
index 3cec70ae..96229507 100644
--- a/src/pages/Shipment/Shipment.js
+++ b/src/pages/Shipment/Shipment.js
@@ -1,3 +1,4 @@
+/* eslint-disable array-callback-return */
/* eslint-disable no-nested-ternary */
import React, { useEffect, useState } from 'react';
import _ from 'lodash';
@@ -41,6 +42,7 @@ import useAlert from '@hooks/useAlert';
import { useStore } from '@zustand/timezone/timezoneStore';
import './ShipmentStyles.css';
import { TIVE_GATEWAY_TIMES } from '@utils/mock';
+import { calculateLatLngBounds } from '@utils/utilMethods';
const Shipment = ({ history }) => {
const muiTheme = useTheme();
@@ -62,6 +64,8 @@ const Shipment = ({ history }) => {
const [expandedRows, setExpandedRows] = useState([]);
const [steps, setSteps] = useState([]);
const [isLoading, setLoading] = useState(false);
+ const [selectedCluster, setSelectedCluster] = useState({});
+ const [zoom, setZoom] = useState(4);
const { data: shipmentData, isLoading: isLoadingShipments, isFetching: isFetchingShipments } = useQuery(
['shipments', shipmentFilter, organization],
@@ -153,11 +157,21 @@ const Shipment = ({ history }) => {
sensorReportData,
);
const filteredRows = _.filter(formattedRows, { type: shipmentFilter });
- setRows(filteredRows);
+ if (_.isEmpty(selectedCluster)) {
+ setRows(filteredRows);
+ }
setAllMarkers(_.map(filteredRows, 'allMarkers'));
}, [shipmentFilter, shipmentData, custodianData, custodyData,
itemData, allGatewayData, sensorAlertData, sensorReportData]);
+ useEffect(() => {
+ if (!_.isEmpty(markers) || !_.isEmpty(selectedCluster)) {
+ setZoom(12);
+ } else {
+ setZoom(4);
+ }
+ }, [markers, selectedCluster]);
+
useEffect(() => {
if (selectedShipment) {
processMarkers(selectedShipment, true);
@@ -176,6 +190,22 @@ const Shipment = ({ history }) => {
}
}, [selectedShipment, expandedRows]);
+ useEffect(() => {
+ if (!_.isEmpty(selectedCluster) && !_.isEmpty(rows)) {
+ const { lat, lng } = selectedCluster;
+ const { radius } = user.organization;
+ const values = calculateLatLngBounds(lat, lng, radius);
+ const filteredRows = rows.filter((obj) => !_.isEmpty(obj.allMarkers));
+ const clusterFilteredRows = filteredRows.filter((obj) => {
+ const firstMarker = _.first(obj.allMarkers);
+ const isLatInRange = firstMarker.lat >= values.minLat && firstMarker.lat <= values.maxLat;
+ const isLngInRange = firstMarker.lng >= values.minLng && firstMarker.lng <= values.maxLng;
+ return isLatInRange && isLngInRange;
+ });
+ setRows(clusterFilteredRows);
+ }
+ }, [selectedCluster]);
+
const processMarkers = (shipment, setExpanded = false) => {
const dateFormat = !_.isEmpty(unitData) && _.find(unitData, (unit) => (_.toLower(unit.unit_of_measure_for) === 'date')).unit_of_measure;
const timeFormat = !_.isEmpty(unitData) && _.find(unitData, (unit) => (_.toLower(unit.unit_of_measure_for) === 'time')).unit_of_measure;
@@ -537,6 +567,7 @@ const Shipment = ({ history }) => {
setSelectedMarker({});
setExpandedRows([]);
setSteps([]);
+ setSelectedCluster({});
};
const renderSensorData = (marker) => {
@@ -673,6 +704,36 @@ const Shipment = ({ history }) => {
+ {!_.isEmpty(selectedCluster) && (
+
+ )}
@@ -687,20 +748,12 @@ const Shipment = ({ history }) => {
isMarkerShown={!_.isEmpty(markers)}
showPath
markers={markers}
- googleMapURL={window.env.MAP_API_URL}
- zoom={_.isEmpty(markers) ? 2.5 : 12}
+ zoom={zoom}
setSelectedMarker={setSelectedMarker}
- loadingElement={
-
- }
- containerElement={
-
- }
- mapElement={
-
- }
- clusterClick={processMarkers}
+ containerStyle={{ height: '600px' }}
unitOfMeasure={unitData}
+ setSelectedCluster={setSelectedCluster}
+ selectedCluster={selectedCluster}
/>
diff --git a/src/pages/Shipment/ShipmentStyles.css b/src/pages/Shipment/ShipmentStyles.css
index da6aa0b7..a1fc7b28 100644
--- a/src/pages/Shipment/ShipmentStyles.css
+++ b/src/pages/Shipment/ShipmentStyles.css
@@ -27,6 +27,17 @@
background-color: var(--color-palette-primary-main);
}
+.shipmentGoBackButton {
+ margin-bottom: 24px;
+ margin-left: 16px;
+ background-color: var(--color-palette-primary-main);
+ color: var(--color-palette-background-default);
+}
+
+.shipmentGoBackButton:hover {
+ background-color: var(--color-palette-primary-main);
+}
+
.shipmentTab {
color: var(--color-palette-background-default);
}
diff --git a/src/utils/utilMethods.js b/src/utils/utilMethods.js
index da90c6e2..3ea82aa1 100644
--- a/src/utils/utilMethods.js
+++ b/src/utils/utilMethods.js
@@ -93,3 +93,18 @@ export const dateDifference = (initialDate, finalDate) => {
const dateString = `${days} days, ${hours} hrs., ${minutes} min.`;
return dateString;
};
+
+export const calculateLatLngBounds = (lat, lng, miles) => {
+ const milesToLatDegree = miles / 69;
+ const maxLat = lat + milesToLatDegree;
+ const minLat = lat - milesToLatDegree;
+ const milesToLngDegree = miles / (69 * Math.cos(lat * (Math.PI / 180)));
+ const maxLng = lng + milesToLngDegree;
+ const minLng = lng - milesToLngDegree;
+ return {
+ maxLat,
+ minLat,
+ maxLng,
+ minLng,
+ };
+};