Files
SweetHome/app/owner/profits/page.js

592 lines
22 KiB
JavaScript
Raw Normal View History

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"
// >
// &larr;
// </button>
// <button
// onClick={() => setCurrentMonth(new Date(currentMonth.getFullYear(), currentMonth.getMonth() + 1, 1))}
// className="p-2 hover:bg-gray-100 rounded-lg"
// >
// &rarr;
// </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>
// );
// }
'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';
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';
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-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-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-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));
}
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-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('حدث خطأ أثناء تصدير التقرير');
}
};
if (isLoading) {
return (
2026-04-22 10:52:08 +03:00
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
<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>
</div>
</div>
);
}
return (
2026-04-22 10:52:08 +03:00
<div className="min-h-screen bg-gray-50 py-8" dir="rtl">
<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>
</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>
</div>
</div>
);
2026-04-01 01:34:51 +03:00
}