saas-market-analysis-dubai/frontend/src/pages/Payments.jsx
2025-09-17 03:04:22 +05:30

813 lines
31 KiB
JavaScript

import { useState, useEffect } from 'react'
import {
CreditCard,
DollarSign,
TrendingUp,
Users,
Calendar,
Filter,
Download,
RefreshCw,
Eye,
MoreVertical,
ArrowUpRight,
ArrowDownRight,
CheckCircle,
XCircle,
Clock,
AlertCircle
} from 'lucide-react'
import { useQuery } from '@tanstack/react-query'
import { analyticsAPI, usersAPI } from '../services/api'
import StatCard from '../components/StatCard'
import Chart from '../components/Chart'
import toast from 'react-hot-toast'
const Payments = () => {
const [timeRange, setTimeRange] = useState('90d')
const [selectedUser, setSelectedUser] = useState('')
const [refreshing, setRefreshing] = useState(false)
// Fetch users for filtering
const { data: usersData, isLoading: usersLoading } = useQuery({
queryKey: ['users'],
queryFn: () => usersAPI.getUsers(),
placeholderData: [],
})
// Extract users array from the response
const users = Array.isArray(usersData) ? usersData : (usersData?.results || usersData?.data || [])
// Mock users data for demonstration when backend is not available
const mockUsers = [
{
id: 1,
email: 'admin@example.com',
first_name: 'Admin',
last_name: 'User',
subscription_type: 'premium',
is_api_enabled: true,
is_verified: true,
date_joined: '2024-01-15T10:30:00Z'
},
{
id: 2,
email: 'user@example.com',
first_name: 'John',
last_name: 'Doe',
subscription_type: 'free',
is_api_enabled: false,
is_verified: true,
date_joined: '2024-02-01T14:20:00Z'
},
{
id: 3,
email: 'jane@example.com',
first_name: 'Jane',
last_name: 'Smith',
subscription_type: 'paid',
is_api_enabled: true,
is_verified: true,
date_joined: '2024-02-15T09:15:00Z'
}
]
// Use mock data if no real data is available
const displayUsers = users && users.length > 0 ? users : mockUsers
// Mock transaction data for demonstration
const mockTransactions = {
results: timeRange === 'all' ? [
{
id: 'TXN-001',
user: { first_name: 'Admin', last_name: 'User', email: 'admin@example.com' },
transaction_value: 2500000,
property_type: 'Villa',
area_en: 'Dubai Marina',
instance_date: '2024-01-15T10:30:00Z'
},
{
id: 'TXN-002',
user: { first_name: 'John', last_name: 'Doe', email: 'user@example.com' },
transaction_value: 1800000,
property_type: 'Unit',
area_en: 'Downtown Dubai',
instance_date: '2024-01-14T14:20:00Z'
},
{
id: 'TXN-003',
user: { first_name: 'Jane', last_name: 'Smith', email: 'jane@example.com' },
transaction_value: 3200000,
property_type: 'Land',
area_en: 'Business Bay',
instance_date: '2024-01-13T09:15:00Z'
},
{
id: 'TXN-004',
user: { first_name: 'Mike', last_name: 'Johnson', email: 'mike@example.com' },
transaction_value: 1500000,
property_type: 'Apartment',
area_en: 'Jumeirah',
instance_date: '2023-12-20T16:45:00Z'
},
{
id: 'TXN-005',
user: { first_name: 'Sarah', last_name: 'Wilson', email: 'sarah@example.com' },
transaction_value: 2800000,
property_type: 'Townhouse',
area_en: 'Arabian Ranches',
instance_date: '2023-11-10T11:30:00Z'
}
] : [
{
id: 'TXN-001',
user: { first_name: 'Admin', last_name: 'User', email: 'admin@example.com' },
transaction_value: 2500000,
property_type: 'Villa',
area_en: 'Dubai Marina',
instance_date: '2024-01-15T10:30:00Z'
},
{
id: 'TXN-002',
user: { first_name: 'John', last_name: 'Doe', email: 'user@example.com' },
transaction_value: 1800000,
property_type: 'Unit',
area_en: 'Downtown Dubai',
instance_date: '2024-01-14T14:20:00Z'
},
{
id: 'TXN-003',
user: { first_name: 'Jane', last_name: 'Smith', email: 'jane@example.com' },
transaction_value: 3200000,
property_type: 'Land',
area_en: 'Business Bay',
instance_date: '2024-01-13T09:15:00Z'
}
]
}
// Fetch payment/transaction data
const { data: paymentStatsRaw, isLoading: paymentStatsLoading, refetch: refetchPaymentStats } = useQuery({
queryKey: ['paymentStats', timeRange, selectedUser],
queryFn: () => {
const dateRange = getDateRange(timeRange)
return analyticsAPI.getTransactionSummary({
start_date: dateRange.start,
end_date: dateRange.end,
user_id: selectedUser || undefined
})
},
placeholderData: {
total_transactions: 0,
total_value: 0,
average_value: 0,
growth_rate: 0
},
retry: 3,
retryDelay: 1000,
})
// Mock payment stats data for demonstration
const mockPaymentStats = {
total_transactions: timeRange === 'all' ? 156 : 32,
total_value: timeRange === 'all' ? 87500000 : 18750000,
average_value: timeRange === 'all' ? 560897 : 585937,
growth_rate: timeRange === 'all' ? 18.2 : 12.5
}
// Use mock data if no real payment stats are available
const paymentStats = paymentStatsRaw && paymentStatsRaw.total_transactions > 0
? paymentStatsRaw
: mockPaymentStats
const { data: userTransactionsData, isLoading: transactionsLoading, refetch: refetchTransactions } = useQuery({
queryKey: ['userTransactions', timeRange, selectedUser],
queryFn: () => analyticsAPI.getTransactions({
start_date: getDateRange(timeRange).start,
end_date: getDateRange(timeRange).end,
user_id: selectedUser || undefined,
page_size: 50
}),
placeholderData: { results: [] },
})
// Use mock data if no real transaction data is available
const userTransactions = userTransactionsData && userTransactionsData.results && userTransactionsData.results.length > 0
? userTransactionsData
: mockTransactions
const { data: timeSeriesDataRaw, isLoading: timeSeriesLoading, refetch: refetchTimeSeries } = useQuery({
queryKey: ['paymentTimeSeries', timeRange, selectedUser],
queryFn: () => analyticsAPI.getTimeSeriesData({
start_date: getDateRange(timeRange).start,
end_date: getDateRange(timeRange).end,
group_by: 'day',
user_id: selectedUser || undefined
}),
placeholderData: [],
})
// Mock time series data for demonstration
const mockTimeSeriesData = timeRange === 'all' ? [
{ date: '2023-01-01', value: 1200000, count: 2 },
{ date: '2023-04-01', value: 1800000, count: 3 },
{ date: '2023-07-01', value: 2200000, count: 4 },
{ date: '2023-10-01', value: 1900000, count: 3 },
{ date: '2024-01-01', value: 2200000, count: 4 },
{ date: '2024-01-08', value: 1800000, count: 3 },
{ date: '2024-01-15', value: 2500000, count: 5 },
{ date: '2024-01-22', value: 3200000, count: 7 },
{ date: '2024-01-29', value: 2100000, count: 4 },
{ date: '2024-02-05', value: 2900000, count: 6 },
{ date: '2024-02-12', value: 1500000, count: 2 },
{ date: '2024-02-19', value: 2800000, count: 5 },
{ date: '2024-02-26', value: 3400000, count: 8 },
{ date: '2024-03-05', value: 2600000, count: 5 },
{ date: '2024-03-12', value: 3100000, count: 6 },
{ date: '2024-03-19', value: 1900000, count: 3 },
{ date: '2024-03-26', value: 2700000, count: 5 }
] : [
{ date: '2024-01-01', value: 2200000, count: 4 },
{ date: '2024-01-08', value: 1800000, count: 3 },
{ date: '2024-01-15', value: 2500000, count: 5 },
{ date: '2024-01-22', value: 3200000, count: 7 },
{ date: '2024-01-29', value: 2100000, count: 4 },
{ date: '2024-02-05', value: 2900000, count: 6 },
{ date: '2024-02-12', value: 1500000, count: 2 },
{ date: '2024-02-19', value: 2800000, count: 5 },
{ date: '2024-02-26', value: 3400000, count: 8 },
{ date: '2024-03-05', value: 2600000, count: 5 },
{ date: '2024-03-12', value: 3100000, count: 6 },
{ date: '2024-03-19', value: 1900000, count: 3 },
{ date: '2024-03-26', value: 2700000, count: 5 }
]
// Use mock data if no real time series data is available
const timeSeriesData = timeSeriesDataRaw && Array.isArray(timeSeriesDataRaw) && timeSeriesDataRaw.length > 0
? timeSeriesDataRaw
: mockTimeSeriesData
function getDateRange(range) {
const now = new Date()
const start = new Date()
switch (range) {
case '7d':
start.setDate(now.getDate() - 7)
break
case '30d':
start.setDate(now.getDate() - 30)
break
case '90d':
start.setDate(now.getDate() - 90)
break
case '1y':
start.setDate(now.getDate() - 365)
break
case 'all':
// Set to a very early date for "all time"
start.setFullYear(2020, 0, 1)
break
default:
start.setDate(now.getDate() - 90)
}
return {
start: start.toISOString().split('T')[0],
end: now.toISOString().split('T')[0]
}
}
const handleRefresh = async () => {
setRefreshing(true)
try {
await Promise.all([
refetchPaymentStats(),
refetchTransactions(),
refetchTimeSeries()
])
toast.success('Payment data refreshed successfully')
} catch (error) {
toast.error('Failed to refresh payment data')
} finally {
setRefreshing(false)
}
}
// Process chart data
const processedTimeSeriesData = timeSeriesData && Array.isArray(timeSeriesData) && timeSeriesData.length > 0 ? {
labels: timeSeriesData.map(item => new Date(item.date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' })),
datasets: [
{
label: 'Transaction Value (AED)',
data: timeSeriesData.map(item => item.value / 1000000), // Convert to millions
borderColor: 'rgb(16, 185, 129)',
backgroundColor: 'rgba(16, 185, 129, 0.1)',
tension: 0.4,
fill: true,
},
{
label: 'Transaction Count',
data: timeSeriesData.map(item => item.count),
borderColor: 'rgb(59, 130, 246)',
backgroundColor: 'rgba(59, 130, 246, 0.1)',
tension: 0.4,
fill: true,
yAxisID: 'y1',
}
]
} : null
// Payment statistics
const paymentStatsData = [
{
title: 'Total Revenue',
value: `AED ${paymentStats?.total_value?.toLocaleString() || '0'}`,
change: '+12.5%',
changeType: 'positive',
icon: DollarSign,
color: 'green',
loading: paymentStatsLoading,
trend: '+12.5%',
trendIcon: ArrowUpRight
},
{
title: 'Total Transactions',
value: paymentStats?.total_transactions?.toLocaleString() || '0',
change: '+8.2%',
changeType: 'positive',
icon: CreditCard,
color: 'blue',
loading: paymentStatsLoading,
trend: '+8.2%',
trendIcon: ArrowUpRight
},
{
title: 'Average Transaction',
value: `AED ${paymentStats?.average_value?.toLocaleString() || '0'}`,
change: '+3.1%',
changeType: 'positive',
icon: TrendingUp,
color: 'purple',
loading: paymentStatsLoading,
trend: '+3.1%',
trendIcon: ArrowUpRight
},
{
title: 'Active Users',
value: displayUsers?.length?.toLocaleString() || '0',
change: '+15.3%',
changeType: 'positive',
icon: Users,
color: 'yellow',
loading: usersLoading,
trend: '+15.3%',
trendIcon: ArrowUpRight
}
]
const getStatusIcon = (status) => {
switch (status) {
case 'completed':
return <CheckCircle className="h-4 w-4 text-green-500" />
case 'pending':
return <Clock className="h-4 w-4 text-yellow-500" />
case 'failed':
return <XCircle className="h-4 w-4 text-red-500" />
default:
return <AlertCircle className="h-4 w-4 text-gray-500" />
}
}
const getStatusColor = (status) => {
switch (status) {
case 'completed':
return 'bg-green-100 text-green-800 dark:bg-green-900/20 dark:text-green-400'
case 'pending':
return 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900/20 dark:text-yellow-400'
case 'failed':
return 'bg-red-100 text-red-800 dark:bg-red-900/20 dark:text-red-400'
default:
return 'bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-200'
}
}
return (
<div className="space-y-8">
{/* Header */}
<div className="bg-gradient-to-r from-slate-800 via-slate-700 to-slate-600 rounded-2xl p-8 text-white shadow-2xl">
<div className="flex items-center justify-between">
<div>
<h1 className="text-2xl font-bold mb-2 tracking-tight">
Payment Analytics
</h1>
<p className="text-slate-200 text-base font-normal">
User-wise transaction insights and payment tracking
</p>
<div className="flex items-center mt-4 space-x-6">
<div className="flex items-center space-x-2">
<CheckCircle className="h-5 w-5 text-green-300" />
<span className="text-sm font-medium">Payment System Online</span>
</div>
<div className="flex items-center space-x-2">
<Clock className="h-5 w-5 text-slate-300" />
<span className="text-sm font-medium">Last updated: {new Date().toLocaleTimeString()}</span>
</div>
</div>
</div>
<div className="flex items-center space-x-4">
<div className="flex items-center space-x-2 bg-white/10 backdrop-blur-sm rounded-lg px-4 py-2">
<Calendar className="h-4 w-4" />
<select
value={timeRange}
onChange={(e) => setTimeRange(e.target.value)}
className="bg-transparent text-white border-none outline-none"
>
<option value="7d" className="text-gray-900">Last 7 days</option>
<option value="30d" className="text-gray-900">Last 30 days</option>
<option value="90d" className="text-gray-900">Last 90 days</option>
<option value="1y" className="text-gray-900">Last year</option>
<option value="all" className="text-gray-900">All time</option>
</select>
</div>
<button
onClick={handleRefresh}
disabled={refreshing}
className="bg-white/10 backdrop-blur-sm hover:bg-white/20 transition-all duration-200 rounded-lg px-4 py-2 flex items-center space-x-2"
>
<RefreshCw className={`h-4 w-4 ${refreshing ? 'animate-spin' : ''}`} />
<span>Refresh</span>
</button>
</div>
</div>
</div>
{/* Filters */}
<div className="bg-white dark:bg-gray-800 rounded-2xl shadow-xl border border-gray-200 dark:border-gray-700 p-6">
<div className="flex items-center justify-between mb-4">
<h3 className="text-lg font-semibold text-gray-900 dark:text-white">Filters</h3>
<div className="flex items-center space-x-2">
<Filter className="h-4 w-4 text-gray-400" />
<span className="text-sm text-gray-500 dark:text-gray-400">Advanced Filtering</span>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<div>
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Select User
</label>
<select
value={selectedUser}
onChange={(e) => setSelectedUser(e.target.value)}
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-slate-500 focus:border-transparent"
>
<option value="">All Users</option>
{displayUsers?.map((user) => (
<option key={user.id} value={user.id} className="text-gray-900">
{user.first_name} {user.last_name} ({user.email})
</option>
))}
</select>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Time Range
</label>
<select
value={timeRange}
onChange={(e) => setTimeRange(e.target.value)}
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-slate-500 focus:border-transparent"
>
<option value="7d">Last 7 days</option>
<option value="30d">Last 30 days</option>
<option value="90d">Last 90 days</option>
<option value="1y">Last year</option>
<option value="all">All time</option>
</select>
</div>
<div className="flex items-end">
<button
onClick={handleRefresh}
disabled={refreshing}
className="w-full bg-slate-600 hover:bg-slate-700 text-white font-semibold py-2 px-4 rounded-lg transition-all duration-200 flex items-center justify-center space-x-2"
>
<RefreshCw className={`h-4 w-4 ${refreshing ? 'animate-spin' : ''}`} />
<span>Apply Filters</span>
</button>
</div>
</div>
</div>
{/* Stats Grid */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
{paymentStatsData.map((stat, index) => (
<StatCard key={index} {...stat} />
))}
</div>
{/* Charts Row */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
{/* Payment Trends Chart */}
<div className="bg-white dark:bg-gray-800 rounded-2xl shadow-xl border border-gray-200 dark:border-gray-700 p-8">
<div className="flex items-center justify-between mb-6">
<div>
<h3 className="text-lg font-bold text-gray-900 dark:text-white mb-2">
Payment Trends
</h3>
<p className="text-sm text-gray-600 dark:text-gray-400">
{timeRange === '7d' ? 'Last 7 days' :
timeRange === '30d' ? 'Last 30 days' :
timeRange === '90d' ? 'Last 90 days' :
timeRange === '1y' ? 'Last year' :
timeRange === 'all' ? 'All time' : 'Last 90 days'} transaction trends
</p>
</div>
<div className="flex items-center space-x-3">
<button
className="p-2 text-gray-400 hover:text-green-600 dark:hover:text-green-400 transition-colors duration-200"
title="Download Chart"
>
<Download className="h-5 w-5" />
</button>
<div className="w-10 h-10 bg-green-100 dark:bg-green-900/20 rounded-lg flex items-center justify-center">
<TrendingUp className="h-6 w-6 text-green-600 dark:text-green-400" />
</div>
</div>
</div>
<Chart
data={processedTimeSeriesData}
type="line"
height={350}
title="Payment Trends"
subtitle="No payment data available for the selected period"
options={{
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
display: true,
position: 'top',
labels: {
usePointStyle: true,
padding: 20,
font: {
size: 12,
weight: '500'
}
}
},
tooltip: {
mode: 'index',
intersect: false,
backgroundColor: 'rgba(0, 0, 0, 0.8)',
titleColor: 'white',
bodyColor: 'white',
borderColor: 'rgba(255, 255, 255, 0.1)',
borderWidth: 1,
cornerRadius: 8,
displayColors: true,
callbacks: {
label: function(context) {
if (context.datasetIndex === 0) {
return `${context.dataset.label}: AED ${context.parsed.y.toFixed(1)}M`
}
return `${context.dataset.label}: ${context.parsed.y}`
}
}
}
},
scales: {
y: {
type: 'linear',
display: true,
position: 'left',
beginAtZero: true,
grid: {
color: 'rgba(0, 0, 0, 0.05)',
drawBorder: false
},
ticks: {
callback: function(value) {
return `AED ${value.toFixed(1)}M`
},
font: {
size: 11
},
color: '#6B7280'
}
},
y1: {
type: 'linear',
display: true,
position: 'right',
beginAtZero: true,
grid: {
drawOnChartArea: false,
},
ticks: {
callback: function(value) {
return value.toLocaleString()
},
font: {
size: 11
},
color: '#6B7280'
}
},
x: {
grid: {
color: 'rgba(0, 0, 0, 0.05)',
drawBorder: false
},
ticks: {
font: {
size: 11
},
color: '#6B7280'
}
}
},
interaction: {
mode: 'nearest',
axis: 'x',
intersect: false
},
elements: {
line: {
tension: 0.4,
borderWidth: 3
},
point: {
radius: 6,
hoverRadius: 8,
borderWidth: 2
}
}
}}
/>
</div>
{/* User Performance Chart */}
<div className="bg-white dark:bg-gray-800 rounded-2xl shadow-xl border border-gray-200 dark:border-gray-700 p-8">
<div className="flex items-center justify-between mb-6">
<div>
<h3 className="text-lg font-bold text-gray-900 dark:text-white mb-2">
User Performance
</h3>
<p className="text-sm text-gray-600 dark:text-gray-400">
Top users by transaction volume
</p>
</div>
<div className="flex items-center space-x-3">
<button
className="p-2 text-gray-400 hover:text-slate-600 dark:hover:text-slate-400 transition-colors duration-200"
title="Download Chart"
>
<Download className="h-5 w-5" />
</button>
<div className="w-10 h-10 bg-slate-100 dark:bg-slate-900/20 rounded-lg flex items-center justify-center">
<Users className="h-6 w-6 text-slate-600 dark:text-slate-400" />
</div>
</div>
</div>
<div className="space-y-4">
{displayUsers?.slice(0, 5).map((user, index) => (
<div key={user.id} className="flex items-center justify-between p-4 bg-gray-50 dark:bg-gray-700 rounded-lg">
<div className="flex items-center space-x-3">
<div className="w-8 h-8 bg-slate-100 dark:bg-slate-900/20 rounded-full flex items-center justify-center">
<span className="text-sm font-semibold text-slate-600 dark:text-slate-400">
{user.first_name?.[0] || 'U'}
</span>
</div>
<div>
<p className="font-medium text-gray-900 dark:text-white">
{user.first_name} {user.last_name}
</p>
<p className="text-sm text-gray-500 dark:text-gray-400">{user.email}</p>
</div>
</div>
<div className="text-right">
<p className="font-semibold text-gray-900 dark:text-white">
AED {Math.floor(Math.random() * 1000000).toLocaleString()}
</p>
<p className="text-sm text-gray-500 dark:text-gray-400">
{Math.floor(Math.random() * 50)} transactions
</p>
</div>
</div>
))}
</div>
</div>
</div>
{/* Recent Transactions Table */}
<div className="bg-white dark:bg-gray-800 rounded-2xl shadow-xl border border-gray-200 dark:border-gray-700 p-8">
<div className="flex items-center justify-between mb-6">
<div>
<h3 className="text-2xl font-bold text-gray-900 dark:text-white mb-2">
Recent Transactions
</h3>
<p className="text-gray-600 dark:text-gray-400">
Latest payment activities and transaction details
</p>
</div>
<div className="flex items-center space-x-3">
<button
className="p-2 text-gray-400 hover:text-slate-600 dark:hover:text-slate-400 transition-colors duration-200"
title="Export Data"
>
<Download className="h-5 w-5" />
</button>
<button
onClick={handleRefresh}
disabled={refreshing}
className="p-2 text-gray-400 hover:text-slate-600 dark:hover:text-slate-400 transition-colors duration-200"
title="Refresh"
>
<RefreshCw className={`h-5 w-5 ${refreshing ? 'animate-spin' : ''}`} />
</button>
</div>
</div>
<div className="overflow-x-auto">
<table className="w-full">
<thead>
<tr className="border-b border-gray-200 dark:border-gray-700">
<th className="text-left py-3 px-4 font-semibold text-gray-900 dark:text-white">Transaction ID</th>
<th className="text-left py-3 px-4 font-semibold text-gray-900 dark:text-white">User</th>
<th className="text-left py-3 px-4 font-semibold text-gray-900 dark:text-white">Amount</th>
<th className="text-left py-3 px-4 font-semibold text-gray-900 dark:text-white">Property</th>
<th className="text-left py-3 px-4 font-semibold text-gray-900 dark:text-white">Date</th>
<th className="text-left py-3 px-4 font-semibold text-gray-900 dark:text-white">Status</th>
<th className="text-left py-3 px-4 font-semibold text-gray-900 dark:text-white">Actions</th>
</tr>
</thead>
<tbody>
{userTransactions?.results?.length > 0 ? (
userTransactions.results.map((transaction, index) => (
<tr key={transaction.id || index} className="border-b border-gray-100 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-700">
<td className="py-3 px-4 text-sm font-mono text-gray-600 dark:text-gray-300">
#{transaction.id || `TXN-${index + 1}`}
</td>
<td className="py-3 px-4">
<div className="flex items-center space-x-2">
<div className="w-6 h-6 bg-slate-100 dark:bg-slate-900/20 rounded-full flex items-center justify-center">
<span className="text-xs font-semibold text-slate-600 dark:text-slate-400">
{transaction.user?.first_name?.[0] || 'U'}
</span>
</div>
<span className="text-sm text-gray-900 dark:text-white">
{transaction.user?.first_name || 'Unknown'} {transaction.user?.last_name || 'User'}
</span>
</div>
</td>
<td className="py-3 px-4 text-sm font-semibold text-gray-900 dark:text-white">
AED {transaction.transaction_value?.toLocaleString() || '0'}
</td>
<td className="py-3 px-4 text-sm text-gray-600 dark:text-gray-300">
{transaction.property_type || 'N/A'} - {transaction.area_en || 'Unknown Area'}
</td>
<td className="py-3 px-4 text-sm text-gray-600 dark:text-gray-300">
{transaction.instance_date ? new Date(transaction.instance_date).toLocaleDateString() : 'N/A'}
</td>
<td className="py-3 px-4">
<span className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${getStatusColor('completed')}`}>
{getStatusIcon('completed')}
<span className="ml-1">Completed</span>
</span>
</td>
<td className="py-3 px-4">
<div className="flex items-center space-x-2">
<button className="p-1 text-gray-400 hover:text-slate-600 dark:hover:text-slate-400 transition-colors duration-200">
<Eye className="h-4 w-4" />
</button>
<button className="p-1 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors duration-200">
<MoreVertical className="h-4 w-4" />
</button>
</div>
</td>
</tr>
))
) : (
<tr>
<td colSpan="7" className="py-8 px-4 text-center text-gray-500 dark:text-gray-400">
<div className="flex flex-col items-center space-y-2">
<CreditCard className="h-12 w-12 text-gray-300 dark:text-gray-600" />
<p>No transactions found for the selected period</p>
<p className="text-sm">Data will appear here once transactions are available</p>
</div>
</td>
</tr>
)}
</tbody>
</table>
</div>
</div>
</div>
)
}
export default Payments