diff --git a/apps/scandic-web/components/HotelReservation/BookingConfirmation/Header/index.tsx b/apps/scandic-web/components/HotelReservation/BookingConfirmation/Header/index.tsx index 48138510d..bf2bf406b 100644 --- a/apps/scandic-web/components/HotelReservation/BookingConfirmation/Header/index.tsx +++ b/apps/scandic-web/components/HotelReservation/BookingConfirmation/Header/index.tsx @@ -11,7 +11,7 @@ import useLang from "@/hooks/useLang" import AddToCalendar from "../../AddToCalendar" import AddToCalendarButton from "./Actions/AddToCalendarButton" -import DownloadInvoice from "./Actions/DownloadInvoice" +// import DownloadInvoice from "./Actions/DownloadInvoice" import { generateDateTime } from "./Actions/helpers" import ManageBooking from "./Actions/ManageBooking" @@ -24,7 +24,7 @@ import type { BookingConfirmationHeaderProps } from "@/types/components/hotelRes export default function Header({ booking, hotel, - mainRef, + // mainRef, refId, }: BookingConfirmationHeaderProps) { const intl = useIntl() @@ -91,7 +91,8 @@ export default function Header({ renderButton={(onPress) => } /> - + {/* Download Invoice will be added later (currently available on My Stay) */} + {/* */} ) diff --git a/apps/scandic-web/components/HotelReservation/EnterDetails/BedType/index.tsx b/apps/scandic-web/components/HotelReservation/EnterDetails/BedType/index.tsx index 334f981c1..31f321986 100644 --- a/apps/scandic-web/components/HotelReservation/EnterDetails/BedType/index.tsx +++ b/apps/scandic-web/components/HotelReservation/EnterDetails/BedType/index.tsx @@ -58,8 +58,7 @@ export default function BedType() { return } - const subscription = methods.watch(() => methods.handleSubmit(onSubmit)()) - return () => subscription.unsubscribe() + methods.watch(() => methods.handleSubmit(onSubmit)()) }, [methods, onSubmit]) return ( diff --git a/apps/scandic-web/components/HotelReservation/EnterDetails/Breakfast/index.tsx b/apps/scandic-web/components/HotelReservation/EnterDetails/Breakfast/index.tsx index e4aa355c9..2d458dd2b 100644 --- a/apps/scandic-web/components/HotelReservation/EnterDetails/Breakfast/index.tsx +++ b/apps/scandic-web/components/HotelReservation/EnterDetails/Breakfast/index.tsx @@ -58,8 +58,7 @@ export default function Breakfast() { if (methods.formState.isSubmitting) { return } - const subscription = methods.watch(() => methods.handleSubmit(onSubmit)()) - return () => subscription.unsubscribe() + methods.watch(() => methods.handleSubmit(onSubmit)()) }, [methods, onSubmit]) return ( diff --git a/apps/scandic-web/components/HotelReservation/EnterDetails/Details/Multiroom/index.tsx b/apps/scandic-web/components/HotelReservation/EnterDetails/Details/Multiroom/index.tsx index 00a6cdbc0..8252e9360 100644 --- a/apps/scandic-web/components/HotelReservation/EnterDetails/Details/Multiroom/index.tsx +++ b/apps/scandic-web/components/HotelReservation/EnterDetails/Details/Multiroom/index.tsx @@ -24,23 +24,20 @@ const formID = "enter-details" export default function Details() { const intl = useIntl() - const { activeRoom, canProceedToPayment, lastRoom } = useEnterDetailsStore( - (state) => ({ - activeRoom: state.activeRoom, - canProceedToPayment: state.canProceedToPayment, - lastRoom: state.lastRoom, - }) - ) + const { canProceedToPayment, lastRoom } = useEnterDetailsStore((state) => ({ + canProceedToPayment: state.canProceedToPayment, + lastRoom: state.lastRoom, + })) const { actions: { updateDetails }, + idx, room, roomNr, } = useRoomContext() const initialData = room.guest - const isPaymentNext = activeRoom === lastRoom - + const isPaymentNext = idx === lastRoom const methods = useForm({ criteriaMode: "all", mode: "all", diff --git a/apps/scandic-web/components/HotelReservation/EnterDetails/Details/RoomOne/index.tsx b/apps/scandic-web/components/HotelReservation/EnterDetails/Details/RoomOne/index.tsx index 5171990b2..6dcffb119 100644 --- a/apps/scandic-web/components/HotelReservation/EnterDetails/Details/RoomOne/index.tsx +++ b/apps/scandic-web/components/HotelReservation/EnterDetails/Details/RoomOne/index.tsx @@ -31,22 +31,20 @@ export default function Details({ user }: DetailsProps) { const intl = useIntl() const [isMemberPriceModalOpen, setIsMemberPriceModalOpen] = useState(false) - const { activeRoom, canProceedToPayment, lastRoom } = useEnterDetailsStore( - (state) => ({ - activeRoom: state.activeRoom, - canProceedToPayment: state.canProceedToPayment, - lastRoom: state.lastRoom, - }) - ) + const { canProceedToPayment, lastRoom } = useEnterDetailsStore((state) => ({ + canProceedToPayment: state.canProceedToPayment, + lastRoom: state.lastRoom, + })) const { actions: { updateDetails }, + idx, room, roomNr, } = useRoomContext() const initialData = room.guest - const memberRate = "member" in room.roomRate ? room.roomRate.member : null - const isPaymentNext = activeRoom === lastRoom + const memberRate = "member" in room.roomRate ? room.roomRate.member : null + const isPaymentNext = idx === lastRoom const methods = useForm({ criteriaMode: "all", diff --git a/apps/scandic-web/components/HotelReservation/EnterDetails/Payment/PaymentClient.tsx b/apps/scandic-web/components/HotelReservation/EnterDetails/Payment/PaymentClient.tsx index ddb3785c6..687a98739 100644 --- a/apps/scandic-web/components/HotelReservation/EnterDetails/Payment/PaymentClient.tsx +++ b/apps/scandic-web/components/HotelReservation/EnterDetails/Payment/PaymentClient.tsx @@ -67,13 +67,13 @@ export default function PaymentClient({ const [showPaymentAlert, setShowPaymentAlert] = useState(false) - const { booking, canProceedToPayment, rooms, totalPrice } = - useEnterDetailsStore((state) => ({ - booking: state.booking, - canProceedToPayment: state.canProceedToPayment, - rooms: state.rooms, - totalPrice: state.totalPrice, - })) + const { booking, rooms, totalPrice } = useEnterDetailsStore((state) => ({ + booking: state.booking, + rooms: state.rooms, + totalPrice: state.totalPrice, + })) + + const allRoomsComplete = rooms.every((r) => r.isComplete) const bookingMustBeGuaranteed = rooms.some(({ room }, idx) => { if (idx === 0 && isUserLoggedIn && room.memberMustBeGuaranteed) { @@ -390,7 +390,7 @@ export default function PaymentClient({ return (
diff --git a/apps/scandic-web/components/HotelReservation/EnterDetails/Room/Multiroom.tsx b/apps/scandic-web/components/HotelReservation/EnterDetails/Room/Multiroom.tsx index 5de08b2bd..8478a3e59 100644 --- a/apps/scandic-web/components/HotelReservation/EnterDetails/Room/Multiroom.tsx +++ b/apps/scandic-web/components/HotelReservation/EnterDetails/Room/Multiroom.tsx @@ -7,7 +7,7 @@ import BedType from "@/components/HotelReservation/EnterDetails/BedType" import Breakfast from "@/components/HotelReservation/EnterDetails/Breakfast" import Details from "@/components/HotelReservation/EnterDetails/Details/Multiroom" import Header from "@/components/HotelReservation/EnterDetails/Room/Header" -import SectionAccordion from "@/components/HotelReservation/EnterDetails/SectionAccordion" +import Section from "@/components/HotelReservation/EnterDetails/Section" import SelectedRoom from "@/components/HotelReservation/EnterDetails/SelectedRoom" import Title from "@/components/TempDesignSystem/Text/Title" import { useRoomContext } from "@/contexts/Details/Room" @@ -16,12 +16,31 @@ import { StepEnum } from "@/types/enums/step" export default function Multiroom() { const intl = useIntl() - const { room, roomNr } = useRoomContext() - const breakfastPackages = useEnterDetailsStore( - (state) => state.breakfastPackages - ) + const { idx, room, roomNr, steps } = useRoomContext() + const { breakfastPackages, rooms } = useEnterDetailsStore((state) => ({ + breakfastPackages: state.breakfastPackages, + rooms: state.rooms, + })) + const showBreakfastStep = !room.breakfastIncluded && !!breakfastPackages?.length + + const arePreviousRoomsValid = rooms.slice(0, idx).every((r) => r.isComplete) + + const isBreakfastStepValid = showBreakfastStep + ? steps[StepEnum.breakfast]?.isValid + : true + + const isBreakfastDisabled = !( + arePreviousRoomsValid && steps[StepEnum.selectBed].isValid + ) + + const isDetailsDisabled = !( + arePreviousRoomsValid && + steps[StepEnum.selectBed].isValid && + isBreakfastStepValid + ) + return ( <section> <Header> @@ -38,34 +57,37 @@ export default function Multiroom() { <SelectedRoom /> {room.bedTypes ? ( - <SectionAccordion + <Section header={intl.formatMessage({ id: "Select bed" })} label={intl.formatMessage({ id: "Request bedtype" })} step={StepEnum.selectBed} + disabled={!arePreviousRoomsValid} > <BedType /> - </SectionAccordion> + </Section> ) : null} {showBreakfastStep ? ( - <SectionAccordion + <Section header={intl.formatMessage({ id: "Food options" })} label={intl.formatMessage({ id: "Select breakfast options", })} step={StepEnum.breakfast} + disabled={isBreakfastDisabled} > <Breakfast /> - </SectionAccordion> + </Section> ) : null} - <SectionAccordion + <Section header={intl.formatMessage({ id: "Details" })} step={StepEnum.details} label={intl.formatMessage({ id: "Enter your details" })} + disabled={isDetailsDisabled} > <Details /> - </SectionAccordion> + </Section> </section> ) } diff --git a/apps/scandic-web/components/HotelReservation/EnterDetails/Room/One.tsx b/apps/scandic-web/components/HotelReservation/EnterDetails/Room/One.tsx index 090bf0988..7aece6f3c 100644 --- a/apps/scandic-web/components/HotelReservation/EnterDetails/Room/One.tsx +++ b/apps/scandic-web/components/HotelReservation/EnterDetails/Room/One.tsx @@ -7,7 +7,7 @@ import BedType from "@/components/HotelReservation/EnterDetails/BedType" import Breakfast from "@/components/HotelReservation/EnterDetails/Breakfast" import Details from "@/components/HotelReservation/EnterDetails/Details/RoomOne" import Header from "@/components/HotelReservation/EnterDetails/Room/Header" -import SectionAccordion from "@/components/HotelReservation/EnterDetails/SectionAccordion" +import Section from "@/components/HotelReservation/EnterDetails/Section" import SelectedRoom from "@/components/HotelReservation/EnterDetails/SelectedRoom" import Title from "@/components/TempDesignSystem/Text/Title" import { useRoomContext } from "@/contexts/Details/Room" @@ -17,13 +17,12 @@ import type { SafeUser } from "@/types/user" export default function RoomOne({ user }: { user: SafeUser }) { const intl = useIntl() - const { room } = useRoomContext() - const { breakfastPackages, rooms } = useEnterDetailsStore((state) => ({ + const { room, steps } = useRoomContext() + const { breakfastPackages, isMultiroom } = useEnterDetailsStore((state) => ({ breakfastPackages: state.breakfastPackages, - rooms: state.rooms, + isMultiroom: state.rooms.length > 1, })) - const isMultiroom = rooms.length > 1 const showBreakfastStep = !room.breakfastIncluded && !!breakfastPackages?.length return ( @@ -44,34 +43,41 @@ export default function RoomOne({ user }: { user: SafeUser }) { <SelectedRoom /> {room.bedTypes ? ( - <SectionAccordion + <Section header={intl.formatMessage({ id: "Select bed" })} label={intl.formatMessage({ id: "Request bedtype" })} step={StepEnum.selectBed} > <BedType /> - </SectionAccordion> + </Section> ) : null} {showBreakfastStep ? ( - <SectionAccordion + <Section header={intl.formatMessage({ id: "Food options" })} label={intl.formatMessage({ id: "Select breakfast options", })} step={StepEnum.breakfast} + disabled={!steps[StepEnum.selectBed].isValid} > <Breakfast /> - </SectionAccordion> + </Section> ) : null} - <SectionAccordion + <Section header={intl.formatMessage({ id: "Details" })} step={StepEnum.details} label={intl.formatMessage({ id: "Enter your details" })} + disabled={ + !( + steps[StepEnum.selectBed].isValid && + steps[StepEnum.breakfast]?.isValid !== false + ) + } > <Details user={user} /> - </SectionAccordion> + </Section> </section> ) } diff --git a/apps/scandic-web/components/HotelReservation/EnterDetails/Section/index.tsx b/apps/scandic-web/components/HotelReservation/EnterDetails/Section/index.tsx new file mode 100644 index 000000000..0af7baf8c --- /dev/null +++ b/apps/scandic-web/components/HotelReservation/EnterDetails/Section/index.tsx @@ -0,0 +1,69 @@ +"use client" +import { useEffect, useState } from "react" +import { useIntl } from "react-intl" + +import Footnote from "@/components/TempDesignSystem/Text/Footnote" +import Subtitle from "@/components/TempDesignSystem/Text/Subtitle" +import { useRoomContext } from "@/contexts/Details/Room" + +import styles from "./section.module.css" + +import type { SectionProps } from "@/types/components/hotelReservation/enterDetails/section" +import { StepEnum } from "@/types/enums/step" + +export default function Section({ + children, + header, + label, + step, + disabled, +}: React.PropsWithChildren<SectionProps>) { + const intl = useIntl() + + const { + room: { bedType, breakfast }, + } = useRoomContext() + + const [title, setTitle] = useState(label) + + const noBreakfastTitle = intl.formatMessage({ id: "No breakfast" }) + const breakfastTitle = intl.formatMessage({ id: "Breakfast buffet" }) + + useEffect(() => { + if (step === StepEnum.selectBed && bedType) { + setTitle(bedType.description) + } + // If breakfast step, check if an option has been selected + if (step === StepEnum.breakfast && breakfast !== undefined) { + if (breakfast === false) { + setTitle(noBreakfastTitle) + } else { + setTitle(breakfastTitle) + } + } + }, [bedType, breakfast, setTitle, step, breakfastTitle, noBreakfastTitle]) + + return ( + <div + className={`${styles.accordion} ${disabled ? styles.disabled : ""}`} + data-step={step} + > + <header className={styles.header}> + <Footnote + className={styles.title} + asChild + textTransform="uppercase" + type="label" + > + <h2>{header}</h2> + </Footnote> + <Subtitle className={styles.selection} type="two"> + {title} + </Subtitle> + </header> + <div className={styles.content}> + <div className={styles.contentWrapper}>{children}</div> + </div> + </div> + ) +} diff --git a/apps/scandic-web/components/HotelReservation/EnterDetails/Section/section.module.css b/apps/scandic-web/components/HotelReservation/EnterDetails/Section/section.module.css new file mode 100644 index 000000000..50870a7f1 --- /dev/null +++ b/apps/scandic-web/components/HotelReservation/EnterDetails/Section/section.module.css @@ -0,0 +1,45 @@ +.accordion { + --header-height: 2.4em; + --circle-height: 24px; + gap: var(--Spacing-x3); + width: 100%; + padding-top: var(--Spacing-x3); + display: grid; + grid-template-areas: "header" "content"; + grid-template-rows: var(--header-height) 1fr; + column-gap: var(--Spacing-x-one-and-half); +} + +.header { + grid-area: header; +} + +.title { + grid-area: title; + text-align: start; +} + +.selection { + grid-area: selection; +} + +.contentWrapper { + padding-bottom: var(--Spacing-x3); +} + +.content { + grid-area: content; + border-bottom: 1px solid var(--Primary-Light-On-Surface-Divider-subtle); +} + +.disabled { + opacity: 0.5; + pointer-events: none; +} + +@media screen and (min-width: 768px) { + .accordion { + column-gap: var(--Spacing-x3); + grid-template-areas: "header" "content"; + } +} diff --git a/apps/scandic-web/components/HotelReservation/EnterDetails/SectionAccordion/index.tsx b/apps/scandic-web/components/HotelReservation/EnterDetails/SectionAccordion/index.tsx deleted file mode 100644 index f966cf246..000000000 --- a/apps/scandic-web/components/HotelReservation/EnterDetails/SectionAccordion/index.tsx +++ /dev/null @@ -1,148 +0,0 @@ -"use client" -import { useEffect, useRef, useState } from "react" -import { useIntl } from "react-intl" - -import { MaterialIcon } from "@scandic-hotels/design-system/Icons" - -import Footnote from "@/components/TempDesignSystem/Text/Footnote" -import Subtitle from "@/components/TempDesignSystem/Text/Subtitle" -import { useRoomContext } from "@/contexts/Details/Room" -import useStickyPosition from "@/hooks/useStickyPosition" - -import styles from "./sectionAccordion.module.css" - -import type { SectionAccordionProps } from "@/types/components/hotelReservation/selectRate/sectionAccordion" -import { StepEnum } from "@/types/enums/step" - -export default function SectionAccordion({ - children, - header, - label, - step, -}: React.PropsWithChildren<SectionAccordionProps>) { - const intl = useIntl() - const stickyPosition = useStickyPosition({}) - const { - actions: { setStep }, - currentStep, - isActiveRoom, - room: { bedType, breakfast, isAvailable }, - steps, - } = useRoomContext() - - const isStepComplete = !!(steps[step]?.isValid && isAvailable) - const [isOpen, setIsOpen] = useState(false) - const [title, setTitle] = useState(label) - - const noBreakfastTitle = intl.formatMessage({ id: "No breakfast" }) - const breakfastTitle = intl.formatMessage({ id: "Breakfast buffet" }) - - // useScrollToActiveSection(step, steps, currentStep === step) - - useEffect(() => { - if (step === StepEnum.selectBed && bedType) { - setTitle(bedType.description) - } - // If breakfast step, check if an option has been selected - if (step === StepEnum.breakfast && breakfast !== undefined) { - if (breakfast === false) { - setTitle(noBreakfastTitle) - } else { - setTitle(breakfastTitle) - } - } - }, [bedType, breakfast, setTitle, step, breakfastTitle, noBreakfastTitle]) - - const accordionRef = useRef<HTMLDivElement>(null) - - useEffect(() => { - const shouldBeOpen = currentStep === step && isActiveRoom && isAvailable - setIsOpen(shouldBeOpen) - - // Scroll to this section when it is opened, - // but wait for the accordion animations to finish, - // else the height calculations will not be correct and - // the scroll position will be off. - if (shouldBeOpen) { - const handleTransitionEnd = () => { - if (accordionRef.current) { - window.scrollTo({ - top: accordionRef.current.offsetTop - stickyPosition.getTopOffset(), - behavior: "smooth", - }) - } - accordionRef.current?.removeEventListener( - "transitionend", - handleTransitionEnd - ) - } - - if (accordionRef.current) { - accordionRef.current.addEventListener( - "transitionend", - handleTransitionEnd, - { once: true } - ) - } - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [currentStep, isActiveRoom, isAvailable, setIsOpen, step]) - - function goToStep() { - setStep(step) - } - - function close() { - setIsOpen(false) - goToStep() - } - - const textColor = - isStepComplete || isOpen ? "uiTextHighContrast" : "baseTextDisabled" - return ( - <div - className={styles.accordion} - data-section-open={isOpen} - data-step={step} - ref={accordionRef} - > - <div className={styles.iconWrapper}> - <div className={styles.circle} data-checked={isStepComplete}> - {isStepComplete ? ( - <MaterialIcon icon="check" color="Icon/Inverted" size={16} /> - ) : null} - </div> - </div> - <header className={styles.header}> - <button - onClick={isOpen ? close : goToStep} - disabled={!isStepComplete} - className={styles.modifyButton} - > - <Footnote - className={styles.title} - asChild - textTransform="uppercase" - type="label" - color={textColor} - > - <h2>{header}</h2> - </Footnote> - <Subtitle className={styles.selection} type="two" color={textColor}> - {title} - </Subtitle> - {isStepComplete && ( - <MaterialIcon - icon="keyboard_arrow_down" - className={`${styles.button} ${isOpen ? styles.buttonOpen : ""}`} - color="Icon/Interactive/Default" - /> - )} - </button> - </header> - <div className={styles.content}> - <div className={styles.contentWrapper}>{children}</div> - </div> - </div> - ) -} diff --git a/apps/scandic-web/components/HotelReservation/EnterDetails/SectionAccordion/sectionAccordion.module.css b/apps/scandic-web/components/HotelReservation/EnterDetails/SectionAccordion/sectionAccordion.module.css deleted file mode 100644 index 5d7c9a8d9..000000000 --- a/apps/scandic-web/components/HotelReservation/EnterDetails/SectionAccordion/sectionAccordion.module.css +++ /dev/null @@ -1,127 +0,0 @@ -.accordion { - --header-height: 2.4em; - --circle-height: 24px; - - gap: var(--Spacing-x3); - width: 100%; - padding-top: var(--Spacing-x3); - transition: 0.3s ease-out; - - display: grid; - grid-template-areas: "circle header" "content content"; - grid-template-columns: auto 1fr; - grid-template-rows: var(--header-height) 0fr; - - column-gap: var(--Spacing-x-one-and-half); - transform-origin: top; -} - -.header { - grid-area: header; -} - -.modifyButton { - display: grid; - grid-template-areas: "title button" "selection button"; - cursor: pointer; - background-color: transparent; - border: none; - width: 100%; - padding: 0; -} - -.modifyButton:disabled { - cursor: default; -} - -.title { - grid-area: title; - text-align: start; -} - -.button { - grid-area: button; - justify-self: flex-end; - transform-origin: 50% 50%; - transition: transform 0.3s; -} - -.buttonOpen { - transform: rotate(180deg); -} -.selection { - grid-area: selection; -} - -.iconWrapper { - position: relative; - grid-area: circle; -} - -.circle { - width: var(--circle-height); - height: var(--circle-height); - 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); -} - -.accordion[data-section-open="true"] .circle[data-checked="false"] { - background-color: var(--UI-Text-Placeholder); -} - -.accordion[data-section-open="false"] .circle[data-checked="false"] { - background-color: var(--Base-Surface-Subtle-Hover); -} - -.accordion[data-section-open="true"] { - grid-template-rows: var(--header-height) 1fr; -} - -.contentWrapper { - opacity: 0; - padding-bottom: var(--Spacing-x3); -} - -.accordion[data-section-open="true"] .contentWrapper { - opacity: 1; -} -.content { - overflow: hidden; - grid-area: content; - border-bottom: 1px solid var(--Primary-Light-On-Surface-Divider-subtle); - transform-origin: top; - transition: opacity 0.2s linear; -} - -.accordion[data-section-open="true"] .content { - overflow: visible; -} - -@media screen and (min-width: 768px) { - .accordion { - column-gap: var(--Spacing-x3); - grid-template-areas: "circle header" "circle content"; - } - - .iconWrapper { - top: var(--Spacing-x1); - } - - .accordion:not(:last-child) .iconWrapper::after { - position: absolute; - left: 12px; - bottom: calc(0px - var(--Spacing-x5)); - top: var(--circle-height); - - content: ""; - border-left: 1px solid var(--Primary-Light-On-Surface-Divider-subtle); - } -} diff --git a/apps/scandic-web/components/HotelReservation/EnterDetails/SelectedRoom/index.tsx b/apps/scandic-web/components/HotelReservation/EnterDetails/SelectedRoom/index.tsx index 08a16d3c6..922383465 100644 --- a/apps/scandic-web/components/HotelReservation/EnterDetails/SelectedRoom/index.tsx +++ b/apps/scandic-web/components/HotelReservation/EnterDetails/SelectedRoom/index.tsx @@ -25,7 +25,7 @@ export default function SelectedRoom() { const lang = useLang() const router = useRouter() const [isPending, startTransition] = useTransition() - const { room, roomNr } = useRoomContext() + const { room, idx } = useRoomContext() const { hotelId, searchParamsStr } = useEnterDetailsStore((state) => ({ hotelId: state.booking.hotelId, searchParamsStr: state.searchParamString, @@ -33,8 +33,8 @@ export default function SelectedRoom() { function changeRoom() { const searchParams = new URLSearchParams(searchParamsStr) - // rooms are index based, thus need for subtraction - searchParams.set("modifyRateIndex", `${roomNr - 1}`) + + searchParams.set("modifyRateIndex", `${idx}`) startTransition(() => { router.push(`${selectRate(lang)}?${searchParams.toString()}`) }) diff --git a/apps/scandic-web/components/HotelReservation/EnterDetails/SelectedRoom/selectedRoom.module.css b/apps/scandic-web/components/HotelReservation/EnterDetails/SelectedRoom/selectedRoom.module.css index 18d14d709..7bb60b37f 100644 --- a/apps/scandic-web/components/HotelReservation/EnterDetails/SelectedRoom/selectedRoom.module.css +++ b/apps/scandic-web/components/HotelReservation/EnterDetails/SelectedRoom/selectedRoom.module.css @@ -41,24 +41,6 @@ align-self: flex-start; } -.iconWrapper { - position: relative; -} - -.circle { - width: 24px; - height: 24px; - border-radius: 100px; - border: 2px solid var(--Base-Border-Inverted); - display: flex; - justify-content: center; - align-items: center; -} - -.circle { - background-color: var(--UI-Input-Controls-Fill-Selected); -} - .wrapper[data-available="false"] .circle { background-color: var(--Base-Surface-Subtle-Hover); } @@ -92,14 +74,4 @@ .rate::after { content: ")"; } - - .wrapper:not(:last-child)::after { - position: absolute; - left: 12px; - bottom: 0; - top: var(--Spacing-x7); - height: 100%; - content: ""; - border-left: 1px solid var(--Primary-Light-On-Surface-Divider-subtle); - } } diff --git a/apps/scandic-web/providers/Details/RoomProvider.tsx b/apps/scandic-web/providers/Details/RoomProvider.tsx index 918c03caf..06d3f4b36 100644 --- a/apps/scandic-web/providers/Details/RoomProvider.tsx +++ b/apps/scandic-web/providers/Details/RoomProvider.tsx @@ -7,22 +7,21 @@ import { RoomContext } from "@/contexts/Details/Room" import type { RoomProviderProps } from "@/types/providers/details/room" export default function RoomProvider({ children, idx }: RoomProviderProps) { - const { actions, activeRoom, currentStep, isComplete, room, steps } = - useEnterDetailsStore((state) => ({ + const { actions, isComplete, room, steps } = useEnterDetailsStore( + (state) => ({ actions: state.rooms[idx].actions, - activeRoom: state.activeRoom, - currentStep: state.rooms[idx].currentStep, isComplete: state.rooms[idx].isComplete, room: state.rooms[idx].room, steps: state.rooms[idx].steps, - })) + }) + ) + return ( <RoomContext.Provider value={{ actions, - currentStep, + idx, isComplete, - isActiveRoom: activeRoom === idx, room, roomNr: idx + 1, steps, diff --git a/apps/scandic-web/providers/EnterDetailsProvider.tsx b/apps/scandic-web/providers/EnterDetailsProvider.tsx index 5652f0e00..ef3fed8ed 100644 --- a/apps/scandic-web/providers/EnterDetailsProvider.tsx +++ b/apps/scandic-web/providers/EnterDetailsProvider.tsx @@ -147,7 +147,6 @@ export default function EnterDetailsProvider({ ) currentRoom.isComplete = !invalidStep - currentRoom.currentStep = invalidStep ? invalidStep.step : null return currentRoom }) @@ -185,18 +184,12 @@ export default function EnterDetailsProvider({ nights ) - const activeRoom = filteredOutMissingRooms.findIndex( - (room) => !room.isComplete - ) - writeToSessionStorage({ - activeRoom, booking, rooms: filteredOutMissingRooms, }) storeRef.current?.setState({ - activeRoom: storedValues.activeRoom, canProceedToPayment, rooms: filteredOutMissingRooms, totalPrice, diff --git a/apps/scandic-web/stores/enter-details/helpers.ts b/apps/scandic-web/stores/enter-details/helpers.ts index e134571df..5abd92ce1 100644 --- a/apps/scandic-web/stores/enter-details/helpers.ts +++ b/apps/scandic-web/stores/enter-details/helpers.ts @@ -7,11 +7,7 @@ import type { Price } from "@/types/components/hotelReservation/price" import type { SelectRateSearchParams } from "@/types/components/hotelReservation/selectRate/selectRate" import { CurrencyEnum } from "@/types/enums/currency" import { StepEnum } from "@/types/enums/step" -import type { - DetailsState, - PersistedState, - RoomState, -} from "@/types/stores/enter-details" +import type { PersistedState, RoomState } from "@/types/stores/enter-details" import type { SafeUser } from "@/types/user" export function extractGuestFromUser(user: NonNullable<SafeUser>) { @@ -513,54 +509,12 @@ export function findNextInvalidStep(roomState: RoomState) { ) } -export const selectNextStep = (room: RoomState) => { - if (room.currentStep === null) { - throw new Error("getNextStep: currentStep is null") - } - - if (!room.steps[room.currentStep]?.isValid) { - return room.currentStep - } - - const stepsArray = Object.values(room.steps) - const currentIndex = stepsArray.findIndex( - (step) => step?.step === room.currentStep - ) - if (currentIndex === stepsArray.length - 1) { - return null - } - - const nextInvalidStep = stepsArray - .slice(currentIndex + 1) - .find((step) => !step.isValid) - - return nextInvalidStep?.step ?? null -} - export const checkRoomProgress = (steps: RoomState["steps"]) => { return Object.values(steps) .filter(Boolean) .every((step) => step.isValid) } -export function handleStepProgression(room: RoomState, state: DetailsState) { - const isAllRoomsCompleted = state.rooms.every((r) => r.isComplete) - if (isAllRoomsCompleted) { - room.currentStep = null - state.canProceedToPayment = true - } else if (room.isComplete) { - room.currentStep = null - const nextRoomIndex = state.rooms.findIndex((r) => !r.isComplete) - state.activeRoom = nextRoomIndex - - const nextRoom = state.rooms[nextRoomIndex] - const nextStep = selectNextStep(nextRoom) - nextRoom.currentStep = nextStep - } else if (selectNextStep(room)) { - room.currentStep = selectNextStep(room) - } -} - export function readFromSessionStorage(): PersistedState | undefined { if (typeof window === "undefined") { return undefined diff --git a/apps/scandic-web/stores/enter-details/index.ts b/apps/scandic-web/stores/enter-details/index.ts index 08db44915..4f7e4ee27 100644 --- a/apps/scandic-web/stores/enter-details/index.ts +++ b/apps/scandic-web/stores/enter-details/index.ts @@ -15,10 +15,8 @@ import { calculateVoucherPrice, checkRoomProgress, extractGuestFromUser, - findNextInvalidStep, getRoomPrice, getTotalPrice, - handleStepProgression, writeToSessionStorage, } from "./helpers" @@ -114,7 +112,6 @@ export function createDetailsStore( }) return create<DetailsState>()((set) => ({ - activeRoom: 0, booking: initialState.booking, breakfastPackages, canProceedToPayment: false, @@ -141,72 +138,8 @@ export function createDetailsStore( delete steps[StepEnum.breakfast] } - const currentStep = - Object.values(steps).find((step) => !step.isValid)?.step ?? - StepEnum.selectBed - return { actions: { - setStep(step) { - return set( - produce((state: DetailsState) => { - const isSameRoom = idx === state.activeRoom - const room = state.rooms[idx] - if (isSameRoom) { - // Closed same accordion as was open - if (step === room.currentStep) { - if (room.isComplete) { - // Room is complete, move to next room or payment - const nextRoomIdx = state.rooms.findIndex( - (r) => !r.isComplete - ) - state.activeRoom = nextRoomIdx - // Done, proceed to payment - if (nextRoomIdx === -1) { - room.currentStep = null - } else { - const nextRoom = state.rooms[nextRoomIdx] - const nextInvalidStep = findNextInvalidStep(nextRoom) - nextRoom.currentStep = nextInvalidStep - } - } else { - room.currentStep = findNextInvalidStep(room) - } - } else { - if (room.steps[step]?.isValid) { - room.currentStep = step - } else { - room.currentStep = findNextInvalidStep(room) - } - } - } else { - const arePreviousRoomsCompleted = state.rooms - .slice(0, idx) - .every((room) => room.isComplete) - if (arePreviousRoomsCompleted) { - state.activeRoom = idx - if (room.steps[step]?.isValid) { - room.currentStep = step - } else { - room.currentStep = findNextInvalidStep(room) - } - } else { - const firstIncompleteRoom = state.rooms.findIndex( - (r) => !r.isComplete - ) - state.activeRoom = firstIncompleteRoom - if (firstIncompleteRoom === -1) { - // All rooms are done, proceed to payment - room.currentStep = null - } else { - const nextRoom = state.rooms[firstIncompleteRoom] - nextRoom.currentStep = findNextInvalidStep(nextRoom) - } - } - } - }) - ) - }, updateBedType(bedType) { return set( produce((state: DetailsState) => { @@ -220,10 +153,7 @@ export function createDetailsStore( state.rooms[idx].isComplete = true } - handleStepProgression(state.rooms[idx], state) - writeToSessionStorage({ - activeRoom: state.activeRoom, booking: state.booking, rooms: state.rooms, }) @@ -340,10 +270,7 @@ export function createDetailsStore( state.rooms[idx].isComplete = true } - handleStepProgression(currentRoom, state) - writeToSessionStorage({ - activeRoom: state.activeRoom, booking: state.booking, rooms: state.rooms, }) @@ -408,10 +335,7 @@ export function createDetailsStore( state.rooms[idx].isComplete = true } - handleStepProgression(state.rooms[idx], state) - writeToSessionStorage({ - activeRoom: state.activeRoom, booking: state.booking, rooms: state.rooms, }) @@ -461,10 +385,7 @@ export function createDetailsStore( state.rooms[idx].isComplete = true } - handleStepProgression(state.rooms[idx], state) - writeToSessionStorage({ - activeRoom: state.activeRoom, booking: state.booking, rooms: state.rooms, }) @@ -490,8 +411,6 @@ export function createDetailsStore( comment: "", }, }, - - currentStep, isComplete: false, steps, } diff --git a/apps/scandic-web/types/components/hotelReservation/selectRate/sectionAccordion.ts b/apps/scandic-web/types/components/hotelReservation/enterDetails/section.ts similarity index 66% rename from apps/scandic-web/types/components/hotelReservation/selectRate/sectionAccordion.ts rename to apps/scandic-web/types/components/hotelReservation/enterDetails/section.ts index 23697b11b..c5cc9b534 100644 --- a/apps/scandic-web/types/components/hotelReservation/selectRate/sectionAccordion.ts +++ b/apps/scandic-web/types/components/hotelReservation/enterDetails/section.ts @@ -1,7 +1,8 @@ import type { StepEnum } from "@/types/enums/step" -export interface SectionAccordionProps { +export interface SectionProps { header: string label: string step: StepEnum + disabled?: boolean } diff --git a/apps/scandic-web/types/contexts/details/room.ts b/apps/scandic-web/types/contexts/details/room.ts index 04685a3f3..453e8604a 100644 --- a/apps/scandic-web/types/contexts/details/room.ts +++ b/apps/scandic-web/types/contexts/details/room.ts @@ -1,19 +1,16 @@ import type { BreakfastPackage } from "@/types/components/hotelReservation/breakfast" import type { BedTypeSchema } from "@/types/components/hotelReservation/enterDetails/bedType" import type { DetailsSchema } from "@/types/components/hotelReservation/enterDetails/details" -import type { StepEnum } from "@/types/enums/step" import type { RoomState } from "@/types/stores/enter-details" export interface RoomContextValue { actions: { - setStep: (step: StepEnum) => void updateBedType: (data: BedTypeSchema) => void updateBreakfast: (data: BreakfastPackage | false) => void updateDetails: (data: DetailsSchema) => void } - currentStep: RoomState["currentStep"] isComplete: RoomState["isComplete"] - isActiveRoom: boolean + idx: number room: RoomState["room"] roomNr: number steps: RoomState["steps"] diff --git a/apps/scandic-web/types/contexts/enter-details.ts b/apps/scandic-web/types/contexts/enter-details.ts index 176418279..4cabf007c 100644 --- a/apps/scandic-web/types/contexts/enter-details.ts +++ b/apps/scandic-web/types/contexts/enter-details.ts @@ -1,3 +1,3 @@ -import { createDetailsStore } from "@/stores/enter-details" +import type { createDetailsStore } from "@/stores/enter-details" export type DetailsStore = ReturnType<typeof createDetailsStore> diff --git a/apps/scandic-web/types/stores/enter-details.ts b/apps/scandic-web/types/stores/enter-details.ts index d9ed9f3c7..648e2590e 100644 --- a/apps/scandic-web/types/stores/enter-details.ts +++ b/apps/scandic-web/types/stores/enter-details.ts @@ -59,13 +59,11 @@ export interface Room extends InitialRoomData { export interface RoomState { actions: { - setStep: (step: StepEnum) => void updateBedType: (data: BedTypeSchema) => void updateBreakfast: (data: BreakfastPackage | false) => void updateDetails: (data: DetailsSchema) => void updateMultiroomDetails: (data: MultiroomDetailsSchema) => void } - currentStep: StepEnum | null isComplete: boolean room: Room steps: { @@ -88,7 +86,6 @@ export interface DetailsState { toggleSummaryOpen: () => void updateSeachParamString: (searchParamString: string) => void } - activeRoom: number booking: SelectRateSearchParams breakfastPackages: BreakfastPackages | null canProceedToPayment: boolean @@ -102,7 +99,6 @@ export interface DetailsState { } export type PersistedState = { - activeRoom: number booking: SelectRateSearchParams rooms: RoomState[] }