diff --git a/app/admin/page.js b/app/admin/page.js
index a98dc5c..93103be 100644
--- a/app/admin/page.js
+++ b/app/admin/page.js
@@ -1,847 +1,179 @@
'use client';
import { motion } from 'framer-motion';
-import { useState, useEffect } from 'react';
+import { useState } from 'react';
import { useTranslation } from 'react-i18next';
-import '../i18n/config';
import {
- Users,
Home,
- Calendar,
- User,
- Clock,
+ Calendar,
+ Users,
DollarSign,
- PlusCircle,
- Edit,
- Trash2,
- CheckCircle,
- XCircle,
- Search,
- Filter,
- Download,
- Eye,
- MapPin,
- Bed,
- Bath,
- Square,
- Star,
- Phone,
- Mail,
- CalendarDays
+ TrendingUp,
+ Bell
} 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 '../i18n/config';
export default function AdminPage() {
const { t, i18n } = useTranslation();
- const [activeTab, setActiveTab] = useState('properties');
- const [searchQuery, setSearchQuery] = useState('');
- const [selectedUser, setSelectedUser] = useState(null);
-
- // احصل على اللغة الحالية من i18n
- const currentLanguage = i18n.language || 'en';
-
- const [stats, setStats] = useState({
- totalUsers: 0,
- totalProperties: 0,
- activeBookings: 0,
- availableProperties: 0
- });
+ const [activeTab, setActiveTab] = useState('dashboard');
+ const [showAddProperty, setShowAddProperty] = useState(false);
+ const [notifications, setNotifications] = useState(3);
- const [properties, setProperties] = useState([
- {
- id: 1,
- name: "luxuryVillaDamascus",
- type: "villa",
- price: 500000,
- location: "Damascus, Al-Mazzeh",
- bedrooms: 5,
- bathrooms: 4,
- area: 450,
- status: "available",
- images: [],
- features: ["swimmingPool", "privateGarden", "parking", "superLuxFinish"]
- },
- {
- id: 2,
- name: "modernApartmentAleppo",
- type: "apartment",
- price: 250000,
- location: "Aleppo, Al-Shahba",
- bedrooms: 3,
- bathrooms: 2,
- area: 180,
- status: "booked",
- images: [],
- features: ["equippedKitchen", "centralHeating", "balcony", "securitySystem"]
- },
- {
- id: 3,
- name: "familyHouseHoms",
- type: "house",
- price: 350000,
- location: "Homs, Baba Amr",
- bedrooms: 4,
- bathrooms: 3,
- area: 300,
- status: "available",
- images: [],
- features: ["largeGarden", "receptionHall", "maidRoom", "garage"]
- },
- {
- id: 4,
- name: "seasideApartmentLatakia",
- type: "apartment",
- price: 300000,
- location: "Latakia, Blue Beach",
- bedrooms: 3,
- bathrooms: 2,
- area: 200,
- status: "available",
- images: [],
- features: ["seaView", "centralHeating", "centralAC", "parking"]
- },
- {
- id: 5,
- name: "villaDaraa",
- type: "villa",
- price: 400000,
- location: "Daraa, Doctors District",
- bedrooms: 4,
- bathrooms: 3,
- area: 350,
- status: "booked",
- images: [],
- features: ["fruitGarden", "highWall", "advancedSecurity", "storage"]
- }
- ]);
-
- const [users, setUsers] = useState([
- {
- id: 1,
- name: "Ahmed Mohamed",
- email: "ahmed@example.com",
- phone: "+963 123 456 789",
- joinDate: "2024-01-15",
- activeBookings: 1,
- totalBookings: 3,
- currentBooking: {
- propertyId: 2,
- propertyName: "modernApartmentAleppo",
- startDate: "2024-02-01",
- endDate: "2024-08-01",
- duration: "6 months",
- totalAmount: 1500000
- }
- },
- {
- id: 2,
- name: "Sara Ahmed",
- email: "sara@example.com",
- phone: "+963 987 654 321",
- joinDate: "2024-02-10",
- activeBookings: 0,
- totalBookings: 2,
- currentBooking: null
- },
- {
- id: 3,
- name: "Mohammed Al-Halabi",
- email: "mohammed@example.com",
- phone: "+963 555 123 456",
- joinDate: "2024-01-25",
- activeBookings: 1,
- totalBookings: 1,
- currentBooking: {
- propertyId: 5,
- propertyName: "villaDaraa",
- startDate: "2024-02-15",
- endDate: "2024-05-15",
- duration: "3 months",
- totalAmount: 1200000
- }
- }
- ]);
-
- const [bookingRequests, setBookingRequests] = useState([
- {
- id: "B001",
- userId: 2,
- userName: "Sara Ahmed",
- propertyId: 1,
- propertyName: "luxuryVillaDamascus",
- startDate: "2024-03-01",
- endDate: "2024-06-01",
- duration: "3 months",
- totalAmount: 1500000,
- status: "pending",
- requestDate: "2024-02-20"
- },
- {
- id: "B002",
- userId: 1,
- userName: "Ahmed Mohamed",
- propertyId: 4,
- propertyName: "seasideApartmentLatakia",
- startDate: "2024-03-15",
- endDate: "2024-05-15",
- duration: "2 months",
- totalAmount: 600000,
- status: "pending",
- requestDate: "2024-02-18"
- }
- ]);
-
- const formatNumber = (num) => {
- return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
- };
-
- const fadeInUp = {
- hidden: { opacity: 0, y: 20 },
- visible: {
- opacity: 1,
- y: 0,
- transition: { duration: 0.5 }
- }
- };
-
- const staggerContainer = {
- hidden: { opacity: 0 },
- visible: {
- opacity: 1,
- transition: {
- staggerChildren: 0.1
- }
- }
- };
-
- const cardHover = {
- rest: {
- scale: 1,
- y: 0,
- boxShadow: "0 4px 6px rgba(0, 0, 0, 0.05)"
- },
- hover: {
- scale: 1.02,
- y: -5,
- boxShadow: "0 10px 25px rgba(0, 0, 0, 0.1)",
- transition: {
- type: "spring",
- stiffness: 300
- }
- }
- };
-
- const buttonHover = {
- rest: { scale: 1 },
- hover: { scale: 1.05 },
- tap: { scale: 0.98 }
- };
-
- useEffect(() => {
- const totalBookings = users.reduce((sum, user) => sum + user.activeBookings, 0);
- const availableProps = properties.filter(p => p.status === "available").length;
-
- setStats({
- totalUsers: users.length,
- totalProperties: properties.length,
- activeBookings: totalBookings,
- availableProperties: availableProps
- });
- }, [users, properties]);
-
- const handleBookingAction = (bookingId, action) => {
- setBookingRequests(prev =>
- prev.map(booking =>
- booking.id === bookingId
- ? { ...booking, status: action === 'accept' ? 'approved' : 'rejected' }
- : booking
- )
- );
- };
-
- const handlePropertyAction = (propertyId, action) => {
- if (action === 'delete') {
- setProperties(prev => prev.filter(p => p.id !== propertyId));
- }
- };
-
- const showUserDetails = (user) => {
- setSelectedUser(user);
- };
-
- const getLocation = (location) => {
- const [city, district] = location.split(",").map(s => s.trim());
-
- // تحويل القيم إلى مفاتيح ترجمة
- const cityKey = city.toLowerCase();
- const districtKey = district.replace(/\s+/g, '');
-
- return `${t(cityKey)}, ${t(districtKey)}`;
- };
+ 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 (
-
- {/* Header بدون زر اختيار اللغة */}
-
-
-
- {t("adminDashboard")}
-
-
{t("manageProperties")}
-
{t("pricesInSYP")}
-
-
-
- {/* إحصائيات */}
-
+
+
-
-
-
+
+
+
+ {t('adminDashboard')}
+
+
+ إدارة العقارات، الحجوزات، والحسابات المالية
+
-
-
{stats.totalUsers}
-
{t("totalUsers")}
-
-
-
- {users.filter(u => u.activeBookings > 0).length} {t("usersWithActiveBookings")}
+
+
-
-
-
-
-
-
-
{stats.totalProperties}
-
{t("totalProperties")}
-
-
-
- {stats.availableProperties} {t("propertiesAvailable")}
-
-
-
-
-
-
-
-
-
-
{stats.activeBookings}
-
{t("activeBookings")}
-
-
-
- {bookingRequests.filter(b => b.status === 'pending').length} {t("bookingRequestsPending")}
-
-
-
-
-
-
-
-
-
-
{stats.availableProperties}
-
{t("availableProperties")}
-
-
-
- {properties.filter(p => p.status === 'available').length} {t("propertiesReadyForRent")}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {activeTab === 'properties' && (
-
-
-
-
{t("propertiesManagement")}
-
{t("addEditDeleteProperties")}
-
-
-
- {t("addNewProperty")}
-
-
-
-
-
-
- setSearchQuery(e.target.value)}
- />
-
-
-
-
- {t("filter")}
-
-
-
- {t("export")}
-
-
-
-
-
- {properties.map((property) => (
-
-
-
-
-
{t(property.name)}
-
-
- {getLocation(property.location)}
-
-
-
- {property.status === 'available' ? t("available") : t("booked")}
-
-
-
-
-
-
-
- {t("bedrooms")}
-
-
{property.bedrooms}
-
-
-
-
-
- {t("bathrooms")}
-
-
{property.bathrooms}
-
-
-
-
-
- {t("area")}
-
-
{property.area} m²
-
-
-
-
-
- {t("price")}
-
-
{formatNumber(property.price)} SYP/{t("month")}
-
-
-
-
-
{t("features")}:
-
- {property.features.map((feature, idx) => (
-
- {t(feature)}
-
- ))}
-
-
-
-
-
-
- {t("viewDetails")}
-
-
-
- {t("edit")}
-
- handlePropertyAction(property.id, 'delete')}
- className="px-3 py-2 bg-red-100 text-red-800 hover:bg-red-200 rounded-lg flex items-center gap-2 text-xs"
- >
-
- {t("delete")}
-
-
-
-
- ))}
-
-
- )}
-
- {activeTab === 'bookings' && (
-
-
-
{t("bookingRequests")}
-
{t("manageBookingRequests")}
-
-
-
- {bookingRequests.map((request) => (
-
-
-
-
-
{t("bookingRequest")} #{request.id}
-
- {request.status === 'pending' ? t("pending") : request.status === 'approved' ? t("approved") : t("rejected")}
-
-
-
-
-
-
{t("user")}
-
{request.userName}
-
-
-
-
{t("property")}
-
{t(request.propertyName)}
-
-
-
-
{t("duration")}
-
{request.duration}
-
-
-
-
{t("totalAmount")}
-
{formatNumber(request.totalAmount)} SYP
-
-
-
-
-
-
- {t("from")} {request.startDate} {t("to")} {request.endDate}
-
-
-
- {t("requestDate")}: {request.requestDate}
-
-
-
-
- {request.status === 'pending' && (
-
- handleBookingAction(request.id, 'accept')}
- className="px-4 py-2.5 bg-emerald-600 hover:bg-emerald-700 text-white rounded-lg flex items-center gap-2 text-sm"
- >
-
- {t("accept")}
-
- handleBookingAction(request.id, 'reject')}
- className="px-4 py-2.5 bg-red-600 hover:bg-red-700 text-white rounded-lg flex items-center gap-2 text-sm"
- >
-
- {t("reject")}
-
-
- )}
-
-
- ))}
-
-
- )}
-
- {activeTab === 'users' && (
-
-
-
{t("users")}
-
{t("viewUserDetails")}
-
-
-
- {users.map((user) => (
-
-
-
-
-
-
-
-
{user.activeBookings}
-
{t("activeBookings")}
-
-
-
{user.totalBookings}
-
{t("totalBookings")}
-
-
-
-
- {user.currentBooking ? (
-
-
-
- {t("currentActiveBooking")}
-
-
-
-
{t("property")}
-
{t(user.currentBooking.propertyName)}
-
-
-
{t("duration")}
-
{user.currentBooking.duration}
-
-
-
{t("totalAmount")}
-
{formatNumber(user.currentBooking.totalAmount)} SYP
-
-
-
{t("bookingPeriod")}
-
- {user.currentBooking.startDate} {t("to")} {user.currentBooking.endDate}
-
-
-
-
- ) : (
-
-
{t("noActiveBookings")}
-
- )}
-
-
- showUserDetails(user)}
- className="px-4 py-2.5 bg-blue-700 hover:bg-blue-800 text-white rounded-lg flex items-center gap-2 text-sm"
- >
-
- {t("viewFullDetails")}
-
-
-
-
- ))}
-
-
- )}
-
-
- {selectedUser && (
-
-
-
-
-
{t("userDetails")}: {selectedUser.name}
+
+
+ {tabs.map((tab) => {
+ const Icon = tab.icon;
+ return (
+ );
+ })}
+
+
+
+
+ {activeTab === 'dashboard' && (
+
+
+
+ )}
+
+ {activeTab === 'properties' && (
+
+
+
+
إدارة العقارات
+
إضافة وتعديل العقارات مع تحديد نسب الأرباح
+
+
-
-
-
-
-
{t("personalInformation")}
-
-
- {t("fullName")}:
- {selectedUser.name}
-
-
- {t("email")}:
- {selectedUser.email}
-
-
- {t("phoneNumber")}:
- {selectedUser.phone}
-
-
- {t("joinDate")}:
- {selectedUser.joinDate}
-
-
-
-
-
-
{t("bookingStatistics")}
-
-
- {t("activeBookings")}:
- {selectedUser.activeBookings}
-
-
- {t("totalBookings")}:
- {selectedUser.totalBookings}
-
-
-
-
+
+
+ )}
+
+ {activeTab === 'bookings' && (
+
+
+
+ )}
+
+ {activeTab === 'users' && (
+
+
+
+ )}
+
+ {activeTab === 'ledger' && (
+
+
+
+ )}
+
+ {activeTab === 'reports' && (
+
+
+ قريباً... تقارير متقدمة
-
-
+
+ )}
- )}
-
+
+ {showAddProperty && (
+
setShowAddProperty(false)}
+ onSuccess={() => {
+ setShowAddProperty(false);
+ }}
+ />
+ )}
+
+
);
}
\ No newline at end of file
diff --git a/app/components/NavLinks.js b/app/components/NavLinks.js
index 415151e..803389a 100644
--- a/app/components/NavLinks.js
+++ b/app/components/NavLinks.js
@@ -4,7 +4,7 @@ import Link from 'next/link';
export function NavLink({ href, children }) {
const pathname = usePathname();
- const isActive = pathname === href;
+ const isActive = pathname === href || pathname.startsWith(href + '/');
return (
{
+ 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 COMMISSION_TYPE.FROM_TENANT:
+ return dailyPrice + commission;
+ case COMMISSION_TYPE.FROM_OWNER:
+ return dailyPrice;
+ case COMMISSION_TYPE.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
new file mode 100644
index 0000000..cdb94c5
--- /dev/null
+++ b/app/components/admin/BookingRequests.js
@@ -0,0 +1,948 @@
+'use client';
+
+import { useState } from 'react';
+import { motion, AnimatePresence } from 'framer-motion';
+import {
+ CheckCircle,
+ XCircle,
+ Clock,
+ User,
+ Home,
+ Calendar,
+ DollarSign,
+ AlertCircle,
+ Key,
+ DoorOpen,
+ Shield,
+ Phone,
+ Mail,
+ MessageCircle,
+ ChevronDown,
+ ChevronUp,
+ FileText,
+ Download,
+ Printer,
+ History
+} from 'lucide-react';
+
+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 RequestDetailsDialog = ({ request, isOpen, onClose }) => {
+ if (!isOpen || !request) return null;
+
+ const formatCurrency = (amount) => {
+ return amount?.toLocaleString() + ' ل.س';
+ };
+
+ return (
+
+ e.stopPropagation()}
+ >
+
+
+
+ تفاصيل الطلب #{request.id}
+
+
+
+
+
+
+
+
+ معلومات المستأجر
+
+
+
+
+
{request.user}
+
+
+
+
+ {request.userType === 'syrian' ? '🇸🇾 هوية سورية' : '🛂 جواز سفر'}
+ {request.identityNumber}
+
+
+
+
+
+
+ {request.userEmail}
+
+
+
+
+
+
+ {request.userPhone}
+
+
+
+
+
+
+
+
+ معلومات العقار
+
+
+
+
+
{request.property}
+
+
+
+
{formatCurrency(request.dailyPrice)}
+
+
+
+
+
+
+
+ تفاصيل الحجز
+
+
+
+
+
{request.startDate}
+
+
+
+
{request.endDate}
+
+
+
+
{request.days} يوم
+
+
+
+
{formatCurrency(request.totalAmount)}
+
+
+
+
+
+
+
+ المعلومات المالية
+
+
+
+
+
{formatCurrency(request.securityDeposit)}
+
+
+
+
{request.commissionRate}%
+
+
+
+
{request.commissionType}
+
+
+
+
{formatCurrency(request.commissionAmount)}
+
+
+
+
+
+
+
+ سجل الإجراءات
+
+
+
+
+
تم إنشاء الطلب: {request.requestDate}
+
+ {request.ownerApproved && (
+
+ )}
+ {request.adminApproved && (
+
+ )}
+ {request.ownerDelivered && (
+
+
+
تم تسليم المفتاح من المالك
+
+ )}
+ {request.notes && (
+
+ )}
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+const RequestCard = ({ request, onAction, onViewDetails }) => {
+ const [expanded, setExpanded] = useState(false);
+
+ const formatCurrency = (amount) => {
+ return amount?.toLocaleString() + ' ل.س';
+ };
+
+ const getStatusColor = (status) => {
+ switch(status) {
+ case 'pending': return 'border-yellow-400 bg-yellow-50';
+ case 'owner_approved': return 'border-blue-400 bg-blue-50';
+ case 'admin_approved': return 'border-green-400 bg-green-50';
+ case 'active': return 'border-purple-400 bg-purple-50';
+ case 'completed': return 'border-gray-400 bg-gray-50';
+ case 'rejected': return 'border-red-400 bg-red-50';
+ default: return 'border-gray-200 bg-white';
+ }
+ };
+
+ const getStatusBadge = (status) => {
+ const styles = {
+ pending: 'bg-yellow-500 text-white',
+ owner_approved: 'bg-blue-500 text-white',
+ admin_approved: 'bg-green-500 text-white',
+ active: 'bg-purple-500 text-white',
+ completed: 'bg-gray-500 text-white',
+ rejected: 'bg-red-500 text-white'
+ };
+
+ const labels = {
+ pending: ' بانتظار الموافقة',
+ owner_approved: ' موافقة المالك',
+ admin_approved: ' موافقة الإدارة',
+ active: ' إيجار نشط',
+ completed: ' منتهي',
+ rejected: ' مرفوض'
+ };
+
+ return (
+
+ {labels[status]}
+
+ );
+ };
+
+ return (
+
+ setExpanded(!expanded)}>
+
+
+ طلب #{request.id}
+ {getStatusBadge(request.status)}
+
+
+ {request.requestDate}
+ {expanded ? (
+
+ ) : (
+
+ )}
+
+
+
+
+
+
+ {request.user}
+
+
+
+ {request.property}
+
+
+
+ {request.days} أيام
+
+
+
+ {formatCurrency(request.totalAmount)}
+
+
+
+
+
+ {expanded && (
+
+
+
+
سلفة ضمان
+
{formatCurrency(request.securityDeposit)}
+
+
+
العمولة
+
{request.commissionRate}% ({request.commissionType})
+
+
+
مدة الإيجار
+
{request.startDate} إلى {request.endDate}
+
+
+
+ {(request.ownerApproved || request.adminApproved) && (
+
+
+
+ معلومات الاتصال
+
+
+
+
+ {request.userEmail}
+
+
+
+ {request.userPhone}
+
+
+
+ )}
+
+
+ {request.status === 'pending' && (
+
+
+
+
+
+ )}
+
+ {request.status === 'owner_approved' && (
+
+
+
+
+
+ )}
+
+ {request.status === 'admin_approved' && (
+
+
+
+
+
+
+ )}
+
+ {request.status === 'active' && (
+
+
+
+
+
+
+ {request.actualStartDate && (
+
+
+ بدأ الإيجار: {request.actualStartDate}
+ المدة: {request.days} يوم
+
+
+
+ )}
+
+ )}
+
+
+ )}
+
+
+ );
+};
+
+export default function BookingRequests() {
+ const [requests, setRequests] = useState([
+ {
+ 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,
+ 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: 'owner_approved',
+ requestDate: '2024-02-24',
+ ownerApproved: true,
+ adminApproved: false,
+ ownerDelivered: false,
+ tenantReceived: false,
+ tenantLeft: false,
+ ownerReceived: 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,
+ securityDepositReturned: null,
+ contractSigned: true,
+ notes: 'عقد موقع إلكترونياً',
+ actualStartDate: '2024-02-20',
+ actualEndDate: null
+ }
+ ]);
+
+ const [filter, setFilter] = useState('all');
+ const [reasonDialog, setReasonDialog] = useState({ isOpen: false, requestId: null, type: null });
+ const [detailsDialog, setDetailsDialog] = useState({ isOpen: false, request: null });
+
+ const handleAction = (action, data) => {
+ switch(action) {
+ case 'owner_approve':
+ handleOwnerApprove(data);
+ break;
+ case 'owner_reject':
+ setReasonDialog({ isOpen: true, requestId: data, type: 'owner' });
+ break;
+ case 'admin_approve':
+ handleAdminApprove(data);
+ break;
+ case 'admin_reject':
+ setReasonDialog({ isOpen: true, requestId: data, type: 'admin' });
+ break;
+ case 'deliver_key':
+ handleKeyDelivery(data.id, data.type);
+ break;
+ case 'receive_property':
+ handleKeyDelivery(data.id, data.type);
+ break;
+ case 'tenant_leave':
+ handleEndRental(data.id, data.type);
+ break;
+ case 'owner_receive':
+ handleEndRental(data.id, data.type);
+ break;
+ case 'view_details':
+ setDetailsDialog({ isOpen: true, request: data });
+ break;
+ default:
+ break;
+ }
+ };
+
+ const handleRejectWithReason = (reason) => {
+ const { requestId, type } = reasonDialog;
+
+ setRequests(prev =>
+ prev.map(req =>
+ req.id === requestId
+ ? {
+ ...req,
+ status: 'rejected',
+ [type === 'owner' ? 'ownerApproved' : 'adminApproved']: false,
+ rejectionReason: reason,
+ rejectionType: type,
+ notes: `${type === 'owner' ? 'رفض من المالك' : 'رفض إداري'}: ${reason}`
+ }
+ : req
+ )
+ );
+
+ setReasonDialog({ isOpen: false, requestId: null, type: null });
+ };
+
+ const handleOwnerApprove = (requestId) => {
+ setRequests(prev =>
+ prev.map(req =>
+ req.id === requestId
+ ? {
+ ...req,
+ ownerApproved: true,
+ status: 'owner_approved',
+ notes: 'تمت الموافقة من قبل المالك'
+ }
+ : req
+ )
+ );
+ };
+
+ const handleAdminApprove = (requestId) => {
+ setRequests(prev =>
+ prev.map(req =>
+ req.id === requestId
+ ? {
+ ...req,
+ adminApproved: true,
+ status: 'admin_approved',
+ notes: 'تمت الموافقة من قبل الإدارة'
+ }
+ : req
+ )
+ );
+ };
+
+ const handleKeyDelivery = (requestId, userType) => {
+ setRequests(prev =>
+ prev.map(req => {
+ if (req.id === requestId) {
+ const updates = {};
+
+ if (userType === 'owner') {
+ updates.ownerDelivered = true;
+ updates.notes = 'تم تسليم المفتاح من قبل المالك';
+ }
+ if (userType === 'tenant') {
+ updates.tenantReceived = true;
+ updates.notes = 'تم استلام العقار من قبل المستأجر';
+ }
+
+ if ((userType === 'owner' && req.tenantReceived) ||
+ (userType === 'tenant' && req.ownerDelivered)) {
+ updates.status = 'active';
+ updates.contractSigned = true;
+ updates.actualStartDate = new Date().toISOString().split('T')[0];
+ updates.notes = 'بدأت فترة الإيجار الفعلية';
+ }
+
+ return { ...req, ...updates };
+ }
+ return req;
+ })
+ );
+ };
+
+ const handleEndRental = (requestId, userType) => {
+ setRequests(prev =>
+ prev.map(req => {
+ if (req.id === requestId) {
+ const updates = {};
+
+ if (userType === 'tenant') {
+ updates.tenantLeft = true;
+ updates.notes = 'غادر المستأجر العقار';
+ }
+ if (userType === 'owner') {
+ updates.ownerReceived = true;
+ updates.notes = 'استلم المالك العقار';
+ }
+
+ if ((userType === 'tenant' && req.ownerReceived) ||
+ (userType === 'owner' && req.tenantLeft)) {
+
+ const actualEndDate = new Date();
+ const actualStartDate = new Date(req.actualStartDate || req.startDate);
+ const actualDays = Math.ceil((actualEndDate - actualStartDate) / (1000 * 60 * 60 * 24));
+ const actualAmount = req.dailyPrice * actualDays;
+
+ const damageDeduction = 0;
+ const refundAmount = req.securityDeposit - damageDeduction;
+
+ updates.status = 'completed';
+ updates.actualEndDate = actualEndDate.toISOString().split('T')[0];
+ updates.actualDays = actualDays;
+ updates.actualAmount = actualAmount;
+ updates.securityDepositReturned = refundAmount;
+ updates.damageDeduction = damageDeduction;
+ updates.notes = `انتهى الإيجار بعد ${actualDays} يوم - المبلغ الفعلي: ${actualAmount.toLocaleString()} ل.س - مسترد الضمان: ${refundAmount.toLocaleString()} ل.س`;
+ }
+
+ return { ...req, ...updates };
+ }
+ return req;
+ })
+ );
+ };
+
+ const filteredRequests = requests.filter(req =>
+ filter === 'all' ? true : req.status === filter
+ );
+
+ const stats = {
+ total: requests.length,
+ pending: requests.filter(r => r.status === 'pending').length,
+ active: requests.filter(r => r.status === 'active').length,
+ completed: requests.filter(r => r.status === 'completed').length
+ };
+
+ return (
+
+
+
+ {stats.total}
+ إجمالي الطلبات
+
+
+ {stats.pending}
+ قيد الانتظار
+
+
+ {stats.active}
+ إيجارات نشطة
+
+
+ {stats.completed}
+ منتهية
+
+
+
+
+
+
تصفية حسب الحالة
+ {filteredRequests.length} طلب
+
+
+ {[
+ { id: 'all', label: 'الكل', color: 'gray' },
+ { id: 'pending', label: 'قيد الانتظار', color: 'yellow' },
+ { id: 'owner_approved', label: 'موافقة المالك', color: 'blue' },
+ { id: 'admin_approved', label: 'موافقة الإدارة', color: 'green' },
+ { id: 'active', label: 'إيجارات نشطة', color: 'purple' },
+ { id: 'completed', label: 'منتهية', color: 'gray' }
+ ].map((tab) => (
+
+ ))}
+
+
+
+
+ {filteredRequests.map((request) => (
+
+ ))}
+
+
+ {filteredRequests.length === 0 && (
+
+
+
+
+ لا توجد طلبات حجز
+ لا توجد طلبات حجز في هذه الفئة
+
+ )}
+
+
setReasonDialog({ isOpen: false, requestId: null, type: null })}
+ onConfirm={handleRejectWithReason}
+ title={reasonDialog.type === 'owner' ? 'رفض من المالك' : 'رفض إداري'}
+ />
+
+ setDetailsDialog({ isOpen: false, request: null })}
+ />
+
+ );
+}
\ No newline at end of file
diff --git a/app/components/admin/DashboardStats.js b/app/components/admin/DashboardStats.js
new file mode 100644
index 0000000..5b15024
--- /dev/null
+++ b/app/components/admin/DashboardStats.js
@@ -0,0 +1,139 @@
+'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
new file mode 100644
index 0000000..65bef51
--- /dev/null
+++ b/app/components/admin/LedgerBook.js
@@ -0,0 +1,352 @@
+'use client';
+
+import { useState, useEffect } from 'react';
+import { motion } from 'framer-motion';
+import {
+ DollarSign,
+ Calendar,
+ User,
+ Home,
+ Download,
+ Filter,
+ Search,
+ TrendingUp,
+ TrendingDown,
+ Wallet,
+ Shield
+} from 'lucide-react';
+import { formatCurrency } from '@/app/utils/calculations';
+
+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
+ });
+
+ 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 = () => {
+ const csvContent = [
+ ['التاريخ', 'الوصف', 'من', 'إلى', 'المبلغ', 'العمولة', 'الحالة'],
+ ...filteredTransactions.map(t => [
+ t.date,
+ t.description,
+ t.fromUser,
+ t.toUser,
+ t.amount,
+ t.commission,
+ t.status
+ ])
+ ].map(row => row.join(',')).join('\n');
+
+ const blob = new Blob([csvContent], { type: 'text/csv' });
+ const url = window.URL.createObjectURL(blob);
+ const a = document.createElement('a');
+ a.href = url;
+ a.download = `ledger_${new Date().toISOString()}.csv`;
+ a.click();
+ };
+
+ return (
+
+
+
+
+
+ إجمالي الإيرادات
+
+ {formatCurrency(summary.totalRevenue)}
+
+
+
+
+
+ أرباح المنصة
+
+ {formatCurrency(summary.commissionEarned)}
+
+
+
+
+
+ سلف الضمان
+
+ {formatCurrency(summary.securityDeposits)}
+
+
+
+
+
+ المدفوعات المعلقة
+
+ {formatCurrency(summary.pendingPayments)}
+
+
+
+
+
+
+
+
+
+
+ | التاريخ |
+ الوصف |
+ من |
+ إلى |
+ المبلغ |
+ العمولة |
+ الحالة |
+
+
+
+ {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
new file mode 100644
index 0000000..de975a2
--- /dev/null
+++ b/app/components/admin/PropertiesTable.js
@@ -0,0 +1,157 @@
+'use client';
+
+import { useState } from 'react';
+import { motion } from 'framer-motion';
+import {
+ Edit,
+ Trash2,
+ Eye,
+ MapPin,
+ Bed,
+ Bath,
+ Square,
+ DollarSign,
+ Percent,
+ MoreVertical
+} from 'lucide-react';
+
+export default function PropertiesTable() {
+ const [properties, setProperties] = useState([
+ {
+ id: 1,
+ title: 'luxuryVillaDamascus',
+ type: 'villa',
+ location: 'دمشق, المزة',
+ price: 500000,
+ commission: 5,
+ commissionType: 'من المالك',
+ bedrooms: 5,
+ bathrooms: 4,
+ area: 450,
+ status: 'available',
+ bookings: 3
+ },
+ {
+ id: 2,
+ title: 'modernApartmentAleppo',
+ type: 'apartment',
+ location: 'حلب, الشهباء',
+ price: 250000,
+ commission: 7,
+ commissionType: 'من المستأجر',
+ bedrooms: 3,
+ bathrooms: 2,
+ area: 180,
+ status: 'booked',
+ bookings: 1
+ }
+ ]);
+
+ 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]}
+
+ );
+ };
+
+ return (
+
+
+
+
+ | العقار |
+ الموقع |
+ السعر/يوم |
+ العمولة |
+ المصدر |
+ التفاصيل |
+ الحالة |
+ الإجراءات |
+
+
+
+ {properties.map((property, index) => (
+
+
+ {property.title}
+ {property.type}
+ |
+
+
+
+ {property.location}
+
+ |
+
+ {formatCurrency(property.price)}
+ |
+
+
+
+ {property.commission}%
+
+ |
+ {property.commissionType} |
+
+
+ {property.bedrooms}
+ {property.bathrooms}
+ {property.area}m²
+
+ |
+
+ {getStatusBadge(property.status)}
+ |
+
+
+
+
+
+
+
+ |
+
+ ))}
+
+
+
+ {properties.length === 0 && (
+
+
+
لا توجد عقارات مضافة بعد
+
+ )}
+
+ );
+}
\ No newline at end of file
diff --git a/app/components/admin/UsersList.js b/app/components/admin/UsersList.js
new file mode 100644
index 0000000..ef7713e
--- /dev/null
+++ b/app/components/admin/UsersList.js
@@ -0,0 +1,190 @@
+'use client';
+
+import { useState } from 'react';
+import { motion } from 'framer-motion';
+import {
+ User,
+ Mail,
+ Phone,
+ Calendar,
+ Home,
+ DollarSign,
+ Search,
+ Filter,
+ Eye
+} from 'lucide-react';
+
+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 filteredUsers = users.filter(user =>
+ user.name.includes(searchTerm) ||
+ user.email.includes(searchTerm) ||
+ user.phone.includes(searchTerm)
+ );
+
+ return (
+
+
+
+
+ setSearchTerm(e.target.value)}
+ className="w-full pr-10 px-4 py-2 border rounded-lg focus:ring-2 focus:ring-blue-500"
+ />
+
+
+
+
+
+ {filteredUsers.map((user, index) => (
+
+
+
+
+
+
+
{user.totalBookings}
+
إجمالي الحجوزات
+
+
+
{user.activeBookings}
+
حجوزات نشطة
+
+
+
+ {user.totalSpent.toLocaleString()}
+
+
إجمالي المنصرف
+
+
+
+
+
+
+ ))}
+
+
+ {selectedUser && (
+
+
+
+
تفاصيل المستخدم
+
+
+
+
+
+
+
+
{selectedUser.name}
+
+
+
+
{selectedUser.email}
+
+
+
+
{selectedUser.phone}
+
+
+
+
+ {selectedUser.identityType === 'syrian' ? 'هوية سورية' : 'جواز سفر'}
+
+
+
+
+
{selectedUser.identityNumber}
+
+
+
+
{selectedUser.joinDate}
+
+
+
+
+
سجل الحجوزات
+
+ لا توجد حجوزات سابقة
+
+
+
+
+
+ )}
+
+ );
+}
\ No newline at end of file
diff --git a/app/components/property/BookingCalendar.js b/app/components/property/BookingCalendar.js
new file mode 100644
index 0000000..8ea9d0c
--- /dev/null
+++ b/app/components/property/BookingCalendar.js
@@ -0,0 +1,162 @@
+'use client';
+
+import { useState } from 'react';
+import { motion } from 'framer-motion';
+import { Calendar as CalendarIcon, ChevronLeft, ChevronRight } from 'lucide-react';
+
+export default function BookingCalendar({ property, onDateSelect }) {
+ const [currentMonth, setCurrentMonth] = useState(new Date());
+ const [selectedRange, setSelectedRange] = useState({ start: null, end: null });
+ const [bookedDates, setBookedDates] = useState(property.bookings || []);
+
+ const daysInMonth = new Date(
+ currentMonth.getFullYear(),
+ currentMonth.getMonth() + 1,
+ 0
+ ).getDate();
+
+ const firstDayOfMonth = new Date(
+ currentMonth.getFullYear(),
+ currentMonth.getMonth(),
+ 1
+ ).getDay();
+
+ const monthNames = [
+ 'يناير', 'فبراير', 'مارس', 'إبريل', 'مايو', 'يونيو',
+ 'يوليو', 'أغسطس', 'سبتمبر', 'أكتوبر', 'نوفمبر', 'ديسمبر'
+ ];
+
+ const isDateBooked = (date) => {
+ const dateStr = date.toISOString().split('T')[0];
+ return bookedDates.some(booking => {
+ const start = new Date(booking.startDate);
+ const end = new Date(booking.endDate);
+ const current = new Date(date);
+ return current >= start && current <= end;
+ });
+ };
+
+ const isInSelectedRange = (date) => {
+ if (!selectedRange.start || !selectedRange.end) return false;
+ const dateStr = date.toISOString().split('T')[0];
+ return dateStr >= selectedRange.start && dateStr <= selectedRange.end;
+ };
+
+ const handleDateClick = (date) => {
+ if (isDateBooked(date)) return;
+
+ const dateStr = date.toISOString().split('T')[0];
+
+ if (!selectedRange.start || (selectedRange.start && selectedRange.end)) {
+ setSelectedRange({ start: dateStr, end: null });
+ } else {
+ if (dateStr > selectedRange.start) {
+ setSelectedRange({ ...selectedRange, end: dateStr });
+ onDateSelect?.({ start: selectedRange.start, end: dateStr });
+ } else {
+ setSelectedRange({ start: dateStr, end: null });
+ }
+ }
+ };
+
+ const changeMonth = (direction) => {
+ setCurrentMonth(new Date(
+ currentMonth.getFullYear(),
+ currentMonth.getMonth() + direction,
+ 1
+ ));
+ };
+
+ const renderDays = () => {
+ const days = [];
+ const totalDays = daysInMonth + firstDayOfMonth;
+
+ for (let i = 0; i < totalDays; i++) {
+ if (i < firstDayOfMonth) {
+ days.push();
+ } else {
+ const dayNumber = i - firstDayOfMonth + 1;
+ const date = new Date(
+ currentMonth.getFullYear(),
+ currentMonth.getMonth(),
+ dayNumber
+ );
+
+ const isBooked = isDateBooked(date);
+ const isSelected = isInSelectedRange(date);
+ const isToday = date.toDateString() === new Date().toDateString();
+
+ days.push(
+ handleDateClick(date)}
+ disabled={isBooked}
+ className={`
+ p-2 rounded-lg text-center transition-all
+ ${isBooked ? 'bg-gray-200 text-gray-400 cursor-not-allowed line-through' : 'hover:bg-amber-100 cursor-pointer'}
+ ${isSelected ? 'bg-amber-500 text-white hover:bg-amber-600' : ''}
+ ${isToday && !isSelected && !isBooked ? 'border-2 border-amber-500' : ''}
+ `}
+ >
+ {dayNumber}
+
+ );
+ }
+ }
+ return days;
+ };
+
+ return (
+
+
+
+
+
+
+ {monthNames[currentMonth.getMonth()]} {currentMonth.getFullYear()}
+
+
+
+
+
+
+
جمعة
+
سبت
+
أحد
+
إثنين
+
ثلاثاء
+
أربعاء
+
خميس
+
+
+ {renderDays()}
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/app/contexts/PropertyContext.js b/app/contexts/PropertyContext.js
new file mode 100644
index 0000000..879fc94
--- /dev/null
+++ b/app/contexts/PropertyContext.js
@@ -0,0 +1,141 @@
+'use client';
+
+import { createContext, useContext, useState, useCallback } from 'react';
+
+const PropertyContext = createContext();
+
+export const useProperties = () => {
+ const context = useContext(PropertyContext);
+ if (!context) {
+ throw new Error('useProperties must be used within PropertyProvider');
+ }
+ return context;
+};
+
+export const PropertyProvider = ({ children }) => {
+ const [properties, setProperties] = useState([]);
+ const [loading, setLoading] = useState(false);
+ const [error, setError] = useState(null);
+
+ const addProperty = useCallback(async (propertyData) => {
+ setLoading(true);
+ try {
+ await new Promise(resolve => setTimeout(resolve, 500));
+
+ const newProperty = {
+ id: Date.now().toString(),
+ ...propertyData,
+ createdAt: new Date().toISOString(),
+ updatedAt: new Date().toISOString(),
+ bookings: [],
+ status: propertyData.status || 'available'
+ };
+
+ setProperties(prev => [...prev, newProperty]);
+ return newProperty;
+ } catch (err) {
+ setError(err.message);
+ throw err;
+ } finally {
+ setLoading(false);
+ }
+ }, []);
+
+ const updateProperty = useCallback(async (id, updates) => {
+ setLoading(true);
+ try {
+ await new Promise(resolve => setTimeout(resolve, 500));
+
+ setProperties(prev =>
+ prev.map(p => p.id === id
+ ? { ...p, ...updates, updatedAt: new Date().toISOString() }
+ : p
+ )
+ );
+ } catch (err) {
+ setError(err.message);
+ throw err;
+ } finally {
+ setLoading(false);
+ }
+ }, []);
+
+ const deleteProperty = useCallback(async (id) => {
+ setLoading(true);
+ try {
+ await new Promise(resolve => setTimeout(resolve, 500));
+ setProperties(prev => prev.filter(p => p.id !== id));
+ } catch (err) {
+ setError(err.message);
+ throw err;
+ } finally {
+ setLoading(false);
+ }
+ }, []);
+
+ const getProperty = useCallback((id) => {
+ return properties.find(p => p.id === id);
+ }, [properties]);
+
+ const checkAvailability = useCallback((propertyId, startDate, endDate) => {
+ const property = properties.find(p => p.id === propertyId);
+ if (!property) return false;
+
+ const checkStart = new Date(startDate);
+ const checkEnd = new Date(endDate);
+
+ return !property.bookings?.some(booking => {
+ if (booking.status === 'cancelled' || booking.status === 'rejected') {
+ return false;
+ }
+
+ const bookingStart = new Date(booking.startDate);
+ const bookingEnd = new Date(booking.endDate);
+
+ return (
+ (checkStart >= bookingStart && checkStart <= bookingEnd) ||
+ (checkEnd >= bookingStart && checkEnd <= bookingEnd) ||
+ (checkStart <= bookingStart && checkEnd >= bookingEnd)
+ );
+ });
+ }, [properties]);
+
+ const getPropertiesByOwner = useCallback((ownerId) => {
+ return properties.filter(p => p.ownerId === ownerId);
+ }, [properties]);
+
+ const getAvailableProperties = useCallback(() => {
+ return properties.filter(p => p.status === 'available');
+ }, [properties]);
+
+ const updatePropertyStatus = useCallback(async (id, status) => {
+ return updateProperty(id, { status });
+ }, [updateProperty]);
+
+ const addBookingToProperty = useCallback(async (propertyId, bookingData) => {
+ const property = getProperty(propertyId);
+ if (!property) throw new Error('Property not found');
+
+ const updatedBookings = [...(property.bookings || []), bookingData];
+ return updateProperty(propertyId, { bookings: updatedBookings });
+ }, [getProperty, updateProperty]);
+
+ return (
+
+ {children}
+
+ );
+};
\ No newline at end of file
diff --git a/app/i18n/config.js b/app/i18n/config.js
index f7bca3e..92da46a 100644
--- a/app/i18n/config.js
+++ b/app/i18n/config.js
@@ -6,7 +6,7 @@ const resources = {
en: {
translation: {
"home": "Home",
- "ourProducts": "Our Products",
+ "ourProducts": "Our Properties",
"admin": "Admin",
"logoAlt": "SweetHome Logo",
"brandNamePart1": "Sweet",
@@ -184,7 +184,7 @@ const resources = {
translation: {
"home": "الرئيسية",
- "ourProducts": "منتجاتنا",
+ "ourProducts": "عقاراتنا",
"admin": "الإدارة",
// "logoAlt": "شعار سويت هوم",
"brandNamePart1": "سويت",
diff --git a/app/layout.js b/app/layout.js
index 37cf276..8aa78be 100644
--- a/app/layout.js
+++ b/app/layout.js
@@ -104,7 +104,7 @@ export default function RootLayout({ children }) {
{t("home")}
-
+
{t("ourProducts")}
@@ -182,7 +182,7 @@ export default function RootLayout({ children }) {
{t("home")}
-
+
{t("ourProducts")}
@@ -229,7 +229,7 @@ export default function RootLayout({ children }) {
-
+
{t("ourProducts")}
diff --git a/app/page.js b/app/page.js
index 30d9cd2..b63f67c 100644
--- a/app/page.js
+++ b/app/page.js
@@ -61,7 +61,7 @@ export default function HomePage() {
{t("house")}
-
+ {/* */}