Merged in feat/LOY-430-reward-nights (pull request #3295)
Feat/LOY-430 reward nights * chore(LOY-430): add reward nights request and dynamic content * chore(LOY-430): fix Reward Night component * Refactor: use existing endpoint and add rewardNight data to that response instead Approved-by: Linus Flood
This commit is contained in:
@@ -27,7 +27,7 @@ export async function GET(request: NextRequest) {
|
||||
}
|
||||
|
||||
const caller = await serverClient()
|
||||
const hotels = await caller.hotel.hotels.getDestinationsMapData({
|
||||
const hotels = await caller.hotel.hotels.getAllHotelData({
|
||||
lang: parsedLang.data,
|
||||
warmup: true,
|
||||
})
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
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 styles from "./rewardNights.module.css"
|
||||
|
||||
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 = hotel.rewardNight.campaign.points
|
||||
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()
|
||||
|
||||
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).toISOString().split("T")[0]
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
.offerPrice {
|
||||
display: grid;
|
||||
gap: var(--Space-x05);
|
||||
}
|
||||
|
||||
.highlightedText {
|
||||
color: var(--Text-Interactive-Secondary);
|
||||
padding-top: var(--Space-x2);
|
||||
}
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
}
|
||||
@@ -28,6 +28,7 @@ import { SJWidget } from "@/components/SJWidget"
|
||||
import JobylonFeed from "./JobylonFeed"
|
||||
|
||||
import type { DynamicContentProps } from "@/types/components/blocks/dynamicContent"
|
||||
import { RewardNights } from "./RewardNights"
|
||||
|
||||
export default function DynamicContent(props: DynamicContentProps) {
|
||||
return (
|
||||
@@ -91,6 +92,8 @@ function DynamicContentBlocks(props: DynamicContentProps) {
|
||||
|
||||
case DynamicContentEnum.Blocks.components.sj_widget:
|
||||
return <SJWidget />
|
||||
case DynamicContentEnum.Blocks.components.reward_nights:
|
||||
return <RewardNights />
|
||||
default:
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { env } from "@/env/server"
|
||||
import { getDestinationsMapData } from "@/lib/trpc/memoizedRequests"
|
||||
import { getAllHotelData } from "@/lib/trpc/memoizedRequests"
|
||||
|
||||
import DynamicMap from "../../Map/DynamicMap"
|
||||
import MapContent from "../../Map/MapContent"
|
||||
@@ -8,7 +8,7 @@ import { getHotelMapMarkers, mapMarkerDataToGeoJson } from "../../Map/utils"
|
||||
import ActiveMapCard from "./ActiveMapCard"
|
||||
|
||||
export default async function OverviewMapContainer() {
|
||||
const hotelData = await getDestinationsMapData()
|
||||
const hotelData = await getAllHotelData()
|
||||
|
||||
if (!hotelData) {
|
||||
return null
|
||||
|
||||
@@ -203,12 +203,10 @@ export const getHotelsByCityIdentifier = cache(
|
||||
})
|
||||
}
|
||||
)
|
||||
export const getDestinationsMapData = cache(
|
||||
async function getMemoizedDestinationsMapData() {
|
||||
const caller = await serverClient()
|
||||
return caller.hotel.hotels.getDestinationsMapData()
|
||||
}
|
||||
)
|
||||
export const getAllHotelData = cache(async function getMemoizedAllHotelData() {
|
||||
const caller = await serverClient()
|
||||
return caller.hotel.hotels.getAllHotelData()
|
||||
})
|
||||
export const getDestinationCityPage = cache(
|
||||
async function getMemoizedDestinationCityPage() {
|
||||
const caller = await serverClient()
|
||||
|
||||
@@ -16,7 +16,7 @@ export const warmupHotelData =
|
||||
|
||||
try {
|
||||
const caller = await serverClient()
|
||||
await caller.hotel.hotels.getDestinationsMapData({
|
||||
await caller.hotel.hotels.getAllHotelData({
|
||||
lang,
|
||||
warmup: true,
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user