'use client'; import { useState, useEffect } from 'react'; import Link from 'next/link'; import { useParams, useRouter, useSearchParams } from 'next/navigation'; import { investors as initialInvestors, bikes as initialBikes, transactions as initialTransactions, rentalPayments as initialRentalPayments } from '@/data/mockData'; import type { Investor } from '@/data/mockData'; import toast from 'react-hot-toast'; import { ArrowLeft, Wallet, TrendingUp, Banknote, Calendar, Phone, Mail, MapPin, Edit, Trash2, Plus, X, Bike, Battery, Unlink, Eye, ShieldAlert, Cpu, User, FileText, CreditCard, DollarSign, Clock, ChevronDown, ExternalLink, Download, Upload, AlertTriangle, Shield, Star, CheckCircle, XCircle, Search, Filter, BookOpen, ArrowRight, Printer, UserCircle, Home, Briefcase, Heart, PhoneCall, PhoneOutgoing, MessageSquare, Save, ShieldCheck, Building2, Users, Check, AlertOctagon, Activity, Award, Camera, History, Settings } from 'lucide-react'; import AssignBikeModal from '../components/AssignBikeModal'; import AssignBatteryModal from '../components/AssignBatteryModal'; import UnassignConfirmModal from '../components/UnassignConfirmModal'; 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', }; function SectionCard({ title, icon: Icon, children, headerBg = 'bg-slate-50', headerBorder = 'border-slate-100', editKey, editingSection, setEditingSection, onEdit, editForm, setEditForm }: { title: string; icon: any; children: React.ReactNode; headerBg?: string; headerBorder?: string; editKey?: string; editingSection?: string | null; setEditingSection?: (s: string | null) => void; onEdit?: () => void; editForm?: any; setEditForm?: any }) { return (

{title}

{editKey && setEditingSection ? ( editingSection !== editKey ? ( ) : (
) ) : null}
{children}
); } export default function InvestorDetailPage() { const params = useParams(); const router = useRouter(); const searchParams = useSearchParams(); const investorId = params.id as string; const [activeTab, setActiveTab] = useState('overview'); useEffect(() => { const tab = searchParams?.get('tab'); if (tab) { setActiveTab(tab); } }, [searchParams]); const [investors, setInvestors] = useState(() => { if (typeof window !== 'undefined') { const stored = localStorage.getItem('jaiben_investors'); if (stored) { try { return JSON.parse(stored); } catch (e) { console.error(e); } } } return initialInvestors; }); const investor = investors.find(i => i.id === investorId); useEffect(() => { if (typeof window !== 'undefined') { localStorage.setItem('jaiben_investors', JSON.stringify(investors)); } }, [investors]); const [bikes, setBikes] = useState(() => { if (typeof window !== 'undefined') { const stored = localStorage.getItem('jaiben_bikes'); if (stored) { try { return JSON.parse(stored); } catch (e) { console.error(e); } } } return initialBikes; }); useEffect(() => { if (typeof window !== 'undefined') { localStorage.setItem('jaiben_bikes', JSON.stringify(bikes)); } }, [bikes]); const assignedBikes = bikes.filter(b => b.investorId === investorId); const [settings, setSettings] = useState(null); useEffect(() => { if (typeof window !== 'undefined') { const stored = localStorage.getItem('companySettings'); if (stored) { try { setSettings(JSON.parse(stored)); } catch (e) { console.error(e); } } } }, []); // Investor transactions are filtered below const [showEditModal, setShowEditModal] = useState(false); const [showAssignBikeModal, setShowAssignBikeModal] = useState(false); const [selectedBikeId, setSelectedBikeId] = useState(''); const [selectedBikeIds, setSelectedBikeIds] = useState([]); const [selectedBikePlanId, setSelectedBikePlanId] = useState(''); const getPlanTargetAssetCount = (plan: any) => { if (plan.assetType === 'battery' || plan.planName?.toLowerCase().includes('battery')) { if (plan.id === 'ip3') return 2; const nameLower = plan.planName?.toLowerCase() || ''; if (nameLower.includes('10')) return 10; if (nameLower.includes('5')) return 5; if (nameLower.includes('1')) return 1; return 1; } else { if (plan.id === 'ip1') return 1; if (plan.id === 'ip2') return 1; const nameLower = plan.planName?.toLowerCase() || ''; if (nameLower.includes('10')) return 10; if (nameLower.includes('5')) return 5; if (nameLower.includes('1')) return 1; return 1; } }; const [showRegisterBikeModal, setShowRegisterBikeModal] = useState(false); const [unassignConfirmModal, setUnassignConfirmModal] = useState<{ show: boolean; type: 'bike' | 'battery'; id: string; name: string; details: string; }>({ show: false, type: 'bike', id: '', name: '', details: '' }); const [registerBikeForm, setRegisterBikeForm] = useState({ plateNumber: '', brand: 'Etron', model: 'ET50', currentRent: 150, location: 'Banani', purchasePrice: 200000, rentalType: 'single_rent', investmentId: '' }); const [showCreateInvestmentModal, setShowCreateInvestmentModal] = useState(false); const [showInvestmentSuccessModal, setShowInvestmentSuccessModal] = useState(false); const [lastCreatedInvestment, setLastCreatedInvestment] = useState(null); 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 [editingSection, setEditingSection] = useState(null); const [editForm, setEditForm] = useState({}); const [showMobileBankingModal, setShowMobileBankingModal] = useState(false); const [showTaxModal, setShowTaxModal] = useState(false); const [showDocModal, setShowDocModal] = useState(false); const [editingBankAccount, setEditingBankAccount] = useState({ id: '', bankName: '', accountName: '', accountNumber: '', branch: '', routing: '', isPrimary: false }); const [bankSaveSuccess, setBankSaveSuccess] = useState(false); const [showDeleteBankModal, setShowDeleteBankModal] = useState(false); const [bankErrors, setBankErrors] = useState<{ bankName?: string; accountName?: string; accountNumber?: string }>({}); 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 [batteries, setBatteries] = useState(() => { if (typeof window !== 'undefined') { const stored = localStorage.getItem('jaiben_batteries'); if (stored) { try { return JSON.parse(stored); } catch (e) { console.error(e); } } } return [ { id: 'BAT-001', serialNumber: 'SN-2024-00001', brand: 'EVE Energy', model: 'Li-Ion 60V50Ah', type: 'lithium-ion', capacity: 50, voltage: 60, purchaseDate: '2024-01-15', purchasePrice: 45000, deposit: 5000, rentPrice: 150, investorId: investorId, investorName: 'Md. Hasan Mahmud', investorSharePercentage: 100, investedAmount: 45000, investmentId: 'ip3', status: 'in-use', currentSoc: 78, health: 95, cycleCount: 156 }, { id: 'BAT-002', serialNumber: 'SN-2024-00002', brand: 'CATL', model: 'LiFePO4 48V40Ah', type: 'lifepo4', capacity: 40, voltage: 48, purchaseDate: '2024-02-10', purchasePrice: 38000, deposit: 4000, rentPrice: 120, investorId: investorId, investorName: 'Md. Hasan Mahmud', investorSharePercentage: 100, investedAmount: 38000, investmentId: 'ip3', status: 'available', currentSoc: 92, health: 98, cycleCount: 45 }, { id: 'BAT-005', serialNumber: 'SN-2024-00005', brand: 'BYD', model: 'Li-Ion 60V50Ah', type: 'lithium-ion', capacity: 50, voltage: 60, purchaseDate: '2024-02-15', purchasePrice: 45000, deposit: 5000, rentPrice: 150, investorId: investorId, investorName: 'Md. Hasan Mahmud', investorSharePercentage: 100, investedAmount: 45000, investmentId: 'ip3', status: 'in-use', currentSoc: 82, health: 97, cycleCount: 18 } ]; }); const [unassignedBatteries, setUnassignedBatteries] = useState(() => { if (typeof window !== 'undefined') { const stored = localStorage.getItem('jaiben_unassigned_batteries'); if (stored) { try { return JSON.parse(stored); } catch (e) { console.error(e); } } } return [ { id: 'BAT-003', serialNumber: 'SN-2024-00003', brand: 'BYD', model: 'Li-Ion 72V60Ah', type: 'lithium-ion', capacity: 60, voltage: 72, purchasePrice: 52000, deposit: 6000, rentPrice: 180, status: 'available', currentSoc: 85, health: 97, cycleCount: 12 }, { id: 'BAT-004', serialNumber: 'SN-2024-00004', brand: 'Panasonic', model: 'Li-Ion 60V40Ah', type: 'lithium-ion', capacity: 40, voltage: 60, purchasePrice: 41000, deposit: 4500, rentPrice: 130, status: 'available', currentSoc: 90, health: 99, cycleCount: 8 } ]; }); useEffect(() => { if (typeof window !== 'undefined') { localStorage.setItem('jaiben_batteries', JSON.stringify(batteries)); } }, [batteries]); useEffect(() => { if (typeof window !== 'undefined') { localStorage.setItem('jaiben_unassigned_batteries', JSON.stringify(unassignedBatteries)); } }, [unassignedBatteries]); // Patch local storage state to make sure inv1 has the Standard Battery Plan (ip3) and correct assigned batteries useEffect(() => { if (!investor) return; const hasIp3 = investor.investments?.some(inv => inv.id === 'ip3'); if (!hasIp3) { const updated = investors.map(i => { if (i.id === investorId) { const investments = i.investments || []; return { ...i, totalInvested: 300000, investments: [ ...investments.filter(inv => inv.id !== 'ip3'), { id: 'ip3', investorId: investorId, planName: 'Standard Battery Plan', planType: 'silver' as const, assetType: 'battery' as const, batteryIds: ['BAT-001', 'BAT-002', 'BAT-005'], totalInvestment: 150000, monthlyReturn: 4500, expectedRoi: 16, actualEarnings: 9000, startDate: '2024-02-01', endDate: '2025-02-01', status: 'active' as const, paymentMethod: 'bank' as const, transactionId: 'invt3', createdAt: '2024-02-01' } ] }; } return i; }); setInvestors(updated); } }, [investors, investorId, investor]); useEffect(() => { const hasBat5 = batteries.some(b => b.id === 'BAT-005'); const hasIp3Assignment = batteries.some(b => b.id === 'BAT-001' && b.investmentId === 'ip3'); if (!hasBat5 || !hasIp3Assignment) { const updated = [ { id: 'BAT-001', serialNumber: 'SN-2024-00001', brand: 'EVE Energy', model: 'Li-Ion 60V50Ah', type: 'lithium-ion', capacity: 50, voltage: 60, purchaseDate: '2024-01-15', purchasePrice: 45000, deposit: 5000, rentPrice: 150, investorId: investorId, investorName: 'Md. Hasan Mahmud', investorSharePercentage: 100, investedAmount: 45000, investmentId: 'ip3', status: 'in-use', currentSoc: 78, health: 95, cycleCount: 156 }, { id: 'BAT-002', serialNumber: 'SN-2024-00002', brand: 'CATL', model: 'LiFePO4 48V40Ah', type: 'lifepo4', capacity: 40, voltage: 48, purchaseDate: '2024-02-10', purchasePrice: 38000, deposit: 4000, rentPrice: 120, investorId: investorId, investorName: 'Md. Hasan Mahmud', investorSharePercentage: 100, investedAmount: 38000, investmentId: 'ip3', status: 'available', currentSoc: 92, health: 98, cycleCount: 45 }, { id: 'BAT-005', serialNumber: 'SN-2024-00005', brand: 'BYD', model: 'Li-Ion 60V50Ah', type: 'lithium-ion', capacity: 50, voltage: 60, purchaseDate: '2024-02-15', purchasePrice: 45000, deposit: 5000, rentPrice: 150, investorId: investorId, investorName: 'Md. Hasan Mahmud', investorSharePercentage: 100, investedAmount: 45000, investmentId: 'ip3', status: 'in-use', currentSoc: 82, health: 97, cycleCount: 18 }, ...batteries.filter(b => b.id !== 'BAT-001' && b.id !== 'BAT-002' && b.id !== 'BAT-005') ]; setBatteries(updated); } }, [batteries, investorId]); const [showAssignBatteryModal, setShowAssignBatteryModal] = useState(false); const [selectedBatteryIds, setSelectedBatteryIds] = useState([]); const [selectedBatteryPlanId, setSelectedBatteryPlanId] = useState(''); const [showRegisterBatteryModal, setShowRegisterBatteryModal] = useState(false); const [showEditBatteryModal, setShowEditBatteryModal] = useState(false); const [selectedBatteryToAssign, setSelectedBatteryToAssign] = useState(null); const [selectedBatteryToEdit, setSelectedBatteryToEdit] = useState(null); const [assignForm, setAssignForm] = useState({ batteryId: '', investmentId: '', deposit: 5000, rentPrice: 150, investorShare: 60, investedAmount: 45000 }); const [registerForm, setRegisterForm] = useState({ serialNumber: '', brand: 'BYD', model: 'Li-Ion 60V50Ah', type: 'lithium-ion', capacity: 50, voltage: 60, purchasePrice: 45000, deposit: 5000, rentPrice: 150, investorShare: 100, investedAmount: 45000, investmentId: '' }); const investorTransactions = initialTransactions.filter(t => t.investorId === investorId); const investorRentalPayments = initialRentalPayments.filter(p => p.investorId === investorId).sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()); const [rentalPage, setRentalPage] = useState(1); const [rentalPageSize] = useState(10); const [rentalSortBy, setRentalSortBy] = useState('date'); const [rentalSortOrder, setRentalSortOrder] = useState<'asc' | 'desc'>('desc'); const [showWithdrawalModal, setShowWithdrawalModal] = useState(false); const [showAutoWithdrawModal, setShowAutoWithdrawModal] = useState(false); const [withdrawSelection, setWithdrawSelection] = useState({ selectAll: true, selectedPlans: [] as string[], selectedBikes: [] as string[], amount: 0, paymentMethod: '', accountId: '' }); const [autoWithdrawSettings, setAutoWithdrawSettings] = useState({ enabled: false, frequency: 'as_per_request' as 'as_per_request' | 'weekly' | 'monthly', minAmount: 1000, accountId: '' }); const [newInvestment, setNewInvestment] = useState({ planName: '', planType: 'gold' as 'silver' | 'gold' | 'platinum' | 'diamond', assetType: 'bike' as 'bike' | 'battery', selectedBikeIds: [] as string[], selectedBatteryIds: [] as string[], totalInvestment: 0, paidAmount: 0, paymentType: 'full' as 'full' | 'partial', monthlyReturn: 0, expectedRoi: 15, startDate: new Date().toISOString().split('T')[0], endDate: '', paymentMethod: 'bank' as 'bank' | 'mobile' | 'cash' | 'cheque', transactionReference: '', notes: '' }); const [showAddPaymentModal, setShowAddPaymentModal] = useState(false); const [newPayment, setNewPayment] = useState({ amount: 0, paymentMethod: 'bank' as 'bank' | 'mobile' | 'cash' | 'cheque', transactionRef: '', date: new Date().toISOString().split('T')[0], notes: '' }); const [investmentPayments, setInvestmentPayments] = useState([ { id: 'pay1', date: '2024-01-15', amount: 50000, paymentMethod: 'bank', transactionRef: 'TXN-001', status: 'completed', notes: 'First payment' } ]); if (!investor) { return (

Investor Not Found

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

Back to Investors
); } const availableBikesForAssignment = bikes.filter(b => !b.investorId && b.status === 'available'); const handleAssignBike = () => { if (!selectedBikePlanId) { toast.error('Please select an investment plan'); return; } if (selectedBikeIds.length === 0) { toast.error('Please select at least one bike'); return; } const assignedBikesList: any[] = []; setBikes(prev => prev.map(b => { if (selectedBikeIds.includes(b.id)) { assignedBikesList.push(b); return { ...b, investorId: investorId, investorName: investor.name, investmentId: selectedBikePlanId, status: 'rented', totalEarnings: b.totalEarnings || 0 }; } return b; })); // Update investor's investments to hold the bike IDs setInvestors(prev => prev.map(inv => { if (inv.id === investor.id) { return { ...inv, investments: inv.investments?.map((item: any) => { if (item.id === selectedBikePlanId) { const currentBikeIds = item.bikeIds || []; const uniqueNewBikeIds = Array.from(new Set([...currentBikeIds, ...selectedBikeIds])); return { ...item, bikeIds: uniqueNewBikeIds }; } return item; }) }; } return inv; })); const bikeNames = assignedBikesList.map(b => `${b.model} (${b.plateNumber})`).join(', '); toast.success(`Successfully assigned ${selectedBikeIds.length} bike(s): ${bikeNames}`); setShowAssignBikeModal(false); setSelectedBikeIds([]); setSelectedBikePlanId(''); }; const handleRegisterAndAssignBike = () => { if (!registerBikeForm.plateNumber) { toast.error('Please enter a plate number'); return; } const evInv = investor.investments?.find((inv: any) => inv.assetType === 'bike' || inv.planName.toLowerCase().includes('ev') || inv.planName.toLowerCase().includes('bike')) || investor.investments?.[0]; const newBike = { id: `BIKE-${Date.now()}`, plateNumber: registerBikeForm.plateNumber, brand: registerBikeForm.brand, model: registerBikeForm.model, currentRent: Number(registerBikeForm.currentRent), location: registerBikeForm.location, purchasePrice: Number(registerBikeForm.purchasePrice), rentalType: registerBikeForm.rentalType, investorId: investorId, investorName: investor.name, investmentId: registerBikeForm.investmentId || evInv?.id || 'ip1', status: 'rented', batteryLevel: 100, totalEarnings: 0 }; setBikes(prev => [...prev, newBike]); setShowRegisterBikeModal(false); toast.success(`New bike ${newBike.model} registered and assigned!`); }; const handleUnassignBike = (bikeId: string) => { setBikes(prev => prev.map(b => { if (b.id === bikeId) { return { ...b, investorId: null, investorName: null, investmentId: null, status: 'available' }; } return b; })); toast.success('Bike successfully unassigned from investor!'); }; 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.paidAmount, paymentMethod: newInvestment.paymentMethod }; const createdInv = { id: invId, investorId: investor.id, ...newInvestment, bikeIds: [], batteryIds: [], actualEarnings: 0, status: 'active' as const, transactionId: transactionRef, createdAt: new Date().toISOString(), debitAccount, journalEntry }; setInvestorJournals([journalEntry, ...investorJournals]); setLastCreatedInvestment(createdInv); setInvestors(prev => prev.map(inv => { if (inv.id === investor.id) { return { ...inv, investments: [...(inv.investments || []), createdInv], totalInvested: inv.totalInvested + newInvestment.totalInvestment }; } return inv; })); setShowCreateInvestmentModal(false); setShowInvestmentSuccessModal(true); setNewInvestment({ planName: '', planType: 'gold', assetType: 'bike', selectedBikeIds: [], selectedBatteryIds: [], totalInvestment: 0, paidAmount: 0, paymentType: 'full', monthlyReturn: 0, expectedRoi: 15, startDate: new Date().toISOString().split('T')[0], endDate: '', paymentMethod: 'bank', transactionReference: '', notes: '' }); }; const handleUnassignBattery = (batteryId: string) => { const batteryToUnassign = batteries.find(b => b.id === batteryId); if (!batteryToUnassign) return; setBatteries(prev => prev.filter(b => b.id !== batteryId)); setUnassignedBatteries(prev => [ ...prev, { ...batteryToUnassign, investorId: null, investorName: null, investorSharePercentage: null, investedAmount: null, investmentId: null, status: 'available' } ]); toast.success('Battery successfully unassigned from investor!'); }; const handleAssignBattery = () => { if (!selectedBatteryPlanId) { toast.error('Please select an investment plan'); return; } if (selectedBatteryIds.length === 0) { toast.error('Please select at least one battery'); return; } const assignedObjList: any[] = []; const chosenBatteries = unassignedBatteries.filter(b => selectedBatteryIds.includes(b.id)); chosenBatteries.forEach(batteryToAssign => { const assignedObj = { ...batteryToAssign, investorId: investorId, investorName: investor.name, investorSharePercentage: Number(assignForm.investorShare), investedAmount: Number(assignForm.investedAmount), investmentId: selectedBatteryPlanId, deposit: Number(assignForm.deposit), rentPrice: Number(assignForm.rentPrice), status: 'in-use' }; assignedObjList.push(assignedObj); }); setBatteries(prev => [...prev, ...assignedObjList]); setUnassignedBatteries(prev => prev.filter(b => !selectedBatteryIds.includes(b.id))); // Update investor's investments to hold the battery IDs setInvestors(prev => prev.map(inv => { if (inv.id === investor.id) { return { ...inv, investments: inv.investments?.map((item: any) => { if (item.id === selectedBatteryPlanId) { const currentBatteryIds = item.batteryIds || []; const uniqueNewBatteryIds = Array.from(new Set([...currentBatteryIds, ...selectedBatteryIds])); return { ...item, batteryIds: uniqueNewBatteryIds }; } return item; }) }; } return inv; })); const serialsList = chosenBatteries.map(b => b.serialNumber).join(', '); toast.success(`Successfully assigned ${selectedBatteryIds.length} battery pack(s): ${serialsList}`); setShowAssignBatteryModal(false); setSelectedBatteryIds([]); setSelectedBatteryPlanId(''); }; const handleRegisterAndAssignBattery = () => { if (!registerForm.serialNumber) { toast.error('Please enter a serial number'); return; } const newBat = { id: `BAT-${Date.now()}`, serialNumber: registerForm.serialNumber, brand: registerForm.brand, model: registerForm.model, type: registerForm.type, capacity: Number(registerForm.capacity), voltage: Number(registerForm.voltage), purchaseDate: new Date().toISOString().split('T')[0], purchasePrice: Number(registerForm.purchasePrice), deposit: Number(registerForm.deposit), rentPrice: Number(registerForm.rentPrice), investorId: investorId, investorName: investor.name, investorSharePercentage: Number(registerForm.investorShare), investedAmount: Number(registerForm.investedAmount), investmentId: registerForm.investmentId, status: 'in-use', currentSoc: 100, health: 100, cycleCount: 0 }; setBatteries(prev => [...prev, newBat]); setShowRegisterBatteryModal(false); toast.success('New battery registered and assigned!'); }; const handleSaveEditBattery = () => { if (!selectedBatteryToEdit) return; setBatteries(prev => prev.map(b => { if (b.id === selectedBatteryToEdit.id) { return { ...b, deposit: Number(assignForm.deposit), rentPrice: Number(assignForm.rentPrice), investorSharePercentage: Number(assignForm.investorShare), investedAmount: Number(assignForm.investedAmount), investmentId: assignForm.investmentId }; } return b; })); setShowEditBatteryModal(false); setSelectedBatteryToEdit(null); toast.success('Battery assignment updated successfully!'); }; const bikeTemplates = settings?.plans?.investment?.map((plan: any) => ({ id: plan.id, name: plan.name, tier: plan.tier || 'Standard', evBasePrice: plan.evBasePrice || 200000, minQuantity: plan.minQuantity || 1, duration: plan.durationMonths || 12, maxInvestment: plan.maxInvestment || 1000000, lockIn: plan.lockInMonths || 3, exitPenalty: plan.earlyExitPenalty || 10, profitShareSingle: plan.ficoSingleRent || 45, profitShareOwn: plan.ficoRentToOwn || 55, profitShareEV: plan.ficoShareEv || 60 })) || [ { id: 'inv_demo_1', name: '1 Bike Plan', tier: 'Economy', evBasePrice: 200000, minQuantity: 1, duration: 12, maxInvestment: 1000000, lockIn: 3, exitPenalty: 10, profitShareSingle: 45, profitShareOwn: 55, profitShareEV: 60 }, { id: 'inv_demo_2', name: '5 Bike Plan', tier: 'Standard', evBasePrice: 180000, minQuantity: 5, duration: 24, maxInvestment: 5000000, lockIn: 6, exitPenalty: 15, profitShareSingle: 50, profitShareOwn: 60, profitShareEV: 65 } ]; const batteryTemplates = settings?.plans?.batteryInvestment?.map((plan: any) => ({ id: plan.id, name: plan.name, tier: plan.tier || 'Standard', evBasePrice: plan.batteryBasePrice || 15000, minQuantity: plan.minQuantity || 10, duration: plan.durationMonths || 12, maxInvestment: plan.maxInvestment || 500000, lockIn: plan.lockInMonths || 3, exitPenalty: plan.earlyExitPenalty || 10, profitSharePercent: plan.profitSharePercent || 40, profitShareSingle: plan.profitSharePercent || 40, profitShareOwn: plan.profitSharePercent || 40, profitShareEV: plan.profitSharePercent || 40 })) || [ { id: 'bat_demo_1', name: '1 Battery Pack Plan', tier: 'Silver', evBasePrice: 45000, minQuantity: 1, duration: 12, maxInvestment: 500000, lockIn: 3, exitPenalty: 8, profitSharePercent: 40, profitShareSingle: 40, profitShareOwn: 40, profitShareEV: 40 }, { id: 'bat_demo_2', name: '5 Battery Pack Plan', tier: 'Gold', evBasePrice: 42000, minQuantity: 5, duration: 18, maxInvestment: 2000000, lockIn: 4, exitPenalty: 10, profitSharePercent: 48, profitShareSingle: 48, profitShareOwn: 48, profitShareEV: 48 }, { id: 'bat_demo_3', name: '10 Battery Pack Fleet', tier: 'Platinum', evBasePrice: 40000, minQuantity: 10, duration: 24, maxInvestment: 5000000, lockIn: 6, exitPenalty: 12, profitSharePercent: 55, profitShareSingle: 55, profitShareOwn: 55, profitShareEV: 55 } ]; const allTemplates = [...bikeTemplates, ...batteryTemplates]; const activeTemplate = allTemplates.find(t => t.name === newInvestment.planName) || (newInvestment.assetType === 'battery' ? batteryTemplates[0] : bikeTemplates[0]); return (
{/* Profile Card Header */}
{/* Profile Image */}
{investor.name.charAt(0)}
{/* Profile Info */}

{investor.name}

{investor.id} • {investor.email}

{investor.referralCode && ( Ref: {investor.referralCode} )} {investor.totalReferrals > 0 && ( Referrals: {investor.totalReferrals} )}
{investor.status} {investor.investments && investor.investments.length > 0 && ( {investor.investments.length} Investment{investor.investments.length > 1 ? 's' : ''} )} KYC {investor.kycStatus} Risk: {investor.riskLevel}
{/*
*/}
{/* Stats Row */}

Total Invested

৳{investor.totalInvested.toLocaleString()}

Total Earnings

৳{investor.totalEarnings.toLocaleString()}

Total Withdrawn

৳{investor.totalWithdrawn.toLocaleString()}

Current Balance

৳{(investor.totalEarnings - investor.totalWithdrawn - investor.pendingEarnings).toLocaleString()}

Active Bikes

{investor.activeBikes}

Active Batteries

{batteries.filter(b => b.investorId === investorId).length}

Pending Request

৳{investor.pendingEarnings.toLocaleString()}

{activeTab === 'overview' && (
setEditForm({ fullName: investor.name, phone: investor.phone, phoneAlt: investor.phoneAlt || '', email: investor.email, nidNumber: investor.nidNumber || '', tinNumber: investor.tinNumber || '', dateOfBirth: investor.dateOfBirth || '', gender: investor.gender || '', occupation: investor.occupation || '', bloodGroup: (investor as any).bloodGroup || '', maritalStatus: (investor as any).maritalStatus || '', religion: (investor as any).religion || '', nationality: (investor as any).nationality || 'Bangladeshi' })} editForm={editForm} setEditForm={setEditForm}> {editingSection === 'personal' ? (
setEditForm({ ...editForm, fullName: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" />
setEditForm({ ...editForm, phone: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" />
setEditForm({ ...editForm, phoneAlt: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" />
setEditForm({ ...editForm, email: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" />
setEditForm({ ...editForm, nidNumber: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" />
setEditForm({ ...editForm, tinNumber: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" />
setEditForm({ ...editForm, dateOfBirth: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" />
setEditForm({ ...editForm, occupation: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" />
setEditForm({ ...editForm, nationality: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" />
) : (

Full Name

{investor.name}

Phone

{investor.phone}

Alternate Phone

{investor.phoneAlt || '-'}

Email

{investor.email}

NID Number

{investor.nidNumber || '-'}

TIN Number

{investor.tinNumber || '-'}

Date of Birth

{investor.dateOfBirth || '-'}

Gender

{investor.gender || '-'}

Occupation

{investor.occupation || '-'}

Blood Group

{(investor as any).bloodGroup || '-'}

Marital Status

{(investor as any).maritalStatus || '-'}

Religion

{(investor as any).religion || '-'}

Nationality

{(investor as any).nationality || 'Bangladeshi'}

)}
setEditForm({ nomineeName: (investor as any).nomineeName || '', nomineeRelation: (investor as any).nomineeRelation || '', nomineeNid: (investor as any).nomineeNid || '', nomineePhone: (investor as any).nomineePhone || '', nomineeEmail: (investor as any).nomineeEmail || '', nomineeAddress: (investor as any).nomineeAddress || '', nomineeShare: (investor as any).nomineeShare || '' })} editForm={editForm} setEditForm={setEditForm}> {editingSection === 'nominee' ? (
setEditForm({ ...editForm, nomineeName: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" />
setEditForm({ ...editForm, nomineeNid: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" />
setEditForm({ ...editForm, nomineePhone: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" />
setEditForm({ ...editForm, nomineeEmail: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" />
setEditForm({ ...editForm, nomineeShare: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" />
setEditForm({ ...editForm, nomineeAddress: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" />
) : (

Nominee Name

{(investor as any).nomineeName || '-'}

Relationship

{(investor as any).nomineeRelation || '-'}

NID Number

{(investor as any).nomineeNid || '-'}

Phone

{(investor as any).nomineePhone || '-'}

Email

{(investor as any).nomineeEmail || '-'}

Share %

{(investor as any).nomineeShare || '-'}

Address

{(investor as any).nomineeAddress || '-'}

)}
setEditForm({ emergencyName: investor.emergencyContactName || '', emergencyRelation: investor.emergencyContactRelation || '', emergencyPhone: investor.emergencyContactPhone || '', emergencyEmail: (investor as any).emergencyEmail || '', emergencyAddress: (investor as any).emergencyAddress || '' })} editForm={editForm} setEditForm={setEditForm}> {editingSection === 'emergency' ? (
setEditForm({ ...editForm, emergencyName: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" />
setEditForm({ ...editForm, emergencyPhone: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" />
setEditForm({ ...editForm, emergencyEmail: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" />
setEditForm({ ...editForm, emergencyAddress: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" />
) : (

Contact Name

{investor.emergencyContactName || '-'}

Relationship

{investor.emergencyContactRelation || '-'}

Phone

{investor.emergencyContactPhone || '-'}

Email

{(investor as any).emergencyEmail || '-'}

Address

{(investor as any).emergencyAddress || '-'}

)}
setEditForm({ companyName: (investor as any).companyName || '', monthlyIncome: (investor as any).monthlyIncome || '', investmentSource: (investor as any).investmentSource || '', profession: investor.occupation || '' })} editForm={editForm} setEditForm={setEditForm}> {editingSection === 'investmentInfo' ? (
setEditForm({ ...editForm, companyName: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" />
setEditForm({ ...editForm, monthlyIncome: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" />
setEditForm({ ...editForm, investmentSource: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="e.g., Savings, Business profit, Inheritance" />
setEditForm({ ...editForm, profession: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" />
) : (

Company / Business Name

{(investor as any).companyName || '-'}

Monthly Income

{(investor as any).monthlyIncome || '-'}

Investment Source

{(investor as any).investmentSource || '-'}

Profession / Occupation

{investor.occupation || '-'}

)}
{ const parts = investor.address.split(',').map(s => s.trim()); setEditForm({ addressLine1: parts.slice(0, 2).join(', '), addressLine2: parts.slice(2, 4).join(', '), division: parts[4] || 'Dhaka', district: parts[5] || 'Dhaka', thana: parts[3] || '', zipCode: parts[6] || '', landmark: parts[7] || '' }); }} editForm={editForm} setEditForm={setEditForm}> {editingSection === 'presentAddress' ? (
setEditForm({ ...editForm, addressLine1: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" />
setEditForm({ ...editForm, addressLine2: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" />
setEditForm({ ...editForm, thana: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" />
setEditForm({ ...editForm, zipCode: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" />
setEditForm({ ...editForm, landmark: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" />
) : (

Address Line 1

{investor.address}

Landmark

-

Division

Dhaka

District

Dhaka

Thana

-

Zip Code

1205

)}
{ const parts = investor.address.split(',').map(s => s.trim()); setEditForm({ addressLine1: parts.slice(0, 2).join(', '), addressLine2: parts.slice(2, 4).join(', '), division: parts[4] || 'Dhaka', district: parts[5] || 'Dhaka', thana: parts[3] || '', zipCode: parts[6] || '', isSameAsPresent: true }); }} editForm={editForm} setEditForm={setEditForm}> {editingSection === 'permanentAddress' ? (
setEditForm({ ...editForm, isSameAsPresent: e.target.checked })} className="rounded text-investor" />
{!editForm.isSameAsPresent && (
setEditForm({ ...editForm, addressLine1: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" />
setEditForm({ ...editForm, addressLine2: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" />
setEditForm({ ...editForm, thana: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" />
setEditForm({ ...editForm, zipCode: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" />
)}
) : (
Same as Present

Address Line 1

{investor.address}

Landmark

-

Division

Dhaka

District

Dhaka

Thana

-

Zip Code

1205

)}
{investor.notes && (

Notes

{investor.notes}

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

Assigned Bikes

{assignedBikes.length} bikes across {investor.investments?.filter((inv: any) => inv.assetType === 'bike' || !inv.assetType).length || 0} EV Investment Plans

{assignedBikes.map(bike => { const investment = investor.investments?.find((inv: any) => inv.id === bike.investmentId); const planColors: Record = { silver: 'from-slate-400 to-slate-600', gold: 'from-amber-400 to-amber-600', platinum: 'from-purple-400 to-purple-600', diamond: 'from-blue-400 to-blue-600', }; const planBadges: Record = { silver: 'bg-slate-100 text-slate-600', gold: 'bg-amber-100 text-amber-700', platinum: 'bg-purple-100 text-purple-700', diamond: 'bg-blue-100 text-blue-700', }; const planBg: Record = { silver: 'bg-slate-50 border-slate-200', gold: 'bg-amber-50 border-amber-200', platinum: 'bg-purple-50 border-purple-200', diamond: 'bg-blue-50 border-blue-200', }; const rentalTypes: Record = { single_rent: { label: 'Single Rent', color: 'text-green-600' }, rent_to_own: { label: 'Rent to Own', color: 'text-blue-600' }, share_ev: { label: 'Share EV', color: 'text-purple-600' }, }; const statusConfig: Record = { rented: { bg: 'bg-green-100', color: 'text-green-700' }, available: { bg: 'bg-blue-100', color: 'text-blue-700' }, maintenance: { bg: 'bg-amber-100', color: 'text-amber-700' }, retired: { bg: 'bg-slate-100', color: 'text-slate-600' }, }; const rentalInfo = rentalTypes[bike.rentalType || 'single_rent'] || rentalTypes.single_rent; const planType = investment?.planType || 'gold'; const status = statusConfig[bike.status] || statusConfig.available; return (

{bike.model}

{bike.brand}

{bike.status}
{planType} Plan • {investment?.planName || 'Investment'}

Plate

{bike.plateNumber.split('-').pop()}

Location

{bike.location}

Rental Type {rentalInfo.label}
Total Earnings ৳{bike.totalEarnings?.toLocaleString() || 0}
Battery 50 ? 'text-green-600' : bike.batteryLevel > 20 ? 'text-amber-600' : 'text-red-600' }`}>{bike.batteryLevel}%
View Details
); })} {assignedBikes.length === 0 && (

No bikes assigned to this investor

Bikes will appear here once assigned to investments

)}
)} {activeTab === 'batteries' && (

Assigned Batteries

{batteries.filter(b => b.investorId === investorId).length} battery packs assigned to this partner

{batteries.filter(b => b.investorId === investorId).map(battery => { const investment = investor.investments?.find((inv: any) => inv.id === battery.investmentId); const dailyPayout = Math.floor(battery.rentPrice * (battery.investorSharePercentage / 100)); const statusConfig: Record = { 'in-use': { bg: 'bg-green-50 text-green-700 border-green-200', text: 'In Use', dot: 'bg-green-500' }, 'available': { bg: 'bg-blue-50 text-blue-700 border-blue-200', text: 'Available', dot: 'bg-blue-500' }, 'maintenance': { bg: 'bg-amber-50 text-amber-700 border-amber-200', text: 'Maintenance', dot: 'bg-amber-500' }, }; const status = statusConfig[battery.status] || { bg: 'bg-slate-50 text-slate-700 border-slate-200', text: battery.status || 'Unknown', dot: 'bg-slate-500' }; return (
{/* Card Header */}
{battery.serialNumber} {status.text}

{battery.brand} • {battery.model}

ID: {battery.id}

Plan: {investment?.planName || 'Unlinked Plan'}
{/* Co-ownership Payout details */}
Refundable Deposit ৳{battery.deposit?.toLocaleString()}
Daily Rent Price ৳{battery.rentPrice}/day
Your Share % {battery.investorSharePercentage}%
Capital Contribution ৳{battery.investedAmount?.toLocaleString()}
Your Daily Payout ৳{dailyPayout}/day
{/* BMS parameters */}

BMS SOC

{battery.currentSoc}%

SOH Health

{battery.health}%

Cycles

{battery.cycleCount}

{/* Actions block */}
); })} {batteries.filter(b => b.investorId === investorId).length === 0 && (

No Batteries Assigned

Link high-yield battery pack assets to this partner

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

Investment Plans

Manage investment portfolios for this investor

{investor.investments?.map((inv) => { const planConfig: Record = { silver: { bg: 'bg-slate-100', border: 'border-slate-300', icon: 'text-slate-500' }, gold: { bg: 'bg-amber-100', border: 'border-amber-300', icon: 'text-amber-500' }, platinum: { bg: 'bg-purple-100', border: 'border-purple-300', icon: 'text-purple-500' }, diamond: { bg: 'bg-blue-100', border: 'border-blue-300', icon: 'text-blue-500' }, }; const style = planConfig[inv.planType] || planConfig.gold; return (

{inv.planName}

{inv.planType} Plan

{inv.status}

Investment

৳{inv.totalInvestment.toLocaleString()}

Total Return

৳{inv.actualEarnings.toLocaleString()}

Duration 12 months
Lock-in Period 3 months
Early Exit Penalty 10%
{inv.startDate} - {inv.endDate || 'Ongoing'} {inv.paymentMethod}

ID: #{inv.id?.slice(-6) || 'N/A'}

View {/* Statement */}
); })}
{(!investor.investments || investor.investments.length === 0) && (

No Investments Yet

Create your first investment plan for this investor

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

Bank Accounts ({investor.bankAccounts?.length || 0})

{investor.bankAccounts && investor.bankAccounts.length > 0 ? ( investor.bankAccounts.map((account: any) => (

{account.bankName}

{account.verified ? ( Verified ) : ( Pending )}

{account.branch}

{!account.verified && ( )}
Account Name {account.accountName}
Account Number {account.accountNumber}
{account.routing && (
Routing {account.routing}
)}
)) ) : (

No bank accounts added

)}

Mobile Banking ({(investor.mobileBanking ? 1 : 0) + (investor.additionalMobileBanking?.length || 0)})

{investor.mobileBanking ? (

{investor.mobileBanking}

{investor.mobileBankingVerified ? ( Verified ) : ( Pending )}

{investor.mobileBankingNumber}

{!investor.mobileBankingVerified && ( )}
) : 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 => { const getAccountingInfo = (type: string, amount: number) => { if (type === 'investment') { return { debitAccount: 'Bank - City Bank (1200)', creditAccount: 'Investor Liabilities (2200)', amount: amount }; } else if (type === 'earning' || type === 'bike_earning' || type === 'investment_return') { return { debitAccount: 'Investor Liabilities (2200)', creditAccount: 'Rental Income (4200)', amount: amount }; } else if (type === 'withdrawal') { return { debitAccount: 'Investor Liabilities (2200)', creditAccount: 'Bank - City Bank (1200)', amount: amount }; } return { debitAccount: 'N/A', creditAccount: 'N/A', amount: amount }; }; return (

{tx.description}

{tx.createdAt}

{tx.referenceNumber && ( )}

{tx.type === 'earning' || tx.type === 'bike_earning' || tx.type === 'investment_return' || tx.type === 'referral_bonus' ? '+' : '-'}৳{tx.amount.toLocaleString()}

{tx.status}
); })}
)} {activeTab === 'documents' && (

KYC Documents

Manage investor verification 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.'}

)} {activeTab === 'rentals' && (

Daily Rental History

Track daily rental payments for investor's bikes

Total Collected

৳{investorRentalPayments.filter(p => p.status === 'paid').reduce((sum, p) => sum + p.amount, 0).toLocaleString()}

Active Rentals

{new Set(investorRentalPayments.filter(p => p.status === 'paid').map(p => p.bikeId)).size}

Pending

৳{investorRentalPayments.filter(p => p.status === 'pending').reduce((sum, p) => sum + p.amount, 0).toLocaleString()}

Failed

৳{investorRentalPayments.filter(p => p.status === 'failed').reduce((sum, p) => sum + p.amount, 0).toLocaleString()}

Daily Payment Records

{investorRentalPayments.length} records
{(() => { const sortedPayments = [...investorRentalPayments].sort((a, b) => { if (rentalSortBy === 'date') { return rentalSortOrder === 'asc' ? new Date(a.date).getTime() - new Date(b.date).getTime() : new Date(b.date).getTime() - new Date(a.date).getTime(); } else if (rentalSortBy === 'bike') { return rentalSortOrder === 'asc' ? a.bikeModel.localeCompare(b.bikeModel) : b.bikeModel.localeCompare(a.bikeModel); } else if (rentalSortBy === 'amount') { return rentalSortOrder === 'asc' ? a.amount - b.amount : b.amount - a.amount; } return 0; }); const totalPages = Math.ceil(sortedPayments.length / rentalPageSize); const paginatedPayments = sortedPayments.slice( (rentalPage - 1) * rentalPageSize, rentalPage * rentalPageSize ); return paginatedPayments.map((payment: any) => { const planColors: Record = { single: { label: 'Single Rent', bg: 'bg-green-100', color: 'text-green-700' }, 'rent-to-own': { label: 'Rent to Own', bg: 'bg-blue-100', color: 'text-blue-700' }, share_ev: { label: 'Share EV', bg: 'bg-purple-100', color: 'text-purple-700' }, }; const statusConfig: Record = { paid: { label: 'Paid', bg: 'bg-green-100', color: 'text-green-700' }, pending: { label: 'Pending', bg: 'bg-amber-100', color: 'text-amber-700' }, failed: { label: 'Failed', bg: 'bg-red-100', color: 'text-red-700' }, }; const plan = planColors[payment.planType] || planColors.single; const status = statusConfig[payment.status] || statusConfig.pending; return ( ); }); })()}
{ if (rentalSortBy === 'date') { setRentalSortOrder(rentalSortOrder === 'asc' ? 'desc' : 'asc'); } else { setRentalSortBy('date'); setRentalSortOrder('desc'); } }} className="px-4 py-3 text-left text-xs font-medium text-slate-500 uppercase cursor-pointer hover:bg-slate-100" >
Date {rentalSortBy === 'date' && ( {rentalSortOrder === 'asc' ? '↑' : '↓'} )}
{ if (rentalSortBy === 'bike') { setRentalSortOrder(rentalSortOrder === 'asc' ? 'desc' : 'asc'); } else { setRentalSortBy('bike'); setRentalSortOrder('asc'); } }} className="px-4 py-3 text-left text-xs font-medium text-slate-500 uppercase cursor-pointer hover:bg-slate-100" >
Bike {rentalSortBy === 'bike' && ( {rentalSortOrder === 'asc' ? '↑' : '↓'} )}
Biker Plan Duration { if (rentalSortBy === 'amount') { setRentalSortOrder(rentalSortOrder === 'asc' ? 'desc' : 'asc'); } else { setRentalSortBy('amount'); setRentalSortOrder('desc'); } }} className="px-4 py-3 text-left text-xs font-medium text-slate-500 uppercase cursor-pointer hover:bg-slate-100" >
Amount {rentalSortBy === 'amount' && ( {rentalSortOrder === 'asc' ? '↑' : '↓'} )}
Method Status

{payment.date}

{payment.transactionId || payment.id}

{payment.bikeModel}

{payment.plateNumber}

{payment.bikerName}
{plan.label} {payment.duration}

৳{payment.amount.toLocaleString()}

{payment.paymentMethod} {status.label}
{investorRentalPayments.length === 0 && (

No rental payments found

)}
{investorRentalPayments.length > rentalPageSize && (

Showing {((rentalPage - 1) * rentalPageSize) + 1} to {Math.min(rentalPage * rentalPageSize, investorRentalPayments.length)} of {investorRentalPayments.length}

{Array.from({ length: Math.ceil(investorRentalPayments.length / rentalPageSize) }, (_, i) => i + 1).map(page => ( ))}
)}
)}
{showWithdrawalModal && (

Request Withdrawal

Available Balance

৳{(investor.totalEarnings - investor.totalWithdrawn - investor.pendingEarnings).toLocaleString()}

Pending Request

৳{investor.pendingEarnings.toLocaleString()}

Total Withdrawn

৳{investor.totalWithdrawn.toLocaleString()}

Select EV Investment Plans & Bikes

{ setWithdrawSelection({ ...withdrawSelection, selectAll: e.target.checked, selectedPlans: e.target.checked ? investor.investments?.map((inv: any) => inv.id) || [] : [], selectedBikes: e.target.checked ? assignedBikes.map(b => b.id) : [] }); }} className="w-4 h-4 text-investor rounded" />

EV Investment Plans

{investor.investments?.map((inv: any) => { const planColors: Record = { silver: 'bg-slate-100 text-slate-700', gold: 'bg-amber-100 text-amber-700', platinum: 'bg-purple-100 text-purple-700', diamond: 'bg-blue-100 text-blue-700', }; const invBikes = assignedBikes.filter(b => b.investmentId === inv.id); const invEarnings = invBikes.reduce((sum: number, b: any) => sum + (b.totalEarnings || 0), 0); return (
{ const newPlans = e.target.checked ? [...withdrawSelection.selectedPlans, inv.id] : withdrawSelection.selectedPlans.filter((p: string) => p !== inv.id); const newBikes = e.target.checked ? [...new Set([...withdrawSelection.selectedBikes, ...invBikes.map((b: any) => b.id)])] : withdrawSelection.selectedBikes.filter((b: string) => !invBikes.find((ib: any) => ib.id === b)); setWithdrawSelection({ ...withdrawSelection, selectedPlans: newPlans, selectedBikes: newBikes }); }} className="w-4 h-4 text-investor rounded" />
{!withdrawSelection.selectAll && invBikes.length > 0 && (
{invBikes.map((bike: any) => (
{ const newBikes = e.target.checked ? [...withdrawSelection.selectedBikes, bike.id] : withdrawSelection.selectedBikes.filter((b: string) => b !== bike.id); setWithdrawSelection({ ...withdrawSelection, selectedBikes: newBikes }); }} className="w-3 h-3 text-investor rounded" /> ৳{bike.totalEarnings?.toLocaleString() || 0}
))}
)}
); })}

Withdrawal Amount

Calculated Amount {withdrawSelection.selectAll ? 'All Selected' : `${withdrawSelection.selectedBikes.length} bikes`}

৳{(withdrawSelection.selectAll ? assignedBikes.reduce((sum: number, b: any) => sum + (b.totalEarnings || 0), 0) : withdrawSelection.selectedBikes.reduce((sum: number, bikeId: string) => { const bike = assignedBikes.find((b: any) => b.id === bikeId); return sum + (bike?.totalEarnings || 0); }, 0) ).toLocaleString()}

Based on {withdrawSelection.selectAll ? 'all' : withdrawSelection.selectedBikes.length} selected bike(s) earnings

Payment Method

{investor.bankAccounts?.map((account: any) => (
setWithdrawSelection({ ...withdrawSelection, paymentMethod: 'bank', accountId: account.id })} className={`p-4 rounded-lg border cursor-pointer transition-all ${withdrawSelection.accountId === account.id ? 'border-investor bg-investor/5' : 'border-slate-200 hover:border-slate-300'}`} >

{account.bankName}

{account.isPrimary && Primary}

{account.accountNumber}

))}
{investor.mobileBanking && (
setWithdrawSelection({ ...withdrawSelection, paymentMethod: 'mobile', accountId: 'mobile' })} className={`mt-3 p-4 rounded-lg border cursor-pointer transition-all ${withdrawSelection.paymentMethod === 'mobile' ? 'border-investor bg-investor/5' : 'border-slate-200 hover:border-slate-300'}`} >

{investor.mobileBanking}

{investor.mobileBankingNumber}

)}
)} {showAutoWithdrawModal && (

Auto-Withdraw Settings

Enable Auto-Withdraw

Automatically withdraw earnings

{autoWithdrawSettings.enabled && ( <>
{[ { value: 'as_per_request', label: 'As Requested' }, { value: 'weekly', label: 'Weekly' }, { value: 'monthly', label: 'Monthly' } ].map(opt => ( ))}
setAutoWithdrawSettings({ ...autoWithdrawSettings, minAmount: Number(e.target.value) })} className="w-full pl-8 pr-4 py-2 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-investor" />

Minimum balance required for auto-withdrawal

{investor.bankAccounts?.map((account: any) => (
setAutoWithdrawSettings({ ...autoWithdrawSettings, accountId: account.id })} className={`p-3 rounded-lg border cursor-pointer flex items-center gap-3 ${autoWithdrawSettings.accountId === account.id ? 'border-investor bg-investor/5' : 'border-slate-200'}`} >
{autoWithdrawSettings.accountId === account.id &&
}

{account.bankName}

{account.accountNumber}

{account.isPrimary && Primary}
))} {investor.mobileBanking && (
setAutoWithdrawSettings({ ...autoWithdrawSettings, accountId: 'mobile' })} className={`p-3 rounded-lg border cursor-pointer flex items-center gap-3 ${autoWithdrawSettings.accountId === 'mobile' ? 'border-investor bg-investor/5' : 'border-slate-200'}`} >
{autoWithdrawSettings.accountId === 'mobile' &&
}

{investor.mobileBanking}

{investor.mobileBankingNumber}

)}
)}
)} setShowAssignBatteryModal(false)} investor={investor} batteries={batteries} unassignedBatteries={unassignedBatteries} onAssign={(planId, batteryIds) => { const assignedBatteriesList: any[] = []; setBatteries(prev => prev.map(b => { if (batteryIds.includes(b.id)) { assignedBatteriesList.push(b); return { ...b, investorId: investorId, investorName: investor.name, investmentId: planId, status: 'in-use', investedAmount: b.purchasePrice || 45000, investorSharePercentage: 100, deposit: b.deposit || 5000, rentPrice: b.rentPrice || 150 }; } return b; })); setUnassignedBatteries(prev => prev.filter(b => !batteryIds.includes(b.id))); setInvestors(prev => prev.map(inv => { if (inv.id === investor.id) { return { ...inv, investments: inv.investments?.map((item: any) => { if (item.id === planId) { const currentBatteryIds = item.batteryIds || []; const uniqueNewBatteryIds = Array.from(new Set([...currentBatteryIds, ...batteryIds])); return { ...item, batteryIds: uniqueNewBatteryIds }; } return item; }) }; } return inv; })); const batteryNames = assignedBatteriesList.map(b => `${b.brand} ${b.model}`).join(', '); toast.success(`Successfully assigned ${batteryIds.length} battery/ies: ${batteryNames}`); }} /> {showRegisterBatteryModal && (

Register & Assign New Battery

setRegisterForm({ ...registerForm, serialNumber: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm font-mono font-semibold" />
setRegisterForm({ ...registerForm, model: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" />
setRegisterForm({ ...registerForm, capacity: Number(e.target.value) })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" />
setRegisterForm({ ...registerForm, voltage: Number(e.target.value) })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" />
setRegisterForm({ ...registerForm, purchasePrice: Number(e.target.value), investedAmount: Number(e.target.value) })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" />
setRegisterForm({ ...registerForm, deposit: Number(e.target.value) })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" />
setRegisterForm({ ...registerForm, rentPrice: Number(e.target.value) })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" />
setRegisterForm({ ...registerForm, investorShare: Number(e.target.value) })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" />
setRegisterForm({ ...registerForm, investedAmount: Number(e.target.value) })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" />
)} {showEditBatteryModal && (

Edit Battery Assignment

SELECTED ASSET

{selectedBatteryToEdit?.brand} {selectedBatteryToEdit?.model} ({selectedBatteryToEdit?.serialNumber})

ID: {selectedBatteryToEdit?.id}

setAssignForm({ ...assignForm, deposit: Number(e.target.value) })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" />
setAssignForm({ ...assignForm, rentPrice: Number(e.target.value) })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" />
setAssignForm({ ...assignForm, investorShare: Number(e.target.value) })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" />
setAssignForm({ ...assignForm, investedAmount: Number(e.target.value) })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" />
)} setShowAssignBikeModal(false)} investor={investor} bikes={bikes} onAssign={(planId, bikeIds) => { const assignedBikesList: any[] = []; setBikes(prev => prev.map(b => { if (bikeIds.includes(b.id)) { assignedBikesList.push(b); return { ...b, investorId: investorId, investorName: investor.name, investmentId: planId, status: 'rented', totalEarnings: b.totalEarnings || 0 }; } return b; })); setInvestors(prev => prev.map(inv => { if (inv.id === investor.id) { return { ...inv, investments: inv.investments?.map((item: any) => { if (item.id === planId) { const currentBikeIds = item.bikeIds || []; const uniqueNewBikeIds = Array.from(new Set([...currentBikeIds, ...bikeIds])); return { ...item, bikeIds: uniqueNewBikeIds }; } return item; }) }; } return inv; })); const bikeNames = assignedBikesList.map(b => `${b.model} (${b.plateNumber})`).join(', '); toast.success(`Successfully assigned ${bikeIds.length} bike(s): ${bikeNames}`); }} /> setUnassignConfirmModal(prev => ({ ...prev, show: false }))} type={unassignConfirmModal.type} name={unassignConfirmModal.name} details={unassignConfirmModal.details} onConfirm={() => { if (unassignConfirmModal.type === 'bike') { handleUnassignBike(unassignConfirmModal.id); } else { handleUnassignBattery(unassignConfirmModal.id); } }} /> {showRegisterBikeModal && (

Register & Assign New Bike

setRegisterBikeForm({ ...registerBikeForm, plateNumber: e.target.value })} placeholder="DHAKA-METRO-HA-1234" className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" />
setRegisterBikeForm({ ...registerBikeForm, brand: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" />
setRegisterBikeForm({ ...registerBikeForm, model: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" />
setRegisterBikeForm({ ...registerBikeForm, purchasePrice: Number(e.target.value) })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" />
setRegisterBikeForm({ ...registerBikeForm, currentRent: Number(e.target.value) })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" />
setRegisterBikeForm({ ...registerBikeForm, location: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" />
)} {showCreateInvestmentModal && (

Create New Investment

Set up investment for {investor.name}

{/* Asset Type Selector */}
{(newInvestment.assetType === 'battery' ? batteryTemplates : bikeTemplates).map((plan: any) => ( ))}
setNewInvestment({ ...newInvestment, planName: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm font-semibold" placeholder="Plan name" />

= Qty × Base Price

{ const val = Number(e.target.value); setNewInvestment({ ...newInvestment, totalInvestment: val, paidAmount: newInvestment.assetType === 'battery' ? val : (newInvestment.paymentType === 'full' ? val : Math.max(val * 0.5, newInvestment.paidAmount)) }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-yellow-50 font-bold" />

{newInvestment.assetType === 'battery' ? 'Partner Profit Sharing Percentage' : 'FICO Share - Partner Yield Sharing Percentages'}

Profit sharing ratio when {newInvestment.assetType}s are utilized

{newInvestment.assetType === 'battery' ? (
Profit Share Percent (%) {activeTemplate.profitSharePercent || activeTemplate.profitShareSingle}%
) : (
)}
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" />

Payment Options

{newInvestment.assetType === 'battery' ? (

Full Payment Only

Battery plans require full upfront capital

৳{newInvestment.totalInvestment.toLocaleString()}
) : ( <>
)}
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" />