2026-04-22 01:02:45 +06:00
'use client' ;
2026-05-19 17:25:32 +06:00
import { useState , useEffect } from 'react' ;
2026-04-22 01:02:45 +06:00
import Link from 'next/link' ;
2026-05-19 20:45:10 +06:00
import { useParams , useRouter , useSearchParams } from 'next/navigation' ;
2026-05-14 22:25:24 +06:00
import { investors as initialInvestors , bikes as initialBikes , transactions as initialTransactions , rentalPayments as initialRentalPayments } from '@/data/mockData' ;
2026-04-22 01:02:45 +06:00
import type { Investor } from '@/data/mockData' ;
2026-05-14 13:54:02 +06:00
import toast from 'react-hot-toast' ;
import {
2026-05-19 17:25:32 +06:00
ArrowLeft , Wallet , TrendingUp , Banknote , Calendar , Phone , Mail , MapPin , Edit , Trash2 , Plus , X , Bike , Battery , Unlink , Eye , ShieldAlert , Cpu ,
2026-05-14 13:54:02 +06:00
User , FileText , CreditCard , DollarSign , Clock , ChevronDown , ExternalLink , Download , Upload ,
AlertTriangle , Shield , Star , CheckCircle , XCircle , Search , Filter , BookOpen , ArrowRight , Printer ,
2026-05-19 17:25:32 +06:00
UserCircle , Home , Briefcase , Heart , PhoneCall , PhoneOutgoing , MessageSquare , Save ,
2026-05-14 22:25:24 +06:00
ShieldCheck , Building2 , Users , Check , AlertOctagon , Activity , Award , Camera , History , Settings
2026-04-22 01:02:45 +06:00
} from 'lucide-react' ;
2026-05-19 19:27:03 +06:00
import AssignBikeModal from '../components/AssignBikeModal' ;
import AssignBatteryModal from '../components/AssignBatteryModal' ;
import UnassignConfirmModal from '../components/UnassignConfirmModal' ;
2026-04-22 01:02:45 +06:00
const statusColors : Record < string , string > = {
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 < string , string > = {
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 < string , string > = {
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 < string , string > = {
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' ,
} ;
2026-05-14 13:54:02 +06:00
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 (
< div className = "bg-white rounded-xl border border-slate-200 overflow-hidden" >
< div className = { ` ${ headerBg } px-5 py-4 border-b ${ headerBorder } flex items-center justify-between ` } >
< div className = "flex items-center gap-2" >
< Icon className = "w-5 h-5 text-slate-600" / >
< h3 className = "font-semibold text-slate-800" > { title } < / h3 >
< / div >
{ editKey && setEditingSection ? (
editingSection !== editKey ? (
< button onClick = { ( ) = > { setEditingSection ( editKey ) ; onEdit ? . ( ) ; } } className = "p-1.5 hover:bg-white rounded-lg transition-colors" >
< Edit className = "w-4 h-4 text-slate-500" / >
< / button >
) : (
< div className = "flex gap-1" >
< button onClick = { ( ) = > { toast . success ( 'Updated' ) ; setEditingSection ( null ) ; } } className = "px-3 py-1.5 bg-green-600 text-white rounded-lg text-xs font-medium hover:bg-green-700" > Save < / button >
< button onClick = { ( ) = > setEditingSection ( null ) } className = "px-3 py-1.5 border border-slate-200 text-slate-600 rounded-lg text-xs font-medium hover:bg-slate-50" > Cancel < / button >
< / div >
)
) : null }
< / div >
< div className = "p-4" >
{ children }
< / div >
< / div >
) ;
}
2026-04-22 01:02:45 +06:00
export default function InvestorDetailPage() {
const params = useParams ( ) ;
2026-05-14 13:54:02 +06:00
const router = useRouter ( ) ;
2026-05-19 20:45:10 +06:00
const searchParams = useSearchParams ( ) ;
2026-04-22 01:02:45 +06:00
const investorId = params . id as string ;
2026-05-14 13:54:02 +06:00
2026-05-19 20:45:10 +06:00
const [ activeTab , setActiveTab ] = useState ( 'overview' ) ;
useEffect ( ( ) = > {
const tab = searchParams ? . get ( 'tab' ) ;
if ( tab ) {
setActiveTab ( tab ) ;
}
} , [ searchParams ] ) ;
2026-05-19 18:11:35 +06:00
const [ investors , setInvestors ] = useState < Investor [ ] > ( ( ) = > {
if ( typeof window !== 'undefined' ) {
const stored = localStorage . getItem ( 'jaiben_investors' ) ;
if ( stored ) {
try {
return JSON . parse ( stored ) ;
} catch ( e ) {
console . error ( e ) ;
}
}
}
return initialInvestors ;
} ) ;
2026-04-22 01:02:45 +06:00
const investor = investors . find ( i = > i . id === investorId ) ;
2026-05-14 13:54:02 +06:00
2026-05-19 18:11:35 +06:00
useEffect ( ( ) = > {
if ( typeof window !== 'undefined' ) {
localStorage . setItem ( 'jaiben_investors' , JSON . stringify ( investors ) ) ;
}
} , [ investors ] ) ;
const [ bikes , setBikes ] = useState < any [ ] > ( ( ) = > {
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 ) ;
2026-05-19 17:25:32 +06:00
const [ settings , setSettings ] = useState < any > ( null ) ;
useEffect ( ( ) = > {
if ( typeof window !== 'undefined' ) {
const stored = localStorage . getItem ( 'companySettings' ) ;
if ( stored ) {
try {
setSettings ( JSON . parse ( stored ) ) ;
} catch ( e ) {
console . error ( e ) ;
}
}
}
} , [ ] ) ;
2026-04-22 01:18:03 +06:00
// Investor transactions are filtered below
2026-05-14 13:54:02 +06:00
2026-04-22 01:02:45 +06:00
const [ showEditModal , setShowEditModal ] = useState ( false ) ;
const [ showAssignBikeModal , setShowAssignBikeModal ] = useState ( false ) ;
const [ selectedBikeId , setSelectedBikeId ] = useState ( '' ) ;
2026-05-19 19:27:03 +06:00
const [ selectedBikeIds , setSelectedBikeIds ] = useState < string [ ] > ( [ ] ) ;
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 ;
}
} ;
2026-05-19 18:11:35 +06:00
const [ showRegisterBikeModal , setShowRegisterBikeModal ] = useState ( false ) ;
2026-05-19 19:27:03 +06:00
const [ unassignConfirmModal , setUnassignConfirmModal ] = useState < {
show : boolean ;
type : 'bike' | 'battery' ;
id : string ;
name : string ;
details : string ;
} > ( {
show : false ,
type : 'bike' ,
id : '' ,
name : '' ,
details : ''
} ) ;
2026-05-19 18:11:35 +06:00
const [ registerBikeForm , setRegisterBikeForm ] = useState ( {
plateNumber : '' ,
brand : 'Etron' ,
model : 'ET50' ,
currentRent : 150 ,
location : 'Banani' ,
purchasePrice : 200000 ,
rentalType : 'single_rent' ,
investmentId : ''
} ) ;
2026-04-22 01:09:51 +06:00
const [ showCreateInvestmentModal , setShowCreateInvestmentModal ] = useState ( false ) ;
2026-05-14 19:49:57 +06:00
const [ showInvestmentSuccessModal , setShowInvestmentSuccessModal ] = useState ( false ) ;
const [ lastCreatedInvestment , setLastCreatedInvestment ] = useState < any > ( null ) ;
2026-04-26 14:56:12 +06:00
const [ showInvoiceModal , setShowInvoiceModal ] = useState ( false ) ;
const [ showJournalModal , setShowJournalModal ] = useState ( false ) ;
const [ selectedInvoice , setSelectedInvoice ] = useState < any > ( null ) ;
const [ investorJournals , setInvestorJournals ] = useState < any [ ] > ( [ ] ) ;
2026-04-22 01:18:03 +06:00
const [ showBankModal , setShowBankModal ] = useState ( false ) ;
2026-05-14 13:54:02 +06:00
const [ editingSection , setEditingSection ] = useState < string | null > ( null ) ;
const [ editForm , setEditForm ] = useState < any > ( { } ) ;
2026-04-22 01:18:03 +06:00
const [ showMobileBankingModal , setShowMobileBankingModal ] = useState ( false ) ;
const [ showTaxModal , setShowTaxModal ] = useState ( false ) ;
const [ showDocModal , setShowDocModal ] = useState ( false ) ;
2026-05-14 20:15:39 +06:00
const [ editingBankAccount , setEditingBankAccount ] = useState < any > ( { 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 } > ( { } ) ;
2026-04-22 01:18:03 +06:00
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 ) ;
2026-05-19 17:25:32 +06:00
2026-05-19 18:11:35 +06:00
const [ batteries , setBatteries ] = useState < any [ ] > ( ( ) = > {
if ( typeof window !== 'undefined' ) {
const stored = localStorage . getItem ( 'jaiben_batteries' ) ;
if ( stored ) {
try {
return JSON . parse ( stored ) ;
} catch ( e ) {
console . error ( e ) ;
}
}
2026-05-19 17:25:32 +06:00
}
2026-05-19 18:11:35 +06:00
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
}
] ;
} ) ;
2026-05-19 17:25:32 +06:00
2026-05-19 18:11:35 +06:00
const [ unassignedBatteries , setUnassignedBatteries ] = useState < any [ ] > ( ( ) = > {
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 ] ) ;
2026-05-19 17:25:32 +06:00
const [ showAssignBatteryModal , setShowAssignBatteryModal ] = useState ( false ) ;
2026-05-19 19:27:03 +06:00
const [ selectedBatteryIds , setSelectedBatteryIds ] = useState < string [ ] > ( [ ] ) ;
const [ selectedBatteryPlanId , setSelectedBatteryPlanId ] = useState ( '' ) ;
2026-05-19 17:25:32 +06:00
const [ showRegisterBatteryModal , setShowRegisterBatteryModal ] = useState ( false ) ;
const [ showEditBatteryModal , setShowEditBatteryModal ] = useState ( false ) ;
const [ selectedBatteryToAssign , setSelectedBatteryToAssign ] = useState < any > ( null ) ;
const [ selectedBatteryToEdit , setSelectedBatteryToEdit ] = useState < any > ( 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 : ''
} ) ;
2026-04-22 01:18:03 +06:00
const investorTransactions = initialTransactions . filter ( t = > t . investorId === investorId ) ;
2026-05-14 22:25:24 +06:00
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 : ''
} ) ;
2026-04-22 01:09:51 +06:00
const [ newInvestment , setNewInvestment ] = useState ( {
planName : '' ,
planType : 'gold' as 'silver' | 'gold' | 'platinum' | 'diamond' ,
2026-05-19 17:25:32 +06:00
assetType : 'bike' as 'bike' | 'battery' ,
2026-04-22 01:09:51 +06:00
selectedBikeIds : [ ] as string [ ] ,
2026-05-19 17:25:32 +06:00
selectedBatteryIds : [ ] as string [ ] ,
2026-04-22 01:09:51 +06:00
totalInvestment : 0 ,
2026-05-15 03:10:37 +06:00
paidAmount : 0 ,
2026-05-15 12:25:24 +06:00
paymentType : 'full' as 'full' | 'partial' ,
2026-04-22 01:09:51 +06:00
monthlyReturn : 0 ,
expectedRoi : 15 ,
startDate : new Date ( ) . toISOString ( ) . split ( 'T' ) [ 0 ] ,
endDate : '' ,
paymentMethod : 'bank' as 'bank' | 'mobile' | 'cash' | 'cheque' ,
transactionReference : '' ,
notes : ''
} ) ;
2026-05-14 23:15:10 +06:00
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 < any [ ] > ( [
{ id : 'pay1' , date : '2024-01-15' , amount : 50000 , paymentMethod : 'bank' , transactionRef : 'TXN-001' , status : 'completed' , notes : 'First payment' }
] ) ;
2026-04-22 01:02:45 +06:00
if ( ! investor ) {
return (
< div className = "p-4 lg:p-6" >
< div className = "text-center py-12" >
< h2 className = "text-xl font-bold text-slate-800" > Investor Not Found < / h2 >
< p className = "text-slate-500 mt-2" > The investor you 're looking for doesn' t exist . < / p >
< Link href = "/admin/investors" className = "mt-4 inline-flex items-center gap-2 text-investor hover:underline" >
< ArrowLeft className = "w-4 h-4" / > Back to Investors
< / Link >
< / div >
< / div >
) ;
}
2026-05-19 18:11:35 +06:00
const availableBikesForAssignment = bikes . filter ( b = > ! b . investorId && b . status === 'available' ) ;
2026-04-22 01:02:45 +06:00
const handleAssignBike = ( ) = > {
2026-05-19 19:27:03 +06:00
if ( ! selectedBikePlanId ) {
toast . error ( 'Please select an investment plan' ) ;
return ;
}
if ( selectedBikeIds . length === 0 ) {
toast . error ( 'Please select at least one bike' ) ;
2026-05-19 18:11:35 +06:00
return ;
}
2026-05-19 19:27:03 +06:00
const assignedBikesList : any [ ] = [ ] ;
2026-05-19 18:11:35 +06:00
setBikes ( prev = > prev . map ( b = > {
2026-05-19 19:27:03 +06:00
if ( selectedBikeIds . includes ( b . id ) ) {
assignedBikesList . push ( b ) ;
2026-05-19 18:11:35 +06:00
return {
. . . b ,
investorId : investorId ,
investorName : investor.name ,
2026-05-19 19:27:03 +06:00
investmentId : selectedBikePlanId ,
2026-05-19 18:11:35 +06:00
status : 'rented' ,
totalEarnings : b.totalEarnings || 0
} ;
}
return b ;
} ) ) ;
2026-05-19 19:27:03 +06:00
// 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 } ` ) ;
2026-04-22 01:02:45 +06:00
setShowAssignBikeModal ( false ) ;
2026-05-19 19:27:03 +06:00
setSelectedBikeIds ( [ ] ) ;
setSelectedBikePlanId ( '' ) ;
2026-04-22 01:02:45 +06:00
} ;
2026-05-19 18:11:35 +06:00
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!' ) ;
} ;
2026-04-22 01:09:51 +06:00
const handleCreateInvestment = ( ) = > {
2026-04-26 14:56:12 +06:00
const invId = ` INV- ${ Date . now ( ) } ` ;
const year = new Date ( ) . getFullYear ( ) ;
const transactionRef = newInvestment . transactionReference || ` INV/ ${ year } / ${ String ( investor . investments . length + 1 ) . padStart ( 4 , '0' ) } ` ;
2026-05-14 13:54:02 +06:00
2026-04-26 14:56:12 +06:00
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' } ;
}
} ;
2026-05-14 13:54:02 +06:00
2026-04-26 14:56:12 +06:00
const debitAccount = getDebitAccount ( newInvestment . paymentMethod ) ;
2026-05-14 13:54:02 +06:00
2026-04-26 14:56:12 +06:00
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' ,
2026-05-15 03:10:37 +06:00
amount : newInvestment.paidAmount ,
2026-04-26 14:56:12 +06:00
paymentMethod : newInvestment.paymentMethod
} ;
2026-05-14 13:54:02 +06:00
2026-05-19 17:25:32 +06:00
const createdInv = {
2026-04-22 01:09:51 +06:00
id : invId ,
investorId : investor.id ,
. . . newInvestment ,
2026-05-19 17:25:32 +06:00
bikeIds : [ ] ,
batteryIds : [ ] ,
2026-04-22 01:09:51 +06:00
actualEarnings : 0 ,
2026-05-19 17:25:32 +06:00
status : 'active' as const ,
2026-04-26 14:56:12 +06:00
transactionId : transactionRef ,
2026-05-14 19:49:57 +06:00
createdAt : new Date ( ) . toISOString ( ) ,
debitAccount ,
journalEntry
2026-05-19 17:25:32 +06:00
} ;
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 ;
} ) ) ;
2026-05-14 13:54:02 +06:00
2026-04-22 01:09:51 +06:00
setShowCreateInvestmentModal ( false ) ;
2026-05-14 19:49:57 +06:00
setShowInvestmentSuccessModal ( true ) ;
2026-04-22 01:09:51 +06:00
setNewInvestment ( {
planName : '' ,
planType : 'gold' ,
2026-05-19 17:25:32 +06:00
assetType : 'bike' ,
2026-04-22 01:09:51 +06:00
selectedBikeIds : [ ] ,
2026-05-19 17:25:32 +06:00
selectedBatteryIds : [ ] ,
2026-04-22 01:09:51 +06:00
totalInvestment : 0 ,
2026-05-15 03:10:37 +06:00
paidAmount : 0 ,
2026-05-15 12:25:24 +06:00
paymentType : 'full' ,
2026-04-22 01:09:51 +06:00
monthlyReturn : 0 ,
expectedRoi : 15 ,
startDate : new Date ( ) . toISOString ( ) . split ( 'T' ) [ 0 ] ,
endDate : '' ,
paymentMethod : 'bank' ,
transactionReference : '' ,
notes : ''
} ) ;
} ;
2026-05-19 17:25:32 +06:00
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 = ( ) = > {
2026-05-19 19:27:03 +06:00
if ( ! selectedBatteryPlanId ) {
toast . error ( 'Please select an investment plan' ) ;
return ;
}
if ( selectedBatteryIds . length === 0 ) {
toast . error ( 'Please select at least one battery' ) ;
2026-05-19 17:25:32 +06:00
return ;
}
2026-05-19 19:27:03 +06:00
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 ) ) ) ;
2026-05-19 17:25:32 +06:00
2026-05-19 19:27:03 +06:00
// 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 } ` ) ;
2026-05-19 17:25:32 +06:00
setShowAssignBatteryModal ( false ) ;
2026-05-19 19:27:03 +06:00
setSelectedBatteryIds ( [ ] ) ;
setSelectedBatteryPlanId ( '' ) ;
2026-05-19 17:25:32 +06:00
} ;
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 ] ) ;
2026-04-22 01:02:45 +06:00
return (
< div className = "p-4 lg:p-6" >
2026-05-14 13:54:02 +06:00
< button onClick = { ( ) = > router . push ( '/admin/investors' ) } className = "flex items-center gap-2 text-slate-600 hover:text-slate-800 mb-4" >
< ArrowLeft className = "w-4 h-4" / > Back to Investors
< / button >
{ /* Profile Card Header */ }
< div className = "bg-white rounded-xl shadow-sm border border-slate-100 overflow-hidden mb-6" >
< div className = "p-5" >
< div className = "flex flex-col lg:flex-row lg:items-start gap-5" >
{ /* Profile Image */ }
< div className = "relative group flex-shrink-0 mx-auto lg:mx-0" >
< div className = "w-24 h-24 rounded-full bg-gradient-to-br from-purple-500 to-blue-500 flex items-center justify-center shadow-lg border-4 border-white" >
< span className = "text-3xl font-extrabold text-white" > { investor . name . charAt ( 0 ) } < / span >
< / div >
< label className = "absolute bottom-0 right-0 w-8 h-8 bg-blue-600 rounded-full flex items-center justify-center cursor-pointer opacity-0 group-hover:opacity-100 transition-opacity shadow-lg border-2 border-white" >
< Camera className = "w-4 h-4 text-white" / >
< input type = "file" accept = "image/*" className = "hidden" onChange = { ( ) = > toast . success ( 'Profile image updated' ) } / >
< / label >
< / div >
2026-04-22 01:02:45 +06:00
2026-05-14 13:54:02 +06:00
{ /* Profile Info */ }
< div className = "flex-1" >
< div className = "flex flex-col lg:flex-row lg:items-start lg:justify-between gap-4" >
< div >
< h1 className = "text-2xl font-extrabold text-slate-800" > { investor . name } < / h1 >
< p className = "text-sm text-slate-500 mt-0.5" > { investor . id } & bull ; { investor . email } < / p >
< div className = "flex items-center gap-2 mt-1" >
{ investor . referralCode && (
< span className = "inline-flex items-center gap-1 text-xs font-medium px-2 py-0.5 rounded bg-purple-50 text-purple-700 border border-purple-100" >
< Star className = "w-3 h-3" / > Ref : { investor . referralCode }
< / span >
) }
{ investor . totalReferrals > 0 && (
< span className = "text-xs text-slate-400" > Referrals : { investor . totalReferrals } < / span >
) }
< / div >
< div className = "flex flex-wrap items-center gap-2 mt-2" >
< span className = { ` inline-flex items-center gap-1 text-xs font-medium px-2.5 py-1 rounded-full ${ statusColors [ investor . status ] } ` } > { investor . status } < / span >
{ investor . investments && investor . investments . length > 0 && (
< span className = { ` inline-flex items-center gap-1 text-xs font-medium px-2.5 py-1 rounded-full ${ planColors [ investor . investments [ 0 ] . planType ] } ` } >
{ investor . investments . length } Investment { investor . investments . length > 1 ? 's' : '' }
< / span >
) }
< span className = { ` inline-flex items-center gap-1 text-xs font-medium px-2.5 py-1 rounded-full ${ kycColors [ investor . kycStatus ] } ` } >
< ShieldCheck className = "w-3 h-3" / > KYC { investor . kycStatus }
< / span >
< span className = { ` inline-flex items-center gap-1 text-xs font-medium px-2.5 py-1 rounded-full ${ investor . riskLevel === 'low' ? 'bg-green-100 text-green-700' : investor . riskLevel === 'medium' ? 'bg-amber-100 text-amber-700' : 'bg-red-100 text-red-700' } ` } >
Risk : { investor . riskLevel }
< / span >
< / div >
< / div >
2026-05-14 22:28:46 +06:00
{ / * < d i v c l a s s N a m e = " f l e x i t e m s - c e n t e r g a p - 2 f l e x - s h r i n k - 0 " >
2026-05-14 13:54:02 +06:00
< button onClick = { ( ) = > setShowEditModal ( true ) } className = "py-2 px-4 border border-slate-200 text-slate-600 rounded-lg font-semibold text-sm hover:bg-slate-50 flex items-center gap-2" >
< Edit className = "w-4 h-4" / > Edit
< / button >
< button className = "py-2 px-4 bg-red-500 text-white rounded-lg font-semibold text-sm hover:bg-red-600 flex items-center gap-2" >
< Trash2 className = "w-4 h-4" / > Delete
< / button >
2026-05-14 22:28:46 +06:00
< / div > * / }
2026-05-14 13:54:02 +06:00
< / div >
2026-04-22 01:02:45 +06:00
2026-05-14 13:54:02 +06:00
{ /* Stats Row */ }
2026-05-19 17:33:09 +06:00
< div className = "grid grid-cols-3 lg:grid-cols-7 gap-3 mt-5" >
2026-05-14 13:54:02 +06:00
< div className = "p-3 bg-purple-50 rounded-lg border border-purple-100" >
< p className = "text-xs text-purple-600 font-medium" > Total Invested < / p >
< p className = "text-sm font-bold text-purple-700" > ৳ { investor . totalInvested . toLocaleString ( ) } < / p >
< / div >
< div className = "p-3 bg-green-50 rounded-lg border border-green-100" >
< p className = "text-xs text-green-600 font-medium" > Total Earnings < / p >
< p className = "text-sm font-bold text-green-700" > ৳ { investor . totalEarnings . toLocaleString ( ) } < / p >
< / div >
2026-05-14 22:25:24 +06:00
< div className = "p-3 bg-slate-50 rounded-lg border border-slate-100" >
< p className = "text-xs text-slate-600 font-medium" > Total Withdrawn < / p >
< p className = "text-sm font-bold text-slate-700" > ৳ { investor . totalWithdrawn . toLocaleString ( ) } < / p >
< / div >
2026-05-14 13:54:02 +06:00
< div className = "p-3 bg-blue-50 rounded-lg border border-blue-100" >
2026-05-14 22:25:24 +06:00
< p className = "text-xs text-blue-600 font-medium" > Current Balance < / p >
< p className = "text-sm font-bold text-blue-700" > ৳ { ( investor . totalEarnings - investor . totalWithdrawn - investor . pendingEarnings ) . toLocaleString ( ) } < / p >
2026-05-14 13:54:02 +06:00
< / div >
< div className = "p-3 bg-amber-50 rounded-lg border border-amber-100" >
< p className = "text-xs text-amber-600 font-medium" > Active Bikes < / p >
< p className = "text-sm font-bold text-amber-700" > { investor . activeBikes } < / p >
< / div >
2026-05-19 17:33:09 +06:00
< div className = "p-3 bg-emerald-50 rounded-lg border border-emerald-100" >
< p className = "text-xs text-emerald-600 font-medium" > Active Batteries < / p >
< p className = "text-sm font-bold text-emerald-700" > { batteries . filter ( b = > b . investorId === investorId ) . length } < / p >
< / div >
2026-05-14 13:54:02 +06:00
< div className = "p-3 bg-red-50 rounded-lg border border-red-100" >
2026-05-14 22:25:24 +06:00
< p className = "text-xs text-red-600 font-medium" > Pending Request < / p >
2026-05-14 13:54:02 +06:00
< p className = "text-sm font-bold text-red-700" > ৳ { investor . pendingEarnings . toLocaleString ( ) } < / p >
< / div >
< / div >
2026-04-22 01:02:45 +06:00
< / div >
< / div >
< / div >
< / div >
< div className = "bg-white rounded-xl shadow-sm border border-slate-100 mb-6" >
< div className = "border-b border-slate-100 flex overflow-x-auto" >
< button
onClick = { ( ) = > setActiveTab ( 'overview' ) }
className = { ` px-4 py-3 text-sm font-medium whitespace-nowrap ${ activeTab === 'overview' ? 'border-b-2 border-investor text-investor' : 'text-slate-500' } ` }
>
< User className = "w-4 h-4 inline mr-1" / > Overview
< / button >
< button
onClick = { ( ) = > setActiveTab ( 'investments' ) }
className = { ` px-4 py-3 text-sm font-medium whitespace-nowrap ${ activeTab === 'investments' ? 'border-b-2 border-investor text-investor' : 'text-slate-500' } ` }
>
< TrendingUp className = "w-4 h-4 inline mr-1" / > Investments ( { investor . investments ? . length || 0 } )
< / button >
< button
onClick = { ( ) = > setActiveTab ( 'bikes' ) }
className = { ` px-4 py-3 text-sm font-medium whitespace-nowrap ${ activeTab === 'bikes' ? 'border-b-2 border-investor text-investor' : 'text-slate-500' } ` }
>
< Bike className = "w-4 h-4 inline mr-1" / > Bikes ( { assignedBikes . length } )
< / button >
2026-05-19 17:25:32 +06:00
< button
onClick = { ( ) = > setActiveTab ( 'batteries' ) }
className = { ` px-4 py-3 text-sm font-medium whitespace-nowrap ${ activeTab === 'batteries' ? 'border-b-2 border-investor text-investor' : 'text-slate-500' } ` }
>
< Battery className = "w-4 h-4 inline mr-1" / > Batteries ( { batteries . filter ( b = > b . investorId === investorId ) . length } )
< / button >
2026-04-22 01:02:45 +06:00
< button
onClick = { ( ) = > setActiveTab ( 'financial' ) }
className = { ` px-4 py-3 text-sm font-medium whitespace-nowrap ${ activeTab === 'financial' ? 'border-b-2 border-investor text-investor' : 'text-slate-500' } ` }
>
2026-05-14 22:25:24 +06:00
< Banknote className = "w-4 h-4 inline mr-1" / > Account Info
< / button >
< button
onClick = { ( ) = > setActiveTab ( 'rentals' ) }
className = { ` px-4 py-3 text-sm font-medium whitespace-nowrap ${ activeTab === 'rentals' ? 'border-b-2 border-investor text-investor' : 'text-slate-500' } ` }
>
< History className = "w-4 h-4 inline mr-1" / > Rental History
2026-04-22 01:02:45 +06:00
< / button >
< button
onClick = { ( ) = > setActiveTab ( 'transactions' ) }
className = { ` px-4 py-3 text-sm font-medium whitespace-nowrap ${ activeTab === 'transactions' ? 'border-b-2 border-investor text-investor' : 'text-slate-500' } ` }
>
< DollarSign className = "w-4 h-4 inline mr-1" / > Transactions
< / button >
< button
onClick = { ( ) = > setActiveTab ( 'documents' ) }
className = { ` px-4 py-3 text-sm font-medium whitespace-nowrap ${ activeTab === 'documents' ? 'border-b-2 border-investor text-investor' : 'text-slate-500' } ` }
>
< FileText className = "w-4 h-4 inline mr-1" / > Documents
< / button >
2026-05-14 22:25:24 +06:00
2026-04-22 01:02:45 +06:00
< / div >
< div className = "p-5" >
{ activeTab === 'overview' && (
2026-05-14 13:54:02 +06:00
< div className = "space-y-4" >
< div className = "flex gap-4" >
< div className = "flex-1 space-y-4" >
< SectionCard title = "Personal Information" icon = { User } headerBg = "bg-emerald-50" headerBorder = "border-emerald-100" editKey = "personal" editingSection = { editingSection } setEditingSection = { setEditingSection } onEdit = { ( ) = > 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' ? (
< div className = "grid grid-cols-1 md:grid-cols-2 gap-3" >
< div className = "md:col-span-2" >
< label className = "text-xs text-slate-500 mb-1 block" > Full Name < / label >
< input type = "text" value = { editForm . fullName || '' } onChange = { ( e ) = > setEditForm ( { . . . editForm , fullName : e.target.value } ) } className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" / >
< / div >
< div >
< label className = "text-xs text-slate-500 mb-1 block" > Phone < / label >
< input type = "text" value = { editForm . phone || '' } onChange = { ( e ) = > setEditForm ( { . . . editForm , phone : e.target.value } ) } className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" / >
< / div >
< div >
< label className = "text-xs text-slate-500 mb-1 block" > Alternate Phone < / label >
< input type = "text" value = { editForm . phoneAlt || '' } onChange = { ( e ) = > setEditForm ( { . . . editForm , phoneAlt : e.target.value } ) } className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" / >
< / div >
< div className = "md:col-span-2" >
< label className = "text-xs text-slate-500 mb-1 block" > Email < / label >
< input type = "email" value = { editForm . email || '' } onChange = { ( e ) = > setEditForm ( { . . . editForm , email : e.target.value } ) } className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" / >
< / div >
< div >
< label className = "text-xs text-slate-500 mb-1 block" > NID Number < / label >
< input type = "text" value = { editForm . nidNumber || '' } onChange = { ( e ) = > setEditForm ( { . . . editForm , nidNumber : e.target.value } ) } className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" / >
< / div >
< div >
< label className = "text-xs text-slate-500 mb-1 block" > TIN Number < / label >
< input type = "text" value = { editForm . tinNumber || '' } onChange = { ( e ) = > setEditForm ( { . . . editForm , tinNumber : e.target.value } ) } className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" / >
< / div >
< div >
< label className = "text-xs text-slate-500 mb-1 block" > Date of Birth < / label >
< input type = "date" value = { editForm . dateOfBirth || '' } onChange = { ( e ) = > setEditForm ( { . . . editForm , dateOfBirth : e.target.value } ) } className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" / >
< / div >
< div >
< label className = "text-xs text-slate-500 mb-1 block" > Gender < / label >
< select value = { editForm . gender || '' } onChange = { ( e ) = > setEditForm ( { . . . editForm , gender : e.target.value } ) } className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" >
< option value = "" > Select < / option >
< option value = "Male" > Male < / option >
< option value = "Female" > Female < / option >
< option value = "Other" > Other < / option >
< / select >
< / div >
< div >
< label className = "text-xs text-slate-500 mb-1 block" > Occupation < / label >
< input type = "text" value = { editForm . occupation || '' } onChange = { ( e ) = > setEditForm ( { . . . editForm , occupation : e.target.value } ) } className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" / >
< / div >
< div >
< label className = "text-xs text-slate-500 mb-1 block" > Blood Group < / label >
< select value = { editForm . bloodGroup || '' } onChange = { ( e ) = > setEditForm ( { . . . editForm , bloodGroup : e.target.value } ) } className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" >
< option value = "" > Select < / option >
< option value = "A+" > A + < / option >
< option value = "A-" > A - < / option >
< option value = "B+" > B + < / option >
< option value = "B-" > B - < / option >
< option value = "O+" > O + < / option >
< option value = "O-" > O - < / option >
< option value = "AB+" > AB + < / option >
< option value = "AB-" > AB - < / option >
< / select >
< / div >
< div >
< label className = "text-xs text-slate-500 mb-1 block" > Marital Status < / label >
< select value = { editForm . maritalStatus || '' } onChange = { ( e ) = > setEditForm ( { . . . editForm , maritalStatus : e.target.value } ) } className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" >
< option value = "" > Select < / option >
< option value = "Single" > Single < / option >
< option value = "Married" > Married < / option >
< option value = "Divorced" > Divorced < / option >
< option value = "Widowed" > Widowed < / option >
< / select >
< / div >
< div >
< label className = "text-xs text-slate-500 mb-1 block" > Religion < / label >
< select value = { editForm . religion || '' } onChange = { ( e ) = > setEditForm ( { . . . editForm , religion : e.target.value } ) } className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" >
< option value = "" > Select < / option >
< option value = "Islam" > Islam < / option >
< option value = "Hinduism" > Hinduism < / option >
< option value = "Christianity" > Christianity < / option >
< option value = "Buddhism" > Buddhism < / option >
< option value = "Other" > Other < / option >
< / select >
< / div >
< div >
< label className = "text-xs text-slate-500 mb-1 block" > Nationality < / label >
< input type = "text" value = { editForm . nationality || '' } onChange = { ( e ) = > setEditForm ( { . . . editForm , nationality : e.target.value } ) } className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" / >
< / div >
2026-04-22 01:02:45 +06:00
< / div >
2026-05-14 13:54:02 +06:00
) : (
< div className = "grid grid-cols-1 md:grid-cols-2 gap-3" >
< div className = "p-3 bg-slate-50 rounded-lg" >
< p className = "text-xs text-slate-500" > Full Name < / p >
< p className = "font-medium text-slate-700" > { investor . name } < / p >
< / div >
< div className = "p-3 bg-slate-50 rounded-lg" >
< p className = "text-xs text-slate-500" > Phone < / p >
< p className = "font-medium text-slate-700" > { investor . phone } < / p >
< / div >
< div className = "p-3 bg-slate-50 rounded-lg" >
< p className = "text-xs text-slate-500" > Alternate Phone < / p >
< p className = "font-medium text-slate-700" > { investor . phoneAlt || '-' } < / p >
< / div >
< div className = "p-3 bg-slate-50 rounded-lg" >
< p className = "text-xs text-slate-500" > Email < / p >
< p className = "font-medium text-slate-700" > { investor . email } < / p >
< / div >
< div className = "p-3 bg-slate-50 rounded-lg" >
< p className = "text-xs text-slate-500" > NID Number < / p >
< p className = "font-medium text-slate-700" > { investor . nidNumber || '-' } < / p >
< / div >
< div className = "p-3 bg-slate-50 rounded-lg" >
< p className = "text-xs text-slate-500" > TIN Number < / p >
< p className = "font-medium text-slate-700" > { investor . tinNumber || '-' } < / p >
< / div >
< div className = "p-3 bg-slate-50 rounded-lg" >
< p className = "text-xs text-slate-500" > Date of Birth < / p >
< p className = "font-medium text-slate-700" > { investor . dateOfBirth || '-' } < / p >
< / div >
< div className = "p-3 bg-slate-50 rounded-lg" >
< p className = "text-xs text-slate-500" > Gender < / p >
< p className = "font-medium text-slate-700" > { investor . gender || '-' } < / p >
< / div >
< div className = "p-3 bg-slate-50 rounded-lg" >
< p className = "text-xs text-slate-500" > Occupation < / p >
< p className = "font-medium text-slate-700" > { investor . occupation || '-' } < / p >
< / div >
< div className = "p-3 bg-slate-50 rounded-lg" >
< p className = "text-xs text-slate-500" > Blood Group < / p >
< p className = "font-medium text-slate-700" > { ( investor as any ) . bloodGroup || '-' } < / p >
< / div >
< div className = "p-3 bg-slate-50 rounded-lg" >
< p className = "text-xs text-slate-500" > Marital Status < / p >
< p className = "font-medium text-slate-700" > { ( investor as any ) . maritalStatus || '-' } < / p >
< / div >
< div className = "p-3 bg-slate-50 rounded-lg" >
< p className = "text-xs text-slate-500" > Religion < / p >
< p className = "font-medium text-slate-700" > { ( investor as any ) . religion || '-' } < / p >
< / div >
< div className = "p-3 bg-slate-50 rounded-lg" >
< p className = "text-xs text-slate-500" > Nationality < / p >
< p className = "font-medium text-slate-700" > { ( investor as any ) . nationality || 'Bangladeshi' } < / p >
< / div >
2026-04-22 01:02:45 +06:00
< / div >
2026-05-14 13:54:02 +06:00
) }
< / SectionCard >
< SectionCard title = "Nominee Details" icon = { Heart } headerBg = "bg-orange-50" headerBorder = "border-orange-100" editKey = "nominee" editingSection = { editingSection } setEditingSection = { setEditingSection } onEdit = { ( ) = > 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' ? (
< div className = "grid grid-cols-1 md:grid-cols-2 gap-3" >
< div >
< label className = "text-xs text-slate-500 mb-1 block" > Nominee Name < / label >
< input type = "text" value = { editForm . nomineeName || '' } onChange = { ( e ) = > setEditForm ( { . . . editForm , nomineeName : e.target.value } ) } className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" / >
< / div >
< div >
< label className = "text-xs text-slate-500 mb-1 block" > Relationship < / label >
< select value = { editForm . nomineeRelation || '' } onChange = { ( e ) = > setEditForm ( { . . . editForm , nomineeRelation : e.target.value } ) } className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" >
< option value = "" > Select < / option >
< option value = "Spouse" > Spouse < / option >
< option value = "Child" > Child < / option >
< option value = "Parent" > Parent < / option >
< option value = "Sibling" > Sibling < / option >
< option value = "Other" > Other < / option >
< / select >
< / div >
< div >
< label className = "text-xs text-slate-500 mb-1 block" > NID Number < / label >
< input type = "text" value = { editForm . nomineeNid || '' } onChange = { ( e ) = > setEditForm ( { . . . editForm , nomineeNid : e.target.value } ) } className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" / >
< / div >
< div >
< label className = "text-xs text-slate-500 mb-1 block" > Phone < / label >
< input type = "text" value = { editForm . nomineePhone || '' } onChange = { ( e ) = > setEditForm ( { . . . editForm , nomineePhone : e.target.value } ) } className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" / >
< / div >
< div >
< label className = "text-xs text-slate-500 mb-1 block" > Email < / label >
< input type = "email" value = { editForm . nomineeEmail || '' } onChange = { ( e ) = > setEditForm ( { . . . editForm , nomineeEmail : e.target.value } ) } className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" / >
< / div >
< div >
< label className = "text-xs text-slate-500 mb-1 block" > Share % < / label >
< input type = "number" value = { editForm . nomineeShare || '' } onChange = { ( e ) = > setEditForm ( { . . . editForm , nomineeShare : e.target.value } ) } className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" / >
< / div >
< div className = "md:col-span-2" >
< label className = "text-xs text-slate-500 mb-1 block" > Address < / label >
< input type = "text" value = { editForm . nomineeAddress || '' } onChange = { ( e ) = > setEditForm ( { . . . editForm , nomineeAddress : e.target.value } ) } className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" / >
< / div >
2026-04-22 01:02:45 +06:00
< / div >
2026-05-14 13:54:02 +06:00
) : (
< div className = "grid grid-cols-1 md:grid-cols-2 gap-3" >
< div className = "p-3 bg-slate-50 rounded-lg" >
< p className = "text-xs text-slate-500" > Nominee Name < / p >
< p className = "font-medium text-slate-700" > { ( investor as any ) . nomineeName || '-' } < / p >
< / div >
< div className = "p-3 bg-slate-50 rounded-lg" >
< p className = "text-xs text-slate-500" > Relationship < / p >
< p className = "font-medium text-slate-700" > { ( investor as any ) . nomineeRelation || '-' } < / p >
< / div >
< div className = "p-3 bg-slate-50 rounded-lg" >
< p className = "text-xs text-slate-500" > NID Number < / p >
< p className = "font-medium text-slate-700" > { ( investor as any ) . nomineeNid || '-' } < / p >
< / div >
< div className = "p-3 bg-slate-50 rounded-lg" >
< p className = "text-xs text-slate-500" > Phone < / p >
< p className = "font-medium text-slate-700" > { ( investor as any ) . nomineePhone || '-' } < / p >
< / div >
< div className = "p-3 bg-slate-50 rounded-lg" >
< p className = "text-xs text-slate-500" > Email < / p >
< p className = "font-medium text-slate-700" > { ( investor as any ) . nomineeEmail || '-' } < / p >
< / div >
< div className = "p-3 bg-slate-50 rounded-lg" >
< p className = "text-xs text-slate-500" > Share % < / p >
< p className = "font-medium text-slate-700" > { ( investor as any ) . nomineeShare || '-' } < / p >
< / div >
< div className = "p-3 bg-slate-50 rounded-lg md:col-span-2" >
< p className = "text-xs text-slate-500" > Address < / p >
< p className = "font-medium text-slate-700" > { ( investor as any ) . nomineeAddress || '-' } < / p >
< / div >
2026-04-22 01:02:45 +06:00
< / div >
2026-05-14 13:54:02 +06:00
) }
< / SectionCard >
< SectionCard title = "Emergency Contact" icon = { PhoneCall } headerBg = "bg-red-50" headerBorder = "border-red-100" editKey = "emergency" editingSection = { editingSection } setEditingSection = { setEditingSection } onEdit = { ( ) = > 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' ? (
< div className = "grid grid-cols-1 md:grid-cols-2 gap-3" >
2026-04-22 01:02:45 +06:00
< div >
2026-05-14 13:54:02 +06:00
< label className = "text-xs text-slate-500 mb-1 block" > Contact Name < / label >
< input type = "text" value = { editForm . emergencyName || '' } onChange = { ( e ) = > setEditForm ( { . . . editForm , emergencyName : e.target.value } ) } className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" / >
< / div >
< div >
< label className = "text-xs text-slate-500 mb-1 block" > Relationship < / label >
< select value = { editForm . emergencyRelation || '' } onChange = { ( e ) = > setEditForm ( { . . . editForm , emergencyRelation : e.target.value } ) } className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" >
< option value = "" > Select < / option >
< option value = "Spouse" > Spouse < / option >
< option value = "Child" > Child < / option >
< option value = "Parent" > Parent < / option >
< option value = "Sibling" > Sibling < / option >
< option value = "Friend" > Friend < / option >
< option value = "Other" > Other < / option >
< / select >
< / div >
< div >
< label className = "text-xs text-slate-500 mb-1 block" > Phone < / label >
< input type = "text" value = { editForm . emergencyPhone || '' } onChange = { ( e ) = > setEditForm ( { . . . editForm , emergencyPhone : e.target.value } ) } className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" / >
< / div >
< div >
< label className = "text-xs text-slate-500 mb-1 block" > Email < / label >
< input type = "email" value = { editForm . emergencyEmail || '' } onChange = { ( e ) = > setEditForm ( { . . . editForm , emergencyEmail : e.target.value } ) } className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" / >
< / div >
< div className = "md:col-span-2" >
< label className = "text-xs text-slate-500 mb-1 block" > Address < / label >
< input type = "text" value = { editForm . emergencyAddress || '' } onChange = { ( e ) = > setEditForm ( { . . . editForm , emergencyAddress : e.target.value } ) } className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" / >
< / div >
< / div >
) : (
< div className = "grid grid-cols-1 md:grid-cols-2 gap-3" >
< div className = "p-3 bg-slate-50 rounded-lg" >
< p className = "text-xs text-slate-500" > Contact Name < / p >
< p className = "font-medium text-slate-700" > { investor . emergencyContactName || '-' } < / p >
< / div >
< div className = "p-3 bg-slate-50 rounded-lg" >
< p className = "text-xs text-slate-500" > Relationship < / p >
< p className = "font-medium text-slate-700" > { investor . emergencyContactRelation || '-' } < / p >
< / div >
< div className = "p-3 bg-slate-50 rounded-lg" >
< p className = "text-xs text-slate-500" > Phone < / p >
< p className = "font-medium text-slate-700" > { investor . emergencyContactPhone || '-' } < / p >
< / div >
< div className = "p-3 bg-slate-50 rounded-lg" >
< p className = "text-xs text-slate-500" > Email < / p >
< p className = "font-medium text-slate-700" > { ( investor as any ) . emergencyEmail || '-' } < / p >
< / div >
< div className = "p-3 bg-slate-50 rounded-lg md:col-span-2" >
< p className = "text-xs text-slate-500" > Address < / p >
< p className = "font-medium text-slate-700" > { ( investor as any ) . emergencyAddress || '-' } < / p >
2026-04-22 01:02:45 +06:00
< / div >
< / div >
) }
2026-05-14 13:54:02 +06:00
< / SectionCard >
< / div >
< div className = "flex-1 space-y-4" >
< SectionCard title = "Investment Information" icon = { Briefcase } headerBg = "bg-blue-50" headerBorder = "border-blue-100" editKey = "investmentInfo" editingSection = { editingSection } setEditingSection = { setEditingSection } onEdit = { ( ) = > 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' ? (
< div className = "grid grid-cols-1 md:grid-cols-2 gap-3" >
< div className = "md:col-span-2" >
< label className = "text-xs text-slate-500 mb-1 block" > Company / Business Name < / label >
< input type = "text" value = { editForm . companyName || '' } onChange = { ( e ) = > setEditForm ( { . . . editForm , companyName : e.target.value } ) } className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" / >
< / div >
2026-04-22 01:02:45 +06:00
< div >
2026-05-14 13:54:02 +06:00
< label className = "text-xs text-slate-500 mb-1 block" > Monthly Income ( ৳ ) < / label >
< input type = "number" value = { editForm . monthlyIncome || '' } onChange = { ( e ) = > setEditForm ( { . . . editForm , monthlyIncome : e.target.value } ) } className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" / >
< / div >
< div >
< label className = "text-xs text-slate-500 mb-1 block" > Investment Source < / label >
< input type = "text" value = { editForm . investmentSource || '' } onChange = { ( e ) = > 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" / >
< / div >
< div className = "md:col-span-2" >
< label className = "text-xs text-slate-500 mb-1 block" > Profession / Occupation < / label >
< input type = "text" value = { editForm . profession || '' } onChange = { ( e ) = > setEditForm ( { . . . editForm , profession : e.target.value } ) } className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" / >
< / div >
< / div >
) : (
< div className = "space-y-2" >
< div className = "grid grid-cols-1 md:grid-cols-2 gap-2" >
< div className = "p-3 bg-slate-50 rounded-lg" >
< p className = "text-xs text-slate-500" > Company / Business Name < / p >
< p className = "font-medium text-slate-700" > { ( investor as any ) . companyName || '-' } < / p >
< / div >
< div className = "p-3 bg-slate-50 rounded-lg" >
< p className = "text-xs text-slate-500" > Monthly Income < / p >
< p className = "font-medium text-slate-700" > { ( investor as any ) . monthlyIncome || '-' } < / p >
< / div >
< div className = "p-3 bg-slate-50 rounded-lg" >
< p className = "text-xs text-slate-500" > Investment Source < / p >
< p className = "font-medium text-slate-700" > { ( investor as any ) . investmentSource || '-' } < / p >
< / div >
< div className = "p-3 bg-slate-50 rounded-lg" >
< p className = "text-xs text-slate-500" > Profession / Occupation < / p >
< p className = "font-medium text-slate-700" > { investor . occupation || '-' } < / p >
< / div >
2026-04-22 01:02:45 +06:00
< / div >
< / div >
) }
2026-05-14 13:54:02 +06:00
< / SectionCard >
< SectionCard title = "Present Address" icon = { Home } headerBg = "bg-green-50" headerBorder = "border-green-100" editKey = "presentAddress" editingSection = { editingSection } setEditingSection = { setEditingSection } onEdit = { ( ) = > {
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' ? (
< div className = "grid grid-cols-1 md:grid-cols-2 gap-3" >
< div className = "md:col-span-2" >
< label className = "text-xs text-slate-500 mb-1 block" > Address Line 1 ( House / Flat + Road ) < / label >
< input type = "text" value = { editForm . addressLine1 || '' } onChange = { ( e ) = > setEditForm ( { . . . editForm , addressLine1 : e.target.value } ) } className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" / >
< / div >
< div className = "md:col-span-2" >
< label className = "text-xs text-slate-500 mb-1 block" > Address Line 2 ( Block + Area ) < / label >
< input type = "text" value = { editForm . addressLine2 || '' } onChange = { ( e ) = > setEditForm ( { . . . editForm , addressLine2 : e.target.value } ) } className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" / >
< / div >
< div >
< label className = "text-xs text-slate-500 mb-1 block" > Division < / label >
< select value = { editForm . division || '' } onChange = { ( e ) = > setEditForm ( { . . . editForm , division : e.target.value } ) } className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" >
< option value = "Dhaka" > Dhaka < / option >
< option value = "Chittagong" > Chittagong < / option >
< option value = "Sylhet" > Sylhet < / option >
< option value = "Khulna" > Khulna < / option >
< option value = "Barisal" > Barisal < / option >
< option value = "Rangpur" > Rangpur < / option >
< option value = "Mymensingh" > Mymensingh < / option >
< / select >
< / div >
< div >
< label className = "text-xs text-slate-500 mb-1 block" > District < / label >
< select value = { editForm . district || '' } onChange = { ( e ) = > setEditForm ( { . . . editForm , district : e.target.value } ) } className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" >
< option value = "Dhaka" > Dhaka < / option >
< option value = "Gazipur" > Gazipur < / option >
< option value = "Narayanganj" > Narayanganj < / option >
< option value = "Tangail" > Tangail < / option >
< option value = "Kishoreganj" > Kishoreganj < / option >
< / select >
< / div >
< div >
< label className = "text-xs text-slate-500 mb-1 block" > Thana < / label >
< input type = "text" value = { editForm . thana || '' } onChange = { ( e ) = > setEditForm ( { . . . editForm , thana : e.target.value } ) } className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" / >
< / div >
2026-04-22 01:02:45 +06:00
< div >
2026-05-14 13:54:02 +06:00
< label className = "text-xs text-slate-500 mb-1 block" > Zip Code < / label >
< input type = "text" value = { editForm . zipCode || '' } onChange = { ( e ) = > setEditForm ( { . . . editForm , zipCode : e.target.value } ) } className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" / >
< / div >
< div className = "md:col-span-2" >
< label className = "text-xs text-slate-500 mb-1 block" > Landmark < / label >
< input type = "text" value = { editForm . landmark || '' } onChange = { ( e ) = > setEditForm ( { . . . editForm , landmark : e.target.value } ) } className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" / >
< / div >
< / div >
) : (
< div className = "space-y-2" >
< div className = "p-3 bg-slate-50 rounded-lg" >
< p className = "text-xs text-slate-500" > Address Line 1 < / p >
< p className = "font-medium text-slate-700" > { investor . address } < / p >
< / div >
< div className = "p-3 bg-slate-50 rounded-lg" >
< p className = "text-xs text-slate-500" > Landmark < / p >
< p className = "font-medium text-slate-700" > - < / p >
< / div >
< div className = "grid grid-cols-2 gap-2" >
< div className = "p-3 bg-slate-50 rounded-lg" >
< p className = "text-xs text-slate-500" > Division < / p >
< p className = "font-medium text-slate-700" > Dhaka < / p >
< / div >
< div className = "p-3 bg-slate-50 rounded-lg" >
< p className = "text-xs text-slate-500" > District < / p >
< p className = "font-medium text-slate-700" > Dhaka < / p >
< / div >
< div className = "p-3 bg-slate-50 rounded-lg" >
< p className = "text-xs text-slate-500" > Thana < / p >
< p className = "font-medium text-slate-700" > - < / p >
< / div >
< div className = "p-3 bg-slate-50 rounded-lg" >
< p className = "text-xs text-slate-500" > Zip Code < / p >
< p className = "font-medium text-slate-700" > 1205 < / p >
< / div >
2026-04-22 01:02:45 +06:00
< / div >
< / div >
) }
2026-05-14 13:54:02 +06:00
< / SectionCard >
2026-04-22 01:02:45 +06:00
2026-05-14 13:54:02 +06:00
< SectionCard title = "Permanent Address" icon = { Home } headerBg = "bg-emerald-50" headerBorder = "border-emerald-100" editKey = "permanentAddress" editingSection = { editingSection } setEditingSection = { setEditingSection } onEdit = { ( ) = > {
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' ? (
2026-04-22 01:02:45 +06:00
< div >
2026-05-14 13:54:02 +06:00
< div className = "flex items-center gap-2 mb-4" >
< input type = "checkbox" id = "sameAsPresent" checked = { editForm . isSameAsPresent || false } onChange = { ( e ) = > setEditForm ( { . . . editForm , isSameAsPresent : e.target.checked } ) } className = "rounded text-investor" / >
< label htmlFor = "sameAsPresent" className = "text-sm text-slate-600" > Same as Present Address < / label >
< / div >
{ ! editForm . isSameAsPresent && (
< div className = "grid grid-cols-1 md:grid-cols-2 gap-3" >
< div className = "md:col-span-2" >
< label className = "text-xs text-slate-500 mb-1 block" > Address Line 1 ( House / Flat + Road ) < / label >
< input type = "text" value = { editForm . addressLine1 || '' } onChange = { ( e ) = > setEditForm ( { . . . editForm , addressLine1 : e.target.value } ) } className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" / >
< / div >
< div className = "md:col-span-2" >
< label className = "text-xs text-slate-500 mb-1 block" > Address Line 2 ( Block + Area ) < / label >
< input type = "text" value = { editForm . addressLine2 || '' } onChange = { ( e ) = > setEditForm ( { . . . editForm , addressLine2 : e.target.value } ) } className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" / >
< / div >
< div >
< label className = "text-xs text-slate-500 mb-1 block" > Division < / label >
< select value = { editForm . division || '' } onChange = { ( e ) = > setEditForm ( { . . . editForm , division : e.target.value } ) } className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" >
< option value = "Dhaka" > Dhaka < / option >
< option value = "Chittagong" > Chittagong < / option >
< option value = "Sylhet" > Sylhet < / option >
< option value = "Khulna" > Khulna < / option >
< option value = "Barisal" > Barisal < / option >
< option value = "Rangpur" > Rangpur < / option >
< option value = "Mymensingh" > Mymensingh < / option >
< / select >
< / div >
< div >
< label className = "text-xs text-slate-500 mb-1 block" > District < / label >
< select value = { editForm . district || '' } onChange = { ( e ) = > setEditForm ( { . . . editForm , district : e.target.value } ) } className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" >
< option value = "Dhaka" > Dhaka < / option >
< option value = "Gazipur" > Gazipur < / option >
< option value = "Narayanganj" > Narayanganj < / option >
< option value = "Tangail" > Tangail < / option >
< option value = "Kishoreganj" > Kishoreganj < / option >
< / select >
< / div >
< div >
< label className = "text-xs text-slate-500 mb-1 block" > Thana < / label >
< input type = "text" value = { editForm . thana || '' } onChange = { ( e ) = > setEditForm ( { . . . editForm , thana : e.target.value } ) } className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" / >
< / div >
< div >
< label className = "text-xs text-slate-500 mb-1 block" > Zip Code < / label >
< input type = "text" value = { editForm . zipCode || '' } onChange = { ( e ) = > setEditForm ( { . . . editForm , zipCode : e.target.value } ) } className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" / >
< / div >
< / div >
) }
2026-04-22 01:02:45 +06:00
< / div >
2026-05-14 13:54:02 +06:00
) : (
< div className = "space-y-2" >
< div className = "flex items-center gap-2 mb-2" >
< span className = "text-xs bg-blue-100 text-blue-700 px-2 py-1 rounded-full" > Same as Present < / span >
< / div >
< div className = "p-3 bg-slate-50 rounded-lg" >
< p className = "text-xs text-slate-500" > Address Line 1 < / p >
< p className = "font-medium text-slate-700" > { investor . address } < / p >
< / div >
< div className = "p-3 bg-slate-50 rounded-lg" >
< p className = "text-xs text-slate-500" > Landmark < / p >
< p className = "font-medium text-slate-700" > - < / p >
< / div >
< div className = "grid grid-cols-2 gap-2" >
< div className = "p-3 bg-slate-50 rounded-lg" >
< p className = "text-xs text-slate-500" > Division < / p >
< p className = "font-medium text-slate-700" > Dhaka < / p >
< / div >
< div className = "p-3 bg-slate-50 rounded-lg" >
< p className = "text-xs text-slate-500" > District < / p >
< p className = "font-medium text-slate-700" > Dhaka < / p >
< / div >
< div className = "p-3 bg-slate-50 rounded-lg" >
< p className = "text-xs text-slate-500" > Thana < / p >
< p className = "font-medium text-slate-700" > - < / p >
< / div >
< div className = "p-3 bg-slate-50 rounded-lg" >
< p className = "text-xs text-slate-500" > Zip Code < / p >
< p className = "font-medium text-slate-700" > 1205 < / p >
< / div >
2026-04-22 01:02:45 +06:00
< / div >
< / div >
) }
2026-05-14 13:54:02 +06:00
< / SectionCard >
2026-04-22 01:02:45 +06:00
< / div >
2026-05-14 13:54:02 +06:00
< / div >
2026-04-22 01:02:45 +06:00
{ investor . notes && (
< div className = "mt-6" >
< h3 className = "font-semibold text-slate-800 mb-2" > Notes < / h3 >
< p className = "text-sm text-slate-600 bg-slate-50 p-3 rounded-lg" > { investor . notes } < / p >
< / div >
) }
< / div >
) }
{ activeTab === 'bikes' && (
< div >
2026-05-19 18:11:35 +06:00
< div className = "flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 mb-6" >
2026-05-14 19:49:57 +06:00
< div >
2026-05-19 18:11:35 +06:00
< h3 className = "font-bold text-lg text-slate-800 flex items-center gap-2" >
< Bike className = "w-5 h-5 text-investor" / >
Assigned Bikes
< / h3 >
< p className = "text-sm text-slate-500" >
{ assignedBikes . length } bikes across { investor . investments ? . filter ( ( inv : any ) = > inv . assetType === 'bike' || ! inv . assetType ) . length || 0 } EV Investment Plans
< / p >
< / div >
< div className = "flex flex-wrap items-center gap-2" >
< button
onClick = { ( ) = > {
2026-05-19 19:27:03 +06:00
setSelectedBikeIds ( [ ] ) ;
setSelectedBikePlanId ( '' ) ;
2026-05-19 18:11:35 +06:00
setShowAssignBikeModal ( true ) ;
} }
className = "px-4 py-2 bg-blue-50 text-blue-700 border border-blue-200 rounded-lg text-sm font-semibold hover:bg-blue-100 flex items-center gap-2 transition-colors"
>
< Plus className = "w-4 h-4" / > Assign Existing
< / button >
2026-05-14 19:49:57 +06:00
< / div >
2026-04-22 01:02:45 +06:00
< / div >
< div className = "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4" >
2026-05-14 19:49:57 +06:00
{ assignedBikes . map ( bike = > {
const investment = investor . investments ? . find ( ( inv : any ) = > inv . id === bike . investmentId ) ;
const planColors : Record < string , string > = {
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 < string , string > = {
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 < string , string > = {
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 < string , { label : string ; color : string } > = {
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 < string , { bg : string ; color : string } > = {
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' } ,
} ;
2026-05-19 18:11:35 +06:00
const rentalInfo = rentalTypes [ bike . rentalType || 'single_rent' ] || rentalTypes . single_rent ;
2026-05-14 19:49:57 +06:00
const planType = investment ? . planType || 'gold' ;
const status = statusConfig [ bike . status ] || statusConfig . available ;
return (
2026-05-19 18:11:35 +06:00
< div
2026-05-14 19:49:57 +06:00
key = { bike . id }
2026-05-19 18:11:35 +06:00
className = { ` block bg-white rounded-xl border ${ planBg [ planType ] } overflow-hidden hover:shadow-lg transition-all group relative ` }
2026-05-14 19:49:57 +06:00
>
< div className = { ` h-2 bg-gradient-to-r ${ planColors [ planType ] } ` } / >
< div className = "p-4" >
< div className = "flex items-start gap-3 mb-3" >
< div className = { ` w-12 h-12 rounded-xl bg-gradient-to-br ${ planColors [ planType ] } flex items-center justify-center shadow-sm ` } >
< Bike className = "w-6 h-6 text-white" / >
< / div >
< div className = "flex-1 min-w-0" >
< h4 className = "font-semibold text-slate-800 truncate" > { bike . model } < / h4 >
< p className = "text-sm text-slate-500" > { bike . brand } < / p >
< / div >
< span className = { ` px-2 py-1 rounded-full text-xs font-medium ${ status . bg } ${ status . color } capitalize ` } >
{ bike . status }
< / span >
2026-04-22 01:02:45 +06:00
< / div >
2026-05-14 19:49:57 +06:00
< div className = { ` inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-medium ${ planBadges [ planType ] } capitalize mb-3 ` } >
2026-05-14 22:25:24 +06:00
< span className = { ` w-2 h-2 rounded-full ${ planType === 'silver' ? 'bg-slate-500' :
2026-05-14 19:49:57 +06:00
planType === 'gold' ? 'bg-amber-500' :
2026-05-19 19:10:12 +06:00
planType === 'platinum' ? 'bg-purple-500' : 'bg-blue-500'
2026-05-14 22:25:24 +06:00
} ` } />
2026-05-14 19:49:57 +06:00
{ planType } Plan • { investment ? . planName || 'Investment' }
< / div >
< div className = "grid grid-cols-2 gap-2 mb-3" >
< div className = "bg-white/80 rounded-lg p-2" >
< p className = "text-xs text-slate-400" > Plate < / p >
< p className = "text-sm font-medium text-slate-700 truncate" > { bike . plateNumber . split ( '-' ) . pop ( ) } < / p >
< / div >
< div className = "bg-white/80 rounded-lg p-2" >
< p className = "text-xs text-slate-400" > Location < / p >
< p className = "text-sm font-medium text-slate-700 truncate" > { bike . location } < / p >
< / div >
< / div >
< div className = "bg-white/80 rounded-lg p-3 space-y-2" >
< div className = "flex items-center justify-between" >
< span className = "text-xs text-slate-500" > Rental Type < / span >
< span className = { ` text-xs font-semibold ${ rentalInfo . color } ` } > { rentalInfo . label } < / span >
< / div >
< div className = "flex items-center justify-between" >
< span className = "text-xs text-slate-500" > Total Earnings < / span >
< span className = "text-sm font-bold text-green-600" > ৳ { bike . totalEarnings ? . toLocaleString ( ) || 0 } < / span >
< / div >
< div className = "flex items-center justify-between" >
< span className = "text-xs text-slate-500" > Battery < / span >
2026-05-14 22:25:24 +06:00
< span className = { ` text-xs font-medium ${ bike . batteryLevel > 50 ? 'text-green-600' :
2026-05-14 19:49:57 +06:00
bike . batteryLevel > 20 ? 'text-amber-600' : 'text-red-600'
2026-05-14 22:25:24 +06:00
} ` }>{bike.batteryLevel}%</span>
2026-05-14 19:49:57 +06:00
< / div >
2026-04-22 01:02:45 +06:00
< / div >
2026-05-19 18:11:35 +06:00
< div className = "mt-3 pt-3 border-t border-slate-100 flex justify-end gap-2" >
2026-05-19 20:27:26 +06:00
< Link href = { ` /admin/fleet/ ${ bike . id } ` } className = "text-xs text-slate-500 hover:text-slate-800 font-semibold transition-colors px-2 py-1 bg-slate-100 rounded" >
2026-05-19 18:11:35 +06:00
View Details
< / Link >
< button
onClick = { ( ) = > {
2026-05-19 19:27:03 +06:00
setUnassignConfirmModal ( {
show : true ,
type : 'bike' ,
id : bike.id ,
name : bike.model ,
details : bike.plateNumber
} ) ;
2026-05-19 18:11:35 +06:00
} }
className = "text-xs text-red-600 hover:text-red-800 font-semibold transition-colors px-2 py-1 bg-red-50 rounded"
>
Unassign
< / button >
< / div >
2026-04-22 01:02:45 +06:00
< / div >
2026-05-19 18:11:35 +06:00
< / div >
2026-05-14 19:49:57 +06:00
) ;
} ) }
2026-04-22 01:02:45 +06:00
{ assignedBikes . length === 0 && (
2026-05-14 19:49:57 +06:00
< div className = "col-span-full text-center py-12 border-2 border-dashed border-slate-200 rounded-xl" >
< Bike className = "w-12 h-12 mx-auto mb-3 text-slate-300" / >
< p className = "text-slate-500" > No bikes assigned to this investor < / p >
< p className = "text-sm text-slate-400 mt-1" > Bikes will appear here once assigned to investments < / p >
2026-04-22 01:02:45 +06:00
< / div >
) }
< / div >
< / div >
) }
2026-05-19 17:25:32 +06:00
{ activeTab === 'batteries' && (
< div >
< div className = "flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 mb-6" >
< div >
< h3 className = "font-bold text-lg text-slate-800 flex items-center gap-2" >
< Battery className = "w-5 h-5 text-emerald-600 animate-pulse" / >
Assigned Batteries
< / h3 >
< p className = "text-sm text-slate-500" >
{ batteries . filter ( b = > b . investorId === investorId ) . length } battery packs assigned to this partner
< / p >
< / div >
< div className = "flex flex-wrap items-center gap-2" >
< button
onClick = { ( ) = > {
const batInv = investor . investments ? . find ( ( inv : any ) = > inv . assetType === 'battery' || inv . planName . toLowerCase ( ) . includes ( 'battery' ) ) || investor . investments ? . [ 0 ] ;
setAssignForm ( {
batteryId : '' ,
investmentId : batInv?.id || '' ,
deposit : 5000 ,
rentPrice : 150 ,
investorShare : 100 ,
investedAmount : 45000
} ) ;
2026-05-19 19:27:03 +06:00
setSelectedBatteryIds ( [ ] ) ;
setSelectedBatteryPlanId ( '' ) ;
2026-05-19 17:25:32 +06:00
setShowAssignBatteryModal ( true ) ;
} }
className = "px-4 py-2 bg-emerald-50 text-emerald-700 border border-emerald-200 rounded-lg text-sm font-semibold hover:bg-emerald-100 flex items-center gap-2 transition-colors"
>
< Plus className = "w-4 h-4" / > Assign Existing
< / button >
< / div >
< / div >
< div className = "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-5" >
{ 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 < string , { bg : string ; text : string ; dot : string } > = {
'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 (
< div
key = { battery . id }
className = "bg-white rounded-xl border border-slate-200 overflow-hidden hover:shadow-md hover:border-emerald-300 transition-all group relative flex flex-col"
>
< div className = "h-1.5 bg-gradient-to-r from-emerald-400 to-green-600" / >
< div className = "p-4 flex-1 flex flex-col justify-between space-y-4" >
{ /* Card Header */ }
< div >
< div className = "flex items-start justify-between" >
< span className = "text-xs font-semibold font-mono text-slate-400 tracking-wider" >
{ battery . serialNumber }
< / span >
< span className = { ` inline-flex items-center gap-1.5 px-2 py-0.5 rounded-full text-xs font-semibold border ${ status . bg } ` } >
< span className = { ` w-1.5 h-1.5 rounded-full ${ status . dot } animate-pulse ` } / >
{ status . text }
< / span >
< / div >
< h4 className = "font-bold text-slate-800 mt-1" > { battery . brand } & bull ; { battery . model } < / h4 >
< p className = "text-xs text-slate-400 mt-0.5" > ID : { battery . id } < / p >
< div className = "mt-1.5 flex items-center" >
< span className = "text-xs font-semibold px-2 py-0.5 rounded bg-emerald-50 text-emerald-700 border border-emerald-100" >
Plan : { investment ? . planName || 'Unlinked Plan' }
< / span >
< / div >
< / div >
{ /* Co-ownership Payout details */ }
< div className = "bg-slate-50 border border-slate-100 rounded-lg p-3 space-y-2" >
< div className = "flex justify-between text-xs" >
< span className = "text-slate-500" > Refundable Deposit < / span >
< span className = "font-semibold text-slate-700" > ৳ { battery . deposit ? . toLocaleString ( ) } < / span >
< / div >
< div className = "flex justify-between text-xs" >
< span className = "text-slate-500" > Daily Rent Price < / span >
< span className = "font-semibold text-slate-700" > ৳ { battery . rentPrice } / day < / span >
< / div >
< div className = "flex justify-between text-xs" >
< span className = "text-slate-500" > Your Share % < / span >
< span className = "font-bold text-emerald-600" > { battery . investorSharePercentage } % < / span >
< / div >
< div className = "flex justify-between text-xs" >
< span className = "text-slate-500" > Capital Contribution < / span >
< span className = "font-semibold text-slate-700" > ৳ { battery . investedAmount ? . toLocaleString ( ) } < / span >
< / div >
< div className = "pt-2 border-t border-dashed border-slate-200 flex justify-between items-center text-xs" >
< span className = "font-medium text-emerald-800" > Your Daily Payout < / span >
< span className = "text-sm font-extrabold text-emerald-600" > ৳ { dailyPayout } / day < / span >
< / div >
< / div >
{ /* BMS parameters */ }
< div className = "grid grid-cols-3 gap-2 bg-emerald-50/40 rounded-lg p-2.5 text-center border border-emerald-100/50" >
< div >
< p className = "text-[10px] text-emerald-700 font-medium" > BMS SOC < / p >
< div className = "flex items-center justify-center gap-1 mt-0.5" >
< Cpu className = "w-3 h-3 text-emerald-600" / >
< span className = "text-xs font-bold text-slate-800" > { battery . currentSoc } % < / span >
< / div >
< / div >
< div >
< p className = "text-[10px] text-emerald-700 font-medium" > SOH Health < / p >
< p className = "text-xs font-bold text-slate-800 mt-0.5" > { battery . health } % < / p >
< / div >
< div >
< p className = "text-[10px] text-emerald-700 font-medium" > Cycles < / p >
< p className = "text-xs font-bold text-slate-800 mt-0.5" > { battery . cycleCount } < / p >
< / div >
< / div >
{ /* Actions block */ }
< div className = "pt-3 border-t border-slate-100 flex items-center justify-between gap-2" >
< button
onClick = { ( ) = > router . push ( ` /admin/batteries/ ${ battery . id } ` ) }
className = "p-1.5 text-slate-400 hover:text-slate-600 hover:bg-slate-50 rounded-lg transition-all flex items-center justify-center gap-1 border border-slate-150"
title = "View Battery BMS"
>
< Eye className = "w-4 h-4 text-emerald-600" / > < span className = "text-xs text-slate-500 font-medium" > Details < / span >
< / button >
< div className = "flex items-center gap-1" >
< button
onClick = { ( ) = > {
setSelectedBatteryToEdit ( battery ) ;
setAssignForm ( {
batteryId : battery.id ,
investmentId : battery.investmentId || '' ,
deposit : battery.deposit || 5000 ,
rentPrice : battery.rentPrice || 150 ,
investorShare : battery.investorSharePercentage || 100 ,
investedAmount : battery.investedAmount || 45000
} ) ;
setShowEditBatteryModal ( true ) ;
} }
className = "px-2.5 py-1 text-xs font-bold text-slate-600 border border-slate-200 rounded hover:bg-slate-50 transition-all flex items-center gap-1"
>
< Edit className = "w-3 h-3" / > Edit
< / button >
< button
2026-05-19 19:27:03 +06:00
onClick = { ( ) = > {
setUnassignConfirmModal ( {
show : true ,
type : 'battery' ,
id : battery.id ,
name : ` ${ battery . brand } ${ battery . model } ` ,
details : battery.serialNumber
} ) ;
} }
2026-05-19 17:25:32 +06:00
className = "px-2 py-1 text-xs font-bold text-red-600 hover:bg-red-50 rounded transition-all flex items-center gap-0.5"
title = "Unassign Battery"
>
< Unlink className = "w-3.5 h-3.5" / > Remove
< / button >
< / div >
< / div >
< / div >
< / div >
) ;
} ) }
{ batteries . filter ( b = > b . investorId === investorId ) . length === 0 && (
< div className = "col-span-full text-center py-16 bg-slate-50 border-2 border-dashed border-slate-200 rounded-xl" >
< Battery className = "w-12 h-12 mx-auto mb-3 text-slate-300 animate-bounce" / >
< h4 className = "font-semibold text-slate-600 mb-2" > No Batteries Assigned < / h4 >
< p className = "text-slate-400 mb-4" > Link high - yield battery pack assets to this partner < / p >
< button
onClick = { ( ) = > {
const batInv = investor . investments ? . find ( ( inv : any ) = > inv . assetType === 'battery' || inv . planName . toLowerCase ( ) . includes ( 'battery' ) ) || investor . investments ? . [ 0 ] ;
setAssignForm ( {
batteryId : '' ,
investmentId : batInv?.id || '' ,
deposit : 5000 ,
rentPrice : 150 ,
investorShare : 100 ,
investedAmount : 45000
} ) ;
setShowAssignBatteryModal ( true ) ;
} }
className = "px-4 py-2 bg-emerald-600 text-white rounded-lg text-sm font-semibold hover:bg-emerald-700"
>
Assign First Battery
< / button >
< / div >
) }
< / div >
< / div >
) }
2026-04-22 01:02:45 +06:00
{ activeTab === 'investments' && (
< div className = "space-y-6" >
< div className = "flex items-center justify-between" >
2026-05-14 19:49:57 +06:00
< div >
2026-05-19 16:20:31 +06:00
< h3 className = "font-semibold text-slate-800" > EV Investment Plans < / h3 >
2026-05-14 19:49:57 +06:00
< p className = "text-sm text-slate-500" > Manage investment portfolios for this investor < / p >
< / div >
< button onClick = { ( ) = > setShowCreateInvestmentModal ( true ) } className = "py-2 px-4 bg-investor text-white rounded-lg text-sm font-medium hover:bg-investor-dark flex items-center gap-2" >
< Plus className = "w-4 h-4" / > Create Investment
2026-04-22 01:02:45 +06:00
< / button >
< / div >
2026-05-14 19:49:57 +06:00
< div className = "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4" >
{ investor . investments ? . map ( ( inv ) = > {
const planConfig : Record < string , { bg : string ; border : string ; icon : string } > = {
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 ;
2026-05-14 22:25:24 +06:00
2026-05-14 19:49:57 +06:00
return (
< div key = { inv . id } className = { ` bg-white rounded-xl border ${ style . border } overflow-hidden ` } >
< div className = { ` ${ style . bg } p-4 flex items-center justify-between ` } >
< div >
< h4 className = "font-semibold text-slate-800" > { inv . planName } < / h4 >
< p className = "text-sm text-slate-500 capitalize" > { inv . planType } Plan < / p >
< / div >
< span className = { ` text-xs font-medium px-2.5 py-1 rounded-full ${ inv . status === 'active' ? 'bg-green-100 text-green-700' : 'bg-slate-200 text-slate-600' } ` } >
{ inv . status }
< / span >
2026-04-22 01:02:45 +06:00
< / div >
2026-05-14 19:49:57 +06:00
< div className = "p-4 space-y-4" >
< div className = "grid grid-cols-2 gap-3" >
< div className = "bg-slate-50 rounded-lg p-3" >
< p className = "text-xs text-slate-500" > Investment < / p >
< p className = "font-bold text-slate-800" > ৳ { inv . totalInvestment . toLocaleString ( ) } < / p >
< / div >
< div className = "bg-purple-50 rounded-lg p-3" >
< p className = "text-xs text-purple-600" > Total Return < / p >
< p className = "font-bold text-purple-700" > ৳ { inv . actualEarnings . toLocaleString ( ) } < / p >
< / div >
< / div >
2026-05-14 22:25:24 +06:00
2026-05-14 19:49:57 +06:00
< div className = "bg-slate-50 rounded-lg p-3 space-y-1.5" >
< div className = "flex justify-between text-xs" >
< span className = "text-slate-400" > Duration < / span >
< span className = "font-medium" > 12 months < / span >
< / div >
< div className = "flex justify-between text-xs" >
< span className = "text-slate-400" > Lock - in Period < / span >
< span className = "font-medium" > 3 months < / span >
< / div >
< div className = "flex justify-between text-xs" >
< span className = "text-slate-400" > Early Exit Penalty < / span >
< span className = "font-medium text-red-500" > 10 % < / span >
< / div >
< / div >
< div className = "flex items-center justify-between text-sm" >
< span className = "text-slate-400" > { inv . startDate } - { inv . endDate || 'Ongoing' } < / span >
< span className = "capitalize text-slate-500" > { inv . paymentMethod } < / span >
< / div >
< div className = "pt-3 border-t border-slate-100" >
< p className = "text-xs text-slate-400 mb-2" > ID : # { inv . id ? . slice ( - 6 ) || 'N/A' } < / p >
< div className = "flex gap-2" >
< Link href = { ` /admin/investors/ ${ investor . id } /investments/ ${ inv . id } ` } className = "flex-1 py-2 text-sm font-medium text-slate-600 hover:bg-slate-50 rounded-lg border border-slate-200 text-center" >
View
< / Link >
2026-05-14 22:30:13 +06:00
{ / * < L i n k h r e f = { ` / a d m i n / i n v e s t o r s / $ { i n v e s t o r . i d } / i n v e s t m e n t s / $ { i n v . i d } / s t a t e m e n t ` } c l a s s N a m e = " f l e x - 1 p y - 2 t e x t - s m f o n t - m e d i u m t e x t - i n v e s t o r h o v e r : b g - i n v e s t o r / 1 0 r o u n d e d - l g b o r d e r b o r d e r - i n v e s t o r / 3 0 t e x t - c e n t e r f l e x i t e m s - c e n t e r j u s t i f y - c e n t e r g a p - 1 " >
2026-05-14 22:28:46 +06:00
< Printer className = "w-4 h-4" / > Statement
2026-05-14 22:30:13 +06:00
< / Link > * / }
2026-05-14 19:49:57 +06:00
< / div >
< / div >
2026-04-22 01:02:45 +06:00
< / div >
< / div >
2026-05-14 19:49:57 +06:00
) ;
} ) }
2026-04-22 01:02:45 +06:00
< / div >
2026-05-14 19:49:57 +06:00
{ ( ! investor . investments || investor . investments . length === 0 ) && (
< div className = "text-center py-16 bg-slate-50 rounded-xl border-2 border-dashed border-slate-200" >
< TrendingUp className = "w-12 h-12 mx-auto mb-3 text-slate-300" / >
< h4 className = "font-semibold text-slate-600 mb-2" > No Investments Yet < / h4 >
< p className = "text-slate-400 mb-4" > Create your first investment plan for this investor < / p >
< button onClick = { ( ) = > setShowCreateInvestmentModal ( true ) } className = "px-4 py-2 bg-investor text-white rounded-lg text-sm font-medium hover:bg-investor-dark" >
< Plus className = "w-4 h-4 inline mr-1" / > Create Investment
< / button >
< / div >
) }
2026-04-22 01:02:45 +06:00
< / div >
) }
{ activeTab === 'financial' && (
< div className = "space-y-6" >
< div className = "grid lg:grid-cols-2 gap-6" >
< div >
2026-04-22 01:18:03 +06:00
< div className = "flex items-center justify-between mb-4" >
2026-05-14 20:15:39 +06:00
< h3 className = "font-semibold text-slate-800" > Bank Accounts ( { investor . bankAccounts ? . length || 0 } ) < / h3 >
2026-04-22 01:18:03 +06:00
< button onClick = { ( ) = > {
2026-05-14 20:15:39 +06:00
setEditingBankAccount ( { id : '' , bankName : '' , accountName : '' , accountNumber : '' , branch : '' , routing : '' , isPrimary : false } ) ;
2026-04-22 01:18:03 +06:00
setShowBankModal ( true ) ;
} } className = "text-sm text-investor hover:underline flex items-center gap-1" >
2026-05-14 20:15:39 +06:00
< Plus className = "w-4 h-4" / > Add Account
2026-04-22 01:18:03 +06:00
< / button >
< / div >
2026-04-22 01:02:45 +06:00
< div className = "space-y-3" >
2026-05-14 20:15:39 +06:00
{ investor . bankAccounts && investor . bankAccounts . length > 0 ? (
investor . bankAccounts . map ( ( account : any ) = > (
< div key = { account . id } className = "p-4 bg-white border border-slate-200 rounded-xl hover:shadow-md transition-all" >
< div className = "flex items-start justify-between mb-3" >
< div className = "flex items-center gap-3" >
2026-05-15 01:31:21 +06:00
< div className = "w-10 h-10 rounded-lg flex items-center justify-center bg-slate-100" >
< Banknote className = "w-5 h-5 text-slate-500" / >
2026-05-14 20:15:39 +06:00
< / div >
< div >
2026-05-19 20:45:10 +06:00
< div className = "flex items-center gap-2 flex-wrap" >
< p className = "font-semibold text-slate-800" > { account . bankName } < / p >
{ account . verified ? (
< span className = "flex items-center gap-0.5 text-[10px] bg-green-100 text-green-700 px-2 py-0.5 rounded-full font-semibold" >
< Check className = "w-2.5 h-2.5 text-green-600" / > Verified
< / span >
) : (
< span className = "flex items-center gap-0.5 text-[10px] bg-amber-100 text-amber-700 px-2 py-0.5 rounded-full font-semibold" >
< Clock className = "w-2.5 h-2.5 text-amber-600 animate-pulse" / > Pending
< / span >
) }
< / div >
2026-05-14 20:15:39 +06:00
< p className = "text-xs text-slate-500" > { account . branch } < / p >
< / div >
< / div >
2026-05-19 20:45:10 +06:00
< div className = "flex items-center gap-1.5" >
{ ! account . verified && (
< button
onClick = { ( ) = > {
setInvestors ( prev = > prev . map ( inv = > {
if ( inv . id === investorId ) {
return {
. . . inv ,
bankAccounts : inv.bankAccounts?.map ( ( ba : any ) = >
ba . id === account . id ? { . . . ba , verified : true } : ba
)
} ;
}
return inv ;
} ) ) ;
toast . success ( ` ${ account . bankName } verified successfully! ` ) ;
} }
className = "flex items-center gap-1 text-xs bg-green-600 hover:bg-green-700 text-white px-2.5 py-1 rounded-lg transition-all font-semibold shadow-sm"
title = "Verify Bank Account"
>
< Check className = "w-3.5 h-3.5" / > Verify
< / button >
) }
< button onClick = { ( ) = > {
setEditingBankAccount ( account ) ;
setShowBankModal ( true ) ;
} } className = "p-1.5 hover:bg-slate-100 rounded-lg text-slate-500" >
< Edit className = "w-4 h-4" / >
< / button >
< / div >
2026-04-22 01:18:03 +06:00
< / div >
2026-05-14 20:15:39 +06:00
< div className = "space-y-2 text-sm" >
< div className = "flex justify-between" >
< span className = "text-slate-500" > Account Name < / span >
< span className = "font-medium text-slate-700" > { account . accountName } < / span >
< / div >
< div className = "flex justify-between" >
< span className = "text-slate-500" > Account Number < / span >
< span className = "font-mono text-slate-700" > { account . accountNumber } < / span >
< / div >
{ account . routing && (
< div className = "flex justify-between" >
< span className = "text-slate-500" > Routing < / span >
< span className = "font-medium text-slate-700" > { account . routing } < / span >
< / div >
) }
2026-04-22 01:18:03 +06:00
< / div >
2026-04-22 01:02:45 +06:00
< / div >
2026-05-14 20:15:39 +06:00
) )
2026-04-22 01:18:03 +06:00
) : (
2026-05-14 20:15:39 +06:00
< div className = "text-center py-10 border-2 border-dashed border-slate-200 rounded-xl" >
< Banknote className = "w-10 h-10 mx-auto mb-3 text-slate-300" / >
< p className = "text-sm text-slate-500 mb-2" > No bank accounts added < / p >
< button onClick = { ( ) = > {
setEditingBankAccount ( { id : '' , bankName : '' , accountName : '' , accountNumber : '' , branch : '' , routing : '' , isPrimary : false } ) ;
setShowBankModal ( true ) ;
} } className = "px-4 py-2 text-sm bg-investor text-white rounded-lg hover:bg-investor-dark" >
Add First Account
< / button >
2026-04-22 01:02:45 +06:00
< / div >
) }
< / div >
< / div >
< div >
2026-04-22 01:18:03 +06:00
< div className = "flex items-center justify-between mb-4" >
2026-05-19 20:45:10 +06:00
< h3 className = "font-semibold text-slate-800" > Mobile Banking ( { ( investor . mobileBanking ? 1 : 0 ) + ( investor . additionalMobileBanking ? . length || 0 ) } ) < / h3 >
2026-04-22 01:18:03 +06:00
< 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 >
2026-04-22 01:02:45 +06:00
< div className = "space-y-3" >
2026-04-22 01:18:03 +06:00
{ investor . mobileBanking ? (
< div className = "flex items-center gap-3 p-3 bg-purple-50 rounded-lg" >
< Phone className = "w-5 h-5 text-purple-500" / >
< div className = "flex-1" >
2026-05-19 20:45:10 +06:00
< div className = "flex items-center gap-2" >
< p className = "text-xs text-purple-600 font-medium" > { investor . mobileBanking } < / p >
{ investor . mobileBankingVerified ? (
< span className = "flex items-center gap-0.5 text-[9px] bg-green-100 text-green-700 px-1.5 py-0.5 rounded-full font-semibold" >
< Check className = "w-2.5 h-2.5 text-green-600" / > Verified
< / span >
) : (
< span className = "flex items-center gap-0.5 text-[9px] bg-amber-100 text-amber-700 px-1.5 py-0.5 rounded-full font-semibold" >
< Clock className = "w-2.5 h-2.5 text-amber-600 animate-pulse" / > Pending
< / span >
) }
< / div >
2026-04-22 01:18:03 +06:00
< p className = "text-xs text-slate-400" > { investor . mobileBankingNumber } < / p >
2026-04-22 01:02:45 +06:00
< / div >
2026-05-19 20:45:10 +06:00
< div className = "flex items-center gap-1.5" >
{ ! investor . mobileBankingVerified && (
< button
onClick = { ( ) = > {
setInvestors ( prev = > prev . map ( inv = > {
if ( inv . id === investorId ) {
return { . . . inv , mobileBankingVerified : true } ;
}
return inv ;
} ) ) ;
toast . success ( ` ${ investor . mobileBanking } verified successfully! ` ) ;
} }
className = "flex items-center gap-1 text-[10px] bg-green-600 hover:bg-green-700 text-white px-2 py-0.5 rounded transition-colors font-medium shadow-sm"
>
< Check className = "w-3.5 h-3.5" / > Verify
< / button >
) }
< button onClick = { ( ) = > {
setEditingMobileBanking ( { provider : investor.mobileBanking || '' , number : investor . mobileBankingNumber || '' , isPrimary : true } ) ;
setEditingMobileIndex ( - 1 ) ;
setShowMobileBankingModal ( true ) ;
} } className = "p-1 hover:bg-purple-100 rounded text-purple-500" >
< Edit className = "w-4 h-4" / >
< / button >
< / div >
2026-04-22 01:02:45 +06:00
< / div >
2026-04-22 01:18:03 +06:00
) : null }
2026-04-22 01:02:45 +06:00
{ investor . additionalMobileBanking ? . map ( ( mb , idx ) = > (
< 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" / >
< div className = "flex-1" >
< p className = "text-xs text-slate-500" > { mb . provider } < / p >
< p className = "font-medium text-slate-700" > { mb . number } < / p >
< / div >
2026-05-19 20:45:10 +06:00
< div className = "flex items-center gap-1.5" >
{ mb . verified ? (
< span className = "flex items-center gap-0.5 text-[9px] bg-green-100 text-green-700 px-1.5 py-0.5 rounded-full font-semibold" >
< Check className = "w-2.5 h-2.5 text-green-600" / > Verified
< / span >
) : (
< >
< span className = "flex items-center gap-0.5 text-[9px] bg-amber-100 text-amber-700 px-1.5 py-0.5 rounded-full font-semibold" >
< Clock className = "w-2.5 h-2.5 text-amber-600 animate-pulse" / > Pending
< / span >
< button
onClick = { ( ) = > {
setInvestors ( prev = > prev . map ( inv = > {
if ( inv . id === investorId ) {
return {
. . . inv ,
additionalMobileBanking : inv.additionalMobileBanking?.map ( ( a : any , i : number ) = >
i === idx ? { . . . a , verified : true } : a
)
} ;
}
return inv ;
} ) ) ;
toast . success ( ` ${ mb . provider } verified successfully! ` ) ;
} }
className = "flex items-center gap-1 text-[10px] bg-green-600 hover:bg-green-700 text-white px-2 py-0.5 rounded transition-colors font-medium shadow-sm"
>
< Check className = "w-3.5 h-3.5" / > Verify
< / button >
< / >
) }
< 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 >
2026-04-22 01:02:45 +06:00
< / div >
) ) }
2026-04-22 01:18:03 +06:00
{ ( ! 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 >
) }
2026-04-22 01:02:45 +06:00
< / div >
2026-04-22 01:18:03 +06:00
< 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 >
2026-04-22 01:02:45 +06:00
< div className = "space-y-3" >
2026-04-22 01:18:03 +06:00
{ investor . tinNumber || investor . passportNumber ? (
< >
{ investor . tinNumber && (
< div className = "flex items-center gap-3 p-3 bg-slate-50 rounded-lg" >
< FileText className = "w-5 h-5 text-slate-400" / >
< div >
< p className = "text-xs text-slate-500" > TIN < / p >
< p className = "font-medium text-slate-700" > { investor . tinNumber } < / p >
< / div >
< / div >
) }
{ investor . passportNumber && (
< div className = "flex items-center gap-3 p-3 bg-slate-50 rounded-lg" >
< FileText className = "w-5 h-5 text-slate-400" / >
< div >
< p className = "text-xs text-slate-500" > Passport < / p >
< p className = "font-medium text-slate-700" > { investor . passportNumber } < / p >
< / 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 >
2026-04-22 01:02:45 +06:00
< / div >
) }
< / div >
< / div >
< / div >
< div >
< h3 className = "font-semibold text-slate-800 mb-4" > Investment Summary < / h3 >
< div className = "grid grid-cols-2 lg:grid-cols-4 gap-4" >
< div className = "bg-purple-50 rounded-lg p-4" >
< p className = "text-sm text-purple-600" > Total Invested < / p >
< p className = "text-xl font-bold text-purple-700" > ৳ { investor . totalInvested . toLocaleString ( ) } < / p >
< / div >
< div className = "bg-green-50 rounded-lg p-4" >
< p className = "text-sm text-green-600" > Total Earnings < / p >
< p className = "text-xl font-bold text-green-700" > ৳ { investor . totalEarnings . toLocaleString ( ) } < / p >
< / div >
< div className = "bg-amber-50 rounded-lg p-4" >
< p className = "text-sm text-amber-600" > Pending Earnings < / p >
< p className = "text-xl font-bold text-amber-700" > ৳ { investor . pendingEarnings . toLocaleString ( ) } < / p >
< / div >
< div className = "bg-slate-50 rounded-lg p-4" >
< p className = "text-sm text-slate-600" > Total Withdrawn < / p >
< p className = "text-xl font-bold text-slate-700" > ৳ { investor . totalWithdrawn . toLocaleString ( ) } < / p >
< / div >
< / div >
< / div >
< / div >
) }
{ activeTab === 'transactions' && (
< div >
< div className = "flex items-center justify-between mb-4" >
2026-04-22 01:18:03 +06:00
< h3 className = "font-semibold text-slate-800" > All Transactions < / h3 >
< div className = "flex gap-2" >
2026-05-14 22:25:24 +06:00
< button onClick = { ( ) = > setShowWithdrawalModal ( true ) } 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" >
2026-04-22 01:18:03 +06:00
< 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" >
< Download className = "w-4 h-4" / > Export
< / button >
< / div >
2026-04-22 01:02:45 +06:00
< / div >
2026-04-22 01:18:03 +06:00
< 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 >
2026-04-22 01:02:45 +06:00
< / div >
2026-04-22 01:18:03 +06:00
< 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 >
2026-04-22 01:02:45 +06:00
< / div >
< / div >
2026-04-22 01:18:03 +06:00
) ) }
{ 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 >
2026-04-22 01:02:45 +06:00
< / div >
2026-04-22 01:18:03 +06:00
) }
< / div >
< / div >
< div >
< h4 className = "font-semibold text-slate-700 mb-3" > Transaction History < / h4 >
< div className = "space-y-2" >
2026-05-14 22:25:24 +06:00
{ 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 (
< 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 = { ` w-10 h-10 rounded-full flex items-center justify-center ${ tx . type === 'earning' || tx . type === 'bike_earning' || tx . type === 'investment_return' ? 'bg-green-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 ${ tx . type === 'earning' || tx . type === 'bike_earning' ? 'text-green-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 >
< p className = "text-sm font-medium text-slate-700" > { tx . description } < / p >
< div className = "flex items-center gap-2" >
< p className = "text-xs text-slate-400" > { tx . createdAt } < / p >
{ tx . referenceNumber && (
< button
onClick = { ( ) = > {
const accounting = getAccountingInfo ( tx . type , tx . amount ) ;
setSelectedInvoice ( {
reference : tx.referenceNumber ,
date : tx.createdAt ,
type : tx . type ,
amount : tx.amount ,
description : tx.description ,
status : tx.status ,
investorName : investor.name ,
investorId : investor.id ,
debitAccount : accounting.debitAccount ,
creditAccount : accounting.creditAccount
} ) ;
setShowInvoiceModal ( true ) ;
} }
className = "text-xs text-investor hover:underline flex items-center gap-1"
>
• Ref : { tx . referenceNumber }
< BookOpen className = "w-3 h-3" / >
< / button >
) }
< / div >
< / div >
2026-04-22 01:18:03 +06:00
< / div >
2026-05-14 22:25:24 +06:00
< div className = "flex items-center gap-3" >
< div className = "text-right" >
< p className = { ` text-lg font-bold ${ tx . type === 'earning' || tx . type === 'bike_earning' || tx . type === 'investment_return' || tx . type === 'referral_bonus' ? 'text-green-600' : 'text-red-600' } ` } >
{ tx . type === 'earning' || tx . type === 'bike_earning' || tx . type === 'investment_return' || tx . type === 'referral_bonus' ? '+' : '-' } ৳ { tx . amount . toLocaleString ( ) }
< / p >
< span className = { ` text-xs font-medium px-2 py-1 rounded-full ${ tx . status === 'completed' ? 'bg-green-100 text-green-700' :
tx . status === 'pending' ? 'bg-amber-100 text-amber-700' : 'bg-slate-100 text-slate-600'
} ` }>
{ tx . status }
< / span >
2026-04-26 14:56:12 +06:00
< / div >
2026-04-22 01:18:03 +06:00
< / div >
< / div >
2026-05-14 22:25:24 +06:00
) ;
} ) }
2026-04-22 01:18:03 +06:00
< / div >
2026-04-22 01:02:45 +06:00
< / div >
< / div >
) }
{ activeTab === 'documents' && (
2026-05-14 22:25:24 +06:00
< div className = "space-y-6" >
< div className = "flex items-center justify-between" >
< div >
< h3 className = "font-semibold text-slate-800" > KYC Documents < / h3 >
< p className = "text-sm text-slate-500" > Manage investor verification documents < / p >
< / div >
< button onClick = { ( ) = > setShowDocModal ( true ) } className = "px-4 py-2 bg-investor text-white rounded-lg text-sm font-medium hover:bg-investor-dark flex items-center gap-2" >
2026-04-22 01:18:03 +06:00
< Plus className = "w-4 h-4" / > Upload Document
< / button >
< / div >
2026-05-14 13:54:02 +06:00
2026-05-14 22:25:24 +06:00
< div className = "space-y-2" >
2026-04-22 01:02:45 +06:00
{ investor . kycDocuments ? . map ( ( doc , idx ) = > (
2026-05-14 22:25:24 +06:00
< div key = { idx } className = "flex items-center justify-between p-4 bg-white border border-slate-200 rounded-xl" >
2026-04-22 01:02:45 +06:00
< div className = "flex items-center gap-3" >
2026-05-14 13:54:02 +06:00
< div className = { ` w-12 h-12 rounded-lg flex items-center justify-center ${ doc . type === 'nid' ? 'bg-blue-100' :
2026-04-22 01:18:03 +06:00
doc . type === 'passport' ? 'bg-purple-100' :
2026-05-14 13:54:02 +06:00
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' :
2026-04-22 01:18:03 +06:00
doc . type === 'passport' ? 'text-purple-600' :
2026-05-14 13:54:02 +06:00
doc . type === 'bank_statement' ? 'text-green-600' :
doc . type === 'tin_certificate' ? 'text-amber-600' : 'text-slate-600'
} ` } />
2026-04-22 01:18:03 +06:00
< / div >
2026-04-22 01:02:45 +06:00
< div >
< p className = "text-sm font-medium text-slate-700 capitalize" > { doc . type . replace ( '_' , ' ' ) } < / p >
2026-04-22 01:18:03 +06:00
< p className = "text-xs text-slate-400" > { doc . number || 'No document number' } • Uploaded : { doc . uploadedAt || 'N/A' } < / p >
2026-04-22 01:02:45 +06:00
< / div >
< / div >
< div className = "flex items-center gap-2" >
{ doc . verified ? (
2026-04-22 01:18:03 +06:00
< span className = "flex items-center gap-1 text-xs font-medium px-3 py-1.5 rounded-full bg-green-100 text-green-700" >
2026-04-22 01:02:45 +06:00
< CheckCircle className = "w-3 h-3" / > Verified
< / span >
) : (
2026-04-22 01:18:03 +06:00
< 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 Review
2026-04-22 01:02:45 +06:00
< / span >
) }
2026-05-14 22:25:24 +06:00
< button onClick = { ( ) = > alert ( 'Edit document' ) } className = "p-2 hover:bg-slate-100 rounded-lg" >
2026-04-22 01:18:03 +06:00
< Edit className = "w-4 h-4 text-slate-400" / >
< / button >
2026-05-14 13:54:02 +06:00
< button onClick = { ( ) = > { if ( confirm ( 'Delete this document?' ) ) alert ( 'Document deleted' ) ; } } className = "p-2 hover:bg-red-50 rounded-lg" >
2026-04-22 01:18:03 +06:00
< Trash2 className = "w-4 h-4 text-red-400" / >
< / button >
2026-04-22 01:02:45 +06:00
< / div >
< / div >
) ) }
2026-05-14 22:25:24 +06:00
2026-04-22 01:02:45 +06:00
{ ( ! investor . kycDocuments || investor . kycDocuments . length === 0 ) && (
2026-04-22 01:18:03 +06:00
< div className = "text-center py-12 text-slate-400 border-2 border-dashed border-slate-200 rounded-lg" >
2026-04-22 01:02:45 +06:00
< FileText className = "w-12 h-12 mx-auto mb-2 opacity-50" / >
2026-04-22 01:18:03 +06:00
< p > No documents uploaded yet < / p >
2026-04-22 01:02:45 +06:00
< / div >
) }
< / div >
2026-04-22 01:18:03 +06:00
< div className = "bg-blue-50 border border-blue-200 rounded-lg p-4" >
< h4 className = "font-semibold text-blue-800 mb-2" > KYC Status : { investor . kycStatus . toUpperCase ( ) } < / h4 >
< p className = "text-sm text-blue-700" >
2026-05-14 13:54:02 +06:00
{ investor . kycStatus === 'verified'
2026-04-22 01:18:03 +06:00
? 'All documents have been verified. Investor is fully verified.'
: investor . kycStatus === 'pending'
2026-05-14 13:54:02 +06:00
? 'Documents are under review. Verification typically takes 24-48 hours.'
: 'Please upload required documents for verification.' }
2026-04-22 01:18:03 +06:00
< / p >
2026-04-22 01:02:45 +06:00
< / div >
< / div >
) }
2026-05-14 22:25:24 +06:00
{ activeTab === 'rentals' && (
< div className = "space-y-6" >
< div className = "flex items-center justify-between" >
< div >
< h3 className = "font-semibold text-slate-800" > Daily Rental History < / h3 >
< p className = "text-sm text-slate-500" > Track daily rental payments for investor ' s bikes < / p >
< / div >
< button className = "px-4 py-2 bg-investor text-white rounded-lg text-sm font-medium hover:bg-investor-dark flex items-center gap-2" >
< Download className = "w-4 h-4" / > Export
< / button >
< / div >
< div className = "grid grid-cols-1 md:grid-cols-4 gap-4" >
< div className = "bg-white rounded-xl border border-slate-200 p-4" >
< div className = "flex items-center gap-3" >
< div className = "w-10 h-10 bg-green-100 rounded-lg flex items-center justify-center" >
< DollarSign className = "w-5 h-5 text-green-600" / >
< / div >
< div >
< p className = "text-sm text-slate-500" > Total Collected < / p >
< p className = "text-xl font-bold text-green-600" > ৳ { investorRentalPayments . filter ( p = > p . status === 'paid' ) . reduce ( ( sum , p ) = > sum + p . amount , 0 ) . toLocaleString ( ) } < / p >
< / div >
< / div >
< / div >
< div className = "bg-white rounded-xl border border-slate-200 p-4" >
< div className = "flex items-center gap-3" >
< div className = "w-10 h-10 bg-blue-100 rounded-lg flex items-center justify-center" >
< Bike className = "w-5 h-5 text-blue-600" / >
< / div >
< div >
< p className = "text-sm text-slate-500" > Active Rentals < / p >
< p className = "text-xl font-bold text-slate-800" > { new Set ( investorRentalPayments . filter ( p = > p . status === 'paid' ) . map ( p = > p . bikeId ) ) . size } < / p >
< / div >
< / div >
< / div >
< div className = "bg-white rounded-xl border border-slate-200 p-4" >
< div className = "flex items-center gap-3" >
< div className = "w-10 h-10 bg-amber-100 rounded-lg flex items-center justify-center" >
< Clock className = "w-5 h-5 text-amber-600" / >
< / div >
< div >
< p className = "text-sm text-slate-500" > Pending < / p >
< p className = "text-xl font-bold text-amber-600" > ৳ { investorRentalPayments . filter ( p = > p . status === 'pending' ) . reduce ( ( sum , p ) = > sum + p . amount , 0 ) . toLocaleString ( ) } < / p >
< / div >
< / div >
< / div >
< div className = "bg-white rounded-xl border border-slate-200 p-4" >
< div className = "flex items-center gap-3" >
< div className = "w-10 h-10 bg-red-100 rounded-lg flex items-center justify-center" >
< AlertTriangle className = "w-5 h-5 text-red-600" / >
< / div >
< div >
< p className = "text-sm text-slate-500" > Failed < / p >
< p className = "text-xl font-bold text-red-600" > ৳ { investorRentalPayments . filter ( p = > p . status === 'failed' ) . reduce ( ( sum , p ) = > sum + p . amount , 0 ) . toLocaleString ( ) } < / p >
< / div >
< / div >
< / div >
< / div >
< div className = "bg-white rounded-xl border border-slate-200 overflow-hidden" >
< div className = "p-4 border-b border-slate-100 flex items-center justify-between" >
< div className = "flex items-center gap-2" >
< h4 className = "font-semibold text-slate-800" > Daily Payment Records < / h4 >
< span className = "px-2 py-1 bg-slate-100 text-slate-600 text-xs rounded-full" > { investorRentalPayments . length } records < / span >
< / div >
< div className = "flex items-center gap-2" >
< select className = "px-3 py-1.5 border border-slate-200 rounded-lg text-sm" >
< option value = "all" > All Bikes < / option >
{ assignedBikes . map ( ( bike : any ) = > (
< option key = { bike . id } value = { bike . id } > { bike . model } < / option >
) ) }
< / select >
< select className = "px-3 py-1.5 border border-slate-200 rounded-lg text-sm" >
< option value = "all" > All Plans < / option >
< option value = "single" > Single Rent < / option >
< option value = "rent-to-own" > Rent to Own < / option >
< option value = "share_ev" > Share EV < / option >
< / select >
< select className = "px-3 py-1.5 border border-slate-200 rounded-lg text-sm" >
< option value = "all" > All Status < / option >
< option value = "paid" > Paid < / option >
< option value = "pending" > Pending < / option >
< option value = "failed" > Failed < / option >
< / select >
< / div >
< / div >
< div className = "overflow-x-auto" >
< table className = "w-full" >
< thead className = "bg-slate-50" >
< tr >
< th
onClick = { ( ) = > {
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"
>
< div className = "flex items-center gap-1" >
Date
{ rentalSortBy === 'date' && (
< span className = "text-investor" > { rentalSortOrder === 'asc' ? '↑' : '↓' } < / span >
) }
< / div >
< / th >
< th
onClick = { ( ) = > {
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"
>
< div className = "flex items-center gap-1" >
Bike
{ rentalSortBy === 'bike' && (
< span className = "text-investor" > { rentalSortOrder === 'asc' ? '↑' : '↓' } < / span >
) }
< / div >
< / th >
< th className = "px-4 py-3 text-left text-xs font-medium text-slate-500 uppercase" > Biker < / th >
< th className = "px-4 py-3 text-left text-xs font-medium text-slate-500 uppercase" > Plan < / th >
< th className = "px-4 py-3 text-left text-xs font-medium text-slate-500 uppercase" > Duration < / th >
< th
onClick = { ( ) = > {
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"
>
< div className = "flex items-center gap-1" >
Amount
{ rentalSortBy === 'amount' && (
< span className = "text-investor" > { rentalSortOrder === 'asc' ? '↑' : '↓' } < / span >
) }
< / div >
< / th >
< th className = "px-4 py-3 text-left text-xs font-medium text-slate-500 uppercase" > Method < / th >
< th className = "px-4 py-3 text-left text-xs font-medium text-slate-500 uppercase" > Status < / th >
< / tr >
< / thead >
< tbody className = "divide-y divide-slate-100" >
{ ( ( ) = > {
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 < string , { label : string ; bg : string ; color : string } > = {
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 < string , { label : string ; bg : string ; color : string } > = {
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 (
< tr key = { payment . id } className = "hover:bg-slate-50" >
< td className = "px-4 py-3" >
< div >
< p className = "text-sm font-medium text-slate-800" > { payment . date } < / p >
< p className = "text-xs text-slate-400" > { payment . transactionId || payment . id } < / p >
< / div >
< / td >
< td className = "px-4 py-3" >
< div className = "flex items-center gap-2" >
< Bike className = "w-4 h-4 text-slate-400" / >
< div >
< p className = "text-sm font-medium text-slate-800" > { payment . bikeModel } < / p >
< p className = "text-xs text-slate-400" > { payment . plateNumber } < / p >
< / div >
< / div >
< / td >
< td className = "px-4 py-3" >
< div className = "flex items-center gap-2" >
< User className = "w-4 h-4 text-slate-400" / >
< span className = "text-sm text-slate-700" > { payment . bikerName } < / span >
< / div >
< / td >
< td className = "px-4 py-3" >
< span className = { ` px-2 py-1 rounded-full text-xs font-medium ${ plan . bg } ${ plan . color } ` } >
{ plan . label }
< / span >
< / td >
< td className = "px-4 py-3" >
< span className = "text-sm text-slate-600" > { payment . duration } < / span >
< / td >
< td className = "px-4 py-3" >
< p className = "text-sm font-bold text-slate-800" > ৳ { payment . amount . toLocaleString ( ) } < / p >
< / td >
< td className = "px-4 py-3" >
< span className = "text-sm text-slate-600 capitalize" > { payment . paymentMethod } < / span >
< / td >
< td className = "px-4 py-3" >
< span className = { ` px-2 py-1 rounded-full text-xs font-medium ${ status . bg } ${ status . color } ` } >
{ status . label }
< / span >
< / td >
< / tr >
) ;
} ) ;
} ) ( ) }
< / tbody >
< / table >
{ investorRentalPayments . length === 0 && (
< div className = "text-center py-12 text-slate-400" >
< DollarSign className = "w-12 h-12 mx-auto mb-2 opacity-50" / >
< p > No rental payments found < / p >
< / div >
) }
< / div >
{ investorRentalPayments . length > rentalPageSize && (
< div className = "p-4 border-t border-slate-100 flex items-center justify-between" >
< p className = "text-sm text-slate-500" >
Showing { ( ( rentalPage - 1 ) * rentalPageSize ) + 1 } to { Math . min ( rentalPage * rentalPageSize , investorRentalPayments . length ) } of { investorRentalPayments . length }
< / p >
< div className = "flex items-center gap-2" >
< button
onClick = { ( ) = > setRentalPage ( p = > Math . max ( 1 , p - 1 ) ) }
disabled = { rentalPage === 1 }
className = "px-3 py-1.5 border border-slate-200 rounded-lg text-sm disabled:opacity-50 disabled:cursor-not-allowed hover:bg-slate-50"
>
Previous
< / button >
{ Array . from ( { length : Math.ceil ( investorRentalPayments . length / rentalPageSize ) } , ( _ , i ) = > i + 1 ) . map ( page = > (
< button
key = { page }
onClick = { ( ) = > setRentalPage ( page ) }
className = { ` px-3 py-1.5 border rounded-lg text-sm ${ rentalPage === page
? 'bg-investor text-white border-investor'
: 'border-slate-200 hover:bg-slate-50'
} ` }
>
{ page }
< / button >
) ) }
< button
onClick = { ( ) = > setRentalPage ( p = > Math . min ( Math . ceil ( investorRentalPayments . length / rentalPageSize ) , p + 1 ) ) }
disabled = { rentalPage === Math . ceil ( investorRentalPayments . length / rentalPageSize ) }
className = "px-3 py-1.5 border border-slate-200 rounded-lg text-sm disabled:opacity-50 disabled:cursor-not-allowed hover:bg-slate-50"
>
Next
< / button >
< / div >
< / div >
) }
< / div >
< / div >
) }
2026-04-22 01:02:45 +06:00
< / div >
< / div >
2026-05-14 22:25:24 +06:00
{ showWithdrawalModal && (
< div className = "fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4" >
< div className = "bg-white rounded-xl shadow-xl w-full max-w-2xl max-h-[90vh] overflow-hidden flex flex-col" >
< div className = "p-5 border-b border-slate-100 flex items-center justify-between" >
< h2 className = "text-lg font-bold text-slate-800 flex items-center gap-2" >
< Banknote className = "w-5 h-5 text-red-500" / > Request Withdrawal
< / h2 >
< button onClick = { ( ) = > setShowWithdrawalModal ( 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 overflow-y-auto flex-1 space-y-6" >
< div className = "grid grid-cols-3 gap-4" >
< div className = "bg-green-50 rounded-lg p-4 border border-green-100" >
< p className = "text-xs text-green-600 font-medium" > Available Balance < / p >
< p className = "text-lg font-bold text-green-700" > ৳ { ( investor . totalEarnings - investor . totalWithdrawn - investor . pendingEarnings ) . toLocaleString ( ) } < / p >
< / div >
< div className = "bg-amber-50 rounded-lg p-4 border border-amber-100" >
< p className = "text-xs text-amber-600 font-medium" > Pending Request < / p >
< p className = "text-lg font-bold text-amber-700" > ৳ { investor . pendingEarnings . toLocaleString ( ) } < / p >
< / div >
< div className = "bg-slate-50 rounded-lg p-4 border border-slate-100" >
< p className = "text-xs text-slate-600 font-medium" > Total Withdrawn < / p >
< p className = "text-lg font-bold text-slate-700" > ৳ { investor . totalWithdrawn . toLocaleString ( ) } < / p >
< / div >
< / div >
< div >
2026-05-19 16:20:31 +06:00
< h4 className = "font-semibold text-slate-800 mb-3" > Select EV Investment Plans & Bikes < / h4 >
2026-05-14 22:25:24 +06:00
< div className = "space-y-4" >
< div className = "flex items-center gap-3 p-4 bg-slate-50 rounded-lg border border-slate-200" >
< input
type = "checkbox"
id = "selectAll"
checked = { withdrawSelection . selectAll }
onChange = { ( e ) = > {
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"
/ >
< label htmlFor = "selectAll" className = "flex-1" >
< span className = "font-medium text-slate-800" > Select All < / span >
< p className = "text-xs text-slate-500" > Include all investments and bikes < / p >
< / label >
< / div >
< div className = "border border-slate-200 rounded-lg overflow-hidden" >
< div className = "bg-slate-50 px-4 py-2 border-b border-slate-200" >
2026-05-19 16:20:31 +06:00
< p className = "text-sm font-medium text-slate-700" > EV Investment Plans < / p >
2026-05-14 22:25:24 +06:00
< / div >
< div className = "divide-y divide-slate-100" >
{ investor . investments ? . map ( ( inv : any ) = > {
const planColors : Record < string , string > = {
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 (
< div key = { inv . id } className = "p-4" >
< div className = "flex items-center gap-3 mb-2" >
< input
type = "checkbox"
id = { ` plan- ${ inv . id } ` }
checked = { withdrawSelection . selectAll || withdrawSelection . selectedPlans . includes ( inv . id ) }
disabled = { withdrawSelection . selectAll }
onChange = { ( e ) = > {
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"
/ >
< label htmlFor = { ` plan- ${ inv . id } ` } className = "flex-1 flex items-center justify-between" >
< div >
< span className = "font-medium text-slate-800" > { inv . planName } < / span >
< span className = { ` ml-2 px-2 py-0.5 rounded text-xs capitalize ${ planColors [ inv . planType ] } ` } > { inv . planType } < / span >
< / div >
< span className = "text-sm font-bold text-green-600" > ৳ { invEarnings . toLocaleString ( ) } < / span >
< / label >
< / div >
{ ! withdrawSelection . selectAll && invBikes . length > 0 && (
< div className = "ml-7 pl-4 border-l-2 border-slate-200 space-y-2" >
{ invBikes . map ( ( bike : any ) = > (
< div key = { bike . id } className = "flex items-center gap-2" >
< input
type = "checkbox"
id = { ` bike- ${ bike . id } ` }
checked = { withdrawSelection . selectedBikes . includes ( bike . id ) }
onChange = { ( e ) = > {
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"
/ >
< label htmlFor = { ` bike- ${ bike . id } ` } className = "flex-1 text-sm text-slate-600" >
{ bike . model } - { bike . plateNumber }
< / label >
< span className = "text-xs text-green-600 font-medium" > ৳ { bike . totalEarnings ? . toLocaleString ( ) || 0 } < / span >
< / div >
) ) }
< / div >
) }
< / div >
) ;
} ) }
< / div >
< / div >
< / div >
< / div >
< div >
< h4 className = "font-semibold text-slate-800 mb-3" > Withdrawal Amount < / h4 >
< div className = "bg-gradient-to-r from-green-50 to-emerald-50 border border-green-200 rounded-xl p-5" >
< div className = "flex items-center justify-between mb-2" >
< span className = "text-sm text-slate-600" > Calculated Amount < / span >
< span className = { ` text-xs px-2 py-0.5 rounded-full ${ withdrawSelection . selectAll ? 'bg-green-100 text-green-700' : 'bg-amber-100 text-amber-700' } ` } >
{ withdrawSelection . selectAll ? 'All Selected' : ` ${ withdrawSelection . selectedBikes . length } bikes ` }
< / span >
< / div >
< p className = "text-3xl font-bold text-green-700 mb-1" >
৳ { ( 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 ( ) }
< / p >
< p className = "text-xs text-slate-500" >
Based on { withdrawSelection . selectAll ? 'all' : withdrawSelection . selectedBikes . length } selected bike ( s ) earnings
< / p >
< / div >
< / div >
< div >
< h4 className = "font-semibold text-slate-800 mb-3" > Payment Method < / h4 >
< div className = "grid grid-cols-2 gap-3" >
{ investor . bankAccounts ? . map ( ( account : any ) = > (
< div
key = { account . id }
onClick = { ( ) = > 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' } ` }
>
< div className = "flex items-center gap-2 mb-1" >
< div className = { ` w-8 h-8 rounded-lg flex items-center justify-center ${ account . isPrimary ? 'bg-green-100' : 'bg-slate-100' } ` } >
< Banknote className = { ` w-4 h-4 ${ account . isPrimary ? 'text-green-600' : 'text-slate-500' } ` } / >
< / div >
< div >
< p className = "text-sm font-medium text-slate-800" > { account . bankName } < / p >
{ account . isPrimary && < span className = "text-xs text-green-600" > Primary < / span > }
< / div >
< / div >
< p className = "text-xs text-slate-500" > { account . accountNumber } < / p >
< / div >
) ) }
< / div >
{ investor . mobileBanking && (
< div
onClick = { ( ) = > 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' } ` }
>
< div className = "flex items-center gap-2" >
< div className = "w-8 h-8 rounded-lg bg-pink-100 flex items-center justify-center" >
< Phone className = "w-4 h-4 text-pink-600" / >
< / div >
< div >
< p className = "text-sm font-medium text-slate-800" > { investor . mobileBanking } < / p >
< p className = "text-xs text-slate-500" > { investor . mobileBankingNumber } < / p >
< / div >
< / div >
< / div >
) }
< / div >
< / div >
< div className = "p-5 border-t border-slate-100 flex justify-between items-center" >
< button
onClick = { ( ) = > { setShowWithdrawalModal ( false ) ; setShowAutoWithdrawModal ( true ) ; } }
className = "px-4 py-2 text-sm text-slate-600 hover:text-investor flex items-center gap-1"
>
< Settings className = "w-4 h-4" / > Configure Auto - Withdraw
< / button >
< div className = "flex gap-3" >
< button onClick = { ( ) = > setShowWithdrawalModal ( false ) } className = "px-4 py-2 border border-slate-200 text-slate-600 rounded-lg text-sm hover:bg-slate-50" >
Cancel
< / button >
< button
disabled = { ! withdrawSelection . accountId || ( withdrawSelection . selectedPlans . length === 0 && ! withdrawSelection . selectAll ) }
className = "px-6 py-2 bg-red-500 text-white rounded-lg text-sm font-medium hover:bg-red-600 disabled:opacity-50 disabled:cursor-not-allowed"
>
Submit Request
< / button >
< / div >
< / div >
< / div >
< / div >
) }
{ showAutoWithdrawModal && (
< 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 flex items-center gap-2" >
< Settings className = "w-5 h-5 text-investor" / > Auto - Withdraw Settings
< / h2 >
< button onClick = { ( ) = > setShowAutoWithdrawModal ( 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-5" >
< div className = "flex items-center justify-between p-4 bg-slate-50 rounded-lg" >
< div >
< p className = "font-medium text-slate-800" > Enable Auto - Withdraw < / p >
< p className = "text-xs text-slate-500" > Automatically withdraw earnings < / p >
< / div >
< button
onClick = { ( ) = > setAutoWithdrawSettings ( { . . . autoWithdrawSettings , enabled : ! autoWithdrawSettings . enabled } ) }
className = { ` w-12 h-6 rounded-full transition-colors ${ autoWithdrawSettings . enabled ? 'bg-investor' : 'bg-slate-300' } ` }
>
< div className = { ` w-5 h-5 bg-white rounded-full shadow transition-transform ${ autoWithdrawSettings . enabled ? 'translate-x-6' : 'translate-x-0.5' } ` } / >
< / button >
< / div >
{ autoWithdrawSettings . enabled && (
< >
< div >
< label className = "text-sm font-medium text-slate-700 mb-2 block" > Withdrawal Frequency < / label >
< div className = "grid grid-cols-3 gap-2" >
{ [
{ value : 'as_per_request' , label : 'As Requested' } ,
{ value : 'weekly' , label : 'Weekly' } ,
{ value : 'monthly' , label : 'Monthly' }
] . map ( opt = > (
< button
key = { opt . value }
onClick = { ( ) = > setAutoWithdrawSettings ( { . . . autoWithdrawSettings , frequency : opt.value as any } ) }
className = { ` px-3 py-2 rounded-lg text-sm font-medium transition-colors ${ autoWithdrawSettings . frequency === opt . value ? 'bg-investor text-white' : 'bg-slate-100 text-slate-600 hover:bg-slate-200' } ` }
>
{ opt . label }
< / button >
) ) }
< / div >
< / div >
< div >
< label className = "text-sm font-medium text-slate-700 mb-2 block" > Minimum Amount < / label >
< div className = "relative" >
< span className = "absolute left-3 top-1/2 -translate-y-1/2 text-slate-500" > ৳ < / span >
< input
type = "number"
value = { autoWithdrawSettings . minAmount }
onChange = { ( e ) = > 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"
/ >
< / div >
< p className = "text-xs text-slate-400 mt-1" > Minimum balance required for auto - withdrawal < / p >
< / div >
< div >
< label className = "text-sm font-medium text-slate-700 mb-2 block" > Destination Account < / label >
< div className = "space-y-2" >
{ investor . bankAccounts ? . map ( ( account : any ) = > (
< div
key = { account . id }
onClick = { ( ) = > 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' } ` }
>
< div className = { ` w-4 h-4 rounded-full border-2 ${ autoWithdrawSettings . accountId === account . id ? 'border-investor bg-investor' : 'border-slate-300' } ` } >
{ autoWithdrawSettings . accountId === account . id && < div className = "w-full h-full rounded-full bg-white scale-50" / > }
< / div >
< div >
< p className = "text-sm font-medium" > { account . bankName } < / p >
< p className = "text-xs text-slate-500" > { account . accountNumber } < / p >
< / div >
{ account . isPrimary && < span className = "ml-auto text-xs bg-green-100 text-green-700 px-2 py-0.5 rounded" > Primary < / span > }
< / div >
) ) }
{ investor . mobileBanking && (
< div
onClick = { ( ) = > 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' } ` }
>
< div className = { ` w-4 h-4 rounded-full border-2 ${ autoWithdrawSettings . accountId === 'mobile' ? 'border-investor bg-investor' : 'border-slate-300' } ` } >
{ autoWithdrawSettings . accountId === 'mobile' && < div className = "w-full h-full rounded-full bg-white scale-50" / > }
< / div >
< div >
< p className = "text-sm font-medium" > { investor . mobileBanking } < / p >
< p className = "text-xs text-slate-500" > { investor . mobileBankingNumber } < / p >
< / div >
< / div >
) }
< / div >
< / div >
< / >
) }
< / div >
< div className = "p-5 border-t border-slate-100 flex justify-end gap-3" >
< button onClick = { ( ) = > setShowAutoWithdrawModal ( false ) } className = "px-4 py-2 border border-slate-200 text-slate-600 rounded-lg text-sm hover:bg-slate-50" >
Cancel
< / button >
< button
onClick = { ( ) = > { alert ( 'Auto-withdraw settings saved!' ) ; setShowAutoWithdrawModal ( false ) ; } }
className = "px-4 py-2 bg-investor text-white rounded-lg text-sm font-medium hover:bg-investor-dark"
>
Save Settings
< / button >
< / div >
< / div >
< / div >
) }
2026-05-19 19:27:03 +06:00
< AssignBatteryModal
isOpen = { showAssignBatteryModal }
onClose = { ( ) = > 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 } ` ) ;
} }
/ >
2026-05-19 17:25:32 +06:00
{ showRegisterBatteryModal && (
< 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-lg overflow-hidden flex flex-col" >
< div className = "p-5 border-b border-emerald-100 bg-emerald-50 flex items-center justify-between" >
< h2 className = "text-lg font-bold text-emerald-800 flex items-center gap-2" >
< Battery className = "w-5 h-5 text-emerald-600 animate-bounce" / >
Register & Assign New Battery
< / h2 >
< button onClick = { ( ) = > setShowRegisterBatteryModal ( false ) } className = "p-2 hover:bg-emerald-100 rounded-lg text-emerald-600" >
< X className = "w-5 h-5" / >
< / button >
< / div >
< div className = "p-5 space-y-4 overflow-y-auto max-h-[75vh]" >
< div className = "grid grid-cols-2 gap-4" >
< div >
< label className = "text-sm font-medium text-slate-600 mb-1 block" > Serial Number * < / label >
< input
type = "text"
value = { registerForm . serialNumber }
onChange = { ( e ) = > 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"
/ >
< / div >
< div >
< label className = "text-sm font-medium text-slate-600 mb-1 block" > Brand * < / label >
< select
value = { registerForm . brand }
onChange = { ( e ) = > setRegisterForm ( { . . . registerForm , brand : e.target.value } ) }
className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
>
< option value = "BYD" > BYD < / option >
< option value = "CATL" > CATL < / option >
< option value = "EVE Energy" > EVE Energy < / option >
< option value = "Panasonic" > Panasonic < / option >
< / select >
< / div >
< / div >
< div className = "grid grid-cols-2 gap-4" >
< div >
< label className = "text-sm font-medium text-slate-600 mb-1 block" > Model * < / label >
< input
type = "text"
value = { registerForm . model }
onChange = { ( e ) = > setRegisterForm ( { . . . registerForm , model : e.target.value } ) }
className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
/ >
< / div >
< div >
< label className = "text-sm font-medium text-slate-600 mb-1 block" > Cell Chemistry * < / label >
< select
value = { registerForm . type }
onChange = { ( e ) = > setRegisterForm ( { . . . registerForm , type : e . target . value } ) }
className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
>
< option value = "lithium-ion" > Lithium - Ion < / option >
< option value = "lifepo4" > LiFePO4 < / option >
< / select >
< / div >
< / div >
< div className = "grid grid-cols-3 gap-4" >
< div >
< label className = "text-sm font-medium text-slate-600 mb-1 block" > Capacity ( Ah ) < / label >
< input
type = "number"
value = { registerForm . capacity }
onChange = { ( e ) = > setRegisterForm ( { . . . registerForm , capacity : Number ( e . target . value ) } ) }
className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
/ >
< / div >
< div >
< label className = "text-sm font-medium text-slate-600 mb-1 block" > Voltage ( V ) < / label >
< input
type = "number"
value = { registerForm . voltage }
onChange = { ( e ) = > setRegisterForm ( { . . . registerForm , voltage : Number ( e . target . value ) } ) }
className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
/ >
< / div >
< div >
< label className = "text-sm font-medium text-slate-600 mb-1 block" > Purchase Price ( ৳ ) < / label >
< input
type = "number"
value = { registerForm . purchasePrice }
onChange = { ( e ) = > 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"
/ >
< / div >
< / div >
< div className = "border-t border-dashed border-slate-200 pt-3" >
< label className = "text-sm font-semibold text-emerald-800 mb-2 block" > Assignment Settings < / label >
< div className = "space-y-4" >
< div >
< label className = "text-sm font-medium text-slate-600 mb-1 block" > Link to Investment Plan * < / label >
< select
value = { registerForm . investmentId }
onChange = { ( e ) = > setRegisterForm ( { . . . registerForm , investmentId : e.target.value } ) }
className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
>
< option value = "" > Select plan < / option >
{ investor . investments ? . map ( ( inv : any ) = > (
< option key = { inv . id } value = { inv . id } >
{ inv . planName } ( # { inv . id ? . slice ( - 6 ) } )
< / option >
) ) }
< / select >
< / div >
< div className = "grid grid-cols-2 gap-4" >
< div >
< label className = "text-sm font-medium text-slate-600 mb-1 block" > Refundable Deposit ( ৳ ) * < / label >
< input
type = "number"
value = { registerForm . deposit }
onChange = { ( e ) = > setRegisterForm ( { . . . registerForm , deposit : Number ( e . target . value ) } ) }
className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
/ >
< / div >
< div >
< label className = "text-sm font-medium text-slate-600 mb-1 block" > Daily Rent Price ( ৳ ) * < / label >
< input
type = "number"
value = { registerForm . rentPrice }
onChange = { ( e ) = > setRegisterForm ( { . . . registerForm , rentPrice : Number ( e . target . value ) } ) }
className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
/ >
< / div >
< / div >
< div className = "grid grid-cols-2 gap-4" >
< div >
< label className = "text-sm font-medium text-slate-600 mb-1 block" > Investor Share % * < / label >
< input
type = "number"
value = { registerForm . investorShare }
onChange = { ( e ) = > setRegisterForm ( { . . . registerForm , investorShare : Number ( e . target . value ) } ) }
className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
/ >
< / div >
< div >
< label className = "text-sm font-medium text-slate-600 mb-1 block" > Capital Invested ( ৳ ) * < / label >
< input
type = "number"
value = { registerForm . investedAmount }
onChange = { ( e ) = > setRegisterForm ( { . . . registerForm , investedAmount : Number ( e . target . value ) } ) }
className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
/ >
< / div >
< / div >
< / div >
< / div >
< / div >
< div className = "p-5 border-t border-slate-100 flex justify-end gap-3" >
< button
onClick = { ( ) = > setShowRegisterBatteryModal ( false ) }
className = "px-4 py-2 border border-slate-200 text-slate-600 rounded-lg text-sm font-semibold hover:bg-slate-50"
>
Cancel
< / button >
< button
onClick = { handleRegisterAndAssignBattery }
disabled = { ! registerForm . serialNumber || ! registerForm . investmentId }
className = "px-4 py-2 bg-emerald-600 text-white rounded-lg text-sm font-semibold hover:bg-emerald-700 disabled:opacity-50 disabled:cursor-not-allowed shadow-sm"
>
Register & Assign
< / button >
< / div >
< / div >
< / div >
) }
{ showEditBatteryModal && (
< 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 overflow-hidden flex flex-col" >
< div className = "p-5 border-b border-emerald-100 bg-emerald-50 flex items-center justify-between" >
< h2 className = "text-lg font-bold text-emerald-800 flex items-center gap-2" >
< Battery className = "w-5 h-5 text-emerald-600 animate-pulse" / >
Edit Battery Assignment
< / h2 >
< button onClick = { ( ) = > setShowEditBatteryModal ( false ) } className = "p-2 hover:bg-emerald-100 rounded-lg text-emerald-600" >
< X className = "w-5 h-5" / >
< / button >
< / div >
< div className = "p-5 space-y-4 overflow-y-auto max-h-[75vh]" >
< div className = "bg-slate-50 p-3 rounded-lg border border-slate-200" >
< p className = "text-xs text-slate-500 font-medium" > SELECTED ASSET < / p >
< p className = "font-bold text-slate-800 text-sm mt-0.5" >
{ selectedBatteryToEdit ? . brand } { selectedBatteryToEdit ? . model } ( { selectedBatteryToEdit ? . serialNumber } )
< / p >
< p className = "text-xs text-slate-400" > ID : { selectedBatteryToEdit ? . id } < / p >
< / div >
< div >
< label className = "text-sm font-medium text-slate-600 mb-1 block" > Link to Investment Plan * < / label >
< select
value = { assignForm . investmentId }
onChange = { ( e ) = > setAssignForm ( { . . . assignForm , investmentId : e.target.value } ) }
className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
>
< option value = "" > Select plan < / option >
{ investor . investments ? . map ( ( inv : any ) = > (
< option key = { inv . id } value = { inv . id } >
{ inv . planName } ( # { inv . id ? . slice ( - 6 ) } )
< / option >
) ) }
< / select >
< / div >
< div className = "grid grid-cols-2 gap-4" >
< div >
< label className = "text-sm font-medium text-slate-600 mb-1 block" > Refundable Deposit ( ৳ ) * < / label >
< input
type = "number"
value = { assignForm . deposit }
onChange = { ( e ) = > setAssignForm ( { . . . assignForm , deposit : Number ( e . target . value ) } ) }
className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
/ >
< / div >
< div >
< label className = "text-sm font-medium text-slate-600 mb-1 block" > Daily Rent Price ( ৳ ) * < / label >
< input
type = "number"
value = { assignForm . rentPrice }
onChange = { ( e ) = > setAssignForm ( { . . . assignForm , rentPrice : Number ( e . target . value ) } ) }
className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
/ >
< / div >
< / div >
< div className = "grid grid-cols-2 gap-4" >
< div >
< label className = "text-sm font-medium text-slate-600 mb-1 block" > Investor Share % * < / label >
< input
type = "number"
value = { assignForm . investorShare }
onChange = { ( e ) = > setAssignForm ( { . . . assignForm , investorShare : Number ( e . target . value ) } ) }
className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
/ >
< / div >
< div >
< label className = "text-sm font-medium text-slate-600 mb-1 block" > Contribution ( ৳ ) * < / label >
< input
type = "number"
value = { assignForm . investedAmount }
onChange = { ( e ) = > setAssignForm ( { . . . assignForm , investedAmount : Number ( e . target . value ) } ) }
className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
/ >
< / div >
< / div >
< / div >
< div className = "p-5 border-t border-slate-100 flex justify-end gap-3" >
< button
onClick = { ( ) = > setShowEditBatteryModal ( false ) }
className = "px-4 py-2 border border-slate-200 text-slate-600 rounded-lg text-sm font-semibold hover:bg-slate-50"
>
Cancel
< / button >
< button
onClick = { handleSaveEditBattery }
disabled = { ! assignForm . investmentId }
className = "px-4 py-2 bg-emerald-600 text-white rounded-lg text-sm font-semibold hover:bg-emerald-700 disabled:opacity-50 disabled:cursor-not-allowed shadow-sm"
>
Save Changes
< / button >
< / div >
< / div >
< / div >
) }
2026-05-19 19:27:03 +06:00
< AssignBikeModal
isOpen = { showAssignBikeModal }
onClose = { ( ) = > 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 } ` ) ;
} }
/ >
< UnassignConfirmModal
isOpen = { unassignConfirmModal . show }
onClose = { ( ) = > 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 ) ;
}
} }
/ >
2026-04-22 01:09:51 +06:00
2026-05-19 18:11:35 +06:00
{ showRegisterBikeModal && (
< 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" > Register & Assign New Bike < / h2 >
< button onClick = { ( ) = > setShowRegisterBikeModal ( 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" > Plate Number < / label >
< input
type = "text"
value = { registerBikeForm . plateNumber }
onChange = { ( e ) = > 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"
/ >
< / div >
< div className = "grid grid-cols-2 gap-3" >
< div >
< label className = "text-sm font-medium text-slate-600 mb-1 block" > Brand < / label >
< input
type = "text"
value = { registerBikeForm . brand }
onChange = { ( e ) = > setRegisterBikeForm ( { . . . registerBikeForm , brand : e.target.value } ) }
className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
/ >
< / div >
< div >
< label className = "text-sm font-medium text-slate-600 mb-1 block" > Model < / label >
< input
type = "text"
value = { registerBikeForm . model }
onChange = { ( e ) = > setRegisterBikeForm ( { . . . registerBikeForm , model : e.target.value } ) }
className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
/ >
< / div >
< / div >
< div className = "grid grid-cols-2 gap-3" >
< div >
< label className = "text-sm font-medium text-slate-600 mb-1 block" > Purchase Price ( ৳ ) < / label >
< input
type = "number"
value = { registerBikeForm . purchasePrice }
onChange = { ( e ) = > setRegisterBikeForm ( { . . . registerBikeForm , purchasePrice : Number ( e . target . value ) } ) }
className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
/ >
< / div >
< div >
< label className = "text-sm font-medium text-slate-600 mb-1 block" > Daily Rent ( ৳ ) < / label >
< input
type = "number"
value = { registerBikeForm . currentRent }
onChange = { ( e ) = > setRegisterBikeForm ( { . . . registerBikeForm , currentRent : Number ( e . target . value ) } ) }
className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
/ >
< / div >
< / div >
< div >
< label className = "text-sm font-medium text-slate-600 mb-1 block" > Location < / label >
< input
type = "text"
value = { registerBikeForm . location }
onChange = { ( e ) = > setRegisterBikeForm ( { . . . registerBikeForm , location : e.target.value } ) }
className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
/ >
< / div >
< div >
< label className = "text-sm font-medium text-slate-600 mb-1 block" > Rental Type < / label >
< select
value = { registerBikeForm . rentalType }
onChange = { ( e ) = > setRegisterBikeForm ( { . . . registerBikeForm , rentalType : e.target.value } ) }
className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
>
< option value = "single_rent" > Single Rent < / option >
< option value = "rent_to_own" > Rent to Own < / option >
< option value = "share_ev" > Share EV < / option >
< / select >
< / div >
< div >
< label className = "text-sm font-medium text-slate-600 mb-1 block" > Attach to Investment Plan < / label >
< select
value = { registerBikeForm . investmentId }
onChange = { ( e ) = > setRegisterBikeForm ( { . . . registerBikeForm , investmentId : e.target.value } ) }
className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
>
< option value = "" > Select plan < / option >
{ investor . investments ? . filter ( ( inv : any ) = > inv . assetType === 'bike' || ! inv . assetType ) . map ( ( inv : any ) = > (
< option key = { inv . id } value = { inv . id } > { inv . planName } ( { inv . id } ) < / option >
) ) }
< / select >
< / div >
< / div >
< div className = "p-5 border-t border-slate-100 flex justify-end gap-3" >
< button onClick = { ( ) = > setShowRegisterBikeModal ( 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 = { handleRegisterAndAssignBike } className = "px-4 py-2 bg-investor text-white rounded-lg text-sm hover:bg-investor-dark" > Register & Assign < / button >
< / div >
< / div >
< / div >
) }
2026-04-22 01:09:51 +06:00
{ showCreateInvestmentModal && (
< div className = "fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4" >
2026-05-14 19:49:57 +06:00
< div className = "bg-white rounded-xl shadow-xl w-full max-w-3xl max-h-[90vh] overflow-hidden flex flex-col" >
2026-04-22 01:09:51 +06:00
< div className = "p-5 border-b border-slate-100 flex items-center justify-between" >
2026-05-14 19:49:57 +06:00
< div >
< h2 className = "text-lg font-bold text-slate-800" > Create New Investment < / h2 >
< p className = "text-sm text-slate-500" > Set up investment for { investor . name } < / p >
< / div >
2026-04-22 01:09:51 +06:00
< button onClick = { ( ) = > setShowCreateInvestmentModal ( false ) } className = "p-2 hover:bg-slate-100 rounded-lg" >
< X className = "w-5 h-5 text-slate-400" / >
< / button >
< / div >
2026-05-14 19:49:57 +06:00
< div className = "p-5 overflow-y-auto flex-1 space-y-5" >
2026-05-19 17:25:32 +06:00
{ /* Asset Type Selector */ }
< div >
< label className = "text-sm font-semibold text-slate-700 mb-2 block" > Asset Investment Category * < / label >
< div className = "grid grid-cols-2 gap-4" >
< button
onClick = { ( ) = > {
setNewInvestment ( {
. . . newInvestment ,
assetType : 'bike' ,
planName : bikeTemplates [ 0 ] . name ,
planType : bikeTemplates [ 0 ] . tier . toLowerCase ( ) as any ,
totalInvestment : bikeTemplates [ 0 ] . evBasePrice * bikeTemplates [ 0 ] . minQuantity ,
paidAmount : bikeTemplates [ 0 ] . evBasePrice * bikeTemplates [ 0 ] . minQuantity ,
paymentType : 'full'
} ) ;
} }
className = { ` py-3 px-4 rounded-xl border-2 flex items-center justify-center gap-2 font-bold text-sm transition-all ${ newInvestment . assetType === 'bike'
? 'bg-investor/10 border-investor text-investor shadow-sm'
: 'bg-white border-slate-200 text-slate-600 hover:bg-slate-50'
} ` }
>
< Bike className = "w-5 h-5 text-investor" / > Bike Investment Plan
< / button >
< button
onClick = { ( ) = > {
setNewInvestment ( {
. . . newInvestment ,
assetType : 'battery' ,
planName : batteryTemplates [ 0 ] . name ,
planType : batteryTemplates [ 0 ] . tier . toLowerCase ( ) as any ,
totalInvestment : batteryTemplates [ 0 ] . evBasePrice * batteryTemplates [ 0 ] . minQuantity ,
paidAmount : batteryTemplates [ 0 ] . evBasePrice * batteryTemplates [ 0 ] . minQuantity ,
paymentType : 'full'
} ) ;
} }
className = { ` py-3 px-4 rounded-xl border-2 flex items-center justify-center gap-2 font-bold text-sm transition-all ${ newInvestment . assetType === 'battery'
? 'bg-emerald-50 border-emerald-500 text-emerald-800 shadow-sm'
: 'bg-white border-slate-200 text-slate-600 hover:bg-slate-50'
} ` }
>
< Battery className = "w-5 h-5 text-emerald-600 animate-pulse" / > Battery Investment Plan
< / button >
< / div >
< / div >
2026-05-14 19:49:57 +06:00
< div >
< label className = "text-sm font-medium text-slate-600 mb-2 block" > Select Plan Template < / label >
2026-05-19 17:25:32 +06:00
< div className = "grid grid-cols-2 md:grid-cols-3 gap-3" >
{ ( newInvestment . assetType === 'battery' ? batteryTemplates : bikeTemplates ) . map ( ( plan : any ) = > (
2026-05-14 19:49:57 +06:00
< button
key = { plan . id }
onClick = { ( ) = > {
setNewInvestment ( {
. . . newInvestment ,
planName : plan.name ,
planType : plan.tier.toLowerCase ( ) as any ,
totalInvestment : plan.evBasePrice * plan . minQuantity ,
2026-05-15 03:10:37 +06:00
paidAmount : plan.evBasePrice * plan . minQuantity ,
2026-05-15 12:25:24 +06:00
paymentType : 'full' ,
2026-05-14 19:49:57 +06:00
monthlyReturn : 0
} ) ;
} }
className = { ` p-4 rounded-lg border-2 text-left transition-all ${ newInvestment . planName === plan . name ? 'border-investor bg-investor/5' : 'border-slate-200 hover:border-investor/50' } ` }
>
< p className = "font-semibold text-slate-800" > { plan . name } < / p >
2026-05-19 17:25:32 +06:00
< p className = "text-xs text-slate-500" > ৳ { plan . evBasePrice . toLocaleString ( ) } × { plan . minQuantity } { newInvestment . assetType } ( s ) < / p >
2026-05-14 19:49:57 +06:00
< p className = "text-sm text-slate-600 mt-1" > Duration : { plan . duration } months < / p >
< / button >
) ) }
< / div >
< / div >
2026-04-22 01:09:51 +06:00
< div className = "grid grid-cols-2 gap-4" >
< div >
< label className = "text-sm font-medium text-slate-600 mb-1 block" > Plan Name * < / label >
2026-05-14 13:54:02 +06:00
< input
type = "text"
2026-04-22 01:09:51 +06:00
value = { newInvestment . planName }
onChange = { ( e ) = > setNewInvestment ( { . . . newInvestment , planName : e.target.value } ) }
2026-05-19 17:25:32 +06:00
className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm font-semibold"
2026-05-14 19:49:57 +06:00
placeholder = "Plan name"
2026-04-22 01:09:51 +06:00
/ >
< / div >
< div >
2026-05-19 17:25:32 +06:00
< label className = "text-sm font-medium text-slate-600 mb-1 block" > Plan Type ( Tier ) < / label >
2026-05-14 13:54:02 +06:00
< select
2026-04-22 01:09:51 +06:00
value = { newInvestment . planType }
2026-05-14 19:49:57 +06:00
onChange = { ( e ) = > setNewInvestment ( { . . . newInvestment , planType : e.target.value as any } ) }
2026-04-22 01:09:51 +06:00
className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
>
2026-05-14 19:49:57 +06:00
< option value = "silver" > Silver < / option >
< option value = "gold" > Gold < / option >
< option value = "platinum" > Platinum < / option >
< option value = "diamond" > Diamond < / option >
2026-04-22 01:09:51 +06:00
< / select >
< / div >
< / div >
2026-05-14 19:49:57 +06:00
< div className = "grid grid-cols-3 gap-4" >
< div >
2026-05-19 17:25:32 +06:00
< label className = "text-sm font-medium text-slate-600 mb-1 block" > { newInvestment . assetType === 'battery' ? 'Battery Pack Cost (৳)' : 'EV Base Price (৳)' } < / label >
< input type = "text" disabled value = { ` ৳ ${ activeTemplate . evBasePrice . toLocaleString ( ) } ` } className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-slate-100 font-semibold cursor-not-allowed text-slate-700" / >
2026-05-14 19:49:57 +06:00
< / div >
< div >
2026-05-19 17:25:32 +06:00
< label className = "text-sm font-medium text-slate-600 mb-1 block" > Min Quantity ( { newInvestment . assetType === 'battery' ? 'Packs' : 'Bikes' } ) < / label >
< input type = "text" disabled value = { activeTemplate . minQuantity } className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-slate-100 font-semibold cursor-not-allowed text-slate-700" / >
2026-05-14 19:49:57 +06:00
< / div >
< div >
< label className = "text-sm font-medium text-slate-600 mb-1 block" > Min Investment ( ৳ ) < / label >
2026-05-19 17:25:32 +06:00
< input type = "text" value = { ` ৳ ${ ( activeTemplate . evBasePrice * activeTemplate . minQuantity ) . toLocaleString ( ) } ` } disabled className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-slate-100 font-bold cursor-not-allowed text-slate-700" / >
< p className = "text-[10px] text-slate-400 mt-1" > = Qty × Base Price < / p >
2026-04-22 01:09:51 +06:00
< / div >
< / div >
2026-05-14 19:49:57 +06:00
< div className = "grid grid-cols-2 gap-4" >
2026-04-22 01:09:51 +06:00
< div >
< label className = "text-sm font-medium text-slate-600 mb-1 block" > Investment Amount ( ৳ ) * < / label >
2026-05-14 13:54:02 +06:00
< input
2026-04-22 01:09:51 +06:00
type = "number"
value = { newInvestment . totalInvestment }
2026-05-15 03:10:37 +06:00
onChange = { ( e ) = > {
const val = Number ( e . target . value ) ;
setNewInvestment ( {
. . . newInvestment ,
totalInvestment : val ,
2026-05-19 17:25:32 +06:00
paidAmount : newInvestment.assetType === 'battery' ? val : ( newInvestment . paymentType === 'full' ? val : Math.max ( val * 0.5 , newInvestment . paidAmount ) )
2026-05-15 03:10:37 +06:00
} ) ;
} }
2026-05-19 17:25:32 +06:00
className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-yellow-50 font-bold"
2026-04-22 01:09:51 +06:00
/ >
< / div >
< div >
2026-05-19 17:25:32 +06:00
< label className = "text-sm font-medium text-slate-600 mb-1 block" > Max Investment Cap ( ৳ ) < / label >
< input type = "text" value = { ` ৳ ${ activeTemplate . maxInvestment . toLocaleString ( ) } ` } disabled className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-slate-100 cursor-not-allowed font-semibold text-slate-700" / >
2026-05-14 19:49:57 +06:00
< / div >
< / div >
< div className = "grid grid-cols-3 gap-4" >
< div >
< label className = "text-sm font-medium text-slate-600 mb-1 block" > Duration ( Months ) < / label >
2026-05-19 17:25:32 +06:00
< input type = "text" value = { ` ${ activeTemplate . duration } Months ` } disabled className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-slate-100 cursor-not-allowed font-semibold text-slate-700" / >
2026-04-22 01:09:51 +06:00
< / div >
< div >
2026-05-14 19:49:57 +06:00
< label className = "text-sm font-medium text-slate-600 mb-1 block" > Lock - in Period ( Months ) < / label >
2026-05-19 17:25:32 +06:00
< input type = "text" value = { ` ${ activeTemplate . lockIn } Months ` } disabled className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-slate-100 cursor-not-allowed font-semibold text-slate-700" / >
2026-05-14 19:49:57 +06:00
< / div >
< div >
< label className = "text-sm font-medium text-slate-600 mb-1 block" > Early Exit Penalty ( % ) < / label >
2026-05-19 17:25:32 +06:00
< input type = "text" value = { ` ${ activeTemplate . exitPenalty } % ` } disabled className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-slate-100 cursor-not-allowed font-semibold text-slate-700" / >
2026-05-14 19:49:57 +06:00
< / div >
< / div >
2026-05-19 17:25:32 +06:00
< div className = "bg-emerald-50 border border-emerald-200 rounded-xl p-4" >
< h4 className = "text-sm font-semibold text-emerald-800 mb-3 flex items-center gap-2" >
2026-05-14 19:49:57 +06:00
< TrendingUp className = "w-4 h-4" / >
2026-05-19 17:25:32 +06:00
{ newInvestment . assetType === 'battery' ? 'Partner Profit Sharing Percentage' : 'FICO Share - Partner Yield Sharing Percentages' }
2026-05-14 19:49:57 +06:00
< / h4 >
2026-05-19 17:25:32 +06:00
< p className = "text-xs text-emerald-600 mb-3" > Profit sharing ratio when { newInvestment . assetType } s are utilized < / p >
{ newInvestment . assetType === 'battery' ? (
< div className = "flex items-center justify-between bg-white p-3 rounded-lg border border-emerald-250" >
< span className = "text-sm font-medium text-slate-700" > Profit Share Percent ( % ) < / span >
< span className = "font-bold text-emerald-600 text-lg" > { activeTemplate . profitSharePercent || activeTemplate . profitShareSingle } % < / span >
2026-05-14 19:49:57 +06:00
< / div >
2026-05-19 17:25:32 +06:00
) : (
< div className = "grid grid-cols-3 gap-4" >
< div >
< label className = "text-xs text-slate-500 mb-1 block" > Single Rent ( % ) < / label >
< input type = "text" value = { ` ${ activeTemplate . profitShareSingle } % ` } disabled className = "w-full px-3 py-2 border border-emerald-250 rounded-lg text-sm bg-white cursor-not-allowed font-semibold text-slate-700 text-center" / >
< / div >
< div >
< label className = "text-xs text-slate-500 mb-1 block" > Rent to Own ( % ) < / label >
< input type = "text" value = { ` ${ activeTemplate . profitShareOwn } % ` } disabled className = "w-full px-3 py-2 border border-emerald-250 rounded-lg text-sm bg-white cursor-not-allowed font-semibold text-slate-700 text-center" / >
< / div >
< div >
< label className = "text-xs text-slate-500 mb-1 block" > Share an EV ( % ) < / label >
< input type = "text" value = { ` ${ activeTemplate . profitShareEV } % ` } disabled className = "w-full px-3 py-2 border border-emerald-250 rounded-lg text-sm bg-white cursor-not-allowed font-semibold text-slate-700 text-center" / >
< / div >
2026-05-14 19:49:57 +06:00
< / div >
2026-05-19 17:25:32 +06:00
) }
2026-04-22 01:09:51 +06:00
< / div >
< div className = "grid grid-cols-2 gap-4" >
< div >
< label className = "text-sm font-medium text-slate-600 mb-1 block" > Start Date * < / label >
2026-05-14 13:54:02 +06:00
< input
2026-04-22 01:09:51 +06:00
type = "date"
value = { newInvestment . startDate }
onChange = { ( e ) = > setNewInvestment ( { . . . newInvestment , startDate : e.target.value } ) }
className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
/ >
< / div >
< div >
< label className = "text-sm font-medium text-slate-600 mb-1 block" > End Date < / label >
2026-05-14 13:54:02 +06:00
< input
2026-04-22 01:09:51 +06:00
type = "date"
value = { newInvestment . endDate }
onChange = { ( e ) = > setNewInvestment ( { . . . newInvestment , endDate : e.target.value } ) }
className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
/ >
< / div >
< / div >
2026-05-15 12:25:24 +06:00
< div className = "bg-slate-50 rounded-xl p-4 border border-slate-200" >
< h4 className = "text-sm font-semibold text-slate-800 mb-3 flex items-center gap-2" >
< CreditCard className = "w-4 h-4 text-investor" / > Payment Options
< / h4 >
2026-05-19 17:25:32 +06:00
{ newInvestment . assetType === 'battery' ? (
< div className = "flex justify-between items-center bg-white p-3.5 rounded-lg border border-slate-200 shadow-sm" >
< div >
< p className = "font-semibold text-slate-800 text-sm flex items-center gap-1.5" >
< Check className = "w-4 h-4 text-emerald-600" / > Full Payment Only
< / p >
< p className = "text-xs text-slate-400 mt-0.5" > Battery plans require full upfront capital < / p >
2026-05-15 12:25:24 +06:00
< / div >
< span className = "text-lg font-bold text-green-600" > ৳ { newInvestment . totalInvestment . toLocaleString ( ) } < / span >
< / div >
2026-05-19 17:25:32 +06:00
) : (
< >
2026-05-19 17:33:09 +06:00
< div className = "grid grid-cols-1 gap-3 mb-4" >
2026-05-19 17:25:32 +06:00
< label className = { ` flex items-center gap-3 p-3 rounded-lg border-2 cursor-pointer transition-all ${ newInvestment . paymentType === 'full' ? 'border-investor bg-investor/5' : 'border-slate-200 hover:border-slate-300' } ` } >
< input
type = "radio"
name = "paymentType"
value = "full"
checked = { newInvestment . paymentType === 'full' }
onChange = { ( ) = > setNewInvestment ( { . . . newInvestment , paymentType : 'full' , paidAmount : newInvestment.totalInvestment } ) }
className = "w-4 h-4 text-investor"
/ >
< div className = "flex-1" >
< p className = "font-medium text-slate-800 text-sm" > Full Payment < / p >
< p className = "text-xs text-slate-500" > Pay total amount at once < / p >
< / div >
< span className = "text-lg font-bold text-green-600" > ৳ { newInvestment . totalInvestment . toLocaleString ( ) } < / span >
< / label >
< / div >
< / >
2026-05-15 12:25:24 +06:00
) }
< / div >
2026-04-22 01:09:51 +06:00
< div className = "grid grid-cols-2 gap-4" >
< div >
2026-05-14 19:49:57 +06:00
< label className = "text-sm font-medium text-slate-600 mb-1 block" > Payment Method < / label >
2026-05-14 13:54:02 +06:00
< select
2026-04-22 01:09:51 +06:00
value = { newInvestment . paymentMethod }
onChange = { ( e ) = > setNewInvestment ( { . . . newInvestment , paymentMethod : e.target.value as any } ) }
className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
>
< option value = "bank" > Bank Transfer < / option >
< option value = "mobile" > Mobile Banking < / option >
< option value = "cash" > Cash < / option >
< option value = "cheque" > Cheque < / option >
< / select >
< / div >
< div >
< label className = "text-sm font-medium text-slate-600 mb-1 block" > Transaction Reference < / label >
2026-05-14 13:54:02 +06:00
< input
2026-04-22 01:09:51 +06:00
type = "text"
value = { newInvestment . transactionReference }
onChange = { ( e ) = > 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"
/ >
< / div >
< / div >
< div >
2026-05-14 19:49:57 +06:00
< label className = "text-sm font-medium text-slate-600 mb-1 block" > Description < / label >
2026-05-14 13:54:02 +06:00
< textarea
2026-04-22 01:09:51 +06:00
value = { newInvestment . notes }
onChange = { ( e ) = > setNewInvestment ( { . . . newInvestment , notes : e.target.value } ) }
className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
rows = { 2 }
/ >
< / div >
2026-05-14 19:49:57 +06:00
< div className = "bg-green-50 border border-green-200 rounded-xl p-4" >
< h4 className = "font-semibold text-green-800 mb-3 flex items-center gap-2" >
< TrendingUp className = "w-4 h-4" / >
Auto - Journal Entry
< / h4 >
< div className = "bg-white rounded-lg p-4 border border-green-100" >
< div className = "flex items-center justify-between text-sm mb-2" >
< span className = "text-slate-500" > Date : < / span >
< span className = "font-medium" > { newInvestment . startDate || 'Not set' } < / span >
< / div >
< div className = "flex items-center justify-between text-sm mb-3" >
< span className = "text-slate-500" > Reference : < / span >
< span className = "font-medium" > { newInvestment . transactionReference || ` INV/ ${ new Date ( ) . getFullYear ( ) } /auto ` } < / span >
< / div >
< div className = "space-y-2" >
< div className = "flex items-center justify-between p-3 bg-green-50 rounded-lg border border-green-200" >
< div >
< p className = "text-xs text-green-600 font-medium uppercase" > Debit ( Dr ) < / p >
< p className = "font-medium text-slate-800" >
{ newInvestment . paymentMethod === 'bank' ? 'Bank - City Bank' : newInvestment . paymentMethod === 'cash' ? 'Cash in Hand' : 'bKash Business' } ( { newInvestment . paymentMethod === 'bank' ? '1200' : newInvestment . paymentMethod === 'cash' ? '1100' : '1300' } )
< / p >
2026-04-26 14:56:12 +06:00
< / div >
2026-05-15 03:10:37 +06:00
< p className = "font-bold text-green-700" > ৳ { newInvestment . paidAmount . toLocaleString ( ) } < / p >
2026-05-14 19:49:57 +06:00
< / div >
< div className = "flex justify-center" >
< div className = "w-8 h-8 rounded-full bg-green-200 flex items-center justify-center" >
< span className = "text-green-600" > ▼ < / span >
2026-04-26 14:56:12 +06:00
< / div >
2026-05-14 19:49:57 +06:00
< / div >
< div className = "flex items-center justify-between p-3 bg-blue-50 rounded-lg border border-blue-200" >
< div >
< p className = "text-xs text-blue-600 font-medium uppercase" > Credit ( Cr ) < / p >
< p className = "font-medium text-slate-800" > Investor Liabilities ( 2200 ) < / p >
2026-04-26 14:56:12 +06:00
< / div >
2026-05-15 03:10:37 +06:00
< p className = "font-bold text-blue-700" > ৳ { newInvestment . paidAmount . toLocaleString ( ) } < / p >
2026-04-26 14:56:12 +06:00
< / div >
< / div >
2026-05-14 19:49:57 +06:00
< / div >
< / div >
2026-05-15 03:10:37 +06:00
2026-04-22 01:09:51 +06:00
< / div >
< div className = "p-5 border-t border-slate-100 flex justify-end gap-3" >
2026-05-14 19:49:57 +06:00
< button onClick = { ( ) = > setShowCreateInvestmentModal ( false ) } className = "px-4 py-2 border border-slate-200 text-slate-600 rounded-lg text-sm hover:bg-slate-50" >
Cancel
< / button >
2026-05-14 13:54:02 +06:00
< button
onClick = { handleCreateInvestment }
2026-04-22 01:09:51 +06:00
disabled = { ! newInvestment . planName || ! newInvestment . totalInvestment }
2026-05-14 19:49:57 +06:00
className = "px-4 py-2 bg-investor text-white rounded-lg text-sm font-medium hover:bg-investor-dark disabled:opacity-50 flex items-center gap-2"
2026-04-22 01:09:51 +06:00
>
2026-05-14 19:49:57 +06:00
< Plus className = "w-4 h-4" / > Create Investment
2026-04-22 01:09:51 +06:00
< / button >
< / div >
< / div >
< / div >
) }
2026-04-22 01:18:03 +06:00
{ 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" >
2026-05-14 20:15:39 +06:00
< h2 className = "text-lg font-bold text-slate-800" > { editingBankAccount . id ? 'Edit' : 'Add' } Bank Account < / h2 >
2026-04-22 01:18:03 +06:00
< 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" >
2026-05-14 20:15:39 +06:00
{ bankSaveSuccess ? (
< div className = "text-center py-8" >
< div className = "w-16 h-16 bg-green-100 rounded-full flex items-center justify-center mx-auto mb-4" >
< Check className = "w-8 h-8 text-green-600" / >
< / div >
< h3 className = "text-lg font-bold text-slate-800 mb-2" > Bank Account Saved ! < / h3 >
< p className = "text-sm text-slate-500" >
{ editingBankAccount . isPrimary ? 'Primary account has been updated.' : 'Bank account has been saved successfully.' }
< / p >
< / div >
) : (
< >
< div >
< label className = "text-sm font-medium text-slate-600 mb-1 block" > Bank Name < span className = "text-red-500" > * < / span > < / label >
< input
type = "text"
value = { editingBankAccount . bankName }
onChange = { ( e ) = > {
setEditingBankAccount ( { . . . editingBankAccount , bankName : e.target.value } ) ;
setBankErrors ( { . . . bankErrors , bankName : '' } ) ;
} }
className = { ` w-full px-3 py-2 border rounded-lg text-sm ${ bankErrors . bankName ? 'border-red-300 bg-red-50' : 'border-slate-200' } ` }
placeholder = "e.g., Standard Chartered Bank"
/ >
{ bankErrors . bankName && < p className = "text-xs text-red-500 mt-1" > { bankErrors . bankName } < / p > }
< / div >
< div >
< label className = "text-sm font-medium text-slate-600 mb-1 block" > Branch < / label >
< input type = "text" value = { editingBankAccount . branch } onChange = { ( e ) = > setEditingBankAccount ( { . . . editingBankAccount , branch : 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 < span className = "text-red-500" > * < / span > < / label >
< input
type = "text"
value = { editingBankAccount . accountName }
onChange = { ( e ) = > {
setEditingBankAccount ( { . . . editingBankAccount , accountName : e.target.value } ) ;
setBankErrors ( { . . . bankErrors , accountName : '' } ) ;
} }
className = { ` w-full px-3 py-2 border rounded-lg text-sm ${ bankErrors . accountName ? 'border-red-300 bg-red-50' : 'border-slate-200' } ` }
placeholder = "Account holder name"
/ >
{ bankErrors . accountName && < p className = "text-xs text-red-500 mt-1" > { bankErrors . accountName } < / p > }
< / div >
< div >
< label className = "text-sm font-medium text-slate-600 mb-1 block" > Account Number < span className = "text-red-500" > * < / span > < / label >
< input
type = "text"
value = { editingBankAccount . accountNumber }
onChange = { ( e ) = > {
setEditingBankAccount ( { . . . editingBankAccount , accountNumber : e.target.value } ) ;
setBankErrors ( { . . . bankErrors , accountNumber : '' } ) ;
} }
className = { ` w-full px-3 py-2 border rounded-lg text-sm ${ bankErrors . accountNumber ? 'border-red-300 bg-red-50' : 'border-slate-200' } ` }
placeholder = "Account number"
/ >
{ bankErrors . accountNumber && < p className = "text-xs text-red-500 mt-1" > { bankErrors . accountNumber } < / p > }
< / div >
< div >
< label className = "text-sm font-medium text-slate-600 mb-1 block" > Routing Number < / label >
< input type = "text" value = { editingBankAccount . routing } onChange = { ( e ) = > setEditingBankAccount ( { . . . editingBankAccount , routing : e.target.value } ) } className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder = "Routing number" / >
< / div >
< div className = "flex items-center gap-3 p-3 bg-slate-50 rounded-lg" >
< input
type = "checkbox"
id = "isPrimary"
checked = { editingBankAccount . isPrimary }
onChange = { ( e ) = > setEditingBankAccount ( { . . . editingBankAccount , isPrimary : e.target.checked } ) }
className = "w-4 h-4 text-investor rounded border-slate-300"
/ >
< label htmlFor = "isPrimary" className = "text-sm text-slate-700" >
< span className = "font-medium" > Set as Primary Account < / span >
< p className = "text-xs text-slate-500" > Primary account will be used for withdrawals by default < / p >
< / label >
< / div >
< / >
) }
2026-04-22 01:18:03 +06:00
< / div >
< div className = "p-5 border-t border-slate-100 flex justify-between" >
2026-05-14 20:15:39 +06:00
{ bankSaveSuccess ? (
< div className = "flex gap-2 ml-auto" >
< button onClick = { ( ) = > { setShowBankModal ( false ) ; setBankSaveSuccess ( false ) ; } } className = "px-4 py-2 bg-investor text-white rounded-lg text-sm hover:bg-investor-dark" > Done < / button >
< / div >
) : (
< >
{ editingBankAccount . id && (
< button onClick = { ( ) = > setShowDeleteBankModal ( true ) } className = "px-4 py-2 border border-red-200 text-red-600 rounded-lg text-sm hover:bg-red-50 flex items-center gap-1" >
< Trash2 className = "w-4 h-4" / > 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 = { ( ) = > {
const errors : { bankName? : string ; accountName? : string ; accountNumber? : string } = { } ;
if ( ! editingBankAccount . bankName ) errors . bankName = 'Bank name is required' ;
if ( ! editingBankAccount . accountName ) errors . accountName = 'Account name is required' ;
if ( ! editingBankAccount . accountNumber ) errors . accountNumber = 'Account number is required' ;
if ( Object . keys ( errors ) . length > 0 ) {
setBankErrors ( errors ) ;
return ;
}
setBankErrors ( { } ) ;
setBankSaveSuccess ( true ) ;
} } className = "px-4 py-2 bg-investor text-white rounded-lg text-sm hover:bg-investor-dark" > Save < / button >
< / div >
< / >
2026-04-22 01:18:03 +06:00
) }
< / 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 >
< 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 >
) }
2026-05-14 20:15:39 +06:00
{ showDeleteBankModal && (
< div className = "fixed inset-0 bg-black/50 flex items-center justify-center z-[60] p-4" >
< div className = "bg-white rounded-xl shadow-xl w-full max-w-md" >
< div className = "p-5 border-b border-slate-100 flex items-center justify-between" >
< h3 className = "text-lg font-bold text-slate-800 flex items-center gap-2" >
< Trash2 className = "w-5 h-5 text-red-500" / > Delete Bank Account
< / h3 >
< button onClick = { ( ) = > setShowDeleteBankModal ( 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" >
< p className = "text-slate-600 mb-4" > Are you sure you want to delete this bank account ? < / p >
< div className = "bg-slate-50 rounded-lg p-4 space-y-2" >
< div className = "flex justify-between" >
< span className = "text-slate-500" > Bank < / span >
< span className = "font-medium" > { editingBankAccount . bankName } < / span >
< / div >
< div className = "flex justify-between" >
< span className = "text-slate-500" > Account < / span >
< span className = "font-mono" > { editingBankAccount . accountNumber } < / span >
< / div >
< / div >
{ editingBankAccount . isPrimary && (
< div className = "mt-3 p-3 bg-amber-50 border border-amber-200 rounded-lg flex items-center gap-2" >
< AlertTriangle className = "w-5 h-5 text-amber-600" / >
< p className = "text-sm text-amber-700" > This is the primary account . Deleting it may affect withdrawal settings . < / p >
< / div >
) }
< / div >
< div className = "p-5 border-t border-slate-100 flex justify-end gap-3" >
< button onClick = { ( ) = > setShowDeleteBankModal ( 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 = { ( ) = > {
console . log ( 'Deleting bank account:' , editingBankAccount . id ) ;
setShowDeleteBankModal ( false ) ;
setShowBankModal ( false ) ;
} }
className = "px-4 py-2 bg-red-500 text-white rounded-lg text-sm font-medium hover:bg-red-600"
>
Delete Account
< / button >
< / div >
< / div >
< / div >
) }
2026-04-22 01:18:03 +06:00
{ 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 10 MB < / 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 >
) }
2026-04-26 14:56:12 +06:00
{ showJournalModal && (
< div className = "fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4" >
< div className = "bg-white rounded-xl shadow-xl w-full max-w-2xl max-h-[90vh] overflow-hidden flex flex-col" >
< div className = "p-5 border-b border-slate-100 flex items-center justify-between" >
< h2 className = "text-lg font-bold text-slate-800" > Journal Records < / h2 >
< button onClick = { ( ) = > setShowJournalModal ( 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 overflow-y-auto flex-1" >
{ investorJournals . length > 0 ? (
< div className = "space-y-3" >
{ investorJournals . map ( ( journal , idx ) = > (
< div key = { idx } className = "bg-slate-50 rounded-lg p-4 border border-slate-200" >
< div className = "flex items-center justify-between mb-3" >
< div className = "flex items-center gap-2" >
< span className = "text-xs font-mono text-slate-500" > { journal . reference } < / span >
< span className = "text-xs px-2 py-0.5 rounded-full bg-green-100 text-green-700" > Auto < / span >
< / div >
< span className = "text-xs text-slate-500" > { journal . date } < / span >
< / div >
< div className = "space-y-2" >
< div className = "flex items-center justify-between bg-white p-2 rounded border border-slate-200" >
< div >
< p className = "text-sm font-medium" > Debit ( Dr ) < / p >
< p className = "text-xs text-slate-500" > Bank - City Bank ( 1200 ) < / p >
< / div >
< p className = "font-bold text-green-600" > ৳ { journal . amount . toLocaleString ( ) } < / p >
< / div >
< div className = "flex items-center justify-center py-0.5" >
< ArrowRight className = "w-4 h-4 text-slate-400 rotate-90" / >
< / div >
< div className = "flex items-center justify-between bg-white p-2 rounded border border-slate-200" >
< div >
< p className = "text-sm font-medium" > Credit ( Cr ) < / p >
< p className = "text-xs text-slate-500" > Investor Liabilities ( 2200 ) < / p >
< / div >
< p className = "font-bold text-red-600" > ৳ { journal . amount . toLocaleString ( ) } < / p >
< / div >
< / div >
< div className = "mt-2 pt-2 border-t border-slate-200 text-xs text-slate-500" >
{ journal . description }
< / div >
< / div >
) ) }
< / div >
) : (
< div className = "text-center py-8 text-slate-400" >
< BookOpen className = "w-12 h-12 mx-auto mb-2 opacity-50" / >
< p > No journal records yet < / p >
< p className = "text-sm" > Create an investment to see auto - journal entries < / p >
< / div >
) }
< / div >
< / div >
< / div >
) }
{ showInvoiceModal && selectedInvoice && (
< 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-lg" >
< div className = "p-5 border-b border-slate-100 flex items-center justify-between" >
< h2 className = "text-lg font-bold text-slate-800" > Invoice < / h2 >
< button onClick = { ( ) = > setShowInvoiceModal ( false ) } className = "p-2 hover:bg-slate-100 rounded-lg" >
< X className = "w-5 h-5 text-slate-400" / >
< / button >
< / div >
2026-05-14 13:54:02 +06:00
2026-04-26 14:56:12 +06:00
< div className = "p-6" id = "invoice-content" >
< div className = "text-center border-b border-slate-200 pb-4 mb-4" >
< h1 className = "text-xl font-extrabold text-investor" > JAIBEN Mobility Ltd < / h1 >
< p className = "text-xs text-slate-500" > EV Rental & Investment Company < / p >
< / div >
2026-05-14 13:54:02 +06:00
2026-04-26 14:56:12 +06:00
< div className = "flex justify-between mb-4" >
< div >
< p className = "text-xs text-slate-500" > Invoice No < / p >
< p className = "text-sm font-medium" > { selectedInvoice . reference } < / p >
< / div >
< div className = "text-right" >
< p className = "text-xs text-slate-500" > Date < / p >
< p className = "text-sm font-medium" > { selectedInvoice . date } < / p >
< / div >
< / div >
2026-05-14 13:54:02 +06:00
2026-04-26 14:56:12 +06:00
< div className = "mb-4" >
< p className = "text-xs text-slate-500" > Investor < / p >
< p className = "text-sm font-medium" > { selectedInvoice . investorName } < / p >
< p className = "text-xs text-slate-400" > { selectedInvoice . investorId } < / p >
< / div >
2026-05-14 13:54:02 +06:00
2026-04-26 14:56:12 +06:00
< table className = "w-full mb-4" >
< thead >
< tr className = "border-b border-slate-200" >
< th className = "text-left py-2 text-xs text-slate-500" > Description < / th >
< th className = "text-right py-2 text-xs text-slate-500" > Amount < / th >
< / tr >
< / thead >
< tbody >
< tr className = "border-b border-slate-100" >
< td className = "py-3 text-sm" > { selectedInvoice . description } < / td >
< td className = "py-3 text-sm text-right font-medium" > ৳ { selectedInvoice . amount . toLocaleString ( ) } < / td >
< / tr >
< / tbody >
< tfoot >
< tr >
< td className = "py-2 text-sm font-medium" > Total < / td >
< td className = "py-2 text-sm font-bold text-right" > ৳ { selectedInvoice . amount . toLocaleString ( ) } < / td >
< / tr >
< / tfoot >
< / table >
2026-05-14 13:54:02 +06:00
2026-04-26 14:56:12 +06:00
< div className = "flex justify-between mb-2" >
< span className = "text-xs text-slate-500" > Status < / span >
2026-05-14 13:54:02 +06:00
< span className = { ` text-xs px-2 py-1 rounded-full ${ selectedInvoice . status === 'completed' ? 'bg-green-100 text-green-700' :
2026-04-26 14:56:12 +06:00
selectedInvoice . status === 'pending' ? 'bg-amber-100 text-amber-700' : 'bg-slate-100 text-slate-700'
2026-05-14 13:54:02 +06:00
} ` }>
2026-04-26 14:56:12 +06:00
{ selectedInvoice . status }
< / span >
< / div >
2026-05-14 13:54:02 +06:00
2026-04-26 14:56:12 +06:00
< div className = "mt-4 pt-4 border-t border-slate-200" >
< p className = "text-xs text-slate-500 mb-2" > Double Entry Accounting ( Journal ) < / p >
< div className = "bg-slate-50 rounded-lg p-3 border border-slate-200" >
< div className = "flex items-center justify-between mb-2" >
< div >
< p className = "text-xs font-medium" > Debit ( Dr ) < / p >
< p className = "text-xs text-slate-500" > { selectedInvoice . debitAccount || 'N/A' } < / p >
< / div >
< p className = "text-sm font-bold" > ৳ { selectedInvoice . amount . toLocaleString ( ) } < / p >
< / div >
< div className = "flex items-center justify-center py-1" >
< ArrowRight className = "w-4 h-4 text-slate-400 rotate-90" / >
< / div >
< div className = "flex items-center justify-between" >
< div >
< p className = "text-xs font-medium" > Credit ( Cr ) < / p >
< p className = "text-xs text-slate-500" > { selectedInvoice . creditAccount || 'N/A' } < / p >
< / div >
< p className = "text-sm font-bold" > ৳ { selectedInvoice . amount . toLocaleString ( ) } < / p >
< / div >
< / div >
< / div >
2026-05-14 13:54:02 +06:00
2026-04-26 14:56:12 +06:00
< div className = "mt-6 pt-4 border-t border-slate-200 text-center" >
< p className = "text-xs text-slate-400" > Thank you for your investment ! < / p >
< p className = "text-xs text-slate-400" > Generated on { new Date ( ) . toLocaleDateString ( ) } < / p >
< / div >
< / div >
2026-05-14 13:54:02 +06:00
2026-04-26 14:56:12 +06:00
< div className = "p-5 border-t border-slate-100 flex justify-between" >
2026-05-14 13:54:02 +06:00
< button
onClick = { ( ) = > window . print ( ) }
2026-04-26 14:56:12 +06:00
className = "px-4 py-2 bg-investor text-white rounded-lg text-sm hover:bg-investor-dark flex items-center gap-2"
>
< Printer className = "w-4 h-4" / > Print
< / button >
2026-05-14 13:54:02 +06:00
< button
2026-04-26 14:56:12 +06:00
onClick = { ( ) = > {
import ( 'jspdf' ) . then ( jsPDF = > {
const doc = new jsPDF . default ( ) ;
doc . setFontSize ( 18 ) ;
doc . setTextColor ( 6 , 95 , 70 ) ;
doc . text ( 'JAIBEN Mobility Ltd' , 20 , 20 ) ;
doc . setFontSize ( 12 ) ;
doc . setTextColor ( 0 ) ;
doc . text ( 'Invoice: ' + selectedInvoice . reference , 20 , 30 ) ;
doc . text ( 'Date: ' + selectedInvoice . date , 20 , 38 ) ;
doc . text ( 'Investor: ' + selectedInvoice . investorName , 20 , 46 ) ;
doc . text ( 'Description: ' + selectedInvoice . description , 20 , 54 ) ;
doc . text ( 'Amount: ৳' + selectedInvoice . amount . toLocaleString ( ) , 20 , 64 ) ;
doc . text ( 'Status: ' + selectedInvoice . status , 20 , 72 ) ;
if ( selectedInvoice . debitAccount ) {
doc . setFontSize ( 10 ) ;
doc . setTextColor ( 100 ) ;
doc . text ( 'Double Entry Accounting:' , 20 , 85 ) ;
doc . text ( 'Dr: ' + selectedInvoice . debitAccount + ' ৳' + selectedInvoice . amount . toLocaleString ( ) , 20 , 93 ) ;
doc . text ( 'Cr: ' + selectedInvoice . creditAccount + ' ৳' + selectedInvoice . amount . toLocaleString ( ) , 20 , 101 ) ;
}
doc . setFontSize ( 9 ) ;
doc . setTextColor ( 150 ) ;
doc . text ( 'Thank you for your investment!' , 20 , 115 ) ;
doc . save ( ` invoice- ${ selectedInvoice . reference } .pdf ` ) ;
} ) ;
2026-05-14 13:54:02 +06:00
} }
2026-04-26 14:56:12 +06:00
className = "px-4 py-2 border border-slate-200 text-slate-600 rounded-lg text-sm hover:bg-slate-50 flex items-center gap-2"
>
< Download className = "w-4 h-4" / > PDF
< / button >
< button onClick = { ( ) = > setShowInvoiceModal ( false ) } className = "px-4 py-2 border border-slate-200 text-slate-600 rounded-lg text-sm hover:bg-slate-50" > Close < / button >
< / div >
< / div >
< / div >
) }
2026-05-14 19:49:57 +06:00
{ showInvestmentSuccessModal && lastCreatedInvestment && (
< 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-lg" >
< div className = "p-5 border-b border-slate-100 flex items-center justify-between bg-green-50" >
< div className = "flex items-center gap-3" >
< div className = "w-12 h-12 bg-green-100 rounded-full flex items-center justify-center" >
< Check className = "w-6 h-6 text-green-600" / >
< / div >
< div >
< h2 className = "text-lg font-bold text-green-800" > Investment Created ! < / h2 >
< p className = "text-sm text-green-600" > Auto - journal entry generated < / p >
< / div >
< / div >
< button onClick = { ( ) = > setShowInvestmentSuccessModal ( false ) } className = "p-2 hover:bg-green-100 rounded-lg" >
< X className = "w-5 h-5 text-green-400" / >
< / button >
< / div >
< div className = "p-5 space-y-4" >
< div className = "bg-slate-50 rounded-lg p-4 space-y-2" >
< div className = "flex justify-between" >
< span className = "text-sm text-slate-500" > Investment ID < / span >
< span className = "text-sm font-medium" > # { lastCreatedInvestment . id . slice ( - 8 ) } < / span >
< / div >
< div className = "flex justify-between" >
< span className = "text-sm text-slate-500" > Transaction Ref < / span >
< span className = "text-sm font-medium" > { lastCreatedInvestment . transactionId } < / span >
< / div >
< div className = "flex justify-between" >
< span className = "text-sm text-slate-500" > Amount < / span >
< span className = "text-sm font-bold text-green-600" > ৳ { lastCreatedInvestment . totalInvestment . toLocaleString ( ) } < / span >
< / div >
< div className = "flex justify-between" >
< span className = "text-sm text-slate-500" > Plan < / span >
< span className = "text-sm font-medium" > { lastCreatedInvestment . planName } < / span >
< / div >
< div className = "flex justify-between" >
< span className = "text-sm text-slate-500" > Date < / span >
< span className = "text-sm font-medium" > { lastCreatedInvestment . startDate } < / span >
< / div >
< / div >
< div className = "bg-green-50 border border-green-200 rounded-xl p-4" >
< h4 className = "text-sm font-semibold text-green-800 mb-3 flex items-center gap-2" >
< FileText className = "w-4 h-4" / >
Auto - Journal Entry
< / h4 >
< div className = "bg-white rounded-lg p-3 border border-green-100" >
< div className = "space-y-2" >
< div className = "flex items-center justify-between p-2 bg-green-50 rounded border border-green-200" >
< div >
< p className = "text-xs text-green-600 font-medium uppercase" > Debit ( Dr ) < / p >
< p className = "text-sm font-medium text-slate-800" > { lastCreatedInvestment . debitAccount ? . name } < / p >
< p className = "text-xs text-slate-400" > { lastCreatedInvestment . debitAccount ? . code } < / p >
< / div >
< p className = "font-bold text-green-700" > ৳ { lastCreatedInvestment . totalInvestment . toLocaleString ( ) } < / p >
< / div >
< div className = "flex justify-center" >
< div className = "w-6 h-6 rounded-full bg-green-200 flex items-center justify-center" >
< span className = "text-green-600 text-xs" > ▼ < / span >
< / div >
< / div >
< div className = "flex items-center justify-between p-2 bg-blue-50 rounded border border-blue-200" >
< div >
< p className = "text-xs text-blue-600 font-medium uppercase" > Credit ( Cr ) < / p >
< p className = "text-sm font-medium text-slate-800" > Investor Liabilities < / p >
< p className = "text-xs text-slate-400" > 2200 < / p >
< / div >
< p className = "font-bold text-blue-700" > ৳ { lastCreatedInvestment . totalInvestment . toLocaleString ( ) } < / p >
< / div >
< / div >
< / div >
< / div >
< / div >
< div className = "p-5 border-t border-slate-100 flex justify-end gap-3" >
< button onClick = { ( ) = > setShowInvestmentSuccessModal ( false ) } className = "px-4 py-2 bg-green-600 text-white rounded-lg text-sm font-medium hover:bg-green-700 flex items-center gap-2" >
< Check className = "w-4 h-4" / > Done
< / button >
< / div >
< / div >
< / div >
) }
2026-05-14 23:15:10 +06:00
{ showAddPaymentModal && (
< 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-lg" >
< div className = "p-5 border-b border-slate-100 flex items-center justify-between" >
< h3 className = "text-lg font-bold text-slate-800 flex items-center gap-2" >
< CreditCard className = "w-5 h-5 text-investor" / > Record Payment
< / h3 >
< button onClick = { ( ) = > setShowAddPaymentModal ( false ) } className = "p-1 hover:bg-slate-100 rounded-lg" >
< X className = "w-5 h-5 text-slate-500" / >
< / button >
< / div >
< div className = "p-5 space-y-4" >
< div >
< label className = "text-sm font-medium text-slate-700 mb-1 block" > Amount ( ৳ ) < span className = "text-red-500" > * < / span > < / label >
< input
type = "number"
value = { newPayment . amount || '' }
onChange = { ( e ) = > setNewPayment ( { . . . newPayment , amount : Number ( e . target . value ) } ) }
className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-investor"
placeholder = "Enter payment amount"
/ >
< / div >
< div className = "grid grid-cols-2 gap-4" >
< div >
< label className = "text-sm font-medium text-slate-700 mb-1 block" > Date < / label >
< input
type = "date"
value = { newPayment . date }
onChange = { ( e ) = > setNewPayment ( { . . . newPayment , date : e.target.value } ) }
className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-investor"
/ >
< / div >
< div >
< label className = "text-sm font-medium text-slate-700 mb-1 block" > Payment Method < / label >
< select
value = { newPayment . paymentMethod }
onChange = { ( e ) = > setNewPayment ( { . . . newPayment , paymentMethod : e.target.value as any } ) }
className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-investor"
>
< option value = "bank" > Bank Transfer < / option >
< option value = "mobile" > Mobile Banking < / option >
< option value = "cash" > Cash < / option >
< option value = "cheque" > Cheque < / option >
< / select >
< / div >
< / div >
< div >
< label className = "text-sm font-medium text-slate-700 mb-1 block" > Transaction Reference < / label >
< input
type = "text"
value = { newPayment . transactionRef }
onChange = { ( e ) = > setNewPayment ( { . . . newPayment , transactionRef : e.target.value } ) }
className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-investor"
placeholder = "e.g., TXN-12345"
/ >
< / div >
< div >
< label className = "text-sm font-medium text-slate-700 mb-1 block" > Notes < / label >
< textarea
value = { newPayment . notes }
onChange = { ( e ) = > setNewPayment ( { . . . newPayment , notes : e.target.value } ) }
className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-investor"
rows = { 2 }
/ >
< / div >
< / div >
< div className = "p-5 border-t border-slate-100 flex justify-end gap-3" >
< button onClick = { ( ) = > setShowAddPaymentModal ( 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 = { ( ) = > {
if ( newPayment . amount <= 0 ) {
alert ( 'Please enter a valid amount' ) ;
return ;
}
const payment = {
id : ` pay ${ Date . now ( ) } ` ,
date : newPayment.date ,
amount : newPayment.amount ,
paymentMethod : newPayment.paymentMethod ,
transactionRef : newPayment.transactionRef || ` AUTO- ${ Date . now ( ) } ` ,
status : 'completed' ,
notes : newPayment.notes
} ;
setInvestmentPayments ( [ . . . investmentPayments , payment ] ) ;
setNewPayment ( { amount : 0 , paymentMethod : 'bank' , transactionRef : '' , date : new Date ( ) . toISOString ( ) . split ( 'T' ) [ 0 ] , notes : '' } ) ;
setShowAddPaymentModal ( false ) ;
alert ( 'Payment recorded successfully!' ) ;
} }
disabled = { newPayment . amount <= 0 }
className = "px-4 py-2 bg-investor text-white rounded-lg text-sm font-medium hover:bg-investor-dark disabled:opacity-50"
>
Record Payment
< / button >
< / div >
< / div >
< / div >
) }
2026-04-22 01:02:45 +06:00
< / div >
) ;
}