fix: improve loading on destination overview page
- Only load data from Contentstack - Use static JSON for destination list - Some logic improvements to data handling and types
This commit is contained in:
+17
-15
@@ -27,21 +27,23 @@ export default async function Destination({
|
||||
<AccordionItem title={country} subtitle={accordionSubtitle}>
|
||||
<div className={styles.container}>
|
||||
<ul className={styles.citiesList}>
|
||||
{cities.map((city) => (
|
||||
<li key={city.id}>
|
||||
{city.url ? (
|
||||
<Link
|
||||
href={city.url}
|
||||
color="baseTextMediumContrast"
|
||||
textDecoration="underline"
|
||||
>
|
||||
{`${city.name} (${city.hotelCount})`}
|
||||
</Link>
|
||||
) : (
|
||||
<Body>{`${city.name} (${city.hotelCount})`}</Body>
|
||||
)}
|
||||
</li>
|
||||
))}
|
||||
{cities.map((city) =>
|
||||
city.hotelCount > 0 ? (
|
||||
<li key={city.id}>
|
||||
{city.url ? (
|
||||
<Link
|
||||
href={city.url}
|
||||
color="baseTextMediumContrast"
|
||||
textDecoration="underline"
|
||||
>
|
||||
{`${city.name} (${city.hotelCount})`}
|
||||
</Link>
|
||||
) : (
|
||||
<Body>{`${city.name} (${city.hotelCount})`}</Body>
|
||||
)}
|
||||
</li>
|
||||
) : null
|
||||
)}
|
||||
</ul>
|
||||
{countryUrl && (
|
||||
<Link href={countryUrl} variant="icon" color="burgundy" weight="bold">
|
||||
|
||||
+23
-21
@@ -7,9 +7,7 @@ import styles from "./destinationsList.module.css"
|
||||
|
||||
import type { DestinationsListProps } from "@/types/components/destinationOverviewPage/destinationsList/destinationsData"
|
||||
|
||||
export default function DestinationsList({
|
||||
destinations,
|
||||
}: DestinationsListProps) {
|
||||
export function DestinationsList({ destinations }: DestinationsListProps) {
|
||||
const middleIndex = Math.ceil(destinations.length / 2)
|
||||
const accordionLeft = destinations.slice(0, middleIndex)
|
||||
const accordionRight = destinations.slice(middleIndex)
|
||||
@@ -17,27 +15,31 @@ export default function DestinationsList({
|
||||
return (
|
||||
<div className={styles.listContainer}>
|
||||
<Accordion className={styles.accordion}>
|
||||
{accordionLeft.map((data) => (
|
||||
<Destination
|
||||
key={data.country}
|
||||
country={data.country}
|
||||
countryUrl={data.countryUrl}
|
||||
numberOfHotels={data.numberOfHotels}
|
||||
cities={data.cities}
|
||||
/>
|
||||
))}
|
||||
{accordionLeft.map((data) =>
|
||||
data.numberOfHotels > 0 ? (
|
||||
<Destination
|
||||
key={data.country}
|
||||
country={data.country}
|
||||
countryUrl={data.countryUrl}
|
||||
numberOfHotels={data.numberOfHotels}
|
||||
cities={data.cities}
|
||||
/>
|
||||
) : null
|
||||
)}
|
||||
</Accordion>
|
||||
<Divider color="subtle" className={styles.divider} />
|
||||
<Accordion className={styles.accordion}>
|
||||
{accordionRight.map((data) => (
|
||||
<Destination
|
||||
key={data.country}
|
||||
country={data.country}
|
||||
countryUrl={data.countryUrl}
|
||||
numberOfHotels={data.numberOfHotels}
|
||||
cities={data.cities}
|
||||
/>
|
||||
))}
|
||||
{accordionRight.map((data) =>
|
||||
data.numberOfHotels > 0 ? (
|
||||
<Destination
|
||||
key={data.country}
|
||||
country={data.country}
|
||||
countryUrl={data.countryUrl}
|
||||
numberOfHotels={data.numberOfHotels}
|
||||
cities={data.cities}
|
||||
/>
|
||||
) : null
|
||||
)}
|
||||
</Accordion>
|
||||
</div>
|
||||
)
|
||||
|
||||
+9
-6
@@ -1,16 +1,19 @@
|
||||
import { getDestinationsList } from "@/lib/trpc/memoizedRequests"
|
||||
|
||||
import Title from "@/components/TempDesignSystem/Text/Title"
|
||||
import { getIntl } from "@/i18n"
|
||||
|
||||
import DestinationsList from "./DestinationsList"
|
||||
import { DestinationsList } from "./DestinationsList"
|
||||
|
||||
import styles from "./hotelsSection.module.css"
|
||||
|
||||
import type { HotelsSectionProps } from "@/types/components/destinationOverviewPage/destinationsList/destinationsData"
|
||||
|
||||
export default async function HotelsSection({
|
||||
destinations,
|
||||
}: HotelsSectionProps) {
|
||||
export default async function HotelsSection() {
|
||||
const intl = await getIntl()
|
||||
const destinations = await getDestinationsList()
|
||||
|
||||
if (destinations.length === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<section className={styles.container}>
|
||||
|
||||
+2
-20
@@ -6,17 +6,8 @@ import MapContent from "../../Map/MapContent"
|
||||
import MapProvider from "../../Map/MapProvider"
|
||||
import { getHotelMapMarkers, mapMarkerDataToGeoJson } from "../../Map/utils"
|
||||
import ActiveMapCard from "./ActiveMapCard"
|
||||
import InputForm from "./InputForm"
|
||||
|
||||
import type { MapLocation } from "@/types/components/mapLocation"
|
||||
|
||||
interface OverviewMapContainerProps {
|
||||
defaultLocation: MapLocation
|
||||
}
|
||||
|
||||
export default async function OverviewMapContainer({
|
||||
defaultLocation,
|
||||
}: OverviewMapContainerProps) {
|
||||
export default async function OverviewMapContainer() {
|
||||
const hotelData = await getAllHotels()
|
||||
|
||||
if (!hotelData) {
|
||||
@@ -28,22 +19,13 @@ export default async function OverviewMapContainer({
|
||||
|
||||
const markers = getHotelMapMarkers(hotelData)
|
||||
const geoJson = mapMarkerDataToGeoJson(markers)
|
||||
const defaultCoordinates = defaultLocation
|
||||
? {
|
||||
lat: defaultLocation.latitude,
|
||||
lng: defaultLocation.longitude,
|
||||
}
|
||||
: null
|
||||
const defaultZoom = defaultLocation?.default_zoom ?? 3
|
||||
|
||||
return (
|
||||
<MapProvider apiKey={googleMapsApiKey} pageType="overview">
|
||||
<InputForm />
|
||||
<DynamicMap
|
||||
mapId={googleMapId}
|
||||
markers={markers}
|
||||
defaultCoordinates={defaultCoordinates}
|
||||
defaultZoom={defaultZoom}
|
||||
fitBounds
|
||||
gestureHandling="cooperative"
|
||||
>
|
||||
<MapContent geojson={geoJson} />
|
||||
|
||||
+12
-2
@@ -2,11 +2,21 @@
|
||||
position: relative;
|
||||
display: grid;
|
||||
width: 100%;
|
||||
max-width: var(--max-width);
|
||||
height: 700px;
|
||||
height: 610px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
.mapContainer {
|
||||
height: 580px;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 1367px) {
|
||||
.mapContainer {
|
||||
height: 560px;
|
||||
}
|
||||
}
|
||||
|
||||
.main {
|
||||
display: grid;
|
||||
gap: var(--Spacing-x9);
|
||||
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
"use client"
|
||||
|
||||
import styles from "./destinationOverviewPage.module.css"
|
||||
|
||||
import type { PropsWithChildren } from "react"
|
||||
|
||||
export function DestinationOverviewPageError({ children }: PropsWithChildren) {
|
||||
return (
|
||||
<main className={styles.main}>
|
||||
<div className={styles.blocks}>{children}</div>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
+25
-16
@@ -1,9 +1,7 @@
|
||||
import {
|
||||
getDestinationOverviewPage,
|
||||
getDestinationsList,
|
||||
} from "@/lib/trpc/memoizedRequests"
|
||||
import { getDestinationOverviewPage } from "@/lib/trpc/memoizedRequests"
|
||||
|
||||
import Blocks from "@/components/Blocks"
|
||||
import SkeletonShimmer from "@/components/SkeletonShimmer"
|
||||
import TrackingSDK from "@/components/TrackingSDK"
|
||||
|
||||
import HotelsSection from "./HotelsSection"
|
||||
@@ -12,10 +10,7 @@ import OverviewMapContainer from "./OverviewMapContainer"
|
||||
import styles from "./destinationOverviewPage.module.css"
|
||||
|
||||
export default async function DestinationOverviewPage() {
|
||||
const [pageData, destinationsData] = await Promise.all([
|
||||
getDestinationOverviewPage(),
|
||||
getDestinationsList(),
|
||||
])
|
||||
const pageData = await getDestinationOverviewPage()
|
||||
|
||||
if (!pageData) {
|
||||
return null
|
||||
@@ -26,21 +21,35 @@ export default async function DestinationOverviewPage() {
|
||||
return (
|
||||
<>
|
||||
<div className={styles.mapContainer}>
|
||||
<OverviewMapContainer
|
||||
defaultLocation={destinationOverviewPage.location}
|
||||
/>
|
||||
<OverviewMapContainer />
|
||||
</div>
|
||||
<main className={styles.main}>
|
||||
<div className={styles.blocks}>
|
||||
<Blocks blocks={destinationOverviewPage.blocks} />
|
||||
</div>
|
||||
</main>
|
||||
{destinationsData && (
|
||||
<aside className={styles.hotelsAccordions}>
|
||||
<HotelsSection destinations={destinationsData} />
|
||||
</aside>
|
||||
)}
|
||||
<aside className={styles.hotelsAccordions}>
|
||||
<HotelsSection />
|
||||
</aside>
|
||||
<TrackingSDK pageData={tracking} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export function DestinationOverviewPageLoading() {
|
||||
return (
|
||||
<>
|
||||
<div className={styles.mapContainer}>
|
||||
<SkeletonShimmer width={"100%"} height={"100%"} />
|
||||
</div>
|
||||
<main className={styles.main}>
|
||||
<div className={styles.blocks}>
|
||||
<SkeletonShimmer width={"100%"} height={"100%"} />
|
||||
</div>
|
||||
</main>
|
||||
<aside className={styles.hotelsAccordions}>
|
||||
<SkeletonShimmer width={"100%"} height={"100%"} />
|
||||
</aside>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.mapWrapper::after {
|
||||
.mapWrapperWithCloseButton:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import "client-only"
|
||||
|
||||
import { Map, type MapProps, useMap } from "@vis.gl/react-google-maps"
|
||||
import { cx } from "class-variance-authority"
|
||||
import { type PropsWithChildren, useEffect, useRef } from "react"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
@@ -28,8 +29,8 @@ const BACKUP_COORDINATES = {
|
||||
interface DynamicMapProps {
|
||||
markers: DestinationMarker[]
|
||||
mapId: string
|
||||
defaultCoordinates: google.maps.LatLngLiteral | null
|
||||
defaultZoom: number
|
||||
defaultCenter?: google.maps.LatLngLiteral
|
||||
defaultZoom?: number
|
||||
fitBounds?: boolean
|
||||
gestureHandling?: "greedy" | "cooperative" | "auto" | "none"
|
||||
onClose?: () => void
|
||||
@@ -38,8 +39,8 @@ interface DynamicMapProps {
|
||||
export default function DynamicMap({
|
||||
markers,
|
||||
mapId,
|
||||
defaultCoordinates,
|
||||
defaultZoom,
|
||||
defaultCenter = BACKUP_COORDINATES,
|
||||
defaultZoom = 3,
|
||||
fitBounds = true,
|
||||
onClose,
|
||||
gestureHandling = "auto",
|
||||
@@ -61,18 +62,14 @@ export default function DynamicMap({
|
||||
}, [activeMarker, pageType])
|
||||
|
||||
useEffect(() => {
|
||||
if (map && fitBounds) {
|
||||
if (markers.length) {
|
||||
const bounds = new google.maps.LatLngBounds()
|
||||
markers.forEach((marker) => {
|
||||
bounds.extend(marker.coordinates)
|
||||
})
|
||||
map.fitBounds(bounds, 100)
|
||||
} else if (defaultCoordinates) {
|
||||
map.setCenter(defaultCoordinates)
|
||||
}
|
||||
if (map && fitBounds && markers?.length) {
|
||||
const bounds = new google.maps.LatLngBounds()
|
||||
markers.forEach((marker) => {
|
||||
bounds.extend(marker.coordinates)
|
||||
})
|
||||
map.fitBounds(bounds, 100)
|
||||
}
|
||||
}, [map, markers, defaultCoordinates, fitBounds])
|
||||
}, [map, fitBounds, markers])
|
||||
|
||||
useHandleKeyUp((event: KeyboardEvent) => {
|
||||
if (event.key === "Escape" && onClose) {
|
||||
@@ -94,7 +91,7 @@ export default function DynamicMap({
|
||||
}
|
||||
|
||||
const mapOptions: MapProps = {
|
||||
defaultCenter: defaultCoordinates || BACKUP_COORDINATES, // Default center will be overridden by the bounds
|
||||
defaultCenter, // Default center will be overridden by the bounds
|
||||
minZoom: 3,
|
||||
maxZoom: 18,
|
||||
defaultZoom,
|
||||
@@ -105,7 +102,13 @@ export default function DynamicMap({
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.mapWrapper} ref={ref}>
|
||||
<div
|
||||
className={cx(
|
||||
styles.mapWrapper,
|
||||
onClose && styles.mapWrapperWithCloseButton
|
||||
)}
|
||||
ref={ref}
|
||||
>
|
||||
<ErrorBoundary fallback={<h2>Unable to display map</h2>}>
|
||||
<Map {...mapOptions}>{children}</Map>
|
||||
</ErrorBoundary>
|
||||
|
||||
@@ -71,7 +71,7 @@ export default function Map({
|
||||
|
||||
const markers = getHotelMapMarkers(hotels)
|
||||
const geoJson = mapMarkerDataToGeoJson(markers)
|
||||
const defaultCoordinates = activeHotel
|
||||
const defaultCenter = activeHotel
|
||||
? {
|
||||
lat: activeHotel.hotel.location.latitude,
|
||||
lng: activeHotel.hotel.location.longitude,
|
||||
@@ -81,7 +81,7 @@ export default function Map({
|
||||
lat: defaultLocation.latitude,
|
||||
lng: defaultLocation.longitude,
|
||||
}
|
||||
: null
|
||||
: undefined
|
||||
const defaultZoom = activeHotel
|
||||
? 15
|
||||
: (defaultLocation?.default_zoom ?? (pageType === "city" ? 10 : 3))
|
||||
@@ -171,7 +171,7 @@ export default function Map({
|
||||
markers={markers}
|
||||
mapId={mapId}
|
||||
onClose={handleClose}
|
||||
defaultCoordinates={defaultCoordinates}
|
||||
defaultCenter={defaultCenter}
|
||||
defaultZoom={defaultZoom}
|
||||
fitBounds={!activeHotel}
|
||||
gestureHandling="greedy"
|
||||
|
||||
Reference in New Issue
Block a user