2026-06-05 15:02:41 +05:30

291 lines
18 KiB
JavaScript

import React, { useState } from 'react';
import { Head } from '@inertiajs/react';
import AdminLayout from '@/Layouts/AdminLayout';
import {
FileText,
Users,
ShieldAlert,
CreditCard,
Search,
RefreshCw,
Download,
Building2,
DollarSign
} from 'lucide-react';
export default function AnalyticsHub({
worker_stats = { total: 0, verified: 0, active: 0, inactive: 0 },
sponsor_stats = { total: 0, verified: 0, active_subscribers: 0, unpaid: 0 },
dispute_stats = { total: 0, open: 0, under_review: 0, resolved: 0 },
safety_stats = { total: 0, pending: 0, resolved: 0 },
payments = [],
total_revenue = 0
}) {
const [searchTerm, setSearchTerm] = useState('');
const [statusFilter, setStatusFilter] = useState('all');
// Filter payments dynamically
const filteredPayments = payments.filter(payment => {
const matchesSearch =
payment.id.toString().toLowerCase().includes(searchTerm.toLowerCase()) ||
payment.user_name.toLowerCase().includes(searchTerm.toLowerCase()) ||
payment.user_email.toLowerCase().includes(searchTerm.toLowerCase()) ||
payment.description.toLowerCase().includes(searchTerm.toLowerCase());
const matchesStatus = statusFilter === 'all' || payment.status.toLowerCase() === statusFilter.toLowerCase();
return matchesSearch && matchesStatus;
});
const handleExportCSV = () => {
const headers = ['Payment ID,Customer,Email,Details,Amount,Status,Date\n'];
const rows = filteredPayments.map(p =>
`"${p.id}","${p.user_name}","${p.user_email}","${p.description}",${p.amount},"${p.status}","${p.created_at}"`
);
const blob = new Blob([headers.concat(rows.join('\n'))], { type: 'text/csv;charset=utf-8;' });
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.setAttribute('download', `financial_report_${new Date().toISOString().split('T')[0]}.csv`);
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
};
return (
<AdminLayout title="Reports">
<Head title="Reports" />
<div className="font-sans max-w-7xl mx-auto space-y-8 pb-12">
{/* Simplified Header */}
<div className="flex flex-col md:flex-row md:items-center justify-between gap-4 border-b border-slate-200 pb-5">
<div>
<h1 className="text-xl font-black text-slate-800 uppercase tracking-tight">Platform Reports</h1>
<p className="text-xs text-slate-500 mt-0.5 font-medium">Summary of workers, employers, disputes, and payments.</p>
</div>
<div className="flex items-center gap-2">
<button
onClick={() => window.location.reload()}
className="inline-flex items-center px-3.5 py-2 bg-white border border-slate-200 text-slate-700 rounded-xl text-xs font-black uppercase tracking-wider hover:bg-slate-50 transition-colors shadow-sm gap-1.5"
>
<RefreshCw className="w-3.5 h-3.5" />
<span>Refresh</span>
</button>
<button
onClick={handleExportCSV}
className="inline-flex items-center px-4 py-2 bg-[#0F6E56] hover:bg-[#085041] text-white rounded-xl text-xs font-black uppercase tracking-wider transition-all shadow-md gap-1.5"
>
<Download className="w-3.5 h-3.5" />
<span>Export CSV</span>
</button>
</div>
</div>
{/* Simplified Summary Cards */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
{/* Workers Card */}
<div className="bg-white border border-slate-200 rounded-2xl p-5 shadow-sm space-y-4 hover:border-[#0F6E56]/30 transition-all">
<div className="flex justify-between items-start">
<div className="p-2.5 bg-teal-50 rounded-xl text-[#0F6E56]">
<Users className="w-5 h-5" />
</div>
<span className="text-[10px] bg-slate-105 text-[#0F6E56] font-extrabold uppercase px-2 py-0.5 rounded tracking-wide">
Workers
</span>
</div>
<div>
<span className="text-[10px] text-slate-400 font-bold uppercase tracking-wider block">Total Workers</span>
<span className="text-3xl font-black text-slate-800 block mt-1 tracking-tight">{worker_stats.total}</span>
</div>
<div className="pt-2 border-t border-slate-100 grid grid-cols-2 gap-2 text-[10px] font-bold text-slate-500">
<div>
<span className="text-slate-400 uppercase tracking-widest text-[8px] block">Verified</span>
<span className="text-slate-700 font-extrabold text-xs">{worker_stats.verified}</span>
</div>
<div>
<span className="text-slate-400 uppercase tracking-widest text-[8px] block">Active</span>
<span className="text-slate-700 font-extrabold text-xs">{worker_stats.active}</span>
</div>
</div>
</div>
{/* Sponsors Card */}
<div className="bg-white border border-slate-200 rounded-2xl p-5 shadow-sm space-y-4 hover:border-[#0F6E56]/30 transition-all">
<div className="flex justify-between items-start">
<div className="p-2.5 bg-blue-50 rounded-xl text-blue-600">
<Building2 className="w-5 h-5" />
</div>
<span className="text-[10px] bg-blue-50 text-blue-700 font-extrabold uppercase px-2 py-0.5 rounded tracking-wide">
Employers
</span>
</div>
<div>
<span className="text-[10px] text-slate-400 font-bold uppercase tracking-wider block">Total Employers</span>
<span className="text-3xl font-black text-slate-800 block mt-1 tracking-tight">{sponsor_stats.total}</span>
</div>
<div className="pt-2 border-t border-slate-100 grid grid-cols-2 gap-2 text-[10px] font-bold text-slate-500">
<div>
<span className="text-slate-400 uppercase tracking-widest text-[8px] block">Active Plan</span>
<span className="text-slate-700 font-extrabold text-xs">{sponsor_stats.active_subscribers}</span>
</div>
<div>
<span className="text-slate-400 uppercase tracking-widest text-[8px] block">Unpaid</span>
<span className="text-slate-700 font-extrabold text-xs">{sponsor_stats.unpaid}</span>
</div>
</div>
</div>
{/* Disputes Card */}
<div className="bg-white border border-slate-200 rounded-2xl p-5 shadow-sm space-y-4 hover:border-[#0F6E56]/30 transition-all">
<div className="flex justify-between items-start">
<div className="p-2.5 bg-orange-50 rounded-xl text-orange-600">
<ShieldAlert className="w-5 h-5" />
</div>
<span className="text-[10px] bg-orange-50 text-orange-700 font-extrabold uppercase px-2 py-0.5 rounded tracking-wide">
Disputes
</span>
</div>
<div>
<span className="text-[10px] text-slate-400 font-bold uppercase tracking-wider block">Total Disputes</span>
<span className="text-3xl font-black text-slate-800 block mt-1 tracking-tight">{dispute_stats.open + dispute_stats.under_review}</span>
</div>
<div className="pt-2 border-t border-slate-100 grid grid-cols-2 gap-2 text-[10px] font-bold text-slate-500">
<div>
<span className="text-slate-400 uppercase tracking-widest text-[8px] block">Resolved</span>
<span className="text-slate-700 font-extrabold text-xs">{dispute_stats.resolved}</span>
</div>
<div>
<span className="text-slate-400 uppercase tracking-widest text-[8px] block">Pending Flags</span>
<span className="text-slate-700 font-extrabold text-xs">{safety_stats.pending}</span>
</div>
</div>
</div>
{/* Revenue Card */}
<div className="bg-white border border-slate-200 rounded-2xl p-5 shadow-sm space-y-4 hover:border-[#0F6E56]/30 transition-all">
<div className="flex justify-between items-start">
<div className="p-2.5 bg-emerald-50 rounded-xl text-emerald-600">
<DollarSign className="w-5 h-5" />
</div>
<span className="text-[10px] bg-emerald-50 text-emerald-700 font-extrabold uppercase px-2 py-0.5 rounded tracking-wide">
Revenue
</span>
</div>
<div>
<span className="text-[10px] text-slate-400 font-bold uppercase tracking-wider block">Total Revenue</span>
<span className="text-3xl font-black text-slate-800 block mt-1 tracking-tight">
AED {total_revenue.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
</span>
</div>
<div className="pt-2 border-t border-slate-100 grid grid-cols-2 gap-2 text-[10px] font-bold text-slate-500">
<div>
<span className="text-slate-400 uppercase tracking-widest text-[8px] block">Total Paid</span>
<span className="text-slate-700 font-extrabold text-xs">{payments.length}</span>
</div>
<div>
<span className="text-slate-400 uppercase tracking-widest text-[8px] block">Average Amount</span>
<span className="text-slate-700 font-extrabold text-xs">
AED {payments.length > 0 ? (total_revenue / payments.length).toFixed(0) : '0'}
</span>
</div>
</div>
</div>
</div>
{/* Simplified Payment Ledger */}
<div className="bg-white border border-slate-200 rounded-3xl p-6 shadow-sm space-y-6">
<div className="flex flex-col md:flex-row md:items-center justify-between gap-4 border-b border-slate-100 pb-5">
<div>
<h3 className="text-sm font-black text-slate-800 uppercase tracking-widest flex items-center gap-2">
<CreditCard className="w-4.5 h-4.5 text-[#0F6E56]" />
<span>Payment List</span>
</h3>
<p className="text-[10px] text-slate-400 font-semibold mt-0.5">Search and check payments.</p>
</div>
{/* Search & Status Filters */}
<div className="flex flex-wrap items-center gap-3">
<div className="relative">
<Search className="w-4 h-4 text-slate-400 absolute left-3.5 top-1/2 -translate-y-1/2" />
<input
type="text"
placeholder="Search payments..."
className="bg-slate-50 border border-slate-200 rounded-xl pl-9 pr-4 py-2 text-xs font-semibold focus:bg-white outline-none focus:ring-2 focus:ring-[#0F6E56]/10 w-64 shadow-sm"
value={searchTerm}
onChange={e => setSearchTerm(e.target.value)}
/>
</div>
<select
className="bg-slate-50 border border-slate-200 rounded-xl px-4 py-2 text-xs font-semibold outline-none cursor-pointer focus:bg-white focus:ring-2 focus:ring-[#0F6E56]/10 shadow-sm"
value={statusFilter}
onChange={e => setStatusFilter(e.target.value)}
>
<option value="all">All Payments</option>
<option value="success">Success</option>
<option value="pending">Pending</option>
<option value="failed">Failed</option>
</select>
</div>
</div>
{/* Payments Table */}
<div className="overflow-x-auto">
<table className="w-full text-left border-collapse">
<thead>
<tr className="border-b border-slate-150 text-[10px] text-slate-400 font-black uppercase tracking-widest">
<th className="px-5 py-3">Payment ID</th>
<th className="px-5 py-3">Customer</th>
<th className="px-5 py-3">Details</th>
<th className="px-5 py-3">Amount</th>
<th className="px-5 py-3">Status</th>
<th className="px-5 py-3">Date</th>
</tr>
</thead>
<tbody className="divide-y divide-slate-100 text-xs font-bold text-slate-700">
{filteredPayments.map(p => (
<tr key={p.id} className="hover:bg-slate-50/50 transition-colors">
<td className="px-5 py-4 font-mono text-[#0F6E56]">TXN-{p.id}</td>
<td className="px-5 py-4">
<div>
<span className="font-extrabold text-slate-800 block">{p.user_name}</span>
<span className="text-[10px] text-slate-400 font-semibold block">{p.user_email}</span>
</div>
</td>
<td className="px-5 py-4 font-semibold text-slate-650">{p.description}</td>
<td className="px-5 py-4 font-extrabold text-slate-800">
{p.amount.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} {p.currency}
</td>
<td className="px-5 py-4">
<span className={`px-2 py-0.5 rounded-full text-[9px] font-black uppercase tracking-wide ${
p.status === 'success' ? 'bg-emerald-50 text-emerald-700 border border-emerald-100' :
p.status === 'pending' ? 'bg-amber-50 text-amber-700 border border-amber-100' :
'bg-red-50 text-red-700 border border-red-100'
}`}>
{p.status}
</span>
</td>
<td className="px-5 py-4 text-slate-400 font-semibold">{p.created_at}</td>
</tr>
))}
{filteredPayments.length === 0 && (
<tr>
<td colSpan="6" className="p-12 text-center text-slate-400 font-bold uppercase">
No payments found.
</td>
</tr>
)}
</tbody>
</table>
</div>
</div>
</div>
</AdminLayout>
);
}