feat: add investment success modal and redesign investor bike cards with plan-based styling

This commit is contained in:
sazzadulalambd
2026-05-14 19:49:57 +06:00
parent 456e7200fc
commit d9a879f53e
3 changed files with 1133 additions and 213 deletions

View File

@@ -86,6 +86,8 @@ export default function InvestorDetailPage() {
const [showAssignBikeModal, setShowAssignBikeModal] = useState(false);
const [selectedBikeId, setSelectedBikeId] = useState('');
const [showCreateInvestmentModal, setShowCreateInvestmentModal] = useState(false);
const [showInvestmentSuccessModal, setShowInvestmentSuccessModal] = useState(false);
const [lastCreatedInvestment, setLastCreatedInvestment] = useState<any>(null);
const [showInvoiceModal, setShowInvoiceModal] = useState(false);
const [showJournalModal, setShowJournalModal] = useState(false);
const [selectedInvoice, setSelectedInvoice] = useState<any>(null);
@@ -172,35 +174,22 @@ export default function InvestorDetailPage() {
paymentMethod: newInvestment.paymentMethod
};
setInvestorJournals([journalEntry, ...investorJournals]);
setInvestorJournals([journalEntry, ...investorJournals]);
console.log('Creating Investment:', {
setLastCreatedInvestment({
id: invId,
investorId: investor.id,
...newInvestment,
actualEarnings: 0,
status: 'active' as const,
status: 'active',
transactionId: transactionRef,
createdAt: new Date().toISOString()
createdAt: new Date().toISOString(),
debitAccount,
journalEntry
});
alert(`Investment created successfully!
Investor: ${investor.name}
Investment ID: ${invId}
Transaction Ref: ${transactionRef}
Amount: ৳${newInvestment.totalInvestment.toLocaleString()}
Accounting Entry Created (Auto-Journal):
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Date: ${newInvestment.startDate}
Ref: ${transactionRef}
Description: ${investor.name} - ${newInvestment.planName}
Debit (Dr): ${debitAccount.name}${newInvestment.totalInvestment.toLocaleString()}
Credit (Cr): Investor Liability ৳${newInvestment.totalInvestment.toLocaleString()}
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`);
setShowCreateInvestmentModal(false);
setShowInvestmentSuccessModal(true);
setNewInvestment({
planName: '',
planType: 'gold',
@@ -908,59 +897,114 @@ Credit (Cr): Investor Liability ৳${newInvestment.totalInvestment.toLocaleStri
{activeTab === 'bikes' && (
<div>
<div className="flex items-center justify-between mb-4">
<h3 className="font-semibold text-slate-800">Assigned Bikes</h3>
<button
onClick={() => setShowAssignBikeModal(true)}
className="py-2 px-3 bg-investor text-white rounded-lg text-sm font-medium hover:bg-investor-dark flex items-center gap-1"
>
<Plus className="w-4 h-4" /> Assign Bike
</button>
<div>
<h3 className="font-semibold text-slate-800">Assigned Bikes</h3>
<p className="text-sm text-slate-500">{assignedBikes.length} bikes across {investor.investments?.length || 0} investment plans</p>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{assignedBikes.map(bike => (
<div key={bike.id} className="bg-slate-50 rounded-xl p-4">
<div className="flex items-start justify-between mb-3">
<div className="flex items-center gap-3">
<div className="w-12 h-12 rounded-xl bg-white flex items-center justify-center shadow-sm">
<Bike className="w-6 h-6 text-blue-600" />
{assignedBikes.map(bike => {
const investment = investor.investments?.find((inv: any) => inv.id === bike.investmentId);
const planColors: Record<string, string> = {
silver: 'from-slate-400 to-slate-600',
gold: 'from-amber-400 to-amber-600',
platinum: 'from-purple-400 to-purple-600',
diamond: 'from-blue-400 to-blue-600',
};
const planBadges: Record<string, string> = {
silver: 'bg-slate-100 text-slate-600',
gold: 'bg-amber-100 text-amber-700',
platinum: 'bg-purple-100 text-purple-700',
diamond: 'bg-blue-100 text-blue-700',
};
const planBg: Record<string, string> = {
silver: 'bg-slate-50 border-slate-200',
gold: 'bg-amber-50 border-amber-200',
platinum: 'bg-purple-50 border-purple-200',
diamond: 'bg-blue-50 border-blue-200',
};
const rentalTypes: Record<string, { label: string; color: string }> = {
single_rent: { label: 'Single Rent', color: 'text-green-600' },
rent_to_own: { label: 'Rent to Own', color: 'text-blue-600' },
share_ev: { label: 'Share EV', color: 'text-purple-600' },
};
const statusConfig: Record<string, { bg: string; color: string }> = {
rented: { bg: 'bg-green-100', color: 'text-green-700' },
available: { bg: 'bg-blue-100', color: 'text-blue-700' },
maintenance: { bg: 'bg-amber-100', color: 'text-amber-700' },
retired: { bg: 'bg-slate-100', color: 'text-slate-600' },
};
const rentalInfo = rentalTypes[bike.rentalType || 'single_rent'];
const planType = investment?.planType || 'gold';
const status = statusConfig[bike.status] || statusConfig.available;
return (
<Link
key={bike.id}
href={`/bikes?bike=${bike.id}`}
className={`block bg-white rounded-xl border ${planBg[planType]} overflow-hidden hover:shadow-lg transition-all hover:scale-[1.02] cursor-pointer group`}
>
<div className={`h-2 bg-gradient-to-r ${planColors[planType]}`} />
<div className="p-4">
<div className="flex items-start gap-3 mb-3">
<div className={`w-12 h-12 rounded-xl bg-gradient-to-br ${planColors[planType]} flex items-center justify-center shadow-sm`}>
<Bike className="w-6 h-6 text-white" />
</div>
<div className="flex-1 min-w-0">
<h4 className="font-semibold text-slate-800 truncate">{bike.model}</h4>
<p className="text-sm text-slate-500">{bike.brand}</p>
</div>
<span className={`px-2 py-1 rounded-full text-xs font-medium ${status.bg} ${status.color} capitalize`}>
{bike.status}
</span>
</div>
<div>
<p className="font-semibold text-slate-700">{bike.model}</p>
<p className="text-xs text-slate-400">{bike.brand}</p>
<div className={`inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-medium ${planBadges[planType]} capitalize mb-3`}>
<span className={`w-2 h-2 rounded-full ${
planType === 'silver' ? 'bg-slate-500' :
planType === 'gold' ? 'bg-amber-500' :
planType === 'platinum' ? 'bg-purple-500' : 'bg-blue-500'
}`} />
{planType} Plan {investment?.planName || 'Investment'}
</div>
<div className="grid grid-cols-2 gap-2 mb-3">
<div className="bg-white/80 rounded-lg p-2">
<p className="text-xs text-slate-400">Plate</p>
<p className="text-sm font-medium text-slate-700 truncate">{bike.plateNumber.split('-').pop()}</p>
</div>
<div className="bg-white/80 rounded-lg p-2">
<p className="text-xs text-slate-400">Location</p>
<p className="text-sm font-medium text-slate-700 truncate">{bike.location}</p>
</div>
</div>
<div className="bg-white/80 rounded-lg p-3 space-y-2">
<div className="flex items-center justify-between">
<span className="text-xs text-slate-500">Rental Type</span>
<span className={`text-xs font-semibold ${rentalInfo.color}`}>{rentalInfo.label}</span>
</div>
<div className="flex items-center justify-between">
<span className="text-xs text-slate-500">Total Earnings</span>
<span className="text-sm font-bold text-green-600">{bike.totalEarnings?.toLocaleString() || 0}</span>
</div>
<div className="flex items-center justify-between">
<span className="text-xs text-slate-500">Battery</span>
<span className={`text-xs font-medium ${
bike.batteryLevel > 50 ? 'text-green-600' :
bike.batteryLevel > 20 ? 'text-amber-600' : 'text-red-600'
}`}>{bike.batteryLevel}%</span>
</div>
</div>
</div>
<span className={`text-xs font-medium px-2.5 py-1 rounded-full ${bikeStatusColors[bike.status]}`}>
{bike.status}
</span>
</div>
<div className="space-y-2 text-xs">
<div className="flex justify-between">
<span className="text-slate-500">Plate</span>
<span className="font-medium">{bike.plateNumber}</span>
</div>
<div className="flex justify-between">
<span className="text-slate-500">Location</span>
<span className="font-medium">{bike.location}</span>
</div>
<div className="flex justify-between">
<span className="text-slate-500">Battery</span>
<span className={`font-medium ${bike.batteryLevel > 50 ? 'text-green-600' : bike.batteryLevel > 20 ? 'text-amber-600' : 'text-red-600'}`}>{bike.batteryLevel}%</span>
</div>
<div className="flex justify-between">
<span className="text-slate-500">Purchase Price</span>
<span className="font-medium text-purple-600">{bike.purchasePrice?.toLocaleString() || 0}</span>
</div>
<div className="flex justify-between">
<span className="text-slate-500">Total Earnings</span>
<span className="font-medium text-green-600">{bike.totalEarnings?.toLocaleString() || 0}</span>
</div>
</div>
</div>
))}
</Link>
);
})}
{assignedBikes.length === 0 && (
<div className="col-span-full text-center py-8 text-slate-400">
<Bike className="w-12 h-12 mx-auto mb-2 opacity-50" />
<p>No bikes assigned yet</p>
<div className="col-span-full text-center py-12 border-2 border-dashed border-slate-200 rounded-xl">
<Bike className="w-12 h-12 mx-auto mb-3 text-slate-300" />
<p className="text-slate-500">No bikes assigned to this investor</p>
<p className="text-sm text-slate-400 mt-1">Bikes will appear here once assigned to investments</p>
</div>
)}
</div>
@@ -970,57 +1014,92 @@ Credit (Cr): Investor Liability ৳${newInvestment.totalInvestment.toLocaleStri
{activeTab === 'investments' && (
<div className="space-y-6">
<div className="flex items-center justify-between">
<h3 className="font-semibold text-slate-800">Investment Plans</h3>
<button onClick={() => setShowCreateInvestmentModal(true)} className="py-2 px-3 bg-investor text-white rounded-lg text-sm font-medium hover:bg-investor-dark flex items-center gap-1">
<Plus className="w-4 h-4" /> Add Investment
<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>
<button onClick={() => setShowCreateInvestmentModal(true)} className="py-2 px-4 bg-investor text-white rounded-lg text-sm font-medium hover:bg-investor-dark flex items-center gap-2">
<Plus className="w-4 h-4" /> Create Investment
</button>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{investor.investments?.map((inv) => (
<div key={inv.id} className="bg-slate-50 rounded-xl p-4">
<div className="flex items-start justify-between mb-3">
<div>
<p className="font-semibold text-slate-700">{inv.planName}</p>
<p className="text-xs text-slate-400">{inv.planType} Plan</p>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{investor.investments?.map((inv) => {
const planConfig: Record<string, { bg: string; border: string; icon: string }> = {
silver: { bg: 'bg-slate-100', border: 'border-slate-300', icon: 'text-slate-500' },
gold: { bg: 'bg-amber-100', border: 'border-amber-300', icon: 'text-amber-500' },
platinum: { bg: 'bg-purple-100', border: 'border-purple-300', icon: 'text-purple-500' },
diamond: { bg: 'bg-blue-100', border: 'border-blue-300', icon: 'text-blue-500' },
};
const style = planConfig[inv.planType] || planConfig.gold;
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>
<span className={`text-xs font-medium px-2.5 py-1 rounded-full ${planColors[inv.planType]}`}>
{inv.status}
</span>
</div>
<div className="grid grid-cols-2 gap-2 text-xs mb-3">
<div className="bg-white rounded-lg p-2">
<p className="text-slate-400">Investment</p>
<p className="font-medium text-purple-600">{inv.totalInvestment.toLocaleString()}</p>
</div>
<div className="bg-white rounded-lg p-2">
<p className="text-slate-400">Monthly Return</p>
<p className="font-medium text-green-600">{inv.monthlyReturn.toLocaleString()}</p>
</div>
<div className="bg-white rounded-lg p-2">
<p className="text-slate-400">Expected ROI</p>
<p className="font-medium text-slate-700">{inv.expectedRoi}%</p>
</div>
<div className="bg-white rounded-lg p-2">
<p className="text-slate-400">Actual Earned</p>
<p className="font-medium text-green-600">{inv.actualEarnings.toLocaleString()}</p>
<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={`/admin/investors/${investor.id}/investments/${inv.id}`} className="flex-1 py-2 text-sm font-medium text-slate-600 hover:bg-slate-50 rounded-lg border border-slate-200 text-center">
View
</Link>
<button className="flex-1 py-2 text-sm font-medium text-investor hover:bg-investor/10 rounded-lg border border-investor/30">Statement</button>
</div>
</div>
</div>
</div>
<div className="flex items-center justify-between text-xs">
<span className="text-slate-400">{inv.startDate} to {inv.endDate || 'Ongoing'}</span>
<span className="capitalize">{inv.paymentMethod}</span>
</div>
</div>
))}
{(!investor.investments || investor.investments.length === 0) && (
<div className="col-span-full text-center py-8 text-slate-400">
<TrendingUp className="w-12 h-12 mx-auto mb-2 opacity-50" />
<p>No investments yet</p>
<button className="mt-2 px-4 py-2 bg-investor text-white rounded-lg text-sm hover:bg-investor-dark">
Create First Investment
</button>
</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 for this investor</p>
<button onClick={() => setShowCreateInvestmentModal(true)} className="px-4 py-2 bg-investor text-white rounded-lg text-sm font-medium hover:bg-investor-dark">
<Plus className="w-4 h-4 inline mr-1" /> Create Investment
</button>
</div>
)}
</div>
)}
@@ -1455,15 +1534,46 @@ Credit (Cr): Investor Liability ৳${newInvestment.totalInvestment.toLocaleStri
{showCreateInvestmentModal && (
<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="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">
<h2 className="text-lg font-bold text-slate-800">Create New Investment</h2>
<div>
<h2 className="text-lg font-bold text-slate-800">Create New Investment</h2>
<p className="text-sm text-slate-500">Set up investment for {investor.name}</p>
</div>
<button onClick={() => setShowCreateInvestmentModal(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-4">
<div className="p-5 overflow-y-auto flex-1 space-y-5">
<div>
<label className="text-sm font-medium text-slate-600 mb-2 block">Select Plan Template</label>
<div className="grid grid-cols-2 gap-3">
{[
{ id: 'inv_demo_1', name: '1 Bike Plan', tier: 'Economy', evBasePrice: 200000, minQuantity: 1, duration: 12, maxInvestment: 1000000 },
{ id: 'inv_demo_2', name: '5 Bike Plan', tier: 'Standard', evBasePrice: 180000, minQuantity: 5, duration: 24, maxInvestment: 5000000 },
].map(plan => (
<button
key={plan.id}
onClick={() => {
setNewInvestment({
...newInvestment,
planName: plan.name,
planType: plan.tier.toLowerCase() as any,
totalInvestment: plan.evBasePrice * plan.minQuantity,
monthlyReturn: 0
});
}}
className={`p-4 rounded-lg border-2 text-left transition-all ${newInvestment.planName === plan.name ? 'border-investor bg-investor/5' : 'border-slate-200 hover:border-investor/50'}`}
>
<p className="font-semibold text-slate-800">{plan.name}</p>
<p className="text-xs text-slate-500">{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="grid grid-cols-2 gap-4">
<div>
<label className="text-sm font-medium text-slate-600 mb-1 block">Plan Name *</label>
@@ -1472,77 +1582,90 @@ Credit (Cr): Investor Liability ৳${newInvestment.totalInvestment.toLocaleStri
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="e.g., Gold EV Fleet 2024"
placeholder="Plan name"
/>
</div>
<div>
<label className="text-sm font-medium text-slate-600 mb-1 block">Plan Type *</label>
<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, expectedRoi: e.target.value === 'silver' ? 12 : e.target.value === 'gold' ? 15 : e.target.value === 'platinum' ? 18 : 20 })}
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"
>
<option value="silver">Silver (12% ROI)</option>
<option value="gold">Gold (15% ROI)</option>
<option value="platinum">Platinum (18% ROI)</option>
<option value="diamond">Diamond (20% ROI)</option>
<option value="silver">Silver</option>
<option value="gold">Gold</option>
<option value="platinum">Platinum</option>
<option value="diamond">Diamond</option>
</select>
</div>
</div>
<div>
<label className="text-sm font-medium text-slate-600 mb-1 block">Select Bikes</label>
<div className="grid grid-cols-2 gap-2 max-h-32 overflow-y-auto border border-slate-200 rounded-lg p-2">
{availableBikesForAssignment.map(bike => (
<label key={bike.id} className="flex items-center gap-2 p-2 hover:bg-slate-50 rounded cursor-pointer">
<input
type="checkbox"
checked={newInvestment.selectedBikeIds.includes(bike.id)}
onChange={(e) => {
if (e.target.checked) {
setNewInvestment({ ...newInvestment, selectedBikeIds: [...newInvestment.selectedBikeIds, bike.id] });
} else {
setNewInvestment({ ...newInvestment, selectedBikeIds: newInvestment.selectedBikeIds.filter(id => id !== bike.id) });
}
}}
className="rounded text-investor"
/>
<span className="text-sm">{bike.model} ({bike.plateNumber})</span>
</label>
))}
</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={200000} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-slate-50" />
</div>
<div>
<label className="text-sm font-medium text-slate-600 mb-1 block">Minimum Quantity (Bikes)</label>
<input type="number" value={1} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-slate-50" />
</div>
<div>
<label className="text-sm font-medium text-slate-600 mb-1 block">Min Investment ()</label>
<input type="number" value={200000} 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-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), monthlyReturn: Math.round(Number(e.target.value) * newInvestment.expectedRoi / 100 / 12) })}
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
placeholder="0"
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">Monthly Return ()</label>
<input
type="number"
value={newInvestment.monthlyReturn}
onChange={(e) => setNewInvestment({ ...newInvestment, monthlyReturn: Number(e.target.value) })}
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
placeholder="0"
/>
<label className="text-sm font-medium text-slate-600 mb-1 block">Max Investment ()</label>
<input type="number" value={1000000} 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="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={12} 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">Expected ROI (%)</label>
<input
type="number"
value={newInvestment.expectedRoi}
onChange={(e) => setNewInvestment({ ...newInvestment, expectedRoi: Number(e.target.value) })}
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
placeholder="15"
/>
<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">Early 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 - Jaiben's 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 an 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>
@@ -1569,7 +1692,7 @@ Credit (Cr): Investor Liability ৳${newInvestment.totalInvestment.toLocaleStri
<div className="grid grid-cols-2 gap-4">
<div>
<label className="text-sm font-medium text-slate-600 mb-1 block">Payment Method *</label>
<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 })}
@@ -1594,72 +1717,66 @@ Credit (Cr): Investor Liability ৳${newInvestment.totalInvestment.toLocaleStri
</div>
<div>
<label className="text-sm font-medium text-slate-600 mb-1 block">Notes</label>
<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="Additional notes..."
/>
</div>
{(() => {
const getDebitAccountDisplay = (method: string) => {
switch (method) {
case 'bank': return 'Bank - City Bank (1200)';
case 'cash': return 'Cash in Hand (1100)';
case 'mobile': return 'bKash Business (1300)';
case 'cheque': return 'Cheque Receivable (1410)';
default: return 'Bank - City Bank (1200)';
}
};
const debitDisplay = getDebitAccountDisplay(newInvestment.paymentMethod);
return (
<div className="bg-green-50 border border-green-200 rounded-lg p-4">
<div className="flex items-center gap-2 mb-3">
<TrendingUp className="w-5 h-5 text-green-600" />
<h4 className="font-semibold text-green-800">Auto-Journal Entry</h4>
<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.totalInvestment.toLocaleString()}</p>
</div>
<div className="text-sm text-green-700 space-y-2">
<p className="text-xs text-green-600 uppercase font-medium mb-2">Double EntryAccounting</p>
<div className="flex items-center justify-between bg-white rounded p-2 border border-green-100">
<div>
<p className="font-medium">Debit (Dr)</p>
<p className="text-xs text-green-600">{debitDisplay}</p>
</div>
<p className="font-bold text-green-700">{newInvestment.totalInvestment.toLocaleString()}</p>
<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 className="flex items-center justify-center py-1">
<div className="w-6 h-6 rounded-full bg-green-200 flex items-center justify-center">
<span className="text-green-600 text-xs"></span>
</div>
</div>
<div className="flex items-center justify-between bg-white rounded p-2 border border-green-100">
<div>
<p className="font-medium">Credit (Cr)</p>
<p className="text-xs text-green-600">Investor Liabilities (2200)</p>
</div>
<p className="font-bold text-green-700">{newInvestment.totalInvestment.toLocaleString()}</p>
</div>
<div className="mt-3 pt-2 border-t border-green-200">
<p className="text-xs text-green-600">Transaction Ref: Auto Generate</p>
</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={() => setShowCreateInvestmentModal(false)} className="px-4 py-2 border border-slate-200 text-slate-600 rounded-lg text-sm hover:bg-slate-50">Cancel</button>
<button onClick={() => setShowCreateInvestmentModal(false)} className="px-4 py-2 border border-slate-200 text-slate-600 rounded-lg text-sm hover:bg-slate-50">
Cancel
</button>
<button
onClick={handleCreateInvestment}
disabled={!newInvestment.planName || !newInvestment.totalInvestment}
className="px-4 py-2 bg-investor text-white rounded-lg text-sm hover:bg-investor-dark disabled:opacity-50 flex items-center gap-2"
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"
>
<TrendingUp className="w-4 h-4" /> Create Investment
<Plus className="w-4 h-4" /> Create Investment
</button>
</div>
</div>
@@ -2015,6 +2132,90 @@ Credit (Cr): Investor Liability ৳${newInvestment.totalInvestment.toLocaleStri
</div>
</div>
)}
{showInvestmentSuccessModal && lastCreatedInvestment && (
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4">
<div className="bg-white rounded-xl shadow-xl w-full max-w-lg">
<div className="p-5 border-b border-slate-100 flex items-center justify-between bg-green-50">
<div className="flex items-center gap-3">
<div className="w-12 h-12 bg-green-100 rounded-full flex items-center justify-center">
<Check className="w-6 h-6 text-green-600" />
</div>
<div>
<h2 className="text-lg font-bold text-green-800">Investment Created!</h2>
<p className="text-sm text-green-600">Auto-journal entry generated</p>
</div>
</div>
<button onClick={() => setShowInvestmentSuccessModal(false)} className="p-2 hover:bg-green-100 rounded-lg">
<X className="w-5 h-5 text-green-400" />
</button>
</div>
<div className="p-5 space-y-4">
<div className="bg-slate-50 rounded-lg p-4 space-y-2">
<div className="flex justify-between">
<span className="text-sm text-slate-500">Investment ID</span>
<span className="text-sm font-medium">#{lastCreatedInvestment.id.slice(-8)}</span>
</div>
<div className="flex justify-between">
<span className="text-sm text-slate-500">Transaction Ref</span>
<span className="text-sm font-medium">{lastCreatedInvestment.transactionId}</span>
</div>
<div className="flex justify-between">
<span className="text-sm text-slate-500">Amount</span>
<span className="text-sm font-bold text-green-600">{lastCreatedInvestment.totalInvestment.toLocaleString()}</span>
</div>
<div className="flex justify-between">
<span className="text-sm text-slate-500">Plan</span>
<span className="text-sm font-medium">{lastCreatedInvestment.planName}</span>
</div>
<div className="flex justify-between">
<span className="text-sm text-slate-500">Date</span>
<span className="text-sm font-medium">{lastCreatedInvestment.startDate}</span>
</div>
</div>
<div className="bg-green-50 border border-green-200 rounded-xl p-4">
<h4 className="text-sm font-semibold text-green-800 mb-3 flex items-center gap-2">
<FileText className="w-4 h-4" />
Auto-Journal Entry
</h4>
<div className="bg-white rounded-lg p-3 border border-green-100">
<div className="space-y-2">
<div className="flex items-center justify-between p-2 bg-green-50 rounded border border-green-200">
<div>
<p className="text-xs text-green-600 font-medium uppercase">Debit (Dr)</p>
<p className="text-sm font-medium text-slate-800">{lastCreatedInvestment.debitAccount?.name}</p>
<p className="text-xs text-slate-400">{lastCreatedInvestment.debitAccount?.code}</p>
</div>
<p className="font-bold text-green-700">{lastCreatedInvestment.totalInvestment.toLocaleString()}</p>
</div>
<div className="flex justify-center">
<div className="w-6 h-6 rounded-full bg-green-200 flex items-center justify-center">
<span className="text-green-600 text-xs"></span>
</div>
</div>
<div className="flex items-center justify-between p-2 bg-blue-50 rounded border border-blue-200">
<div>
<p className="text-xs text-blue-600 font-medium uppercase">Credit (Cr)</p>
<p className="text-sm font-medium text-slate-800">Investor Liabilities</p>
<p className="text-xs text-slate-400">2200</p>
</div>
<p className="font-bold text-blue-700">{lastCreatedInvestment.totalInvestment.toLocaleString()}</p>
</div>
</div>
</div>
</div>
</div>
<div className="p-5 border-t border-slate-100 flex justify-end gap-3">
<button onClick={() => setShowInvestmentSuccessModal(false)} className="px-4 py-2 bg-green-600 text-white rounded-lg text-sm font-medium hover:bg-green-700 flex items-center gap-2">
<Check className="w-4 h-4" /> Done
</button>
</div>
</div>
</div>
)}
</div>
);
}