![]() Server : Apache/2 System : Linux server-15-235-50-60 5.15.0-164-generic #174-Ubuntu SMP Fri Nov 14 20:25:16 UTC 2025 x86_64 User : gositeme ( 1004) PHP Version : 8.2.29 Disable Function : exec,system,passthru,shell_exec,proc_close,proc_open,dl,popen,show_source,posix_kill,posix_mkfifo,posix_getpwuid,posix_setpgid,posix_setsid,posix_setuid,posix_setgid,posix_seteuid,posix_setegid,posix_uname Directory : /home/gositeme/domains/lavocat.ca/private_html/src/components/payments/ |
'use client';
import React, { useState, useEffect } from 'react';
import {
LineChart,
Line,
BarChart,
Bar,
PieChart,
Pie,
Cell,
XAxis,
YAxis,
CartesianGrid,
Tooltip,
Legend,
ResponsiveContainer
} from 'recharts';
import {
DollarSign,
TrendingUp,
TrendingDown,
Calendar,
CreditCard,
Download,
Filter
} from 'lucide-react';
interface PaymentAnalyticsProps {
userId: string;
userRole: string;
}
interface PaymentData {
id: string;
amount: number;
currency: string;
type: string;
status: string;
createdAt: string;
societyDiscount: number;
platformFeeAmount: number;
lawyerPayoutAmount: number;
}
interface AnalyticsData {
monthlyData: Array<{
month: string;
total: number;
completed: number;
pending: number;
failed: number;
}>;
paymentTypeData: Array<{
type: string;
amount: number;
count: number;
}>;
statusData: Array<{
status: string;
count: number;
amount: number;
}>;
topCases: Array<{
caseId: string;
caseTitle: string;
totalAmount: number;
paymentCount: number;
}>;
summary: {
totalPayments: number;
totalAmount: number;
averageAmount: number;
successRate: number;
totalDiscounts: number;
totalFees: number;
pendingPayments?: number;
};
}
const PaymentAnalytics: React.FC<PaymentAnalyticsProps> = ({ userId, userRole }) => {
const [analyticsData, setAnalyticsData] = useState<AnalyticsData | null>(null);
const [loading, setLoading] = useState(true);
const [dateRange, setDateRange] = useState('6months');
const [selectedChart, setSelectedChart] = useState('monthly');
const COLORS = ['#0088FE', '#00C49F', '#FFBB28', '#FF8042', '#8884D8'];
useEffect(() => {
fetchAnalyticsData();
}, [userId, dateRange]);
const fetchAnalyticsData = async () => {
try {
setLoading(true);
const response = await fetch(`/api/user/payment-analytics?dateRange=${dateRange}`);
if (response.ok) {
const data = await response.json();
setAnalyticsData(data);
} else {
console.error('Failed to fetch analytics data');
}
} catch (error) {
console.error('Error fetching analytics data:', error);
} finally {
setLoading(false);
}
};
const formatCurrency = (amount: number, currency: string = 'CAD') => {
return new Intl.NumberFormat('en-CA', {
style: 'currency',
currency: currency
}).format(amount);
};
const exportData = async (format: 'csv' | 'excel' | 'pdf') => {
try {
const response = await fetch(`/api/user/payment-analytics/export?format=${format}&dateRange=${dateRange}`);
if (response.ok) {
const blob = await response.blob();
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `payment-analytics-${dateRange}.${format}`;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
document.body.removeChild(a);
}
} catch (error) {
console.error('Error exporting data:', error);
}
};
if (loading) {
return (
<div className="flex items-center justify-center p-8">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
<span className="ml-3 text-gray-600">Loading analytics...</span>
</div>
);
}
if (!analyticsData) {
return (
<div className="text-center p-8">
<p className="text-gray-500">No analytics data available</p>
</div>
);
}
return (
<div className="space-y-6">
{/* Header */}
<div className="flex justify-between items-center">
<div>
<h2 className="text-2xl font-bold text-gray-900">Payment Analytics</h2>
<p className="text-gray-600">Detailed financial insights and trends</p>
</div>
<div className="flex space-x-2">
<select
value={dateRange}
onChange={(e) => setDateRange(e.target.value)}
className="px-3 py-2 border border-gray-300 rounded-md text-sm"
>
<option value="1month">Last Month</option>
<option value="3months">Last 3 Months</option>
<option value="6months">Last 6 Months</option>
<option value="1year">Last Year</option>
</select>
<button
onClick={() => exportData('csv')}
className="inline-flex items-center px-3 py-2 border border-gray-300 rounded-md text-sm font-medium text-gray-700 bg-white hover:bg-gray-50"
>
<Download className="h-4 w-4 mr-2" />
Export
</button>
</div>
</div>
{/* Summary Cards */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
<div className="bg-white rounded-lg shadow p-6">
<div className="flex items-center">
<div className="p-2 bg-blue-100 rounded-lg">
<DollarSign className="h-6 w-6 text-blue-600" />
</div>
<div className="ml-4">
<p className="text-sm font-medium text-gray-600">Total Amount</p>
<p className="text-2xl font-bold text-gray-900">
{formatCurrency(analyticsData.summary.totalAmount)}
</p>
</div>
</div>
</div>
<div className="bg-white rounded-lg shadow p-6">
<div className="flex items-center">
<div className="p-2 bg-green-100 rounded-lg">
<TrendingUp className="h-6 w-6 text-green-600" />
</div>
<div className="ml-4">
<p className="text-sm font-medium text-gray-600">Success Rate</p>
<p className="text-2xl font-bold text-gray-900">
{analyticsData.summary.successRate.toFixed(1)}%
</p>
</div>
</div>
</div>
<div className="bg-white rounded-lg shadow p-6">
<div className="flex items-center">
<div className="p-2 bg-yellow-100 rounded-lg">
<CreditCard className="h-6 w-6 text-yellow-600" />
</div>
<div className="ml-4">
<p className="text-sm font-medium text-gray-600">Total Payments</p>
<p className="text-2xl font-bold text-gray-900">
{analyticsData.summary.totalPayments}
</p>
</div>
</div>
</div>
<div className="bg-white rounded-lg shadow p-6">
<div className="flex items-center">
<div className="p-2 bg-purple-100 rounded-lg">
<Calendar className="h-6 w-6 text-purple-600" />
</div>
<div className="ml-4">
<p className="text-sm font-medium text-gray-600">Average Amount</p>
<p className="text-2xl font-bold text-gray-900">
{formatCurrency(analyticsData.summary.averageAmount)}
</p>
</div>
</div>
</div>
</div>
{/* Chart Navigation */}
<div className="bg-white rounded-lg shadow">
<div className="border-b border-gray-200">
<nav className="-mb-px flex space-x-8 px-6">
{[
{ id: 'monthly', label: 'Monthly Trends' },
{ id: 'types', label: 'Payment Types' },
{ id: 'status', label: 'Payment Status' },
{ id: 'cases', label: 'Top Cases' }
].map((tab) => (
<button
key={tab.id}
onClick={() => setSelectedChart(tab.id)}
className={`py-4 px-1 border-b-2 font-medium text-sm ${
selectedChart === tab.id
? 'border-blue-500 text-blue-600'
: 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'
}`}
>
{tab.label}
</button>
))}
</nav>
</div>
<div className="p-6">
{/* Monthly Trends Chart */}
{selectedChart === 'monthly' && (
<div className="h-80">
<ResponsiveContainer width="100%" height="100%">
<LineChart data={analyticsData.monthlyData}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="month" />
<YAxis />
<Tooltip
formatter={(value: number) => formatCurrency(value)}
labelFormatter={(label) => `Month: ${label}`}
/>
<Legend />
<Line
type="monotone"
dataKey="total"
stroke="#8884d8"
strokeWidth={2}
name="Total Amount"
/>
<Line
type="monotone"
dataKey="completed"
stroke="#82ca9d"
strokeWidth={2}
name="Completed"
/>
<Line
type="monotone"
dataKey="pending"
stroke="#ffc658"
strokeWidth={2}
name="Pending"
/>
</LineChart>
</ResponsiveContainer>
</div>
)}
{/* Payment Types Chart */}
{selectedChart === 'types' && (
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
<div className="h-80">
<ResponsiveContainer width="100%" height="100%">
<PieChart>
<Pie
data={analyticsData.paymentTypeData}
cx="50%"
cy="50%"
labelLine={false}
label={({ type, percent }) => `${type} ${((percent || 0) * 100).toFixed(0)}%`}
outerRadius={80}
fill="#8884d8"
dataKey="amount"
>
{analyticsData.paymentTypeData.map((entry, index) => (
<Cell key={`cell-${index}`} fill={COLORS[index % COLORS.length]} />
))}
</Pie>
<Tooltip formatter={(value: number) => formatCurrency(value)} />
</PieChart>
</ResponsiveContainer>
</div>
<div className="space-y-4">
<h3 className="text-lg font-medium text-gray-900">Payment Types Breakdown</h3>
{analyticsData.paymentTypeData.map((type, index) => (
<div key={type.type} className="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
<div className="flex items-center">
<div
className="w-4 h-4 rounded-full mr-3"
style={{ backgroundColor: COLORS[index % COLORS.length] }}
></div>
<span className="font-medium">{type.type}</span>
</div>
<div className="text-right">
<div className="font-medium">{formatCurrency(type.amount)}</div>
<div className="text-sm text-gray-500">{type.count} payments</div>
</div>
</div>
))}
</div>
</div>
)}
{/* Payment Status Chart */}
{selectedChart === 'status' && (
<div className="h-80">
<ResponsiveContainer width="100%" height="100%">
<BarChart data={analyticsData.statusData}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="status" />
<YAxis />
<Tooltip
formatter={(value: number) => formatCurrency(value)}
labelFormatter={(label) => `Status: ${label}`}
/>
<Legend />
<Bar dataKey="amount" fill="#8884d8" name="Amount" />
<Bar dataKey="count" fill="#82ca9d" name="Count" />
</BarChart>
</ResponsiveContainer>
</div>
)}
{/* Top Cases Chart */}
{selectedChart === 'cases' && (
<div className="space-y-4">
<h3 className="text-lg font-medium text-gray-900">Top Cases by Revenue</h3>
<div className="overflow-x-auto">
<table className="min-w-full divide-y divide-gray-200">
<thead className="bg-gray-50">
<tr>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Case
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Total Amount
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Payment Count
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Average Amount
</th>
</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{analyticsData.topCases.map((caseData) => (
<tr key={caseData.caseId}>
<td className="px-6 py-4 whitespace-nowrap">
<div className="text-sm font-medium text-gray-900">
{caseData.caseTitle}
</div>
<div className="text-sm text-gray-500">
ID: {caseData.caseId}
</div>
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
{formatCurrency(caseData.totalAmount)}
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
{caseData.paymentCount}
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
{formatCurrency(caseData.totalAmount / caseData.paymentCount)}
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
)}
</div>
</div>
{/* Additional Insights */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
<div className="bg-white rounded-lg shadow p-6">
<h3 className="text-lg font-medium text-gray-900 mb-4">Discounts & Fees</h3>
<div className="space-y-3">
<div className="flex justify-between">
<span className="text-gray-600">Total Discounts Applied</span>
<span className="font-medium text-green-600">
{formatCurrency(analyticsData.summary.totalDiscounts)}
</span>
</div>
<div className="flex justify-between">
<span className="text-gray-600">Total Platform Fees</span>
<span className="font-medium text-gray-900">
{formatCurrency(analyticsData.summary.totalFees)}
</span>
</div>
<hr />
<div className="flex justify-between">
<span className="text-gray-600">Net Amount</span>
<span className="font-medium text-gray-900">
{formatCurrency(analyticsData.summary.totalAmount - analyticsData.summary.totalDiscounts)}
</span>
</div>
</div>
</div>
<div className="bg-white rounded-lg shadow p-6">
<h3 className="text-lg font-medium text-gray-900 mb-4">Performance Metrics</h3>
<div className="space-y-3">
<div className="flex justify-between">
<span className="text-gray-600">Payment Success Rate</span>
<span className="font-medium text-green-600">
{analyticsData.summary.successRate.toFixed(1)}%
</span>
</div>
<div className="flex justify-between">
<span className="text-gray-600">Average Payment Amount</span>
<span className="font-medium text-gray-900">
{formatCurrency(analyticsData.summary.averageAmount)}
</span>
</div>
<div className="flex justify-between">
<span className="text-gray-600">Total Transactions</span>
<span className="font-medium text-gray-900">
{analyticsData.summary.totalPayments}
</span>
</div>
</div>
</div>
</div>
</div>
);
};
export default PaymentAnalytics;