From 618447f046973c14574c278ca82102dab1b9d0a7 Mon Sep 17 00:00:00 2001 From: Paulo Gomes da Cruz Junior Date: Fri, 28 Feb 2025 16:02:55 -0800 Subject: [PATCH 1/2] chore(554): adjusting map polygon error message --- frontend/src/components/OpeningsMap/index.tsx | 118 +++++++---- .../src/components/RecentOpenings/index.tsx | 200 +++++++++--------- .../src/components/RecentOpenings/styles.scss | 1 + 3 files changed, 177 insertions(+), 142 deletions(-) diff --git a/frontend/src/components/OpeningsMap/index.tsx b/frontend/src/components/OpeningsMap/index.tsx index 17630d9c..e76e1321 100644 --- a/frontend/src/components/OpeningsMap/index.tsx +++ b/frontend/src/components/OpeningsMap/index.tsx @@ -1,27 +1,27 @@ -import React, { useEffect, useState } from 'react'; -import { OpeningPolygon } from '../../types/OpeningPolygon'; -import { MapLayer } from '../../types/MapLayer'; -import { allLayers } from './constants'; -import axios from 'axios'; -import { getAuthIdToken } from '../../services/AuthService'; -import { env } from '../../env'; -import { convertGeoJsonToLatLng } from '../../map-services/BcGwLatLongUtils'; +import React, { useEffect, useState } from "react"; +import { OpeningPolygon } from "../../types/OpeningPolygon"; +import { MapLayer } from "../../types/MapLayer"; +import { allLayers } from "./constants"; +import axios from "axios"; +import { getAuthIdToken } from "../../services/AuthService"; +import { env } from "../../env"; +import { convertGeoJsonToLatLng } from "../../map-services/BcGwLatLongUtils"; import { LayersControl, MapContainer, TileLayer, - WMSTileLayer -} from 'react-leaflet'; -import { LatLngExpression } from 'leaflet'; + WMSTileLayer, +} from "react-leaflet"; +import { LatLngExpression } from "leaflet"; -import OpeningsMapEntry from '../OpeningsMapEntry'; +import OpeningsMapEntry from "../OpeningsMapEntry"; const backendUrl = env.VITE_BACKEND_URL; interface MapProps { openingIds: number[] | null; openingId: number | null; - setOpeningPolygonNotFound: (value: boolean) => void; + setOpeningPolygonNotFound: (value: boolean, openingId: number | null) => void; mapHeight?: number; } @@ -29,23 +29,27 @@ const OpeningsMap: React.FC = ({ openingIds, openingId, setOpeningPolygonNotFound, - mapHeight = 400 + mapHeight = 400, }) => { const [selectedOpeningIds, setSelectedOpeningIds] = useState([]); const [openings, setOpenings] = useState([]); - const [position, setPosition] = useState([48.43737, -123.35883]); + const [position, setPosition] = useState([ + 48.43737, -123.35883, + ]); const [layers, setLayers] = useState([]); const authToken = getAuthIdToken(); const [zoomLevel, setZoomLevel] = useState(13); - const getOpeningPolygonAndProps = async (selectedOpeningId: number | null): Promise => { + const getOpeningPolygonAndProps = async ( + selectedOpeningId: number | null + ): Promise => { const urlApi = `/api/feature-service/polygon-and-props/${selectedOpeningId}`; const response = await axios.get(backendUrl.concat(urlApi), { headers: { - 'Content-Type': 'application/json', - 'Access-Control-Allow-Origin': window.location.origin, - Authorization: `Bearer ${authToken}` - } + "Content-Type": "application/json", + "Access-Control-Allow-Origin": window.location.origin, + Authorization: `Bearer ${authToken}`, + }, }); const { data } = response; @@ -54,7 +58,9 @@ const OpeningsMap: React.FC = ({ const openingsList: OpeningPolygon[] = []; for (let i = 0, len = data.features.length; i < len; i++) { if (data.features[i].geometry) { - const openingGeometry = convertGeoJsonToLatLng(data.features[i].geometry.coordinates); + const openingGeometry = convertGeoJsonToLatLng( + data.features[i].geometry.coordinates + ); const openingObj: OpeningPolygon = { key: data.features[i].id, @@ -62,9 +68,11 @@ const OpeningsMap: React.FC = ({ properties: data.features[i].properties, id: data.features[i].id, positionLat: (data.bbox[1] + data.bbox[3]) / 2, - positionLong: (data.bbox[0] + data.bbox[2]) / 2 + positionLong: (data.bbox[0] + data.bbox[2]) / 2, }; openingsList.push(openingObj); + } else { + setOpeningPolygonNotFound(true, selectedOpeningId); } } @@ -72,12 +80,16 @@ const OpeningsMap: React.FC = ({ if (openingsList.length) { return openingsList[0]; } + } else { + setOpeningPolygonNotFound(true, selectedOpeningId); } return null; }; - const callBcGwApi = async (currentOpeningId: number): Promise => { + const callBcGwApi = async ( + currentOpeningId: number + ): Promise => { return await getOpeningPolygonAndProps(currentOpeningId); }; @@ -86,43 +98,54 @@ const OpeningsMap: React.FC = ({ const options = { enableHighAccuracy: true, timeout: 5000, - maximumAge: 0 + maximumAge: 0, }; const requestCurrentLocation = () => { - navigator.geolocation.getCurrentPosition((currentPosition: GeolocationPosition) => { - setPosition({ lat: currentPosition.coords.latitude, lng: currentPosition.coords.longitude }); - setZoomLevel(8); - }, () => { - setPosition({ lat: 51.339506220208065, lng: -121.40991210937501 }); - setZoomLevel(6); - }, options); + navigator.geolocation.getCurrentPosition( + (currentPosition: GeolocationPosition) => { + setPosition({ + lat: currentPosition.coords.latitude, + lng: currentPosition.coords.longitude, + }); + setZoomLevel(8); + }, + () => { + setPosition({ lat: 51.339506220208065, lng: -121.40991210937501 }); + setZoomLevel(6); + }, + options + ); }; - const permissionResult = await navigator.permissions.query({ name: 'geolocation' }); - if (permissionResult.state === "granted" || permissionResult.state === "prompt") { + const permissionResult = await navigator.permissions.query({ + name: "geolocation", + }); + if ( + permissionResult.state === "granted" || + permissionResult.state === "prompt" + ) { requestCurrentLocation(); } - } - }; - const loadOpeniningPolygons = async (providedIds: number[]): Promise => { - setOpeningPolygonNotFound(false); + const loadOpeniningPolygons = async ( + providedIds: number[] + ): Promise => { + setOpeningPolygonNotFound(false, null); if (providedIds?.length) { const results = await Promise.all(providedIds.map(callBcGwApi)); setOpenings(results.filter((opening) => opening !== null)); - } else { setOpenings([]); } - const filtered = allLayers.filter(l => l.name.length > 0); + const filtered = allLayers.filter((l) => l.name.length > 0); if (filtered.length) { setLayers(filtered); } - } + }; useEffect(() => { setSelectedOpeningIds(openingId ? [openingId] : []); @@ -142,13 +165,15 @@ const OpeningsMap: React.FC = ({ } }, [openingIds]); - useEffect(() => { loadOpeniningPolygons(selectedOpeningIds); }, [selectedOpeningIds]); + useEffect(() => { + loadOpeniningPolygons(selectedOpeningIds); + }, [selectedOpeningIds]); useEffect(() => { (async () => { await getUserLocation(); })(); - }, []) + }, []); return ( = ({ /> {/* Display Opening polygons, if any */} - + {/* Default layers */} {layers.length && ( @@ -176,14 +205,13 @@ const OpeningsMap: React.FC = ({ format: layer.format, layers: layer.layers, transparent: layer.transparent, - styles: layer.styles.map(s => s.name).join(',') + styles: layer.styles.map((s) => s.name).join(","), }} /> ))} )} - ); }; diff --git a/frontend/src/components/RecentOpenings/index.tsx b/frontend/src/components/RecentOpenings/index.tsx index f01306da..46bbee51 100644 --- a/frontend/src/components/RecentOpenings/index.tsx +++ b/frontend/src/components/RecentOpenings/index.tsx @@ -1,35 +1,52 @@ -import React, { useState } from 'react'; -import { Button, InlineNotification, Table, TableBody, TableHead, TableHeader, TableRow } from '@carbon/react'; -import './styles.scss'; -import { Location } from '@carbon/icons-react'; -import OpeningsMap from '../OpeningsMap'; -import SectionTitle from '../SectionTitle'; -import useBreakpoint from '../../hooks/UseBreakpoint'; -import { useQuery } from '@tanstack/react-query'; -import { fetchUserRecentOpenings } from '../../services/OpeningService'; -import TableSkeleton from '../TableSkeleton'; -import { recentOpeningsHeaders } from './constants'; -import EmptySection from '../EmptySection'; -import OpeningRow from './OpeningRow'; -import ComingSoonModal from '../ComingSoonModal'; +import React, { useState } from "react"; +import { + Button, + InlineNotification, + Table, + TableBody, + TableHead, + TableHeader, + TableRow, +} from "@carbon/react"; +import "./styles.scss"; +import { Location } from "@carbon/icons-react"; +import OpeningsMap from "../OpeningsMap"; +import SectionTitle from "../SectionTitle"; +import useBreakpoint from "../../hooks/UseBreakpoint"; +import { useQuery } from "@tanstack/react-query"; +import { fetchUserRecentOpenings } from "../../services/OpeningService"; +import TableSkeleton from "../TableSkeleton"; +import { recentOpeningsHeaders } from "./constants"; +import EmptySection from "../EmptySection"; +import OpeningRow from "./OpeningRow"; +import ComingSoonModal from "../ComingSoonModal"; const RecentOpenings = () => { const [showMap, setShowMap] = useState(false); const [selectedOpeningIds, setSelectedOpeningIds] = useState([]); - const [openingPolygonNotFound, setOpeningPolygonNotFound] = useState(false); + const [openingPolygonNotFound, setOpeningPolygonNotFound] = + useState(false); + const [faultyOpeningPolygonId, setFaultyOpeningPolygonId] = useState< + number | null + >(null); const [openingDetails, setOpeningDetails] = useState(""); const breakpoint = useBreakpoint(); const recentOpeningsQuery = useQuery({ queryKey: ["opening", "recent"], queryFn: () => fetchUserRecentOpenings(), - refetchOnMount: "always" + refetchOnMount: "always", }); const toggleMap = () => { setShowMap(!showMap); }; + const handleMapError = (value: boolean, openingId: number | null) => { + setOpeningPolygonNotFound(value); + setFaultyOpeningPolygonId(openingId); + }; + /** * Toggles the selection of an opening ID. * If the ID is already selected, it is removed; otherwise, it is added. @@ -38,15 +55,15 @@ const RecentOpenings = () => { */ const handleRowSelection = (id: number) => { setSelectedOpeningIds((prev) => - prev.includes(id) ? prev.filter((openingId) => openingId !== id) : [...prev, id] + prev.includes(id) + ? prev.filter((openingId) => openingId !== id) + : [...prev, id] ); }; return (
-
+
{ className="map-button" renderIcon={Location} type="button" - size={breakpoint === 'sm' ? 'sm' : 'lg'} + size={breakpoint === "sm" ? "sm" : "lg"} onClick={toggleMap} disabled={!recentOpeningsQuery.data?.data.length} > - {showMap ? 'Hide map' : 'Show map'} + {showMap ? "Hide map" : "Show map"}
- { - showMap - ? ( - - ) - : null - } - { - openingPolygonNotFound - ? ( - - ) - : null - } + {openingPolygonNotFound && ( + + )} + {showMap && ( + + )} + {/* Table skeleton */} - { - recentOpeningsQuery.isLoading - ? - : null - } + {recentOpeningsQuery.isLoading ? ( + + ) : null} {/* Empty Table */} - { - !recentOpeningsQuery.isLoading && !recentOpeningsQuery.data?.data.length ? ( - - ) - : null - } + {!recentOpeningsQuery.isLoading && + !recentOpeningsQuery.data?.data.length ? ( + + ) : null} {/* Loaded table content */} - { - !recentOpeningsQuery.isLoading && recentOpeningsQuery.data?.data.length ? - ( - - - - { - recentOpeningsHeaders.map((header) => ( - {header.header} - )) - } - - - - { - recentOpeningsQuery.data?.data.map((row) => ( - - )) - } - -
- ) - : null - } + {!recentOpeningsQuery.isLoading && + recentOpeningsQuery.data?.data.length ? ( + + + + {recentOpeningsHeaders.map((header) => ( + {header.header} + ))} + + + + {recentOpeningsQuery.data?.data.map((row) => ( + + ))} + +
+ ) : null} Date: Fri, 28 Feb 2025 16:17:49 -0800 Subject: [PATCH 2/2] chore(554): updating function call --- .../Openings/OpeningsSearchTab/index.tsx | 46 ++++++++++++------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/frontend/src/components/SilvicultureSearch/Openings/OpeningsSearchTab/index.tsx b/frontend/src/components/SilvicultureSearch/Openings/OpeningsSearchTab/index.tsx index 95616d11..d0584884 100644 --- a/frontend/src/components/SilvicultureSearch/Openings/OpeningsSearchTab/index.tsx +++ b/frontend/src/components/SilvicultureSearch/Openings/OpeningsSearchTab/index.tsx @@ -23,7 +23,11 @@ import PaginationContext from "../../../../contexts/PaginationContext"; const OpeningsSearchTab: React.FC = () => { const [showSpatial, setShowSpatial] = useState(false); - const [openingPolygonNotFound, setOpeningPolygonNotFound] = useState(false); + const [openingPolygonNotFound, setOpeningPolygonNotFound] = + useState(false); + const [faultyOpeningPolygonId, setFaultyOpeningPolygonId] = useState< + number | null + >(null); const [filtersApplied, setFiltersApplied] = useState(false); const [searchParams, setSearchParams] = useState>({}); const [finalParams, setFinalParams] = useState>({}); // Store params for query after search @@ -43,20 +47,29 @@ const OpeningsSearchTab: React.FC = () => { setShowSpatial(!showSpatial); }; + const handleMapError = (value: boolean, openingId: number | null) => { + setOpeningPolygonNotFound(value); + setFaultyOpeningPolygonId(openingId); + }; + const toggleFiltersApplied = () => { setFiltersApplied(!filtersApplied); }; const hasFilters = countActiveFilters(filters) > 0 || searchTerm.length > 0; - const handleSearch = () => { setIsNoFilterSearch(!hasFilters); if (hasFilters) { toggleFiltersApplied(); setFiltersApplied(true); // Set filters as applied to show results - setFinalParams({ ...searchParams, ...filters, page: currentPage, perPage: itemsPerPage }); // Only update finalParams on search + setFinalParams({ + ...searchParams, + ...filters, + page: currentPage, + perPage: itemsPerPage, + }); // Only update finalParams on search setIsSearchTriggered(true); // Trigger the search } }; @@ -66,7 +79,7 @@ const OpeningsSearchTab: React.FC = () => { ...prevParams, ...filters, page: currentPage, - perPage: itemsPerPage + perPage: itemsPerPage, })); }; @@ -74,33 +87,31 @@ const OpeningsSearchTab: React.FC = () => { setFinalParams((prevParams) => ({ ...prevParams, page: currentPage, - perPage: itemsPerPage + perPage: itemsPerPage, })); - } + }; const handleSearchInputChange = (searchInput: string) => { setSearchParams((prevParams) => ({ ...prevParams, searchInput, page: 1, //going back to page 1 when the user clicks to make another search - perPage: itemsPerPage + perPage: itemsPerPage, })); }; const handleCheckboxChange = (columnKey: string) => { if (columnKey === "select-default") { //set to the deafult - setHeaders(searchScreenColumns) - } - else if (columnKey === "select-all") { + setHeaders(searchScreenColumns); + } else if (columnKey === "select-all") { setHeaders((prevHeaders) => prevHeaders.map((header) => ({ ...header, selected: true, // Select all headers })) ); - } - else { + } else { setHeaders((prevHeaders) => prevHeaders.map((header) => header.key === columnKey @@ -109,7 +120,6 @@ const OpeningsSearchTab: React.FC = () => { ) ); } - }; useEffect(() => { @@ -143,7 +153,7 @@ const OpeningsSearchTab: React.FC = () => { hasParams = true; setFilters((prevFilters: OpeningFilters) => ({ ...prevFilters, - [key]: urlParams.get(key) + [key]: urlParams.get(key), })); } }); @@ -154,7 +164,6 @@ const OpeningsSearchTab: React.FC = () => { } setHasExternalParams(hasParams); - }, []); return ( @@ -165,12 +174,15 @@ const OpeningsSearchTab: React.FC = () => { onSearchClick={handleSearch} /> {showSpatial ? ( -
+