Merged in feat/SW-1076-no-room-availability (pull request #1467)
Feat/SW-1076 no room availability * fix: update booking error codes * feat(SW-1076): handle no room availabilty on enter-details * fix: parse to json in api mutation instead of expecting json * fix: remove 'isComplete' state from sectionAccordion because it was not needed Approved-by: Simon.Emanuelsson
This commit is contained in:
@@ -7,6 +7,7 @@ import { FormProvider, useForm } from "react-hook-form"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import {
|
||||
BookingErrorCodeEnum,
|
||||
BookingStatusEnum,
|
||||
PAYMENT_METHOD_TITLES,
|
||||
PaymentMethodEnum,
|
||||
@@ -107,6 +108,15 @@ export default function PaymentClient({
|
||||
const initiateBooking = trpc.booking.create.useMutation({
|
||||
onSuccess: (result) => {
|
||||
if (result) {
|
||||
if ("error" in result) {
|
||||
if (result.cause === BookingErrorCodeEnum.AvailabilityError) {
|
||||
window.location.reload() // reload to refetch room data because we dont know which room is unavailable
|
||||
} else {
|
||||
handlePaymentError(result.cause)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
setBookingNumber(result.id)
|
||||
|
||||
const priceChange = result.rooms.find(
|
||||
|
||||
@@ -25,14 +25,12 @@ export default function SectionAccordion({
|
||||
actions: { setStep },
|
||||
currentStep,
|
||||
isActiveRoom,
|
||||
room: { bedType, breakfast },
|
||||
room: { bedType, breakfast, isAvailable },
|
||||
steps,
|
||||
} = useRoomContext()
|
||||
|
||||
const [isComplete, setIsComplete] = useState(false)
|
||||
const isStepComplete = !!(steps[step]?.isValid && isAvailable)
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
const isValid = steps[step]?.isValid ?? false
|
||||
|
||||
const [title, setTitle] = useState(label)
|
||||
|
||||
const noBreakfastTitle = intl.formatMessage({ id: "No breakfast" })
|
||||
@@ -54,14 +52,10 @@ export default function SectionAccordion({
|
||||
}
|
||||
}, [bedType, breakfast, setTitle, step, breakfastTitle, noBreakfastTitle])
|
||||
|
||||
useEffect(() => {
|
||||
setIsComplete(isValid)
|
||||
}, [isValid, setIsComplete])
|
||||
|
||||
const accordionRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
const shouldBeOpen = currentStep === step && isActiveRoom
|
||||
const shouldBeOpen = currentStep === step && isActiveRoom && isAvailable
|
||||
setIsOpen(shouldBeOpen)
|
||||
|
||||
// Scroll to this section when it is opened,
|
||||
@@ -91,7 +85,7 @@ export default function SectionAccordion({
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [currentStep, isActiveRoom, setIsOpen, step])
|
||||
}, [currentStep, isActiveRoom, isAvailable, setIsOpen, step])
|
||||
|
||||
function goToStep() {
|
||||
setStep(step)
|
||||
@@ -103,7 +97,7 @@ export default function SectionAccordion({
|
||||
}
|
||||
|
||||
const textColor =
|
||||
isComplete || isOpen ? "uiTextHighContrast" : "baseTextDisabled"
|
||||
isStepComplete || isOpen ? "uiTextHighContrast" : "baseTextDisabled"
|
||||
return (
|
||||
<div
|
||||
className={styles.accordion}
|
||||
@@ -112,8 +106,8 @@ export default function SectionAccordion({
|
||||
ref={accordionRef}
|
||||
>
|
||||
<div className={styles.iconWrapper}>
|
||||
<div className={styles.circle} data-checked={isComplete}>
|
||||
{isComplete ? (
|
||||
<div className={styles.circle} data-checked={isStepComplete}>
|
||||
{isStepComplete ? (
|
||||
<CheckIcon color="white" height="16" width="16" />
|
||||
) : null}
|
||||
</div>
|
||||
@@ -121,7 +115,7 @@ export default function SectionAccordion({
|
||||
<header className={styles.header}>
|
||||
<button
|
||||
onClick={isOpen ? close : goToStep}
|
||||
disabled={!isComplete}
|
||||
disabled={!isStepComplete}
|
||||
className={styles.modifyButton}
|
||||
>
|
||||
<Footnote
|
||||
@@ -136,7 +130,7 @@ export default function SectionAccordion({
|
||||
<Subtitle className={styles.selection} type="two" color={textColor}>
|
||||
{title}
|
||||
</Subtitle>
|
||||
{isComplete && (
|
||||
{isStepComplete && (
|
||||
<ChevronDownIcon
|
||||
className={`${styles.button} ${isOpen ? styles.buttonOpen : ""}`}
|
||||
color="burgundy"
|
||||
|
||||
@@ -9,6 +9,7 @@ import { useEnterDetailsStore } from "@/stores/enter-details"
|
||||
|
||||
import { CheckIcon, EditIcon } from "@/components/Icons"
|
||||
import Button from "@/components/TempDesignSystem/Button"
|
||||
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
||||
import Footnote from "@/components/TempDesignSystem/Text/Footnote"
|
||||
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
|
||||
import { useRoomContext } from "@/contexts/Details/Room"
|
||||
@@ -39,7 +40,7 @@ export default function SelectedRoom() {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.wrapper}>
|
||||
<div className={styles.wrapper} data-available={room.isAvailable}>
|
||||
<div className={styles.iconWrapper}>
|
||||
<div className={styles.circle}>
|
||||
<CheckIcon color="white" height="16" width="16" />
|
||||
@@ -74,13 +75,15 @@ export default function SelectedRoom() {
|
||||
</Subtitle>
|
||||
<Button
|
||||
variant="icon"
|
||||
intent="text"
|
||||
size="small"
|
||||
color="burgundy"
|
||||
onClick={changeRoom}
|
||||
disabled={isPending}
|
||||
>
|
||||
<EditIcon color="burgundy" />
|
||||
{intl.formatMessage({ id: "Change room" })}
|
||||
<Caption color="burgundy" type="bold">
|
||||
{intl.formatMessage({ id: "Change room" })}
|
||||
</Caption>
|
||||
</Button>
|
||||
</div>
|
||||
{room.roomTypeCode && (
|
||||
|
||||
@@ -5,6 +5,13 @@
|
||||
gap: var(--Spacing-x-one-and-half);
|
||||
}
|
||||
|
||||
.wrapper[data-available="false"] .title,
|
||||
.wrapper[data-available="false"] .description,
|
||||
.wrapper[data-available="false"] .details {
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.main {
|
||||
width: 100%;
|
||||
border-bottom: 1px solid var(--Primary-Light-On-Surface-Divider-subtle);
|
||||
@@ -52,6 +59,10 @@
|
||||
background-color: var(--UI-Input-Controls-Fill-Selected);
|
||||
}
|
||||
|
||||
.wrapper[data-available="false"] .circle {
|
||||
background-color: var(--Base-Surface-Subtle-Hover);
|
||||
}
|
||||
|
||||
.rate {
|
||||
color: var(--UI-Text-Placeholder);
|
||||
display: block;
|
||||
|
||||
@@ -381,7 +381,7 @@ export default function SummaryUI({
|
||||
totalPrice.local.currency
|
||||
)}
|
||||
</Body>
|
||||
{totalPrice.local.regularPrice && (
|
||||
{totalPrice.local.regularPrice ? (
|
||||
<Caption color="uiTextMediumContrast" striked={true}>
|
||||
{formatPrice(
|
||||
intl,
|
||||
@@ -389,7 +389,7 @@ export default function SummaryUI({
|
||||
totalPrice.local.currency
|
||||
)}
|
||||
</Caption>
|
||||
)}
|
||||
) : null}
|
||||
{totalPrice.requested && (
|
||||
<Caption color="uiTextMediumContrast">
|
||||
{intl.formatMessage(
|
||||
|
||||
@@ -63,6 +63,7 @@ const rooms: RoomState[] = [
|
||||
roomRate: roomRate,
|
||||
roomType: "Standard",
|
||||
roomTypeCode: "QS",
|
||||
isAvailable: true,
|
||||
},
|
||||
steps: {
|
||||
[StepEnum.selectBed]: {
|
||||
@@ -100,6 +101,7 @@ const rooms: RoomState[] = [
|
||||
roomRate: roomRate,
|
||||
roomType: "Standard",
|
||||
roomTypeCode: "QS",
|
||||
isAvailable: true,
|
||||
},
|
||||
steps: {
|
||||
[StepEnum.selectBed]: {
|
||||
|
||||
Reference in New Issue
Block a user