feat: list credit card according to design
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
.container {
|
||||
display: grid;
|
||||
gap: var(--Spacing-x3);
|
||||
gap: var(--Spacing-x2);
|
||||
max-width: 510px;
|
||||
}
|
||||
|
||||
@@ -9,14 +9,24 @@
|
||||
gap: var(--Spacing-x1);
|
||||
}
|
||||
|
||||
.card {
|
||||
margin-top: 2rem;
|
||||
.cardContainer {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
grid-template-rows: repeat(3, auto);
|
||||
gap: 0.5rem;
|
||||
gap: var(--Spacing-x1);
|
||||
}
|
||||
|
||||
.subTitle {
|
||||
grid-column: span 2;
|
||||
.card {
|
||||
display: grid;
|
||||
align-items: center;
|
||||
column-gap: var(--Spacing-x1);
|
||||
grid-template-columns: auto auto auto 1fr;
|
||||
justify-items: flex-end;
|
||||
padding: var(--Spacing-x1) var(--Spacing-x-one-and-half,);
|
||||
border-radius: var(--Corner-radius-Small);
|
||||
background-color: var(--Base-Background-Primary-Normal);
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
.container {
|
||||
gap: var(--Spacing-x2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { serverClient } from "@/lib/trpc/server"
|
||||
|
||||
import { PlusCircleIcon } from "@/components/Icons"
|
||||
import Link from "@/components/TempDesignSystem/Link"
|
||||
import { CreditCard, Delete } from "@/components/Icons"
|
||||
import AddCreditCardButton from "@/components/Profile/AddCreditCardButton"
|
||||
import Button from "@/components/TempDesignSystem/Button"
|
||||
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
||||
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
|
||||
import { getIntl } from "@/i18n"
|
||||
|
||||
@@ -24,27 +26,38 @@ export default async function CreditCardSlot() {
|
||||
})}
|
||||
</Body>
|
||||
</article>
|
||||
{creditCards &&
|
||||
creditCards.length > 0 &&
|
||||
creditCards.map((card, idx) => (
|
||||
<div className={styles.card} key={idx}>
|
||||
<Subtitle className={styles.subTitle}>
|
||||
Name: {card.attribute.cardName}
|
||||
</Subtitle>
|
||||
<span> Type: {card.attribute.cardType} </span>
|
||||
<span> Alias: {card.attribute.alias}</span>
|
||||
<span> Number: {card.attribute.truncatedNumber}</span>
|
||||
<span>
|
||||
Expiration Date: {card.attribute.expirationDate.split("T")[0]}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
<Link href="#" variant="icon">
|
||||
<PlusCircleIcon color="burgundy" />
|
||||
<Body color="burgundy" textTransform="underlined">
|
||||
{formatMessage({ id: "Add new card" })}
|
||||
</Body>
|
||||
</Link>
|
||||
{creditCards?.length ? (
|
||||
<div className={styles.cardContainer}>
|
||||
{creditCards.map((card, idx) => (
|
||||
<CreditCardRow
|
||||
key={idx}
|
||||
cardType={card.attribute.cardType}
|
||||
truncatedNumber={card.attribute.truncatedNumber}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
) : null}
|
||||
<AddCreditCardButton />
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
function CreditCardRow({
|
||||
truncatedNumber,
|
||||
cardType,
|
||||
}: {
|
||||
truncatedNumber: string
|
||||
cardType: string
|
||||
}) {
|
||||
const maskedCardNumber = `**** ${truncatedNumber.slice(12, 16)}`
|
||||
return (
|
||||
<div className={styles.card}>
|
||||
<CreditCard color="black" />
|
||||
<Body textTransform="bold">{cardType}</Body>
|
||||
<Caption color="textMediumContrast">{maskedCardNumber}</Caption>
|
||||
<Button variant="icon" theme="base" intent="text">
|
||||
<Delete color="burgundy" />
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
.info {
|
||||
display: grid;
|
||||
gap: var(--Spacing-x-one-and-half) var(--Spacing-x7);
|
||||
grid-template-columns: repeat(3, auto);
|
||||
width: 100%;
|
||||
justify-items: flex-start;
|
||||
}
|
||||
|
||||
@@ -16,4 +16,15 @@
|
||||
display: grid;
|
||||
gap: var(--Spacing-x1);
|
||||
grid-template-columns: auto auto 1fr;
|
||||
justify-items: flex-end;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
.info {
|
||||
grid-template-columns: repeat(3, auto);
|
||||
}
|
||||
.item {
|
||||
justify-items: flex-start;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,5 +7,11 @@
|
||||
border-radius: var(--Corner-radius-xLarge);
|
||||
display: grid;
|
||||
gap: var(--Spacing-x4);
|
||||
padding: var(--Spacing-x3) var(--Spacing-x3) var(--Spacing-x4);
|
||||
padding: var(--Spacing-x2) var(--Spacing-x2) var(--Spacing-x4);
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
.profile-layout {
|
||||
padding: var(--Spacing-x3) var(--Spacing-x3) var(--Spacing-x4);
|
||||
}
|
||||
}
|
||||
|
||||
40
components/Icons/CreditCard.tsx
Normal file
40
components/Icons/CreditCard.tsx
Normal file
@@ -0,0 +1,40 @@
|
||||
import * as variants from "./variants"
|
||||
|
||||
import type { IconProps } from "@/types/components/icon"
|
||||
|
||||
export default function CreditCardIcon({
|
||||
className,
|
||||
color,
|
||||
...props
|
||||
}: IconProps) {
|
||||
const classNames = variants.iconVariants({ className, color })
|
||||
return (
|
||||
<svg
|
||||
className={classNames}
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<mask
|
||||
id="mask0_7561_16317"
|
||||
style={{ maskType: "alpha" }}
|
||||
maskUnits="userSpaceOnUse"
|
||||
x="0"
|
||||
y="0"
|
||||
width="24"
|
||||
height="24"
|
||||
>
|
||||
<rect width="24" height="24" fill="#D9D9D9" />
|
||||
</mask>
|
||||
<svg mask="url(#mask0_7561_16317)">
|
||||
<path
|
||||
d="M21.75 6.1001V17.9001C21.75 18.4157 21.5664 18.8571 21.1992 19.2243C20.832 19.5915 20.3906 19.7751 19.875 19.7751H4.125C3.60937 19.7751 3.16796 19.5915 2.80077 19.2243C2.43359 18.8571 2.25 18.4157 2.25 17.9001V6.1001C2.25 5.58446 2.43359 5.14306 2.80077 4.77587C3.16796 4.40869 3.60937 4.2251 4.125 4.2251H19.875C20.3906 4.2251 20.832 4.40869 21.1992 4.77587C21.5664 5.14306 21.75 5.58446 21.75 6.1001ZM4.125 8.1251H19.875V6.1001H4.125V8.1251ZM4.125 11.9251V17.9001H19.875V11.9251H4.125Z"
|
||||
fill="#26201E"
|
||||
/>
|
||||
</svg>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
36
components/Icons/Delete.tsx
Normal file
36
components/Icons/Delete.tsx
Normal file
@@ -0,0 +1,36 @@
|
||||
import { iconVariants } from "./variants"
|
||||
|
||||
import type { IconProps } from "@/types/components/icon"
|
||||
|
||||
export default function DeleteIcon({ className, color, ...props }: IconProps) {
|
||||
const classNames = iconVariants({ className, color })
|
||||
return (
|
||||
<svg
|
||||
className={classNames}
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<mask
|
||||
id="mask0_7561_16314"
|
||||
style={{ maskType: "alpha" }}
|
||||
maskUnits="userSpaceOnUse"
|
||||
x="0"
|
||||
y="0"
|
||||
width="24"
|
||||
height="24"
|
||||
>
|
||||
<rect width="24" height="24" fill="#D9D9D9" />
|
||||
</mask>
|
||||
<g mask="url(#mask0_7561_16314)">
|
||||
<path
|
||||
d="M7.07656 20.75C6.5599 20.75 6.11823 20.5667 5.75156 20.2C5.3849 19.8333 5.20156 19.3917 5.20156 18.875V6.075H5.16406C4.90573 6.075 4.6849 5.98333 4.50156 5.8C4.31823 5.61667 4.22656 5.39583 4.22656 5.1375C4.22656 4.87917 4.31823 4.65833 4.50156 4.475C4.6849 4.29167 4.90573 4.2 5.16406 4.2H9.05156V4.1875C9.05156 3.92917 9.14323 3.70833 9.32656 3.525C9.5099 3.34167 9.73073 3.25 9.98906 3.25H14.0391C14.2974 3.25 14.5182 3.34167 14.7016 3.525C14.8849 3.70833 14.9766 3.92917 14.9766 4.1875V4.2H18.8641C19.1224 4.2 19.3432 4.29167 19.5266 4.475C19.7099 4.65833 19.8016 4.87917 19.8016 5.1375C19.8016 5.39583 19.7099 5.61667 19.5266 5.8C19.3432 5.98333 19.1224 6.075 18.8641 6.075H18.8266V18.875C18.8266 19.3917 18.6432 19.8333 18.2766 20.2C17.9099 20.5667 17.4682 20.75 16.9516 20.75H7.07656ZM16.9516 6.075H7.07656V18.875H16.9516V6.075ZM10.0641 16.9125C10.3224 16.9125 10.5432 16.8208 10.7266 16.6375C10.9099 16.4542 11.0016 16.2333 11.0016 15.975V8.975C11.0016 8.71667 10.9099 8.49583 10.7266 8.3125C10.5432 8.12917 10.3224 8.0375 10.0641 8.0375C9.80573 8.0375 9.5849 8.12917 9.40156 8.3125C9.21823 8.49583 9.12656 8.71667 9.12656 8.975V15.975C9.12656 16.2333 9.21823 16.4542 9.40156 16.6375C9.5849 16.8208 9.80573 16.9125 10.0641 16.9125ZM13.9641 16.9125C14.2224 16.9125 14.4432 16.8208 14.6266 16.6375C14.8099 16.4542 14.9016 16.2333 14.9016 15.975V8.975C14.9016 8.71667 14.8099 8.49583 14.6266 8.3125C14.4432 8.12917 14.2224 8.0375 13.9641 8.0375C13.7057 8.0375 13.4849 8.12917 13.3016 8.3125C13.1182 8.49583 13.0266 8.71667 13.0266 8.975V15.975C13.0266 16.2333 13.1182 16.4542 13.3016 16.6375C13.4849 16.8208 13.7057 16.9125 13.9641 16.9125Z"
|
||||
fill="#4D001B"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
@@ -13,6 +13,8 @@ export { default as ChevronRightIcon } from "./ChevronRight"
|
||||
export { default as CloseIcon } from "./Close"
|
||||
export { default as CoffeeIcon } from "./Coffee"
|
||||
export { default as ConciergeIcon } from "./Concierge"
|
||||
export { default as CreditCard } from "./CreditCard"
|
||||
export { default as Delete } from "./Delete"
|
||||
export { default as DoorOpenIcon } from "./DoorOpen"
|
||||
export { default as ElectricBikeIcon } from "./ElectricBike"
|
||||
export { default as EmailIcon } from "./Email"
|
||||
|
||||
31
components/Profile/AddCreditCardButton/index.tsx
Normal file
31
components/Profile/AddCreditCardButton/index.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
"use client"
|
||||
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { trpc } from "@/lib/trpc/client"
|
||||
|
||||
import { PlusCircleIcon } from "@/components/Icons"
|
||||
import Button from "@/components/TempDesignSystem/Button"
|
||||
|
||||
export default function AddCreditCardButton() {
|
||||
const { formatMessage } = useIntl()
|
||||
|
||||
async function handleAddCreditCard() {
|
||||
// TODO: initiate add credit card flow and redirect user to planet:
|
||||
// const { url } = trpc.user.creditCard.add.useMutation()
|
||||
// router.redirect(url)
|
||||
console.log("Credit card added!")
|
||||
}
|
||||
return (
|
||||
<Button
|
||||
variant="icon"
|
||||
theme="base"
|
||||
intent="text"
|
||||
onClick={handleAddCreditCard}
|
||||
wrapping
|
||||
>
|
||||
<PlusCircleIcon color="burgundy" />
|
||||
{formatMessage({ id: "Add new card" })}
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
@@ -13,11 +13,16 @@
|
||||
/* TODO: Waiting for variables for buttons from Design team */
|
||||
font-family: var(--typography-Body-Bold-fontFamily);
|
||||
font-size: var(--typography-Body-Bold-fontSize);
|
||||
font-weight: var(--typography-Body-Bold-fontWeight);
|
||||
font-weight: 500;
|
||||
line-height: 24px;
|
||||
letter-spacing: 0.6%;
|
||||
}
|
||||
|
||||
.wrapping {
|
||||
padding-left: 0 !important;
|
||||
padding-right: 0 !important;
|
||||
}
|
||||
|
||||
/* INTENT */
|
||||
.primary,
|
||||
a.primary {
|
||||
@@ -69,7 +74,7 @@ a.default {
|
||||
}
|
||||
|
||||
.medium {
|
||||
gap: var(--Spacing-x-quarter);
|
||||
gap: var(--Spacing-x-half);
|
||||
height: 48px;
|
||||
padding: var(--Spacing-x-one-and-half) var(--Spacing-x2);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ export default function Button({
|
||||
intent,
|
||||
size,
|
||||
variant,
|
||||
wrapping,
|
||||
...props
|
||||
}: ButtonProps) {
|
||||
const Comp = asChild ? Slot : "button"
|
||||
@@ -22,6 +23,7 @@ export default function Button({
|
||||
intent,
|
||||
size,
|
||||
theme,
|
||||
wrapping,
|
||||
variant,
|
||||
})
|
||||
return <Comp className={classNames} disabled={disabled} {...props} />
|
||||
|
||||
@@ -30,6 +30,9 @@ export const buttonVariants = cva(styles.btn, {
|
||||
default: styles.default,
|
||||
icon: styles.icon,
|
||||
},
|
||||
wrapping: {
|
||||
true: styles.wrapping,
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
intent: "primary",
|
||||
|
||||
@@ -26,7 +26,7 @@ const config = {
|
||||
defaultVariants: {
|
||||
color: "burgundy",
|
||||
textAlign: "left",
|
||||
textTransform: "capitalize",
|
||||
textTransform: "regular",
|
||||
type: "one",
|
||||
},
|
||||
} as const
|
||||
|
||||
Reference in New Issue
Block a user