From d8e82cef19787134b466b6b491b33e909a2d27a9 Mon Sep 17 00:00:00 2001 From: sazzadulalambd Date: Sat, 16 May 2026 10:20:12 +0600 Subject: [PATCH] feat: add payment tracking and manual payment submission to investment details and configure standalone deployment mode --- .gitignore | 5 +- deploy.sh | 20 +++ next.config.ts | 1 + server.js | 19 +++ src/app/investor/investments/[id]/page.tsx | 159 +++++++++++++++------ 5 files changed, 159 insertions(+), 45 deletions(-) create mode 100755 deploy.sh create mode 100644 server.js diff --git a/.gitignore b/.gitignore index 8a04b51..0456f35 100644 --- a/.gitignore +++ b/.gitignore @@ -49,4 +49,7 @@ next-env.d.ts **/public/worker-*.js.map **/docs -**/.docs \ No newline at end of file +**/.docs + +**/deploy.zip +**/deploy \ No newline at end of file diff --git a/deploy.sh b/deploy.sh new file mode 100755 index 0000000..aed7317 --- /dev/null +++ b/deploy.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +# Build the project +echo "Building project..." +npm run build + +# Create a deployment folder +echo "Preparing deployment files..." +mkdir -p deploy +cp -r .next deploy/ +cp -r public deploy/ +cp server.js deploy/ +cp package.json deploy/ +cp next.config.ts deploy/ + +# Optional: Zip the files +echo "Zipping deployment files..." +cd deploy && zip -r ../deploy.zip . && cd .. + +echo "Done! Upload 'deploy.zip' to your cPanel directory and follow the guide." diff --git a/next.config.ts b/next.config.ts index 8c97613..fb6add4 100644 --- a/next.config.ts +++ b/next.config.ts @@ -8,6 +8,7 @@ const withPWA = withPWAInit({ } as any); const nextConfig: NextConfig = { + output: 'standalone', images: { remotePatterns: [ { diff --git a/server.js b/server.js new file mode 100644 index 0000000..c95eb59 --- /dev/null +++ b/server.js @@ -0,0 +1,19 @@ +const { createServer } = require('http') +const { parse } = require('url') +const next = require('next') + +const dev = process.env.NODE_ENV !== 'production' +const app = next({ dev }) +const handle = app.getRequestHandler() + +const port = process.env.PORT || 3000 + +app.prepare().then(() => { + createServer((req, res) => { + const parsedUrl = parse(req.url, true) + handle(req, res, parsedUrl) + }).listen(port, (err) => { + if (err) throw err + console.log(`> Ready on http://localhost:${port}`) + }) +}) diff --git a/src/app/investor/investments/[id]/page.tsx b/src/app/investor/investments/[id]/page.tsx index 26ab247..71e8bef 100644 --- a/src/app/investor/investments/[id]/page.tsx +++ b/src/app/investor/investments/[id]/page.tsx @@ -13,6 +13,16 @@ import { investors } from '@/data/mockData'; import toast from 'react-hot-toast'; import InvestorNotification from '@/components/InvestorNotification'; +interface PaymentRecord { + id: string; + date: string; + amount: number; + installmentNo: number | null; + type: 'full' | 'partial' | 'installment'; + method: string; + status: 'completed' | 'pending'; +} + export default function InvestorInvestmentDetailPage({ params }: { params: Promise<{ id: string }> }) { const resolvedParams = use(params); const { id: investmentId } = resolvedParams; @@ -23,7 +33,12 @@ export default function InvestorInvestmentDetailPage({ params }: { params: Promi const [activeTab, setActiveTab] = useState('overview'); const [showPaymentModal, setShowPaymentModal] = useState(false); - const [selectedInstallment, setSelectedInstallment] = useState<'full' | '2' | '3'>('3'); + 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' }, + ]; if (!investment) { return ( @@ -42,6 +57,9 @@ export default function InvestorInvestmentDetailPage({ params }: { params: Promi ); } + const totalPaid = paymentHistory.reduce((sum, p) => p.status === 'completed' ? sum + p.amount : sum, 0); + const dueAmount = investment.totalInvestment - totalPaid; + const planConfig: Record = { silver: { badge: 'bg-slate-200 text-slate-700' }, gold: { badge: 'bg-amber-100 text-amber-700' }, @@ -70,16 +88,23 @@ export default function InvestorInvestmentDetailPage({ params }: { params: Promi { id: 'tx8', date: '2024-05-08', description: 'Rental Income - Bike CD-5678', amount: 500, status: 'completed' }, ]; - const dueAmount = investment.totalInvestment * 0.33; - const paidAmount = investment.totalInvestment * 0.67; - const handlePaymentSubmit = () => { - toast.success(`Payment of ৳${(dueAmount / (selectedInstallment === '2' ? 2 : 3)).toLocaleString()} initiated successfully!`); + 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; + } + toast.success(`Payment of ৳${amount.toLocaleString()} submitted successfully!`); setShowPaymentModal(false); + setPaymentAmount(''); }; return ( -
+
@@ -152,6 +177,7 @@ export default function InvestorInvestmentDetailPage({ params }: { params: Promi {[ { key: 'overview', label: 'Overview', icon: FileText, count: null }, { key: 'bikes', label: 'Bikes', icon: Bike, count: demoBikes.length }, + { key: 'payments', label: 'Payments', icon: Wallet, count: null }, { key: 'transactions', label: 'Transactions', icon: CreditCard, count: demoTransactions.length }, { key: 'statement', label: 'Statement', icon: Receipt, count: null }, @@ -346,6 +372,62 @@ export default function InvestorInvestmentDetailPage({ params }: { params: Promi
)} + + {activeTab === 'payments' && ( +
+
+

{paymentHistory.length} payments made

+ +
+ +
+ + + + + + + + + + + + + {paymentHistory.map((payment) => ( + + + + + + + + + ))} + +
DateTypeInstallmentMethodAmountStatus
{payment.date}{payment.type}{payment.installmentNo ? `#${payment.installmentNo}` : '-'}{payment.method}৳{payment.amount.toLocaleString()}{payment.status}
+
+ +
+ {paymentHistory.map((payment) => ( +
+
+
+

৳{payment.amount.toLocaleString()}

+

{payment.date} • {payment.method}

+
+ {payment.status} +
+
+ {payment.type} + {payment.installmentNo && Inst #{payment.installmentNo}} +
+
+ ))} +
+
+ )}
@@ -354,55 +436,44 @@ export default function InvestorInvestmentDetailPage({ params }: { params: Promi
-

Pay Due Amount

+

Make Payment

-
-

Total Due

-

৳{dueAmount.toLocaleString()}

+
+
+ Total Investment + ৳{investment.totalInvestment.toLocaleString()} +
+
+ Already Paid + ৳{totalPaid.toLocaleString()} +
+
+ Due Amount + ৳{dueAmount.toLocaleString()} +
-

Installment List

-
-
-
- -
-

Installment 1

-

Paid on Jan 15, 2024

-
-
- ৳{(investment.totalInvestment * 0.4).toLocaleString()} -
-
-
- -
-

Installment 2

-

Due: Jun 15, 2024

-
-
- ৳{(investment.totalInvestment * 0.3).toLocaleString()} -
-
-
- -
-

Installment 3

-

Due: Jul 15, 2024

-
-
- ৳{(investment.totalInvestment * 0.3).toLocaleString()} -
+ +
+ + 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" /> +
+
+ + +
-

Select Payment

+