diff --git a/apps/scandic-web/components/Blocks/DynamicContent/RewardNights/OfferPrice.tsx b/apps/scandic-web/components/Blocks/DynamicContent/RewardNights/OfferPrice.tsx
new file mode 100644
index 000000000..960e104ab
--- /dev/null
+++ b/apps/scandic-web/components/Blocks/DynamicContent/RewardNights/OfferPrice.tsx
@@ -0,0 +1,39 @@
+"use client"
+import { useIntl } from "react-intl"
+
+import { Typography } from "@scandic-hotels/design-system/Typography"
+
+import { formatDate, type HotelData } from "./util"
+
+import styles from "./rewardNights.module.css"
+
+export function OfferPrice(offer: HotelData["rewardNight"]["campaign"]) {
+ const intl = useIntl()
+
+ return (
+
+
+
+ {intl.formatMessage({
+ id: "rewardNights.offerPrice",
+ defaultMessage: "Offer price",
+ })}
+
+
+
+
+ {intl.formatMessage({
+ id: "rewardNights.stayBetween:",
+ defaultMessage: "Stay between:",
+ })}
+
+
+
+
+
+
+ )
+}
diff --git a/apps/scandic-web/components/Blocks/DynamicContent/RewardNights/Table.tsx b/apps/scandic-web/components/Blocks/DynamicContent/RewardNights/Table.tsx
new file mode 100644
index 000000000..197c7fb66
--- /dev/null
+++ b/apps/scandic-web/components/Blocks/DynamicContent/RewardNights/Table.tsx
@@ -0,0 +1,166 @@
+"use client"
+
+import {
+ createColumnHelper,
+ flexRender,
+ getCoreRowModel,
+ getSortedRowModel,
+ type SortingState,
+ useReactTable,
+} from "@tanstack/react-table"
+import { cx } from "class-variance-authority"
+import { useState } from "react"
+import { useIntl } from "react-intl"
+
+import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
+import Table from "@scandic-hotels/design-system/Table"
+import { TextLink } from "@scandic-hotels/design-system/TextLink"
+import { Typography } from "@scandic-hotels/design-system/Typography"
+
+import { OfferPrice } from "./OfferPrice"
+import {
+ formatPoints,
+ hasActiveCampaign,
+ type HotelData,
+ nameToSort,
+} from "./util"
+
+import styles from "./rewardNights.module.css"
+
+interface RewardNightsTableProps {
+ hotelData: HotelData[]
+}
+export function RewardNightsTable({ hotelData }: RewardNightsTableProps) {
+ const intl = useIntl()
+
+ const [sorting, setSorting] = useState([
+ { id: "destination", desc: false },
+ ])
+
+ const columnHelper = createColumnHelper()
+ const columns = [
+ columnHelper.accessor("name", {
+ header: intl.formatMessage({
+ id: "rewardNights.table.hotel",
+ defaultMessage: "Hotel",
+ }),
+ sortingFn: (a, b) =>
+ nameToSort(a.original.name).localeCompare(nameToSort(b.original.name)),
+ cell: ({ row }) => (
+ {row.original.name}
+ ),
+ }),
+
+ columnHelper.accessor((row) => `${row.city}, ${row.country}`, {
+ id: "destination",
+ header: intl.formatMessage({
+ id: "rewardNights.table.destination",
+ defaultMessage: "Destination",
+ }),
+ sortingFn: (a, b) => a.original.city.localeCompare(b.original.city),
+ cell: ({ row }) => {
+ const hotel = row.original
+ const hasCampaign = hasActiveCampaign(hotel.rewardNight.campaign)
+
+ return (
+ <>
+ {/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
+ {hotel.city}, {hotel.country}
+ {hasCampaign ? (
+
+ ) : null}
+ >
+ )
+ },
+ }),
+
+ columnHelper.accessor((row) => row.rewardNight.points, {
+ id: "points",
+ header: intl.formatMessage({
+ id: "common.points",
+ defaultMessage: "Points",
+ }),
+ sortingFn: (a, b) =>
+ a.original.rewardNight.points - b.original.rewardNight.points,
+ cell: ({ row }) => {
+ const hotel = row.original
+ const hasCampaign = hasActiveCampaign(hotel.rewardNight.campaign)
+
+ return (
+
+ {formatPoints(hotel.rewardNight.points)}
+
+ {hasCampaign ? (
+
+ {formatPoints(hotel.rewardNight.campaign.points)}
+
+ ) : null}
+
+ )
+ },
+ }),
+ ]
+
+ const table = useReactTable({
+ data: hotelData,
+ columns,
+ state: { sorting },
+ onSortingChange: setSorting,
+ getCoreRowModel: getCoreRowModel(),
+ getSortedRowModel: getSortedRowModel(),
+ enableSortingRemoval: false,
+ })
+
+ return (
+
+
+ {table.getHeaderGroups().map((headerGroup) => (
+
+ {headerGroup.headers.map((header) => (
+
+ {flexRender(
+ header.column.columnDef.header,
+ header.getContext()
+ )}
+
+
+ ))}
+
+ ))}
+
+
+
+ {table.getRowModel().rows.map((row) => (
+
+ {row.getVisibleCells().map((cell) => (
+
+ {flexRender(cell.column.columnDef.cell, cell.getContext())}
+
+ ))}
+
+ ))}
+
+
+ )
+}
diff --git a/apps/scandic-web/components/Blocks/DynamicContent/RewardNights/index.tsx b/apps/scandic-web/components/Blocks/DynamicContent/RewardNights/index.tsx
index 2a71cef4d..8facad665 100644
--- a/apps/scandic-web/components/Blocks/DynamicContent/RewardNights/index.tsx
+++ b/apps/scandic-web/components/Blocks/DynamicContent/RewardNights/index.tsx
@@ -1,128 +1,19 @@
-import { cx } from "class-variance-authority"
-
-import Table from "@scandic-hotels/design-system/Table"
-import { TextLink } from "@scandic-hotels/design-system/TextLink"
-import { Typography } from "@scandic-hotels/design-system/Typography"
-
import { getAllHotelData } from "@/lib/trpc/memoizedRequests"
-import { getIntl } from "@/i18n"
+import { RewardNightsTable } from "./Table"
-import styles from "./rewardNights.module.css"
-
-import type { RewardNight } from "@scandic-hotels/trpc/types/hotel"
+import type { HotelData } from "./util"
export async function RewardNights() {
- const intl = await getIntl()
const hotelData = await getAllHotelData()
- return (
-
-
-
-
- {intl.formatMessage({
- id: "rewardNights.table.hotel",
- defaultMessage: "Hotel",
- })}
-
-
- {intl.formatMessage({
- id: "rewardNights.table.destination",
- defaultMessage: "Destination",
- })}
-
-
- {intl.formatMessage({
- id: "common.points",
- defaultMessage: "Points",
- })}
-
-
-
-
- {hotelData.map((data) => {
- const { hotel } = data
- const hasCampaign = hasActiveCampaign(hotel.rewardNight.campaign)
- return (
-
-
- {hotel.name}
-
-
- {/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
- {`${hotel.address.city}, ${hotel.address.country}`}
- {hasCampaign ? (
-
- ) : null}
-
-
-
- {formatPoints(hotel.rewardNight.points)}
- {hasCampaign ? (
-
-
- {formatPoints(hotel.rewardNight.campaign.points)}
-
-
- ) : null}
-
-
-
- )
- })}
-
-
- )
-}
-interface OfferPriceProps {
- points: number
- start: string
- end: string
-}
-async function OfferPrice(offer: OfferPriceProps) {
- const intl = await getIntl()
+ const rewardNightsData: HotelData[] = hotelData.map(({ url, hotel }) => ({
+ url: url ?? "",
+ name: hotel.name,
+ city: hotel.address.city,
+ country: hotel.address.country,
+ rewardNight: hotel.rewardNight,
+ }))
- return (
-
-
-
- {intl.formatMessage({
- id: "rewardNights.offerPrice",
- defaultMessage: "Offer price",
- })}
-
-
-
-
- {intl.formatMessage({
- id: "rewardNights.stayBetween:",
- defaultMessage: "Stay between:",
- })}
-
-
-
-
-
-
- )
-}
-
-function formatPoints(number: number) {
- const format = new Intl.NumberFormat("fr-FR")
- return format.format(number).replace(/\u202F/g, " ")
-}
-
-function formatDate(date?: string) {
- return new Date(date ?? Date.now()).toISOString().split("T")[0]
-}
-
-function hasActiveCampaign(campaign: RewardNight["campaign"]) {
- return campaign.points && formatDate(campaign.end) >= formatDate()
+ return
}
diff --git a/apps/scandic-web/components/Blocks/DynamicContent/RewardNights/rewardNights.module.css b/apps/scandic-web/components/Blocks/DynamicContent/RewardNights/rewardNights.module.css
index 14273ea70..50047fed7 100644
--- a/apps/scandic-web/components/Blocks/DynamicContent/RewardNights/rewardNights.module.css
+++ b/apps/scandic-web/components/Blocks/DynamicContent/RewardNights/rewardNights.module.css
@@ -11,3 +11,12 @@
.grid {
display: grid;
}
+
+.icon {
+ transition: transform 0.3s;
+ padding-left: var(--Space-x05);
+
+ &.isTransformed {
+ transform: rotate(180deg);
+ }
+}
diff --git a/apps/scandic-web/components/Blocks/DynamicContent/RewardNights/util.ts b/apps/scandic-web/components/Blocks/DynamicContent/RewardNights/util.ts
new file mode 100644
index 000000000..9335223bf
--- /dev/null
+++ b/apps/scandic-web/components/Blocks/DynamicContent/RewardNights/util.ts
@@ -0,0 +1,28 @@
+import type { RewardNight } from "@scandic-hotels/trpc/types/hotel"
+
+export interface HotelData {
+ url: string
+ name: string
+ city: string
+ country: string
+ rewardNight: RewardNight
+}
+
+export function formatDate(date?: string) {
+ return new Date(date ?? new Date()).toISOString().split("T")[0]
+}
+
+export function formatPoints(number: number) {
+ const format = new Intl.NumberFormat("fr-FR")
+ return format.format(number).replace(/\u202F/g, " ")
+}
+
+export function hasActiveCampaign(
+ campaign: HotelData["rewardNight"]["campaign"]
+) {
+ return campaign.points && formatDate(campaign.end) >= formatDate()
+}
+
+export function nameToSort(name: string) {
+ return name.toLowerCase().replaceAll("scandic", "").trim()
+}