diff --git a/src/app/admin/investors/[id]/page copy.tsx b/src/app/admin/investors/[id]/page copy.tsx new file mode 100644 index 0000000..a8208b2 --- /dev/null +++ b/src/app/admin/investors/[id]/page copy.tsx @@ -0,0 +1,1542 @@ +'use client'; + +import { useState } from 'react'; +import Link from 'next/link'; +import { useParams } from 'next/navigation'; +import { investors as initialInvestors, bikes as initialBikes, transactions as initialTransactions } from '@/data/mockData'; +import type { Investor } from '@/data/mockData'; +import { + ArrowLeft, Wallet, TrendingUp, Banknote, Calendar, Phone, Mail, MapPin, Edit, Trash2, Plus, X, Bike, + User, FileText, CreditCard, DollarSign, Clock, ChevronDown, ExternalLink, Download, Upload, + AlertTriangle, Shield, Star, CheckCircle, XCircle, Search, Filter, BookOpen, ArrowRight, Printer +} from 'lucide-react'; + +const statusColors: Record = { + active: 'bg-green-100 text-green-700', + pending: 'bg-amber-100 text-amber-700', + inactive: 'bg-slate-100 text-slate-500', + suspended: 'bg-red-100 text-red-700', +}; + +const planColors: Record = { + silver: 'bg-slate-200 text-slate-700', + gold: 'bg-yellow-100 text-yellow-700', + platinum: 'bg-purple-100 text-purple-700', + diamond: 'bg-blue-100 text-blue-700', +}; + +const kycColors: Record = { + verified: 'bg-green-100 text-green-700', + pending: 'bg-amber-100 text-amber-700', + rejected: 'bg-red-100 text-red-700', + not_submitted: 'bg-slate-100 text-slate-500', +}; + +const bikeStatusColors: Record = { + available: 'bg-blue-100 text-blue-700', + rented: 'bg-green-100 text-green-700', + maintenance: 'bg-amber-100 text-amber-700', + retired: 'bg-slate-100 text-slate-500', +}; + +export default function InvestorDetailPage() { + const params = useParams(); + const investorId = params.id as string; + + const [investors] = useState(initialInvestors); + const investor = investors.find(i => i.id === investorId); + + const assignedBikes = initialBikes.filter(b => b.investorId === investorId); + // Investor transactions are filtered below + + const [activeTab, setActiveTab] = useState('overview'); + const [showEditModal, setShowEditModal] = useState(false); + const [showAssignBikeModal, setShowAssignBikeModal] = useState(false); + const [selectedBikeId, setSelectedBikeId] = useState(''); + const [showCreateInvestmentModal, setShowCreateInvestmentModal] = useState(false); + const [showInvoiceModal, setShowInvoiceModal] = useState(false); + const [showJournalModal, setShowJournalModal] = useState(false); + const [selectedInvoice, setSelectedInvoice] = useState(null); + const [investorJournals, setInvestorJournals] = useState([]); + 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(null); + const investorTransactions = initialTransactions.filter(t => t.investorId === investorId); + const [newInvestment, setNewInvestment] = useState({ + planName: '', + planType: 'gold' as 'silver' | 'gold' | 'platinum' | 'diamond', + selectedBikeIds: [] as string[], + totalInvestment: 0, + monthlyReturn: 0, + expectedRoi: 15, + startDate: new Date().toISOString().split('T')[0], + endDate: '', + paymentMethod: 'bank' as 'bank' | 'mobile' | 'cash' | 'cheque', + transactionReference: '', + notes: '' + }); + + if (!investor) { + return ( +
+
+

Investor Not Found

+

The investor you're looking for doesn't exist.

+ + Back to Investors + +
+
+ ); + } + + const availableBikesForAssignment = initialBikes.filter(b => !b.investorId && b.status === 'available'); + + const handleAssignBike = () => { + alert('Bike assignment functionality - would update bike investorId here'); + setShowAssignBikeModal(false); + setSelectedBikeId(''); + }; + + const handleCreateInvestment = () => { + const invId = `INV-${Date.now()}`; + const year = new Date().getFullYear(); + const transactionRef = newInvestment.transactionReference || `INV/${year}/${String(investor.investments.length + 1).padStart(4, '0')}`; + + const getDebitAccount = (method: string) => { + switch (method) { + case 'bank': return { code: '1200', name: 'Bank - City Bank' }; + case 'cash': return { code: '1100', name: 'Cash in Hand' }; + case 'mobile': return { code: '1300', name: 'bKash Business' }; + case 'cheque': return { code: '1410', name: 'Cheque Receivable' }; + default: return { code: '1200', name: 'Bank - City Bank' }; + } + }; + + const debitAccount = getDebitAccount(newInvestment.paymentMethod); + + const journalEntry = { + entryId: `JE-${Date.now()}`, + date: newInvestment.startDate, + reference: transactionRef, + description: `${investor.name} - ${newInvestment.planName}`, + entries: [ + { accountCode: debitAccount.code, accountName: debitAccount.name, debit: newInvestment.totalInvestment, credit: 0 }, + { accountCode: '2200', accountName: 'Investor Liabilities', debit: 0, credit: newInvestment.totalInvestment }, + ], + isAuto: true, + sourceType: 'investor_funding', + createdAt: new Date().toISOString(), + type: 'investment', + amount: newInvestment.totalInvestment, + paymentMethod: newInvestment.paymentMethod + }; + + setInvestorJournals([journalEntry, ...investorJournals]); + + console.log('Creating Investment:', { + id: invId, + investorId: investor.id, + ...newInvestment, + actualEarnings: 0, + status: 'active' as const, + transactionId: transactionRef, + createdAt: new Date().toISOString() + }); + + alert(`Investment created successfully! + +Investor: ${investor.name} +Investment ID: ${invId} +Transaction Ref: ${transactionRef} +Amount: ৳${newInvestment.totalInvestment.toLocaleString()} + +Accounting Entry Created (Auto-Journal): +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +Date: ${newInvestment.startDate} +Ref: ${transactionRef} +Description: ${investor.name} - ${newInvestment.planName} + +Debit (Dr): ${debitAccount.name} ৳${newInvestment.totalInvestment.toLocaleString()} +Credit (Cr): Investor Liability ৳${newInvestment.totalInvestment.toLocaleString()} +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`); + setShowCreateInvestmentModal(false); + setNewInvestment({ + planName: '', + planType: 'gold', + selectedBikeIds: [], + totalInvestment: 0, + monthlyReturn: 0, + expectedRoi: 15, + startDate: new Date().toISOString().split('T')[0], + endDate: '', + paymentMethod: 'bank', + transactionReference: '', + notes: '' + }); + }; + + return ( +
+
+
+ + + +
+

{investor.name}

+

{investor.id} • {investor.email}

+
+
+
+ + +
+
+ +
+ + {investor.status} + + {investor.investments && investor.investments.length > 0 && ( + + {investor.investments.length} Investment{investor.investments.length > 1 ? 's' : ''} + + )} + + KYC {investor.kycStatus} + + + Risk: {investor.riskLevel} + +
+ +
+
+
+
+ +
+
+

৳{investor.totalInvested.toLocaleString()}

+

Total Invested

+
+
+
+
+
+
+ +
+
+

৳{investor.totalEarnings.toLocaleString()}

+

Total Earnings

+
+
+
+
+
+
+ +
+
+

{investor.activeBikes}

+

Active Bikes

+
+
+
+
+
+
+ +
+
+

{investor.roi}%

+

ROI

+
+
+
+
+ +
+
+ + + + + + +
+ +
+ {activeTab === 'overview' && ( +
+
+
+

Personal Information

+
+
+ +
+

Full Name

+

{investor.name}

+
+
+
+ +
+

Phone

+

{investor.phone}

+
+
+
+ +
+

Email

+

{investor.email}

+
+
+
+ +
+

Address

+

{investor.address}

+
+
+ {investor.dateOfBirth && ( +
+ +
+

Date of Birth

+

{investor.dateOfBirth}

+
+
+ )} + {investor.nidNumber && ( +
+ +
+

NID Number

+

{investor.nidNumber}

+
+
+ )} +
+
+ +
+

Emergency Contact

+
+ {investor.emergencyContactName && ( +
+ +
+

Contact

+

{investor.emergencyContactName}

+

{investor.emergencyContactRelation} • {investor.emergencyContactPhone}

+
+
+ )} +
+ +

Investment Details

+
+
+ +
+

Total Investments

+

+ {investor.investments?.length || 0} active investments +

+
+
+
+ +
+

Overall Status

+

{investor.status}

+
+
+ {investor.referralCode && ( +
+ +
+

Referral Code

+

{investor.referralCode}

+

Referrals: {investor.totalReferrals} • Earnings: ৳{investor.referralEarnings}

+
+
+ )} +
+
+
+ + {investor.notes && ( +
+

Notes

+

{investor.notes}

+
+ )} +
+ )} + + {activeTab === 'bikes' && ( +
+
+

Assigned Bikes

+ +
+
+ {assignedBikes.map(bike => ( +
+
+
+
+ +
+
+

{bike.model}

+

{bike.brand}

+
+
+ + {bike.status} + +
+
+
+ Plate + {bike.plateNumber} +
+
+ Location + {bike.location} +
+
+ Battery + 50 ? 'text-green-600' : bike.batteryLevel > 20 ? 'text-amber-600' : 'text-red-600'}`}>{bike.batteryLevel}% +
+
+ Purchase Price + ৳{bike.purchasePrice?.toLocaleString() || 0} +
+
+ Total Earnings + ৳{bike.totalEarnings?.toLocaleString() || 0} +
+
+
+ ))} + {assignedBikes.length === 0 && ( +
+ +

No bikes assigned yet

+
+ )} +
+
+ )} + + {activeTab === 'investments' && ( +
+
+

Investment Plans

+ +
+
+ {investor.investments?.map((inv) => ( +
+
+
+

{inv.planName}

+

{inv.planType} Plan

+
+ + {inv.status} + +
+
+
+

Investment

+

৳{inv.totalInvestment.toLocaleString()}

+
+
+

Monthly Return

+

৳{inv.monthlyReturn.toLocaleString()}

+
+
+

Expected ROI

+

{inv.expectedRoi}%

+
+
+

Actual Earned

+

৳{inv.actualEarnings.toLocaleString()}

+
+
+
+ {inv.startDate} to {inv.endDate || 'Ongoing'} + {inv.paymentMethod} +
+
+ ))} + {(!investor.investments || investor.investments.length === 0) && ( +
+ +

No investments yet

+ +
+ )} +
+
+ )} + + {activeTab === 'financial' && ( +
+
+
+
+

Bank Details

+ +
+
+ {investor.bankName ? ( + <> +
+ +
+

Bank

+

{investor.bankName}

+

{investor.bankBranch}

+
+
+
+ +
+

Account

+

{investor.bankAccountName}

+

{investor.bankAccountNumber}

+ {investor.bankRouting &&

Routing: {investor.bankRouting}

} +
+
+ + ) : ( +
+ +

No bank details added

+
+ )} +
+
+ +
+
+

Mobile Banking

+ +
+
+ {investor.mobileBanking ? ( +
+ +
+

Primary

+

{investor.mobileBanking}

+

{investor.mobileBankingNumber}

+
+ +
+ ) : null} + {investor.additionalMobileBanking?.map((mb, idx) => ( +
+ +
+

{mb.provider}

+

{mb.number}

+
+ {mb.verified ? ( + Verified + ) : ( + Pending + )} + + +
+ ))} + {(!investor.mobileBanking && (!investor.additionalMobileBanking || investor.additionalMobileBanking.length === 0)) && ( +
+ +

No mobile banking added

+
+ )} +
+ +
+

Tax Information

+ +
+
+ {investor.tinNumber || investor.passportNumber ? ( + <> + {investor.tinNumber && ( +
+ +
+

TIN

+

{investor.tinNumber}

+
+
+ )} + {investor.passportNumber && ( +
+ +
+

Passport

+

{investor.passportNumber}

+
+
+ )} + + ) : ( +
+ +

No tax info added

+
+ )} +
+
+
+ +
+

Investment Summary

+
+
+

Total Invested

+

৳{investor.totalInvested.toLocaleString()}

+
+
+

Total Earnings

+

৳{investor.totalEarnings.toLocaleString()}

+
+
+

Pending Earnings

+

৳{investor.pendingEarnings.toLocaleString()}

+
+
+

Total Withdrawn

+

৳{investor.totalWithdrawn.toLocaleString()}

+
+
+
+
+ )} + + {activeTab === 'transactions' && ( +
+
+

All Transactions

+
+ + +
+
+ +
+

Pending Withdrawals

+
+ {investorTransactions.filter(t => t.type === 'withdrawal' && t.status === 'pending').map(tx => ( +
+
+
+ +
+
+

{tx.description}

+

Ref: {tx.referenceNumber || tx.id} • {tx.createdAt}

+
+
+
+

-৳{tx.amount.toLocaleString()}

+ + Pending + +
+
+ ))} + {investorTransactions.filter(t => t.type === 'withdrawal' && t.status === 'pending').length === 0 && ( +
+

No pending withdrawals

+
+ )} +
+
+ +
+

Transaction History

+
+ {investorTransactions.map(tx => ( +
+
+
+ +
+
+

{tx.description}

+
+

{tx.createdAt}

+ {tx.referenceNumber && ( + + )} +
+
+
+
+

+ {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()} +

+ + {tx.status} + +
+
+ ))} + {investorTransactions.length === 0 && ( +
+ +

No transactions yet

+
+ )} +
+
+
+ )} + + {activeTab === 'documents' && ( +
+
+

KYC Documents

+ +
+ +
+ {investor.kycDocuments?.map((doc, idx) => ( +
+
+
+ +
+
+

{doc.type.replace('_', ' ')}

+

{doc.number || 'No document number'} • Uploaded: {doc.uploadedAt || 'N/A'}

+
+
+
+ {doc.verified ? ( + + Verified + + ) : ( + + Pending Review + + )} + + +
+
+ ))} + {(!investor.kycDocuments || investor.kycDocuments.length === 0) && ( +
+ +

No documents uploaded yet

+
+ )} +
+ +
+

KYC Status: {investor.kycStatus.toUpperCase()}

+

+ {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.'} +

+
+
+ )} +
+
+ + {showAssignBikeModal && ( +
+
+
+

Assign Bike to Investor

+ +
+ +
+ + +
+ +
+ + +
+
+
+ )} + + {showCreateInvestmentModal && ( +
+
+
+

Create New Investment

+ +
+ +
+
+
+ + setNewInvestment({ ...newInvestment, planName: e.target.value })} + className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" + placeholder="e.g., Gold EV Fleet 2024" + /> +
+
+ + +
+
+ +
+ +
+ {availableBikesForAssignment.map(bike => ( + + ))} +
+
+ +
+
+ + setNewInvestment({ ...newInvestment, totalInvestment: Number(e.target.value), monthlyReturn: Math.round(Number(e.target.value) * newInvestment.expectedRoi / 100 / 12) })} + className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" + placeholder="0" + /> +
+
+ + setNewInvestment({ ...newInvestment, monthlyReturn: Number(e.target.value) })} + className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" + placeholder="0" + /> +
+
+ + setNewInvestment({ ...newInvestment, expectedRoi: Number(e.target.value) })} + className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" + placeholder="15" + /> +
+
+ +
+
+ + setNewInvestment({ ...newInvestment, startDate: e.target.value })} + className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" + /> +
+
+ + setNewInvestment({ ...newInvestment, endDate: e.target.value })} + className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" + /> +
+
+ +
+
+ + +
+
+ + setNewInvestment({ ...newInvestment, transactionReference: e.target.value })} + className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" + placeholder="Auto-generated if empty" + /> +
+
+ +
+ +