133 lines
4.5 KiB
TypeScript
133 lines
4.5 KiB
TypeScript
"use client";
|
|
|
|
import Image from "next/image";
|
|
|
|
import type { PayChannel } from "@/data/schemas/payment";
|
|
import type { PaymentMethodConfig } from "@/lib/payment/payment_method";
|
|
|
|
const PAYMENT_METHODS: readonly {
|
|
channel: PayChannel;
|
|
title: string;
|
|
logoSrc?: string;
|
|
}[] = [
|
|
{
|
|
channel: "stripe",
|
|
title: "Stripe",
|
|
},
|
|
{
|
|
channel: "ezpay",
|
|
title: "GCash",
|
|
logoSrc: "/images/subscription/gcash-logo.svg",
|
|
},
|
|
];
|
|
|
|
export interface PaymentMethodSelectorProps {
|
|
config: PaymentMethodConfig;
|
|
value: PayChannel;
|
|
density?: "default" | "compact";
|
|
disabled?: boolean;
|
|
caption?: string;
|
|
className?: string;
|
|
analyticsKey: string;
|
|
onChange: (channel: PayChannel) => void;
|
|
}
|
|
|
|
export function PaymentMethodSelector({
|
|
config,
|
|
value,
|
|
density = "default",
|
|
disabled = false,
|
|
caption = "Stripe by default",
|
|
className,
|
|
analyticsKey,
|
|
onChange,
|
|
}: PaymentMethodSelectorProps) {
|
|
if (!config.showPaymentMethodSelector) return null;
|
|
const isCompact = density === "compact";
|
|
const ezpayDisplayName = config.ezpayDisplayName ?? "GCash";
|
|
const configuredPaymentMethods = PAYMENT_METHODS.map((method) =>
|
|
method.channel === "ezpay"
|
|
? {
|
|
...method,
|
|
title: ezpayDisplayName,
|
|
logoSrc:
|
|
ezpayDisplayName === "GCash" ? method.logoSrc : undefined,
|
|
}
|
|
: method,
|
|
);
|
|
|
|
const paymentMethods =
|
|
config.initialPayChannel === "ezpay"
|
|
? [...configuredPaymentMethods].sort((a, b) =>
|
|
a.channel === "ezpay" ? -1 : b.channel === "ezpay" ? 1 : 0,
|
|
)
|
|
: configuredPaymentMethods;
|
|
|
|
return (
|
|
<section
|
|
className={`flex flex-col ${isCompact ? "gap-2" : "gap-[clamp(8px,1.852vw,10px)]"} ${className ?? ""}`.trim()}
|
|
data-density={density}
|
|
aria-labelledby="payment-method-title"
|
|
>
|
|
<div
|
|
className={`flex items-baseline justify-between gap-sm ${isCompact ? "px-0.5" : "px-xs"}`}
|
|
>
|
|
<h2
|
|
id="payment-method-title"
|
|
className={`m-0 font-bold leading-[1.2] text-auth-text-primary ${isCompact ? "text-base" : "text-(length:--responsive-card-title,var(--font-size-lg))"}`}
|
|
style={{ fontFamily: "var(--font-athelas), var(--font-system)" }}
|
|
>
|
|
Payment Method
|
|
</h2>
|
|
{!isCompact ? (
|
|
<span className="whitespace-nowrap text-(length:--responsive-caption,var(--font-size-sm)) leading-[1.2] text-text-secondary">
|
|
{caption}
|
|
</span>
|
|
) : null}
|
|
</div>
|
|
<div className="grid grid-cols-2 gap-sm">
|
|
{paymentMethods.map((method) => {
|
|
const selected = method.channel === value;
|
|
const classes = [
|
|
`flex cursor-pointer items-center justify-center text-center font-[inherit] text-[#3c3b3b] transition-[border-color,box-shadow,transform] duration-150 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent active:enabled:scale-98 disabled:cursor-not-allowed disabled:opacity-72 ${isCompact ? "min-h-13 rounded-[18px] px-3 py-2" : "min-h-[clamp(54px,11.481vw,62px)] rounded-(--responsive-card-radius-sm,18px) px-[clamp(12px,2.593vw,14px)] py-[clamp(9px,1.852vw,10px)]"}`,
|
|
selected
|
|
? "border-2 border-[#f657a0] bg-[#fff4f9] shadow-[0_8px_18px_rgba(217,47,127,0.24),0_0_0_3px_rgba(217,47,127,0.18)] -translate-y-0.5"
|
|
: "border border-[rgba(246,87,160,0.18)] bg-white shadow-[0_5px_7px_0_rgba(247,89,168,0.08)]",
|
|
]
|
|
.filter(Boolean)
|
|
.join(" ");
|
|
|
|
return (
|
|
<button
|
|
key={method.channel}
|
|
type="button"
|
|
data-analytics-key={analyticsKey}
|
|
data-analytics-label="Select payment method"
|
|
className={classes}
|
|
disabled={disabled}
|
|
aria-pressed={selected}
|
|
aria-label={method.title}
|
|
onClick={() => onChange(method.channel)}
|
|
>
|
|
{method.logoSrc ? (
|
|
<Image
|
|
src={method.logoSrc}
|
|
alt=""
|
|
aria-hidden="true"
|
|
width={122}
|
|
height={29}
|
|
className={`block object-contain ${isCompact ? "max-h-8 w-[min(100%,92px)]" : "max-h-8.5 w-[min(100%,92px)]"}`}
|
|
/>
|
|
) : (
|
|
<span className="text-(length:--responsive-body,16px) font-bold leading-[1.1] text-[#1e1e1e]">
|
|
{method.title}
|
|
</span>
|
|
)}
|
|
</button>
|
|
);
|
|
})}
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|