Merged in feat/SW-3224-move-bookingcode-filter-to-booking-flow (pull request #2608)
feat(SW-3224): Move bookingcode filter to the booking-flow package * feat(SW-3224): Moved bookingcode filter to the booking-flow package Approved-by: Anton Gunnarsson
This commit is contained in:
@@ -4,6 +4,7 @@ import { useCallback, useMemo, useRef, useState } from "react"
|
|||||||
import { useIntl } from "react-intl"
|
import { useIntl } from "react-intl"
|
||||||
import { useMediaQuery } from "usehooks-ts"
|
import { useMediaQuery } from "usehooks-ts"
|
||||||
|
|
||||||
|
import BookingCodeFilter from "@scandic-hotels/booking-flow/BookingCodeFilter"
|
||||||
import {
|
import {
|
||||||
BookingCodeFilterEnum,
|
BookingCodeFilterEnum,
|
||||||
useBookingCodeFilterStore,
|
useBookingCodeFilterStore,
|
||||||
@@ -27,7 +28,6 @@ import Link from "@/components/TempDesignSystem/Link"
|
|||||||
import useLang from "@/hooks/useLang"
|
import useLang from "@/hooks/useLang"
|
||||||
import { useScrollToTop } from "@/hooks/useScrollToTop"
|
import { useScrollToTop } from "@/hooks/useScrollToTop"
|
||||||
|
|
||||||
import BookingCodeFilter from "../../BookingCodeFilter"
|
|
||||||
import FilterAndSortModal from "../../Filters/FilterAndSortModal"
|
import FilterAndSortModal from "../../Filters/FilterAndSortModal"
|
||||||
import HotelListing from "../HotelListing"
|
import HotelListing from "../HotelListing"
|
||||||
import { getVisibleHotels } from "./utils"
|
import { getVisibleHotels } from "./utils"
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
|
import BookingCodeFilter from "@scandic-hotels/booking-flow/BookingCodeFilter"
|
||||||
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
||||||
import Subtitle from "@scandic-hotels/design-system/Subtitle"
|
import Subtitle from "@scandic-hotels/design-system/Subtitle"
|
||||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||||
|
|
||||||
import HotelCardListing from "@/components/HotelReservation/HotelCardListing"
|
import HotelCardListing from "@/components/HotelReservation/HotelCardListing"
|
||||||
import BookingCodeFilter from "@/components/HotelReservation/SelectHotel/BookingCodeFilter"
|
|
||||||
import HotelFilter from "@/components/HotelReservation/SelectHotel/Filters/HotelFilter"
|
import HotelFilter from "@/components/HotelReservation/SelectHotel/Filters/HotelFilter"
|
||||||
import HotelCount from "@/components/HotelReservation/SelectHotel/HotelCount"
|
import HotelCount from "@/components/HotelReservation/SelectHotel/HotelCount"
|
||||||
import HotelSorter from "@/components/HotelReservation/SelectHotel/HotelSorter"
|
import HotelSorter from "@/components/HotelReservation/SelectHotel/HotelSorter"
|
||||||
|
|||||||
@@ -1,124 +0,0 @@
|
|||||||
.bookingCodeFilter {
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dialog {
|
|
||||||
border-radius: var(--Corner-radius-md);
|
|
||||||
background-color: var(--Surface-Primary-Default);
|
|
||||||
box-shadow: var(--popup-box-shadow);
|
|
||||||
max-width: 340px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.radioGroup {
|
|
||||||
display: grid;
|
|
||||||
gap: var(--Space-x1);
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.radio {
|
|
||||||
padding: var(--Space-x1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.radio[data-hovered] {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
.radio[data-focus-visible]::before {
|
|
||||||
outline: 1px auto var(--Border-Interactive-Focus);
|
|
||||||
}
|
|
||||||
|
|
||||||
.radio {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.radio::before {
|
|
||||||
flex-shrink: 0;
|
|
||||||
content: "";
|
|
||||||
margin-right: var(--Space-x15);
|
|
||||||
background-color: var(--Surface-UI-Fill-Default);
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
border-radius: 50%;
|
|
||||||
box-shadow: inset 0 0 0 2px var(--Base-Border-Normal);
|
|
||||||
}
|
|
||||||
|
|
||||||
.radio[data-selected]::before {
|
|
||||||
box-shadow: inset 0 0 0 8px var(--Surface-UI-Fill-Active);
|
|
||||||
}
|
|
||||||
|
|
||||||
.modalOverlay {
|
|
||||||
position: fixed;
|
|
||||||
inset: 0;
|
|
||||||
background-color: var(--Overlay-40);
|
|
||||||
|
|
||||||
&[data-entering] {
|
|
||||||
animation: overlay-fade 200ms;
|
|
||||||
}
|
|
||||||
|
|
||||||
&[data-exiting] {
|
|
||||||
animation: overlay-fade 150ms reverse ease-in;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal {
|
|
||||||
position: fixed;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
padding: var(--Space-x2) var(--Space-x05);
|
|
||||||
border-radius: var(--Corner-radius-md) var(--Corner-radius-md) 0 0;
|
|
||||||
background-color: var(--Surface-Primary-Default);
|
|
||||||
box-shadow: 0px 0px 14px 6px rgba(0, 0, 0, 0.1);
|
|
||||||
|
|
||||||
&[data-entering] {
|
|
||||||
animation: modal-anim 200ms;
|
|
||||||
}
|
|
||||||
|
|
||||||
&[data-exiting] {
|
|
||||||
animation: modal-anim 150ms reverse ease-in;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.modalDialog {
|
|
||||||
display: grid;
|
|
||||||
gap: var(--Space-x2);
|
|
||||||
padding: 0 var(--Space-x1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.header {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
padding: 0 var(--Space-x1);
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-width: 768px) {
|
|
||||||
.radioGroup {
|
|
||||||
padding: var(--Space-x1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.modalOverlay {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes overlay-fade {
|
|
||||||
from {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
to {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes modal-anim {
|
|
||||||
from {
|
|
||||||
transform: translateY(100%);
|
|
||||||
}
|
|
||||||
|
|
||||||
to {
|
|
||||||
transform: translateY(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,225 +0,0 @@
|
|||||||
"use client"
|
|
||||||
|
|
||||||
import { useEffect, useState } from "react"
|
|
||||||
import {
|
|
||||||
Dialog,
|
|
||||||
DialogTrigger,
|
|
||||||
Modal,
|
|
||||||
ModalOverlay,
|
|
||||||
Popover,
|
|
||||||
Radio,
|
|
||||||
RadioGroup,
|
|
||||||
} from "react-aria-components"
|
|
||||||
import { useIntl } from "react-intl"
|
|
||||||
import { useMediaQuery } from "usehooks-ts"
|
|
||||||
|
|
||||||
import { BookingCodeFilterEnum } from "@scandic-hotels/booking-flow/stores/bookingCode-filter"
|
|
||||||
import { ChipButton } from "@scandic-hotels/design-system/ChipButton"
|
|
||||||
import { IconButton } from "@scandic-hotels/design-system/IconButton"
|
|
||||||
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
|
||||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
|
||||||
import { trpc } from "@scandic-hotels/trpc/client"
|
|
||||||
import { RateTypeEnum } from "@scandic-hotels/trpc/enums/rateType"
|
|
||||||
|
|
||||||
import { useRatesStore } from "@/stores/select-rate"
|
|
||||||
|
|
||||||
import { useRoomContext } from "@/contexts/SelectRate/Room"
|
|
||||||
import useLang from "@/hooks/useLang"
|
|
||||||
|
|
||||||
import styles from "./bookingCodeFilter.module.css"
|
|
||||||
|
|
||||||
export default function BookingCodeFilter() {
|
|
||||||
const intl = useIntl()
|
|
||||||
const lang = useLang()
|
|
||||||
const [isOpen, setIsOpen] = useState(false)
|
|
||||||
const [isDesktop, setIsDesktop] = useState(false)
|
|
||||||
const displayAsPopover = useMediaQuery("(min-width: 768px)")
|
|
||||||
const utils = trpc.useUtils()
|
|
||||||
const {
|
|
||||||
actions: { appendRegularRates, selectFilter },
|
|
||||||
bookingRoom,
|
|
||||||
rooms,
|
|
||||||
selectedFilter,
|
|
||||||
selectedPackages,
|
|
||||||
} = useRoomContext()
|
|
||||||
const booking = useRatesStore((state) => state.booking)
|
|
||||||
|
|
||||||
const bookingCodeFilterItems = [
|
|
||||||
{
|
|
||||||
label: intl.formatMessage({
|
|
||||||
defaultMessage: "Booking code rates",
|
|
||||||
}),
|
|
||||||
value: BookingCodeFilterEnum.Discounted,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: intl.formatMessage({
|
|
||||||
defaultMessage: "All rates",
|
|
||||||
}),
|
|
||||||
value: BookingCodeFilterEnum.All,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
async function updateFilterValue(selectedFilter: string) {
|
|
||||||
selectFilter(selectedFilter as BookingCodeFilterEnum)
|
|
||||||
if (selectedFilter === BookingCodeFilterEnum.All) {
|
|
||||||
const room = await utils.hotel.availability.selectRate.room.fetch({
|
|
||||||
booking: {
|
|
||||||
...booking,
|
|
||||||
room: {
|
|
||||||
...bookingRoom,
|
|
||||||
bookingCode: undefined,
|
|
||||||
packages: selectedPackages.map((pkg) => pkg.code),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
lang,
|
|
||||||
})
|
|
||||||
appendRegularRates(room?.roomConfigurations)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const hideFilter = rooms.some((room) =>
|
|
||||||
room.products.some((product) => {
|
|
||||||
const isRedemption = Array.isArray(product)
|
|
||||||
if (isRedemption) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (product.rateDefinition.rateType) {
|
|
||||||
case RateTypeEnum.Arb:
|
|
||||||
case RateTypeEnum.CorporateCheque:
|
|
||||||
case RateTypeEnum.Voucher:
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
// To fix the hyderation error
|
|
||||||
useEffect(() => {
|
|
||||||
setIsDesktop(displayAsPopover)
|
|
||||||
}, [displayAsPopover])
|
|
||||||
|
|
||||||
if (hideFilter || !booking.bookingCode) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className={styles.bookingCodeFilter}>
|
|
||||||
<DialogTrigger isOpen={isOpen} onOpenChange={setIsOpen}>
|
|
||||||
<ChipButton variant="Outlined">
|
|
||||||
{
|
|
||||||
bookingCodeFilterItems.find(
|
|
||||||
(item) => item.value === selectedFilter
|
|
||||||
)?.label
|
|
||||||
}
|
|
||||||
<MaterialIcon
|
|
||||||
icon="keyboard_arrow_down"
|
|
||||||
size={20}
|
|
||||||
color="CurrentColor"
|
|
||||||
/>
|
|
||||||
</ChipButton>
|
|
||||||
{isDesktop ? (
|
|
||||||
<Popover placement="bottom end" isNonModal>
|
|
||||||
<Dialog className={styles.dialog}>
|
|
||||||
{({ close }) => {
|
|
||||||
function handleChangeFilterValue(value: string) {
|
|
||||||
updateFilterValue(value)
|
|
||||||
close()
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<Typography variant="Body/Paragraph/mdRegular">
|
|
||||||
<RadioGroup
|
|
||||||
aria-label={intl.formatMessage({
|
|
||||||
defaultMessage: "Booking Code Filter",
|
|
||||||
})}
|
|
||||||
onChange={handleChangeFilterValue}
|
|
||||||
name="bookingCodeFilter"
|
|
||||||
value={selectedFilter}
|
|
||||||
className={styles.radioGroup}
|
|
||||||
>
|
|
||||||
{bookingCodeFilterItems.map((item) => (
|
|
||||||
<Radio
|
|
||||||
aria-label={item.label}
|
|
||||||
key={item.value}
|
|
||||||
value={item.value}
|
|
||||||
className={styles.radio}
|
|
||||||
autoFocus={selectedFilter === item.value}
|
|
||||||
>
|
|
||||||
{item.label}
|
|
||||||
</Radio>
|
|
||||||
))}
|
|
||||||
</RadioGroup>
|
|
||||||
</Typography>
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</Dialog>
|
|
||||||
</Popover>
|
|
||||||
) : (
|
|
||||||
<ModalOverlay className={styles.modalOverlay} isDismissable>
|
|
||||||
<Modal className={styles.modal}>
|
|
||||||
<Dialog className={styles.modalDialog}>
|
|
||||||
{({ close }) => {
|
|
||||||
function handleChangeFilterValue(value: string) {
|
|
||||||
updateFilterValue(value)
|
|
||||||
close()
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className={styles.header}>
|
|
||||||
<Typography variant="Title/Subtitle/md">
|
|
||||||
<h3>
|
|
||||||
{intl.formatMessage({
|
|
||||||
defaultMessage: "Room rates",
|
|
||||||
})}
|
|
||||||
</h3>
|
|
||||||
</Typography>
|
|
||||||
<IconButton
|
|
||||||
theme="Black"
|
|
||||||
style="Muted"
|
|
||||||
onPress={() => {
|
|
||||||
close()
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<MaterialIcon
|
|
||||||
icon="close"
|
|
||||||
size={24}
|
|
||||||
color="CurrentColor"
|
|
||||||
/>
|
|
||||||
</IconButton>
|
|
||||||
</div>
|
|
||||||
<Typography variant="Body/Paragraph/mdRegular">
|
|
||||||
<RadioGroup
|
|
||||||
aria-label={intl.formatMessage({
|
|
||||||
defaultMessage: "Booking Code Filter",
|
|
||||||
})}
|
|
||||||
onChange={handleChangeFilterValue}
|
|
||||||
name="bookingCodeFilter"
|
|
||||||
value={selectedFilter}
|
|
||||||
className={styles.radioGroup}
|
|
||||||
>
|
|
||||||
{bookingCodeFilterItems.map((item) => (
|
|
||||||
<Radio
|
|
||||||
aria-label={item.label}
|
|
||||||
key={item.value}
|
|
||||||
value={item.value}
|
|
||||||
className={styles.radio}
|
|
||||||
>
|
|
||||||
{item.label}
|
|
||||||
</Radio>
|
|
||||||
))}
|
|
||||||
</RadioGroup>
|
|
||||||
</Typography>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</Dialog>
|
|
||||||
</Modal>
|
|
||||||
</ModalOverlay>
|
|
||||||
)}
|
|
||||||
</DialogTrigger>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -56,7 +56,6 @@ export default function RoomsHeader() {
|
|||||||
<div className={styles.filters}>
|
<div className={styles.filters}>
|
||||||
<RemoveBookingCodeButton />
|
<RemoveBookingCodeButton />
|
||||||
<RoomPackageFilter />
|
<RoomPackageFilter />
|
||||||
{/* <BookingCodeFilter /> */}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -13,12 +13,16 @@ import {
|
|||||||
import { useIntl } from "react-intl"
|
import { useIntl } from "react-intl"
|
||||||
import { useMediaQuery } from "usehooks-ts"
|
import { useMediaQuery } from "usehooks-ts"
|
||||||
|
|
||||||
import { BookingCodeFilterEnum,useBookingCodeFilterStore } from "@scandic-hotels/booking-flow/stores/bookingCode-filter"
|
|
||||||
import { ChipButton } from "@scandic-hotels/design-system/ChipButton"
|
import { ChipButton } from "@scandic-hotels/design-system/ChipButton"
|
||||||
import { IconButton } from "@scandic-hotels/design-system/IconButton"
|
import { IconButton } from "@scandic-hotels/design-system/IconButton"
|
||||||
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
||||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||||
|
|
||||||
|
import {
|
||||||
|
BookingCodeFilterEnum,
|
||||||
|
useBookingCodeFilterStore,
|
||||||
|
} from "../../stores/bookingCode-filter"
|
||||||
|
|
||||||
import styles from "./bookingCodeFilter.module.css"
|
import styles from "./bookingCodeFilter.module.css"
|
||||||
|
|
||||||
export default function BookingCodeFilter() {
|
export default function BookingCodeFilter() {
|
||||||
@@ -11,6 +11,7 @@
|
|||||||
"test:watch": "vitest"
|
"test:watch": "vitest"
|
||||||
},
|
},
|
||||||
"exports": {
|
"exports": {
|
||||||
|
"./BookingCodeFilter": "./lib/components/BookingCodeFilter/index.tsx",
|
||||||
"./BookingWidget": "./lib/components/BookingWidget/index.tsx",
|
"./BookingWidget": "./lib/components/BookingWidget/index.tsx",
|
||||||
"./BookingWidget/FloatingBookingWidget": "./lib/components/BookingWidget/FloatingBookingWidget/index.tsx",
|
"./BookingWidget/FloatingBookingWidget": "./lib/components/BookingWidget/FloatingBookingWidget/index.tsx",
|
||||||
"./BookingWidget/Skeleton": "./lib/components/BookingWidget/Skeleton.tsx",
|
"./BookingWidget/Skeleton": "./lib/components/BookingWidget/Skeleton.tsx",
|
||||||
|
|||||||
Reference in New Issue
Block a user