239 lines
8.5 KiB
JavaScript
239 lines
8.5 KiB
JavaScript
'use client';
|
|
|
|
import { useState, useEffect } from 'react';
|
|
import { motion } from 'framer-motion';
|
|
import { useRouter } from 'next/navigation';
|
|
import {
|
|
DollarSign,
|
|
TrendingUp,
|
|
Calendar,
|
|
Users,
|
|
Building,
|
|
Download,
|
|
Loader2,
|
|
ArrowLeft,
|
|
} from 'lucide-react';
|
|
import toast, { Toaster } from 'react-hot-toast';
|
|
import AuthService from '@/app/services/AuthService';
|
|
import { getOwnerStatistics } from '@/app/utils/api';
|
|
|
|
const StatCard = ({ title, value, icon: Icon, color, subtitle }) => (
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 20 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
className="bg-white rounded-2xl shadow-sm border border-gray-200 p-6 hover:shadow-md transition-all"
|
|
>
|
|
<div className="flex items-center justify-between mb-4">
|
|
<div className={`w-12 h-12 ${color} rounded-xl flex items-center justify-center`}>
|
|
<Icon className="w-6 h-6 text-white" />
|
|
</div>
|
|
</div>
|
|
<h3 className="text-sm text-gray-500 mb-1">{title}</h3>
|
|
<div className="text-2xl font-bold text-gray-900">{value}</div>
|
|
{subtitle && <p className="text-xs text-gray-400 mt-1">{subtitle}</p>}
|
|
</motion.div>
|
|
);
|
|
|
|
export default function OwnerAccountBookPage() {
|
|
const router = useRouter();
|
|
const [user, setUser] = useState(null);
|
|
const [isLoading, setIsLoading] = useState(true);
|
|
const [stats, setStats] = useState({
|
|
totalRevenue: 0,
|
|
totalReservations: 0,
|
|
activeProperties: 0,
|
|
});
|
|
const [transactions, setTransactions] = useState([]);
|
|
|
|
useEffect(() => {
|
|
if (AuthService.isGuest()) {
|
|
router.push('/auth/choose-role');
|
|
return;
|
|
}
|
|
if (!AuthService.isOwner()) {
|
|
router.push('/');
|
|
return;
|
|
}
|
|
|
|
const authUser = AuthService.getUser();
|
|
if (authUser) {
|
|
setUser({
|
|
name: authUser.name || authUser.email,
|
|
email: authUser.email,
|
|
});
|
|
}
|
|
|
|
async function fetchData() {
|
|
try {
|
|
const data = await getOwnerStatistics();
|
|
if (data) {
|
|
setStats({
|
|
totalRevenue: data.totalRevenue ?? 0,
|
|
totalReservations: data.totalReservations ?? 0,
|
|
activeProperties: data.activeProperties ?? 0,
|
|
});
|
|
if (data.transactions) {
|
|
setTransactions(data.transactions);
|
|
}
|
|
}
|
|
} catch {
|
|
toast.error('تعذر تحميل إحصائيات الحساب');
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
}
|
|
fetchData();
|
|
}, [router]);
|
|
|
|
const formatCurrency = (amount) => {
|
|
const num = Number(amount) || 0;
|
|
return num.toLocaleString() + ' ل.س';
|
|
};
|
|
|
|
const handleExport = () => {
|
|
toast.success('جاري تصدير البيانات...');
|
|
};
|
|
|
|
if (isLoading) {
|
|
return (
|
|
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
|
|
<div className="text-center">
|
|
<Loader2 className="w-12 h-12 text-amber-500 animate-spin mx-auto mb-4" />
|
|
<p className="text-gray-600">جاري تحميل بيانات الحساب...</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="min-h-screen bg-gray-50 py-8" dir="rtl">
|
|
<Toaster position="top-center" reverseOrder={false} />
|
|
<div className="container mx-auto px-4 max-w-7xl">
|
|
<motion.div
|
|
initial={{ opacity: 0, y: -20 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
className="flex flex-col md:flex-row justify-between items-start md:items-center mb-8 gap-4"
|
|
>
|
|
<div className="flex items-center gap-4">
|
|
<button
|
|
onClick={() => router.back()}
|
|
className="p-2 hover:bg-gray-200 rounded-xl transition-colors"
|
|
>
|
|
<ArrowLeft className="w-5 h-5 text-gray-600" />
|
|
</button>
|
|
<div>
|
|
<h1 className="text-3xl font-bold text-gray-900 mb-2">دفتر الحسابات</h1>
|
|
<p className="text-gray-600">
|
|
مرحباً {user?.name}، نظرة عامة على حساباتك
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<button
|
|
onClick={handleExport}
|
|
className="px-5 py-2.5 bg-amber-500 text-white rounded-xl font-medium hover:bg-amber-600 transition-colors flex items-center gap-2 shadow-sm"
|
|
>
|
|
<Download className="w-5 h-5" />
|
|
تصدير
|
|
</button>
|
|
</motion.div>
|
|
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6 mb-8">
|
|
<StatCard
|
|
title="إجمالي الإيرادات"
|
|
value={formatCurrency(stats.totalRevenue)}
|
|
icon={DollarSign}
|
|
color="bg-green-500"
|
|
/>
|
|
<StatCard
|
|
title="إجمالي الحجوزات"
|
|
value={stats.totalReservations}
|
|
icon={Calendar}
|
|
color="bg-blue-500"
|
|
/>
|
|
<StatCard
|
|
title="العقارات النشطة"
|
|
value={stats.activeProperties}
|
|
icon={Building}
|
|
color="bg-amber-500"
|
|
/>
|
|
</div>
|
|
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 20 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
transition={{ delay: 0.2 }}
|
|
className="bg-white rounded-2xl shadow-sm border border-gray-200 overflow-hidden"
|
|
>
|
|
<div className="px-6 py-4 border-b border-gray-200">
|
|
<h2 className="text-lg font-bold text-gray-900">المعاملات المالية</h2>
|
|
</div>
|
|
{transactions.length > 0 ? (
|
|
<div className="overflow-x-auto">
|
|
<table className="min-w-full divide-y divide-gray-200 text-sm">
|
|
<thead className="bg-gray-50">
|
|
<tr>
|
|
<th className="px-4 py-3 text-center text-xs font-semibold text-gray-500 uppercase tracking-wider">
|
|
التاريخ
|
|
</th>
|
|
<th className="px-4 py-3 text-center text-xs font-semibold text-gray-500 uppercase tracking-wider">
|
|
البيان
|
|
</th>
|
|
<th className="px-4 py-3 text-center text-xs font-semibold text-gray-500 uppercase tracking-wider">
|
|
المبلغ
|
|
</th>
|
|
<th className="px-4 py-3 text-center text-xs font-semibold text-gray-500 uppercase tracking-wider">
|
|
الحالة
|
|
</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody className="bg-white divide-y divide-gray-100">
|
|
{transactions.map((tx, idx) => (
|
|
<tr
|
|
key={tx.id || idx}
|
|
className={`hover:bg-amber-50/40 transition-colors ${
|
|
idx % 2 === 0 ? 'bg-white' : 'bg-gray-50'
|
|
}`}
|
|
>
|
|
<td className="px-4 py-3 whitespace-nowrap text-center text-gray-700">
|
|
{tx.date}
|
|
</td>
|
|
<td className="px-4 py-3 whitespace-nowrap text-center text-gray-800 font-medium">
|
|
{tx.description}
|
|
</td>
|
|
<td className="px-4 py-3 whitespace-nowrap text-center font-mono font-semibold text-gray-800">
|
|
{formatCurrency(tx.amount)}
|
|
</td>
|
|
<td className="px-4 py-3 whitespace-nowrap text-center">
|
|
<span
|
|
className={`inline-flex items-center px-2 py-1 rounded-lg text-xs font-medium ${
|
|
tx.status === 'completed'
|
|
? 'bg-green-100 text-green-800'
|
|
: tx.status === 'pending'
|
|
? 'bg-yellow-100 text-yellow-800'
|
|
: 'bg-red-100 text-red-800'
|
|
}`}
|
|
>
|
|
{tx.status === 'completed'
|
|
? 'مكتمل'
|
|
: tx.status === 'pending'
|
|
? 'قيد الانتظار'
|
|
: 'ملغي'}
|
|
</span>
|
|
</td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
) : (
|
|
<div className="p-12 text-center">
|
|
<DollarSign className="w-12 h-12 text-gray-300 mx-auto mb-4" />
|
|
<p className="text-gray-500">لا توجد معاملات مالية بعد</p>
|
|
</div>
|
|
)}
|
|
</motion.div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|