Merged in feat/sw-1314-transfer-sas-points (pull request #1508)
SW-1314 Transfer SAS points Approved-by: Linus Flood
This commit is contained in:
@@ -0,0 +1,165 @@
|
||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||
|
||||
import { getProfileSafely } from "@/lib/trpc/memoizedRequests"
|
||||
|
||||
import ArrowFromIcon from "@/components/Icons/ArrowFrom"
|
||||
import ArrowToIcon from "@/components/Icons/ArrowTo"
|
||||
import Image from "@/components/Image"
|
||||
import SkeletonShimmer from "@/components/SkeletonShimmer"
|
||||
import { getIntl } from "@/i18n"
|
||||
|
||||
import { TransferPointsFormClient } from "./TransferPointsFormClient"
|
||||
|
||||
import styles from "./transferPoints.module.css"
|
||||
|
||||
import type { Lang } from "@/constants/languages"
|
||||
|
||||
const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))
|
||||
export async function TransferPointsForm({ lang }: { lang: Lang }) {
|
||||
const profile = await getProfileSafely()
|
||||
const scandicPoints = profile?.membership?.currentPoints ?? 0
|
||||
|
||||
// TODO get from api
|
||||
await wait(1_000)
|
||||
const sasPoints = 250_000
|
||||
const exchangeRate = 2
|
||||
|
||||
return (
|
||||
<TransferPointsFormContent
|
||||
sasPoints={sasPoints}
|
||||
scandicPoints={scandicPoints}
|
||||
exchangeRate={exchangeRate}
|
||||
lang={lang}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export async function TransferPointsFormSkeleton({ lang }: { lang: Lang }) {
|
||||
return (
|
||||
<TransferPointsFormContent
|
||||
sasPoints={null}
|
||||
scandicPoints={null}
|
||||
exchangeRate={null}
|
||||
lang={lang}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
async function TransferPointsFormContent({
|
||||
sasPoints,
|
||||
scandicPoints,
|
||||
exchangeRate,
|
||||
lang,
|
||||
}: {
|
||||
sasPoints: number | null
|
||||
scandicPoints: number | null
|
||||
exchangeRate: number | null
|
||||
lang: Lang
|
||||
}) {
|
||||
const intl = await getIntl()
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<section className={styles.card}>
|
||||
<div className={styles.highFive}>
|
||||
<Image
|
||||
src="/_static/img/scandic-high-five.svg"
|
||||
alt=""
|
||||
width="111"
|
||||
height="139"
|
||||
sizes="100vw"
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.transferContainer}>
|
||||
<div className={styles.transferFrom}>
|
||||
<div>
|
||||
<div className={styles.labelWithIcon}>
|
||||
<Typography variant="Tag/sm">
|
||||
<p>{intl.formatMessage({ id: "Transfer from" })}</p>
|
||||
</Typography>
|
||||
<ArrowFromIcon />
|
||||
</div>
|
||||
<Typography variant="Title/Subtitle/md">
|
||||
<p>{intl.formatMessage({ id: "SAS EuroBonus" })}</p>
|
||||
</Typography>
|
||||
</div>
|
||||
<div>
|
||||
<Typography variant="Tag/sm">
|
||||
<p className={styles.balanceLabel}>
|
||||
{intl.formatMessage({ id: "Balance" })}
|
||||
</p>
|
||||
</Typography>
|
||||
{sasPoints === null ? (
|
||||
<SkeletonShimmer width="10ch" height="20px" />
|
||||
) : (
|
||||
<Typography variant="Body/Paragraph/mdRegular">
|
||||
<p>
|
||||
{intl.formatMessage(
|
||||
{ id: "{points, number} p" },
|
||||
{ points: sasPoints }
|
||||
)}
|
||||
</p>
|
||||
</Typography>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.noPointsWarning}>
|
||||
{sasPoints === 0 && (
|
||||
<Typography variant="Body/Paragraph/mdRegular">
|
||||
<p>
|
||||
{intl.formatMessage({
|
||||
id: "You have no points to transfer.",
|
||||
})}
|
||||
</p>
|
||||
</Typography>
|
||||
)}
|
||||
</div>
|
||||
<div className={styles.transferTo}>
|
||||
<div>
|
||||
<div className={styles.labelWithIcon}>
|
||||
<ArrowToIcon />
|
||||
<Typography variant="Tag/sm">
|
||||
<p>{intl.formatMessage({ id: "Transfer to" })}</p>
|
||||
</Typography>
|
||||
</div>
|
||||
<Typography variant="Title/Subtitle/md">
|
||||
<p>{intl.formatMessage({ id: "Scandic Friends" })}</p>
|
||||
</Typography>
|
||||
</div>
|
||||
<div>
|
||||
<Typography variant="Tag/sm">
|
||||
<p className={styles.balanceLabel}>
|
||||
{intl.formatMessage({ id: "Balance" })}
|
||||
</p>
|
||||
</Typography>
|
||||
{scandicPoints === null ? (
|
||||
<SkeletonShimmer width="10ch" height="20px" />
|
||||
) : (
|
||||
<Typography variant="Body/Paragraph/mdRegular">
|
||||
<p>
|
||||
{intl.formatMessage(
|
||||
{ id: "{points, number} p" },
|
||||
{ points: scandicPoints }
|
||||
)}
|
||||
</p>
|
||||
</Typography>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<TransferPointsFormClient
|
||||
sasPoints={sasPoints}
|
||||
exchangeRate={exchangeRate}
|
||||
lang={lang}
|
||||
/>
|
||||
</section>
|
||||
<Typography variant="Body/Supporting text (caption)/smRegular">
|
||||
<p style={{ color: "var(--Text-Tertiary)" }}>
|
||||
{intl.formatMessage({
|
||||
id: "Transferred points will not be level qualifying",
|
||||
})}
|
||||
</p>
|
||||
</Typography>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,247 @@
|
||||
"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 { Typography } from "@scandic-hotels/design-system/Typography"
|
||||
|
||||
import { SAS_TRANSFER_POINT_KEY } from "@/app/[lang]/(partner)/(sas)/(protected)/sas-x-scandic/sasUtils"
|
||||
import SwipeIcon from "@/components/Icons/Swipe"
|
||||
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} />
|
||||
<SwipeIcon color="white" />
|
||||
</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>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import { Suspense } from "react"
|
||||
|
||||
import { env } from "@/env/server"
|
||||
|
||||
import SectionContainer from "@/components/Section/Container"
|
||||
import SectionHeader from "@/components/Section/Header"
|
||||
import SectionLink from "@/components/Section/Link"
|
||||
import { getLang } from "@/i18n/serverContext"
|
||||
|
||||
import {
|
||||
TransferPointsForm,
|
||||
TransferPointsFormSkeleton,
|
||||
} from "./TransferPointsForm"
|
||||
|
||||
type Props = {
|
||||
title?: string
|
||||
link?: { href: string; text: string }
|
||||
subtitle?: string
|
||||
}
|
||||
|
||||
export default async function SASTransferPoints({
|
||||
title,
|
||||
subtitle,
|
||||
link,
|
||||
}: Props) {
|
||||
if (!env.SAS_ENABLED) {
|
||||
return null
|
||||
}
|
||||
|
||||
const lang = getLang()
|
||||
|
||||
return (
|
||||
<SectionContainer>
|
||||
<SectionHeader link={link} preamble={subtitle} title={title} />
|
||||
<SectionLink link={link} variant="mobile" />
|
||||
<Suspense fallback={<TransferPointsFormSkeleton lang={lang} />}>
|
||||
<TransferPointsForm lang={lang} />
|
||||
</Suspense>
|
||||
</SectionContainer>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,297 @@
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: var(--Spacing-x3);
|
||||
}
|
||||
|
||||
.card {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--Spacing-x3);
|
||||
padding: var(--Spacing-x3) var(--Spacing-x9);
|
||||
background-color: var(--Background-Secondary);
|
||||
border-radius: var(--Corner-radius-Large);
|
||||
box-shadow: 0px 0px 4px 2px rgba(0, 0, 0, 0.1);
|
||||
margin-top: var(--Spacing-x9);
|
||||
}
|
||||
|
||||
.highFive {
|
||||
height: 110px;
|
||||
width: 110px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
|
||||
& > img {
|
||||
position: absolute;
|
||||
bottom: -5%;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.labelWithIcon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--Spacing-x-half);
|
||||
color: var(--Text-Tertiary);
|
||||
margin-bottom: var(--Spacing-x-half);
|
||||
}
|
||||
|
||||
.transferContainer {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto 1fr;
|
||||
}
|
||||
|
||||
.transferFrom {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.transferTo {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
text-align: right;
|
||||
|
||||
.labelWithIcon {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
.noPointsWarning {
|
||||
align-self: end;
|
||||
color: var(--Surface-Feedback-Information-Accent);
|
||||
}
|
||||
|
||||
.balanceLabel {
|
||||
color: var(--Text-Tertiary);
|
||||
margin-top: var(--Spacing-x3);
|
||||
}
|
||||
|
||||
.formWrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--Spacing-x3);
|
||||
width: 100%;
|
||||
padding: var(--Spacing-x4);
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.slider {
|
||||
color: var(--text-color);
|
||||
width: 100%;
|
||||
padding-top: 24px;
|
||||
|
||||
.sliderTrack {
|
||||
position: relative;
|
||||
height: 30px;
|
||||
width: 100%;
|
||||
|
||||
&:before {
|
||||
height: 10px;
|
||||
width: 100%;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
background-color: var(--Border-Divider-Accent);
|
||||
border-radius: var(--Corner-radius-Small);
|
||||
opacity: 0.3;
|
||||
transition: background-color 300ms ease;
|
||||
}
|
||||
}
|
||||
|
||||
.sliderFill {
|
||||
height: 10px;
|
||||
background: linear-gradient(90deg, #8f4350 25.5%, #4d001b 100%);
|
||||
position: relative;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
border-radius: var(--Corner-radius-Small);
|
||||
}
|
||||
|
||||
.sliderOutput {
|
||||
position: absolute;
|
||||
top: -30px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%) translateY(4px);
|
||||
background-color: var(--Surface-Brand-Primary-1-OnSurface-Default);
|
||||
color: var(--Text-Brand-OnPrimary-2-Accent);
|
||||
padding: var(--Spacing-x-half) var(--Spacing-x1);
|
||||
border-radius: var(--Corner-radius-Small);
|
||||
opacity: 0;
|
||||
transition:
|
||||
opacity 0.15s,
|
||||
transform 0.15s;
|
||||
transition-delay: 0.1s;
|
||||
}
|
||||
|
||||
.sliderThumb {
|
||||
position: relative;
|
||||
top: 50%;
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
border-radius: 50%;
|
||||
background-color: var(--Surface-Brand-Primary-1-OnSurface-Default);
|
||||
box-shadow: 0px 0px 14px 6px rgba(0, 0, 0, 0.1);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
transition: background-color 300ms ease;
|
||||
|
||||
& > svg {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
transition: opacity 300ms ease;
|
||||
}
|
||||
|
||||
&[data-focus-visible] {
|
||||
outline: 2px solid var(--Surface-Feedback-Neutral);
|
||||
}
|
||||
|
||||
&[data-dragging] .sliderOutput {
|
||||
opacity: 1;
|
||||
transform: translateX(-50%) translateY(0px);
|
||||
}
|
||||
}
|
||||
|
||||
&[data-disabled] {
|
||||
.sliderTrack:before {
|
||||
background-color: var(--Border-Interactive-Disabled);
|
||||
}
|
||||
.sliderThumb {
|
||||
background-color: var(--Surface-UI-Fill-Disabled);
|
||||
box-shadow: none;
|
||||
|
||||
& > svg {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.pointsInput {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.conversionRate {
|
||||
color: var(--Text-Tertiary);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.inputsWrapper {
|
||||
display: grid;
|
||||
gap: 36px;
|
||||
width: 100%;
|
||||
grid-template-columns: 1fr auto 1fr;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.transferButton {
|
||||
max-width: 260px;
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.modalContainer {
|
||||
max-width: 512px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
gap: var(--Spacing-x3);
|
||||
padding-inline: var(--Spacing-x3);
|
||||
}
|
||||
|
||||
.expiryText {
|
||||
color: var(--Text-Tertiary);
|
||||
}
|
||||
|
||||
.divider {
|
||||
background-color: var(--Border-Divider-Subtle);
|
||||
width: calc(100% + var(--Spacing-x6) + var(--Spacing-x6));
|
||||
height: 1px;
|
||||
margin-inline: calc(var(--Spacing-x6) * -1);
|
||||
}
|
||||
|
||||
.buttonContainer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--Spacing-x3);
|
||||
}
|
||||
|
||||
.pointsOutput {
|
||||
border-radius: var(--Corner-radius-Medium);
|
||||
background-color: var(--Surface-Primary-Disabled);
|
||||
height: 100%;
|
||||
color: var(--Text-Tertiary);
|
||||
padding: var(--Spacing-x1) var(--Spacing-x2);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--Spacing-x-half);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 767px) {
|
||||
.card {
|
||||
padding: var(--Spacing-x3);
|
||||
padding-top: var(--Spacing-x6);
|
||||
}
|
||||
|
||||
.highFive {
|
||||
height: 75px;
|
||||
width: 75px;
|
||||
}
|
||||
|
||||
.transferContainer {
|
||||
grid-template-columns: 1fr;
|
||||
gap: var(--Spacing-x4);
|
||||
}
|
||||
.transferFrom {
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.transferTo {
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
|
||||
text-align: left;
|
||||
|
||||
.labelWithIcon {
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
}
|
||||
|
||||
.noPointsWarning {
|
||||
grid-row: 3;
|
||||
}
|
||||
|
||||
.balanceLabel {
|
||||
margin-top: 0;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.inputsWrapper {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
column-gap: var(--Spacing-x1);
|
||||
}
|
||||
|
||||
.conversionRate {
|
||||
grid-row: 2;
|
||||
grid-column: span 2;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.slider {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.modalContainer {
|
||||
padding-inline: var(--Spacing-x2);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user