feat: implement radio-based payment selection UI and rename paymentOption to paymentType
This commit is contained in:
@@ -134,7 +134,7 @@ export default function InvestorDetailPage() {
|
|||||||
selectedBikeIds: [] as string[],
|
selectedBikeIds: [] as string[],
|
||||||
totalInvestment: 0,
|
totalInvestment: 0,
|
||||||
paidAmount: 0,
|
paidAmount: 0,
|
||||||
paymentOption: 'full' as 'full' | 'partial',
|
paymentType: 'full' as 'full' | 'partial',
|
||||||
monthlyReturn: 0,
|
monthlyReturn: 0,
|
||||||
expectedRoi: 15,
|
expectedRoi: 15,
|
||||||
startDate: new Date().toISOString().split('T')[0],
|
startDate: new Date().toISOString().split('T')[0],
|
||||||
@@ -233,7 +233,7 @@ export default function InvestorDetailPage() {
|
|||||||
selectedBikeIds: [],
|
selectedBikeIds: [],
|
||||||
totalInvestment: 0,
|
totalInvestment: 0,
|
||||||
paidAmount: 0,
|
paidAmount: 0,
|
||||||
paymentOption: 'full',
|
paymentType: 'full',
|
||||||
monthlyReturn: 0,
|
monthlyReturn: 0,
|
||||||
expectedRoi: 15,
|
expectedRoi: 15,
|
||||||
startDate: new Date().toISOString().split('T')[0],
|
startDate: new Date().toISOString().split('T')[0],
|
||||||
@@ -2234,7 +2234,7 @@ export default function InvestorDetailPage() {
|
|||||||
planType: plan.tier.toLowerCase() as any,
|
planType: plan.tier.toLowerCase() as any,
|
||||||
totalInvestment: plan.evBasePrice * plan.minQuantity,
|
totalInvestment: plan.evBasePrice * plan.minQuantity,
|
||||||
paidAmount: plan.evBasePrice * plan.minQuantity,
|
paidAmount: plan.evBasePrice * plan.minQuantity,
|
||||||
paymentOption: 'full',
|
paymentType: 'full',
|
||||||
monthlyReturn: 0
|
monthlyReturn: 0
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
@@ -2301,7 +2301,7 @@ export default function InvestorDetailPage() {
|
|||||||
setNewInvestment({
|
setNewInvestment({
|
||||||
...newInvestment,
|
...newInvestment,
|
||||||
totalInvestment: val,
|
totalInvestment: val,
|
||||||
paidAmount: newInvestment.paymentOption === 'full' ? val : Math.max(val * 0.5, newInvestment.paidAmount)
|
paidAmount: newInvestment.paymentType === 'full' ? val : Math.max(val * 0.5, newInvestment.paidAmount)
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-yellow-50"
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-yellow-50"
|
||||||
@@ -2328,6 +2328,8 @@ export default function InvestorDetailPage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className="bg-amber-50 border border-amber-200 rounded-xl p-4">
|
<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">
|
<h4 className="text-sm font-semibold text-amber-800 mb-3 flex items-center gap-2">
|
||||||
<TrendingUp className="w-4 h-4" />
|
<TrendingUp className="w-4 h-4" />
|
||||||
@@ -2370,6 +2372,61 @@ export default function InvestorDetailPage() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</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', paidAmount: newInvestment.totalInvestment })}
|
||||||
|
className="w-4 h-4 text-investor"
|
||||||
|
/>
|
||||||
|
<div className="flex-1">
|
||||||
|
<p className="font-medium text-slate-800 text-sm">Full Payment</p>
|
||||||
|
<p className="text-xs text-slate-500">Pay total amount at once</p>
|
||||||
|
</div>
|
||||||
|
<span className="text-lg font-bold text-green-600">৳{newInvestment.totalInvestment.toLocaleString()}</span>
|
||||||
|
</label>
|
||||||
|
<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', paidAmount: 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.paidAmount}
|
||||||
|
onChange={(e) => {
|
||||||
|
const val = Number(e.target.value);
|
||||||
|
if (val >= newInvestment.totalInvestment * 0.5 && val <= newInvestment.totalInvestment) {
|
||||||
|
setNewInvestment({ ...newInvestment, paidAmount: 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.paidAmount).toLocaleString()}</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div className="grid grid-cols-2 gap-4">
|
<div className="grid grid-cols-2 gap-4">
|
||||||
<div>
|
<div>
|
||||||
@@ -2447,54 +2504,7 @@ export default function InvestorDetailPage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-3">
|
|
||||||
<label className="text-sm font-medium text-slate-600 block">Payment Options</label>
|
|
||||||
<div className="grid grid-cols-2 gap-4">
|
|
||||||
<button
|
|
||||||
onClick={() => setNewInvestment({ ...newInvestment, paymentOption: 'full', paidAmount: newInvestment.totalInvestment })}
|
|
||||||
className={`p-4 rounded-xl border-2 text-left transition-all ${newInvestment.paymentOption === 'full' ? 'border-investor bg-investor/5' : 'border-slate-200 hover:border-slate-300'}`}
|
|
||||||
>
|
|
||||||
<div className="flex items-center justify-between mb-1">
|
|
||||||
<p className="font-bold text-slate-800">Full Payment</p>
|
|
||||||
{newInvestment.paymentOption === 'full' && <CheckCircle className="w-4 h-4 text-investor" />}
|
|
||||||
</div>
|
|
||||||
<p className="text-xs text-slate-500 mb-2">Pay total amount at once</p>
|
|
||||||
<p className="text-lg font-bold text-investor">৳{newInvestment.totalInvestment.toLocaleString()}</p>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button
|
|
||||||
onClick={() => setNewInvestment({ ...newInvestment, paymentOption: 'partial', paidAmount: Math.max(newInvestment.totalInvestment * 0.5, newInvestment.paidAmount) })}
|
|
||||||
className={`p-4 rounded-xl border-2 text-left transition-all ${newInvestment.paymentOption === 'partial' ? 'border-investor bg-investor/5' : 'border-slate-200 hover:border-slate-300'}`}
|
|
||||||
>
|
|
||||||
<div className="flex items-center justify-between mb-1">
|
|
||||||
<p className="font-bold text-slate-800">Partial Payment</p>
|
|
||||||
{newInvestment.paymentOption === 'partial' && <CheckCircle className="w-4 h-4 text-investor" />}
|
|
||||||
</div>
|
|
||||||
<p className="text-xs text-slate-500 mb-2">Pay initial amount (50% min)</p>
|
|
||||||
<p className="text-lg font-bold text-slate-800">Min ৳{(newInvestment.totalInvestment * 0.5).toLocaleString()}</p>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{newInvestment.paymentOption === 'partial' && (
|
|
||||||
<div className="bg-slate-50 p-4 rounded-lg border border-slate-200 mt-3 animate-in fade-in slide-in-from-top-2">
|
|
||||||
<label className="text-sm font-medium text-slate-700 mb-2 block">Enter Initial Payment Amount (৳)</label>
|
|
||||||
<div className="relative">
|
|
||||||
<span className="absolute left-3 top-1/2 -translate-y-1/2 text-slate-400">৳</span>
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
value={newInvestment.paidAmount}
|
|
||||||
min={newInvestment.totalInvestment * 0.5}
|
|
||||||
max={newInvestment.totalInvestment}
|
|
||||||
onChange={(e) => setNewInvestment({ ...newInvestment, paidAmount: Number(e.target.value) })}
|
|
||||||
className="w-full pl-8 pr-4 py-2 border border-slate-200 rounded-lg focus:ring-2 focus:ring-investor outline-none text-sm"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<p className="text-xs text-slate-400 mt-2 flex items-center gap-1">
|
|
||||||
<AlertTriangle className="w-3 h-3 text-amber-500" /> Remaining ৳{(newInvestment.totalInvestment - newInvestment.paidAmount).toLocaleString()} will be recorded as pending.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="p-5 border-t border-slate-100 flex justify-end gap-3">
|
<div className="p-5 border-t border-slate-100 flex justify-end gap-3">
|
||||||
|
|||||||
Reference in New Issue
Block a user