Merged in feat/best-friend-hero (pull request #338)
Feat(SW-170): Update overview hero Approved-by: Christel Westerberg
This commit is contained in:
36
components/Icons/Copy.tsx
Normal file
36
components/Icons/Copy.tsx
Normal file
@@ -0,0 +1,36 @@
|
||||
import { iconVariants } from "./variants"
|
||||
|
||||
import type { IconProps } from "@/types/components/icon"
|
||||
|
||||
export default function CopyIcon({ className, color, ...props }: IconProps) {
|
||||
const classNames = iconVariants({ className, color })
|
||||
return (
|
||||
<svg
|
||||
className={classNames}
|
||||
fill="none"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
width="20"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<mask
|
||||
height="20"
|
||||
id="mask0_1572_4523"
|
||||
maskUnits="userSpaceOnUse"
|
||||
style={{ maskType: "alpha" }}
|
||||
width="20"
|
||||
x="0"
|
||||
y="0"
|
||||
>
|
||||
<rect width="20" height="20" fill="#D9D9D9" />
|
||||
</mask>
|
||||
<g mask="url(#mask0_1572_4523)">
|
||||
<path
|
||||
d="M7.54804 15.4742C7.17293 15.4742 6.85587 15.3447 6.59685 15.0857C6.33783 14.8267 6.20831 14.5096 6.20831 14.1345V4.64737C6.20831 4.27226 6.33783 3.95519 6.59685 3.69616C6.85587 3.43713 7.17293 3.30762 7.54804 3.30762H15.0352C15.4103 3.30762 15.7273 3.43713 15.9864 3.69616C16.2454 3.95519 16.3749 4.27226 16.3749 4.64737V14.1345C16.3749 14.5096 16.2454 14.8267 15.9864 15.0857C15.7273 15.3447 15.4103 15.4742 15.0352 15.4742H7.54804ZM7.54804 14.3909H15.0352C15.0993 14.3909 15.158 14.3642 15.2115 14.3108C15.2649 14.2574 15.2916 14.1986 15.2916 14.1345V4.64737C15.2916 4.58326 15.2649 4.52449 15.2115 4.47106C15.158 4.41764 15.0993 4.39093 15.0352 4.39093H7.54804C7.48393 4.39093 7.42517 4.41764 7.37175 4.47106C7.31832 4.52449 7.2916 4.58326 7.2916 4.64737V14.1345C7.2916 14.1986 7.31832 14.2574 7.37175 14.3108C7.42517 14.3642 7.48393 14.3909 7.54804 14.3909ZM4.96473 18.0575C4.58963 18.0575 4.27257 17.928 4.01354 17.669C3.75451 17.41 3.625 17.0929 3.625 16.7178V6.68901C3.625 6.53528 3.67642 6.40657 3.77927 6.30289C3.8821 6.19921 4.00977 6.14737 4.16227 6.14737C4.31476 6.14737 4.44389 6.19921 4.54967 6.30289C4.65543 6.40657 4.70831 6.53528 4.70831 6.68901V16.7178C4.70831 16.7819 4.73502 16.8407 4.78844 16.8941C4.84187 16.9475 4.90063 16.9742 4.96473 16.9742H12.9935C13.1473 16.9742 13.276 17.0257 13.3796 17.1285C13.4833 17.2313 13.5352 17.359 13.5352 17.5115C13.5352 17.664 13.4833 17.7931 13.3796 17.8989C13.276 18.0047 13.1473 18.0575 12.9935 18.0575H4.96473Z"
|
||||
fill="#060606"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
@@ -10,6 +10,8 @@ import SoonestStays from "@/components/MyPages/Blocks/Stays/Soonest"
|
||||
import UpcomingStays from "@/components/MyPages/Blocks/Stays/Upcoming"
|
||||
import { removeMultipleSlashes } from "@/utils/url"
|
||||
|
||||
import PointsOverview from "../Blocks/Points/Overview"
|
||||
|
||||
import {
|
||||
AccountPageContentProps,
|
||||
ContentProps,
|
||||
@@ -23,6 +25,8 @@ function DynamicComponent({ component, props }: AccountPageContentProps) {
|
||||
switch (component) {
|
||||
case DynamicContentComponents.membership_overview:
|
||||
return <Overview {...props} />
|
||||
case DynamicContentComponents.points_overview:
|
||||
return <PointsOverview {...props} />
|
||||
case DynamicContentComponents.previous_stays:
|
||||
return <PreviousStays {...props} />
|
||||
case DynamicContentComponents.soonest_stays:
|
||||
|
||||
@@ -8,6 +8,7 @@ import CurrentBenefitsBlock from "../../Blocks/Benefits/CurrentLevel"
|
||||
import NextLevelBenefitsBlock from "../../Blocks/Benefits/NextLevel"
|
||||
import CurrentPointsBalance from "../../Blocks/Points/CurrentPointsBalance"
|
||||
import EarnAndBurn from "../../Blocks/Points/EarnAndBurn"
|
||||
import PointsOverview from "../../Blocks/Points/Overview"
|
||||
|
||||
import {
|
||||
AccountPageContentProps,
|
||||
@@ -21,14 +22,9 @@ import {
|
||||
function DynamicComponent({ component, props }: AccountPageContentProps) {
|
||||
switch (component) {
|
||||
case DynamicContentComponents.membership_overview:
|
||||
return (
|
||||
<Overview
|
||||
lang={props.lang}
|
||||
link={props.link}
|
||||
subtitle={null}
|
||||
title={props.title}
|
||||
/>
|
||||
)
|
||||
return <Overview {...props} />
|
||||
case DynamicContentComponents.points_overview:
|
||||
return <PointsOverview {...props} />
|
||||
case DynamicContentComponents.current_benefits:
|
||||
return <CurrentBenefitsBlock {...props} />
|
||||
case DynamicContentComponents.next_benefits:
|
||||
|
||||
@@ -8,7 +8,6 @@ import SectionHeader from "@/components/Section/Header"
|
||||
import SectionLink from "@/components/Section/Link"
|
||||
import Chip from "@/components/TempDesignSystem/Chip"
|
||||
import Grids from "@/components/TempDesignSystem/Grids"
|
||||
import BiroScript from "@/components/TempDesignSystem/Text/BiroScript"
|
||||
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
|
||||
import { getIntl } from "@/i18n"
|
||||
@@ -30,9 +29,8 @@ export default async function NextLevelBenefitsBlock({
|
||||
return null
|
||||
}
|
||||
const nextLevel = getMembershipLevelObject(
|
||||
user.memberships[0].membershipLevel as MembershipLevelEnum,
|
||||
lang,
|
||||
"nextLevel"
|
||||
user.memberships[0].nextLevel as MembershipLevelEnum,
|
||||
lang
|
||||
)
|
||||
if (!nextLevel) {
|
||||
// TODO: handle this case, when missing or when user is top level?
|
||||
|
||||
@@ -1,25 +1,27 @@
|
||||
"use client"
|
||||
|
||||
import Image from "@/components/Image"
|
||||
import CopyIcon from "@/components/Icons/Copy"
|
||||
import Button from "@/components/TempDesignSystem/Button"
|
||||
import { getMembership } from "@/utils/user"
|
||||
|
||||
import type { User } from "@/types/user"
|
||||
import styles from "./copybutton.module.css"
|
||||
|
||||
export default function CopyButton({ memberships }: Pick<User, "memberships">) {
|
||||
import type { CopyButtonProps } from "@/types/components/myPages/membership"
|
||||
|
||||
export default function CopyButton({ membershipNumber }: CopyButtonProps) {
|
||||
function handleCopy() {
|
||||
const membership = getMembership(memberships)
|
||||
console.log(`COPIED! (${membership ? membership.membershipNumber : "N/A"})`)
|
||||
navigator.clipboard.writeText(membershipNumber)
|
||||
}
|
||||
|
||||
return (
|
||||
<Button onClick={handleCopy} type="button" variant="icon">
|
||||
<Image
|
||||
alt="Copy Icon"
|
||||
height={20}
|
||||
src="/_static/icons/copy.svg"
|
||||
width={20}
|
||||
/>
|
||||
<Button
|
||||
onClick={handleCopy}
|
||||
className={styles.button}
|
||||
type="button"
|
||||
variant="icon"
|
||||
size="small"
|
||||
intent="tertiary"
|
||||
>
|
||||
<CopyIcon color="pale" />
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
.button {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
.hero {
|
||||
border-radius: var(--Corner-radius-xLarge);
|
||||
display: grid;
|
||||
gap: var(--Spacing-x2);
|
||||
grid-template-columns: 1fr;
|
||||
padding: var(--Spacing-x7) var(--Spacing-x6);
|
||||
}
|
||||
|
||||
.burgundy {
|
||||
background-color: var(--Scandic-Brand-Burgundy);
|
||||
}
|
||||
|
||||
.red {
|
||||
background-color: var(--Scandic-Brand-Scandic-Red);
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
.hero {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
}
|
||||
7
components/MyPages/Blocks/Overview/Friend/Hero/hero.ts
Normal file
7
components/MyPages/Blocks/Overview/Friend/Hero/hero.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { VariantProps } from "class-variance-authority"
|
||||
|
||||
import { heroVariants } from "./heroVariants"
|
||||
|
||||
export interface HeroProps
|
||||
extends Omit<React.HTMLAttributes<HTMLDivElement>, "color">,
|
||||
VariantProps<typeof heroVariants> {}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { cva } from "class-variance-authority"
|
||||
|
||||
import styles from "./hero.module.css"
|
||||
|
||||
export const heroVariants = cva(styles.hero, {
|
||||
variants: {
|
||||
color: {
|
||||
burgundy: styles.burgundy,
|
||||
red: styles.red,
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
color: "red",
|
||||
},
|
||||
})
|
||||
7
components/MyPages/Blocks/Overview/Friend/Hero/index.tsx
Normal file
7
components/MyPages/Blocks/Overview/Friend/Hero/index.tsx
Normal file
@@ -0,0 +1,7 @@
|
||||
import { HeroProps } from "./hero"
|
||||
import { heroVariants } from "./heroVariants"
|
||||
|
||||
export default function Hero({ className, color, children }: HeroProps) {
|
||||
const classNames = heroVariants({ className, color })
|
||||
return <section className={classNames}>{children}</section>
|
||||
}
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
|
||||
import styles from "./membershipLevel.module.css"
|
||||
|
||||
import type { MembershipLevelProps } from "@/types/components/myPages/membershipLevel"
|
||||
import type { MembershipLevelProps } from "@/types/components/myPages/membership"
|
||||
|
||||
export default function MembershipLevel({ level }: MembershipLevelProps) {
|
||||
switch (level) {
|
||||
@@ -0,0 +1,34 @@
|
||||
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
||||
import { getIntl } from "@/i18n"
|
||||
|
||||
import CopyButton from "../../Buttons/CopyButton"
|
||||
import { MembershipNumberProps } from "./membershipNumber"
|
||||
import { membershipNumberVariants } from "./membershipNumberVariants"
|
||||
|
||||
import styles from "./membershipNumber.module.css"
|
||||
|
||||
export default async function MembershipNumber({
|
||||
className,
|
||||
color,
|
||||
membership,
|
||||
}: MembershipNumberProps) {
|
||||
const { formatMessage } = await getIntl()
|
||||
const classNames = membershipNumberVariants({ className, color })
|
||||
|
||||
return (
|
||||
<div className={classNames}>
|
||||
<Caption color="pale">
|
||||
{formatMessage({ id: "Membership ID" })}
|
||||
{": "}
|
||||
</Caption>
|
||||
<span className={styles.icon}>
|
||||
<Caption className={styles.icon} color="pale">
|
||||
{membership.membershipNumber ?? "N/A"}
|
||||
</Caption>
|
||||
{membership && (
|
||||
<CopyButton membershipNumber={membership.membershipNumber} />
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
.membershipContainer {
|
||||
align-items: center;
|
||||
background: var(--Scandic-Brand-Burgundy);
|
||||
border-radius: var(--Corner-radius-Small);
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
justify-items: center;
|
||||
padding: var(--Spacing-x1) var(--Spacing-x7) 0 var(--Spacing-x7);
|
||||
}
|
||||
|
||||
.icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
padding-left: var(--Spacing-x2);
|
||||
}
|
||||
|
||||
.burgundy {
|
||||
background-color: var(--Main-Brand-Burgundy);
|
||||
}
|
||||
|
||||
.red {
|
||||
background-color: var(--Scandic-Brand-Scandic-Red);
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
.membershipContainer {
|
||||
grid-template-columns: auto auto;
|
||||
padding: 0 0 0 var(--Spacing-x2);
|
||||
gap: var(--Spacing-x-half);
|
||||
}
|
||||
|
||||
.icon {
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { VariantProps } from "class-variance-authority"
|
||||
|
||||
import { membershipNumberVariants } from "./membershipNumberVariants"
|
||||
|
||||
import { User } from "@/types/user"
|
||||
|
||||
export interface MembershipNumberProps
|
||||
extends Omit<React.HTMLAttributes<HTMLDivElement>, "color">,
|
||||
VariantProps<typeof membershipNumberVariants> {
|
||||
membership: User["memberships"][number]
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { cva } from "class-variance-authority"
|
||||
|
||||
import styles from "./membershipNumber.module.css"
|
||||
|
||||
export const membershipNumberVariants = cva(styles.membershipContainer, {
|
||||
variants: {
|
||||
color: {
|
||||
burgundy: styles.burgundy,
|
||||
red: styles.red,
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
color: "burgundy",
|
||||
},
|
||||
})
|
||||
@@ -9,7 +9,7 @@
|
||||
.header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--Spacing-x1);
|
||||
gap: var(--Spacing-x2);
|
||||
}
|
||||
|
||||
.levelLabel {
|
||||
@@ -35,14 +35,7 @@
|
||||
background: var(--Scandic-Brand-Burgundy);
|
||||
border-radius: var(--Corner-radius-Small);
|
||||
display: grid;
|
||||
gap: var(--Spacing-x1);
|
||||
grid-template-columns: 1fr;
|
||||
justify-items: center;
|
||||
padding: var(--Spacing-x1) var(--Spacing-x2);
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
.membershipContainer {
|
||||
grid-template-columns: auto auto;
|
||||
}
|
||||
padding: var(--Spacing-x1) var(--Spacing-x7) 0 var(--Spacing-x7);
|
||||
}
|
||||
|
||||
@@ -1,30 +1,39 @@
|
||||
import { membershipLevels } from "@/constants/membershipLevels"
|
||||
|
||||
import BiroScript from "@/components/TempDesignSystem/Text/BiroScript"
|
||||
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
||||
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||
import Title from "@/components/TempDesignSystem/Text/Title"
|
||||
import { getIntl } from "@/i18n"
|
||||
import { getMembership } from "@/utils/user"
|
||||
import { getMembership, isHighestMembership } from "@/utils/user"
|
||||
|
||||
import MembershipLevel from "./MemberShipLevel"
|
||||
import { MembershipNumberProps } from "./MemershipNumber/membershipNumber"
|
||||
import MembershipLevel from "./MembershipLevel"
|
||||
import MembershipNumber from "./MemershipNumber"
|
||||
|
||||
import styles from "./friend.module.css"
|
||||
|
||||
import type { UserProps } from "@/types/components/myPages/user"
|
||||
|
||||
export default async function Friend({ user }: UserProps) {
|
||||
export default async function Friend({
|
||||
user,
|
||||
color,
|
||||
}: UserProps & Pick<MembershipNumberProps, "color">) {
|
||||
const { formatMessage } = await getIntl()
|
||||
const membership = getMembership(user.memberships)
|
||||
if (!membership?.membershipLevel) {
|
||||
return null
|
||||
}
|
||||
const isHighestLevel = isHighestMembership(membership.membershipLevel)
|
||||
|
||||
return (
|
||||
<section className={styles.friend}>
|
||||
<header className={styles.header}>
|
||||
<BiroScript className={styles.levelLabel} color="pale">
|
||||
{formatMessage({ id: "Current level" })}:
|
||||
</BiroScript>
|
||||
<Body color="white" textTransform="bold" textAlign="center">
|
||||
{formatMessage(
|
||||
isHighestLevel
|
||||
? { id: "Highest level" }
|
||||
: { id: "Your current level" }
|
||||
)}
|
||||
</Body>
|
||||
{membership ? (
|
||||
<MembershipLevel
|
||||
level={membershipLevels[membership.membershipLevel]}
|
||||
@@ -35,12 +44,7 @@ export default async function Friend({ user }: UserProps) {
|
||||
<Title className={styles.name} color="pale" level="h3">
|
||||
{user.name}
|
||||
</Title>
|
||||
<div className={styles.membershipContainer}>
|
||||
<Caption color="pale">
|
||||
{formatMessage({ id: "Membership ID" })}:{" "}
|
||||
{membership ? membership.membershipNumber : "N/A"}
|
||||
</Caption>
|
||||
</div>
|
||||
<MembershipNumber membership={membership} color={color} />
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||
import { getIntl } from "@/i18n"
|
||||
import { getMembership } from "@/utils/user"
|
||||
|
||||
import type { UserProps } from "@/types/components/myPages/user"
|
||||
|
||||
export default async function ExpiringPoints({ user }: UserProps) {
|
||||
const { formatMessage } = await getIntl()
|
||||
const membership = getMembership(user.memberships)
|
||||
// TODO - add correct points when available from API
|
||||
if (!membership /* || !membership.expiringPoints*/) {
|
||||
// TODO: handle this case?
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<section>
|
||||
<Body color="white" textTransform="bold" textAlign="center">
|
||||
{membership.currentPoints} {formatMessage({ id: "points expiring by" })}{" "}
|
||||
{membership.expirationDate}
|
||||
</Body>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
import BiroScript from "@/components/TempDesignSystem/Text/BiroScript"
|
||||
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||
import Title from "@/components/TempDesignSystem/Text/Title"
|
||||
import { getIntl } from "@/i18n"
|
||||
import { getMembershipLevelObject } from "@/utils/membershipLevel"
|
||||
import { getMembership } from "@/utils/user"
|
||||
|
||||
import styles from "./nextLevel.module.css"
|
||||
|
||||
import type { UserProps } from "@/types/components/myPages/user"
|
||||
import type { LangParams } from "@/types/params"
|
||||
|
||||
export default async function NextLevel({
|
||||
user,
|
||||
lang,
|
||||
}: UserProps & LangParams) {
|
||||
const { formatMessage } = await getIntl()
|
||||
const membership = getMembership(user.memberships)
|
||||
|
||||
if (!membership?.membershipLevel) {
|
||||
// TODO: handle this case?
|
||||
return null
|
||||
}
|
||||
|
||||
const nextLevel = getMembershipLevelObject(
|
||||
membership.membershipLevel,
|
||||
lang,
|
||||
"nextLevel"
|
||||
)
|
||||
if (!nextLevel) {
|
||||
// TODO: already at top level, no next level exists
|
||||
return null
|
||||
}
|
||||
return (
|
||||
<section>
|
||||
<Body color="white" textAlign="center">
|
||||
{formatMessage({ id: "Next level" })}:
|
||||
</Body>
|
||||
<Title
|
||||
className={styles.nextLevel}
|
||||
color="white"
|
||||
level="h3"
|
||||
textAlign="center"
|
||||
>
|
||||
{nextLevel?.name || "N/A"}
|
||||
<BiroScript>{formatMessage({ id: "Coming up" })}!</BiroScript>
|
||||
</Title>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
.nextLevel {
|
||||
align-items: center;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
.nextLevel {
|
||||
gap: var(--Spacing-x1);
|
||||
grid-template-columns: auto auto;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
.points {
|
||||
display: grid;
|
||||
gap: var(--Spacing-x5);
|
||||
text-wrap: balance;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
.points {
|
||||
grid-template-rows: auto auto auto;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
row-gap: 0;
|
||||
column-gap: var(--Spacing-x2);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import styles from "./container.module.css"
|
||||
|
||||
export default function PointsContainer({ children }: React.PropsWithChildren) {
|
||||
return <section className={styles.points}>{children}</section>
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||
import Title from "@/components/TempDesignSystem/Text/Title"
|
||||
import { getIntl } from "@/i18n"
|
||||
|
||||
import styles from "./pointsColumn.module.css"
|
||||
|
||||
import type {
|
||||
NightsColumn,
|
||||
PointsColumn,
|
||||
PointsColumnProps,
|
||||
} from "@/types/components/myPages/points"
|
||||
|
||||
export const YourPointsColumn = ({ points }: PointsColumn) =>
|
||||
PointsColumn({
|
||||
points,
|
||||
title: "Your points",
|
||||
subtitle: "as of today",
|
||||
})
|
||||
|
||||
export const NextLevelPointsColumn = ({
|
||||
points,
|
||||
subtitleParam,
|
||||
}: PointsColumn) =>
|
||||
PointsColumn({
|
||||
points,
|
||||
title: "Points needed to level up",
|
||||
subtitleParam,
|
||||
subtitle: "next level:",
|
||||
})
|
||||
|
||||
export const StayOnLevelColumn = ({ points, subtitleParam }: PointsColumn) =>
|
||||
PointsColumn({
|
||||
points,
|
||||
title: "Points needed to stay on level",
|
||||
subtitleParam,
|
||||
subtitle: "by",
|
||||
})
|
||||
|
||||
export const NextLevelNightsColumn = ({
|
||||
nights,
|
||||
subtitleParam,
|
||||
}: NightsColumn) =>
|
||||
PointsColumn({
|
||||
nights,
|
||||
title: "Nights needed to level up",
|
||||
subtitleParam,
|
||||
subtitle: "by",
|
||||
})
|
||||
|
||||
async function PointsColumn({
|
||||
points,
|
||||
nights,
|
||||
title,
|
||||
subtitle,
|
||||
subtitleParam,
|
||||
}: PointsColumnProps) {
|
||||
const { formatMessage } = await getIntl()
|
||||
|
||||
return (
|
||||
<article>
|
||||
<Body
|
||||
color="white"
|
||||
textTransform="bold"
|
||||
textAlign="center"
|
||||
className={styles.firstRow}
|
||||
>
|
||||
{formatMessage({
|
||||
id: title,
|
||||
})}
|
||||
</Body>
|
||||
<Title color="white" level="h2" textAlign="center">
|
||||
{points ?? nights ?? "N/A"}
|
||||
</Title>
|
||||
<Body color="white" textAlign="center">
|
||||
{formatMessage({ id: subtitle })} {subtitleParam}
|
||||
</Body>
|
||||
</article>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
@media screen and (min-width: 768px) {
|
||||
.firstRow {
|
||||
align-content: flex-end;
|
||||
}
|
||||
}
|
||||
@@ -1,34 +1,30 @@
|
||||
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||
import Title from "@/components/TempDesignSystem/Text/Title"
|
||||
import { getIntl } from "@/i18n"
|
||||
import { MembershipLevelEnum } from "@/constants/membershipLevels"
|
||||
|
||||
import { getMembershipLevelObject } from "@/utils/membershipLevel"
|
||||
import { getMembership } from "@/utils/user"
|
||||
|
||||
import styles from "./totalPoints.module.css"
|
||||
import PointsContainer from "./Container"
|
||||
import { NextLevelPointsColumn, YourPointsColumn } from "./PointsColumn"
|
||||
|
||||
import type { UserProps } from "@/types/components/myPages/user"
|
||||
import { UserProps } from "@/types/components/myPages/user"
|
||||
import { LangParams } from "@/types/params"
|
||||
|
||||
export default async function Points({ user }: UserProps) {
|
||||
const { formatMessage } = await getIntl()
|
||||
export default async function Points({ user, lang }: UserProps & LangParams) {
|
||||
const membership = getMembership(user.memberships)
|
||||
const nextLevel = getMembershipLevelObject(
|
||||
membership?.nextLevel as MembershipLevelEnum,
|
||||
lang
|
||||
)
|
||||
|
||||
return (
|
||||
<section className={styles.points}>
|
||||
<article>
|
||||
<Body color="white" textAlign="center">
|
||||
{formatMessage({ id: "Total Points" })}
|
||||
</Body>
|
||||
<Title color="white" level="h2" textAlign="center">
|
||||
{membership ? membership.currentPoints : "N/A"}
|
||||
</Title>
|
||||
</article>
|
||||
<article>
|
||||
<Body color="white" textAlign="center">
|
||||
{formatMessage({ id: "Points until next level" })}
|
||||
{/* TODO */}
|
||||
</Body>
|
||||
<Title color="white" level="h2" textAlign="center">
|
||||
{membership ? membership.currentPoints : "N/A"}
|
||||
</Title>
|
||||
</article>
|
||||
</section>
|
||||
<PointsContainer>
|
||||
<YourPointsColumn points={membership?.currentPoints} />
|
||||
{nextLevel && (
|
||||
<NextLevelPointsColumn
|
||||
points={membership?.pointsRequiredToNextlevel}
|
||||
subtitleParam={nextLevel.name}
|
||||
/>
|
||||
)}
|
||||
</PointsContainer>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
.points {
|
||||
display: grid;
|
||||
gap: var(--Spacing-x2);
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
.points {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import Divider from "@/components/TempDesignSystem/Divider"
|
||||
|
||||
import NextLevel from "./NextLevel"
|
||||
import ExpiringPoints from "./ExpiringPoints"
|
||||
import Points from "./Points"
|
||||
|
||||
import styles from "./stats.module.css"
|
||||
@@ -11,9 +11,9 @@ import type { LangParams } from "@/types/params"
|
||||
export default function Stats({ user, lang }: UserProps & LangParams) {
|
||||
return (
|
||||
<section className={styles.stats}>
|
||||
<Points user={user} />
|
||||
<Divider variant="default" color="white" />
|
||||
<NextLevel user={user} lang={lang} />
|
||||
<Points user={user} lang={lang} />
|
||||
<Divider variant="default" color="pale" />
|
||||
<ExpiringPoints user={user} />
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import SectionHeader from "@/components/Section/Header"
|
||||
import SectionLink from "@/components/Section/Link"
|
||||
import Divider from "@/components/TempDesignSystem/Divider"
|
||||
|
||||
import Hero from "./Friend/Hero"
|
||||
import Friend from "./Friend"
|
||||
import Stats from "./Stats"
|
||||
|
||||
@@ -23,14 +24,15 @@ export default async function Overview({
|
||||
if (!user) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<SectionContainer>
|
||||
<SectionHeader link={link} subtitle={subtitle} title={title} topTitle />
|
||||
<section className={styles.overview}>
|
||||
<Friend user={user} />
|
||||
<Hero color="red">
|
||||
<Friend user={user} color="burgundy" />
|
||||
<Divider className={styles.divider} color="peach" />
|
||||
<Stats user={user} lang={lang} />
|
||||
</section>
|
||||
</Hero>
|
||||
<SectionLink link={link} variant="mobile" />
|
||||
</SectionContainer>
|
||||
)
|
||||
|
||||
@@ -1,12 +1,3 @@
|
||||
.overview {
|
||||
background-color: var(--Scandic-Brand-Scandic-Red);
|
||||
border-radius: var(--Corner-radius-xLarge);
|
||||
display: grid;
|
||||
gap: var(--Spacing-x2);
|
||||
grid-template-columns: 1fr;
|
||||
padding: var(--Spacing-x7) var(--Spacing-x6);
|
||||
}
|
||||
|
||||
.divider {
|
||||
padding-top: var(--Spacing-x2);
|
||||
}
|
||||
@@ -25,11 +16,6 @@
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
.overview {
|
||||
gap: var(--Spacing-x2);
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
|
||||
.divider {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ async function CurrentPointsBalance({
|
||||
<SectionContainer>
|
||||
<SectionHeader title={title} link={link} subtitle={subtitle} />
|
||||
<div className={styles.card}>
|
||||
<h2>{`${formatMessage({ id: "Total Points" })}*`}</h2>
|
||||
<h2>{`${formatMessage({ id: "Your points" })}*`}</h2>
|
||||
<p className={styles.points}>
|
||||
{`${formatMessage({ id: "Points" })}: ${membership ? membership.currentPoints : "N/A"}`}
|
||||
</p>
|
||||
|
||||
55
components/MyPages/Blocks/Points/Overview/Points/index.tsx
Normal file
55
components/MyPages/Blocks/Points/Overview/Points/index.tsx
Normal file
@@ -0,0 +1,55 @@
|
||||
import {
|
||||
MembershipLevelEnum,
|
||||
membershipLevels,
|
||||
} from "@/constants/membershipLevels"
|
||||
|
||||
import PointsContainer from "@/components/MyPages/Blocks/Overview/Stats/Points/Container"
|
||||
import {
|
||||
NextLevelNightsColumn,
|
||||
NextLevelPointsColumn,
|
||||
StayOnLevelColumn,
|
||||
YourPointsColumn,
|
||||
} from "@/components/MyPages/Blocks/Overview/Stats/Points/PointsColumn"
|
||||
import { getMembershipLevelObject } from "@/utils/membershipLevel"
|
||||
import { getMembership } from "@/utils/user"
|
||||
|
||||
import { UserProps } from "@/types/components/myPages/user"
|
||||
import { LangParams } from "@/types/params"
|
||||
|
||||
/* TODO */
|
||||
export default async function Points({ user, lang }: UserProps & LangParams) {
|
||||
const membership = getMembership(user.memberships)
|
||||
const nextLevel = getMembershipLevelObject(
|
||||
membership?.nextLevel as MembershipLevelEnum,
|
||||
lang
|
||||
)
|
||||
|
||||
return (
|
||||
<PointsContainer>
|
||||
<YourPointsColumn points={membership?.currentPoints} />
|
||||
{nextLevel && (
|
||||
<>
|
||||
{membership?.currentPoints ? (
|
||||
<StayOnLevelColumn
|
||||
points={membership?.currentPoints} //TODO
|
||||
subtitleParam={membership?.expirationDate}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<NextLevelPointsColumn
|
||||
points={membership?.pointsRequiredToNextlevel}
|
||||
subtitleParam={nextLevel.name}
|
||||
/>
|
||||
{nextLevel?.level === membershipLevels.L7 && (
|
||||
<NextLevelNightsColumn
|
||||
nights={100} //TODO
|
||||
subtitleParam={membership?.expirationDate}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</PointsContainer>
|
||||
)
|
||||
}
|
||||
39
components/MyPages/Blocks/Points/Overview/index.tsx
Normal file
39
components/MyPages/Blocks/Points/Overview/index.tsx
Normal file
@@ -0,0 +1,39 @@
|
||||
import { serverClient } from "@/lib/trpc/server"
|
||||
|
||||
import SectionContainer from "@/components/Section/Container"
|
||||
import SectionHeader from "@/components/Section/Header"
|
||||
import SectionLink from "@/components/Section/Link"
|
||||
import Divider from "@/components/TempDesignSystem/Divider"
|
||||
|
||||
import Friend from "../../Overview/Friend"
|
||||
import Hero from "../../Overview/Friend/Hero"
|
||||
import Stats from "../../Overview/Stats"
|
||||
|
||||
import styles from "./overview.module.css"
|
||||
|
||||
import type { AccountPageComponentProps } from "@/types/components/myPages/myPage/accountPage"
|
||||
import type { LangParams } from "@/types/params"
|
||||
|
||||
export default async function PointsOverview({
|
||||
link,
|
||||
subtitle,
|
||||
title,
|
||||
lang,
|
||||
}: AccountPageComponentProps & LangParams) {
|
||||
const user = await serverClient().user.get()
|
||||
if (!user) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<SectionContainer>
|
||||
<SectionHeader link={link} subtitle={subtitle} title={title} topTitle />
|
||||
<Hero color="burgundy">
|
||||
<Friend user={user} color="red" />
|
||||
<Divider className={styles.divider} color="peach" />
|
||||
<Stats user={user} lang={lang} />
|
||||
</Hero>
|
||||
<SectionLink link={link} variant="mobile" />
|
||||
</SectionContainer>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
.divider {
|
||||
padding-top: var(--Spacing-x2);
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
.divider {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,10 @@
|
||||
border-bottom-color: var(--Scandic-Brand-Burgundy);
|
||||
}
|
||||
|
||||
.pale {
|
||||
border-bottom-color: var(--Primary-Dark-On-Surface-Text);
|
||||
}
|
||||
|
||||
.peach {
|
||||
border-bottom-color: var(--Primary-Light-On-Surface-Divider);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ export const dividerVariants = cva(styles.divider, {
|
||||
beige: styles.beige,
|
||||
white: styles.white,
|
||||
subtle: styles.subtle,
|
||||
pale: styles.pale,
|
||||
},
|
||||
opacity: {
|
||||
100: styles.opacity100,
|
||||
|
||||
@@ -5,11 +5,13 @@
|
||||
"All rooms comes with standard amenities": "Alle værelser er udstyret med standardfaciliteter",
|
||||
"Already a friend?": "Allerede en ven?",
|
||||
"Arrival date": "Ankomstdato",
|
||||
"as of today": "fra idag",
|
||||
"As our": "Som vores",
|
||||
"As our Close Friend": "Som vores nære ven",
|
||||
"At the hotel": "På hotellet",
|
||||
"Book": "Bestil",
|
||||
"Booking number": "Bestillingsnummer",
|
||||
"by": "inden",
|
||||
"Cancel": "Afbestille",
|
||||
"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.",
|
||||
"Choose room": "Vælg rum",
|
||||
@@ -24,7 +26,7 @@
|
||||
"Could not find requested resource": "Kunne ikke finde den anmodede ressource",
|
||||
"Country": "Land",
|
||||
"Country code": "Landekode",
|
||||
"Current level": "Nuværende niveau",
|
||||
"Your current level": "Dit nuværende niveau",
|
||||
"Current password": "Nuværende kodeord",
|
||||
"characters": "tegn",
|
||||
"Date of Birth": "Fødselsdato",
|
||||
@@ -41,6 +43,7 @@
|
||||
"From": "Fra",
|
||||
"Get inspired": "Blive inspireret",
|
||||
"Go back to overview": "Gå tilbage til oversigten",
|
||||
"Highest level": "Højeste niveau",
|
||||
"How it works": "Hvordan det virker",
|
||||
"Join Scandic Friends": "Tilmeld dig Scandic Friends",
|
||||
"Language": "Sprog",
|
||||
@@ -60,12 +63,13 @@
|
||||
"My wishes": "Mine ønsker",
|
||||
"New password": "Nyt kodeord",
|
||||
"Next": "Næste",
|
||||
"Next level": "Næste niveau",
|
||||
"next level:": "Næste niveau:",
|
||||
"No content published": "Intet indhold offentliggjort",
|
||||
"No transactions available": "Ingen tilgængelige transaktioner",
|
||||
"Not found": "Ikke fundet",
|
||||
"night": "nat",
|
||||
"nights": "nætter",
|
||||
"Nights needed to level up": "Nætter nødvendige for at komme i niveau",
|
||||
"number": "nummer",
|
||||
"On your journey": "På din rejse",
|
||||
"Open": "Åben",
|
||||
@@ -77,9 +81,10 @@
|
||||
"Please enter a valid phone number": "Indtast venligst et gyldigt telefonnummer",
|
||||
"Points": "Points",
|
||||
"Points may take up to 10 days to be displayed.": "Det kan tage op til 10 dage at få vist point.",
|
||||
"Points until next level": "Point indtil næste niveau",
|
||||
"Points needed to level up": "Point nødvendige for at komme i niveau",
|
||||
"Points needed to stay on level": "Point nødvendige for at holde sig på niveau",
|
||||
"points expiring by": "point udløber den",
|
||||
"Previous victories": "Tidligere sejre",
|
||||
"points until next level": "point indtil næste niveau",
|
||||
"Read more": "Læs mere",
|
||||
"Read more about the hotel": "Læs mere om hotellet",
|
||||
"Retype new password": "Gentag den nye adgangskode",
|
||||
@@ -95,6 +100,7 @@
|
||||
"Street": "Gade",
|
||||
"special character": "speciel karakter",
|
||||
"Total Points": "Samlet antal point",
|
||||
"Your points": "Dine pointer",
|
||||
"Transaction date": "Overførselsdato",
|
||||
"Transactions": "Transaktioner",
|
||||
"Tripadvisor reviews": "{rating} ({count} anmeldelser på Tripadvisor)",
|
||||
|
||||
@@ -5,11 +5,13 @@
|
||||
"All rooms comes with standard amenities": "Alle Zimmer sind mit den üblichen Annehmlichkeiten ausgestattet",
|
||||
"Already a friend?": "Schon ein Freund?",
|
||||
"Arrival date": "Ankunftsdatum",
|
||||
"as of today": "Ab heute",
|
||||
"As our": "Als unsere",
|
||||
"As our Close Friend": "Als unser enger Freund",
|
||||
"At the hotel": "Im Hotel",
|
||||
"Book": "Buch",
|
||||
"Booking number": "Buchungsnummer",
|
||||
"by": "bis",
|
||||
"Cancel": "Stornieren",
|
||||
"Check out the credit cards saved to your profile. Pay with a saved card when signed in for a smoother web experience.": "Sehen Sie sich die in Ihrem Profil gespeicherten Kreditkarten an. Bezahlen Sie mit einer gespeicherten Karte, wenn Sie angemeldet sind, für ein reibungsloseres Web-Erlebnis.",
|
||||
"Choose room": "Zimmer wählen",
|
||||
@@ -24,7 +26,7 @@
|
||||
"Could not find requested resource": "Die angeforderte Ressource konnte nicht gefunden werden.",
|
||||
"Country": "Land",
|
||||
"Country code": "Landesvorwahl",
|
||||
"Current level": "Aktuelles Level",
|
||||
"Your current level": "Ihr aktuelles Level",
|
||||
"Current password": "Aktuelles Passwort",
|
||||
"characters": "figuren",
|
||||
"Date of Birth": "Geburtsdatum",
|
||||
@@ -41,6 +43,7 @@
|
||||
"From": "Fromm",
|
||||
"Get inspired": "Lass dich inspirieren",
|
||||
"Go back to overview": "Zurück zur Übersicht",
|
||||
"Highest level": "Höchstes Level",
|
||||
"How it works": "Wie es funktioniert",
|
||||
"Join Scandic Friends": "Treten Sie Scandic Friends bei",
|
||||
"Language": "Sprache",
|
||||
@@ -60,12 +63,13 @@
|
||||
"My wishes": "Meine Wünsche",
|
||||
"New password": "Neues Kennwort",
|
||||
"Next": "Nächste",
|
||||
"Next level": "Nächste Ebene",
|
||||
"next level:": "Nächste Ebene:",
|
||||
"No content published": "Kein Inhalt veröffentlicht",
|
||||
"No transactions available": "Keine Transaktionen verfügbar",
|
||||
"Not found": "Nicht gefunden",
|
||||
"night": "nacht",
|
||||
"nights": "nächte",
|
||||
"Nights needed to level up": "Nächte, die zum Levelaufstieg benötigt werden",
|
||||
"number": "nummer",
|
||||
"On your journey": "Auf deiner Reise",
|
||||
"Open": "Offen",
|
||||
@@ -77,9 +81,10 @@
|
||||
"Please enter a valid phone number": "Bitte geben Sie eine gültige Telefonnummer ein",
|
||||
"Points": "Punkte",
|
||||
"Points may take up to 10 days to be displayed.": "Es kann bis zu 10 Tage dauern, bis Punkte angezeigt werden.",
|
||||
"Points until next level": "Punkte bis zum nächsten Level",
|
||||
"Points needed to level up": "Punkte, die zum Levelaufstieg benötigt werden",
|
||||
"Points needed to stay on level": "Erforderliche Punkte, um auf diesem Niveau zu bleiben",
|
||||
"points expiring by": "punkte verfallen bis zum",
|
||||
"Previous victories": "Bisherige Siege",
|
||||
"points until next level": "punkte bis zum nächsten Level",
|
||||
"Read more": "Mehr lesen",
|
||||
"Read more about the hotel": "Lesen Sie mehr über das Hotel",
|
||||
"Retype new password": "Neues Passwort erneut eingeben",
|
||||
@@ -95,6 +100,7 @@
|
||||
"Street": "Straße",
|
||||
"special character": "sonderzeichen",
|
||||
"Total Points": "Gesamtpunktzahl",
|
||||
"Your points": "Deine Punkte",
|
||||
"Transaction date": "Transaktionsdatum",
|
||||
"Transactions": "Transaktionen",
|
||||
"Tripadvisor reviews": "{rating} ({count} Bewertungen auf Tripadvisor)",
|
||||
|
||||
@@ -5,11 +5,13 @@
|
||||
"All rooms comes with standard amenities": "All rooms comes with standard amenities",
|
||||
"Already a friend?": "Already a friend?",
|
||||
"Arrival date": "Arrival date",
|
||||
"as of today": "as of today",
|
||||
"As our": "As our",
|
||||
"As our Close Friend": "As our Close Friend",
|
||||
"At the hotel": "At the hotel",
|
||||
"Book": "Book",
|
||||
"Booking number": "Booking number",
|
||||
"by": "by",
|
||||
"Cancel": "Cancel",
|
||||
"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.",
|
||||
"Choose room": "Choose room",
|
||||
@@ -24,7 +26,7 @@
|
||||
"Could not find requested resource": "Could not find requested resource",
|
||||
"Country": "Country",
|
||||
"Country code": "Country code",
|
||||
"Current level": "Current level",
|
||||
"Your current level": "Your current level",
|
||||
"Current password": "Current password",
|
||||
"characters": "characters",
|
||||
"Date of Birth": "Date of Birth",
|
||||
@@ -46,6 +48,7 @@
|
||||
"hotelPages.rooms.roomCard.person": "person",
|
||||
"hotelPages.rooms.roomCard.persons": "persons",
|
||||
"hotelPages.rooms.roomCard.seeRoomDetails": "See room details",
|
||||
"Highest level": "Highest level",
|
||||
"How it works": "How it works",
|
||||
"Join Scandic Friends": "Join Scandic Friends",
|
||||
"Language": "Language",
|
||||
@@ -65,12 +68,13 @@
|
||||
"My wishes": "My wishes",
|
||||
"New password": "New password",
|
||||
"Next": "Next",
|
||||
"Next level": "Next level",
|
||||
"next level:": "next level:",
|
||||
"No content published": "No content published",
|
||||
"No transactions available": "No transactions available",
|
||||
"Not found": "Not found",
|
||||
"night": "night",
|
||||
"nights": "nights",
|
||||
"Nights needed to level up": "Nights needed to level up",
|
||||
"number": "number",
|
||||
"On your journey": "On your journey",
|
||||
"Open": "Open",
|
||||
@@ -82,9 +86,10 @@
|
||||
"Please enter a valid phone number": "Please enter a valid phone number",
|
||||
"Points": "Points",
|
||||
"Points may take up to 10 days to be displayed.": "Points may take up to 10 days to be displayed.",
|
||||
"Points until next level": "Points until next level",
|
||||
"Points needed to level up": "Points needed to level up",
|
||||
"Points needed to stay on level": "Points needed to stay on level",
|
||||
"points expiring by": "points expiring by",
|
||||
"Previous victories": "Previous victories",
|
||||
"points until next level": "points until next level",
|
||||
"Read more": "Read more",
|
||||
"Read more about the hotel": "Read more about the hotel",
|
||||
"Retype new password": "Retype new password",
|
||||
@@ -100,6 +105,7 @@
|
||||
"Street": "Street",
|
||||
"special character": "special character",
|
||||
"Total Points": "Total Points",
|
||||
"Your points": "Your points",
|
||||
"Transaction date": "Transaction date",
|
||||
"Transactions": "Transactions",
|
||||
"Tripadvisor reviews": "{rating} ({count} reviews on Tripadvisor)",
|
||||
|
||||
@@ -5,11 +5,13 @@
|
||||
"All rooms comes with standard amenities": "Kaikissa huoneissa on perusmukavuudet",
|
||||
"Already a friend?": "Oletko jo ystävä?",
|
||||
"Arrival date": "Saapumispäivä",
|
||||
"as of today": "tästä päivästä lähtien",
|
||||
"As our": "Kuin meidän",
|
||||
"As our Close Friend": "Läheisenä ystävänämme",
|
||||
"At the hotel": "Hotellissa",
|
||||
"Book": "Kirja",
|
||||
"Booking number": "Varausnumero",
|
||||
"by": "mennessä",
|
||||
"Cancel": "Peruuttaa",
|
||||
"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.",
|
||||
"Choose room": "Valitse huone",
|
||||
@@ -24,7 +26,7 @@
|
||||
"Could not find requested resource": "Pyydettyä resurssia ei löytynyt",
|
||||
"Country": "Maa",
|
||||
"Country code": "Maatunnus",
|
||||
"Current level": "Nykyinen taso",
|
||||
"Your current level": "Nykyinen tasosi",
|
||||
"Current password": "Nykyinen salasana",
|
||||
"characters": "hahmoja",
|
||||
"Date of Birth": "Syntymäaika",
|
||||
@@ -41,6 +43,7 @@
|
||||
"From": "From",
|
||||
"Get inspired": "Inspiroidu",
|
||||
"Go back to overview": "Palaa yleiskatsaukseen",
|
||||
"Highest level": "Korkein taso",
|
||||
"How it works": "Kuinka se toimii",
|
||||
"Join Scandic Friends": "Liity Scandic Friends",
|
||||
"Language": "Kieli",
|
||||
@@ -60,12 +63,13 @@
|
||||
"My wishes": "Toiveeni",
|
||||
"New password": "Uusi salasana",
|
||||
"Next": "Seuraava",
|
||||
"Next level": "Seuraava taso",
|
||||
"next level:": "Seuraava taso:",
|
||||
"No content published": "Ei julkaistua sisältöä",
|
||||
"No transactions available": "Ei tapahtumia saatavilla",
|
||||
"Not found": "Ei löydetty",
|
||||
"night": "yö",
|
||||
"nights": "yöt",
|
||||
"Nights needed to level up": "Yöt, joita tarvitaan tasolle",
|
||||
"number": "määrä",
|
||||
"On your journey": "Matkallasi",
|
||||
"Open": "Avata",
|
||||
@@ -77,9 +81,10 @@
|
||||
"Please enter a valid phone number": "Ole hyvä ja näppäile voimassaoleva puhelinnumero",
|
||||
"Points": "Pisteet",
|
||||
"Points may take up to 10 days to be displayed.": "Pisteiden näyttäminen voi kestää jopa 10 päivää.",
|
||||
"Points until next level": "Pisteitä seuraavalle tasolle",
|
||||
"Points needed to level up": "Pisteitä tarvitaan tasolle pääsemiseksi",
|
||||
"Points needed to stay on level": "Tällä tasolla pysymiseen tarvittavat pisteet",
|
||||
"points expiring by": "pisteet vanhenevat viimeistään",
|
||||
"Previous victories": "Edelliset voitot",
|
||||
"points until next level": "pisteitä seuraavalle tasolle",
|
||||
"Read more": "Lue lisää",
|
||||
"Read more about the hotel": "Lue lisää hotellista",
|
||||
"Retype new password": "Kirjoita uusi salasana uudelleen",
|
||||
@@ -95,6 +100,7 @@
|
||||
"Street": "Katu",
|
||||
"special character": "erikoishahmo",
|
||||
"Total Points": "Kokonaispisteet",
|
||||
"Your points": "Sinun pisteesi",
|
||||
"Transaction date": "Tapahtuman päivämäärä",
|
||||
"Transactions": "Tapahtumat",
|
||||
"Tripadvisor reviews": "{rating} ({count} arvostelua TripAdvisorissa)",
|
||||
|
||||
@@ -5,11 +5,13 @@
|
||||
"All rooms comes with standard amenities": "Alle rommene har standard fasiliteter",
|
||||
"Already a friend?": "Allerede en venn?",
|
||||
"Arrival date": "Ankomstdato",
|
||||
"as of today": "per idag",
|
||||
"As our": "Som vår",
|
||||
"As our Close Friend": "Som vår nære venn",
|
||||
"At the hotel": "På hotellet",
|
||||
"Book": "Bok",
|
||||
"Booking number": "Bestillingsnummer",
|
||||
"by": "innen",
|
||||
"Cancel": "Avbryt",
|
||||
"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.",
|
||||
"Choose room": "Velg rom",
|
||||
@@ -24,7 +26,7 @@
|
||||
"Could not find requested resource": "Kunne ikke finne den forespurte ressursen",
|
||||
"Country": "Land",
|
||||
"Country code": "Landskode",
|
||||
"Current level": "Nåværende nivå",
|
||||
"Your current level": "Ditt nåværende nivå",
|
||||
"Current password": "Nåværende passord",
|
||||
"characters": "tegn",
|
||||
"Date of Birth": "Fødselsdato",
|
||||
@@ -41,6 +43,7 @@
|
||||
"From": "Fra",
|
||||
"Get inspired": "Bli inspirert",
|
||||
"Go back to overview": "Gå tilbake til oversikten",
|
||||
"Highest level": "Høyeste nivå",
|
||||
"How it works": "Hvordan det fungerer",
|
||||
"Join Scandic Friends": "Bli med i Scandic Friends",
|
||||
"Language": "Språk",
|
||||
@@ -60,12 +63,13 @@
|
||||
"My wishes": "Mine ønsker",
|
||||
"New password": "Nytt passord",
|
||||
"Next": "Neste",
|
||||
"Next level": "Neste nivå",
|
||||
"next level:": "Neste nivå:",
|
||||
"No content published": "Ingen innhold publisert",
|
||||
"No transactions available": "Ingen transaksjoner tilgjengelig",
|
||||
"Not found": "Ikke funnet",
|
||||
"night": "natt",
|
||||
"nights": "netter",
|
||||
"Nights needed to level up": "Netter som trengs for å komme opp i nivå",
|
||||
"number": "antall",
|
||||
"On your journey": "På reisen din",
|
||||
"Open": "Åpen",
|
||||
@@ -77,9 +81,10 @@
|
||||
"Please enter a valid phone number": "Vennligst oppgi et gyldig telefonnummer",
|
||||
"Points": "Poeng",
|
||||
"Points may take up to 10 days to be displayed.": "Det kan ta opptil 10 dager før poeng vises.",
|
||||
"Points until next level": "Poeng til neste nivå",
|
||||
"Points needed to level up": "Poeng som trengs for å komme opp i nivå",
|
||||
"Points needed to stay on level": "Poeng som trengs for å holde seg på nivå",
|
||||
"points expiring by": "poeng utløper innen",
|
||||
"Previous victories": "Tidligere seire",
|
||||
"points until next level": "poeng til neste nivå",
|
||||
"Read more": "Les mer",
|
||||
"Read more about the hotel": "Les mer om hotellet",
|
||||
"Retype new password": "Skriv inn nytt passord på nytt",
|
||||
@@ -95,6 +100,7 @@
|
||||
"Street": "Gate",
|
||||
"special character": "spesiell karakter",
|
||||
"Total Points": "Totale poeng",
|
||||
"Your points": "Dine poeng",
|
||||
"Transaction date": "Transaksjonsdato",
|
||||
"Transactions": "Transaksjoner",
|
||||
"Tripadvisor reviews": "{rating} ({count} anmeldelser på Tripadvisor)",
|
||||
|
||||
@@ -5,11 +5,13 @@
|
||||
"All rooms comes with standard amenities": "Alla rum har standardbekvämligheter",
|
||||
"Already a friend?": "Redan en vän?",
|
||||
"Arrival date": "Ankomstdatum",
|
||||
"as of today": "från och med idag",
|
||||
"As our": "Som vår",
|
||||
"As our Close Friend": "Som vår nära vän",
|
||||
"At the hotel": "På hotellet",
|
||||
"Book": "Boka",
|
||||
"Booking number": "Bokningsnummer",
|
||||
"by": "innan",
|
||||
"Cancel": "Avbryt",
|
||||
"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.",
|
||||
"Choose room": "Välj rum",
|
||||
@@ -24,7 +26,7 @@
|
||||
"Could not find requested resource": "Det gick inte att hitta den begärda resursen",
|
||||
"Country": "Land",
|
||||
"Country code": "Landskod",
|
||||
"Current level": "Nuvarande nivå",
|
||||
"Your current level": "Din nuvarande nivå",
|
||||
"Current password": "Nuvarande lösenord",
|
||||
"characters": "tecken",
|
||||
"Date of Birth": "Födelsedatum",
|
||||
@@ -41,6 +43,7 @@
|
||||
"From": "Från",
|
||||
"Get inspired": "Bli inspirerad",
|
||||
"Go back to overview": "Gå tillbaka till översikten",
|
||||
"Highest level": "Högsta nivå",
|
||||
"How it works": "Hur det fungerar",
|
||||
"hotelPages.rooms.title": "Rum",
|
||||
"hotelPages.rooms.roomCard.person": "person",
|
||||
@@ -64,12 +67,13 @@
|
||||
"My wishes": "Mina önskningar",
|
||||
"New password": "Nytt lösenord",
|
||||
"Next": "Nästa",
|
||||
"Next level": "Nästa nivå",
|
||||
"next level:": "Nästa nivå:",
|
||||
"No content published": "Inget innehåll publicerat",
|
||||
"No transactions available": "Inga transaktioner tillgängliga",
|
||||
"Not found": "Hittades inte",
|
||||
"night": "natt",
|
||||
"nights": "nätter",
|
||||
"Nights needed to level up": "Nätter som behövs för att gå upp i nivå",
|
||||
"number": "nummer",
|
||||
"On your journey": "På din resa",
|
||||
"Open": "Öppna",
|
||||
@@ -81,9 +85,10 @@
|
||||
"Please enter a valid phone number": "Var vänlig och ange ett giltigt telefonnummer",
|
||||
"Points": "Poäng",
|
||||
"Points may take up to 10 days to be displayed.": "Det kan ta upp till 10 dagar innan poäng visas.",
|
||||
"Points until next level": "Poäng till nästa nivå",
|
||||
"Points needed to level up": "Poäng som behövs för att gå upp i nivå",
|
||||
"Points needed to stay on level": "Poäng som behövs för att hålla sig på nivå",
|
||||
"points expiring by": "poäng förfaller till",
|
||||
"Previous victories": "Tidigare segrar",
|
||||
"points until next level": "poäng till nästa nivå",
|
||||
"Read more": "Läs mer",
|
||||
"Read more about the hotel": "Läs mer om hotellet",
|
||||
"Retype new password": "Upprepa nytt lösenord",
|
||||
@@ -99,6 +104,7 @@
|
||||
"Street": "Gata",
|
||||
"special character": "speciell karaktär",
|
||||
"Total Points": "Total poäng",
|
||||
"Your points": "Dina poäng",
|
||||
"Transaction date": "Transaktionsdatum",
|
||||
"Transactions": "Transaktioner",
|
||||
"Tripadvisor reviews": "{rating} ({count} recensioner på Tripadvisor)",
|
||||
|
||||
@@ -25,6 +25,8 @@ export const getUserSchema = z.object({
|
||||
membershipLevel: z.nativeEnum(MembershipLevelEnum).optional(),
|
||||
memberSince: z.string(),
|
||||
membershipType: z.string(),
|
||||
nextLevel: z.string().optional(),
|
||||
pointsRequiredToNextlevel: z.number().optional(),
|
||||
})
|
||||
),
|
||||
phoneNumber: z.string().optional(),
|
||||
|
||||
@@ -3,3 +3,7 @@ import { membershipLevels } from "@/constants/membershipLevels"
|
||||
export type MembershipLevelProps = {
|
||||
level: membershipLevels
|
||||
}
|
||||
|
||||
export type CopyButtonProps = {
|
||||
membershipNumber: string
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
export enum DynamicContentComponents {
|
||||
membership_overview = "membership_overview",
|
||||
points_overview = "points_overview",
|
||||
soonest_stays = "soonest_stays",
|
||||
previous_stays = "previous_stays",
|
||||
upcoming_stays = "upcoming_stays",
|
||||
|
||||
16
types/components/myPages/points.ts
Normal file
16
types/components/myPages/points.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
interface PointsOrNightColumn {
|
||||
title?: string
|
||||
subtitle?: string
|
||||
subtitleParam?: string
|
||||
}
|
||||
|
||||
export interface PointsColumn extends PointsOrNightColumn {
|
||||
points: number | undefined
|
||||
nights?: never
|
||||
}
|
||||
export interface NightsColumn extends PointsOrNightColumn {
|
||||
points?: never
|
||||
nights: number | undefined
|
||||
}
|
||||
|
||||
export type PointsColumnProps = PointsColumn | NightsColumn
|
||||
@@ -8,13 +8,9 @@ import levelsData from "@/components/Loyalty/Blocks/DynamicContent/LoyaltyLevels
|
||||
|
||||
export function getMembershipLevelObject(
|
||||
membershipLevel: MembershipLevelEnum,
|
||||
lang: Lang,
|
||||
level: "currentLevel" | "nextLevel" = "currentLevel"
|
||||
lang: Lang
|
||||
) {
|
||||
let levelNumber = membershipLevels[membershipLevel]
|
||||
if (level === "nextLevel") {
|
||||
levelNumber += 1
|
||||
}
|
||||
|
||||
return levelsData[lang].levels.find((level) => level.level === levelNumber)
|
||||
return levelsData[lang].levels.find(
|
||||
(level) => level.level === membershipLevels[membershipLevel]
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { z } from "zod"
|
||||
|
||||
import { MembershipLevelEnum } from "@/constants/membershipLevels"
|
||||
import { getMembershipCardsSchema } from "@/server/routers/user/output"
|
||||
|
||||
import { User } from "@/types/user"
|
||||
@@ -29,6 +30,12 @@ export function getMembershipCards(
|
||||
})
|
||||
}
|
||||
|
||||
export function isHighestMembership(
|
||||
membershipLevel: MembershipLevelEnum | undefined
|
||||
) {
|
||||
return membershipLevel == MembershipLevelEnum.L7
|
||||
}
|
||||
|
||||
export function getInitials(
|
||||
firstName: User["firstName"],
|
||||
lastName: User["lastName"]
|
||||
|
||||
Reference in New Issue
Block a user