LOY-493/Sidepeek upcoming stays * chore(LOY-493): Add icon to next stay card cta * chore(LOY-493): better folder org for stays * chore(LOY-494): more folder reorg * feat(LOY-493): Implement Sidepeek for Upcoming Stays Approved-by: Matilda Landström
119 lines
3.4 KiB
TypeScript
119 lines
3.4 KiB
TypeScript
"use client"
|
|
|
|
import { useIntl } from "react-intl"
|
|
|
|
import { useSidePeekScrollToTop } from "@scandic-hotels/common/hooks/useSidePeekScrollToTop"
|
|
import { BackToTopButton } from "@scandic-hotels/design-system/BackToTopButton"
|
|
import { LoadingSpinner } from "@scandic-hotels/design-system/LoadingSpinner"
|
|
import SidePeekSelfControlled from "@scandic-hotels/design-system/SidePeekSelfControlled"
|
|
import { Typography } from "@scandic-hotels/design-system/Typography"
|
|
import { trpc } from "@scandic-hotels/trpc/client"
|
|
|
|
import useLang from "@/hooks/useLang"
|
|
|
|
import ShowMoreButton from "../../ShowMoreButton"
|
|
import { StayCard } from "../../StayCard"
|
|
import { groupStaysByYear } from "../../utils/groupStaysByYear"
|
|
|
|
import styles from "./upcomingStaysSidePeek.module.css"
|
|
|
|
import type { UpcomingStaysNonNullResponseObject } from "@/types/components/myPages/stays/upcoming"
|
|
|
|
interface UpcomingStaysSidePeekProps {
|
|
isOpen: boolean
|
|
onClose: () => void
|
|
}
|
|
|
|
export function UpcomingStaysSidePeek({
|
|
isOpen,
|
|
onClose,
|
|
}: UpcomingStaysSidePeekProps) {
|
|
const intl = useIntl()
|
|
const lang = useLang()
|
|
|
|
const { scrollContainerRef, showBackToTop, scrollToTop } =
|
|
useSidePeekScrollToTop()
|
|
|
|
const { data, isFetching, fetchNextPage, hasNextPage, isLoading } =
|
|
trpc.user.stays.upcoming.useInfiniteQuery(
|
|
{
|
|
limit: 10,
|
|
lang,
|
|
includeFirstStay: true,
|
|
},
|
|
{
|
|
getNextPageParam: (lastPage) => {
|
|
return lastPage?.nextCursor
|
|
},
|
|
enabled: isOpen,
|
|
}
|
|
)
|
|
|
|
function loadMoreData() {
|
|
if (hasNextPage) {
|
|
fetchNextPage()
|
|
}
|
|
}
|
|
|
|
const stays = data?.pages
|
|
.filter((page): page is UpcomingStaysNonNullResponseObject => !!page?.data)
|
|
.flatMap((page) => page.data)
|
|
|
|
const staysByYear = stays ? groupStaysByYear(stays, "asc") : []
|
|
|
|
return (
|
|
<SidePeekSelfControlled
|
|
title={intl.formatMessage({
|
|
id: "stays.upcoming.title",
|
|
defaultMessage: "Upcoming stays",
|
|
})}
|
|
isOpen={isOpen}
|
|
onClose={onClose}
|
|
>
|
|
<div ref={scrollContainerRef} className={styles.content}>
|
|
{isLoading ? (
|
|
<div className={styles.loadingContainer}>
|
|
<LoadingSpinner />
|
|
</div>
|
|
) : (
|
|
<>
|
|
{staysByYear.map(({ year, stays }) => (
|
|
<section key={year} className={styles.yearSection}>
|
|
<div className={styles.yearHeader}>
|
|
<Typography variant="Title/Overline/sm">
|
|
<span className={styles.yearText}>{year}</span>
|
|
</Typography>
|
|
</div>
|
|
<div className={styles.staysList}>
|
|
{stays.map((stay) => (
|
|
<StayCard
|
|
key={stay.attributes.confirmationNumber}
|
|
stay={stay}
|
|
/>
|
|
))}
|
|
</div>
|
|
</section>
|
|
))}
|
|
{hasNextPage && (
|
|
<ShowMoreButton
|
|
disabled={isFetching}
|
|
loadMoreData={loadMoreData}
|
|
/>
|
|
)}
|
|
</>
|
|
)}
|
|
{showBackToTop && !isLoading && (
|
|
<BackToTopButton
|
|
label={intl.formatMessage({
|
|
id: "common.backToTop",
|
|
defaultMessage: "Back to top",
|
|
})}
|
|
onPress={scrollToTop}
|
|
position="right"
|
|
/>
|
|
)}
|
|
</div>
|
|
</SidePeekSelfControlled>
|
|
)
|
|
}
|