From 00275248fa1f7489239c55c9f3b37e0713eb6d1e Mon Sep 17 00:00:00 2001 From: mohitb35 <44917347+mohitb35@users.noreply.github.com> Date: Fri, 8 Nov 2024 10:58:09 +0530 Subject: [PATCH 01/14] feat: enhance date formatting function to handle string cleanup and zero-padding --- src/utils/countryCurrency/getFormattedDate.ts | 39 +++++++++++-------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/src/utils/countryCurrency/getFormattedDate.ts b/src/utils/countryCurrency/getFormattedDate.ts index d2c075c990..a0c59bbb27 100644 --- a/src/utils/countryCurrency/getFormattedDate.ts +++ b/src/utils/countryCurrency/getFormattedDate.ts @@ -3,23 +3,30 @@ import parseISO from 'date-fns/parseISO'; import { localeMapForDate } from '../language/getLanguageName'; export default function formatDate(date: number | Date | string) { - if (date) { - try { - if (typeof date === 'string') { - return format(parseISO(date), 'LLLL d, yyyy', { - locale: localeMapForDate[localStorage.getItem('language') || 'en'], - }); - } - else { - return format(date, 'LLLL d, yyyy', { - locale: localeMapForDate[localStorage.getItem('language') || 'en'], - }); - } - } catch (error) { - console.log(error); - return ''; + if (!date) { + return ''; + } + + try { + if (typeof date === 'string') { + // Clean up date string + const cleanDateString = date.split(' ')[0]; // Remove time portion + // Zero-padding month and day if needed + const [year, month, day] = cleanDateString.split('-'); + const isoDateString = `${year}-${month.padStart(2, '0')}-${day.padStart( + 2, + '0' + )}`; + return format(parseISO(isoDateString), 'LLLL d, yyyy', { + locale: localeMapForDate[localStorage.getItem('language') || 'en'], + }); + } else { + return format(date, 'LLLL d, yyyy', { + locale: localeMapForDate[localStorage.getItem('language') || 'en'], + }); } - } else { + } catch (error) { + console.log(error); return ''; } } From afac8f625dae4f8b85d35309f360506317b302ec Mon Sep 17 00:00:00 2001 From: mohitb35 <44917347+mohitb35@users.noreply.github.com> Date: Fri, 8 Nov 2024 10:59:41 +0530 Subject: [PATCH 02/14] feat: simplify conditional rendering in KeyInfo component for project details --- .../ProjectDetails/components/KeyInfo.tsx | 162 +++++++++--------- 1 file changed, 79 insertions(+), 83 deletions(-) diff --git a/src/features/projectsV2/ProjectDetails/components/KeyInfo.tsx b/src/features/projectsV2/ProjectDetails/components/KeyInfo.tsx index 6c1e9b5a85..3c1b90dce9 100644 --- a/src/features/projectsV2/ProjectDetails/components/KeyInfo.tsx +++ b/src/features/projectsV2/ProjectDetails/components/KeyInfo.tsx @@ -35,102 +35,98 @@ const KeyInfo = ({ const tProjectDetails = useTranslations('ProjectDetails'); const locale = useLocale(); - const addZeroToDate = (val: string) => { - const arr = val.split('-'); - const newDateArr = [arr[0]]; - if (arr[1].length === 1) { - newDateArr.push(`0${arr[1]}`); - } else { - newDateArr.push(arr[1]); - } - if (arr[2].length === 1) { - newDateArr.push(`0${arr[2]}`); - } else { - newDateArr.push(arr[2]); - } - return newDateArr.join('-'); - }; + const showAbandonmentInfo = abandonment !== null && abandonment > 0; + const showProtectionStarted = + startingProtectionYear !== null && startingProtectionYear > 0; + const showPlantingDensity = plantingDensity !== null && plantingDensity > 0; + const showEmployees = employees !== null && employees > 0; + const showDegradationYear = degradationYear !== null && degradationYear > 0; + const showActivitySeasons = + activitySeasons !== null && activitySeasons.length > 0; + const showPlantingSeasons = + plantingSeasons !== null && plantingSeasons.length > 0; + const restorationDate = firstTreePlanted ? formatDate(firstTreePlanted) : ''; return (
-
- {abandonment && ( - - {tProjectDetails('abandonment')} - -
- {tProjectDetails('yearAbandonedInfo')} -
-
- - } - > -

- {tCommon('approx')} {abandonment} -

-
- )} - {firstTreePlanted && ( - - - - )} - {startingProtectionYear && ( - - - - )} -
+ {(showAbandonmentInfo || + restorationDate.length > 0 || + showProtectionStarted) && ( +
+ {showAbandonmentInfo && ( + + {tProjectDetails('abandonment')} + +
+ {tProjectDetails('yearAbandonedInfo')} +
+
+ + } + > +

+ {tCommon('approx')} {abandonment} +

+
+ )} + {restorationDate.length > 0 && ( + + + + )} + {showProtectionStarted && ( + + + + )} +
+ )} -
- {plantingDensity && ( - - <> - {getFormattedNumber(locale, plantingDensity)} - {maxPlantingDensity !== null - ? `-${getFormattedNumber( - locale, - maxPlantingDensity - )} ${tProjectDetails('treePerHa')}` - : ` ${tProjectDetails('treePerHa')}`} - - - )} - {employees && ( - -

{employees}

-
- )} -
+ {(showPlantingDensity || showEmployees) && ( +
+ {showPlantingDensity && ( + + <> + {getFormattedNumber(locale, plantingDensity)} + {maxPlantingDensity !== null + ? `-${getFormattedNumber( + locale, + maxPlantingDensity + )} ${tProjectDetails('treePerHa')}` + : ` ${tProjectDetails('treePerHa')}`} + + + )} + {showEmployees && ( + +

{employees}

+
+ )} +
+ )} -
- {degradationYear && ( + {showDegradationYear && ( +

{degradationYear}

- )} -
+
+ )} - {activitySeasons && activitySeasons.length > 0 && ( + {showActivitySeasons && ( )} - {plantingSeasons && plantingSeasons.length > 0 && ( + {showPlantingSeasons && ( From 838dd27263ef4f7350297ec20acbc9b36e7f7d2c Mon Sep 17 00:00:00 2001 From: mohitb35 <44917347+mohitb35@users.noreply.github.com> Date: Fri, 8 Nov 2024 11:18:08 +0530 Subject: [PATCH 03/14] fix: minor alignment correction for site dropdown/explore buttons --- .../Layout/ProjectsLayout/ProjectsLayout.module.scss | 2 +- .../ProjectSiteDropDown/SiteDropdown.module.scss | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/features/common/Layout/ProjectsLayout/ProjectsLayout.module.scss b/src/features/common/Layout/ProjectsLayout/ProjectsLayout.module.scss index d29b89e84c..be2720c0a2 100644 --- a/src/features/common/Layout/ProjectsLayout/ProjectsLayout.module.scss +++ b/src/features/common/Layout/ProjectsLayout/ProjectsLayout.module.scss @@ -49,7 +49,7 @@ $layoutPaddingBottom: 30px; .mapFeatureExplorerOverlay { position: absolute; - top: calc($layoutPaddingTop + 8px); + top: calc($layoutPaddingTop + 10px); right: calc($layoutPaddingSide + 10px); z-index: 1; } diff --git a/src/features/projectsV2/ProjectsMap/ProjectSiteDropDown/SiteDropdown.module.scss b/src/features/projectsV2/ProjectsMap/ProjectSiteDropDown/SiteDropdown.module.scss index e01505b4e3..05e9dd760a 100644 --- a/src/features/projectsV2/ProjectsMap/ProjectSiteDropDown/SiteDropdown.module.scss +++ b/src/features/projectsV2/ProjectsMap/ProjectSiteDropDown/SiteDropdown.module.scss @@ -1,5 +1,8 @@ @import '../../../../theme/theme'; +$layoutPaddingTop: 24px; +$layoutPaddingSide: 20px; + .dropdownButton { display: flex; border-radius: 12px; @@ -12,8 +15,8 @@ gap: 18px; position: absolute; z-index: 2; - top: 35px; - right: 30px; + top: calc($layoutPaddingTop + 10px); + right: calc($layoutPaddingSide + 10px); } .siteIconAndTextContainer { display: flex; From faddf5bbfd87514bf1bb15195a71624e188873d9 Mon Sep 17 00:00:00 2001 From: mohitb35 <44917347+mohitb35@users.noreply.github.com> Date: Fri, 8 Nov 2024 15:13:39 +0530 Subject: [PATCH 04/14] fix: allows project details to overflow --- .../projectsV2/ProjectDetails/ProjectDetails.module.scss | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/features/projectsV2/ProjectDetails/ProjectDetails.module.scss b/src/features/projectsV2/ProjectDetails/ProjectDetails.module.scss index 7a03e87d4a..e41cbb3982 100644 --- a/src/features/projectsV2/ProjectDetails/ProjectDetails.module.scss +++ b/src/features/projectsV2/ProjectDetails/ProjectDetails.module.scss @@ -2,7 +2,8 @@ .projectDetailsContainer { width: 100%; - height: 100%; + height: calc(100% + 30px); + padding-bottom: 30px; overflow-y: auto; overflow-x: hidden; padding-right: 10px; From 24d0ee36b1743d1fe500a9e50f3fb53f96d84ef9 Mon Sep 17 00:00:00 2001 From: mohitb35 <44917347+mohitb35@users.noreply.github.com> Date: Fri, 8 Nov 2024 17:28:52 +0530 Subject: [PATCH 05/14] fix: ensure map resizes as per container dimensions --- .../ProjectsMap/MultipleProjectsView.tsx | 17 ++++------- .../ProjectsMap/ProjectsMap.module.scss | 19 +++++++++++++ .../ProjectsMap/SingleProjectView.tsx | 17 +++++++---- src/features/projectsV2/ProjectsMap/index.tsx | 14 ++++++++-- .../projectsV2/ProjectsMapContext.tsx | 28 +++++++++++++++---- src/utils/mapsV2/zoomToLocation.tsx | 9 +++--- .../mapsV2/zoomToPolygonPlantLocation.tsx | 12 ++++---- src/utils/mapsV2/zoomToProjectSite.ts | 14 +++++----- 8 files changed, 88 insertions(+), 42 deletions(-) diff --git a/src/features/projectsV2/ProjectsMap/MultipleProjectsView.tsx b/src/features/projectsV2/ProjectsMap/MultipleProjectsView.tsx index 0984050b18..e282902818 100644 --- a/src/features/projectsV2/ProjectsMap/MultipleProjectsView.tsx +++ b/src/features/projectsV2/ProjectsMap/MultipleProjectsView.tsx @@ -1,26 +1,21 @@ import type { CategorizedProjects } from './ProjectMarkers'; -import type { SetState } from '../../common/types/common'; -import type { ViewState } from 'react-map-gl-v7/maplibre'; import type { MapRef } from '../../common/types/projectv2'; import { useEffect, useMemo } from 'react'; import ProjectMarkers from './ProjectMarkers'; import { useProjects } from '../ProjectsContext'; +import { useProjectsMap } from '../ProjectsMapContext'; // Add this import import { getProjectCategory } from '../../../utils/projectV2'; import { zoomOutMap } from '../../../utils/mapsV2/zoomToProjectSite'; interface MultipleProjectsViewProps { - setViewState: SetState; mapRef: MapRef; page: 'project-list' | 'project-details'; } -const MultipleProjectsView = ({ - setViewState, - mapRef, - page, -}: MultipleProjectsViewProps) => { +const MultipleProjectsView = ({ mapRef, page }: MultipleProjectsViewProps) => { const { projects, isLoading, isError, filteredProjects } = useProjects(); + const { handleViewStateChange } = useProjectsMap(); if (isLoading || isError || !projects) { return null; } @@ -34,11 +29,10 @@ const MultipleProjectsView = ({ ? mapRef.current.getMap() : mapRef.current; zoomOutMap(map, () => { - setViewState((prevState) => ({ - ...prevState, + handleViewStateChange({ ...map.getCenter(), zoom: map.getZoom(), - })); + }); }); } }); @@ -68,6 +62,7 @@ const MultipleProjectsView = ({ } ); }, [projects, filteredProjects, isLoading, isError]); + return ( ); diff --git a/src/features/projectsV2/ProjectsMap/ProjectsMap.module.scss b/src/features/projectsV2/ProjectsMap/ProjectsMap.module.scss index 38ef95c5d6..68597e5e69 100644 --- a/src/features/projectsV2/ProjectsMap/ProjectsMap.module.scss +++ b/src/features/projectsV2/ProjectsMap/ProjectsMap.module.scss @@ -60,6 +60,25 @@ .mapContainer { width: 100%; height: 100%; + position: relative; + overflow: hidden; + + :global { + .maplibregl-map { + width: 100% !important; + height: 100% !important; + } + + .maplibregl-canvas-container { + width: 100% !important; + height: 100% !important; + + canvas { + width: 100% !important; + height: 100% !important; + } + } + } } .android :global(.maplibregl-control-container .maplibregl-ctrl-bottom-right) { diff --git a/src/features/projectsV2/ProjectsMap/SingleProjectView.tsx b/src/features/projectsV2/ProjectsMap/SingleProjectView.tsx index 9a2ece1dab..98d07e376f 100644 --- a/src/features/projectsV2/ProjectsMap/SingleProjectView.tsx +++ b/src/features/projectsV2/ProjectsMap/SingleProjectView.tsx @@ -21,7 +21,7 @@ const SingleProjectView = ({ mapRef }: Props) => { useProjects(); if (singleProject === null) return null; - const { isSatelliteView, setViewState, setIsSatelliteView } = + const { isSatelliteView, handleViewStateChange, setIsSatelliteView } = useProjectsMap(); const router = useRouter(); @@ -48,13 +48,13 @@ const SingleProjectView = ({ mapRef }: Props) => { zoomToPolygonPlantLocation( polygonCoordinates, mapRef, - setViewState, + handleViewStateChange, 4000 ); } else if (isPointLocation) { const [lon, lat] = coordinates; if (typeof lon === 'number' && typeof lat === 'number') { - zoomToLocation(setViewState, lon, lat, 20, 4000, mapRef); + zoomToLocation(handleViewStateChange, lon, lat, 20, 4000, mapRef); } } }, [selectedPlantLocation, router.isReady]); @@ -67,7 +67,7 @@ const SingleProjectView = ({ mapRef }: Props) => { mapRef, sitesGeojson, selectedSite, - setViewState, + handleViewStateChange, 4000 ); } else { @@ -75,7 +75,14 @@ const SingleProjectView = ({ mapRef }: Props) => { if (typeof latitude === 'number' && typeof longitude === 'number') { // Zoom into the project location that has no site - zoomToLocation(setViewState, longitude, latitude, 10, 4000, mapRef); + zoomToLocation( + handleViewStateChange, + longitude, + latitude, + 10, + 4000, + mapRef + ); } } }, [selectedSite, sitesGeojson, router.isReady, selectedPlantLocation]); diff --git a/src/features/projectsV2/ProjectsMap/index.tsx b/src/features/projectsV2/ProjectsMap/index.tsx index 7d7ac9f10b..f75e514760 100644 --- a/src/features/projectsV2/ProjectsMap/index.tsx +++ b/src/features/projectsV2/ProjectsMap/index.tsx @@ -1,3 +1,4 @@ +import type { ViewStateChangeEvent } from 'react-map-gl-v7/maplibre'; import type { ViewMode } from '../../common/Layout/ProjectsLayout/MobileProjectsLayout'; import type { SetState } from '../../common/types/common'; import type { PlantLocationSingle } from '../../common/types/plantLocation'; @@ -37,7 +38,8 @@ export type ProjectsMapProps = ProjectsMapMobileProps | ProjectsMapDesktopProps; function ProjectsMap(props: ProjectsMapProps) { // const [mobileOS, setMobileOS] = useState(null); const mapRef: MapRef = useRef(null); - const { viewState, setViewState, mapState, mapOptions } = useProjectsMap(); + const { viewState, handleViewStateChange, mapState, mapOptions } = + useProjectsMap(); const { plantLocations, setHoveredPlantLocation, @@ -93,6 +95,13 @@ function ProjectsMap(props: ProjectsMapProps) { mobileOS, }; + const onMove = useCallback( + (evt: ViewStateChangeEvent) => { + handleViewStateChange(evt.viewState); + }, + [handleViewStateChange] + ); + const onMouseMove = useCallback( (e) => { if (props.page !== 'project-details') return; @@ -150,7 +159,6 @@ function ProjectsMap(props: ProjectsMapProps) { }; const multipleProjectsViewProps = { mapRef, - setViewState, page: props.page, }; const mapContainerClass = `${styles.mapContainer} ${ @@ -163,7 +171,7 @@ function ProjectsMap(props: ProjectsMapProps) { setViewState(e.viewState)} + onMove={onMove} onMouseMove={onMouseMove} onMouseOut={() => setHoveredPlantLocation(null)} onClick={onClick} diff --git a/src/features/projectsV2/ProjectsMapContext.tsx b/src/features/projectsV2/ProjectsMapContext.tsx index 52b9d4916e..70785df1c7 100644 --- a/src/features/projectsV2/ProjectsMapContext.tsx +++ b/src/features/projectsV2/ProjectsMapContext.tsx @@ -6,6 +6,12 @@ import type { SetState } from '../common/types/common'; import { useContext, useMemo, createContext, useState, useEffect } from 'react'; import getMapStyle from '../../utils/maps/getMapStyle'; +// Update ViewState type to ensure width and height are included +interface ExtendedViewState extends ViewState { + width?: string | number; + height?: string | number; +} + interface MapState { mapStyle: MapStyle; dragPan: boolean; @@ -20,14 +26,17 @@ const EMPTY_STYLE = { layers: [] as MapStyle['layers'], } as const; -export const DEFAULT_VIEW_STATE: ViewState = { +export const DEFAULT_VIEW_STATE: ExtendedViewState = { longitude: 0, latitude: 0, zoom: 2, bearing: 0, pitch: 0, padding: { top: 0, bottom: 0, left: 0, right: 0 }, + width: '100%', + height: '100%', }; + const DEFAULT_MAP_STATE: MapState = { mapStyle: EMPTY_STYLE, dragPan: true, @@ -42,7 +51,7 @@ export type MapOptions = { interface ProjectsMapState { viewState: ViewState; - setViewState: SetState; + handleViewStateChange: (newViewState: Partial) => void; mapState: MapState; isSatelliteView: boolean; setIsSatelliteView: SetState; @@ -65,6 +74,15 @@ export const ProjectsMapProvider: FC = ({ children }) => { showProjects: true, }); + const handleViewStateChange = (newViewState: Partial) => { + setViewState((prev) => ({ + ...prev, + ...newViewState, + width: '100%', // Always ensure width and height are set + height: '100%', + })); + }; + useEffect(() => { async function loadMapStyle() { const result = await getMapStyle('default'); @@ -86,11 +104,11 @@ export const ProjectsMapProvider: FC = ({ children }) => { () => ({ mapState, viewState, - setViewState, - mapOptions, - updateMapOption, + handleViewStateChange, isSatelliteView, setIsSatelliteView, + mapOptions, + updateMapOption, }), [mapState, viewState, mapOptions, isSatelliteView] ); diff --git a/src/utils/mapsV2/zoomToLocation.tsx b/src/utils/mapsV2/zoomToLocation.tsx index 946caae832..69d208f2d7 100644 --- a/src/utils/mapsV2/zoomToLocation.tsx +++ b/src/utils/mapsV2/zoomToLocation.tsx @@ -1,9 +1,8 @@ -import { SetState } from '../../features/common/types/common'; -import { ViewState } from 'react-map-gl-v7/maplibre'; -import { MapRef } from '../../features/common/types/projectv2'; +import type { ViewState } from 'react-map-gl-v7/maplibre'; +import type { MapRef } from '../../features/common/types/projectv2'; export default function zoomToLocation( - setViewState: SetState, + handleViewStateChange: (viewState: Partial) => void, longitude: number, latitude: number, zoom = 10, @@ -33,6 +32,6 @@ export default function zoomToLocation( right: 0, }, }; - setViewState(newViewState); + handleViewStateChange(newViewState); }); } diff --git a/src/utils/mapsV2/zoomToPolygonPlantLocation.tsx b/src/utils/mapsV2/zoomToPolygonPlantLocation.tsx index d4cee4a233..36a1315067 100644 --- a/src/utils/mapsV2/zoomToPolygonPlantLocation.tsx +++ b/src/utils/mapsV2/zoomToPolygonPlantLocation.tsx @@ -1,13 +1,13 @@ +import type { Position } from 'geojson'; +import type { ViewState } from 'react-map-gl-v7/maplibre'; +import type { MapRef } from '../../features/common/types/projectv2'; + import * as turf from '@turf/turf'; -import { SetState } from '../../features/common/types/common'; -import { Position } from 'geojson'; -import { ViewState } from 'react-map-gl-v7/maplibre'; -import { MapRef } from '../../features/common/types/projectv2'; export function zoomToPolygonPlantLocation( coordinates: Position[], mapRef: MapRef, - setViewState: SetState, + handleViewStateChange: (viewState: Partial) => void, duration = 3000 ) { if (!mapRef.current) { @@ -42,6 +42,6 @@ export function zoomToPolygonPlantLocation( right: 0, }, }; - setViewState(newViewState); + handleViewStateChange(newViewState); }); } diff --git a/src/utils/mapsV2/zoomToProjectSite.ts b/src/utils/mapsV2/zoomToProjectSite.ts index e9d04a6c77..9e9775d7ae 100644 --- a/src/utils/mapsV2/zoomToProjectSite.ts +++ b/src/utils/mapsV2/zoomToProjectSite.ts @@ -1,10 +1,10 @@ +import type { SitesGeoJSON } from '../../features/common/types/ProjectPropsContextInterface'; +import type { ViewState } from 'react-map-gl-v7'; +import type { Map } from 'maplibre-gl'; +import type { MapRef } from '../../features/common/types/projectv2'; + import * as turf from '@turf/turf'; -import { SitesGeoJSON } from '../../features/common/types/ProjectPropsContextInterface'; -import { SetState } from '../../features/common/types/common'; -import { ViewState } from 'react-map-gl-v7'; -import { Map } from 'maplibre-gl'; import { DEFAULT_VIEW_STATE } from '../../features/projectsV2/ProjectsMapContext'; -import { MapRef } from '../../features/common/types/projectv2'; export function zoomOutMap(map: Map, callback: () => void) { map.flyTo({ @@ -21,7 +21,7 @@ export function zoomInToProjectSite( mapRef: MapRef, geoJson: SitesGeoJSON | null, selectedSite: number, - setViewState: SetState, + handleViewStateChange: (viewState: Partial) => void, duration = 1200 ) { if (!geoJson || !mapRef.current) { @@ -65,6 +65,6 @@ export function zoomInToProjectSite( right: 0, }, }; - setViewState(newViewState); + handleViewStateChange(newViewState); }); } From e8405634c4e91c896e1e2f5d3e7e8aae861a8731 Mon Sep 17 00:00:00 2001 From: mohitb35 <44917347+mohitb35@users.noreply.github.com> Date: Mon, 11 Nov 2024 10:51:21 +0530 Subject: [PATCH 06/14] fix: adjusts position of zoom buttons on mobile --- src/features/projectsV2/ProjectsMap/ProjectsMap.module.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/features/projectsV2/ProjectsMap/ProjectsMap.module.scss b/src/features/projectsV2/ProjectsMap/ProjectsMap.module.scss index 68597e5e69..beda0eadfb 100644 --- a/src/features/projectsV2/ProjectsMap/ProjectsMap.module.scss +++ b/src/features/projectsV2/ProjectsMap/ProjectsMap.module.scss @@ -82,11 +82,11 @@ } .android :global(.maplibregl-control-container .maplibregl-ctrl-bottom-right) { - bottom: 85px; + bottom: 105px; } .ios :global(.maplibregl-control-container .maplibregl-ctrl-bottom-right) { - bottom: 150px; + bottom: 130px; } // Plant Location Styles From d865bbd11d76b81f265f14917bb5b71afa0352dd Mon Sep 17 00:00:00 2001 From: mohitb35 <44917347+mohitb35@users.noreply.github.com> Date: Mon, 11 Nov 2024 11:58:25 +0530 Subject: [PATCH 07/14] chore: adds command for https dev mode to package.json --- .gitignore | 2 ++ package.json | 1 + 2 files changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 3958983a36..350759877d 100644 --- a/.gitignore +++ b/.gitignore @@ -37,6 +37,8 @@ cypress.env.json # certificates ./certificates/localhost.crt ./certificates/localhost.key +certificates/localhost-key.pem +certificates/localhost.pem # tenant # tenant.config.js diff --git a/package.json b/package.json index c4876806fa..be7af0c142 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ ".next/cache" ], "scripts": { + "dev-https": "next dev --experimental-https", "dev": "next dev", "build": "npx prisma generate && next build", "analyze": "ANALYZE=true npm run build", From a07e8d50e8b8829d9750d2125d91a593003cc8c6 Mon Sep 17 00:00:00 2001 From: mohitb35 <44917347+mohitb35@users.noreply.github.com> Date: Mon, 11 Nov 2024 11:35:19 +0530 Subject: [PATCH 08/14] feat: prevents map re-centering when result set > 30 - also debounces the useEffect that runs on updates to `filteredProjects` to prevent excessive map centering - adds a check to prevent map centering if position won't significantly change --- src/features/projectsV2/ProjectsMap/index.tsx | 38 +++++++++++++------ src/utils/projectV2.ts | 20 ++++++++++ 2 files changed, 46 insertions(+), 12 deletions(-) diff --git a/src/features/projectsV2/ProjectsMap/index.tsx b/src/features/projectsV2/ProjectsMap/index.tsx index f75e514760..b1d0a9257e 100644 --- a/src/features/projectsV2/ProjectsMap/index.tsx +++ b/src/features/projectsV2/ProjectsMap/index.tsx @@ -4,13 +4,14 @@ import type { SetState } from '../../common/types/common'; import type { PlantLocationSingle } from '../../common/types/plantLocation'; import type { ExtendedMapLibreMap, MapRef } from '../../common/types/projectv2'; -import { useCallback, useEffect, useMemo, useRef } from 'react'; +import { useCallback, useMemo, useRef } from 'react'; import 'maplibre-gl/dist/maplibre-gl.css'; import Map, { NavigationControl } from 'react-map-gl-v7/maplibre'; import { useProjectsMap } from '../ProjectsMapContext'; import MultipleProjectsView from './MultipleProjectsView'; import SingleProjectView from './SingleProjectView'; import { + areMapCoordsEqual, calculateCentroid, centerMapOnCoordinates, getDeviceType, @@ -22,6 +23,7 @@ import { useProjects } from '../ProjectsContext'; import MultiPlantLocationInfo from '../ProjectDetails/components/MultiPlantLocationInfo'; import SinglePlantLocationInfo from '../ProjectDetails/components/SinglePlantLocationInfo'; import styles from './ProjectsMap.module.scss'; +import { useDebouncedEffect } from '../../../utils/useDebouncedEffect'; export type ProjectsMapDesktopProps = { isMobile: false; @@ -53,20 +55,32 @@ function ProjectsMap(props: ProjectsMapProps) { selectedSamplePlantLocation, } = useProjects(); - useEffect(() => { - const canCenterMap = - filteredProjects !== undefined && - filteredProjects.length > 0 && - mapRef.current; - if (!canCenterMap) return; + useDebouncedEffect( + () => { + const map = mapRef.current; + const shouldCenterMap = + filteredProjects !== undefined && + filteredProjects.length > 0 && + (filteredProjects.length < 30 || + filteredProjects.length === projects?.length) && + map !== null; + if (!shouldCenterMap) return; - const validFeatures = getValidFeatures(filteredProjects); - if (validFeatures.length === 0) return; + const validFeatures = getValidFeatures(filteredProjects); + if (validFeatures.length === 0) return; + + const centroid = calculateCentroid(validFeatures); + if (!centroid.geometry) return; + + const currentCenter = map.getCenter(); + if (areMapCoordsEqual(currentCenter, centroid.geometry.coordinates)) + return; - const centroid = calculateCentroid(validFeatures); - if (centroid.geometry) centerMapOnCoordinates(mapRef, centroid.geometry.coordinates); - }, [filteredProjects]); + }, + 250, + [filteredProjects] + ); const shouldShowSingleProjectsView = singleProject !== null && props.page === 'project-details'; diff --git a/src/utils/projectV2.ts b/src/utils/projectV2.ts index 75a1b3858e..099d5d3d53 100644 --- a/src/utils/projectV2.ts +++ b/src/utils/projectV2.ts @@ -221,6 +221,26 @@ export const generateProjectLink = ( )}`; }; +/** + * Compares the coordinates of the map center with the centroid coordinates to determine if they are approximately equal. + * + * @param mapCenter - The center coordinates of the map, containing longitude (`lng`) and latitude (`lat`). Can be `undefined`. + * @param centroidCoords - The centroid coordinates as a tuple containing longitude and latitude. + * @param epsilon - The threshold for determining equality, representing a very small difference (default is 0.00009, which corresponds to approximately 10 meters at the Equator). + * @returns `true` if the coordinates are approximately equal within the specified threshold, otherwise `false`. + */ +export const areMapCoordsEqual = ( + mapCenter: { lng: number; lat: number } | undefined, + centroidCoords: Position, + epsilon = 0.00009 // Very small difference threshold corresponding to 10m at Equator +): boolean => { + if (mapCenter === undefined) return false; + return ( + Math.abs(mapCenter.lng - centroidCoords[0]) < epsilon && + Math.abs(mapCenter.lat - centroidCoords[1]) < epsilon + ); +}; + /** * Takes a relative path and returns a localized version with the correct locale prefix. * Query parameters are stripped from the input path. From 2a1c4d463bf578037c7a2e428280bb108f8117d6 Mon Sep 17 00:00:00 2001 From: mohitb35 <44917347+mohitb35@users.noreply.github.com> Date: Mon, 11 Nov 2024 12:52:01 +0530 Subject: [PATCH 09/14] fix: italicizes scientificName --- .../components/microComponents/PlantInfoCard.tsx | 4 +++- .../ProjectDetails/styles/PlantLocationInfo.module.scss | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/features/projectsV2/ProjectDetails/components/microComponents/PlantInfoCard.tsx b/src/features/projectsV2/ProjectDetails/components/microComponents/PlantInfoCard.tsx index 003e6df0e1..2b3fa20a3e 100644 --- a/src/features/projectsV2/ProjectDetails/components/microComponents/PlantInfoCard.tsx +++ b/src/features/projectsV2/ProjectDetails/components/microComponents/PlantInfoCard.tsx @@ -57,7 +57,9 @@ const PlantInfoCard = ({ })}
{scientificName && ( -
+

{tProjectDetails('scientificName')}

{scientificName}

diff --git a/src/features/projectsV2/ProjectDetails/styles/PlantLocationInfo.module.scss b/src/features/projectsV2/ProjectDetails/styles/PlantLocationInfo.module.scss index 695d38daa9..d7ef64f5c9 100644 --- a/src/features/projectsV2/ProjectDetails/styles/PlantLocationInfo.module.scss +++ b/src/features/projectsV2/ProjectDetails/styles/PlantLocationInfo.module.scss @@ -177,4 +177,8 @@ display: flex; flex-direction: column; gap: 12px; + + .scientificName .data { + font-style: italic; + } } From a6ed9e274067382a9d9511e54be883e7221b5562 Mon Sep 17 00:00:00 2001 From: mohitb35 <44917347+mohitb35@users.noreply.github.com> Date: Mon, 11 Nov 2024 13:12:37 +0530 Subject: [PATCH 10/14] fix: show locale formatted values for site area --- .../ProjectSiteDropDown/ProjectSiteList.tsx | 7 ++++++- .../ProjectsMap/ProjectSiteDropDown/index.tsx | 13 +++++++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/features/projectsV2/ProjectsMap/ProjectSiteDropDown/ProjectSiteList.tsx b/src/features/projectsV2/ProjectsMap/ProjectSiteDropDown/ProjectSiteList.tsx index 64e00cefa5..500633e98e 100644 --- a/src/features/projectsV2/ProjectsMap/ProjectSiteDropDown/ProjectSiteList.tsx +++ b/src/features/projectsV2/ProjectsMap/ProjectSiteDropDown/ProjectSiteList.tsx @@ -1,3 +1,5 @@ +import { useLocale } from 'next-intl'; +import { getFormattedRoundedNumber } from '../../../../utils/getFormattedNumber'; import type { SetState } from '../../../common/types/common'; import type { PlantLocation, @@ -27,6 +29,7 @@ const ProjectSiteList = ({ setSelectedPlantLocation, setSelectedSamplePlantLocation, }: ProjectSiteListProps) => { + const locale = useLocale(); const handleSiteSelection = (index: number) => { setSelectedPlantLocation(null); setSelectedSamplePlantLocation(null); @@ -46,7 +49,9 @@ const ProjectSiteList = ({ key={index} >

{site.siteName}

-

{Math.round(site.siteArea)}ha

+

+ {getFormattedRoundedNumber(locale, site.siteArea, 0)} ha +

); })} diff --git a/src/features/projectsV2/ProjectsMap/ProjectSiteDropDown/index.tsx b/src/features/projectsV2/ProjectsMap/ProjectSiteDropDown/index.tsx index 533d291465..3f45f911e8 100644 --- a/src/features/projectsV2/ProjectsMap/ProjectSiteDropDown/index.tsx +++ b/src/features/projectsV2/ProjectsMap/ProjectSiteDropDown/index.tsx @@ -6,7 +6,7 @@ import type { } from '../../../common/types/plantLocation'; import { useState, useMemo, useCallback } from 'react'; -import { useTranslations } from 'next-intl'; +import { useLocale, useTranslations } from 'next-intl'; import { useRouter } from 'next/router'; import { area } from '@turf/turf'; import SiteIcon from '../../../../../public/assets/images/icons/projectV2/SiteIcon'; @@ -15,6 +15,7 @@ import DropdownUpArrow from '../../../../temp/icons/DropdownUpArrow'; import DropdownDownArrow from '../../../../temp/icons/DropdownDownArrow'; import ProjectSiteList from './ProjectSiteList'; import { truncateString } from '../../../../utils/getTruncatedString'; +import { getFormattedRoundedNumber } from '../../../../utils/getFormattedNumber'; export interface SiteProperties { lastUpdated: { @@ -52,6 +53,7 @@ const ProjectSiteDropdown = ({ }: Props) => { const [isMenuOpen, setIsMenuOpen] = useState(false); const tProjectDetails = useTranslations('ProjectDetails'); + const locale = useLocale(); const router = useRouter(); const { query } = router; const siteList = useMemo(() => { @@ -93,7 +95,14 @@ const ProjectSiteDropdown = ({ })} - {Math.round(selectedSiteData?.siteArea)} ha + + {getFormattedRoundedNumber( + locale, + selectedSiteData?.siteArea, + 0 + )}{' '} + ha +

{truncateString(selectedSiteData?.siteName, 40)} From 2492c34b55ef443a47161ca845e441c9eb40ebb8 Mon Sep 17 00:00:00 2001 From: mohitb35 <44917347+mohitb35@users.noreply.github.com> Date: Mon, 11 Nov 2024 13:33:06 +0530 Subject: [PATCH 11/14] fix: enhance date formatting validation and error handling --- src/utils/countryCurrency/getFormattedDate.ts | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/utils/countryCurrency/getFormattedDate.ts b/src/utils/countryCurrency/getFormattedDate.ts index a0c59bbb27..57c41a63f2 100644 --- a/src/utils/countryCurrency/getFormattedDate.ts +++ b/src/utils/countryCurrency/getFormattedDate.ts @@ -11,8 +11,20 @@ export default function formatDate(date: number | Date | string) { if (typeof date === 'string') { // Clean up date string const cleanDateString = date.split(' ')[0]; // Remove time portion + if (!cleanDateString.includes('-')) { + return ''; + } // Zero-padding month and day if needed const [year, month, day] = cleanDateString.split('-'); + if (!year || !month || !day) { + return ''; + } + // Validate ranges + const monthNum = parseInt(month, 10); + const dayNum = parseInt(day, 10); + if (monthNum < 1 || monthNum > 12 || dayNum < 1 || dayNum > 31) { + return ''; + } const isoDateString = `${year}-${month.padStart(2, '0')}-${day.padStart( 2, '0' @@ -26,7 +38,10 @@ export default function formatDate(date: number | Date | string) { }); } } catch (error) { - console.log(error); + console.error('Date formatting failed:', { + input: date, + error: error instanceof Error ? error.message : String(error), + }); return ''; } } From 5196518e6baa2fb8ee0be3fc1e77b9fea193419b Mon Sep 17 00:00:00 2001 From: mohitb35 <44917347+mohitb35@users.noreply.github.com> Date: Mon, 11 Nov 2024 14:45:22 +0530 Subject: [PATCH 12/14] feat: format species tree count with locale-specific number formatting --- .../components/microComponents/SpeciesPlanted.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/features/projectsV2/ProjectDetails/components/microComponents/SpeciesPlanted.tsx b/src/features/projectsV2/ProjectDetails/components/microComponents/SpeciesPlanted.tsx index 82f0ee9139..ab8e465282 100644 --- a/src/features/projectsV2/ProjectDetails/components/microComponents/SpeciesPlanted.tsx +++ b/src/features/projectsV2/ProjectDetails/components/microComponents/SpeciesPlanted.tsx @@ -2,7 +2,8 @@ import type { PlantedSpecies } from '../../../../common/types/plantLocation'; import { useCallback } from 'react'; import styles from '../../styles/PlantLocationInfo.module.scss'; -import { useTranslations } from 'next-intl'; +import { useLocale, useTranslations } from 'next-intl'; +import { getFormattedNumber } from '../../../../../utils/getFormattedNumber'; interface Props { totalTreesCount: number; @@ -11,6 +12,7 @@ interface Props { const SpeciesPlanted = ({ totalTreesCount, plantedSpecies }: Props) => { const tProjectDetails = useTranslations('ProjectDetails'); + const locale = useLocale(); const getPlantedTreePercentage = useCallback( (treeCount: number) => { const result = (treeCount / totalTreesCount) * 100; @@ -33,7 +35,7 @@ const SpeciesPlanted = ({ totalTreesCount, plantedSpecies }: Props) => {

{species.scientificName}

-

{species.treeCount}

+

{getFormattedNumber(locale, species.treeCount)}

{`${getPlantedTreePercentage(species.treeCount)}%`}

From 269860844303397de3ac604bb60bcc293d164d6b Mon Sep 17 00:00:00 2001 From: mohitb35 <44917347+mohitb35@users.noreply.github.com> Date: Mon, 11 Nov 2024 14:55:27 +0530 Subject: [PATCH 13/14] fix: adjust mobile bottom positioning for satellite layer toggle & zoom controls --- src/features/projectsV2/ProjectsMap/ProjectsMap.module.scss | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/features/projectsV2/ProjectsMap/ProjectsMap.module.scss b/src/features/projectsV2/ProjectsMap/ProjectsMap.module.scss index beda0eadfb..d05ac62296 100644 --- a/src/features/projectsV2/ProjectsMap/ProjectsMap.module.scss +++ b/src/features/projectsV2/ProjectsMap/ProjectsMap.module.scss @@ -50,11 +50,11 @@ } .layerToggleAndroid { - bottom: 162px; + bottom: 182px; } .layerToggleIos { - bottom: 225px; + bottom: 240px; } .mapContainer { @@ -86,7 +86,7 @@ } .ios :global(.maplibregl-control-container .maplibregl-ctrl-bottom-right) { - bottom: 130px; + bottom: 165px; } // Plant Location Styles From 625587d059dc21ab9f91a69ddd999903c5a06e64 Mon Sep 17 00:00:00 2001 From: mohitb35 <44917347+mohitb35@users.noreply.github.com> Date: Tue, 12 Nov 2024 13:16:51 +0530 Subject: [PATCH 14/14] fix: ensure planting density is always passed to PlantingDetails component for multi-tree-registration --- .../ProjectDetails/components/MultiPlantLocationInfo.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/features/projectsV2/ProjectDetails/components/MultiPlantLocationInfo.tsx b/src/features/projectsV2/ProjectDetails/components/MultiPlantLocationInfo.tsx index 823422bd98..141f28c004 100644 --- a/src/features/projectsV2/ProjectDetails/components/MultiPlantLocationInfo.tsx +++ b/src/features/projectsV2/ProjectDetails/components/MultiPlantLocationInfo.tsx @@ -90,7 +90,7 @@ const MultiPlantLocationInfo = ({ , , isMultiTreeRegistration && plantLocationInfo.plantedSpecies.length > 0 && (