diff --git a/packages/website/src/common/Chart/MultipleSensorsCharts/__snapshots__/index.test.tsx.snap b/packages/website/src/common/Chart/MultipleSensorsCharts/__snapshots__/index.test.tsx.snap
index 437f6901e..83ebefbea 100644
--- a/packages/website/src/common/Chart/MultipleSensorsCharts/__snapshots__/index.test.tsx.snap
+++ b/packages/website/src/common/Chart/MultipleSensorsCharts/__snapshots__/index.test.tsx.snap
@@ -141,9 +141,6 @@ exports[`MultipleSensorsCharts should render with given state from Redux store 1
type="button"
>
Download
-
diff --git a/packages/website/src/common/SiteDetails/Surveys/ReefCheckSurveyCard/index.test.tsx b/packages/website/src/common/SiteDetails/Surveys/ReefCheckSurveyCard/index.test.tsx
index ceebef2af..0718acb39 100644
--- a/packages/website/src/common/SiteDetails/Surveys/ReefCheckSurveyCard/index.test.tsx
+++ b/packages/website/src/common/SiteDetails/Surveys/ReefCheckSurveyCard/index.test.tsx
@@ -16,12 +16,16 @@ describe('ReefCheckSurveyCard', () => {
);
}
- it('should render date', () => {
+ it('should render correctly', () => {
const { getByText } = renderReefCheckSurveyCard();
expect(
getByText(`Date: ${new Date(mockReefCheckSurvey.date).toLocaleString()}`),
).toBeInTheDocument();
+ expect(
+ getByText(`Depth: ${mockReefCheckSurvey.depth}m`),
+ ).toBeInTheDocument();
+ expect(getByText('User: Reef Check')).toBeInTheDocument();
});
it('should render user if submittedBy is present', () => {
@@ -48,7 +52,7 @@ describe('ReefCheckSurveyCard', () => {
'Count',
'INVERTEBRATES (2)',
'Count',
- 'BLEACHING AND CORAL DIDEASES',
+ 'BLEACHING AND CORAL DISEASES',
'YES/NO',
'IMPACT',
'YES/NO',
diff --git a/packages/website/src/common/SiteDetails/Surveys/ReefCheckSurveyCard/index.tsx b/packages/website/src/common/SiteDetails/Surveys/ReefCheckSurveyCard/index.tsx
index 5304a4fc2..c07f92663 100644
--- a/packages/website/src/common/SiteDetails/Surveys/ReefCheckSurveyCard/index.tsx
+++ b/packages/website/src/common/SiteDetails/Surveys/ReefCheckSurveyCard/index.tsx
@@ -57,10 +57,13 @@ const ReefCheckSurveyCardComponent = ({
return (
- Date: {new Date(survey.date).toLocaleString()}
- {survey.submittedBy && (
- User: {survey.submittedBy}
- )}
+
+
+ Date: {new Date(survey.date).toLocaleString()}
+
+ Depth: {survey.depth}m
+
+ User: {survey.submittedBy ?? 'Reef Check'}
@@ -75,7 +78,7 @@ const ReefCheckSurveyCardComponent = ({
Count
- BLEACHING AND CORAL DIDEASES
+ BLEACHING AND CORAL DISEASES
YES/NO
IMPACT
diff --git a/packages/website/src/helpers/bleachingAlertIntervals.ts b/packages/website/src/helpers/bleachingAlertIntervals.ts
index d2f4db7c8..5f1c89d5d 100644
--- a/packages/website/src/helpers/bleachingAlertIntervals.ts
+++ b/packages/website/src/helpers/bleachingAlertIntervals.ts
@@ -84,11 +84,6 @@ export const findIntervalByLevel = (
}
};
-export const findMaxLevel = (intervals: Interval[]): number => {
- const levels = intervals.map((item) => item.level);
- return Math.max(...levels);
-};
-
export const getColorByLevel = (level: number): string => {
return findIntervalByLevel(level).color;
};
diff --git a/packages/website/src/routes/HomeMap/Map/Markers/SiteMarker.tsx b/packages/website/src/routes/HomeMap/Map/Markers/SiteMarker.tsx
index 6e532c2c1..bd054ad0b 100644
--- a/packages/website/src/routes/HomeMap/Map/Markers/SiteMarker.tsx
+++ b/packages/website/src/routes/HomeMap/Map/Markers/SiteMarker.tsx
@@ -1,17 +1,18 @@
-import { Marker, useMap } from 'react-leaflet';
+import React from 'react';
+import { CircleMarker, Marker } from 'react-leaflet';
import { useDispatch, useSelector } from 'react-redux';
import { Site } from 'store/Sites/types';
import {
- siteOnMapSelector,
- setSiteOnMap,
+ isSelectedOnMapSelector,
setSearchResult,
+ setSiteOnMap,
} from 'store/Homepage/homepageSlice';
-import { useMarkerIcon } from 'helpers/map';
-import { hasDeployedSpotter } from 'helpers/siteUtils';
import {
alertColorFinder,
alertIconFinder,
} from 'helpers/bleachingAlertIntervals';
+import { useMarkerIcon } from 'helpers/map';
+import { hasDeployedSpotter } from 'helpers/siteUtils';
import Popup from '../Popup';
// To make sure we can see all the sites all the time, and especially
@@ -20,21 +21,54 @@ const LNG_OFFSETS = [-360, 0, 360];
interface SiteMarkerProps {
site: Site;
- setCenter: (inputMap: L.Map, latLng: [number, number], zoom: number) => void;
}
/**
* All in one site marker with icon, offset duplicates, and popup built in.
*/
-export default function SiteMarker({ site, setCenter }: SiteMarkerProps) {
- const siteOnMap = useSelector(siteOnMapSelector);
- const map = useMap();
+export const CircleSiteMarker = React.memo(({ site }: SiteMarkerProps) => {
+ const isSelected = useSelector(isSelectedOnMapSelector(site.id));
+ const dispatch = useDispatch();
+ const { tempWeeklyAlert } = site.collectionData || {};
+
+ if (site.polygon.type !== 'Point') return null;
+
+ const [lng, lat] = site.polygon.coordinates;
+
+ return (
+ <>
+ {LNG_OFFSETS.map((offset) => (
+ {
+ dispatch(setSearchResult());
+ dispatch(setSiteOnMap(site));
+ },
+ }}
+ key={`${site.id}-${offset}`}
+ center={[lat, lng + offset]}
+ radius={5}
+ fillColor={alertColorFinder(tempWeeklyAlert)}
+ fillOpacity={1}
+ color="black"
+ weight={1}
+ data-alert={tempWeeklyAlert}
+ >
+ {isSelected && }
+
+ ))}
+ >
+ );
+});
+
+export const SensorSiteMarker = React.memo(({ site }: SiteMarkerProps) => {
+ const isSelected = useSelector(isSelectedOnMapSelector(site.id));
const dispatch = useDispatch();
const { tempWeeklyAlert } = site.collectionData || {};
const markerIcon = useMarkerIcon(
hasDeployedSpotter(site),
site.hasHobo,
- siteOnMap?.id === site.id,
+ isSelected,
alertColorFinder(tempWeeklyAlert),
alertIconFinder(tempWeeklyAlert),
);
@@ -42,26 +76,25 @@ export default function SiteMarker({ site, setCenter }: SiteMarkerProps) {
if (site.polygon.type !== 'Point') return null;
const [lng, lat] = site.polygon.coordinates;
+
return (
<>
- {LNG_OFFSETS.map((offset) => {
- return (
- {
- if (map) setCenter(map, [lat, lng], 6);
- dispatch(setSearchResult());
- dispatch(setSiteOnMap(site));
- },
- }}
- key={`${site.id}-${offset}`}
- icon={markerIcon}
- position={[lat, lng + offset]}
- >
-
-
- );
- })}
+ {LNG_OFFSETS.map((offset) => (
+ {
+ dispatch(setSearchResult());
+ dispatch(setSiteOnMap(site));
+ },
+ }}
+ key={`${site.id}-${offset}`}
+ icon={markerIcon}
+ position={[lat, lng + offset]}
+ data-alert={tempWeeklyAlert}
+ >
+ {isSelected && }
+
+ ))}
>
);
-}
+});
diff --git a/packages/website/src/routes/HomeMap/Map/Markers/index.tsx b/packages/website/src/routes/HomeMap/Map/Markers/index.tsx
index 1767074ff..41fa4d937 100644
--- a/packages/website/src/routes/HomeMap/Map/Markers/index.tsx
+++ b/packages/website/src/routes/HomeMap/Map/Markers/index.tsx
@@ -1,35 +1,13 @@
import { useSelector } from 'react-redux';
-import { LayerGroup, useMap } from 'react-leaflet';
-import MarkerClusterGroup from 'react-leaflet-markercluster';
-import { useCallback, useEffect, useState, useMemo } from 'react';
-import L from 'leaflet';
+import { useMemo } from 'react';
import { sitesToDisplayListSelector } from 'store/Sites/sitesListSlice';
import { Site } from 'store/Sites/types';
-import { siteOnMapSelector } from 'store/Homepage/homepageSlice';
import 'leaflet/dist/leaflet.css';
import { CollectionDetails } from 'store/Collection/types';
-import {
- findIntervalByLevel,
- findMaxLevel,
- getColorByLevel,
- Interval,
-} from 'helpers/bleachingAlertIntervals';
-import SiteMarker from './SiteMarker';
+import { hasDeployedSpotter } from 'helpers/siteUtils';
+import { CircleSiteMarker, SensorSiteMarker } from './SiteMarker';
-const clusterIcon = (cluster: any) => {
- const alerts: Interval[] = cluster.getAllChildMarkers().map((marker: any) => {
- const { site } = marker?.options?.children?.[0]?.props || {};
- const { tempWeeklyAlert } = site?.collectionData || {};
- return findIntervalByLevel(tempWeeklyAlert);
- });
- const color = getColorByLevel(findMaxLevel(alerts));
- const count = cluster.getChildCount();
- return L.divIcon({
- html: `${count}
`,
- className: `leaflet-marker-icon marker-cluster custom-cluster-icon marker-cluster-small leaflet-zoom-animated leaflet-interactive`,
- iconSize: L.point(40, 40, true),
- });
-};
+const hasSpotter = (site: Site) => site.hasHobo || hasDeployedSpotter(site);
export const SiteMarkers = ({ collection }: SiteMarkersProps) => {
const storedSites = useSelector(sitesToDisplayListSelector);
@@ -37,65 +15,17 @@ export const SiteMarkers = ({ collection }: SiteMarkersProps) => {
() => collection?.sites || storedSites || [],
[collection?.sites, storedSites],
);
- const siteOnMap = useSelector(siteOnMapSelector);
- const map = useMap();
- const [visibleSites, setVisibleSites] = useState(sitesList);
-
- const setCenter = useCallback(
- (inputMap: L.Map, latLng: [number, number], zoom: number) => {
- const maxZoom = Math.max(inputMap.getZoom() || 6, zoom);
- const pointBounds = L.latLngBounds(latLng, latLng);
- inputMap.flyToBounds(pointBounds, {
- duration: 2,
- maxZoom,
- paddingTopLeft: L.point(0, 200),
- });
- },
- [],
- );
-
- const filterSitesByViewport = useCallback(() => {
- if (!map) return;
-
- const bounds = map.getBounds();
- const filtered = sitesList.filter((site: Site) => {
- if (!site.polygon || site.polygon.type !== 'Point') return false;
- const [lng, lat] = site.polygon.coordinates;
- return bounds.contains([lat, lng]);
- });
- setVisibleSites(filtered);
- }, [map, sitesList]);
-
- useEffect(() => {
- if (!map) return undefined;
-
- filterSitesByViewport();
- map.on('moveend', filterSitesByViewport);
-
- return () => {
- map.off('moveend', filterSitesByViewport);
- return undefined;
- };
- }, [map, filterSitesByViewport]);
-
- useEffect(() => {
- if (map && siteOnMap?.polygon.type === 'Point') {
- const [lng, lat] = siteOnMap.polygon.coordinates;
- setCenter(map, [lat, lng], 6);
- }
- }, [map, siteOnMap, setCenter]);
return (
-
-
- {visibleSites.map((site: Site) => (
-
- ))}
-
-
+ <>
+ {sitesList.map((site: Site) =>
+ sitesList[site.id] && hasSpotter(site) ? (
+
+ ) : (
+
+ ),
+ )}
+ >
);
};
diff --git a/packages/website/src/routes/HomeMap/Map/index.tsx b/packages/website/src/routes/HomeMap/Map/index.tsx
index 4f66eb8b1..43a834b9c 100644
--- a/packages/website/src/routes/HomeMap/Map/index.tsx
+++ b/packages/website/src/routes/HomeMap/Map/index.tsx
@@ -18,7 +18,10 @@ import withStyles, {
} from '@mui/styles/withStyles';
import MyLocationIcon from '@mui/icons-material/MyLocation';
import { sitesListLoadingSelector } from 'store/Sites/sitesListSlice';
-import { searchResultSelector } from 'store/Homepage/homepageSlice';
+import {
+ searchResultSelector,
+ siteOnMapSelector,
+} from 'store/Homepage/homepageSlice';
import { CollectionDetails } from 'store/Collection/types';
import { MapLayerName } from 'store/Homepage/types';
import { mapConstants } from 'constants/maps';
@@ -69,6 +72,7 @@ const HomepageMap = ({
const loading = useSelector(sitesListLoadingSelector);
const searchResult = useSelector(searchResultSelector);
const ref = useRef(null);
+ const siteOnMap = useSelector(siteOnMapSelector);
const onLocationSearch = () => {
if (navigator.geolocation) {
@@ -119,6 +123,20 @@ const HomepageMap = ({
// const onBaseLayerChange = ({ name }: LayersControlEvent) => {
// setLegendName(name);
// };
+ useEffect(() => {
+ const map = ref.current?.leafletElement;
+ if (map && siteOnMap?.polygon.type === 'Point') {
+ const [lng, lat] = siteOnMap.polygon.coordinates;
+ const latLng = [lat, lng] as [number, number];
+ const pointBounds = L.latLngBounds(latLng, latLng);
+ const maxZoom = Math.max(map.getZoom() || 6);
+ map.flyToBounds(pointBounds, {
+ duration: 2,
+ maxZoom,
+ paddingTopLeft: L.point(0, 200),
+ });
+ }
+ }, [siteOnMap]);
const ExpandIcon = showSiteTable ? FullscreenIcon : FullscreenExitIcon;
diff --git a/packages/website/src/routes/SiteRoutes/ReefCheckSurveys/ReefCheckSurveyDetails.tsx b/packages/website/src/routes/SiteRoutes/ReefCheckSurveys/ReefCheckSurveyDetails.tsx
index 3f54b204e..a584d8a78 100644
--- a/packages/website/src/routes/SiteRoutes/ReefCheckSurveys/ReefCheckSurveyDetails.tsx
+++ b/packages/website/src/routes/SiteRoutes/ReefCheckSurveys/ReefCheckSurveyDetails.tsx
@@ -65,6 +65,11 @@ const surveyFields = [
// eslint-disable-next-line prettier/prettier
] satisfies Array>;
+const formatFieldValue = (value: any, formatter?: (v: any) => string) => {
+ if (value != null && formatter) return formatter(value);
+ return value ?? '';
+};
+
const ReefCheckSurveyDetailsComponent = ({
classes,
}: ReefCheckSurveyDetailsProps) => {
@@ -89,7 +94,7 @@ const ReefCheckSurveyDetailsComponent = ({
className={classes.skeleton}
/>
) : (
- formatter?.(survey[field]) ?? survey[field] ?? ''
+ formatFieldValue(survey[field], formatter)
)}
diff --git a/packages/website/src/routes/SiteRoutes/SiteApplication/Form/__snapshots__/index.test.tsx.snap b/packages/website/src/routes/SiteRoutes/SiteApplication/Form/__snapshots__/index.test.tsx.snap
index 7121e7bf1..1097cc923 100644
--- a/packages/website/src/routes/SiteRoutes/SiteApplication/Form/__snapshots__/index.test.tsx.snap
+++ b/packages/website/src/routes/SiteRoutes/SiteApplication/Form/__snapshots__/index.test.tsx.snap
@@ -784,7 +784,7 @@ exports[`renders as expected 1`] = `