2026-04-01 01:34:51 +03:00
|
|
|
|
// 'use client';
|
|
|
|
|
|
|
|
|
|
|
|
// import { useState, useEffect } from 'react';
|
|
|
|
|
|
// import { motion } from 'framer-motion';
|
|
|
|
|
|
// import { useRouter } from 'next/navigation';
|
2026-04-22 10:52:08 +03:00
|
|
|
|
// import {
|
|
|
|
|
|
// DollarSign,
|
|
|
|
|
|
// TrendingUp,
|
2026-04-01 01:34:51 +03:00
|
|
|
|
// Wallet,
|
2026-04-22 10:52:08 +03:00
|
|
|
|
// Star,
|
|
|
|
|
|
// Eye,
|
|
|
|
|
|
// Download,
|
|
|
|
|
|
// CalendarDays
|
2026-04-01 01:34:51 +03:00
|
|
|
|
// } from 'lucide-react';
|
|
|
|
|
|
// import toast, { Toaster } from 'react-hot-toast';
|
2026-04-22 10:52:08 +03:00
|
|
|
|
// import AuthService from '@/app/services/AuthService';
|
2026-04-01 01:34:51 +03:00
|
|
|
|
|
2026-04-22 10:52:08 +03:00
|
|
|
|
// const StatCard = ({ title, value, icon: Icon, color }) => {
|
2026-04-01 01:34:51 +03:00
|
|
|
|
// return (
|
|
|
|
|
|
// <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"
|
|
|
|
|
|
// >
|
2026-04-22 10:52:08 +03:00
|
|
|
|
// <div className="flex items-center justify-between mb-4">
|
2026-04-01 01:34:51 +03:00
|
|
|
|
// <div className={`w-12 h-12 ${color} rounded-xl flex items-center justify-center`}>
|
|
|
|
|
|
// <Icon className="w-6 h-6 text-white" />
|
|
|
|
|
|
// </div>
|
|
|
|
|
|
// </div>
|
2026-04-22 10:52:08 +03:00
|
|
|
|
// <h3 className="text-sm text-gray-500 mb-1">{title}</h3>
|
2026-04-01 01:34:51 +03:00
|
|
|
|
// <div className="text-2xl font-bold text-gray-900">{value}</div>
|
|
|
|
|
|
// </motion.div>
|
|
|
|
|
|
// );
|
|
|
|
|
|
// };
|
|
|
|
|
|
|
|
|
|
|
|
// const PropertyProfitCard = ({ property, onViewDetails }) => {
|
2026-04-22 10:52:08 +03:00
|
|
|
|
// const formatCurrency = (amount) => `$${amount?.toLocaleString()}`;
|
2026-04-01 01:34:51 +03:00
|
|
|
|
|
|
|
|
|
|
// return (
|
|
|
|
|
|
// <motion.div
|
|
|
|
|
|
// initial={{ opacity: 0, y: 20 }}
|
|
|
|
|
|
// animate={{ opacity: 1, y: 0 }}
|
|
|
|
|
|
// className="bg-white rounded-2xl shadow-sm border border-gray-200 overflow-hidden hover:shadow-md transition-all"
|
|
|
|
|
|
// >
|
|
|
|
|
|
// <div className="p-5">
|
|
|
|
|
|
// <div className="flex justify-between items-start mb-4">
|
|
|
|
|
|
// <div>
|
2026-04-22 10:52:08 +03:00
|
|
|
|
// <h3 className="font-bold text-lg text-gray-900">{property.title}</h3>
|
|
|
|
|
|
// {property.isNotSeized && (
|
|
|
|
|
|
// <span className="inline-block mt-1 px-2 py-0.5 bg-amber-100 text-amber-800 rounded-full text-xs font-medium">
|
|
|
|
|
|
// غير محجوز
|
|
|
|
|
|
// </span>
|
|
|
|
|
|
// )}
|
2026-04-01 01:34:51 +03:00
|
|
|
|
// </div>
|
2026-04-22 10:52:08 +03:00
|
|
|
|
// <span className="text-xs text-gray-500">{property.location}</span>
|
2026-04-01 01:34:51 +03:00
|
|
|
|
// </div>
|
|
|
|
|
|
|
2026-04-22 10:52:08 +03:00
|
|
|
|
// <div className="grid grid-cols-3 gap-4 mb-4">
|
|
|
|
|
|
// <div className="text-center">
|
|
|
|
|
|
// <div className="text-sm text-gray-500">الإيرادات</div>
|
|
|
|
|
|
// <div className="text-lg font-bold text-amber-600">{formatCurrency(property.revenue)}</div>
|
|
|
|
|
|
// </div>
|
|
|
|
|
|
// <div className="text-center">
|
|
|
|
|
|
// <div className="text-sm text-gray-500">العمولة</div>
|
|
|
|
|
|
// <div className="text-lg font-bold text-blue-600">{formatCurrency(property.commission)}</div>
|
2026-04-01 01:34:51 +03:00
|
|
|
|
// </div>
|
2026-04-22 10:52:08 +03:00
|
|
|
|
// <div className="text-center">
|
|
|
|
|
|
// <div className="text-sm text-gray-500">المتبقي</div>
|
|
|
|
|
|
// <div className="text-lg font-bold text-green-600">{formatCurrency(property.remaining)}</div>
|
2026-04-01 01:34:51 +03:00
|
|
|
|
// </div>
|
|
|
|
|
|
// </div>
|
|
|
|
|
|
|
2026-04-22 10:52:08 +03:00
|
|
|
|
// <div className="flex justify-between items-center pt-3 border-t border-gray-100">
|
|
|
|
|
|
// <div className="flex items-center gap-2">
|
|
|
|
|
|
// <Star className="w-4 h-4 text-amber-500" />
|
|
|
|
|
|
// <span className="text-sm font-medium text-gray-700">التقييم العام:</span>
|
|
|
|
|
|
// <span className="text-sm font-medium text-gray-900">{property.valuation}</span>
|
2026-04-01 01:34:51 +03:00
|
|
|
|
// </div>
|
2026-04-22 10:52:08 +03:00
|
|
|
|
// <div className="flex items-center gap-1 text-sm text-gray-500">
|
|
|
|
|
|
// <CalendarDays className="w-4 h-4" />
|
|
|
|
|
|
// <span>مؤجر {property.rentedCount} مرة</span>
|
2026-04-01 01:34:51 +03:00
|
|
|
|
// </div>
|
|
|
|
|
|
// </div>
|
|
|
|
|
|
|
|
|
|
|
|
// <button
|
|
|
|
|
|
// onClick={() => onViewDetails(property)}
|
2026-04-22 10:52:08 +03:00
|
|
|
|
// className="w-full mt-4 py-2 bg-gray-100 text-gray-700 rounded-xl text-sm font-medium hover:bg-gray-200 transition-colors flex items-center justify-center gap-2"
|
2026-04-01 01:34:51 +03:00
|
|
|
|
// >
|
|
|
|
|
|
// <Eye className="w-4 h-4" />
|
|
|
|
|
|
// عرض التفاصيل
|
|
|
|
|
|
// </button>
|
|
|
|
|
|
// </div>
|
|
|
|
|
|
// </motion.div>
|
|
|
|
|
|
// );
|
|
|
|
|
|
// };
|
|
|
|
|
|
|
2026-04-22 10:52:08 +03:00
|
|
|
|
// const PropertyCalendar = ({ year, month }) => {
|
|
|
|
|
|
// const [currentMonth, setCurrentMonth] = useState(new Date(year, month - 1));
|
|
|
|
|
|
// const monthNames = ['يناير', 'فبراير', 'مارس', 'إبريل', 'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر', 'أكتوبر', 'نوفمبر', 'ديسمبر'];
|
|
|
|
|
|
// const weekDays = ['إثنين', 'ثلاثاء', 'أربعاء', 'خميس', 'جمعة', 'سبت', 'أحد'];
|
2026-04-01 01:34:51 +03:00
|
|
|
|
|
2026-04-22 10:52:08 +03:00
|
|
|
|
// const getDaysInMonth = (date) => {
|
|
|
|
|
|
// return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate();
|
2026-04-01 01:34:51 +03:00
|
|
|
|
// };
|
|
|
|
|
|
|
2026-04-22 10:52:08 +03:00
|
|
|
|
// const getFirstDayOfMonth = (date) => {
|
|
|
|
|
|
// const day = new Date(date.getFullYear(), date.getMonth(), 1).getDay();
|
|
|
|
|
|
// return day === 0 ? 6 : day - 1;
|
|
|
|
|
|
// };
|
2026-04-01 01:34:51 +03:00
|
|
|
|
|
2026-04-22 10:52:08 +03:00
|
|
|
|
// const daysInMonth = getDaysInMonth(currentMonth);
|
|
|
|
|
|
// const firstDayIndex = getFirstDayOfMonth(currentMonth);
|
2026-04-01 01:34:51 +03:00
|
|
|
|
|
2026-04-22 10:52:08 +03:00
|
|
|
|
// const cells = [];
|
|
|
|
|
|
// for (let i = 0; i < firstDayIndex; i++) {
|
|
|
|
|
|
// cells.push(<div key={`empty-${i}`} className="p-2 md:p-3 text-center" />);
|
|
|
|
|
|
// }
|
|
|
|
|
|
// for (let d = 1; d <= daysInMonth; d++) {
|
|
|
|
|
|
// cells.push(
|
|
|
|
|
|
// <div
|
|
|
|
|
|
// key={d}
|
|
|
|
|
|
// className="p-2 md:p-3 text-center rounded-xl hover:bg-gray-100 transition-colors"
|
2026-04-01 01:34:51 +03:00
|
|
|
|
// >
|
2026-04-22 10:52:08 +03:00
|
|
|
|
// {d}
|
|
|
|
|
|
// </div>
|
|
|
|
|
|
// );
|
|
|
|
|
|
// }
|
2026-04-01 01:34:51 +03:00
|
|
|
|
|
2026-04-22 10:52:08 +03:00
|
|
|
|
// return (
|
|
|
|
|
|
// <div className="bg-white rounded-2xl shadow-sm border border-gray-200 p-6">
|
|
|
|
|
|
// <div className="flex justify-between items-center mb-6">
|
|
|
|
|
|
// <h3 className="text-lg font-bold text-gray-900 flex items-center gap-2">
|
|
|
|
|
|
// <CalendarDays className="w-5 h-5 text-amber-500" />
|
|
|
|
|
|
// {monthNames[currentMonth.getMonth()]} {currentMonth.getFullYear()}
|
|
|
|
|
|
// </h3>
|
|
|
|
|
|
// <div className="flex gap-2">
|
|
|
|
|
|
// <button
|
|
|
|
|
|
// onClick={() => setCurrentMonth(new Date(currentMonth.getFullYear(), currentMonth.getMonth() - 1, 1))}
|
|
|
|
|
|
// className="p-2 hover:bg-gray-100 rounded-lg"
|
|
|
|
|
|
// >
|
|
|
|
|
|
// ←
|
|
|
|
|
|
// </button>
|
|
|
|
|
|
// <button
|
|
|
|
|
|
// onClick={() => setCurrentMonth(new Date(currentMonth.getFullYear(), currentMonth.getMonth() + 1, 1))}
|
|
|
|
|
|
// className="p-2 hover:bg-gray-100 rounded-lg"
|
|
|
|
|
|
// >
|
|
|
|
|
|
// →
|
|
|
|
|
|
// </button>
|
|
|
|
|
|
// </div>
|
|
|
|
|
|
// </div>
|
2026-04-01 01:34:51 +03:00
|
|
|
|
|
2026-04-22 10:52:08 +03:00
|
|
|
|
// <div className="grid grid-cols-7 gap-1 mb-3 text-center text-sm font-medium text-gray-500">
|
|
|
|
|
|
// {weekDays.map(day => (
|
|
|
|
|
|
// <div key={day}>{day}</div>
|
|
|
|
|
|
// ))}
|
|
|
|
|
|
// </div>
|
2026-04-01 01:34:51 +03:00
|
|
|
|
|
2026-04-22 10:52:08 +03:00
|
|
|
|
// <div className="grid grid-cols-7 gap-1">
|
|
|
|
|
|
// {cells}
|
|
|
|
|
|
// </div>
|
|
|
|
|
|
// </div>
|
2026-04-01 01:34:51 +03:00
|
|
|
|
// );
|
|
|
|
|
|
// };
|
|
|
|
|
|
|
|
|
|
|
|
// export default function OwnerProfitsPage() {
|
|
|
|
|
|
// const router = useRouter();
|
|
|
|
|
|
// const [user, setUser] = useState(null);
|
|
|
|
|
|
// const [isLoading, setIsLoading] = useState(true);
|
|
|
|
|
|
|
2026-04-22 10:52:08 +03:00
|
|
|
|
// const [summary] = useState({
|
|
|
|
|
|
// totalRevenue: 4290,
|
|
|
|
|
|
// totalCommission: 644,
|
|
|
|
|
|
// remainingBalance: 3647,
|
|
|
|
|
|
// });
|
|
|
|
|
|
|
|
|
|
|
|
// const [properties] = useState([
|
|
|
|
|
|
// {
|
|
|
|
|
|
// id: 1,
|
|
|
|
|
|
// title: 'Damascus Olive Residence',
|
|
|
|
|
|
// location: 'دمشق، المزة',
|
|
|
|
|
|
// isNotSeized: true,
|
|
|
|
|
|
// revenue: 3240,
|
|
|
|
|
|
// commission: 486,
|
|
|
|
|
|
// remaining: 2754,
|
|
|
|
|
|
// valuation: 'جيد جدا',
|
|
|
|
|
|
// rentedCount: 18,
|
|
|
|
|
|
// },
|
|
|
|
|
|
// ]);
|
|
|
|
|
|
|
|
|
|
|
|
// useEffect(() => {
|
|
|
|
|
|
// if (AuthService.isGuest()) {
|
|
|
|
|
|
// router.push('/auth/choose-role');
|
|
|
|
|
|
// return;
|
|
|
|
|
|
// }
|
|
|
|
|
|
// if (!AuthService.isOwner()) {
|
|
|
|
|
|
// router.push('/');
|
|
|
|
|
|
// return;
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
2026-04-01 01:34:51 +03:00
|
|
|
|
// const authUser = AuthService.getUser();
|
2026-04-22 10:52:08 +03:00
|
|
|
|
// if (authUser) {
|
2026-04-01 01:34:51 +03:00
|
|
|
|
// setUser({
|
|
|
|
|
|
// name: authUser.name || authUser.email,
|
|
|
|
|
|
// email: authUser.email,
|
|
|
|
|
|
// });
|
|
|
|
|
|
// }
|
|
|
|
|
|
// setIsLoading(false);
|
2026-04-22 10:52:08 +03:00
|
|
|
|
// }, [router]);
|
2026-04-01 01:34:51 +03:00
|
|
|
|
|
2026-04-22 10:52:08 +03:00
|
|
|
|
// const formatCurrency = (amount) => `$${amount?.toLocaleString()}`;
|
|
|
|
|
|
|
|
|
|
|
|
// const handleViewDetails = (property) => {
|
|
|
|
|
|
// toast.info(`عرض تفاصيل ${property.title}`);
|
2026-04-01 01:34:51 +03:00
|
|
|
|
// };
|
|
|
|
|
|
|
2026-04-22 10:52:08 +03:00
|
|
|
|
// const handleExportReport = () => {
|
|
|
|
|
|
// toast.success('جاري تصدير التقرير...');
|
2026-04-01 01:34:51 +03:00
|
|
|
|
// };
|
|
|
|
|
|
|
|
|
|
|
|
// if (isLoading) {
|
|
|
|
|
|
// return (
|
2026-04-22 10:52:08 +03:00
|
|
|
|
// <div className="min-h-screen flex items-center justify-center">
|
2026-04-01 01:34:51 +03:00
|
|
|
|
// <div className="text-center">
|
2026-04-22 10:52:08 +03:00
|
|
|
|
// <div className="w-16 h-16 border-4 border-amber-500 border-t-transparent rounded-full animate-spin mx-auto mb-4" />
|
|
|
|
|
|
// <p className="text-gray-600">جاري التحميل...</p>
|
2026-04-01 01:34:51 +03:00
|
|
|
|
// </div>
|
|
|
|
|
|
// </div>
|
|
|
|
|
|
// );
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
// return (
|
|
|
|
|
|
// <div className="min-h-screen bg-gray-50 py-8">
|
|
|
|
|
|
// <Toaster position="top-center" reverseOrder={false} />
|
2026-04-22 10:52:08 +03:00
|
|
|
|
// <div className="container mx-auto px-4 max-w-6xl">
|
|
|
|
|
|
// <div className="mb-8">
|
|
|
|
|
|
// <h1 className="text-3xl font-bold text-gray-900 mb-2">دفتر الحسابات</h1>
|
|
|
|
|
|
// <p className="text-gray-600">نظرة عامة على أرباح المالك</p>
|
|
|
|
|
|
// </div>
|
2026-04-01 01:34:51 +03:00
|
|
|
|
|
2026-04-22 10:52:08 +03:00
|
|
|
|
// <div className="grid grid-cols-1 md:grid-cols-3 gap-6 mb-12">
|
2026-04-01 01:34:51 +03:00
|
|
|
|
// <StatCard
|
2026-04-22 10:52:08 +03:00
|
|
|
|
// title="الإيرادات"
|
|
|
|
|
|
// value={formatCurrency(summary.totalRevenue)}
|
|
|
|
|
|
// icon={DollarSign}
|
|
|
|
|
|
// color="bg-green-500"
|
2026-04-01 01:34:51 +03:00
|
|
|
|
// />
|
|
|
|
|
|
// <StatCard
|
2026-04-22 10:52:08 +03:00
|
|
|
|
// title="العمولة"
|
|
|
|
|
|
// value={formatCurrency(summary.totalCommission)}
|
|
|
|
|
|
// icon={TrendingUp}
|
2026-04-01 01:34:51 +03:00
|
|
|
|
// color="bg-blue-500"
|
|
|
|
|
|
// />
|
|
|
|
|
|
// <StatCard
|
2026-04-22 10:52:08 +03:00
|
|
|
|
// title="المتبقي"
|
|
|
|
|
|
// value={formatCurrency(summary.remainingBalance)}
|
|
|
|
|
|
// icon={Wallet}
|
|
|
|
|
|
// color="bg-amber-500"
|
2026-04-01 01:34:51 +03:00
|
|
|
|
// />
|
|
|
|
|
|
// </div>
|
|
|
|
|
|
|
2026-04-22 10:52:08 +03:00
|
|
|
|
// <div className="mb-12">
|
|
|
|
|
|
// <h2 className="text-xl font-bold text-gray-900 mb-4">عقاراتي</h2>
|
|
|
|
|
|
// <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
|
|
|
|
// {properties.map((property) => (
|
|
|
|
|
|
// <PropertyProfitCard
|
|
|
|
|
|
// key={property.id}
|
|
|
|
|
|
// property={property}
|
|
|
|
|
|
// onViewDetails={handleViewDetails}
|
|
|
|
|
|
// />
|
|
|
|
|
|
// ))}
|
2026-04-01 01:34:51 +03:00
|
|
|
|
// </div>
|
2026-04-22 10:52:08 +03:00
|
|
|
|
// </div>
|
2026-04-01 01:34:51 +03:00
|
|
|
|
|
2026-04-22 10:52:08 +03:00
|
|
|
|
// <div className="mb-12">
|
|
|
|
|
|
// <h2 className="text-xl font-bold text-gray-900 mb-4">تقويم العقار</h2>
|
|
|
|
|
|
// <PropertyCalendar year={2026} month={3} />
|
2026-04-01 01:34:51 +03:00
|
|
|
|
// </div>
|
|
|
|
|
|
|
2026-04-22 10:52:08 +03:00
|
|
|
|
// {/* <div className="flex justify-end">
|
|
|
|
|
|
// <button
|
|
|
|
|
|
// onClick={handleExportReport}
|
|
|
|
|
|
// className="px-6 py-3 bg-amber-500 text-white rounded-xl font-medium hover:bg-amber-600 transition-colors flex items-center justify-center gap-2"
|
|
|
|
|
|
// >
|
|
|
|
|
|
// <Download className="w-5 h-5" />
|
|
|
|
|
|
// تصدير التقرير
|
|
|
|
|
|
// </button>
|
|
|
|
|
|
// </div> */}
|
2026-04-01 01:34:51 +03:00
|
|
|
|
// </div>
|
|
|
|
|
|
// </div>
|
|
|
|
|
|
// );
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
2026-03-20 13:20:30 +03:00
|
|
|
|
'use client';
|
|
|
|
|
|
|
|
|
|
|
|
import { useState, useEffect } from 'react';
|
|
|
|
|
|
import { motion } from 'framer-motion';
|
|
|
|
|
|
import { useRouter } from 'next/navigation';
|
2026-04-22 10:52:08 +03:00
|
|
|
|
import { Download, Loader2 } from 'lucide-react';
|
2026-03-20 13:20:30 +03:00
|
|
|
|
import toast, { Toaster } from 'react-hot-toast';
|
2026-04-22 10:52:08 +03:00
|
|
|
|
import * as XLSX from 'xlsx';
|
2026-04-01 01:34:51 +03:00
|
|
|
|
import AuthService from '@/app/services/AuthService';
|
2026-03-20 13:20:30 +03:00
|
|
|
|
|
|
|
|
|
|
export default function OwnerProfitsPage() {
|
|
|
|
|
|
const router = useRouter();
|
|
|
|
|
|
const [user, setUser] = useState(null);
|
|
|
|
|
|
const [isLoading, setIsLoading] = useState(true);
|
2026-04-22 10:52:08 +03:00
|
|
|
|
const [tableData, setTableData] = useState([]);
|
2026-03-28 14:53:45 +00:00
|
|
|
|
|
2026-04-22 10:52:08 +03:00
|
|
|
|
const sampleData = [
|
2026-04-01 01:34:51 +03:00
|
|
|
|
{
|
|
|
|
|
|
id: 1,
|
2026-04-22 10:52:08 +03:00
|
|
|
|
property: 'A000000001',
|
|
|
|
|
|
bookingNumber: 'XX-101',
|
|
|
|
|
|
fromDate: '2025-05-01',
|
|
|
|
|
|
toDate: '2025-05-07',
|
|
|
|
|
|
amountReceived: 500,
|
|
|
|
|
|
platformCommission: 0,
|
|
|
|
|
|
transferredToOwner: 0,
|
|
|
|
|
|
transferReceipt: '—',
|
2026-04-01 01:34:51 +03:00
|
|
|
|
},
|
2026-04-22 10:52:08 +03:00
|
|
|
|
{
|
|
|
|
|
|
id: 2,
|
|
|
|
|
|
property: 'A000000002',
|
|
|
|
|
|
bookingNumber: 'XX-202',
|
|
|
|
|
|
fromDate: '2025-05-10',
|
|
|
|
|
|
toDate: '2025-05-15',
|
|
|
|
|
|
amountReceived: 300,
|
|
|
|
|
|
platformCommission: 0,
|
|
|
|
|
|
transferredToOwner: 0,
|
|
|
|
|
|
transferReceipt: '—',
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
id: 3,
|
|
|
|
|
|
property: 'A000000003',
|
|
|
|
|
|
bookingNumber: 'XX-309',
|
|
|
|
|
|
fromDate: '2025-06-01',
|
|
|
|
|
|
toDate: '2025-06-05',
|
|
|
|
|
|
amountReceived: 800,
|
|
|
|
|
|
platformCommission: 150,
|
|
|
|
|
|
transferredToOwner: 0,
|
|
|
|
|
|
transferReceipt: 'قيد الانتظار',
|
|
|
|
|
|
},
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const computeRows = (data) => {
|
|
|
|
|
|
return data.map((item) => {
|
|
|
|
|
|
const platformProfit = item.amountReceived * 0.05;
|
|
|
|
|
|
const ownerDue = item.amountReceived - platformProfit;
|
|
|
|
|
|
return {
|
|
|
|
|
|
...item,
|
|
|
|
|
|
platformProfit,
|
|
|
|
|
|
ownerDue,
|
|
|
|
|
|
};
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
2026-03-20 13:20:30 +03:00
|
|
|
|
|
2026-04-01 01:34:51 +03:00
|
|
|
|
useEffect(() => {
|
2026-04-01 01:46:48 +03:00
|
|
|
|
if (AuthService.isGuest()) {
|
2026-04-01 01:34:51 +03:00
|
|
|
|
router.push('/auth/choose-role');
|
2026-04-01 01:46:48 +03:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!AuthService.isOwner()) {
|
|
|
|
|
|
router.push('/');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const authUser = AuthService.getUser();
|
|
|
|
|
|
if (authUser) {
|
|
|
|
|
|
setUser({
|
|
|
|
|
|
name: authUser.name || authUser.email,
|
|
|
|
|
|
email: authUser.email,
|
|
|
|
|
|
});
|
2026-03-20 13:20:30 +03:00
|
|
|
|
}
|
2026-04-22 10:52:08 +03:00
|
|
|
|
|
|
|
|
|
|
const stored = localStorage.getItem('ownerProfitsTable');
|
|
|
|
|
|
if (stored) {
|
|
|
|
|
|
setTableData(computeRows(JSON.parse(stored)));
|
|
|
|
|
|
} else {
|
|
|
|
|
|
setTableData(computeRows(sampleData));
|
|
|
|
|
|
localStorage.setItem('ownerProfitsTable', JSON.stringify(sampleData));
|
|
|
|
|
|
}
|
2026-03-20 13:20:30 +03:00
|
|
|
|
setIsLoading(false);
|
2026-04-01 01:34:51 +03:00
|
|
|
|
}, [router]);
|
|
|
|
|
|
|
2026-04-22 10:52:08 +03:00
|
|
|
|
const totals = tableData.reduce(
|
|
|
|
|
|
(acc, row) => {
|
|
|
|
|
|
acc.totalAmountReceived += row.amountReceived;
|
|
|
|
|
|
acc.totalCommission += row.platformCommission;
|
|
|
|
|
|
acc.totalPlatformProfit += row.platformProfit;
|
|
|
|
|
|
acc.totalOwnerDue += row.ownerDue;
|
|
|
|
|
|
acc.totalTransferred += row.transferredToOwner;
|
|
|
|
|
|
return acc;
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
totalAmountReceived: 0,
|
|
|
|
|
|
totalCommission: 0,
|
|
|
|
|
|
totalPlatformProfit: 0,
|
|
|
|
|
|
totalOwnerDue: 0,
|
|
|
|
|
|
totalTransferred: 0,
|
|
|
|
|
|
}
|
|
|
|
|
|
);
|
2026-03-20 13:20:30 +03:00
|
|
|
|
|
2026-04-01 01:34:51 +03:00
|
|
|
|
const handleExportReport = () => {
|
2026-04-22 10:52:08 +03:00
|
|
|
|
try {
|
|
|
|
|
|
const exportData = tableData.map((row) => ({
|
|
|
|
|
|
'العقار': row.property,
|
|
|
|
|
|
'رقم الحجز': row.bookingNumber,
|
|
|
|
|
|
'من تاريخ': row.fromDate,
|
|
|
|
|
|
'حتى تاريخ': row.toDate,
|
|
|
|
|
|
'العروض المستلم': row.amountReceived,
|
|
|
|
|
|
'عمولة المنصة': row.platformCommission,
|
|
|
|
|
|
'ربح المنصة (5%)': row.platformProfit,
|
|
|
|
|
|
'المستحق للمالك': row.ownerDue,
|
|
|
|
|
|
'تم التحويل للمالك': row.transferredToOwner,
|
|
|
|
|
|
'رقم وصل التحويل': row.transferReceipt,
|
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
|
|
exportData.push({
|
|
|
|
|
|
'العقار': 'الإجمالي العام',
|
|
|
|
|
|
'رقم الحجز': '',
|
|
|
|
|
|
'من تاريخ': '',
|
|
|
|
|
|
'حتى تاريخ': '',
|
|
|
|
|
|
'العروض المستلم': totals.totalAmountReceived,
|
|
|
|
|
|
'عمولة المنصة': totals.totalCommission,
|
|
|
|
|
|
'ربح المنصة (5%)': totals.totalPlatformProfit,
|
|
|
|
|
|
'المستحق للمالك': totals.totalOwnerDue,
|
|
|
|
|
|
'تم التحويل للمالك': totals.totalTransferred,
|
|
|
|
|
|
'رقم وصل التحويل': '—',
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const worksheet = XLSX.utils.json_to_sheet(exportData);
|
|
|
|
|
|
const colWidths = [
|
|
|
|
|
|
{ wch: 15 },
|
|
|
|
|
|
{ wch: 12 },
|
|
|
|
|
|
{ wch: 12 },
|
|
|
|
|
|
{ wch: 12 },
|
|
|
|
|
|
{ wch: 14 },
|
|
|
|
|
|
{ wch: 14 },
|
|
|
|
|
|
{ wch: 16 },
|
|
|
|
|
|
{ wch: 16 },
|
|
|
|
|
|
{ wch: 16 },
|
|
|
|
|
|
{ wch: 18 },
|
|
|
|
|
|
];
|
|
|
|
|
|
worksheet['!cols'] = colWidths;
|
|
|
|
|
|
|
|
|
|
|
|
const workbook = XLSX.utils.book_new();
|
|
|
|
|
|
XLSX.utils.book_append_sheet(workbook, worksheet, 'أرباح المالك');
|
|
|
|
|
|
|
|
|
|
|
|
XLSX.writeFile(workbook, `تقرير_الأرباح_${new Date().toISOString().slice(0,19).replace(/:/g, '-')}.xlsx`);
|
|
|
|
|
|
|
|
|
|
|
|
toast.success('تم تصدير التقرير بنجاح!');
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('خطأ في التصدير:', error);
|
|
|
|
|
|
toast.error('حدث خطأ أثناء تصدير التقرير');
|
|
|
|
|
|
}
|
2026-03-20 13:20:30 +03:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
if (isLoading) {
|
|
|
|
|
|
return (
|
2026-04-22 10:52:08 +03:00
|
|
|
|
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
|
2026-03-20 13:20:30 +03:00
|
|
|
|
<div className="text-center">
|
2026-04-22 10:52:08 +03:00
|
|
|
|
<Loader2 className="w-12 h-12 text-amber-500 animate-spin mx-auto mb-4" />
|
|
|
|
|
|
<p className="text-gray-600">جاري تحميل بيانات الأرباح...</p>
|
2026-03-20 13:20:30 +03:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
2026-04-22 10:52:08 +03:00
|
|
|
|
<div className="min-h-screen bg-gray-50 py-8" dir="rtl">
|
2026-03-20 13:20:30 +03:00
|
|
|
|
<Toaster position="top-center" reverseOrder={false} />
|
2026-04-22 10:52:08 +03:00
|
|
|
|
<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>
|
|
|
|
|
|
<h1 className="text-3xl font-bold text-gray-900 mb-2">أرباح المالك</h1>
|
|
|
|
|
|
<p className="text-gray-600">
|
|
|
|
|
|
مرحباً {user?.name}
|
|
|
|
|
|
</p>
|
2026-03-20 13:20:30 +03:00
|
|
|
|
</div>
|
2026-04-01 01:34:51 +03:00
|
|
|
|
<button
|
|
|
|
|
|
onClick={handleExportReport}
|
2026-04-22 10:52:08 +03:00
|
|
|
|
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"
|
2026-04-01 01:34:51 +03:00
|
|
|
|
>
|
|
|
|
|
|
<Download className="w-5 h-5" />
|
|
|
|
|
|
تصدير التقرير
|
|
|
|
|
|
</button>
|
2026-04-22 10:52:08 +03:00
|
|
|
|
</motion.div>
|
|
|
|
|
|
|
|
|
|
|
|
<motion.div
|
|
|
|
|
|
initial={{ opacity: 0, y: 20 }}
|
|
|
|
|
|
animate={{ opacity: 1, y: 0 }}
|
|
|
|
|
|
className="bg-white rounded-2xl shadow-lg border border-gray-200 overflow-hidden"
|
|
|
|
|
|
>
|
|
|
|
|
|
<div className="overflow-x-auto">
|
|
|
|
|
|
<table className="min-w-full divide-y divide-gray-200 text-sm">
|
|
|
|
|
|
<thead className="bg-gray-800 text-gray-100">
|
|
|
|
|
|
<tr>
|
|
|
|
|
|
<th className="px-4 py-4 text-center text-xs font-semibold uppercase tracking-wider">العقار</th>
|
|
|
|
|
|
<th className="px-4 py-4 text-center text-xs font-semibold uppercase tracking-wider">رقم الحجز</th>
|
|
|
|
|
|
<th className="px-4 py-4 text-center text-xs font-semibold uppercase tracking-wider">من تاريخ</th>
|
|
|
|
|
|
<th className="px-4 py-4 text-center text-xs font-semibold uppercase tracking-wider">حتى تاريخ</th>
|
|
|
|
|
|
<th className="px-4 py-4 text-center text-xs font-semibold uppercase tracking-wider">العروض المستلم</th>
|
|
|
|
|
|
<th className="px-4 py-4 text-center text-xs font-semibold uppercase tracking-wider">عمولة المنصة</th>
|
|
|
|
|
|
<th className="px-4 py-4 text-center text-xs font-semibold uppercase tracking-wider bg-amber-50 text-amber-800">
|
|
|
|
|
|
ربح المنصة <span className="font-normal text-[11px] block">(5% من العربون)</span>
|
|
|
|
|
|
</th>
|
|
|
|
|
|
<th className="px-4 py-4 text-center text-xs font-semibold uppercase tracking-wider">المستحق للمالك</th>
|
|
|
|
|
|
<th className="px-4 py-4 text-center text-xs font-semibold uppercase tracking-wider">تم التحويل للمالك</th>
|
|
|
|
|
|
<th className="px-4 py-4 text-center text-xs font-semibold uppercase tracking-wider">رقم وصل التحويل</th>
|
|
|
|
|
|
</tr>
|
|
|
|
|
|
</thead>
|
|
|
|
|
|
<tbody className="bg-white divide-y divide-gray-100">
|
|
|
|
|
|
{tableData.map((row, idx) => (
|
|
|
|
|
|
<tr
|
|
|
|
|
|
key={row.id}
|
|
|
|
|
|
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 font-medium text-gray-800">
|
|
|
|
|
|
{row.property}
|
|
|
|
|
|
</td>
|
|
|
|
|
|
<td className="px-4 py-3 whitespace-nowrap text-center text-gray-700">
|
|
|
|
|
|
{row.bookingNumber}
|
|
|
|
|
|
</td>
|
|
|
|
|
|
<td className="px-4 py-3 whitespace-nowrap text-center text-gray-700">
|
|
|
|
|
|
{row.fromDate}
|
|
|
|
|
|
</td>
|
|
|
|
|
|
<td className="px-4 py-3 whitespace-nowrap text-center text-gray-700">
|
|
|
|
|
|
{row.toDate}
|
|
|
|
|
|
</td>
|
|
|
|
|
|
<td className="px-4 py-3 whitespace-nowrap text-center font-mono font-semibold text-gray-800">
|
|
|
|
|
|
{row.amountReceived}
|
|
|
|
|
|
</td>
|
|
|
|
|
|
<td className="px-4 py-3 whitespace-nowrap text-center font-mono text-gray-700">
|
|
|
|
|
|
{row.platformCommission}
|
|
|
|
|
|
</td>
|
|
|
|
|
|
<td className="px-4 py-3 whitespace-nowrap text-center font-mono font-bold text-amber-700 bg-amber-50/50">
|
|
|
|
|
|
{row.platformProfit}
|
|
|
|
|
|
</td>
|
|
|
|
|
|
<td className="px-4 py-3 whitespace-nowrap text-center font-mono font-semibold text-emerald-700">
|
|
|
|
|
|
{row.ownerDue}
|
|
|
|
|
|
</td>
|
|
|
|
|
|
<td className="px-4 py-3 whitespace-nowrap text-center font-mono text-gray-700">
|
|
|
|
|
|
{row.transferredToOwner}
|
|
|
|
|
|
</td>
|
|
|
|
|
|
<td className="px-4 py-3 whitespace-nowrap text-center text-gray-500 text-xs">
|
|
|
|
|
|
{row.transferReceipt}
|
|
|
|
|
|
</td>
|
|
|
|
|
|
</tr>
|
|
|
|
|
|
))}
|
|
|
|
|
|
</tbody>
|
|
|
|
|
|
<tfoot className="bg-gray-100 border-t-2 border-gray-300">
|
|
|
|
|
|
<tr>
|
|
|
|
|
|
<td colSpan="4" className="px-4 py-4 text-right font-bold text-gray-800">
|
|
|
|
|
|
الإجمالي العام
|
|
|
|
|
|
</td>
|
|
|
|
|
|
<td className="px-4 py-4 text-center font-bold font-mono text-gray-800">
|
|
|
|
|
|
{totals.totalAmountReceived}
|
|
|
|
|
|
</td>
|
|
|
|
|
|
<td className="px-4 py-4 text-center font-bold font-mono text-gray-800">
|
|
|
|
|
|
{totals.totalCommission}
|
|
|
|
|
|
</td>
|
|
|
|
|
|
<td className="px-4 py-4 text-center font-bold font-mono text-amber-700 bg-amber-100/60">
|
|
|
|
|
|
{totals.totalPlatformProfit}
|
|
|
|
|
|
</td>
|
|
|
|
|
|
<td className="px-4 py-4 text-center font-bold font-mono text-emerald-700">
|
|
|
|
|
|
{totals.totalOwnerDue}
|
|
|
|
|
|
</td>
|
|
|
|
|
|
<td className="px-4 py-4 text-center font-bold font-mono text-gray-800">
|
|
|
|
|
|
{totals.totalTransferred}
|
|
|
|
|
|
</td>
|
|
|
|
|
|
<td className="px-4 py-4 text-center text-gray-500">—</td>
|
|
|
|
|
|
</tr>
|
|
|
|
|
|
</tfoot>
|
|
|
|
|
|
</table>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="bg-gray-50 px-6 py-3 text-xs text-gray-500 border-t border-gray-200">
|
|
|
|
|
|
<span className="inline-flex items-center gap-1"></span> ملاحظة:
|
|
|
|
|
|
<strong> ربح المنصة </strong> يُحتسب تلقائياً بنسبة <strong className="text-amber-600">5%</strong> من قيمة «العروض المستلم».
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</motion.div>
|
2026-03-20 13:20:30 +03:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
);
|
2026-04-01 01:34:51 +03:00
|
|
|
|
}
|