diff --git a/apps/scandic-web/app/[lang]/(live)/(public)/hotelreservation/(standard)/details/page.tsx b/apps/scandic-web/app/[lang]/(live)/(public)/hotelreservation/(standard)/details/page.tsx
index 3feed3122..3bbf2990a 100644
--- a/apps/scandic-web/app/[lang]/(live)/(public)/hotelreservation/(standard)/details/page.tsx
+++ b/apps/scandic-web/app/[lang]/(live)/(public)/hotelreservation/(standard)/details/page.tsx
@@ -119,6 +119,7 @@ export default async function DetailsPage(
-
+
-
+
diff --git a/apps/scandic-web/components/HotelReservation/EnterDetails/Details/Multiroom/index.tsx b/apps/scandic-web/components/HotelReservation/EnterDetails/Details/Multiroom/index.tsx
index 2cfadd2c4..d71c903fd 100644
--- a/apps/scandic-web/components/HotelReservation/EnterDetails/Details/Multiroom/index.tsx
+++ b/apps/scandic-web/components/HotelReservation/EnterDetails/Details/Multiroom/index.tsx
@@ -1,5 +1,6 @@
"use client"
import { zodResolver } from "@hookform/resolvers/zod"
+import { parsePhoneNumberFromString } from "libphonenumber-js"
import { useCallback, useEffect, useMemo } from "react"
import { FormProvider, useForm } from "react-hook-form"
import { useIntl } from "react-intl"
@@ -52,6 +53,14 @@ export default function Details() {
[idx, rooms]
)
+ const initialPhoneNumber = initialData.phoneNumber
+ const parsedInitialPhoneNumber = initialPhoneNumber
+ ? parsePhoneNumberFromString(initialPhoneNumber)
+ : undefined
+ let initialPhoneNumberCC = initialData.phoneNumberCC
+ if (parsedInitialPhoneNumber && !initialPhoneNumberCC) {
+ initialPhoneNumberCC = parsedInitialPhoneNumber.country ?? ""
+ }
const methods = useForm({
defaultValues: {
countryCode: initialData.countryCode,
@@ -60,7 +69,10 @@ export default function Details() {
join: initialData.join,
lastName: initialData.lastName,
membershipNo: initialData.membershipNo,
- phoneNumber: initialData.phoneNumber,
+ phoneNumber: parsedInitialPhoneNumber?.isValid()
+ ? parsedInitialPhoneNumber?.number
+ : initialPhoneNumber,
+ phoneNumberCC: initialPhoneNumberCC,
specialRequest: {
comment: room.specialRequest.comment,
},
@@ -89,7 +101,7 @@ export default function Details() {
}
}, [handleSubmit, isValid, setIncomplete, updateDetails])
- useEffect(updateDetailsStore, [methods.formState.isValid, updateDetailsStore])
+ useEffect(updateDetailsStore, [updateDetailsStore])
// Trigger validation of the room manually when another room changes its data.
// Only do it if the field has a value, to avoid error states before the user
diff --git a/apps/scandic-web/components/HotelReservation/EnterDetails/Details/Multiroom/schema.ts b/apps/scandic-web/components/HotelReservation/EnterDetails/Details/Multiroom/schema.ts
index 3c430bafa..4db659b33 100644
--- a/apps/scandic-web/components/HotelReservation/EnterDetails/Details/Multiroom/schema.ts
+++ b/apps/scandic-web/components/HotelReservation/EnterDetails/Details/Multiroom/schema.ts
@@ -50,6 +50,7 @@ export function getMultiroomDetailsSchema(
multiroomErrors.PHONE_REQUIRED,
multiroomErrors.PHONE_REQUESTED
),
+ phoneNumberCC: z.string(),
membershipNo: z
.string()
.optional()
diff --git a/apps/scandic-web/components/HotelReservation/EnterDetails/Details/RoomOne/index.tsx b/apps/scandic-web/components/HotelReservation/EnterDetails/Details/RoomOne/index.tsx
index 9a93d8e26..39fc77255 100644
--- a/apps/scandic-web/components/HotelReservation/EnterDetails/Details/RoomOne/index.tsx
+++ b/apps/scandic-web/components/HotelReservation/EnterDetails/Details/RoomOne/index.tsx
@@ -1,5 +1,6 @@
"use client"
import { zodResolver } from "@hookform/resolvers/zod"
+import { parsePhoneNumberFromString } from "libphonenumber-js"
import { useCallback, useEffect } from "react"
import { FormProvider, useForm } from "react-hook-form"
import { useIntl } from "react-intl"
@@ -10,8 +11,6 @@ import SpecialRequests from "@/components/HotelReservation/EnterDetails/Details/
import CountrySelect from "@/components/TempDesignSystem/Form/Country"
import Input from "@/components/TempDesignSystem/Form/Input"
import Phone from "@/components/TempDesignSystem/Form/Phone"
-import PhoneCountryCode from "@/components/TempDesignSystem/Form/Phone/CountryCode"
-import PhoneNumber from "@/components/TempDesignSystem/Form/Phone/Number"
import Footnote from "@/components/TempDesignSystem/Text/Footnote"
import { useRoomContext } from "@/contexts/Details/Room"
@@ -45,6 +44,14 @@ export default function Details({ user }: DetailsProps) {
const memberRate = "member" in room.roomRate ? room.roomRate.member : null
+ const initialPhoneNumber = user?.phoneNumber || initialData.phoneNumber
+ const parsedInitialPhoneNumber = initialPhoneNumber
+ ? parsePhoneNumberFromString(initialPhoneNumber)
+ : undefined
+ let initialPhoneNumberCC = initialData.phoneNumberCC
+ if (parsedInitialPhoneNumber && !initialPhoneNumberCC) {
+ initialPhoneNumberCC = parsedInitialPhoneNumber.country ?? ""
+ }
const methods = useForm({
defaultValues: {
countryCode: user?.address?.countryCode || initialData.countryCode,
@@ -55,7 +62,10 @@ export default function Details({ user }: DetailsProps) {
join: initialData.join,
lastName: user?.lastName || initialData.lastName,
membershipNo: initialData.membershipNo,
- phoneNumber: user?.phoneNumber || initialData.phoneNumber,
+ phoneNumber: parsedInitialPhoneNumber?.isValid()
+ ? parsedInitialPhoneNumber?.number
+ : initialPhoneNumber,
+ phoneNumberCC: initialPhoneNumberCC,
zipCode: "zipCode" in initialData ? initialData.zipCode : undefined,
specialRequest: {
comment: room.specialRequest.comment,
@@ -92,7 +102,7 @@ export default function Details({ user }: DetailsProps) {
}
}, [handleSubmit, isValid, onSubmit, setIncomplete])
- useEffect(updateDetailsStore, [methods.formState.isValid, updateDetailsStore])
+ useEffect(updateDetailsStore, [updateDetailsStore])
return (
@@ -161,10 +171,6 @@ export default function Details({ user }: DetailsProps) {
readOnly={!!user}
registerOptions={{ required: true, onBlur: updateDetailsStore }}
/>
-
{user ? null : (
-
{
+ // If not checked trigger(name) forces validation on mount
+ // which shows error message before user even can see the form
+ if (value.inputValue) {
+ setValue(name, value.phone)
+ trigger(name)
+ } else {
+ setValue(name, "")
+ }
+ },
+ })
+
+ function handleSelectCountry(value: ParsedCountry) {
+ setCountry(value.iso2)
+ }
+
+ function handleChange(evt: ChangeEvent) {
+ handlePhoneValueChange(evt)
+ }
+
+ return (
+
+
(
+
+
+ {intl.formatMessage({
+ defaultMessage: "Country code",
+ })}
+
+
+ {props.children}
+
+
+
+
+
+
+ )}
+ />
+
+
+
+
+
+ )
+}
diff --git a/apps/scandic-web/components/TempDesignSystem/Form/DeprecatedPhone/phone.module.css b/apps/scandic-web/components/TempDesignSystem/Form/DeprecatedPhone/phone.module.css
new file mode 100644
index 000000000..b0521c7d4
--- /dev/null
+++ b/apps/scandic-web/components/TempDesignSystem/Form/DeprecatedPhone/phone.module.css
@@ -0,0 +1,108 @@
+.phone {
+ display: grid;
+ grid-template-columns: 1fr;
+ gap: var(--Spacing-x2);
+
+ --react-international-phone-background-color: var(--Main-Grey-White);
+ --react-international-phone-border-color: var(--Scandic-Beige-40);
+ --react-international-phone-dropdown-preferred-list-divider-color: var(
+ --Scandic-Brand-Pale-Peach
+ );
+ --react-international-phone-selected-dropdown-item-background-color: var(
+ --Scandic-Blue-00
+ );
+ --react-international-phone-text-color: var(--Main-Grey-100);
+
+ --react-international-phone-dropdown-preferred-list-divider-margin: 8px;
+
+ --react-international-phone-height: 60px;
+ --react-international-phone-dropdown-top: calc(
+ var(--react-international-phone-height) + var(--Spacing-x1)
+ );
+ --react-international-phone-dial-code-preview-font-size: var(
+ --typography-Body-Regular-fontSize
+ );
+}
+
+@media (min-width: 385px) {
+ .phone {
+ grid-template-columns: minmax(124px, 164px) 1fr;
+ }
+}
+
+.phone:has(.input:active, .input:focus) {
+ --react-international-phone-border-color: var(--Scandic-Blue-90);
+}
+
+.phone :global(.react-international-phone-country-selector-dropdown) {
+ background: var(--Main-Grey-White);
+ border-radius: var(--Corner-radius-md);
+ box-shadow: 0px 4px 24px 0px rgba(0, 0, 0, 0.08);
+ gap: var(--Spacing-x1);
+ outline: none;
+ padding: var(--Spacing-x2);
+}
+
+.phone
+ :global(.react-international-phone-country-selector-dropdown__list-item) {
+ border-radius: var(--Corner-radius-md);
+ padding: var(--Spacing-x1) var(--Spacing-x1) var(--Spacing-x1)
+ var(--Spacing-x-one-and-half);
+}
+
+.phone
+ :global(.react-international-phone-country-selector-button__button-content) {
+ align-self: center;
+}
+
+.select {
+ align-content: center;
+ background-color: var(--Main-Grey-White);
+ border-color: var(--Scandic-Beige-40);
+ border-style: solid;
+ border-width: 1px;
+ border-radius: var(--Corner-radius-md);
+ display: grid;
+ gap: var(--Spacing-x-half);
+ grid-template-rows: auto auto;
+ height: 60px;
+ padding: var(--Spacing-x1) var(--Spacing-x2);
+ transition: border-color 200ms ease;
+}
+
+.select {
+ width: 100%;
+}
+
+.select[aria-expanded="true"] .chevron {
+ transform: rotate(180deg);
+}
+
+.selectContainer {
+ background-color: var(--Main-Grey-White);
+ border: none;
+ display: grid;
+ gap: var(--Spacing-x1);
+ grid-template-columns: auto 1fr auto;
+ height: 18px;
+ justify-content: flex-start;
+ order: 2;
+}
+
+.arrow {
+ display: none;
+}
+
+.flag {
+ height: 18px;
+ margin: 0;
+ width: 18px;
+}
+
+.select .dialCode {
+ border: none;
+ color: var(--UI-Text-High-contrast);
+ line-height: 1;
+ justify-self: flex-start;
+ padding: 0;
+}
diff --git a/apps/scandic-web/components/TempDesignSystem/Form/Phone/CountryCode.tsx b/apps/scandic-web/components/TempDesignSystem/Form/Phone/CountryCode.tsx
deleted file mode 100644
index 519e8fd1c..000000000
--- a/apps/scandic-web/components/TempDesignSystem/Form/Phone/CountryCode.tsx
+++ /dev/null
@@ -1,25 +0,0 @@
-import { useIntl } from "react-intl"
-
-import { Select } from "@scandic-hotels/design-system/Select"
-
-import { countryPhoneCodes } from "@/constants/countryPhoneCodes"
-
-export default function PhoneCountryCode() {
- const intl = useIntl()
- const countries = Object.entries(countryPhoneCodes).map(
- ([countryName, countryCode]) => ({
- label: countryName,
- value: countryCode,
- })
- )
- console.log("length: ", countries.length)
- return (
-
- )
-}
diff --git a/apps/scandic-web/components/TempDesignSystem/Form/Phone/Number.tsx b/apps/scandic-web/components/TempDesignSystem/Form/Phone/Number.tsx
deleted file mode 100644
index 3a742aebc..000000000
--- a/apps/scandic-web/components/TempDesignSystem/Form/Phone/Number.tsx
+++ /dev/null
@@ -1,16 +0,0 @@
-import { useIntl } from "react-intl"
-
-import Input from "@/components/TempDesignSystem/Form/Input"
-
-export default function PhoneNumber() {
- const intl = useIntl()
- return (
-
- )
-}
diff --git a/apps/scandic-web/components/TempDesignSystem/Form/Phone/index.tsx b/apps/scandic-web/components/TempDesignSystem/Form/Phone/index.tsx
index f6021b64c..a795b93e0 100644
--- a/apps/scandic-web/components/TempDesignSystem/Form/Phone/index.tsx
+++ b/apps/scandic-web/components/TempDesignSystem/Form/Phone/index.tsx
@@ -1,12 +1,8 @@
"use client"
import "react-international-phone/style.css"
-import {
- isValidPhoneNumber,
- parsePhoneNumberWithError,
-} from "libphonenumber-js"
import { TextField } from "react-aria-components"
-import { useController, useFormContext, useWatch } from "react-hook-form"
+import { useFormContext, useWatch } from "react-hook-form"
import {
CountrySelector,
DialCodePreview,
@@ -17,6 +13,8 @@ import { useIntl } from "react-intl"
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
+import { getDefaultCountryFromLang } from "@/constants/languages"
+
import ErrorMessage from "@/components/TempDesignSystem/Form/ErrorMessage"
import AriaInputWithLabel from "@/components/TempDesignSystem/Form/Input/AriaInputWithLabel"
import Label from "@/components/TempDesignSystem/Form/Label"
@@ -25,17 +23,12 @@ import useLang from "@/hooks/useLang"
import styles from "./phone.module.css"
-import type { ChangeEvent } from "react"
-
-import type {
- LowerCaseCountryCode,
- PhoneProps,
-} from "@/types/components/form/phone"
-import type { Lang } from "@/constants/languages"
+import type { PhoneProps } from "@/types/components/form/phone"
export default function Phone({
ariaLabel = "Phone number input",
className = "",
+ countrySelectorName = "phoneNumberCC",
disabled = false,
label,
name = "phoneNumber",
@@ -47,48 +40,27 @@ export default function Phone({
}: PhoneProps) {
const intl = useIntl()
const lang = useLang()
- const { control, setValue, trigger } = useFormContext()
- const phone = useWatch({ name })
+ const { formState, getFieldState, register, setValue } = useFormContext()
+ const fieldState = getFieldState(name)
+ const [phoneNumber, phoneNumberCC] = useWatch({
+ name: [name, countrySelectorName],
+ }) as [string, string]
- const { field, fieldState, formState } = useController({
- control,
- disabled,
- name,
- rules: registerOptions,
+ const { country, setCountry } = usePhoneInput({
+ defaultCountry: phoneNumberCC
+ ? phoneNumberCC
+ : getDefaultCountryFromLang(lang),
})
- const defaultPhoneNumber = formState.defaultValues?.phoneNumber ?? ""
-
- // If defaultPhoneNumber exists and is valid, parse it to get the country code,
- // otherwise set the default country from the lang.
- const defaultCountry = isValidPhoneNumber(defaultPhoneNumber)
- ? parsePhoneNumberWithError(defaultPhoneNumber).country?.toLowerCase()
- : getDefaultCountryFromLang(lang)
-
- const { country, handlePhoneValueChange, inputValue, setCountry } =
- usePhoneInput({
- defaultCountry,
- disableDialCodeAndPrefix: true,
- forceDialCode: true,
- value: phone,
- onChange: (value) => {
- // If not checked trigger(name) forces validation on mount
- // which shows error message before user even can see the form
- if (value.inputValue) {
- setValue(name, value.phone)
- trigger(name)
- } else {
- setValue(name, "")
- }
- },
- })
-
function handleSelectCountry(value: ParsedCountry) {
setCountry(value.iso2)
- }
-
- function handleChange(evt: ChangeEvent) {
- handlePhoneValueChange(evt)
+ setValue(countrySelectorName, value.iso2, {
+ shouldDirty: true,
+ shouldValidate: true,
+ })
+ if (registerOptions.onBlur) {
+ registerOptions.onBlur(value)
+ }
}
return (
@@ -134,41 +106,24 @@ export default function Phone({
/>
-
+
)
}
-
-function getDefaultCountryFromLang(lang: Lang): LowerCaseCountryCode {
- const countryMap: Record = {
- sv: "se",
- da: "dk",
- fi: "fi",
- no: "no",
- de: "de",
- en: "se", // Default to Sweden for English
- }
- return countryMap[lang] || "se"
-}
diff --git a/apps/scandic-web/constants/countryPhoneCodes.ts b/apps/scandic-web/constants/countryPhoneCodes.ts
deleted file mode 100644
index 1a3443daa..000000000
--- a/apps/scandic-web/constants/countryPhoneCodes.ts
+++ /dev/null
@@ -1,252 +0,0 @@
-export const countryPhoneCodes = {
- Sweden: "+46",
- Norway: "+47",
- Denmark: "+45",
- Finland: "+358",
- Germany: "+49",
- Afghanistan: "+93",
- Albania: "+355",
- Algeria: "+213",
- "American Samoa": "+1684",
- Andorra: "+376",
- Angola: "+244",
- Anguilla: "+1264",
- Antarctica: "+672",
- "Antigua and Barbuda": "+1268",
- Argentina: "+54",
- Armenia: "+374",
- Aruba: "+297",
- Australia: "+61",
- Austria: "+43",
- Azerbaijan: "+994",
- Bahamas: "+1242",
- Bahrain: "+973",
- Bangladesh: "+880",
- Barbados: "+1246",
- Belarus: "+375",
- Belgium: "+32",
- Belize: "+501",
- Benin: "+229",
- Bermuda: "+1441",
- Bhutan: "+975",
- Bolivia: "+591",
- Bonaire: "+599",
- "Bosnia and Herzegovina": "+387",
- Botswana: "+267",
- "Bouvet Island": "+47",
- Brazil: "+55",
- "British Indian Ocean Territory": "+246",
- "Brunei Darussalam": "+673",
- Bulgaria: "+359",
- "Burkina Faso": "+226",
- Burundi: "+257",
- Cambodia: "+855",
- Cameroon: "+237",
- Canada: "+1",
- "Cape Verde": "+238",
- "Cayman Islands": "+1345",
- "Central African Republic": "+236",
- Chad: "+235",
- Chile: "+56",
- China: "+86",
- "Christmas Island": "+61",
- "Cocos (Keeling) Islands": "+61",
- Colombia: "+57",
- Comoros: "+269",
- Congo: "+242",
- "Congo, The Democratic Republic of the": "+243",
- "Cook Islands": "+682",
- "Costa Rica": "+506",
- "Côte d'Ivoire": "+225",
- Croatia: "+385",
- Cuba: "+53",
- Curacao: "+599",
- Cyprus: "+357",
- "Czech Republic": "+420",
- Djibouti: "+253",
- Dominica: "+1767",
- "Dominican Republic": "+1809",
- Ecuador: "+593",
- Egypt: "+20",
- "El Salvador": "+503",
- "Equatorial Guinea": "+240",
- Eritrea: "+291",
- Estonia: "+372",
- Eswatini: "+268",
- Ethiopia: "+251",
- "Falkland Islands (Malvinas)": "+500",
- "Faroe Islands": "+298",
- Fiji: "+679",
- France: "+33",
- "French Guiana": "+594",
- "French Polynesia": "+689",
- "French Southern Territories": "+262",
- Gabon: "+241",
- Gambia: "+220",
- Georgia: "+995",
- Ghana: "+233",
- Gibraltar: "+350",
- Greece: "+30",
- Greenland: "+299",
- Grenada: "+1473",
- Guadeloupe: "+590",
- Guam: "+1671",
- Guatemala: "+502",
- Guernsey: "+44",
- Guinea: "+224",
- "Guinea-Bissau": "+245",
- Guyana: "+592",
- Haiti: "+509",
- "Heard Island and Mcdonald Islands": "+61",
- "Holy See (Vatican City State)": "+379",
- Honduras: "+504",
- "Hong Kong": "+852",
- Hungary: "+36",
- Iceland: "+354",
- India: "+91",
- Indonesia: "+62",
- "Iran, Islamic Republic Of": "+98",
- Iraq: "+964",
- Ireland: "+353",
- "Isle of Man": "+44",
- Israel: "+972",
- Italy: "+39",
- Jamaica: "+1876",
- Japan: "+81",
- Jersey: "+44",
- Jordan: "+962",
- Kazakhstan: "+7",
- Kenya: "+254",
- Kiribati: "+686",
- 'Korea, Democratic People"S Republic of': "+850",
- "Korea, Republic of": "+82",
- Kuwait: "+965",
- Kyrgyzstan: "+996",
- Laos: "+856",
- Latvia: "+371",
- Lebanon: "+961",
- Lesotho: "+266",
- Liberia: "+231",
- "Libyan Arab Jamahiriya": "+218",
- Liechtenstein: "+423",
- Lithuania: "+370",
- Luxembourg: "+352",
- Macao: "+853",
- "Macedonia, The Former Yugoslav Republic of": "+389",
- Madagascar: "+261",
- Malawi: "+265",
- Malaysia: "+60",
- Maldives: "+960",
- Mali: "+223",
- Malta: "+356",
- "Marshall Islands": "+692",
- Martinique: "+596",
- Mauritania: "+222",
- Mauritius: "+230",
- Mayotte: "+262",
- Mexico: "+52",
- "Micronesia, Federated States of": "+691",
- "Moldova, Republic of": "+373",
- Monaco: "+377",
- Mongolia: "+976",
- Montenegro: "+382",
- Montserrat: "+1664",
- Morocco: "+212",
- Mozambique: "+258",
- Myanmar: "+95",
- Namibia: "+264",
- Nauru: "+674",
- Nepal: "+977",
- Netherlands: "+31",
- "Netherlands Antilles": "+599",
- "New Caledonia": "+687",
- "New Zealand": "+64",
- Nicaragua: "+505",
- Niger: "+227",
- Nigeria: "+234",
- Niue: "+683",
- "Norfolk Island": "+672",
- "Northern Mariana Islands": "+1670",
- Oman: "+968",
- Pakistan: "+92",
- Palau: "+680",
- Palestine: "+970",
- Panama: "+507",
- "Papua New Guinea": "+675",
- Paraguay: "+595",
- Peru: "+51",
- Philippines: "+63",
- Pitcairn: "+64",
- Poland: "+48",
- Portugal: "+351",
- "Puerto Rico": "+1787",
- Qatar: "+974",
- RWANDA: "+250",
- Reunion: "+262",
- Romania: "+40",
- "Russian Federation": "+7",
- "Saint Barthelemy": "+590",
- "Saint Helena": "+290",
- "Saint Kitts and Nevis": "+1869",
- "Saint Lucia": "+1758",
- "Saint Martin": "+590",
- "Saint Pierre and Miquelon": "+508",
- "Saint Vincent and the Grenadines": "+1784",
- Samoa: "+685",
- "San Marino": "+378",
- "Sao Tome and Principe": "+239",
- "Saudi Arabia": "+966",
- Senegal: "+221",
- Serbia: "+381",
- Seychelles: "+248",
- "Sierra Leone": "+232",
- Singapore: "+65",
- "Sint Maarten": "+1721",
- Slovakia: "+421",
- Slovenia: "+386",
- "Solomon Islands": "+677",
- Somalia: "+252",
- "South Africa": "+27",
- "South Georgia and the South Sandwich Islands": "+500",
- "South Sudan": "+211",
- Spain: "+34",
- "Sri Lanka": "+94",
- Sudan: "+249",
- Suriname: "+597",
- "Svalbard and Jan Mayen": "+47",
- Switzerland: "+41",
- "Syrian Arab Republic": "+963",
- Taiwan: "+886",
- Tajikistan: "+992",
- "Tanzania, United Republic of": "+255",
- Thailand: "+66",
- "Timor-Leste": "+670",
- Togo: "+228",
- Tokelau: "+690",
- Tonga: "+676",
- "Trinidad and Tobago": "+1868",
- Tunisia: "+216",
- Turkey: "+90",
- Turkmenistan: "+993",
- "Turks and Caicos Islands": "+1649",
- Tuvalu: "+688",
- Uganda: "+256",
- Ukraine: "+380",
- "United Arab Emirates": "+971",
- "United Kingdom": "+44",
- "United States": "+1",
- "United States Minor Outlying Islands": "+1",
- Uruguay: "+598",
- Uzbekistan: "+998",
- Vanuatu: "+678",
- Venezuela: "+58",
- Vietnam: "+84",
- "Virgin Islands, British": "+1284",
- "Virgin Islands, U.S.": "+1340",
- "Wallis and Futuna": "+681",
- "Western Sahara": "+212",
- Yemen: "+967",
- Zambia: "+260",
- Zimbabwe: "+263",
- "Åland Islands": "+358",
-}
diff --git a/apps/scandic-web/constants/languages.ts b/apps/scandic-web/constants/languages.ts
index ceb7a061e..9d4ca065e 100644
--- a/apps/scandic-web/constants/languages.ts
+++ b/apps/scandic-web/constants/languages.ts
@@ -1,3 +1,5 @@
+import type { LowerCaseCountryCode } from "@/types/components/form/phone"
+
export enum Lang {
da = "da",
de = "de",
@@ -106,3 +108,15 @@ export function getLocalizedLanguageOptions(currentLang: Lang) {
}
})
}
+
+export function getDefaultCountryFromLang(lang: Lang): LowerCaseCountryCode {
+ const countryMap: Record = {
+ sv: "se",
+ da: "dk",
+ fi: "fi",
+ no: "no",
+ de: "de",
+ en: "se", // Default to Sweden for English
+ }
+ return countryMap[lang] || "se"
+}
diff --git a/apps/scandic-web/providers/EnterDetailsProvider.tsx b/apps/scandic-web/providers/EnterDetailsProvider.tsx
index 1cd1ee724..1b4dbac04 100644
--- a/apps/scandic-web/providers/EnterDetailsProvider.tsx
+++ b/apps/scandic-web/providers/EnterDetailsProvider.tsx
@@ -27,6 +27,7 @@ export default function EnterDetailsProvider({
booking,
breakfastPackages,
children,
+ lang,
rooms,
searchParamsStr,
user,
@@ -73,7 +74,8 @@ export default function EnterDetailsProvider({
initialData,
searchParamsStr,
user,
- breakfastPackages
+ breakfastPackages,
+ lang
)
}
diff --git a/apps/scandic-web/stores/enter-details/helpers.ts b/apps/scandic-web/stores/enter-details/helpers.ts
index cbfb33440..063ef322d 100644
--- a/apps/scandic-web/stores/enter-details/helpers.ts
+++ b/apps/scandic-web/stores/enter-details/helpers.ts
@@ -1,4 +1,5 @@
import isEqual from "fast-deep-equal"
+import { parsePhoneNumberFromString } from "libphonenumber-js"
import {
sumPackages,
@@ -16,6 +17,13 @@ import type { PersistedState, RoomState } from "@/types/stores/enter-details"
import type { SafeUser } from "@/types/user"
export function extractGuestFromUser(user: NonNullable) {
+ let phoneNumberCC = ""
+ if (user.phoneNumber) {
+ const parsedPhoneNumber = parsePhoneNumberFromString(user.phoneNumber)
+ if (parsedPhoneNumber?.country) {
+ phoneNumberCC = parsedPhoneNumber.country.toLowerCase()
+ }
+ }
return {
countryCode: user.address.countryCode?.toString(),
email: user.email,
@@ -24,6 +32,7 @@ export function extractGuestFromUser(user: NonNullable) {
join: false,
membershipNo: user.membership?.membershipNumber,
phoneNumber: user.phoneNumber ?? "",
+ phoneNumberCC,
}
}
diff --git a/apps/scandic-web/stores/enter-details/index.ts b/apps/scandic-web/stores/enter-details/index.ts
index e6f5ba4c5..e74b49563 100644
--- a/apps/scandic-web/stores/enter-details/index.ts
+++ b/apps/scandic-web/stores/enter-details/index.ts
@@ -4,6 +4,7 @@ import { useContext } from "react"
import { create, useStore } from "zustand"
import { REDEMPTION } from "@/constants/booking"
+import { getDefaultCountryFromLang } from "@/constants/languages"
import { dt } from "@/lib/dt"
import {
@@ -34,6 +35,7 @@ import type {
RoomState,
} from "@/types/stores/enter-details"
import type { SafeUser } from "@/types/user"
+import type { Lang } from "@/constants/routes/hotelReservation"
const defaultGuestState = {
countryCode: "",
@@ -44,6 +46,7 @@ const defaultGuestState = {
lastName: "",
membershipNo: "",
phoneNumber: "",
+ phoneNumberCC: "",
zipCode: "",
}
@@ -53,7 +56,8 @@ export function createDetailsStore(
initialState: InitialState,
searchParams: string,
user: SafeUser,
- breakfastPackages: BreakfastPackages
+ breakfastPackages: BreakfastPackages,
+ lang: Lang
) {
const isMember = !!user
const isRedemption =
@@ -328,6 +332,8 @@ export function createDetailsStore(
currentRoom.guest.firstName = data.firstName
currentRoom.guest.join = data.join
currentRoom.guest.lastName = data.lastName
+ currentRoom.guest.phoneNumber = data.phoneNumber
+ currentRoom.guest.phoneNumberCC = data.phoneNumberCC
if (data.specialRequest?.comment) {
currentRoom.specialRequest.comment =
@@ -339,7 +345,6 @@ export function createDetailsStore(
} else {
currentRoom.guest.membershipNo = data.membershipNo
}
- currentRoom.guest.phoneNumber = data.phoneNumber
// Only valid for room 1
if (idx === 0 && data.join && !isMember) {
@@ -397,7 +402,10 @@ export function createDetailsStore(
guest:
isMember && idx === 0
? deepmerge(defaultGuestState, extractGuestFromUser(user))
- : defaultGuestState,
+ : {
+ ...defaultGuestState,
+ phoneNumberCC: getDefaultCountryFromLang(lang),
+ },
roomPrice: getRoomPrice(room.roomRate, isMember && idx === 0),
specialRequest: {
comment: "",
diff --git a/apps/scandic-web/types/components/form/phone.ts b/apps/scandic-web/types/components/form/phone.ts
index 537c17cc1..332741cb2 100644
--- a/apps/scandic-web/types/components/form/phone.ts
+++ b/apps/scandic-web/types/components/form/phone.ts
@@ -6,6 +6,7 @@ export type LowerCaseCountryCode = Lowercase
export interface PhoneProps {
ariaLabel?: string
className?: string
+ countrySelectorName?: string
disabled?: boolean
label: string
name?: string
diff --git a/apps/scandic-web/types/providers/enter-details.ts b/apps/scandic-web/types/providers/enter-details.ts
index 9c47e0de4..73e74987e 100644
--- a/apps/scandic-web/types/providers/enter-details.ts
+++ b/apps/scandic-web/types/providers/enter-details.ts
@@ -1,11 +1,13 @@
import type { Room } from "@/types/providers/details/room"
import type { SafeUser } from "@/types/user"
+import type { Lang } from "@/constants/routes/hotelReservation"
import type { BreakfastPackages } from "../components/hotelReservation/breakfast"
import type { DetailsBooking } from "../components/hotelReservation/enterDetails/details"
export interface DetailsProviderProps extends React.PropsWithChildren {
booking: DetailsBooking
breakfastPackages: BreakfastPackages
+ lang: Lang
rooms: Room[]
searchParamsStr: string
user: SafeUser
diff --git a/apps/scandic-web/utils/zod/phoneValidator.ts b/apps/scandic-web/utils/zod/phoneValidator.ts
index 6a512d996..80e697806 100644
--- a/apps/scandic-web/utils/zod/phoneValidator.ts
+++ b/apps/scandic-web/utils/zod/phoneValidator.ts
@@ -1,86 +1,20 @@
-import {
- isPossiblePhoneNumber,
- ParseError,
- parsePhoneNumberWithError,
- validatePhoneNumberLength,
-} from "libphonenumber-js"
import { z } from "zod"
-const enum ParseErrorMessage {
- INVALID_COUNTRY = "INVALID_COUNTRY",
- INVALID_LENGTH = "INVALID_LENGTH",
- NOT_A_NUMBER = "NOT_A_NUMBER",
- TOO_LONG = "TOO_LONG",
- TOO_SHORT = "TOO_SHORT",
-}
-
export function phoneValidator(
msg = "Required field",
invalidMsg = "Invalid type"
) {
return z
.string({ invalid_type_error: invalidMsg, required_error: msg })
- .min(1, { message: msg })
+ .min(5, { message: "The number you have entered is too short" })
.superRefine((value, ctx) => {
if (value) {
- try {
- const phoneNumber = parsePhoneNumberWithError(value)
- if (phoneNumber) {
- if (isPossiblePhoneNumber(value, phoneNumber.country)) {
- return validatePhoneNumberLength(value, phoneNumber.country)
- } else {
- ctx.addIssue({
- code: z.ZodIssueCode.custom,
- message: "Please enter a valid phone number",
- })
- }
- }
- } catch (error) {
- if (error instanceof ParseError) {
- /**
- * Only setup for when we need proper validation,
- * should probably move to .superRefine to be able
- * to return different messages depending on error.
- */
- switch (error.message) {
- case ParseErrorMessage.INVALID_COUNTRY:
- ctx.addIssue({
- code: z.ZodIssueCode.custom,
- message:
- "The country selected and country code doesn't match",
- })
- break
- case ParseErrorMessage.INVALID_LENGTH:
- ctx.addIssue({
- code: z.ZodIssueCode.custom,
- message: "Please enter a valid phone number",
- })
- break
- case ParseErrorMessage.NOT_A_NUMBER:
- ctx.addIssue({
- code: z.ZodIssueCode.custom,
- message: "Please enter a number",
- })
- break
- case ParseErrorMessage.TOO_LONG:
- ctx.addIssue({
- code: z.ZodIssueCode.custom,
- message: "The number you have entered is too long",
- })
- break
- case ParseErrorMessage.TOO_SHORT:
- ctx.addIssue({
- code: z.ZodIssueCode.custom,
- message: "The number you have entered is too short",
- })
- break
- }
- } else {
- ctx.addIssue({
- code: z.ZodIssueCode.custom,
- message: "The number you have entered is not valid",
- })
- }
+ const containsAlphabeticChars = /[a-z]/gi.test(value)
+ if (containsAlphabeticChars) {
+ ctx.addIssue({
+ code: z.ZodIssueCode.custom,
+ message: "Please enter a valid phone number",
+ })
}
}
})