feat: guest information form enter details

This commit is contained in:
Simon Emanuelsson
2024-10-03 11:12:36 +02:00
parent 4103e3fb37
commit 451d461c7f
50 changed files with 834 additions and 442 deletions

View File

@@ -1,11 +1,12 @@
import { notFound } from "next/navigation" import { notFound } from "next/navigation"
import { getProfileSafely } from "@/lib/trpc/memoizedRequests"
import { serverClient } from "@/lib/trpc/server" import { serverClient } from "@/lib/trpc/server"
import Details from "@/components/HotelReservation/EnterDetails/Details"
import HotelSelectionHeader from "@/components/HotelReservation/HotelSelectionHeader" import HotelSelectionHeader from "@/components/HotelReservation/HotelSelectionHeader"
import BedSelection from "@/components/HotelReservation/SelectRate/BedSelection" import BedSelection from "@/components/HotelReservation/SelectRate/BedSelection"
import BreakfastSelection from "@/components/HotelReservation/SelectRate/BreakfastSelection" import BreakfastSelection from "@/components/HotelReservation/SelectRate/BreakfastSelection"
import Details from "@/components/HotelReservation/SelectRate/Details"
import Payment from "@/components/HotelReservation/SelectRate/Payment" import Payment from "@/components/HotelReservation/SelectRate/Payment"
import RoomSelection from "@/components/HotelReservation/SelectRate/RoomSelection" import RoomSelection from "@/components/HotelReservation/SelectRate/RoomSelection"
import SectionAccordion from "@/components/HotelReservation/SelectRate/SectionAccordion" import SectionAccordion from "@/components/HotelReservation/SelectRate/SectionAccordion"
@@ -79,6 +80,7 @@ export default async function SectionsPage({
searchParams, searchParams,
}: PageArgs<LangParams & { section: string }, SectionPageProps>) { }: PageArgs<LangParams & { section: string }, SectionPageProps>) {
setLang(params.lang) setLang(params.lang)
const profile = await getProfileSafely()
const hotel = await serverClient().hotel.hotelData.get({ const hotel = await serverClient().hotel.hotelData.get({
hotelId: "811", hotelId: "811",
@@ -114,6 +116,11 @@ export default async function SectionsPage({
const currentSearchParams = new URLSearchParams(searchParams).toString() const currentSearchParams = new URLSearchParams(searchParams).toString()
let user = null
if (profile && !("error" in profile)) {
user = profile
}
return ( return (
<div> <div>
<HotelSelectionHeader hotel={hotel.data.attributes} /> <HotelSelectionHeader hotel={hotel.data.attributes} />
@@ -171,7 +178,7 @@ export default async function SectionsPage({
header={intl.formatMessage({ id: "Your details" })} header={intl.formatMessage({ id: "Your details" })}
path={`details?${currentSearchParams}`} path={`details?${currentSearchParams}`}
> >
{params.section === "details" && <Details nextPath="payment" />} {params.section === "details" ? <Details user={user} /> : null}
</SectionAccordion> </SectionAccordion>
<SectionAccordion <SectionAccordion
header={intl.formatMessage({ id: "Payment info" })} header={intl.formatMessage({ id: "Payment info" })}

View File

@@ -10,6 +10,7 @@ import type { ClearSearchButtonProps } from "@/types/components/search"
export default function ClearSearchButton({ export default function ClearSearchButton({
getItemProps, getItemProps,
handleClearSearchHistory,
highlightedIndex, highlightedIndex,
index, index,
}: ClearSearchButtonProps) { }: ClearSearchButtonProps) {
@@ -18,13 +19,6 @@ export default function ClearSearchButton({
variant: index === highlightedIndex ? "active" : "default", variant: index === highlightedIndex ? "active" : "default",
}) })
function handleClick() {
// noop
// the click bubbles to handleOnSelect
// where selectedItem = "clear-search"
// which is the value for item below
}
return ( return (
<button <button
{...getItemProps({ {...getItemProps({
@@ -34,7 +28,7 @@ export default function ClearSearchButton({
item: "clear-search", item: "clear-search",
role: "button", role: "button",
})} })}
onClick={handleClick} onClick={handleClearSearchHistory}
tabIndex={0} tabIndex={0}
type="button" type="button"
> >

View File

@@ -20,6 +20,7 @@ import type { SearchListProps } from "@/types/components/search"
export default function SearchList({ export default function SearchList({
getItemProps, getItemProps,
getMenuProps, getMenuProps,
handleClearSearchHistory,
highlightedIndex, highlightedIndex,
isOpen, isOpen,
locations, locations,
@@ -125,6 +126,7 @@ export default function SearchList({
<Divider className={styles.divider} color="beige" /> <Divider className={styles.divider} color="beige" />
<ClearSearchButton <ClearSearchButton
getItemProps={getItemProps} getItemProps={getItemProps}
handleClearSearchHistory={handleClearSearchHistory}
highlightedIndex={highlightedIndex} highlightedIndex={highlightedIndex}
index={searchHistory.length} index={searchHistory.length}
/> />
@@ -161,6 +163,7 @@ export default function SearchList({
<Divider className={styles.divider} color="beige" /> <Divider className={styles.divider} color="beige" />
<ClearSearchButton <ClearSearchButton
getItemProps={getItemProps} getItemProps={getItemProps}
handleClearSearchHistory={handleClearSearchHistory}
highlightedIndex={highlightedIndex} highlightedIndex={highlightedIndex}
index={searchHistory.length} index={searchHistory.length}
/> />

View File

@@ -43,6 +43,11 @@ export default function Search({ locations }: SearchProps) {
[locations] [locations]
) )
function handleClearSearchHistory() {
localStorage.removeItem(localStorageKey)
dispatch({ type: ActionType.CLEAR_HISTORY_LOCATIONS })
}
function handleOnBlur() { function handleOnBlur() {
if (!value && state.searchData?.name) { if (!value && state.searchData?.name) {
setValue(name, state.searchData.name) setValue(name, state.searchData.name)
@@ -79,11 +84,8 @@ export default function Search({ locations }: SearchProps) {
} }
} }
function handleOnSelect(selectedItem: Location | null | "clear-search") { function handleOnSelect(selectedItem: Location | null) {
if (selectedItem === "clear-search") { if (selectedItem) {
localStorage.removeItem(localStorageKey)
dispatch({ type: ActionType.CLEAR_HISTORY_LOCATIONS })
} else if (selectedItem) {
const stringified = JSON.stringify(selectedItem) const stringified = JSON.stringify(selectedItem)
setValue("location", encodeURIComponent(stringified)) setValue("location", encodeURIComponent(stringified))
sessionStorage.setItem(sessionStorageKey, stringified) sessionStorage.setItem(sessionStorageKey, stringified)
@@ -167,6 +169,7 @@ export default function Search({ locations }: SearchProps) {
<SearchList <SearchList
getItemProps={getItemProps} getItemProps={getItemProps}
getMenuProps={getMenuProps} getMenuProps={getMenuProps}
handleClearSearchHistory={handleClearSearchHistory}
highlightedIndex={highlightedIndex} highlightedIndex={highlightedIndex}
isOpen={isOpen} isOpen={isOpen}
locations={state.locations} locations={state.locations}

View File

@@ -0,0 +1,25 @@
.container {
display: grid;
gap: var(--Spacing-x2);
padding: var(--Spacing-x3) 0px;
}
.form {
display: grid;
gap: var(--Spacing-x2);
grid-template-columns: 1fr 1fr;
width: min(100%, 600px);
}
.country,
.email,
.phone {
grid-column: 1/-1;
}
.footer {
display: grid;
gap: var(--Spacing-x3);
justify-items: flex-start;
margin-top: var(--Spacing-x1);
}

View File

@@ -0,0 +1,117 @@
"use client"
import { zodResolver } from "@hookform/resolvers/zod"
import { FormProvider, useForm } from "react-hook-form"
import { useIntl } from "react-intl"
import Button from "@/components/TempDesignSystem/Button"
import CheckboxCard from "@/components/TempDesignSystem/Form/Checkbox/Card"
import CountrySelect from "@/components/TempDesignSystem/Form/Country"
import Input from "@/components/TempDesignSystem/Form/Input"
import Phone from "@/components/TempDesignSystem/Form/Phone"
import Body from "@/components/TempDesignSystem/Text/Body"
import { detailsSchema, signedInDetailsSchema } from "./schema"
import styles from "./details.module.css"
import type {
DetailsProps,
DetailsSchema,
} from "@/types/components/enterDetails/details"
export default function Details({ user }: DetailsProps) {
const intl = useIntl()
const list = [
{ title: intl.formatMessage({ id: "Earn bonus nights & points" }) },
{ title: intl.formatMessage({ id: "Get member benefits & offers" }) },
{ title: intl.formatMessage({ id: "Join at no cost" }) },
]
const methods = useForm<DetailsSchema>({
defaultValues: {
countryCode: user?.address?.countryCode ?? "",
email: user?.email ?? "",
firstname: user?.firstName ?? "",
lastname: user?.lastName ?? "",
phoneNumber: user?.phoneNumber ?? "",
},
mode: "all",
resolver: zodResolver(user ? signedInDetailsSchema : detailsSchema),
reValidateMode: "onChange",
})
return (
<FormProvider {...methods}>
<section className={styles.container}>
<header>
<Body color="uiTextHighContrast" textTransform="bold">
{intl.formatMessage({ id: "Guest information" })}
</Body>
</header>
<form className={styles.form}>
<Input
label={intl.formatMessage({ id: "Firstname" })}
name="firstname"
readOnly={!!user}
registerOptions={{ required: true }}
/>
<Input
label={intl.formatMessage({ id: "Lastname" })}
name="lastname"
readOnly={!!user}
registerOptions={{ required: true }}
/>
<CountrySelect
className={styles.country}
label={intl.formatMessage({ id: "Country" })}
name="countryCode"
readOnly={!!user}
registerOptions={{ required: true }}
/>
<Input
className={styles.email}
label={intl.formatMessage({ id: "Email address" })}
name="email"
readOnly={!!user}
registerOptions={{ required: true }}
/>
<Phone
className={styles.phone}
label={intl.formatMessage({ id: "Phone number" })}
name="phoneNumber"
readOnly={!!user}
registerOptions={{ required: true }}
/>
</form>
<footer className={styles.footer}>
{user ? null : (
<CheckboxCard
list={list}
saving
subtitle={intl.formatMessage(
{
id: "{difference}{amount} {currency}",
},
{
amount: "491",
currency: "SEK",
difference: "-",
}
)}
title={intl.formatMessage({ id: "Join Scandic Friends" })}
/>
)}
<Button
disabled={!methods.formState.isValid}
intent="secondary"
size="small"
theme="base"
>
{intl.formatMessage({ id: "Proceed to payment method" })}
</Button>
</footer>
</section>
</FormProvider>
)
}

View File

@@ -0,0 +1,19 @@
import { z } from "zod"
import { phoneValidator } from "@/utils/phoneValidator"
export const detailsSchema = z.object({
countryCode: z.string(),
email: z.string().email(),
firstname: z.string(),
lastname: z.string(),
phoneNumber: phoneValidator(),
})
export const signedInDetailsSchema = z.object({
countryCode: z.string().optional(),
email: z.string().email().optional(),
firstname: z.string().optional(),
lastname: z.string().optional(),
phoneNumber: phoneValidator().optional(),
})

View File

@@ -48,10 +48,10 @@ export default async function HotelCard({ hotel }: HotelCardProps) {
<Title as="h4" textTransform="capitalize"> <Title as="h4" textTransform="capitalize">
{hotelData.name} {hotelData.name}
</Title> </Title>
<Footnote color="textMediumContrast"> <Footnote color="uiTextMediumContrast">
{`${hotelData.address.streetAddress}, ${hotelData.address.city}`} {`${hotelData.address.streetAddress}, ${hotelData.address.city}`}
</Footnote> </Footnote>
<Footnote color="textMediumContrast"> <Footnote color="uiTextMediumContrast">
{`${hotelData.location.distanceToCentre} ${intl.formatMessage({ id: "km to city center" })}`} {`${hotelData.location.distanceToCentre} ${intl.formatMessage({ id: "km to city center" })}`}
</Footnote> </Footnote>
</section> </section>
@@ -79,7 +79,7 @@ export default async function HotelCard({ hotel }: HotelCardProps) {
{price?.regularAmount} {price?.currency} / {price?.regularAmount} {price?.currency} /
{intl.formatMessage({ id: "night" })} {intl.formatMessage({ id: "night" })}
</Caption> </Caption>
<Footnote color="textMediumContrast">approx 280 eur</Footnote> <Footnote color="uiTextMediumContrast">approx 280 eur</Footnote>
</div> </div>
<div> <div>
<Chip intent="primary" className={styles.member}> <Chip intent="primary" className={styles.member}>
@@ -90,7 +90,7 @@ export default async function HotelCard({ hotel }: HotelCardProps) {
{price?.memberAmount} {price?.currency} / {price?.memberAmount} {price?.currency} /
{intl.formatMessage({ id: "night" })} {intl.formatMessage({ id: "night" })}
</Caption> </Caption>
<Footnote color="textMediumContrast">approx 280 eur</Footnote> <Footnote color="uiTextMediumContrast">approx 280 eur</Footnote>
</div> </div>
<Button <Button
asChild asChild

View File

@@ -0,0 +1,36 @@
import { iconVariants } from "./variants"
import type { IconProps } from "@/types/components/icon"
export default function HeartIcon({ className, color, ...props }: IconProps) {
const classNames = iconVariants({ className, color })
return (
<svg
className={classNames}
fill="none"
height="24"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<mask
height="24"
id="mask0_69_3298"
maskUnits="userSpaceOnUse"
style={{ maskType: "alpha" }}
width="24"
x="0"
y="0"
>
<rect width="24" height="24" fill="#D9D9D9" />
</mask>
<g mask="url(#mask0_69_3298)">
<path
d="M12 20.0875C11.775 20.0875 11.5521 20.0479 11.3313 19.9687C11.1104 19.8896 10.9125 19.7666 10.7375 19.6L9.1625 18.1625C7.3625 16.5208 5.76042 14.9125 4.35625 13.3375C2.95208 11.7625 2.25 10.0416 2.25 8.17498C2.25 6.65816 2.75865 5.39145 3.77595 4.37485C4.79327 3.35827 6.06087 2.84998 7.57875 2.84998C8.43458 2.84998 9.24792 3.03539 10.0188 3.40623C10.7896 3.77706 11.45 4.29998 12 4.97498C12.5667 4.29998 13.231 3.77706 13.993 3.40623C14.755 3.03539 15.5657 2.84998 16.425 2.84998C17.9418 2.84998 19.2085 3.35827 20.2251 4.37485C21.2417 5.39145 21.75 6.65816 21.75 8.17498C21.75 10.0416 21.05 11.7646 19.65 13.3437C18.25 14.9229 16.6458 16.5291 14.8375 18.1625L13.2625 19.6C13.0875 19.7666 12.8896 19.8896 12.6687 19.9687C12.4479 20.0479 12.225 20.0875 12 20.0875ZM11.107 6.89753C10.6773 6.20749 10.1729 5.67289 9.59375 5.29373C9.01458 4.91456 8.33962 4.72498 7.56885 4.72498C6.5849 4.72498 5.76493 5.05206 5.10895 5.70623C4.45298 6.36039 4.125 7.18202 4.125 8.1711C4.125 9.0283 4.42917 9.93908 5.0375 10.9035C5.64583 11.8678 6.37083 12.8041 7.2125 13.7125C8.05417 14.6208 8.92083 15.4687 9.8125 16.2562C10.7042 17.0437 11.4333 17.6916 12 18.2C12.5667 17.6916 13.2958 17.0437 14.1875 16.2562C15.0792 15.4687 15.9458 14.6208 16.7875 13.7125C17.6292 12.8041 18.3542 11.8678 18.9625 10.9035C19.5708 9.93908 19.875 9.0283 19.875 8.1711C19.875 7.18202 19.547 6.36039 18.8911 5.70623C18.2351 5.05206 17.4151 4.72498 16.4311 4.72498C15.6604 4.72498 14.9833 4.91456 14.4 5.29373C13.8167 5.67289 13.3102 6.20749 12.8805 6.89753C12.7768 7.06583 12.6466 7.18956 12.4899 7.26873C12.3331 7.34789 12.1685 7.38748 11.9961 7.38748C11.8237 7.38748 11.6583 7.34789 11.5 7.26873C11.3417 7.18956 11.2107 7.06583 11.107 6.89753Z"
fill="#26201E"
/>
</g>
</svg>
)
}

View File

@@ -2,6 +2,11 @@
margin: 0; margin: 0;
} }
.baseIconLowContrast,
.baseIconLowContrast * {
fill: var(--Base-Icon-Low-contrast);
}
.black, .black,
.black * { .black * {
fill: #000; fill: #000;
@@ -46,3 +51,13 @@
.white * { .white * {
fill: var(--UI-Opacity-White-100); fill: var(--UI-Opacity-White-100);
} }
.uiTextHighContrast,
.uiTextHighContrast * {
fill: var(--UI-Text-High-contrast);
}
.uiTextMediumContrast,
.uiTextMediumContrast * {
fill: var(--UI-Text-Medium-contrast);
}

View File

@@ -30,6 +30,7 @@ export { default as ErrorCircleIcon } from "./ErrorCircle"
export { default as FitnessIcon } from "./Fitness" export { default as FitnessIcon } from "./Fitness"
export { default as GiftIcon } from "./Gift" export { default as GiftIcon } from "./Gift"
export { default as GlobeIcon } from "./Globe" export { default as GlobeIcon } from "./Globe"
export { default as HeartIcon } from "./Heart"
export { default as HouseIcon } from "./House" export { default as HouseIcon } from "./House"
export { default as ImageIcon } from "./Image" export { default as ImageIcon } from "./Image"
export { default as InfoCircleIcon } from "./InfoCircle" export { default as InfoCircleIcon } from "./InfoCircle"

View File

@@ -5,6 +5,7 @@ import styles from "./icon.module.css"
const config = { const config = {
variants: { variants: {
color: { color: {
baseIconLowContrast: styles.baseIconLowContrast,
black: styles.black, black: styles.black,
burgundy: styles.burgundy, burgundy: styles.burgundy,
grey80: styles.grey80, grey80: styles.grey80,
@@ -14,6 +15,8 @@ const config = {
red: styles.red, red: styles.red,
green: styles.green, green: styles.green,
white: styles.white, white: styles.white,
uiTextHighContrast: styles.uiTextHighContrast,
uiTextMediumContrast: styles.uiTextMediumContrast,
}, },
}, },
defaultVariants: { defaultVariants: {

View File

@@ -0,0 +1,70 @@
.checkbox {
background-color: var(--Base-Surface-Primary-light-Normal);
border: 1px solid var(--Base-Border-Subtle);
border-radius: var(--Corner-radius-Large);
cursor: pointer;
display: flex;
flex-direction: column;
gap: var(--Spacing-x1);
padding: var(--Spacing-x-one-and-half) var(--Spacing-x2);
transition: all 200ms ease;
width: min(100%, 600px);
}
.checkbox:hover {
background-color: var(--Base-Surface-Secondary-light-Hover);
}
.checkbox:has(:checked) {
background-color: var(--Primary-Light-Surface-Normal);
border-color: var(--Base-Border-Hover);
}
.header {
align-items: center;
display: grid;
gap: 0px var(--Spacing-x1);
grid-template-areas:
"title icon"
"subtitle icon";
}
.icon {
grid-area: icon;
justify-self: flex-end;
transition: fill 200ms ease;
}
.checkbox:hover .icon,
.checkbox:hover .icon *,
.checkbox:has(:checked) .icon,
.checkbox:has(:checked) .icon * {
fill: var(--Base-Text-Medium-contrast);
}
.checkbox[data-declined="true"]:hover .icon,
.checkbox[data-declined="true"]:hover .icon *,
.checkbox[data-declined="true"]:has(:checked) .icon,
.checkbox[data-declined="true"]:has(:checked) .icon * {
fill: var(--Base-Text-Disabled);
}
.subtitle {
grid-area: subtitle;
}
.title {
grid-area: title;
}
.list {
list-style: none;
margin: 0;
padding: 0;
}
.listItem {
align-items: center;
display: flex;
gap: var(--Spacing-x-quarter);
}

View File

@@ -0,0 +1,14 @@
import type { IconProps } from "@/types/components/icon"
export interface CheckboxCardProps {
Icon?: (props: IconProps) => JSX.Element
declined?: boolean
list?: {
title: string
}[]
name?: string
saving?: boolean
subtitle?: string
text?: string
title: string
}

View File

@@ -0,0 +1,65 @@
"use client"
import { CheckIcon, CloseIcon, HeartIcon } from "@/components/Icons"
import Caption from "@/components/TempDesignSystem/Text/Caption"
import Footnote from "@/components/TempDesignSystem/Text/Footnote"
import styles from "./card.module.css"
import type { CheckboxCardProps } from "./card"
export default function CheckboxCard({
Icon = HeartIcon,
declined = false,
list,
name = "join",
saving = false,
subtitle,
text,
title,
}: CheckboxCardProps) {
return (
<label className={styles.checkbox} data-declined={declined} htmlFor={name}>
<header className={styles.header}>
<Caption className={styles.title} textTransform="bold" uppercase>
{title}
</Caption>
{subtitle ? (
<Caption
className={styles.subtitle}
color={saving ? "baseTextAccent" : "uiTextHighContrast"}
textTransform="bold"
>
{subtitle}
</Caption>
) : null}
<Icon
className={styles.icon}
color="uiTextHighContrast"
height={32}
width={32}
/>
</header>
{list ? (
<ul className={styles.list}>
{list.map((listItem) => (
<li key={listItem.title} className={styles.listItem}>
{declined ? (
<CloseIcon
color="uiTextMediumContrast"
height={20}
width={20}
/>
) : (
<CheckIcon color="baseIconLowContrast" height={20} width={20} />
)}
<Footnote color="uiTextMediumContrast">{listItem.title}</Footnote>
</li>
))}
</ul>
) : null}
{text ? <Footnote color="uiTextMediumContrast">{text}</Footnote> : null}
<input aria-hidden id={name} hidden name={name} type="checkbox" />
</label>
)
}

View File

@@ -1,9 +1,11 @@
import type { RegisterOptions } from "react-hook-form" import type { RegisterOptions } from "react-hook-form"
export type CountryProps = { export type CountryProps = {
className?: string
label: string label: string
name?: string name?: string
placeholder?: string placeholder?: string
readOnly?: boolean
registerOptions?: RegisterOptions registerOptions?: RegisterOptions
} }

View File

@@ -28,8 +28,10 @@ import type {
} from "./country" } from "./country"
export default function CountrySelect({ export default function CountrySelect({
className = "",
label, label,
name = "country", name = "country",
readOnly = false,
registerOptions = {}, registerOptions = {},
}: CountryProps) { }: CountryProps) {
const { formatMessage } = useIntl() const { formatMessage } = useIntl()
@@ -54,12 +56,13 @@ export default function CountrySelect({
const selectCountryLabel = formatMessage({ id: "Select a country" }) const selectCountryLabel = formatMessage({ id: "Select a country" })
return ( return (
<div className={styles.container} ref={setRef}> <div className={`${styles.container} ${className}`} ref={setRef}>
<ComboBox <ComboBox
aria-label={formatMessage({ id: "Select country of residence" })} aria-label={formatMessage({ id: "Select country of residence" })}
className={styles.select} className={styles.select}
isRequired={!!registerOptions?.required}
isInvalid={fieldState.invalid} isInvalid={fieldState.invalid}
isReadOnly={readOnly}
isRequired={!!registerOptions?.required}
name={field.name} name={field.name}
onBlur={field.onBlur} onBlur={field.onBlur}
onSelectionChange={handleChange} onSelectionChange={handleChange}

View File

@@ -0,0 +1,25 @@
import { type ForwardedRef,forwardRef } from "react"
import { Input as AriaInput, Label as AriaLabel } from "react-aria-components"
import Label from "@/components/TempDesignSystem/Form/Label"
import Body from "@/components/TempDesignSystem/Text/Body"
import styles from "./input.module.css"
import type { AriaInputWithLabelProps } from "./input"
const AriaInputWithLabel = forwardRef(function AriaInputWithLabelComponent(
{ label, ...props }: AriaInputWithLabelProps,
ref: ForwardedRef<HTMLInputElement>
) {
return (
<AriaLabel className={styles.container} htmlFor={props.name}>
<Body asChild fontOnly>
<AriaInput {...props} className={styles.input} ref={ref} />
</Body>
<Label required={!!props.required}>{label}</Label>
</AriaLabel>
)
})
export default AriaInputWithLabel

View File

@@ -0,0 +1,55 @@
.container {
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-Medium);
display: grid;
height: 60px;
padding: var(--Spacing-x1) var(--Spacing-x2);
transition: border-color 200ms ease;
}
.container:has(.input:active, .input:focus) {
border-color: var(--Scandic-Blue-90);
}
.container:has(.input:disabled) {
background-color: var(--Main-Grey-10);
border: none;
color: var(--Main-Grey-40);
}
.container:has(.input[data-invalid="true"], .input[aria-invalid="true"]) {
border-color: var(--Scandic-Red-60);
}
.input {
background: none;
border: none;
color: var(--Main-Grey-100);
height: 18px;
margin: 0;
order: 2;
overflow: visible;
padding: 0;
}
.input:not(:active, :focus):placeholder-shown {
height: 0px;
transition: height 150ms ease;
}
.input:focus,
.input:focus:placeholder-shown,
.input:active,
.input:active:placeholder-shown {
height: 18px;
transition: height 150ms ease;
outline: none;
}
.input:disabled {
color: var(--Main-Grey-40);
}

View File

@@ -0,0 +1,4 @@
export interface AriaInputWithLabelProps
extends React.InputHTMLAttributes<HTMLInputElement> {
label: string
}

View File

@@ -1,15 +1,9 @@
"use client" "use client"
import { import { Text, TextField } from "react-aria-components"
Input as AriaInput,
Label as AriaLabel,
Text,
TextField,
} from "react-aria-components"
import { Controller, useFormContext } from "react-hook-form" import { Controller, useFormContext } from "react-hook-form"
import { CheckIcon, InfoCircleIcon } from "@/components/Icons" import { CheckIcon, InfoCircleIcon } from "@/components/Icons"
import Label from "@/components/TempDesignSystem/Form/Label" import AriaInputWithLabel from "@/components/TempDesignSystem/Form/Input/AriaInputWithLabel"
import Body from "@/components/TempDesignSystem/Text/Body"
import Caption from "@/components/TempDesignSystem/Text/Caption" import Caption from "@/components/TempDesignSystem/Text/Caption"
import styles from "./input.module.css" import styles from "./input.module.css"
@@ -20,11 +14,13 @@ import type { InputProps } from "./input"
export default function Input({ export default function Input({
"aria-label": ariaLabel, "aria-label": ariaLabel,
className = "",
disabled = false, disabled = false,
helpText = "", helpText = "",
label, label,
name, name,
placeholder = "", placeholder = "",
readOnly = false,
registerOptions = {}, registerOptions = {},
type = "text", type = "text",
}: InputProps) { }: InputProps) {
@@ -44,6 +40,7 @@ export default function Input({
render={({ field, fieldState }) => ( render={({ field, fieldState }) => (
<TextField <TextField
aria-label={ariaLabel} aria-label={ariaLabel}
className={className}
isDisabled={field.disabled} isDisabled={field.disabled}
isInvalid={fieldState.invalid} isInvalid={fieldState.invalid}
isRequired={!!registerOptions.required} isRequired={!!registerOptions.required}
@@ -53,19 +50,16 @@ export default function Input({
validationBehavior="aria" validationBehavior="aria"
value={field.value} value={field.value}
> >
<AriaLabel className={styles.container} htmlFor={field.name}> <AriaInputWithLabel
<Body asChild fontOnly> {...field}
<AriaInput aria-labelledby={field.name}
{...numberAttributes} id={field.name}
aria-labelledby={field.name} label={label}
className={styles.input} placeholder={placeholder}
id={field.name} readOnly={readOnly}
placeholder={placeholder} required={!!registerOptions.required}
type={type} type={type}
/> />
</Body>
<Label required={!!registerOptions.required}>{label}</Label>
</AriaLabel>
{helpText && !fieldState.error ? ( {helpText && !fieldState.error ? (
<Caption asChild color="black"> <Caption asChild color="black">
<Text className={styles.helpText} slot="description"> <Text className={styles.helpText} slot="description">

View File

@@ -1,59 +1,3 @@
.container {
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-Medium);
display: grid;
height: 60px;
padding: var(--Spacing-x1) var(--Spacing-x2);
transition: border-color 200ms ease;
}
.container:has(.input:active, .input:focus) {
border-color: var(--Scandic-Blue-90);
}
.container:has(.input:disabled) {
background-color: var(--Main-Grey-10);
border: none;
color: var(--Main-Grey-40);
}
.container:has(.input[data-invalid="true"], .input[aria-invalid="true"]) {
border-color: var(--Scandic-Red-60);
}
.input {
background: none;
border: none;
color: var(--Main-Grey-100);
height: 18px;
margin: 0;
order: 2;
overflow: visible;
padding: 0;
}
.input:not(:active, :focus):placeholder-shown {
height: 0px;
transition: height 150ms ease;
}
.input:focus,
.input:focus:placeholder-shown,
.input:active,
.input:active:placeholder-shown {
height: 18px;
transition: height 150ms ease;
outline: none;
}
.input:disabled {
color: var(--Main-Grey-40);
}
.helpText { .helpText {
align-items: flex-start; align-items: flex-start;
display: flex; display: flex;

View File

@@ -1,4 +1,4 @@
import type { RegisterOptions, UseFormRegister } from "react-hook-form" import type { RegisterOptions } from "react-hook-form"
export interface InputProps export interface InputProps
extends React.InputHTMLAttributes<HTMLInputElement> { extends React.InputHTMLAttributes<HTMLInputElement> {

View File

@@ -5,6 +5,7 @@
letter-spacing: 0.03px; letter-spacing: 0.03px;
line-height: 120%; line-height: 120%;
text-align: left; text-align: left;
transition: font-size 100ms ease;
} }
span.small { span.small {
@@ -21,7 +22,6 @@ input:active ~ .label,
input:not(:placeholder-shown) ~ .label { input:not(:placeholder-shown) ~ .label {
display: block; display: block;
font-size: 12px; font-size: 12px;
transition: font-size 100ms ease;
} }
input:focus ~ .label { input:focus ~ .label {

View File

@@ -1,17 +1,11 @@
"use client" "use client"
import { import { Text, TextField } from "react-aria-components"
Input as AriaInput,
Label as AriaLabel,
Text,
TextField,
} from "react-aria-components"
import { Controller, useFormContext } from "react-hook-form" import { Controller, useFormContext } from "react-hook-form"
import { useIntl } from "react-intl" import { useIntl } from "react-intl"
import { CheckIcon, CloseIcon } from "@/components/Icons" import { CheckIcon, CloseIcon } from "@/components/Icons"
import Error from "@/components/TempDesignSystem/Form/ErrorMessage/Error" import Error from "@/components/TempDesignSystem/Form/ErrorMessage/Error"
import Label from "@/components/TempDesignSystem/Form/Label" import AriaInputWithLabel from "@/components/TempDesignSystem/Form/Input/AriaInputWithLabel"
import Body from "@/components/TempDesignSystem/Text/Body"
import Caption from "@/components/TempDesignSystem/Text/Caption" import Caption from "@/components/TempDesignSystem/Text/Caption"
import { type IconProps, Key, type NewPasswordProps } from "./newPassword" import { type IconProps, Key, type NewPasswordProps } from "./newPassword"
@@ -47,20 +41,14 @@ export default function NewPassword({
value={field.value} value={field.value}
type="password" type="password"
> >
<AriaLabel className={styles.container} htmlFor={field.name}> <AriaInputWithLabel
<Body asChild fontOnly> {...field}
<AriaInput aria-labelledby={field.name}
aria-labelledby={field.name} id={field.name}
className={styles.input} label={formatMessage({ id: "New password" })}
id={field.name} placeholder={placeholder}
placeholder={placeholder} type="password"
type="password" />
/>
</Body>
<Label required={!!registerOptions.required}>
{formatMessage({ id: "New password" })}
</Label>
</AriaLabel>
{field.value ? ( {field.value ? (
<div className={styles.errors}> <div className={styles.errors}>
<Caption asChild color="black"> <Caption asChild color="black">

View File

@@ -1,59 +1,3 @@
.container {
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-Medium);
display: grid;
height: 60px;
padding: var(--Spacing-x1) var(--Spacing-x2);
transition: border-color 200ms ease;
}
.container:has(.input:active, .input:focus) {
border-color: var(--Scandic-Blue-90);
}
.container:has(.input:disabled) {
background-color: var(--Main-Grey-10);
border: none;
color: var(--Main-Grey-40);
}
.container:has(.input[data-invalid="true"], .input[aria-invalid="true"]) {
border-color: var(--Scandic-Red-60);
}
.input {
background: none;
border: none;
color: var(--Main-Grey-100);
height: 18px;
margin: 0;
order: 2;
overflow: visible;
padding: 0;
}
.input:not(:active, :focus):placeholder-shown {
height: 0px;
transition: height 150ms ease;
}
.input:focus,
.input:focus:placeholder-shown,
.input:active,
.input:active:placeholder-shown {
height: 18px;
transition: height 150ms ease;
outline: none;
}
.input:disabled {
color: var(--Main-Grey-40);
}
.helpText { .helpText {
align-items: flex-start; align-items: flex-start;
display: flex; display: flex;

View File

@@ -2,11 +2,7 @@
import "react-international-phone/style.css" import "react-international-phone/style.css"
import { isValidPhoneNumber, parsePhoneNumber } from "libphonenumber-js" import { isValidPhoneNumber, parsePhoneNumber } from "libphonenumber-js"
import { import { TextField } from "react-aria-components"
Input as AriaInput,
Label as AriaLabel,
TextField,
} from "react-aria-components"
import { useController, useFormContext, useWatch } from "react-hook-form" import { useController, useFormContext, useWatch } from "react-hook-form"
import { import {
CountrySelector, CountrySelector,
@@ -18,6 +14,7 @@ import { useIntl } from "react-intl"
import { ChevronDownIcon } from "@/components/Icons" import { ChevronDownIcon } from "@/components/Icons"
import ErrorMessage from "@/components/TempDesignSystem/Form/ErrorMessage" import ErrorMessage from "@/components/TempDesignSystem/Form/ErrorMessage"
import AriaInputWithLabel from "@/components/TempDesignSystem/Form/Input/AriaInputWithLabel"
import Label from "@/components/TempDesignSystem/Form/Label" import Label from "@/components/TempDesignSystem/Form/Label"
import Body from "@/components/TempDesignSystem/Text/Body" import Body from "@/components/TempDesignSystem/Text/Body"
@@ -29,10 +26,12 @@ import type { PhoneProps } from "./phone"
export default function Phone({ export default function Phone({
ariaLabel = "Phone number input", ariaLabel = "Phone number input",
className = "",
disabled = false, disabled = false,
label, label,
name = "phoneNumber", name = "phoneNumber",
placeholder = "", placeholder = "",
readOnly = false,
registerOptions = { registerOptions = {
required: true, required: true,
}, },
@@ -72,8 +71,9 @@ export default function Phone({
} }
return ( return (
<div className={styles.phone}> <div className={`${styles.phone} ${className}`}>
<CountrySelector <CountrySelector
disabled={readOnly}
dropdownArrowClassName={styles.arrow} dropdownArrowClassName={styles.arrow}
flagClassName={styles.flag} flagClassName={styles.flag}
onSelect={handleSelectCountry} onSelect={handleSelectCountry}
@@ -114,25 +114,21 @@ export default function Phone({
isDisabled={disabled ?? field.disabled} isDisabled={disabled ?? field.disabled}
isInvalid={fieldState.invalid} isInvalid={fieldState.invalid}
isRequired={!!registerOptions?.required} isRequired={!!registerOptions?.required}
isReadOnly={readOnly}
name={field.name} name={field.name}
type="tel" type="tel"
> >
<AriaLabel className={styles.inputContainer} htmlFor={field.name}> <AriaInputWithLabel
<Body asChild fontOnly> {...field}
<AriaInput id={field.name}
className={styles.input} label={label}
id={field.name} onChange={handleChange}
name={field.name} placeholder={placeholder}
onBlur={field.onBlur} readOnly={readOnly}
onChange={handleChange} required={!!registerOptions.required}
placeholder={placeholder} type="tel"
ref={field.ref} value={inputValue}
required={!!registerOptions.required} />
value={inputValue}
/>
</Body>
<Label required={!!registerOptions.required}>{label}</Label>
</AriaLabel>
<ErrorMessage errors={formState.errors} name={field.name} /> <ErrorMessage errors={formState.errors} name={field.name} />
</TextField> </TextField>
</div> </div>

View File

@@ -19,6 +19,9 @@
--react-international-phone-dropdown-top: calc( --react-international-phone-dropdown-top: calc(
var(--react-international-phone-height) + var(--Spacing-x1) var(--react-international-phone-height) + var(--Spacing-x1)
); );
--react-international-phone-dial-code-preview-font-size: var(
--typography-Body-Regular-fontSize
);
} }
.phone:has(.input:active, .input:focus) { .phone:has(.input:active, .input:focus) {
@@ -46,7 +49,6 @@
align-self: center; align-self: center;
} }
.inputContainer,
.select { .select {
align-content: center; align-content: center;
background-color: var(--Main-Grey-White); background-color: var(--Main-Grey-White);
@@ -93,42 +95,8 @@
.select .dialCode { .select .dialCode {
border: none; border: none;
color: var(--Main-Grey-100); color: var(--UI-Text-High-contrast);
line-height: 1; line-height: 1;
justify-self: flex-start; justify-self: flex-start;
padding: 0; padding: 0;
} }
.inputContainer:has(.input:not(:focus):placeholder-shown) {
gap: 0;
grid-template-rows: 1fr;
}
.inputContainer:has(.input:active, .input:focus) {
border-color: var(--Scandic-Blue-90);
}
.inputContainer:has(.input[data-invalid="true"], .input[aria-invalid="true"]) {
border-color: var(--Scandic-Red-60);
}
.input {
background: none;
border: none;
color: var(--Main-Grey-100);
height: 18px;
margin: 0;
order: 2;
overflow: visible;
padding: 0;
}
.input:not(:active, :focus):placeholder-shown {
height: 0px;
}
.input:focus,
.input:focus:placeholder-shown {
height: 18px;
outline: none;
}

View File

@@ -2,9 +2,11 @@ import type { RegisterOptions } from "react-hook-form"
export type PhoneProps = { export type PhoneProps = {
ariaLabel?: string ariaLabel?: string
className?: string
disabled?: boolean disabled?: boolean
label: string label: string
name?: string name?: string
placeholder?: string placeholder?: string
readOnly?: boolean
registerOptions?: RegisterOptions registerOptions?: RegisterOptions
} }

View File

@@ -92,6 +92,10 @@
color: var(--Base-Text-Medium-contrast); color: var(--Base-Text-Medium-contrast);
} }
.uiTextHighContrast {
color: var(--UI-Text-High-contrast);
}
.uiTextPlaceholder { .uiTextPlaceholder {
color: var(--UI-Text-Placeholder); color: var(--UI-Text-Placeholder);
} }

View File

@@ -15,6 +15,7 @@ const config = {
white: styles.white, white: styles.white,
peach50: styles.peach50, peach50: styles.peach50,
peach80: styles.peach80, peach80: styles.peach80,
uiTextHighContrast: styles.uiTextHighContrast,
uiTextPlaceholder: styles.uiTextPlaceholder, uiTextPlaceholder: styles.uiTextPlaceholder,
}, },
textAlign: { textAlign: {

View File

@@ -35,6 +35,10 @@ p.caption {
text-decoration: var(--typography-Caption-Regular-textDecoration); text-decoration: var(--typography-Caption-Regular-textDecoration);
} }
.baseTextAccent {
color: var(--Base-Text-Accent);
}
.black { .black {
color: var(--Main-Grey-100); color: var(--Main-Grey-100);
} }
@@ -67,10 +71,14 @@ p.caption {
color: var(--UI-Text-Medium-contrast); color: var(--UI-Text-Medium-contrast);
} }
.uiTextHighContrast {
color: var(--UI-Text-High-contrast);
}
.center { .center {
text-align: center; text-align: center;
} }
.left { .left {
text-align: left; text-align: left;
} }

View File

@@ -11,6 +11,7 @@ export default function Caption({
fontOnly = false, fontOnly = false,
textAlign, textAlign,
textTransform, textTransform,
uppercase,
...props ...props
}: CaptionProps) { }: CaptionProps) {
const Comp = asChild ? Slot : "p" const Comp = asChild ? Slot : "p"
@@ -18,12 +19,14 @@ export default function Caption({
? fontOnlycaptionVariants({ ? fontOnlycaptionVariants({
className, className,
textTransform, textTransform,
uppercase,
}) })
: captionVariants({ : captionVariants({
className, className,
color, color,
textTransform, textTransform,
textAlign, textAlign,
uppercase,
}) })
return <Comp className={classNames} {...props} /> return <Comp className={classNames} {...props} />
} }

View File

@@ -5,12 +5,14 @@ import styles from "./caption.module.css"
const config = { const config = {
variants: { variants: {
color: { color: {
baseTextAccent: styles.baseTextAccent,
black: styles.black, black: styles.black,
burgundy: styles.burgundy, burgundy: styles.burgundy,
pale: styles.pale, pale: styles.pale,
textMediumContrast: styles.textMediumContrast, textMediumContrast: styles.textMediumContrast,
red: styles.red, red: styles.red,
white: styles.white, white: styles.white,
uiTextHighContrast: styles.uiTextHighContrast,
uiTextActive: styles.uiTextActive, uiTextActive: styles.uiTextActive,
uiTextMediumContrast: styles.uiTextMediumContrast, uiTextMediumContrast: styles.uiTextMediumContrast,
}, },
@@ -23,6 +25,9 @@ const config = {
center: styles.center, center: styles.center,
left: styles.left, left: styles.left,
}, },
uppercase: {
true: styles.uppercase,
},
}, },
defaultVariants: { defaultVariants: {
color: "black", color: "black",
@@ -39,6 +44,9 @@ const fontOnlyConfig = {
regular: styles.regular, regular: styles.regular,
uppercase: styles.uppercase, uppercase: styles.uppercase,
}, },
uppercase: {
true: styles.uppercase,
},
}, },
defaultVariants: { defaultVariants: {
textTransform: "regular", textTransform: "regular",

View File

@@ -59,7 +59,7 @@
color: var(--Scandic-Peach-50); color: var(--Scandic-Peach-50);
} }
.textMediumContrast { .uiTextMediumContrast {
color: var(--UI-Text-Medium-contrast); color: var(--UI-Text-Medium-contrast);
} }

View File

@@ -9,7 +9,7 @@ const config = {
burgundy: styles.burgundy, burgundy: styles.burgundy,
pale: styles.pale, pale: styles.pale,
peach50: styles.peach50, peach50: styles.peach50,
textMediumContrast: styles.textMediumContrast, uiTextMediumContrast: styles.uiTextMediumContrast,
uiTextPlaceholder: styles.uiTextPlaceholder, uiTextPlaceholder: styles.uiTextPlaceholder,
}, },
textAlign: { textAlign: {

View File

@@ -14,7 +14,6 @@
"Any changes you've made will be lost.": "Alle ændringer, du har foretaget, går tabt.", "Any changes you've made will be lost.": "Alle ændringer, du har foretaget, går tabt.",
"Are you sure you want to remove the card ending with {lastFourDigits} from your member profile?": "Er du sikker på, at du vil fjerne kortet, der slutter me {lastFourDigits} fra din medlemsprofil?", "Are you sure you want to remove the card ending with {lastFourDigits} from your member profile?": "Er du sikker på, at du vil fjerne kortet, der slutter me {lastFourDigits} fra din medlemsprofil?",
"Arrival date": "Ankomstdato", "Arrival date": "Ankomstdato",
"as of today": "fra idag",
"As our": "Som vores {level}", "As our": "Som vores {level}",
"As our Close Friend": "Som vores nære ven", "As our Close Friend": "Som vores nære ven",
"At latest": "Senest", "At latest": "Senest",
@@ -25,15 +24,12 @@
"Book": "Book", "Book": "Book",
"Book reward night": "Book bonusnat", "Book reward night": "Book bonusnat",
"Booking number": "Bookingnummer", "Booking number": "Bookingnummer",
"booking.nights": "{totalNights, plural, one {# nat} other {# nætter}}",
"Breakfast": "Morgenmad", "Breakfast": "Morgenmad",
"Breakfast excluded": "Morgenmad ikke inkluderet", "Breakfast excluded": "Morgenmad ikke inkluderet",
"Breakfast included": "Morgenmad inkluderet", "Breakfast included": "Morgenmad inkluderet",
"Bus terminal": "Busstation", "Bus terminal": "Busstation",
"Business": "Forretning", "Business": "Forretning",
"by": "inden",
"Cancel": "Afbestille", "Cancel": "Afbestille",
"characters": "tegn",
"Check in": "Check ind", "Check in": "Check ind",
"Check out": "Check ud", "Check out": "Check ud",
"Check out the credit cards saved to your profile. Pay with a saved card when signed in for a smoother web experience.": "Tjek de kreditkort, der er gemt på din profil. Betal med et gemt kort, når du er logget ind for en mere jævn weboplevelse.", "Check out the credit cards saved to your profile. Pay with a saved card when signed in for a smoother web experience.": "Tjek de kreditkort, der er gemt på din profil. Betal med et gemt kort, når du er logget ind for en mere jævn weboplevelse.",
@@ -70,6 +66,7 @@
"Distance to city centre": "{number}km til centrum", "Distance to city centre": "{number}km til centrum",
"Do you want to start the day with Scandics famous breakfast buffé?": "Vil du starte dagen med Scandics berømte morgenbuffet?", "Do you want to start the day with Scandics famous breakfast buffé?": "Vil du starte dagen med Scandics berømte morgenbuffet?",
"Download the Scandic app": "Download Scandic-appen", "Download the Scandic app": "Download Scandic-appen",
"Earn bonus nights & points": "Optjen bonusnætter og point",
"Edit": "Redigere", "Edit": "Redigere",
"Edit profile": "Rediger profil", "Edit profile": "Rediger profil",
"Email": "E-mail", "Email": "E-mail",
@@ -77,9 +74,9 @@
"Explore all levels and benefits": "Udforsk alle niveauer og fordele", "Explore all levels and benefits": "Udforsk alle niveauer og fordele",
"Explore nearby": "Udforsk i nærheden", "Explore nearby": "Udforsk i nærheden",
"Extras to your booking": "Tillæg til din booking", "Extras to your booking": "Tillæg til din booking",
"FAQ": "Ofte stillede spørgsmål",
"Failed to delete credit card, please try again later.": "Kunne ikke slette kreditkort. Prøv venligst igen senere.", "Failed to delete credit card, please try again later.": "Kunne ikke slette kreditkort. Prøv venligst igen senere.",
"Fair": "Messe", "Fair": "Messe",
"FAQ": "Ofte stillede spørgsmål",
"Find booking": "Find booking", "Find booking": "Find booking",
"Find hotels": "Find hotel", "Find hotels": "Find hotel",
"Flexibility": "Fleksibilitet", "Flexibility": "Fleksibilitet",
@@ -88,6 +85,7 @@
"Free rebooking": "Gratis ombooking", "Free rebooking": "Gratis ombooking",
"From": "Fra", "From": "Fra",
"Get inspired": "Bliv inspireret", "Get inspired": "Bliv inspireret",
"Get member benefits & offers": "Få medlemsfordele og tilbud",
"Go back to edit": "Gå tilbage til redigering", "Go back to edit": "Gå tilbage til redigering",
"Go back to overview": "Gå tilbage til oversigten", "Go back to overview": "Gå tilbage til oversigten",
"Guests & Rooms": "Gæster & værelser", "Guests & Rooms": "Gæster & værelser",
@@ -97,15 +95,12 @@
"Hotel": "Hotel", "Hotel": "Hotel",
"Hotel facilities": "Hotel faciliteter", "Hotel facilities": "Hotel faciliteter",
"Hotel surroundings": "Hotel omgivelser", "Hotel surroundings": "Hotel omgivelser",
"hotelPages.rooms.roomCard.person": "person",
"hotelPages.rooms.roomCard.persons": "personer",
"hotelPages.rooms.roomCard.seeRoomDetails": "Se værelsesdetaljer",
"Hotels": "Hoteller", "Hotels": "Hoteller",
"How do you want to sleep?": "Hvordan vil du sove?", "How do you want to sleep?": "Hvordan vil du sove?",
"How it works": "Hvordan det virker", "How it works": "Hvordan det virker",
"Image gallery": "Billedgalleri", "Image gallery": "Billedgalleri",
"Join Scandic Friends": "Tilmeld dig Scandic Friends", "Join Scandic Friends": "Tilmeld dig Scandic Friends",
"km to city center": "km til byens centrum", "Join at no cost": "Tilmeld dig uden omkostninger",
"Language": "Sprog", "Language": "Sprog",
"Latest searches": "Seneste søgninger", "Latest searches": "Seneste søgninger",
"Level": "Niveau", "Level": "Niveau",
@@ -132,9 +127,9 @@
"Member price": "Medlemspris", "Member price": "Medlemspris",
"Member price from": "Medlemspris fra", "Member price from": "Medlemspris fra",
"Members": "Medlemmer", "Members": "Medlemmer",
"Membership cards": "Medlemskort",
"Membership ID": "Medlems-id", "Membership ID": "Medlems-id",
"Membership ID copied to clipboard": "Medlems-ID kopieret til udklipsholder", "Membership ID copied to clipboard": "Medlems-ID kopieret til udklipsholder",
"Membership cards": "Medlemskort",
"Menu": "Menu", "Menu": "Menu",
"Modify": "Ændre", "Modify": "Ændre",
"Month": "Måned", "Month": "Måned",
@@ -149,9 +144,6 @@
"Nearby companies": "Nærliggende virksomheder", "Nearby companies": "Nærliggende virksomheder",
"New password": "Nyt kodeord", "New password": "Nyt kodeord",
"Next": "Næste", "Next": "Næste",
"next level:": "Næste niveau:",
"night": "nat",
"nights": "nætter",
"Nights needed to level up": "Nætter nødvendige for at komme i niveau", "Nights needed to level up": "Nætter nødvendige for at komme i niveau",
"No content published": "Intet indhold offentliggjort", "No content published": "Intet indhold offentliggjort",
"No matching location found": "Der blev ikke fundet nogen matchende placering", "No matching location found": "Der blev ikke fundet nogen matchende placering",
@@ -162,13 +154,11 @@
"Non-refundable": "Ikke-refunderbart", "Non-refundable": "Ikke-refunderbart",
"Not found": "Ikke fundet", "Not found": "Ikke fundet",
"Nr night, nr adult": "{nights, number} nat, {adults, number} voksen", "Nr night, nr adult": "{nights, number} nat, {adults, number} voksen",
"number": "nummer",
"On your journey": "På din rejse", "On your journey": "På din rejse",
"Open": "Åben", "Open": "Åben",
"Open language menu": "Åbn sprogmenuen", "Open language menu": "Åbn sprogmenuen",
"Open menu": "Åbn menuen", "Open menu": "Åbn menuen",
"Open my pages menu": "Åbn mine sider menuen", "Open my pages menu": "Åbn mine sider menuen",
"or": "eller",
"Overview": "Oversigt", "Overview": "Oversigt",
"Parking": "Parkering", "Parking": "Parkering",
"Parking / Garage": "Parkering / Garage", "Parking / Garage": "Parkering / Garage",
@@ -180,7 +170,6 @@
"Phone is required": "Telefonnummer er påkrævet", "Phone is required": "Telefonnummer er påkrævet",
"Phone number": "Telefonnummer", "Phone number": "Telefonnummer",
"Please enter a valid phone number": "Indtast venligst et gyldigt telefonnummer", "Please enter a valid phone number": "Indtast venligst et gyldigt telefonnummer",
"points": "Point",
"Points": "Point", "Points": "Point",
"Points being calculated": "Point udregnes", "Points being calculated": "Point udregnes",
"Points earned prior to May 1, 2021": "Point optjent inden 1. maj 2021", "Points earned prior to May 1, 2021": "Point optjent inden 1. maj 2021",
@@ -223,29 +212,25 @@
"Something went wrong and we couldn't add your card. Please try again later.": "Noget gik galt, og vi kunne ikke tilføje dit kort. Prøv venligst igen senere.", "Something went wrong and we couldn't add your card. Please try again later.": "Noget gik galt, og vi kunne ikke tilføje dit kort. Prøv venligst igen senere.",
"Something went wrong and we couldn't remove your card. Please try again later.": "Noget gik galt, og vi kunne ikke fjerne dit kort. Prøv venligst igen senere.", "Something went wrong and we couldn't remove your card. Please try again later.": "Noget gik galt, og vi kunne ikke fjerne dit kort. Prøv venligst igen senere.",
"Something went wrong!": "Noget gik galt!", "Something went wrong!": "Noget gik galt!",
"special character": "speciel karakter",
"spendable points expiring by": "{points} Brugbare point udløber den {date}",
"Sports": "Sport", "Sports": "Sport",
"Standard price": "Standardpris", "Standard price": "Standardpris",
"Street": "Gade", "Street": "Gade",
"Successfully updated profile!": "Profilen er opdateret med succes!", "Successfully updated profile!": "Profilen er opdateret med succes!",
"Summary": "Opsummering", "Summary": "Opsummering",
"TUI Points": "TUI Points",
"Tell us what information and updates you'd like to receive, and how, by clicking the link below.": "Fortæl os, hvilke oplysninger og opdateringer du gerne vil modtage, og hvordan, ved at klikke på linket nedenfor.", "Tell us what information and updates you'd like to receive, and how, by clicking the link below.": "Fortæl os, hvilke oplysninger og opdateringer du gerne vil modtage, og hvordan, ved at klikke på linket nedenfor.",
"Thank you": "Tak", "Thank you": "Tak",
"Theatre": "Teater", "Theatre": "Teater",
"There are no transactions to display": "Der er ingen transaktioner at vise", "There are no transactions to display": "Der er ingen transaktioner at vise",
"Things nearby HOTEL_NAME": "Ting i nærheden af {hotelName}", "Things nearby HOTEL_NAME": "Ting i nærheden af {hotelName}",
"to": "til",
"Total Points": "Samlet antal point", "Total Points": "Samlet antal point",
"Tourist": "Turist", "Tourist": "Turist",
"Transaction date": "Overførselsdato", "Transaction date": "Overførselsdato",
"Transactions": "Transaktioner", "Transactions": "Transaktioner",
"Transportations": "Transport", "Transportations": "Transport",
"Tripadvisor reviews": "{rating} ({count} anmeldelser på Tripadvisor)", "Tripadvisor reviews": "{rating} ({count} anmeldelser på Tripadvisor)",
"TUI Points": "TUI Points",
"Type of bed": "Sengtype", "Type of bed": "Sengtype",
"Type of room": "Værelsestype", "Type of room": "Værelsestype",
"uppercase letter": "stort bogstav",
"Use bonus cheque": "Brug Bonus Cheque", "Use bonus cheque": "Brug Bonus Cheque",
"User information": "Brugeroplysninger", "User information": "Brugeroplysninger",
"View as list": "Vis som liste", "View as list": "Vis som liste",
@@ -271,9 +256,9 @@
"You canceled adding a new credit card.": "Du har annulleret tilføjelsen af et nyt kreditkort.", "You canceled adding a new credit card.": "Du har annulleret tilføjelsen af et nyt kreditkort.",
"You have no previous stays.": "Du har ingen tidligere ophold.", "You have no previous stays.": "Du har ingen tidligere ophold.",
"You have no upcoming stays.": "Du har ingen kommende ophold.", "You have no upcoming stays.": "Du har ingen kommende ophold.",
"Your Challenges Conquer & Earn!": "Dine udfordringer Overvind og tjen!",
"Your card was successfully removed!": "Dit kort blev fjernet!", "Your card was successfully removed!": "Dit kort blev fjernet!",
"Your card was successfully saved!": "Dit kort blev gemt!", "Your card was successfully saved!": "Dit kort blev gemt!",
"Your Challenges Conquer & Earn!": "Dine udfordringer Overvind og tjen!",
"Your current level": "Dit nuværende niveau", "Your current level": "Dit nuværende niveau",
"Your details": "Dine oplysninger", "Your details": "Dine oplysninger",
"Your level": "Dit niveau", "Your level": "Dit niveau",
@@ -281,5 +266,23 @@
"Zip code": "Postnummer", "Zip code": "Postnummer",
"Zoo": "Zoo", "Zoo": "Zoo",
"Zoom in": "Zoom ind", "Zoom in": "Zoom ind",
"Zoom out": "Zoom ud" "Zoom out": "Zoom ud",
"as of today": "fra idag",
"booking.nights": "{totalNights, plural, one {# nat} other {# nætter}}",
"by": "inden",
"characters": "tegn",
"hotelPages.rooms.roomCard.person": "person",
"hotelPages.rooms.roomCard.persons": "personer",
"hotelPages.rooms.roomCard.seeRoomDetails": "Se værelsesdetaljer",
"km to city center": "km til byens centrum",
"next level:": "Næste niveau:",
"night": "nat",
"nights": "nætter",
"number": "nummer",
"or": "eller",
"points": "Point",
"special character": "speciel karakter",
"spendable points expiring by": "{points} Brugbare point udløber den {date}",
"to": "til",
"uppercase letter": "stort bogstav"
} }

View File

@@ -279,6 +279,9 @@
"Your points to spend": "Meine Punkte", "Your points to spend": "Meine Punkte",
"Zip code": "PLZ", "Zip code": "PLZ",
"Zoo": "Zoo", "Zoo": "Zoo",
"Earn bonus nights & points": "Sammeln Sie Bonusnächte und -punkte",
"Get member benefits & offers": "Holen Sie sich Vorteile und Angebote für Mitglieder",
"Join at no cost": "Kostenlos beitreten",
"Zoom in": "Vergrößern", "Zoom in": "Vergrößern",
"Zoom out": "Verkleinern" "Zoom out": "Verkleinern"
} }

View File

@@ -14,7 +14,6 @@
"Any changes you've made will be lost.": "Any changes you've made will be lost.", "Any changes you've made will be lost.": "Any changes you've made will be lost.",
"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?", "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", "Arrival date": "Arrival date",
"as of today": "as of today",
"As our": "As our {level}", "As our": "As our {level}",
"As our Close Friend": "As our Close Friend", "As our Close Friend": "As our Close Friend",
"At latest": "At latest", "At latest": "At latest",
@@ -25,17 +24,12 @@
"Book": "Book", "Book": "Book",
"Book reward night": "Book reward night", "Book reward night": "Book reward night",
"Booking number": "Booking number", "Booking number": "Booking number",
"booking.adults": "{totalAdults, plural, one {# adult} other {# adults}}",
"booking.nights": "{totalNights, plural, one {# night} other {# nights}}",
"booking.rooms": "{totalRooms, plural, one {# room} other {# rooms}}",
"Breakfast": "Breakfast", "Breakfast": "Breakfast",
"Breakfast excluded": "Breakfast excluded", "Breakfast excluded": "Breakfast excluded",
"Breakfast included": "Breakfast included", "Breakfast included": "Breakfast included",
"Bus terminal": "Bus terminal", "Bus terminal": "Bus terminal",
"Business": "Business", "Business": "Business",
"by": "by",
"Cancel": "Cancel", "Cancel": "Cancel",
"characters": "characters",
"Check in": "Check in", "Check in": "Check in",
"Check out": "Check out", "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 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.",
@@ -73,26 +67,31 @@
"Distance to city centre": "{number}km to city centre", "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é?", "Do you want to start the day with Scandics famous breakfast buffé?": "Do you want to start the day with Scandics famous breakfast buffé?",
"Download the Scandic app": "Download the Scandic app", "Download the Scandic app": "Download the Scandic app",
"Earn bonus nights & points": "Earn bonus nights & points",
"Edit": "Edit", "Edit": "Edit",
"Edit profile": "Edit profile", "Edit profile": "Edit profile",
"Email": "Email", "Email": "Email",
"Email address": "Email address",
"Enter destination or hotel": "Enter destination or hotel", "Enter destination or hotel": "Enter destination or hotel",
"Explore all levels and benefits": "Explore all levels and benefits", "Explore all levels and benefits": "Explore all levels and benefits",
"Explore nearby": "Explore nearby", "Explore nearby": "Explore nearby",
"Extras to your booking": "Extras to your booking", "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.", "Failed to delete credit card, please try again later.": "Failed to delete credit card, please try again later.",
"Fair": "Fair", "Fair": "Fair",
"FAQ": "FAQ",
"Find booking": "Find booking", "Find booking": "Find booking",
"Find hotels": "Find hotels", "Find hotels": "Find hotels",
"Firstname": "Firstname",
"Flexibility": "Flexibility", "Flexibility": "Flexibility",
"Former Scandic Hotel": "Former Scandic Hotel", "Former Scandic Hotel": "Former Scandic Hotel",
"Free cancellation": "Free cancellation", "Free cancellation": "Free cancellation",
"Free rebooking": "Free rebooking", "Free rebooking": "Free rebooking",
"From": "From", "From": "From",
"Get inspired": "Get inspired", "Get inspired": "Get inspired",
"Get member benefits & offers": "Get member benefits & offers",
"Go back to edit": "Go back to edit", "Go back to edit": "Go back to edit",
"Go back to overview": "Go back to overview", "Go back to overview": "Go back to overview",
"Guest information": "Guest information",
"Guests & Rooms": "Guests & Rooms", "Guests & Rooms": "Guests & Rooms",
"Hi": "Hi", "Hi": "Hi",
"Highest level": "Highest level", "Highest level": "Highest level",
@@ -100,16 +99,14 @@
"Hotel": "Hotel", "Hotel": "Hotel",
"Hotel facilities": "Hotel facilities", "Hotel facilities": "Hotel facilities",
"Hotel surroundings": "Hotel surroundings", "Hotel surroundings": "Hotel surroundings",
"hotelPages.rooms.roomCard.person": "person",
"hotelPages.rooms.roomCard.persons": "persons",
"hotelPages.rooms.roomCard.seeRoomDetails": "See room details",
"Hotels": "Hotels", "Hotels": "Hotels",
"How do you want to sleep?": "How do you want to sleep?", "How do you want to sleep?": "How do you want to sleep?",
"How it works": "How it works", "How it works": "How it works",
"Image gallery": "Image gallery", "Image gallery": "Image gallery",
"Join Scandic Friends": "Join Scandic Friends", "Join Scandic Friends": "Join Scandic Friends",
"km to city center": "km to city center", "Join at no cost": "Join at no cost",
"Language": "Language", "Language": "Language",
"Lastname": "Lastname",
"Latest searches": "Latest searches", "Latest searches": "Latest searches",
"Level": "Level", "Level": "Level",
"Level 1": "Level 1", "Level 1": "Level 1",
@@ -135,9 +132,9 @@
"Member price": "Member price", "Member price": "Member price",
"Member price from": "Member price from", "Member price from": "Member price from",
"Members": "Members", "Members": "Members",
"Membership cards": "Membership cards",
"Membership ID": "Membership ID", "Membership ID": "Membership ID",
"Membership ID copied to clipboard": "Membership ID copied to clipboard", "Membership ID copied to clipboard": "Membership ID copied to clipboard",
"Membership cards": "Membership cards",
"Menu": "Menu", "Menu": "Menu",
"Modify": "Modify", "Modify": "Modify",
"Month": "Month", "Month": "Month",
@@ -152,9 +149,6 @@
"Nearby companies": "Nearby companies", "Nearby companies": "Nearby companies",
"New password": "New password", "New password": "New password",
"Next": "Next", "Next": "Next",
"next level:": "next level:",
"night": "night",
"nights": "nights",
"Nights needed to level up": "Nights needed to level up", "Nights needed to level up": "Nights needed to level up",
"No content published": "No content published", "No content published": "No content published",
"No matching location found": "No matching location found", "No matching location found": "No matching location found",
@@ -165,13 +159,11 @@
"Non-refundable": "Non-refundable", "Non-refundable": "Non-refundable",
"Not found": "Not found", "Not found": "Not found",
"Nr night, nr adult": "{nights, number} night, {adults, number} adult", "Nr night, nr adult": "{nights, number} night, {adults, number} adult",
"number": "number",
"On your journey": "On your journey", "On your journey": "On your journey",
"Open": "Open", "Open": "Open",
"Open language menu": "Open language menu", "Open language menu": "Open language menu",
"Open menu": "Open menu", "Open menu": "Open menu",
"Open my pages menu": "Open my pages menu", "Open my pages menu": "Open my pages menu",
"or": "or",
"Overview": "Overview", "Overview": "Overview",
"Parking": "Parking", "Parking": "Parking",
"Parking / Garage": "Parking / Garage", "Parking / Garage": "Parking / Garage",
@@ -183,7 +175,6 @@
"Phone is required": "Phone is required", "Phone is required": "Phone is required",
"Phone number": "Phone number", "Phone number": "Phone number",
"Please enter a valid phone number": "Please enter a valid phone number", "Please enter a valid phone number": "Please enter a valid phone number",
"points": "Points",
"Points": "Points", "Points": "Points",
"Points being calculated": "Points being calculated", "Points being calculated": "Points being calculated",
"Points earned prior to May 1, 2021": "Points earned prior to May 1, 2021", "Points earned prior to May 1, 2021": "Points earned prior to May 1, 2021",
@@ -191,6 +182,7 @@
"Points needed to level up": "Points needed to level up", "Points needed to level up": "Points needed to level up",
"Points needed to stay on level": "Points needed to stay on level", "Points needed to stay on level": "Points needed to stay on level",
"Previous victories": "Previous victories", "Previous victories": "Previous victories",
"Proceed to payment method": "Proceed to payment method",
"Public price from": "Public price from", "Public price from": "Public price from",
"Public transport": "Public transport", "Public transport": "Public transport",
"Read more": "Read more", "Read more": "Read more",
@@ -227,29 +219,25 @@
"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 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 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!", "Something went wrong!": "Something went wrong!",
"special character": "special character",
"spendable points expiring by": "{points} spendable points expiring by {date}",
"Sports": "Sports", "Sports": "Sports",
"Standard price": "Standard price", "Standard price": "Standard price",
"Street": "Street", "Street": "Street",
"Successfully updated profile!": "Successfully updated profile!", "Successfully updated profile!": "Successfully updated profile!",
"Summary": "Summary", "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.", "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.",
"Thank you": "Thank you", "Thank you": "Thank you",
"Theatre": "Theatre", "Theatre": "Theatre",
"There are no transactions to display": "There are no transactions to display", "There are no transactions to display": "There are no transactions to display",
"Things nearby HOTEL_NAME": "Things nearby {hotelName}", "Things nearby HOTEL_NAME": "Things nearby {hotelName}",
"to": "to",
"Total Points": "Total Points", "Total Points": "Total Points",
"Tourist": "Tourist", "Tourist": "Tourist",
"Transaction date": "Transaction date", "Transaction date": "Transaction date",
"Transactions": "Transactions", "Transactions": "Transactions",
"Transportations": "Transportations", "Transportations": "Transportations",
"Tripadvisor reviews": "{rating} ({count} reviews on Tripadvisor)", "Tripadvisor reviews": "{rating} ({count} reviews on Tripadvisor)",
"TUI Points": "TUI Points",
"Type of bed": "Type of bed", "Type of bed": "Type of bed",
"Type of room": "Type of room", "Type of room": "Type of room",
"uppercase letter": "uppercase letter",
"Use bonus cheque": "Use bonus cheque", "Use bonus cheque": "Use bonus cheque",
"User information": "User information", "User information": "User information",
"View as list": "View as list", "View as list": "View as list",
@@ -275,9 +263,9 @@
"You canceled adding a new credit card.": "You canceled adding a new credit card.", "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 previous stays.": "You have no previous stays.",
"You have no upcoming stays.": "You have no upcoming 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 removed!": "Your card was successfully removed!",
"Your card was successfully saved!": "Your card was successfully saved!", "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 current level": "Your current level",
"Your details": "Your details", "Your details": "Your details",
"Your level": "Your level", "Your level": "Your level",
@@ -285,5 +273,26 @@
"Zip code": "Zip code", "Zip code": "Zip code",
"Zoo": "Zoo", "Zoo": "Zoo",
"Zoom in": "Zoom in", "Zoom in": "Zoom in",
"Zoom out": "Zoom out" "Zoom out": "Zoom out",
"as of today": "as of today",
"booking.adults": "{totalAdults, plural, one {# adult} other {# adults}}",
"booking.nights": "{totalNights, plural, one {# night} other {# nights}}",
"booking.rooms": "{totalRooms, plural, one {# room} other {# rooms}}",
"by": "by",
"characters": "characters",
"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",
"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",
"{difference}{amount} {currency}": "{difference}{amount} {currency}"
} }

View File

@@ -14,7 +14,6 @@
"Any changes you've made will be lost.": "Kaikki tekemäsi muutokset menetetään.", "Any changes you've made will be lost.": "Kaikki tekemäsi muutokset menetetään.",
"Are you sure you want to remove the card ending with {lastFourDigits} from your member profile?": "Haluatko varmasti poistaa kortin, joka päättyy numeroon {lastFourDigits} jäsenprofiilistasi?", "Are you sure you want to remove the card ending with {lastFourDigits} from your member profile?": "Haluatko varmasti poistaa kortin, joka päättyy numeroon {lastFourDigits} jäsenprofiilistasi?",
"Arrival date": "Saapumispäivä", "Arrival date": "Saapumispäivä",
"as of today": "tänään",
"As our": "{level}-etu", "As our": "{level}-etu",
"As our Close Friend": "Läheisenä ystävänämme", "As our Close Friend": "Läheisenä ystävänämme",
"At latest": "Viimeistään", "At latest": "Viimeistään",
@@ -25,15 +24,12 @@
"Book": "Varaa", "Book": "Varaa",
"Book reward night": "Kirjapalkinto-ilta", "Book reward night": "Kirjapalkinto-ilta",
"Booking number": "Varausnumero", "Booking number": "Varausnumero",
"booking.nights": "{totalNights, plural, one {# yö} other {# yötä}}",
"Breakfast": "Aamiainen", "Breakfast": "Aamiainen",
"Breakfast excluded": "Aamiainen ei sisälly", "Breakfast excluded": "Aamiainen ei sisälly",
"Breakfast included": "Aamiainen sisältyy", "Breakfast included": "Aamiainen sisältyy",
"Bus terminal": "Bussiasema", "Bus terminal": "Bussiasema",
"Business": "Business", "Business": "Business",
"by": "mennessä",
"Cancel": "Peruuttaa", "Cancel": "Peruuttaa",
"characters": "hahmoja",
"Check in": "Sisäänkirjautuminen", "Check in": "Sisäänkirjautuminen",
"Check out": "Uloskirjautuminen", "Check out": "Uloskirjautuminen",
"Check out the credit cards saved to your profile. Pay with a saved card when signed in for a smoother web experience.": "Tarkista profiiliisi tallennetut luottokortit. Maksa tallennetulla kortilla kirjautuneena, jotta verkkokokemus on sujuvampi.", "Check out the credit cards saved to your profile. Pay with a saved card when signed in for a smoother web experience.": "Tarkista profiiliisi tallennetut luottokortit. Maksa tallennetulla kortilla kirjautuneena, jotta verkkokokemus on sujuvampi.",
@@ -70,6 +66,7 @@
"Distance to city centre": "{number}km Etäisyys kaupunkiin", "Distance to city centre": "{number}km Etäisyys kaupunkiin",
"Do you want to start the day with Scandics famous breakfast buffé?": "Haluatko aloittaa päiväsi Scandicsin kuuluisalla aamiaisbuffella?", "Do you want to start the day with Scandics famous breakfast buffé?": "Haluatko aloittaa päiväsi Scandicsin kuuluisalla aamiaisbuffella?",
"Download the Scandic app": "Lataa Scandic-sovellus", "Download the Scandic app": "Lataa Scandic-sovellus",
"Earn bonus nights & points": "Ansaitse bonusöitä ja -pisteitä",
"Edit": "Muokata", "Edit": "Muokata",
"Edit profile": "Muokkaa profiilia", "Edit profile": "Muokkaa profiilia",
"Email": "Sähköposti", "Email": "Sähköposti",
@@ -77,9 +74,9 @@
"Explore all levels and benefits": "Tutustu kaikkiin tasoihin ja etuihin", "Explore all levels and benefits": "Tutustu kaikkiin tasoihin ja etuihin",
"Explore nearby": "Tutustu lähialueeseen", "Explore nearby": "Tutustu lähialueeseen",
"Extras to your booking": "Varauksessa lisäpalveluita", "Extras to your booking": "Varauksessa lisäpalveluita",
"FAQ": "UKK",
"Failed to delete credit card, please try again later.": "Luottokortin poistaminen epäonnistui, yritä myöhemmin uudelleen.", "Failed to delete credit card, please try again later.": "Luottokortin poistaminen epäonnistui, yritä myöhemmin uudelleen.",
"Fair": "Messukeskus", "Fair": "Messukeskus",
"FAQ": "UKK",
"Find booking": "Etsi varaus", "Find booking": "Etsi varaus",
"Find hotels": "Etsi hotelleja", "Find hotels": "Etsi hotelleja",
"Flexibility": "Joustavuus", "Flexibility": "Joustavuus",
@@ -88,6 +85,7 @@
"Free rebooking": "Ilmainen uudelleenvaraus", "Free rebooking": "Ilmainen uudelleenvaraus",
"From": "From", "From": "From",
"Get inspired": "Inspiroidu", "Get inspired": "Inspiroidu",
"Get member benefits & offers": "Hanki jäsenetuja ja -tarjouksia",
"Go back to edit": "Palaa muokkaamaan", "Go back to edit": "Palaa muokkaamaan",
"Go back to overview": "Palaa yleiskatsaukseen", "Go back to overview": "Palaa yleiskatsaukseen",
"Guests & Rooms": "Vieraat & Huoneet", "Guests & Rooms": "Vieraat & Huoneet",
@@ -97,15 +95,12 @@
"Hotel": "Hotelli", "Hotel": "Hotelli",
"Hotel facilities": "Hotellin palvelut", "Hotel facilities": "Hotellin palvelut",
"Hotel surroundings": "Hotellin ympäristö", "Hotel surroundings": "Hotellin ympäristö",
"hotelPages.rooms.roomCard.person": "henkilö",
"hotelPages.rooms.roomCard.persons": "Henkilöä",
"hotelPages.rooms.roomCard.seeRoomDetails": "Katso huoneen tiedot",
"Hotels": "Hotellit", "Hotels": "Hotellit",
"How do you want to sleep?": "Kuinka haluat nukkua?", "How do you want to sleep?": "Kuinka haluat nukkua?",
"How it works": "Kuinka se toimii", "How it works": "Kuinka se toimii",
"Image gallery": "Kuvagalleria", "Image gallery": "Kuvagalleria",
"Join Scandic Friends": "Liity jäseneksi", "Join Scandic Friends": "Liity jäseneksi",
"km to city center": "km keskustaan", "Join at no cost": "Liity maksutta",
"Language": "Kieli", "Language": "Kieli",
"Latest searches": "Viimeisimmät haut", "Latest searches": "Viimeisimmät haut",
"Level": "Level", "Level": "Level",
@@ -132,9 +127,9 @@
"Member price": "Jäsenhinta", "Member price": "Jäsenhinta",
"Member price from": "Jäsenhinta alkaen", "Member price from": "Jäsenhinta alkaen",
"Members": "Jäsenet", "Members": "Jäsenet",
"Membership cards": "Jäsenkortit",
"Membership ID": "Jäsentunnus", "Membership ID": "Jäsentunnus",
"Membership ID copied to clipboard": "Jäsenyystunnus kopioitu leikepöydälle", "Membership ID copied to clipboard": "Jäsenyystunnus kopioitu leikepöydälle",
"Membership cards": "Jäsenkortit",
"Menu": "Valikko", "Menu": "Valikko",
"Modify": "Muokkaa", "Modify": "Muokkaa",
"Month": "Kuukausi", "Month": "Kuukausi",
@@ -149,9 +144,6 @@
"Nearby companies": "Läheiset yritykset", "Nearby companies": "Läheiset yritykset",
"New password": "Uusi salasana", "New password": "Uusi salasana",
"Next": "Seuraava", "Next": "Seuraava",
"next level:": "pistettä tasolle:",
"night": "yö",
"nights": "yötä",
"Nights needed to level up": "Yöt, joita tarvitaan tasolle", "Nights needed to level up": "Yöt, joita tarvitaan tasolle",
"No content published": "Ei julkaistua sisältöä", "No content published": "Ei julkaistua sisältöä",
"No matching location found": "Vastaavaa sijaintia ei löytynyt", "No matching location found": "Vastaavaa sijaintia ei löytynyt",
@@ -162,13 +154,11 @@
"Non-refundable": "Ei palautettavissa", "Non-refundable": "Ei palautettavissa",
"Not found": "Ei löydetty", "Not found": "Ei löydetty",
"Nr night, nr adult": "{nights, number} yö, {adults, number} aikuinen", "Nr night, nr adult": "{nights, number} yö, {adults, number} aikuinen",
"number": "määrä",
"On your journey": "Matkallasi", "On your journey": "Matkallasi",
"Open": "Avata", "Open": "Avata",
"Open language menu": "Avaa kielivalikko", "Open language menu": "Avaa kielivalikko",
"Open menu": "Avaa valikko", "Open menu": "Avaa valikko",
"Open my pages menu": "Avaa omat sivut -valikko", "Open my pages menu": "Avaa omat sivut -valikko",
"or": "tai",
"Overview": "Yleiskatsaus", "Overview": "Yleiskatsaus",
"Parking": "Pysäköinti", "Parking": "Pysäköinti",
"Parking / Garage": "Pysäköinti / Autotalli", "Parking / Garage": "Pysäköinti / Autotalli",
@@ -180,7 +170,6 @@
"Phone is required": "Puhelin vaaditaan", "Phone is required": "Puhelin vaaditaan",
"Phone number": "Puhelinnumero", "Phone number": "Puhelinnumero",
"Please enter a valid phone number": "Ole hyvä ja näppäile voimassaoleva puhelinnumero", "Please enter a valid phone number": "Ole hyvä ja näppäile voimassaoleva puhelinnumero",
"points": "pistettä",
"Points": "Pisteet", "Points": "Pisteet",
"Points being calculated": "Pisteitä lasketaan", "Points being calculated": "Pisteitä lasketaan",
"Points earned prior to May 1, 2021": "Pisteet, jotka ansaittu ennen 1.5.2021", "Points earned prior to May 1, 2021": "Pisteet, jotka ansaittu ennen 1.5.2021",
@@ -223,29 +212,25 @@
"Something went wrong and we couldn't add your card. Please try again later.": "Jotain meni pieleen, emmekä voineet lisätä korttiasi. Yritä myöhemmin uudelleen.", "Something went wrong and we couldn't add your card. Please try again later.": "Jotain meni pieleen, emmekä voineet lisätä korttiasi. Yritä myöhemmin uudelleen.",
"Something went wrong and we couldn't remove your card. Please try again later.": "Jotain meni pieleen, emmekä voineet poistaa korttiasi. Yritä myöhemmin uudelleen.", "Something went wrong and we couldn't remove your card. Please try again later.": "Jotain meni pieleen, emmekä voineet poistaa korttiasi. Yritä myöhemmin uudelleen.",
"Something went wrong!": "Jotain meni pieleen!", "Something went wrong!": "Jotain meni pieleen!",
"special character": "erikoishahmo",
"spendable points expiring by": "{points} pistettä vanhenee {date} mennessä",
"Sports": "Urheilu", "Sports": "Urheilu",
"Standard price": "Normaali hinta", "Standard price": "Normaali hinta",
"Street": "Katu", "Street": "Katu",
"Successfully updated profile!": "Profiilin päivitys onnistui!", "Successfully updated profile!": "Profiilin päivitys onnistui!",
"Summary": "Yhteenveto", "Summary": "Yhteenveto",
"TUI Points": "TUI Points",
"Tell us what information and updates you'd like to receive, and how, by clicking the link below.": "Kerro meille, mitä tietoja ja päivityksiä haluat saada ja miten, napsauttamalla alla olevaa linkkiä.", "Tell us what information and updates you'd like to receive, and how, by clicking the link below.": "Kerro meille, mitä tietoja ja päivityksiä haluat saada ja miten, napsauttamalla alla olevaa linkkiä.",
"Thank you": "Kiitos", "Thank you": "Kiitos",
"Theatre": "Teatteri", "Theatre": "Teatteri",
"There are no transactions to display": "Näytettäviä tapahtumia ei ole", "There are no transactions to display": "Näytettäviä tapahtumia ei ole",
"Things nearby HOTEL_NAME": "Lähellä olevia asioita {hotelName}", "Things nearby HOTEL_NAME": "Lähellä olevia asioita {hotelName}",
"to": "to",
"Total Points": "Kokonaispisteet", "Total Points": "Kokonaispisteet",
"Tourist": "Turisti", "Tourist": "Turisti",
"Transaction date": "Tapahtuman päivämäärä", "Transaction date": "Tapahtuman päivämäärä",
"Transactions": "Tapahtumat", "Transactions": "Tapahtumat",
"Transportations": "Kuljetukset", "Transportations": "Kuljetukset",
"Tripadvisor reviews": "{rating} ({count} arvostelua TripAdvisorissa)", "Tripadvisor reviews": "{rating} ({count} arvostelua TripAdvisorissa)",
"TUI Points": "TUI Points",
"Type of bed": "Vuodetyyppi", "Type of bed": "Vuodetyyppi",
"Type of room": "Huonetyyppi", "Type of room": "Huonetyyppi",
"uppercase letter": "iso kirjain",
"Use bonus cheque": "Käytä bonussekkiä", "Use bonus cheque": "Käytä bonussekkiä",
"User information": "Käyttäjän tiedot", "User information": "Käyttäjän tiedot",
"View as list": "Näytä listana", "View as list": "Näytä listana",
@@ -271,9 +256,9 @@
"You canceled adding a new credit card.": "Peruutit uuden luottokortin lisäämisen.", "You canceled adding a new credit card.": "Peruutit uuden luottokortin lisäämisen.",
"You have no previous stays.": "Sinulla ei ole aiempia majoituksia.", "You have no previous stays.": "Sinulla ei ole aiempia majoituksia.",
"You have no upcoming stays.": "Sinulla ei ole tulevia majoituksia.", "You have no upcoming stays.": "Sinulla ei ole tulevia majoituksia.",
"Your Challenges Conquer & Earn!": "Voita ja ansaitse haasteesi!",
"Your card was successfully removed!": "Korttisi poistettiin onnistuneesti!", "Your card was successfully removed!": "Korttisi poistettiin onnistuneesti!",
"Your card was successfully saved!": "Korttisi tallennettu onnistuneesti!", "Your card was successfully saved!": "Korttisi tallennettu onnistuneesti!",
"Your Challenges Conquer & Earn!": "Voita ja ansaitse haasteesi!",
"Your current level": "Nykyinen tasosi", "Your current level": "Nykyinen tasosi",
"Your details": "Tietosi", "Your details": "Tietosi",
"Your level": "Tasosi", "Your level": "Tasosi",
@@ -281,5 +266,23 @@
"Zip code": "Postinumero", "Zip code": "Postinumero",
"Zoo": "Eläintarha", "Zoo": "Eläintarha",
"Zoom in": "Lähennä", "Zoom in": "Lähennä",
"Zoom out": "Loitonna" "Zoom out": "Loitonna",
"as of today": "tänään",
"booking.nights": "{totalNights, plural, one {# yö} other {# yötä}}",
"by": "mennessä",
"characters": "hahmoja",
"hotelPages.rooms.roomCard.person": "henkilö",
"hotelPages.rooms.roomCard.persons": "Henkilöä",
"hotelPages.rooms.roomCard.seeRoomDetails": "Katso huoneen tiedot",
"km to city center": "km keskustaan",
"next level:": "pistettä tasolle:",
"night": "yö",
"nights": "yötä",
"number": "määrä",
"or": "tai",
"points": "pistettä",
"special character": "erikoishahmo",
"spendable points expiring by": "{points} pistettä vanhenee {date} mennessä",
"to": "to",
"uppercase letter": "iso kirjain"
} }

View File

@@ -14,7 +14,6 @@
"Any changes you've made will be lost.": "Eventuelle endringer du har gjort, går tapt.", "Any changes you've made will be lost.": "Eventuelle endringer du har gjort, går tapt.",
"Are you sure you want to remove the card ending with {lastFourDigits} from your member profile?": "Er du sikker på at du vil fjerne kortet som slutter på {lastFourDigits} fra medlemsprofilen din?", "Are you sure you want to remove the card ending with {lastFourDigits} from your member profile?": "Er du sikker på at du vil fjerne kortet som slutter på {lastFourDigits} fra medlemsprofilen din?",
"Arrival date": "Ankomstdato", "Arrival date": "Ankomstdato",
"as of today": "per idag",
"As our": "Som vår {level}", "As our": "Som vår {level}",
"As our Close Friend": "Som vår nære venn", "As our Close Friend": "Som vår nære venn",
"At latest": "Senest", "At latest": "Senest",
@@ -25,15 +24,12 @@
"Book": "Bestill", "Book": "Bestill",
"Book reward night": "Bestill belønningskveld", "Book reward night": "Bestill belønningskveld",
"Booking number": "Bestillingsnummer", "Booking number": "Bestillingsnummer",
"booking.nights": "{totalNights, plural, one {# natt} other {# netter}}",
"Breakfast": "Frokost", "Breakfast": "Frokost",
"Breakfast excluded": "Frokost ekskludert", "Breakfast excluded": "Frokost ekskludert",
"Breakfast included": "Frokost inkludert", "Breakfast included": "Frokost inkludert",
"Bus terminal": "Bussterminal", "Bus terminal": "Bussterminal",
"Business": "Forretnings", "Business": "Forretnings",
"by": "innen",
"Cancel": "Avbryt", "Cancel": "Avbryt",
"characters": "tegn",
"Check in": "Sjekk inn", "Check in": "Sjekk inn",
"Check out": "Sjekk ut", "Check out": "Sjekk ut",
"Check out the credit cards saved to your profile. Pay with a saved card when signed in for a smoother web experience.": "Sjekk ut kredittkortene som er lagret på profilen din. Betal med et lagret kort når du er pålogget for en jevnere nettopplevelse.", "Check out the credit cards saved to your profile. Pay with a saved card when signed in for a smoother web experience.": "Sjekk ut kredittkortene som er lagret på profilen din. Betal med et lagret kort når du er pålogget for en jevnere nettopplevelse.",
@@ -70,6 +66,7 @@
"Distance to city centre": "{number}km til sentrum", "Distance to city centre": "{number}km til sentrum",
"Do you want to start the day with Scandics famous breakfast buffé?": "Vil du starte dagen med Scandics berømte frokostbuffé?", "Do you want to start the day with Scandics famous breakfast buffé?": "Vil du starte dagen med Scandics berømte frokostbuffé?",
"Download the Scandic app": "Last ned Scandic-appen", "Download the Scandic app": "Last ned Scandic-appen",
"Earn bonus nights & points": "Tjen bonusnetter og poeng",
"Edit": "Redigere", "Edit": "Redigere",
"Edit profile": "Rediger profil", "Edit profile": "Rediger profil",
"Email": "E-post", "Email": "E-post",
@@ -77,9 +74,9 @@
"Explore all levels and benefits": "Utforsk alle nivåer og fordeler", "Explore all levels and benefits": "Utforsk alle nivåer og fordeler",
"Explore nearby": "Utforsk i nærheten", "Explore nearby": "Utforsk i nærheten",
"Extras to your booking": "Tilvalg til bestillingen din", "Extras to your booking": "Tilvalg til bestillingen din",
"FAQ": "FAQ",
"Failed to delete credit card, please try again later.": "Kunne ikke slette kredittkortet, prøv igjen senere.", "Failed to delete credit card, please try again later.": "Kunne ikke slette kredittkortet, prøv igjen senere.",
"Fair": "Messe", "Fair": "Messe",
"FAQ": "FAQ",
"Find booking": "Finn booking", "Find booking": "Finn booking",
"Find hotels": "Finn hotell", "Find hotels": "Finn hotell",
"Flexibility": "Fleksibilitet", "Flexibility": "Fleksibilitet",
@@ -88,6 +85,7 @@
"Free rebooking": "Gratis ombooking", "Free rebooking": "Gratis ombooking",
"From": "Fra", "From": "Fra",
"Get inspired": "Bli inspirert", "Get inspired": "Bli inspirert",
"Get member benefits & offers": "Få medlemsfordeler og tilbud",
"Go back to edit": "Gå tilbake til redigering", "Go back to edit": "Gå tilbake til redigering",
"Go back to overview": "Gå tilbake til oversikten", "Go back to overview": "Gå tilbake til oversikten",
"Guests & Rooms": "Gjester & rom", "Guests & Rooms": "Gjester & rom",
@@ -97,15 +95,12 @@
"Hotel": "Hotel", "Hotel": "Hotel",
"Hotel facilities": "Hotelfaciliteter", "Hotel facilities": "Hotelfaciliteter",
"Hotel surroundings": "Hotellomgivelser", "Hotel surroundings": "Hotellomgivelser",
"hotelPages.rooms.roomCard.person": "person",
"hotelPages.rooms.roomCard.persons": "personer",
"hotelPages.rooms.roomCard.seeRoomDetails": "Se detaljer om rommet",
"Hotels": "Hoteller", "Hotels": "Hoteller",
"How do you want to sleep?": "Hvordan vil du sove?", "How do you want to sleep?": "Hvordan vil du sove?",
"How it works": "Hvordan det fungerer", "How it works": "Hvordan det fungerer",
"Image gallery": "Bildegalleri", "Image gallery": "Bildegalleri",
"Join Scandic Friends": "Bli med i Scandic Friends", "Join Scandic Friends": "Bli med i Scandic Friends",
"km to city center": "km til sentrum", "Join at no cost": "Bli med uten kostnad",
"Language": "Språk", "Language": "Språk",
"Latest searches": "Siste søk", "Latest searches": "Siste søk",
"Level": "Nivå", "Level": "Nivå",
@@ -132,9 +127,9 @@
"Member price": "Medlemspris", "Member price": "Medlemspris",
"Member price from": "Medlemspris fra", "Member price from": "Medlemspris fra",
"Members": "Medlemmer", "Members": "Medlemmer",
"Membership cards": "Medlemskort",
"Membership ID": "Medlems-ID", "Membership ID": "Medlems-ID",
"Membership ID copied to clipboard": "Medlems-ID kopiert til utklippstavlen", "Membership ID copied to clipboard": "Medlems-ID kopiert til utklippstavlen",
"Membership cards": "Medlemskort",
"Menu": "Menu", "Menu": "Menu",
"Modify": "Endre", "Modify": "Endre",
"Month": "Måned", "Month": "Måned",
@@ -149,9 +144,6 @@
"Nearby companies": "Nærliggende selskaper", "Nearby companies": "Nærliggende selskaper",
"New password": "Nytt passord", "New password": "Nytt passord",
"Next": "Neste", "Next": "Neste",
"next level:": "Neste nivå:",
"night": "natt",
"nights": "netter",
"Nights needed to level up": "Netter som trengs for å komme opp i nivå", "Nights needed to level up": "Netter som trengs for å komme opp i nivå",
"No content published": "Ingen innhold publisert", "No content published": "Ingen innhold publisert",
"No matching location found": "Fant ingen samsvarende plassering", "No matching location found": "Fant ingen samsvarende plassering",
@@ -162,13 +154,11 @@
"Non-refundable": "Ikke-refunderbart", "Non-refundable": "Ikke-refunderbart",
"Not found": "Ikke funnet", "Not found": "Ikke funnet",
"Nr night, nr adult": "{nights, number} natt, {adults, number} voksen", "Nr night, nr adult": "{nights, number} natt, {adults, number} voksen",
"number": "antall",
"On your journey": "På reisen din", "On your journey": "På reisen din",
"Open": "Åpen", "Open": "Åpen",
"Open language menu": "Åpne språkmenyen", "Open language menu": "Åpne språkmenyen",
"Open menu": "Åpne menyen", "Open menu": "Åpne menyen",
"Open my pages menu": "Åpne mine sider menyen", "Open my pages menu": "Åpne mine sider menyen",
"or": "eller",
"Overview": "Oversikt", "Overview": "Oversikt",
"Parking": "Parkering", "Parking": "Parkering",
"Parking / Garage": "Parkering / Garasje", "Parking / Garage": "Parkering / Garasje",
@@ -180,7 +170,6 @@
"Phone is required": "Telefon kreves", "Phone is required": "Telefon kreves",
"Phone number": "Telefonnummer", "Phone number": "Telefonnummer",
"Please enter a valid phone number": "Vennligst oppgi et gyldig telefonnummer", "Please enter a valid phone number": "Vennligst oppgi et gyldig telefonnummer",
"points": "poeng",
"Points": "Poeng", "Points": "Poeng",
"Points being calculated": "Poeng beregnes", "Points being calculated": "Poeng beregnes",
"Points earned prior to May 1, 2021": "Opptjente poeng før 1. mai 2021", "Points earned prior to May 1, 2021": "Opptjente poeng før 1. mai 2021",
@@ -223,29 +212,25 @@
"Something went wrong and we couldn't add your card. Please try again later.": "Noe gikk galt, og vi kunne ikke legge til kortet ditt. Prøv igjen senere.", "Something went wrong and we couldn't add your card. Please try again later.": "Noe gikk galt, og vi kunne ikke legge til kortet ditt. Prøv igjen senere.",
"Something went wrong and we couldn't remove your card. Please try again later.": "Noe gikk galt, og vi kunne ikke fjerne kortet ditt. Vennligst prøv igjen senere.", "Something went wrong and we couldn't remove your card. Please try again later.": "Noe gikk galt, og vi kunne ikke fjerne kortet ditt. Vennligst prøv igjen senere.",
"Something went wrong!": "Noe gikk galt!", "Something went wrong!": "Noe gikk galt!",
"special character": "spesiell karakter",
"spendable points expiring by": "{points} Brukbare poeng utløper innen {date}",
"Sports": "Sport", "Sports": "Sport",
"Standard price": "Standardpris", "Standard price": "Standardpris",
"Street": "Gate", "Street": "Gate",
"Successfully updated profile!": "Vellykket oppdatert profil!", "Successfully updated profile!": "Vellykket oppdatert profil!",
"Summary": "Sammendrag", "Summary": "Sammendrag",
"TUI Points": "TUI Points",
"Tell us what information and updates you'd like to receive, and how, by clicking the link below.": "Fortell oss hvilken informasjon og hvilke oppdateringer du ønsker å motta, og hvordan, ved å klikke på lenken nedenfor.", "Tell us what information and updates you'd like to receive, and how, by clicking the link below.": "Fortell oss hvilken informasjon og hvilke oppdateringer du ønsker å motta, og hvordan, ved å klikke på lenken nedenfor.",
"Thank you": "Takk", "Thank you": "Takk",
"Theatre": "Teater", "Theatre": "Teater",
"There are no transactions to display": "Det er ingen transaksjoner å vise", "There are no transactions to display": "Det er ingen transaksjoner å vise",
"Things nearby HOTEL_NAME": "Ting i nærheten av {hotelName}", "Things nearby HOTEL_NAME": "Ting i nærheten av {hotelName}",
"to": "til",
"Total Points": "Totale poeng", "Total Points": "Totale poeng",
"Tourist": "Turist", "Tourist": "Turist",
"Transaction date": "Transaksjonsdato", "Transaction date": "Transaksjonsdato",
"Transactions": "Transaksjoner", "Transactions": "Transaksjoner",
"Transportations": "Transport", "Transportations": "Transport",
"Tripadvisor reviews": "{rating} ({count} anmeldelser på Tripadvisor)", "Tripadvisor reviews": "{rating} ({count} anmeldelser på Tripadvisor)",
"TUI Points": "TUI Points",
"Type of bed": "Sengtype", "Type of bed": "Sengtype",
"Type of room": "Romtype", "Type of room": "Romtype",
"uppercase letter": "stor bokstav",
"Use bonus cheque": "Bruk bonussjekk", "Use bonus cheque": "Bruk bonussjekk",
"User information": "Brukerinformasjon", "User information": "Brukerinformasjon",
"View as list": "Vis som liste", "View as list": "Vis som liste",
@@ -271,9 +256,9 @@
"You canceled adding a new credit card.": "Du kansellerte å legge til et nytt kredittkort.", "You canceled adding a new credit card.": "Du kansellerte å legge til et nytt kredittkort.",
"You have no previous stays.": "Du har ingen tidligere opphold.", "You have no previous stays.": "Du har ingen tidligere opphold.",
"You have no upcoming stays.": "Du har ingen kommende opphold.", "You have no upcoming stays.": "Du har ingen kommende opphold.",
"Your Challenges Conquer & Earn!": "Dine utfordringer Erobre og tjen!",
"Your card was successfully removed!": "Kortet ditt ble fjernet!", "Your card was successfully removed!": "Kortet ditt ble fjernet!",
"Your card was successfully saved!": "Kortet ditt ble lagret!", "Your card was successfully saved!": "Kortet ditt ble lagret!",
"Your Challenges Conquer & Earn!": "Dine utfordringer Erobre og tjen!",
"Your current level": "Ditt nåværende nivå", "Your current level": "Ditt nåværende nivå",
"Your details": "Dine detaljer", "Your details": "Dine detaljer",
"Your level": "Ditt nivå", "Your level": "Ditt nivå",
@@ -281,5 +266,23 @@
"Zip code": "Post kode", "Zip code": "Post kode",
"Zoo": "Dyrehage", "Zoo": "Dyrehage",
"Zoom in": "Zoom inn", "Zoom in": "Zoom inn",
"Zoom out": "Zoom ut" "Zoom out": "Zoom ut",
"as of today": "per idag",
"booking.nights": "{totalNights, plural, one {# natt} other {# netter}}",
"by": "innen",
"characters": "tegn",
"hotelPages.rooms.roomCard.person": "person",
"hotelPages.rooms.roomCard.persons": "personer",
"hotelPages.rooms.roomCard.seeRoomDetails": "Se detaljer om rommet",
"km to city center": "km til sentrum",
"next level:": "Neste nivå:",
"night": "natt",
"nights": "netter",
"number": "antall",
"or": "eller",
"points": "poeng",
"special character": "spesiell karakter",
"spendable points expiring by": "{points} Brukbare poeng utløper innen {date}",
"to": "til",
"uppercase letter": "stor bokstav"
} }

View File

@@ -14,7 +14,6 @@
"Any changes you've made will be lost.": "Alla ändringar du har gjort kommer att gå förlorade.", "Any changes you've made will be lost.": "Alla ändringar du har gjort kommer att gå förlorade.",
"Are you sure you want to remove the card ending with {lastFourDigits} from your member profile?": "Är du säker på att du vill ta bort kortet som slutar med {lastFourDigits} från din medlemsprofil?", "Are you sure you want to remove the card ending with {lastFourDigits} from your member profile?": "Är du säker på att du vill ta bort kortet som slutar med {lastFourDigits} från din medlemsprofil?",
"Arrival date": "Ankomstdatum", "Arrival date": "Ankomstdatum",
"as of today": "från och med idag",
"As our": "Som vår {level}", "As our": "Som vår {level}",
"As our Close Friend": "Som vår nära vän", "As our Close Friend": "Som vår nära vän",
"At latest": "Senast", "At latest": "Senast",
@@ -25,15 +24,12 @@
"Book": "Boka", "Book": "Boka",
"Book reward night": "Boka frinatt", "Book reward night": "Boka frinatt",
"Booking number": "Bokningsnummer", "Booking number": "Bokningsnummer",
"booking.nights": "{totalNights, plural, one {# natt} other {# nätter}}",
"Breakfast": "Frukost", "Breakfast": "Frukost",
"Breakfast excluded": "Frukost ingår ej", "Breakfast excluded": "Frukost ingår ej",
"Breakfast included": "Frukost ingår", "Breakfast included": "Frukost ingår",
"Bus terminal": "Bussterminal", "Bus terminal": "Bussterminal",
"Business": "Business", "Business": "Business",
"by": "innan",
"Cancel": "Avbryt", "Cancel": "Avbryt",
"characters": "tecken",
"Check in": "Checka in", "Check in": "Checka in",
"Check out": "Checka ut", "Check out": "Checka ut",
"Check out the credit cards saved to your profile. Pay with a saved card when signed in for a smoother web experience.": "Kolla in kreditkorten som sparats i din profil. Betala med ett sparat kort när du är inloggad för en smidigare webbupplevelse.", "Check out the credit cards saved to your profile. Pay with a saved card when signed in for a smoother web experience.": "Kolla in kreditkorten som sparats i din profil. Betala med ett sparat kort när du är inloggad för en smidigare webbupplevelse.",
@@ -70,6 +66,7 @@
"Distance to city centre": "{number}km till centrum", "Distance to city centre": "{number}km till centrum",
"Do you want to start the day with Scandics famous breakfast buffé?": "Vill du starta dagen med Scandics berömda frukostbuffé?", "Do you want to start the day with Scandics famous breakfast buffé?": "Vill du starta dagen med Scandics berömda frukostbuffé?",
"Download the Scandic app": "Ladda ner Scandic-appen", "Download the Scandic app": "Ladda ner Scandic-appen",
"Earn bonus nights & points": "Tjäna bonusnätter och poäng",
"Edit": "Redigera", "Edit": "Redigera",
"Edit profile": "Redigera profil", "Edit profile": "Redigera profil",
"Email": "E-post", "Email": "E-post",
@@ -77,9 +74,9 @@
"Explore all levels and benefits": "Utforska alla nivåer och fördelar", "Explore all levels and benefits": "Utforska alla nivåer och fördelar",
"Explore nearby": "Utforska i närheten", "Explore nearby": "Utforska i närheten",
"Extras to your booking": "Extra tillval till din bokning", "Extras to your booking": "Extra tillval till din bokning",
"FAQ": "FAQ",
"Failed to delete credit card, please try again later.": "Det gick inte att ta bort kreditkortet, försök igen senare.", "Failed to delete credit card, please try again later.": "Det gick inte att ta bort kreditkortet, försök igen senare.",
"Fair": "Mässa", "Fair": "Mässa",
"FAQ": "FAQ",
"Find booking": "Hitta bokning", "Find booking": "Hitta bokning",
"Find hotels": "Hitta hotell", "Find hotels": "Hitta hotell",
"Flexibility": "Flexibilitet", "Flexibility": "Flexibilitet",
@@ -88,6 +85,7 @@
"Free rebooking": "Fri ombokning", "Free rebooking": "Fri ombokning",
"From": "Från", "From": "Från",
"Get inspired": "Bli inspirerad", "Get inspired": "Bli inspirerad",
"Get member benefits & offers": "Ta del av medlemsförmåner och erbjudanden",
"Go back to edit": "Gå tillbaka till redigeringen", "Go back to edit": "Gå tillbaka till redigeringen",
"Go back to overview": "Gå tillbaka till översikten", "Go back to overview": "Gå tillbaka till översikten",
"Guests & Rooms": "Gäster & rum", "Guests & Rooms": "Gäster & rum",
@@ -97,15 +95,12 @@
"Hotel": "Hotell", "Hotel": "Hotell",
"Hotel facilities": "Hotellfaciliteter", "Hotel facilities": "Hotellfaciliteter",
"Hotel surroundings": "Hotellomgivning", "Hotel surroundings": "Hotellomgivning",
"hotelPages.rooms.roomCard.person": "person",
"hotelPages.rooms.roomCard.persons": "personer",
"hotelPages.rooms.roomCard.seeRoomDetails": "Se information om rummet",
"Hotels": "Hotell", "Hotels": "Hotell",
"How do you want to sleep?": "Hur vill du sova?", "How do you want to sleep?": "Hur vill du sova?",
"How it works": "Hur det fungerar", "How it works": "Hur det fungerar",
"Image gallery": "Bildgalleri", "Image gallery": "Bildgalleri",
"Join Scandic Friends": "Gå med i Scandic Friends", "Join Scandic Friends": "Gå med i Scandic Friends",
"km to city center": "km till stadens centrum", "Join at no cost": "Gå med utan kostnad",
"Language": "Språk", "Language": "Språk",
"Latest searches": "Senaste sökningarna", "Latest searches": "Senaste sökningarna",
"Level": "Nivå", "Level": "Nivå",
@@ -132,9 +127,9 @@
"Member price": "Medlemspris", "Member price": "Medlemspris",
"Member price from": "Medlemspris från", "Member price from": "Medlemspris från",
"Members": "Medlemmar", "Members": "Medlemmar",
"Membership cards": "Medlemskort",
"Membership ID": "Medlems-ID", "Membership ID": "Medlems-ID",
"Membership ID copied to clipboard": "Medlems-ID kopierat till urklipp", "Membership ID copied to clipboard": "Medlems-ID kopierat till urklipp",
"Membership cards": "Medlemskort",
"Menu": "Meny", "Menu": "Meny",
"Modify": "Ändra", "Modify": "Ändra",
"Month": "Månad", "Month": "Månad",
@@ -149,9 +144,6 @@
"Nearby companies": "Närliggande företag", "Nearby companies": "Närliggande företag",
"New password": "Nytt lösenord", "New password": "Nytt lösenord",
"Next": "Nästa", "Next": "Nästa",
"next level:": "Nästa nivå:",
"night": "natt",
"nights": "nätter",
"Nights needed to level up": "Nätter som behövs för att gå upp i nivå", "Nights needed to level up": "Nätter som behövs för att gå upp i nivå",
"No content published": "Inget innehåll publicerat", "No content published": "Inget innehåll publicerat",
"No matching location found": "Ingen matchande plats hittades", "No matching location found": "Ingen matchande plats hittades",
@@ -162,13 +154,11 @@
"Non-refundable": "Ej återbetalningsbar", "Non-refundable": "Ej återbetalningsbar",
"Not found": "Hittades inte", "Not found": "Hittades inte",
"Nr night, nr adult": "{nights, number} natt, {adults, number} vuxen", "Nr night, nr adult": "{nights, number} natt, {adults, number} vuxen",
"number": "nummer",
"On your journey": "På din resa", "On your journey": "På din resa",
"Open": "Öppna", "Open": "Öppna",
"Open language menu": "Öppna språkmenyn", "Open language menu": "Öppna språkmenyn",
"Open menu": "Öppna menyn", "Open menu": "Öppna menyn",
"Open my pages menu": "Öppna mina sidor menyn", "Open my pages menu": "Öppna mina sidor menyn",
"or": "eller",
"Overview": "Översikt", "Overview": "Översikt",
"Parking": "Parkering", "Parking": "Parkering",
"Parking / Garage": "Parkering / Garage", "Parking / Garage": "Parkering / Garage",
@@ -180,7 +170,6 @@
"Phone is required": "Telefonnummer är obligatorisk", "Phone is required": "Telefonnummer är obligatorisk",
"Phone number": "Telefonnummer", "Phone number": "Telefonnummer",
"Please enter a valid phone number": "Var vänlig och ange ett giltigt telefonnummer", "Please enter a valid phone number": "Var vänlig och ange ett giltigt telefonnummer",
"points": "poäng",
"Points": "Poäng", "Points": "Poäng",
"Points being calculated": "Poäng beräknas", "Points being calculated": "Poäng beräknas",
"Points earned prior to May 1, 2021": "Intjänade poäng före den 1 maj 2021", "Points earned prior to May 1, 2021": "Intjänade poäng före den 1 maj 2021",
@@ -223,29 +212,25 @@
"Something went wrong and we couldn't add your card. Please try again later.": "Något gick fel och vi kunde inte lägga till ditt kort. Försök igen senare.", "Something went wrong and we couldn't add your card. Please try again later.": "Något gick fel och vi kunde inte lägga till ditt kort. Försök igen senare.",
"Something went wrong and we couldn't remove your card. Please try again later.": "Något gick fel och vi kunde inte ta bort ditt kort. Försök igen senare.", "Something went wrong and we couldn't remove your card. Please try again later.": "Något gick fel och vi kunde inte ta bort ditt kort. Försök igen senare.",
"Something went wrong!": "Något gick fel!", "Something went wrong!": "Något gick fel!",
"special character": "speciell karaktär",
"spendable points expiring by": "{points} poäng förfaller {date}",
"Sports": "Sport", "Sports": "Sport",
"Standard price": "Standardpris", "Standard price": "Standardpris",
"Street": "Gata", "Street": "Gata",
"Successfully updated profile!": "Profilen har uppdaterats framgångsrikt!", "Successfully updated profile!": "Profilen har uppdaterats framgångsrikt!",
"Summary": "Sammanfattning", "Summary": "Sammanfattning",
"TUI Points": "TUI Points",
"Tell us what information and updates you'd like to receive, and how, by clicking the link below.": "Berätta för oss vilken information och vilka uppdateringar du vill få och hur genom att klicka på länken nedan.", "Tell us what information and updates you'd like to receive, and how, by clicking the link below.": "Berätta för oss vilken information och vilka uppdateringar du vill få och hur genom att klicka på länken nedan.",
"Thank you": "Tack", "Thank you": "Tack",
"Theatre": "Teater", "Theatre": "Teater",
"There are no transactions to display": "Det finns inga transaktioner att visa", "There are no transactions to display": "Det finns inga transaktioner att visa",
"Things nearby HOTEL_NAME": "Saker i närheten av {hotelName}", "Things nearby HOTEL_NAME": "Saker i närheten av {hotelName}",
"to": "till",
"Total Points": "Poäng totalt", "Total Points": "Poäng totalt",
"Tourist": "Turist", "Tourist": "Turist",
"Transaction date": "Transaktionsdatum", "Transaction date": "Transaktionsdatum",
"Transactions": "Transaktioner", "Transactions": "Transaktioner",
"Transportations": "Transport", "Transportations": "Transport",
"Tripadvisor reviews": "{rating} ({count} recensioner på Tripadvisor)", "Tripadvisor reviews": "{rating} ({count} recensioner på Tripadvisor)",
"TUI Points": "TUI Points",
"Type of bed": "Sängtyp", "Type of bed": "Sängtyp",
"Type of room": "Rumstyp", "Type of room": "Rumstyp",
"uppercase letter": "stor bokstav",
"Use bonus cheque": "Use bonus cheque", "Use bonus cheque": "Use bonus cheque",
"User information": "Användarinformation", "User information": "Användarinformation",
"View as list": "Visa som lista", "View as list": "Visa som lista",
@@ -271,9 +256,9 @@
"You canceled adding a new credit card.": "Du avbröt att lägga till ett nytt kreditkort.", "You canceled adding a new credit card.": "Du avbröt att lägga till ett nytt kreditkort.",
"You have no previous stays.": "Du har inga tidigare vistelser.", "You have no previous stays.": "Du har inga tidigare vistelser.",
"You have no upcoming stays.": "Du har inga planerade resor.", "You have no upcoming stays.": "Du har inga planerade resor.",
"Your Challenges Conquer & Earn!": "Dina utmaningar Erövra och tjäna!",
"Your card was successfully removed!": "Ditt kort har tagits bort!", "Your card was successfully removed!": "Ditt kort har tagits bort!",
"Your card was successfully saved!": "Ditt kort har sparats!", "Your card was successfully saved!": "Ditt kort har sparats!",
"Your Challenges Conquer & Earn!": "Dina utmaningar Erövra och tjäna!",
"Your current level": "Din nuvarande nivå", "Your current level": "Din nuvarande nivå",
"Your details": "Dina uppgifter", "Your details": "Dina uppgifter",
"Your level": "Din nivå", "Your level": "Din nivå",
@@ -281,5 +266,23 @@
"Zip code": "Postnummer", "Zip code": "Postnummer",
"Zoo": "Djurpark", "Zoo": "Djurpark",
"Zoom in": "Zooma in", "Zoom in": "Zooma in",
"Zoom out": "Zooma ut" "Zoom out": "Zooma ut",
"as of today": "från och med idag",
"booking.nights": "{totalNights, plural, one {# natt} other {# nätter}}",
"by": "innan",
"characters": "tecken",
"hotelPages.rooms.roomCard.person": "person",
"hotelPages.rooms.roomCard.persons": "personer",
"hotelPages.rooms.roomCard.seeRoomDetails": "Se information om rummet",
"km to city center": "km till stadens centrum",
"next level:": "Nästa nivå:",
"night": "natt",
"nights": "nätter",
"number": "nummer",
"or": "eller",
"points": "poäng",
"special character": "speciell karaktär",
"spendable points expiring by": "{points} poäng förfaller {date}",
"to": "till",
"uppercase letter": "stor bokstav"
} }

View File

@@ -21,7 +21,7 @@ import type {
* is an Interface e.g). * is an Interface e.g).
*/ */
export function discriminatedUnion<T extends Option>(options: T[]) { export function discriminatedUnion<R>(options: Option[]) {
return z return z
.discriminatedUnion("__typename", [ .discriminatedUnion("__typename", [
z.object({ __typename: z.literal(undefined) }), z.object({ __typename: z.literal(undefined) }),
@@ -37,6 +37,12 @@ export function discriminatedUnion<T extends Option>(options: T[]) {
} }
throw new Error(error.message) throw new Error(error.message)
}) })
.transform((data) => {
if (data.__typename === "undefined" || data.__typename === undefined) {
return null
}
return data as R
})
} }
export function discriminatedUnionArray<T extends Option>(options: T[]) { export function discriminatedUnionArray<T extends Option>(options: T[]) {

View File

@@ -10,6 +10,12 @@ export const getProfile = cache(async function getMemoizedProfile() {
return serverClient().user.get() return serverClient().user.get()
}) })
export const getProfileSafely = cache(
async function getMemoizedProfileSafely() {
return serverClient().user.getSafely()
}
)
export const getFooter = cache(async function getMemoizedFooter() { export const getFooter = cache(async function getMemoizedFooter() {
return serverClient().contentstack.base.footer() return serverClient().contentstack.base.footer()
}) })

View File

@@ -551,10 +551,13 @@ const linkSchema = z
}) })
.transform((data) => { .transform((data) => {
if (data.linkConnection.edges.length) { if (data.linkConnection.edges.length) {
const link = pageLinks.transform(data.linkConnection.edges[0].node) const linkNode = data.linkConnection.edges[0].node
if (link) { if (linkNode) {
return { const link = pageLinks.transform(linkNode)
link, if (link) {
return {
link,
}
} }
} }
} }

View File

@@ -25,10 +25,13 @@ export const linkConnectionSchema = z
}) })
.transform((data) => { .transform((data) => {
if (data.linkConnection.edges.length) { if (data.linkConnection.edges.length) {
const link = pageLinks.transform(data.linkConnection.edges[0].node) const linkNode = data.linkConnection.edges[0].node
if (link) { if (linkNode) {
return { const link = pageLinks.transform(linkNode)
link, if (link) {
return {
link,
}
} }
} }
} }
@@ -54,17 +57,20 @@ export const linkConnectionRefs = z
linkConnection: z.object({ linkConnection: z.object({
edges: z.array( edges: z.array(
z.object({ z.object({
node: linkRefsUnionSchema, node: discriminatedUnion(linkRefsUnionSchema.options),
}) })
), ),
}), }),
}) })
.transform((data) => { .transform((data) => {
if (data.linkConnection.edges.length) { if (data.linkConnection.edges.length) {
const link = pageLinks.transformRef(data.linkConnection.edges[0].node) const linkNode = data.linkConnection.edges[0].node
if (link) { if (linkNode) {
return { const link = pageLinks.transformRef(linkNode)
link, if (link) {
return {
link,
}
} }
} }
} }

View File

@@ -1,4 +1,5 @@
import { metrics } from "@opentelemetry/api" import { metrics } from "@opentelemetry/api"
import { SafeParseSuccess } from "zod"
import * as api from "@/lib/api" import * as api from "@/lib/api"
import { import {
@@ -27,8 +28,8 @@ import type {
LoginType, LoginType,
TrackingSDKUserData, TrackingSDKUserData,
} from "@/types/components/tracking" } from "@/types/components/tracking"
import { BlocksEnums } from "@/types/enums/blocks"
import { Transactions } from "@/types/enums/transactions" import { Transactions } from "@/types/enums/transactions"
import { User } from "@/types/user"
import type { MembershipLevel } from "@/constants/membershipLevels" import type { MembershipLevel } from "@/constants/membershipLevels"
// OpenTelemetry metrics: User // OpenTelemetry metrics: User
@@ -161,6 +162,51 @@ export async function getVerifiedUser({ session }: { session: Session }) {
return verifiedData return verifiedData
} }
function parsedUser(data: User, isMFA: boolean) {
const country = countries.find((c) => c.code === data.address.countryCode)
const user = {
address: {
city: data.address.city,
country: country?.name ?? "",
countryCode: data.address.countryCode,
streetAddress: data.address.streetAddress,
zipCode: data.address.zipCode,
},
dateOfBirth: data.dateOfBirth,
email: data.email,
firstName: data.firstName,
language: data.language,
lastName: data.lastName,
membership: getMembership(data.memberships),
memberships: data.memberships,
name: `${data.firstName} ${data.lastName}`,
phoneNumber: data.phoneNumber,
profileId: data.profileId,
}
if (!isMFA) {
if (user.address.city) {
user.address.city = maskValue.text(user.address.city)
}
if (user.address.streetAddress) {
user.address.streetAddress = maskValue.text(user.address.streetAddress)
}
user.address.zipCode = data.address?.zipCode
? maskValue.text(data.address.zipCode)
: ""
user.dateOfBirth = maskValue.all(user.dateOfBirth)
user.email = maskValue.email(user.email)
user.phoneNumber = user.phoneNumber ? maskValue.phone(user.phoneNumber) : ""
}
return user
}
export const userQueryRouter = router({ export const userQueryRouter = router({
get: protectedProcedure get: protectedProcedure
.use(async function (opts) { .use(async function (opts) {
@@ -184,57 +230,25 @@ export const userQueryRouter = router({
return data return data
} }
const verifiedData = data return parsedUser(data.data, ctx.isMFA)
const country = countries.find(
(c) => c.code === verifiedData.data.address.countryCode
)
const user = {
address: {
city: verifiedData.data.address.city,
country: country?.name ?? "",
countryCode: verifiedData.data.address.countryCode,
streetAddress: verifiedData.data.address.streetAddress,
zipCode: verifiedData.data.address.zipCode,
},
dateOfBirth: verifiedData.data.dateOfBirth,
email: verifiedData.data.email,
firstName: verifiedData.data.firstName,
language: verifiedData.data.language,
lastName: verifiedData.data.lastName,
membership: getMembership(verifiedData.data.memberships),
memberships: verifiedData.data.memberships,
name: `${verifiedData.data.firstName} ${verifiedData.data.lastName}`,
phoneNumber: verifiedData.data.phoneNumber,
profileId: verifiedData.data.profileId,
}
if (!ctx.isMFA) {
if (user.address.city) {
user.address.city = maskValue.text(user.address.city)
}
if (user.address.streetAddress) {
user.address.streetAddress = maskValue.text(
user.address.streetAddress
)
}
user.address.zipCode = verifiedData.data.address?.zipCode
? maskValue.text(verifiedData.data.address.zipCode)
: ""
user.dateOfBirth = maskValue.all(user.dateOfBirth)
user.email = maskValue.email(user.email)
user.phoneNumber = user.phoneNumber
? maskValue.phone(user.phoneNumber)
: ""
}
return user
}), }),
getSafely: safeProtectedProcedure.query(async function getUser({ ctx }) {
if (!ctx.session) {
return null
}
const data = await getVerifiedUser({ session: ctx.session })
if (!data) {
return null
}
if ("error" in data) {
return data
}
return parsedUser(data.data, true)
}),
name: safeProtectedProcedure.query(async function ({ ctx }) { name: safeProtectedProcedure.query(async function ({ ctx }) {
if (!ctx.session) { if (!ctx.session) {
return null return null

View File

@@ -0,0 +1,11 @@
import { z } from "zod"
import { detailsSchema } from "@/components/HotelReservation/EnterDetails/Details/schema"
import { User } from "@/types/user"
export interface DetailsSchema extends z.output<typeof detailsSchema> {}
export interface DetailsProps {
user: User | null
}

View File

@@ -15,40 +15,43 @@ export interface SearchListProps {
getItemProps: PropGetters<unknown>["getItemProps"] getItemProps: PropGetters<unknown>["getItemProps"]
getMenuProps: PropGetters<unknown>["getMenuProps"] getMenuProps: PropGetters<unknown>["getMenuProps"]
isOpen: boolean isOpen: boolean
handleClearSearchHistory: () => void
highlightedIndex: HighlightedIndex highlightedIndex: HighlightedIndex
locations: Locations locations: Locations
search: string search: string
searchHistory: Locations | null searchHistory: Locations | null
} }
export interface ListProps { export interface ListProps
getItemProps: PropGetters<unknown>["getItemProps"] extends Pick<
highlightedIndex: HighlightedIndex SearchListProps,
"getItemProps" | "highlightedIndex" | "locations"
> {
initialIndex?: number initialIndex?: number
label?: string label?: string
locations: Locations
} }
export interface ListItemProps { export interface ListItemProps
getItemProps: PropGetters<unknown>["getItemProps"] extends Pick<SearchListProps, "getItemProps" | "highlightedIndex"> {
highlightedIndex: HighlightedIndex
index: number index: number
location: Location location: Location
} }
export interface DialogProps export interface DialogProps
extends React.PropsWithChildren, extends React.PropsWithChildren,
VariantProps<typeof dialogVariants> { VariantProps<typeof dialogVariants>,
Pick<SearchListProps, "getMenuProps"> {
className?: string className?: string
getMenuProps: PropGetters<unknown>["getMenuProps"]
} }
export interface ErrorDialogProps extends React.PropsWithChildren { export interface ErrorDialogProps
getMenuProps: PropGetters<unknown>["getMenuProps"] extends React.PropsWithChildren,
} Pick<SearchListProps, "getMenuProps"> {}
export interface ClearSearchButtonProps { export interface ClearSearchButtonProps
getItemProps: PropGetters<unknown>["getItemProps"] extends Pick<
highlightedIndex: HighlightedIndex SearchListProps,
"getItemProps" | "handleClearSearchHistory" | "highlightedIndex"
> {
index: number index: number
} }

View File

@@ -1,9 +1,7 @@
import { Lang } from "@/constants/languages" import { z } from "zod"
import { systemSchema } from "@/server/routers/contentstack/schemas/system"
export interface System { export interface System {
system: { system: z.output<typeof systemSchema>
content_type_uid: string
locale: Lang
uid: string
}
} }