Files
manoon-headless/src/components/payment/PaymentMethodCard.tsx
Unchained 10b18c6010 feat: add 30-day money back guarantee trust badge above complete order button
- 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
2026-03-29 14:27:05 +02:00

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>
);
}