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}
),
- [address, info, isDesktop, onClose, onGetDirections, renderRouteInfo, t]
+ [isDesktop, label, address, renderRouteInfo, renderPlaceInfo, onGetDirections, t, onClose]
);
useEffect(() => {
@@ -277,8 +387,7 @@ const Popup: FC = ({ active, info, select, onClosePopUp }) => {
}
}, [
POIBody,
- latitude,
- longitude,
+ position,
isDesktop,
setBottomSheetHeight,
setBottomSheetMinHeight,
@@ -297,8 +406,8 @@ const Popup: FC = ({ active, info, select, onClosePopUp }) => {
anchor={isDesktop ? "left" : "bottom"}
offset={active ? 27 : 22}
style={{ maxWidth: isDesktop ? "28.62rem" : "20rem", width: "100%" }}
- longitude={longitude as number}
- latitude={latitude as number}
+ longitude={position[0]}
+ latitude={position[1]}
>
diff --git a/src/atomicui/molecules/Popup/styles.scss b/src/atomicui/molecules/Popup/styles.scss
index 478d8b0d..4e7e15ce 100644
--- a/src/atomicui/molecules/Popup/styles.scss
+++ b/src/atomicui/molecules/Popup/styles.scss
@@ -102,6 +102,86 @@
}
}
+ .place-info-container {
+ gap: 0;
+ margin-top: 0.62rem;
+ border-top: 1px solid var(--grey-color-3);
+ flex-direction: column;
+
+ .icon {
+ width: 100%;
+ max-width: 1.23rem;
+ height: 100%;
+ max-height: 1.23rem;
+ margin-right: 0.62rem;
+ fill: var(--grey-color);
+ }
+
+ .opening-hours {
+ gap: 0;
+ margin-top: 0.62rem;
+ flex-direction: column;
+
+ .accordion-header {
+ gap: 0;
+ align-items: center;
+
+ .spacer {
+ gap: 0;
+ flex-grow: 1;
+ }
+
+ .icon-arrow {
+ fill: var(--grey-color);
+ width: 100%;
+ max-width: 1.23rem;
+ height: 100%;
+ max-height: 1.23rem;
+
+ &.transform {
+ transform: rotate(180deg);
+ }
+ }
+ }
+
+ .accordion-content {
+ gap: 0;
+ flex-direction: column;
+ margin-top: 0.62rem;
+ padding-left: 1.85rem;
+ }
+ }
+
+ .website {
+ gap: 0;
+ margin-top: 0.62rem;
+
+ .urls {
+ gap: 0;
+ width: 100%;
+ flex-direction: column;
+
+ .link-text {
+ max-width: 20rem;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+ }
+ }
+
+ .phone {
+ gap: 0;
+ margin: 0.62rem 0 0 0.2rem;
+
+ .numbers {
+ gap: 0;
+ width: 100%;
+ flex-direction: column;
+ }
+ }
+ }
+
.directions-button {
width: 100%;
height: 2.46rem;
diff --git a/src/atomicui/molecules/SuggestionMarker/SuggestionMarker.tsx b/src/atomicui/molecules/SuggestionMarker/SuggestionMarker.tsx
index f6296c36..5f0b7edd 100644
--- a/src/atomicui/molecules/SuggestionMarker/SuggestionMarker.tsx
+++ b/src/atomicui/molecules/SuggestionMarker/SuggestionMarker.tsx
@@ -91,8 +91,10 @@ const SuggestionMarker: FC = ({ active, onClosePopUp, searchValue, setSea
{active || isHovered ? : setHover(info)} />}
{active ? (
{
getPlaceById: async (PlaceId: string) => {
const input: GetPlaceCommandInput = {
PlaceId,
- Language
+ Language,
+ AdditionalFeatures: ["Contact"]
};
const command = new GetPlaceCommand(input);
return await placesClient?.send(command);