Phase 3 & Phase 4 - Completed

This commit is contained in:
IGNY8 VPS (Salman)
2026-01-07 00:57:26 +00:00
parent 4b6a03a898
commit 909ed1cb17
25 changed files with 5549 additions and 215 deletions

View File

@@ -0,0 +1,192 @@
/**
* PaymentGatewaySelector Component
* Allows users to select between Stripe, PayPal, and Manual payment methods
*/
import React, { useEffect, useState } from 'react';
import { CreditCard, Building2, Wallet, Loader2, Check } from 'lucide-react';
import { getAvailablePaymentGateways, PaymentGateway } from '@/services/billing.api';
interface PaymentGatewayOption {
id: PaymentGateway;
name: string;
description: string;
icon: React.ReactNode;
available: boolean;
recommended?: boolean;
}
interface PaymentGatewaySelectorProps {
selectedGateway: PaymentGateway | null;
onSelectGateway: (gateway: PaymentGateway) => void;
showManual?: boolean;
className?: string;
}
export function PaymentGatewaySelector({
selectedGateway,
onSelectGateway,
showManual = true,
className = '',
}: PaymentGatewaySelectorProps) {
const [loading, setLoading] = useState(true);
const [gateways, setGateways] = useState<PaymentGatewayOption[]>([]);
useEffect(() => {
async function loadGateways() {
try {
const available = await getAvailablePaymentGateways();
const options: PaymentGatewayOption[] = [
{
id: 'stripe',
name: 'Credit/Debit Card',
description: 'Pay securely with your credit or debit card via Stripe',
icon: <CreditCard className="h-6 w-6" />,
available: available.stripe,
recommended: available.stripe,
},
{
id: 'paypal',
name: 'PayPal',
description: 'Pay with your PayPal account or PayPal Credit',
icon: (
<svg className="h-6 w-6" viewBox="0 0 24 24" fill="currentColor">
<path d="M7.076 21.337H2.47a.641.641 0 0 1-.633-.74L4.944.901C5.026.382 5.474 0 5.998 0h7.46c2.57 0 4.578.543 5.69 1.81 1.01 1.15 1.304 2.42 1.012 4.287-.023.143-.047.288-.077.437-.983 5.05-4.349 6.797-8.647 6.797H9.3L7.076 21.337z" />
</svg>
),
available: available.paypal,
},
];
if (showManual) {
options.push({
id: 'manual',
name: 'Bank Transfer',
description: 'Pay via bank transfer with manual confirmation',
icon: <Building2 className="h-6 w-6" />,
available: available.manual,
});
}
setGateways(options);
// Auto-select first available gateway if none selected
if (!selectedGateway) {
const firstAvailable = options.find((g) => g.available);
if (firstAvailable) {
onSelectGateway(firstAvailable.id);
}
}
} catch (error) {
console.error('Failed to load payment gateways:', error);
// Fallback to manual only
setGateways([
{
id: 'manual',
name: 'Bank Transfer',
description: 'Pay via bank transfer with manual confirmation',
icon: <Building2 className="h-6 w-6" />,
available: true,
},
]);
if (!selectedGateway) {
onSelectGateway('manual');
}
} finally {
setLoading(false);
}
}
loadGateways();
}, [selectedGateway, onSelectGateway, showManual]);
if (loading) {
return (
<div className={`flex items-center justify-center py-8 ${className}`}>
<Loader2 className="h-6 w-6 animate-spin text-gray-400" />
<span className="ml-2 text-gray-500">Loading payment options...</span>
</div>
);
}
const availableGateways = gateways.filter((g) => g.available);
if (availableGateways.length === 0) {
return (
<div className={`rounded-lg border border-yellow-200 bg-yellow-50 p-4 ${className}`}>
<p className="text-sm text-yellow-800">
No payment methods are currently available. Please contact support.
</p>
</div>
);
}
return (
<div className={`space-y-3 ${className}`}>
<label className="block text-sm font-medium text-gray-700">
Select Payment Method
</label>
<div className="grid gap-3">
{availableGateways.map((gateway) => (
<button
key={gateway.id}
type="button"
onClick={() => onSelectGateway(gateway.id)}
className={`relative flex items-start rounded-lg border-2 p-4 text-left transition-all ${
selectedGateway === gateway.id
? 'border-indigo-600 bg-indigo-50 ring-1 ring-indigo-600'
: 'border-gray-200 bg-white hover:border-gray-300 hover:bg-gray-50'
}`}
>
<div
className={`flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-full ${
selectedGateway === gateway.id
? 'bg-indigo-600 text-white'
: 'bg-gray-100 text-gray-600'
}`}
>
{gateway.icon}
</div>
<div className="ml-4 flex-1">
<div className="flex items-center">
<span
className={`font-medium ${
selectedGateway === gateway.id ? 'text-indigo-900' : 'text-gray-900'
}`}
>
{gateway.name}
</span>
{gateway.recommended && (
<span className="ml-2 inline-flex items-center rounded-full bg-green-100 px-2 py-0.5 text-xs font-medium text-green-800">
Recommended
</span>
)}
</div>
<p
className={`mt-1 text-sm ${
selectedGateway === gateway.id ? 'text-indigo-700' : 'text-gray-500'
}`}
>
{gateway.description}
</p>
</div>
{selectedGateway === gateway.id && (
<div className="absolute right-4 top-4">
<Check className="h-5 w-5 text-indigo-600" />
</div>
)}
</button>
))}
</div>
{selectedGateway === 'manual' && (
<p className="mt-2 text-xs text-gray-500">
After submitting, you'll receive bank details to complete the transfer.
Your account will be activated once we confirm the payment (usually within 1-2 business days).
</p>
)}
</div>
);
}
export default PaymentGatewaySelector;