- Add green trust badge with checkmark icon above 'Complete Order' button - Add translations for all locales (EN, SR, DE, FR) - Badge includes: '30-Day Money-Back Guarantee' text - Styled with green background and border to match trust/conversion theme
126 lines
4.0 KiB
TypeScript
126 lines
4.0 KiB
TypeScript
"use client";
|
|
|
|
import { cn } from "@/lib/utils";
|
|
import type { PaymentMethod } from "@/lib/saleor/payments/types";
|
|
import { Banknote, CreditCard, Building2, LucideIcon } from "lucide-react";
|
|
import { useTranslations } from "next-intl";
|
|
|
|
// Icon mapping for payment methods
|
|
const iconMap: Record<string, LucideIcon> = {
|
|
Banknote,
|
|
CreditCard,
|
|
Building2,
|
|
};
|
|
|
|
interface PaymentMethodCardProps {
|
|
method: PaymentMethod;
|
|
isSelected: boolean;
|
|
onSelect: () => void;
|
|
disabled?: boolean;
|
|
locale: string;
|
|
}
|
|
|
|
export function PaymentMethodCard({
|
|
method,
|
|
isSelected,
|
|
onSelect,
|
|
disabled = false,
|
|
locale,
|
|
}: PaymentMethodCardProps) {
|
|
const t = useTranslations("Payment");
|
|
const Icon = method.icon ? iconMap[method.icon] : Banknote;
|
|
|
|
// Get translated name and description based on method ID
|
|
const translatedName = t(`${method.id}.name`);
|
|
const translatedDescription = t(`${method.id}.description`);
|
|
|
|
return (
|
|
<label
|
|
className={cn(
|
|
"relative flex cursor-pointer items-start gap-4 rounded-xl border-2 p-5 transition-all duration-300",
|
|
"hover:scale-[1.02] hover:shadow-lg",
|
|
isSelected
|
|
? "border-[#059669] bg-white shadow-xl shadow-[#047857]/30"
|
|
: "border-gray-200 bg-white hover:border-[#3B82F6]",
|
|
(disabled || !method.available) && "cursor-not-allowed opacity-50"
|
|
)}
|
|
>
|
|
<input
|
|
type="radio"
|
|
name="payment-method"
|
|
value={method.id}
|
|
checked={isSelected}
|
|
onChange={onSelect}
|
|
disabled={disabled || !method.available}
|
|
className="sr-only"
|
|
/>
|
|
|
|
{/* Glowing green checkmark for selected */}
|
|
{isSelected && (
|
|
<div className="absolute -right-2 -top-2 z-10">
|
|
<div className="relative">
|
|
{/* Glow effect */}
|
|
<div className="absolute inset-0 rounded-full bg-[#059669] blur-md opacity-70" />
|
|
{/* Green circle with checkmark */}
|
|
<div className="relative flex h-8 w-8 items-center justify-center rounded-full bg-gradient-to-br from-[#059669] to-[#047857] shadow-lg">
|
|
<svg
|
|
className="h-5 w-5 text-white"
|
|
fill="none"
|
|
viewBox="0 0 24 24"
|
|
stroke="currentColor"
|
|
strokeWidth={3}
|
|
>
|
|
<path strokeLinecap="round" strokeLinejoin="round" d="M5 13l4 4L19 7" />
|
|
</svg>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
<div className={cn(
|
|
"flex h-12 w-12 shrink-0 items-center justify-center rounded-xl transition-all duration-300",
|
|
isSelected
|
|
? "bg-gradient-to-br from-[#059669] to-[#047857] shadow-lg shadow-[#047857]/40"
|
|
: "bg-gradient-to-br from-blue-50 to-blue-100"
|
|
)}>
|
|
<Icon className={cn(
|
|
"h-6 w-6 transition-colors",
|
|
isSelected ? "text-white" : "text-[#3B82F6]"
|
|
)} />
|
|
</div>
|
|
|
|
<div className="flex-1 pr-8">
|
|
<div className="flex items-center justify-between">
|
|
<span className={cn(
|
|
"text-lg font-bold transition-colors",
|
|
isSelected ? "text-[#047857]" : "text-gray-900"
|
|
)}>
|
|
{translatedName}
|
|
</span>
|
|
{method.fee > 0 && (
|
|
<span className="text-sm font-semibold text-amber-600 bg-amber-100 px-2 py-1 rounded-full">
|
|
+{new Intl.NumberFormat(locale === 'sr' ? 'sr-RS' : 'en-US', {
|
|
style: 'currency',
|
|
currency: 'RSD',
|
|
}).format(method.fee)}
|
|
</span>
|
|
)}
|
|
</div>
|
|
|
|
<p className={cn(
|
|
"mt-1 text-sm font-medium transition-colors",
|
|
isSelected ? "text-gray-700" : "text-gray-600"
|
|
)}>
|
|
{translatedDescription}
|
|
</p>
|
|
|
|
{!method.available && (
|
|
<span className="mt-2 inline-block text-xs font-medium text-gray-500 bg-gray-100 px-2 py-1 rounded">
|
|
{t(`${method.id}.comingSoon`)}
|
|
</span>
|
|
)}
|
|
</div>
|
|
</label>
|
|
);
|
|
}
|