Merge branch 'develop' into feature/sw-561-design-fixes

This commit is contained in:
Linus Flood
2024-10-08 13:23:07 +02:00
47 changed files with 1053 additions and 399 deletions

View File

@@ -1,176 +0,0 @@
import { notFound } from "next/navigation"
import { getProfileSafely } from "@/lib/trpc/memoizedRequests"
import { serverClient } from "@/lib/trpc/server"
import BedType from "@/components/HotelReservation/EnterDetails/BedType"
import Breakfast from "@/components/HotelReservation/EnterDetails/Breakfast"
import Details from "@/components/HotelReservation/EnterDetails/Details"
import HotelSelectionHeader from "@/components/HotelReservation/HotelSelectionHeader"
import Payment from "@/components/HotelReservation/SelectRate/Payment"
import SectionAccordion from "@/components/HotelReservation/SelectRate/SectionAccordion"
import Summary from "@/components/HotelReservation/SelectRate/Summary"
import { getIntl } from "@/i18n"
import { setLang } from "@/i18n/serverContext"
import styles from "./page.module.css"
import { SectionPageProps } from "@/types/components/hotelReservation/selectRate/section"
import { LangParams, PageArgs } from "@/types/params"
const bedAlternatives = [
{
value: "queen",
name: "Queen bed",
payment: "160 cm",
pricePerNight: 0,
membersPricePerNight: 0,
currency: "SEK",
},
{
value: "king",
name: "King bed",
payment: "160 cm",
pricePerNight: 0,
membersPricePerNight: 0,
currency: "SEK",
},
{
value: "twin",
name: "Twin bed",
payment: "90 cm + 90 cm",
pricePerNight: 82,
membersPricePerNight: 67,
currency: "SEK",
},
]
const breakfastAlternatives = [
{
value: "no",
name: "No breakfast",
payment: "Always cheeper to get it online",
pricePerNight: 0,
currency: "SEK",
},
{
value: "buffe",
name: "Breakfast buffé",
payment: "Always cheeper to get it online",
pricePerNight: 150,
currency: "SEK",
},
]
const getFlexibilityMessage = (value: string) => {
switch (value) {
case "non-refundable":
return "Non refundable"
case "free-rebooking":
return "Free rebooking"
case "free-cancellation":
return "Free cancellation"
}
return undefined
}
export default async function SectionsPage({
params,
searchParams,
}: PageArgs<LangParams & { section: string }, SectionPageProps>) {
setLang(params.lang)
const profile = await getProfileSafely()
const hotel = await serverClient().hotel.hotelData.get({
hotelId: "811",
language: params.lang,
})
if (!hotel) {
// TODO: handle case with hotel missing
return notFound()
}
const rooms = await serverClient().hotel.rates.get({
// TODO: pass the correct hotel ID and all other parameters that should be included in the search
hotelId: hotel.data.id,
})
const intl = await getIntl()
const selectedBed = searchParams.bed
? bedAlternatives.find((a) => a.value === searchParams.bed)?.name
: undefined
const selectedBreakfast = searchParams.breakfast
? breakfastAlternatives.find((a) => a.value === searchParams.breakfast)
?.name
: undefined
const selectedRoom = searchParams.roomClass
? rooms.find((room) => room.id.toString() === searchParams.roomClass)?.name
: undefined
const selectedFlexibility = searchParams.flexibility
? getFlexibilityMessage(searchParams.flexibility)
: undefined
const currentSearchParams = new URLSearchParams(searchParams).toString()
let user = null
if (profile && !("error" in profile)) {
user = profile
}
return (
<div>
<HotelSelectionHeader hotel={hotel.data.attributes} />
<div className={styles.content}>
<div className={styles.main}>
<SectionAccordion
header={intl.formatMessage({ id: "Room & Terms" })}
selection={
selectedRoom
? [
selectedRoom,
intl.formatMessage({ id: selectedFlexibility }),
]
: undefined
}
path={`select-rate?${currentSearchParams}`}
></SectionAccordion>
<SectionAccordion
header={intl.formatMessage({ id: "Bed type" })}
selection={selectedBed}
path={`select-bed?${currentSearchParams}`}
>
{params.section === "select-bed" ? <BedType /> : null}
</SectionAccordion>
<SectionAccordion
header={intl.formatMessage({ id: "Breakfast" })}
selection={selectedBreakfast}
path={`breakfast?${currentSearchParams}`}
>
{params.section === "breakfast" ? <Breakfast /> : null}
</SectionAccordion>
<SectionAccordion
header={intl.formatMessage({ id: "Your details" })}
path={`details?${currentSearchParams}`}
>
{params.section === "details" ? <Details user={user} /> : null}
</SectionAccordion>
<SectionAccordion
header={intl.formatMessage({ id: "Payment info" })}
path={`payment?${currentSearchParams}`}
>
{params.section === "payment" && (
<Payment hotel={hotel.data.attributes} />
)}
</SectionAccordion>
</div>
<div className={styles.summary}>
<Summary />
</div>
</div>
</div>
)
}

View File

@@ -16,10 +16,15 @@
gap: var(--Spacing-x7);
}
.main {
.section {
flex-grow: 1;
}
.summary {
max-width: 340px;
}
.form {
display: grid;
gap: var(--Spacing-x2);
}

View File

@@ -0,0 +1,125 @@
"use client"
import { notFound } from "next/navigation"
import { useState } from "react"
import { useIntl } from "react-intl"
import { trpc } from "@/lib/trpc/client"
import BedType from "@/components/HotelReservation/EnterDetails/BedType"
import Breakfast from "@/components/HotelReservation/EnterDetails/Breakfast"
import Details from "@/components/HotelReservation/EnterDetails/Details"
import HotelSelectionHeader from "@/components/HotelReservation/HotelSelectionHeader"
import Payment from "@/components/HotelReservation/SelectRate/Payment"
import SectionAccordion from "@/components/HotelReservation/SelectRate/SectionAccordion"
import Summary from "@/components/HotelReservation/SelectRate/Summary"
import LoadingSpinner from "@/components/LoadingSpinner"
import styles from "./page.module.css"
import { LangParams, PageArgs } from "@/types/params"
enum StepEnum {
selectBed = "select-bed",
breakfast = "breakfast",
details = "details",
payment = "payment",
}
function isValidStep(step: string): step is StepEnum {
return Object.values(StepEnum).includes(step as StepEnum)
}
export default function StepPage({
params,
}: PageArgs<LangParams & { step: StepEnum }>) {
const { step } = params
const [activeStep, setActiveStep] = useState<StepEnum>(step)
const intl = useIntl()
if (!isValidStep(activeStep)) {
return notFound()
}
const { data: hotel, isLoading: loadingHotel } =
trpc.hotel.hotelData.get.useQuery({
hotelId: "811",
language: params.lang,
})
if (loadingHotel) {
return <LoadingSpinner />
}
if (!hotel) {
// TODO: handle case with hotel missing
return notFound()
}
switch (activeStep) {
case StepEnum.breakfast:
//return <div>Select BREAKFAST</div>
case StepEnum.details:
//return <div>Select DETAILS</div>
case StepEnum.payment:
//return <div>Select PAYMENT</div>
case StepEnum.selectBed:
// return <div>Select BED</div>
}
function onNav(step: StepEnum) {
setActiveStep(step)
if (typeof window !== "undefined") {
window.history.pushState({}, "", step)
}
}
return (
<main className={styles.page}>
<HotelSelectionHeader hotel={hotel.data.attributes} />
<div className={styles.content}>
<section className={styles.section}>
<SectionAccordion
header="Select bed"
isCompleted={true}
isOpen={activeStep === StepEnum.selectBed}
label={intl.formatMessage({ id: "Request bedtype" })}
path="/select-bed"
>
<BedType />
</SectionAccordion>
<SectionAccordion
header="Food options"
isCompleted={true}
isOpen={activeStep === StepEnum.breakfast}
label={intl.formatMessage({ id: "Select breakfast options" })}
path="/breakfast"
>
<Breakfast />
</SectionAccordion>
<SectionAccordion
header="Details"
isCompleted={false}
isOpen={activeStep === StepEnum.details}
label={intl.formatMessage({ id: "Enter your details" })}
path="/details"
>
<Details user={null} />
</SectionAccordion>
<SectionAccordion
header="Payment"
isCompleted={false}
isOpen={activeStep === StepEnum.payment}
label={intl.formatMessage({ id: "Select payment method" })}
path="/hotelreservation/select-bed"
>
<Payment hotel={hotel.data.attributes} />
</SectionAccordion>
</section>
<aside className={styles.summary}>
<Summary />
</aside>
</div>
</main>
)
}

View File

@@ -2,4 +2,5 @@
min-height: 100dvh;
max-width: var(--max-width);
margin: 0 auto;
background-color: var(--Base-Background-Primary-Normal);
}