refactor: simplify payment workflow by removing amount input and update sidebar profile navigation
This commit is contained in:
@@ -33,13 +33,14 @@ export default function InvestorInvestmentDetailPage({ params }: { params: Promi
|
||||
|
||||
const [activeTab, setActiveTab] = useState('overview');
|
||||
const [showPaymentModal, setShowPaymentModal] = useState(false);
|
||||
const [paymentAmount, setPaymentAmount] = useState('');
|
||||
|
||||
const paymentHistory: PaymentRecord[] = [
|
||||
{ id: 'pay1', date: '2024-01-15', amount: 400000, installmentNo: 1, type: 'installment', method: 'Bank Transfer', status: 'completed' },
|
||||
{ id: 'pay2', date: '2024-02-15', amount: 150000, installmentNo: null, type: 'partial', method: 'bKash', status: 'completed' },
|
||||
{ id: 'pay1', date: '2024-01-15', amount: Math.round((investment?.totalInvestment || 0) * 0.5), installmentNo: 1, type: 'installment', method: 'Bank Transfer', status: 'completed' },
|
||||
{ id: 'pay2', date: '2024-02-15', amount: Math.round((investment?.totalInvestment || 0) * 0.25), installmentNo: null, type: 'partial', method: 'bKash', status: 'completed' },
|
||||
];
|
||||
|
||||
|
||||
|
||||
if (!investment) {
|
||||
return (
|
||||
<div className="min-h-screen pb-20 lg:pb-0">
|
||||
@@ -89,18 +90,9 @@ export default function InvestorInvestmentDetailPage({ params }: { params: Promi
|
||||
];
|
||||
|
||||
const handlePaymentSubmit = () => {
|
||||
const amount = parseFloat(paymentAmount);
|
||||
if (!amount || amount <= 0) {
|
||||
toast.error('Please enter a valid amount');
|
||||
return;
|
||||
}
|
||||
if (amount > dueAmount) {
|
||||
toast.error('Amount exceeds due amount');
|
||||
return;
|
||||
}
|
||||
const amount = dueAmount;
|
||||
toast.success(`Payment of ৳${amount.toLocaleString()} submitted successfully!`);
|
||||
setShowPaymentModal(false);
|
||||
setPaymentAmount('');
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -146,7 +138,7 @@ export default function InvestorInvestmentDetailPage({ params }: { params: Promi
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 lg:grid-cols-4 gap-3 lg:gap-4 mb-6">
|
||||
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-3 lg:gap-4 mb-6">
|
||||
<div className="bg-white rounded-xl p-4 lg:p-5 border border-slate-200 shadow-sm">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<div className="w-8 h-8 bg-purple-100 rounded-lg flex items-center justify-center">
|
||||
@@ -156,6 +148,20 @@ export default function InvestorInvestmentDetailPage({ params }: { params: Promi
|
||||
</div>
|
||||
<p className="text-xl lg:text-2xl font-bold text-slate-800">৳{investment.totalInvestment.toLocaleString()}</p>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-xl p-4 lg:p-5 border border-slate-200 shadow-sm bg-green-50/10">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<div className="w-8 h-8 bg-green-100 rounded-lg flex items-center justify-center">
|
||||
<Check className="w-4 h-4 text-green-600" />
|
||||
</div>
|
||||
<p className="text-xs text-slate-500 font-medium">Already Paid</p>
|
||||
</div>
|
||||
<p className="text-xl lg:text-2xl font-bold text-green-600">৳{totalPaid.toLocaleString()}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<div className="bg-white rounded-xl p-4 lg:p-5 border border-slate-200 shadow-sm">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<div className="w-8 h-8 bg-green-100 rounded-lg flex items-center justify-center">
|
||||
@@ -165,18 +171,7 @@ export default function InvestorInvestmentDetailPage({ params }: { params: Promi
|
||||
</div>
|
||||
<p className="text-xl lg:text-2xl font-bold text-green-600">৳{investment.actualEarnings.toLocaleString()}</p>
|
||||
</div>
|
||||
<div className="bg-white rounded-xl p-4 lg:p-5 border border-amber-200 shadow-sm bg-amber-50/50">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<div className="w-8 h-8 bg-amber-200 rounded-lg flex items-center justify-center">
|
||||
<Clock className="w-4 h-4 text-amber-600" />
|
||||
</div>
|
||||
<p className="text-xs text-amber-700 font-medium">Due Amount</p>
|
||||
</div>
|
||||
<p className="text-xl lg:text-2xl font-bold text-amber-600">৳{dueAmount.toLocaleString()}</p>
|
||||
<button onClick={() => setShowPaymentModal(true)} className="mt-2 w-full py-1.5 bg-amber-500 text-white text-xs font-bold rounded-lg hover:bg-amber-600 transition-colors">
|
||||
Pay Now
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-xl p-4 lg:p-5 border border-slate-200 shadow-sm">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<div className="w-8 h-8 bg-blue-100 rounded-lg flex items-center justify-center">
|
||||
@@ -188,6 +183,7 @@ export default function InvestorInvestmentDetailPage({ params }: { params: Promi
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div className="bg-white rounded-xl border border-slate-200 shadow-sm overflow-hidden">
|
||||
<div className="flex overflow-x-auto border-b border-slate-100 justify-between sm:justify-start px-4 lg:px-0">
|
||||
{[
|
||||
@@ -282,40 +278,7 @@ export default function InvestorInvestmentDetailPage({ params }: { params: Promi
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-green-50 border border-green-200 rounded-xl p-5">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<h4 className="font-semibold text-green-800 flex items-center gap-2">
|
||||
<DollarSign className="w-4 h-4 text-green-600" /> Payment History
|
||||
</h4>
|
||||
<button onClick={() => setShowPaymentModal(true)} className="px-3 py-1.5 bg-green-600 text-white text-xs font-bold rounded-lg hover:bg-green-700 transition-colors">
|
||||
Make Payment
|
||||
</button>
|
||||
</div>
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-sm">
|
||||
<thead className="bg-white/50">
|
||||
<tr>
|
||||
<th className="px-3 py-2 text-left text-xs font-semibold text-slate-500">Date</th>
|
||||
<th className="px-3 py-2 text-left text-xs font-semibold text-slate-500">Type</th>
|
||||
<th className="px-3 py-2 text-left text-xs font-semibold text-slate-500">Method</th>
|
||||
<th className="px-3 py-2 text-right text-xs font-semibold text-slate-500">Amount</th>
|
||||
<th className="px-3 py-2 text-center text-xs font-semibold text-slate-500">Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-green-100">
|
||||
{paymentHistory.map((payment) => (
|
||||
<tr key={payment.id} className="bg-white/50">
|
||||
<td className="px-3 py-2 text-slate-600">{payment.date}</td>
|
||||
<td className="px-3 py-2"><span className="px-2 py-0.5 rounded text-[10px] font-bold uppercase bg-slate-100 text-slate-600">{payment.type}</span></td>
|
||||
<td className="px-3 py-2 text-slate-600">{payment.method}</td>
|
||||
<td className="px-3 py-2 text-right font-bold text-green-700">৳{payment.amount.toLocaleString()}</td>
|
||||
<td className="px-3 py-2 text-center"><span className="px-2 py-0.5 rounded text-[10px] font-bold uppercase bg-green-100 text-green-700">{payment.status}</span></td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -426,9 +389,12 @@ export default function InvestorInvestmentDetailPage({ params }: { params: Promi
|
||||
<div className="space-y-4">
|
||||
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3">
|
||||
<p className="text-sm text-slate-500">{paymentHistory.length} payments made</p>
|
||||
<button onClick={() => setShowPaymentModal(true)} className="px-4 py-2 bg-investor text-white rounded-lg text-sm font-medium hover:bg-investor-dark flex items-center gap-2">
|
||||
<DollarSign className="w-4 h-4" /> Make Payment
|
||||
</button>
|
||||
{dueAmount > 0 && (
|
||||
<button onClick={() => setShowPaymentModal(true)} className="px-4 py-2 bg-investor text-white rounded-lg text-sm font-medium hover:bg-investor-dark flex items-center gap-2">
|
||||
<DollarSign className="w-4 h-4" /> Make Payment
|
||||
</button>
|
||||
)}
|
||||
|
||||
</div>
|
||||
|
||||
<div className="overflow-x-auto">
|
||||
@@ -481,7 +447,7 @@ export default function InvestorInvestmentDetailPage({ params }: { params: Promi
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{showPaymentModal && (
|
||||
{showPaymentModal && dueAmount > 0 && (
|
||||
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4">
|
||||
<div className="bg-white rounded-2xl w-full max-w-md shadow-2xl">
|
||||
<div className="flex items-center justify-between p-5 border-b border-slate-100">
|
||||
@@ -506,19 +472,9 @@ export default function InvestorInvestmentDetailPage({ params }: { params: Promi
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-sm font-semibold text-slate-700 mb-2 block">Enter Amount</label>
|
||||
<div className="relative">
|
||||
<span className="absolute left-4 top-1/2 -translate-y-1/2 text-slate-400">৳</span>
|
||||
<input type="number" value={paymentAmount} onChange={(e) => setPaymentAmount(e.target.value)}
|
||||
placeholder="Enter amount"
|
||||
className="w-full pl-8 pr-4 py-3 border border-slate-200 rounded-xl text-lg font-semibold focus:outline-none focus:border-investor" />
|
||||
</div>
|
||||
<div className="flex gap-2 mt-2">
|
||||
<button onClick={() => setPaymentAmount(dueAmount.toString())} className="px-3 py-1 text-xs bg-slate-100 text-slate-600 rounded-lg hover:bg-slate-200">Full Due</button>
|
||||
<button onClick={() => setPaymentAmount((dueAmount / 2).toString())} className="px-3 py-1 text-xs bg-slate-100 text-slate-600 rounded-lg hover:bg-slate-200">Half</button>
|
||||
<button onClick={() => setPaymentAmount((dueAmount / 4).toString())} className="px-3 py-1 text-xs bg-slate-100 text-slate-600 rounded-lg hover:bg-slate-200">25%</button>
|
||||
</div>
|
||||
<div className="bg-amber-50 border border-amber-200 rounded-xl p-4 text-center">
|
||||
<p className="text-sm text-amber-600 mb-1">Fixed Payment Amount</p>
|
||||
<p className="text-3xl font-bold text-amber-700">৳{dueAmount.toLocaleString()}</p>
|
||||
</div>
|
||||
|
||||
<div className="pt-2">
|
||||
@@ -536,7 +492,7 @@ export default function InvestorInvestmentDetailPage({ params }: { params: Promi
|
||||
</div>
|
||||
|
||||
<button onClick={handlePaymentSubmit} className="w-full py-3 bg-investor text-white rounded-xl font-bold hover:bg-investor-dark transition-colors">
|
||||
Pay ৳{paymentAmount ? parseFloat(paymentAmount).toLocaleString() : '0'}
|
||||
Pay ৳{dueAmount.toLocaleString()}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -12,6 +12,8 @@ import {
|
||||
import { investors } from '@/data/mockData';
|
||||
import toast from 'react-hot-toast';
|
||||
import InvestorNotification from '@/components/InvestorNotification';
|
||||
import { logout } from '@/lib/auth';
|
||||
|
||||
|
||||
function SectionCard({ title, icon: Icon, children, headerBg = 'bg-slate-50', headerBorder = 'border-slate-100', editKey, editingSection, setEditingSection, onEdit, editForm, setEditForm }: {
|
||||
title: string; icon: any; children: React.ReactNode; headerBg?: string; headerBorder?: string; editKey?: string; editingSection?: string | null; setEditingSection?: (s: string | null) => void; onEdit?: () => void; editForm?: any; setEditForm?: any
|
||||
@@ -116,6 +118,12 @@ export default function InvestorProfilePage() {
|
||||
setShowDeleteMobileModal(false);
|
||||
};
|
||||
|
||||
const handleLogout = () => {
|
||||
logout();
|
||||
window.location.href = '/login';
|
||||
};
|
||||
|
||||
|
||||
const tabs = [
|
||||
{ id: 'personal', label: 'Personal Info', icon: User },
|
||||
{ id: 'nominee', label: 'Nominee & Emergency', icon: Heart },
|
||||
@@ -131,40 +139,43 @@ export default function InvestorProfilePage() {
|
||||
<div className="max-w-8xl mx-auto mb-12 lg:mb-0">
|
||||
{/* Profile Header */}
|
||||
<div className="bg-white rounded-xl shadow-sm border border-slate-100 overflow-hidden mb-4 sm:mb-6">
|
||||
<div className="p-4 sm:p-5 flex flex-col sm:flex-row items-start gap-4 sm:gap-5">
|
||||
<div className="relative group shrink-0">
|
||||
<div className="w-16 h-16 sm:w-20 sm:h-20 rounded-full bg-gradient-to-br from-purple-500 to-blue-500 flex items-center justify-center shadow-lg">
|
||||
<span className="text-xl sm:text-2xl font-bold text-white">{investor.name.charAt(0)}</span>
|
||||
<div className="p-4 sm:p-5 flex flex-col md:flex-row items-start justify-between gap-4 md:gap-5">
|
||||
<div className="flex flex-col sm:flex-row items-start gap-4 sm:gap-5 flex-1 w-full">
|
||||
<div className="relative group shrink-0">
|
||||
<div className="w-16 h-16 sm:w-20 sm:h-20 rounded-full bg-gradient-to-br from-purple-500 to-blue-500 flex items-center justify-center shadow-lg">
|
||||
<span className="text-xl sm:text-2xl font-bold text-white">{investor.name.charAt(0)}</span>
|
||||
</div>
|
||||
<label className="absolute bottom-0 right-0 w-7 h-7 sm:w-8 sm:h-8 bg-blue-600 rounded-full flex items-center justify-center cursor-pointer opacity-0 group-hover:opacity-100 transition-opacity shadow-lg border-2 border-white">
|
||||
<Camera className="w-3.5 h-3.5 sm:w-4 sm:h-4 text-white" />
|
||||
<input type="file" accept="image/*" className="hidden" />
|
||||
</label>
|
||||
</div>
|
||||
<label className="absolute bottom-0 right-0 w-7 h-7 sm:w-8 sm:h-8 bg-blue-600 rounded-full flex items-center justify-center cursor-pointer opacity-0 group-hover:opacity-100 transition-opacity shadow-lg border-2 border-white">
|
||||
<Camera className="w-3.5 h-3.5 sm:w-4 sm:h-4 text-white" />
|
||||
<input type="file" accept="image/*" className="hidden" />
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div className="flex-1 w-full">
|
||||
<div className="flex flex-wrap items-center gap-2 mb-2">
|
||||
<h1 className="text-xl sm:text-2xl font-bold text-slate-800">{investor.name}</h1>
|
||||
<span className={`px-2 py-0.5 sm:px-2.5 sm:py-1 rounded-full text-xs font-medium ${investor.kycStatus === 'verified' ? 'bg-green-100 text-green-700' : 'bg-amber-100 text-amber-700'}`}>
|
||||
<ShieldCheck className="w-3 h-3 inline mr-1" /> KYC {investor.kycStatus}
|
||||
</span>
|
||||
<span className={`px-2 py-0.5 sm:px-2.5 sm:py-1 rounded-full text-xs font-medium ${investor.status === 'active' ? 'bg-green-100 text-green-700' : 'bg-slate-100 text-slate-500'}`}>
|
||||
{investor.status}
|
||||
</span>
|
||||
<span className={`px-2 py-0.5 sm:px-2.5 sm:py-1 rounded-full text-xs font-medium ${investor.riskLevel === 'low' ? 'bg-green-100 text-green-700' : investor.riskLevel === 'medium' ? 'bg-amber-100 text-amber-700' : 'bg-red-100 text-red-700'}`}>
|
||||
Risk: {investor.riskLevel}
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-xs sm:text-sm text-slate-500 mb-2">{investor.id} • {investor.email}</p>
|
||||
<div className="flex flex-wrap items-center gap-x-3 gap-y-1">
|
||||
<span className="text-xs font-medium px-2 py-0.5 rounded bg-purple-50 text-purple-700">Ref: {investor.referralCode}</span>
|
||||
<span className="text-xs text-slate-400">{investor.totalReferrals} Referrals</span>
|
||||
<span className="text-xs text-slate-400">• {investor.investments?.length || 0} Investments</span>
|
||||
<span className="text-xs text-slate-400">• Referral Earnings: ৳{investor.referralEarnings?.toLocaleString() || 0}</span>
|
||||
<div className="flex-1 w-full">
|
||||
<div className="flex flex-wrap items-center gap-2 mb-2">
|
||||
<h1 className="text-xl sm:text-2xl font-bold text-slate-800">{investor.name}</h1>
|
||||
<span className={`px-2 py-0.5 sm:px-2.5 sm:py-1 rounded-full text-xs font-medium ${investor.kycStatus === 'verified' ? 'bg-green-100 text-green-700' : 'bg-amber-100 text-amber-700'}`}>
|
||||
<ShieldCheck className="w-3 h-3 inline mr-1" /> KYC {investor.kycStatus}
|
||||
</span>
|
||||
<span className={`px-2 py-0.5 sm:px-2.5 sm:py-1 rounded-full text-xs font-medium ${investor.status === 'active' ? 'bg-green-100 text-green-700' : 'bg-slate-100 text-slate-500'}`}>
|
||||
{investor.status}
|
||||
</span>
|
||||
<span className={`px-2 py-0.5 sm:px-2.5 sm:py-1 rounded-full text-xs font-medium ${investor.riskLevel === 'low' ? 'bg-green-100 text-green-700' : investor.riskLevel === 'medium' ? 'bg-amber-100 text-amber-700' : 'bg-red-100 text-red-700'}`}>
|
||||
Risk: {investor.riskLevel}
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-xs sm:text-sm text-slate-500 mb-2">{investor.id} • {investor.email}</p>
|
||||
<div className="flex flex-wrap items-center gap-x-3 gap-y-1">
|
||||
<span className="text-xs font-medium px-2 py-0.5 rounded bg-purple-50 text-purple-700">Ref: {investor.referralCode}</span>
|
||||
<span className="text-xs text-slate-400">{investor.totalReferrals} Referrals</span>
|
||||
<span className="text-xs text-slate-400">• {investor.investments?.length || 0} Investments</span>
|
||||
<span className="text-xs text-slate-400">• Referral Earnings: ৳{investor.referralEarnings?.toLocaleString() || 0}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div className="border-t border-slate-100 grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-6 divide-x divide-slate-100 bg-slate-50/50">
|
||||
<div className="p-3 sm:p-4 text-center">
|
||||
<p className="text-xs text-purple-600 font-medium">Invested</p>
|
||||
@@ -922,10 +933,14 @@ export default function InvestorProfilePage() {
|
||||
</div>
|
||||
<p className="text-xs text-slate-400">{session.location} • {session.time}</p>
|
||||
</div>
|
||||
{!session.current && <button className="px-3 py-1.5 text-xs font-medium text-red-600 hover:bg-red-50 rounded-lg">Revoke</button>}
|
||||
{!session.current ? (
|
||||
<button className="px-3 py-1.5 text-xs font-medium text-red-600 hover:bg-red-50 rounded-lg">Revoke</button>
|
||||
) : (
|
||||
<button onClick={handleLogout} className="px-3 py-1.5 text-xs font-medium text-red-600 hover:bg-red-50 rounded-lg">Log Out</button>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
<button className="w-full py-3 bg-red-50 text-red-600 rounded-lg text-sm font-medium hover:bg-red-100 flex items-center justify-center gap-2">
|
||||
<button onClick={handleLogout} className="w-full py-3 bg-red-50 text-red-600 rounded-lg text-sm font-medium hover:bg-red-100 flex items-center justify-center gap-2">
|
||||
<LogOut className="w-4 h-4" /> Sign Out All Devices
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -215,7 +215,7 @@ export default function Sidebar() {
|
||||
</nav>
|
||||
|
||||
<div className="absolute bottom-0 left-0 right-0 p-3 border-t border-slate-100 bg-white">
|
||||
<Link href="/admin/users/USR-001" className="flex items-center gap-3 px-3 py-2 hover:bg-slate-50 rounded-lg -mx-1">
|
||||
<Link href={isInvestor ? '/investor/profile' : '/admin/users/USR-001'} className="flex items-center gap-3 px-3 py-2 hover:bg-slate-50 rounded-lg -mx-1">
|
||||
<div className="w-8 h-8 rounded-full bg-accent-light flex items-center justify-center">
|
||||
<span className="text-sm font-bold text-accent">{userName.charAt(0).toUpperCase()}</span>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user