diff --git a/app/[lang]/(live)/(public)/hotelreservation/(standard)/select-rate/page.tsx b/app/[lang]/(live)/(public)/hotelreservation/(standard)/select-rate/page.tsx index 8ef2b4cd4..05103acf3 100644 --- a/app/[lang]/(live)/(public)/hotelreservation/(standard)/select-rate/page.tsx +++ b/app/[lang]/(live)/(public)/hotelreservation/(standard)/select-rate/page.tsx @@ -21,8 +21,8 @@ export default async function SelectRatePage({ const selectRoomParams = new URLSearchParams(searchParams) const selectRoomParamsObject = getHotelReservationQueryParams(selectRoomParams) - const adults = selectRoomParamsObject.room[0].adults // TODO: Handle multiple rooms - const children = selectRoomParamsObject.room[0].child?.length // TODO: Handle multiple rooms + const adults = selectRoomParamsObject.room?.[0].adults // TODO: Handle multiple rooms + const children = selectRoomParamsObject.room?.[0].child?.length // TODO: Handle multiple rooms const [hotelData, roomsAvailability, user] = await Promise.all([ serverClient().hotel.hotelData.get({ diff --git a/components/HotelReservation/SelectRate/RoomFilter/index.tsx b/components/HotelReservation/SelectRate/RoomFilter/index.tsx index ff3edc852..d775f8f46 100644 --- a/components/HotelReservation/SelectRate/RoomFilter/index.tsx +++ b/components/HotelReservation/SelectRate/RoomFilter/index.tsx @@ -1,32 +1,93 @@ "use client" +import { zodResolver } from "@hookform/resolvers/zod" +import { useRef } from "react" +import { FormProvider, useForm } from "react-hook-form" import { useIntl } from "react-intl" -import Checkbox from "@/components/TempDesignSystem/Checkbox" +import { roomFilterSchema } from "@/server/routers/hotels/schemas/room" + +import Checkbox from "@/components/TempDesignSystem/Form/Checkbox" import Body from "@/components/TempDesignSystem/Text/Body" import Caption from "@/components/TempDesignSystem/Text/Caption" import styles from "./roomFilter.module.css" -import { RoomFilterProps } from "@/types/components/hotelReservation/selectRate/roomFilter" +import { + RoomFilterFormData, + RoomFilterProps, +} from "@/types/components/hotelReservation/selectRate/roomFilter" function RoomFilter({ numberOfRooms }: RoomFilterProps) { const intl = useIntl() + const methods = useForm({ + defaultValues: { + allergyFriendly: false, + petFriendly: false, + accessibility: false, + }, + mode: "all", + reValidateMode: "onChange", + resolver: zodResolver(roomFilterSchema), + }) + + const formRef = useRef(null) + const { watch, setValue } = methods + const petFriendly = watch("petFriendly") + const allergyFriendly = watch("allergyFriendly") + + const onSubmit = (data: RoomFilterFormData) => { + if (data.petFriendly) { + setValue("allergyFriendly", false) + } else if (data.allergyFriendly) { + setValue("petFriendly", false) + } + console.log("Form submitted with data:", data) + } + return (
- {numberOfRooms}{" "} - {intl.formatMessage( - { id: "Room types available" }, - { numberOfRooms: numberOfRooms } - )} + {intl.formatMessage({ id: "Room types available" }, { numberOfRooms })} -
-
- - Accessibility room -
-
+ +
+
+ formRef.current?.requestSubmit()} + > + + {intl.formatMessage({ id: "Accessibility room" })} + + + { + setValue("petFriendly", !petFriendly) + formRef.current?.requestSubmit() + }} + registerOptions={{ disabled: allergyFriendly }} + > + + {intl.formatMessage({ id: "Pet room" })} + + + { + setValue("allergyFriendly", !allergyFriendly) + formRef.current?.requestSubmit() + }} + registerOptions={{ disabled: petFriendly }} + > + + {intl.formatMessage({ id: "Allergy room" })} + + +
+
+
) } diff --git a/components/HotelReservation/SelectRate/RoomFilter/roomFilter.module.css b/components/HotelReservation/SelectRate/RoomFilter/roomFilter.module.css index 06caf5149..3c715f5c3 100644 --- a/components/HotelReservation/SelectRate/RoomFilter/roomFilter.module.css +++ b/components/HotelReservation/SelectRate/RoomFilter/roomFilter.module.css @@ -3,3 +3,9 @@ flex-direction: row; justify-content: space-between; } + +.roomsFilter { + display: flex; + flex-direction: row; + gap: var(--Spacing-x3); +} diff --git a/components/TempDesignSystem/Checkbox/checkbox.module.css b/components/TempDesignSystem/Checkbox/checkbox.module.css deleted file mode 100644 index c831ba525..000000000 --- a/components/TempDesignSystem/Checkbox/checkbox.module.css +++ /dev/null @@ -1,40 +0,0 @@ -.container { - display: flex; - flex-direction: column; - color: var(--text-color); -} - -.container[data-selected] .checkbox { - border: none; - background: var(--UI-Input-Controls-Fill-Selected); -} - -.checkboxContainer { - display: flex; - align-items: flex-start; - gap: var(--Spacing-x-one-and-half); -} - -.checkbox { - width: 24px; - height: 24px; - min-width: 24px; - background-color: var(--UI-Input-Controls-Surface-Normal); - border: 2px solid var(--UI-Input-Controls-Border-Normal); - border-radius: var(--Corner-radius-Small); - transition: all 200ms; - display: flex; - align-items: center; - justify-content: center; - transition: all 200ms; - forced-color-adjust: none; - cursor: pointer; -} - -.error { - align-items: center; - color: var(--Scandic-Red-60); - display: flex; - gap: var(--Spacing-x-half); - margin-top: var(--Spacing-x1); -} diff --git a/components/TempDesignSystem/Checkbox/checkbox.ts b/components/TempDesignSystem/Checkbox/checkbox.ts deleted file mode 100644 index 8588b7401..000000000 --- a/components/TempDesignSystem/Checkbox/checkbox.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { RegisterOptions } from "react-hook-form" - -export interface CheckboxProps - extends React.InputHTMLAttributes { - name: string - registerOptions?: RegisterOptions -} diff --git a/components/TempDesignSystem/Checkbox/index.tsx b/components/TempDesignSystem/Checkbox/index.tsx deleted file mode 100644 index fde1742ff..000000000 --- a/components/TempDesignSystem/Checkbox/index.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import { Checkbox as AriaCheckbox } from "react-aria-components" -import { useController, useFormContext } from "react-hook-form" - -import { InfoCircleIcon } from "@/components/Icons" -import CheckIcon from "@/components/Icons/Check" -import Caption from "@/components/TempDesignSystem/Text/Caption" - -import { CheckboxProps } from "./checkbox" - -import styles from "./checkbox.module.css" - -export default function Checkbox({ - name, - children, - registerOptions, -}: React.PropsWithChildren) { - const { control } = useFormContext() - const { field, fieldState } = useController({ - control, - name, - rules: registerOptions, - }) - - return ( - - {({ isSelected }) => ( - <> -
-
- {isSelected && } -
- {children} -
- {children && fieldState.error ? ( - - - {fieldState.error.message} - - ) : null} - - )} -
- ) -} diff --git a/components/TempDesignSystem/Form/Checkbox/checkbox.module.css b/components/TempDesignSystem/Form/Checkbox/checkbox.module.css index 99077e212..2e924b226 100644 --- a/components/TempDesignSystem/Form/Checkbox/checkbox.module.css +++ b/components/TempDesignSystem/Form/Checkbox/checkbox.module.css @@ -16,7 +16,7 @@ .checkboxContainer { display: flex; - align-items: flex-start; + align-items: center; gap: var(--Spacing-x-one-and-half); } diff --git a/i18n/dictionaries/da.json b/i18n/dictionaries/da.json index b9c1622c4..ae660a5fe 100644 --- a/i18n/dictionaries/da.json +++ b/i18n/dictionaries/da.json @@ -5,13 +5,16 @@ "A photo of the room": "Et foto af værelset", "About meetings & conferences": "About meetings & conferences", "About the hotel": "About the hotel", + "Accessibility room": "Handicapvenligt værelse", "Activities": "Aktiviteter", "Add code": "Tilføj kode", "Add new card": "Tilføj nyt kort", + "Add room": "Tilføj værelse", "Address": "Adresse", "Adults": "voksne", "Airport": "Lufthavn", "All our breakfast buffets offer gluten free, vegan, and allergy-friendly options.": "Alle vores morgenmadsbuffeter tilbyder glutenfrie, veganske og allergivenlige muligheder.", + "Allergy room": "Allergivenligt værelse", "Already a friend?": "Allerede en ven?", "Amenities": "Faciliteter", "Amusement park": "Forlystelsespark", @@ -214,6 +217,7 @@ "Pay later": "Betal senere", "Pay now": "Betal nu", "Payment info": "Betalingsoplysninger", + "Pet room": "Kæledyrsrum", "Phone": "Telefon", "Phone is required": "Telefonnummer er påkrævet", "Phone number": "Telefonnummer", @@ -243,7 +247,7 @@ "Retype new password": "Gentag den nye adgangskode", "Room & Terms": "Værelse & Vilkår", "Room facilities": "Værelsesfaciliteter", - "Room types available": "værelse {numberOfRooms, plural, one {# type} other {# types}} tilgængelig", + "Room types available": "{numberOfRooms, plural, one {# room type} other {# room types}} tilgængelig", "Rooms": "Værelser", "Rooms & Guests": "Værelser & gæster", "Sauna and gym": "Sauna and gym", @@ -376,6 +380,8 @@ "to": "til", "uppercase letter": "stort bogstav", "{amount} out of {total}": "{amount} ud af {total}", + "room type": "værelsestype", + "room types": "værelsestyper", "{amount} {currency}": "{amount} {currency}", "{difference}{amount} {currency}": "{difference}{amount} {currency}", "{width} cm × {length} cm": "{width} cm × {length} cm" diff --git a/i18n/dictionaries/de.json b/i18n/dictionaries/de.json index a4df8d378..e69b84d40 100644 --- a/i18n/dictionaries/de.json +++ b/i18n/dictionaries/de.json @@ -5,13 +5,16 @@ "A photo of the room": "Ein Foto des Zimmers", "About meetings & conferences": "About meetings & conferences", "About the hotel": "Über das Hotel", + "Accessibility room": "Barrierefreies Zimmer", "Activities": "Aktivitäten", "Add code": "Code hinzufügen", "Add new card": "Neue Karte hinzufügen", + "Add room": "Zimmer hinzufügen", "Address": "Adresse", "Adults": "Erwachsene", "Airport": "Flughafen", "All our breakfast buffets offer gluten free, vegan, and allergy-friendly options.": "Alle unsere Frühstücksbuffets bieten glutenfreie, vegane und allergikerfreundliche Speisen.", + "Allergy room": "Allergiefreundliches Zimmer", "Already a friend?": "Sind wir schon Freunde?", "Amenities": "Annehmlichkeiten", "Amusement park": "Vergnügungspark", @@ -214,6 +217,7 @@ "Pay later": "Später bezahlen", "Pay now": "Jetzt bezahlen", "Payment info": "Zahlungsinformationen", + "Pet room": "Haustierfreundliches Zimmer", "Phone": "Telefon", "Phone is required": "Telefon ist erforderlich", "Phone number": "Telefonnummer", @@ -244,7 +248,7 @@ "Room": "Zimmer", "Room & Terms": "Zimmer & Bedingungen", "Room facilities": "Zimmerausstattung", - "Room types available": "zimmer {numberOfRooms, plural, one {# type} other {# types}} verfügbar", + "Room types available": "{numberOfRooms, plural, one {# room type} other {# room types}} verfügbar", "Rooms": "Räume", "Rooms & Guests": "Zimmer & Gäste", "Sauna and gym": "Sauna and gym", @@ -377,6 +381,8 @@ "to": "zu", "uppercase letter": "großbuchstabe", "{amount} out of {total}": "{amount} von {total}", + "room type": "zimmerart", + "room types": "zimmerarten", "{amount} {currency}": "{amount} {currency}", "{difference}{amount} {currency}": "{difference}{amount} {currency}", "{width} cm × {length} cm": "{width} cm × {length} cm" diff --git a/i18n/dictionaries/en.json b/i18n/dictionaries/en.json index cf5551d18..59762c705 100644 --- a/i18n/dictionaries/en.json +++ b/i18n/dictionaries/en.json @@ -5,16 +5,19 @@ "A photo of the room": "A photo of the room", "About meetings & conferences": "About meetings & conferences", "About the hotel": "About the hotel", + "Accessibility room": "Accessibility room", "Activities": "Activities", "Add Room": "Add room", "Add code": "Add code", "Add new card": "Add new card", "Add to calendar": "Add to calendar", + "Add room": "Add room", "Address": "Address", "Adults": "Adults", "Age": "Age", "Airport": "Airport", "All our breakfast buffets offer gluten free, vegan, and allergy-friendly options.": "All our breakfast buffets offer gluten free, vegan, and allergy-friendly options.", + "Allergy room": "Allergy room", "Already a friend?": "Already a friend?", "Amenities": "Amenities", "Amusement park": "Amusement park", @@ -224,6 +227,7 @@ "Pay now": "Pay now", "Payment info": "Payment info", "Payment received": "Payment received", + "Pet room": "Pet room", "Phone": "Phone", "Phone is required": "Phone is required", "Phone number": "Phone number", @@ -256,7 +260,7 @@ "Retype new password": "Retype new password", "Room & Terms": "Room & Terms", "Room facilities": "Room facilities", - "Room types available": "room {numberOfRooms, plural, one {# type} other {# types}} available", + "Room types available": "{numberOfRooms, plural, one {# room type} other {# room types}} available", "Rooms": "Rooms", "Rooms & Guests": "Rooms & Guests", "Sauna and gym": "Sauna and gym", @@ -391,11 +395,11 @@ "number": "number", "or": "or", "points": "Points", + "room type": "room type", + "room types": "room types", "special character": "special character", "spendable points expiring by": "{points} spendable points expiring by {date}", "to": "to", - "type": "type", - "types": "types", "uppercase letter": "uppercase letter", "{amount} out of {total}": "{amount} out of {total}", "{amount} {currency}": "{amount} {currency}", diff --git a/i18n/dictionaries/fi.json b/i18n/dictionaries/fi.json index e8f430f16..12fb36018 100644 --- a/i18n/dictionaries/fi.json +++ b/i18n/dictionaries/fi.json @@ -5,13 +5,16 @@ "A photo of the room": "Kuva huoneesta", "About meetings & conferences": "About meetings & conferences", "About the hotel": "Tietoja hotellista", + "Accessibility room": "Esteettömyyshuone", "Activities": "Aktiviteetit", "Add code": "Lisää koodi", "Add new card": "Lisää uusi kortti", + "Add room": "Lisää huone", "Address": "Osoite", "Adults": "Aikuista", "Airport": "Lentokenttä", "All our breakfast buffets offer gluten free, vegan, and allergy-friendly options.": "Kaikki aamiaisbuffettimme tarjoavat gluteenittomia, vegaanisia ja allergiaystävällisiä vaihtoehtoja.", + "Allergy room": "Allergiahuone", "Already a friend?": "Oletko jo ystävä?", "Amenities": "Mukavuudet", "Amusement park": "Huvipuisto", @@ -214,6 +217,7 @@ "Pay later": "Maksa myöhemmin", "Pay now": "Maksa nyt", "Payment info": "Maksutiedot", + "Pet room": "Lemmikkihuone", "Phone": "Puhelin", "Phone is required": "Puhelin vaaditaan", "Phone number": "Puhelinnumero", @@ -243,7 +247,7 @@ "Retype new password": "Kirjoita uusi salasana uudelleen", "Room & Terms": "Huone & Ehdot", "Room facilities": "Huoneen varustelu", - "Room types available": "huoneen {numberOfRooms, plural, one {# type} other {# types}} saatavilla", + "Room types available": "{numberOfRooms, plural, one {# room type} other {# room types}} saatavilla", "Rooms": "Huoneet", "Rooms & Guests": "Huoneet & Vieraat", "Rooms & Guestss": "Huoneet & Vieraat", @@ -379,6 +383,8 @@ "{amount} out of {total}": "{amount}/{total}", "type": "tyyppi", "types": "tyypit", + "room type": "huonetyyppi", + "room types": "huonetyypit", "{amount} {currency}": "{amount} {currency}", "{difference}{amount} {currency}": "{difference}{amount} {currency}", "{width} cm × {length} cm": "{width} cm × {length} cm" diff --git a/i18n/dictionaries/no.json b/i18n/dictionaries/no.json index 0cdbf99e9..fa02a3b96 100644 --- a/i18n/dictionaries/no.json +++ b/i18n/dictionaries/no.json @@ -5,13 +5,16 @@ "A photo of the room": "Et bilde av rommet", "About meetings & conferences": "About meetings & conferences", "About the hotel": "Om hotellet", + "Accessibility room": "Tilgjengelighetsrom", "Activities": "Aktiviteter", "Add code": "Legg til kode", "Add new card": "Legg til nytt kort", + "Add room": "Legg til rom", "Address": "Adresse", "Adults": "Voksne", "Airport": "Flyplass", "All our breakfast buffets offer gluten free, vegan, and allergy-friendly options.": "Alle våre frokostbufféer tilbyr glutenfrie, veganske og allergivennlige alternativer.", + "Allergy room": "Allergihus", "Already a friend?": "Allerede Friend?", "Amenities": "Fasiliteter", "Amusement park": "Tivoli", @@ -212,6 +215,7 @@ "Pay later": "Betal senere", "Pay now": "Betal nå", "Payment info": "Betalingsinformasjon", + "Pet room": "Kjæledyrrom", "Phone": "Telefon", "Phone is required": "Telefon kreves", "Phone number": "Telefonnummer", @@ -241,7 +245,7 @@ "Retype new password": "Skriv inn nytt passord på nytt", "Room & Terms": "Rom & Vilkår", "Room facilities": "Romfasiliteter", - "Room types available": "romtyper {numberOfRooms, plural, one {# type} other {# types}} tilgjengelig", + "Room types available": "{numberOfRooms, plural, one {# room type} other {# room types}} tilgjengelig", "Rooms": "Rom", "Rooms & Guests": "Rom og gjester", "Sauna and gym": "Sauna and gym", @@ -375,6 +379,8 @@ "{amount} out of {total}": "{amount} av {total}", "type": "type", "types": "typer", + "room type": "romtype", + "room types": "romtyper", "{amount} {currency}": "{amount} {currency}", "{difference}{amount} {currency}": "{difference}{amount} {currency}", "{width} cm × {length} cm": "{width} cm × {length} cm" diff --git a/i18n/dictionaries/sv.json b/i18n/dictionaries/sv.json index c88675fe9..5ee85ccde 100644 --- a/i18n/dictionaries/sv.json +++ b/i18n/dictionaries/sv.json @@ -5,13 +5,16 @@ "A photo of the room": "Ett foto av rummet", "About meetings & conferences": "About meetings & conferences", "About the hotel": "Om hotellet", + "Accessibility room": "Tillgänglighetsrum", "Activities": "Aktiviteter", "Add code": "Lägg till kod", "Add new card": "Lägg till nytt kort", + "Add room": "Lägg till rum", "Address": "Adress", "Adults": "Vuxna", "Airport": "Flygplats", "All our breakfast buffets offer gluten free, vegan, and allergy-friendly options.": "Alla våra frukostbufféer erbjuder glutenfria, veganska och allergivänliga alternativ.", + "Allergy room": "Allergirum", "Already a friend?": "Är du redan en vän?", "Amenities": "Bekvämligheter", "Amusement park": "Nöjespark", @@ -212,6 +215,7 @@ "Pay later": "Betala senare", "Pay now": "Betala nu", "Payment info": "Betalningsinformation", + "Pet room": "Husdjursrum", "Phone": "Telefon", "Phone is required": "Telefonnummer är obligatorisk", "Phone number": "Telefonnummer", @@ -241,7 +245,7 @@ "Retype new password": "Upprepa nytt lösenord", "Room & Terms": "Rum & Villkor", "Room facilities": "Rumfaciliteter", - "Room types available": "rumstyper {numberOfRooms, plural, one {# type} other {# types}} tillgängliga", + "Room types available": "{numberOfRooms, plural, one {# room type} other {# room types}} tillgängliga", "Rooms": "Rum", "Rooms & Guests": "Rum och gäster", "Sauna and gym": "Sauna and gym", @@ -376,6 +380,8 @@ "{amount} out of {total}": "{amount} av {total}", "type": "typ", "types": "typer", + "room type": "rumtyp", + "room types": "rumstyper", "{amount} {currency}": "{amount} {currency}", "{difference}{amount} {currency}": "{difference}{amount} {currency}", "{width} cm × {length} cm": "{width} cm × {length} cm" diff --git a/server/routers/hotels/query.ts b/server/routers/hotels/query.ts index b5b5ff34b..289429ca7 100644 --- a/server/routers/hotels/query.ts +++ b/server/routers/hotels/query.ts @@ -441,6 +441,7 @@ export const hotelQueryRouter = router({ }, params ) + if (!apiResponse.ok) { const text = await apiResponse.text() roomsAvailabilityFailCounter.add(1, { diff --git a/server/routers/hotels/schemas/room.ts b/server/routers/hotels/schemas/room.ts index 19f922db0..af3a3e8bc 100644 --- a/server/routers/hotels/schemas/room.ts +++ b/server/routers/hotels/schemas/room.ts @@ -92,3 +92,9 @@ export const roomSchema = z roomFacilities: data.attributes.roomFacilities, } }) + +export const roomFilterSchema = z.object({ + accessibility: z.boolean(), + petFriendly: z.boolean(), + allergyFriendly: z.boolean(), +}) diff --git a/types/components/hotelReservation/selectRate/roomFilter.ts b/types/components/hotelReservation/selectRate/roomFilter.ts index 3ac0c6be5..ac31cfdbd 100644 --- a/types/components/hotelReservation/selectRate/roomFilter.ts +++ b/types/components/hotelReservation/selectRate/roomFilter.ts @@ -1,3 +1,9 @@ +import { z } from "zod" + +import { roomFilterSchema } from "@/server/routers/hotels/schemas/room" + export interface RoomFilterProps { numberOfRooms: number } + +export interface RoomFilterFormData extends z.output {}