Add CRUD forms: Bank, Mobile Banking, Tax, Documents + Transactions withdraw list
This commit is contained in:
@@ -47,13 +47,23 @@ export default function InvestorDetailPage() {
|
|||||||
const investor = investors.find(i => i.id === investorId);
|
const investor = investors.find(i => i.id === investorId);
|
||||||
|
|
||||||
const assignedBikes = initialBikes.filter(b => b.investorId === investorId);
|
const assignedBikes = initialBikes.filter(b => b.investorId === investorId);
|
||||||
const investorTransactions = initialTransactions.filter(t => t.userId === investor?.userId);
|
// Investor transactions are filtered below
|
||||||
|
|
||||||
const [activeTab, setActiveTab] = useState('overview');
|
const [activeTab, setActiveTab] = useState('overview');
|
||||||
const [showEditModal, setShowEditModal] = useState(false);
|
const [showEditModal, setShowEditModal] = useState(false);
|
||||||
const [showAssignBikeModal, setShowAssignBikeModal] = useState(false);
|
const [showAssignBikeModal, setShowAssignBikeModal] = useState(false);
|
||||||
const [selectedBikeId, setSelectedBikeId] = useState('');
|
const [selectedBikeId, setSelectedBikeId] = useState('');
|
||||||
const [showCreateInvestmentModal, setShowCreateInvestmentModal] = useState(false);
|
const [showCreateInvestmentModal, setShowCreateInvestmentModal] = useState(false);
|
||||||
|
const [showBankModal, setShowBankModal] = useState(false);
|
||||||
|
const [showMobileBankingModal, setShowMobileBankingModal] = useState(false);
|
||||||
|
const [showTaxModal, setShowTaxModal] = useState(false);
|
||||||
|
const [showDocModal, setShowDocModal] = useState(false);
|
||||||
|
const [editingBank, setEditingBank] = useState({ bankName: '', bankAccountName: '', bankAccountNumber: '', bankBranch: '', bankRouting: '' });
|
||||||
|
const [editingMobileBanking, setEditingMobileBanking] = useState({ provider: '', number: '', isPrimary: false });
|
||||||
|
const [editingTax, setEditingTax] = useState({ tinNumber: '', passportNumber: '' });
|
||||||
|
const [newDoc, setNewDoc] = useState({ type: 'nid', number: '', url: '' });
|
||||||
|
const [editingMobileIndex, setEditingMobileIndex] = useState<number | null>(null);
|
||||||
|
const investorTransactions = initialTransactions.filter(t => t.investorId === investorId);
|
||||||
const [newInvestment, setNewInvestment] = useState({
|
const [newInvestment, setNewInvestment] = useState({
|
||||||
planName: '',
|
planName: '',
|
||||||
planType: 'gold' as 'silver' | 'gold' | 'platinum' | 'diamond',
|
planType: 'gold' as 'silver' | 'gold' | 'platinum' | 'diamond',
|
||||||
@@ -494,43 +504,80 @@ export default function InvestorDetailPage() {
|
|||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<div className="grid lg:grid-cols-2 gap-6">
|
<div className="grid lg:grid-cols-2 gap-6">
|
||||||
<div>
|
<div>
|
||||||
<h3 className="font-semibold text-slate-800 mb-4">Bank Details</h3>
|
<div className="flex items-center justify-between mb-4">
|
||||||
|
<h3 className="font-semibold text-slate-800">Bank Details</h3>
|
||||||
|
<button onClick={() => {
|
||||||
|
setEditingBank({
|
||||||
|
bankName: investor.bankName || '',
|
||||||
|
bankAccountName: investor.bankAccountName || '',
|
||||||
|
bankAccountNumber: investor.bankAccountNumber || '',
|
||||||
|
bankBranch: investor.bankBranch || '',
|
||||||
|
bankRouting: investor.bankRouting || ''
|
||||||
|
});
|
||||||
|
setShowBankModal(true);
|
||||||
|
}} className="text-sm text-investor hover:underline flex items-center gap-1">
|
||||||
|
<Edit className="w-4 h-4" /> {investor.bankName ? 'Edit' : 'Add'}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
{investor.bankName && (
|
{investor.bankName ? (
|
||||||
|
<>
|
||||||
<div className="flex items-center gap-3 p-3 bg-slate-50 rounded-lg">
|
<div className="flex items-center gap-3 p-3 bg-slate-50 rounded-lg">
|
||||||
<Banknote className="w-5 h-5 text-slate-400" />
|
<Banknote className="w-5 h-5 text-slate-400" />
|
||||||
<div>
|
<div className="flex-1">
|
||||||
<p className="text-xs text-slate-500">Bank</p>
|
<p className="text-xs text-slate-500">Bank</p>
|
||||||
<p className="font-medium text-slate-700">{investor.bankName}</p>
|
<p className="font-medium text-slate-700">{investor.bankName}</p>
|
||||||
<p className="text-xs text-slate-400">{investor.bankBranch}</p>
|
<p className="text-xs text-slate-400">{investor.bankBranch}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
{investor.bankAccountNumber && (
|
|
||||||
<div className="flex items-center gap-3 p-3 bg-slate-50 rounded-lg">
|
<div className="flex items-center gap-3 p-3 bg-slate-50 rounded-lg">
|
||||||
<CreditCard className="w-5 h-5 text-slate-400" />
|
<CreditCard className="w-5 h-5 text-slate-400" />
|
||||||
<div>
|
<div className="flex-1">
|
||||||
<p className="text-xs text-slate-500">Account</p>
|
<p className="text-xs text-slate-500">Account</p>
|
||||||
<p className="font-medium text-slate-700">{investor.bankAccountName}</p>
|
<p className="font-medium text-slate-700">{investor.bankAccountName}</p>
|
||||||
<p className="text-xs text-slate-400">{investor.bankAccountNumber}</p>
|
<p className="text-xs text-slate-400">{investor.bankAccountNumber}</p>
|
||||||
|
{investor.bankRouting && <p className="text-xs text-slate-400">Routing: {investor.bankRouting}</p>}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<div className="text-center py-8 text-slate-400 border-2 border-dashed border-slate-200 rounded-lg">
|
||||||
|
<Banknote className="w-8 h-8 mx-auto mb-2 opacity-50" />
|
||||||
|
<p className="text-sm">No bank details added</p>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<h3 className="font-semibold text-slate-800 mb-4">Mobile Banking</h3>
|
<div className="flex items-center justify-between mb-4">
|
||||||
|
<h3 className="font-semibold text-slate-800">Mobile Banking</h3>
|
||||||
|
<button onClick={() => {
|
||||||
|
setEditingMobileBanking({ provider: '', number: '', isPrimary: !investor.mobileBanking });
|
||||||
|
setEditingMobileIndex(null);
|
||||||
|
setShowMobileBankingModal(true);
|
||||||
|
}} className="text-sm text-investor hover:underline flex items-center gap-1">
|
||||||
|
<Plus className="w-4 h-4" /> Add
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
{investor.mobileBanking && (
|
{investor.mobileBanking ? (
|
||||||
<div className="flex items-center gap-3 p-3 bg-slate-50 rounded-lg">
|
<div className="flex items-center gap-3 p-3 bg-purple-50 rounded-lg">
|
||||||
<Phone className="w-5 h-5 text-slate-400" />
|
<Phone className="w-5 h-5 text-purple-500" />
|
||||||
<div>
|
<div className="flex-1">
|
||||||
<p className="text-xs text-slate-500">{investor.mobileBanking} (Primary)</p>
|
<p className="text-xs text-purple-600">Primary</p>
|
||||||
<p className="font-medium text-slate-700">{investor.mobileBankingNumber}</p>
|
<p className="font-medium text-slate-700">{investor.mobileBanking}</p>
|
||||||
|
<p className="text-xs text-slate-400">{investor.mobileBankingNumber}</p>
|
||||||
</div>
|
</div>
|
||||||
|
<button onClick={() => {
|
||||||
|
setEditingMobileBanking({ provider: investor.mobileBanking || '', number: investor.mobileBankingNumber || '', isPrimary: true });
|
||||||
|
setEditingMobileIndex(-1);
|
||||||
|
setShowMobileBankingModal(true);
|
||||||
|
}} className="p-1 hover:bg-purple-100 rounded">
|
||||||
|
<Edit className="w-4 h-4 text-purple-500" />
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
) : null}
|
||||||
{investor.additionalMobileBanking?.map((mb, idx) => (
|
{investor.additionalMobileBanking?.map((mb, idx) => (
|
||||||
<div key={idx} className="flex items-center gap-3 p-3 bg-slate-50 rounded-lg">
|
<div key={idx} className="flex items-center gap-3 p-3 bg-slate-50 rounded-lg">
|
||||||
<Phone className="w-5 h-5 text-slate-400" />
|
<Phone className="w-5 h-5 text-slate-400" />
|
||||||
@@ -538,15 +585,46 @@ export default function InvestorDetailPage() {
|
|||||||
<p className="text-xs text-slate-500">{mb.provider}</p>
|
<p className="text-xs text-slate-500">{mb.provider}</p>
|
||||||
<p className="font-medium text-slate-700">{mb.number}</p>
|
<p className="font-medium text-slate-700">{mb.number}</p>
|
||||||
</div>
|
</div>
|
||||||
{mb.verified && (
|
{mb.verified ? (
|
||||||
<span className="text-xs bg-green-100 text-green-700 px-2 py-1 rounded-full">Verified</span>
|
<span className="text-xs bg-green-100 text-green-700 px-2 py-1 rounded-full">Verified</span>
|
||||||
|
) : (
|
||||||
|
<span className="text-xs bg-amber-100 text-amber-700 px-2 py-1 rounded-full">Pending</span>
|
||||||
)}
|
)}
|
||||||
|
<button onClick={() => {
|
||||||
|
setEditingMobileBanking({ provider: mb.provider, number: mb.number, isPrimary: false });
|
||||||
|
setEditingMobileIndex(idx);
|
||||||
|
setShowMobileBankingModal(true);
|
||||||
|
}} className="p-1 hover:bg-slate-200 rounded">
|
||||||
|
<Edit className="w-4 h-4 text-slate-400" />
|
||||||
|
</button>
|
||||||
|
<button onClick={() => alert(`Delete mobile banking ${mb.provider}`)} className="p-1 hover:bg-red-50 rounded">
|
||||||
|
<Trash2 className="w-4 h-4 text-red-400" />
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
{(!investor.mobileBanking && (!investor.additionalMobileBanking || investor.additionalMobileBanking.length === 0)) && (
|
||||||
|
<div className="text-center py-8 text-slate-400 border-2 border-dashed border-slate-200 rounded-lg">
|
||||||
|
<Phone className="w-8 h-8 mx-auto mb-2 opacity-50" />
|
||||||
|
<p className="text-sm">No mobile banking added</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h3 className="font-semibold text-slate-800 mb-4 mt-6">Tax Information</h3>
|
<div className="flex items-center justify-between mb-4 mt-6">
|
||||||
|
<h3 className="font-semibold text-slate-800">Tax Information</h3>
|
||||||
|
<button onClick={() => {
|
||||||
|
setEditingTax({
|
||||||
|
tinNumber: investor.tinNumber || '',
|
||||||
|
passportNumber: investor.passportNumber || ''
|
||||||
|
});
|
||||||
|
setShowTaxModal(true);
|
||||||
|
}} className="text-sm text-investor hover:underline flex items-center gap-1">
|
||||||
|
<Edit className="w-4 h-4" /> {investor.tinNumber || investor.passportNumber ? 'Edit' : 'Add'}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
|
{investor.tinNumber || investor.passportNumber ? (
|
||||||
|
<>
|
||||||
{investor.tinNumber && (
|
{investor.tinNumber && (
|
||||||
<div className="flex items-center gap-3 p-3 bg-slate-50 rounded-lg">
|
<div className="flex items-center gap-3 p-3 bg-slate-50 rounded-lg">
|
||||||
<FileText className="w-5 h-5 text-slate-400" />
|
<FileText className="w-5 h-5 text-slate-400" />
|
||||||
@@ -565,6 +643,13 @@ export default function InvestorDetailPage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<div className="text-center py-8 text-slate-400 border-2 border-dashed border-slate-200 rounded-lg">
|
||||||
|
<FileText className="w-8 h-8 mx-auto mb-2 opacity-50" />
|
||||||
|
<p className="text-sm">No tax info added</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -596,39 +681,84 @@ export default function InvestorDetailPage() {
|
|||||||
{activeTab === 'transactions' && (
|
{activeTab === 'transactions' && (
|
||||||
<div>
|
<div>
|
||||||
<div className="flex items-center justify-between mb-4">
|
<div className="flex items-center justify-between mb-4">
|
||||||
<h3 className="font-semibold text-slate-800">Transactions</h3>
|
<h3 className="font-semibold text-slate-800">All Transactions</h3>
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<button onClick={() => alert('Withdrawal request modal would open here')} className="py-2 px-3 bg-red-500 text-white rounded-lg text-sm font-medium hover:bg-red-600 flex items-center gap-1">
|
||||||
|
<Banknote className="w-4 h-4" /> Request Withdrawal
|
||||||
|
</button>
|
||||||
<button className="py-2 px-3 border border-slate-200 text-slate-600 rounded-lg text-sm font-medium hover:bg-slate-50 flex items-center gap-1">
|
<button className="py-2 px-3 border border-slate-200 text-slate-600 rounded-lg text-sm font-medium hover:bg-slate-50 flex items-center gap-1">
|
||||||
<Download className="w-4 h-4" /> Export
|
<Download className="w-4 h-4" /> Export
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mb-6">
|
||||||
|
<h4 className="font-semibold text-slate-700 mb-3">Pending Withdrawals</h4>
|
||||||
|
<div className="space-y-2">
|
||||||
|
{investorTransactions.filter(t => t.type === 'withdrawal' && t.status === 'pending').map(tx => (
|
||||||
|
<div key={tx.id} className="flex items-center justify-between p-4 bg-amber-50 border border-amber-200 rounded-lg">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<div className="w-12 h-12 rounded-full bg-red-100 flex items-center justify-center">
|
||||||
|
<Banknote className="w-6 h-6 text-red-600" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="text-sm font-medium text-slate-700">{tx.description}</p>
|
||||||
|
<p className="text-xs text-amber-600">Ref: {tx.referenceNumber || tx.id} • {tx.createdAt}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="text-right">
|
||||||
|
<p className="text-lg font-bold text-red-600">-৳{tx.amount.toLocaleString()}</p>
|
||||||
|
<span className="text-xs font-medium px-2 py-1 rounded-full bg-amber-100 text-amber-700">
|
||||||
|
Pending
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
{investorTransactions.filter(t => t.type === 'withdrawal' && t.status === 'pending').length === 0 && (
|
||||||
|
<div className="text-center py-4 text-slate-400 border border-dashed border-slate-200 rounded-lg">
|
||||||
|
<p className="text-sm">No pending withdrawals</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h4 className="font-semibold text-slate-700 mb-3">Transaction History</h4>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{investorTransactions.map(tx => (
|
{investorTransactions.map(tx => (
|
||||||
<div key={tx.id} className="flex items-center justify-between p-3 bg-slate-50 rounded-lg">
|
<div key={tx.id} className="flex items-center justify-between p-3 bg-slate-50 rounded-lg">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<div className={`w-10 h-10 rounded-full flex items-center justify-center ${
|
<div className={`w-10 h-10 rounded-full flex items-center justify-center ${
|
||||||
tx.type === 'earning' ? 'bg-green-100' :
|
tx.type === 'earning' || tx.type === 'bike_earning' || tx.type === 'investment_return' ? 'bg-green-100' :
|
||||||
tx.type === 'withdrawal' ? 'bg-red-100' : 'bg-blue-100'
|
tx.type === 'withdrawal' ? 'bg-red-100' :
|
||||||
|
tx.type === 'investment' ? 'bg-purple-100' :
|
||||||
|
tx.type === 'referral_bonus' ? 'bg-yellow-100' :
|
||||||
|
tx.type === 'adjustment' || tx.type === 'penalty' ? 'bg-orange-100' : 'bg-blue-100'
|
||||||
}`}>
|
}`}>
|
||||||
<DollarSign className={`w-5 h-5 ${
|
<DollarSign className={`w-5 h-5 ${
|
||||||
tx.type === 'earning' ? 'text-green-600' :
|
tx.type === 'earning' || tx.type === 'bike_earning' ? 'text-green-600' :
|
||||||
tx.type === 'withdrawal' ? 'text-red-600' : 'text-blue-600'
|
tx.type === 'withdrawal' ? 'text-red-600' :
|
||||||
|
tx.type === 'investment' ? 'text-purple-600' :
|
||||||
|
tx.type === 'referral_bonus' ? 'text-yellow-600' :
|
||||||
|
tx.type === 'adjustment' || tx.type === 'penalty' ? 'text-orange-600' : 'text-blue-600'
|
||||||
}`} />
|
}`} />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm font-medium text-slate-700">{tx.description}</p>
|
<p className="text-sm font-medium text-slate-700">{tx.description}</p>
|
||||||
<p className="text-xs text-slate-400">{tx.createdAt}</p>
|
<p className="text-xs text-slate-400">{tx.createdAt} {tx.referenceNumber && `• Ref: ${tx.referenceNumber}`}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-right">
|
<div className="text-right">
|
||||||
<p className={`text-sm font-medium ${
|
<p className={`text-sm font-medium ${
|
||||||
tx.type === 'earning' ? 'text-green-600' :
|
tx.type === 'earning' || tx.type === 'bike_earning' || tx.type === 'investment_return' || tx.type === 'referral_bonus' ? 'text-green-600' :
|
||||||
tx.type === 'withdrawal' ? 'text-red-600' : 'text-blue-600'
|
tx.type === 'withdrawal' || tx.type === 'penalty' || tx.type === 'adjustment' ? 'text-red-600' : 'text-blue-600'
|
||||||
}`}>
|
}`}>
|
||||||
{tx.type === 'earning' ? '+' : tx.type === 'withdrawal' ? '-' : '+'}৳{tx.amount.toLocaleString()}
|
{tx.type === 'earning' || tx.type === 'bike_earning' || tx.type === 'investment_return' || tx.type === 'referral_bonus' ? '+' : tx.type === 'withdrawal' || tx.type === 'penalty' || tx.type === 'adjustment' ? '-' : '+'}৳{tx.amount.toLocaleString()}
|
||||||
</p>
|
</p>
|
||||||
<span className={`text-xs font-medium px-2 py-0.5 rounded-full ${
|
<span className={`text-xs font-medium px-2 py-0.5 rounded-full ${
|
||||||
tx.status === 'completed' ? 'bg-green-100 text-green-700' :
|
tx.status === 'completed' ? 'bg-green-100 text-green-700' :
|
||||||
tx.status === 'pending' ? 'bg-amber-100 text-amber-700' : 'bg-red-100 text-red-700'
|
tx.status === 'pending' ? 'bg-amber-100 text-amber-700' :
|
||||||
|
tx.status === 'cancelled' ? 'bg-red-100 text-red-700' : 'bg-slate-100 text-slate-700'
|
||||||
}`}>
|
}`}>
|
||||||
{tx.status}
|
{tx.status}
|
||||||
</span>
|
</span>
|
||||||
@@ -643,45 +773,76 @@ export default function InvestorDetailPage() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{activeTab === 'documents' && (
|
{activeTab === 'documents' && (
|
||||||
<div>
|
<div>
|
||||||
<h3 className="font-semibold text-slate-800 mb-4">Uploaded Documents</h3>
|
<div className="flex items-center justify-between mb-4">
|
||||||
<div className="space-y-2">
|
<h3 className="font-semibold text-slate-800">KYC Documents</h3>
|
||||||
|
<button onClick={() => setShowDocModal(true)} className="py-2 px-3 bg-investor text-white rounded-lg text-sm font-medium hover:bg-investor-dark flex items-center gap-1">
|
||||||
|
<Plus className="w-4 h-4" /> Upload Document
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-2 mb-6">
|
||||||
{investor.kycDocuments?.map((doc, idx) => (
|
{investor.kycDocuments?.map((doc, idx) => (
|
||||||
<div key={idx} className="flex items-center justify-between p-3 bg-slate-50 rounded-lg">
|
<div key={idx} className="flex items-center justify-between p-4 bg-slate-50 rounded-lg">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<FileText className="w-5 h-5 text-slate-400" />
|
<div className={`w-12 h-12 rounded-lg flex items-center justify-center ${
|
||||||
|
doc.type === 'nid' ? 'bg-blue-100' :
|
||||||
|
doc.type === 'passport' ? 'bg-purple-100' :
|
||||||
|
doc.type === 'bank_statement' ? 'bg-green-100' :
|
||||||
|
doc.type === 'tin_certificate' ? 'bg-amber-100' : 'bg-slate-100'
|
||||||
|
}`}>
|
||||||
|
<FileText className={`w-6 h-6 ${
|
||||||
|
doc.type === 'nid' ? 'text-blue-600' :
|
||||||
|
doc.type === 'passport' ? 'text-purple-600' :
|
||||||
|
doc.type === 'bank_statement' ? 'text-green-600' :
|
||||||
|
doc.type === 'tin_certificate' ? 'text-amber-600' : 'text-slate-600'
|
||||||
|
}`} />
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm font-medium text-slate-700 capitalize">{doc.type.replace('_', ' ')}</p>
|
<p className="text-sm font-medium text-slate-700 capitalize">{doc.type.replace('_', ' ')}</p>
|
||||||
<p className="text-xs text-slate-400">{doc.number || 'No number'}</p>
|
<p className="text-xs text-slate-400">{doc.number || 'No document number'} • Uploaded: {doc.uploadedAt || 'N/A'}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
{doc.verified ? (
|
{doc.verified ? (
|
||||||
<span className="flex items-center gap-1 text-xs font-medium px-2 py-1 rounded-full bg-green-100 text-green-700">
|
<span className="flex items-center gap-1 text-xs font-medium px-3 py-1.5 rounded-full bg-green-100 text-green-700">
|
||||||
<CheckCircle className="w-3 h-3" /> Verified
|
<CheckCircle className="w-3 h-3" /> Verified
|
||||||
</span>
|
</span>
|
||||||
) : (
|
) : (
|
||||||
<span className="flex items-center gap-1 text-xs font-medium px-2 py-1 rounded-full bg-amber-100 text-amber-700">
|
<span className="flex items-center gap-1 text-xs font-medium px-3 py-1.5 rounded-full bg-amber-100 text-amber-700">
|
||||||
<Clock className="w-3 h-3" /> Pending
|
<Clock className="w-3 h-3" /> Pending Review
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
<button onClick={() => alert('Edit document')} className="p-2 hover:bg-slate-200 rounded-lg">
|
||||||
|
<Edit className="w-4 h-4 text-slate-400" />
|
||||||
|
</button>
|
||||||
|
<button onClick={() => { if(confirm('Delete this document?')) alert('Document deleted'); }} className="p-2 hover:bg-red-50 rounded-lg">
|
||||||
|
<Trash2 className="w-4 h-4 text-red-400" />
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
{(!investor.kycDocuments || investor.kycDocuments.length === 0) && (
|
{(!investor.kycDocuments || investor.kycDocuments.length === 0) && (
|
||||||
<div className="text-center py-8 text-slate-400">
|
<div className="text-center py-12 text-slate-400 border-2 border-dashed border-slate-200 rounded-lg">
|
||||||
<FileText className="w-12 h-12 mx-auto mb-2 opacity-50" />
|
<FileText className="w-12 h-12 mx-auto mb-2 opacity-50" />
|
||||||
<p>No documents uploaded</p>
|
<p>No documents uploaded yet</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-4 border-2 border-dashed border-slate-200 rounded-lg p-8 text-center">
|
|
||||||
<FileText className="w-12 h-12 text-slate-300 mx-auto mb-2" />
|
<div className="bg-blue-50 border border-blue-200 rounded-lg p-4">
|
||||||
<p className="text-sm text-slate-500">Drag and drop files here, or click to browse</p>
|
<h4 className="font-semibold text-blue-800 mb-2">KYC Status: {investor.kycStatus.toUpperCase()}</h4>
|
||||||
<button className="mt-2 px-4 py-2 bg-slate-100 text-slate-600 rounded-lg text-sm hover:bg-slate-200">Browse Files</button>
|
<p className="text-sm text-blue-700">
|
||||||
|
{investor.kycStatus === 'verified'
|
||||||
|
? 'All documents have been verified. Investor is fully verified.'
|
||||||
|
: investor.kycStatus === 'pending'
|
||||||
|
? 'Documents are under review. Verification typically takes 24-48 hours.'
|
||||||
|
: 'Please upload required documents for verification.'}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -896,6 +1057,161 @@ export default function InvestorDetailPage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{showBankModal && (
|
||||||
|
<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-md">
|
||||||
|
<div className="p-5 border-b border-slate-100 flex items-center justify-between">
|
||||||
|
<h2 className="text-lg font-bold text-slate-800">Bank Details</h2>
|
||||||
|
<button onClick={() => setShowBankModal(false)} className="p-2 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-600 mb-1 block">Bank Name</label>
|
||||||
|
<input type="text" value={editingBank.bankName} onChange={(e) => setEditingBank({ ...editingBank, bankName: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="e.g., Standard Chartered Bank" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className="text-sm font-medium text-slate-600 mb-1 block">Branch</label>
|
||||||
|
<input type="text" value={editingBank.bankBranch} onChange={(e) => setEditingBank({ ...editingBank, bankBranch: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="e.g., Gulshan Branch" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className="text-sm font-medium text-slate-600 mb-1 block">Account Name</label>
|
||||||
|
<input type="text" value={editingBank.bankAccountName} onChange={(e) => setEditingBank({ ...editingBank, bankAccountName: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="Account holder name" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className="text-sm font-medium text-slate-600 mb-1 block">Account Number</label>
|
||||||
|
<input type="text" value={editingBank.bankAccountNumber} onChange={(e) => setEditingBank({ ...editingBank, bankAccountNumber: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="Account number" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className="text-sm font-medium text-slate-600 mb-1 block">Routing Number</label>
|
||||||
|
<input type="text" value={editingBank.bankRouting} onChange={(e) => setEditingBank({ ...editingBank, bankRouting: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="Routing number" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="p-5 border-t border-slate-100 flex justify-between">
|
||||||
|
{investor.bankName && (
|
||||||
|
<button onClick={() => { alert('Bank details deleted'); setShowBankModal(false); }} className="px-4 py-2 border border-red-200 text-red-600 rounded-lg text-sm hover:bg-red-50">Delete</button>
|
||||||
|
)}
|
||||||
|
<div className="flex gap-2 ml-auto">
|
||||||
|
<button onClick={() => setShowBankModal(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={() => { alert('Bank details saved!'); setShowBankModal(false); }} className="px-4 py-2 bg-investor text-white rounded-lg text-sm hover:bg-investor-dark">Save</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{showMobileBankingModal && (
|
||||||
|
<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-md">
|
||||||
|
<div className="p-5 border-b border-slate-100 flex items-center justify-between">
|
||||||
|
<h2 className="text-lg font-bold text-slate-800">{editingMobileIndex !== null ? 'Edit' : 'Add'} Mobile Banking</h2>
|
||||||
|
<button onClick={() => setShowMobileBankingModal(false)} className="p-2 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-600 mb-1 block">Provider</label>
|
||||||
|
<select value={editingMobileBanking.provider} onChange={(e) => setEditingMobileBanking({ ...editingMobileBanking, provider: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm">
|
||||||
|
<option value="">Select Provider</option>
|
||||||
|
<option value="Bkash">Bkash</option>
|
||||||
|
<option value="Nagad">Nagad</option>
|
||||||
|
<option value="Rocket">Rocket</option>
|
||||||
|
<option value="Upay">Upay</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className="text-sm font-medium text-slate-600 mb-1 block">Mobile Number</label>
|
||||||
|
<input type="text" value={editingMobileBanking.number} onChange={(e) => setEditingMobileBanking({ ...editingMobileBanking, number: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="01XXXXXXXXX" />
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<input type="checkbox" checked={editingMobileBanking.isPrimary} onChange={(e) => setEditingMobileBanking({ ...editingMobileBanking, isPrimary: e.target.checked })} className="rounded text-investor" />
|
||||||
|
<span className="text-sm text-slate-600">Set as primary account</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="p-5 border-t border-slate-100 flex justify-end gap-3">
|
||||||
|
<button onClick={() => setShowMobileBankingModal(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={() => { alert('Mobile banking saved!'); setShowMobileBankingModal(false); }} className="px-4 py-2 bg-investor text-white rounded-lg text-sm hover:bg-investor-dark">Save</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{showTaxModal && (
|
||||||
|
<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-md">
|
||||||
|
<div className="p-5 border-b border-slate-100 flex items-center justify-between">
|
||||||
|
<h2 className="text-lg font-bold text-slate-800">Tax Information</h2>
|
||||||
|
<button onClick={() => setShowTaxModal(false)} className="p-2 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-600 mb-1 block">TIN Number</label>
|
||||||
|
<input type="text" value={editingTax.tinNumber} onChange={(e) => setEditingTax({ ...editingTax, tinNumber: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="TIN Number" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className="text-sm font-medium text-slate-600 mb-1 block">Passport Number</label>
|
||||||
|
<input type="text" value={editingTax.passportNumber} onChange={(e) => setEditingTax({ ...editingTax, passportNumber: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="Passport Number" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="p-5 border-t border-slate-100 flex justify-between">
|
||||||
|
{(investor.tinNumber || investor.passportNumber) && (
|
||||||
|
<button onClick={() => { alert('Tax info deleted'); setShowTaxModal(false); }} className="px-4 py-2 border border-red-200 text-red-600 rounded-lg text-sm hover:bg-red-50">Delete All</button>
|
||||||
|
)}
|
||||||
|
<div className="flex gap-2 ml-auto">
|
||||||
|
<button onClick={() => setShowTaxModal(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={() => { alert('Tax info saved!'); setShowTaxModal(false); }} className="px-4 py-2 bg-investor text-white rounded-lg text-sm hover:bg-investor-dark">Save</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{showDocModal && (
|
||||||
|
<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-md">
|
||||||
|
<div className="p-5 border-b border-slate-100 flex items-center justify-between">
|
||||||
|
<h2 className="text-lg font-bold text-slate-800">Upload Document</h2>
|
||||||
|
<button onClick={() => setShowDocModal(false)} className="p-2 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-600 mb-1 block">Document Type</label>
|
||||||
|
<select value={newDoc.type} onChange={(e) => setNewDoc({ ...newDoc, type: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm">
|
||||||
|
<option value="nid">NID Card</option>
|
||||||
|
<option value="passport">Passport</option>
|
||||||
|
<option value="bank_statement">Bank Statement</option>
|
||||||
|
<option value="tin_certificate">TIN Certificate</option>
|
||||||
|
<option value="trade_license">Trade License</option>
|
||||||
|
<option value="other">Other</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className="text-sm font-medium text-slate-600 mb-1 block">Document Number</label>
|
||||||
|
<input type="text" value={newDoc.number} onChange={(e) => setNewDoc({ ...newDoc, number: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="Document number (optional)" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className="text-sm font-medium text-slate-600 mb-1 block">Upload File</label>
|
||||||
|
<div className="border-2 border-dashed border-slate-200 rounded-lg p-6 text-center">
|
||||||
|
<FileText className="w-10 h-10 text-slate-300 mx-auto mb-2" />
|
||||||
|
<p className="text-sm text-slate-500 mb-2">Click to upload or drag and drop</p>
|
||||||
|
<p className="text-xs text-slate-400">PNG, JPG, PDF up to 10MB</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="p-5 border-t border-slate-100 flex justify-end gap-3">
|
||||||
|
<button onClick={() => setShowDocModal(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={() => { alert('Document uploaded successfully!'); setShowDocModal(false); }} className="px-4 py-2 bg-investor text-white rounded-lg text-sm hover:bg-investor-dark">Upload</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user