Fix(SW-1711)/(SW-2077): Export icons individually * fix(SW-1711): export icons individually Approved-by: Michael Zetterberg Approved-by: Erik Tiekstra
248 lines
7.6 KiB
TypeScript
248 lines
7.6 KiB
TypeScript
"use client"
|
|
|
|
import Link from "next/link"
|
|
import { useParams } from "next/navigation"
|
|
import { useContext, useState } from "react"
|
|
import {
|
|
I18nProvider,
|
|
Slider,
|
|
SliderOutput,
|
|
SliderStateContext,
|
|
SliderThumb,
|
|
SliderTrack,
|
|
TextField,
|
|
} from "react-aria-components"
|
|
import { FormProvider, useForm } from "react-hook-form"
|
|
import { useIntl } from "react-intl"
|
|
|
|
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
|
import { Typography } from "@scandic-hotels/design-system/Typography"
|
|
|
|
import { SAS_TRANSFER_POINT_KEY } from "@/app/[lang]/(partner)/(sas)/(protected)/sas-x-scandic/sasUtils"
|
|
import Image from "@/components/Image"
|
|
import Modal from "@/components/Modal"
|
|
import Button from "@/components/TempDesignSystem/Button"
|
|
import AriaInputWithLabel from "@/components/TempDesignSystem/Form/Input/AriaInputWithLabel"
|
|
|
|
import styles from "./transferPoints.module.css"
|
|
|
|
import type { LangParams } from "@/types/params"
|
|
import type { Lang } from "@/constants/languages"
|
|
|
|
type TransferPointsFormClientProps = {
|
|
sasPoints: number | null
|
|
exchangeRate: number | null
|
|
lang: Lang
|
|
}
|
|
|
|
export function TransferPointsFormClient({
|
|
sasPoints,
|
|
exchangeRate,
|
|
lang,
|
|
}: TransferPointsFormClientProps) {
|
|
const intl = useIntl()
|
|
const formMethods = useForm()
|
|
const [pointState, setPointState] = useState<number | null>(0)
|
|
const selectedPoints = pointState ?? 0
|
|
|
|
const disabled = !exchangeRate
|
|
|
|
const parsedPoints = Math.min(selectedPoints, sasPoints ?? 0)
|
|
|
|
const calculatedPoints = parsedPoints * (exchangeRate ?? 0)
|
|
|
|
const handleUpdatePoints = (points: number | null) => {
|
|
setPointState(points)
|
|
}
|
|
|
|
const hasNoSasPoints = !sasPoints || sasPoints === 0
|
|
|
|
return (
|
|
<FormProvider {...formMethods}>
|
|
<I18nProvider locale={lang}>
|
|
<Slider
|
|
value={parsedPoints}
|
|
onChange={handleUpdatePoints}
|
|
className={styles.slider}
|
|
// Set max value to 1 if sasPoints is 0 since slider requires a range
|
|
maxValue={hasNoSasPoints ? 1 : sasPoints}
|
|
aria-label={intl.formatMessage({ id: "EB points to transfer" })}
|
|
formatOptions={{
|
|
useGrouping: true,
|
|
maximumFractionDigits: 0,
|
|
}}
|
|
isDisabled={disabled || hasNoSasPoints}
|
|
>
|
|
<SliderTrack className={styles.sliderTrack}>
|
|
<SliderFill />
|
|
<SliderThumb className={styles.sliderThumb}>
|
|
<SliderOutput className={styles.sliderOutput} />
|
|
<MaterialIcon icon="swipe" color="Icon/Inverted" />
|
|
</SliderThumb>
|
|
</SliderTrack>
|
|
</Slider>
|
|
</I18nProvider>
|
|
<div className={styles.inputsWrapper}>
|
|
<TextField type="number" isDisabled={disabled}>
|
|
<AriaInputWithLabel
|
|
label={intl.formatMessage({ id: "EB points to transfer" })}
|
|
type="number"
|
|
min={0}
|
|
value={pointState ?? ""}
|
|
className={styles.pointsInput}
|
|
disabled={disabled}
|
|
onChange={(e) => {
|
|
const value = parseInt(e.target.value, 10)
|
|
|
|
handleUpdatePoints(isNaN(value) ? null : value)
|
|
}}
|
|
onBlur={() => {
|
|
handleUpdatePoints(parsedPoints)
|
|
}}
|
|
/>
|
|
</TextField>
|
|
<Typography variant="Body/Supporting text (caption)/smRegular">
|
|
<p className={styles.conversionRate}>
|
|
{/* TODO maybe dynamic string based on exchange rate */}
|
|
{intl.formatMessage({
|
|
id: "1 EuroBonus point = 2 Scandic Friends points",
|
|
})}
|
|
</p>
|
|
</Typography>
|
|
<div className={styles.pointsOutput}>
|
|
<Typography variant="Label/xsRegular">
|
|
<p>{intl.formatMessage({ id: "SF points to receive" })}</p>
|
|
</Typography>
|
|
<Typography variant="Body/Paragraph/mdRegular">
|
|
<p>{intl.formatNumber(calculatedPoints)}</p>
|
|
</Typography>
|
|
</div>
|
|
</div>
|
|
<ConfirmModal
|
|
disabled={disabled || calculatedPoints === 0}
|
|
sasPoints={parsedPoints}
|
|
scandicPoints={calculatedPoints}
|
|
/>
|
|
</FormProvider>
|
|
)
|
|
}
|
|
|
|
function SliderFill() {
|
|
const state = useContext(SliderStateContext)!
|
|
|
|
return (
|
|
<div
|
|
style={{
|
|
width: `${state.getThumbPercent(0) * 100}%`,
|
|
}}
|
|
className={styles.sliderFill}
|
|
/>
|
|
)
|
|
}
|
|
|
|
type ConfirmModalProps = {
|
|
sasPoints: number
|
|
scandicPoints: number
|
|
disabled?: boolean
|
|
}
|
|
|
|
function ConfirmModal({
|
|
sasPoints,
|
|
scandicPoints,
|
|
disabled,
|
|
}: ConfirmModalProps) {
|
|
const { lang } = useParams<LangParams>()
|
|
const [isOpen, setIsOpen] = useState(false)
|
|
const intl = useIntl()
|
|
|
|
const handleToggle = (open: boolean) => {
|
|
setIsOpen(open)
|
|
if (open) {
|
|
const expireIn15Minutes = new Date(
|
|
Date.now() + 15 * 60 * 1000
|
|
).toUTCString()
|
|
document.cookie = `${SAS_TRANSFER_POINT_KEY}=${JSON.stringify(sasPoints)};path=/;expires=${expireIn15Minutes}`
|
|
} else {
|
|
document.cookie = `${SAS_TRANSFER_POINT_KEY}=;path=/;expires=Thu, 01 Jan 1970 00:00:01 GMT`
|
|
}
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<Button
|
|
className={styles.transferButton}
|
|
onClick={() => handleToggle(true)}
|
|
disabled={disabled}
|
|
>
|
|
{intl.formatMessage({ id: "Transfer points" })}
|
|
</Button>
|
|
<Modal isOpen={isOpen} onToggle={handleToggle}>
|
|
<div className={styles.modalContainer}>
|
|
<Image
|
|
src="/_static/img/scandic-money-hand.svg"
|
|
alt=""
|
|
width="133"
|
|
height="119"
|
|
/>
|
|
<Typography variant="Title/Subtitle/lg">
|
|
<h3>
|
|
{intl.formatMessage({ id: "Proceed with point transfer?" })}
|
|
</h3>
|
|
</Typography>
|
|
<div>
|
|
<Typography variant="Body/Paragraph/mdRegular">
|
|
<p>{intl.formatMessage({ id: "You are about to exchange:" })}</p>
|
|
</Typography>
|
|
<Typography variant="Body/Paragraph/mdRegular">
|
|
<p>
|
|
{intl.formatMessage(
|
|
{
|
|
id: "<bold>{sasPoints, number} EuroBonus points</bold> to <bold>{scandicPoints, number} Scandic Friends points</bold>",
|
|
},
|
|
{
|
|
sasPoints,
|
|
scandicPoints,
|
|
bold: (text) => (
|
|
<Typography variant="Body/Paragraph/mdBold">
|
|
<span>{text}</span>
|
|
</Typography>
|
|
),
|
|
}
|
|
)}
|
|
</p>
|
|
</Typography>
|
|
</div>
|
|
<Typography variant="Body/Supporting text (caption)/smRegular">
|
|
<p className={styles.expiryText}>
|
|
{intl.formatMessage({
|
|
id: "Your exchanged points will retain their original expiry date with a maximum validity of 12 months.",
|
|
})}
|
|
</p>
|
|
</Typography>
|
|
<div className={styles.divider} />
|
|
<div className={styles.buttonContainer}>
|
|
<Button asChild theme="base" fullWidth>
|
|
<Link
|
|
href={`/${lang}/sas-x-scandic/login?intent=transfer`}
|
|
color="none"
|
|
>
|
|
{intl.formatMessage({
|
|
id: "Yes, I want to transfer my points",
|
|
})}
|
|
</Link>
|
|
</Button>
|
|
<Button
|
|
fullWidth
|
|
intent="text"
|
|
theme="base"
|
|
onClick={() => handleToggle(false)}
|
|
>
|
|
{intl.formatMessage({ id: "Cancel" })}
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</Modal>
|
|
</>
|
|
)
|
|
}
|