feat: add InvestorNotification component and integrate it across investor dashboard pages
This commit is contained in:
@@ -2,182 +2,186 @@ import { investors, bikes, transactions } from '@/data/mockData';
|
||||
import { Wallet, TrendingUp, Bike, Target, DollarSign, FileText, Phone, BarChart3, Clock, ArrowRight, ShieldCheck, Zap, AlertCircle } from 'lucide-react';
|
||||
import Link from 'next/link';
|
||||
import TransactionList from '@/components/TransactionList';
|
||||
import InvestorNotification from '@/components/InvestorNotification';
|
||||
|
||||
export default function InvestorDashboardPage() {
|
||||
const investor = investors[0]; // mock logged-in investor
|
||||
const investor = investors[0];
|
||||
const investorBikes = bikes.filter(b => b.investorId === investor?.id);
|
||||
const recentTransactions = transactions.filter(t => t.investorId === investor.id).slice(0, 5);
|
||||
|
||||
const availableBalance = investor.totalEarnings - investor.totalWithdrawn - investor.withdrawalPending;
|
||||
|
||||
return (
|
||||
<div className="p-4 lg:p-6 max-w-8xl mx-auto mb-20 lg:mb-0">
|
||||
<div className="flex flex-col lg:flex-row lg:items-center justify-between gap-4 mb-6">
|
||||
<div>
|
||||
<h1 className="text-xl lg:text-2xl font-extrabold text-slate-800">Welcome back, {investor.name.split(' ')[0]} 👋</h1>
|
||||
<p className="text-sm text-slate-500">Here's what's happening with your investments today.</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
{investor.kycStatus === 'verified' ? (
|
||||
<span className="inline-flex items-center gap-1 px-3 py-1.5 rounded-lg bg-green-50 border border-green-100 text-green-700 text-xs font-bold">
|
||||
<ShieldCheck className="w-4 h-4" /> KYC Verified
|
||||
</span>
|
||||
) : (
|
||||
<Link href="/investor/profile" className="inline-flex items-center gap-1 px-3 py-1.5 rounded-lg bg-amber-50 border border-amber-200 text-amber-700 text-xs font-bold hover:bg-amber-100 transition-colors">
|
||||
<AlertCircle className="w-4 h-4" /> Complete KYC
|
||||
</Link>
|
||||
)}
|
||||
<Link href="/investor/plans" className="px-4 py-2 bg-investor text-white rounded-lg text-sm font-bold hover:bg-investor-dark transition-colors flex items-center gap-2 shadow-sm shadow-investor/20">
|
||||
<Zap className="w-4 h-4" /> New Investment
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 mb-6">
|
||||
<div className="bg-white rounded-2xl p-5 border border-slate-200 shadow-sm relative overflow-hidden group hover:border-investor/30 transition-all">
|
||||
<div className="absolute -right-4 -top-4 w-16 h-16 bg-purple-50 rounded-full group-hover:scale-150 transition-transform duration-500"></div>
|
||||
<div className="flex items-center gap-3 mb-2 relative z-10">
|
||||
<div className="w-10 h-10 bg-purple-100 rounded-xl flex items-center justify-center">
|
||||
<Wallet className="w-5 h-5 text-purple-600" />
|
||||
</div>
|
||||
<p className="text-sm text-slate-500 font-medium">Total Invested</p>
|
||||
<div className="min-h-screen ">
|
||||
<InvestorNotification isMobile />
|
||||
<div className="pt-18 lg:pt-0 p-4 lg:p-6 max-w-8xl mx-auto mb-20 lg:mb-0">
|
||||
<div className="flex flex-col lg:flex-row lg:items-center justify-between gap-4 mb-6">
|
||||
<div>
|
||||
<h1 className="text-xl lg:text-2xl font-extrabold text-slate-800">Welcome back, {investor.name.split(' ')[0]} 👋</h1>
|
||||
<p className="text-sm text-slate-500">Here's what's happening with your investments today.</p>
|
||||
</div>
|
||||
<p className="text-2xl font-extrabold text-slate-800 relative z-10">৳{(investor.totalInvested / 1000).toFixed(0)}k</p>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-2xl p-5 border border-slate-200 shadow-sm relative overflow-hidden group hover:border-green-500/30 transition-all">
|
||||
<div className="absolute -right-4 -top-4 w-16 h-16 bg-green-50 rounded-full group-hover:scale-150 transition-transform duration-500"></div>
|
||||
<div className="flex items-center gap-3 mb-2 relative z-10">
|
||||
<div className="w-10 h-10 bg-green-100 rounded-xl flex items-center justify-center">
|
||||
<TrendingUp className="w-5 h-5 text-green-600" />
|
||||
</div>
|
||||
<p className="text-sm text-slate-500 font-medium">Total Earnings</p>
|
||||
</div>
|
||||
<p className="text-2xl font-extrabold text-green-600 relative z-10">৳{(investor.totalEarnings / 1000).toFixed(1)}k</p>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-2xl p-5 border border-slate-200 shadow-sm relative overflow-hidden group hover:border-amber-500/30 transition-all">
|
||||
<div className="absolute -right-4 -top-4 w-16 h-16 bg-amber-50 rounded-full group-hover:scale-150 transition-transform duration-500"></div>
|
||||
<div className="flex items-center gap-3 mb-2 relative z-10">
|
||||
<div className="w-10 h-10 bg-amber-100 rounded-xl flex items-center justify-center">
|
||||
<Bike className="w-5 h-5 text-amber-600" />
|
||||
</div>
|
||||
<p className="text-sm text-slate-500 font-medium">Active Bikes</p>
|
||||
</div>
|
||||
<p className="text-2xl font-extrabold text-amber-600 relative z-10">{investor.activeBikes}</p>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-2xl p-5 border border-slate-200 shadow-sm relative overflow-hidden group hover:border-blue-500/30 transition-all">
|
||||
<div className="absolute -right-4 -top-4 w-16 h-16 bg-blue-50 rounded-full group-hover:scale-150 transition-transform duration-500"></div>
|
||||
<div className="flex items-center gap-3 mb-2 relative z-10">
|
||||
<div className="w-10 h-10 bg-blue-100 rounded-xl flex items-center justify-center">
|
||||
<Target className="w-5 h-5 text-blue-600" />
|
||||
</div>
|
||||
<p className="text-sm text-slate-500 font-medium">Avg. ROI</p>
|
||||
</div>
|
||||
<p className="text-2xl font-extrabold text-blue-600 relative z-10">{investor.roi}%</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid lg:grid-cols-3 gap-6 mb-6">
|
||||
<div className="lg:col-span-2 bg-white rounded-2xl border border-slate-200 shadow-sm flex flex-col">
|
||||
<div className="p-5 border-b border-slate-100 flex items-center justify-between">
|
||||
<h2 className="font-bold text-slate-800 flex items-center gap-2">
|
||||
<Bike className="w-5 h-5 text-slate-400" /> My Portfolio Overview
|
||||
</h2>
|
||||
<Link href="/investor/plans" className="text-sm font-semibold text-investor hover:text-investor-dark flex items-center gap-1">
|
||||
View All <ArrowRight className="w-4 h-4" />
|
||||
</Link>
|
||||
</div>
|
||||
<div className="p-5 flex-1">
|
||||
{investorBikes.length > 0 ? (
|
||||
<div className="space-y-4">
|
||||
{investorBikes.slice(0, 3).map(bike => (
|
||||
<Link
|
||||
key={bike.id}
|
||||
href={`/investor/investments/${investor.investments?.[0].id || 'ip1'}`}
|
||||
className="flex items-center gap-4 p-3 hover:bg-slate-50 rounded-xl transition-colors border border-transparent hover:border-slate-100 group"
|
||||
>
|
||||
<div className="w-12 h-12 bg-slate-100 rounded-lg overflow-hidden shrink-0">
|
||||
<img src={bike.image} alt={bike.model} className="w-full h-full object-cover" />
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<h4 className="font-bold text-slate-800 truncate group-hover:text-investor transition-colors">{bike.model}</h4>
|
||||
<p className="text-xs text-slate-500">{bike.plateNumber}</p>
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<p className="font-bold text-green-600">৳{bike.currentRent || 0}</p>
|
||||
<p className="text-[10px] text-slate-400 uppercase">Daily Rent</p>
|
||||
</div>
|
||||
<div>
|
||||
<span className={`inline-flex px-2 py-1 rounded text-[10px] font-bold uppercase ${bike.status === 'rented' ? 'bg-green-100 text-green-700' :
|
||||
bike.status === 'available' ? 'bg-blue-100 text-blue-700' : 'bg-amber-100 text-amber-700'
|
||||
}`}>
|
||||
{bike.status}
|
||||
</span>
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
{investor.kycStatus === 'verified' ? (
|
||||
<span className="inline-flex items-center gap-1 px-3 py-1.5 rounded-lg bg-green-50 border border-green-100 text-green-700 text-xs font-bold">
|
||||
<ShieldCheck className="w-4 h-4" /> KYC Verified
|
||||
</span>
|
||||
) : (
|
||||
<div className="h-full flex flex-col items-center justify-center text-center py-8">
|
||||
<div className="w-12 h-12 bg-slate-50 rounded-full flex items-center justify-center mb-3">
|
||||
<Bike className="w-6 h-6 text-slate-300" />
|
||||
</div>
|
||||
<p className="text-sm font-semibold text-slate-700">No bikes assigned yet</p>
|
||||
<p className="text-xs text-slate-500 mt-1 max-w-[200px]">Once you make an investment, assigned bikes will appear here.</p>
|
||||
</div>
|
||||
<Link href="/investor/profile" className="inline-flex items-center gap-1 px-3 py-1.5 rounded-lg bg-amber-50 border border-amber-200 text-amber-700 text-xs font-bold hover:bg-amber-100 transition-colors">
|
||||
<AlertCircle className="w-4 h-4" /> Complete KYC
|
||||
</Link>
|
||||
)}
|
||||
<Link href="/investor/plans" className="px-4 py-2 bg-investor text-white rounded-lg text-sm font-bold hover:bg-investor-dark transition-colors flex items-center gap-2 shadow-sm shadow-investor/20">
|
||||
<Zap className="w-4 h-4" /> New Investment
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-2xl border border-slate-200 shadow-sm flex flex-col">
|
||||
<div className="p-5 border-b border-slate-100">
|
||||
<h2 className="font-bold text-slate-800">Quick Actions</h2>
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 mb-6">
|
||||
<div className="bg-white rounded-2xl p-5 border border-slate-200 shadow-sm relative overflow-hidden group hover:border-investor/30 transition-all">
|
||||
<div className="absolute -right-4 -top-4 w-16 h-16 bg-purple-50 rounded-full group-hover:scale-150 transition-transform duration-500"></div>
|
||||
<div className="flex items-center gap-3 mb-2 relative z-10">
|
||||
<div className="w-10 h-10 bg-purple-100 rounded-xl flex items-center justify-center">
|
||||
<Wallet className="w-5 h-5 text-purple-600" />
|
||||
</div>
|
||||
<p className="text-sm text-slate-500 font-medium">Total Invested</p>
|
||||
</div>
|
||||
<p className="text-2xl font-extrabold text-slate-800 relative z-10">৳{(investor.totalInvested / 1000).toFixed(0)}k</p>
|
||||
</div>
|
||||
<div className="p-5 flex-1 flex flex-col gap-3">
|
||||
<div className="bg-investor/5 border border-investor/10 rounded-xl p-4 mb-2">
|
||||
<p className="text-xs text-investor font-semibold mb-1 uppercase">Available to Withdraw</p>
|
||||
<p className="text-2xl font-extrabold text-investor mb-3">৳{availableBalance.toLocaleString()}</p>
|
||||
<Link href="/investor/withdraw" className="w-full py-2.5 bg-investor text-white rounded-lg font-bold text-sm hover:bg-investor-dark flex items-center justify-center gap-2 shadow-sm shadow-investor/20 transition-all">
|
||||
<DollarSign className="w-4 h-4" /> Withdraw Funds
|
||||
|
||||
<div className="bg-white rounded-2xl p-5 border border-slate-200 shadow-sm relative overflow-hidden group hover:border-green-500/30 transition-all">
|
||||
<div className="absolute -right-4 -top-4 w-16 h-16 bg-green-50 rounded-full group-hover:scale-150 transition-transform duration-500"></div>
|
||||
<div className="flex items-center gap-3 mb-2 relative z-10">
|
||||
<div className="w-10 h-10 bg-green-100 rounded-xl flex items-center justify-center">
|
||||
<TrendingUp className="w-5 h-5 text-green-600" />
|
||||
</div>
|
||||
<p className="text-sm text-slate-500 font-medium">Total Earnings</p>
|
||||
</div>
|
||||
<p className="text-2xl font-extrabold text-green-600 relative z-10">৳{(investor.totalEarnings / 1000).toFixed(1)}k</p>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-2xl p-5 border border-slate-200 shadow-sm relative overflow-hidden group hover:border-amber-500/30 transition-all">
|
||||
<div className="absolute -right-4 -top-4 w-16 h-16 bg-amber-50 rounded-full group-hover:scale-150 transition-transform duration-500"></div>
|
||||
<div className="flex items-center gap-3 mb-2 relative z-10">
|
||||
<div className="w-10 h-10 bg-amber-100 rounded-xl flex items-center justify-center">
|
||||
<Bike className="w-5 h-5 text-amber-600" />
|
||||
</div>
|
||||
<p className="text-sm text-slate-500 font-medium">Active Bikes</p>
|
||||
</div>
|
||||
<p className="text-2xl font-extrabold text-amber-600 relative z-10">{investor.activeBikes}</p>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-2xl p-5 border border-slate-200 shadow-sm relative overflow-hidden group hover:border-blue-500/30 transition-all">
|
||||
<div className="absolute -right-4 -top-4 w-16 h-16 bg-blue-50 rounded-full group-hover:scale-150 transition-transform duration-500"></div>
|
||||
<div className="flex items-center gap-3 mb-2 relative z-10">
|
||||
<div className="w-10 h-10 bg-blue-100 rounded-xl flex items-center justify-center">
|
||||
<Target className="w-5 h-5 text-blue-600" />
|
||||
</div>
|
||||
<p className="text-sm text-slate-500 font-medium">Avg. ROI</p>
|
||||
</div>
|
||||
<p className="text-2xl font-extrabold text-blue-600 relative z-10">{investor.roi}%</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid lg:grid-cols-3 gap-6 mb-6">
|
||||
<div className="lg:col-span-2 bg-white rounded-2xl border border-slate-200 shadow-sm flex flex-col">
|
||||
<div className="p-5 border-b border-slate-100 flex items-center justify-between">
|
||||
<h2 className="font-bold text-slate-800 flex items-center gap-2">
|
||||
<Bike className="w-5 h-5 text-slate-400" /> My Portfolio Overview
|
||||
</h2>
|
||||
<Link href="/investor/plans" className="text-sm font-semibold text-investor hover:text-investor-dark flex items-center gap-1">
|
||||
View All <ArrowRight className="w-4 h-4" />
|
||||
</Link>
|
||||
</div>
|
||||
<div className="p-5 flex-1">
|
||||
{investorBikes.length > 0 ? (
|
||||
<div className="space-y-4">
|
||||
{investorBikes.slice(0, 3).map(bike => (
|
||||
<Link
|
||||
key={bike.id}
|
||||
href={`/investor/investments/${investor.investments?.[0].id || 'ip1'}`}
|
||||
className="flex items-center gap-4 p-3 hover:bg-slate-50 rounded-xl transition-colors border border-transparent hover:border-slate-100 group"
|
||||
>
|
||||
<div className="w-12 h-12 bg-slate-100 rounded-lg overflow-hidden shrink-0">
|
||||
<img src={bike.image} alt={bike.model} className="w-full h-full object-cover" />
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<h4 className="font-bold text-slate-800 truncate group-hover:text-investor transition-colors">{bike.model}</h4>
|
||||
<p className="text-xs text-slate-500">{bike.plateNumber}</p>
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<p className="font-bold text-green-600">৳{bike.currentRent || 0}</p>
|
||||
<p className="text-[10px] text-slate-400 uppercase">Daily Rent</p>
|
||||
</div>
|
||||
<div>
|
||||
<span className={`inline-flex px-2 py-1 rounded text-[10px] font-bold uppercase ${bike.status === 'rented' ? 'bg-green-100 text-green-700' :
|
||||
bike.status === 'available' ? 'bg-blue-100 text-blue-700' : 'bg-amber-100 text-amber-700'
|
||||
}`}>
|
||||
{bike.status}
|
||||
</span>
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="h-full flex flex-col items-center justify-center text-center py-8">
|
||||
<div className="w-12 h-12 bg-slate-50 rounded-full flex items-center justify-center mb-3">
|
||||
<Bike className="w-6 h-6 text-slate-300" />
|
||||
</div>
|
||||
<p className="text-sm font-semibold text-slate-700">No bikes assigned yet</p>
|
||||
<p className="text-xs text-slate-500 mt-1 max-w-[200px]">Once you make an investment, assigned bikes will appear here.</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Link href="/investor/profile" className="flex items-center gap-3 p-3 border border-slate-100 rounded-xl hover:bg-slate-50 transition-colors group">
|
||||
<div className="w-10 h-10 bg-slate-100 rounded-lg flex items-center justify-center group-hover:bg-white group-hover:shadow-sm transition-all">
|
||||
<FileText className="w-5 h-5 text-slate-500 group-hover:text-investor" />
|
||||
<div className="bg-white rounded-2xl border border-slate-200 shadow-sm flex flex-col">
|
||||
<div className="p-5 border-b border-slate-100">
|
||||
<h2 className="font-bold text-slate-800">Quick Actions</h2>
|
||||
</div>
|
||||
<div className="p-5 flex-1 flex flex-col gap-3">
|
||||
<div className="bg-investor/5 border border-investor/10 rounded-xl p-4 mb-2">
|
||||
<p className="text-xs text-investor font-semibold mb-1 uppercase">Available to Withdraw</p>
|
||||
<p className="text-2xl font-extrabold text-investor mb-3">৳{availableBalance.toLocaleString()}</p>
|
||||
<Link href="/investor/withdraw" className="w-full py-2.5 bg-investor text-white rounded-lg font-bold text-sm hover:bg-investor-dark flex items-center justify-center gap-2 shadow-sm shadow-investor/20 transition-all">
|
||||
<DollarSign className="w-4 h-4" /> Withdraw Funds
|
||||
</Link>
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<p className="font-semibold text-sm text-slate-800">Update KYC</p>
|
||||
<p className="text-xs text-slate-500">Manage documents</p>
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
<Link href="/investor/plans" className="flex items-center gap-3 p-3 border border-slate-100 rounded-xl hover:bg-slate-50 transition-colors group">
|
||||
<div className="w-10 h-10 bg-slate-100 rounded-lg flex items-center justify-center group-hover:bg-white group-hover:shadow-sm transition-all">
|
||||
<BarChart3 className="w-5 h-5 text-slate-500 group-hover:text-investor" />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<p className="font-semibold text-sm text-slate-800">Earnings & P&L</p>
|
||||
<p className="text-xs text-slate-500">View detailed reports</p>
|
||||
</div>
|
||||
</Link>
|
||||
<Link href="/investor/profile" className="flex items-center gap-3 p-3 border border-slate-100 rounded-xl hover:bg-slate-50 transition-colors group">
|
||||
<div className="w-10 h-10 bg-slate-100 rounded-lg flex items-center justify-center group-hover:bg-white group-hover:shadow-sm transition-all">
|
||||
<FileText className="w-5 h-5 text-slate-500 group-hover:text-investor" />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<p className="font-semibold text-sm text-slate-800">Update KYC</p>
|
||||
<p className="text-xs text-slate-500">Manage documents</p>
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
<Link href="/investor/plans" className="flex items-center gap-3 p-3 border border-slate-100 rounded-xl hover:bg-slate-50 transition-colors group">
|
||||
<div className="w-10 h-10 bg-slate-100 rounded-lg flex items-center justify-center group-hover:bg-white group-hover:shadow-sm transition-all">
|
||||
<BarChart3 className="w-5 h-5 text-slate-500 group-hover:text-investor" />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<p className="font-semibold text-sm text-slate-800">Earnings & P&L</p>
|
||||
<p className="text-xs text-slate-500">View detailed reports</p>
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-2xl border border-slate-200 shadow-sm">
|
||||
<div className="p-5 border-b border-slate-100 flex items-center justify-between">
|
||||
<h2 className="font-bold text-slate-800 flex items-center gap-2">
|
||||
<Clock className="w-5 h-5 text-slate-400" /> Recent Transactions
|
||||
</h2>
|
||||
<button className="text-sm font-semibold text-investor hover:text-investor-dark flex items-center gap-1">
|
||||
View All <ArrowRight className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
<div className="p-0">
|
||||
<TransactionList transactions={recentTransactions} />
|
||||
<div className="bg-white rounded-2xl border border-slate-200 shadow-sm">
|
||||
<div className="p-5 border-b border-slate-100 flex items-center justify-between">
|
||||
<h2 className="font-bold text-slate-800 flex items-center gap-2">
|
||||
<Clock className="w-5 h-5 text-slate-400" /> Recent Transactions
|
||||
</h2>
|
||||
<button className="text-sm font-semibold text-investor hover:text-investor-dark flex items-center gap-1">
|
||||
View All <ArrowRight className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
<div className="p-0">
|
||||
<TransactionList transactions={recentTransactions} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
} from 'lucide-react';
|
||||
import { investors, bikes, transactions } from '@/data/mockData';
|
||||
import toast from 'react-hot-toast';
|
||||
import InvestorNotification from '@/components/InvestorNotification';
|
||||
|
||||
export default function InvestorInvestmentDetailPage({ params }: { params: Promise<{ id: string }> }) {
|
||||
const resolvedParams = use(params);
|
||||
@@ -49,296 +50,299 @@ export default function InvestorInvestmentDetailPage({ params }: { params: Promi
|
||||
const investmentTransactions = transactions.filter((t: any) => t.investorId === investor.id && t.type === 'investment_return');
|
||||
|
||||
return (
|
||||
<div className="p-4 lg:p-6 max-w-6xl mx-auto mb-20 lg:mb-0">
|
||||
<div className="flex flex-col lg:flex-row lg:items-center justify-between gap-4 mb-6">
|
||||
<div className="flex items-center gap-4">
|
||||
<button onClick={() => router.back()} className="p-2 hover:bg-slate-100 rounded-lg transition-colors border border-slate-200">
|
||||
<ArrowLeft className="w-5 h-5 text-slate-600" />
|
||||
</button>
|
||||
<div>
|
||||
<div className="flex items-center gap-3">
|
||||
<h1 className="text-xl lg:text-2xl font-extrabold text-slate-800 flex items-center gap-2">
|
||||
<Target className="w-5 h-5 lg:w-6 lg:h-6 text-investor" />{investment.planName}</h1>
|
||||
<span className={`px-2.5 py-1 rounded-full text-xs font-bold ${style.badge} capitalize`}>
|
||||
{investment.planType} Plan
|
||||
</span>
|
||||
<span className={`px-2.5 py-1 rounded-full text-xs font-bold ${investment.status === 'active' ? 'bg-green-100 text-green-700' : 'bg-slate-100 text-slate-600'} capitalize`}>
|
||||
{investment.status}
|
||||
</span>
|
||||
<div className="min-h-screen ">
|
||||
<InvestorNotification isMobile />
|
||||
<div className="pt-18 lg:pt-0 p-4 lg:p-6 max-w-6xl mx-auto mb-20 lg:mb-0">
|
||||
<div className="flex flex-col lg:flex-row lg:items-center justify-between gap-4 mb-6">
|
||||
<div className="flex items-center gap-4">
|
||||
<button onClick={() => router.back()} className="p-2 hover:bg-slate-100 rounded-lg transition-colors border border-slate-200">
|
||||
<ArrowLeft className="w-5 h-5 text-slate-600" />
|
||||
</button>
|
||||
<div>
|
||||
<div className="flex items-center gap-3">
|
||||
<h1 className="text-xl lg:text-2xl font-extrabold text-slate-800 flex items-center gap-2">
|
||||
<Target className="w-5 h-5 lg:w-6 lg:h-6 text-investor" />{investment.planName}</h1>
|
||||
<span className={`px-2.5 py-1 rounded-full text-xs font-bold ${style.badge} capitalize`}>
|
||||
{investment.planType} Plan
|
||||
</span>
|
||||
<span className={`px-2.5 py-1 rounded-full text-xs font-bold ${investment.status === 'active' ? 'bg-green-100 text-green-700' : 'bg-slate-100 text-slate-600'} capitalize`}>
|
||||
{investment.status}
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-slate-500 text-sm mt-1 flex items-center gap-2">
|
||||
ID: <span className="font-mono text-xs bg-slate-100 px-1.5 py-0.5 rounded">#{investment.id?.toUpperCase()}</span>
|
||||
<span className="text-slate-300">•</span> Started: {investment.startDate}
|
||||
</p>
|
||||
</div>
|
||||
<p className="text-slate-500 text-sm mt-1 flex items-center gap-2">
|
||||
ID: <span className="font-mono text-xs bg-slate-100 px-1.5 py-0.5 rounded">#{investment.id?.toUpperCase()}</span>
|
||||
<span className="text-slate-300">•</span> Started: {investment.startDate}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<button onClick={() => toast.success('Statement download started')} className="px-4 py-2 bg-white border border-slate-200 text-slate-700 rounded-xl text-sm font-bold hover:bg-slate-50 flex items-center gap-2 shadow-sm transition-all">
|
||||
<Download className="w-4 h-4 text-slate-400" /> Download Statement
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<button onClick={() => toast.success('Statement download started')} className="px-4 py-2 bg-white border border-slate-200 text-slate-700 rounded-xl text-sm font-bold hover:bg-slate-50 flex items-center gap-2 shadow-sm transition-all">
|
||||
<Download className="w-4 h-4 text-slate-400" /> Download Statement
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4 mb-8">
|
||||
<div className="bg-white rounded-2xl p-5 border border-slate-200 shadow-sm">
|
||||
<div className="flex items-center gap-3 mb-3">
|
||||
<div className="w-10 h-10 bg-purple-100 rounded-xl flex items-center justify-center">
|
||||
<Wallet className="w-5 h-5 text-purple-600" />
|
||||
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4 mb-8">
|
||||
<div className="bg-white rounded-2xl p-5 border border-slate-200 shadow-sm">
|
||||
<div className="flex items-center gap-3 mb-3">
|
||||
<div className="w-10 h-10 bg-purple-100 rounded-xl flex items-center justify-center">
|
||||
<Wallet className="w-5 h-5 text-purple-600" />
|
||||
</div>
|
||||
<p className="text-sm text-slate-500 font-medium">Invested</p>
|
||||
</div>
|
||||
<p className="text-sm text-slate-500 font-medium">Invested</p>
|
||||
<p className="text-2xl font-extrabold text-slate-800">৳{investment.totalInvestment.toLocaleString()}</p>
|
||||
</div>
|
||||
<p className="text-2xl font-extrabold text-slate-800">৳{investment.totalInvestment.toLocaleString()}</p>
|
||||
</div>
|
||||
<div className="bg-white rounded-2xl p-5 border border-slate-200 shadow-sm">
|
||||
<div className="flex items-center gap-3 mb-3">
|
||||
<div className="w-10 h-10 bg-green-100 rounded-xl flex items-center justify-center">
|
||||
<TrendingUp className="w-5 h-5 text-green-600" />
|
||||
<div className="bg-white rounded-2xl p-5 border border-slate-200 shadow-sm">
|
||||
<div className="flex items-center gap-3 mb-3">
|
||||
<div className="w-10 h-10 bg-green-100 rounded-xl flex items-center justify-center">
|
||||
<TrendingUp className="w-5 h-5 text-green-600" />
|
||||
</div>
|
||||
<p className="text-sm text-slate-500 font-medium">Total Return</p>
|
||||
</div>
|
||||
<p className="text-sm text-slate-500 font-medium">Total Return</p>
|
||||
<p className="text-2xl font-extrabold text-green-600">৳{investment.actualEarnings.toLocaleString()}</p>
|
||||
</div>
|
||||
<p className="text-2xl font-extrabold text-green-600">৳{investment.actualEarnings.toLocaleString()}</p>
|
||||
</div>
|
||||
<div className="bg-white rounded-2xl p-5 border border-slate-200 shadow-sm">
|
||||
<div className="flex items-center gap-3 mb-3">
|
||||
<div className="w-10 h-10 bg-amber-100 rounded-xl flex items-center justify-center">
|
||||
<Clock className="w-5 h-5 text-amber-600" />
|
||||
<div className="bg-white rounded-2xl p-5 border border-slate-200 shadow-sm">
|
||||
<div className="flex items-center gap-3 mb-3">
|
||||
<div className="w-10 h-10 bg-amber-100 rounded-xl flex items-center justify-center">
|
||||
<Clock className="w-5 h-5 text-amber-600" />
|
||||
</div>
|
||||
<p className="text-sm text-slate-500 font-medium">Pending</p>
|
||||
</div>
|
||||
<p className="text-sm text-slate-500 font-medium">Pending</p>
|
||||
<p className="text-2xl font-extrabold text-amber-600">৳{(investment.totalInvestment * 0.24 - investment.actualEarnings).toLocaleString()}</p>
|
||||
</div>
|
||||
<p className="text-2xl font-extrabold text-amber-600">৳{(investment.totalInvestment * 0.24 - investment.actualEarnings).toLocaleString()}</p>
|
||||
</div>
|
||||
<div className="bg-white rounded-2xl p-5 border border-slate-200 shadow-sm">
|
||||
<div className="flex items-center gap-3 mb-3">
|
||||
<div className="w-10 h-10 bg-blue-100 rounded-xl flex items-center justify-center">
|
||||
<Percent className="w-5 h-5 text-blue-600" />
|
||||
<div className="bg-white rounded-2xl p-5 border border-slate-200 shadow-sm">
|
||||
<div className="flex items-center gap-3 mb-3">
|
||||
<div className="w-10 h-10 bg-blue-100 rounded-xl flex items-center justify-center">
|
||||
<Percent className="w-5 h-5 text-blue-600" />
|
||||
</div>
|
||||
<p className="text-sm text-slate-500 font-medium">ROI</p>
|
||||
</div>
|
||||
<p className="text-sm text-slate-500 font-medium">ROI</p>
|
||||
<p className="text-2xl font-extrabold text-blue-600">{investment.expectedRoi}%</p>
|
||||
</div>
|
||||
<p className="text-2xl font-extrabold text-blue-600">{investment.expectedRoi}%</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid lg:grid-cols-3 gap-6">
|
||||
<div className="lg:col-span-2 space-y-6">
|
||||
<div className="bg-white rounded-2xl border border-slate-200 shadow-sm overflow-hidden">
|
||||
<div className="flex overflow-x-auto bg-slate-50 border-b border-slate-100">
|
||||
{['overview', 'bikes', 'pnl', 'transactions'].map((tab) => (
|
||||
<button
|
||||
key={tab}
|
||||
onClick={() => setActiveTab(tab)}
|
||||
className={`px-6 py-4 text-sm font-bold capitalize transition-colors whitespace-nowrap ${activeTab === tab ? 'text-investor bg-white border-b-2 border-investor' : 'text-slate-500 hover:text-slate-700'}`}
|
||||
>
|
||||
{tab}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<div className="grid lg:grid-cols-3 gap-6">
|
||||
<div className="lg:col-span-2 space-y-6">
|
||||
<div className="bg-white rounded-2xl border border-slate-200 shadow-sm overflow-hidden">
|
||||
<div className="flex overflow-x-auto bg-slate-50 border-b border-slate-100">
|
||||
{['overview', 'bikes', 'pnl', 'transactions'].map((tab) => (
|
||||
<button
|
||||
key={tab}
|
||||
onClick={() => setActiveTab(tab)}
|
||||
className={`px-6 py-4 text-sm font-bold capitalize transition-colors whitespace-nowrap ${activeTab === tab ? 'text-investor bg-white border-b-2 border-investor' : 'text-slate-500 hover:text-slate-700'}`}
|
||||
>
|
||||
{tab}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="p-6">
|
||||
{activeTab === 'overview' && (
|
||||
<div className="space-y-6">
|
||||
<div className="grid md:grid-cols-2 gap-4">
|
||||
<div className="bg-slate-50 rounded-2xl p-5 border border-slate-200">
|
||||
<h4 className="font-bold text-slate-800 mb-4 flex items-center gap-2">
|
||||
<FileText className="w-5 h-5 text-slate-400" /> Investment Details
|
||||
<div className="p-6">
|
||||
{activeTab === 'overview' && (
|
||||
<div className="space-y-6">
|
||||
<div className="grid md:grid-cols-2 gap-4">
|
||||
<div className="bg-slate-50 rounded-2xl p-5 border border-slate-200">
|
||||
<h4 className="font-bold text-slate-800 mb-4 flex items-center gap-2">
|
||||
<FileText className="w-5 h-5 text-slate-400" /> Investment Details
|
||||
</h4>
|
||||
<div className="space-y-3 text-sm">
|
||||
<div className="flex justify-between border-b border-slate-200 pb-2">
|
||||
<span className="text-slate-500">Plan Name</span>
|
||||
<span className="font-bold text-slate-800">{investment.planName}</span>
|
||||
</div>
|
||||
<div className="flex justify-between border-b border-slate-200 pb-2">
|
||||
<span className="text-slate-500">Plan Type</span>
|
||||
<span className="font-bold text-slate-800 capitalize">{investment.planType}</span>
|
||||
</div>
|
||||
<div className="flex justify-between border-b border-slate-200 pb-2">
|
||||
<span className="text-slate-500">Period</span>
|
||||
<span className="font-bold text-slate-800">{investment.startDate} - {investment.endDate || 'Ongoing'}</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-slate-500">Payment Method</span>
|
||||
<span className="font-bold text-slate-800 capitalize">{investment.paymentMethod}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-slate-50 rounded-2xl p-5 border border-slate-200">
|
||||
<h4 className="font-bold text-slate-800 mb-4 flex items-center gap-2">
|
||||
<Shield className="w-5 h-5 text-slate-400" /> Plan Policy
|
||||
</h4>
|
||||
<div className="space-y-3 text-sm">
|
||||
<div className="flex justify-between border-b border-slate-200 pb-2">
|
||||
<span className="text-slate-500">Min Duration</span>
|
||||
<span className="font-bold text-slate-800">12 Months</span>
|
||||
</div>
|
||||
<div className="flex justify-between border-b border-slate-200 pb-2">
|
||||
<span className="text-slate-500">Lock-in Period</span>
|
||||
<span className="font-bold text-slate-800">3 Months</span>
|
||||
</div>
|
||||
<div className="flex justify-between border-b border-slate-200 pb-2">
|
||||
<span className="text-slate-500">Exit Penalty</span>
|
||||
<span className="font-bold text-red-500">10%</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-slate-500">Maintenance</span>
|
||||
<span className="font-bold text-green-600">Included</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-amber-50 border border-amber-200 rounded-2xl p-5">
|
||||
<h4 className="font-bold text-amber-800 mb-4 flex items-center gap-2">
|
||||
<Percent className="w-5 h-5 text-amber-600" /> Profit Sharing Configuration
|
||||
</h4>
|
||||
<div className="space-y-3 text-sm">
|
||||
<div className="flex justify-between border-b border-slate-200 pb-2">
|
||||
<span className="text-slate-500">Plan Name</span>
|
||||
<span className="font-bold text-slate-800">{investment.planName}</span>
|
||||
<p className="text-xs text-amber-600 mb-4 uppercase font-bold tracking-wider">Your share based on rental model</p>
|
||||
<div className="grid grid-cols-3 gap-3">
|
||||
<div className="bg-white rounded-xl p-4 text-center border border-amber-200 shadow-sm">
|
||||
<p className="text-xs text-slate-400 mb-1 font-bold uppercase">Single Rent</p>
|
||||
<p className="text-2xl font-extrabold text-slate-800">55%</p>
|
||||
</div>
|
||||
<div className="flex justify-between border-b border-slate-200 pb-2">
|
||||
<span className="text-slate-500">Plan Type</span>
|
||||
<span className="font-bold text-slate-800 capitalize">{investment.planType}</span>
|
||||
<div className="bg-white rounded-xl p-4 text-center border border-amber-200 shadow-sm">
|
||||
<p className="text-xs text-slate-400 mb-1 font-bold uppercase">Rent to Own</p>
|
||||
<p className="text-2xl font-extrabold text-slate-800">45%</p>
|
||||
</div>
|
||||
<div className="flex justify-between border-b border-slate-200 pb-2">
|
||||
<span className="text-slate-500">Period</span>
|
||||
<span className="font-bold text-slate-800">{investment.startDate} - {investment.endDate || 'Ongoing'}</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-slate-500">Payment Method</span>
|
||||
<span className="font-bold text-slate-800 capitalize">{investment.paymentMethod}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-slate-50 rounded-2xl p-5 border border-slate-200">
|
||||
<h4 className="font-bold text-slate-800 mb-4 flex items-center gap-2">
|
||||
<Shield className="w-5 h-5 text-slate-400" /> Plan Policy
|
||||
</h4>
|
||||
<div className="space-y-3 text-sm">
|
||||
<div className="flex justify-between border-b border-slate-200 pb-2">
|
||||
<span className="text-slate-500">Min Duration</span>
|
||||
<span className="font-bold text-slate-800">12 Months</span>
|
||||
</div>
|
||||
<div className="flex justify-between border-b border-slate-200 pb-2">
|
||||
<span className="text-slate-500">Lock-in Period</span>
|
||||
<span className="font-bold text-slate-800">3 Months</span>
|
||||
</div>
|
||||
<div className="flex justify-between border-b border-slate-200 pb-2">
|
||||
<span className="text-slate-500">Exit Penalty</span>
|
||||
<span className="font-bold text-red-500">10%</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-slate-500">Maintenance</span>
|
||||
<span className="font-bold text-green-600">Included</span>
|
||||
<div className="bg-white rounded-xl p-4 text-center border border-amber-200 shadow-sm ring-2 ring-amber-500 ring-offset-2">
|
||||
<p className="text-xs text-slate-400 mb-1 font-bold uppercase">Share EV</p>
|
||||
<p className="text-2xl font-extrabold text-slate-800">40%</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="bg-amber-50 border border-amber-200 rounded-2xl p-5">
|
||||
<h4 className="font-bold text-amber-800 mb-4 flex items-center gap-2">
|
||||
<Percent className="w-5 h-5 text-amber-600" /> Profit Sharing Configuration
|
||||
</h4>
|
||||
<p className="text-xs text-amber-600 mb-4 uppercase font-bold tracking-wider">Your share based on rental model</p>
|
||||
<div className="grid grid-cols-3 gap-3">
|
||||
<div className="bg-white rounded-xl p-4 text-center border border-amber-200 shadow-sm">
|
||||
<p className="text-xs text-slate-400 mb-1 font-bold uppercase">Single Rent</p>
|
||||
<p className="text-2xl font-extrabold text-slate-800">55%</p>
|
||||
{activeTab === 'bikes' && (
|
||||
<div className="space-y-4">
|
||||
{assignedBikes.length > 0 ? assignedBikes.map(bike => (
|
||||
<div key={bike.id} className="p-5 bg-white rounded-2xl border border-slate-200 flex flex-col md:flex-row items-start md:items-center gap-5 hover:border-investor transition-colors group">
|
||||
<div className="w-20 h-20 bg-slate-100 rounded-xl overflow-hidden shrink-0 border border-slate-200">
|
||||
<img src={bike.image} alt={bike.model} className="w-full h-full object-cover" />
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<h5 className="font-bold text-lg text-slate-800 truncate">{bike.model}</h5>
|
||||
<p className="text-sm text-slate-500">{bike.plateNumber} • {bike.brand}</p>
|
||||
<div className="mt-3 flex flex-wrap gap-2">
|
||||
<span className={`px-2 py-0.5 rounded text-[10px] font-bold uppercase ${bike.status === 'rented' ? 'bg-green-100 text-green-700' : 'bg-blue-100 text-blue-700'}`}>
|
||||
{bike.status}
|
||||
</span>
|
||||
<span className="px-2 py-0.5 rounded bg-slate-100 text-slate-600 text-[10px] font-bold uppercase flex items-center gap-1">
|
||||
Daily: ৳{bike.currentRent}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<p className="text-xs text-slate-400 mb-1 uppercase font-bold tracking-wider">Total Earnings</p>
|
||||
<p className="text-xl font-extrabold text-green-600">৳{bike.totalEarnings?.toLocaleString()}</p>
|
||||
<Link href="/investor/portfolio" className="mt-2 text-xs font-bold text-investor hover:underline flex items-center gap-1 justify-end">
|
||||
Live Track <ChevronRight className="w-3 h-3" />
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-white rounded-xl p-4 text-center border border-amber-200 shadow-sm">
|
||||
<p className="text-xs text-slate-400 mb-1 font-bold uppercase">Rent to Own</p>
|
||||
<p className="text-2xl font-extrabold text-slate-800">45%</p>
|
||||
)) : (
|
||||
<div className="text-center py-12 border-2 border-dashed border-slate-200 rounded-2xl">
|
||||
<Bike className="w-12 h-12 mx-auto mb-4 text-slate-300" />
|
||||
<p className="text-slate-500 font-bold">No bikes assigned yet.</p>
|
||||
<p className="text-xs text-slate-400 mt-1">Admin will assign bikes to this investment shortly.</p>
|
||||
</div>
|
||||
<div className="bg-white rounded-xl p-4 text-center border border-amber-200 shadow-sm ring-2 ring-amber-500 ring-offset-2">
|
||||
<p className="text-xs text-slate-400 mb-1 font-bold uppercase">Share EV</p>
|
||||
<p className="text-2xl font-extrabold text-slate-800">40%</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{activeTab === 'pnl' && (
|
||||
<div className="max-w-xl mx-auto">
|
||||
<div className="bg-slate-50 rounded-2xl p-6 border border-slate-200">
|
||||
<h3 className="font-bold text-slate-800 mb-6 text-center text-lg">Detailed P&L Statement</h3>
|
||||
<div className="space-y-4">
|
||||
<div className="flex justify-between items-center pb-4 border-b border-slate-200">
|
||||
<span className="font-bold text-slate-600">Gross Rental Revenue</span>
|
||||
<span className="text-xl font-bold text-slate-800">৳{(investment.actualEarnings / 0.55).toFixed(0)}</span>
|
||||
</div>
|
||||
<div className="space-y-3 py-2">
|
||||
<div className="flex justify-between text-sm">
|
||||
<span className="text-slate-500">Platform Management Fee (45%)</span>
|
||||
<span className="text-red-500 font-bold">-৳{((investment.actualEarnings / 0.55) * 0.45).toFixed(0)}</span>
|
||||
</div>
|
||||
<div className="flex justify-between text-sm">
|
||||
<span className="text-slate-500">Fleet Insurance & Maintenance</span>
|
||||
<span className="text-green-600 font-bold tracking-tight uppercase text-[10px]">Included in Platform Fee</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-between items-center pt-4 border-t-2 border-slate-300">
|
||||
<span className="text-lg font-extrabold text-slate-800">Net Return to Investor</span>
|
||||
<span className="text-2xl font-extrabold text-green-600">৳{investment.actualEarnings.toLocaleString()}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
)}
|
||||
|
||||
{activeTab === 'bikes' && (
|
||||
<div className="space-y-4">
|
||||
{assignedBikes.length > 0 ? assignedBikes.map(bike => (
|
||||
<div key={bike.id} className="p-5 bg-white rounded-2xl border border-slate-200 flex flex-col md:flex-row items-start md:items-center gap-5 hover:border-investor transition-colors group">
|
||||
<div className="w-20 h-20 bg-slate-100 rounded-xl overflow-hidden shrink-0 border border-slate-200">
|
||||
<img src={bike.image} alt={bike.model} className="w-full h-full object-cover" />
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<h5 className="font-bold text-lg text-slate-800 truncate">{bike.model}</h5>
|
||||
<p className="text-sm text-slate-500">{bike.plateNumber} • {bike.brand}</p>
|
||||
<div className="mt-3 flex flex-wrap gap-2">
|
||||
<span className={`px-2 py-0.5 rounded text-[10px] font-bold uppercase ${bike.status === 'rented' ? 'bg-green-100 text-green-700' : 'bg-blue-100 text-blue-700'}`}>
|
||||
{bike.status}
|
||||
</span>
|
||||
<span className="px-2 py-0.5 rounded bg-slate-100 text-slate-600 text-[10px] font-bold uppercase flex items-center gap-1">
|
||||
Daily: ৳{bike.currentRent}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<p className="text-xs text-slate-400 mb-1 uppercase font-bold tracking-wider">Total Earnings</p>
|
||||
<p className="text-xl font-extrabold text-green-600">৳{bike.totalEarnings?.toLocaleString()}</p>
|
||||
<Link href="/investor/portfolio" className="mt-2 text-xs font-bold text-investor hover:underline flex items-center gap-1 justify-end">
|
||||
Live Track <ChevronRight className="w-3 h-3" />
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
)) : (
|
||||
<div className="text-center py-12 border-2 border-dashed border-slate-200 rounded-2xl">
|
||||
<Bike className="w-12 h-12 mx-auto mb-4 text-slate-300" />
|
||||
<p className="text-slate-500 font-bold">No bikes assigned yet.</p>
|
||||
<p className="text-xs text-slate-400 mt-1">Admin will assign bikes to this investment shortly.</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{activeTab === 'pnl' && (
|
||||
<div className="max-w-xl mx-auto">
|
||||
<div className="bg-slate-50 rounded-2xl p-6 border border-slate-200">
|
||||
<h3 className="font-bold text-slate-800 mb-6 text-center text-lg">Detailed P&L Statement</h3>
|
||||
<div className="space-y-4">
|
||||
<div className="flex justify-between items-center pb-4 border-b border-slate-200">
|
||||
<span className="font-bold text-slate-600">Gross Rental Revenue</span>
|
||||
<span className="text-xl font-bold text-slate-800">৳{(investment.actualEarnings / 0.55).toFixed(0)}</span>
|
||||
</div>
|
||||
<div className="space-y-3 py-2">
|
||||
<div className="flex justify-between text-sm">
|
||||
<span className="text-slate-500">Platform Management Fee (45%)</span>
|
||||
<span className="text-red-500 font-bold">-৳{((investment.actualEarnings / 0.55) * 0.45).toFixed(0)}</span>
|
||||
</div>
|
||||
<div className="flex justify-between text-sm">
|
||||
<span className="text-slate-500">Fleet Insurance & Maintenance</span>
|
||||
<span className="text-green-600 font-bold tracking-tight uppercase text-[10px]">Included in Platform Fee</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-between items-center pt-4 border-t-2 border-slate-300">
|
||||
<span className="text-lg font-extrabold text-slate-800">Net Return to Investor</span>
|
||||
<span className="text-2xl font-extrabold text-green-600">৳{investment.actualEarnings.toLocaleString()}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{activeTab === 'transactions' && (
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-left">
|
||||
<thead className="bg-slate-50">
|
||||
<tr>
|
||||
<th className="px-4 py-3 text-xs font-bold text-slate-500 uppercase tracking-wider">Date</th>
|
||||
<th className="px-4 py-3 text-xs font-bold text-slate-500 uppercase tracking-wider">Description</th>
|
||||
<th className="px-4 py-3 text-xs font-bold text-slate-500 uppercase tracking-wider text-right">Amount</th>
|
||||
<th className="px-4 py-3 text-xs font-bold text-slate-500 uppercase tracking-wider">Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-slate-100">
|
||||
{investmentTransactions.map(t => (
|
||||
<tr key={t.id} className="hover:bg-slate-50 transition-colors">
|
||||
<td className="px-4 py-4 text-sm text-slate-600 whitespace-nowrap">{t.createdAt}</td>
|
||||
<td className="px-4 py-4">
|
||||
<p className="text-sm font-bold text-slate-800">{t.description}</p>
|
||||
<p className="text-xs text-slate-400">Ref: {t.id}</p>
|
||||
</td>
|
||||
<td className="px-4 py-4 text-sm font-extrabold text-green-600 text-right">
|
||||
+৳{t.amount.toLocaleString()}
|
||||
</td>
|
||||
<td className="px-4 py-4">
|
||||
<span className="inline-flex px-2 py-0.5 rounded text-[10px] font-bold uppercase bg-green-100 text-green-700">
|
||||
Completed
|
||||
</span>
|
||||
</td>
|
||||
{activeTab === 'transactions' && (
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-left">
|
||||
<thead className="bg-slate-50">
|
||||
<tr>
|
||||
<th className="px-4 py-3 text-xs font-bold text-slate-500 uppercase tracking-wider">Date</th>
|
||||
<th className="px-4 py-3 text-xs font-bold text-slate-500 uppercase tracking-wider">Description</th>
|
||||
<th className="px-4 py-3 text-xs font-bold text-slate-500 uppercase tracking-wider text-right">Amount</th>
|
||||
<th className="px-4 py-3 text-xs font-bold text-slate-500 uppercase tracking-wider">Status</th>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-6">
|
||||
<div className="bg-white rounded-2xl border border-slate-200 p-5 shadow-sm">
|
||||
<h4 className="font-bold text-slate-800 mb-4 flex items-center gap-2">
|
||||
<Zap className="w-5 h-5 text-amber-500" /> Quick Actions
|
||||
</h4>
|
||||
<div className="space-y-3">
|
||||
<button onClick={() => toast.success('Printing report...')} className="w-full px-4 py-3 border border-slate-200 text-slate-700 rounded-xl text-sm font-bold hover:bg-slate-50 flex items-center gap-3 transition-all">
|
||||
<Printer className="w-5 h-5 text-slate-400" /> Print Summary
|
||||
</button>
|
||||
<button onClick={() => toast.success('Exporting to Excel...')} className="w-full px-4 py-3 border border-slate-200 text-slate-700 rounded-xl text-sm font-bold hover:bg-slate-50 flex items-center gap-3 transition-all">
|
||||
<BarChart3 className="w-5 h-5 text-slate-400" /> Export Data
|
||||
</button>
|
||||
<button onClick={() => router.push('/investor/withdraw')} className="w-full px-4 py-3 bg-investor text-white rounded-xl text-sm font-bold hover:bg-investor-dark flex items-center gap-3 shadow-md shadow-investor/20 transition-all">
|
||||
<DollarSign className="w-5 h-5" /> Withdraw Earnings
|
||||
</button>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-slate-100">
|
||||
{investmentTransactions.map(t => (
|
||||
<tr key={t.id} className="hover:bg-slate-50 transition-colors">
|
||||
<td className="px-4 py-4 text-sm text-slate-600 whitespace-nowrap">{t.createdAt}</td>
|
||||
<td className="px-4 py-4">
|
||||
<p className="text-sm font-bold text-slate-800">{t.description}</p>
|
||||
<p className="text-xs text-slate-400">Ref: {t.id}</p>
|
||||
</td>
|
||||
<td className="px-4 py-4 text-sm font-extrabold text-green-600 text-right">
|
||||
+৳{t.amount.toLocaleString()}
|
||||
</td>
|
||||
<td className="px-4 py-4">
|
||||
<span className="inline-flex px-2 py-0.5 rounded text-[10px] font-bold uppercase bg-green-100 text-green-700">
|
||||
Completed
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-blue-50 border border-blue-100 rounded-2xl p-5">
|
||||
<h4 className="font-bold text-blue-800 mb-3 flex items-center gap-2">
|
||||
<Smartphone className="w-5 h-5 text-blue-600" /> Support Desk
|
||||
</h4>
|
||||
<p className="text-xs text-blue-700 mb-4 font-medium leading-relaxed">
|
||||
Need help with this investment or bike assignment? Our team is available 24/7.
|
||||
</p>
|
||||
<button onClick={() => toast.success('Connecting to support...')} className="w-full py-2.5 bg-blue-600 text-white rounded-xl font-bold text-sm hover:bg-blue-700 transition-colors">
|
||||
Contact Support
|
||||
</button>
|
||||
<div className="space-y-6">
|
||||
<div className="bg-white rounded-2xl border border-slate-200 p-5 shadow-sm">
|
||||
<h4 className="font-bold text-slate-800 mb-4 flex items-center gap-2">
|
||||
<Zap className="w-5 h-5 text-amber-500" /> Quick Actions
|
||||
</h4>
|
||||
<div className="space-y-3">
|
||||
<button onClick={() => toast.success('Printing report...')} className="w-full px-4 py-3 border border-slate-200 text-slate-700 rounded-xl text-sm font-bold hover:bg-slate-50 flex items-center gap-3 transition-all">
|
||||
<Printer className="w-5 h-5 text-slate-400" /> Print Summary
|
||||
</button>
|
||||
<button onClick={() => toast.success('Exporting to Excel...')} className="w-full px-4 py-3 border border-slate-200 text-slate-700 rounded-xl text-sm font-bold hover:bg-slate-50 flex items-center gap-3 transition-all">
|
||||
<BarChart3 className="w-5 h-5 text-slate-400" /> Export Data
|
||||
</button>
|
||||
<button onClick={() => router.push('/investor/withdraw')} className="w-full px-4 py-3 bg-investor text-white rounded-xl text-sm font-bold hover:bg-investor-dark flex items-center gap-3 shadow-md shadow-investor/20 transition-all">
|
||||
<DollarSign className="w-5 h-5" /> Withdraw Earnings
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-blue-50 border border-blue-100 rounded-2xl p-5">
|
||||
<h4 className="font-bold text-blue-800 mb-3 flex items-center gap-2">
|
||||
<Smartphone className="w-5 h-5 text-blue-600" /> Support Desk
|
||||
</h4>
|
||||
<p className="text-xs text-blue-700 mb-4 font-medium leading-relaxed">
|
||||
Need help with this investment or bike assignment? Our team is available 24/7.
|
||||
</p>
|
||||
<button onClick={() => toast.success('Connecting to support...')} className="w-full py-2.5 bg-blue-600 text-white rounded-xl font-bold text-sm hover:bg-blue-700 transition-colors">
|
||||
Contact Support
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -5,6 +5,7 @@ import Link from 'next/link';
|
||||
import { Target, Plus, Zap, ChevronRight, ArrowRight, Edit, Trash2, Eye, TrendingUp, X, CreditCard } from 'lucide-react';
|
||||
import { investors } from '@/data/mockData';
|
||||
import toast from 'react-hot-toast';
|
||||
import InvestorNotification from '@/components/InvestorNotification';
|
||||
|
||||
export default function MyInvestmentsPage() {
|
||||
const investor = investors[0];
|
||||
@@ -47,388 +48,391 @@ export default function MyInvestmentsPage() {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="p-4 lg:p-6 max-w-8xl mx-auto mb-20 lg:mb-0">
|
||||
{/* Header */}
|
||||
<div className="mb-6">
|
||||
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
|
||||
<div>
|
||||
<h1 className="text-xl sm:text-2xl font-bold text-slate-800 flex items-center gap-2">
|
||||
<Target className="w-5 h-5 sm:w-6 sm:h-6 text-investor" /> My Investments
|
||||
</h1>
|
||||
<p className="text-sm text-slate-500 mt-1">Manage your active portfolios and track your earnings.</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setShowCreateModal(true)}
|
||||
className="px-4 py-2.5 bg-investor text-white rounded-lg text-sm font-medium hover:bg-investor-dark flex items-center gap-2 shadow-sm"
|
||||
>
|
||||
<Plus className="w-4 h-4" /> Create Investment
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Investment Plans Cards */}
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h3 className="font-semibold text-slate-800">Investment Plans</h3>
|
||||
<p className="text-sm text-slate-500">Manage investment portfolios for this investor</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{investor.investments?.map((inv) => {
|
||||
const style = planConfig[inv.planType] || planConfig.gold;
|
||||
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>
|
||||
</div>
|
||||
<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>
|
||||
<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={`/investor/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">
|
||||
<Eye className="w-4 h-4 inline mr-1" /> View
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
{(!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</p>
|
||||
<button onClick={() => setShowCreateModal(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
|
||||
<div className="min-h-screen ">
|
||||
<InvestorNotification isMobile />
|
||||
<div className="pt-18 lg:pt-0 p-4 lg:p-6 max-w-8xl mx-auto mb-20 lg:mb-0">
|
||||
{/* Header */}
|
||||
<div className="mb-6">
|
||||
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
|
||||
<div>
|
||||
<h1 className="text-xl sm:text-2xl font-bold text-slate-800 flex items-center gap-2">
|
||||
<Target className="w-5 h-5 sm:w-6 sm:h-6 text-investor" /> My Investments
|
||||
</h1>
|
||||
<p className="text-sm text-slate-500 mt-1">Manage your active portfolios and track your earnings.</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setShowCreateModal(true)}
|
||||
className="px-4 py-2.5 bg-investor text-white rounded-lg text-sm font-medium hover:bg-investor-dark flex items-center gap-2 shadow-sm"
|
||||
>
|
||||
<Plus className="w-4 h-4" /> Create Investment
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Investment Plans Cards */}
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h3 className="font-semibold text-slate-800">Investment Plans</h3>
|
||||
<p className="text-sm text-slate-500">Manage investment portfolios for this investor</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{investor.investments?.map((inv) => {
|
||||
const style = planConfig[inv.planType] || planConfig.gold;
|
||||
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>
|
||||
</div>
|
||||
<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>
|
||||
<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={`/investor/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">
|
||||
<Eye className="w-4 h-4 inline mr-1" /> View
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
{(!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</p>
|
||||
<button onClick={() => setShowCreateModal(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>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Create Investment Modal */}
|
||||
{showCreateModal && (
|
||||
<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-3xl max-h-[90vh] overflow-hidden flex flex-col">
|
||||
<div className="p-5 border-b border-slate-100 flex items-center justify-between">
|
||||
<div>
|
||||
<h2 className="text-lg font-bold text-slate-800 flex items-center gap-2">
|
||||
<Plus className="w-5 h-5 text-investor" /> Create New Investment
|
||||
</h2>
|
||||
<p className="text-sm text-slate-500">Set up investment for {investor.name}</p>
|
||||
</div>
|
||||
<button onClick={() => setShowCreateModal(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-5">
|
||||
{!selectedTemplate ? (
|
||||
<>
|
||||
<div>
|
||||
<label className="text-sm font-medium text-slate-600 mb-2 block">Select Plan Template</label>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-3 gap-3">
|
||||
{PLAN_TEMPLATES.map(plan => (
|
||||
<button
|
||||
key={plan.id}
|
||||
onClick={() => {
|
||||
setSelectedTemplate(plan);
|
||||
setNewInvestment({
|
||||
...newInvestment,
|
||||
planName: plan.name,
|
||||
planType: plan.tier.toLowerCase() as any,
|
||||
totalInvestment: plan.evBasePrice * plan.minQuantity,
|
||||
startDate: new Date().toISOString().split('T')[0],
|
||||
});
|
||||
}}
|
||||
className="p-4 rounded-lg border-2 border-slate-200 hover:border-investor/50 text-left transition-all hover:bg-slate-50"
|
||||
>
|
||||
<p className="font-semibold text-slate-800">{plan.name}</p>
|
||||
<p className="text-xs text-slate-500 mt-1">৳{plan.evBasePrice.toLocaleString()} × {plan.minQuantity} bikes</p>
|
||||
<p className="text-sm text-slate-600 mt-1">Duration: {plan.duration} months</p>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<div className="flex items-center gap-3 p-3 bg-slate-50 rounded-lg">
|
||||
<button onClick={() => setSelectedTemplate(null)} className="text-xs text-investor hover:underline">← Change Template</button>
|
||||
<span className="text-slate-300">|</span>
|
||||
<span className="text-sm font-medium text-slate-700">{selectedTemplate.name}</span>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="text-sm font-medium text-slate-600 mb-1 block">Plan Name *</label>
|
||||
<input
|
||||
type="text"
|
||||
value={newInvestment.planName}
|
||||
onChange={(e) => setNewInvestment({ ...newInvestment, planName: e.target.value })}
|
||||
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
||||
placeholder="Plan name"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-sm font-medium text-slate-600 mb-1 block">Plan Type</label>
|
||||
<select
|
||||
value={newInvestment.planType}
|
||||
onChange={(e) => setNewInvestment({ ...newInvestment, planType: e.target.value as any })}
|
||||
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-white"
|
||||
>
|
||||
<option value="silver">Silver</option>
|
||||
<option value="gold">Gold</option>
|
||||
<option value="platinum">Platinum</option>
|
||||
<option value="diamond">Diamond</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">EV Base Price (৳)</label>
|
||||
<input type="number" value={selectedTemplate.evBasePrice} disabled className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-slate-50 cursor-not-allowed" />
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-sm font-medium text-slate-600 mb-1 block">Minimum Quantity</label>
|
||||
<input type="number" value={selectedTemplate.minQuantity} disabled className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-slate-50 cursor-not-allowed" />
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-sm font-medium text-slate-600 mb-1 block">Min Investment (৳)</label>
|
||||
<input type="number" value={selectedTemplate.evBasePrice * selectedTemplate.minQuantity} disabled className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-slate-100 cursor-not-allowed" />
|
||||
<p className="text-xs text-slate-400 mt-1">= Qty × Base Price</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="text-sm font-medium text-slate-600 mb-1 block">Investment Amount (৳) *</label>
|
||||
<input
|
||||
type="number"
|
||||
value={newInvestment.totalInvestment}
|
||||
onChange={(e) => setNewInvestment({ ...newInvestment, totalInvestment: Number(e.target.value) })}
|
||||
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-yellow-50"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-sm font-medium text-slate-600 mb-1 block">Max Investment (৳)</label>
|
||||
<input type="number" value={selectedTemplate.maxInvestment} disabled className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-slate-100 cursor-not-allowed" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3 mb-4">
|
||||
<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', initialPayment: 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>
|
||||
<label className={`flex items-center gap-3 p-3 rounded-lg border-2 cursor-pointer transition-all ${newInvestment.paymentType === 'partial' ? 'border-investor bg-investor/5' : 'border-slate-200 hover:border-slate-300'}`}>
|
||||
<input
|
||||
type="radio"
|
||||
name="paymentType"
|
||||
value="partial"
|
||||
checked={newInvestment.paymentType === 'partial'}
|
||||
onChange={() => setNewInvestment({ ...newInvestment, paymentType: 'partial', initialPayment: Math.floor(newInvestment.totalInvestment * 0.5) })}
|
||||
className="w-4 h-4 text-investor"
|
||||
/>
|
||||
<div className="flex-1">
|
||||
<p className="font-medium text-slate-800 text-sm">Partial Payment</p>
|
||||
<p className="text-xs text-slate-500">Pay initial amount (50% min)</p>
|
||||
</div>
|
||||
<span className="text-sm font-bold text-amber-600">Min ৳{Math.floor(newInvestment.totalInvestment * 0.5).toLocaleString()}</span>
|
||||
</label>
|
||||
</div>
|
||||
{newInvestment.paymentType === 'partial' && (
|
||||
<div className="border-t border-slate-200 pt-4 mt-4">
|
||||
<label className="text-sm font-medium text-slate-600 mb-2 block">Initial Payment (৳) *</label>
|
||||
<input
|
||||
type="number"
|
||||
value={newInvestment.initialPayment}
|
||||
onChange={(e) => {
|
||||
const val = Number(e.target.value);
|
||||
if (val >= newInvestment.totalInvestment * 0.5 && val <= newInvestment.totalInvestment) {
|
||||
setNewInvestment({ ...newInvestment, initialPayment: val });
|
||||
}
|
||||
}}
|
||||
className="w-full px-3 py-2 border border-investor rounded-lg text-sm bg-white"
|
||||
/>
|
||||
<p className="text-xs text-slate-500 mt-2">Balance: ৳{(newInvestment.totalInvestment - newInvestment.initialPayment).toLocaleString()}</p>
|
||||
</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>
|
||||
<input type="number" value={selectedTemplate.duration} disabled className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-slate-100 cursor-not-allowed" />
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-sm font-medium text-slate-600 mb-1 block">Lock-in Period (Months)</label>
|
||||
<input type="number" value={3} disabled className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-slate-100 cursor-not-allowed" />
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-sm font-medium text-slate-600 mb-1 block">Exit Penalty (%)</label>
|
||||
<input type="number" value={10} disabled className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-slate-100 cursor-not-allowed" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-amber-50 border border-amber-200 rounded-xl p-4">
|
||||
<h4 className="text-sm font-semibold text-amber-800 mb-3 flex items-center gap-2">
|
||||
<TrendingUp className="w-4 h-4" /> FICO Share - Profit per Ride
|
||||
</h4>
|
||||
<p className="text-xs text-amber-600 mb-3">Profit sharing when bikes are rented to end customers</p>
|
||||
<div className="grid grid-cols-3 gap-4">
|
||||
<div>
|
||||
<label className="text-xs text-slate-500 mb-1 block">Single Rent (%)</label>
|
||||
<input type="number" value={45} disabled className="w-full px-3 py-2 border border-amber-200 rounded-lg text-sm bg-white cursor-not-allowed" />
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-xs text-slate-500 mb-1 block">Rent to Own (%)</label>
|
||||
<input type="number" value={55} disabled className="w-full px-3 py-2 border border-amber-200 rounded-lg text-sm bg-white cursor-not-allowed" />
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-xs text-slate-500 mb-1 block">Share EV (%)</label>
|
||||
<input type="number" value={60} disabled className="w-full px-3 py-2 border border-amber-200 rounded-lg text-sm bg-white cursor-not-allowed" />
|
||||
</div>
|
||||
</div>
|
||||
</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>
|
||||
<input 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>
|
||||
<input 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>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="text-sm font-medium text-slate-600 mb-1 block">Payment Method</label>
|
||||
<select 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 bg-white">
|
||||
<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>
|
||||
<input 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>
|
||||
<label className="text-sm font-medium text-slate-600 mb-1 block">Description</label>
|
||||
<textarea 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} placeholder="Add notes..." />
|
||||
</div>
|
||||
|
||||
<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>
|
||||
</div>
|
||||
<p className="font-bold text-green-700">৳{newInvestment.paymentType === 'partial' ? newInvestment.initialPayment.toLocaleString() : newInvestment.totalInvestment.toLocaleString()}</p>
|
||||
</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>
|
||||
</div>
|
||||
</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>
|
||||
</div>
|
||||
<p className="font-bold text-blue-700">৳{newInvestment.totalInvestment.toLocaleString()}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="p-5 border-t border-slate-100 flex justify-end gap-3">
|
||||
<button onClick={() => setShowCreateModal(false)} className="px-4 py-2 border border-slate-200 text-slate-600 rounded-lg text-sm hover:bg-slate-50">Cancel</button>
|
||||
{selectedTemplate && (
|
||||
<button onClick={handleCreate} disabled={!newInvestment.planName || !newInvestment.totalInvestment} 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">
|
||||
<Plus className="w-4 h-4" /> Create Investment
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Create Investment Modal */}
|
||||
{showCreateModal && (
|
||||
<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-3xl max-h-[90vh] overflow-hidden flex flex-col">
|
||||
<div className="p-5 border-b border-slate-100 flex items-center justify-between">
|
||||
<div>
|
||||
<h2 className="text-lg font-bold text-slate-800 flex items-center gap-2">
|
||||
<Plus className="w-5 h-5 text-investor" /> Create New Investment
|
||||
</h2>
|
||||
<p className="text-sm text-slate-500">Set up investment for {investor.name}</p>
|
||||
</div>
|
||||
<button onClick={() => setShowCreateModal(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-5">
|
||||
{!selectedTemplate ? (
|
||||
<>
|
||||
<div>
|
||||
<label className="text-sm font-medium text-slate-600 mb-2 block">Select Plan Template</label>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-3 gap-3">
|
||||
{PLAN_TEMPLATES.map(plan => (
|
||||
<button
|
||||
key={plan.id}
|
||||
onClick={() => {
|
||||
setSelectedTemplate(plan);
|
||||
setNewInvestment({
|
||||
...newInvestment,
|
||||
planName: plan.name,
|
||||
planType: plan.tier.toLowerCase() as any,
|
||||
totalInvestment: plan.evBasePrice * plan.minQuantity,
|
||||
startDate: new Date().toISOString().split('T')[0],
|
||||
});
|
||||
}}
|
||||
className="p-4 rounded-lg border-2 border-slate-200 hover:border-investor/50 text-left transition-all hover:bg-slate-50"
|
||||
>
|
||||
<p className="font-semibold text-slate-800">{plan.name}</p>
|
||||
<p className="text-xs text-slate-500 mt-1">৳{plan.evBasePrice.toLocaleString()} × {plan.minQuantity} bikes</p>
|
||||
<p className="text-sm text-slate-600 mt-1">Duration: {plan.duration} months</p>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<div className="flex items-center gap-3 p-3 bg-slate-50 rounded-lg">
|
||||
<button onClick={() => setSelectedTemplate(null)} className="text-xs text-investor hover:underline">← Change Template</button>
|
||||
<span className="text-slate-300">|</span>
|
||||
<span className="text-sm font-medium text-slate-700">{selectedTemplate.name}</span>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="text-sm font-medium text-slate-600 mb-1 block">Plan Name *</label>
|
||||
<input
|
||||
type="text"
|
||||
value={newInvestment.planName}
|
||||
onChange={(e) => setNewInvestment({ ...newInvestment, planName: e.target.value })}
|
||||
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
||||
placeholder="Plan name"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-sm font-medium text-slate-600 mb-1 block">Plan Type</label>
|
||||
<select
|
||||
value={newInvestment.planType}
|
||||
onChange={(e) => setNewInvestment({ ...newInvestment, planType: e.target.value as any })}
|
||||
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-white"
|
||||
>
|
||||
<option value="silver">Silver</option>
|
||||
<option value="gold">Gold</option>
|
||||
<option value="platinum">Platinum</option>
|
||||
<option value="diamond">Diamond</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">EV Base Price (৳)</label>
|
||||
<input type="number" value={selectedTemplate.evBasePrice} disabled className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-slate-50 cursor-not-allowed" />
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-sm font-medium text-slate-600 mb-1 block">Minimum Quantity</label>
|
||||
<input type="number" value={selectedTemplate.minQuantity} disabled className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-slate-50 cursor-not-allowed" />
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-sm font-medium text-slate-600 mb-1 block">Min Investment (৳)</label>
|
||||
<input type="number" value={selectedTemplate.evBasePrice * selectedTemplate.minQuantity} disabled className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-slate-100 cursor-not-allowed" />
|
||||
<p className="text-xs text-slate-400 mt-1">= Qty × Base Price</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="text-sm font-medium text-slate-600 mb-1 block">Investment Amount (৳) *</label>
|
||||
<input
|
||||
type="number"
|
||||
value={newInvestment.totalInvestment}
|
||||
onChange={(e) => setNewInvestment({ ...newInvestment, totalInvestment: Number(e.target.value) })}
|
||||
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-yellow-50"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-sm font-medium text-slate-600 mb-1 block">Max Investment (৳)</label>
|
||||
<input type="number" value={selectedTemplate.maxInvestment} disabled className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-slate-100 cursor-not-allowed" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3 mb-4">
|
||||
<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', initialPayment: 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>
|
||||
<label className={`flex items-center gap-3 p-3 rounded-lg border-2 cursor-pointer transition-all ${newInvestment.paymentType === 'partial' ? 'border-investor bg-investor/5' : 'border-slate-200 hover:border-slate-300'}`}>
|
||||
<input
|
||||
type="radio"
|
||||
name="paymentType"
|
||||
value="partial"
|
||||
checked={newInvestment.paymentType === 'partial'}
|
||||
onChange={() => setNewInvestment({ ...newInvestment, paymentType: 'partial', initialPayment: Math.floor(newInvestment.totalInvestment * 0.5) })}
|
||||
className="w-4 h-4 text-investor"
|
||||
/>
|
||||
<div className="flex-1">
|
||||
<p className="font-medium text-slate-800 text-sm">Partial Payment</p>
|
||||
<p className="text-xs text-slate-500">Pay initial amount (50% min)</p>
|
||||
</div>
|
||||
<span className="text-sm font-bold text-amber-600">Min ৳{Math.floor(newInvestment.totalInvestment * 0.5).toLocaleString()}</span>
|
||||
</label>
|
||||
</div>
|
||||
{newInvestment.paymentType === 'partial' && (
|
||||
<div className="border-t border-slate-200 pt-4 mt-4">
|
||||
<label className="text-sm font-medium text-slate-600 mb-2 block">Initial Payment (৳) *</label>
|
||||
<input
|
||||
type="number"
|
||||
value={newInvestment.initialPayment}
|
||||
onChange={(e) => {
|
||||
const val = Number(e.target.value);
|
||||
if (val >= newInvestment.totalInvestment * 0.5 && val <= newInvestment.totalInvestment) {
|
||||
setNewInvestment({ ...newInvestment, initialPayment: val });
|
||||
}
|
||||
}}
|
||||
className="w-full px-3 py-2 border border-investor rounded-lg text-sm bg-white"
|
||||
/>
|
||||
<p className="text-xs text-slate-500 mt-2">Balance: ৳{(newInvestment.totalInvestment - newInvestment.initialPayment).toLocaleString()}</p>
|
||||
</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>
|
||||
<input type="number" value={selectedTemplate.duration} disabled className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-slate-100 cursor-not-allowed" />
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-sm font-medium text-slate-600 mb-1 block">Lock-in Period (Months)</label>
|
||||
<input type="number" value={3} disabled className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-slate-100 cursor-not-allowed" />
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-sm font-medium text-slate-600 mb-1 block">Exit Penalty (%)</label>
|
||||
<input type="number" value={10} disabled className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-slate-100 cursor-not-allowed" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-amber-50 border border-amber-200 rounded-xl p-4">
|
||||
<h4 className="text-sm font-semibold text-amber-800 mb-3 flex items-center gap-2">
|
||||
<TrendingUp className="w-4 h-4" /> FICO Share - Profit per Ride
|
||||
</h4>
|
||||
<p className="text-xs text-amber-600 mb-3">Profit sharing when bikes are rented to end customers</p>
|
||||
<div className="grid grid-cols-3 gap-4">
|
||||
<div>
|
||||
<label className="text-xs text-slate-500 mb-1 block">Single Rent (%)</label>
|
||||
<input type="number" value={45} disabled className="w-full px-3 py-2 border border-amber-200 rounded-lg text-sm bg-white cursor-not-allowed" />
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-xs text-slate-500 mb-1 block">Rent to Own (%)</label>
|
||||
<input type="number" value={55} disabled className="w-full px-3 py-2 border border-amber-200 rounded-lg text-sm bg-white cursor-not-allowed" />
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-xs text-slate-500 mb-1 block">Share EV (%)</label>
|
||||
<input type="number" value={60} disabled className="w-full px-3 py-2 border border-amber-200 rounded-lg text-sm bg-white cursor-not-allowed" />
|
||||
</div>
|
||||
</div>
|
||||
</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>
|
||||
<input 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>
|
||||
<input 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>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="text-sm font-medium text-slate-600 mb-1 block">Payment Method</label>
|
||||
<select 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 bg-white">
|
||||
<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>
|
||||
<input 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>
|
||||
<label className="text-sm font-medium text-slate-600 mb-1 block">Description</label>
|
||||
<textarea 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} placeholder="Add notes..." />
|
||||
</div>
|
||||
|
||||
<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>
|
||||
</div>
|
||||
<p className="font-bold text-green-700">৳{newInvestment.paymentType === 'partial' ? newInvestment.initialPayment.toLocaleString() : newInvestment.totalInvestment.toLocaleString()}</p>
|
||||
</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>
|
||||
</div>
|
||||
</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>
|
||||
</div>
|
||||
<p className="font-bold text-blue-700">৳{newInvestment.totalInvestment.toLocaleString()}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="p-5 border-t border-slate-100 flex justify-end gap-3">
|
||||
<button onClick={() => setShowCreateModal(false)} className="px-4 py-2 border border-slate-200 text-slate-600 rounded-lg text-sm hover:bg-slate-50">Cancel</button>
|
||||
{selectedTemplate && (
|
||||
<button onClick={handleCreate} disabled={!newInvestment.planName || !newInvestment.totalInvestment} 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">
|
||||
<Plus className="w-4 h-4" /> Create Investment
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,6 +6,7 @@ import {
|
||||
ChevronLeft, ChevronRight, CheckCircle, XCircle, AlertCircle, Calendar
|
||||
} from 'lucide-react';
|
||||
import { investors, bikes, rentalPayments } from '@/data/mockData';
|
||||
import InvestorNotification from '@/components/InvestorNotification';
|
||||
|
||||
export default function RentalHistoryPage() {
|
||||
const investor = investors.find(i => i.id === 'inv1') || investors[0];
|
||||
@@ -64,311 +65,314 @@ export default function RentalHistoryPage() {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="p-4 sm:p-6 max-w-8xl mx-auto">
|
||||
{/* Header */}
|
||||
<div className="mb-6">
|
||||
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
|
||||
<div>
|
||||
<h1 className="text-xl sm:text-2xl font-bold text-slate-800 flex items-center gap-2">
|
||||
<History className="w-5 h-5 sm:w-6 sm:h-6 text-investor" /> Rental History
|
||||
</h1>
|
||||
<p className="text-sm text-slate-500 mt-1">Track daily rental payments from your bikes</p>
|
||||
<div className="min-h-screen">
|
||||
<InvestorNotification isMobile />
|
||||
<div className="pt-18 lg:pt-0 p-4 sm:p-6 max-w-8xl mx-auto">
|
||||
{/* Header */}
|
||||
<div className="mb-6">
|
||||
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
|
||||
<div>
|
||||
<h1 className="text-xl sm:text-2xl font-bold text-slate-800 flex items-center gap-2">
|
||||
<History className="w-5 h-5 sm:w-6 sm:h-6 text-investor" /> Rental History
|
||||
</h1>
|
||||
<p className="text-sm text-slate-500 mt-1">Track daily rental payments from your bikes</p>
|
||||
</div>
|
||||
<button className="px-4 py-2.5 bg-investor text-white rounded-lg text-sm font-medium hover:bg-investor-dark flex items-center gap-2 shadow-sm w-fit">
|
||||
<Download className="w-4 h-4" /> Export
|
||||
</button>
|
||||
</div>
|
||||
<button className="px-4 py-2.5 bg-investor text-white rounded-lg text-sm font-medium hover:bg-investor-dark flex items-center gap-2 shadow-sm w-fit">
|
||||
<Download className="w-4 h-4" /> Export
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Stats Cards */}
|
||||
<div className="grid grid-cols-2 lg:grid-cols-4 gap-3 sm:gap-4 mb-6">
|
||||
<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 shrink-0">
|
||||
<DollarSign className="w-5 h-5 text-green-600" />
|
||||
{/* Stats Cards */}
|
||||
<div className="grid grid-cols-2 lg:grid-cols-4 gap-3 sm:gap-4 mb-6">
|
||||
<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 shrink-0">
|
||||
<DollarSign className="w-5 h-5 text-green-600" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xs text-slate-500">Total Collected</p>
|
||||
<p className="text-lg font-bold text-green-600">৳{totalCollected.toLocaleString()}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xs text-slate-500">Total Collected</p>
|
||||
<p className="text-lg font-bold text-green-600">৳{totalCollected.toLocaleString()}</p>
|
||||
</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 shrink-0">
|
||||
<Bike className="w-5 h-5 text-blue-600" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xs text-slate-500">Active Rentals</p>
|
||||
<p className="text-lg font-bold text-slate-800">{activeRentals}</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 shrink-0">
|
||||
<Clock className="w-5 h-5 text-amber-600" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xs text-slate-500">Pending</p>
|
||||
<p className="text-lg font-bold text-amber-600">৳{totalPending.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 shrink-0">
|
||||
<AlertCircle className="w-5 h-5 text-red-600" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xs text-slate-500">Failed</p>
|
||||
<p className="text-lg font-bold text-red-600">৳{totalFailed.toLocaleString()}</p>
|
||||
</div>
|
||||
</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 shrink-0">
|
||||
<Bike className="w-5 h-5 text-blue-600" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xs text-slate-500">Active Rentals</p>
|
||||
<p className="text-lg font-bold text-slate-800">{activeRentals}</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 shrink-0">
|
||||
<Clock className="w-5 h-5 text-amber-600" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xs text-slate-500">Pending</p>
|
||||
<p className="text-lg font-bold text-amber-600">৳{totalPending.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 shrink-0">
|
||||
<AlertCircle className="w-5 h-5 text-red-600" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xs text-slate-500">Failed</p>
|
||||
<p className="text-lg font-bold text-red-600">৳{totalFailed.toLocaleString()}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Main Table Card */}
|
||||
<div className="bg-white rounded-xl border border-slate-200 overflow-hidden">
|
||||
{/* Filters */}
|
||||
<div className="p-4 border-b border-slate-100">
|
||||
<div className="flex flex-col lg:flex-row lg:items-center lg:justify-between gap-3">
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<div className="relative">
|
||||
<Search className="w-4 h-4 absolute left-3 top-1/2 -translate-y-1/2 text-slate-400" />
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search biker, bike..."
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
className="pl-9 pr-4 py-2 border border-slate-200 rounded-lg text-sm w-48 sm:w-64"
|
||||
/>
|
||||
</div>
|
||||
<select
|
||||
value={bikeFilter}
|
||||
onChange={(e) => { setBikeFilter(e.target.value); setPage(1); }}
|
||||
className="px-3 py-2 border border-slate-200 rounded-lg text-sm bg-white"
|
||||
>
|
||||
<option value="all">All Bikes</option>
|
||||
{investorBikes.map(bike => (
|
||||
<option key={bike.id} value={bike.id}>{bike.model}</option>
|
||||
))}
|
||||
</select>
|
||||
<select
|
||||
value={statusFilter}
|
||||
onChange={(e) => { setStatusFilter(e.target.value); setPage(1); }}
|
||||
className="px-3 py-2 border border-slate-200 rounded-lg text-sm bg-white"
|
||||
>
|
||||
<option value="all">All Status</option>
|
||||
<option value="paid">Paid</option>
|
||||
<option value="pending">Pending</option>
|
||||
<option value="failed">Failed</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<div className="flex items-center gap-1 text-xs text-slate-500 font-medium">
|
||||
<Calendar className="w-4 h-4" /> Date Range
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<input
|
||||
type="date"
|
||||
value={dateFrom}
|
||||
onChange={(e) => { setDateFrom(e.target.value); setPage(1); }}
|
||||
{/* Main Table Card */}
|
||||
<div className="bg-white rounded-xl border border-slate-200 overflow-hidden">
|
||||
{/* Filters */}
|
||||
<div className="p-4 border-b border-slate-100">
|
||||
<div className="flex flex-col lg:flex-row lg:items-center lg:justify-between gap-3">
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<div className="relative">
|
||||
<Search className="w-4 h-4 absolute left-3 top-1/2 -translate-y-1/2 text-slate-400" />
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search biker, bike..."
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
className="pl-9 pr-4 py-2 border border-slate-200 rounded-lg text-sm w-48 sm:w-64"
|
||||
/>
|
||||
</div>
|
||||
<select
|
||||
value={bikeFilter}
|
||||
onChange={(e) => { setBikeFilter(e.target.value); setPage(1); }}
|
||||
className="px-3 py-2 border border-slate-200 rounded-lg text-sm bg-white"
|
||||
/>
|
||||
<span className="text-slate-400">to</span>
|
||||
<input
|
||||
type="date"
|
||||
value={dateTo}
|
||||
onChange={(e) => { setDateTo(e.target.value); setPage(1); }}
|
||||
className="px-3 py-2 border border-slate-200 rounded-lg text-sm bg-white"
|
||||
/>
|
||||
</div>
|
||||
{(dateFrom || dateTo) && (
|
||||
<button
|
||||
onClick={() => { setDateFrom(''); setDateTo(''); setPage(1); }}
|
||||
className="px-2 py-1 text-xs text-red-500 hover:bg-red-50 rounded"
|
||||
>
|
||||
Clear
|
||||
</button>
|
||||
)}
|
||||
<option value="all">All Bikes</option>
|
||||
{investorBikes.map(bike => (
|
||||
<option key={bike.id} value={bike.id}>{bike.model}</option>
|
||||
))}
|
||||
</select>
|
||||
<select
|
||||
value={statusFilter}
|
||||
onChange={(e) => { setStatusFilter(e.target.value); setPage(1); }}
|
||||
className="px-3 py-2 border border-slate-200 rounded-lg text-sm bg-white"
|
||||
>
|
||||
<option value="all">All Status</option>
|
||||
<option value="paid">Paid</option>
|
||||
<option value="pending">Pending</option>
|
||||
<option value="failed">Failed</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<div className="flex items-center gap-1 text-xs text-slate-500 font-medium">
|
||||
<Calendar className="w-4 h-4" /> Date Range
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<input
|
||||
type="date"
|
||||
value={dateFrom}
|
||||
onChange={(e) => { setDateFrom(e.target.value); setPage(1); }}
|
||||
className="px-3 py-2 border border-slate-200 rounded-lg text-sm bg-white"
|
||||
/>
|
||||
<span className="text-slate-400">to</span>
|
||||
<input
|
||||
type="date"
|
||||
value={dateTo}
|
||||
onChange={(e) => { setDateTo(e.target.value); setPage(1); }}
|
||||
className="px-3 py-2 border border-slate-200 rounded-lg text-sm bg-white"
|
||||
/>
|
||||
</div>
|
||||
{(dateFrom || dateTo) && (
|
||||
<button
|
||||
onClick={() => { setDateFrom(''); setDateTo(''); setPage(1); }}
|
||||
className="px-2 py-1 text-xs text-red-500 hover:bg-red-50 rounded"
|
||||
>
|
||||
Clear
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Card View - Mobile/Tablet */}
|
||||
<div className="lg:hidden divide-y divide-slate-100">
|
||||
{paginatedPayments.length > 0 ? paginatedPayments.map((payment) => {
|
||||
const status = statusConfig[payment.status] || statusConfig.pending;
|
||||
const plan = planConfig[payment.planType] || planConfig.single;
|
||||
const StatusIcon = status.icon;
|
||||
return (
|
||||
<div key={payment.id} className="p-4 hover:bg-slate-50">
|
||||
<div className="flex items-start justify-between gap-3 mb-3">
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center gap-2 mb-1">
|
||||
<Bike className="w-4 h-4 text-slate-400 shrink-0" />
|
||||
<p className="text-sm font-semibold text-slate-800 truncate">{payment.bikeModel}</p>
|
||||
{/* Card View - Mobile/Tablet */}
|
||||
<div className="lg:hidden divide-y divide-slate-100">
|
||||
{paginatedPayments.length > 0 ? paginatedPayments.map((payment) => {
|
||||
const status = statusConfig[payment.status] || statusConfig.pending;
|
||||
const plan = planConfig[payment.planType] || planConfig.single;
|
||||
const StatusIcon = status.icon;
|
||||
return (
|
||||
<div key={payment.id} className="p-4 hover:bg-slate-50">
|
||||
<div className="flex items-start justify-between gap-3 mb-3">
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center gap-2 mb-1">
|
||||
<Bike className="w-4 h-4 text-slate-400 shrink-0" />
|
||||
<p className="text-sm font-semibold text-slate-800 truncate">{payment.bikeModel}</p>
|
||||
</div>
|
||||
<p className="text-xs text-slate-400 ml-6">{payment.plateNumber}</p>
|
||||
<div className="flex items-center gap-2 mt-1 ml-6">
|
||||
<User className="w-3 h-3 text-slate-400" />
|
||||
<p className="text-xs text-slate-600">{payment.bikerName}</p>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-xs text-slate-400 ml-6">{payment.plateNumber}</p>
|
||||
<div className="flex items-center gap-2 mt-1 ml-6">
|
||||
<User className="w-3 h-3 text-slate-400" />
|
||||
<p className="text-xs text-slate-600">{payment.bikerName}</p>
|
||||
<div className="text-right shrink-0">
|
||||
<p className="text-base font-bold text-slate-800">৳{payment.amount.toLocaleString()}</p>
|
||||
<span className={`inline-flex items-center gap-1 px-2 py-0.5 rounded text-xs font-medium mt-1 ${status.bg} ${status.color}`}>
|
||||
<StatusIcon className="w-3 h-3" /> {status.label}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-right shrink-0">
|
||||
<p className="text-base font-bold text-slate-800">৳{payment.amount.toLocaleString()}</p>
|
||||
<span className={`inline-flex items-center gap-1 px-2 py-0.5 rounded text-xs font-medium mt-1 ${status.bg} ${status.color}`}>
|
||||
<StatusIcon className="w-3 h-3" /> {status.label}
|
||||
</span>
|
||||
<div className="flex items-center justify-between ml-6 text-xs text-slate-400">
|
||||
<span>{payment.date}</span>
|
||||
<span className={`px-2 py-0.5 rounded ${plan.bg} ${plan.color}`}>{plan.label}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center justify-between ml-6 text-xs text-slate-400">
|
||||
<span>{payment.date}</span>
|
||||
<span className={`px-2 py-0.5 rounded ${plan.bg} ${plan.color}`}>{plan.label}</span>
|
||||
</div>
|
||||
);
|
||||
}) : (
|
||||
<div className="p-8 text-center text-slate-500">
|
||||
<History className="w-10 h-10 mx-auto mb-2 text-slate-300" />
|
||||
<p>No rental payments found</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Table View - Desktop */}
|
||||
<div className="hidden lg:block overflow-x-auto">
|
||||
<table className="w-full">
|
||||
<thead className="bg-slate-50">
|
||||
<tr>
|
||||
<th
|
||||
onClick={() => {
|
||||
if (sortBy === 'date') setSortOrder(sortOrder === 'asc' ? 'desc' : 'asc');
|
||||
else { setSortBy('date'); setSortOrder('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 {sortBy === 'date' && <span className="text-investor">{sortOrder === 'asc' ? '↑' : '↓'}</span>}
|
||||
</div>
|
||||
</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-medium text-slate-500 uppercase">Bike</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 (sortBy === 'amount') setSortOrder(sortOrder === 'asc' ? 'desc' : 'asc');
|
||||
else { setSortBy('amount'); setSortOrder('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 {sortBy === 'amount' && <span className="text-investor">{sortOrder === '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">
|
||||
{paginatedPayments.length > 0 ? paginatedPayments.map((payment) => {
|
||||
const status = statusConfig[payment.status] || statusConfig.pending;
|
||||
const plan = planConfig[payment.planType] || planConfig.single;
|
||||
const StatusIcon = status.icon;
|
||||
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={`inline-flex items-center gap-1 px-2 py-1 rounded-full text-xs font-medium ${status.bg} ${status.color}`}>
|
||||
<StatusIcon className="w-3 h-3" /> {status.label}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}) : (
|
||||
<tr>
|
||||
<td colSpan={8} className="px-4 py-12 text-center text-slate-500">
|
||||
<History className="w-12 h-12 mx-auto mb-2 text-slate-300" />
|
||||
<p>No rental payments found</p>
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{/* Pagination */}
|
||||
{sortedPayments.length > pageSize && (
|
||||
<div className="p-4 border-t border-slate-100 flex flex-col sm:flex-row items-center justify-between gap-3">
|
||||
<p className="text-xs sm:text-sm text-slate-500">
|
||||
Showing {((page - 1) * pageSize) + 1} to {Math.min(page * pageSize, sortedPayments.length)} of {sortedPayments.length}
|
||||
</p>
|
||||
<div className="flex items-center gap-1">
|
||||
<button
|
||||
onClick={() => setPage(p => Math.max(1, p - 1))}
|
||||
disabled={page === 1}
|
||||
className="p-2 border border-slate-200 rounded-lg disabled:opacity-50 hover:bg-slate-50"
|
||||
>
|
||||
<ChevronLeft className="w-4 h-4" />
|
||||
</button>
|
||||
{Array.from({ length: Math.min(5, totalPages) }, (_, i) => {
|
||||
const pageNum = totalPages <= 5 ? i + 1 : page <= 3 ? i + 1 : page >= totalPages - 2 ? totalPages - 4 + i : page - 2 + i;
|
||||
return (
|
||||
<button
|
||||
key={pageNum}
|
||||
onClick={() => setPage(pageNum)}
|
||||
className={`w-8 h-8 rounded-lg text-sm font-medium ${page === pageNum ? 'bg-investor text-white' : 'border border-slate-200 hover:bg-slate-50'}`}
|
||||
>
|
||||
{pageNum}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
<button
|
||||
onClick={() => setPage(p => Math.min(totalPages, p + 1))}
|
||||
disabled={page === totalPages}
|
||||
className="p-2 border border-slate-200 rounded-lg disabled:opacity-50 hover:bg-slate-50"
|
||||
>
|
||||
<ChevronRight className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}) : (
|
||||
<div className="p-8 text-center text-slate-500">
|
||||
<History className="w-10 h-10 mx-auto mb-2 text-slate-300" />
|
||||
<p>No rental payments found</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Table View - Desktop */}
|
||||
<div className="hidden lg:block overflow-x-auto">
|
||||
<table className="w-full">
|
||||
<thead className="bg-slate-50">
|
||||
<tr>
|
||||
<th
|
||||
onClick={() => {
|
||||
if (sortBy === 'date') setSortOrder(sortOrder === 'asc' ? 'desc' : 'asc');
|
||||
else { setSortBy('date'); setSortOrder('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 {sortBy === 'date' && <span className="text-investor">{sortOrder === 'asc' ? '↑' : '↓'}</span>}
|
||||
</div>
|
||||
</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-medium text-slate-500 uppercase">Bike</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 (sortBy === 'amount') setSortOrder(sortOrder === 'asc' ? 'desc' : 'asc');
|
||||
else { setSortBy('amount'); setSortOrder('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 {sortBy === 'amount' && <span className="text-investor">{sortOrder === '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">
|
||||
{paginatedPayments.length > 0 ? paginatedPayments.map((payment) => {
|
||||
const status = statusConfig[payment.status] || statusConfig.pending;
|
||||
const plan = planConfig[payment.planType] || planConfig.single;
|
||||
const StatusIcon = status.icon;
|
||||
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={`inline-flex items-center gap-1 px-2 py-1 rounded-full text-xs font-medium ${status.bg} ${status.color}`}>
|
||||
<StatusIcon className="w-3 h-3" /> {status.label}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}) : (
|
||||
<tr>
|
||||
<td colSpan={8} className="px-4 py-12 text-center text-slate-500">
|
||||
<History className="w-12 h-12 mx-auto mb-2 text-slate-300" />
|
||||
<p>No rental payments found</p>
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{/* Pagination */}
|
||||
{sortedPayments.length > pageSize && (
|
||||
<div className="p-4 border-t border-slate-100 flex flex-col sm:flex-row items-center justify-between gap-3">
|
||||
<p className="text-xs sm:text-sm text-slate-500">
|
||||
Showing {((page - 1) * pageSize) + 1} to {Math.min(page * pageSize, sortedPayments.length)} of {sortedPayments.length}
|
||||
</p>
|
||||
<div className="flex items-center gap-1">
|
||||
<button
|
||||
onClick={() => setPage(p => Math.max(1, p - 1))}
|
||||
disabled={page === 1}
|
||||
className="p-2 border border-slate-200 rounded-lg disabled:opacity-50 hover:bg-slate-50"
|
||||
>
|
||||
<ChevronLeft className="w-4 h-4" />
|
||||
</button>
|
||||
{Array.from({ length: Math.min(5, totalPages) }, (_, i) => {
|
||||
const pageNum = totalPages <= 5 ? i + 1 : page <= 3 ? i + 1 : page >= totalPages - 2 ? totalPages - 4 + i : page - 2 + i;
|
||||
return (
|
||||
<button
|
||||
key={pageNum}
|
||||
onClick={() => setPage(pageNum)}
|
||||
className={`w-8 h-8 rounded-lg text-sm font-medium ${page === pageNum ? 'bg-investor text-white' : 'border border-slate-200 hover:bg-slate-50'}`}
|
||||
>
|
||||
{pageNum}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
<button
|
||||
onClick={() => setPage(p => Math.min(totalPages, p + 1))}
|
||||
disabled={page === totalPages}
|
||||
className="p-2 border border-slate-200 rounded-lg disabled:opacity-50 hover:bg-slate-50"
|
||||
>
|
||||
<ChevronRight className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -4,6 +4,7 @@ import { useState } from 'react';
|
||||
import { CreditCard, Wallet, History, CheckCircle, Clock, Building2, Smartphone, AlertCircle, Settings, X, Bike, ChevronDown, Search, Filter, ChevronLeft, ChevronRight } from 'lucide-react';
|
||||
import { investors, transactions, bikes } from '@/data/mockData';
|
||||
import toast from 'react-hot-toast';
|
||||
import InvestorNotification from '@/components/InvestorNotification';
|
||||
|
||||
export default function InvestorWithdrawPage() {
|
||||
const investor = investors.find(i => i.id === 'inv1') || investors[0];
|
||||
@@ -107,149 +108,108 @@ export default function InvestorWithdrawPage() {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="p-4 lg:p-6 max-w-8xl mx-auto">
|
||||
{/* Header */}
|
||||
<div className="mb-6 flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
|
||||
<div>
|
||||
<h1 className="text-xl lg:text-2xl font-bold text-slate-800 flex items-center gap-2">
|
||||
<CreditCard className="w-5 h-5 lg:w-6 lg:h-6 text-investor" /> Withdraw Funds
|
||||
</h1>
|
||||
<p className="text-sm text-slate-500 mt-1">Request withdrawals to your bank or mobile banking accounts</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setShowAutoWithdrawModal(true)}
|
||||
className="px-4 py-2.5 border border-slate-200 text-slate-600 rounded-lg text-sm font-medium hover:bg-slate-50 flex items-center gap-2 shadow-sm bg-white w-fit"
|
||||
>
|
||||
<Settings className="w-4 h-4" /> Configure Auto-Withdraw
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Balance Cards - Mobile Responsive Grid */}
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-3 mb-4">
|
||||
<div className="sm:col-span-2 bg-investor to-investor-dark rounded-xl p-5 text-white shadow-lg relative overflow-hidden order-first lg:order-first">
|
||||
<div className="absolute top-0 right-0 w-24 h-24 bg-white/10 rounded-full -translate-y-1/2 translate-x-1/3 blur-2xl"></div>
|
||||
<div className="relative z-10">
|
||||
<div className="flex items-center gap-2 text-investor-light mb-2">
|
||||
<Wallet className="w-4 h-4" />
|
||||
<span className="font-medium text-xs">Available Balance</span>
|
||||
</div>
|
||||
<p className="text-3xl font-bold mb-1">৳{availableBalance.toLocaleString()}</p>
|
||||
<p className="text-xs text-white/80">Ready to withdraw</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-white rounded-xl p-4 border border-slate-200 shadow-sm">
|
||||
<p className="text-xs text-amber-600 font-medium mb-1">Pending Request</p>
|
||||
<p className="text-xl font-bold text-amber-600">৳{investor.pendingEarnings.toLocaleString()}</p>
|
||||
</div>
|
||||
<div className="bg-white rounded-xl p-4 border border-slate-200 shadow-sm">
|
||||
<p className="text-xs text-slate-600 font-medium mb-1">Total Withdrawn</p>
|
||||
<p className="text-xl font-bold text-slate-700">৳{investor.totalWithdrawn.toLocaleString()}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Alert + Action Button */}
|
||||
<div className="flex flex-col sm:flex-row items-stretch sm:items-center gap-3 mb-6">
|
||||
<div className="flex-1 bg-amber-50 border border-amber-200 rounded-xl p-4 flex gap-3 order-last sm:order-first">
|
||||
<AlertCircle className="w-5 h-5 text-amber-600 shrink-0 mt-0.5" />
|
||||
<div className="min-h-screen">
|
||||
<InvestorNotification isMobile />
|
||||
<div className="pt-18 lg:pt-0 p-4 lg:p-6 max-w-8xl mx-auto">
|
||||
{/* Header */}
|
||||
<div className="mb-6 flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
|
||||
<div>
|
||||
<h4 className="text-sm font-bold text-amber-800 mb-1">Pending Requests</h4>
|
||||
<p className="text-xs text-amber-700">You currently have <b>৳{investor.pendingEarnings.toLocaleString()}</b> in pending withdrawals. Processing takes 1-3 business days.</p>
|
||||
<h1 className="text-xl lg:text-2xl font-bold text-slate-800 flex items-center gap-2">
|
||||
<CreditCard className="w-5 h-5 lg:w-6 lg:h-6 text-investor" /> Withdraw Funds
|
||||
</h1>
|
||||
<p className="text-sm text-slate-500 mt-1">Request withdrawals to your bank or mobile banking accounts</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setShowAutoWithdrawModal(true)}
|
||||
className="px-4 py-2.5 border border-slate-200 text-slate-600 rounded-lg text-sm font-medium hover:bg-slate-50 flex items-center gap-2 shadow-sm bg-white w-fit"
|
||||
>
|
||||
<Settings className="w-4 h-4" /> Configure Auto-Withdraw
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setShowWithdrawModal(true)}
|
||||
className="px-5 py-3 bg-investor text-white rounded-xl font-semibold text-sm shadow-md hover:bg-investor-dark transition-all flex items-center justify-center gap-2 whitespace-nowrap sm:w-auto"
|
||||
>
|
||||
<CreditCard className="w-4 h-4" /> Withdrawal Request
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Recent Withdrawals Card */}
|
||||
<div className="bg-white rounded-xl border border-slate-200 shadow-sm overflow-hidden">
|
||||
<div className="p-4 border-b border-slate-100 bg-slate-50">
|
||||
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3">
|
||||
<h3 className="font-semibold text-slate-800 flex items-center gap-2">
|
||||
<History className="w-5 h-5 text-slate-400" /> Recent Withdrawals
|
||||
</h3>
|
||||
<div className="flex items-center gap-2">
|
||||
<select
|
||||
value={withdrawFilter}
|
||||
onChange={(e) => { setWithdrawFilter(e.target.value); setWithdrawPage(1); }}
|
||||
className="px-3 py-1.5 border border-slate-200 rounded-lg text-xs bg-white"
|
||||
>
|
||||
<option value="all">All</option>
|
||||
<option value="completed">Completed</option>
|
||||
<option value="pending">Pending</option>
|
||||
</select>
|
||||
{/* Balance Cards - Mobile Responsive Grid */}
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-3 mb-4">
|
||||
<div className="sm:col-span-2 bg-investor to-investor-dark rounded-xl p-5 text-white shadow-lg relative overflow-hidden order-first lg:order-first">
|
||||
<div className="absolute top-0 right-0 w-24 h-24 bg-white/10 rounded-full -translate-y-1/2 translate-x-1/3 blur-2xl"></div>
|
||||
<div className="relative z-10">
|
||||
<div className="flex items-center gap-2 text-investor-light mb-2">
|
||||
<Wallet className="w-4 h-4" />
|
||||
<span className="font-medium text-xs">Available Balance</span>
|
||||
</div>
|
||||
<p className="text-3xl font-bold mb-1">৳{availableBalance.toLocaleString()}</p>
|
||||
<p className="text-xs text-white/80">Ready to withdraw</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-white rounded-xl p-4 border border-slate-200 shadow-sm">
|
||||
<p className="text-xs text-amber-600 font-medium mb-1">Pending Request</p>
|
||||
<p className="text-xl font-bold text-amber-600">৳{investor.pendingEarnings.toLocaleString()}</p>
|
||||
</div>
|
||||
<div className="bg-white rounded-xl p-4 border border-slate-200 shadow-sm">
|
||||
<p className="text-xs text-slate-600 font-medium mb-1">Total Withdrawn</p>
|
||||
<p className="text-xl font-bold text-slate-700">৳{investor.totalWithdrawn.toLocaleString()}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Alert + Action Button */}
|
||||
<div className="flex flex-col sm:flex-row items-stretch sm:items-center gap-3 mb-6">
|
||||
<div className="flex-1 bg-amber-50 border border-amber-200 rounded-xl p-4 flex gap-3 order-last sm:order-first">
|
||||
<AlertCircle className="w-5 h-5 text-amber-600 shrink-0 mt-0.5" />
|
||||
<div>
|
||||
<h4 className="text-sm font-bold text-amber-800 mb-1">Pending Requests</h4>
|
||||
<p className="text-xs text-amber-700">You currently have <b>৳{investor.pendingEarnings.toLocaleString()}</b> in pending withdrawals. Processing takes 1-3 business days.</p>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setShowWithdrawModal(true)}
|
||||
className="px-5 py-3 bg-investor text-white rounded-xl font-semibold text-sm shadow-md hover:bg-investor-dark transition-all flex items-center justify-center gap-2 whitespace-nowrap sm:w-auto"
|
||||
>
|
||||
<CreditCard className="w-4 h-4" /> Withdrawal Request
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
||||
{/* Card View - Mobile/Tablet */}
|
||||
<div className="lg:hidden divide-y divide-slate-100">
|
||||
{paginatedWithdrawals.length > 0 ? paginatedWithdrawals.map((t) => (
|
||||
<div key={t.id} className="p-4 sm:p-5 hover:bg-slate-50 transition-colors">
|
||||
<div className="flex items-start justify-between gap-3 mb-3">
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="text-sm font-medium text-slate-800 mb-1">{t.description || 'Withdrawal'}</p>
|
||||
<div className="flex items-center gap-2 text-xs text-slate-400">
|
||||
<span>{t.createdAt}</span>
|
||||
{t.referenceNumber && (
|
||||
<>
|
||||
<span>•</span>
|
||||
<span className="hidden sm:inline">{t.referenceNumber}</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-right shrink-0">
|
||||
<p className="text-base font-bold text-slate-800">৳{t.amount.toLocaleString()}</p>
|
||||
<span className={`inline-flex items-center gap-1 px-2 py-0.5 rounded-md text-xs font-semibold mt-1 ${t.status === 'completed' ? 'bg-green-100 text-green-700' :
|
||||
t.status === 'pending' ? 'bg-amber-100 text-amber-700' :
|
||||
'bg-red-100 text-red-700'
|
||||
}`}>
|
||||
{t.status === 'completed' && <CheckCircle className="w-3 h-3" />}
|
||||
{t.status === 'pending' && <Clock className="w-3 h-3" />}
|
||||
{t.status}
|
||||
</span>
|
||||
</div>
|
||||
{/* Recent Withdrawals Card */}
|
||||
<div className="bg-white rounded-xl border border-slate-200 shadow-sm overflow-hidden">
|
||||
<div className="p-4 border-b border-slate-100 bg-slate-50">
|
||||
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3">
|
||||
<h3 className="font-semibold text-slate-800 flex items-center gap-2">
|
||||
<History className="w-5 h-5 text-slate-400" /> Recent Withdrawals
|
||||
</h3>
|
||||
<div className="flex items-center gap-2">
|
||||
<select
|
||||
value={withdrawFilter}
|
||||
onChange={(e) => { setWithdrawFilter(e.target.value); setWithdrawPage(1); }}
|
||||
className="px-3 py-1.5 border border-slate-200 rounded-lg text-xs bg-white"
|
||||
>
|
||||
<option value="all">All</option>
|
||||
<option value="completed">Completed</option>
|
||||
<option value="pending">Pending</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
)) : (
|
||||
<div className="px-4 py-12 text-center text-sm text-slate-500">
|
||||
<History className="w-10 h-10 mx-auto mb-2 text-slate-300" />
|
||||
No withdrawals found.
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Table View - Desktop */}
|
||||
</div>
|
||||
|
||||
|
||||
<div className="hidden lg:block overflow-x-auto">
|
||||
<table className="w-full text-left border-collapse">
|
||||
<thead>
|
||||
<tr className="bg-white border-b border-slate-100">
|
||||
<th className="px-4 py-3 text-xs font-semibold text-slate-500 uppercase tracking-wider">Date</th>
|
||||
<th className="px-4 py-3 text-xs font-semibold text-slate-500 uppercase tracking-wider hidden sm:table-cell">Ref</th>
|
||||
<th className="px-4 py-3 text-xs font-semibold text-slate-500 uppercase tracking-wider">Description</th>
|
||||
<th className="px-4 py-3 text-xs font-semibold text-slate-500 uppercase tracking-wider">Amount</th>
|
||||
<th className="px-4 py-3 text-xs font-semibold text-slate-500 uppercase tracking-wider">Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-slate-100">
|
||||
{paginatedWithdrawals.length > 0 ? paginatedWithdrawals.map((t) => (
|
||||
<tr key={t.id} className="hover:bg-slate-50 transition-colors">
|
||||
<td className="px-4 py-3.5 text-xs text-slate-600 whitespace-nowrap">{t.createdAt}</td>
|
||||
<td className="px-4 py-3.5 text-xs text-slate-500 hidden sm:table-cell">{t.referenceNumber || '-'}</td>
|
||||
<td className="px-4 py-3.5">
|
||||
<p className="text-sm font-medium text-slate-800">{t.description || 'Withdrawal'}</p>
|
||||
</td>
|
||||
<td className="px-4 py-3.5 text-sm font-bold text-slate-800">৳{t.amount.toLocaleString()}</td>
|
||||
<td className="px-4 py-3.5">
|
||||
<span className={`inline-flex items-center gap-1 px-2 py-1 rounded-md text-xs font-semibold ${t.status === 'completed' ? 'bg-green-100 text-green-700' :
|
||||
|
||||
{/* Card View - Mobile/Tablet */}
|
||||
<div className="lg:hidden divide-y divide-slate-100">
|
||||
{paginatedWithdrawals.length > 0 ? paginatedWithdrawals.map((t) => (
|
||||
<div key={t.id} className="p-4 sm:p-5 hover:bg-slate-50 transition-colors">
|
||||
<div className="flex items-start justify-between gap-3 mb-3">
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="text-sm font-medium text-slate-800 mb-1">{t.description || 'Withdrawal'}</p>
|
||||
<div className="flex items-center gap-2 text-xs text-slate-400">
|
||||
<span>{t.createdAt}</span>
|
||||
{t.referenceNumber && (
|
||||
<>
|
||||
<span>•</span>
|
||||
<span className="hidden sm:inline">{t.referenceNumber}</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-right shrink-0">
|
||||
<p className="text-base font-bold text-slate-800">৳{t.amount.toLocaleString()}</p>
|
||||
<span className={`inline-flex items-center gap-1 px-2 py-0.5 rounded-md text-xs font-semibold mt-1 ${t.status === 'completed' ? 'bg-green-100 text-green-700' :
|
||||
t.status === 'pending' ? 'bg-amber-100 text-amber-700' :
|
||||
'bg-red-100 text-red-700'
|
||||
}`}>
|
||||
@@ -257,330 +217,374 @@ export default function InvestorWithdrawPage() {
|
||||
{t.status === 'pending' && <Clock className="w-3 h-3" />}
|
||||
{t.status}
|
||||
</span>
|
||||
</td>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)) : (
|
||||
<div className="px-4 py-12 text-center text-sm text-slate-500">
|
||||
<History className="w-10 h-10 mx-auto mb-2 text-slate-300" />
|
||||
No withdrawals found.
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Table View - Desktop */}
|
||||
|
||||
|
||||
<div className="hidden lg:block overflow-x-auto">
|
||||
<table className="w-full text-left border-collapse">
|
||||
<thead>
|
||||
<tr className="bg-white border-b border-slate-100">
|
||||
<th className="px-4 py-3 text-xs font-semibold text-slate-500 uppercase tracking-wider">Date</th>
|
||||
<th className="px-4 py-3 text-xs font-semibold text-slate-500 uppercase tracking-wider hidden sm:table-cell">Ref</th>
|
||||
<th className="px-4 py-3 text-xs font-semibold text-slate-500 uppercase tracking-wider">Description</th>
|
||||
<th className="px-4 py-3 text-xs font-semibold text-slate-500 uppercase tracking-wider">Amount</th>
|
||||
<th className="px-4 py-3 text-xs font-semibold text-slate-500 uppercase tracking-wider">Status</th>
|
||||
</tr>
|
||||
)) : (
|
||||
<tr>
|
||||
<td colSpan={5} className="px-4 py-12 text-center text-sm text-slate-500">
|
||||
<History className="w-10 h-10 mx-auto mb-2 text-slate-300" />
|
||||
No withdrawals found.
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-slate-100">
|
||||
{paginatedWithdrawals.length > 0 ? paginatedWithdrawals.map((t) => (
|
||||
<tr key={t.id} className="hover:bg-slate-50 transition-colors">
|
||||
<td className="px-4 py-3.5 text-xs text-slate-600 whitespace-nowrap">{t.createdAt}</td>
|
||||
<td className="px-4 py-3.5 text-xs text-slate-500 hidden sm:table-cell">{t.referenceNumber || '-'}</td>
|
||||
<td className="px-4 py-3.5">
|
||||
<p className="text-sm font-medium text-slate-800">{t.description || 'Withdrawal'}</p>
|
||||
</td>
|
||||
<td className="px-4 py-3.5 text-sm font-bold text-slate-800">৳{t.amount.toLocaleString()}</td>
|
||||
<td className="px-4 py-3.5">
|
||||
<span className={`inline-flex items-center gap-1 px-2 py-1 rounded-md text-xs font-semibold ${t.status === 'completed' ? 'bg-green-100 text-green-700' :
|
||||
t.status === 'pending' ? 'bg-amber-100 text-amber-700' :
|
||||
'bg-red-100 text-red-700'
|
||||
}`}>
|
||||
{t.status === 'completed' && <CheckCircle className="w-3 h-3" />}
|
||||
{t.status === 'pending' && <Clock className="w-3 h-3" />}
|
||||
{t.status}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
)) : (
|
||||
<tr>
|
||||
<td colSpan={5} className="px-4 py-12 text-center text-sm text-slate-500">
|
||||
<History className="w-10 h-10 mx-auto mb-2 text-slate-300" />
|
||||
No withdrawals found.
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{/* Pagination */}
|
||||
{totalWithdrawPages > 1 && (
|
||||
<div className="p-4 border-t border-slate-100 flex flex-col sm:flex-row items-center justify-between gap-3">
|
||||
<p className="text-xs text-slate-500">
|
||||
Showing {((withdrawPage - 1) * withdrawPageSize) + 1} to {Math.min(withdrawPage * withdrawPageSize, filteredWithdrawals.length)} of {filteredWithdrawals.length}
|
||||
</p>
|
||||
<div className="flex items-center gap-1">
|
||||
<button
|
||||
onClick={() => setWithdrawPage(p => Math.max(1, p - 1))}
|
||||
disabled={withdrawPage === 1}
|
||||
className="p-1.5 border border-slate-200 rounded-lg disabled:opacity-50 hover:bg-slate-50"
|
||||
>
|
||||
<ChevronLeft className="w-4 h-4 text-slate-500" />
|
||||
</button>
|
||||
{Array.from({ length: Math.min(5, totalWithdrawPages) }, (_, i) => {
|
||||
const page = totalWithdrawPages <= 5 ? i + 1 : withdrawPage <= 3 ? i + 1 : withdrawPage >= totalWithdrawPages - 2 ? totalWithdrawPages - 4 + i : withdrawPage - 2 + i;
|
||||
return (
|
||||
<button
|
||||
key={page}
|
||||
onClick={() => setWithdrawPage(page)}
|
||||
className={`w-8 h-8 rounded-lg text-xs font-medium ${withdrawPage === page ? 'bg-investor text-white' : 'border border-slate-200 hover:bg-slate-50'}`}
|
||||
>
|
||||
{page}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
<button
|
||||
onClick={() => setWithdrawPage(p => Math.min(totalWithdrawPages, p + 1))}
|
||||
disabled={withdrawPage === totalWithdrawPages}
|
||||
className="p-1.5 border border-slate-200 rounded-lg disabled:opacity-50 hover:bg-slate-50"
|
||||
>
|
||||
<ChevronRight className="w-4 h-4 text-slate-500" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Pagination */}
|
||||
{totalWithdrawPages > 1 && (
|
||||
<div className="p-4 border-t border-slate-100 flex flex-col sm:flex-row items-center justify-between gap-3">
|
||||
<p className="text-xs text-slate-500">
|
||||
Showing {((withdrawPage - 1) * withdrawPageSize) + 1} to {Math.min(withdrawPage * withdrawPageSize, filteredWithdrawals.length)} of {filteredWithdrawals.length}
|
||||
</p>
|
||||
<div className="flex items-center gap-1">
|
||||
<button
|
||||
onClick={() => setWithdrawPage(p => Math.max(1, p - 1))}
|
||||
disabled={withdrawPage === 1}
|
||||
className="p-1.5 border border-slate-200 rounded-lg disabled:opacity-50 hover:bg-slate-50"
|
||||
>
|
||||
<ChevronLeft className="w-4 h-4 text-slate-500" />
|
||||
</button>
|
||||
{Array.from({ length: Math.min(5, totalWithdrawPages) }, (_, i) => {
|
||||
const page = totalWithdrawPages <= 5 ? i + 1 : withdrawPage <= 3 ? i + 1 : withdrawPage >= totalWithdrawPages - 2 ? totalWithdrawPages - 4 + i : withdrawPage - 2 + i;
|
||||
return (
|
||||
<button
|
||||
key={page}
|
||||
onClick={() => setWithdrawPage(page)}
|
||||
className={`w-8 h-8 rounded-lg text-xs font-medium ${withdrawPage === page ? 'bg-investor text-white' : 'border border-slate-200 hover:bg-slate-50'}`}
|
||||
>
|
||||
{page}
|
||||
{/* Withdrawal Request Modal */}
|
||||
{
|
||||
showWithdrawModal && (
|
||||
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4">
|
||||
<div className="bg-white rounded-xl shadow-xl w-full max-w-2xl max-h-[90vh] overflow-hidden flex flex-col">
|
||||
<div className="p-4 sm:p-5 border-b border-slate-100 flex items-center justify-between bg-gradient-to-r from-slate-50 to-white">
|
||||
<h3 className="text-base sm:text-lg font-bold text-slate-800">Create Withdrawal Request</h3>
|
||||
<button onClick={() => setShowWithdrawModal(false)} className="p-1.5 hover:bg-slate-100 rounded-lg transition-colors">
|
||||
<X className="w-5 h-5 text-slate-400" />
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
<button
|
||||
onClick={() => setWithdrawPage(p => Math.min(totalWithdrawPages, p + 1))}
|
||||
disabled={withdrawPage === totalWithdrawPages}
|
||||
className="p-1.5 border border-slate-200 rounded-lg disabled:opacity-50 hover:bg-slate-50"
|
||||
>
|
||||
<ChevronRight className="w-4 h-4 text-slate-500" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Withdrawal Request Modal */}
|
||||
{
|
||||
showWithdrawModal && (
|
||||
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4">
|
||||
<div className="bg-white rounded-xl shadow-xl w-full max-w-2xl max-h-[90vh] overflow-hidden flex flex-col">
|
||||
<div className="p-4 sm:p-5 border-b border-slate-100 flex items-center justify-between bg-gradient-to-r from-slate-50 to-white">
|
||||
<h3 className="text-base sm:text-lg font-bold text-slate-800">Create Withdrawal Request</h3>
|
||||
<button onClick={() => setShowWithdrawModal(false)} className="p-1.5 hover:bg-slate-100 rounded-lg transition-colors">
|
||||
<X className="w-5 h-5 text-slate-400" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="p-4 sm:p-5 overflow-y-auto flex-1 space-y-4 sm:space-y-5">
|
||||
{/* Balance Cards */}
|
||||
<div className="grid grid-cols-3 gap-2 sm:gap-3">
|
||||
<div className="bg-green-50 rounded-lg p-3 border border-green-200">
|
||||
<p className="text-xs text-green-600 font-medium">Available</p>
|
||||
<p className="text-sm font-bold text-green-700">৳{availableBalance.toLocaleString()}</p>
|
||||
</div>
|
||||
<div className="bg-amber-50 rounded-lg p-3 border border-amber-200">
|
||||
<p className="text-xs text-amber-600 font-medium">Pending</p>
|
||||
<p className="text-sm font-bold text-amber-700">৳{investor.pendingEarnings.toLocaleString()}</p>
|
||||
</div>
|
||||
<div className="bg-slate-50 rounded-lg p-3 border border-slate-200">
|
||||
<p className="text-xs text-slate-600 font-medium">Withdrawn</p>
|
||||
<p className="text-sm font-bold text-slate-700">৳{investor.totalWithdrawn.toLocaleString()}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Select Investment Plans & Bikes */}
|
||||
<div>
|
||||
<h4 className="font-semibold text-slate-800 mb-2">Select Investment Plans & Bikes</h4>
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center gap-3 p-3 bg-slate-50 rounded-lg border border-slate-200">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="selectAll"
|
||||
checked={selectAll}
|
||||
onChange={(e) => toggleSelectAll(e.target.checked)}
|
||||
className="w-5 h-5 text-investor rounded border-slate-300"
|
||||
/>
|
||||
<label htmlFor="selectAll" className="flex-1 cursor-pointer">
|
||||
<span className="font-semibold text-slate-800">Select All</span>
|
||||
<p className="text-xs text-slate-500">Include all investments and bikes</p>
|
||||
</label>
|
||||
<div className="p-4 sm:p-5 overflow-y-auto flex-1 space-y-4 sm:space-y-5">
|
||||
{/* Balance Cards */}
|
||||
<div className="grid grid-cols-3 gap-2 sm:gap-3">
|
||||
<div className="bg-green-50 rounded-lg p-3 border border-green-200">
|
||||
<p className="text-xs text-green-600 font-medium">Available</p>
|
||||
<p className="text-sm font-bold text-green-700">৳{availableBalance.toLocaleString()}</p>
|
||||
</div>
|
||||
<div className="bg-amber-50 rounded-lg p-3 border border-amber-200">
|
||||
<p className="text-xs text-amber-600 font-medium">Pending</p>
|
||||
<p className="text-sm font-bold text-amber-700">৳{investor.pendingEarnings.toLocaleString()}</p>
|
||||
</div>
|
||||
<div className="bg-slate-50 rounded-lg p-3 border border-slate-200">
|
||||
<p className="text-xs text-slate-600 font-medium">Withdrawn</p>
|
||||
<p className="text-sm font-bold text-slate-700">৳{investor.totalWithdrawn.toLocaleString()}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="border border-slate-200 rounded-lg overflow-hidden">
|
||||
<div className="bg-slate-100 px-3 py-2 border-b border-slate-200">
|
||||
<p className="text-sm font-semibold text-slate-700">Investment Plans</p>
|
||||
{/* Select Investment Plans & Bikes */}
|
||||
<div>
|
||||
<h4 className="font-semibold text-slate-800 mb-2">Select Investment Plans & Bikes</h4>
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center gap-3 p-3 bg-slate-50 rounded-lg border border-slate-200">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="selectAll"
|
||||
checked={selectAll}
|
||||
onChange={(e) => toggleSelectAll(e.target.checked)}
|
||||
className="w-5 h-5 text-investor rounded border-slate-300"
|
||||
/>
|
||||
<label htmlFor="selectAll" className="flex-1 cursor-pointer">
|
||||
<span className="font-semibold text-slate-800">Select All</span>
|
||||
<p className="text-xs text-slate-500">Include all investments and bikes</p>
|
||||
</label>
|
||||
</div>
|
||||
<div className="divide-y divide-slate-100">
|
||||
{investor.investments?.map((inv: any) => {
|
||||
const invBikes = assignedBikes.filter((b: any) => b.investmentId === inv.id);
|
||||
const invEarnings = invBikes.reduce((sum: number, b: any) => sum + (b.totalEarnings || 0), 0);
|
||||
return (
|
||||
<div key={inv.id} className="p-3 sm:p-4">
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
id={`plan-${inv.id}`}
|
||||
checked={selectAll || selectedPlans.includes(inv.id)}
|
||||
disabled={selectAll}
|
||||
onChange={() => togglePlan(inv.id, invBikes)}
|
||||
className="w-4 h-4 text-investor rounded border-slate-300"
|
||||
/>
|
||||
<label htmlFor={`plan-${inv.id}`} className="flex-1 flex items-center justify-between cursor-pointer">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="font-medium text-slate-800 text-sm">{inv.planName}</span>
|
||||
<span className={`px-2 py-0.5 rounded text-xs capitalize font-medium ${planColors[inv.planType]}`}>{inv.planType}</span>
|
||||
</div>
|
||||
<span className="text-sm font-bold text-green-600">৳{invEarnings.toLocaleString()}</span>
|
||||
</label>
|
||||
</div>
|
||||
{!selectAll && invBikes.length > 0 && (
|
||||
<div className="ml-6 sm:ml-8 pl-3 sm: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 p-2 bg-white rounded-lg border border-slate-100">
|
||||
<input
|
||||
type="checkbox"
|
||||
id={`bike-${bike.id}`}
|
||||
checked={selectAll || selectedBikes.includes(bike.id)}
|
||||
onChange={() => toggleBike(bike.id)}
|
||||
className="w-4 h-4 text-investor rounded border-slate-300"
|
||||
/>
|
||||
<label htmlFor={`bike-${bike.id}`} className="flex-1 flex items-center gap-2 cursor-pointer">
|
||||
<Bike className="w-4 h-4 text-slate-400" />
|
||||
<div>
|
||||
<span className="text-sm font-medium text-slate-700">{bike.model}</span>
|
||||
<span className="text-xs text-slate-400 ml-2">{bike.plateNumber}</span>
|
||||
</div>
|
||||
</label>
|
||||
<span className="text-xs text-green-600 font-semibold">৳{bike.totalEarnings?.toLocaleString() || 0}</span>
|
||||
|
||||
<div className="border border-slate-200 rounded-lg overflow-hidden">
|
||||
<div className="bg-slate-100 px-3 py-2 border-b border-slate-200">
|
||||
<p className="text-sm font-semibold text-slate-700">Investment Plans</p>
|
||||
</div>
|
||||
<div className="divide-y divide-slate-100">
|
||||
{investor.investments?.map((inv: any) => {
|
||||
const invBikes = assignedBikes.filter((b: any) => b.investmentId === inv.id);
|
||||
const invEarnings = invBikes.reduce((sum: number, b: any) => sum + (b.totalEarnings || 0), 0);
|
||||
return (
|
||||
<div key={inv.id} className="p-3 sm:p-4">
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
id={`plan-${inv.id}`}
|
||||
checked={selectAll || selectedPlans.includes(inv.id)}
|
||||
disabled={selectAll}
|
||||
onChange={() => togglePlan(inv.id, invBikes)}
|
||||
className="w-4 h-4 text-investor rounded border-slate-300"
|
||||
/>
|
||||
<label htmlFor={`plan-${inv.id}`} className="flex-1 flex items-center justify-between cursor-pointer">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="font-medium text-slate-800 text-sm">{inv.planName}</span>
|
||||
<span className={`px-2 py-0.5 rounded text-xs capitalize font-medium ${planColors[inv.planType]}`}>{inv.planType}</span>
|
||||
</div>
|
||||
))}
|
||||
<span className="text-sm font-bold text-green-600">৳{invEarnings.toLocaleString()}</span>
|
||||
</label>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
{!selectAll && invBikes.length > 0 && (
|
||||
<div className="ml-6 sm:ml-8 pl-3 sm: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 p-2 bg-white rounded-lg border border-slate-100">
|
||||
<input
|
||||
type="checkbox"
|
||||
id={`bike-${bike.id}`}
|
||||
checked={selectAll || selectedBikes.includes(bike.id)}
|
||||
onChange={() => toggleBike(bike.id)}
|
||||
className="w-4 h-4 text-investor rounded border-slate-300"
|
||||
/>
|
||||
<label htmlFor={`bike-${bike.id}`} className="flex-1 flex items-center gap-2 cursor-pointer">
|
||||
<Bike className="w-4 h-4 text-slate-400" />
|
||||
<div>
|
||||
<span className="text-sm font-medium text-slate-700">{bike.model}</span>
|
||||
<span className="text-xs text-slate-400 ml-2">{bike.plateNumber}</span>
|
||||
</div>
|
||||
</label>
|
||||
<span className="text-xs text-green-600 font-semibold">৳{bike.totalEarnings?.toLocaleString() || 0}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Withdrawal Amount */}
|
||||
<div className="bg-gradient-to-r from-green-50 to-emerald-50 border border-green-200 rounded-xl p-4">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<h4 className="font-semibold text-slate-800 text-sm">Withdrawal Amount</h4>
|
||||
<span className={`text-xs px-2 py-0.5 rounded-full font-medium ${selectAll ? 'bg-green-100 text-green-700' : 'bg-amber-100 text-amber-700'}`}>
|
||||
{selectAll ? 'All Selected' : `${selectedBikes.length} bikes`}
|
||||
</span>
|
||||
{/* Withdrawal Amount */}
|
||||
<div className="bg-gradient-to-r from-green-50 to-emerald-50 border border-green-200 rounded-xl p-4">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<h4 className="font-semibold text-slate-800 text-sm">Withdrawal Amount</h4>
|
||||
<span className={`text-xs px-2 py-0.5 rounded-full font-medium ${selectAll ? 'bg-green-100 text-green-700' : 'bg-amber-100 text-amber-700'}`}>
|
||||
{selectAll ? 'All Selected' : `${selectedBikes.length} bikes`}
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-2xl sm:text-3xl font-bold text-green-700 mb-1">৳{calculatedAmount.toLocaleString()}</p>
|
||||
<p className="text-xs text-slate-500">Based on {selectAll ? 'all' : selectedBikes.length} selected bike(s) earnings</p>
|
||||
</div>
|
||||
<p className="text-2xl sm:text-3xl font-bold text-green-700 mb-1">৳{calculatedAmount.toLocaleString()}</p>
|
||||
<p className="text-xs text-slate-500">Based on {selectAll ? 'all' : selectedBikes.length} selected bike(s) earnings</p>
|
||||
</div>
|
||||
|
||||
{/* Payment Method */}
|
||||
<div>
|
||||
<h4 className="font-semibold text-slate-800 mb-2">Payment Method</h4>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-2">
|
||||
{(investor as any).bankAccounts?.map((account: any) => (
|
||||
{/* Payment Method */}
|
||||
<div>
|
||||
<h4 className="font-semibold text-slate-800 mb-2">Payment Method</h4>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-2">
|
||||
{(investor as any).bankAccounts?.map((account: any) => (
|
||||
<div
|
||||
key={account.id}
|
||||
onClick={() => { setPaymentMethod('bank'); setSelectedAccount(account.id); }}
|
||||
className={`p-3 sm:p-4 rounded-lg border cursor-pointer transition-all ${selectedAccount === account.id ? 'border-investor bg-investor/5 ring-2 ring-investor/20' : 'border-slate-200 hover:border-slate-300'}`}
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-8 h-8 sm:w-10 sm:h-10 rounded-lg flex items-center justify-center bg-slate-100">
|
||||
<Building2 className="w-4 h-4 sm:w-5 sm:h-5 text-slate-500" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm font-semibold text-slate-800">{account.bankName}</p>
|
||||
<p className="text-xs text-slate-500 font-mono">{account.accountNumber}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
{(investor as any).mobileBanking && (
|
||||
<div
|
||||
key={account.id}
|
||||
onClick={() => { setPaymentMethod('bank'); setSelectedAccount(account.id); }}
|
||||
className={`p-3 sm:p-4 rounded-lg border cursor-pointer transition-all ${selectedAccount === account.id ? 'border-investor bg-investor/5 ring-2 ring-investor/20' : 'border-slate-200 hover:border-slate-300'}`}
|
||||
onClick={() => { setPaymentMethod('mobile'); setSelectedAccount('mobile'); }}
|
||||
className={`mt-2 p-3 sm:p-4 rounded-lg border cursor-pointer transition-all ${selectedAccount === 'mobile' ? 'border-investor bg-investor/5 ring-2 ring-investor/20' : 'border-slate-200 hover:border-slate-300'}`}
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-8 h-8 sm:w-10 sm:h-10 rounded-lg flex items-center justify-center bg-slate-100">
|
||||
<Building2 className="w-4 h-4 sm:w-5 sm:h-5 text-slate-500" />
|
||||
<div className="w-8 h-8 sm:w-10 sm:h-10 rounded-lg bg-pink-100 flex items-center justify-center">
|
||||
<Smartphone className="w-4 h-4 sm:w-5 sm:h-5 text-pink-600" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm font-semibold text-slate-800">{account.bankName}</p>
|
||||
<p className="text-xs text-slate-500 font-mono">{account.accountNumber}</p>
|
||||
<p className="text-sm font-semibold text-slate-800">{(investor as any).mobileBanking}</p>
|
||||
<p className="text-xs text-slate-500 font-mono">{(investor as any).mobileBankingNumber}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
{(investor as any).mobileBanking && (
|
||||
<div
|
||||
onClick={() => { setPaymentMethod('mobile'); setSelectedAccount('mobile'); }}
|
||||
className={`mt-2 p-3 sm:p-4 rounded-lg border cursor-pointer transition-all ${selectedAccount === 'mobile' ? 'border-investor bg-investor/5 ring-2 ring-investor/20' : 'border-slate-200 hover:border-slate-300'}`}
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-8 h-8 sm:w-10 sm:h-10 rounded-lg bg-pink-100 flex items-center justify-center">
|
||||
<Smartphone className="w-4 h-4 sm:w-5 sm:h-5 text-pink-600" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm font-semibold text-slate-800">{(investor as any).mobileBanking}</p>
|
||||
<p className="text-xs text-slate-500 font-mono">{(investor as any).mobileBankingNumber}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="p-4 sm:p-5 border-t border-slate-200 flex justify-end gap-2 sm:gap-3 bg-slate-50">
|
||||
<button onClick={() => setShowWithdrawModal(false)} className="px-4 py-2 sm:py-2.5 border border-slate-300 text-slate-600 rounded-lg text-sm font-medium hover:bg-white hover:shadow-sm transition-all">
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
onClick={handleSubmitWithdraw}
|
||||
disabled={!selectedAccount || (selectedBikes.length === 0 && !selectAll)}
|
||||
className="px-5 py-2 sm:py-2.5 bg-investor text-white rounded-lg text-sm font-semibold hover:bg-investor-dark disabled:opacity-50 disabled:cursor-not-allowed shadow-sm transition-all flex items-center gap-2"
|
||||
>
|
||||
<CreditCard className="w-4 h-4" /> Submit Request
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
{/* Auto-Withdraw Modal */}
|
||||
{
|
||||
showAutoWithdrawModal && (
|
||||
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-[60] p-4">
|
||||
<div className="bg-white rounded-xl shadow-xl w-full max-w-md">
|
||||
<div className="p-5 border-b border-slate-100 flex items-center justify-between">
|
||||
<h3 className="text-lg font-bold text-slate-800">Auto-Withdraw Settings</h3>
|
||||
<button onClick={() => setShowAutoWithdrawModal(false)} className="p-1.5 hover:bg-slate-100 rounded-lg">
|
||||
<X className="w-5 h-5 text-slate-400" />
|
||||
</button>
|
||||
</div>
|
||||
<div className="p-5 space-y-4">
|
||||
<div>
|
||||
<label className="text-sm font-medium text-slate-700 mb-2 block">Enable Auto-Withdraw</label>
|
||||
<div className="flex items-center gap-3 p-3 bg-slate-50 rounded-lg">
|
||||
<button
|
||||
onClick={() => setAutoWithdrawEnabled(!autoWithdrawEnabled)}
|
||||
className={`w-12 h-6 rounded-full transition-colors ${autoWithdrawEnabled ? 'bg-green-500' : 'bg-slate-200'}`}
|
||||
>
|
||||
<span className={`block w-5 h-5 bg-white rounded-full shadow transition-transform ${autoWithdrawEnabled ? 'translate-x-6' : 'translate-x-0.5'}`} />
|
||||
</button>
|
||||
<span className="text-sm text-slate-600">Automatically withdraw earnings</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-sm font-medium text-slate-700 mb-2 block">Withdrawal Frequency</label>
|
||||
<select
|
||||
value={autoWithdrawFreq}
|
||||
onChange={(e) => setAutoWithdrawFreq(e.target.value)}
|
||||
className="w-full px-3 py-2.5 border border-slate-200 rounded-lg text-sm bg-white"
|
||||
>
|
||||
<option value="as_per_request">As Requested</option>
|
||||
<option value="weekly">Weekly</option>
|
||||
<option value="monthly">Monthly</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-sm font-medium text-slate-700 mb-2 block">Minimum Amount</label>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="px-3 py-2.5 bg-slate-100 rounded-lg text-slate-500 font-bold">৳</span>
|
||||
<input
|
||||
type="number"
|
||||
value={autoWithdrawMin}
|
||||
onChange={(e) => setAutoWithdrawMin(e.target.value)}
|
||||
className="flex-1 px-3 py-2.5 border border-slate-200 rounded-lg text-sm"
|
||||
placeholder="1000"
|
||||
/>
|
||||
</div>
|
||||
<p className="text-xs text-slate-500 mt-1.5">Minimum balance required for auto-withdrawal</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-sm font-medium text-slate-700 mb-2 block">Destination Account</label>
|
||||
<div className="space-y-2">
|
||||
{(investor as any).bankAccounts?.map((account: any) => (
|
||||
<div key={account.id} className="flex items-center gap-3 p-3 bg-slate-50 rounded-lg border border-slate-100">
|
||||
<input
|
||||
type="radio"
|
||||
name="destAccount"
|
||||
checked={autoWithdrawAccount === account.id}
|
||||
onChange={() => setAutoWithdrawAccount(account.id)}
|
||||
className="w-4 h-4 text-investor"
|
||||
/>
|
||||
<div className="flex-1">
|
||||
<p className="font-medium text-slate-700">{account.bankName}</p>
|
||||
<p className="text-xs text-slate-500 font-mono">{account.accountNumber}</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
{(investor as any).mobileBanking && (
|
||||
<div className="flex items-center gap-3 p-3 bg-purple-50 rounded-lg border border-purple-100">
|
||||
<input
|
||||
type="radio"
|
||||
name="destAccount"
|
||||
checked={autoWithdrawAccount === 'mobile'}
|
||||
onChange={() => setAutoWithdrawAccount('mobile')}
|
||||
className="w-4 h-4 text-investor"
|
||||
/>
|
||||
<div className="flex-1">
|
||||
<p className="font-medium text-slate-700">{(investor as any).mobileBanking}</p>
|
||||
<p className="text-xs text-slate-500 font-mono">{(investor as any).mobileBankingNumber}</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-5 border-t border-slate-100 flex justify-end gap-3 bg-slate-50">
|
||||
<button onClick={() => setShowAutoWithdrawModal(false)} className="px-4 py-2 border border-slate-200 text-slate-600 rounded-lg text-sm font-medium hover:bg-white">Cancel</button>
|
||||
<button onClick={handleSaveAutoWithdraw} className="px-5 py-2 bg-investor text-white rounded-lg text-sm font-medium hover:bg-investor-dark shadow-sm">Save Settings</button>
|
||||
|
||||
<div className="p-4 sm:p-5 border-t border-slate-200 flex justify-end gap-2 sm:gap-3 bg-slate-50">
|
||||
<button onClick={() => setShowWithdrawModal(false)} className="px-4 py-2 sm:py-2.5 border border-slate-300 text-slate-600 rounded-lg text-sm font-medium hover:bg-white hover:shadow-sm transition-all">
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
onClick={handleSubmitWithdraw}
|
||||
disabled={!selectedAccount || (selectedBikes.length === 0 && !selectAll)}
|
||||
className="px-5 py-2 sm:py-2.5 bg-investor text-white rounded-lg text-sm font-semibold hover:bg-investor-dark disabled:opacity-50 disabled:cursor-not-allowed shadow-sm transition-all flex items-center gap-2"
|
||||
>
|
||||
<CreditCard className="w-4 h-4" /> Submit Request
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
{/* Auto-Withdraw Modal */}
|
||||
{
|
||||
showAutoWithdrawModal && (
|
||||
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-[60] p-4">
|
||||
<div className="bg-white rounded-xl shadow-xl w-full max-w-md">
|
||||
<div className="p-5 border-b border-slate-100 flex items-center justify-between">
|
||||
<h3 className="text-lg font-bold text-slate-800">Auto-Withdraw Settings</h3>
|
||||
<button onClick={() => setShowAutoWithdrawModal(false)} className="p-1.5 hover:bg-slate-100 rounded-lg">
|
||||
<X className="w-5 h-5 text-slate-400" />
|
||||
</button>
|
||||
</div>
|
||||
<div className="p-5 space-y-4">
|
||||
<div>
|
||||
<label className="text-sm font-medium text-slate-700 mb-2 block">Enable Auto-Withdraw</label>
|
||||
<div className="flex items-center gap-3 p-3 bg-slate-50 rounded-lg">
|
||||
<button
|
||||
onClick={() => setAutoWithdrawEnabled(!autoWithdrawEnabled)}
|
||||
className={`w-12 h-6 rounded-full transition-colors ${autoWithdrawEnabled ? 'bg-green-500' : 'bg-slate-200'}`}
|
||||
>
|
||||
<span className={`block w-5 h-5 bg-white rounded-full shadow transition-transform ${autoWithdrawEnabled ? 'translate-x-6' : 'translate-x-0.5'}`} />
|
||||
</button>
|
||||
<span className="text-sm text-slate-600">Automatically withdraw earnings</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-sm font-medium text-slate-700 mb-2 block">Withdrawal Frequency</label>
|
||||
<select
|
||||
value={autoWithdrawFreq}
|
||||
onChange={(e) => setAutoWithdrawFreq(e.target.value)}
|
||||
className="w-full px-3 py-2.5 border border-slate-200 rounded-lg text-sm bg-white"
|
||||
>
|
||||
<option value="as_per_request">As Requested</option>
|
||||
<option value="weekly">Weekly</option>
|
||||
<option value="monthly">Monthly</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-sm font-medium text-slate-700 mb-2 block">Minimum Amount</label>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="px-3 py-2.5 bg-slate-100 rounded-lg text-slate-500 font-bold">৳</span>
|
||||
<input
|
||||
type="number"
|
||||
value={autoWithdrawMin}
|
||||
onChange={(e) => setAutoWithdrawMin(e.target.value)}
|
||||
className="flex-1 px-3 py-2.5 border border-slate-200 rounded-lg text-sm"
|
||||
placeholder="1000"
|
||||
/>
|
||||
</div>
|
||||
<p className="text-xs text-slate-500 mt-1.5">Minimum balance required for auto-withdrawal</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-sm font-medium text-slate-700 mb-2 block">Destination Account</label>
|
||||
<div className="space-y-2">
|
||||
{(investor as any).bankAccounts?.map((account: any) => (
|
||||
<div key={account.id} className="flex items-center gap-3 p-3 bg-slate-50 rounded-lg border border-slate-100">
|
||||
<input
|
||||
type="radio"
|
||||
name="destAccount"
|
||||
checked={autoWithdrawAccount === account.id}
|
||||
onChange={() => setAutoWithdrawAccount(account.id)}
|
||||
className="w-4 h-4 text-investor"
|
||||
/>
|
||||
<div className="flex-1">
|
||||
<p className="font-medium text-slate-700">{account.bankName}</p>
|
||||
<p className="text-xs text-slate-500 font-mono">{account.accountNumber}</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
{(investor as any).mobileBanking && (
|
||||
<div className="flex items-center gap-3 p-3 bg-purple-50 rounded-lg border border-purple-100">
|
||||
<input
|
||||
type="radio"
|
||||
name="destAccount"
|
||||
checked={autoWithdrawAccount === 'mobile'}
|
||||
onChange={() => setAutoWithdrawAccount('mobile')}
|
||||
className="w-4 h-4 text-investor"
|
||||
/>
|
||||
<div className="flex-1">
|
||||
<p className="font-medium text-slate-700">{(investor as any).mobileBanking}</p>
|
||||
<p className="text-xs text-slate-500 font-mono">{(investor as any).mobileBankingNumber}</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-5 border-t border-slate-100 flex justify-end gap-3 bg-slate-50">
|
||||
<button onClick={() => setShowAutoWithdrawModal(false)} className="px-4 py-2 border border-slate-200 text-slate-600 rounded-lg text-sm font-medium hover:bg-white">Cancel</button>
|
||||
<button onClick={handleSaveAutoWithdraw} className="px-5 py-2 bg-investor text-white rounded-lg text-sm font-medium hover:bg-investor-dark shadow-sm">Save Settings</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div >
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user