feat: implement investor withdrawal workflow and add new navigation routes for investments, plans, and withdrawals

This commit is contained in:
sazzadulalambd
2026-05-15 01:31:21 +06:00
parent 1fd8c5153a
commit c1ab1eb0a3
7 changed files with 1288 additions and 81 deletions

View File

@@ -0,0 +1,394 @@
'use client';
import { useState } from 'react';
import Link from 'next/link';
import { useRouter } from 'next/navigation';
import {
ArrowLeft, Zap, Shield, TrendingUp, Check, Info,
Calendar, DollarSign, CreditCard, FileText, ChevronRight,
AlertCircle, ArrowDown, Wallet, Clock, Activity
} from 'lucide-react';
import toast from 'react-hot-toast';
const PLAN_TEMPLATES = [
{
id: '1bike',
name: '1 Bike Plan',
description: 'Investment plan for 1 bike - perfect for small investors',
evBasePrice: 200000,
minQty: 1,
minInvestment: 200000,
maxInvestment: 1000000,
duration: 12,
lockIn: 3,
exitPenalty: 10,
ficoShare: { single: 45, own: 55, ev: 60 },
icon: Zap,
color: 'bg-blue-600',
lightColor: 'bg-blue-50 text-blue-700'
},
{
id: '5bike',
name: '5 Bike Plan',
description: 'Perfect for established investors looking for better returns',
evBasePrice: 180000,
minQty: 5,
minInvestment: 900000,
maxInvestment: 5000000,
duration: 24,
lockIn: 6,
exitPenalty: 15,
ficoShare: { single: 40, own: 50, ev: 55 },
icon: TrendingUp,
color: 'bg-purple-600',
lightColor: 'bg-purple-50 text-purple-700'
}
];
export default function NewInvestmentPage() {
const router = useRouter();
const [step, setStep] = useState<'select' | 'form'>('select');
const [selectedTemplate, setSelectedTemplate] = useState<typeof PLAN_TEMPLATES[0] | null>(null);
const [formData, setFormData] = useState({
planName: '',
planType: 'Gold',
investmentAmount: 0,
initialPayment: 0,
startDate: new Date().toISOString().split('T')[0],
endDate: '',
paymentMethod: 'Bank Transfer',
transactionRef: '',
description: ''
});
const handleSelectTemplate = (template: typeof PLAN_TEMPLATES[0]) => {
setSelectedTemplate(template);
setFormData({
...formData,
planName: template.name,
investmentAmount: template.minInvestment,
initialPayment: Math.floor(template.minInvestment * 0.5) // Default 50% initial
});
setStep('form');
};
const handleCreateInvestment = (e: React.FormEvent) => {
e.preventDefault();
if (formData.investmentAmount < (selectedTemplate?.minInvestment || 0)) {
toast.error(`Minimum investment for this plan is ৳${selectedTemplate?.minInvestment.toLocaleString()}`);
return;
}
toast.success('Investment request created successfully! Admin will review and assign bikes.');
router.push('/investor/plans');
};
return (
<div className="p-4 lg:p-6 max-w-5xl mx-auto mb-20 lg:mb-0">
<div className="flex items-center gap-4 mb-8">
<button
onClick={() => step === 'form' ? setStep('select') : router.back()}
className="p-2 hover:bg-slate-100 rounded-xl transition-colors border border-slate-200"
>
<ArrowLeft className="w-5 h-5 text-slate-600" />
</button>
<div>
<h1 className="text-xl lg:text-2xl font-extrabold text-slate-800">
{step === 'select' ? 'Select Investment Plan' : 'Configure New Investment'}
</h1>
<p className="text-sm text-slate-500">
{step === 'select' ? 'Choose a template to start' : `Set up investment under ${selectedTemplate?.name}`}
</p>
</div>
</div>
{step === 'select' && (
<div className="grid md:grid-cols-2 gap-6">
{PLAN_TEMPLATES.map((plan) => {
const Icon = plan.icon;
return (
<div
key={plan.id}
className="bg-white rounded-2xl border-2 border-slate-100 p-6 hover:border-investor transition-all group cursor-pointer shadow-sm hover:shadow-md"
onClick={() => handleSelectTemplate(plan)}
>
<div className={`w-14 h-14 rounded-2xl mb-6 flex items-center justify-center ${plan.lightColor}`}>
<Icon className="w-7 h-7" />
</div>
<h3 className="text-xl font-extrabold text-slate-800 mb-2">{plan.name}</h3>
<p className="text-sm text-slate-500 mb-6 leading-relaxed">{plan.description}</p>
<div className="space-y-3 mb-8">
<div className="flex justify-between items-center text-sm">
<span className="text-slate-400 font-medium">Min Investment</span>
<span className="text-slate-800 font-extrabold">{plan.minInvestment.toLocaleString()}</span>
</div>
<div className="flex justify-between items-center text-sm">
<span className="text-slate-400 font-medium">EV Base Price</span>
<span className="text-slate-800 font-extrabold">{plan.evBasePrice.toLocaleString()}</span>
</div>
<div className="flex justify-between items-center text-sm">
<span className="text-slate-400 font-medium">Duration</span>
<span className="text-slate-800 font-extrabold">{plan.duration} Months</span>
</div>
</div>
<button className="w-full py-3.5 bg-slate-50 text-slate-800 rounded-xl font-bold group-hover:bg-investor group-hover:text-white transition-all flex items-center justify-center gap-2">
Select Template <ChevronRight className="w-4 h-4" />
</button>
</div>
);
})}
</div>
)}
{step === 'form' && selectedTemplate && (
<form onSubmit={handleCreateInvestment} className="space-y-6">
<div className="grid lg:grid-cols-3 gap-6">
<div className="lg:col-span-2 space-y-6">
{/* Core Configuration */}
<div className="bg-white rounded-2xl border border-slate-200 p-6 shadow-sm">
<h3 className="text-lg font-bold text-slate-800 mb-6 flex items-center gap-2">
<FileText className="w-5 h-5 text-investor" /> Plan Configuration
</h3>
<div className="grid md:grid-cols-2 gap-5 mb-6">
<div>
<label className="block text-sm font-bold text-slate-700 mb-2">Plan Name *</label>
<input
type="text"
value={formData.planName}
onChange={(e) => setFormData({...formData, planName: e.target.value})}
className="w-full px-4 py-3 bg-slate-50 border border-slate-200 rounded-xl text-sm focus:ring-2 focus:ring-investor/20 focus:border-investor outline-none transition-all font-medium"
placeholder="e.g. My First Bike"
required
/>
</div>
<div>
<label className="block text-sm font-bold text-slate-700 mb-2">Plan Type</label>
<select
value={formData.planType}
onChange={(e) => setFormData({...formData, planType: e.target.value})}
className="w-full px-4 py-3 bg-slate-50 border border-slate-200 rounded-xl text-sm focus:ring-2 focus:ring-investor/20 focus:border-investor outline-none transition-all font-medium"
>
<option>Gold</option>
<option>Silver</option>
<option>Platinum</option>
<option>Diamond</option>
</select>
</div>
</div>
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 p-4 bg-slate-50 rounded-xl border border-slate-100 mb-6 text-center">
<div>
<p className="text-[10px] text-slate-400 font-bold uppercase mb-1">EV Base Price</p>
<p className="text-sm font-bold text-slate-800">{selectedTemplate.evBasePrice.toLocaleString()}</p>
</div>
<div>
<p className="text-[10px] text-slate-400 font-bold uppercase mb-1">Min Qty</p>
<p className="text-sm font-bold text-slate-800">{selectedTemplate.minQty} Bike(s)</p>
</div>
<div>
<p className="text-[10px] text-slate-400 font-bold uppercase mb-1">Min Invest</p>
<p className="text-sm font-bold text-slate-800">{selectedTemplate.minInvestment.toLocaleString()}</p>
</div>
<div>
<p className="text-[10px] text-slate-400 font-bold uppercase mb-1">Max Invest</p>
<p className="text-sm font-bold text-slate-800">{selectedTemplate.maxInvestment.toLocaleString()}</p>
</div>
</div>
<div className="grid md:grid-cols-2 gap-5">
<div>
<label className="block text-sm font-bold text-slate-700 mb-2">Investment Amount () *</label>
<input
type="number"
value={formData.investmentAmount}
onChange={(e) => setFormData({...formData, investmentAmount: Number(e.target.value)})}
className="w-full px-4 py-3 border border-slate-200 rounded-xl text-sm focus:ring-2 focus:ring-investor/20 focus:border-investor outline-none transition-all font-extrabold"
required
/>
</div>
<div>
<label className="block text-sm font-bold text-slate-700 mb-2">Initial Payment () *</label>
<input
type="number"
value={formData.initialPayment}
onChange={(e) => setFormData({...formData, initialPayment: Number(e.target.value)})}
className="w-full px-4 py-3 border border-investor rounded-xl text-sm focus:ring-2 focus:ring-investor/20 outline-none transition-all font-extrabold text-investor"
placeholder="Amount to pay now"
required
/>
<p className="text-[10px] text-slate-400 mt-1 font-medium">Pay part of your investment now to confirm</p>
</div>
</div>
</div>
{/* Profit Sharing Policy */}
<div className="bg-white rounded-2xl border border-slate-200 p-6 shadow-sm">
<h3 className="text-lg font-bold text-slate-800 mb-4 flex items-center gap-2">
<TrendingUp className="w-5 h-5 text-investor" /> FICO Share - Jaiben's Profit per Ride
</h3>
<p className="text-xs text-slate-500 mb-6 font-medium">Profit sharing when bikes are rented to end customers</p>
<div className="grid grid-cols-3 gap-4">
<div className="bg-slate-50 rounded-xl p-4 border border-slate-100 text-center">
<p className="text-[10px] text-slate-500 font-bold uppercase mb-1">Single Rent</p>
<p className="text-xl font-extrabold text-slate-800">{selectedTemplate.ficoShare.single}%</p>
</div>
<div className="bg-slate-50 rounded-xl p-4 border border-slate-100 text-center">
<p className="text-[10px] text-slate-500 font-bold uppercase mb-1">Rent to Own</p>
<p className="text-xl font-extrabold text-slate-800">{selectedTemplate.ficoShare.own}%</p>
</div>
<div className="bg-slate-50 rounded-xl p-4 border border-slate-100 text-center">
<p className="text-[10px] text-slate-500 font-bold uppercase mb-1">Share an EV</p>
<p className="text-xl font-extrabold text-slate-800">{selectedTemplate.ficoShare.ev}%</p>
</div>
</div>
</div>
{/* Auto-Journal Entry Visualization */}
<div className="bg-slate-900 rounded-2xl p-6 shadow-xl overflow-hidden relative">
<div className="absolute top-0 right-0 w-32 h-32 bg-investor/10 rounded-full blur-3xl -mr-16 -mt-16" />
<h3 className="text-lg font-bold text-white mb-6 flex items-center gap-2">
<Activity className="w-5 h-5 text-investor" /> Auto-Journal Entry (Draft)
</h3>
<div className="space-y-4 relative z-10">
<div className="flex justify-between items-center text-xs font-bold uppercase text-slate-400 mb-2 px-2">
<span>Account Details</span>
<span>Amount (৳)</span>
</div>
<div className="bg-white/5 border border-white/10 rounded-xl p-4 flex items-center justify-between">
<div>
<p className="text-[10px] text-green-400 font-bold uppercase mb-1">Debit (Dr)</p>
<p className="text-sm font-bold text-white">Bank - City Bank</p>
<p className="text-[10px] text-slate-500 font-mono">CODE: 1200</p>
</div>
<p className="text-lg font-extrabold text-green-400">৳{formData.investmentAmount.toLocaleString()}</p>
</div>
<div className="flex justify-center -my-2 relative z-20">
<div className="bg-investor w-8 h-8 rounded-full flex items-center justify-center shadow-lg">
<ArrowDown className="w-4 h-4 text-white" />
</div>
</div>
<div className="bg-white/5 border border-white/10 rounded-xl p-4 flex items-center justify-between">
<div>
<p className="text-[10px] text-blue-400 font-bold uppercase mb-1">Credit (Cr)</p>
<p className="text-sm font-bold text-white">Investor Liabilities</p>
<p className="text-[10px] text-slate-500 font-mono">CODE: 2200</p>
</div>
<p className="text-lg font-extrabold text-blue-400">৳{formData.investmentAmount.toLocaleString()}</p>
</div>
<div className="pt-4 border-t border-white/10 mt-2 flex justify-between items-center px-2">
<p className="text-[10px] text-slate-500 font-medium">Reference: <span className="text-slate-300 font-mono">INV/{new Date().getFullYear()}/AUTO-{Math.random().toString(36).substring(7).toUpperCase()}</span></p>
<p className="text-[10px] text-slate-500 font-medium tracking-tight uppercase">Status: <span className="text-amber-400">Draft - On Creation</span></p>
</div>
</div>
</div>
</div>
<div className="space-y-6">
{/* Time & Period */}
<div className="bg-white rounded-2xl border border-slate-200 p-6 shadow-sm">
<h3 className="text-base font-bold text-slate-800 mb-4 flex items-center gap-2">
<Calendar className="w-5 h-5 text-investor" /> Schedule
</h3>
<div className="space-y-4">
<div>
<label className="block text-[11px] font-bold text-slate-500 uppercase tracking-wider mb-2">Start Date *</label>
<input
type="date"
value={formData.startDate}
onChange={(e) => setFormData({...formData, startDate: e.target.value})}
className="w-full px-4 py-2.5 bg-slate-50 border border-slate-200 rounded-xl text-sm focus:ring-2 focus:ring-investor/20 outline-none font-bold"
required
/>
</div>
<div>
<label className="block text-[11px] font-bold text-slate-500 uppercase tracking-wider mb-2">End Date (Optional)</label>
<input
type="date"
value={formData.endDate}
onChange={(e) => setFormData({...formData, endDate: e.target.value})}
className="w-full px-4 py-2.5 bg-slate-50 border border-slate-200 rounded-xl text-sm focus:ring-2 focus:ring-investor/20 outline-none font-bold"
/>
</div>
<div className="p-3 bg-blue-50 rounded-xl border border-blue-100 flex items-start gap-3">
<AlertCircle className="w-4 h-4 text-blue-600 shrink-0 mt-0.5" />
<p className="text-[10px] text-blue-700 font-medium leading-relaxed">
Duration is fixed at <strong>{selectedTemplate.duration} months</strong> with a <strong>{selectedTemplate.lockIn} month</strong> lock-in period as per template.
</p>
</div>
</div>
</div>
{/* Payment Details */}
<div className="bg-white rounded-2xl border border-slate-200 p-6 shadow-sm">
<h3 className="text-base font-bold text-slate-800 mb-4 flex items-center gap-2">
<CreditCard className="w-5 h-5 text-investor" /> Payment
</h3>
<div className="space-y-4">
<div>
<label className="block text-[11px] font-bold text-slate-500 uppercase tracking-wider mb-2">Method</label>
<select
value={formData.paymentMethod}
onChange={(e) => setFormData({...formData, paymentMethod: e.target.value})}
className="w-full px-4 py-2.5 bg-slate-50 border border-slate-200 rounded-xl text-sm focus:ring-2 focus:ring-investor/20 outline-none font-bold"
>
<option>Bank Transfer</option>
<option>Mobile Banking</option>
<option>Cheque</option>
<option>Cash</option>
</select>
</div>
<div>
<label className="block text-[11px] font-bold text-slate-500 uppercase tracking-wider mb-2">Reference ID</label>
<input
type="text"
value={formData.transactionRef}
onChange={(e) => setFormData({...formData, transactionRef: e.target.value})}
className="w-full px-4 py-2.5 bg-slate-50 border border-slate-200 rounded-xl text-sm focus:ring-2 focus:ring-investor/20 outline-none font-bold"
placeholder="Auto-generated if empty"
/>
</div>
<div className="pt-4 border-t border-slate-100">
<div className="flex justify-between items-center mb-1">
<span className="text-xs text-slate-500 font-bold uppercase">Pay Now</span>
<span className="text-lg font-extrabold text-investor">৳{formData.initialPayment.toLocaleString()}</span>
</div>
<div className="flex justify-between items-center">
<span className="text-xs text-slate-500 font-bold uppercase">Balance Due</span>
<span className="text-sm font-bold text-slate-800">৳{(formData.investmentAmount - formData.initialPayment).toLocaleString()}</span>
</div>
</div>
</div>
</div>
{/* Actions */}
<div className="space-y-3">
<button
type="submit"
className="w-full py-4 bg-investor text-white rounded-2xl font-bold text-lg shadow-lg shadow-investor/30 hover:bg-investor-dark transition-all transform hover:-translate-y-1 active:scale-95"
>
Create Investment
</button>
<button
type="button"
onClick={() => setStep('select')}
className="w-full py-3 text-slate-600 font-bold text-sm hover:bg-slate-100 rounded-xl transition-colors"
>
Cancel & Back
</button>
</div>
</div>
</div>
</form>
)}
</div>
);
}