From dcf0cce46204010b26e791dd8230ce0853257206 Mon Sep 17 00:00:00 2001 From: Ahmad Azizi <91204996+its-aazizi@users.noreply.github.com> Date: Wed, 6 Nov 2024 06:50:11 +0500 Subject: [PATCH] updated place popup with opening hours, websites and phone numbers --- src/assets/svgs/icon-clock.svg | 3 + src/assets/svgs/icon-phone.svg | 3 + src/assets/svgs/index.ts | 2 + src/atomicui/molecules/Popup/Popup.test.tsx | 18 +- src/atomicui/molecules/Popup/Popup.tsx | 167 +++++++++++++++--- src/atomicui/molecules/Popup/styles.scss | 80 +++++++++ .../SuggestionMarker/SuggestionMarker.tsx | 4 +- src/services/usePlaceService.ts | 3 +- 8 files changed, 240 insertions(+), 40 deletions(-) create mode 100644 src/assets/svgs/icon-clock.svg create mode 100644 src/assets/svgs/icon-phone.svg diff --git a/src/assets/svgs/icon-clock.svg b/src/assets/svgs/icon-clock.svg new file mode 100644 index 00000000..5c089c05 --- /dev/null +++ b/src/assets/svgs/icon-clock.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/svgs/icon-phone.svg b/src/assets/svgs/icon-phone.svg new file mode 100644 index 00000000..e5dcba3a --- /dev/null +++ b/src/assets/svgs/icon-phone.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/svgs/index.ts b/src/assets/svgs/index.ts index 25abdd69..caf781e4 100644 --- a/src/assets/svgs/index.ts +++ b/src/assets/svgs/index.ts @@ -124,3 +124,5 @@ export { ReactComponent as IconCustomers } from "./icon-customers.svg"; export { ReactComponent as IconLight } from "./icon-light.svg"; export { ReactComponent as IconDark } from "./icon-dark.svg"; export { ReactComponent as IconFaqsPrimary } from "./icon-faqs-primary.svg"; +export { ReactComponent as IconClock } from "./icon-clock.svg"; +export { ReactComponent as IconPhone } from "./icon-phone.svg"; diff --git a/src/atomicui/molecules/Popup/Popup.test.tsx b/src/atomicui/molecules/Popup/Popup.test.tsx index 8ac1da11..1e439744 100644 --- a/src/atomicui/molecules/Popup/Popup.test.tsx +++ b/src/atomicui/molecules/Popup/Popup.test.tsx @@ -28,14 +28,18 @@ const useMapReturnValue: { error: null }, viewpoint: { longitude: -122.3408586, latitude: 47.6149975 }, - mapProvider: "Esri", + mapProvider: "Here", mapUnit: "Imperial", isCurrentLocationDisabled: false }; jest.mock("hooks", () => ({ useMap: () => useMapReturnValue, - usePlace: () => ({}), + usePlace: () => ({ + getPlaceData: jest.fn(), + isFetchingPlaceData: false, + clearPoiList: jest.fn() + }), useRoute: () => ({ getRoute: () => {} }), @@ -51,14 +55,10 @@ describe("", () => { const renderedComponent = render( diff --git a/src/atomicui/molecules/Popup/Popup.tsx b/src/atomicui/molecules/Popup/Popup.tsx index cf6740d8..799bff7b 100644 --- a/src/atomicui/molecules/Popup/Popup.tsx +++ b/src/atomicui/molecules/Popup/Popup.tsx @@ -3,15 +3,27 @@ import { FC, memo, useCallback, useEffect, useMemo, useRef, useState } from "react"; -import { Button, Flex, Placeholder, Text, View } from "@aws-amplify/ui-react"; +import { Button, Flex, Link, Placeholder, Text, View } from "@aws-amplify/ui-react"; +import { GetPlaceCommandOutput } from "@aws-sdk/client-geo-places"; import { CalculateRoutesCommandInput, CalculateRoutesCommandOutput } from "@aws-sdk/client-geo-routes"; -import { IconCar, IconClose, IconCopyPages, IconDirections, IconInfo } from "@demo/assets/svgs"; +import { + IconArrow, + IconCar, + IconClock, + IconClose, + IconCopyPages, + IconDirections, + IconGlobe, + IconInfo, + IconPhone +} from "@demo/assets/svgs"; import BottomSheetHeights from "@demo/core/constants/bottomSheetHeights"; import { useMap, usePlace, useRoute } from "@demo/hooks"; import useBottomSheet from "@demo/hooks/useBottomSheet"; import useDeviceMediaQuery from "@demo/hooks/useDeviceMediaQuery"; -import { DistanceUnitEnum, MapUnitEnum, SuggestionType, TravelMode } from "@demo/types"; +import { DistanceUnitEnum, MapUnitEnum, TravelMode } from "@demo/types"; import { ResponsiveUIEnum, TriggeredByEnum } from "@demo/types/Enums"; +import { uuid } from "@demo/utils"; import { humanReadableTime } from "@demo/utils/dateTimeUtils"; import { calculateGeodesicDistance } from "@demo/utils/geoCalculation"; import { Units } from "@turf/turf"; @@ -24,19 +36,22 @@ const { METRIC } = MapUnitEnum; const { KILOMETERS, MILES } = DistanceUnitEnum; interface PopupProps { + placeId: string; + position: number[]; + label?: string; active: boolean; - info: SuggestionType; select: (id?: string) => Promise; onClosePopUp?: () => void; } -const Popup: FC = ({ active, info, select, onClosePopUp }) => { +const Popup: FC = ({ placeId, position, label, active, select, onClosePopUp }) => { const [isLoading, setIsLoading] = useState(true); const [routeData, setRouteData] = useState(); + const [placeData, setPlaceData] = useState(undefined); + const [isExpanded, setIsExpanded] = useState(false); const { setPOICard, setBottomSheetMinHeight, setBottomSheetHeight, setUI, bottomSheetHeight, ui } = useBottomSheet(); const { currentLocationData, viewpoint, mapUnit } = useMap(); - const { clearPoiList } = usePlace(); + const { getPlaceData, isFetchingPlaceData, clearPoiList } = usePlace(); const { getRoute, setDirections, isFetchingRoute } = useRoute(); - const [longitude, latitude] = useMemo(() => info.position as number[], [info]); const { isDesktop } = useDeviceMediaQuery(); const { t, i18n } = useTranslation(); const currentLang = i18n.language; @@ -45,6 +60,15 @@ const Popup: FC = ({ active, info, select, onClosePopUp }) => { const isLanguageRTL = ["ar", "he"].includes(currentLang); const POICardRef = useRef(null); + useEffect(() => { + (async () => { + if (!placeData) { + const pd = await getPlaceData(placeId); + setPlaceData(pd); + } + })(); + }, [placeData, getPlaceData, placeId]); + const geodesicDistance = useMemo( () => calculateGeodesicDistance( @@ -54,10 +78,10 @@ const Popup: FC = ({ active, info, select, onClosePopUp }) => { currentLocationData.currentLocation.latitude as number ] : [viewpoint.longitude, viewpoint.latitude], - [longitude, latitude], + position, mapUnit === METRIC ? (KILOMETERS.toLowerCase() as Units) : (MILES.toLowerCase() as Units) ), - [viewpoint, currentLocationData, longitude, latitude, mapUnit] + [viewpoint, currentLocationData, position, mapUnit] ); const localizeGeodesicDistance = useMemo(() => { @@ -89,7 +113,7 @@ const Popup: FC = ({ active, info, select, onClosePopUp }) => { currentLocationData?.currentLocation?.longitude, currentLocationData?.currentLocation?.latitude ] as number[], - Destination: [longitude, latitude], + Destination: position, TravelMode: TravelMode.CAR }; try { @@ -101,7 +125,7 @@ const Popup: FC = ({ active, info, select, onClosePopUp }) => { } })(); } - }, [active, currentLocationData?.currentLocation, geodesicDistance, getRoute, latitude, longitude, routeData]); + }, [active, currentLocationData, geodesicDistance, getRoute, position, routeData]); const onClose = useCallback( async (ui: ResponsiveUIEnum) => { @@ -123,13 +147,29 @@ const Popup: FC = ({ active, info, select, onClosePopUp }) => { ); const onGetDirections = useCallback(() => { - setDirections(info); + setDirections({ + id: uuid.randomUUID(), + placeId, + position, + label, + address: placeData?.Address + }); clearPoiList(); if (!isDesktop) { onClose(ResponsiveUIEnum.direction_to_routes); } - }, [clearPoiList, info, isDesktop, onClose, setDirections]); + }, [clearPoiList, isDesktop, label, onClose, placeData, placeId, position, setDirections]); + + const address = useMemo(() => { + if (label) { + const split = label.split(","); + split.shift(); + return split.join(",").trim(); + } else { + return `${position[1]}, ${position[0]}`; + } + }, [label, position]); const renderRouteInfo = useMemo(() => { if (currentLocationData?.error) { @@ -206,15 +246,84 @@ const Popup: FC = ({ active, info, select, onClosePopUp }) => { t ]); - const address = useMemo(() => { - if (info?.address?.Label) { - const split = info.address.Label.split(","); - split.shift(); - return split.join(",").trim(); - } else { - return `${latitude}, ${longitude}`; - } - }, [info, latitude, longitude]); + const renderPlaceInfo = useMemo(() => { + const openingHours = placeData?.OpeningHours?.map(oh => oh.Display); + const areOpeningHoursPresent = openingHours && openingHours.every(oh => oh !== undefined); + const websites = placeData?.Contacts?.Websites?.map(w => w.Value); + const areWebsitesPresent = websites && websites.every(w => w !== undefined); + const phones = placeData?.Contacts?.Phones?.map(p => p.Value); + const arePhonesPresent = phones && phones.every(p => p !== undefined); + const renderNull = !areOpeningHoursPresent && !areWebsitesPresent && !arePhonesPresent; + + return isFetchingPlaceData ? ( + + + + + + + + + + + + + + + + + ) : renderNull ? null : ( + + {areOpeningHoursPresent && ( + + setIsExpanded(!isExpanded)}> + + + Schedule + + + + + {isExpanded && ( + + {openingHours.map((oh, idx) => ( + + {oh} + + ))} + + )} + + )} + {areWebsitesPresent && ( + + + + {websites.map((w, idx) => ( + + + {w} + + + ))} + + + )} + {arePhonesPresent && ( + + + + {phones.map((p, idx) => ( + + {p} + + ))} + + + )} + + ); + }, [isExpanded, isFetchingPlaceData, placeData]); const POIBody = useCallback( () => ( @@ -234,7 +343,7 @@ const Popup: FC = ({ active, info, select, onClosePopUp }) => { )} {`${ - info.address?.Label?.split(",")[0] + label?.split(",")[0] }`} @@ -244,11 +353,12 @@ const Popup: FC = ({ active, info, select, onClosePopUp }) => { navigator.clipboard.writeText(`${info.address?.Label?.split(",")[0]}` + ", " + address)} + onClick={() => navigator.clipboard.writeText(`${label?.split(",")[0]}` + ", " + address)} /> )} {renderRouteInfo} + {renderPlaceInfo}