Merged in feat/sw-1245-bw-button-update (pull request #2262)

Feat/sw 1245 - Booking widget - change button text when new values

* feat(sw-1245) - use isDirty to update button text

* Change text only in booking flow

* Revert test code


Approved-by: Michael Zetterberg
This commit is contained in:
Linus Flood
2025-06-02 13:37:53 +00:00
parent 7694a188da
commit 81887c83ff
7 changed files with 79 additions and 37 deletions

View File

@@ -28,10 +28,14 @@ export default function DatePickerForm({ name = "date" }: DatePickerFormProps) {
const close = useCallback(() => {
if (!selectedDate.toDate) {
setValue(name, {
fromDate: selectedDate.fromDate,
toDate: dt(selectedDate.fromDate).add(1, "day").format("YYYY-MM-DD"),
})
setValue(
name,
{
fromDate: selectedDate.fromDate,
toDate: dt(selectedDate.fromDate).add(1, "day").format("YYYY-MM-DD"),
},
{ shouldDirty: true }
)
}
setIsOpen(false)
@@ -54,10 +58,14 @@ export default function DatePickerForm({ name = "date" }: DatePickerFormProps) {
// Handle form value updates based on the requirements
if (selectedDate.fromDate && selectedDate.toDate) {
// Both dates were previously selected, starting fresh with new date
setValue(name, {
fromDate: dateClickedFormatted,
toDate: undefined,
})
setValue(
name,
{
fromDate: dateClickedFormatted,
toDate: undefined,
},
{ shouldDirty: true }
)
} else if (selectedDate.fromDate && !selectedDate.toDate) {
// If the selected day is the same as the first date, we don't need to update the form value
if (dateClicked.isSame(selectedDate.fromDate)) {
@@ -66,16 +74,24 @@ export default function DatePickerForm({ name = "date" }: DatePickerFormProps) {
// We're selecting the second date
if (dateClicked.isBefore(selectedDate.fromDate)) {
// If second selected date is before first date, swap them
setValue(name, {
fromDate: dateClickedFormatted,
toDate: selectedDate.fromDate,
})
setValue(
name,
{
fromDate: dateClickedFormatted,
toDate: selectedDate.fromDate,
},
{ shouldDirty: true }
)
} else {
// If second selected date is after first date, keep order
setValue(name, {
fromDate: selectedDate.fromDate,
toDate: dateClickedFormatted,
})
setValue(
name,
{
fromDate: selectedDate.fromDate,
toDate: dateClickedFormatted,
},
{ shouldDirty: true }
)
}
}
}

View File

@@ -60,13 +60,18 @@ export default function BookingCode() {
function updateBookingCodeFormValue(value: string) {
// Set value and show error if validation fails
setValue("bookingCode.value", value.toUpperCase(), { shouldValidate: true })
setValue("bookingCode.value", value.toUpperCase(), {
shouldValidate: true,
shouldDirty: true,
})
if (getValues(REDEMPTION)) {
// Remove the redemption as user types booking code and show notification for the same
// Add delay to handle table mode rendering
setTimeout(function () {
setValue(REDEMPTION, false)
setValue(REDEMPTION, false, {
shouldDirty: true,
})
})
// Hide the above notification popup after 5 seconds by re-triggering validation
// This is kept consistent with location search field error notification timeout
@@ -87,7 +92,10 @@ export default function BookingCode() {
) {
setShowRemember(false)
if (codeError) {
setValue("bookingCode.value", "", { shouldValidate: true })
setValue("bookingCode.value", "", {
shouldValidate: true,
shouldDirty: true,
})
}
}
},
@@ -306,7 +314,7 @@ export function RemoveExtraRooms({ ...props }: ButtonProps) {
// Timeout to delay the event scheduling issue with touch events on mobile
window.setTimeout(() => {
const rooms = getValues("rooms")[0]
setValue("rooms", [rooms], { shouldValidate: true })
setValue("rooms", [rooms], { shouldValidate: true, shouldDirty: true })
trigger("bookingCode.value")
trigger(REDEMPTION)
}, 300)
@@ -357,12 +365,12 @@ function TabletBookingCode({
}
}
if (!isOpen && !bookingCode?.value) {
setValue("bookingCode.flag", false)
setValue("bookingCode.flag", false, { shouldDirty: true })
setIsOpen(isOpen)
} else if (!codeError || isOpen) {
setIsOpen(isOpen)
if (isOpen || bookingCode?.value) {
setValue("bookingCode.flag", true)
setValue("bookingCode.flag", true, { shouldDirty: true })
}
}
}
@@ -378,7 +386,7 @@ function TabletBookingCode({
{...register("bookingCode.flag", {
onChange: function () {
if (bookingCode?.value || isOpen) {
setValue("bookingCode.flag", true)
setValue("bookingCode.flag", true, { shouldDirty: true })
}
},
})}

View File

@@ -1,17 +1,20 @@
"use client"
import { usePathname } from "next/navigation"
import { useFormContext, useWatch } from "react-hook-form"
import { useIntl } from "react-intl"
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
import { Typography } from "@scandic-hotels/design-system/Typography"
import { hotelreservation } from "@/constants/routes/hotelReservation"
import { dt } from "@/lib/dt"
import DatePicker from "@/components/DatePicker"
import GuestsRoomsPickerForm from "@/components/GuestsRoomsPicker"
import SkeletonShimmer from "@/components/SkeletonShimmer"
import Button from "@/components/TempDesignSystem/Button"
import useLang from "@/hooks/useLang"
import { RemoveExtraRooms } from "./BookingCode"
import { Search, SearchSkeleton } from "./Search"
@@ -30,9 +33,13 @@ export default function FormContent({
}: BookingWidgetFormContentProps) {
const intl = useIntl()
const {
formState: { errors },
formState: { errors, isDirty },
} = useFormContext<BookingWidgetSchema>()
const lang = useLang()
const pathName = usePathname()
const isBookingFlow = pathName.includes(hotelreservation(lang))
const selectedDate = useWatch<BookingWidgetSchema, "date">({ name: "date" })
const nights = dt(selectedDate.toDate).diff(dt(selectedDate.fromDate), "days")
@@ -121,9 +128,9 @@ export default function FormContent({
className={styles.buttonText}
>
<span>
{intl.formatMessage({
defaultMessage: "Search",
})}
{isDirty && isBookingFlow
? intl.formatMessage({ defaultMessage: "Update" })
: intl.formatMessage({ defaultMessage: "Search" })}
</span>
</Typography>
<span className={styles.icon}>

View File

@@ -33,7 +33,8 @@ export default function Form({ type, onClose }: BookingWidgetFormProps) {
type,
})
const { handleSubmit, setValue } = useFormContext<BookingWidgetSchema>()
const { handleSubmit, setValue, reset } =
useFormContext<BookingWidgetSchema>()
function onSubmit(data: BookingWidgetSchema) {
const bookingFlowPage = data.hotel ? selectRate(lang) : selectHotel(lang)
@@ -54,11 +55,14 @@ export default function Form({ type, onClose }: BookingWidgetFormProps) {
router.push(`${bookingFlowPage}?${bookingWidgetParams.toString()}`)
})
if (!data.bookingCode?.value) {
setValue("bookingCode.remember", false)
setValue("bookingCode.remember", false, {
shouldDirty: true,
})
localStorage.removeItem("bookingCode")
} else if (data.bookingCode?.remember) {
localStorage.setItem("bookingCode", JSON.stringify(data.bookingCode))
}
reset(data)
}
return (

View File

@@ -24,13 +24,13 @@ export default function AdultSelector({
function increaseAdultsCount() {
if (currentAdults < 6) {
setValue(name, currentAdults + 1)
setValue(name, currentAdults + 1, { shouldDirty: true })
}
}
function decreaseAdultsCount() {
if (currentAdults > 1) {
setValue(name, currentAdults - 1)
setValue(name, currentAdults - 1, { shouldDirty: true })
}
}

View File

@@ -26,16 +26,22 @@ export default function ChildSelector({
function increaseChildrenCount(roomIndex: number) {
if (currentChildren.length < 5) {
setValue(`rooms.${roomIndex}.childrenInRoom.${currentChildren.length}`, {
age: undefined,
bed: undefined,
})
setValue(
`rooms.${roomIndex}.childrenInRoom.${currentChildren.length}`,
{
age: undefined,
bed: undefined,
},
{ shouldDirty: true }
)
}
}
function decreaseChildrenCount(roomIndex: number) {
if (currentChildren.length > 0) {
currentChildren.pop()
setValue(`rooms.${roomIndex}.childrenInRoom`, currentChildren)
setValue(`rooms.${roomIndex}.childrenInRoom`, currentChildren, {
shouldDirty: true,
})
}
}

View File

@@ -58,6 +58,7 @@ export default function GuestsRoomsPickerDialog({
const handleAddRoom = useCallback(() => {
setValue("rooms", [...roomsValue, { adults: 1, childrenInRoom: [] }], {
shouldValidate: true,
shouldDirty: true,
})
}, [roomsValue, setValue])
@@ -66,7 +67,7 @@ export default function GuestsRoomsPickerDialog({
setValue(
"rooms",
roomsValue.filter((_, i) => i !== index),
{ shouldValidate: true }
{ shouldValidate: true, shouldDirty: true }
)
},
[roomsValue, setValue]