313 lines
14 KiB
TypeScript
313 lines
14 KiB
TypeScript
"use client";
|
|
|
|
import React, { useEffect, useState } from "react";
|
|
import { useParams, useRouter } from "next/navigation";
|
|
import axios from "axios";
|
|
import { ApiServerBaseUrl } from "@/utils/baseurl.utils";
|
|
|
|
const InvoiceDetail: React.FC = () => {
|
|
const params = useParams();
|
|
const router = useRouter();
|
|
const invoiceId = params?.id;
|
|
|
|
const [paymentData, setPaymentData] = useState<any>(null);
|
|
const [loading, setLoading] = useState(true);
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
useEffect(() => {
|
|
const fetchPaymentData = async () => {
|
|
try {
|
|
// Check if it's a trial invoice
|
|
if (invoiceId === "trial-inv") {
|
|
setPaymentData({
|
|
createdAt: new Date().toISOString(),
|
|
endDate: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString(),
|
|
status: "active",
|
|
email: "user@example.com", // You could fetch meaningful user email if stored in localStorage
|
|
planId: "Free Trial",
|
|
amount: 0
|
|
});
|
|
setLoading(false);
|
|
return;
|
|
}
|
|
|
|
const sessionId = localStorage.getItem("payment_session");
|
|
if (!sessionId) {
|
|
setError("No payment session found");
|
|
setLoading(false);
|
|
return;
|
|
}
|
|
|
|
const res: any = await axios.get(`${ApiServerBaseUrl}/payment/details`, {
|
|
params: { session_id: sessionId },
|
|
});
|
|
|
|
setPaymentData(res.data.data);
|
|
} catch (err: any) {
|
|
setError(err.response?.data?.error || "Failed to fetch payment details");
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
fetchPaymentData();
|
|
}, [invoiceId]);
|
|
|
|
const invoice = paymentData
|
|
? {
|
|
number: `#${invoiceId}`,
|
|
issueDate: paymentData.createdAt
|
|
? new Date(paymentData.createdAt).toLocaleDateString()
|
|
: "N/A",
|
|
validTill: paymentData.endDate
|
|
? new Date(paymentData.endDate).toLocaleDateString()
|
|
: "N/A",
|
|
status: paymentData.status || "pending",
|
|
issuedFor: paymentData.email || "",
|
|
customerId: "",
|
|
plan: paymentData.planId || "N/A",
|
|
bankName: "Bank of Canada",
|
|
accountNo: "1234567890",
|
|
country: "Canada",
|
|
items: [
|
|
{
|
|
sno: 1,
|
|
plan: paymentData.planId || "N/A",
|
|
qty: 1,
|
|
price: paymentData.amount,
|
|
amount: paymentData.amount,
|
|
},
|
|
],
|
|
subtotal: paymentData.amount,
|
|
tax: 0,
|
|
grandTotal: paymentData.amount,
|
|
createdAt: paymentData.createdAt,
|
|
}
|
|
: null;
|
|
|
|
const handlePrint = () => window.print();
|
|
|
|
if (loading) {
|
|
return (
|
|
<div className="flex items-center justify-center min-h-screen text-gray-400">
|
|
Loading invoice details...
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (error || !invoice) {
|
|
return (
|
|
<div className="flex flex-col items-center justify-center min-h-screen text-center px-4">
|
|
<p className="text-red-500 mb-4">{error || "Invoice not found"}</p>
|
|
<button
|
|
onClick={() => router.back()}
|
|
className="mt-4 px-6 py-3 rounded-xl font-bold bg-gradient-to-r from-[#4361ee] to-[#c026d3] text-white"
|
|
>
|
|
Go Back
|
|
</button>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="relative min-h-screen px-4 py-10 overflow-hidden bg-[#111111]">
|
|
|
|
{/* ===================== PRICING-PAGE BACKGROUND GLOWS ===================== */}
|
|
|
|
<div className="absolute top-[180px] left-52 w-[100px] h-[100px]
|
|
bg-[#1d8be0] rounded-full blur-2xl opacity-[1.5] animate-zoomslow"></div>
|
|
|
|
<div className="absolute top-10 left-0 w-[60px] h-[60px]
|
|
bg-[#6cb655] rounded-full blur-2xl opacity-[1.5] animate-zoomslower"></div>
|
|
|
|
<div className="absolute -left-[80px] bottom-[140px] w-[100px] h-[200px]
|
|
bg-[#db21d9] blur-3xl opacity-1 animate-zoomslow"></div>
|
|
|
|
<div className="absolute bottom-20 left-[440px] w-[60px] h-[60px]
|
|
bg-[#db21d9] rounded-full blur-xl opacity-80 -translate-x-1/2 translate-y-1/2"></div>
|
|
|
|
<div className="absolute top-[100px] right-[260px] w-[100px] h-[100px]
|
|
bg-[#f28f50] rounded-full blur-2xl opacity-80 animate-zoomfast"></div>
|
|
|
|
<div className="absolute top-10 right-0 w-[60px] h-[60px]
|
|
bg-[#6cb655] rounded-full blur-2xl opacity-[1.5] animate-zoomslower"></div>
|
|
|
|
<div className="absolute bottom-20 right-[180px] w-[80px] h-[80px]
|
|
bg-[#783e8d] rounded-full blur-2xl opacity-80 animate-zoomslow"></div>
|
|
|
|
<div className="absolute top-[280px] -right-[20px] w-[160px] h-[160px]
|
|
bg-[#f1b74d] rounded-full blur-2xl opacity-1 animate-zoomslower"></div>
|
|
|
|
{/* ===================== PAGE CONTENT ===================== */}
|
|
|
|
<div className="relative z-10 max-w-5xl mx-auto">
|
|
|
|
{/* Back Button */}
|
|
<button
|
|
onClick={() => router.back()}
|
|
className="mb-6 flex items-center gap-2 text-gray-400 hover:text-white transition-colors"
|
|
>
|
|
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
|
|
</svg>
|
|
Back to Account Settings
|
|
</button>
|
|
|
|
{/* Invoice Card */}
|
|
<div className="glass-card rounded-2xl p-8 lg:p-12">
|
|
<div className="flex justify-between items-start mb-12">
|
|
<div>
|
|
<h1 className="text-3xl font-extrabold text-white mb-2">INVOICE</h1>
|
|
|
|
<div className="flex items-center gap-3 mb-2">
|
|
<h3 className="text-sm font-semibold text-gray-400">Invoice No:</h3>
|
|
<p className="text-white font-bold text-lg">{invoice.number}</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="text-right">
|
|
<div className="flex items-center gap-2 mb-4">
|
|
<div className="w-12 h-12">
|
|
<img src="/assets/images/logo_sb.png" alt="logo" className="inline w-12" />
|
|
</div>
|
|
<div>
|
|
<h2 className="text-xl font-bold text-white">SOCIAL BUDDY</h2>
|
|
<p className="text-xs text-gray-400 uppercase tracking-wider">Social Media Manager</p>
|
|
</div>
|
|
</div>
|
|
|
|
<p className="text-sm text-gray-400">Social Buddy</p>
|
|
<p className="text-sm text-gray-400">support@socialbuddy.com</p>
|
|
<p className="text-sm text-gray-400">+1-555-SOCIAL-1</p>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Invoice info */}
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 mb-12">
|
|
<div className="space-y-4">
|
|
<div>
|
|
<div className="flex items-center gap-3 mb-2">
|
|
<h3 className="text-sm font-semibold text-gray-400">Issue For:</h3>
|
|
<p className="text-white">{invoice.createdAt}</p>
|
|
</div>
|
|
|
|
<p className="text-gray-400 text-sm">Customer ID: N/A</p>
|
|
<p className="text-gray-400 text-sm">Plan: {invoice.plan}</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="space-y-4 md:text-right">
|
|
<div className="flex items-center gap-3">
|
|
<h3 className="text-sm font-semibold text-gray-400">Issue Date:</h3>
|
|
<p className="text-white">{invoice.issueDate}</p>
|
|
</div>
|
|
|
|
<div className="flex items-center gap-3">
|
|
<h3 className="text-sm font-semibold text-gray-400">Valid Till:</h3>
|
|
<p className="text-white">{invoice.validTill}</p>
|
|
</div>
|
|
|
|
<div className="flex items-center gap-3">
|
|
<h3 className="text-sm font-semibold text-gray-400">Status:</h3>
|
|
<span className="px-4 py-1 rounded-full bg-yellow-500/20 text-yellow-400 text-sm font-semibold">
|
|
{invoice.status}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Table */}
|
|
<div className="mb-12 overflow-x-auto">
|
|
<table className="w-full">
|
|
<thead>
|
|
<tr className="border-b border-white/10">
|
|
<th className="py-4 px-4 text-gray-400 text-sm">S.NO</th>
|
|
<th className="py-4 px-4 text-gray-400 text-sm">PLAN</th>
|
|
<th className="py-4 px-4 text-gray-400 text-sm text-center">QTY</th>
|
|
<th className="py-4 px-4 text-gray-400 text-sm text-right">PRICE</th>
|
|
<th className="py-4 px-4 text-gray-400 text-sm text-right">AMOUNT</th>
|
|
</tr>
|
|
</thead>
|
|
|
|
<tbody>
|
|
{invoice.items.map((item) => (
|
|
<tr key={item.sno} className="border-b border-white/5">
|
|
<td className="py-4 px-4 text-white">{item.sno}</td>
|
|
<td className="py-4 px-4 text-white">{item.plan}</td>
|
|
<td className="py-4 px-4 text-center text-white">{item.qty}</td>
|
|
<td className="py-4 px-4 text-right text-white">${item.price.toFixed(2)}</td>
|
|
<td className="py-4 px-4 text-right text-white font-semibold">
|
|
${item.amount.toFixed(2)}
|
|
</td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
{/* Totals */}
|
|
<div className="flex justify-end mb-12">
|
|
<div className="w-full md:w-1/2 lg:w-1/3 space-y-3">
|
|
<div className="flex justify-between py-2 border-b border-white/10">
|
|
<span className="text-gray-400">Subtotal:</span>
|
|
<span className="text-white font-semibold">${invoice.subtotal.toFixed(2)}</span>
|
|
</div>
|
|
|
|
<div className="flex justify-between py-2 border-b border-white/10">
|
|
<span className="text-gray-400">Tax:</span>
|
|
<span className="text-white font-semibold">${invoice.tax.toFixed(2)}</span>
|
|
</div>
|
|
|
|
<div className="flex justify-between py-3 border-t border-white/20">
|
|
<span className="text-white font-bold text-lg">Grand Total:</span>
|
|
<span className="text-white font-bold text-xl">
|
|
${invoice.grandTotal.toFixed(2)}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Print Button */}
|
|
<div className="flex justify-end">
|
|
<button
|
|
onClick={handlePrint}
|
|
className="px-6 py-3 rounded-xl font-bold bg-gradient-to-r from-[#4361ee] to-[#c026d3] text-white flex items-center gap-2"
|
|
>
|
|
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2}
|
|
d="M17 17h2a2 2 0 002-2v-4a2 2 0 00-2-2H5a2 2 0 00-2 2v4a2 2 0 002 2h2m2 4h6a2 2 0 002-2v-4a2 2 0 00-2-2H9a2 2 0 00-2 2v4a2 2 0 002 2zm8-12V5a2 2 0 00-2-2H9a2 2 0 00-2 2v4h10z"
|
|
/>
|
|
</svg>
|
|
Print
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Print Styles */}
|
|
<style jsx global>{`
|
|
@media print {
|
|
body {
|
|
background: white !important;
|
|
}
|
|
.glass-card {
|
|
background: white !important;
|
|
border: 1px solid #e5e7eb !important;
|
|
box-shadow: none !important;
|
|
}
|
|
button {
|
|
display: none !important;
|
|
}
|
|
.text-white {
|
|
color: black !important;
|
|
}
|
|
.text-gray-400 {
|
|
color: #6b7280 !important;
|
|
}
|
|
}
|
|
`}</style>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default InvoiceDetail; |