feat(SW-1273): find my booking page with rudimentary validation and redirect
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
.main {
|
||||
width: var(--max-width-content);
|
||||
padding: var(--Spacing-x5) var(--Spacing-x1);
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.form {
|
||||
max-width: 640px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import FindMyBooking from "@/components/HotelReservation/FindMyBooking"
|
||||
|
||||
import styles from "./page.module.css"
|
||||
|
||||
export default async function GetBookingPage() {
|
||||
return (
|
||||
<main className={styles.main}>
|
||||
<div className={styles.form}>
|
||||
<FindMyBooking />
|
||||
</div>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
import { usePathname } from "next/navigation"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { findMyBooking } from "@/constants/myBooking"
|
||||
import { logout } from "@/constants/routes/handleAuth"
|
||||
import { myPages } from "@/constants/routes/myPages"
|
||||
import useDropdownStore from "@/stores/main-menu"
|
||||
@@ -147,7 +148,10 @@ export function MainMenu({
|
||||
<span className={styles.mobileSeparator} />
|
||||
</li>
|
||||
<li className={styles.mobileLinkRow}>
|
||||
<a className={styles.mobileLinkButton} href="">
|
||||
<a
|
||||
className={styles.mobileLinkButton}
|
||||
href={findMyBooking[lang]}
|
||||
>
|
||||
{intl.formatMessage({ id: "Find booking" })}
|
||||
</a>
|
||||
</li>
|
||||
|
||||
@@ -5,10 +5,12 @@ import { Dialog, Modal } from "react-aria-components"
|
||||
import { useIntl } from "react-intl"
|
||||
import { useMediaQuery } from "usehooks-ts"
|
||||
|
||||
import { findMyBooking } from "@/constants/myBooking"
|
||||
import useDropdownStore from "@/stores/main-menu"
|
||||
|
||||
import LanguageSwitcher from "@/components/LanguageSwitcher"
|
||||
import { useHandleKeyUp } from "@/hooks/useHandleKeyUp"
|
||||
import useLang from "@/hooks/useLang"
|
||||
|
||||
import HeaderLink from "../../HeaderLink"
|
||||
import TopLink from "../../TopLink"
|
||||
@@ -24,6 +26,7 @@ export default function MobileMenu({
|
||||
topLink,
|
||||
isLoggedIn,
|
||||
}: React.PropsWithChildren<MobileMenuProps>) {
|
||||
const lang = useLang()
|
||||
const intl = useIntl()
|
||||
const {
|
||||
toggleDropdown,
|
||||
@@ -83,7 +86,7 @@ export default function MobileMenu({
|
||||
>
|
||||
{children}
|
||||
<footer className={styles.footer}>
|
||||
<HeaderLink href="#" iconName={IconName.Search}>
|
||||
<HeaderLink href={findMyBooking[lang]} iconName={IconName.Search}>
|
||||
{intl.formatMessage({ id: "Find booking" })}
|
||||
</HeaderLink>
|
||||
<TopLink isLoggedIn={isLoggedIn} topLink={topLink} iconSize={20} />
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { findMyBooking } from "@/constants/myBooking"
|
||||
import { getHeader } from "@/lib/trpc/memoizedRequests"
|
||||
|
||||
import { auth } from "@/auth"
|
||||
@@ -5,6 +6,7 @@ import LanguageSwitcher from "@/components/LanguageSwitcher"
|
||||
import SkeletonShimmer from "@/components/SkeletonShimmer"
|
||||
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
||||
import { getIntl } from "@/i18n"
|
||||
import { getLang } from "@/i18n/serverContext"
|
||||
import { isValidSession } from "@/utils/session"
|
||||
|
||||
import HeaderLink from "../HeaderLink"
|
||||
@@ -18,6 +20,7 @@ export default async function TopMenu() {
|
||||
// cached
|
||||
const intl = await getIntl()
|
||||
// both preloaded
|
||||
const lang = getLang()
|
||||
const header = await getHeader()
|
||||
const session = await auth()
|
||||
const isLoggedIn = isValidSession(session)
|
||||
@@ -34,7 +37,7 @@ export default async function TopMenu() {
|
||||
<LanguageSwitcher type="desktopHeader" />
|
||||
|
||||
<Caption type="regular" color="textMediumContrast" asChild>
|
||||
<HeaderLink href="#" iconName={IconName.Search}>
|
||||
<HeaderLink href={findMyBooking[lang]} iconName={IconName.Search}>
|
||||
{intl.formatMessage({ id: "Find booking" })}
|
||||
</HeaderLink>
|
||||
</Caption>
|
||||
|
||||
@@ -24,3 +24,12 @@ export const myBooking = {
|
||||
sv: "https://test.scandichotels.se/hotelreservation/din-bokning",
|
||||
},
|
||||
}
|
||||
|
||||
export const findMyBooking = {
|
||||
da: "/da/hotelreservation/hent-booking",
|
||||
de: "/de/hotelreservation/mein-bereich",
|
||||
en: "/en/hotelreservation/get-booking",
|
||||
fi: "/fi/hotelreservation/hae-varaus",
|
||||
no: "/no/hotelreservation/get-booking",
|
||||
sv: "/sv/hotelreservation/hitta-bokning",
|
||||
}
|
||||
|
||||
@@ -107,6 +107,7 @@
|
||||
"By signing up you accept the Scandic Friends <termsAndConditionsLink>Terms and Conditions</termsAndConditionsLink>. Your membership is valid until further notice, and you can terminate your membership at any time by sending an email to Scandic's customer service": "Ved at tilmelde dig accepterer du Scandic Friends <termsAndConditionsLink>vilkår og betingelser</termsAndConditionsLink>. Dit medlemskab er gyldigt indtil videre, og du kan til enhver tid opsige dit medlemskab ved at sende en e-mail til Scandics kundeservice",
|
||||
"Cabaret seating": "Cabaret seating",
|
||||
"Campaign": "Kampagne",
|
||||
"Can't find your stay?": "Kan du ikke finde dit ophold?",
|
||||
"Cancel": "Afbestille",
|
||||
"Cancel stay": "Annuller ophold",
|
||||
"Cancellation cost": "Annulleret pris",
|
||||
@@ -227,9 +228,11 @@
|
||||
"Filter": "Filter",
|
||||
"Filter and sort": "Filtrer og sorter",
|
||||
"Filter by": "Filtrer efter",
|
||||
"Find": "Finde",
|
||||
"Find booking": "Find booking",
|
||||
"Find hotels": "Find hotel",
|
||||
"Find hotels and destinations": "Find hoteller og destinationer",
|
||||
"Find your stay": "Find dit ophold",
|
||||
"First name": "Fornavn",
|
||||
"First name can't contain any special characters": "Fornavn kan ikke indeholde specielle tegn",
|
||||
"First name is required": "Fornavn er påkrævet",
|
||||
@@ -469,6 +472,7 @@
|
||||
"Phone": "Telefon",
|
||||
"Phone is required": "Telefonnummer er påkrævet",
|
||||
"Phone number": "Telefonnummer",
|
||||
"Please call customer service at {phoneNr}": "Ring venligst til kundeservice på {phoneNr}",
|
||||
"Please enter a valid phone number": "Indtast venligst et gyldigt telefonnummer",
|
||||
"Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to confirm your account linking.": "Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to confirm your account linking.",
|
||||
"Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to unlink your accounts.": "Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to unlink your accounts.",
|
||||
@@ -653,6 +657,7 @@
|
||||
"View all": "Vis alle",
|
||||
"View all hotels in {country}": "Se alle hoteller i {country}",
|
||||
"View and buy add-ons": "View and buy add-ons",
|
||||
"View and manage your stay made on Scandic's website": "Se og administrer dit ophold på Scandics hjemmeside",
|
||||
"View as list": "Vis som liste",
|
||||
"View as map": "Vis som kort",
|
||||
"View room details": "View room details",
|
||||
|
||||
@@ -108,6 +108,7 @@
|
||||
"By signing up you accept the Scandic Friends <termsAndConditionsLink>Terms and Conditions</termsAndConditionsLink>. Your membership is valid until further notice, and you can terminate your membership at any time by sending an email to Scandic's customer service": "Mit Ihrer Anmeldung akzeptieren Sie die <termsAndConditionsLink>Allgemeinen Geschäftsbedingungen</termsAndConditionsLink> von Scandic Friends. Ihre Mitgliedschaft ist bis auf Weiteres gültig und Sie können sie jederzeit kündigen, indem Sie eine E-Mail an den Kundenservice von Scandic senden.",
|
||||
"Cabaret seating": "Cabaret seating",
|
||||
"Campaign": "Kampagne",
|
||||
"Can't find your stay?": "Sie können Ihren Aufenthalt nicht finden?",
|
||||
"Cancel": "Stornieren",
|
||||
"Cancel stay": "Stornieren",
|
||||
"Cancellation cost": "Stornierungskosten",
|
||||
@@ -228,9 +229,11 @@
|
||||
"Filter": "Filter",
|
||||
"Filter and sort": "Filtern und sortieren",
|
||||
"Filter by": "Filtern nach",
|
||||
"Find": "Finden",
|
||||
"Find booking": "Buchung finden",
|
||||
"Find hotels": "Hotels finden",
|
||||
"Find hotels and destinations": "Finden Sie Hotels und Reiseziele",
|
||||
"Find your stay": "Finden Sie Ihren Aufenthalt",
|
||||
"First name": "Vorname",
|
||||
"First name can't contain any special characters": "Der Vorname darf keine Sonderzeichen enthalten",
|
||||
"First name is required": "Vorname ist erforderlich",
|
||||
@@ -471,6 +474,7 @@
|
||||
"Phone": "Telefon",
|
||||
"Phone is required": "Telefon ist erforderlich",
|
||||
"Phone number": "Telefonnummer",
|
||||
"Please call customer service at {phoneNr}": "Bitte rufen Sie den Kundendienst unter {phoneNr} an",
|
||||
"Please enter a valid phone number": "Bitte geben Sie eine gültige Telefonnummer ein",
|
||||
"Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to confirm your account linking.": "Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to confirm your account linking.",
|
||||
"Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to unlink your accounts.": "Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to unlink your accounts.",
|
||||
@@ -654,6 +658,7 @@
|
||||
"View all": "Alle anzeigen",
|
||||
"View all hotels in {country}": "Alle Hotels in {country} anzeigen",
|
||||
"View and buy add-ons": "View and buy add-ons",
|
||||
"View and manage your stay made on Scandic's website": "Sehen und verwalten Sie Ihren Aufenthalt auf der Website von Scandic",
|
||||
"View as list": "Als Liste anzeigen",
|
||||
"View as map": "Als Karte anzeigen",
|
||||
"View room details": "View room details",
|
||||
|
||||
@@ -107,6 +107,7 @@
|
||||
"By signing up you accept the Scandic Friends <termsAndConditionsLink>Terms and Conditions</termsAndConditionsLink>. Your membership is valid until further notice, and you can terminate your membership at any time by sending an email to Scandic's customer service": "By signing up you accept the Scandic Friends <termsAndConditionsLink>Terms and Conditions</termsAndConditionsLink>. Your membership is valid until further notice, and you can terminate your membership at any time by sending an email to Scandic's customer service",
|
||||
"Cabaret seating": "Cabaret seating",
|
||||
"Campaign": "Campaign",
|
||||
"Can't find your stay?": "Can't find your stay?",
|
||||
"Cancel": "Cancel",
|
||||
"Cancel booking": "Cancel booking",
|
||||
"Cancel stay": "Cancel stay",
|
||||
@@ -229,9 +230,11 @@
|
||||
"Filter": "Filter",
|
||||
"Filter and sort": "Filter and sort",
|
||||
"Filter by": "Filter by",
|
||||
"Find": "Find",
|
||||
"Find booking": "Find booking",
|
||||
"Find hotels": "Find hotels",
|
||||
"Find hotels and destinations": "Find hotels and destinations",
|
||||
"Find your stay": "Find your stay",
|
||||
"First name": "First name",
|
||||
"First name can't contain any special characters": "First name can't contain any special characters",
|
||||
"First name is required": "First name is required",
|
||||
@@ -473,6 +476,7 @@
|
||||
"Phone": "Phone",
|
||||
"Phone is required": "Phone is required",
|
||||
"Phone number": "Phone number",
|
||||
"Please call customer service at {phoneNr}": "Please call customer service at {phoneNr}",
|
||||
"Please enter a valid phone number": "Please enter a valid phone number",
|
||||
"Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to confirm your account linking.": "Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to confirm your account linking.",
|
||||
"Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to unlink your accounts.": "Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to unlink your accounts.",
|
||||
@@ -659,6 +663,7 @@
|
||||
"View all": "View all",
|
||||
"View all hotels in {country}": "View all hotels in {country}",
|
||||
"View and buy add-ons": "View and buy add-ons",
|
||||
"View and manage your stay made on Scandic's website": "View and manage your stay made on Scandic's website",
|
||||
"View as list": "View as list",
|
||||
"View as map": "View as map",
|
||||
"View room details": "View room details",
|
||||
|
||||
@@ -106,6 +106,7 @@
|
||||
"By signing up you accept the Scandic Friends <termsAndConditionsLink>Terms and Conditions</termsAndConditionsLink>. Your membership is valid until further notice, and you can terminate your membership at any time by sending an email to Scandic's customer service": "Rekisteröitymällä hyväksyt Scandic Friendsin <termsAndConditionsLink>käyttöehdot</termsAndConditionsLink>. Jäsenyytesi on voimassa toistaiseksi ja voit lopettaa jäsenyytesi milloin tahansa lähettämällä sähköpostia Scandicin asiakaspalveluun",
|
||||
"Cabaret seating": "Cabaret seating",
|
||||
"Campaign": "Kampanja",
|
||||
"Can't find your stay?": "Etkö löydä majoitusta?",
|
||||
"Cancel": "Peruuttaa",
|
||||
"Cancel stay": "Peruuta majoitus",
|
||||
"Cancellation cost": "Peruutusmaksu",
|
||||
@@ -227,9 +228,11 @@
|
||||
"Filter": "Suodatin",
|
||||
"Filter and sort": "Suodata ja lajittele",
|
||||
"Filter by": "Suodatusperuste",
|
||||
"Find": "Löytää",
|
||||
"Find booking": "Etsi varaus",
|
||||
"Find hotels": "Etsi hotelleja",
|
||||
"Find hotels and destinations": "Etsi hotelleja ja kohteita",
|
||||
"Find your stay": "Etsi majoitus",
|
||||
"First name": "Etunimi",
|
||||
"First name can't contain any special characters": "Etunimi ei voi sisältää erikoismerkkejä",
|
||||
"First name is required": "Etunimi vaaditaan",
|
||||
@@ -470,6 +473,7 @@
|
||||
"Phone": "Puhelin",
|
||||
"Phone is required": "Puhelin vaaditaan",
|
||||
"Phone number": "Puhelinnumero",
|
||||
"Please call customer service at {phoneNr}": "Soita asiakaspalveluun numeroon {phoneNr}",
|
||||
"Please enter a valid phone number": "Ole hyvä ja näppäile voimassaoleva puhelinnumero",
|
||||
"Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to confirm your account linking.": "Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to confirm your account linking.",
|
||||
"Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to unlink your accounts.": "Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to unlink your accounts.",
|
||||
@@ -654,6 +658,7 @@
|
||||
"View all": "Näytä kaikki",
|
||||
"View all hotels in {country}": "Näytä kaikki hotellit maassa {country}",
|
||||
"View and buy add-ons": "View and buy add-ons",
|
||||
"View and manage your stay made on Scandic's website": "Tarkastele ja hallinnoi yöpymistäsi Scandicin verkkosivuilla",
|
||||
"View as list": "Näytä listana",
|
||||
"View as map": "Näytä kartalla",
|
||||
"View room details": "View room details",
|
||||
|
||||
@@ -106,6 +106,7 @@
|
||||
"By signing up you accept the Scandic Friends <termsAndConditionsLink>Terms and Conditions</termsAndConditionsLink>. Your membership is valid until further notice, and you can terminate your membership at any time by sending an email to Scandic's customer service": "Ved å registrere deg godtar du Scandic Friends <termsAndConditionsLink>vilkår og betingelser</termsAndConditionsLink>. Medlemskapet ditt er gyldig inntil videre, og du kan si opp medlemskapet ditt når som helst ved å sende en e-post til Scandics kundeservice",
|
||||
"Cabaret seating": "Cabaret seating",
|
||||
"Campaign": "Kampanje",
|
||||
"Can't find your stay?": "Finner du ikke oppholdet ditt?",
|
||||
"Cancel": "Avbryt",
|
||||
"Cancel stay": "Avbryt opphold",
|
||||
"Cancellation cost": "Annulleret pris",
|
||||
@@ -226,9 +227,11 @@
|
||||
"Filter": "Filter",
|
||||
"Filter and sort": "Filtrer og sorter",
|
||||
"Filter by": "Filtrer etter",
|
||||
"Find": "Finne",
|
||||
"Find booking": "Finn booking",
|
||||
"Find hotels": "Finn hotell",
|
||||
"Find hotels and destinations": "Finn hoteller og destinasjoner",
|
||||
"Find your stay": "Finn ditt opphold",
|
||||
"First name": "Fornavn",
|
||||
"First name can't contain any special characters": "Fornavn kan ikke inneholde spesielle tegn",
|
||||
"First name is required": "Fornavn kreves",
|
||||
@@ -467,6 +470,7 @@
|
||||
"Phone": "Telefon",
|
||||
"Phone is required": "Telefon kreves",
|
||||
"Phone number": "Telefonnummer",
|
||||
"Please call customer service at {phoneNr}": "Ring kundeservice på {phoneNr}",
|
||||
"Please enter a valid phone number": "Vennligst oppgi et gyldig telefonnummer",
|
||||
"Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to confirm your account linking.": "Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to confirm your account linking.",
|
||||
"Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to unlink your accounts.": "Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to unlink your accounts.",
|
||||
@@ -650,6 +654,7 @@
|
||||
"View all": "Vis alle",
|
||||
"View all hotels in {country}": "Se alle hotellene i {country}",
|
||||
"View and buy add-ons": "View and buy add-ons",
|
||||
"View and manage your stay made on Scandic's website": "Se og administrer ditt opphold på Scandics nettside",
|
||||
"View as list": "Vis som liste",
|
||||
"View as map": "Vis som kart",
|
||||
"View room details": "View room details",
|
||||
|
||||
@@ -106,6 +106,7 @@
|
||||
"By signing up you accept the Scandic Friends <termsAndConditionsLink>Terms and Conditions</termsAndConditionsLink>. Your membership is valid until further notice, and you can terminate your membership at any time by sending an email to Scandic's customer service": "Genom att registrera dig accepterar du Scandic Friends <termsAndConditionsLink>Användarvillkor</termsAndConditionsLink>. Ditt medlemskap gäller tills vidare och du kan när som helst säga upp ditt medlemskap genom att skicka ett mejl till Scandics kundtjänst",
|
||||
"Cabaret seating": "Cabaret seating",
|
||||
"Campaign": "Kampanj",
|
||||
"Can't find your stay?": "Hittar du inte din vistelse?",
|
||||
"Cancel": "Avbryt",
|
||||
"Cancel stay": "Avboka vistelse",
|
||||
"Cancellation cost": "Avbokningskostnad",
|
||||
@@ -226,9 +227,11 @@
|
||||
"Filter": "Filter",
|
||||
"Filter and sort": "Filtrera och sortera",
|
||||
"Filter by": "Filtrera på",
|
||||
"Find": "Hitta",
|
||||
"Find booking": "Hitta bokning",
|
||||
"Find hotels": "Hitta hotell",
|
||||
"Find hotels and destinations": "Hitta hotell och destinationer",
|
||||
"Find your stay": "Hitta din vistelse",
|
||||
"First name": "Förnamn",
|
||||
"First name can't contain any special characters": "Förnamn får inte innehålla några specialtecken",
|
||||
"First name is required": "Förnamn är obligatoriskt",
|
||||
@@ -467,6 +470,7 @@
|
||||
"Phone": "Telefon",
|
||||
"Phone is required": "Telefonnummer är obligatorisk",
|
||||
"Phone number": "Telefonnummer",
|
||||
"Please call customer service at {phoneNr}": "Ring kundtjänst på {phoneNr}",
|
||||
"Please enter a valid phone number": "Var vänlig och ange ett giltigt telefonnummer",
|
||||
"Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to confirm your account linking.": "Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to confirm your account linking.",
|
||||
"Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to unlink your accounts.": "Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to unlink your accounts.",
|
||||
@@ -650,6 +654,7 @@
|
||||
"View all": "Visa alla",
|
||||
"View all hotels in {country}": "Visa alla hotell i {country}",
|
||||
"View and buy add-ons": "View and buy add-ons",
|
||||
"View and manage your stay made on Scandic's website": "Se och hantera din vistelse gjord på Scandics hemsida",
|
||||
"View as list": "Visa som lista",
|
||||
"View as map": "Visa som karta",
|
||||
"View room details": "View room details",
|
||||
|
||||
@@ -54,7 +54,7 @@ const nextConfig = {
|
||||
|
||||
output: "standalone",
|
||||
|
||||
webpack: function (config, options) {
|
||||
webpack: function (config) {
|
||||
config.module.rules.push(
|
||||
{
|
||||
test: /\.(graphql|gql)/,
|
||||
@@ -273,6 +273,31 @@ const nextConfig = {
|
||||
destination:
|
||||
"/:lang/hotelreservation/payment-callback?status=:status",
|
||||
},
|
||||
// Find my booking
|
||||
{
|
||||
source: "/en/hotelreservation/get-booking",
|
||||
destination: "/en/hotelreservation/get-booking",
|
||||
},
|
||||
{
|
||||
source: "/no/hotelreservation/get-booking",
|
||||
destination: "/no/hotelreservation/get-booking",
|
||||
},
|
||||
{
|
||||
source: "/da/hotelreservation/hent-booking",
|
||||
destination: "/da/hotelreservation/get-booking",
|
||||
},
|
||||
{
|
||||
source: "/de/hotelreservation/mein-bereich",
|
||||
destination: "/de/hotelreservation/get-booking",
|
||||
},
|
||||
{
|
||||
source: "/fi/hotelreservation/hae-varaus",
|
||||
destination: "/fi/hotelreservation/get-booking",
|
||||
},
|
||||
{
|
||||
source: "/sv/hotelreservation/hitta-bokning",
|
||||
destination: "/sv/hotelreservation/get-booking",
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
|
||||
@@ -108,6 +108,11 @@ export const cancelBookingInput = z.object({
|
||||
language: z.nativeEnum(Lang).transform((val) => langToApiLang[val]),
|
||||
})
|
||||
|
||||
export const createRefIdInput = z.object({
|
||||
confirmationNumber: z.string(),
|
||||
lastName: z.string(),
|
||||
})
|
||||
|
||||
// Query
|
||||
const confirmationNumberInput = z.object({
|
||||
confirmationNumber: z.string(),
|
||||
|
||||
@@ -10,7 +10,12 @@ import {
|
||||
} from "@/server/trpc"
|
||||
|
||||
import { getHotel } from "../hotels/query"
|
||||
import { bookingConfirmationInput, getBookingStatusInput } from "./input"
|
||||
import encryptValue from "../utils/encryptValue"
|
||||
import {
|
||||
bookingConfirmationInput,
|
||||
createRefIdInput,
|
||||
getBookingStatusInput,
|
||||
} from "./input"
|
||||
import { bookingConfirmationSchema, createBookingSchema } from "./output"
|
||||
import { getBookedHotelRoom } from "./utils"
|
||||
|
||||
@@ -228,4 +233,14 @@ export const bookingQueryRouter = router({
|
||||
|
||||
return verifiedData.data
|
||||
}),
|
||||
createRefId: serviceProcedure
|
||||
.input(createRefIdInput)
|
||||
.mutation(async function ({ input }) {
|
||||
const { confirmationNumber, lastName } = input
|
||||
const encryptedRefId = encryptValue(`${confirmationNumber},${lastName}`)
|
||||
|
||||
return {
|
||||
refId: encryptedRefId,
|
||||
}
|
||||
}),
|
||||
})
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { metrics } from "@opentelemetry/api"
|
||||
|
||||
import { Lang } from "@/constants/languages"
|
||||
import { findMyBooking } from "@/constants/myBooking"
|
||||
import { baseUrls } from "@/constants/routes/baseUrls"
|
||||
import { batchRequest } from "@/lib/graphql/batchRequest"
|
||||
import {
|
||||
@@ -179,6 +180,18 @@ export const languageSwitcherQueryRouter = router({
|
||||
}
|
||||
|
||||
if (!uid || !lang) {
|
||||
// we have pages that are not currently routed within contentstack context,
|
||||
// therefor this fix is needed for some of these pages
|
||||
if (input && Object.values(findMyBooking).includes(input.pathName)) {
|
||||
const urls: Record<string, { url: string }> = {}
|
||||
return {
|
||||
lang,
|
||||
urls: Object.entries(findMyBooking).reduce((acc, [lang, url]) => {
|
||||
acc[lang] = { url }
|
||||
return urls
|
||||
}, urls),
|
||||
}
|
||||
}
|
||||
return { lang: lang, urls: baseUrls }
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
.form {
|
||||
box-shadow: 0 0 14px 6px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.form > div {
|
||||
padding: var(--Spacing-x3);
|
||||
}
|
||||
|
||||
.inputs {
|
||||
display: grid;
|
||||
gap: var(--Spacing-x3);
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
.inputs {
|
||||
grid-template-areas:
|
||||
"a a"
|
||||
"b c"
|
||||
"d d";
|
||||
}
|
||||
|
||||
.inputs > div:nth-child(1) {
|
||||
grid-area: a;
|
||||
}
|
||||
.inputs > div:nth-child(2) {
|
||||
grid-area: b;
|
||||
}
|
||||
.inputs > div:nth-child(3) {
|
||||
grid-area: c;
|
||||
}
|
||||
.inputs > div:nth-child(4) {
|
||||
grid-area: d;
|
||||
}
|
||||
}
|
||||
|
||||
.buttons {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-top: 1px solid var(--Base-Border-Subtle);
|
||||
gap: var(--Spacing-x2);
|
||||
}
|
||||
|
||||
.buttons > button {
|
||||
width: 140px;
|
||||
}
|
||||
|
||||
.footnote {
|
||||
display: grid;
|
||||
gap: var(--Spacing-x-half);
|
||||
}
|
||||
121
components/HotelReservation/FindMyBooking/index.tsx
Normal file
121
components/HotelReservation/FindMyBooking/index.tsx
Normal file
@@ -0,0 +1,121 @@
|
||||
"use client"
|
||||
|
||||
import { zodResolver } from "@hookform/resolvers/zod"
|
||||
import { useRouter } from "next/navigation"
|
||||
import { FormProvider, useForm } from "react-hook-form"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { trpc } from "@/lib/trpc/client"
|
||||
|
||||
import Button from "@/components/TempDesignSystem/Button"
|
||||
import Input from "@/components/TempDesignSystem/Form/Input"
|
||||
import Link from "@/components/TempDesignSystem/Link"
|
||||
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
||||
import Title from "@/components/TempDesignSystem/Text/Title"
|
||||
import useLang from "@/hooks/useLang"
|
||||
|
||||
import { type FindMyBookingFormSchema, findMyBookingFormSchema } from "./schema"
|
||||
|
||||
import styles from "./findMyBooking.module.css"
|
||||
|
||||
export default function Form() {
|
||||
const router = useRouter()
|
||||
const intl = useIntl()
|
||||
const lang = useLang()
|
||||
const form = useForm<FindMyBookingFormSchema>({
|
||||
defaultValues: {
|
||||
reservationNumber: "",
|
||||
firstName: "",
|
||||
lastName: "",
|
||||
email: "",
|
||||
},
|
||||
resolver: zodResolver(findMyBookingFormSchema),
|
||||
mode: "all",
|
||||
criteriaMode: "all",
|
||||
reValidateMode: "onChange",
|
||||
})
|
||||
|
||||
const update = trpc.booking.createRefId.useMutation({
|
||||
onSuccess: (result) => {
|
||||
router.push(
|
||||
`/${lang}/hotelreservation/my-stay/${encodeURIComponent(result.refId)}`
|
||||
)
|
||||
},
|
||||
onError: (error) => {
|
||||
console.log("Failed to create ref id", error)
|
||||
},
|
||||
})
|
||||
|
||||
async function onSubmit(data: FindMyBookingFormSchema) {
|
||||
const value = new URLSearchParams(data).toString()
|
||||
document.cookie = `bv=${value}; Path=/; Max-Age=30; Secure; SameSite=Strict`
|
||||
update.mutate({
|
||||
confirmationNumber: data.reservationNumber,
|
||||
lastName: data.lastName,
|
||||
})
|
||||
}
|
||||
return (
|
||||
<FormProvider {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)} className={styles.form}>
|
||||
<div>
|
||||
<Title level="h2">
|
||||
{intl.formatMessage({ id: "Find your stay" })}
|
||||
</Title>
|
||||
<Body>
|
||||
{intl.formatMessage({
|
||||
id: "View and manage your stay made on Scandic's website",
|
||||
})}
|
||||
</Body>
|
||||
</div>
|
||||
<div className={styles.inputs}>
|
||||
<Input
|
||||
label="Reservation number"
|
||||
name="reservationNumber"
|
||||
placeholder="XXXXXX"
|
||||
registerOptions={{ required: true }}
|
||||
/>
|
||||
<Input
|
||||
label="First name"
|
||||
name="firstName"
|
||||
placeholder="Anna"
|
||||
registerOptions={{ required: true }}
|
||||
/>
|
||||
<Input
|
||||
label="Last name"
|
||||
name="lastName"
|
||||
placeholder="Andersson"
|
||||
registerOptions={{ required: true }}
|
||||
/>
|
||||
<Input
|
||||
label="Email"
|
||||
name="email"
|
||||
placeholder="anna@scandichotels.com"
|
||||
registerOptions={{ required: true }}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.buttons}>
|
||||
<div className={styles.footnote}>
|
||||
<Caption type="bold">
|
||||
{intl.formatMessage({ id: "Can't find your stay?" })}
|
||||
</Caption>
|
||||
<Caption>
|
||||
{intl.formatMessage<React.ReactNode>(
|
||||
{ id: "Please call customer service at {phoneNr}" },
|
||||
{ phoneNr: <Link href="tel:XXXXXX">XXXXXX</Link> }
|
||||
)}
|
||||
</Caption>
|
||||
</div>
|
||||
<Button
|
||||
type="submit"
|
||||
intent="primary"
|
||||
theme="base"
|
||||
disabled={form.formState.isSubmitting}
|
||||
>
|
||||
{intl.formatMessage({ id: "Find" })}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</FormProvider>
|
||||
)
|
||||
}
|
||||
15
components/HotelReservation/FindMyBooking/schema.ts
Normal file
15
components/HotelReservation/FindMyBooking/schema.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { z } from "zod"
|
||||
|
||||
export const findMyBookingFormSchema = z.object({
|
||||
reservationNumber: z.string(),
|
||||
firstName: z.string().max(250).trim().min(1, {
|
||||
message: "First name is required",
|
||||
}),
|
||||
lastName: z.string().max(250).trim().min(1, {
|
||||
message: "Last name is required",
|
||||
}),
|
||||
email: z.string().max(250).email(),
|
||||
})
|
||||
|
||||
export interface FindMyBookingFormSchema
|
||||
extends z.output<typeof findMyBookingFormSchema> {}
|
||||
Reference in New Issue
Block a user