diff --git a/apps/scandic-web/components/Blocks/DynamicContent/Stays/Previous/Cards.tsx b/apps/scandic-web/components/Blocks/DynamicContent/Stays/Previous/Cards.tsx
index e2977b2d5..c5c8afa65 100644
--- a/apps/scandic-web/components/Blocks/DynamicContent/Stays/Previous/Cards.tsx
+++ b/apps/scandic-web/components/Blocks/DynamicContent/Stays/Previous/Cards.tsx
@@ -1,64 +1,38 @@
"use client"
-import { LoadingSpinner } from "@scandic-hotels/design-system/LoadingSpinner"
-import { trpc } from "@scandic-hotels/trpc/client"
-
-import useLang from "@/hooks/useLang"
+import { useState } from "react"
import ListContainer from "../ListContainer"
-import ShowMoreButton from "../ShowMoreButton"
import { Card } from "./Card"
+import { INITIAL_STAYS_FETCH_LIMIT } from "./data"
+import { PreviousStaysSidePeek } from "./PreviousStaysSidePeek"
+import { SeeAllCard } from "./SeeAllCard"
import styles from "./cards.module.css"
-import type {
- PreviousStaysClientProps,
- PreviousStaysNonNullResponseObject,
-} from "@/types/components/myPages/stays/previous"
+import type { PreviousStaysClientProps } from "@/types/components/myPages/stays/previous"
+
+const MAX_VISIBLE_STAYS = 5
export function Cards({ initialPreviousStays }: PreviousStaysClientProps) {
- const lang = useLang()
- const { data, isFetching, fetchNextPage, hasNextPage, isLoading } =
- trpc.user.stays.previous.useInfiniteQuery(
- {
- limit: 6,
- lang,
- },
- {
- getNextPageParam: (lastPage) => {
- return lastPage?.nextCursor
- },
- initialData: {
- pageParams: [undefined, 1],
- pages: [initialPreviousStays],
- },
- }
- )
+ const [isSidePeekOpen, setIsSidePeekOpen] = useState(false)
- if (isLoading) {
- return
- }
-
- function loadMoreData() {
- if (hasNextPage) {
- fetchNextPage()
- }
- }
-
- const stays = data.pages
- .filter((page): page is PreviousStaysNonNullResponseObject => !!page?.data)
- .flatMap((page) => page.data)
+ const stays = initialPreviousStays.data
+ const visibleStays = stays.slice(0, MAX_VISIBLE_STAYS)
+ const hasMoreStays = stays.length >= INITIAL_STAYS_FETCH_LIMIT
return (
- {stays.map((stay) => (
+ {visibleStays.map((stay) => (
))}
+ {hasMoreStays && setIsSidePeekOpen(true)} />}
- {hasNextPage ? (
-
- ) : null}
+ setIsSidePeekOpen(false)}
+ />
)
}
diff --git a/apps/scandic-web/components/Blocks/DynamicContent/Stays/Previous/PreviousStaysSidePeek/index.tsx b/apps/scandic-web/components/Blocks/DynamicContent/Stays/Previous/PreviousStaysSidePeek/index.tsx
new file mode 100644
index 000000000..505a87003
--- /dev/null
+++ b/apps/scandic-web/components/Blocks/DynamicContent/Stays/Previous/PreviousStaysSidePeek/index.tsx
@@ -0,0 +1,102 @@
+"use client"
+
+import { useIntl } from "react-intl"
+
+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 { Card } from "../Card"
+import { groupStaysByYear } from "../utils/groupStaysByYear"
+
+import styles from "./previousStaysSidePeek.module.css"
+
+import type { PreviousStaysNonNullResponseObject } from "@/types/components/myPages/stays/previous"
+
+interface PreviousStaysSidePeekProps {
+ isOpen: boolean
+ onClose: () => void
+}
+
+export function PreviousStaysSidePeek({
+ isOpen,
+ onClose,
+}: PreviousStaysSidePeekProps) {
+ const intl = useIntl()
+ const lang = useLang()
+
+ const { data, isFetching, fetchNextPage, hasNextPage, isLoading } =
+ trpc.user.stays.previous.useInfiniteQuery(
+ {
+ limit: 10,
+ lang,
+ },
+ {
+ getNextPageParam: (lastPage) => {
+ return lastPage?.nextCursor
+ },
+ enabled: isOpen,
+ }
+ )
+
+ function loadMoreData() {
+ if (hasNextPage) {
+ fetchNextPage()
+ }
+ }
+
+ const stays = data?.pages
+ .filter((page): page is PreviousStaysNonNullResponseObject => !!page?.data)
+ .flatMap((page) => page.data)
+
+ const staysByYear = stays ? groupStaysByYear(stays) : []
+
+ return (
+
+
+ {isLoading ? (
+
+
+
+ ) : (
+ <>
+ {staysByYear.map(({ year, stays }) => (
+
+
+
+ {year}
+
+
+
+ {stays.map((stay) => (
+
+ ))}
+
+
+ ))}
+ {hasNextPage && (
+
+ )}
+ >
+ )}
+
+
+ )
+}
diff --git a/apps/scandic-web/components/Blocks/DynamicContent/Stays/Previous/PreviousStaysSidePeek/previousStaysSidePeek.module.css b/apps/scandic-web/components/Blocks/DynamicContent/Stays/Previous/PreviousStaysSidePeek/previousStaysSidePeek.module.css
new file mode 100644
index 000000000..f653a0768
--- /dev/null
+++ b/apps/scandic-web/components/Blocks/DynamicContent/Stays/Previous/PreviousStaysSidePeek/previousStaysSidePeek.module.css
@@ -0,0 +1,34 @@
+.content {
+ display: flex;
+ flex-direction: column;
+ gap: var(--Space-x3);
+}
+
+.loadingContainer {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ padding: var(--Space-x4);
+}
+
+.yearSection {
+ display: flex;
+ flex-direction: column;
+ gap: var(--Space-x2);
+}
+
+.yearHeader {
+ background: var(--Surface-Primary-Hover-Accent);
+ padding: var(--Space-x1) var(--Space-x2);
+ border-radius: var(--Corner-radius-sm);
+}
+
+.yearText {
+ color: var(--Text-Heading);
+}
+
+.staysList {
+ display: flex;
+ flex-direction: column;
+ gap: var(--Space-x2);
+}
diff --git a/apps/scandic-web/components/Blocks/DynamicContent/Stays/Previous/SeeAllCard/index.tsx b/apps/scandic-web/components/Blocks/DynamicContent/Stays/Previous/SeeAllCard/index.tsx
new file mode 100644
index 000000000..8cf590234
--- /dev/null
+++ b/apps/scandic-web/components/Blocks/DynamicContent/Stays/Previous/SeeAllCard/index.tsx
@@ -0,0 +1,30 @@
+"use client"
+
+import { useIntl } from "react-intl"
+
+import { Button } from "@scandic-hotels/design-system/Button"
+import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
+
+import styles from "./seeAllCard.module.css"
+
+interface SeeAllCardProps {
+ onPress: () => void
+}
+
+export function SeeAllCard({ onPress }: SeeAllCardProps) {
+ const intl = useIntl()
+
+ return (
+
+
+
+ )
+}
diff --git a/apps/scandic-web/components/Blocks/DynamicContent/Stays/Previous/SeeAllCard/seeAllCard.module.css b/apps/scandic-web/components/Blocks/DynamicContent/Stays/Previous/SeeAllCard/seeAllCard.module.css
new file mode 100644
index 000000000..225a87f0c
--- /dev/null
+++ b/apps/scandic-web/components/Blocks/DynamicContent/Stays/Previous/SeeAllCard/seeAllCard.module.css
@@ -0,0 +1,12 @@
+.card {
+ border-radius: var(--Corner-radius-lg);
+ border: 1px solid var(--Border-Default);
+ background: var(--Surface-Secondary-Default);
+ display: flex;
+ padding: var(--Space-x15);
+ align-items: center;
+ justify-content: center;
+ align-self: stretch;
+ height: 100%;
+ min-height: 134px;
+}
diff --git a/apps/scandic-web/components/Blocks/DynamicContent/Stays/Previous/data.ts b/apps/scandic-web/components/Blocks/DynamicContent/Stays/Previous/data.ts
new file mode 100644
index 000000000..b084ffdfb
--- /dev/null
+++ b/apps/scandic-web/components/Blocks/DynamicContent/Stays/Previous/data.ts
@@ -0,0 +1 @@
+export const INITIAL_STAYS_FETCH_LIMIT = 6
diff --git a/apps/scandic-web/components/Blocks/DynamicContent/Stays/Previous/index.tsx b/apps/scandic-web/components/Blocks/DynamicContent/Stays/Previous/index.tsx
index eb15fd2a4..4dc692448 100644
--- a/apps/scandic-web/components/Blocks/DynamicContent/Stays/Previous/index.tsx
+++ b/apps/scandic-web/components/Blocks/DynamicContent/Stays/Previous/index.tsx
@@ -3,11 +3,12 @@ import { serverClient } from "@/lib/trpc/server"
import ClaimPoints from "@/components/Blocks/DynamicContent/Points/ClaimPoints"
import { Section } from "@/components/Section"
-import SectionHeader from "@/components/Section/Header/Deprecated"
+import { SectionHeader } from "@/components/Section/Header"
import SectionLink from "@/components/Section/Link"
import { Cards } from "./Cards"
import { ClientPreviousStays } from "./Client"
+import { INITIAL_STAYS_FETCH_LIMIT } from "./data"
import styles from "./previous.module.css"
@@ -19,7 +20,7 @@ export default async function PreviousStays({
}: AccountPageComponentProps) {
const caller = await serverClient()
const initialPreviousStays = await caller.user.stays.previous({
- limit: 6,
+ limit: INITIAL_STAYS_FETCH_LIMIT,
})
if (!initialPreviousStays?.data.length) {
@@ -31,7 +32,7 @@ export default async function PreviousStays({
return (
-
+
diff --git a/apps/scandic-web/components/Blocks/DynamicContent/Stays/Previous/utils/groupStaysByYear.ts b/apps/scandic-web/components/Blocks/DynamicContent/Stays/Previous/utils/groupStaysByYear.ts
new file mode 100644
index 000000000..9c5107f85
--- /dev/null
+++ b/apps/scandic-web/components/Blocks/DynamicContent/Stays/Previous/utils/groupStaysByYear.ts
@@ -0,0 +1,29 @@
+import { dt } from "@scandic-hotels/common/dt"
+
+import type { Stay } from "@scandic-hotels/trpc/routers/user/output"
+
+export interface StaysByYear {
+ year: number
+ stays: Stay[]
+}
+
+/**
+ * Groups stays by year based on checkinDate.
+ * @returns an array sorted by year in descending order (most recent first).
+ */
+export function groupStaysByYear(stays: Stay[]): StaysByYear[] {
+ const groupedMap = new Map()
+
+ for (const stay of stays) {
+ const year = dt(stay.attributes.checkinDate).year()
+
+ if (!groupedMap.has(year)) {
+ groupedMap.set(year, [])
+ }
+ groupedMap.get(year)!.push(stay)
+ }
+
+ return Array.from(groupedMap.entries())
+ .sort(([yearA], [yearB]) => yearB - yearA)
+ .map(([year, stays]) => ({ year, stays }))
+}