187 lines
5.6 KiB
JavaScript
187 lines
5.6 KiB
JavaScript
import React from 'react';
|
|
import { CreditCard, Trash2, Star, Banknote, Building2, FileCheck } from 'lucide-react';
|
|
import { Button } from './ui/button';
|
|
|
|
/**
|
|
* Card brand icon mapping
|
|
*/
|
|
const getBrandIcon = (brand) => {
|
|
const brandLower = brand?.toLowerCase();
|
|
// Return text abbreviation for known brands
|
|
switch (brandLower) {
|
|
case 'visa':
|
|
return 'VISA';
|
|
case 'mastercard':
|
|
return 'MC';
|
|
case 'amex':
|
|
case 'american_express':
|
|
return 'AMEX';
|
|
case 'discover':
|
|
return 'DISC';
|
|
default:
|
|
return null;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Get icon for payment method type
|
|
*/
|
|
const getPaymentTypeIcon = (paymentType) => {
|
|
switch (paymentType) {
|
|
case 'cash':
|
|
return Banknote;
|
|
case 'bank_transfer':
|
|
return Building2;
|
|
case 'check':
|
|
return FileCheck;
|
|
default:
|
|
return CreditCard;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Format payment type for display
|
|
*/
|
|
const formatPaymentType = (paymentType) => {
|
|
switch (paymentType) {
|
|
case 'cash':
|
|
return 'Cash';
|
|
case 'bank_transfer':
|
|
return 'Bank Transfer';
|
|
case 'check':
|
|
return 'Check';
|
|
case 'card':
|
|
return 'Card';
|
|
default:
|
|
return paymentType;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* PaymentMethodCard - Displays a single payment method
|
|
*/
|
|
const PaymentMethodCard = ({
|
|
method,
|
|
onSetDefault,
|
|
onDelete,
|
|
loading = false,
|
|
showActions = true,
|
|
}) => {
|
|
const PaymentIcon = getPaymentTypeIcon(method.payment_type);
|
|
const brandAbbr = method.card_brand ? getBrandIcon(method.card_brand) : null;
|
|
const isExpired = method.card_exp_year && method.card_exp_month &&
|
|
new Date(method.card_exp_year, method.card_exp_month) < new Date();
|
|
|
|
return (
|
|
<div
|
|
className={`flex items-center justify-between p-4 border rounded-xl ${
|
|
method.is_default
|
|
? 'border-brand-purple bg-[var(--lavender-500)]'
|
|
: 'border-[var(--neutral-800)] bg-white'
|
|
} ${isExpired ? 'opacity-70' : ''}`}
|
|
>
|
|
<div className="flex items-center gap-4">
|
|
{/* Payment Method Icon */}
|
|
<div className={`p-3 rounded-full ${
|
|
method.is_default
|
|
? 'bg-brand-purple text-white'
|
|
: 'bg-[var(--lavender-300)] text-brand-purple'
|
|
}`}>
|
|
<PaymentIcon className="h-5 w-5" />
|
|
</div>
|
|
|
|
{/* Payment Method Details */}
|
|
<div>
|
|
{method.payment_type === 'card' ? (
|
|
<>
|
|
<div className="flex items-center gap-2">
|
|
{brandAbbr && (
|
|
<span className="text-xs font-bold text-[var(--purple-ink)] bg-[var(--lavender-300)] px-2 py-0.5 rounded">
|
|
{brandAbbr}
|
|
</span>
|
|
)}
|
|
<span
|
|
className="font-medium text-[var(--purple-ink)]"
|
|
style={{ fontFamily: "'Inter', sans-serif" }}
|
|
>
|
|
{method.card_brand ? method.card_brand.charAt(0).toUpperCase() + method.card_brand.slice(1) : 'Card'} •••• {method.card_last4 || '****'}
|
|
</span>
|
|
{method.is_default && (
|
|
<span className="flex items-center gap-1 text-xs text-brand-purple font-medium">
|
|
<Star className="h-3 w-3 fill-current" />
|
|
Default
|
|
</span>
|
|
)}
|
|
</div>
|
|
<p
|
|
className={`text-sm ${isExpired ? 'text-red-500' : 'text-brand-purple'}`}
|
|
style={{ fontFamily: "'Nunito Sans', sans-serif" }}
|
|
>
|
|
{isExpired ? 'Expired' : 'Expires'} {method.card_exp_month?.toString().padStart(2, '0')}/{method.card_exp_year?.toString().slice(-2)}
|
|
{method.card_funding && (
|
|
<span className="ml-2 text-xs capitalize">({method.card_funding})</span>
|
|
)}
|
|
</p>
|
|
</>
|
|
) : (
|
|
<>
|
|
<div className="flex items-center gap-2">
|
|
<span
|
|
className="font-medium text-[var(--purple-ink)]"
|
|
style={{ fontFamily: "'Inter', sans-serif" }}
|
|
>
|
|
{formatPaymentType(method.payment_type)}
|
|
</span>
|
|
{method.is_default && (
|
|
<span className="flex items-center gap-1 text-xs text-brand-purple font-medium">
|
|
<Star className="h-3 w-3 fill-current" />
|
|
Default
|
|
</span>
|
|
)}
|
|
</div>
|
|
{method.manual_notes && (
|
|
<p
|
|
className="text-sm text-brand-purple"
|
|
style={{ fontFamily: "'Nunito Sans', sans-serif" }}
|
|
>
|
|
{method.manual_notes}
|
|
</p>
|
|
)}
|
|
</>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Actions */}
|
|
{showActions && (
|
|
<div className="flex items-center gap-2">
|
|
{!method.is_default && (
|
|
<Button
|
|
type="button"
|
|
variant="outline"
|
|
size="sm"
|
|
onClick={() => onSetDefault?.(method.id)}
|
|
disabled={loading}
|
|
className="border border-brand-purple text-brand-purple hover:bg-[var(--lavender-300)] rounded-lg text-xs px-3"
|
|
>
|
|
Set Default
|
|
</Button>
|
|
)}
|
|
<Button
|
|
type="button"
|
|
variant="outline"
|
|
size="sm"
|
|
onClick={() => onDelete?.(method.id)}
|
|
disabled={loading}
|
|
className="border border-red-500 text-red-500 hover:bg-red-50 rounded-lg p-2"
|
|
>
|
|
<Trash2 className="h-4 w-4" />
|
|
</Button>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default PaymentMethodCard;
|