diff --git a/app/[lang]/(live)/(public)/hotelreservation/[section]/page.tsx b/app/[lang]/(live)/(public)/hotelreservation/[section]/page.tsx deleted file mode 100644 index fa2581a54..000000000 --- a/app/[lang]/(live)/(public)/hotelreservation/[section]/page.tsx +++ /dev/null @@ -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) { - 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 ( -
- - -
-
- - - {params.section === "select-bed" ? : null} - - - {params.section === "breakfast" ? : null} - - - {params.section === "details" ?
: null} - - - {params.section === "payment" && ( - - )} - -
-
- -
-
-
- ) -} diff --git a/app/[lang]/(live)/(public)/hotelreservation/[step]/page.module.css b/app/[lang]/(live)/(public)/hotelreservation/[step]/page.module.css index 3266c418d..8962fd5ee 100644 --- a/app/[lang]/(live)/(public)/hotelreservation/[step]/page.module.css +++ b/app/[lang]/(live)/(public)/hotelreservation/[step]/page.module.css @@ -16,10 +16,15 @@ gap: var(--Spacing-x7); } -.main { +.section { flex-grow: 1; } .summary { max-width: 340px; } + +.form { + display: grid; + gap: var(--Spacing-x2); +} diff --git a/app/[lang]/(live)/(public)/hotelreservation/[step]/page.tsx b/app/[lang]/(live)/(public)/hotelreservation/[step]/page.tsx index 3d24e1b1e..25fcac397 100644 --- a/app/[lang]/(live)/(public)/hotelreservation/[step]/page.tsx +++ b/app/[lang]/(live)/(public)/hotelreservation/[step]/page.tsx @@ -1,58 +1,18 @@ "use client" -import { notFound } from "next/navigation" +import { useState } from "react" import { useIntl } from "react-intl" +import { notFound } from "@/server/errors/next" + +import SectionAccordion from "@/components/HotelReservation/SelectRate/SectionAccordion" import Summary from "@/components/HotelReservation/SelectRate/Summary" +import Button from "@/components/TempDesignSystem/Button" import styles from "./page.module.css" 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", -// }, -// ] - enum StepEnum { "select-bed" = "select-bed", breakfast = "breakfast", @@ -68,22 +28,100 @@ function isValidStep(step: string): step is Step { export default function StepPage({ params, -}: PageArgs) { - const { step } = params +}: PageArgs) { + const [activeStep, setActiveStep] = useState(params.step) const intl = useIntl() - if (isValidStep(step)) { + if (!isValidStep(activeStep)) { return notFound() } - switch (step) { + switch (activeStep) { case StepEnum.breakfast: - return
Select BREAKFAST
+ //return
Select BREAKFAST
case StepEnum.details: - return
Select DETAILS
+ //return
Select DETAILS
case StepEnum.payment: - return
Select PAYMENT
+ //return
Select PAYMENT
case StepEnum["select-bed"]: - return
Select BED
+ // return
Select BED
} + + function onNav(step: Step) { + setActiveStep(step) + if (typeof window !== "undefined") { + window.history.pushState({}, "", step) + } + } + + return ( +
+
+
+ +
+ Hejhej lorem ipsim heheheheh andi fpok veoi cdfionaw aoiwube + cskdfaen + +
+
+ +
+ Hejhej lorem ipsim heheheheh andi fpok veoi cdfionaw aoiwube + cskdfaen + +
+
+ +
+ Hejhej lorem ipsim heheheheh andi fpok veoi cdfionaw aoiwube + cskdfaen + +
+
+ +
+ Hejhej lorem ipsim heheheheh andi fpok veoi cdfionaw aoiwube + cskdfaen Hejhej lorem ipsim heheheheh andi fpok veoi cdfionaw + aoiwube cskdfaen Hejhej lorem ipsim heheheheh andi fpok veoi + cdfionaw aoiwube cskdfaen Hejhej lorem ipsim heheheheh andi fpok + veoi cdfionaw aoiwube cskdfaen v Hejhej lorem ipsim heheheheh andi + fpok veoi cdfionaw aoiwube cskdfaen Hejhej lorem ipsim heheheheh + andi fpok veoi cdfionaw aoiwube cskdfaen + +
+
+
+ +
+
+ ) } diff --git a/app/[lang]/(live)/(public)/hotelreservation/layout.module.css b/app/[lang]/(live)/(public)/hotelreservation/layout.module.css index 4478bdb18..0969a7151 100644 --- a/app/[lang]/(live)/(public)/hotelreservation/layout.module.css +++ b/app/[lang]/(live)/(public)/hotelreservation/layout.module.css @@ -1,3 +1,4 @@ .layout { min-height: 100dvh; + background-color: var(--Base-Background-Primary-Normal); } diff --git a/components/HotelReservation/SelectRate/SectionAccordion/index.tsx b/components/HotelReservation/SelectRate/SectionAccordion/index.tsx index ee6a65de3..1e0cf315d 100644 --- a/components/HotelReservation/SelectRate/SectionAccordion/index.tsx +++ b/components/HotelReservation/SelectRate/SectionAccordion/index.tsx @@ -1,57 +1,91 @@ -import { getHotelDataSchema } from "@/server/routers/hotels/output" -import tempHotelData from "@/server/routers/hotels/tempHotelData.json" +"use client" +import { useEffect, useRef } from "react" +import { useIntl } from "react-intl" -import { CheckCircleIcon, ChevronDownIcon } from "@/components/Icons" -import Button from "@/components/TempDesignSystem/Button" +import { CheckIcon, ChevronDownIcon } from "@/components/Icons" import Link from "@/components/TempDesignSystem/Link" -import Body from "@/components/TempDesignSystem/Text/Body" -import Caption from "@/components/TempDesignSystem/Text/Caption" -import { getIntl } from "@/i18n" - -import HotelSelectionHeader from "../../HotelSelectionHeader" +import Footnote from "@/components/TempDesignSystem/Text/Footnote" +import Subtitle from "@/components/TempDesignSystem/Text/Subtitle" import styles from "./sectionAccordion.module.css" import { SectionAccordionProps } from "@/types/components/hotelReservation/selectRate/sectionAccordion" -export default async function SectionAccordion({ +export default function SectionAccordion({ header, - selection, + isOpen, + isCompleted, + label, path, children, }: React.PropsWithChildren) { - const hotel = getHotelDataSchema.parse(tempHotelData) + const intl = useIntl() - const intl = await getIntl() + const contentRef = useRef(null) + const circleRef = useRef(null) + + useEffect(() => { + const content = contentRef.current + const circle = circleRef.current + if (content) { + if (isOpen) { + content.style.maxHeight = `${content.scrollHeight}px` + } else { + content.style.maxHeight = "0" + } + } + + if (circle) { + if (isOpen) { + circle.style.backgroundColor = `var(--UI-Text-Placeholder);` + } else { + circle.style.backgroundColor = `var(--Base-Surface-Subtle-Hover);` + } + } + }, [isOpen]) return ( -
- - -
-
- -
-
- -

{header}

- - {(Array.isArray(selection) ? selection : [selection]).map((s) => ( - - {s} - - ))} -
- {selection && ( - - )} -
- +
+
+
+ {isCompleted ? ( + + ) : null}
- {children} -
+
+
+
+ +

{header}

+
+ + {label} + +
+ {isCompleted && !isOpen && ( + + {intl.formatMessage({ id: "Modify" })}{" "} + + + )} +
+
+ {children} +
+
+ ) } diff --git a/components/HotelReservation/SelectRate/SectionAccordion/sectionAccordion.module.css b/components/HotelReservation/SelectRate/SectionAccordion/sectionAccordion.module.css index ce9dec013..8c1a05ba4 100644 --- a/components/HotelReservation/SelectRate/SectionAccordion/sectionAccordion.module.css +++ b/components/HotelReservation/SelectRate/SectionAccordion/sectionAccordion.module.css @@ -1,21 +1,73 @@ .wrapper { - border-bottom: 1px solid var(--Base-Border-Normal); + position: relative; + display: flex; + flex-direction: row; + gap: var(--Spacing-x3); + + padding-top: var(--Spacing-x3); } -.top { +.wrapper:not(:last-child)::after { + position: absolute; + left: 12px; + bottom: 0; + top: var(--Spacing-x5); + height: 100%; + content: ""; + border-left: 1px solid var(--Primary-Light-On-Surface-Divider-subtle); +} + +.main { + display: flex; + flex-direction: column; + gap: var(--Spacing-x3); + width: 100%; + border-bottom: 1px solid var(--Primary-Light-On-Surface-Divider-subtle); padding-bottom: var(--Spacing-x3); - padding-top: var(--Spacing-x3); +} + +.headerContainer { display: flex; justify-content: space-between; align-items: center; - gap: var(--Spacing-x2); -} - -.header { - flex-grow: 1; } .selection { font-weight: 450; font-size: var(--typography-Title-4-fontSize); } + +.iconWrapper { + position: relative; + top: var(--Spacing-x1); + z-index: 10; +} + +.circle { + width: 24px; + height: 24px; + border-radius: 100px; + transition: background-color 0.4s; + border: 2px solid var(--Base-Border-Inverted); + display: flex; + justify-content: center; + align-items: center; +} + +.circle[data-checked="true"] { + background-color: var(--UI-Input-Controls-Fill-Selected); +} + +.wrapper[data-open="true"] .circle[data-checked="false"] { + background-color: var(--UI-Text-Placeholder); +} + +.wrapper[data-open="false"] .circle[data-checked="false"] { + background-color: var(--Base-Surface-Subtle-Hover); +} + +.content { + overflow: hidden; + transition: max-height 0.4s ease-out; + max-height: 0; +} diff --git a/components/Icons/icon.module.css b/components/Icons/icon.module.css index 282c214cd..3fa235d32 100644 --- a/components/Icons/icon.module.css +++ b/components/Icons/icon.module.css @@ -61,3 +61,8 @@ .uiTextMediumContrast * { fill: var(--UI-Text-Medium-contrast); } + +.blue, +.blue * { + fill: var(--UI-Input-Controls-Fill-Selected); +} diff --git a/components/Icons/variants.ts b/components/Icons/variants.ts index cf2703bd4..12b9cb574 100644 --- a/components/Icons/variants.ts +++ b/components/Icons/variants.ts @@ -17,6 +17,7 @@ const config = { white: styles.white, uiTextHighContrast: styles.uiTextHighContrast, uiTextMediumContrast: styles.uiTextMediumContrast, + blue: styles.blue, }, }, defaultVariants: { diff --git a/components/TempDesignSystem/Text/Subtitle/subtitle.module.css b/components/TempDesignSystem/Text/Subtitle/subtitle.module.css index 07f795811..1fab1f59c 100644 --- a/components/TempDesignSystem/Text/Subtitle/subtitle.module.css +++ b/components/TempDesignSystem/Text/Subtitle/subtitle.module.css @@ -58,3 +58,7 @@ .pale { color: var(--Scandic-Brand-Pale-Peach); } + +.highContrast { + color: var(--UI-Text-High-contrast); +} diff --git a/components/TempDesignSystem/Text/Subtitle/variants.ts b/components/TempDesignSystem/Text/Subtitle/variants.ts index afb33bde1..b73c42230 100644 --- a/components/TempDesignSystem/Text/Subtitle/variants.ts +++ b/components/TempDesignSystem/Text/Subtitle/variants.ts @@ -8,6 +8,7 @@ const config = { black: styles.black, burgundy: styles.burgundy, pale: styles.pale, + highContrast: styles.highContrast, }, textAlign: { center: styles.center, diff --git a/middlewares/bookingFlow.ts b/middlewares/bookingFlow.ts index b65802619..74c3818f1 100644 --- a/middlewares/bookingFlow.ts +++ b/middlewares/bookingFlow.ts @@ -14,19 +14,8 @@ import type { MiddlewareMatcher } from "@/types/middleware" export const middleware: NextMiddleware = async (request) => { const { nextUrl } = request - const lang = findLang(nextUrl.pathname)! - - const pathWithoutTrailingSlash = removeTrailingSlash(nextUrl.pathname) - const pathNameWithoutLang = pathWithoutTrailingSlash.replace(`/${lang}`, "") - const { contentType, uid } = await resolveEntry(pathNameWithoutLang, lang) const headers = getDefaultRequestHeaders(request) - if (uid) { - headers.set("x-uid", uid) - } - if (contentType) { - headers.set("x-contenttype", contentType) - } return NextResponse.next({ request: { headers, diff --git a/types/components/hotelReservation/selectRate/sectionAccordion.ts b/types/components/hotelReservation/selectRate/sectionAccordion.ts index 19d13f836..802e471f9 100644 --- a/types/components/hotelReservation/selectRate/sectionAccordion.ts +++ b/types/components/hotelReservation/selectRate/sectionAccordion.ts @@ -1,5 +1,7 @@ export interface SectionAccordionProps { header: string - selection?: string | string[] + isOpen: boolean + isCompleted: boolean + label: string path: string }