From 93f6f6795089c467bd9ee2ebacbf13b53b395b55 Mon Sep 17 00:00:00 2001 From: Ethan Tam Date: Thu, 21 Nov 2024 01:23:47 -0800 Subject: [PATCH 1/7] [feat] filtering using technology filter --- api/maps/AddMarkers.tsx | 32 +++++++++++++++++-------- components/Filter/index.tsx | 3 +++ components/FilterBar/index.tsx | 3 +++ components/MapViewScreen/index.tsx | 22 ++++++++++++++++- components/TechnologyDropdown/index.tsx | 7 +++++- 5 files changed, 55 insertions(+), 12 deletions(-) diff --git a/api/maps/AddMarkers.tsx b/api/maps/AddMarkers.tsx index 7b6460e..1b6dfd4 100644 --- a/api/maps/AddMarkers.tsx +++ b/api/maps/AddMarkers.tsx @@ -1,4 +1,4 @@ -import { useMemo, useState } from 'react'; +import { useEffect, useState } from 'react'; import ReactDOM from 'react-dom/client'; import { Cluster, MarkerClusterer } from '@googlemaps/markerclusterer'; import { useMap } from '@vis.gl/react-google-maps'; @@ -16,6 +16,8 @@ export default function AddMarker({ null, ); // track currently open modal + const [clusterer, setClusterer] = useState(null); + const map = useMap(); const handleMarkerClick = ( @@ -92,15 +94,19 @@ export default function AddMarker({ return mapZoom; }; - const clusterer = useMemo(() => { - if (!map) return null; + useEffect(() => { + if (!map || !projects) return; + + // clear previous cluster markers + if (clusterer) { + clusterer.clearMarkers(); + } const renderer = { render(cluster: Cluster) { const count = cluster.markers?.length ?? 0; const position = cluster.position; - // create a container for the custom icon const container = document.createElement('div'); const root = ReactDOM.createRoot(container); root.render(); @@ -112,22 +118,28 @@ export default function AddMarker({ }, }; - const setClusterer = new MarkerClusterer({ map, renderer }); + const newClusterer = new MarkerClusterer({ map, renderer }); - setClusterer.addListener('click', function (cluster: Cluster) { + // Set zoom behavior for clusters + newClusterer.addListener('click', (cluster: Cluster) => { const mapZoom = map.getZoom() ?? 0; const minZoom = getMinZoom(cluster, mapZoom); - if (mapZoom && mapZoom < minZoom) { - const idleListener = map.addListener('idle', function () { + const idleListener = map.addListener('idle', () => { map.setZoom(minZoom); idleListener.remove(); }); } }); - return setClusterer; - }, [map]); + setClusterer(newClusterer); + + // Cleanup on unmount or dependencies change + return () => { + newClusterer.clearMarkers(); + setClusterer(null); + }; + }, [map, projects]); return ( <> diff --git a/components/Filter/index.tsx b/components/Filter/index.tsx index f94a02d..198a008 100644 --- a/components/Filter/index.tsx +++ b/components/Filter/index.tsx @@ -16,6 +16,7 @@ interface FilterProps { selectedFilters: Filters; filterChangeHandlers: FilterChangeHandlers; handleButtonClick: (filter: FilterType) => void; + handleFilterButtonClick: () => void; } export default function Filter({ @@ -24,6 +25,7 @@ export default function Filter({ selectedFilters, filterChangeHandlers, handleButtonClick, + handleFilterButtonClick, }: FilterProps) { return ( @@ -36,6 +38,7 @@ export default function Filter({ icon={filter.icon} label={filter.label} currFilter={filter} + handleFilterButtonClick={handleFilterButtonClick} /> ) : filter.id === 'status' ? ( void; selectedFilters: Filters; setSelectedFilters: React.Dispatch>; + handleFilterButtonClick: () => void; }) => { const [activeFilter, setActiveFilter] = useState(null); @@ -52,6 +54,7 @@ export const FilterBar = ({ selectedFilters={selectedFilters} filterChangeHandlers={filterChangeHandlers} handleButtonClick={handleButtonClick} + handleFilterButtonClick={handleFilterButtonClick} /> ))} diff --git a/components/MapViewScreen/index.tsx b/components/MapViewScreen/index.tsx index 7e1969f..6cb0532 100644 --- a/components/MapViewScreen/index.tsx +++ b/components/MapViewScreen/index.tsx @@ -52,6 +52,25 @@ export default function MapViewScreen({ location: [], }); + // show projects based on selected filters + const handleFilterButtonClick = () => { + const { status, technology, projectSize, location } = selectedFilters; + console.log( + 'status: ', + status, + 'technology: ', + technology, + 'projectSize: ', + projectSize, + 'location: ', + location, + ); + const filteredProjects = projects?.filter(project => + technology.includes(project.renewable_energy_technology), + ); + setFilteredProjects(filteredProjects); + }; + const handleFilterChange = (filter: FilterType) => { console.log(filter); }; @@ -74,8 +93,9 @@ export default function MapViewScreen({ onFilterChange={handleFilterChange} selectedFilters={selectedFilters} setSelectedFilters={setSelectedFilters} + handleFilterButtonClick={handleFilterButtonClick} /> - + ); diff --git a/components/TechnologyDropdown/index.tsx b/components/TechnologyDropdown/index.tsx index a3058f6..59f57fe 100644 --- a/components/TechnologyDropdown/index.tsx +++ b/components/TechnologyDropdown/index.tsx @@ -35,6 +35,7 @@ interface TechnologyDropdownProps { icon: React.ReactNode; label: string; currFilter: FilterType; + handleFilterButtonClick: () => void; } export default function TechnologyDropdown({ @@ -44,6 +45,7 @@ export default function TechnologyDropdown({ icon, label, currFilter, + handleFilterButtonClick, }: TechnologyDropdownProps) { const filter = { categories: [ @@ -169,7 +171,10 @@ export default function TechnologyDropdown({ ))} ))} - + APPLY From 9c1dca32242bbb154250e4a56f91496656420829 Mon Sep 17 00:00:00 2001 From: Ethan Tam Date: Thu, 21 Nov 2024 16:43:51 -0800 Subject: [PATCH 2/7] [feat] search over filtered projects --- components/MapViewScreen/index.tsx | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/components/MapViewScreen/index.tsx b/components/MapViewScreen/index.tsx index 6cb0532..59bb969 100644 --- a/components/MapViewScreen/index.tsx +++ b/components/MapViewScreen/index.tsx @@ -51,6 +51,8 @@ export default function MapViewScreen({ projectSize: { min: 0, max: 0 }, location: [], }); + const [filteredWithoutSearch, setFilteredWithoutSearch] = + useState(projects); // show projects based on selected filters const handleFilterButtonClick = () => { @@ -69,21 +71,21 @@ export default function MapViewScreen({ technology.includes(project.renewable_energy_technology), ); setFilteredProjects(filteredProjects); + setFilteredWithoutSearch(filteredProjects); }; - const handleFilterChange = (filter: FilterType) => { - console.log(filter); - }; - + // search within filtered projects useEffect(() => { - let filtered: Project[] = []; - filtered = - projects?.filter(project => + const searchedProjects = + filteredWithoutSearch?.filter(project => project.project_name.toLowerCase().includes(searchTerm.toLowerCase()), - ) ?? null; + ) ?? []; + setFilteredProjects(searchedProjects); + }, [searchTerm, filteredWithoutSearch, setFilteredProjects]); - setFilteredProjects(filtered); - }, [projects, searchTerm, setFilteredProjects]); + const handleFilterChange = (filter: FilterType) => { + console.log(filter); + }; return ( <> From 6468f1725db8f02b92150be7f325aa47a1170c93 Mon Sep 17 00:00:00 2001 From: Ethan Tam Date: Fri, 22 Nov 2024 00:28:48 -0800 Subject: [PATCH 3/7] [feat] added clear filters function --- components/MapViewScreen/index.tsx | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/components/MapViewScreen/index.tsx b/components/MapViewScreen/index.tsx index 59bb969..276208c 100644 --- a/components/MapViewScreen/index.tsx +++ b/components/MapViewScreen/index.tsx @@ -67,6 +67,7 @@ export default function MapViewScreen({ 'location: ', location, ); + // add all filtering logic here const filteredProjects = projects?.filter(project => technology.includes(project.renewable_energy_technology), ); @@ -74,6 +75,18 @@ export default function MapViewScreen({ setFilteredWithoutSearch(filteredProjects); }; + // clear filters + // const clearFilters = () => { + // setSelectedFilters({ + // status: [], + // technology: [], + // projectSize: { min: 0, max: 0 }, + // location: [], + // }); + // setFilteredProjects(projects); + // setFilteredWithoutSearch(projects); + // }; + // search within filtered projects useEffect(() => { const searchedProjects = From 5f7bd3072ed63041221ac5ecba3ad131bfd23a15 Mon Sep 17 00:00:00 2001 From: Ethan Tam Date: Sat, 23 Nov 2024 15:54:41 -0800 Subject: [PATCH 4/7] added clear function, removed map updates on search --- components/Filter/index.tsx | 3 ++ components/FilterBar/index.tsx | 3 ++ components/MapViewScreen/index.tsx | 47 ++++++++++--------------- components/TechnologyDropdown/index.tsx | 13 ++++--- components/TechnologyDropdown/styles.ts | 6 ++++ 5 files changed, 40 insertions(+), 32 deletions(-) diff --git a/components/Filter/index.tsx b/components/Filter/index.tsx index 198a008..a95f799 100644 --- a/components/Filter/index.tsx +++ b/components/Filter/index.tsx @@ -17,6 +17,7 @@ interface FilterProps { filterChangeHandlers: FilterChangeHandlers; handleButtonClick: (filter: FilterType) => void; handleFilterButtonClick: () => void; + clearFilters: () => void; } export default function Filter({ @@ -26,6 +27,7 @@ export default function Filter({ filterChangeHandlers, handleButtonClick, handleFilterButtonClick, + clearFilters, }: FilterProps) { return ( @@ -39,6 +41,7 @@ export default function Filter({ label={filter.label} currFilter={filter} handleFilterButtonClick={handleFilterButtonClick} + clearFilters={clearFilters} /> ) : filter.id === 'status' ? ( void; selectedFilters: Filters; setSelectedFilters: React.Dispatch>; handleFilterButtonClick: () => void; + clearFilters: () => void; }) => { const [activeFilter, setActiveFilter] = useState(null); @@ -55,6 +57,7 @@ export const FilterBar = ({ filterChangeHandlers={filterChangeHandlers} handleButtonClick={handleButtonClick} handleFilterButtonClick={handleFilterButtonClick} + clearFilters={clearFilters} /> ))} diff --git a/components/MapViewScreen/index.tsx b/components/MapViewScreen/index.tsx index 276208c..23ef42a 100644 --- a/components/MapViewScreen/index.tsx +++ b/components/MapViewScreen/index.tsx @@ -51,50 +51,40 @@ export default function MapViewScreen({ projectSize: { min: 0, max: 0 }, location: [], }); - const [filteredWithoutSearch, setFilteredWithoutSearch] = + + const [filteredProjectsWithSearch, setFilteredProjectsWithSearch] = useState(projects); // show projects based on selected filters const handleFilterButtonClick = () => { + /* eslint-disable @typescript-eslint/no-unused-vars */ const { status, technology, projectSize, location } = selectedFilters; - console.log( - 'status: ', - status, - 'technology: ', - technology, - 'projectSize: ', - projectSize, - 'location: ', - location, - ); // add all filtering logic here const filteredProjects = projects?.filter(project => technology.includes(project.renewable_energy_technology), ); setFilteredProjects(filteredProjects); - setFilteredWithoutSearch(filteredProjects); }; // clear filters - // const clearFilters = () => { - // setSelectedFilters({ - // status: [], - // technology: [], - // projectSize: { min: 0, max: 0 }, - // location: [], - // }); - // setFilteredProjects(projects); - // setFilteredWithoutSearch(projects); - // }; + const clearFilters = () => { + setSelectedFilters({ + status: [], + technology: [], + projectSize: { min: 0, max: 0 }, + location: [], + }); + setFilteredProjects(projects); + }; - // search within filtered projects + // search within all projects useEffect(() => { - const searchedProjects = - filteredWithoutSearch?.filter(project => + const searchedProjects: Project[] = + projects?.filter(project => project.project_name.toLowerCase().includes(searchTerm.toLowerCase()), ) ?? []; - setFilteredProjects(searchedProjects); - }, [searchTerm, filteredWithoutSearch, setFilteredProjects]); + setFilteredProjectsWithSearch(searchedProjects); + }, [projects, searchTerm]); const handleFilterChange = (filter: FilterType) => { console.log(filter); @@ -109,9 +99,10 @@ export default function MapViewScreen({ selectedFilters={selectedFilters} setSelectedFilters={setSelectedFilters} handleFilterButtonClick={handleFilterButtonClick} + clearFilters={clearFilters} /> - + ); } diff --git a/components/TechnologyDropdown/index.tsx b/components/TechnologyDropdown/index.tsx index 59f57fe..b49c48c 100644 --- a/components/TechnologyDropdown/index.tsx +++ b/components/TechnologyDropdown/index.tsx @@ -25,6 +25,7 @@ import { ExitStyles, FilterContentDiv, FilterDropdownStyles, + FilterIconStyles, IconStyles, } from './styles'; @@ -36,6 +37,7 @@ interface TechnologyDropdownProps { label: string; currFilter: FilterType; handleFilterButtonClick: () => void; + clearFilters: () => void; } export default function TechnologyDropdown({ @@ -46,6 +48,7 @@ export default function TechnologyDropdown({ label, currFilter, handleFilterButtonClick, + clearFilters, }: TechnologyDropdownProps) { const filter = { categories: [ @@ -140,12 +143,14 @@ export default function TechnologyDropdown({ return ( - handleButtonClick(currFilter)}> - {icon} - + + handleButtonClick(currFilter)}> + {icon} + + handleButtonClick(currFilter)}> {label} - + diff --git a/components/TechnologyDropdown/styles.ts b/components/TechnologyDropdown/styles.ts index 89a5a57..97e2c61 100644 --- a/components/TechnologyDropdown/styles.ts +++ b/components/TechnologyDropdown/styles.ts @@ -72,6 +72,12 @@ export const ButtonWithIconStyles = styled.div` cursor: pointer; `; +export const FilterIconStyles = styled.div` + display: flex; + align-items: center; + flex-direction: row; +`; + export const IconStyles = styled.div` width: '3rem', height: '3rem', From eb902a13c34494d55056673a65dfc66b82a4cbdf Mon Sep 17 00:00:00 2001 From: Ethan Tam Date: Sat, 23 Nov 2024 23:55:14 -0800 Subject: [PATCH 5/7] [fix] fixed issues --- api/maps/AddMarkers.tsx | 21 ++++++---------- components/MapViewScreen/index.tsx | 39 ++++++++++++++++++++++-------- 2 files changed, 37 insertions(+), 23 deletions(-) diff --git a/api/maps/AddMarkers.tsx b/api/maps/AddMarkers.tsx index dcdceef..89c4e38 100644 --- a/api/maps/AddMarkers.tsx +++ b/api/maps/AddMarkers.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from 'react'; +import { useMemo, useState } from 'react'; import ReactDOM from 'react-dom/client'; import { Cluster, MarkerClusterer } from '@googlemaps/markerclusterer'; import { useMap } from '@vis.gl/react-google-maps'; @@ -16,8 +16,6 @@ export default function AddMarker({ null, ); // track currently open modal - const [clusterer, setClusterer] = useState(null); - const map = useMap(); const handleMarkerClick = ( @@ -36,18 +34,15 @@ export default function AddMarker({ setSelectedProjectId(null); // close modal }; - useEffect(() => { - if (!map || !projects) return; - - // clear previous cluster markers - if (clusterer) { - clusterer.clearMarkers(); - } + const clusterer = useMemo(() => { + if (!map) return null; const renderer = { render(cluster: Cluster) { const count = cluster.markers?.length ?? 0; const position = cluster.position; + + // create a container for the custom icon const container = document.createElement('div'); const root = ReactDOM.createRoot(container); root.render(); @@ -71,14 +66,14 @@ export default function AddMarker({ } }; - const newClusterer = new MarkerClusterer({ + const setClusterer = new MarkerClusterer({ map, renderer, onClusterClick: clusterHandler, }); - setClusterer(newClusterer); - }, [map, projects]); + return setClusterer; + }, [map]); return ( <> diff --git a/components/MapViewScreen/index.tsx b/components/MapViewScreen/index.tsx index 23ef42a..2ff914c 100644 --- a/components/MapViewScreen/index.tsx +++ b/components/MapViewScreen/index.tsx @@ -51,8 +51,10 @@ export default function MapViewScreen({ projectSize: { min: 0, max: 0 }, location: [], }); + const [filteredProjectsFromDropdowns, setFilteredProjectsFromDropdowns] = + useState(projects); - const [filteredProjectsWithSearch, setFilteredProjectsWithSearch] = + const [filteredProjectsFromSearch, setFilteredProjectsFromSearch] = useState(projects); // show projects based on selected filters @@ -60,10 +62,10 @@ export default function MapViewScreen({ /* eslint-disable @typescript-eslint/no-unused-vars */ const { status, technology, projectSize, location } = selectedFilters; // add all filtering logic here - const filteredProjects = projects?.filter(project => + const technologyProjects = projects?.filter(project => technology.includes(project.renewable_energy_technology), ); - setFilteredProjects(filteredProjects); + setFilteredProjectsFromDropdowns(technologyProjects); }; // clear filters @@ -77,17 +79,34 @@ export default function MapViewScreen({ setFilteredProjects(projects); }; - // search within all projects + // search within all projects or filtered projects from dropdowns useEffect(() => { + let projectsToSearch: Project[] = []; + + if (filteredProjectsFromDropdowns.length > 0) { + projectsToSearch = filteredProjectsFromDropdowns; + } else { + projectsToSearch = projects; + } + const searchedProjects: Project[] = - projects?.filter(project => + projectsToSearch?.filter(project => project.project_name.toLowerCase().includes(searchTerm.toLowerCase()), ) ?? []; - setFilteredProjectsWithSearch(searchedProjects); - }, [projects, searchTerm]); + + setFilteredProjectsFromSearch(searchedProjects); + }, [projects, searchTerm, filteredProjectsFromDropdowns]); + + useEffect(() => { + setFilteredProjects(filteredProjectsFromDropdowns); + }, [filteredProjectsFromDropdowns, setFilteredProjects]); + + useEffect(() => { + setFilteredProjects(filteredProjectsFromSearch); + }, [filteredProjectsFromSearch, setFilteredProjects]); const handleFilterChange = (filter: FilterType) => { - console.log(filter); + return; }; return ( @@ -101,8 +120,8 @@ export default function MapViewScreen({ handleFilterButtonClick={handleFilterButtonClick} clearFilters={clearFilters} /> - - + + ); } From 37eee59d65b9adb9401cbb8b0afe4b772b885152 Mon Sep 17 00:00:00 2001 From: Ethan Tam Date: Sat, 23 Nov 2024 23:58:59 -0800 Subject: [PATCH 6/7] [fix] fixed issues --- components/MapViewScreen/index.tsx | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/components/MapViewScreen/index.tsx b/components/MapViewScreen/index.tsx index 2ff914c..9fc46f6 100644 --- a/components/MapViewScreen/index.tsx +++ b/components/MapViewScreen/index.tsx @@ -57,17 +57,6 @@ export default function MapViewScreen({ const [filteredProjectsFromSearch, setFilteredProjectsFromSearch] = useState(projects); - // show projects based on selected filters - const handleFilterButtonClick = () => { - /* eslint-disable @typescript-eslint/no-unused-vars */ - const { status, technology, projectSize, location } = selectedFilters; - // add all filtering logic here - const technologyProjects = projects?.filter(project => - technology.includes(project.renewable_energy_technology), - ); - setFilteredProjectsFromDropdowns(technologyProjects); - }; - // clear filters const clearFilters = () => { setSelectedFilters({ @@ -79,6 +68,17 @@ export default function MapViewScreen({ setFilteredProjects(projects); }; + // show projects based on selected filters + const handleFilterButtonClick = () => { + /* eslint-disable @typescript-eslint/no-unused-vars */ + const { status, technology, projectSize, location } = selectedFilters; + // add all filtering logic here + const technologyProjects = projects?.filter(project => + technology.includes(project.renewable_energy_technology), + ); + setFilteredProjectsFromDropdowns(technologyProjects); + }; + // search within all projects or filtered projects from dropdowns useEffect(() => { let projectsToSearch: Project[] = []; From 2da013da0d0acb8e7ff1f16bdd876a7a864cdec7 Mon Sep 17 00:00:00 2001 From: Ethan Tam Date: Mon, 2 Dec 2024 01:43:41 -0800 Subject: [PATCH 7/7] [fix] fixed bug --- components/FilterBar/index.tsx | 3 --- components/MapViewScreen/index.tsx | 6 +----- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/components/FilterBar/index.tsx b/components/FilterBar/index.tsx index 805d048..e04b1cb 100644 --- a/components/FilterBar/index.tsx +++ b/components/FilterBar/index.tsx @@ -5,14 +5,12 @@ import { FilterContainerStyles } from './styles'; export const FilterBar = ({ filters, - onFilterChange, selectedFilters, setSelectedFilters, handleFilterButtonClick, clearFilters, }: { filters: FilterType[]; - onFilterChange: (filter: FilterType) => void; selectedFilters: Filters; setSelectedFilters: React.Dispatch>; handleFilterButtonClick: () => void; @@ -22,7 +20,6 @@ export const FilterBar = ({ const handleButtonClick = (filter: FilterType) => { setActiveFilter(activeFilter?.id === filter.id ? null : filter); - onFilterChange(filter); }; const handleTechnologyChange = (options: string[]) => { diff --git a/components/MapViewScreen/index.tsx b/components/MapViewScreen/index.tsx index 9501e10..833a133 100644 --- a/components/MapViewScreen/index.tsx +++ b/components/MapViewScreen/index.tsx @@ -71,6 +71,7 @@ export default function MapViewScreen({ location: [], }); setFilteredProjects(projects); + setFilteredProjectsFromDropdowns(projects); }; // show projects based on selected filters @@ -110,16 +111,11 @@ export default function MapViewScreen({ setFilteredProjects(filteredProjectsFromSearch); }, [filteredProjectsFromSearch, setFilteredProjects]); - const handleFilterChange = (filter: FilterType) => { - return; - }; - return ( <>