268 lines
17 KiB
JavaScript
268 lines
17 KiB
JavaScript
import React from 'react';
|
|
import { Head } from '@inertiajs/react';
|
|
import AdminLayout from '@/Layouts/AdminLayout';
|
|
import {
|
|
Download,
|
|
ArrowUpRight,
|
|
ArrowDownRight,
|
|
Calendar,
|
|
Filter,
|
|
Search,
|
|
CreditCard,
|
|
TrendingUp,
|
|
Users,
|
|
BadgeDollarSign,
|
|
CheckCircle2,
|
|
Clock,
|
|
FileText,
|
|
MoreHorizontal,
|
|
XCircle,
|
|
Building2,
|
|
Zap,
|
|
Globe
|
|
} from 'lucide-react';
|
|
import {
|
|
Table,
|
|
TableBody,
|
|
TableCell,
|
|
TableHead,
|
|
TableHeader,
|
|
TableRow,
|
|
} from '@/components/ui/table';
|
|
import {
|
|
Dialog,
|
|
DialogContent,
|
|
DialogHeader,
|
|
DialogTitle,
|
|
DialogFooter,
|
|
} from "@/components/ui/dialog";
|
|
|
|
export default function PaymentsIndex({ stats, payments: initialPayments }) {
|
|
const [selectedPayment, setSelectedPayment] = React.useState(null);
|
|
const [isDialogOpen, setIsDialogOpen] = React.useState(false);
|
|
|
|
const payments = initialPayments || [];
|
|
|
|
const analytics = [
|
|
{ title: 'Total Revenue', value: stats?.total_revenue ?? '0.00', icon: BadgeDollarSign, trend: 'Live', positive: true, color: 'emerald' },
|
|
{ title: 'Active Subscriptions', value: String(stats?.active_subscriptions ?? 0), icon: Users, trend: 'Live', positive: true, color: 'blue' },
|
|
{ title: 'Failed Payments', value: String(stats?.failed_payments ?? 0), icon: XCircle, trend: 'Live', positive: true, color: 'rose' },
|
|
{ title: 'Average Ticket', value: stats?.average_ticket ?? '0.00', icon: TrendingUp, trend: 'Live', positive: true, color: 'amber' },
|
|
];
|
|
|
|
const openDetails = (pay) => {
|
|
setSelectedPayment(pay);
|
|
setIsDialogOpen(true);
|
|
};
|
|
|
|
return (
|
|
<AdminLayout title="Payments">
|
|
<Head title="Payments" />
|
|
|
|
<div className="space-y-8">
|
|
{/* Stats Overview */}
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
|
{analytics.map((item, i) => (
|
|
<div key={i} className="bg-white p-6 rounded-2xl border border-slate-200 shadow-sm hover:shadow-md transition-shadow">
|
|
<div className="flex items-center justify-between mb-4">
|
|
<div className={`w-12 h-12 rounded-xl flex items-center justify-center bg-${item.color}-50 text-${item.color}-600 shadow-inner`}>
|
|
<item.icon className="w-6 h-6" />
|
|
</div>
|
|
<div className={`flex items-center space-x-1 px-2 py-1 rounded-full text-[10px] font-black uppercase tracking-tight ${
|
|
item.positive ? 'bg-emerald-50 text-emerald-600' : 'bg-rose-50 text-rose-600'
|
|
}`}>
|
|
{item.positive ? <ArrowUpRight className="w-3 h-3" /> : <ArrowDownRight className="w-3 h-3" />}
|
|
<span>{item.trend}</span>
|
|
</div>
|
|
</div>
|
|
<h3 className="text-sm font-semibold text-slate-500 mb-1">{item.title}</h3>
|
|
<div className="flex items-baseline space-x-2">
|
|
<span className="text-2xl font-black text-slate-900 tracking-tight">{item.title.includes('Revenue') || item.title.includes('Ticket') ? 'AED ' : ''}{item.value}</span>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
|
|
{/* Main Content Area */}
|
|
<div className="bg-white rounded-2xl border border-slate-200 shadow-sm overflow-hidden">
|
|
<div className="p-6 border-b border-slate-100 flex flex-col md:flex-row md:items-center justify-between gap-4">
|
|
<div>
|
|
<h2 className="text-lg font-black text-slate-900 tracking-tight">Recent Transactions</h2>
|
|
<p className="text-xs text-slate-500 font-medium uppercase tracking-widest mt-1">Audit log of all employer subscription payments</p>
|
|
</div>
|
|
<div className="flex items-center space-x-3">
|
|
<div className="relative">
|
|
<Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-slate-400" />
|
|
<input
|
|
type="text"
|
|
placeholder="Search by Employer..."
|
|
className="pl-10 pr-4 py-2 bg-slate-50 border border-slate-100 rounded-lg text-xs font-bold focus:ring-4 focus:ring-teal-500/10 outline-none w-48 lg:w-64"
|
|
/>
|
|
</div>
|
|
<button className="p-2.5 bg-slate-50 text-slate-500 rounded-lg border border-slate-100 hover:bg-slate-100 transition-colors">
|
|
<Filter className="w-4 h-4" />
|
|
</button>
|
|
<button className="flex items-center space-x-2 px-4 py-2 bg-[#0F6E56] text-white rounded-lg text-xs font-bold uppercase tracking-widest hover:bg-[#085041] transition-all shadow-lg shadow-teal-500/10">
|
|
<Download className="w-4 h-4" />
|
|
<span>Export</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<Table>
|
|
<TableHeader>
|
|
<TableRow className="bg-slate-50/50 hover:bg-slate-50/50">
|
|
<TableHead className="font-bold text-slate-500 text-[10px] uppercase tracking-widest h-14 pl-8">Employer</TableHead>
|
|
<TableHead className="font-bold text-slate-500 text-[10px] uppercase tracking-widest h-14">Plan Name</TableHead>
|
|
<TableHead className="font-bold text-slate-500 text-[10px] uppercase tracking-widest h-14">Amount (AED)</TableHead>
|
|
<TableHead className="font-bold text-slate-500 text-[10px] uppercase tracking-widest h-14">Date</TableHead>
|
|
<TableHead className="font-bold text-slate-500 text-[10px] uppercase tracking-widest h-14">Status</TableHead>
|
|
<TableHead className="font-bold text-slate-500 text-[10px] uppercase tracking-widest h-14 text-right pr-8">Ref</TableHead>
|
|
</TableRow>
|
|
</TableHeader>
|
|
<TableBody>
|
|
{payments.length === 0 ? (
|
|
<TableRow>
|
|
<TableCell colSpan={6} className="py-10 text-center font-bold text-slate-400 uppercase pl-8">
|
|
No transactions found.
|
|
</TableCell>
|
|
</TableRow>
|
|
) : (
|
|
payments.map((pay) => (
|
|
<TableRow
|
|
key={pay.id}
|
|
className="hover:bg-slate-50 transition-colors cursor-pointer group"
|
|
onClick={() => openDetails(pay)}
|
|
>
|
|
<TableCell className="py-5 pl-8">
|
|
<div className="flex items-center space-x-3">
|
|
<div className="w-8 h-8 rounded-lg bg-slate-100 flex items-center justify-center text-slate-400 group-hover:bg-teal-50 group-hover:text-teal-600 transition-colors">
|
|
<Building2 className="w-4 h-4" />
|
|
</div>
|
|
<span className="font-bold text-slate-900 text-sm">{pay.employer}</span>
|
|
</div>
|
|
</TableCell>
|
|
<TableCell>
|
|
<span className="px-2.5 py-1 bg-blue-50 text-blue-600 rounded-lg text-[10px] font-black uppercase tracking-tight">
|
|
{pay.plan}
|
|
</span>
|
|
</TableCell>
|
|
<TableCell className="font-black text-slate-900 tracking-tight">{pay.amount}</TableCell>
|
|
<TableCell className="text-xs font-bold text-slate-400">{pay.date}</TableCell>
|
|
<TableCell>
|
|
<div className="flex items-center space-x-2">
|
|
{pay.status === 'Completed' && <CheckCircle2 className="w-4 h-4 text-emerald-500" />}
|
|
{pay.status === 'Pending' && <Clock className="w-4 h-4 text-amber-500" />}
|
|
{pay.status === 'Failed' && <XCircle className="w-4 h-4 text-rose-500" />}
|
|
<span className={`text-[10px] font-black uppercase tracking-widest ${
|
|
pay.status === 'Completed' ? 'text-emerald-600' :
|
|
pay.status === 'Pending' ? 'text-amber-600' : 'text-rose-600'
|
|
}`}>
|
|
{pay.status}
|
|
</span>
|
|
</div>
|
|
</TableCell>
|
|
<TableCell className="text-right pr-8">
|
|
<span className="text-[10px] font-mono font-bold text-slate-300 uppercase">{pay.id}</span>
|
|
</TableCell>
|
|
</TableRow>
|
|
))
|
|
)}
|
|
</TableBody>
|
|
</Table>
|
|
|
|
<div className="p-6 border-t border-slate-100 flex items-center justify-between bg-slate-50/30">
|
|
<p className="text-[10px] font-black text-slate-400 uppercase tracking-widest">Showing {payments.length} transactions</p>
|
|
<div className="flex items-center space-x-2">
|
|
<button className="px-4 py-2 bg-white border border-slate-200 rounded-lg text-[10px] font-black uppercase tracking-widest text-slate-500 hover:bg-slate-50 transition-colors">Previous</button>
|
|
<button className="px-4 py-2 bg-white border border-slate-200 rounded-lg text-[10px] font-black uppercase tracking-widest text-slate-500 hover:bg-slate-50 transition-colors">Next</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>
|
|
<DialogContent className="max-w-2xl bg-white p-0 overflow-hidden rounded-[32px] border-none shadow-2xl">
|
|
<div className="bg-slate-900 p-8 text-white relative">
|
|
<div className="absolute top-0 right-0 p-8 opacity-10">
|
|
<BadgeDollarSign className="w-32 h-32" />
|
|
</div>
|
|
<div className="relative z-10 space-y-2">
|
|
<div className="flex items-center space-x-2 text-[10px] font-black uppercase tracking-[0.2em] text-teal-400">
|
|
<Zap className="w-3 h-3" />
|
|
<span>Transaction Details</span>
|
|
</div>
|
|
<h2 className="text-3xl font-black tracking-tight">{selectedPayment?.id}</h2>
|
|
<p className="text-slate-400 text-sm font-medium">{selectedPayment?.date} • External Reference: TRX-99283-PL</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="p-8 space-y-8">
|
|
<div className="grid grid-cols-2 gap-8">
|
|
<div className="space-y-4">
|
|
<div className="space-y-1">
|
|
<label className="text-[10px] font-black text-slate-400 uppercase tracking-widest">Employer</label>
|
|
<div className="flex items-center space-x-2 text-sm font-bold text-slate-900">
|
|
<Building2 className="w-4 h-4 text-[#0F6E56]" />
|
|
<span>{selectedPayment?.employer}</span>
|
|
</div>
|
|
</div>
|
|
<div className="space-y-1">
|
|
<label className="text-[10px] font-black text-slate-400 uppercase tracking-widest">Subscription Plan</label>
|
|
<div className="flex items-center space-x-2 text-sm font-bold text-slate-900">
|
|
<Zap className="w-4 h-4 text-amber-500" />
|
|
<span>{selectedPayment?.plan}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="space-y-4 text-right">
|
|
<div className="space-y-1">
|
|
<label className="text-[10px] font-black text-slate-400 uppercase tracking-widest">Amount Paid</label>
|
|
<div className="text-2xl font-black text-slate-900">AED {selectedPayment?.amount}</div>
|
|
</div>
|
|
<div className="space-y-1">
|
|
<label className="text-[10px] font-black text-slate-400 uppercase tracking-widest">Payment Status</label>
|
|
<div className={`text-xs font-black uppercase tracking-widest ${
|
|
selectedPayment?.status === 'Completed' ? 'text-emerald-600' :
|
|
selectedPayment?.status === 'Pending' ? 'text-amber-600' : 'text-rose-600'
|
|
}`}>
|
|
{selectedPayment?.status}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="bg-slate-50 rounded-2xl p-6 border border-slate-100 grid grid-cols-2 gap-6">
|
|
<div className="space-y-1">
|
|
<label className="text-[10px] font-black text-slate-400 uppercase tracking-widest">Billing Period</label>
|
|
<div className="text-xs font-bold text-slate-700 flex items-center space-x-2">
|
|
<Calendar className="w-3.5 h-3.5" />
|
|
<span>{selectedPayment?.period}</span>
|
|
</div>
|
|
</div>
|
|
<div className="space-y-1">
|
|
<label className="text-[10px] font-black text-slate-400 uppercase tracking-widest">Payment Method</label>
|
|
<div className="text-xs font-bold text-slate-700 flex items-center space-x-2">
|
|
<CreditCard className="w-3.5 h-3.5" />
|
|
<span>{selectedPayment?.method}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex items-center justify-between pt-4 border-t border-slate-100">
|
|
<div className="flex items-center space-x-2 text-[10px] font-bold text-slate-400 uppercase tracking-widest">
|
|
<Globe className="w-3.5 h-3.5" />
|
|
<span>IP: 192.168.1.1 (Dubai, UAE)</span>
|
|
</div>
|
|
<button className="text-[10px] font-black text-[#0F6E56] uppercase tracking-widest hover:underline flex items-center space-x-1">
|
|
<FileText className="w-3.5 h-3.5" />
|
|
<span>Download PDF Invoice</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</DialogContent>
|
|
</Dialog>
|
|
</AdminLayout>
|
|
);
|
|
}
|