Merged in feat/LOY-501-table-sorting (pull request #3321)

feat(LOY-501): add sorting to Reward Night Table

* feat(LOY-501): add sorting using Tanstack Table


Approved-by: Chuma Mcphoy (We Ahead)
This commit is contained in:
Matilda Landström
2025-12-11 14:08:01 +00:00
parent 7faa9933a2
commit 5770147af4
5 changed files with 252 additions and 119 deletions

View File

@@ -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 (
<Table intent="striped" variant="content" style={{ textWrap: "balance" }}>
<Table.THead>
<Table.TR>
<Table.TH>
{intl.formatMessage({
id: "rewardNights.table.hotel",
defaultMessage: "Hotel",
})}
</Table.TH>
<Table.TH>
{intl.formatMessage({
id: "rewardNights.table.destination",
defaultMessage: "Destination",
})}
</Table.TH>
<Table.TH>
{intl.formatMessage({
id: "common.points",
defaultMessage: "Points",
})}
</Table.TH>
</Table.TR>
</Table.THead>
<Table.TBody>
{hotelData.map((data) => {
const { hotel } = data
const hasCampaign = hasActiveCampaign(hotel.rewardNight.campaign)
return (
<Table.TR key={hotel.id}>
<Table.TD style={{ alignContent: "flex-start" }}>
<TextLink href={data.url ?? ""}>{hotel.name}</TextLink>
</Table.TD>
<Table.TD>
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
{`${hotel.address.city}, ${hotel.address.country}`}
{hasCampaign ? (
<OfferPrice {...hotel.rewardNight.campaign} />
) : null}
</Table.TD>
<Table.TD style={{ alignContent: "flex-start" }}>
<div className={cx({ [styles.grid]: hasCampaign })}>
{formatPoints(hotel.rewardNight.points)}
{hasCampaign ? (
<Typography
variant="Body/Paragraph/mdBold"
className={styles.highlightedText}
>
<span>
{formatPoints(hotel.rewardNight.campaign.points)}
</span>
</Typography>
) : null}
</div>
</Table.TD>
</Table.TR>
)
})}
</Table.TBody>
</Table>
)
}
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 (
<div className={styles.offerPrice}>
<Typography variant="Body/Paragraph/mdBold">
<p className={styles.highlightedText}>
{intl.formatMessage({
id: "rewardNights.offerPrice",
defaultMessage: "Offer price",
})}
</p>
</Typography>
<Typography variant="Label/xsBold">
<p>
{intl.formatMessage({
id: "rewardNights.stayBetween:",
defaultMessage: "Stay between:",
})}
</p>
</Typography>
<Typography variant="Label/xsRegular">
<time>
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
{formatDate(offer.start)} - {formatDate(offer.end)}
</time>
</Typography>
</div>
)
}
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 <RewardNightsTable hotelData={rewardNightsData} />
}