From 445bde8e2e2f565e6055866a2c23affde6e6f179 Mon Sep 17 00:00:00 2001 From: Erik Tiekstra Date: Tue, 22 Oct 2024 15:45:00 +0200 Subject: [PATCH 1/5] feat(SW-671): Added correct icons to amenities list --- .../AmenitiesList/amenitiesList.module.css | 4 + .../HotelPage/AmenitiesList/index.tsx | 12 +- components/ContentType/HotelPage/data.ts | 292 +++++++++++++++++- .../HotelReservation/HotelCard/index.tsx | 8 +- .../SelectRate/HotelInfoCard/index.tsx | 2 +- components/Icons/Accesories.tsx | 40 +++ components/Icons/Air.tsx | 36 +++ .../Icons/{Coffee.tsx => CoffeeAlt.tsx} | 6 +- components/Icons/ConvenienceStore24h.tsx | 40 +++ components/Icons/Cool.tsx | 36 +++ components/Icons/DoorOpen.tsx | 6 +- components/Icons/Dresser.tsx | 36 +++ components/Icons/ElectricCar.tsx | 40 +++ components/Icons/Fan.tsx | 36 +++ components/Icons/Footstool.tsx | 40 +++ components/Icons/Garage.tsx | 36 +++ components/Icons/Golf.tsx | 36 +++ components/Icons/Groceries.tsx | 40 +++ components/Icons/Hanger.tsx | 36 +++ components/Icons/HangerAlt.tsx | 40 +++ components/Icons/Heat.tsx | 36 +++ components/Icons/Kayaking.tsx | 40 +++ components/Icons/Kettle.tsx | 36 +++ components/Icons/Lamp.tsx | 36 +++ components/Icons/LaundryMachine.tsx | 40 +++ components/Icons/LocalBar.tsx | 40 +++ components/Icons/Nature.tsx | 36 +++ components/Icons/Nightlife.tsx | 40 +++ components/Icons/NoSmoking.tsx | 40 +++ components/Icons/OutdoorFurniture.tsx | 40 +++ components/Icons/RoomService.tsx | 40 +++ components/Icons/Skateboarding.tsx | 40 +++ components/Icons/Smoking.tsx | 36 +++ components/Icons/Spa.tsx | 36 +++ components/Icons/Street.tsx | 36 +++ components/Icons/Swim.tsx | 36 +++ components/Icons/Thermostat.tsx | 40 +++ components/Icons/Tshirt.tsx | 36 +++ components/Icons/TvCasting.tsx | 40 +++ components/Icons/get-icon-by-icon-name.ts | 102 +++++- components/Icons/index.tsx | 34 +- server/routers/hotels/output.ts | 9 +- server/routers/hotels/query.ts | 8 +- types/components/hotelPage/facilities.ts | 8 +- types/components/icon.ts | 40 ++- types/enums/facilities.ts | 262 ++++++++++++++++ utils/facilityCards.ts | 16 +- 47 files changed, 1970 insertions(+), 55 deletions(-) create mode 100644 components/Icons/Accesories.tsx create mode 100644 components/Icons/Air.tsx rename components/Icons/{Coffee.tsx => CoffeeAlt.tsx} (96%) create mode 100644 components/Icons/ConvenienceStore24h.tsx create mode 100644 components/Icons/Cool.tsx create mode 100644 components/Icons/Dresser.tsx create mode 100644 components/Icons/ElectricCar.tsx create mode 100644 components/Icons/Fan.tsx create mode 100644 components/Icons/Footstool.tsx create mode 100644 components/Icons/Garage.tsx create mode 100644 components/Icons/Golf.tsx create mode 100644 components/Icons/Groceries.tsx create mode 100644 components/Icons/Hanger.tsx create mode 100644 components/Icons/HangerAlt.tsx create mode 100644 components/Icons/Heat.tsx create mode 100644 components/Icons/Kayaking.tsx create mode 100644 components/Icons/Kettle.tsx create mode 100644 components/Icons/Lamp.tsx create mode 100644 components/Icons/LaundryMachine.tsx create mode 100644 components/Icons/LocalBar.tsx create mode 100644 components/Icons/Nature.tsx create mode 100644 components/Icons/Nightlife.tsx create mode 100644 components/Icons/NoSmoking.tsx create mode 100644 components/Icons/OutdoorFurniture.tsx create mode 100644 components/Icons/RoomService.tsx create mode 100644 components/Icons/Skateboarding.tsx create mode 100644 components/Icons/Smoking.tsx create mode 100644 components/Icons/Spa.tsx create mode 100644 components/Icons/Street.tsx create mode 100644 components/Icons/Swim.tsx create mode 100644 components/Icons/Thermostat.tsx create mode 100644 components/Icons/Tshirt.tsx create mode 100644 components/Icons/TvCasting.tsx create mode 100644 types/enums/facilities.ts diff --git a/components/ContentType/HotelPage/AmenitiesList/amenitiesList.module.css b/components/ContentType/HotelPage/AmenitiesList/amenitiesList.module.css index f7dcea5fc..e35dc13a6 100644 --- a/components/ContentType/HotelPage/AmenitiesList/amenitiesList.module.css +++ b/components/ContentType/HotelPage/AmenitiesList/amenitiesList.module.css @@ -20,6 +20,10 @@ gap: var(--Spacing-x1); } +.icon { + flex-shrink: 0; +} + .showAllAmenities { width: fit-content; } diff --git a/components/ContentType/HotelPage/AmenitiesList/index.tsx b/components/ContentType/HotelPage/AmenitiesList/index.tsx index 8b6a1977e..8c90f19c2 100644 --- a/components/ContentType/HotelPage/AmenitiesList/index.tsx +++ b/components/ContentType/HotelPage/AmenitiesList/index.tsx @@ -16,9 +16,7 @@ export default async function AmenitiesList({ detailedFacilities, }: AmenitiesListProps) { const intl = await getIntl() - const sortedAmenities = detailedFacilities - .sort((a, b) => b.sortOrder - a.sortOrder) - .slice(0, 5) + const facilities = detailedFacilities.slice(0, 5) const lang = getLang() return (
@@ -26,11 +24,13 @@ export default async function AmenitiesList({ {intl.formatMessage({ id: "At the hotel" })}
- {sortedAmenities.map((facility) => { - const IconComponent = mapFacilityToIcon(facility.name) + {facilities.map((facility) => { + const IconComponent = mapFacilityToIcon(facility.id) return (
- {IconComponent && } + {IconComponent && ( + + )} {facility.name}
) diff --git a/components/ContentType/HotelPage/data.ts b/components/ContentType/HotelPage/data.ts index ace3a0408..e4043c53e 100644 --- a/components/ContentType/HotelPage/data.ts +++ b/components/ContentType/HotelPage/data.ts @@ -3,21 +3,287 @@ import { FC } from "react" import { getIconByIconName } from "@/components/Icons/get-icon-by-icon-name" import { IconName, IconProps } from "@/types/components/icon" +import { FacilityEnum } from "@/types/enums/facilities" -const facilityToIconMap: { [key: string]: IconName } = { - Bar: IconName.Bar, - "Bikes for loan": IconName.Biking, - Gym: IconName.Fitness, - "Free WiFi": IconName.Wifi, - //TODO: Ask design team what icon(s) should be used for meetings. - "Meeting rooms": IconName.People2, - "Meeting / conference facilities": IconName.People2, - "Pet-friendly rooms": IconName.Pets, - Sauna: IconName.Sauna, - Restaurant: IconName.Restaurant, +const facilityToIconMap: Record = { + [FacilityEnum.Bar]: IconName.LocalBar, + [FacilityEnum.Skybar]: IconName.LocalBar, + [FacilityEnum.RooftopBar]: IconName.LocalBar, + [FacilityEnum.BikesForLoan]: IconName.Biking, + [FacilityEnum.Gym]: IconName.Fitness, + [FacilityEnum.GymTrainingFacilities]: IconName.Fitness, + [FacilityEnum.KeyAccessOnlyToHealthClubGym]: IconName.Fitness, + [FacilityEnum.FreeWiFi]: IconName.Wifi, + [FacilityEnum.MeetingRooms]: IconName.People2, + [FacilityEnum.MeetingConferenceFacilities]: IconName.People2, + [FacilityEnum.PetFriendlyRooms]: IconName.Pets, + [FacilityEnum.Sauna]: IconName.Sauna, + [FacilityEnum.Restaurant]: IconName.Restaurant, + [FacilityEnum.ParkingGarage]: IconName.Garage, + [FacilityEnum.ParkingElectricCharging]: IconName.ElectricCar, + [FacilityEnum.ParkingFreeParking]: IconName.Parking, + [FacilityEnum.ParkingOutdoor]: IconName.Parking, + [FacilityEnum.ParkingAdditionalCost]: IconName.Parking, + [FacilityEnum.DisabledParking]: IconName.Parking, + [FacilityEnum.OutdoorTerrace]: IconName.OutdoorFurniture, + [FacilityEnum.RoomService]: IconName.RoomService, + [FacilityEnum.LaundryRoom]: IconName.LaundryMachine, + [FacilityEnum.LaundryService]: IconName.LaundryMachine, + [FacilityEnum.LaundryServiceExpress]: IconName.LaundryMachine, + [FacilityEnum.ScandicShop24Hrs]: IconName.ConvenienceStore24h, + [FacilityEnum.ServesBreakfastAlwaysIncluded]: IconName.CoffeeAlt, + [FacilityEnum.ServesBreakfastNotAlwaysIncluded]: IconName.CoffeeAlt, + [FacilityEnum.ServesOrganicBreakfastAlwaysIncluded]: IconName.CoffeeAlt, + [FacilityEnum.ServesOrganicBreakfastNotAlwaysIncluded]: IconName.CoffeeAlt, + [FacilityEnum.Breakfast]: IconName.CoffeeAlt, + [FacilityEnum.EBikesChargingStation]: IconName.ElectricBike, + [FacilityEnum.Shopping]: IconName.Shopping, + [FacilityEnum.Golf]: IconName.Golf, + [FacilityEnum.GolfCourse0To30Km]: IconName.Golf, + [FacilityEnum.TVWithChromecast1]: IconName.TvCasting, + [FacilityEnum.TVWithChromecast2]: IconName.TvCasting, + [FacilityEnum.DJLiveMusic]: IconName.Nightlife, + [FacilityEnum.DiscoNightClub]: IconName.Nightlife, + [FacilityEnum.CoffeeInReceptionAtCharge]: IconName.CoffeeAlt, + [FacilityEnum.CoffeeShop]: IconName.CoffeeAlt, + [FacilityEnum.CoffeeTeaFacilities]: IconName.CoffeeAlt, + [FacilityEnum.SkateboardsForLoan]: IconName.Skateboarding, + [FacilityEnum.KayaksForLoan]: IconName.Kayaking, + [FacilityEnum.LifestyleConcierge]: IconName.Concierge, + [FacilityEnum.WellnessAndSaunaEntranceFeeAdmission16PlusYears]: + IconName.Sauna, + [FacilityEnum.WellnessPoolSaunaEntranceFeeAdmission16PlusYears]: + IconName.Sauna, + [FacilityEnum.Cafe]: IconName.Restaurant, + [FacilityEnum.Pool]: IconName.Swim, + [FacilityEnum.PoolSwimmingPoolJacuzziAtHotel]: IconName.Swim, + [FacilityEnum.VendingMachineWithNecessities]: IconName.Groceries, + + [FacilityEnum.Jacuzzi]: IconName.StarFilled, + [FacilityEnum.JacuzziInRoom]: IconName.StarFilled, + + [FacilityEnum.AccessibleBathingControls]: IconName.StarFilled, + [FacilityEnum.AccessibleBathtubs]: IconName.StarFilled, + [FacilityEnum.AccessibleElevators]: IconName.StarFilled, + [FacilityEnum.AccessibleLightSwitch]: IconName.StarFilled, + [FacilityEnum.AccessibleRoomsAtHotel1]: IconName.StarFilled, + [FacilityEnum.AccessibleRoomsAtHotel2]: IconName.StarFilled, + [FacilityEnum.AccessibleToilets]: IconName.StarFilled, + [FacilityEnum.AccessibleWashBasins]: IconName.StarFilled, + [FacilityEnum.AdaptedRoomDoors]: IconName.StarFilled, + [FacilityEnum.AdjoiningConventionCentre]: IconName.StarFilled, + [FacilityEnum.AirConAirCooling]: IconName.StarFilled, + [FacilityEnum.AirConditioningInRoom]: IconName.StarFilled, + [FacilityEnum.AirportMaxDistance8Km]: IconName.StarFilled, + [FacilityEnum.AlarmsContinuouslyMonitored]: IconName.StarFilled, + [FacilityEnum.AlarmsHaveStrobeLightsForDeafHardHearingInAllGuestRooms]: + IconName.StarFilled, + [FacilityEnum.AlarmsHaveStrobeLightsForDeafHardHearingInAllHallways]: + IconName.StarFilled, + [FacilityEnum.AlarmsHaveStrobeLightsForDeafHardHearingInAllPublicAreas]: + IconName.StarFilled, + [FacilityEnum.AllAudibleSmokeAlarmsHardwired]: IconName.StarFilled, + [FacilityEnum.AllExteriorDoorsRequireKeyAccessAtNightOrAutomaticallyLock]: + IconName.StarFilled, + [FacilityEnum.AllGuestRoomDoorsHaveViewports]: IconName.StarFilled, + [FacilityEnum.AllGuestRoomDoorsSelfClosing]: IconName.StarFilled, + [FacilityEnum.AllParkingAreasPatrolled]: IconName.StarFilled, + [FacilityEnum.AllParkingAreasWellLit]: IconName.StarFilled, + [FacilityEnum.AllStairsWellsVentilated]: IconName.StarFilled, + [FacilityEnum.ArmchairBed]: IconName.StarFilled, + [FacilityEnum.AudibleAlarms]: IconName.StarFilled, + [FacilityEnum.AudibleSmokeAlarmsInAllHalls]: IconName.StarFilled, + [FacilityEnum.AudibleSmokeAlarmsInAllPublicAreas]: IconName.StarFilled, + [FacilityEnum.AudibleSmokeAlarmsInAllRooms]: IconName.StarFilled, + [FacilityEnum.AudioVisualEquipmentAvailable]: IconName.StarFilled, + [FacilityEnum.AutolinkFireDepartment]: IconName.StarFilled, + [FacilityEnum.AutomatedExternalDefibrillatorOnSiteAED]: IconName.StarFilled, + [FacilityEnum.AutomaticFireDoors]: IconName.StarFilled, + [FacilityEnum.AutoRecallElevators]: IconName.StarFilled, + [FacilityEnum.BalconiesAccessibleToAdjoiningRooms]: IconName.StarFilled, + [FacilityEnum.Ballroom]: IconName.StarFilled, + [FacilityEnum.Banquet]: IconName.StarFilled, + [FacilityEnum.BasicMedicalEquipmentOnSite]: IconName.StarFilled, + [FacilityEnum.BathroomsAdaptedForDisabledGuests]: IconName.StarFilled, + [FacilityEnum.Beach]: IconName.StarFilled, + [FacilityEnum.Beach0To1Km]: IconName.StarFilled, + [FacilityEnum.BeautySalon]: IconName.StarFilled, + [FacilityEnum.BedroomsWithWheelchairAccess]: IconName.StarFilled, + [FacilityEnum.Bowling]: IconName.StarFilled, + [FacilityEnum.BrailleLargePrintHotelLiterature]: IconName.StarFilled, + [FacilityEnum.BrailleLargePrintMenus]: IconName.StarFilled, + [FacilityEnum.Business1]: IconName.StarFilled, + [FacilityEnum.Business2]: IconName.StarFilled, + [FacilityEnum.BusinessCentre]: IconName.StarFilled, + [FacilityEnum.CashFree8pmTill6am]: IconName.StarFilled, + [FacilityEnum.CashFreeHotel]: IconName.StarFilled, + [FacilityEnum.ChildrenWelcome]: IconName.StarFilled, + [FacilityEnum.City]: IconName.StarFilled, + [FacilityEnum.ColourTVInRoomsAllScandicHotels]: IconName.StarFilled, + [FacilityEnum.ComplimentaryColdRefreshments]: IconName.StarFilled, + [FacilityEnum.CongressHall]: IconName.StarFilled, + [FacilityEnum.ConventionCentre]: IconName.StarFilled, + [FacilityEnum.Couples]: IconName.StarFilled, + [FacilityEnum.DeadboltsOnConnectingDoors]: IconName.StarFilled, + [FacilityEnum.DeadboltsSecondaryLocksOnAllGuestRoomDoors]: + IconName.StarFilled, + [FacilityEnum.Defibrillator]: IconName.StarFilled, + [FacilityEnum.Desk]: IconName.StarFilled, + [FacilityEnum.DirectDialPhoneInRoomsAllScandic]: IconName.StarFilled, + [FacilityEnum.DisabledEmergencyPlan1]: IconName.StarFilled, + [FacilityEnum.DisabledEmergencyPlan2]: IconName.StarFilled, + [FacilityEnum.DO_NOT_USE_Restaurant]: IconName.StarFilled, + [FacilityEnum.Downtown]: IconName.StarFilled, + [FacilityEnum.DrinkableTapWater]: IconName.StarFilled, + [FacilityEnum.DVDPlayer]: IconName.StarFilled, + [FacilityEnum.ElectronicKeyCards]: IconName.StarFilled, + [FacilityEnum.Elevator]: IconName.StarFilled, + [FacilityEnum.EmergencyBackUpGenerators]: IconName.StarFilled, + [FacilityEnum.EmergencyCallButtonOnPhone]: IconName.StarFilled, + [FacilityEnum.EmergencyCodesOrButtonsInRooms]: IconName.StarFilled, + [FacilityEnum.EmergencyEvacuationPlan1]: IconName.StarFilled, + [FacilityEnum.EmergencyEvacuationPlan2]: IconName.StarFilled, + [FacilityEnum.EmergencyEvaluationDrillFrequency]: IconName.StarFilled, + [FacilityEnum.EmergencyInfoInAllRooms]: IconName.StarFilled, + [FacilityEnum.EmergencyLightingAllScandic]: IconName.StarFilled, + [FacilityEnum.EmergencyLightningInAllPublicAreas]: IconName.StarFilled, + [FacilityEnum.EmergencyServiceResponseTimeInMinutes]: IconName.StarFilled, + [FacilityEnum.Entertainment]: IconName.StarFilled, + [FacilityEnum.EventVenue]: IconName.StarFilled, + [FacilityEnum.ExchangeFacility]: IconName.StarFilled, + [FacilityEnum.ExitMapsInRooms]: IconName.StarFilled, + [FacilityEnum.ExitSignsLit]: IconName.StarFilled, + [FacilityEnum.ExtraFamilyFriendly]: IconName.StarFilled, + [FacilityEnum.Families]: IconName.StarFilled, + [FacilityEnum.FaxFacilityInRoom]: IconName.StarFilled, + [FacilityEnum.Financial]: IconName.StarFilled, + [FacilityEnum.FireDetectorsAllScandic]: IconName.StarFilled, + [FacilityEnum.FireDetectorsInAllHalls]: IconName.StarFilled, + [FacilityEnum.FireDetectorsInAllPublicAreas]: IconName.StarFilled, + [FacilityEnum.FireDetectorsInAllRooms]: IconName.StarFilled, + [FacilityEnum.FireExtinguishersInAllPublicAreas]: IconName.StarFilled, + [FacilityEnum.FireExtinguishersInPublicAreasAllScandic]: IconName.StarFilled, + [FacilityEnum.FireSafetyAllScandic]: IconName.StarFilled, + [FacilityEnum.FirstAidAvailable]: IconName.StarFilled, + [FacilityEnum.FoodDrinks247]: IconName.StarFilled, + [FacilityEnum.GiftShop]: IconName.StarFilled, + [FacilityEnum.GuestRoomDoorsHaveASecondLock]: IconName.StarFilled, + [FacilityEnum.Hairdresser]: IconName.StarFilled, + [FacilityEnum.HairdryerInRoomAllScandic]: IconName.StarFilled, + [FacilityEnum.HandicapFacilities]: IconName.StarFilled, + [FacilityEnum.HandrailsInBathrooms]: IconName.StarFilled, + [FacilityEnum.HearingInductionLoops]: IconName.StarFilled, + [FacilityEnum.Highway1]: IconName.StarFilled, + [FacilityEnum.Highway2]: IconName.StarFilled, + [FacilityEnum.Hiking0To3Km]: IconName.StarFilled, + [FacilityEnum.HotelCompliesWithAAASecurityStandards]: IconName.StarFilled, + [FacilityEnum.HotelIsFollowingScandicsSafetySecurityPolicy]: + IconName.StarFilled, + [FacilityEnum.HotelWorksAccordingToScandicsAccessibilityConcepts]: + IconName.StarFilled, + [FacilityEnum.IceMachine]: IconName.StarFilled, + [FacilityEnum.IceMachineReception]: IconName.StarFilled, + [FacilityEnum.IDRequiredToReplaceAGuestRoomKey]: IconName.StarFilled, + [FacilityEnum.IfNoWhatAreTheHoursUse24ClockEx0000To0600]: IconName.StarFilled, + [FacilityEnum.InCountry]: IconName.StarFilled, + [FacilityEnum.IndustrialPark]: IconName.StarFilled, + [FacilityEnum.InternetHighSpeedInternetConnectionAllScandic]: + IconName.StarFilled, + [FacilityEnum.InternetHotSpotsAllScandic]: IconName.StarFilled, + [FacilityEnum.IroningRoom]: IconName.StarFilled, + [FacilityEnum.IronIroningBoardAllScandic]: IconName.StarFilled, + [FacilityEnum.KeyAccessOnlySecuredFloorsAvailable]: IconName.StarFilled, + [FacilityEnum.KidsPlayRoom]: IconName.StarFilled, + [FacilityEnum.KidsUpToAndIncluding12YearsStayForFree]: IconName.StarFilled, + [FacilityEnum.KitchenInRoom]: IconName.StarFilled, + [FacilityEnum.Lake0To1Km]: IconName.StarFilled, + [FacilityEnum.LakeOrSea0To1Km]: IconName.StarFilled, + [FacilityEnum.LaptopSafe]: IconName.StarFilled, + [FacilityEnum.Leisure]: IconName.StarFilled, + [FacilityEnum.LuggageLockers]: IconName.StarFilled, + [FacilityEnum.Massage]: IconName.StarFilled, + [FacilityEnum.MinibarInRoom]: IconName.StarFilled, + [FacilityEnum.MobileLift]: IconName.StarFilled, + [FacilityEnum.Mountains0To1Km]: IconName.StarFilled, + [FacilityEnum.MovieChannelsInRoomAllScandic]: IconName.StarFilled, + [FacilityEnum.MultipleExitsOnEachFloor]: IconName.StarFilled, + [FacilityEnum.NonSmokingRoomsAllScandic]: IconName.StarFilled, + [FacilityEnum.OnSiteTrainingFacilities]: IconName.StarFilled, + [FacilityEnum.OtherExplainInBriefDescription]: IconName.StarFilled, + [FacilityEnum.OvernightSecurity]: IconName.StarFilled, + [FacilityEnum.ParkingAttendant]: IconName.StarFilled, + [FacilityEnum.PCHookUpInRoom]: IconName.StarFilled, + [FacilityEnum.PillowAlarmsAvailable]: IconName.StarFilled, + [FacilityEnum.PlayStationInPlayArea]: IconName.StarFilled, + [FacilityEnum.PrintingService]: IconName.StarFilled, + [FacilityEnum.PropertyMeetsRequirementsFireSafety]: IconName.StarFilled, + [FacilityEnum.PublicAddressSystem]: IconName.StarFilled, + [FacilityEnum.RelaxationSuite]: IconName.StarFilled, + [FacilityEnum.RestrictedRoomAccessAllScandic]: IconName.StarFilled, + [FacilityEnum.RoomsAccessibleFromTheInterior]: IconName.StarFilled, + [FacilityEnum.RoomWindowsOpen]: IconName.StarFilled, + [FacilityEnum.RoomWindowsThatOpenHaveLockingDevice]: IconName.StarFilled, + [FacilityEnum.Rural1]: IconName.StarFilled, + [FacilityEnum.Rural2]: IconName.StarFilled, + [FacilityEnum.SafeDepositBoxInRoomsAllScandic]: IconName.StarFilled, + [FacilityEnum.SafeDepositBoxInRoomsCanHoldA17InchLaptop]: IconName.StarFilled, + [FacilityEnum.SafeDepositBoxInRoomsCannotHoldALaptop]: IconName.StarFilled, + [FacilityEnum.SafetyChainsOnGuestRoomDoor]: IconName.StarFilled, + [FacilityEnum.SecondaryLocksOnSlidingGlassDoors]: IconName.StarFilled, + [FacilityEnum.SecondaryLocksOnWindows]: IconName.StarFilled, + [FacilityEnum.Security24Hours]: IconName.StarFilled, + [FacilityEnum.SecurityEscortsAvailableOnRequest]: IconName.StarFilled, + [FacilityEnum.SecurityPersonnelOnSite]: IconName.StarFilled, + [FacilityEnum.SeparateFloorsForWomen]: IconName.StarFilled, + [FacilityEnum.ServiceGuideDogsAllowed]: IconName.StarFilled, + [FacilityEnum.ServiceSecurity24Hrs]: IconName.StarFilled, + [FacilityEnum.Skiing0To1Km]: IconName.StarFilled, + [FacilityEnum.SmokeDetectorsAllScandic]: IconName.StarFilled, + [FacilityEnum.Solarium]: IconName.StarFilled, + [FacilityEnum.SpecialNeedsMenus]: IconName.StarFilled, + [FacilityEnum.Sports]: IconName.StarFilled, + [FacilityEnum.SprinklersAllScandic]: IconName.StarFilled, + [FacilityEnum.SprinklersInAllHalls]: IconName.StarFilled, + [FacilityEnum.SprinklersInAllPublicAreas]: IconName.StarFilled, + [FacilityEnum.SprinklersInAllRooms]: IconName.StarFilled, + [FacilityEnum.StaffInDuplicateKeys]: IconName.StarFilled, + [FacilityEnum.StaffRedCrossCertifiedInCPR]: IconName.StarFilled, + [FacilityEnum.StaffTrainedForDisabledGuests]: IconName.StarFilled, + [FacilityEnum.StaffTrainedInAutomatedExternalDefibrillatorUsageAED]: + IconName.StarFilled, + [FacilityEnum.StaffTrainedInCPR]: IconName.StarFilled, + [FacilityEnum.StaffTrainedInFirstAid]: IconName.StarFilled, + [FacilityEnum.StaffTrainedInFirstAidTechniques]: IconName.StarFilled, + [FacilityEnum.StaffTrainedToCaterForDisabledGuestsAllScandic]: + IconName.StarFilled, + [FacilityEnum.Suburbs]: IconName.StarFilled, + [FacilityEnum.SwingboltLock]: IconName.StarFilled, + [FacilityEnum.TeleConferencingFacilitiesAvailable]: IconName.StarFilled, + [FacilityEnum.TelevisionsWithSubtitlesOrClosedCaptions]: IconName.StarFilled, + [FacilityEnum.Tennis1]: IconName.StarFilled, + [FacilityEnum.Tennis2]: IconName.StarFilled, + [FacilityEnum.TennisPadel]: IconName.StarFilled, + [FacilityEnum.Theatre]: IconName.StarFilled, + [FacilityEnum.TrouserPress]: IconName.StarFilled, + [FacilityEnum.UniformSecurityOnPremises]: IconName.StarFilled, + [FacilityEnum.UtilityRoomForIroning]: IconName.StarFilled, + [FacilityEnum.VideoSurveillanceInHallways]: IconName.StarFilled, + [FacilityEnum.VideoSurveillanceInPublicAreas]: IconName.StarFilled, + [FacilityEnum.VideoSurveillanceMonitored24HrsADay]: IconName.StarFilled, + [FacilityEnum.VideoSurveillanceOfAllParkingAreas]: IconName.StarFilled, + [FacilityEnum.VideoSurveillanceOfExteriorFrontEntrance]: IconName.StarFilled, + [FacilityEnum.VideoSurveillanceRecorded24HrsADayParkingArea]: + IconName.StarFilled, + [FacilityEnum.WallMountedCycleRack]: IconName.StarFilled, + [FacilityEnum.WellLitWalkways]: IconName.StarFilled, + [FacilityEnum.WheelchairAccess]: IconName.StarFilled, + [FacilityEnum.WideCorridors]: IconName.StarFilled, + [FacilityEnum.WideEntrance]: IconName.StarFilled, + [FacilityEnum.WideRestaurantEntrance]: IconName.StarFilled, + [FacilityEnum.WiFiWirelessInternetAccessAllScandic]: IconName.StarFilled, } -export function mapFacilityToIcon(facilityName: string): FC | null { - const iconName = facilityToIconMap[facilityName] +export function mapFacilityToIcon(id: FacilityEnum): FC | null { + const iconName = facilityToIconMap[id] return getIconByIconName(iconName) || null } diff --git a/components/HotelReservation/HotelCard/index.tsx b/components/HotelReservation/HotelCard/index.tsx index ea3eb29b7..67ce49f17 100644 --- a/components/HotelReservation/HotelCard/index.tsx +++ b/components/HotelReservation/HotelCard/index.tsx @@ -22,9 +22,7 @@ export default async function HotelCard({ hotel }: HotelCardProps) { const { hotelData } = hotel const { price } = hotel - const sortedAmenities = hotelData.detailedFacilities - .sort((a, b) => b.sortOrder - a.sortOrder) - .slice(0, 5) + const amenities = hotelData.detailedFacilities.slice(0, 5) return (
@@ -57,8 +55,8 @@ export default async function HotelCard({ hotel }: HotelCardProps) {
- {sortedAmenities.map((facility) => { - const IconComponent = mapFacilityToIcon(facility.name) + {amenities.map((facility) => { + const IconComponent = mapFacilityToIcon(facility.id) return (
{IconComponent && } diff --git a/components/HotelReservation/SelectRate/HotelInfoCard/index.tsx b/components/HotelReservation/SelectRate/HotelInfoCard/index.tsx index 5e0a24623..1ec58dd07 100644 --- a/components/HotelReservation/SelectRate/HotelInfoCard/index.tsx +++ b/components/HotelReservation/SelectRate/HotelInfoCard/index.tsx @@ -67,7 +67,7 @@ export default function HotelInfoCard({ hotelData }: HotelInfoCardProps) { {intl.formatMessage({ id: "At the hotel" })} {sortedFacilities?.map((facility) => { - const IconComponent = mapFacilityToIcon(facility.name) + const IconComponent = mapFacilityToIcon(facility.id) return (
{IconComponent && ( diff --git a/components/Icons/Accesories.tsx b/components/Icons/Accesories.tsx new file mode 100644 index 000000000..9aaf0c894 --- /dev/null +++ b/components/Icons/Accesories.tsx @@ -0,0 +1,40 @@ +import { iconVariants } from "./variants" + +import type { IconProps } from "@/types/components/icon" + +export default function AccesoriesIcon({ + className, + color, + ...props +}: IconProps) { + const classNames = iconVariants({ className, color }) + return ( + + + + + + + + + ) +} diff --git a/components/Icons/Air.tsx b/components/Icons/Air.tsx new file mode 100644 index 000000000..239566515 --- /dev/null +++ b/components/Icons/Air.tsx @@ -0,0 +1,36 @@ +import { iconVariants } from "./variants" + +import type { IconProps } from "@/types/components/icon" + +export default function AirIcon({ className, color, ...props }: IconProps) { + const classNames = iconVariants({ className, color }) + return ( + + + + + + + + + ) +} diff --git a/components/Icons/Coffee.tsx b/components/Icons/CoffeeAlt.tsx similarity index 96% rename from components/Icons/Coffee.tsx rename to components/Icons/CoffeeAlt.tsx index 840f78b0d..e76da5126 100644 --- a/components/Icons/Coffee.tsx +++ b/components/Icons/CoffeeAlt.tsx @@ -2,7 +2,11 @@ import { iconVariants } from "./variants" import type { IconProps } from "@/types/components/icon" -export default function CoffeeIcon({ className, color, ...props }: IconProps) { +export default function CoffeeAltIcon({ + className, + color, + ...props +}: IconProps) { const classNames = iconVariants({ className, color }) return ( + + + + + + + + ) +} diff --git a/components/Icons/Cool.tsx b/components/Icons/Cool.tsx new file mode 100644 index 000000000..efa5ae67f --- /dev/null +++ b/components/Icons/Cool.tsx @@ -0,0 +1,36 @@ +import { iconVariants } from "./variants" + +import type { IconProps } from "@/types/components/icon" + +export default function CoolIcon({ className, color, ...props }: IconProps) { + const classNames = iconVariants({ className, color }) + return ( + + + + + + + + + ) +} diff --git a/components/Icons/DoorOpen.tsx b/components/Icons/DoorOpen.tsx index f70c29859..93bc2caf4 100644 --- a/components/Icons/DoorOpen.tsx +++ b/components/Icons/DoorOpen.tsx @@ -2,7 +2,11 @@ import { iconVariants } from "./variants" import type { IconProps } from "@/types/components/icon" -export default function CoffeeIcon({ className, color, ...props }: IconProps) { +export default function DoorOpenIcon({ + className, + color, + ...props +}: IconProps) { const classNames = iconVariants({ className, color }) return ( + + + + + + + + ) +} diff --git a/components/Icons/ElectricCar.tsx b/components/Icons/ElectricCar.tsx new file mode 100644 index 000000000..1f9500b64 --- /dev/null +++ b/components/Icons/ElectricCar.tsx @@ -0,0 +1,40 @@ +import { iconVariants } from "./variants" + +import type { IconProps } from "@/types/components/icon" + +export default function ElectricCarIcon({ + className, + color, + ...props +}: IconProps) { + const classNames = iconVariants({ className, color }) + return ( + + + + + + + + + ) +} diff --git a/components/Icons/Fan.tsx b/components/Icons/Fan.tsx new file mode 100644 index 000000000..128200104 --- /dev/null +++ b/components/Icons/Fan.tsx @@ -0,0 +1,36 @@ +import { iconVariants } from "./variants" + +import type { IconProps } from "@/types/components/icon" + +export default function FanIcon({ className, color, ...props }: IconProps) { + const classNames = iconVariants({ className, color }) + return ( + + + + + + + + + ) +} diff --git a/components/Icons/Footstool.tsx b/components/Icons/Footstool.tsx new file mode 100644 index 000000000..becb0fd80 --- /dev/null +++ b/components/Icons/Footstool.tsx @@ -0,0 +1,40 @@ +import { iconVariants } from "./variants" + +import type { IconProps } from "@/types/components/icon" + +export default function FootstoolIcon({ + className, + color, + ...props +}: IconProps) { + const classNames = iconVariants({ className, color }) + return ( + + + + + + + + + ) +} diff --git a/components/Icons/Garage.tsx b/components/Icons/Garage.tsx new file mode 100644 index 000000000..b715532cc --- /dev/null +++ b/components/Icons/Garage.tsx @@ -0,0 +1,36 @@ +import { iconVariants } from "./variants" + +import type { IconProps } from "@/types/components/icon" + +export default function GarageIcon({ className, color, ...props }: IconProps) { + const classNames = iconVariants({ className, color }) + return ( + + + + + + + + + ) +} diff --git a/components/Icons/Golf.tsx b/components/Icons/Golf.tsx new file mode 100644 index 000000000..027c906d7 --- /dev/null +++ b/components/Icons/Golf.tsx @@ -0,0 +1,36 @@ +import { iconVariants } from "./variants" + +import type { IconProps } from "@/types/components/icon" + +export default function GolfIcon({ className, color, ...props }: IconProps) { + const classNames = iconVariants({ className, color }) + return ( + + + + + + + + + ) +} diff --git a/components/Icons/Groceries.tsx b/components/Icons/Groceries.tsx new file mode 100644 index 000000000..404f09f7b --- /dev/null +++ b/components/Icons/Groceries.tsx @@ -0,0 +1,40 @@ +import { iconVariants } from "./variants" + +import type { IconProps } from "@/types/components/icon" + +export default function GroceriesIcon({ + className, + color, + ...props +}: IconProps) { + const classNames = iconVariants({ className, color }) + return ( + + + + + + + + + ) +} diff --git a/components/Icons/Hanger.tsx b/components/Icons/Hanger.tsx new file mode 100644 index 000000000..63c869619 --- /dev/null +++ b/components/Icons/Hanger.tsx @@ -0,0 +1,36 @@ +import { iconVariants } from "./variants" + +import type { IconProps } from "@/types/components/icon" + +export default function HangerIcon({ className, color, ...props }: IconProps) { + const classNames = iconVariants({ className, color }) + return ( + + + + + + + + + ) +} diff --git a/components/Icons/HangerAlt.tsx b/components/Icons/HangerAlt.tsx new file mode 100644 index 000000000..732656e60 --- /dev/null +++ b/components/Icons/HangerAlt.tsx @@ -0,0 +1,40 @@ +import { iconVariants } from "./variants" + +import type { IconProps } from "@/types/components/icon" + +export default function HangerAltIcon({ + className, + color, + ...props +}: IconProps) { + const classNames = iconVariants({ className, color }) + return ( + + + + + + + + + ) +} diff --git a/components/Icons/Heat.tsx b/components/Icons/Heat.tsx new file mode 100644 index 000000000..ef093e955 --- /dev/null +++ b/components/Icons/Heat.tsx @@ -0,0 +1,36 @@ +import { iconVariants } from "./variants" + +import type { IconProps } from "@/types/components/icon" + +export default function HeatIcon({ className, color, ...props }: IconProps) { + const classNames = iconVariants({ className, color }) + return ( + + + + + + + + + ) +} diff --git a/components/Icons/Kayaking.tsx b/components/Icons/Kayaking.tsx new file mode 100644 index 000000000..1d4061a3a --- /dev/null +++ b/components/Icons/Kayaking.tsx @@ -0,0 +1,40 @@ +import { iconVariants } from "./variants" + +import type { IconProps } from "@/types/components/icon" + +export default function KayakingIcon({ + className, + color, + ...props +}: IconProps) { + const classNames = iconVariants({ className, color }) + return ( + + + + + + + + + ) +} diff --git a/components/Icons/Kettle.tsx b/components/Icons/Kettle.tsx new file mode 100644 index 000000000..28713719a --- /dev/null +++ b/components/Icons/Kettle.tsx @@ -0,0 +1,36 @@ +import { iconVariants } from "./variants" + +import type { IconProps } from "@/types/components/icon" + +export default function KettleIcon({ className, color, ...props }: IconProps) { + const classNames = iconVariants({ className, color }) + return ( + + + + + + + + + ) +} diff --git a/components/Icons/Lamp.tsx b/components/Icons/Lamp.tsx new file mode 100644 index 000000000..bde8af3dd --- /dev/null +++ b/components/Icons/Lamp.tsx @@ -0,0 +1,36 @@ +import { iconVariants } from "./variants" + +import type { IconProps } from "@/types/components/icon" + +export default function LampIcon({ className, color, ...props }: IconProps) { + const classNames = iconVariants({ className, color }) + return ( + + + + + + + + + ) +} diff --git a/components/Icons/LaundryMachine.tsx b/components/Icons/LaundryMachine.tsx new file mode 100644 index 000000000..b4225197d --- /dev/null +++ b/components/Icons/LaundryMachine.tsx @@ -0,0 +1,40 @@ +import { iconVariants } from "./variants" + +import type { IconProps } from "@/types/components/icon" + +export default function LaundryMachineIcon({ + className, + color, + ...props +}: IconProps) { + const classNames = iconVariants({ className, color }) + return ( + + + + + + + + + ) +} diff --git a/components/Icons/LocalBar.tsx b/components/Icons/LocalBar.tsx new file mode 100644 index 000000000..7a5b3bb51 --- /dev/null +++ b/components/Icons/LocalBar.tsx @@ -0,0 +1,40 @@ +import { iconVariants } from "./variants" + +import type { IconProps } from "@/types/components/icon" + +export default function LocalBarIcon({ + className, + color, + ...props +}: IconProps) { + const classNames = iconVariants({ className, color }) + return ( + + + + + + + + + ) +} diff --git a/components/Icons/Nature.tsx b/components/Icons/Nature.tsx new file mode 100644 index 000000000..4c48c377f --- /dev/null +++ b/components/Icons/Nature.tsx @@ -0,0 +1,36 @@ +import { iconVariants } from "./variants" + +import type { IconProps } from "@/types/components/icon" + +export default function NatureIcon({ className, color, ...props }: IconProps) { + const classNames = iconVariants({ className, color }) + return ( + + + + + + + + + ) +} diff --git a/components/Icons/Nightlife.tsx b/components/Icons/Nightlife.tsx new file mode 100644 index 000000000..093099cb9 --- /dev/null +++ b/components/Icons/Nightlife.tsx @@ -0,0 +1,40 @@ +import { iconVariants } from "./variants" + +import type { IconProps } from "@/types/components/icon" + +export default function NightlifeIcon({ + className, + color, + ...props +}: IconProps) { + const classNames = iconVariants({ className, color }) + return ( + + + + + + + + + ) +} diff --git a/components/Icons/NoSmoking.tsx b/components/Icons/NoSmoking.tsx new file mode 100644 index 000000000..bdaa7d3f3 --- /dev/null +++ b/components/Icons/NoSmoking.tsx @@ -0,0 +1,40 @@ +import { iconVariants } from "./variants" + +import type { IconProps } from "@/types/components/icon" + +export default function NoSmokingIcon({ + className, + color, + ...props +}: IconProps) { + const classNames = iconVariants({ className, color }) + return ( + + + + + + + + + ) +} diff --git a/components/Icons/OutdoorFurniture.tsx b/components/Icons/OutdoorFurniture.tsx new file mode 100644 index 000000000..3ddac9f38 --- /dev/null +++ b/components/Icons/OutdoorFurniture.tsx @@ -0,0 +1,40 @@ +import { iconVariants } from "./variants" + +import type { IconProps } from "@/types/components/icon" + +export default function OutdoorFurnitureIcon({ + className, + color, + ...props +}: IconProps) { + const classNames = iconVariants({ className, color }) + return ( + + + + + + + + + ) +} diff --git a/components/Icons/RoomService.tsx b/components/Icons/RoomService.tsx new file mode 100644 index 000000000..00aadd7d0 --- /dev/null +++ b/components/Icons/RoomService.tsx @@ -0,0 +1,40 @@ +import { iconVariants } from "./variants" + +import type { IconProps } from "@/types/components/icon" + +export default function RoomServiceIcon({ + className, + color, + ...props +}: IconProps) { + const classNames = iconVariants({ className, color }) + return ( + + + + + + + + + ) +} diff --git a/components/Icons/Skateboarding.tsx b/components/Icons/Skateboarding.tsx new file mode 100644 index 000000000..6c0106ff3 --- /dev/null +++ b/components/Icons/Skateboarding.tsx @@ -0,0 +1,40 @@ +import { iconVariants } from "./variants" + +import type { IconProps } from "@/types/components/icon" + +export default function SkateboardingIcon({ + className, + color, + ...props +}: IconProps) { + const classNames = iconVariants({ className, color }) + return ( + + + + + + + + + ) +} diff --git a/components/Icons/Smoking.tsx b/components/Icons/Smoking.tsx new file mode 100644 index 000000000..58c30abce --- /dev/null +++ b/components/Icons/Smoking.tsx @@ -0,0 +1,36 @@ +import { iconVariants } from "./variants" + +import type { IconProps } from "@/types/components/icon" + +export default function SmokingIcon({ className, color, ...props }: IconProps) { + const classNames = iconVariants({ className, color }) + return ( + + + + + + + + + ) +} diff --git a/components/Icons/Spa.tsx b/components/Icons/Spa.tsx new file mode 100644 index 000000000..f3141a4e4 --- /dev/null +++ b/components/Icons/Spa.tsx @@ -0,0 +1,36 @@ +import { iconVariants } from "./variants" + +import type { IconProps } from "@/types/components/icon" + +export default function SpaIcon({ className, color, ...props }: IconProps) { + const classNames = iconVariants({ className, color }) + return ( + + + + + + + + + ) +} diff --git a/components/Icons/Street.tsx b/components/Icons/Street.tsx new file mode 100644 index 000000000..414df197c --- /dev/null +++ b/components/Icons/Street.tsx @@ -0,0 +1,36 @@ +import { iconVariants } from "./variants" + +import type { IconProps } from "@/types/components/icon" + +export default function StreetIcon({ className, color, ...props }: IconProps) { + const classNames = iconVariants({ className, color }) + return ( + + + + + + + + + ) +} diff --git a/components/Icons/Swim.tsx b/components/Icons/Swim.tsx new file mode 100644 index 000000000..abd2bd29a --- /dev/null +++ b/components/Icons/Swim.tsx @@ -0,0 +1,36 @@ +import { iconVariants } from "./variants" + +import type { IconProps } from "@/types/components/icon" + +export default function SwimIcon({ className, color, ...props }: IconProps) { + const classNames = iconVariants({ className, color }) + return ( + + + + + + + + + ) +} diff --git a/components/Icons/Thermostat.tsx b/components/Icons/Thermostat.tsx new file mode 100644 index 000000000..2fd3ebe97 --- /dev/null +++ b/components/Icons/Thermostat.tsx @@ -0,0 +1,40 @@ +import { iconVariants } from "./variants" + +import type { IconProps } from "@/types/components/icon" + +export default function ThermostatIcon({ + className, + color, + ...props +}: IconProps) { + const classNames = iconVariants({ className, color }) + return ( + + + + + + + + + ) +} diff --git a/components/Icons/Tshirt.tsx b/components/Icons/Tshirt.tsx new file mode 100644 index 000000000..e6643725b --- /dev/null +++ b/components/Icons/Tshirt.tsx @@ -0,0 +1,36 @@ +import { iconVariants } from "./variants" + +import type { IconProps } from "@/types/components/icon" + +export default function TshirtIcon({ className, color, ...props }: IconProps) { + const classNames = iconVariants({ className, color }) + return ( + + + + + + + + + ) +} diff --git a/components/Icons/TvCasting.tsx b/components/Icons/TvCasting.tsx new file mode 100644 index 000000000..ea6e7b90d --- /dev/null +++ b/components/Icons/TvCasting.tsx @@ -0,0 +1,40 @@ +import { iconVariants } from "./variants" + +import type { IconProps } from "@/types/components/icon" + +export default function TvCastingIcon({ + className, + color, + ...props +}: IconProps) { + const classNames = iconVariants({ className, color }) + return ( + + + + + + + + + ) +} diff --git a/components/Icons/get-icon-by-icon-name.ts b/components/Icons/get-icon-by-icon-name.ts index 15752b50c..f5410421f 100644 --- a/components/Icons/get-icon-by-icon-name.ts +++ b/components/Icons/get-icon-by-icon-name.ts @@ -4,8 +4,10 @@ import FacebookIcon from "./Facebook" import InstagramIcon from "./Instagram" import TripAdvisorIcon from "./TripAdvisor" import { + AccesoriesIcon, AccessibilityIcon, AccountCircleIcon, + AirIcon, AirplaneIcon, ArrowRightIcon, BarIcon, @@ -22,27 +24,48 @@ import { ChevronRightSmallIcon, CloseIcon, CloseLargeIcon, - CoffeeIcon, + CoffeeAltIcon, ConciergeIcon, + ConvenienceStore24hIcon, + CoolIcon, CrossCircle, CulturalIcon, DoorOpenIcon, + DresserIcon, ElectricBikeIcon, + ElectricCarIcon, EmailIcon, EyeHideIcon, EyeShowIcon, + FanIcon, FitnessIcon, + FootstoolIcon, GalleryIcon, + GarageIcon, GiftIcon, GlobeIcon, + GolfIcon, + GroceriesIcon, + HangerAltIcon, + HangerIcon, + HeatIcon, HouseIcon, ImageIcon, InfoCircleIcon, + KayakingIcon, + KettleIcon, + LampIcon, + LaundryMachineIcon, + LocalBarIcon, LocationIcon, LockIcon, MapIcon, MinusIcon, MuseumIcon, + NatureIcon, + NightlifeIcon, + NoSmokingIcon, + OutdoorFurnitureIcon, ParkingIcon, People2Icon, PersonIcon, @@ -51,14 +74,23 @@ import { PlusCircleIcon, PlusIcon, RestaurantIcon, + RoomServiceIcon, SaunaIcon, SearchIcon, ServiceIcon, ShoppingIcon, + SkateboardingIcon, + SmokingIcon, SnowflakeIcon, + SpaIcon, StarFilledIcon, + StreetIcon, + SwimIcon, + ThermostatIcon, TrainIcon, + TshirtIcon, TshirtWashIcon, + TvCastingIcon, WarningTriangle, WifiIcon, } from "." @@ -67,10 +99,14 @@ import { IconName, IconProps } from "@/types/components/icon" export function getIconByIconName(icon?: IconName): FC | null { switch (icon) { + case IconName.Accesories: + return AccesoriesIcon case IconName.Accessibility: return AccessibilityIcon case IconName.AccountCircle: return AccountCircleIcon + case IconName.Air: + return AirIcon case IconName.Airplane: return AirplaneIcon case IconName.ArrowRight: @@ -105,32 +141,56 @@ export function getIconByIconName(icon?: IconName): FC | null { return CloseIcon case IconName.CloseLarge: return CloseLargeIcon - case IconName.Coffee: - return CoffeeIcon + case IconName.ConvenienceStore24h: + return ConvenienceStore24hIcon + case IconName.Cool: + return CoolIcon + case IconName.CoffeeAlt: + return CoffeeAltIcon case IconName.Concierge: return ConciergeIcon case IconName.Cultural: return CulturalIcon case IconName.DoorOpen: return DoorOpenIcon + case IconName.Dresser: + return DresserIcon case IconName.ElectricBike: return ElectricBikeIcon + case IconName.ElectricCar: + return ElectricCarIcon case IconName.Email: return EmailIcon case IconName.EyeHide: return EyeHideIcon case IconName.EyeShow: return EyeShowIcon + case IconName.Fan: + return FanIcon case IconName.Facebook: return FacebookIcon case IconName.Fitness: return FitnessIcon + case IconName.Footstool: + return FootstoolIcon case IconName.Gallery: return GalleryIcon + case IconName.Garage: + return GarageIcon case IconName.Gift: return GiftIcon case IconName.Globe: return GlobeIcon + case IconName.Golf: + return GolfIcon + case IconName.Groceries: + return GroceriesIcon + case IconName.Hanger: + return HangerIcon + case IconName.HangerAlt: + return HangerAltIcon + case IconName.Heat: + return HeatIcon case IconName.House: return HouseIcon case IconName.Image: @@ -139,6 +199,16 @@ export function getIconByIconName(icon?: IconName): FC | null { return InfoCircleIcon case IconName.Instagram: return InstagramIcon + case IconName.Kayaking: + return KayakingIcon + case IconName.Kettle: + return KettleIcon + case IconName.Lamp: + return LampIcon + case IconName.LaundryMachine: + return LaundryMachineIcon + case IconName.LocalBar: + return LocalBarIcon case IconName.Location: return LocationIcon case IconName.Lock: @@ -149,6 +219,14 @@ export function getIconByIconName(icon?: IconName): FC | null { return MinusIcon case IconName.Museum: return MuseumIcon + case IconName.Nature: + return NatureIcon + case IconName.Nightlife: + return NightlifeIcon + case IconName.NoSmoking: + return NoSmokingIcon + case IconName.OutdoorFurniture: + return OutdoorFurnitureIcon case IconName.Parking: return ParkingIcon case IconName.Person: @@ -165,6 +243,12 @@ export function getIconByIconName(icon?: IconName): FC | null { return PlusCircleIcon case IconName.Restaurant: return RestaurantIcon + case IconName.RoomService: + return RoomServiceIcon + case IconName.Smoking: + return SmokingIcon + case IconName.Spa: + return SpaIcon case IconName.Sauna: return SaunaIcon case IconName.Search: @@ -173,16 +257,28 @@ export function getIconByIconName(icon?: IconName): FC | null { return ServiceIcon case IconName.Shopping: return ShoppingIcon + case IconName.Skateboarding: + return SkateboardingIcon case IconName.Snowflake: return SnowflakeIcon case IconName.StarFilled: return StarFilledIcon + case IconName.Street: + return StreetIcon + case IconName.Swim: + return SwimIcon + case IconName.Thermostat: + return ThermostatIcon + case IconName.Tshirt: + return TshirtIcon case IconName.Train: return TrainIcon case IconName.Tripadvisor: return TripAdvisorIcon case IconName.TshirtWash: return TshirtWashIcon + case IconName.TvCasting: + return TvCastingIcon case IconName.WarningTriangle: return WarningTriangle case IconName.Wifi: diff --git a/components/Icons/index.tsx b/components/Icons/index.tsx index e8a0b6433..8a7764dd6 100644 --- a/components/Icons/index.tsx +++ b/components/Icons/index.tsx @@ -1,5 +1,7 @@ +export { default as AccesoriesIcon } from "./Accesories" export { default as AccessibilityIcon } from "./Accessibility" export { default as AccountCircleIcon } from "./AccountCircle" +export { default as AirIcon } from "./Air" export { default as AirplaneIcon } from "./Airplane" export { default as ArrowRightIcon } from "./ArrowRight" export { default as BarIcon } from "./Bar" @@ -17,34 +19,55 @@ export { default as ChevronRightIcon } from "./ChevronRight" export { default as ChevronRightSmallIcon } from "./ChevronRightSmall" export { default as CloseIcon } from "./Close" export { default as CloseLargeIcon } from "./CloseLarge" -export { default as CoffeeIcon } from "./Coffee" +export { default as CoffeeAltIcon } from "./CoffeeAlt" export { default as ConciergeIcon } from "./Concierge" +export { default as ConvenienceStore24hIcon } from "./ConvenienceStore24h" +export { default as CoolIcon } from "./Cool" export { default as CreditCard } from "./CreditCard" export { default as CrossCircle } from "./CrossCircle" export { default as CulturalIcon } from "./Cultural" export { default as DeleteIcon } from "./Delete" export { default as DoorOpenIcon } from "./DoorOpen" +export { default as DresserIcon } from "./Dresser" export { default as EditIcon } from "./Edit" export { default as ElectricBikeIcon } from "./ElectricBike" +export { default as ElectricCarIcon } from "./ElectricCar" export { default as EmailIcon } from "./Email" export { default as ErrorCircleIcon } from "./ErrorCircle" export { default as EyeHideIcon } from "./EyeHide" export { default as EyeShowIcon } from "./EyeShow" +export { default as FanIcon } from "./Fan" export { default as FitnessIcon } from "./Fitness" +export { default as FootstoolIcon } from "./Footstool" export { default as GalleryIcon } from "./Gallery" +export { default as GarageIcon } from "./Garage" export { default as GiftIcon } from "./Gift" export { default as GlobeIcon } from "./Globe" +export { default as GolfIcon } from "./Golf" +export { default as GroceriesIcon } from "./Groceries" +export { default as HangerIcon } from "./Hanger" +export { default as HangerAltIcon } from "./HangerAlt" export { default as HeartIcon } from "./Heart" +export { default as HeatIcon } from "./Heat" export { default as HouseIcon } from "./House" export { default as ImageIcon } from "./Image" export { default as InfoCircleIcon } from "./InfoCircle" +export { default as KayakingIcon } from "./Kayaking" +export { default as KettleIcon } from "./Kettle" export { default as KingBedIcon } from "./KingBed" +export { default as LampIcon } from "./Lamp" +export { default as LaundryMachineIcon } from "./LaundryMachine" +export { default as LocalBarIcon } from "./LocalBar" export { default as LocationIcon } from "./Location" export { default as LockIcon } from "./Lock" export { default as MapIcon } from "./Map" export { default as MinusIcon } from "./Minus" export { default as MuseumIcon } from "./Museum" +export { default as NatureIcon } from "./Nature" +export { default as NightlifeIcon } from "./Nightlife" export { default as NoBreakfastIcon } from "./NoBreakfast" +export { default as NoSmokingIcon } from "./NoSmoking" +export { default as OutdoorFurnitureIcon } from "./OutdoorFurniture" export { default as ParkingIcon } from "./Parking" export { default as People2Icon } from "./People2" export { default as PersonIcon } from "./Person" @@ -54,14 +77,23 @@ export { default as PlusIcon } from "./Plus" export { default as PlusCircleIcon } from "./PlusCircle" export { default as PriceTagIcon } from "./PriceTag" export { default as RestaurantIcon } from "./Restaurant" +export { default as RoomServiceIcon } from "./RoomService" export { default as SaunaIcon } from "./Sauna" export { default as ScandicLogoIcon } from "./ScandicLogo" export { default as SearchIcon } from "./Search" export { default as ServiceIcon } from "./Service" export { default as ShoppingIcon } from "./Shopping" +export { default as SkateboardingIcon } from "./Skateboarding" +export { default as SmokingIcon } from "./Smoking" export { default as SnowflakeIcon } from "./Snowflake" +export { default as SpaIcon } from "./Spa" export { default as StarFilledIcon } from "./StarFilled" +export { default as StreetIcon } from "./Street" +export { default as SwimIcon } from "./Swim" +export { default as ThermostatIcon } from "./Thermostat" export { default as TrainIcon } from "./Train" +export { default as TshirtIcon } from "./Tshirt" export { default as TshirtWashIcon } from "./TshirtWash" +export { default as TvCastingIcon } from "./TvCasting" export { default as WarningTriangle } from "./WarningTriangle" export { default as WifiIcon } from "./Wifi" diff --git a/server/routers/hotels/output.ts b/server/routers/hotels/output.ts index 5692497f1..89e286185 100644 --- a/server/routers/hotels/output.ts +++ b/server/routers/hotels/output.ts @@ -6,6 +6,7 @@ import { imageMetaDataSchema, imageSizesSchema } from "./schemas/image" import { roomSchema } from "./schemas/room" import { getPoiGroupByCategoryName } from "./utils" +import { FacilityEnum } from "@/types/enums/facilities" import { PointOfInterestCategoryNameEnum } from "@/types/hotel" const ratingsSchema = z @@ -142,7 +143,7 @@ const hotelContentSchema = z.object({ }) const detailedFacilitySchema = z.object({ - id: z.number(), + id: z.nativeEnum(FacilityEnum), name: z.string(), public: z.boolean(), sortOrder: z.number(), @@ -402,7 +403,11 @@ export const getHotelDataSchema = z.object({ }), location: locationSchema, hotelContent: hotelContentSchema, - detailedFacilities: z.array(detailedFacilitySchema), + detailedFacilities: z + .array(detailedFacilitySchema) + .transform((facilities) => + facilities.sort((a, b) => b.sortOrder - a.sortOrder) + ), healthFacilities: z.array(healthFacilitySchema), merchantInformationData: merchantInformationSchema, rewardNight: rewardNightSchema, diff --git a/server/routers/hotels/query.ts b/server/routers/hotels/query.ts index 4b3f1a84e..2fa4cfbe8 100644 --- a/server/routers/hotels/query.ts +++ b/server/routers/hotels/query.ts @@ -46,7 +46,7 @@ import { TWENTYFOUR_HOURS, } from "./utils" -import { FacilityEnum } from "@/types/components/hotelPage/facilities" +import { FacilityCardTypeEnum } from "@/types/components/hotelPage/facilities" import { AvailabilityEnum } from "@/types/components/hotelReservation/selectHotel/selectHotel" import type { RequestOptionsWithOutBody } from "@/types/fetch" import type { Facility } from "@/types/hotel" @@ -231,15 +231,15 @@ export const hotelQueryRouter = router({ const facilities: Facility[] = [ { ...apiJson.data.attributes.restaurantImages, - id: FacilityEnum.restaurant, + id: FacilityCardTypeEnum.restaurant, }, { ...apiJson.data.attributes.conferencesAndMeetings, - id: FacilityEnum.conference, + id: FacilityCardTypeEnum.conference, }, { ...apiJson.data.attributes.healthAndWellness, - id: FacilityEnum.wellness, + id: FacilityCardTypeEnum.wellness, }, ] diff --git a/types/components/hotelPage/facilities.ts b/types/components/hotelPage/facilities.ts index 98a6a444d..333313fe0 100644 --- a/types/components/hotelPage/facilities.ts +++ b/types/components/hotelPage/facilities.ts @@ -34,7 +34,7 @@ export type CardGridProps = { facilitiesCardGrid: FacilityGrid } -export enum FacilityEnum { +export enum FacilityCardTypeEnum { wellness = "wellness-and-exercise", conference = "meetings-and-conferences", restaurant = "restaurant-and-bar", @@ -46,9 +46,3 @@ export enum RestaurantHeadings { restaurant = "Restaurant", breakfastRestaurant = "Breakfast restaurant", } - -export enum FacilityIds { - bar = 1606, - rooftopBar = 1014, - restaurant = 1383, -} diff --git a/types/components/icon.ts b/types/components/icon.ts index f50a79259..290c9ed32 100644 --- a/types/components/icon.ts +++ b/types/components/icon.ts @@ -7,8 +7,10 @@ export interface IconProps VariantProps {} export enum IconName { + Accesories = "Accesories", Accessibility = "Accessibility", AccountCircle = "AccountCircle", + Air = "Air", Airplane = "Airplane", ArrowRight = "ArrowRight", Bar = "Bar", @@ -18,7 +20,6 @@ export enum IconName { Camera = "Camera", Cellphone = "Cellphone", Check = "Check", - CrossCircle = "CrossCircle", CheckCircle = "CheckCircle", ChevronDown = "ChevronDown", ChevronLeft = "ChevronLeft", @@ -26,45 +27,76 @@ export enum IconName { ChevronRightSmall = "ChevronRightSmall", Close = "Close", CloseLarge = "CloseLarge", - Coffee = "Coffee", + CoffeeAlt = "CoffeeAlt", Concierge = "Concierge", + ConvenienceStore24h = "ConvenienceStore24h", + Cool = "Cool", + CrossCircle = "CrossCircle", Cultural = "Cultural", DoorOpen = "DoorOpen", + Dresser = "Dresser", ElectricBike = "ElectricBike", + ElectricCar = "ElectricCar", Email = "Email", EyeHide = "EyeHide", EyeShow = "EyeShow", Facebook = "Facebook", + Fan = "Fan", Fitness = "Fitness", + Footstool = "Footstool", Gallery = "Gallery", + Garage = "Garage", Gift = "Gift", Globe = "Globe", + Golf = "Golf", + Groceries = "Groceries", + Hanger = "Hanger", + HangerAlt = "HangerAlt", + Heat = "Heat", House = "House", Image = "Image", InfoCircle = "InfoCircle", Instagram = "Instagram", + Kayaking = "Kayaking", + Kettle = "Kettle", + Lamp = "Lamp", + LaundryMachine = "LaundryMachine", + LocalBar = "LocalBar", Location = "Location", Lock = "Lock", Map = "Map", Minus = "Minus", Museum = "Museum", + Nature = "Nature", + Nightlife = "Nightlife", + NoSmoking = "NoSmoking", + OutdoorFurniture = "OutdoorFurniture", Parking = "Parking", - Person = "Person", People2 = "People2", + Person = "Person", Pets = "Pets", Phone = "Phone", Plus = "Plus", PlusCircle = "PlusCircle", Restaurant = "Restaurant", + RoomService = "RoomService", Sauna = "Sauna", Search = "Search", Service = "Service", Shopping = "Shopping", + Skateboarding = "Skateboarding", + Smoking = "Smoking", Snowflake = "Snowflake", + Spa = "Spa", StarFilled = "StarFilled", + Street = "Street", + Swim = "Swim", + Thermostat = "Thermostat", Train = "Train", Tripadvisor = "Tripadvisor", + Tshirt = "Tshirt", TshirtWash = "TshirtWash", - Wifi = "Wifi", + TvCasting = "TvCasting", WarningTriangle = "WarningTriangle", + Wifi = "Wifi", } diff --git a/types/enums/facilities.ts b/types/enums/facilities.ts new file mode 100644 index 000000000..169aad6c0 --- /dev/null +++ b/types/enums/facilities.ts @@ -0,0 +1,262 @@ +export enum FacilityEnum { + AccessibleBathingControls = 2065, + AccessibleBathtubs = 2062, + AccessibleElevators = 2067, + AccessibleLightSwitch = 2066, + AccessibleRoomsAtHotel1 = 2659, + AccessibleRoomsAtHotel2 = 3010, + AccessibleToilets = 2068, + AccessibleWashBasins = 2063, + AdaptedRoomDoors = 2080, + AdjoiningConventionCentre = 1560, + AirConAirCooling = 2660, + AirConditioningInRoom = 5763, + AirportMaxDistance8Km = 1856, + AlarmsContinuouslyMonitored = 1876, + AlarmsHaveStrobeLightsForDeafHardHearingInAllGuestRooms = 1877, + AlarmsHaveStrobeLightsForDeafHardHearingInAllHallways = 1878, + AlarmsHaveStrobeLightsForDeafHardHearingInAllPublicAreas = 1879, + AllAudibleSmokeAlarmsHardwired = 1884, + AllExteriorDoorsRequireKeyAccessAtNightOrAutomaticallyLock = 2004, + AllGuestRoomDoorsHaveViewports = 2006, + AllGuestRoomDoorsSelfClosing = 2007, + AllParkingAreasPatrolled = 1933, + AllParkingAreasWellLit = 2013, + AllStairsWellsVentilated = 2053, + ArmchairBed = 104126, + AudibleAlarms = 1880, + AudibleSmokeAlarmsInAllHalls = 1881, + AudibleSmokeAlarmsInAllPublicAreas = 1882, + AudibleSmokeAlarmsInAllRooms = 1883, + AudioVisualEquipmentAvailable = 961, + AutolinkFireDepartment = 1886, + AutomatedExternalDefibrillatorOnSiteAED = 1917, + AutomaticFireDoors = 1887, + AutoRecallElevators = 1885, + BalconiesAccessibleToAdjoiningRooms = 1962, + Ballroom = 1609, + Banquet = 1557, + Bar = 1014, + BasicMedicalEquipmentOnSite = 1920, + BathroomsAdaptedForDisabledGuests = 2064, + Beach = 1827, + Beach0To1Km = 1019, + BeautySalon = 1015, + BedroomsWithWheelchairAccess = 2081, + BikesForLoan = 5550, + Bowling = 185105, + BrailleLargePrintHotelLiterature = 2069, + BrailleLargePrintMenus = 2070, + Breakfast = 5807, + Business1 = 1385, + Business2 = 83715, + BusinessCentre = 962, + Cafe = 1381, + CashFree8pmTill6am = 327877, + CashFreeHotel = 345180, + ChildrenWelcome = 1828, + City = 1857, + CoffeeInReceptionAtCharge = 332224, + CoffeeShop = 956, + CoffeeTeaFacilities = 5776, + ColourTVInRoomsAllScandicHotels = 5773, + ComplimentaryColdRefreshments = 157965, + CongressHall = 1558, + ConventionCentre = 963, + Couples = 2663, + DeadboltsOnConnectingDoors = 1990, + DeadboltsSecondaryLocksOnAllGuestRoomDoors = 1985, + Defibrillator = 99872, + Desk = 5777, + DirectDialPhoneInRoomsAllScandic = 5772, + DisabledEmergencyPlan1 = 1888, + DisabledEmergencyPlan2 = 2074, + DisabledParking = 2072, + DiscoNightClub = 958, + DJLiveMusic = 162587, + DO_NOT_USE_Restaurant = 1377, + Downtown = 969, + DrinkableTapWater = 5553, + DVDPlayer = 5778, + EBikesChargingStation = 265711, + ElectronicKeyCards = 1994, + Elevator = 959, + EmergencyBackUpGenerators = 1889, + EmergencyCallButtonOnPhone = 1998, + EmergencyCodesOrButtonsInRooms = 2075, + EmergencyEvacuationPlan1 = 1890, + EmergencyEvacuationPlan2 = 1895, + EmergencyEvaluationDrillFrequency = 1896, + EmergencyInfoInAllRooms = 1897, + EmergencyLightingAllScandic = 952, + EmergencyLightningInAllPublicAreas = 1898, + EmergencyServiceResponseTimeInMinutes = 1899, + Entertainment = 970, + EventVenue = 1559, + ExchangeFacility = 1605, + ExitMapsInRooms = 1900, + ExitSignsLit = 1901, + ExtraFamilyFriendly = 242920, + Families = 2664, + FaxFacilityInRoom = 5764, + Financial = 1409, + FireDetectorsAllScandic = 1869, + FireDetectorsInAllHalls = 1903, + FireDetectorsInAllPublicAreas = 1905, + FireDetectorsInAllRooms = 1906, + FireExtinguishersInAllPublicAreas = 1907, + FireExtinguishersInPublicAreasAllScandic = 1870, + FireSafetyAllScandic = 1871, + FirstAidAvailable = 1915, + FoodDrinks247 = 324100, + FreeWiFi = 1833, + GiftShop = 1376, + Golf = 1016, + GolfCourse0To30Km = 1607, + GuestRoomDoorsHaveASecondLock = 2005, + Gym = 1829, + GymTrainingFacilities = 2669, + Hairdresser = 348860, + HairdryerInRoomAllScandic = 5765, + HandicapFacilities = 2076, + HandrailsInBathrooms = 2078, + HearingInductionLoops = 2077, + Highway1 = 1858, + Highway2 = 1864, + Hiking0To3Km = 239346, + HotelCompliesWithAAASecurityStandards = 2011, + HotelIsFollowingScandicsSafetySecurityPolicy = 5559, + HotelWorksAccordingToScandicsAccessibilityConcepts = 5560, + IceMachine = 1405, + IceMachineReception = 332194, + IDRequiredToReplaceAGuestRoomKey = 2024, + IfNoWhatAreTheHoursUse24ClockEx0000To0600 = 1912, + InCountry = 1867, + IndustrialPark = 1859, + InternetHighSpeedInternetConnectionAllScandic = 5804, + InternetHotSpotsAllScandic = 1832, + IroningRoom = 238849, + IronIroningBoardAllScandic = 5780, + Jacuzzi = 162573, + JacuzziInRoom = 5766, + KayaksForLoan = 162585, + KeyAccessOnlySecuredFloorsAvailable = 2050, + KeyAccessOnlyToHealthClubGym = 2061, + KidsPlayRoom = 239349, + KidsUpToAndIncluding12YearsStayForFree = 5561, + KitchenInRoom = 5767, + Lake0To1Km = 1865, + LakeOrSea0To1Km = 245437, + LaptopSafe = 5283, + LaundryRoom = 326031, + LaundryService = 1834, + LaundryServiceExpress = 162583, + Leisure = 83716, + LifestyleConcierge = 162584, + LuggageLockers = 324098, + Massage = 348859, + MeetingConferenceFacilities = 5806, + MeetingRooms = 1017, + MinibarInRoom = 5768, + MobileLift = 113185, + Mountains0To1Km = 1866, + MovieChannelsInRoomAllScandic = 5770, + MultipleExitsOnEachFloor = 2012, + NonSmokingRoomsAllScandic = 5771, + OnSiteTrainingFacilities = 3014, + OtherExplainInBriefDescription = 1608, + OutdoorTerrace = 1382, + OvernightSecurity = 1913, + ParkingAdditionalCost = 1406, + ParkingAttendant = 1914, + ParkingElectricCharging = 5554, + ParkingFreeParking = 5562, + ParkingGarage = 2665, + ParkingOutdoor = 162574, + PCHookUpInRoom = 5769, + PetFriendlyRooms = 1835, + PillowAlarmsAvailable = 2079, + PlayStationInPlayArea = 175449, + Pool = 1831, + PoolSwimmingPoolJacuzziAtHotel = 2667, + PrintingService = 1380, + PropertyMeetsRequirementsFireSafety = 1875, + PublicAddressSystem = 2014, + RelaxationSuite = 5564, + Restaurant = 1383, + RestrictedRoomAccessAllScandic = 1872, + RooftopBar = 239348, + RoomsAccessibleFromTheInterior = 950, + RoomService = 1378, + RoomWindowsOpen = 2016, + RoomWindowsThatOpenHaveLockingDevice = 2020, + Rural1 = 1861, + Rural2 = 1868, + SafeDepositBoxInRoomsAllScandic = 5775, + SafeDepositBoxInRoomsCanHoldA17InchLaptop = 200124, + SafeDepositBoxInRoomsCannotHoldALaptop = 200123, + SafetyChainsOnGuestRoomDoor = 2047, + Sauna = 1379, + ScandicShop24Hrs = 1408, + SecondaryLocksOnSlidingGlassDoors = 2048, + SecondaryLocksOnWindows = 2049, + Security24Hours = 1911, + SecurityEscortsAvailableOnRequest = 1936, + SecurityPersonnelOnSite = 1934, + SeparateFloorsForWomen = 2051, + ServesBreakfastAlwaysIncluded = 1407, + ServesBreakfastNotAlwaysIncluded = 5556, + ServesOrganicBreakfastAlwaysIncluded = 5557, + ServesOrganicBreakfastNotAlwaysIncluded = 5558, + ServiceGuideDogsAllowed = 2071, + ServiceSecurity24Hrs = 326033, + Shopping = 971, + SkateboardsForLoan = 162586, + Skiing0To1Km = 245031, + Skybar = 1606, + SmokeDetectorsAllScandic = 1873, + Solarium = 1830, + SpecialNeedsMenus = 2082, + Sports = 83717, + SprinklersAllScandic = 1874, + SprinklersInAllHalls = 1908, + SprinklersInAllPublicAreas = 1909, + SprinklersInAllRooms = 1910, + StaffInDuplicateKeys = 1942, + StaffRedCrossCertifiedInCPR = 1941, + StaffTrainedForDisabledGuests = 1928, + StaffTrainedInAutomatedExternalDefibrillatorUsageAED = 1940, + StaffTrainedInCPR = 1923, + StaffTrainedInFirstAid = 1939, + StaffTrainedInFirstAidTechniques = 951, + StaffTrainedToCaterForDisabledGuestsAllScandic = 2073, + Suburbs = 1860, + SwingboltLock = 2052, + TeleConferencingFacilitiesAvailable = 1018, + TelevisionsWithSubtitlesOrClosedCaptions = 2083, + Tennis1 = 1836, + Tennis2 = 1838, + TennisPadel = 239350, + Theatre = 1862, + TrouserPress = 5779, + TVWithChromecast1 = 229127, + TVWithChromecast2 = 229144, + UniformSecurityOnPremises = 1935, + UtilityRoomForIroning = 324097, + VendingMachineWithNecessities = 324099, + VideoSurveillanceInHallways = 2056, + VideoSurveillanceInPublicAreas = 1386, + VideoSurveillanceMonitored24HrsADay = 2058, + VideoSurveillanceOfAllParkingAreas = 2055, + VideoSurveillanceOfExteriorFrontEntrance = 2054, + VideoSurveillanceRecorded24HrsADayParkingArea = 2059, + WallMountedCycleRack = 199642, + WellLitWalkways = 2060, + WellnessAndSaunaEntranceFeeAdmission16PlusYears = 267806, + WellnessPoolSaunaEntranceFeeAdmission16PlusYears = 307754, + WheelchairAccess = 2084, + WideCorridors = 2086, + WideEntrance = 2085, + WideRestaurantEntrance = 2087, + WiFiWirelessInternetAccessAllScandic = 5774, +} diff --git a/utils/facilityCards.ts b/utils/facilityCards.ts index dd8c0a1a4..2ff48b946 100644 --- a/utils/facilityCards.ts +++ b/utils/facilityCards.ts @@ -10,12 +10,12 @@ import { type Facilities, type FacilityCard, type FacilityCardType, - FacilityEnum, + FacilityCardTypeEnum, type FacilityGrid, - FacilityIds, type FacilityImage, RestaurantHeadings, } from "@/types/components/hotelPage/facilities" +import { FacilityEnum } from "@/types/enums/facilities" import type { Amenities, Facility } from "@/types/hotel" import type { CardProps } from "@/components/TempDesignSystem/Card/card" @@ -72,7 +72,7 @@ export function setFacilityCardGrids(facilities: Facility[]): Facilities { }) switch (facility.id) { - case FacilityEnum.wellness: + case FacilityCardTypeEnum.wellness: card = setCardProps( "one", "Sauna and gym", @@ -83,7 +83,7 @@ export function setFacilityCardGrids(facilities: Facility[]): Facilities { grid.unshift(card) break - case FacilityEnum.conference: + case FacilityCardTypeEnum.conference: card = setCardProps( "primaryDim", "Events that make an impression", @@ -94,7 +94,7 @@ export function setFacilityCardGrids(facilities: Facility[]): Facilities { grid.push(card) break - case FacilityEnum.restaurant: + case FacilityCardTypeEnum.restaurant: //const title = getRestaurantHeading(amenities) // TODO will be used later card = setCardProps( "primaryDark", @@ -114,10 +114,12 @@ export function setFacilityCardGrids(facilities: Facility[]): Facilities { export function getRestaurantHeading(amenities: Amenities): RestaurantHeadings { const hasBar = amenities.some( (facility) => - facility.id === FacilityIds.bar || facility.id === FacilityIds.rooftopBar + facility.id === FacilityEnum.Bar || + facility.id === FacilityEnum.RooftopBar || + facility.id === FacilityEnum.Skybar ) const hasRestaurant = amenities.some( - (facility) => facility.id === FacilityIds.restaurant + (facility) => facility.id === FacilityEnum.Restaurant ) if (hasBar && hasRestaurant) { From 2d23f9bbf3083dbff281a60125ccb3eb3a9f9406 Mon Sep 17 00:00:00 2001 From: Simon Emanuelsson Date: Tue, 22 Oct 2024 11:43:08 +0200 Subject: [PATCH 2/5] feat: booking confirmation page with hardcoded data --- .../booking-confirmation/loading.tsx | 0 .../booking-confirmation/page.module.css | 157 ++++++++++ .../booking-confirmation/page.tsx | 279 ++++++++++++++++++ .../(confirmation)/layout.module.css | 5 + .../(confirmation)/layout.tsx | 15 + .../{ => (standard)}/[step]/layout.module.css | 0 .../{ => (standard)}/[step]/layout.tsx | 0 .../{ => (standard)}/[step]/page.tsx | 0 .../{ => (standard)}/layout.module.css | 0 .../{ => (standard)}/layout.tsx | 0 .../{ => (standard)}/page.tsx | 0 .../select-hotel/map/page.module.css | 0 .../select-hotel/map/page.tsx | 2 +- .../select-hotel/page.module.css | 0 .../{ => (standard)}/select-hotel/page.tsx | 2 +- .../{ => (standard)}/select-hotel/utils.ts | 0 .../select-rate/page.module.css | 0 .../{ => (standard)}/select-rate/page.tsx | 0 .../booking-confirmation/page.module.css | 16 - .../booking-confirmation/page.tsx | 67 ----- .../hotelreservation/[...paths]/page.tsx | 1 + .../booking-confirmation/page.tsx | 3 + .../@bookingwidget/hotelreservation/page.tsx | 1 + .../IntroSection/index.tsx | 58 ---- .../IntroSection/introSection.module.css | 26 -- .../BookingConfirmation/StaySection/index.tsx | 81 ----- .../StaySection/staySection.module.css | 78 ----- .../SummarySection/index.tsx | 40 --- .../SummarySection/summarySection.module.css | 13 - .../tempConfirmationData.ts | 27 -- .../EnterDetails/Payment/index.tsx | 2 +- components/Icons/Download.tsx | 40 +++ components/Icons/Printer.tsx | 36 +++ components/Icons/index.tsx | 2 + constants/booking.ts | 7 + i18n/dictionaries/en.json | 93 +++--- i18n/index.ts | 2 +- lib/dt.ts | 2 + server/routers/booking/input.ts | 6 +- server/routers/booking/mutation.ts | 163 +++++----- server/routers/booking/output.ts | 47 ++- server/routers/booking/query.ts | 121 +++++++- 42 files changed, 859 insertions(+), 533 deletions(-) rename app/[lang]/(live)/(public)/hotelreservation/{ => (confirmation)}/booking-confirmation/loading.tsx (100%) create mode 100644 app/[lang]/(live)/(public)/hotelreservation/(confirmation)/booking-confirmation/page.module.css create mode 100644 app/[lang]/(live)/(public)/hotelreservation/(confirmation)/booking-confirmation/page.tsx create mode 100644 app/[lang]/(live)/(public)/hotelreservation/(confirmation)/layout.module.css create mode 100644 app/[lang]/(live)/(public)/hotelreservation/(confirmation)/layout.tsx rename app/[lang]/(live)/(public)/hotelreservation/{ => (standard)}/[step]/layout.module.css (100%) rename app/[lang]/(live)/(public)/hotelreservation/{ => (standard)}/[step]/layout.tsx (100%) rename app/[lang]/(live)/(public)/hotelreservation/{ => (standard)}/[step]/page.tsx (100%) rename app/[lang]/(live)/(public)/hotelreservation/{ => (standard)}/layout.module.css (100%) rename app/[lang]/(live)/(public)/hotelreservation/{ => (standard)}/layout.tsx (100%) rename app/[lang]/(live)/(public)/hotelreservation/{ => (standard)}/page.tsx (100%) rename app/[lang]/(live)/(public)/hotelreservation/{ => (standard)}/select-hotel/map/page.module.css (100%) rename app/[lang]/(live)/(public)/hotelreservation/{ => (standard)}/select-hotel/map/page.tsx (95%) rename app/[lang]/(live)/(public)/hotelreservation/{ => (standard)}/select-hotel/page.module.css (100%) rename app/[lang]/(live)/(public)/hotelreservation/{ => (standard)}/select-hotel/page.tsx (95%) rename app/[lang]/(live)/(public)/hotelreservation/{ => (standard)}/select-hotel/utils.ts (100%) rename app/[lang]/(live)/(public)/hotelreservation/{ => (standard)}/select-rate/page.module.css (100%) rename app/[lang]/(live)/(public)/hotelreservation/{ => (standard)}/select-rate/page.tsx (100%) delete mode 100644 app/[lang]/(live)/(public)/hotelreservation/booking-confirmation/page.module.css delete mode 100644 app/[lang]/(live)/(public)/hotelreservation/booking-confirmation/page.tsx create mode 100644 app/[lang]/(live)/@bookingwidget/hotelreservation/[...paths]/page.tsx create mode 100644 app/[lang]/(live)/@bookingwidget/hotelreservation/booking-confirmation/page.tsx create mode 100644 app/[lang]/(live)/@bookingwidget/hotelreservation/page.tsx delete mode 100644 components/HotelReservation/BookingConfirmation/IntroSection/index.tsx delete mode 100644 components/HotelReservation/BookingConfirmation/IntroSection/introSection.module.css delete mode 100644 components/HotelReservation/BookingConfirmation/StaySection/index.tsx delete mode 100644 components/HotelReservation/BookingConfirmation/StaySection/staySection.module.css delete mode 100644 components/HotelReservation/BookingConfirmation/SummarySection/index.tsx delete mode 100644 components/HotelReservation/BookingConfirmation/SummarySection/summarySection.module.css delete mode 100644 components/HotelReservation/BookingConfirmation/tempConfirmationData.ts create mode 100644 components/Icons/Download.tsx create mode 100644 components/Icons/Printer.tsx diff --git a/app/[lang]/(live)/(public)/hotelreservation/booking-confirmation/loading.tsx b/app/[lang]/(live)/(public)/hotelreservation/(confirmation)/booking-confirmation/loading.tsx similarity index 100% rename from app/[lang]/(live)/(public)/hotelreservation/booking-confirmation/loading.tsx rename to app/[lang]/(live)/(public)/hotelreservation/(confirmation)/booking-confirmation/loading.tsx diff --git a/app/[lang]/(live)/(public)/hotelreservation/(confirmation)/booking-confirmation/page.module.css b/app/[lang]/(live)/(public)/hotelreservation/(confirmation)/booking-confirmation/page.module.css new file mode 100644 index 000000000..06b39ef41 --- /dev/null +++ b/app/[lang]/(live)/(public)/hotelreservation/(confirmation)/booking-confirmation/page.module.css @@ -0,0 +1,157 @@ +.details, +.guest, +.header, +.hgroup, +.hotel, +.list, +.main, +.section, +.receipt, +.total { + display: flex; + flex-direction: column; +} + +.main { + gap: var(--Spacing-x5); + margin: 0 auto; + width: min(calc(100dvw - (var(--Spacing-x3) * 2)), 708px); +} + +.header, +.hgroup { + align-items: center; +} + +.header { + gap: var(--Spacing-x3); +} + +.hgroup { + gap: var(--Spacing-x-half); +} + +.body { + max-width: 560px; +} + +.section { + display: flex; + flex-direction: column; + gap: var(--Spacing-x9); +} + +.booking { + display: grid; + gap: var(--Spacing-x-one-and-half); + grid-template-areas: + "image" + "details" + "actions"; +} + +.actions, +.details { + background-color: var(--Base-Surface-Subtle-Normal); + border-radius: var(--Corner-radius-Medium); +} + +.details { + gap: var(--Spacing-x3); + grid-area: details; + padding: var(--Spacing-x2); +} + +.tempImage { + align-items: center; + background-color: lightgrey; + border-radius: var(--Corner-radius-Medium); + display: flex; + grid-area: image; + justify-content: center; +} + +.actions { + display: grid; + grid-area: actions; + padding: var(--Spacing-x1) var(--Spacing-x2); +} + +.list { + gap: var(--Spacing-x-one-and-half); + list-style: none; + margin: 0; + padding: 0; +} + +.listItem { + align-items: center; + display: flex; + gap: var(--Spacing-x1); + justify-content: space-between; +} + +.summary { + display: grid; + gap: var(--Spacing-x3); +} + +.guest, +.hotel { + gap: var(--Spacing-x-half); +} + +.receipt, +.total { + gap: var(--Spacing-x2); +} + +.divider { + grid-column: 1 / -1; +} + +@media screen and (max-width: 767px) { + .actions { + & > button[class*="btn"][class*="icon"][class*="small"] { + border-bottom: 1px solid var(--Base-Border-Subtle); + border-radius: 0; + justify-content: space-between; + + &:last-of-type { + border-bottom: none; + } + + & > svg { + order: 2; + } + } + } + + .tempImage { + min-height: 250px; + } +} + +@media screen and (min-width: 768px) { + .booking { + grid-template-areas: + "details image" + "actions actions"; + grid-template-columns: 1fr minmax(256px, min(256px, 100%)); + } + + .actions { + gap: var(--Spacing-x7); + grid-template-columns: repeat(auto-fit, minmax(50px, auto)); + justify-content: center; + padding: var(--Spacing-x1) var(--Spacing-x3); + } + + .details { + padding: var(--Spacing-x3) var(--Spacing-x3) var(--Spacing-x2); + } + + .summary { + grid-template-columns: 1fr 1fr; + } +} diff --git a/app/[lang]/(live)/(public)/hotelreservation/(confirmation)/booking-confirmation/page.tsx b/app/[lang]/(live)/(public)/hotelreservation/(confirmation)/booking-confirmation/page.tsx new file mode 100644 index 000000000..23b5270d4 --- /dev/null +++ b/app/[lang]/(live)/(public)/hotelreservation/(confirmation)/booking-confirmation/page.tsx @@ -0,0 +1,279 @@ +import { dt } from "@/lib/dt" +import { serverClient } from "@/lib/trpc/server" + +import { + CalendarIcon, + DownloadIcon, + ImageIcon, + PrinterIcon, +} from "@/components/Icons" +import Button from "@/components/TempDesignSystem/Button" +import Divider from "@/components/TempDesignSystem/Divider" +import Link from "@/components/TempDesignSystem/Link" +import Body from "@/components/TempDesignSystem/Text/Body" +import Caption from "@/components/TempDesignSystem/Text/Caption" +import Subtitle from "@/components/TempDesignSystem/Text/Subtitle" +import Title from "@/components/TempDesignSystem/Text/Title" +import { getIntl } from "@/i18n" + +import styles from "./page.module.css" + +import type { LangParams, PageArgs } from "@/types/params" + +export default async function BookingConfirmationPage({ + params, +}: PageArgs) { + const booking = await serverClient().booking.confirmation({ + confirmationNumber: "991697377", + }) + + if (!booking) { + return null + } + + const intl = await getIntl() + const text = intl.formatMessage( + { id: "booking.confirmation.text" }, + { + emailLink: (str) => ( + + {str} + + ), + } + ) + + const fromDate = dt(booking.temp.fromDate).locale(params.lang) + const toDate = dt(booking.temp.toDate).locale(params.lang) + const nights = intl.formatMessage( + { id: "booking.nights" }, + { + totalNights: dt(toDate.format("YYYY-MM-DD")).diff( + dt(fromDate.format("YYYY-MM-DD")), + "days" + ), + } + ) + + return ( +
+
+
+ + {intl.formatMessage({ id: "booking.confirmation.title" })} + + + {booking.hotel.name} + +
+ + {text} + +
+
+
+
+
+ + {intl.formatMessage( + { id: "Reference #{bookingNr}" }, + { bookingNr: "A92320VV" } + )} + +
+
    +
  • + {intl.formatMessage({ id: "Check-in" })} + + {`${fromDate.format("ddd, D MMM")} ${intl.formatMessage({ id: "from" })} ${fromDate.format("HH:mm")}`} + +
  • +
  • + {intl.formatMessage({ id: "Check-out" })} + + {`${toDate.format("ddd, D MMM")} ${intl.formatMessage({ id: "from" })} ${toDate.format("HH:mm")}`} + +
  • +
  • + {intl.formatMessage({ id: "Breakfast" })} + + {booking.temp.breakfastFrom} - {booking.temp.breakfastTo} + +
  • +
  • + {intl.formatMessage({ id: "Cancellation policy" })} + + {intl.formatMessage({ id: booking.temp.cancelPolicy })} + +
  • +
  • + {intl.formatMessage({ id: "Rebooking" })} + {`${intl.formatMessage({ id: "Free until" })} ${fromDate.subtract(3, "day").format("ddd, D MMM")}`} +
  • +
+
+ +
+ + + +
+
+
+
+ + {intl.formatMessage({ id: "Guest" })} + +
+ + {`${booking.guest.firstName} ${booking.guest.lastName}${booking.guest.memberbershipNumber ? ` (${intl.formatMessage({ id: "member no" })} ${booking.guest.memberbershipNumber})` : ""}`} + + {booking.guest.email} + + {booking.guest.phoneNumber} + +
+
+
+ + {intl.formatMessage({ id: "Your hotel" })} + +
+ + {booking.hotel.name} + + {booking.hotel.email} + + {booking.hotel.phoneNumber} + +
+
+ +
+
+ + {`${booking.temp.room.type}, ${nights}`} + + {booking.temp.room.price} +
+ {booking.temp.packages.map((pkg) => ( +
+ + {pkg.name} + + {pkg.price} +
+ ))} +
+
+
+ + {intl.formatMessage({ id: "VAT" })} + + {booking.temp.room.vat} +
+
+ + {intl.formatMessage({ id: "Total cost" })} + + {booking.temp.total} + + {`${intl.formatMessage({ id: "Approx." })} ${booking.temp.totalInEuro}`} + +
+
+ +
+ + {`${intl.formatMessage({ id: "Payment received" })} ${dt(booking.temp.payment).locale(params.lang).format("D MMM YYYY, h:mm z")}`} + + + {intl.formatMessage( + { id: "{card} ending with {cardno}" }, + { + card: "Mastercard", + cardno: "2202", + } + )} + +
+
+
+
+ ) +} + +// const { email, hotel, stay, summary } = tempConfirmationData + +// const confirmationNumber = useMemo(() => { +// if (typeof window === "undefined") return "" + +// const storedConfirmationNumber = sessionStorage.getItem( +// BOOKING_CONFIRMATION_NUMBER +// ) +// TODO: cleanup stored values +// sessionStorage.removeItem(BOOKING_CONFIRMATION_NUMBER) +// return storedConfirmationNumber +// }, []) + +// const bookingStatus = useHandleBookingStatus( +// confirmationNumber, +// BookingStatusEnum.BookingCompleted, +// maxRetries, +// retryInterval +// ) + +// if ( +// confirmationNumber === null || +// bookingStatus.isError || +// (bookingStatus.isFetched && !bookingStatus.data) +// ) { +// // TODO: handle error +// throw new Error("Error fetching booking status") +// } + +// if ( +// bookingStatus.data?.reservationStatus === BookingStatusEnum.BookingCompleted +// ) { +// return diff --git a/app/[lang]/(live)/(public)/hotelreservation/(confirmation)/layout.module.css b/app/[lang]/(live)/(public)/hotelreservation/(confirmation)/layout.module.css new file mode 100644 index 000000000..3f89e6f51 --- /dev/null +++ b/app/[lang]/(live)/(public)/hotelreservation/(confirmation)/layout.module.css @@ -0,0 +1,5 @@ +.layout { + background-color: var(--Base-Surface-Primary-light-Normal); + min-height: 100dvh; + padding: 80px 0 160px; +} diff --git a/app/[lang]/(live)/(public)/hotelreservation/(confirmation)/layout.tsx b/app/[lang]/(live)/(public)/hotelreservation/(confirmation)/layout.tsx new file mode 100644 index 000000000..971c66e0d --- /dev/null +++ b/app/[lang]/(live)/(public)/hotelreservation/(confirmation)/layout.tsx @@ -0,0 +1,15 @@ +import { notFound } from "next/navigation" + +import { env } from "@/env/server" + +import styles from "./layout.module.css" + +// route groups needed as layouts have different bgc +export default function ConfirmedBookingLayout({ + children, +}: React.PropsWithChildren) { + if (env.HIDE_FOR_NEXT_RELEASE) { + return notFound() + } + return
{children}
+} diff --git a/app/[lang]/(live)/(public)/hotelreservation/[step]/layout.module.css b/app/[lang]/(live)/(public)/hotelreservation/(standard)/[step]/layout.module.css similarity index 100% rename from app/[lang]/(live)/(public)/hotelreservation/[step]/layout.module.css rename to app/[lang]/(live)/(public)/hotelreservation/(standard)/[step]/layout.module.css diff --git a/app/[lang]/(live)/(public)/hotelreservation/[step]/layout.tsx b/app/[lang]/(live)/(public)/hotelreservation/(standard)/[step]/layout.tsx similarity index 100% rename from app/[lang]/(live)/(public)/hotelreservation/[step]/layout.tsx rename to app/[lang]/(live)/(public)/hotelreservation/(standard)/[step]/layout.tsx diff --git a/app/[lang]/(live)/(public)/hotelreservation/[step]/page.tsx b/app/[lang]/(live)/(public)/hotelreservation/(standard)/[step]/page.tsx similarity index 100% rename from app/[lang]/(live)/(public)/hotelreservation/[step]/page.tsx rename to app/[lang]/(live)/(public)/hotelreservation/(standard)/[step]/page.tsx diff --git a/app/[lang]/(live)/(public)/hotelreservation/layout.module.css b/app/[lang]/(live)/(public)/hotelreservation/(standard)/layout.module.css similarity index 100% rename from app/[lang]/(live)/(public)/hotelreservation/layout.module.css rename to app/[lang]/(live)/(public)/hotelreservation/(standard)/layout.module.css diff --git a/app/[lang]/(live)/(public)/hotelreservation/layout.tsx b/app/[lang]/(live)/(public)/hotelreservation/(standard)/layout.tsx similarity index 100% rename from app/[lang]/(live)/(public)/hotelreservation/layout.tsx rename to app/[lang]/(live)/(public)/hotelreservation/(standard)/layout.tsx diff --git a/app/[lang]/(live)/(public)/hotelreservation/page.tsx b/app/[lang]/(live)/(public)/hotelreservation/(standard)/page.tsx similarity index 100% rename from app/[lang]/(live)/(public)/hotelreservation/page.tsx rename to app/[lang]/(live)/(public)/hotelreservation/(standard)/page.tsx diff --git a/app/[lang]/(live)/(public)/hotelreservation/select-hotel/map/page.module.css b/app/[lang]/(live)/(public)/hotelreservation/(standard)/select-hotel/map/page.module.css similarity index 100% rename from app/[lang]/(live)/(public)/hotelreservation/select-hotel/map/page.module.css rename to app/[lang]/(live)/(public)/hotelreservation/(standard)/select-hotel/map/page.module.css diff --git a/app/[lang]/(live)/(public)/hotelreservation/select-hotel/map/page.tsx b/app/[lang]/(live)/(public)/hotelreservation/(standard)/select-hotel/map/page.tsx similarity index 95% rename from app/[lang]/(live)/(public)/hotelreservation/select-hotel/map/page.tsx rename to app/[lang]/(live)/(public)/hotelreservation/(standard)/select-hotel/map/page.tsx index 89f4e62ca..8dca8f4c7 100644 --- a/app/[lang]/(live)/(public)/hotelreservation/select-hotel/map/page.tsx +++ b/app/[lang]/(live)/(public)/hotelreservation/(standard)/select-hotel/map/page.tsx @@ -3,7 +3,7 @@ import { env } from "@/env/server" import { fetchAvailableHotels, getFiltersFromHotels, -} from "@/app/[lang]/(live)/(public)/hotelreservation/select-hotel/utils" +} from "@/app/[lang]/(live)/(public)/hotelreservation/(standard)/select-hotel/utils" import SelectHotelMap from "@/components/HotelReservation/SelectHotel/SelectHotelMap" import { setLang } from "@/i18n/serverContext" diff --git a/app/[lang]/(live)/(public)/hotelreservation/select-hotel/page.module.css b/app/[lang]/(live)/(public)/hotelreservation/(standard)/select-hotel/page.module.css similarity index 100% rename from app/[lang]/(live)/(public)/hotelreservation/select-hotel/page.module.css rename to app/[lang]/(live)/(public)/hotelreservation/(standard)/select-hotel/page.module.css diff --git a/app/[lang]/(live)/(public)/hotelreservation/select-hotel/page.tsx b/app/[lang]/(live)/(public)/hotelreservation/(standard)/select-hotel/page.tsx similarity index 95% rename from app/[lang]/(live)/(public)/hotelreservation/select-hotel/page.tsx rename to app/[lang]/(live)/(public)/hotelreservation/(standard)/select-hotel/page.tsx index b6548122c..7dcec9130 100644 --- a/app/[lang]/(live)/(public)/hotelreservation/select-hotel/page.tsx +++ b/app/[lang]/(live)/(public)/hotelreservation/(standard)/select-hotel/page.tsx @@ -3,7 +3,7 @@ import { selectHotelMap } from "@/constants/routes/hotelReservation" import { fetchAvailableHotels, getFiltersFromHotels, -} from "@/app/[lang]/(live)/(public)/hotelreservation/select-hotel/utils" +} from "@/app/[lang]/(live)/(public)/hotelreservation/(standard)/select-hotel/utils" import HotelCardListing from "@/components/HotelReservation/HotelCardListing" import HotelFilter from "@/components/HotelReservation/SelectHotel/HotelFilter" import { ChevronRightIcon } from "@/components/Icons" diff --git a/app/[lang]/(live)/(public)/hotelreservation/select-hotel/utils.ts b/app/[lang]/(live)/(public)/hotelreservation/(standard)/select-hotel/utils.ts similarity index 100% rename from app/[lang]/(live)/(public)/hotelreservation/select-hotel/utils.ts rename to app/[lang]/(live)/(public)/hotelreservation/(standard)/select-hotel/utils.ts diff --git a/app/[lang]/(live)/(public)/hotelreservation/select-rate/page.module.css b/app/[lang]/(live)/(public)/hotelreservation/(standard)/select-rate/page.module.css similarity index 100% rename from app/[lang]/(live)/(public)/hotelreservation/select-rate/page.module.css rename to app/[lang]/(live)/(public)/hotelreservation/(standard)/select-rate/page.module.css diff --git a/app/[lang]/(live)/(public)/hotelreservation/select-rate/page.tsx b/app/[lang]/(live)/(public)/hotelreservation/(standard)/select-rate/page.tsx similarity index 100% rename from app/[lang]/(live)/(public)/hotelreservation/select-rate/page.tsx rename to app/[lang]/(live)/(public)/hotelreservation/(standard)/select-rate/page.tsx diff --git a/app/[lang]/(live)/(public)/hotelreservation/booking-confirmation/page.module.css b/app/[lang]/(live)/(public)/hotelreservation/booking-confirmation/page.module.css deleted file mode 100644 index 2771aef75..000000000 --- a/app/[lang]/(live)/(public)/hotelreservation/booking-confirmation/page.module.css +++ /dev/null @@ -1,16 +0,0 @@ -.main { - display: flex; - justify-content: center; - padding: var(--Spacing-x4); - background-color: var(--Scandic-Brand-Warm-White); - min-height: 100dvh; - max-width: var(--max-width); - margin: 0 auto; -} - -.section { - display: flex; - flex-direction: column; - gap: var(--Spacing-x4); - width: 100%; -} diff --git a/app/[lang]/(live)/(public)/hotelreservation/booking-confirmation/page.tsx b/app/[lang]/(live)/(public)/hotelreservation/booking-confirmation/page.tsx deleted file mode 100644 index 0a22c5bc0..000000000 --- a/app/[lang]/(live)/(public)/hotelreservation/booking-confirmation/page.tsx +++ /dev/null @@ -1,67 +0,0 @@ -"use client" - -import { useMemo } from "react" - -import { - BOOKING_CONFIRMATION_NUMBER, - BookingStatusEnum, -} from "@/constants/booking" - -import IntroSection from "@/components/HotelReservation/BookingConfirmation/IntroSection" -import StaySection from "@/components/HotelReservation/BookingConfirmation/StaySection" -import SummarySection from "@/components/HotelReservation/BookingConfirmation/SummarySection" -import { tempConfirmationData } from "@/components/HotelReservation/BookingConfirmation/tempConfirmationData" -import LoadingSpinner from "@/components/LoadingSpinner" -import { useHandleBookingStatus } from "@/hooks/booking/useHandleBookingStatus" - -import styles from "./page.module.css" - -const maxRetries = 10 -const retryInterval = 2000 - -export default function BookingConfirmationPage() { - const { email, hotel, stay, summary } = tempConfirmationData - - const confirmationNumber = useMemo(() => { - if (typeof window === "undefined") return "" - - const storedConfirmationNumber = sessionStorage.getItem( - BOOKING_CONFIRMATION_NUMBER - ) - // TODO: cleanup stored values - // sessionStorage.removeItem(BOOKING_CONFIRMATION_NUMBER) - return storedConfirmationNumber - }, []) - - const bookingStatus = useHandleBookingStatus( - confirmationNumber, - BookingStatusEnum.BookingCompleted, - maxRetries, - retryInterval - ) - - if ( - confirmationNumber === null || - bookingStatus.isError || - (bookingStatus.isFetched && !bookingStatus.data) - ) { - // TODO: handle error - throw new Error("Error fetching booking status") - } - - if ( - bookingStatus.data?.reservationStatus === BookingStatusEnum.BookingCompleted - ) { - return ( -
-
- - - -
-
- ) - } - - return -} diff --git a/app/[lang]/(live)/@bookingwidget/hotelreservation/[...paths]/page.tsx b/app/[lang]/(live)/@bookingwidget/hotelreservation/[...paths]/page.tsx new file mode 100644 index 000000000..2ebaca014 --- /dev/null +++ b/app/[lang]/(live)/@bookingwidget/hotelreservation/[...paths]/page.tsx @@ -0,0 +1 @@ +export { default } from "../../page" diff --git a/app/[lang]/(live)/@bookingwidget/hotelreservation/booking-confirmation/page.tsx b/app/[lang]/(live)/@bookingwidget/hotelreservation/booking-confirmation/page.tsx new file mode 100644 index 000000000..e0ff199ee --- /dev/null +++ b/app/[lang]/(live)/@bookingwidget/hotelreservation/booking-confirmation/page.tsx @@ -0,0 +1,3 @@ +export default function ConfirmedBookingSlot() { + return null +} diff --git a/app/[lang]/(live)/@bookingwidget/hotelreservation/page.tsx b/app/[lang]/(live)/@bookingwidget/hotelreservation/page.tsx new file mode 100644 index 000000000..03a82e5f5 --- /dev/null +++ b/app/[lang]/(live)/@bookingwidget/hotelreservation/page.tsx @@ -0,0 +1 @@ +export { default } from "../page" diff --git a/components/HotelReservation/BookingConfirmation/IntroSection/index.tsx b/components/HotelReservation/BookingConfirmation/IntroSection/index.tsx deleted file mode 100644 index 448dc82e1..000000000 --- a/components/HotelReservation/BookingConfirmation/IntroSection/index.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import { useIntl } from "react-intl" - -import Button from "@/components/TempDesignSystem/Button" -import Link from "@/components/TempDesignSystem/Link" -import Body from "@/components/TempDesignSystem/Text/Body" -import Subtitle from "@/components/TempDesignSystem/Text/Subtitle" -import Title from "@/components/TempDesignSystem/Text/Title" - -import styles from "./introSection.module.css" - -import { IntroSectionProps } from "@/types/components/hotelReservation/bookingConfirmation/bookingConfirmation" - -export default function IntroSection({ email }: IntroSectionProps) { - const intl = useIntl() - - return ( -
-
- - {intl.formatMessage({ id: "Thank you" })} - - - {intl.formatMessage({ id: "We look forward to your visit!" })} - -
- - {intl.formatMessage({ - id: "We have sent a detailed confirmation of your booking to your email: ", - })} - {email} - -
- - -
-
- ) -} diff --git a/components/HotelReservation/BookingConfirmation/IntroSection/introSection.module.css b/components/HotelReservation/BookingConfirmation/IntroSection/introSection.module.css deleted file mode 100644 index 5b79c3796..000000000 --- a/components/HotelReservation/BookingConfirmation/IntroSection/introSection.module.css +++ /dev/null @@ -1,26 +0,0 @@ -.section { - display: flex; - flex-direction: column; - gap: var(--Spacing-x3); - width: 100%; -} - -.buttons { - display: flex; - flex-direction: column; - align-items: center; - gap: var(--Spacing-x2); -} - -.button { - width: 100%; - max-width: 240px; - justify-content: center; -} - -@media screen and (min-width: 1367px) { - .buttons { - flex-direction: row; - justify-content: space-around; - } -} diff --git a/components/HotelReservation/BookingConfirmation/StaySection/index.tsx b/components/HotelReservation/BookingConfirmation/StaySection/index.tsx deleted file mode 100644 index 7907ac191..000000000 --- a/components/HotelReservation/BookingConfirmation/StaySection/index.tsx +++ /dev/null @@ -1,81 +0,0 @@ -import { useIntl } from "react-intl" - -import { ArrowRightIcon, ScandicLogoIcon } from "@/components/Icons" -import Image from "@/components/Image" -import Body from "@/components/TempDesignSystem/Text/Body" -import Caption from "@/components/TempDesignSystem/Text/Caption" -import Title from "@/components/TempDesignSystem/Text/Title" - -import styles from "./staySection.module.css" - -import { StaySectionProps } from "@/types/components/hotelReservation/bookingConfirmation/bookingConfirmation" - -export default function StaySection({ hotel, stay }: StaySectionProps) { - const intl = useIntl() - - const nightsText = - stay.nights > 1 - ? intl.formatMessage({ id: "nights" }) - : intl.formatMessage({ id: "night" }) - - return ( - <> -
- -
-
- - - {hotel.name} - - - {hotel.address} - {hotel.phone} - -
- - {`${stay.nights} ${nightsText}`} - - {stay.start} - - {stay.end} - - -
-
-
-
- - {intl.formatMessage({ id: "Breakfast" })} - - - {`${intl.formatMessage({ id: "Weekdays" })} ${hotel.breakfast.start}-${hotel.breakfast.end}`} - {`${intl.formatMessage({ id: "Weekends" })} ${hotel.breakfast.start}-${hotel.breakfast.end}`} - -
-
- {intl.formatMessage({ id: "Check in" })} - - {intl.formatMessage({ id: "From" })} - {hotel.checkIn} - -
-
- - {intl.formatMessage({ id: "Check out" })} - - - {intl.formatMessage({ id: "At latest" })} - {hotel.checkOut} - -
-
- - ) -} diff --git a/components/HotelReservation/BookingConfirmation/StaySection/staySection.module.css b/components/HotelReservation/BookingConfirmation/StaySection/staySection.module.css deleted file mode 100644 index 1eae5c732..000000000 --- a/components/HotelReservation/BookingConfirmation/StaySection/staySection.module.css +++ /dev/null @@ -1,78 +0,0 @@ -.card { - display: flex; - width: 100%; - background-color: var(--Base-Surface-Primary-light-Normal); - border: 1px solid var(--Base-Border-Subtle); - border-radius: var(--Corner-radius-Small); - overflow: hidden; -} - -.image { - height: 100%; - width: 105px; - object-fit: cover; -} - -.info { - display: flex; - flex-direction: column; - width: 100%; - gap: var(--Spacing-x1); - padding: var(--Spacing-x2); -} - -.hotel, -.stay { - display: flex; - flex-direction: column; - gap: var(--Spacing-x-half); -} - -.caption { - display: flex; - flex-direction: column; -} - -.dates { - display: flex; - align-items: center; - gap: var(--Spacing-x-half); -} - -.table { - display: flex; - justify-content: space-between; - padding: var(--Spacing-x2); - border-radius: var(--Corner-radius-Small); - background-color: var(--Base-Surface-Primary-dark-Normal); - width: 100%; -} - -.breakfast, -.checkIn, -.checkOut { - display: flex; - flex-direction: column; - gap: var(--Spacing-x-half); -} - -@media screen and (min-width: 1367px) { - .card { - flex-direction: column; - } - .image { - width: 100%; - max-height: 195px; - } - - .info { - flex-direction: row; - justify-content: space-between; - } - - .hotel, - .stay { - width: 100%; - max-width: 230px; - } -} diff --git a/components/HotelReservation/BookingConfirmation/SummarySection/index.tsx b/components/HotelReservation/BookingConfirmation/SummarySection/index.tsx deleted file mode 100644 index 16eb84330..000000000 --- a/components/HotelReservation/BookingConfirmation/SummarySection/index.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { useIntl } from "react-intl" - -import Caption from "@/components/TempDesignSystem/Text/Caption" -import Title from "@/components/TempDesignSystem/Text/Title" - -import styles from "./summarySection.module.css" - -import { SummarySectionProps } from "@/types/components/hotelReservation/bookingConfirmation/bookingConfirmation" - -export default function SummarySection({ summary }: SummarySectionProps) { - const intl = useIntl() - const roomType = `${intl.formatMessage({ id: "Type of room" })}: ${summary.roomType}` - const bedType = `${intl.formatMessage({ id: "Type of bed" })}: ${summary.bedType}` - const breakfast = `${intl.formatMessage({ id: "Breakfast" })}: ${summary.breakfast}` - const flexibility = `${intl.formatMessage({ id: "Flexibility" })}: ${summary.flexibility}` - - return ( -
- - {intl.formatMessage({ id: "Summary" })} - - - {roomType} - 1648 SEK - - - {bedType} - 0 SEK - - - {breakfast} - 198 SEK - - - {flexibility} - 200 SEK - -
- ) -} diff --git a/components/HotelReservation/BookingConfirmation/SummarySection/summarySection.module.css b/components/HotelReservation/BookingConfirmation/SummarySection/summarySection.module.css deleted file mode 100644 index b65d92e76..000000000 --- a/components/HotelReservation/BookingConfirmation/SummarySection/summarySection.module.css +++ /dev/null @@ -1,13 +0,0 @@ -.section { - width: 100%; -} - -.summary { - display: flex; - justify-content: space-between; - border-bottom: 1px solid var(--Base-Border-Subtle); -} - -.summary span { - padding: var(--Spacing-x2) var(--Spacing-x0); -} diff --git a/components/HotelReservation/BookingConfirmation/tempConfirmationData.ts b/components/HotelReservation/BookingConfirmation/tempConfirmationData.ts deleted file mode 100644 index 2dbf572e7..000000000 --- a/components/HotelReservation/BookingConfirmation/tempConfirmationData.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { BookingConfirmation } from "@/types/components/hotelReservation/bookingConfirmation/bookingConfirmation" - -export const tempConfirmationData: BookingConfirmation = { - email: "lisa.andersson@outlook.com", - hotel: { - name: "Helsinki Hub", - address: "Kaisaniemenkatu 7, Helsinki", - location: "Helsinki", - phone: "+358 300 870680", - image: - "https://test3.scandichotels.com/imagevault/publishedmedia/i11isd60bh119s9486b7/downtown-camper-by-scandic-lobby-reception-desk-ch.jpg?w=640", - checkIn: "15.00", - checkOut: "12.00", - breakfast: { start: "06:30", end: "10:00" }, - }, - stay: { - nights: 1, - start: "2024.03.09", - end: "2024.03.10", - }, - summary: { - roomType: "Standard Room", - bedType: "King size", - breakfast: "Yes", - flexibility: "Yes", - }, -} diff --git a/components/HotelReservation/EnterDetails/Payment/index.tsx b/components/HotelReservation/EnterDetails/Payment/index.tsx index 3d582e7e3..5ad6bdaf9 100644 --- a/components/HotelReservation/EnterDetails/Payment/index.tsx +++ b/components/HotelReservation/EnterDetails/Payment/index.tsx @@ -66,7 +66,7 @@ export default function Payment({ resolver: zodResolver(paymentSchema), }) - const initiateBooking = trpc.booking.booking.create.useMutation({ + const initiateBooking = trpc.booking.create.useMutation({ onSuccess: (result) => { if (result?.confirmationNumber) { setConfirmationNumber(result.confirmationNumber) diff --git a/components/Icons/Download.tsx b/components/Icons/Download.tsx new file mode 100644 index 000000000..7c1e9017a --- /dev/null +++ b/components/Icons/Download.tsx @@ -0,0 +1,40 @@ +import { iconVariants } from "./variants" + +import type { IconProps } from "@/types/components/icon" + +export default function DownloadIcon({ + className, + color, + ...props +}: IconProps) { + const classNames = iconVariants({ className, color }) + return ( + + + + + + + + + ) +} diff --git a/components/Icons/Printer.tsx b/components/Icons/Printer.tsx new file mode 100644 index 000000000..d703940da --- /dev/null +++ b/components/Icons/Printer.tsx @@ -0,0 +1,36 @@ +import { iconVariants } from "./variants" + +import type { IconProps } from "@/types/components/icon" + +export default function PrinterIcon({ className, color, ...props }: IconProps) { + const classNames = iconVariants({ className, color }) + return ( + + + + + + + + + ) +} diff --git a/components/Icons/index.tsx b/components/Icons/index.tsx index 8a7764dd6..e7547b178 100644 --- a/components/Icons/index.tsx +++ b/components/Icons/index.tsx @@ -28,6 +28,7 @@ export { default as CrossCircle } from "./CrossCircle" export { default as CulturalIcon } from "./Cultural" export { default as DeleteIcon } from "./Delete" export { default as DoorOpenIcon } from "./DoorOpen" +export { default as DownloadIcon } from "./Download" export { default as DresserIcon } from "./Dresser" export { default as EditIcon } from "./Edit" export { default as ElectricBikeIcon } from "./ElectricBike" @@ -76,6 +77,7 @@ export { default as PhoneIcon } from "./Phone" export { default as PlusIcon } from "./Plus" export { default as PlusCircleIcon } from "./PlusCircle" export { default as PriceTagIcon } from "./PriceTag" +export { default as PrinterIcon } from "./Printer" export { default as RestaurantIcon } from "./Restaurant" export { default as RoomServiceIcon } from "./RoomService" export { default as SaunaIcon } from "./Sauna" diff --git a/constants/booking.ts b/constants/booking.ts index 9f5b6fed9..81e23cd23 100644 --- a/constants/booking.ts +++ b/constants/booking.ts @@ -4,6 +4,13 @@ export enum BookingStatusEnum { BookingCompleted = "BookingCompleted", } +export enum BedTypeEnum { + Crib = "Crib", + ExtraBed = "ExtraBed", + ParentsBed = "ParentsBed", + Unknown = "Unknown", +} + export const BOOKING_CONFIRMATION_NUMBER = "bookingConfirmationNumber" export enum PaymentMethodEnum { diff --git a/i18n/dictionaries/en.json b/i18n/dictionaries/en.json index 759c424d0..3500a8d01 100644 --- a/i18n/dictionaries/en.json +++ b/i18n/dictionaries/en.json @@ -9,6 +9,7 @@ "Add Room": "Add room", "Add code": "Add code", "Add new card": "Add new card", + "Add to calendar": "Add to calendar", "Address": "Address", "Adults": "Adults", "Age": "Age", @@ -25,7 +26,6 @@ "Approx.": "Approx.", "Are you sure you want to remove the card ending with {lastFourDigits} from your member profile?": "Are you sure you want to remove the card ending with {lastFourDigits} from your member profile?", "Arrival date": "Arrival date", - "as of today": "as of today", "As our": "As our {level}", "As our Close Friend": "As our Close Friend", "At latest": "At latest", @@ -39,12 +39,6 @@ "Book": "Book", "Book reward night": "Book reward night", "Booking number": "Booking number", - "booking.adults": "{totalAdults, plural, one {# adult} other {# adults}}", - "booking.children": "{totalChildren, plural, one {# child} other {# children}}", - "booking.guests": "Max {nrOfGuests, plural, one {# guest} other {# guests}}", - "booking.nights": "{totalNights, plural, one {# night} other {# nights}}", - "booking.rooms": "{totalRooms, plural, one {# room} other {# rooms}}", - "booking.terms": "By paying with any of the payment methods available, I accept the terms for this booking and the general Terms & Conditions, and understand that Scandic will process my personal data for this booking in accordance with Scandic's Privacy policy. I also accept that Scandic require a valid credit card during my visit in case anything is left unpaid.", "Breakfast": "Breakfast", "Breakfast buffet": "Breakfast buffet", "Breakfast excluded": "Breakfast excluded", @@ -53,12 +47,13 @@ "Breakfast selection in next step.": "Breakfast selection in next step.", "Bus terminal": "Bus terminal", "Business": "Business", - "by": "by", "Cancel": "Cancel", - "characters": "characters", + "Cancellation policy": "Cancellation policy", "Check in": "Check in", "Check out": "Check out", "Check out the credit cards saved to your profile. Pay with a saved card when signed in for a smoother web experience.": "Check out the credit cards saved to your profile. Pay with a saved card when signed in for a smoother web experience.", + "Check-in": "Check-in", + "Check-out": "Check-out", "Child age is required": "Child age is required", "Children": "Children", "Choose room": "Choose room", @@ -100,6 +95,7 @@ "Distance to city centre": "{number}km to city centre", "Do you want to start the day with Scandics famous breakfast buffé?": "Do you want to start the day with Scandics famous breakfast buffé?", "Done": "Done", + "Download invoice": "Download invoice", "Download the Scandic app": "Download the Scandic app", "Driving directions": "Driving directions", "Earn bonus nights & points": "Earn bonus nights & points", @@ -114,9 +110,9 @@ "Explore all levels and benefits": "Explore all levels and benefits", "Explore nearby": "Explore nearby", "Extras to your booking": "Extras to your booking", + "FAQ": "FAQ", "Failed to delete credit card, please try again later.": "Failed to delete credit card, please try again later.", "Fair": "Fair", - "FAQ": "FAQ", "Find booking": "Find booking", "Find hotels": "Find hotels", "First name": "First name", @@ -125,14 +121,14 @@ "Former Scandic Hotel": "Former Scandic Hotel", "Free cancellation": "Free cancellation", "Free rebooking": "Free rebooking", + "Free until": "Free until", "From": "From", "Get inspired": "Get inspired", "Get member benefits & offers": "Get member benefits & offers", "Go back to edit": "Go back to edit", "Go back to overview": "Go back to overview", - "guest": "guest", + "Guest": "Guest", "Guest information": "Guest information", - "guests": "guests", "Guests & Rooms": "Guests & Rooms", "Hi": "Hi", "Highest level": "Highest level", @@ -140,9 +136,6 @@ "Hotel": "Hotel", "Hotel facilities": "Hotel facilities", "Hotel surroundings": "Hotel surroundings", - "hotelPages.rooms.roomCard.person": "person", - "hotelPages.rooms.roomCard.persons": "persons", - "hotelPages.rooms.roomCard.seeRoomDetails": "See room details", "Hotels": "Hotels", "How do you want to sleep?": "How do you want to sleep?", "How it works": "How it works", @@ -153,10 +146,9 @@ "In extra bed": "In extra bed", "Included": "Included", "It is not posible to manage your communication preferences right now, please try again later or contact support if the problem persists.": "It is not posible to manage your communication preferences right now, please try again later or contact support if the problem persists.", - "Join at no cost": "Join at no cost", "Join Scandic Friends": "Join Scandic Friends", + "Join at no cost": "Join at no cost", "King bed": "King bed", - "km to city center": "km to city center", "Language": "Language", "Last name": "Last name", "Latest searches": "Latest searches", @@ -176,7 +168,7 @@ "Log in here": "Log in here", "Log in/Join": "Log in/Join", "Log out": "Log out", - "lowercase letter": "lowercase letter", + "MY SAVED CARDS": "MY SAVED CARDS", "Main menu": "Main menu", "Manage preferences": "Manage preferences", "Map": "Map", @@ -186,9 +178,9 @@ "Member price": "Member price", "Member price from": "Member price from", "Members": "Members", - "Membership cards": "Membership cards", "Membership ID": "Membership ID", "Membership ID copied to clipboard": "Membership ID copied to clipboard", + "Membership cards": "Membership cards", "Menu": "Menu", "Modify": "Modify", "Month": "Month", @@ -198,16 +190,11 @@ "My pages": "My pages", "My pages menu": "My pages menu", "My payment cards": "My payment cards", - "MY SAVED CARDS": "MY SAVED CARDS", "My wishes": "My wishes", - "n/a": "n/a", "Nearby": "Nearby", "Nearby companies": "Nearby companies", "New password": "New password", "Next": "Next", - "next level:": "next level:", - "night": "night", - "nights": "nights", "Nights needed to level up": "Nights needed to level up", "No breakfast": "No breakfast", "No content published": "No content published", @@ -220,14 +207,12 @@ "Nordic Swan Ecolabel": "Nordic Swan Ecolabel", "Not found": "Not found", "Nr night, nr adult": "{nights, number} night, {adults, number} adult", - "number": "number", + "OTHER PAYMENT METHODS": "OTHER PAYMENT METHODS", "On your journey": "On your journey", "Open": "Open", "Open language menu": "Open language menu", "Open menu": "Open menu", "Open my pages menu": "Open my pages menu", - "or": "or", - "OTHER PAYMENT METHODS": "OTHER PAYMENT METHODS", "Overview": "Overview", "Parking": "Parking", "Parking / Garage": "Parking / Garage", @@ -235,11 +220,11 @@ "Pay later": "Pay later", "Pay now": "Pay now", "Payment info": "Payment info", + "Payment received": "Payment received", "Phone": "Phone", "Phone is required": "Phone is required", "Phone number": "Phone number", "Please enter a valid phone number": "Please enter a valid phone number", - "points": "Points", "Points": "Points", "Points being calculated": "Points being calculated", "Points earned prior to May 1, 2021": "Points earned prior to May 1, 2021", @@ -247,6 +232,7 @@ "Points needed to level up": "Points needed to level up", "Points needed to stay on level": "Points needed to stay on level", "Previous victories": "Previous victories", + "Print confirmation": "Print confirmation", "Proceed to login": "Proceed to login", "Proceed to payment method": "Proceed to payment method", "Public price from": "Public price from", @@ -256,6 +242,8 @@ "Read more & book a table": "Read more & book a table", "Read more about the hotel": "Read more about the hotel", "Read more about wellness & exercise": "Read more about wellness & exercise", + "Rebooking": "Rebooking", + "Reference #{bookingNr}": "Reference #{bookingNr}", "Remove card from member profile": "Remove card from member profile", "Request bedtype": "Request bedtype", "Restaurant": "{count, plural, one {#Restaurant} other {#Restaurants}}", @@ -301,34 +289,32 @@ "Something went wrong and we couldn't add your card. Please try again later.": "Something went wrong and we couldn't add your card. Please try again later.", "Something went wrong and we couldn't remove your card. Please try again later.": "Something went wrong and we couldn't remove your card. Please try again later.", "Something went wrong!": "Something went wrong!", - "special character": "special character", - "spendable points expiring by": "{points} spendable points expiring by {date}", "Sports": "Sports", "Standard price": "Standard price", "Street": "Street", "Successfully updated profile!": "Successfully updated profile!", "Summary": "Summary", + "TUI Points": "TUI Points", "Tell us what information and updates you'd like to receive, and how, by clicking the link below.": "Tell us what information and updates you'd like to receive, and how, by clicking the link below.", "Terms and conditions": "Terms and conditions", "Thank you": "Thank you", "Theatre": "Theatre", "There are no transactions to display": "There are no transactions to display", "Things nearby HOTEL_NAME": "Things nearby {hotelName}", - "to": "to", - "Total incl VAT": "Total incl VAT", "Total Points": "Total Points", + "Total cost": "Total cost", + "Total incl VAT": "Total incl VAT", "Tourist": "Tourist", "Transaction date": "Transaction date", "Transactions": "Transactions", "Transportations": "Transportations", "Tripadvisor reviews": "{rating} ({count} reviews on Tripadvisor)", - "TUI Points": "TUI Points", "Type of bed": "Type of bed", "Type of room": "Type of room", - "uppercase letter": "uppercase letter", "Use bonus cheque": "Use bonus cheque", "Use code/voucher": "Use code/voucher", "User information": "User information", + "VAT": "VAT", "View as list": "View as list", "View as map": "View as map", "View your booking": "View your booking", @@ -347,18 +333,19 @@ "Where to": "Where to", "Which room class suits you the best?": "Which room class suits you the best?", "Year": "Year", - "Yes, discard changes": "Yes, discard changes", "Yes, I accept the Terms and conditions for Scandic Friends and understand that Scandic will process my personal data in accordance with": "Yes, I accept the Terms and conditions for Scandic Friends and understand that Scandic will process my personal data in accordance with", + "Yes, discard changes": "Yes, discard changes", "Yes, remove my card": "Yes, remove my card", "You can always change your mind later and add breakfast at the hotel.": "You can always change your mind later and add breakfast at the hotel.", "You canceled adding a new credit card.": "You canceled adding a new credit card.", "You have no previous stays.": "You have no previous stays.", "You have no upcoming stays.": "You have no upcoming stays.", + "Your Challenges Conquer & Earn!": "Your Challenges Conquer & Earn!", "Your card was successfully removed!": "Your card was successfully removed!", "Your card was successfully saved!": "Your card was successfully saved!", - "Your Challenges Conquer & Earn!": "Your Challenges Conquer & Earn!", "Your current level": "Your current level", "Your details": "Your details", + "Your hotel": "Your hotel", "Your level": "Your level", "Your points to spend": "Your points to spend", "Your room": "Your room", @@ -366,7 +353,39 @@ "Zoo": "Zoo", "Zoom in": "Zoom in", "Zoom out": "Zoom out", + "as of today": "as of today", + "booking.adults": "{totalAdults, plural, one {# adult} other {# adults}}", + "booking.children": "{totalChildren, plural, one {# child} other {# children}}", + "booking.confirmation.text": "Thank you for booking with us! We look forward to welcoming you and hope you have a pleasant stay. If you have any questions or need to make changes to your reservation, please email us.", + "booking.confirmation.title": "Your booking is confirmed", + "booking.guests": "Max {nrOfGuests, plural, one {# guest} other {# guests}}", + "booking.nights": "{totalNights, plural, one {# night} other {# nights}}", + "booking.rooms": "{totalRooms, plural, one {# room} other {# rooms}}", + "booking.terms": "By paying with any of the payment methods available, I accept the terms for this booking and the general Terms & Conditions, and understand that Scandic will process my personal data for this booking in accordance with Scandic's Privacy policy. I also accept that Scandic require a valid credit card during my visit in case anything is left unpaid.", + "by": "by", + "characters": "characters", + "from": "from", + "guest": "guest", + "guests": "guests", + "hotelPages.rooms.roomCard.person": "person", + "hotelPages.rooms.roomCard.persons": "persons", + "hotelPages.rooms.roomCard.seeRoomDetails": "See room details", + "km to city center": "km to city center", + "lowercase letter": "lowercase letter", + "member no": "member no", + "n/a": "n/a", + "next level:": "next level:", + "night": "night", + "nights": "nights", + "number": "number", + "or": "or", + "points": "Points", + "special character": "special character", + "spendable points expiring by": "{points} spendable points expiring by {date}", + "to": "to", + "uppercase letter": "uppercase letter", "{amount} {currency}": "{amount} {currency}", + "{card} ending with {cardno}": "{card} ending with {cardno}", "{difference}{amount} {currency}": "{difference}{amount} {currency}", "{width} cm × {length} cm": "{width} cm × {length} cm" -} +} \ No newline at end of file diff --git a/i18n/index.ts b/i18n/index.ts index be2d744bb..0a8ebb67d 100644 --- a/i18n/index.ts +++ b/i18n/index.ts @@ -8,7 +8,7 @@ import { Lang } from "@/constants/languages" const cache = createIntlCache() async function initIntl(lang: Lang) { - return createIntl( + return createIntl( { defaultLocale: Lang.en, locale: lang, diff --git a/lib/dt.ts b/lib/dt.ts index b90938475..5cbe77692 100644 --- a/lib/dt.ts +++ b/lib/dt.ts @@ -7,6 +7,7 @@ import d from "dayjs" import advancedFormat from "dayjs/plugin/advancedFormat" import isToday from "dayjs/plugin/isToday" import relativeTime from "dayjs/plugin/relativeTime" +import timezone from "dayjs/plugin/timezone" import utc from "dayjs/plugin/utc" /** @@ -59,6 +60,7 @@ d.locale("no", { d.extend(advancedFormat) d.extend(isToday) d.extend(relativeTime) +d.extend(timezone) d.extend(utc) export const dt = d diff --git a/server/routers/booking/input.ts b/server/routers/booking/input.ts index 7e9b9b2c8..cbda7d3ef 100644 --- a/server/routers/booking/input.ts +++ b/server/routers/booking/input.ts @@ -63,6 +63,10 @@ export const createBookingInput = z.object({ }) // Query -export const getBookingStatusInput = z.object({ +const confirmationNumberInput = z.object({ confirmationNumber: z.string(), }) + +export const bookingConfirmationInput = confirmationNumberInput + +export const getBookingStatusInput = confirmationNumberInput diff --git a/server/routers/booking/mutation.ts b/server/routers/booking/mutation.ts index 1ff3422af..9e5677d32 100644 --- a/server/routers/booking/mutation.ts +++ b/server/routers/booking/mutation.ts @@ -35,96 +35,95 @@ async function getMembershipNumber( } export const bookingMutationRouter = router({ - booking: router({ - create: serviceProcedure - .input(createBookingInput) - .mutation(async function ({ ctx, input }) { - const { checkInDate, checkOutDate, hotelId } = input + create: serviceProcedure.input(createBookingInput).mutation(async function ({ + ctx, + input, + }) { + const { checkInDate, checkOutDate, hotelId } = input - // TODO: add support for user token OR service token in procedure - // then we can fetch membership number if user token exists - const loggingAttributes = { - // membershipNumber: await getMembershipNumber(ctx.session), - checkInDate, - checkOutDate, - hotelId, - } + // TODO: add support for user token OR service token in procedure + // then we can fetch membership number if user token exists + const loggingAttributes = { + // membershipNumber: await getMembershipNumber(ctx.session), + checkInDate, + checkOutDate, + hotelId, + } - createBookingCounter.add(1, { hotelId, checkInDate, checkOutDate }) + createBookingCounter.add(1, { hotelId, checkInDate, checkOutDate }) - console.info( - "api.booking.booking.create start", - JSON.stringify({ - query: loggingAttributes, - }) - ) - const headers = { - Authorization: `Bearer ${ctx.serviceToken}`, - } + console.info( + "api.booking.create start", + JSON.stringify({ + query: loggingAttributes, + }) + ) + const headers = { + Authorization: `Bearer ${ctx.serviceToken}`, + } - const apiResponse = await api.post(api.endpoints.v1.booking, { - headers, - body: input, + const apiResponse = await api.post(api.endpoints.v1.booking, { + headers, + body: input, + }) + + if (!apiResponse.ok) { + const text = await apiResponse.text() + createBookingFailCounter.add(1, { + hotelId, + checkInDate, + checkOutDate, + error_type: "http_error", + error: JSON.stringify({ + status: apiResponse.status, + }), + }) + console.error( + "api.booking.create error", + JSON.stringify({ + query: loggingAttributes, + error: { + status: apiResponse.status, + statusText: apiResponse.statusText, + error: text, + }, }) + ) + return null + } - if (!apiResponse.ok) { - const text = await apiResponse.text() - createBookingFailCounter.add(1, { - hotelId, - checkInDate, - checkOutDate, - error_type: "http_error", - error: JSON.stringify({ - status: apiResponse.status, - }), - }) - console.error( - "api.booking.booking.create error", - JSON.stringify({ - query: loggingAttributes, - error: { - status: apiResponse.status, - statusText: apiResponse.statusText, - error: text, - }, - }) - ) - return null - } + const apiJson = await apiResponse.json() + const verifiedData = createBookingSchema.safeParse(apiJson) + if (!verifiedData.success) { + createBookingFailCounter.add(1, { + hotelId, + checkInDate, + checkOutDate, + error_type: "validation_error", + }) - const apiJson = await apiResponse.json() - const verifiedData = createBookingSchema.safeParse(apiJson) - if (!verifiedData.success) { - createBookingFailCounter.add(1, { - hotelId, - checkInDate, - checkOutDate, - error_type: "validation_error", - }) - - console.error( - "api.booking.booking.create validation error", - JSON.stringify({ - query: loggingAttributes, - error: verifiedData.error, - }) - ) - return null - } - - createBookingSuccessCounter.add(1, { - hotelId, - checkInDate, - checkOutDate, + console.error( + "api.booking.create validation error", + JSON.stringify({ + query: loggingAttributes, + error: verifiedData.error, }) + ) + return null + } - console.info( - "api.booking.booking.create success", - JSON.stringify({ - query: loggingAttributes, - }) - ) - return verifiedData.data - }), + createBookingSuccessCounter.add(1, { + hotelId, + checkInDate, + checkOutDate, + }) + + console.info( + "api.booking.create success", + JSON.stringify({ + query: loggingAttributes, + }) + ) + return verifiedData.data }), }) diff --git a/server/routers/booking/output.ts b/server/routers/booking/output.ts index 7535aac6e..aacf1ca6b 100644 --- a/server/routers/booking/output.ts +++ b/server/routers/booking/output.ts @@ -1,5 +1,8 @@ import { z } from "zod" +import { BedTypeEnum } from "@/constants/booking" + +// MUTATION export const createBookingSchema = z .object({ data: z.object({ @@ -32,4 +35,46 @@ export const createBookingSchema = z paymentUrl: d.data.attributes.paymentUrl, })) -type CreateBookingData = z.infer +// QUERY +const childrenAgesSchema = z.object({ + age: z.number(), + bedType: z.nativeEnum(BedTypeEnum), +}) + +const guestSchema = z.object({ + firstName: z.string(), + lastName: z.string(), +}) + +const packagesSchema = z.object({ + accessibility: z.boolean(), + allergyFriendly: z.boolean(), + breakfast: z.boolean(), + petFriendly: z.boolean(), +}) + +export const bookingConfirmationSchema = z + .object({ + data: z.object({ + attributes: z.object({ + adults: z.number(), + checkInDate: z.date({ coerce: true }), + checkOutDate: z.date({ coerce: true }), + createDateTime: z.date({ coerce: true }), + childrenAges: z.array(childrenAgesSchema), + computedReservationStatus: z.string(), + confirmationNumber: z.string(), + currencyCode: z.string(), + guest: guestSchema, + hasPayRouting: z.boolean(), + hotelId: z.string(), + packages: packagesSchema, + rateCode: z.string(), + reservationStatus: z.string(), + totalPrice: z.number(), + }), + id: z.string(), + type: z.literal("booking"), + }), + }) + .transform(({ data }) => data.attributes) diff --git a/server/routers/booking/query.ts b/server/routers/booking/query.ts index d053782bd..76b874ba3 100644 --- a/server/routers/booking/query.ts +++ b/server/routers/booking/query.ts @@ -4,10 +4,20 @@ import * as api from "@/lib/api" import { badRequestError, serverErrorByStatus } from "@/server/errors/trpc" import { router, serviceProcedure } from "@/server/trpc" -import { getBookingStatusInput } from "./input" -import { createBookingSchema } from "./output" +import { bookingConfirmationInput, getBookingStatusInput } from "./input" +import { bookingConfirmationSchema, createBookingSchema } from "./output" const meter = metrics.getMeter("trpc.booking") +const getBookingConfirmationCounter = meter.createCounter( + "trpc.booking.confirmation" +) +const getBookingConfirmationSuccessCounter = meter.createCounter( + "trpc.booking.confirmation-success" +) +const getBookingConfirmationFailCounter = meter.createCounter( + "trpc.booking.confirmation-fail" +) + const getBookingStatusCounter = meter.createCounter("trpc.booking.status") const getBookingStatusSuccessCounter = meter.createCounter( "trpc.booking.status-success" @@ -17,6 +27,113 @@ const getBookingStatusFailCounter = meter.createCounter( ) export const bookingQueryRouter = router({ + confirmation: serviceProcedure + .input(bookingConfirmationInput) + .query(async function ({ ctx, input: { confirmationNumber } }) { + getBookingConfirmationCounter.add(1, { confirmationNumber }) + + const apiResponse = await api.get( + `${api.endpoints.v1.booking}/${confirmationNumber}`, + { + headers: { + Authorization: `Bearer ${ctx.serviceToken}`, + }, + } + ) + + if (!apiResponse.ok) { + const responseMessage = await apiResponse.text() + getBookingConfirmationFailCounter.add(1, { + confirmationNumber, + error_type: "http_error", + error: responseMessage, + }) + console.error( + "api.booking.confirmation error", + JSON.stringify({ + query: { confirmationNumber }, + error: { + status: apiResponse.status, + statusText: apiResponse.statusText, + text: responseMessage, + }, + }) + ) + + throw serverErrorByStatus(apiResponse.status, apiResponse) + } + + const apiJson = await apiResponse.json() + const booking = bookingConfirmationSchema.safeParse(apiJson) + if (!booking.success) { + getBookingConfirmationFailCounter.add(1, { + confirmationNumber, + error_type: "validation_error", + error: JSON.stringify(booking.error), + }) + console.error( + "api.booking.confirmation validation error", + JSON.stringify({ + query: { confirmationNumber }, + error: booking.error, + }) + ) + throw badRequestError() + } + + getBookingConfirmationSuccessCounter.add(1, { confirmationNumber }) + console.info( + "api.booking.confirmation success", + JSON.stringify({ + query: { confirmationNumber }, + }) + ) + + return { + ...booking.data, + temp: { + breakfastFrom: "06:30", + breakfastTo: "11:00", + cancelPolicy: "Free rebooking", + fromDate: "2024-10-21 14:00", + packages: [ + { + name: "Breakfast buffet", + price: "150 SEK", + }, + { + name: "Member discount", + price: "-297 SEK", + }, + { + name: "Points used / remaining", + price: "0 / 1044", + }, + ], + payment: "2024-08-09 1:47", + room: { + price: "2 589 SEK", + type: "Cozy Cabin", + vat: "684,79 SEK", + }, + toDate: "2024-10-22 11:00", + total: "2 739 SEK", + totalInEuro: "265 EUR", + }, + guest: { + email: "sarah.obrian@gmail.com", + firstName: "Sarah", + lastName: "O'Brian", + memberbershipNumber: "19822", + phoneNumber: "+46702446688", + }, + hotel: { + email: "bookings@scandichotels.com", + name: "Downtown Camper by Scandic", + phoneNumber: "+4689001350", + }, + } + }), status: serviceProcedure.input(getBookingStatusInput).query(async function ({ ctx, input, From d5abe13a7e323a3a20f52c66394ae993065b1e98 Mon Sep 17 00:00:00 2001 From: Michael Zetterberg Date: Wed, 23 Oct 2024 12:18:08 +0200 Subject: [PATCH 3/5] fix: add feature flag for service token scopes --- server/tokenManager.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/server/tokenManager.ts b/server/tokenManager.ts index cdd6078dc..24180d017 100644 --- a/server/tokenManager.ts +++ b/server/tokenManager.ts @@ -70,7 +70,12 @@ async function fetchServiceToken(scopes: string[]) { } export async function getServiceToken() { - const scopes = ["profile", "hotel", "booking"] + let scopes: string[] = [] + if (env.HIDE_FOR_NEXT_RELEASE) { + scopes = ["profile"] + } else { + scopes = ["profile", "hotel", "booking"] + } const tag = generateServiceTokenTag(scopes) const getCachedJwt = unstable_cache( async (scopes) => { From 52c5f6d29ecfce9f0766432096f0891ce7d1c933 Mon Sep 17 00:00:00 2001 From: Michael Zetterberg Date: Wed, 23 Oct 2024 12:18:55 +0200 Subject: [PATCH 4/5] fix: feature flag booking widget and sitewidealert --- app/[lang]/(live)/@bookingwidget/loading.tsx | 6 ++++++ app/[lang]/(live)/@bookingwidget/page.tsx | 5 +++++ app/[lang]/(live)/@sitewidealert/page.tsx | 6 ++++++ 3 files changed, 17 insertions(+) diff --git a/app/[lang]/(live)/@bookingwidget/loading.tsx b/app/[lang]/(live)/@bookingwidget/loading.tsx index 2c203967d..5e05ba68c 100644 --- a/app/[lang]/(live)/@bookingwidget/loading.tsx +++ b/app/[lang]/(live)/@bookingwidget/loading.tsx @@ -1,8 +1,14 @@ +import { env } from "@/env/server" + import LoadingSpinner from "@/components/LoadingSpinner" import styles from "./loading.module.css" export default function LoadingBookingWidget() { + if (env.HIDE_FOR_NEXT_RELEASE) { + return null + } + return (
diff --git a/app/[lang]/(live)/@bookingwidget/page.tsx b/app/[lang]/(live)/@bookingwidget/page.tsx index 13a414cba..7e197d0fa 100644 --- a/app/[lang]/(live)/@bookingwidget/page.tsx +++ b/app/[lang]/(live)/@bookingwidget/page.tsx @@ -1,8 +1,13 @@ +import { env } from "@/env/server" import { serverClient } from "@/lib/trpc/server" import BookingWidget, { preload } from "@/components/BookingWidget" export default async function BookingWidgetPage() { + if (env.HIDE_FOR_NEXT_RELEASE) { + return null + } + preload() // Get the booking widget show/hide status based on page specific settings diff --git a/app/[lang]/(live)/@sitewidealert/page.tsx b/app/[lang]/(live)/@sitewidealert/page.tsx index 618f3b5cb..be7ae2256 100644 --- a/app/[lang]/(live)/@sitewidealert/page.tsx +++ b/app/[lang]/(live)/@sitewidealert/page.tsx @@ -1,11 +1,17 @@ import { Suspense } from "react" +import { env } from "@/env/server" + import SitewideAlert, { preload } from "@/components/SitewideAlert" import { setLang } from "@/i18n/serverContext" import type { LangParams, PageArgs } from "@/types/params" export default function SitewideAlertPage({ params }: PageArgs) { + if (env.HIDE_FOR_NEXT_RELEASE) { + return null + } + setLang(params.lang) preload() From 025c5c8291e5a1a4d0b09e03ac902ab8ade6e6a1 Mon Sep 17 00:00:00 2001 From: Tobias Johansson Date: Wed, 23 Oct 2024 11:51:44 +0000 Subject: [PATCH 5/5] Merged in feat/SW-659-payment-send-query-params (pull request #727) feat(SW-659): Receive query params from Planet callbacks * feat(SW-659): read confirmation number from url and update callback url if dev * fix(SW-659): moved callback url into env variable Approved-by: Simon.Emanuelsson --- .../booking-confirmation/page.tsx | 6 ++- .../payment-callback/[lang]/[status]/route.ts | 16 ++++++-- .../EnterDetails/Payment/index.tsx | 39 +++++++++++-------- constants/booking.ts | 2 +- env/client.ts | 4 ++ 5 files changed, 44 insertions(+), 23 deletions(-) diff --git a/app/[lang]/(live)/(public)/hotelreservation/(confirmation)/booking-confirmation/page.tsx b/app/[lang]/(live)/(public)/hotelreservation/(confirmation)/booking-confirmation/page.tsx index 23b5270d4..ef7e818c4 100644 --- a/app/[lang]/(live)/(public)/hotelreservation/(confirmation)/booking-confirmation/page.tsx +++ b/app/[lang]/(live)/(public)/hotelreservation/(confirmation)/booking-confirmation/page.tsx @@ -22,9 +22,11 @@ import type { LangParams, PageArgs } from "@/types/params" export default async function BookingConfirmationPage({ params, -}: PageArgs) { + searchParams, +}: PageArgs) { + const confirmationNumber = searchParams.confirmationNumber const booking = await serverClient().booking.confirmation({ - confirmationNumber: "991697377", + confirmationNumber, }) if (!booking) { diff --git a/app/api/web/payment-callback/[lang]/[status]/route.ts b/app/api/web/payment-callback/[lang]/[status]/route.ts index 4154aff14..baa8e4b8a 100644 --- a/app/api/web/payment-callback/[lang]/[status]/route.ts +++ b/app/api/web/payment-callback/[lang]/[status]/route.ts @@ -1,6 +1,6 @@ import { NextRequest, NextResponse } from "next/server" -import { env } from "process" +import { BOOKING_CONFIRMATION_NUMBER } from "@/constants/booking" import { Lang } from "@/constants/languages" import { bookingConfirmation, @@ -17,14 +17,24 @@ export async function GET( console.log(`[payment-callback] callback started`) const lang = params.lang as Lang const status = params.status - const returnUrl = new URL(`${publicURL}/${payment[lang]}`) - if (status === "success") { + const queryParams = request.nextUrl.searchParams + const confirmationNumber = queryParams.get(BOOKING_CONFIRMATION_NUMBER) + + if (status === "success" && confirmationNumber) { const confirmationUrl = new URL(`${publicURL}/${bookingConfirmation[lang]}`) + confirmationUrl.searchParams.set( + BOOKING_CONFIRMATION_NUMBER, + confirmationNumber + ) + console.log(`[payment-callback] redirecting to: ${confirmationUrl}`) return NextResponse.redirect(confirmationUrl) } + const returnUrl = new URL(`${publicURL}/${payment[lang]}`) + returnUrl.search = queryParams.toString() + if (status === "cancel") { returnUrl.searchParams.set("cancel", "true") } diff --git a/components/HotelReservation/EnterDetails/Payment/index.tsx b/components/HotelReservation/EnterDetails/Payment/index.tsx index 5ad6bdaf9..0da2b79e2 100644 --- a/components/HotelReservation/EnterDetails/Payment/index.tsx +++ b/components/HotelReservation/EnterDetails/Payment/index.tsx @@ -1,14 +1,13 @@ "use client" import { zodResolver } from "@hookform/resolvers/zod" -import { useRouter } from "next/navigation" -import { useEffect, useState } from "react" +import { useRouter, useSearchParams } from "next/navigation" +import { useEffect, useMemo, useState } from "react" import { Label as AriaLabel } from "react-aria-components" import { FormProvider, useForm } from "react-hook-form" import { useIntl } from "react-intl" import { - BOOKING_CONFIRMATION_NUMBER, BookingStatusEnum, PAYMENT_METHOD_TITLES, PaymentMethodEnum, @@ -17,7 +16,9 @@ import { bookingTermsAndConditions, privacyPolicy, } from "@/constants/currentWebHrefs" +import { env } from "@/env/client" import { trpc } from "@/lib/trpc/client" +import { useEnterDetailsStore } from "@/stores/enter-details" import LoadingSpinner from "@/components/LoadingSpinner" import Button from "@/components/TempDesignSystem/Button" @@ -51,6 +52,9 @@ export default function Payment({ const router = useRouter() const lang = useLang() const intl = useIntl() + const queryParams = useSearchParams() + const { firstName, lastName, email, phoneNumber, countryCode } = + useEnterDetailsStore((state) => state.data) const [confirmationNumber, setConfirmationNumber] = useState("") const methods = useForm({ @@ -90,14 +94,15 @@ export default function Payment({ ) useEffect(() => { - if (confirmationNumber && bookingStatus?.data?.paymentUrl) { - // Planet doesn't support query params so we have to store values in session storage - sessionStorage.setItem(BOOKING_CONFIRMATION_NUMBER, confirmationNumber) + if (bookingStatus?.data?.paymentUrl) { router.push(bookingStatus.data.paymentUrl) } - }, [confirmationNumber, bookingStatus, router]) + }, [bookingStatus, router]) function handleSubmit(data: PaymentFormData) { + const allQueryParams = + queryParams.size > 0 ? `?${queryParams.toString()}` : "" + // set payment method to card if saved card is submitted const paymentMethod = isPaymentMethodEnum(data.paymentMethod) ? data.paymentMethod @@ -118,13 +123,13 @@ export default function Payment({ rateCode: "SAVEEU", roomTypeCode: "QC", guest: { - title: "Mr", - firstName: "Test", - lastName: "User", - email: "test.user@scandichotels.com", - phoneCountryCodePrefix: "string", - phoneNumber: "string", - countryCode: "string", + title: "Mr", // TODO: do we need title? + firstName, + lastName, + email, + phoneCountryCodePrefix: phoneNumber.slice(0, 3), + phoneNumber: phoneNumber.slice(3), + countryCode, }, packages: { breakfast: true, @@ -150,9 +155,9 @@ export default function Payment({ phoneCountryCode: "", phoneSubscriber: "", }, - success: `api/web/payment-callback/${lang}/success`, - error: `api/web/payment-callback/${lang}/error`, - cancel: `api/web/payment-callback/${lang}/cancel`, + success: `${env.NEXT_PUBLIC_PAYMENT_CALLBACK_URL}/${lang}/success`, + error: `${env.NEXT_PUBLIC_PAYMENT_CALLBACK_URL}/${lang}/error${allQueryParams}`, + cancel: `${env.NEXT_PUBLIC_PAYMENT_CALLBACK_URL}/${lang}/cancel${allQueryParams}`, }, }) } diff --git a/constants/booking.ts b/constants/booking.ts index 81e23cd23..8f5acb120 100644 --- a/constants/booking.ts +++ b/constants/booking.ts @@ -11,7 +11,7 @@ export enum BedTypeEnum { Unknown = "Unknown", } -export const BOOKING_CONFIRMATION_NUMBER = "bookingConfirmationNumber" +export const BOOKING_CONFIRMATION_NUMBER = "confirmationNumber" export enum PaymentMethodEnum { card = "card", diff --git a/env/client.ts b/env/client.ts index 467100c01..4eafd5592 100644 --- a/env/client.ts +++ b/env/client.ts @@ -5,10 +5,14 @@ export const env = createEnv({ client: { NEXT_PUBLIC_NODE_ENV: z.enum(["development", "test", "production"]), NEXT_PUBLIC_PORT: z.string().default("3000"), + NEXT_PUBLIC_PAYMENT_CALLBACK_URL: z + .string() + .default("/api/web/payment-callback"), }, emptyStringAsUndefined: true, runtimeEnv: { NEXT_PUBLIC_NODE_ENV: process.env.NODE_ENV, NEXT_PUBLIC_PORT: process.env.NEXT_PUBLIC_PORT, + NEXT_PUBLIC_PAYMENT_CALLBACK_URL: `${process.env.NODE_ENV === "development" ? `http://localhost:${process.env.NEXT_PUBLIC_PORT}` : ""}/api/web/payment-callback`, }, })