diff --git a/app/admin/add-admin/page.js b/app/admin/add-admin/page.js
deleted file mode 100644
index 3a7dce9..0000000
--- a/app/admin/add-admin/page.js
+++ /dev/null
@@ -1,113 +0,0 @@
-'use client';
-
-import { useEffect, useState } from 'react';
-import AuthService from '@/app/services/AuthService';
-import Link from 'next/link';
-
-export default function AddAdminPage() {
- const [isAdmin, setIsAdmin] = useState(false);
- const [checked, setChecked] = useState(false);
- const [formState, setFormState] = useState({ fullName: '', email: '', password: '' });
- const [saved, setSaved] = useState(false);
-
- useEffect(() => {
- setIsAdmin(AuthService.isAuthenticated() && AuthService.isAdmin());
- setChecked(true);
- }, []);
-
- const handleChange = (field) => (event) => {
- setFormState((prev) => ({ ...prev, [field]: event.target.value }));
- };
-
- const handleSubmit = (event) => {
- event.preventDefault();
- setSaved(true);
- console.log('Add admin payload', formState);
- };
-
- if (!checked) {
- return (
-
- );
- }
-
- if (!isAdmin) {
- return (
-
-
-
- العودة للرئيسية
-
-
-
- );
- }
-
- return (
-
-
-
-
-
لوحة المدير
-
إضافة مدير جديد
-
انشئ حساب مسؤول جديد مع صلاحيات الإدارة.
-
-
-
-
-
- بيانات المدير
-
- {saved && (
-
- تم حفظ بيانات المدير بنجاح
-
- )}
-
-
-
-
- );
-}
diff --git a/app/admin/error.js b/app/admin/error.js
deleted file mode 100644
index a88abe0..0000000
--- a/app/admin/error.js
+++ /dev/null
@@ -1,27 +0,0 @@
-'use client';
-
-import { motion } from 'framer-motion';
-import { AlertTriangle, RefreshCw, Home } from 'lucide-react';
-import Link from 'next/link';
-
-export default function Error({ error, reset }) {
- return (
-
-
-
- حدث خطأ
- نعتذر، حدث خطأ أثناء تحميل الصفحة
-
-
-
- الرئيسية
-
-
-
-
- );
-}
diff --git a/app/admin/loading.js b/app/admin/loading.js
deleted file mode 100644
index 0c01106..0000000
--- a/app/admin/loading.js
+++ /dev/null
@@ -1,14 +0,0 @@
-'use client';
-
-import { motion } from 'framer-motion';
-
-export default function Loading() {
- return (
-
-
-
- جاري التحميل...
-
-
- );
-}
diff --git a/app/admin/page.js b/app/admin/page.js
deleted file mode 100644
index 007988d..0000000
--- a/app/admin/page.js
+++ /dev/null
@@ -1,230 +0,0 @@
-'use client';
-
-import { motion } from 'framer-motion';
-import { useState, useEffect } from 'react';
-import { useTranslation } from 'react-i18next';
-import Link from 'next/link';
-import {
- Home,
- Calendar,
- Users,
- DollarSign,
- TrendingUp,
- Bell,
- Frown
-} from 'lucide-react';
-import DashboardStats from '../components/admin/DashboardStats';
-import PropertiesTable from '../components/admin/PropertiesTable';
-import BookingRequests from '../components/admin/BookingRequests';
-import UsersList from '../components/admin/UsersList';
-import LedgerBook from '../components/admin/LedgerBook';
-import AddPropertyForm from '../components/admin/AddPropertyForm';
-import { PropertyProvider } from '../contexts/PropertyContext';
-import AuthService from '../services/AuthService';
-import '../i18n/config';
-
-export default function AdminPage() {
- const { t, i18n } = useTranslation();
- const [activeTab, setActiveTab] = useState('dashboard');
- const [showAddProperty, setShowAddProperty] = useState(false);
- const [notifications, setNotifications] = useState(3);
- const [isAdmin, setIsAdmin] = useState(false);
- const [checked, setChecked] = useState(false);
-
- useEffect(() => {
- setIsAdmin(AuthService.isAuthenticated() && AuthService.isAdmin());
- setChecked(true);
- }, []);
-
- // ─── 404 for non-admins ───
- if (checked && !isAdmin) {
- return (
-
-
-
-
-
- 404 - الصفحة غير موجودة
- عذراً، لا يمكنك الوصول إلى هذه الصفحة
-
-
- العودة للرئيسية
-
-
-
- );
- }
-
- if (!checked) {
- return (
-
- );
- }
-
- const tabs = [
- { id: 'dashboard', label: 'لوحة التحكم', icon: Home },
- { id: 'properties', label: 'العقارات', icon: Home },
- { id: 'bookings', label: 'طلبات الحجز', icon: Calendar, badge: notifications },
- { id: 'users', label: 'المستخدمين', icon: Users },
- { id: 'ledger', label: 'دفتر الحسابات', icon: DollarSign },
- // { id: 'reports', label: 'التقارير', icon: TrendingUp }
- ];
-
- return (
-
-
-
-
-
-
- {t('adminDashboard')}
-
-
- إدارة العقارات، الحجوزات، والحسابات المالية
-
-
-
-
-
-
-
-
-
- {tabs.map((tab) => {
- const Icon = tab.icon;
- return (
-
- );
- })}
-
-
-
-
- {activeTab === 'dashboard' && (
-
-
-
- )}
-
- {activeTab === 'properties' && (
-
-
-
-
إدارة العقارات
-
إضافة وتعديل العقارات مع تحديد نسب الأرباح
-
-
-
-
-
- )}
-
- {activeTab === 'bookings' && (
-
-
-
- )}
-
- {activeTab === 'users' && (
-
-
-
- )}
-
- {activeTab === 'ledger' && (
-
-
-
- )}
-
- {activeTab === 'reports' && (
-
-
- قريباً... تقارير متقدمة
-
-
- )}
-
-
- {showAddProperty && (
-
setShowAddProperty(false)}
- onSuccess={() => {
- setShowAddProperty(false);
- }}
- />
- )}
-
-
- );
-}
\ No newline at end of file
diff --git a/app/admin/privacy/page.js b/app/admin/privacy/page.js
deleted file mode 100644
index 35bdb5d..0000000
--- a/app/admin/privacy/page.js
+++ /dev/null
@@ -1,85 +0,0 @@
-'use client';
-
-import { useEffect, useState } from 'react';
-import AuthService from '@/app/services/AuthService';
-import Link from 'next/link';
-
-const initialPolicy = `1. نحترم خصوصيتك ونلتزم بحماية بياناتك الشخصية.
-2. يتم استخدام المعلومات لتحسين تجربة المستخدم وتأمين الخدمة.
-3. لا نشارك البيانات مع أطراف خارجية بدون موافقتك.
-4. يمكنك طلب حذف بياناتك من النظام في أي وقت.`;
-
-export default function PrivacyPolicyAdminPage() {
- const [isAdmin, setIsAdmin] = useState(false);
- const [checked, setChecked] = useState(false);
- const [policyText, setPolicyText] = useState(initialPolicy);
- const [saved, setSaved] = useState(false);
-
- useEffect(() => {
- setIsAdmin(AuthService.isAuthenticated() && AuthService.isAdmin());
- setChecked(true);
- }, []);
-
- const handleSave = (event) => {
- event.preventDefault();
- setSaved(true);
- console.log('Privacy policy updated:', policyText);
- };
-
- if (!checked) {
- return (
-
- );
- }
-
- if (!isAdmin) {
- return (
-
-
-
هذه الصفحة لتحرير سياسة الخصوصية ولا يمكن الوصول إليها إلا للمدير.
-
- العودة للرئيسية
-
-
-
- );
- }
- return (
-
-
-
-
-
-
لوحة المدير
-
قم بتحديث نص سياسة الخصوصية
-
-
-
-
-
-
-
- );
-}
diff --git a/app/components/FloatingSidebar.js b/app/components/FloatingSidebar.js
index 51ab56a..428a2bb 100644
--- a/app/components/FloatingSidebar.js
+++ b/app/components/FloatingSidebar.js
@@ -3,12 +3,12 @@
import { useState } from 'react';
import { motion } from 'framer-motion';
import Link from 'next/link';
-import { Heart, Bell, CreditCard, Shield, UserPlus, Settings } from 'lucide-react';
+import { Heart, Bell, CreditCard, Settings } from 'lucide-react';
import { useFavorites } from '@/app/contexts/FavoritesContext';
import { useNotifications } from '@/app/contexts/NotificationsContext';
import AuthService from '@/app/services/AuthService';
-export default function FloatingSidebar({ isRTL, isAdmin }) {
+export default function FloatingSidebar({ isRTL }) {
const { favorites } = useFavorites();
const { unreadCount } = useNotifications();
const [tooltip, setTooltip] = useState(null);
@@ -62,139 +62,97 @@ export default function FloatingSidebar({ isRTL, isAdmin }) {
animate="animate"
>
- {isAdmin ? (
- <>
-
showTooltip('addAdmin')}
- onMouseLeave={hideTooltip}
- >
-
-
-
- {renderTooltip('addAdmin', 'إضافة أدمن')}
-
-
-
showTooltip('editPrivacy')}
- onMouseLeave={hideTooltip}
- >
-
-
-
- {renderTooltip('editPrivacy', 'تعديل سياسة الخصوصية')}
-
- >
- ) : (
- <>
-
showTooltip('favorites')}
- onMouseLeave={hideTooltip}
- >
-
-
-
- {favorites.length > 0 && (
-
- {favorites.length}
-
- )}
-
-
- {renderTooltip('favorites', 'المفضلة')}
-
-
showTooltip('notifications')}
- onMouseLeave={hideTooltip}
- >
-
-
-
- {unreadCount > 0 && (
-
- {unreadCount}
-
- )}
-
-
- {renderTooltip('notifications', 'الإشعارات')}
-
-
showTooltip('payments')}
- onMouseLeave={hideTooltip}
- >
-
-
-
- {renderTooltip('payments', 'المدفوعات')}
-
-
showTooltip('settings')}
- onMouseLeave={hideTooltip}
- >
-
-
-
- {renderTooltip('settings', 'الإعدادات')}
-
- >
- )}
+
showTooltip('favorites')}
+ onMouseLeave={hideTooltip}
+ >
+
+
+
+ {favorites.length > 0 && (
+
+ {favorites.length}
+
+ )}
+
+
+ {renderTooltip('favorites', 'المفضلة')}
+
+
showTooltip('notifications')}
+ onMouseLeave={hideTooltip}
+ >
+
+
+
+ {unreadCount > 0 && (
+
+ {unreadCount}
+
+ )}
+
+
+ {renderTooltip('notifications', 'الإشعارات')}
+
+
showTooltip('payments')}
+ onMouseLeave={hideTooltip}
+ >
+
+
+
+ {renderTooltip('payments', 'المدفوعات')}
+
+
showTooltip('settings')}
+ onMouseLeave={hideTooltip}
+ >
+
+
+
+ {renderTooltip('settings', 'الإعدادات')}
+
);
-}
\ No newline at end of file
+}
diff --git a/app/components/admin/AddPropertyForm.js b/app/components/admin/AddPropertyForm.js
deleted file mode 100644
index d9b45b6..0000000
--- a/app/components/admin/AddPropertyForm.js
+++ /dev/null
@@ -1,357 +0,0 @@
-'use client';
-
-import { useState } from 'react';
-import { motion } from 'framer-motion';
-import { useProperties } from '@/app/contexts/PropertyContext';
-import { CommissionType, CitiesList } from '@/app/enums';
-import { X, MapPin, Home, DollarSign, Percent } from 'lucide-react';
-
-export default function AddPropertyForm({ onClose, onSuccess }) {
- const { addProperty } = useProperties();
- const [formData, setFormData] = useState({
- title: '',
- description: '',
- city: '',
- district: '',
- address: '',
- latitude: '',
- longitude: '',
-
- type: 'apartment',
- bedrooms: 1,
- bathrooms: 1,
- area: 0,
- floor: 1,
-
- dailyPrice: 0,
- commissionRate: 5,
- commissionType: CommissionType.FROM_OWNER,
-
- securityDeposit: 0,
-
- images: [],
- features: [],
-
- status: 'available'
- });
-
- const [selectedFeatures, setSelectedFeatures] = useState([]);
-
- const featuresList = [
- 'مسبح',
- 'حديقة خاصة',
- 'موقف سيارات',
- 'مطبخ مجهز',
- 'تدفئة مركزية',
- 'بلكونة',
- 'نظام أمني',
- 'حديقة كبيرة',
- 'صالة استقبال',
- 'غرفة خادمة',
- 'كراج',
- 'إطلالة بحرية',
- 'تكييف مركزي',
- 'مخزن'
-];
-
- const handleSubmit = async (e) => {
- e.preventDefault();
-
- const propertyData = {
- ...formData,
- features: selectedFeatures,
- priceDisplay: {
- daily: formData.dailyPrice,
- monthly: formData.dailyPrice * 30,
- withCommission: calculateCommissionPrice(formData)
- },
- location: {
- lat: formData.latitude,
- lng: formData.longitude,
- address: formData.address
- }
- };
-
- try {
- await addProperty(propertyData);
- onSuccess?.();
- onClose();
- } catch (error) {
- console.error('Error adding property:', error);
- }
- };
-
- const calculateCommissionPrice = (data) => {
- const { dailyPrice, commissionRate, commissionType } = data;
- const commission = (dailyPrice * commissionRate) / 100;
-
- switch(commissionType) {
- case CommissionType.FROM_TENANT:
- return dailyPrice + commission;
- case CommissionType.FROM_OWNER:
- return dailyPrice;
- case CommissionType.FROM_BOTH:
- return dailyPrice + (commission / 2);
- default:
- return dailyPrice;
- }
- };
-
- return (
-
-
-
-
إضافة عقار جديد
-
-
-
-
-
-
- );
-}
\ No newline at end of file
diff --git a/app/components/admin/BookingRequests.js b/app/components/admin/BookingRequests.js
deleted file mode 100644
index 85459b4..0000000
--- a/app/components/admin/BookingRequests.js
+++ /dev/null
@@ -1,1614 +0,0 @@
-'use client';
-
-import { useCallback, useEffect, useState } from 'react';
-import { motion, AnimatePresence } from 'framer-motion';
-import {
- CheckCircle,
- XCircle,
- Clock,
- User,
- Home,
- Calendar,
- DollarSign,
- AlertCircle,
- Key,
- DoorOpen,
- Shield,
- Phone,
- Mail,
- ChevronDown,
- ChevronUp,
- FileText,
- Download,
- Printer,
- History,
- Loader2,
- CreditCard
-} from 'lucide-react';
-import jsPDF from 'jspdf';
-import html2canvas from 'html2canvas';
-import toast, { Toaster } from 'react-hot-toast';
-import AuthService from '../../services/AuthService';
-import { adminConfirmDeposit, getRentProperty, getReservations } from '../../utils/api';
-
-const RESERVATION_STATUS = Object.freeze({
- pending: 0,
- ownerConfirmed: 1,
- depositPaid: 2,
- depositConfirmed: 3,
- completed: 4,
- cancelled: 5,
-});
-
-const STATUS_KEY_BY_CODE = Object.freeze({
- [RESERVATION_STATUS.pending]: 'pending',
- [RESERVATION_STATUS.ownerConfirmed]: 'ownerConfirmed',
- [RESERVATION_STATUS.depositPaid]: 'depositPaid',
- [RESERVATION_STATUS.depositConfirmed]: 'depositConfirmed',
- [RESERVATION_STATUS.completed]: 'completed',
- [RESERVATION_STATUS.cancelled]: 'cancelled',
-});
-
-const STATUS_META = Object.freeze({
- pending: {
- label: 'قيد الانتظار',
- card: 'border-yellow-400 bg-yellow-50',
- badge: 'bg-yellow-500 text-white',
- pdf: 'pending',
- tabActive: 'bg-yellow-600 text-white shadow-lg',
- tabIdle: 'bg-yellow-100 text-yellow-800 hover:bg-yellow-200',
- },
- ownerConfirmed: {
- label: 'موافقة المالك',
- card: 'border-blue-400 bg-blue-50',
- badge: 'bg-blue-500 text-white',
- pdf: 'ownerConfirmed',
- tabActive: 'bg-blue-600 text-white shadow-lg',
- tabIdle: 'bg-blue-100 text-blue-800 hover:bg-blue-200',
- },
- depositPaid: {
- label: 'تم دفع العربون',
- card: 'border-indigo-400 bg-indigo-50',
- badge: 'bg-indigo-500 text-white',
- pdf: 'depositPaid',
- tabActive: 'bg-indigo-600 text-white shadow-lg',
- tabIdle: 'bg-indigo-100 text-indigo-800 hover:bg-indigo-200',
- },
- depositConfirmed: {
- label: 'تم تأكيد العربون',
- card: 'border-green-400 bg-green-50',
- badge: 'bg-green-500 text-white',
- pdf: 'depositConfirmed',
- tabActive: 'bg-green-600 text-white shadow-lg',
- tabIdle: 'bg-green-100 text-green-800 hover:bg-green-200',
- },
- completed: {
- label: 'منتهي',
- card: 'border-gray-400 bg-gray-50',
- badge: 'bg-gray-500 text-white',
- pdf: 'completed',
- tabActive: 'bg-gray-600 text-white shadow-lg',
- tabIdle: 'bg-gray-100 text-gray-800 hover:bg-gray-200',
- },
- cancelled: {
- label: 'ملغي',
- card: 'border-red-400 bg-red-50',
- badge: 'bg-red-500 text-white',
- pdf: 'cancelled',
- tabActive: 'bg-red-600 text-white shadow-lg',
- tabIdle: 'bg-red-100 text-red-800 hover:bg-red-200',
- },
- unknown: {
- label: 'غير معروف',
- card: 'border-gray-200 bg-white',
- badge: 'bg-gray-200 text-gray-700',
- pdf: 'unknown',
- tabActive: 'bg-gray-600 text-white shadow-lg',
- tabIdle: 'bg-gray-100 text-gray-800 hover:bg-gray-200',
- },
-});
-
-const FILTER_TABS = [
- {
- id: 'all',
- label: 'الكل',
- tabActive: 'bg-gray-600 text-white shadow-lg',
- tabIdle: 'bg-gray-100 text-gray-800 hover:bg-gray-200',
- },
- {
- id: 'pending',
- label: STATUS_META.pending.label,
- tabActive: STATUS_META.pending.tabActive,
- tabIdle: STATUS_META.pending.tabIdle,
- },
- {
- id: 'ownerConfirmed',
- label: STATUS_META.ownerConfirmed.label,
- tabActive: STATUS_META.ownerConfirmed.tabActive,
- tabIdle: STATUS_META.ownerConfirmed.tabIdle,
- },
- {
- id: 'depositPaid',
- label: STATUS_META.depositPaid.label,
- tabActive: STATUS_META.depositPaid.tabActive,
- tabIdle: STATUS_META.depositPaid.tabIdle,
- },
- {
- id: 'depositConfirmed',
- label: STATUS_META.depositConfirmed.label,
- tabActive: STATUS_META.depositConfirmed.tabActive,
- tabIdle: STATUS_META.depositConfirmed.tabIdle,
- },
- {
- id: 'completed',
- label: STATUS_META.completed.label,
- tabActive: STATUS_META.completed.tabActive,
- tabIdle: STATUS_META.completed.tabIdle,
- },
- {
- id: 'cancelled',
- label: STATUS_META.cancelled.label,
- tabActive: STATUS_META.cancelled.tabActive,
- tabIdle: STATUS_META.cancelled.tabIdle,
- },
-];
-
-function getStatusKey(status) {
- return STATUS_KEY_BY_CODE[Number(status)] ?? 'unknown';
-}
-
-function getStatusMeta(status) {
- return STATUS_META[getStatusKey(status)] ?? STATUS_META.unknown;
-}
-
-function formatDisplayDate(value, locale = 'ar-SY') {
- if (!value) return '—';
-
- const date = new Date(value);
- if (Number.isNaN(date.getTime())) {
- return String(value);
- }
-
- return date.toLocaleDateString(locale);
-}
-
-function formatInputDate(value) {
- if (!value) return '—';
-
- const date = new Date(value);
- if (Number.isNaN(date.getTime())) {
- return String(value);
- }
-
- return date.toISOString().split('T')[0];
-}
-
-function calculateReservationDays(startDate, endDate) {
- const start = new Date(startDate);
- const end = new Date(endDate);
-
- if (Number.isNaN(start.getTime()) || Number.isNaN(end.getTime())) {
- return 0;
- }
-
- const days = Math.ceil((end - start) / (1000 * 60 * 60 * 24));
- return days > 0 ? days : 0;
-}
-
-function pickFirstNumber(...values) {
- for (const value of values) {
- const parsed = Number(value);
- if (Number.isFinite(parsed)) {
- return parsed;
- }
- }
-
- return 0;
-}
-
-function pickFirstText(...values) {
- for (const value of values) {
- if (typeof value === 'string' && value.trim()) {
- return value.trim();
- }
- }
-
- return '';
-}
-
-function normalizeCommissionType(value) {
- if (typeof value === 'string' && value.trim()) {
- return value.trim();
- }
-
- if (value === 0) return 'من المالك';
- if (value === 1) return 'من المستأجر';
- if (value === 2) return 'من الطرفين';
-
- return 'غير محدد';
-}
-
-function buildReservationDisplayName(reservation) {
- return (
- pickFirstText(
- reservation?.customer?.name,
- reservation?.tenant?.name,
- reservation?.user?.name,
- reservation?.customerName,
- reservation?.tenantName,
- reservation?.userName,
- reservation?.fullName,
- reservation?.name,
- ) || `الحجز #${reservation?.id ?? reservation?.reservationId ?? '—'}`
- );
-}
-
-function normalizeReservation(reservation, property) {
- const info = property?.propertyInformation ?? property ?? {};
- const status = Number.isFinite(Number(reservation?.status))
- ? Number(reservation.status)
- : RESERVATION_STATUS.pending;
- const days = pickFirstNumber(
- reservation?.days,
- reservation?.durationInDays,
- calculateReservationDays(reservation?.startDate, reservation?.endDate),
- );
- const totalAmount = pickFirstNumber(
- reservation?.totalPrice,
- reservation?.totalAmount,
- reservation?.price,
- );
- const securityDeposit = pickFirstNumber(
- reservation?.securityDeposit,
- reservation?.deposit,
- property?.deposit,
- property?.securityDeposit,
- );
- const dailyPrice = pickFirstNumber(
- reservation?.dailyPrice,
- reservation?.pricePerDay,
- days > 0 ? totalAmount / days : 0,
- );
- const reservationId = pickFirstNumber(reservation?.reservationId, reservation?.id);
-
- return {
- ...reservation,
- id: reservationId || reservation?.id,
- reservationId: reservationId || reservation?.id,
- user: buildReservationDisplayName(reservation),
- userEmail: pickFirstText(
- reservation?.customer?.email,
- reservation?.tenant?.email,
- reservation?.user?.email,
- reservation?.customerEmail,
- reservation?.tenantEmail,
- reservation?.userEmail,
- ),
- userPhone: pickFirstText(
- reservation?.customer?.phoneNumber,
- reservation?.tenant?.phoneNumber,
- reservation?.user?.phoneNumber,
- reservation?.customerPhone,
- reservation?.tenantPhone,
- reservation?.userPhone,
- reservation?.phoneNumber,
- ),
- userType: pickFirstText(
- reservation?.customerTypeName,
- reservation?.customerType,
- reservation?.tenantType,
- 'غير محدد',
- ),
- identityNumber: pickFirstText(
- reservation?.customerIdentityNumber,
- reservation?.tenantIdentityNumber,
- reservation?.identityNumber,
- ),
- property: pickFirstText(info?.address, reservation?.propertyName) || `العقار #${reservation?.propertyId ?? '—'}`,
- propertyId: reservation?.propertyId,
- startDate: formatInputDate(reservation?.startDate),
- endDate: formatInputDate(reservation?.endDate),
- days,
- totalAmount,
- totalPrice: totalAmount,
- dailyPrice,
- commissionRate: pickFirstNumber(reservation?.commissionRate),
- commissionType: normalizeCommissionType(reservation?.commissionType),
- commissionAmount: pickFirstNumber(reservation?.commissionAmount),
- securityDeposit,
- status,
- requestDate: formatDisplayDate(reservation?.createdAt),
- createdAt: reservation?.createdAt,
- ownerApproved: status >= RESERVATION_STATUS.ownerConfirmed,
- adminApproved: status >= RESERVATION_STATUS.depositConfirmed,
- ownerDelivered: Boolean(reservation?.ownerDelivered),
- tenantReceived: Boolean(reservation?.tenantReceived),
- tenantLeft: Boolean(reservation?.tenantLeft),
- ownerReceived: Boolean(reservation?.ownerReceived),
- securityDepositPaid: status >= RESERVATION_STATUS.depositPaid,
- securityDepositReturned: reservation?.securityDepositReturned ?? null,
- contractSigned: Boolean(reservation?.contractSigned),
- notes: pickFirstText(reservation?.comment, reservation?.notes),
- actualStartDate: reservation?.actualStartDate ?? null,
- actualEndDate: reservation?.actualEndDate ?? null,
- _property: info,
- };
-}
-
-const ReasonDialog = ({ isOpen, onClose, onConfirm, title, defaultReason = '' }) => {
- const [reason, setReason] = useState(defaultReason);
- const [otherReason, setOtherReason] = useState('');
-
- const commonReasons = [
- 'أعمال صيانة في العقار',
- 'العقار غير متاح في هذه التواريخ',
- 'مشكلة في وثائق المستأجر',
- 'المالك غير متاح للتسليم',
- 'تأخر في دفع الضمان',
- 'سبب آخر'
- ];
-
- if (!isOpen) return null;
-
- return (
-
- e.stopPropagation()}
- >
-
-
-
{title}
-
يرجى تحديد سبب الرفض
-
-
-
- {commonReasons.map((r) => (
-
- ))}
-
-
-
-
- );
-};
-
-const PDFExportButton = ({ request, onExportComplete }) => {
- const [isExporting, setIsExporting] = useState(false);
-
- const formatCurrency = (amount) => {
- return amount?.toLocaleString() + ' ل.س';
- };
-
- const generatePDF = async () => {
- if (!request) {
- toast.error('لا توجد بيانات للتصدير');
- return;
- }
-
- const statusMeta = getStatusMeta(request.status);
- const userTypeLabel = request.userType || 'غير محدد';
- setIsExporting(true);
- const loadingToast = toast.loading('جاري إنشاء ملف PDF...', { id: 'pdf-export' });
-
- try {
- const printContent = document.createElement('div');
- printContent.style.width = '800px';
- printContent.style.padding = '40px';
- printContent.style.backgroundColor = 'white';
- printContent.style.fontFamily = 'Cairo, Arial, sans-serif';
- printContent.style.direction = 'rtl';
- printContent.style.position = 'absolute';
- printContent.style.left = '-9999px';
- printContent.style.top = '-9999px';
- printContent.style.zIndex = '-9999';
-
- printContent.innerHTML = `
-
-
-
-
-
-
-
-
-
-
-
-
- معلومات المستأجر
-
-
-
- الاسم الكامل:
- ${request.user || 'غير محدد'}
-
-
- نوع الهوية:
- ${userTypeLabel}
-
-
- رقم الهوية:
- ${request.identityNumber || 'غير محدد'}
-
-
- البريد الإلكتروني:
- ${request.userEmail || 'غير محدد'}
-
-
- رقم الهاتف:
- ${request.userPhone || 'غير محدد'}
-
-
-
-
-
-
- معلومات العقار
-
-
-
- العقار:
- ${request.property || 'غير محدد'}
-
-
- السعر اليومي:
- ${formatCurrency(request.dailyPrice)}
-
-
-
-
-
-
- تفاصيل الحجز
-
-
-
- تاريخ البداية:
- ${request.startDate || 'غير محدد'}
-
-
- تاريخ النهاية:
- ${request.endDate || 'غير محدد'}
-
-
- عدد الأيام:
- ${request.days || 0} يوم
-
-
- الحالة:
-
-
- ${statusMeta.label}
-
-
-
-
-
-
-
-
- المعلومات المالية
-
-
-
- سلفة الضمان:
- ${formatCurrency(request.securityDeposit)}
-
-
- المبلغ الإجمالي:
- ${formatCurrency(request.totalAmount)}
-
-
- نسبة العمولة:
- ${request.commissionRate || 0}%
-
-
- نوع العمولة:
- ${request.commissionType || 'غير محدد'}
-
-
- قيمة العمولة:
- ${formatCurrency(request.commissionAmount)}
-
-
-
-
- ${request.notes ? `
-
-
- ملاحظات
-
-
- ${request.notes}
-
-
- ` : ''}
-
-
-
- سجل الإجراءات
-
-
-
- ${request.requestDate}
- تم إنشاء الطلب
-
- ${request.ownerApproved ? `
-
- ✓
- تمت موافقة المالك
-
- ` : ''}
- ${request.adminApproved ? `
-
- ✓
- تمت موافقة الإدارة
-
- ` : ''}
- ${request.ownerDelivered ? `
-
-
- تم تسليم المفتاح
-
- ` : ''}
- ${request.tenantReceived ? `
-
-
- تم استلام العقار
-
- ` : ''}
-
-
-
-
-
-
-
- `;
-
- document.body.appendChild(printContent);
-
- await new Promise(resolve => setTimeout(resolve, 200));
-
- const canvas = await html2canvas(printContent, {
- scale: 2.5,
- backgroundColor: '#ffffff',
- logging: false,
- useCORS: true,
- windowWidth: printContent.scrollWidth,
- windowHeight: printContent.scrollHeight,
- onclone: (clonedDoc, element) => {
- }
- });
-
- const imgData = canvas.toDataURL('image/png');
- const pdf = new jsPDF({
- orientation: 'portrait',
- unit: 'mm',
- format: 'a4',
- compress: true
- });
-
- const imgWidth = 210;
- const pageHeight = 297;
- const imgHeight = (canvas.height * imgWidth) / canvas.width;
- let heightLeft = imgHeight;
- let position = 0;
-
- pdf.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight);
- heightLeft -= pageHeight;
-
- let pageCount = 1;
- while (heightLeft > 0) {
- position = heightLeft - imgHeight;
- pdf.addPage();
- pdf.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight);
- heightLeft -= pageHeight;
- pageCount++;
- }
-
- const fileName = `تقرير_طلب_${request.id}_${new Date().toISOString().split('T')[0]}.pdf`;
- pdf.save(fileName);
-
- document.body.removeChild(printContent);
-
- toast.success(`تم تصدير التقرير بنجاح! (${pageCount} صفحة)`, { id: 'pdf-export' });
-
- if (onExportComplete) {
- onExportComplete();
- }
-
- } catch (error) {
- console.error('Error generating PDF:', error);
- toast.error('حدث خطأ أثناء إنشاء ملف PDF', { id: 'pdf-export' });
- } finally {
- setIsExporting(false);
- }
- };
-
- return (
-
- );
-};
-
-const RequestDetailsDialog = ({ request, isOpen, onClose }) => {
- if (!isOpen || !request) return null;
-
- const formatCurrency = (amount) => {
- return amount?.toLocaleString() + ' ل.س';
- };
-
- return (
-
- e.stopPropagation()}
- >
-
-
-
-
- تفاصيل الطلب #{request.id}
-
-
جميع معلومات الطلب في مكان واحد
-
-
-
-
-
-
-
-
- معلومات المستأجر
-
-
-
-
-
{request.user}
-
-
-
-
- {request.userType || 'غير محدد'}
- {request.identityNumber}
-
-
-
-
-
-
- {request.userEmail}
-
-
-
-
-
-
- {request.userPhone}
-
-
-
-
-
-
-
-
- معلومات العقار
-
-
-
-
-
{request.property}
-
-
-
-
{formatCurrency(request.dailyPrice)}
-
-
-
-
-
-
-
- تفاصيل الحجز
-
-
-
-
-
{request.startDate}
-
-
-
-
{request.endDate}
-
-
-
-
{request.days} يوم
-
-
-
-
{formatCurrency(request.totalAmount)}
-
-
-
-
-
-
-
- المعلومات المالية
-
-
-
-
-
{formatCurrency(request.securityDeposit)}
-
-
-
-
- {request.securityDepositPaid ? '✓ تم الدفع' : '⏳ في انتظار الدفع'}
-
-
-
-
-
{request.commissionRate}%
-
-
-
-
{request.commissionType}
-
-
-
-
{formatCurrency(request.commissionAmount)}
-
-
-
-
-
-
-
- سجل الإجراءات
-
-
-
-
-
تم إنشاء الطلب: {request.requestDate}
-
- {request.ownerApproved && (
-
- )}
- {request.adminApproved && (
-
- )}
- {request.ownerDelivered && (
-
-
-
تم تسليم المفتاح من المالك
-
- )}
- {request.notes && (
-
- )}
-
-
-
-
-
-
-
-
-
-
-
-
- );
-};
-
-const RequestCard = ({ request, onConfirmDeposit, onViewDetails, confirmingDepositId }) => {
- const [expanded, setExpanded] = useState(
- Number(request.status) === RESERVATION_STATUS.depositPaid,
- );
- const isConfirmingDeposit = confirmingDepositId === request.id;
- const statusMeta = getStatusMeta(request.status);
- const canConfirmDeposit = Number(request.status) === RESERVATION_STATUS.depositPaid;
-
- const formatCurrency = (amount) => {
- const value = Number(amount) || 0;
- return value.toLocaleString() + ' ل.س';
- };
-
- return (
-
- setExpanded((prev) => !prev)}>
-
-
- طلب #{request.id}
-
- {statusMeta.label}
-
-
-
- {request.requestDate}
- {expanded ? (
-
- ) : (
-
- )}
-
-
-
-
-
-
- {request.user}
-
-
-
- {request.property}
-
-
-
- {request.days} أيام
-
-
-
- {formatCurrency(request.totalAmount)}
-
-
-
-
-
- {expanded && (
-
-
-
-
العربون
-
{formatCurrency(request.securityDeposit)}
-
-
-
حالة العربون
-
- {request.securityDepositPaid ? 'تم دفع العربون' : 'بانتظار الدفع'}
-
-
-
-
العمولة
-
{request.commissionRate}% ({request.commissionType})
-
-
-
مدة الإيجار
-
{request.startDate} إلى {request.endDate}
-
-
-
- {(request.userEmail || request.userPhone) && (
-
-
-
- معلومات الاتصال
-
-
-
-
- {request.userEmail || 'غير متوفر'}
-
-
-
-
{request.userPhone || 'غير متوفر'}
-
-
-
- )}
-
-
-
-
-
- {canConfirmDeposit && (
-
- )}
-
-
- {Number(request.status) === RESERVATION_STATUS.depositConfirmed && (
-
- تم تأكيد العربون من قبل الإدارة.
-
- )}
-
- {request.notes && (
-
- {request.notes}
-
- )}
-
-
- )}
-
-
- );
-};
-
-export default function BookingRequests() {
- /*
- Legacy dummy data kept for reference only. The page now loads real
- reservations from the API instead of rendering these local examples.
-
- const DUMMY_REQUESTS = [
- {
- id: 'REQ001',
- user: 'أحمد محمد',
- userEmail: 'ahmed@example.com',
- userPhone: '0938123456',
- userType: 'syrian',
- identityNumber: '123456789',
- property: 'فيلا فاخرة في دمشق',
- propertyId: 1,
- startDate: '2024-03-01',
- endDate: '2024-03-10',
- days: 10,
- totalAmount: 5000000,
- dailyPrice: 500000,
- commissionRate: 5,
- commissionType: 'من المالك',
- commissionAmount: 250000,
- securityDeposit: 500000,
- status: 'pending',
- requestDate: '2024-02-25',
- ownerApproved: false,
- adminApproved: false,
- ownerDelivered: false,
- tenantReceived: false,
- tenantLeft: false,
- ownerReceived: false,
- securityDepositPaid: false,
- securityDepositReturned: null,
- contractSigned: false,
- notes: '',
- actualStartDate: null,
- actualEndDate: null
- },
- {
- id: 'REQ002',
- user: 'سارة أحمد',
- userEmail: 'sara@example.com',
- userPhone: '0945123789',
- userType: 'passport',
- identityNumber: 'AB123456',
- property: 'شقة حديثة في حلب',
- propertyId: 2,
- startDate: '2024-03-05',
- endDate: '2024-03-15',
- days: 10,
- totalAmount: 2500000,
- dailyPrice: 250000,
- commissionRate: 7,
- commissionType: 'من المستأجر',
- commissionAmount: 175000,
- securityDeposit: 250000,
- status: 'admin_approved',
- requestDate: '2024-02-24',
- ownerApproved: true,
- adminApproved: true,
- ownerDelivered: false,
- tenantReceived: false,
- tenantLeft: false,
- ownerReceived: false,
- securityDepositPaid: false,
- securityDepositReturned: null,
- contractSigned: false,
- notes: '',
- actualStartDate: null,
- actualEndDate: null
- },
- {
- id: 'REQ003',
- user: 'محمد الحلبي',
- userEmail: 'mohammed@example.com',
- userPhone: '0956123456',
- userType: 'syrian',
- identityNumber: '987654321',
- property: 'شقة بجانب البحر في اللاذقية',
- propertyId: 3,
- startDate: '2024-02-20',
- endDate: '2024-03-20',
- days: 30,
- totalAmount: 9000000,
- dailyPrice: 300000,
- commissionRate: 5,
- commissionType: 'من الاثنين',
- commissionAmount: 450000,
- securityDeposit: 500000,
- status: 'active',
- requestDate: '2024-02-15',
- ownerApproved: true,
- adminApproved: true,
- ownerDelivered: true,
- tenantReceived: true,
- tenantLeft: false,
- ownerReceived: false,
- securityDepositPaid: true,
- securityDepositReturned: null,
- contractSigned: true,
- notes: 'عقد موقع إلكترونياً',
- actualStartDate: '2024-02-20',
- actualEndDate: null
- }
- ];
- */
-
- const [requests, setRequests] = useState([]);
- const [filter, setFilter] = useState('depositPaid');
- const [detailsDialog, setDetailsDialog] = useState({ isOpen: false, request: null });
- const [confirmingDepositId, setConfirmingDepositId] = useState(null);
- const [isLoading, setIsLoading] = useState(true);
- const [loadError, setLoadError] = useState('');
-
- const loadReservations = useCallback(async () => {
- setIsLoading(true);
- setLoadError('');
-
- try {
- if (!AuthService.isAdmin()) {
- throw new Error('هذه الصفحة متاحة للإدارة فقط');
- }
-
- let list = await getReservations();
- if (!Array.isArray(list)) {
- list = [];
- }
-
- const propertyCache = new Map();
- const uniquePropertyIds = [...new Set(list.map((item) => item?.propertyId).filter((id) => id != null))];
-
- await Promise.all(
- uniquePropertyIds.map(async (propertyId) => {
- try {
- const property = await getRentProperty(propertyId);
- propertyCache.set(propertyId, property);
- } catch (error) {
- console.warn('[Admin] Failed to load property details for reservation:', propertyId, error);
- propertyCache.set(propertyId, null);
- }
- }),
- );
-
- const normalizedRequests = list
- .map((reservation) => normalizeReservation(reservation, propertyCache.get(reservation?.propertyId)))
- .filter((reservation) => reservation?.id != null)
- .sort((left, right) => {
- const leftDate = new Date(left.createdAt || 0).getTime();
- const rightDate = new Date(right.createdAt || 0).getTime();
- return rightDate - leftDate;
- });
-
- setRequests(normalizedRequests);
- } catch (error) {
- console.error('[Admin] Failed to load reservations:', error);
- setLoadError(error.message || 'فشل تحميل الحجوزات');
- setRequests([]);
- toast.error(error.message || 'فشل تحميل الحجوزات');
- } finally {
- setIsLoading(false);
- }
- }, []);
-
- useEffect(() => {
- loadReservations();
- }, [loadReservations]);
-
- const handleDepositConfirmation = async (request) => {
- if (!AuthService.isAdmin()) {
- console.warn('[Admin] Deposit confirmation blocked: current user is not admin', {
- user: AuthService.getUser(),
- roles: AuthService.getRoles?.(),
- request,
- });
- toast.error('هذا الإجراء متاح للإدارة فقط');
- return;
- }
-
- if (Number(request?.status) !== RESERVATION_STATUS.depositPaid) {
- console.warn('[Admin] Deposit confirmation blocked: reservation is not in depositPaid state', {
- requestId: request?.id,
- reservationId: request?.reservationId,
- status: request?.status,
- });
- toast.error('يمكن تأكيد العربون فقط عندما تكون الحالة depositPaid');
- return;
- }
-
- const reservationId = Number(request?.reservationId ?? request?.id);
- if (!Number.isFinite(reservationId)) {
- toast.error('تعذر تحديد رقم الحجز المطلوب');
- return;
- }
-
- const adminUser = AuthService.getUser();
- const parsedAdminId = Number(adminUser?.id);
- const adminId = Number.isFinite(parsedAdminId) ? parsedAdminId : adminUser?.id;
- const comment =
- typeof request?.comment === 'string' && request.comment.trim()
- ? request.comment.trim()
- : null;
-
- console.log('[Admin] Preparing admin confirm deposit request', {
- requestId: request?.id,
- reservationId,
- adminId,
- adminUser,
- status: request?.status,
- endpoint: '/Reservations/AdminConfirmDeposit/admin-confirm-deposit',
- payload: {
- reservationId,
- adminId,
- comment,
- },
- });
-
- if (adminId == null || adminId === '') {
- console.warn('[Admin] Deposit confirmation blocked: adminId is missing', {
- adminUser,
- parsedAdminId,
- });
- toast.error('لم نتمكن من تحديد هوية المدير');
- return;
- }
-
- setConfirmingDepositId(request.id);
-
- try {
- const result = await adminConfirmDeposit(reservationId, adminId, comment);
-
- console.log('[Admin] Deposit confirmation response', {
- requestId: request?.id,
- reservationId,
- adminId,
- comment,
- status: result?.status,
- ok: result?.ok,
- message: result?.message,
- data: result?.data,
- });
-
- if (!result.ok) {
- throw new Error(result.message || result.data?.message || `HTTP ${result.status}`);
- }
-
- setRequests((prev) =>
- prev.map((req) =>
- req.id === request.id
- ? {
- ...req,
- status: RESERVATION_STATUS.depositConfirmed,
- adminApproved: true,
- securityDepositPaid: true,
- notes: comment || 'تم تأكيد العربون من قبل الإدارة',
- }
- : req,
- ),
- );
-
- toast.success('تم تأكيد العربون بنجاح');
- } catch (err) {
- console.error('[Admin] Deposit confirmation failed:', err);
- toast.error(err.message || 'فشل تأكيد العربون');
- } finally {
- setConfirmingDepositId(null);
- }
- };
-
- const filteredRequests = requests.filter((req) =>
- filter === 'all' ? true : getStatusKey(req.status) === filter,
- );
-
- const counts = FILTER_TABS.reduce((acc, tab) => {
- acc[tab.id] =
- tab.id === 'all'
- ? requests.length
- : requests.filter((req) => getStatusKey(req.status) === tab.id).length;
- return acc;
- }, {});
-
- const stats = {
- total: requests.length,
- depositPaid: counts.depositPaid || 0,
- depositConfirmed: counts.depositConfirmed || 0,
- completed: counts.completed || 0,
- };
-
- return (
-
-
-
-
-
-
متابعة حجوزات العربون
-
- يتم جلب جميع الحجوزات من الخادم، مع فتح الحجوزات ذات الحالة depositPaid (2) بشكل افتراضي.
-
-
-
-
-
-
- {loadError && (
-
- {loadError}
-
- )}
-
-
-
- {stats.total}
- إجمالي الحجوزات
-
-
- {stats.depositPaid}
- تم دفع العربون
-
-
- {stats.depositConfirmed}
- تم تأكيد العربون
-
-
- {stats.completed}
- منتهية
-
-
-
-
-
-
تصفية حسب الحالة
- {filteredRequests.length} طلب
-
-
-
- {FILTER_TABS.map((tab) => (
-
- ))}
-
-
-
- {isLoading ? (
-
-
-
- ) : (
-
- {filteredRequests.map((request) => (
- setDetailsDialog({ isOpen: true, request: selectedRequest })}
- confirmingDepositId={confirmingDepositId}
- />
- ))}
-
- )}
-
- {!isLoading && filteredRequests.length === 0 && (
-
-
-
-
- لا توجد حجوزات مطابقة
-
- لم يتم العثور على حجوزات ضمن الحالة الحالية.
-
-
- )}
-
-
setDetailsDialog({ isOpen: false, request: null })}
- />
-
- );
-}
diff --git a/app/components/admin/DashboardStats.js b/app/components/admin/DashboardStats.js
deleted file mode 100644
index 5b15024..0000000
--- a/app/components/admin/DashboardStats.js
+++ /dev/null
@@ -1,139 +0,0 @@
-'use client';
-
-import { motion } from 'framer-motion';
-import { Users, Home, Calendar, DollarSign } from 'lucide-react';
-import { useEffect, useState } from 'react';
-
-export default function DashboardStats() {
- const [stats, setStats] = useState({
- totalUsers: 0,
- totalProperties: 0,
- activeBookings: 0,
- totalRevenue: 0,
- pendingRequests: 0,
- availableProperties: 0
- });
-
- useEffect(() => {
- setStats({
- totalUsers: 156,
- totalProperties: 89,
- activeBookings: 34,
- totalRevenue: 12500000,
- pendingRequests: 12,
- availableProperties: 45
- });
- }, []);
-
- const formatNumber = (num) => {
- return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
- };
-
- const formatCurrency = (amount) => {
- return `${formatNumber(amount)} ل.س`;
- };
-
- const cards = [
- {
- title: 'إجمالي المستخدمين',
- value: stats.totalUsers,
- icon: Users,
- color: 'from-blue-600 to-blue-700',
- bgColor: 'bg-blue-100',
- iconColor: 'text-blue-600'
- },
- {
- title: 'إجمالي العقارات',
- value: stats.totalProperties,
- icon: Home,
- color: 'from-emerald-600 to-emerald-700',
- bgColor: 'bg-emerald-100',
- iconColor: 'text-emerald-600'
- },
- {
- title: 'الحجوزات النشطة',
- value: stats.activeBookings,
- icon: Calendar,
- color: 'from-purple-600 to-purple-700',
- bgColor: 'bg-purple-100',
- iconColor: 'text-purple-600'
- },
- {
- title: 'الإيرادات',
- value: formatCurrency(stats.totalRevenue),
- icon: DollarSign,
- color: 'from-amber-600 to-amber-700',
- bgColor: 'bg-amber-100',
- iconColor: 'text-amber-600'
- }
- ];
-
- return (
-
-
- {cards.map((card, index) => {
- const Icon = card.icon;
- return (
-
-
-
-
-
-
-
{card.value}
-
{card.title}
-
-
-
- آخر تحديث: الآن
-
-
- );
- })}
-
-
-
-
- طلبات حجز معلقة
- {stats.pendingRequests}
- بحاجة لموافقة
-
-
-
- عقارات متاحة
- {stats.availableProperties}
- جاهزة للإيجار
-
-
-
- نسبة الإشغال
-
- {Math.round((stats.activeBookings / stats.totalProperties) * 100)}%
-
- من إجمالي العقارات
-
-
-
- );
-}
\ No newline at end of file
diff --git a/app/components/admin/LedgerBook.js b/app/components/admin/LedgerBook.js
deleted file mode 100644
index fe86650..0000000
--- a/app/components/admin/LedgerBook.js
+++ /dev/null
@@ -1,607 +0,0 @@
-'use client';
-
-import { useState, useEffect } from 'react';
-import { motion } from 'framer-motion';
-import {
- DollarSign,
- Calendar,
- User,
- Home,
- Download,
- Filter,
- Search,
- TrendingUp,
- TrendingDown,
- Wallet,
- Shield,
- FileText,
- Printer,
- X,
- CheckCircle
-} from 'lucide-react';
-import { formatCurrency } from '@/app/utils/calculations';
-import toast, { Toaster } from 'react-hot-toast';
-import * as XLSX from 'xlsx';
-
-export default function LedgerBook({ userType = 'admin' }) {
- const [transactions, setTransactions] = useState([]);
- const [filteredTransactions, setFilteredTransactions] = useState([]);
- const [dateRange, setDateRange] = useState({ start: '', end: '' });
- const [searchTerm, setSearchTerm] = useState('');
- const [summary, setSummary] = useState({
- totalRevenue: 0,
- pendingPayments: 0,
- securityDeposits: 0,
- commissionEarned: 0
- });
- const [isExporting, setIsExporting] = useState(false);
-
- useEffect(() => {
- loadTransactions();
- }, []);
-
- useEffect(() => {
- filterTransactions();
- calculateSummary();
- }, [transactions, dateRange, searchTerm]);
-
- const loadTransactions = async () => {
- const mockTransactions = [
- {
- id: 'T001',
- date: '2024-02-20',
- type: 'rent_payment',
- description: 'دفعة إيجار - فيلا في دمشق',
- amount: 500000,
- commission: 25000,
- fromUser: 'أحمد محمد',
- toUser: 'مالك العقار',
- propertyId: 1,
- propertyName: 'luxuryVillaDamascus',
- status: 'completed',
- paymentMethod: 'cash'
- },
- {
- id: 'T002',
- date: '2024-02-19',
- type: 'security_deposit',
- description: 'سلفة ضمان - شقة في حلب',
- amount: 250000,
- commission: 0,
- fromUser: 'سارة أحمد',
- toUser: 'مالك العقار',
- propertyId: 2,
- propertyName: 'modernApartmentAleppo',
- status: 'pending_refund',
- paymentMethod: 'cash'
- },
- {
- id: 'T003',
- date: '2024-02-18',
- type: 'commission',
- description: 'عمولة منصة - فيلا في درعا',
- amount: 30000,
- commission: 30000,
- fromUser: 'محمد الحلبي',
- toUser: 'المنصة',
- propertyId: 5,
- propertyName: 'villaDaraa',
- status: 'completed',
- paymentMethod: 'cash'
- }
- ];
- setTransactions(mockTransactions);
- };
-
- const filterTransactions = () => {
- let filtered = [...transactions];
-
- if (dateRange.start && dateRange.end) {
- filtered = filtered.filter(t =>
- t.date >= dateRange.start && t.date <= dateRange.end
- );
- }
-
- if (searchTerm) {
- filtered = filtered.filter(t =>
- t.description.includes(searchTerm) ||
- t.fromUser.includes(searchTerm) ||
- t.toUser.includes(searchTerm)
- );
- }
-
- setFilteredTransactions(filtered);
- };
-
- const calculateSummary = () => {
- const summary = filteredTransactions.reduce((acc, t) => {
- if (t.type === 'rent_payment' || t.type === 'commission') {
- acc.totalRevenue += t.amount;
- }
- if (t.type === 'security_deposit' && t.status === 'pending_refund') {
- acc.securityDeposits += t.amount;
- }
- if (t.commission) {
- acc.commissionEarned += t.commission;
- }
- if (t.status === 'pending') {
- acc.pendingPayments += t.amount;
- }
- return acc;
- }, {
- totalRevenue: 0,
- pendingPayments: 0,
- securityDeposits: 0,
- commissionEarned: 0
- });
-
- setSummary(summary);
- };
-
- const getTransactionIcon = (type) => {
- switch(type) {
- case 'rent_payment':
- return
;
- case 'security_deposit':
- return
;
- case 'commission':
- return
;
- default:
- return
;
- }
- };
-
- const exportToExcel = async () => {
- if (filteredTransactions.length === 0) {
- toast.error('لا توجد معاملات للتصدير');
- return;
- }
-
- setIsExporting(true);
- toast.loading('جاري تصدير البيانات...', { id: 'export' });
-
- try {
- const exportData = filteredTransactions.map(t => ({
- 'رقم العملية': t.id,
- 'التاريخ': t.date,
- 'نوع العملية': t.type === 'rent_payment' ? 'دفعة إيجار' :
- t.type === 'security_deposit' ? 'سلفة ضمان' :
- t.type === 'commission' ? 'عمولة' : 'أخرى',
- 'الوصف': t.description,
- 'من': t.fromUser,
- 'إلى': t.toUser,
- 'المبلغ (ل.س)': t.amount,
- 'العمولة (ل.س)': t.commission || 0,
- 'الحالة': t.status === 'completed' ? 'مكتمل' :
- t.status === 'pending' ? 'معلق' :
- t.status === 'pending_refund' ? 'بإنتظار الاسترداد' : 'مؤكد',
- }));
-
- const summaryRow = {
- 'رقم العملية': '',
- 'التاريخ': '',
- 'نوع العملية': '',
- 'الوصف': '',
- 'من': '',
- 'إلى': '',
- 'المبلغ (ل.س)': summary.totalRevenue,
- 'العمولة (ل.س)': summary.commissionEarned,
- 'الحالة': ''
- };
-
- exportData.push(summaryRow);
-
- const worksheet = XLSX.utils.json_to_sheet(exportData);
-
- const columnWidths = [
- { wch: 12 }, // رقم العملية
- { wch: 12 }, // التاريخ
- { wch: 12 }, // نوع العملية
- { wch: 30 }, // الوصف
- { wch: 20 }, // من
- { wch: 20 }, // إلى
- { wch: 15 }, // المبلغ
- { wch: 15 }, // العمولة
- { wch: 12 }, // الحالة
- ];
- worksheet['!cols'] = columnWidths;
-
- const range = XLSX.utils.decode_range(worksheet['!ref']);
- for (let C = range.s.c; C <= range.e.c; ++C) {
- const address = XLSX.utils.encode_col(C) + '1';
- if (!worksheet[address]) continue;
- worksheet[address].s = {
- font: { bold: true, sz: 12 },
- fill: { fgColor: { rgb: "F59E0B" } },
- alignment: { horizontal: "center", vertical: "center" }
- };
- }
-
-
- const workbook = XLSX.utils.book_new();
- XLSX.utils.book_append_sheet(workbook, worksheet, 'دفتر الحسابات');
-
- const fileName = `دفتر_الحسابات_${new Date().toISOString().split('T')[0]}.xlsx`;
-
- XLSX.writeFile(workbook, fileName);
-
- toast.success(`تم تصدير ${filteredTransactions.length} معاملة بنجاح!`, { id: 'export' });
-
- } catch (error) {
- console.error('Error exporting to Excel:', error);
- toast.error('حدث خطأ أثناء تصدير البيانات', { id: 'export' });
- } finally {
- setIsExporting(false);
- }
- };
-
- const printReport = () => {
- const printWindow = window.open('', '_blank');
- printWindow.document.write(`
-
-
-
-
-
تقرير دفتر الحسابات
-
-
-
-
-
-
-
-
إجمالي الإيرادات
-
${formatCurrency(summary.totalRevenue)}
-
-
-
أرباح المنصة
-
${formatCurrency(summary.commissionEarned)}
-
-
-
سلف الضمان
-
${formatCurrency(summary.securityDeposits)}
-
-
-
المدفوعات المعلقة
-
${formatCurrency(summary.pendingPayments)}
-
-
-
-
-
-
- | التاريخ |
- الوصف |
- من |
- إلى |
- المبلغ |
- العمولة |
- الحالة |
-
-
-
- ${filteredTransactions.map(t => `
-
- | ${t.date} |
- ${t.description} |
- ${t.fromUser} |
- ${t.toUser} |
- ${formatCurrency(t.amount)} |
- ${t.commission ? formatCurrency(t.commission) : '-'} |
- ${t.status === 'completed' ? 'مكتمل' : t.status === 'pending' ? 'معلق' : 'بإنتظار الرد'} |
-
- `).join('')}
-
-
-
-
-
-
-
-
-
-
- `);
- printWindow.document.close();
- };
-
- return (
-
-
-
-
-
-
-
- إجمالي الإيرادات
-
- {formatCurrency(summary.totalRevenue)}
-
-
-
-
-
- أرباح المنصة
-
- {formatCurrency(summary.commissionEarned)}
-
-
-
-
-
- سلف الضمان
-
- {formatCurrency(summary.securityDeposits)}
-
-
-
-
-
- المدفوعات المعلقة
-
- {formatCurrency(summary.pendingPayments)}
-
-
-
-
-
-
-
- setSearchTerm(e.target.value)}
- className="w-full pl-12 pr-4 py-3 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500"
- />
-
-
-
- setDateRange({...dateRange, start: e.target.value})}
- className="px-4 py-3 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500"
- />
- إلى
- setDateRange({...dateRange, end: e.target.value})}
- className="px-4 py-3 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500"
- />
-
-
-
-
-
-
-
-
- {(dateRange.start || dateRange.end || searchTerm) && (
-
-
- {filteredTransactions.length} معاملة من إجمالي {transactions.length}
-
-
-
- )}
-
-
-
-
-
-
-
- | التاريخ |
- الوصف |
- من |
- إلى |
- المبلغ |
- العمولة |
- الحالة |
-
-
-
- {filteredTransactions.map((transaction, index) => (
-
-
-
-
- {transaction.date}
-
- |
-
-
- {getTransactionIcon(transaction.type)}
- {transaction.description}
-
- |
-
-
-
- {transaction.fromUser}
-
- |
-
-
-
- {transaction.toUser}
-
- |
-
- {formatCurrency(transaction.amount)}
- |
-
- {transaction.commission ? formatCurrency(transaction.commission) : '-'}
- |
-
-
- {transaction.status === 'completed' ? 'مكتمل' :
- transaction.status === 'pending' ? 'معلق' : 'بإنتظار الرد'}
-
- |
-
- ))}
-
-
-
-
- {filteredTransactions.length === 0 && (
-
-
-
لا توجد معاملات في هذه الفترة
-
- )}
-
-
- {userType === 'owner' && (
-
-
-
- أرصدة المستأجرين
-
-
-
- )}
-
- );
-}
\ No newline at end of file
diff --git a/app/components/admin/PropertiesTable.js b/app/components/admin/PropertiesTable.js
deleted file mode 100644
index d35f71b..0000000
--- a/app/components/admin/PropertiesTable.js
+++ /dev/null
@@ -1,636 +0,0 @@
-'use client';
-
-import { useState } from 'react';
-import { motion, AnimatePresence } from 'framer-motion';
-import {
- Edit,
- Trash2,
- Eye,
- MapPin,
- Bed,
- Bath,
- Square,
- DollarSign,
- Percent,
- MoreVertical,
- X,
- CheckCircle,
- AlertCircle,
- Calendar,
- User,
- Home,
- Building,
- Clock
-} from 'lucide-react';
-import toast, { Toaster } from 'react-hot-toast';
-
-const DeleteConfirmationModal = ({ isOpen, onClose, onConfirm, propertyTitle }) => {
- if (!isOpen) return null;
-
- return (
-
- e.stopPropagation()}
- >
-
-
-
تأكيد الحذف
-
- هل أنت متأكد من حذف العقار: "{propertyTitle}"؟
-
-
هذا الإجراء لا يمكن التراجع عنه
-
-
-
-
-
-
-
-
- );
-};
-
-const PropertyViewModal = ({ property, isOpen, onClose }) => {
- if (!isOpen || !property) return null;
-
- const formatCurrency = (amount) => {
- return amount?.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") + ' ل.س';
- };
-
- return (
-
- e.stopPropagation()}
- >
-
-
-
-
{property.title}
-
{property.location}
-
-
-
-
-
-
-
-
-
-
{property.type === 'villa' ? 'فيلا' : property.type === 'apartment' ? 'شقة' : 'بيت'}
-
نوع العقار
-
-
-
-
{formatCurrency(property.price)}
-
السعر اليومي
-
-
-
-
{property.commission}%
-
نسبة العمولة
-
-
-
-
{property.bookings || 0}
-
عدد الحجوزات
-
-
-
-
-
-
- الموقع
-
-
{property.location}
-
-
-
-
المواصفات
-
-
-
-
{property.bedrooms}
-
غرف نوم
-
-
-
-
{property.bathrooms}
-
حمامات
-
-
-
-
{property.area}
-
م²
-
-
-
-
-
-
-
- معلومات العمولة
-
-
-
-
-
{property.commission}%
-
-
-
-
{property.commissionType}
-
-
-
-
- {formatCurrency((property.price * property.commission) / 100)}
-
-
-
-
-
- {property.status === 'available' ? 'متاح' : 'محجوز'}
-
-
-
-
-
-
-
- );
-};
-
-const PropertyEditModal = ({ property, isOpen, onClose, onSave }) => {
- const [formData, setFormData] = useState({ ...property });
- const [isSaving, setIsSaving] = useState(false);
-
- const formatCurrency = (amount) => {
- return amount?.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
- };
-
- const handleSave = () => {
- setIsSaving(true);
- setTimeout(() => {
- onSave(formData);
- setIsSaving(false);
- onClose();
- toast.success('تم تحديث العقار بنجاح');
- }, 1000);
- };
-
- if (!isOpen || !property) return null;
-
- return (
-
- e.stopPropagation()}
- >
-
-
-
تعديل العقار
-
-
-
يمكنك تعديل معلومات العقار
-
-
-
-
-
-
- setFormData({...formData, title: e.target.value})}
- className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-amber-500"
- />
-
-
-
-
-
-
-
- setFormData({...formData, location: e.target.value})}
- className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-amber-500"
- />
-
-
-
- setFormData({...formData, price: parseInt(e.target.value)})}
- className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-amber-500"
- />
-
-
-
- setFormData({...formData, commission: parseFloat(e.target.value)})}
- className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-amber-500"
- />
-
-
-
-
-
-
-
- setFormData({...formData, bedrooms: parseInt(e.target.value)})}
- className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-amber-500"
- />
-
-
-
- setFormData({...formData, bathrooms: parseInt(e.target.value)})}
- className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-amber-500"
- />
-
-
-
- setFormData({...formData, area: parseInt(e.target.value)})}
- className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-amber-500"
- />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
-};
-
-const MoreActionsMenu = ({ property, isOpen, onClose, onViewBookings, onViewReports }) => {
- if (!isOpen) return null;
-
- return (
- <>
-
-
-
- >
- );
-};
-
-export default function PropertiesTable() {
- const [properties, setProperties] = useState([
- {
- id: 1,
- title: 'فيلا فاخرة في المزة',
- type: 'villa',
- location: 'دمشق, المزة',
- price: 500000,
- commission: 5,
- commissionType: 'من المالك',
- bedrooms: 5,
- bathrooms: 4,
- area: 450,
- status: 'available',
- bookings: 3
- },
- {
- id: 2,
- title: 'شقة حديثة في الشهباء',
- type: 'apartment',
- location: 'حلب, الشهباء',
- price: 250000,
- commission: 7,
- commissionType: 'من المستأجر',
- bedrooms: 3,
- bathrooms: 2,
- area: 180,
- status: 'booked',
- bookings: 1
- }
- ]);
-
- const [viewModal, setViewModal] = useState({ isOpen: false, property: null });
- const [editModal, setEditModal] = useState({ isOpen: false, property: null });
- const [deleteModal, setDeleteModal] = useState({ isOpen: false, property: null });
- const [moreMenu, setMoreMenu] = useState({ isOpen: false, property: null, anchorEl: null });
-
- const formatCurrency = (amount) => {
- return amount.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") + ' ل.س';
- };
-
- const getStatusBadge = (status) => {
- const styles = {
- available: 'bg-green-100 text-green-800',
- booked: 'bg-red-100 text-red-800',
- maintenance: 'bg-yellow-100 text-yellow-800'
- };
-
- const labels = {
- available: 'متاح',
- booked: 'محجوز',
- maintenance: 'صيانة'
- };
-
- return (
-
- {labels[status]}
-
- );
- };
-
- const handleView = (property) => {
- setViewModal({ isOpen: true, property });
- };
-
- const handleEdit = (property) => {
- setEditModal({ isOpen: true, property });
- };
-
- const handleDelete = (property) => {
- setDeleteModal({ isOpen: true, property });
- };
-
- const confirmDelete = () => {
- if (deleteModal.property) {
- setProperties(prev => prev.filter(p => p.id !== deleteModal.property.id));
- setDeleteModal({ isOpen: false, property: null });
- toast.success('تم حذف العقار بنجاح');
- }
- };
-
- const handleSaveEdit = (updatedProperty) => {
- setProperties(prev => prev.map(p =>
- p.id === updatedProperty.id ? updatedProperty : p
- ));
- toast.success('تم تحديث العقار بنجاح');
- };
-
- const handleMoreClick = (event, property) => {
- event.stopPropagation();
- setMoreMenu({ isOpen: true, property, anchorEl: event.currentTarget });
- };
-
- const handleViewBookings = (property) => {
- toast.success(`جاري عرض حجوزات ${property.title}`);
- };
-
- const handleViewReports = (property) => {
- toast.success(`جاري عرض تقرير أرباح ${property.title}`);
- };
-
- return (
-
-
-
-
-
-
- | العقار |
- الموقع |
- السعر/يوم |
- العمولة |
- المصدر |
- التفاصيل |
- الحالة |
- الإجراءات |
-
-
-
- {properties.map((property, index) => (
-
-
- {property.title}
-
- {property.type === 'villa' ? 'فيلا' :
- property.type === 'apartment' ? 'شقة' :
- property.type === 'house' ? 'بيت' : 'استوديو'}
-
- |
-
-
-
- {property.location}
-
- |
-
- {formatCurrency(property.price)}
- |
-
-
-
- {property.commission}%
-
- |
- {property.commissionType} |
-
-
- {property.bedrooms}
- {property.bathrooms}
- {property.area}m²
-
- |
-
- {getStatusBadge(property.status)}
- |
-
-
-
-
-
- {moreMenu.isOpen && moreMenu.property?.id === property.id && (
- setMoreMenu({ isOpen: false, property: null, anchorEl: null })}
- onViewBookings={handleViewBookings}
- onViewReports={handleViewReports}
- />
- )}
-
- |
-
- ))}
-
-
-
- {properties.length === 0 && (
-
-
-
لا توجد عقارات مضافة بعد
-
- )}
-
-
setViewModal({ isOpen: false, property: null })}
- />
-
- setEditModal({ isOpen: false, property: null })}
- onSave={handleSaveEdit}
- />
-
- setDeleteModal({ isOpen: false, property: null })}
- onConfirm={confirmDelete}
- propertyTitle={deleteModal.property?.title}
- />
-
- );
-}
\ No newline at end of file
diff --git a/app/components/admin/UsersList.js b/app/components/admin/UsersList.js
deleted file mode 100644
index 8085bc2..0000000
--- a/app/components/admin/UsersList.js
+++ /dev/null
@@ -1,773 +0,0 @@
-'use client';
-
-import { useState } from 'react';
-import { motion, AnimatePresence } from 'framer-motion';
-import {
- User,
- Mail,
- Phone,
- Calendar,
- Home,
- DollarSign,
- Search,
- Filter,
- Eye,
- X,
- CheckCircle,
- XCircle,
- ChevronDown,
- Users,
- Award,
- Clock,
- TrendingUp,
- CalendarDays,
- Shield
-} from 'lucide-react';
-import toast, { Toaster } from 'react-hot-toast';
-
-const FilterDialog = ({ isOpen, onClose, filters, onApplyFilters, onResetFilters }) => {
- const [localFilters, setLocalFilters] = useState({ ...filters });
-
- const identityTypes = [
- { id: 'all', label: 'الكل' },
- { id: 'syrian', label: 'هوية سورية' },
- { id: 'passport', label: 'جواز سفر' }
- ];
-
- const bookingRanges = [
- { id: 'all', label: 'الكل' },
- { id: '0-5', label: '0 - 5 حجوزات' },
- { id: '5-10', label: '5 - 10 حجوزات' },
- { id: '10-20', label: '10 - 20 حجوزات' },
- { id: '20+', label: 'أكثر من 20 حجز' }
- ];
-
- const spendingRanges = [
- { id: 'all', label: 'الكل' },
- { id: '0-500000', label: 'أقل من 500,000 ل.س' },
- { id: '500000-1000000', label: '500,000 - 1,000,000 ل.س' },
- { id: '1000000-5000000', label: '1,000,000 - 5,000,000 ل.س' },
- { id: '5000000+', label: 'أكثر من 5,000,000 ل.س' }
- ];
-
- const dateRanges = [
- { id: 'all', label: 'الكل' },
- { id: 'today', label: 'اليوم' },
- { id: 'week', label: 'آخر 7 أيام' },
- { id: 'month', label: 'آخر 30 يوم' },
- { id: 'year', label: 'آخر 12 شهر' }
- ];
-
- const applyFilters = () => {
- onApplyFilters(localFilters);
- onClose();
- toast.success('تم تطبيق الفلاتر بنجاح');
- };
-
- const resetFilters = () => {
- const resetData = {
- identityType: 'all',
- minBookings: '',
- maxBookings: '',
- minSpending: '',
- maxSpending: '',
- dateRange: 'all',
- activeOnly: false,
- inactiveOnly: false
- };
- setLocalFilters(resetData);
- onResetFilters();
- onClose();
- toast.success('تم إعادة تعيين الفلاتر');
- };
-
- if (!isOpen) return null;
-
- return (
-
- e.stopPropagation()}
- >
-
-
-
-
-
- تصفية متقدمة
-
-
حدد معايير التصفية المطلوبة
-
-
-
-
-
-
-
-
-
- {identityTypes.map((type) => (
-
- ))}
-
-
-
-
-
-
- setLocalFilters({...localFilters, minBookings: e.target.value})}
- className="px-4 py-2 border border-gray-300 rounded-xl focus:ring-2 focus:ring-blue-500"
- />
- setLocalFilters({...localFilters, maxBookings: e.target.value})}
- className="px-4 py-2 border border-gray-300 rounded-xl focus:ring-2 focus:ring-blue-500"
- />
-
-
- {bookingRanges.slice(1).map((range) => (
-
- ))}
-
-
-
-
-
-
- setLocalFilters({...localFilters, minSpending: e.target.value})}
- className="px-4 py-2 border border-gray-300 rounded-xl focus:ring-2 focus:ring-blue-500"
- />
- setLocalFilters({...localFilters, maxSpending: e.target.value})}
- className="px-4 py-2 border border-gray-300 rounded-xl focus:ring-2 focus:ring-blue-500"
- />
-
-
- {spendingRanges.slice(1).map((range) => (
-
- ))}
-
-
-
-
-
-
- {dateRanges.map((range) => (
-
- ))}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
-};
-
-const UserDetailsModal = ({ user, isOpen, onClose }) => {
- if (!isOpen || !user) return null;
-
- const formatCurrency = (amount) => {
- return amount?.toLocaleString() + ' ل.س';
- };
-
- const userBookings = [
- {
- id: 'BK001',
- property: 'فيلا فاخرة في المزة',
- startDate: '2024-03-10',
- endDate: '2024-03-15',
- amount: 2500000,
- status: 'completed'
- },
- {
- id: 'BK002',
- property: 'شقة حديثة في الشهباء',
- startDate: '2024-02-20',
- endDate: '2024-02-25',
- amount: 1250000,
- status: 'completed'
- },
- {
- id: 'BK003',
- property: 'بيت عائلي في بابا عمرو',
- startDate: '2024-04-01',
- endDate: '2024-04-10',
- amount: 3500000,
- status: 'confirmed'
- }
- ];
-
- return (
-
- e.stopPropagation()}
- >
-
-
-
-
-
- تفاصيل المستخدم
-
-
{user.name}
-
-
-
-
-
-
-
-
-
-
- معلومات شخصية
-
-
-
- الاسم الكامل:
- {user.name}
-
-
- البريد الإلكتروني:
- {user.email}
-
-
- رقم الهاتف:
- {user.phone}
-
-
- تاريخ التسجيل:
- {user.joinDate}
-
-
-
-
-
-
-
- معلومات الهوية
-
-
-
- نوع الهوية:
-
- {user.identityType === 'syrian' ? 'هوية سورية' : 'جواز سفر'}
-
-
-
- رقم الهوية:
- {user.identityNumber}
-
-
-
-
-
-
-
-
{user.totalBookings}
-
إجمالي الحجوزات
-
-
-
{user.activeBookings}
-
حجوزات نشطة
-
-
-
{formatCurrency(user.totalSpent)}
-
إجمالي المنصرف
-
-
-
-
-
-
- سجل الحجوزات
-
-
- {userBookings.map((booking) => (
-
-
-
{booking.property}
-
-
- {booking.startDate} - {booking.endDate}
-
-
-
-
-
{formatCurrency(booking.amount)}
-
المبلغ الإجمالي
-
-
- {booking.status === 'completed' ? 'مكتمل' : 'مؤكد'}
-
-
-
- ))}
-
- {userBookings.length === 0 && (
-
-
-
لا توجد حجوزات سابقة
-
- )}
-
-
-
-
-
-
-
-
- );
-};
-
-export default function UsersList() {
- const [users, setUsers] = useState([
- {
- id: 1,
- name: 'أحمد محمد',
- email: 'ahmed@example.com',
- phone: '0938123456',
- identityType: 'syrian',
- identityNumber: '123456789',
- joinDate: '2024-01-15',
- totalBookings: 3,
- activeBookings: 1,
- totalSpent: 1500000
- },
- {
- id: 2,
- name: 'سارة أحمد',
- email: 'sara@example.com',
- phone: '0945123789',
- identityType: 'passport',
- identityNumber: 'AB123456',
- joinDate: '2024-02-10',
- totalBookings: 2,
- activeBookings: 0,
- totalSpent: 500000
- }
- ]);
-
- const [searchTerm, setSearchTerm] = useState('');
- const [selectedUser, setSelectedUser] = useState(null);
- const [showFilterDialog, setShowFilterDialog] = useState(false);
- const [filters, setFilters] = useState({
- identityType: 'all',
- minBookings: '',
- maxBookings: '',
- minSpending: '',
- maxSpending: '',
- dateRange: 'all',
- activeOnly: false,
- inactiveOnly: false
- });
-
- const applyFilters = (newFilters) => {
- setFilters(newFilters);
- };
-
- const resetFilters = () => {
- setFilters({
- identityType: 'all',
- minBookings: '',
- maxBookings: '',
- minSpending: '',
- maxSpending: '',
- dateRange: 'all',
- activeOnly: false,
- inactiveOnly: false
- });
- setSearchTerm('');
- };
-
- const filteredUsers = users.filter(user => {
- if (searchTerm && !user.name.includes(searchTerm) && !user.email.includes(searchTerm) && !user.phone.includes(searchTerm)) {
- return false;
- }
-
- if (filters.identityType !== 'all' && user.identityType !== filters.identityType) {
- return false;
- }
-
- if (filters.minBookings && user.totalBookings < parseInt(filters.minBookings)) {
- return false;
- }
- if (filters.maxBookings && user.totalBookings > parseInt(filters.maxBookings)) {
- return false;
- }
-
- if (filters.minSpending && user.totalSpent < parseInt(filters.minSpending)) {
- return false;
- }
- if (filters.maxSpending && user.totalSpent > parseInt(filters.maxSpending)) {
- return false;
- }
-
- if (filters.activeOnly && user.activeBookings === 0) {
- return false;
- }
- if (filters.inactiveOnly && user.activeBookings > 0) {
- return false;
- }
-
- if (filters.dateRange !== 'all') {
- const joinDate = new Date(user.joinDate);
- const today = new Date();
- const diffDays = Math.floor((today - joinDate) / (1000 * 60 * 60 * 24));
-
- switch(filters.dateRange) {
- case 'today':
- if (joinDate.toDateString() !== today.toDateString()) return false;
- break;
- case 'week':
- if (diffDays > 7) return false;
- break;
- case 'month':
- if (diffDays > 30) return false;
- break;
- case 'year':
- if (diffDays > 365) return false;
- break;
- }
- }
-
- return true;
- });
-
- const filterStats = {
- total: filteredUsers.length,
- filtered: filteredUsers.length !== users.length
- };
-
- const getActiveFiltersCount = () => {
- let count = 0;
- if (filters.identityType !== 'all') count++;
- if (filters.minBookings || filters.maxBookings) count++;
- if (filters.minSpending || filters.maxSpending) count++;
- if (filters.dateRange !== 'all') count++;
- if (filters.activeOnly || filters.inactiveOnly) count++;
- return count;
- };
-
- return (
-
-
-
-
-
-
- setSearchTerm(e.target.value)}
- className="w-full pr-12 px-4 py-3 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500"
- />
-
-
-
- {filterStats.filtered && (
-
- )}
-
-
-
- {getActiveFiltersCount() > 0 && (
-
- الفلاتر النشطة:
- {filters.identityType !== 'all' && (
-
- {filters.identityType === 'syrian' ? 'هوية سورية' : 'جواز سفر'}
-
- )}
- {(filters.minBookings || filters.maxBookings) && (
-
- الحجوزات: {filters.minBookings || '0'} - {filters.maxBookings || '∞'}
-
- )}
- {(filters.minSpending || filters.maxSpending) && (
-
- الإنفاق: {parseInt(filters.minSpending || 0).toLocaleString()} - {parseInt(filters.maxSpending || '∞').toLocaleString()} ل.س
-
- )}
- {filters.dateRange !== 'all' && (
-
- {filters.dateRange === 'today' ? 'اليوم' :
- filters.dateRange === 'week' ? 'آخر 7 أيام' :
- filters.dateRange === 'month' ? 'آخر 30 يوم' : 'آخر 12 شهر'}
-
- )}
- {filters.activeOnly && (
-
- لديهم حجوزات نشطة
-
- )}
- {filters.inactiveOnly && (
-
- بدون حجوزات نشطة
-
- )}
-
- )}
-
-
-
- عرض {filteredUsers.length} مستخدم
- {filterStats.filtered && (
- (من {users.length})
- )}
-
-
-
-
- {filteredUsers.map((user, index) => (
-
-
-
-
- {user.name.charAt(0).toUpperCase()}
-
-
-
{user.name}
-
-
-
- {user.email}
-
-
-
-
- {user.identityType === 'syrian' ? 'هوية سورية' : 'جواز سفر'}
-
-
-
-
-
-
-
-
{user.totalBookings}
-
إجمالي الحجوزات
-
-
-
0 ? 'text-green-600' : 'text-gray-400'}`}>
- {user.activeBookings}
-
-
حجوزات نشطة
-
-
-
- {user.totalSpent.toLocaleString()}
-
-
إجمالي المنصرف
-
-
-
-
-
-
- ))}
-
-
- {filteredUsers.length === 0 && (
-
-
- لا توجد نتائج
- لا يوجد مستخدمون يطابقون معايير البحث
- {(searchTerm || getActiveFiltersCount() > 0) && (
-
- )}
-
- )}
-
-
setShowFilterDialog(false)}
- filters={filters}
- onApplyFilters={applyFilters}
- onResetFilters={resetFilters}
- />
-
- setSelectedUser(null)}
- />
-
- );
-}
\ No newline at end of file
diff --git a/app/enums/BookingStatus.js b/app/enums/BookingStatus.js
index ea4f05a..602f779 100644
--- a/app/enums/BookingStatus.js
+++ b/app/enums/BookingStatus.js
@@ -1,38 +1,29 @@
-/**
- * BookingStatus Enum
- * Backend values are strings
- * Used in: Reservation workflow
- */
+
const BookingStatus = Object.freeze({
PENDING: 'pending',
- OWNER_APPROVED: 'owner_approved',
- ADMIN_APPROVED: 'admin_approved',
- ACTIVE: 'active',
- COMPLETED: 'completed',
- REJECTED: 'rejected',
- CANCELLED: 'cancelled',
+ ownerConfirmed: 'ownerConfirmed',
+ depositPaid: 'depositPaid',
+ depositConfirmed: 'depositConfirmed',
+ completed: 'completed',
+ cancelled: 'cancelled',
});
-// Map status → Arabic label
const BookingStatusLabels = Object.freeze({
- [BookingStatus.PENDING]: 'بانتظار الموافقة',
- [BookingStatus.OWNER_APPROVED]: 'موافقة المالك',
- [BookingStatus.ADMIN_APPROVED]: 'موافقة الإدارة',
- [BookingStatus.ACTIVE]: 'إيجار نشط',
- [BookingStatus.COMPLETED]: 'منتهي',
- [BookingStatus.REJECTED]: 'مرفوض',
- [BookingStatus.CANCELLED]: 'ملغي',
+ [BookingStatus.PENDING]: 'قيد الانتظار',
+ [BookingStatus.ownerConfirmed]: 'مؤكد من المالك',
+ [BookingStatus.depositPaid]: 'تم دفع السلفة',
+ [BookingStatus.depositConfirmed]: 'تم تأكيد الدفع',
+ [BookingStatus.completed]: 'منتهي',
+ [BookingStatus.cancelled]: 'ملغي',
});
-// Map status → color class (Tailwind bg)
const BookingStatusColors = Object.freeze({
[BookingStatus.PENDING]: 'yellow',
- [BookingStatus.OWNER_APPROVED]: 'blue',
- [BookingStatus.ADMIN_APPROVED]: 'green',
- [BookingStatus.ACTIVE]: 'purple',
- [BookingStatus.COMPLETED]: 'gray',
- [BookingStatus.REJECTED]: 'red',
- [BookingStatus.CANCELLED]: 'red',
+ [BookingStatus.ownerConfirmed]: 'blue',
+ [BookingStatus.depositPaid]: 'orange',
+ [BookingStatus.depositConfirmed]: 'green',
+ [BookingStatus.completed]: 'teal',
+ [BookingStatus.cancelled]: 'red',
});
export { BookingStatus, BookingStatusLabels, BookingStatusColors };
diff --git a/app/enums/UserRole.js b/app/enums/UserRole.js
index ca2e712..5c9ade1 100644
--- a/app/enums/UserRole.js
+++ b/app/enums/UserRole.js
@@ -7,21 +7,18 @@ const UserRole = Object.freeze({
GUEST: 'guest',
CUSTOMER: 'customer',
OWNER: 'owner',
- ADMIN: 'admin',
});
const UserRoleLabels = Object.freeze({
[UserRole.GUEST]: 'زائر',
[UserRole.CUSTOMER]: 'مستأجر',
[UserRole.OWNER]: 'مالك عقار',
- [UserRole.ADMIN]: 'مدير النظام',
});
const UserRoleColors = Object.freeze({
[UserRole.GUEST]: 'gray',
[UserRole.CUSTOMER]: 'blue',
[UserRole.OWNER]: 'amber',
- [UserRole.ADMIN]: 'red',
});
export { UserRole, UserRoleLabels, UserRoleColors };
diff --git a/app/favorites/page.js b/app/favorites/page.js
index 1832795..a03aaea 100644
--- a/app/favorites/page.js
+++ b/app/favorites/page.js
@@ -12,14 +12,14 @@ import AuthService from '@/app/services/AuthService';
export default function FavoritesPage() {
const router = useRouter();
const { favorites, isLoading: favoritesLoading, removeFavorite } = useFavorites();
- const [isAdmin, setIsAdmin] = useState(false);
useEffect(() => {
- if (AuthService.isAdmin()) {
- router.push('/');
- return;
- }
- setIsAdmin(AuthService.isAdmin());
+ // Admin check removed
+ // if (AuthService.isAdmin()) {
+ // router.push('/');
+ // return;
+ // }
+ // setIsAdmin(AuthService.isAdmin());
}, [router]);
const formatCurrency = (amount) => {
diff --git a/app/login/page.js b/app/login/page.js
index 94ab127..c62aabb 100644
--- a/app/login/page.js
+++ b/app/login/page.js
@@ -126,11 +126,7 @@ export default function LoginPage() {
}
}
- const userRole = AuthService.isAdmin()
- ? "admin"
- : AuthService.isOwner()
- ? "owner"
- : "customer";
+ const userRole = AuthService.isOwner() ? "owner" : "customer";
console.log("[Login] User role:", userRole);
setIsSuccess(true);
@@ -139,11 +135,7 @@ export default function LoginPage() {
});
setTimeout(() => {
- if (userRole === "admin") {
- router.push("/admin");
- } else {
- router.push("/");
- }
+ router.push("/");
}, 1500);
} else if (result.status === 206) {
console.log("[Login] 206 — OTP required");
diff --git a/app/payments/page.js b/app/payments/page.js
index 5041477..fe8e70f 100644
--- a/app/payments/page.js
+++ b/app/payments/page.js
@@ -26,10 +26,11 @@ export default function PaymentsPage() {
const [payingId, setPayingId] = useState(null);
useEffect(() => {
- if (AuthService.isAdmin()) {
- router.push('/');
- return;
- }
+ // Admin check removed
+ // if (AuthService.isAdmin()) {
+ // router.push('/');
+ // return;
+ // }
loadReservations();
}, [router]);
diff --git a/app/property/[id]/PropertyDetail.js b/app/property/[id]/PropertyDetail.js
index 09c61f7..af0885b 100644
--- a/app/property/[id]/PropertyDetail.js
+++ b/app/property/[id]/PropertyDetail.js
@@ -1289,7 +1289,7 @@ export default function PropertyDetailsPage() {
{/* Sidebar */}
{/* Booking Card */}
- {property.isRent && !AuthService.isAdmin() && (
+ {property.isRent && (