import React, { useState } from 'react'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { api } from '@/lib/api/client'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Copy, CheckCircle, Activity, DollarSign, ChevronRight, Clock, Info, Wallet, CreditCard, Tag } from 'lucide-react'; import { toast } from 'sonner'; import { Link } from 'react-router-dom'; import { formatPrice, getCurrencySettings } from '@/lib/currency'; // Affiliate types interface AffiliateProfile { status: 'active' | 'pending' | 'approved' | 'rejected'; referral_code: string; commission_rate: number; custom_commission_rate: number | null; global_commission_rate: number; total_earnings: number; pending_earnings: number; collections_enabled?: boolean; } interface PaginatedReferrals { referrals: AffiliateReferral[]; total: number; page: number; limit: number; total_pages: number; } interface AffiliateReferral { id: number; status: 'pending' | 'approved' | 'rejected'; commission_amount: string; created_at: string; order_id: number; currency: string; approved_at?: string; cancelled_reason?: string; } interface AffiliatePayout { id: number; amount: string; currency: string; method: string; status: string; notes: string; created_at: string; completed_at: string; } interface PaymentDetails { payment_method: string; payment_details: { bank_name?: string; account_number?: string; account_holder?: string; swift_code?: string; bank_address?: string; email?: string; name?: string; notes?: string; }; } interface AffiliateSettings { woonoow_affiliate_payment_methods?: string[]; } // Payment method labels const PAYMENT_METHOD_LABELS: Record = { bank_transfer: 'Bank Transfer', paypal: 'PayPal', wise: 'Wise', skrill: 'Skrill', payoneer: 'Payoneer', custom: 'Custom (Other)', }; // Format amount using site's currency settings function formatAmount(amount: number | string, currency?: string): string { const amountNum = typeof amount === 'string' ? parseFloat(amount) : amount; const settings = getCurrencySettings(); const decimals = currency === 'IDR' ? 0 : settings.decimals; const formatted = formatNumberWithSeparators(amountNum, decimals, settings.thousandSeparator, settings.decimalSeparator); return `${settings.symbol}${formatted}`; } function formatNumberWithSeparators( value: number, decimals: number, thousandSeparator: string, decimalSeparator: string ): string { const rounded = value.toFixed(decimals); const [integerPart, decimalPart] = rounded.split('.'); const formattedInteger = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, thousandSeparator); if (decimals > 0 && decimalPart) { return `${formattedInteger}${decimalSeparator}${decimalPart}`; } return formattedInteger; } export default function AffiliateDashboard() { const queryClient = useQueryClient(); const [copied, setCopied] = useState(false); const [showPaymentForm, setShowPaymentForm] = useState(false); const [selectedMethod, setSelectedMethod] = useState(''); const [paymentFormData, setPaymentFormData] = useState>({}); // Fetch affiliate settings for available payment methods const { data: settings } = useQuery({ queryKey: ['affiliate-settings'], queryFn: async () => { try { return await api.get('/modules/affiliate/settings'); } catch { return { woonoow_affiliate_payment_methods: ['bank_transfer'] }; } }, }); const availableMethods = settings?.woonoow_affiliate_payment_methods || ['bank_transfer']; // Fetch dashboard info const { data: profile, isLoading } = useQuery({ queryKey: ['affiliate-profile'], queryFn: async () => { try { const response = await api.get('/account/affiliate'); return response; } catch (err: any) { if (err.status === 404) return null; throw err; } }, retry: false }); // Fetch referrals const { data: referralsResponse, isLoading: isLoadingReferrals } = useQuery({ queryKey: ['affiliate-referrals'], queryFn: async () => { return await api.get('/account/affiliate/referrals?limit=5'); }, enabled: !!profile && profile.status === 'active' }); const referrals = referralsResponse?.referrals || []; // Fetch payout history const { data: payouts = [], isLoading: isLoadingPayouts } = useQuery({ queryKey: ['affiliate-payouts'], queryFn: async () => { return await api.get('/account/affiliate/payouts'); }, enabled: !!profile && profile.status === 'active' }); // Fetch payment details const { data: savedPaymentDetails } = useQuery({ queryKey: ['affiliate-payment-details'], queryFn: async () => { return await api.get('/account/affiliate/payment-details'); }, enabled: !!profile && profile.status === 'active' }); // Initialize form when data loads OR when edit mode is opened const initFormFromSaved = React.useCallback(() => { if (savedPaymentDetails?.payment_method) { setSelectedMethod(savedPaymentDetails.payment_method); setPaymentFormData(savedPaymentDetails.payment_details || {}); } }, [savedPaymentDetails]); // Initialize when saved data changes and form is open React.useEffect(() => { if (showPaymentForm && savedPaymentDetails) { initFormFromSaved(); } }, [showPaymentForm, savedPaymentDetails, initFormFromSaved]); const updatePaymentMutation = useMutation({ mutationFn: async (data: { payment_method: string; payment_details: Record }) => { return await api.post('/account/affiliate/payment-details', data); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['affiliate-payment-details'] }); toast.success('Payment details saved successfully!'); setShowPaymentForm(false); }, onError: () => { toast.error('Failed to save payment details.'); } }); const applyMutation = useMutation({ mutationFn: async () => { return await api.post('/account/affiliate/apply'); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['affiliate-profile'] }); toast.success('Successfully applied for the affiliate program!'); }, onError: () => { toast.error('Failed to apply. Please try again later.'); } }); if (isLoading) { return
Loading...
; } if (!profile) { return (

Affiliate Program

Join our Affiliate Program

Earn commissions by referring customers to our store! You'll get a unique referral link that tracks any purchases made by your referrals.

); } if (profile.status === 'pending') { return (

Affiliate Program

Application Pending

Your application is currently being reviewed. We will notify you once it's approved.

); } const woonoowConfig = (window as any).woonoowCustomer || {}; const basePath = woonoowConfig.basePath || ''; const shopPath = basePath ? `${basePath}/shop` : '/shop'; const referralLink = `${window.location.origin}${shopPath}?ref=${profile.referral_code}`; const handleCopy = () => { navigator.clipboard.writeText(referralLink); setCopied(true); toast.success('Referral link copied to clipboard!'); setTimeout(() => setCopied(false), 2000); }; const totalEarnings = profile.total_earnings || 0; const pendingEarnings = profile.pending_earnings || 0; const handleSavePayment = () => { if (!selectedMethod) { toast.error('Please select a payment method'); return; } updatePaymentMutation.mutate({ payment_method: selectedMethod, payment_details: paymentFormData }); }; const renderPaymentFields = () => { switch (selectedMethod) { case 'bank_transfer': return (
setPaymentFormData({ ...paymentFormData, bank_name: e.target.value })} placeholder="e.g., Bank Central Asia" />
setPaymentFormData({ ...paymentFormData, account_holder: e.target.value })} placeholder="Full name as on account" />
setPaymentFormData({ ...paymentFormData, account_number: e.target.value })} placeholder="Account number" />
setPaymentFormData({ ...paymentFormData, swift_code: e.target.value })} placeholder="e.g., CENAIDJA (for Indonesian banks)" />
setPaymentFormData({ ...paymentFormData, bank_address: e.target.value })} placeholder="Bank branch address" />
); case 'paypal': case 'wise': case 'skrill': case 'payoneer': return (
setPaymentFormData({ ...paymentFormData, name: e.target.value })} placeholder="Your name" />
setPaymentFormData({ ...paymentFormData, email: e.target.value })} placeholder="your@email.com" />
); case 'custom': return (