feat: implement bike-based withdrawal system with bulk selection and auto-withdraw settings
This commit is contained in:
@@ -1,38 +1,98 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { CreditCard, Wallet, ArrowUpRight, History, CheckCircle, Clock, Building2, Smartphone, AlertCircle, ChevronDown } from 'lucide-react';
|
import { CreditCard, Wallet, ArrowUpRight, History, CheckCircle, Clock, Building2, Smartphone, AlertCircle, ChevronDown, Settings, X, Bike } from 'lucide-react';
|
||||||
import { investors, transactions } from '@/data/mockData';
|
import { investors, transactions, bikes } from '@/data/mockData';
|
||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
|
|
||||||
export default function InvestorWithdrawPage() {
|
export default function InvestorWithdrawPage() {
|
||||||
const investor = investors[0]; // mock logged-in investor
|
const investor = investors.find(i => i.id === 'inv1') || investors[0];
|
||||||
const availableBalance = investor.totalEarnings - investor.totalWithdrawn - investor.withdrawalPending;
|
const investorBikes = bikes.filter(b => b.investorId === investor.id);
|
||||||
|
const availableBalance = investor.totalEarnings - investor.totalWithdrawn - investor.pendingEarnings;
|
||||||
|
|
||||||
const withdrawHistory = transactions.filter(t => t.investorId === investor.id && t.type === 'withdrawal').sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
|
const withdrawHistory = transactions.filter(t => t.investorId === investor.id && t.type === 'withdrawal').sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
|
||||||
|
|
||||||
const [amount, setAmount] = useState('');
|
const [showWithdrawModal, setShowWithdrawModal] = useState(false);
|
||||||
const [method, setMethod] = useState<'bank' | 'mobile'>('bank');
|
const [selectAll, setSelectAll] = useState(false);
|
||||||
|
const [selectedPlans, setSelectedPlans] = useState<string[]>([]);
|
||||||
|
const [selectedBikes, setSelectedBikes] = useState<string[]>([]);
|
||||||
|
const [paymentMethod, setPaymentMethod] = useState<'bank' | 'mobile'>('bank');
|
||||||
const [selectedAccount, setSelectedAccount] = useState('');
|
const [selectedAccount, setSelectedAccount] = useState('');
|
||||||
|
const [showAutoWithdrawModal, setShowAutoWithdrawModal] = useState(false);
|
||||||
|
const [autoWithdrawEnabled, setAutoWithdrawEnabled] = useState(false);
|
||||||
|
const [autoWithdrawFreq, setAutoWithdrawFreq] = useState('as_per_request');
|
||||||
|
const [autoWithdrawMin, setAutoWithdrawMin] = useState('1000');
|
||||||
|
const [autoWithdrawAccount, setAutoWithdrawAccount] = useState('');
|
||||||
|
|
||||||
const handleWithdraw = (e: React.FormEvent) => {
|
const planColors: Record<string, string> = {
|
||||||
e.preventDefault();
|
silver: 'bg-slate-100 text-slate-700',
|
||||||
const withdrawAmount = Number(amount);
|
gold: 'bg-amber-100 text-amber-700',
|
||||||
if (!withdrawAmount || withdrawAmount <= 0) {
|
platinum: 'bg-purple-100 text-purple-700',
|
||||||
toast.error('Please enter a valid amount');
|
diamond: 'bg-blue-100 text-blue-700',
|
||||||
return;
|
};
|
||||||
|
|
||||||
|
const bikeStatusColors: Record<string, { bg: string; text: string }> = {
|
||||||
|
available: { bg: 'bg-blue-50', text: 'text-blue-600' },
|
||||||
|
rented: { bg: 'bg-green-50', text: 'text-green-600' },
|
||||||
|
maintenance: { bg: 'bg-amber-50', text: 'text-amber-600' },
|
||||||
|
};
|
||||||
|
|
||||||
|
const assignedBikes = investorBikes;
|
||||||
|
|
||||||
|
const calculatedAmount = selectAll
|
||||||
|
? assignedBikes.reduce((sum: number, b: any) => sum + (b.totalEarnings || 0), 0)
|
||||||
|
: selectedBikes.reduce((sum: number, bikeId: string) => {
|
||||||
|
const bike = assignedBikes.find((b: any) => b.id === bikeId);
|
||||||
|
return sum + (bike?.totalEarnings || 0);
|
||||||
|
}, 0);
|
||||||
|
|
||||||
|
const toggleSelectAll = (checked: boolean) => {
|
||||||
|
setSelectAll(checked);
|
||||||
|
if (checked) {
|
||||||
|
setSelectedPlans(investor.investments?.map((inv: any) => inv.id) || []);
|
||||||
|
setSelectedBikes(assignedBikes.map(b => b.id));
|
||||||
|
} else {
|
||||||
|
setSelectedPlans([]);
|
||||||
|
setSelectedBikes([]);
|
||||||
}
|
}
|
||||||
if (withdrawAmount > availableBalance) {
|
};
|
||||||
toast.error('Insufficient available balance');
|
|
||||||
return;
|
const togglePlan = (planId: string, invBikes: any[]) => {
|
||||||
|
const isSelected = selectedPlans.includes(planId);
|
||||||
|
if (isSelected) {
|
||||||
|
setSelectedPlans(selectedPlans.filter(p => p !== planId));
|
||||||
|
setSelectedBikes(selectedBikes.filter(b => !invBikes.find((ib: any) => ib.id === b)));
|
||||||
|
} else {
|
||||||
|
setSelectedPlans([...selectedPlans, planId]);
|
||||||
|
setSelectedBikes([...new Set([...selectedBikes, ...invBikes.map((b: any) => b.id)])]);
|
||||||
}
|
}
|
||||||
|
setSelectAll(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleBike = (bikeId: string) => {
|
||||||
|
setSelectedBikes(prev => prev.includes(bikeId) ? prev.filter(b => b !== bikeId) : [...prev, bikeId]);
|
||||||
|
setSelectAll(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmitWithdraw = () => {
|
||||||
if (!selectedAccount) {
|
if (!selectedAccount) {
|
||||||
toast.error('Please select an account to receive funds');
|
toast.error('Please select a payment method');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (selectedBikes.length === 0 && !selectAll) {
|
||||||
|
toast.error('Please select at least one bike or select all');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
toast.success(`Withdrawal request for ৳${calculatedAmount.toLocaleString()} submitted successfully.`);
|
||||||
|
setSelectedPlans([]);
|
||||||
|
setSelectedBikes([]);
|
||||||
|
setSelectAll(false);
|
||||||
|
setShowWithdrawModal(false);
|
||||||
|
};
|
||||||
|
|
||||||
toast.success(`Withdrawal request for ৳${withdrawAmount.toLocaleString()} submitted successfully.`);
|
const handleSaveAutoWithdraw = () => {
|
||||||
setAmount('');
|
toast.success('Auto-withdraw settings saved!');
|
||||||
|
setShowAutoWithdrawModal(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -60,8 +120,8 @@ export default function InvestorWithdrawPage() {
|
|||||||
|
|
||||||
<div className="mt-6 pt-4 border-t border-white/20 grid grid-cols-2 gap-4 relative z-10">
|
<div className="mt-6 pt-4 border-t border-white/20 grid grid-cols-2 gap-4 relative z-10">
|
||||||
<div>
|
<div>
|
||||||
<p className="text-xs text-white/70 mb-1">Total Earnings</p>
|
<p className="text-xs text-white/70 mb-1">Pending Request</p>
|
||||||
<p className="font-semibold">৳{investor.totalEarnings.toLocaleString()}</p>
|
<p className="font-semibold">৳{investor.pendingEarnings.toLocaleString()}</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="text-xs text-white/70 mb-1">Total Withdrawn</p>
|
<p className="text-xs text-white/70 mb-1">Total Withdrawn</p>
|
||||||
@@ -74,99 +134,27 @@ export default function InvestorWithdrawPage() {
|
|||||||
<AlertCircle className="w-5 h-5 text-amber-600 shrink-0 mt-0.5" />
|
<AlertCircle className="w-5 h-5 text-amber-600 shrink-0 mt-0.5" />
|
||||||
<div>
|
<div>
|
||||||
<h4 className="text-sm font-bold text-amber-800 mb-1">Pending Requests</h4>
|
<h4 className="text-sm font-bold text-amber-800 mb-1">Pending Requests</h4>
|
||||||
<p className="text-xs text-amber-700">You currently have <b>৳{investor.withdrawalPending.toLocaleString()}</b> in pending withdrawals. Processing takes 1-3 business days.</p>
|
<p className="text-xs text-amber-700">You currently have <b>৳{investor.pendingEarnings.toLocaleString()}</b> in pending withdrawals. Processing takes 1-3 business days.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Withdrawal Form */}
|
{/* Right Side */}
|
||||||
<div className="lg:col-span-2">
|
<div className="lg:col-span-2 space-y-6">
|
||||||
<div className="bg-white rounded-2xl border border-slate-200 p-6 shadow-sm">
|
|
||||||
<h3 className="text-lg font-bold text-slate-800 mb-6 flex items-center gap-2">
|
|
||||||
<ArrowUpRight className="w-5 h-5 text-investor" /> Create Withdrawal Request
|
|
||||||
</h3>
|
|
||||||
|
|
||||||
<form onSubmit={handleWithdraw} className="space-y-6">
|
|
||||||
<div>
|
|
||||||
<label className="block text-sm font-semibold text-slate-700 mb-2">Amount (৳)</label>
|
|
||||||
<div className="relative">
|
|
||||||
<span className="absolute left-4 top-1/2 -translate-y-1/2 text-slate-500 font-bold">৳</span>
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
value={amount}
|
|
||||||
onChange={(e) => setAmount(e.target.value)}
|
|
||||||
placeholder="0.00"
|
|
||||||
className="w-full pl-8 pr-4 py-3 border border-slate-200 rounded-xl text-lg font-semibold focus:outline-none focus:border-investor focus:ring-1 focus:ring-investor transition-all"
|
|
||||||
/>
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
onClick={() => setShowAutoWithdrawModal(true)}
|
||||||
onClick={() => setAmount(availableBalance.toString())}
|
className="px-4 py-2 text-sm text-slate-600 hover:text-investor flex items-center gap-1"
|
||||||
className="absolute right-3 top-1/2 -translate-y-1/2 px-3 py-1 bg-slate-100 text-xs font-bold text-slate-600 rounded-lg hover:bg-slate-200"
|
|
||||||
>
|
>
|
||||||
MAX
|
<Settings className="w-4 h-4" /> Configure Auto-Withdraw
|
||||||
</button>
|
</button>
|
||||||
</div>
|
{/* Withdraw Button */}
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label className="block text-sm font-semibold text-slate-700 mb-3">Withdrawal Method</label>
|
|
||||||
<div className="grid grid-cols-2 gap-4">
|
|
||||||
<div
|
|
||||||
onClick={() => setMethod('bank')}
|
|
||||||
className={`p-4 rounded-xl border-2 cursor-pointer transition-all ${method === 'bank' ? 'border-investor bg-investor/5' : 'border-slate-100 bg-white hover:border-slate-200'}`}
|
|
||||||
>
|
|
||||||
<Building2 className={`w-6 h-6 mb-2 ${method === 'bank' ? 'text-investor' : 'text-slate-400'}`} />
|
|
||||||
<p className={`font-semibold text-sm ${method === 'bank' ? 'text-investor' : 'text-slate-600'}`}>Bank Transfer</p>
|
|
||||||
<p className="text-xs text-slate-500">1-3 business days</p>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
onClick={() => setMethod('mobile')}
|
|
||||||
className={`p-4 rounded-xl border-2 cursor-pointer transition-all ${method === 'mobile' ? 'border-investor bg-investor/5' : 'border-slate-100 bg-white hover:border-slate-200'}`}
|
|
||||||
>
|
|
||||||
<Smartphone className={`w-6 h-6 mb-2 ${method === 'mobile' ? 'text-investor' : 'text-slate-400'}`} />
|
|
||||||
<p className={`font-semibold text-sm ${method === 'mobile' ? 'text-investor' : 'text-slate-600'}`}>Mobile Banking</p>
|
|
||||||
<p className="text-xs text-slate-500">Instant transfer</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label className="block text-sm font-semibold text-slate-700 mb-2">Select Account</label>
|
|
||||||
<div className="relative">
|
|
||||||
<select
|
|
||||||
value={selectedAccount}
|
|
||||||
onChange={(e) => setSelectedAccount(e.target.value)}
|
|
||||||
className="w-full px-4 py-3 border border-slate-200 rounded-xl text-sm appearance-none focus:outline-none focus:border-investor focus:ring-1 focus:ring-investor transition-all bg-white"
|
|
||||||
>
|
|
||||||
<option value="" disabled>Choose an account...</option>
|
|
||||||
{method === 'bank' ? (
|
|
||||||
investor.bankAccounts?.map(acc => (
|
|
||||||
<option key={acc.id} value={acc.id}>{acc.bankName} - {acc.accountNumber} {acc.isPrimary ? '(Primary)' : ''}</option>
|
|
||||||
))
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<option value="bkash">{investor.mobileBanking} - {investor.mobileBankingNumber}</option>
|
|
||||||
{investor.additionalMobileBanking?.map((acc, idx) => (
|
|
||||||
<option key={idx} value={`mob-${idx}`}>{acc.provider} - {acc.number}</option>
|
|
||||||
))}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</select>
|
|
||||||
<ChevronDown className="w-4 h-4 text-slate-500 absolute right-4 top-1/2 -translate-y-1/2 pointer-events-none" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="pt-2">
|
|
||||||
<button
|
<button
|
||||||
type="submit"
|
onClick={() => setShowWithdrawModal(true)}
|
||||||
className="w-full py-3.5 bg-investor text-white rounded-xl font-bold text-sm shadow-md shadow-investor/20 hover:bg-investor-dark transition-all flex items-center justify-center gap-2"
|
className="w-full py-4 bg-investor text-white rounded-xl font-bold text-base shadow-md shadow-investor/20 hover:bg-investor-dark transition-all flex items-center justify-center gap-2"
|
||||||
>
|
>
|
||||||
<CreditCard className="w-4 h-4" /> Submit Withdrawal Request
|
<CreditCard className="w-5 h-5" /> Withdrawal Request
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Withdrawal History */}
|
{/* Withdrawal History */}
|
||||||
@@ -219,6 +207,290 @@ export default function InvestorWithdrawPage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Withdrawal Request Modal */}
|
||||||
|
{showWithdrawModal && (
|
||||||
|
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4">
|
||||||
|
<div className="bg-white rounded-xl shadow-xl w-full max-w-2xl max-h-[90vh] overflow-hidden flex flex-col">
|
||||||
|
<div className="p-5 border-b border-slate-100 flex items-center justify-between">
|
||||||
|
<h3 className="text-lg font-bold text-slate-800">Create Withdrawal Request</h3>
|
||||||
|
<button onClick={() => setShowWithdrawModal(false)} className="p-1 hover:bg-slate-100 rounded-lg">
|
||||||
|
<X className="w-5 h-5 text-slate-400" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="p-5 overflow-y-auto flex-1 space-y-5">
|
||||||
|
{/* Balance Cards */}
|
||||||
|
<div className="grid grid-cols-3 gap-4">
|
||||||
|
<div className="bg-green-50 rounded-lg p-4 border border-green-100">
|
||||||
|
<p className="text-xs text-green-600 font-medium">Available Balance</p>
|
||||||
|
<p className="text-lg font-bold text-green-700">৳{availableBalance.toLocaleString()}</p>
|
||||||
|
</div>
|
||||||
|
<div className="bg-amber-50 rounded-lg p-4 border border-amber-100">
|
||||||
|
<p className="text-xs text-amber-600 font-medium">Pending Request</p>
|
||||||
|
<p className="text-lg font-bold text-amber-700">৳{investor.pendingEarnings.toLocaleString()}</p>
|
||||||
|
</div>
|
||||||
|
<div className="bg-slate-50 rounded-lg p-4 border border-slate-100">
|
||||||
|
<p className="text-xs text-slate-600 font-medium">Total Withdrawn</p>
|
||||||
|
<p className="text-lg font-bold text-slate-700">৳{investor.totalWithdrawn.toLocaleString()}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Select Investment Plans & Bikes */}
|
||||||
|
<div>
|
||||||
|
<h4 className="font-semibold text-slate-800 mb-3">Select Investment Plans & Bikes</h4>
|
||||||
|
<div className="space-y-4">
|
||||||
|
{/* Select All */}
|
||||||
|
<div className="flex items-center gap-3 p-4 bg-slate-50 rounded-lg border border-slate-200">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
id="selectAll"
|
||||||
|
checked={selectAll}
|
||||||
|
onChange={(e) => toggleSelectAll(e.target.checked)}
|
||||||
|
className="w-4 h-4 text-investor rounded"
|
||||||
|
/>
|
||||||
|
<label htmlFor="selectAll" className="flex-1">
|
||||||
|
<span className="font-medium text-slate-800">Select All</span>
|
||||||
|
<p className="text-xs text-slate-500">Include all investments and bikes</p>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Investment Plans with Bikes */}
|
||||||
|
<div className="border border-slate-200 rounded-lg overflow-hidden">
|
||||||
|
<div className="bg-slate-50 px-4 py-2 border-b border-slate-200">
|
||||||
|
<p className="text-sm font-medium text-slate-700">Investment Plans</p>
|
||||||
|
</div>
|
||||||
|
<div className="divide-y divide-slate-100">
|
||||||
|
{investor.investments?.map((inv: any) => {
|
||||||
|
const invBikes = assignedBikes.filter((b: any) => b.investmentId === inv.id);
|
||||||
|
const invEarnings = invBikes.reduce((sum: number, b: any) => sum + (b.totalEarnings || 0), 0);
|
||||||
|
return (
|
||||||
|
<div key={inv.id} className="p-4">
|
||||||
|
<div className="flex items-center gap-3 mb-2">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
id={`plan-${inv.id}`}
|
||||||
|
checked={selectAll || selectedPlans.includes(inv.id)}
|
||||||
|
disabled={selectAll}
|
||||||
|
onChange={() => togglePlan(inv.id, invBikes)}
|
||||||
|
className="w-4 h-4 text-investor rounded"
|
||||||
|
/>
|
||||||
|
<label htmlFor={`plan-${inv.id}`} className="flex-1 flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<span className="font-medium text-slate-800">{inv.planName}</span>
|
||||||
|
<span className={`ml-2 px-2 py-0.5 rounded text-xs capitalize ${planColors[inv.planType]}`}>{inv.planType}</span>
|
||||||
|
</div>
|
||||||
|
<span className="text-sm font-bold text-green-600">৳{invEarnings.toLocaleString()}</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
{!selectAll && invBikes.length > 0 && (
|
||||||
|
<div className="ml-7 pl-4 border-l-2 border-slate-200 space-y-2">
|
||||||
|
{invBikes.map((bike: any) => {
|
||||||
|
const statusStyle = bikeStatusColors[bike.status] || bikeStatusColors.available;
|
||||||
|
return (
|
||||||
|
<div key={bike.id} className="flex items-center gap-2">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
id={`bike-${bike.id}`}
|
||||||
|
checked={selectAll || selectedBikes.includes(bike.id)}
|
||||||
|
onChange={() => toggleBike(bike.id)}
|
||||||
|
className="w-3 h-3 text-investor rounded"
|
||||||
|
/>
|
||||||
|
<label htmlFor={`bike-${bike.id}`} className="flex-1 text-sm text-slate-600">
|
||||||
|
<span className="font-medium">{bike.model}</span>
|
||||||
|
<span className="text-xs text-slate-400 ml-2">{bike.plateNumber}</span>
|
||||||
|
</label>
|
||||||
|
<span className="text-xs text-green-600 font-medium">৳{bike.totalEarnings?.toLocaleString() || 0}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Withdrawal Amount */}
|
||||||
|
<div>
|
||||||
|
<h4 className="font-semibold text-slate-800 mb-3">Withdrawal Amount</h4>
|
||||||
|
<div className="bg-gradient-to-r from-green-50 to-emerald-50 border border-green-200 rounded-xl p-5">
|
||||||
|
<div className="flex items-center justify-between mb-2">
|
||||||
|
<span className="text-sm text-slate-600">Calculated Amount</span>
|
||||||
|
<span className={`text-xs px-2 py-0.5 rounded-full ${selectAll ? 'bg-green-100 text-green-700' : 'bg-amber-100 text-amber-700'}`}>
|
||||||
|
{selectAll ? 'All Selected' : `${selectedBikes.length} bikes`}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<p className="text-3xl font-bold text-green-700 mb-1">
|
||||||
|
৳{calculatedAmount.toLocaleString()}
|
||||||
|
</p>
|
||||||
|
<p className="text-xs text-slate-500">
|
||||||
|
Based on {selectAll ? 'all' : selectedBikes.length} selected bike(s) earnings
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Payment Method */}
|
||||||
|
<div>
|
||||||
|
<h4 className="font-semibold text-slate-800 mb-3">Payment Method</h4>
|
||||||
|
<div className="grid grid-cols-2 gap-3">
|
||||||
|
{(investor as any).bankAccounts?.map((account: any) => (
|
||||||
|
<div
|
||||||
|
key={account.id}
|
||||||
|
onClick={() => { setPaymentMethod('bank'); setSelectedAccount(account.id); }}
|
||||||
|
className={`p-4 rounded-lg border cursor-pointer transition-all ${selectedAccount === account.id ? 'border-investor bg-investor/5' : 'border-slate-200 hover:border-slate-300'}`}
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-2 mb-1">
|
||||||
|
<div className={`w-8 h-8 rounded-lg flex items-center justify-center ${account.isPrimary ? 'bg-green-100' : 'bg-slate-100'}`}>
|
||||||
|
<Building2 className={`w-4 h-4 ${account.isPrimary ? 'text-green-600' : 'text-slate-500'}`} />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="text-sm font-medium text-slate-800">{account.bankName}</p>
|
||||||
|
{account.isPrimary && <span className="text-xs text-green-600">Primary</span>}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p className="text-xs text-slate-500">{account.accountNumber}</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
{(investor as any).mobileBanking && (
|
||||||
|
<div
|
||||||
|
onClick={() => { setPaymentMethod('mobile'); setSelectedAccount('mobile'); }}
|
||||||
|
className={`mt-3 p-4 rounded-lg border cursor-pointer transition-all ${selectedAccount === 'mobile' ? 'border-investor bg-investor/5' : 'border-slate-200 hover:border-slate-300'}`}
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<div className="w-8 h-8 rounded-lg bg-pink-100 flex items-center justify-center">
|
||||||
|
<Smartphone className="w-4 h-4 text-pink-600" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="text-sm font-medium text-slate-800">{(investor as any).mobileBanking}</p>
|
||||||
|
<p className="text-xs text-slate-500">{(investor as any).mobileBankingNumber}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="p-5 border-t border-slate-100 flex justify-between items-center">
|
||||||
|
|
||||||
|
<div className="flex gap-3">
|
||||||
|
<button onClick={() => setShowWithdrawModal(false)} className="px-4 py-2 border border-slate-200 text-slate-600 rounded-lg text-sm hover:bg-slate-50">
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={handleSubmitWithdraw}
|
||||||
|
disabled={!selectedAccount || (selectedBikes.length === 0 && !selectAll)}
|
||||||
|
className="px-6 py-2 bg-investor text-white rounded-lg text-sm font-medium hover:bg-investor-dark disabled:opacity-50 disabled:cursor-not-allowed"
|
||||||
|
>
|
||||||
|
Submit Request
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Auto-Withdraw Modal */}
|
||||||
|
{showAutoWithdrawModal && (
|
||||||
|
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-[60] p-4">
|
||||||
|
<div className="bg-white rounded-xl shadow-xl w-full max-w-md">
|
||||||
|
<div className="p-5 border-b border-slate-100 flex items-center justify-between">
|
||||||
|
<h3 className="text-lg font-bold text-slate-800">Auto-Withdraw Settings</h3>
|
||||||
|
<button onClick={() => setShowAutoWithdrawModal(false)} className="p-1 hover:bg-slate-100 rounded-lg">
|
||||||
|
<X className="w-5 h-5 text-slate-400" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className="p-5 space-y-4">
|
||||||
|
<div>
|
||||||
|
<label className="text-sm font-medium text-slate-700 mb-2 block">Enable Auto-Withdraw</label>
|
||||||
|
<div className="flex items-center gap-3 p-3 bg-slate-50 rounded-lg">
|
||||||
|
<button
|
||||||
|
onClick={() => setAutoWithdrawEnabled(!autoWithdrawEnabled)}
|
||||||
|
className={`w-12 h-6 rounded-full transition-colors ${autoWithdrawEnabled ? 'bg-green-500' : 'bg-slate-200'}`}
|
||||||
|
>
|
||||||
|
<span className={`block w-5 h-5 bg-white rounded-full shadow transition-transform ${autoWithdrawEnabled ? 'translate-x-6' : 'translate-x-0.5'}`} />
|
||||||
|
</button>
|
||||||
|
<span className="text-sm text-slate-600">Automatically withdraw earnings</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className="text-sm font-medium text-slate-700 mb-2 block">Withdrawal Frequency</label>
|
||||||
|
<select
|
||||||
|
value={autoWithdrawFreq}
|
||||||
|
onChange={(e) => setAutoWithdrawFreq(e.target.value)}
|
||||||
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
||||||
|
>
|
||||||
|
<option value="as_per_request">As Requested</option>
|
||||||
|
<option value="weekly">Weekly</option>
|
||||||
|
<option value="monthly">Monthly</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className="text-sm font-medium text-slate-700 mb-2 block">Minimum Amount</label>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span className="px-3 py-2 bg-slate-100 rounded-lg text-slate-500 font-bold">৳</span>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
value={autoWithdrawMin}
|
||||||
|
onChange={(e) => setAutoWithdrawMin(e.target.value)}
|
||||||
|
className="flex-1 px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
||||||
|
placeholder="1000"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<p className="text-xs text-slate-500 mt-1">Minimum balance required for auto-withdrawal</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className="text-sm font-medium text-slate-700 mb-2 block">Destination Account</label>
|
||||||
|
<div className="space-y-2">
|
||||||
|
{(investor as any).bankAccounts?.map((account: any) => (
|
||||||
|
<div key={account.id} className="flex items-center gap-3 p-3 bg-slate-50 rounded-lg">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="destAccount"
|
||||||
|
checked={autoWithdrawAccount === account.id}
|
||||||
|
onChange={() => setAutoWithdrawAccount(account.id)}
|
||||||
|
className="text-investor"
|
||||||
|
/>
|
||||||
|
<div className="flex-1">
|
||||||
|
<p className="font-medium text-slate-700">{account.bankName}</p>
|
||||||
|
<p className="text-xs text-slate-500 font-mono">{account.accountNumber}</p>
|
||||||
|
</div>
|
||||||
|
{account.isPrimary && <span className="text-xs bg-green-100 text-green-700 px-2 py-1 rounded-full">Primary</span>}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
{(investor as any).mobileBanking && (
|
||||||
|
<div className="flex items-center gap-3 p-3 bg-purple-50 rounded-lg">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="destAccount"
|
||||||
|
checked={autoWithdrawAccount === 'mobile'}
|
||||||
|
onChange={() => setAutoWithdrawAccount('mobile')}
|
||||||
|
className="text-investor"
|
||||||
|
/>
|
||||||
|
<div className="flex-1">
|
||||||
|
<p className="font-medium text-slate-700">{(investor as any).mobileBanking}</p>
|
||||||
|
<p className="text-xs text-slate-500 font-mono">{(investor as any).mobileBankingNumber}</p>
|
||||||
|
</div>
|
||||||
|
<span className="text-xs bg-purple-100 text-purple-700 px-2 py-1 rounded-full">Primary</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="p-5 border-t border-slate-100 flex justify-end gap-3">
|
||||||
|
<button onClick={() => setShowAutoWithdrawModal(false)} className="px-4 py-2 border border-slate-200 text-slate-600 rounded-lg text-sm hover:bg-slate-50">Cancel</button>
|
||||||
|
<button onClick={handleSaveAutoWithdraw} className="px-4 py-2 bg-investor text-white rounded-lg text-sm font-medium hover:bg-investor-dark">Save Settings</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user