1022 lines
41 KiB
JavaScript
1022 lines
41 KiB
JavaScript
|
|
'use client';
|
||
|
|
|
||
|
|
import { useState, useEffect } from 'react';
|
||
|
|
import { motion, AnimatePresence } from 'framer-motion';
|
||
|
|
import { useRouter } from 'next/navigation';
|
||
|
|
import Link from 'next/link';
|
||
|
|
import {
|
||
|
|
PlusCircle,
|
||
|
|
Building,
|
||
|
|
Home,
|
||
|
|
DollarSign,
|
||
|
|
MapPin,
|
||
|
|
Bed,
|
||
|
|
Bath,
|
||
|
|
Square,
|
||
|
|
Edit,
|
||
|
|
Trash2,
|
||
|
|
Eye,
|
||
|
|
Calendar,
|
||
|
|
TrendingUp,
|
||
|
|
Users,
|
||
|
|
FileText,
|
||
|
|
Image as ImageIcon,
|
||
|
|
CheckCircle,
|
||
|
|
XCircle,
|
||
|
|
AlertCircle,
|
||
|
|
ChevronRight,
|
||
|
|
ChevronLeft,
|
||
|
|
Loader2,
|
||
|
|
Clock,
|
||
|
|
Wifi,
|
||
|
|
Zap,
|
||
|
|
Flame,
|
||
|
|
Droplets,
|
||
|
|
Cigarette,
|
||
|
|
Dog,
|
||
|
|
Music,
|
||
|
|
Sofa,
|
||
|
|
DoorOpen,
|
||
|
|
Warehouse,
|
||
|
|
Layers,
|
||
|
|
Wind,
|
||
|
|
Pencil,
|
||
|
|
Save,
|
||
|
|
X
|
||
|
|
} from 'lucide-react';
|
||
|
|
import toast, { Toaster } from 'react-hot-toast';
|
||
|
|
|
||
|
|
const DeleteConfirmationModal = ({ isOpen, onClose, onConfirm, propertyTitle }) => {
|
||
|
|
if (!isOpen) return null;
|
||
|
|
|
||
|
|
return (
|
||
|
|
<motion.div
|
||
|
|
initial={{ opacity: 0 }}
|
||
|
|
animate={{ opacity: 1 }}
|
||
|
|
exit={{ opacity: 0 }}
|
||
|
|
className="fixed inset-0 bg-black/50 backdrop-blur-sm flex items-center justify-center p-4 z-50"
|
||
|
|
onClick={onClose}
|
||
|
|
>
|
||
|
|
<motion.div
|
||
|
|
initial={{ scale: 0.9, y: 20 }}
|
||
|
|
animate={{ scale: 1, y: 0 }}
|
||
|
|
exit={{ scale: 0.9, y: 20 }}
|
||
|
|
className="bg-white rounded-2xl w-full max-w-md p-6 shadow-2xl"
|
||
|
|
onClick={(e) => e.stopPropagation()}
|
||
|
|
>
|
||
|
|
<div className="text-center mb-4">
|
||
|
|
<div className="w-16 h-16 bg-red-100 rounded-full flex items-center justify-center mx-auto mb-3">
|
||
|
|
<AlertCircle className="w-8 h-8 text-red-600" />
|
||
|
|
</div>
|
||
|
|
<h3 className="text-xl font-bold text-gray-900">تأكيد الحذف</h3>
|
||
|
|
<p className="text-sm text-gray-500 mt-2">
|
||
|
|
هل أنت متأكد من حذف العقار: <span className="font-bold text-gray-700">"{propertyTitle}"</span>؟
|
||
|
|
</p>
|
||
|
|
<p className="text-xs text-red-500 mt-1">هذا الإجراء لا يمكن التراجع عنه</p>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div className="flex gap-3 pt-3">
|
||
|
|
<button
|
||
|
|
onClick={onClose}
|
||
|
|
className="flex-1 bg-gray-100 text-gray-700 py-3 rounded-xl font-medium hover:bg-gray-200 transition-colors"
|
||
|
|
>
|
||
|
|
إلغاء
|
||
|
|
</button>
|
||
|
|
<button
|
||
|
|
onClick={onConfirm}
|
||
|
|
className="flex-1 bg-red-600 text-white py-3 rounded-xl font-medium hover:bg-red-700 transition-colors"
|
||
|
|
>
|
||
|
|
نعم، احذف
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
</motion.div>
|
||
|
|
</motion.div>
|
||
|
|
);
|
||
|
|
};
|
||
|
|
|
||
|
|
const PropertyViewModal = ({ isOpen, onClose, property }) => {
|
||
|
|
if (!isOpen || !property) return null;
|
||
|
|
|
||
|
|
const getServiceIcon = (serviceId) => {
|
||
|
|
const icons = {
|
||
|
|
electricity: Zap,
|
||
|
|
internet: Wifi,
|
||
|
|
heating: Flame,
|
||
|
|
water: Droplets,
|
||
|
|
airConditioning: Wind,
|
||
|
|
parking: Warehouse,
|
||
|
|
elevator: Layers
|
||
|
|
};
|
||
|
|
const Icon = icons[serviceId];
|
||
|
|
return Icon ? <Icon className="w-4 h-4" /> : null;
|
||
|
|
};
|
||
|
|
|
||
|
|
return (
|
||
|
|
<motion.div
|
||
|
|
initial={{ opacity: 0 }}
|
||
|
|
animate={{ opacity: 1 }}
|
||
|
|
exit={{ opacity: 0 }}
|
||
|
|
className="fixed inset-0 bg-black/50 backdrop-blur-sm flex items-center justify-center p-4 z-50"
|
||
|
|
onClick={onClose}
|
||
|
|
>
|
||
|
|
<motion.div
|
||
|
|
initial={{ scale: 0.9, y: 20 }}
|
||
|
|
animate={{ scale: 1, y: 0 }}
|
||
|
|
exit={{ scale: 0.9, y: 20 }}
|
||
|
|
className="bg-white rounded-2xl w-full max-w-4xl max-h-[90vh] overflow-y-auto shadow-2xl"
|
||
|
|
onClick={(e) => e.stopPropagation()}
|
||
|
|
>
|
||
|
|
<div className="sticky top-0 bg-gradient-to-r from-amber-500 to-amber-600 p-6 text-white flex justify-between items-center">
|
||
|
|
<div>
|
||
|
|
<h2 className="text-2xl font-bold">{property.title}</h2>
|
||
|
|
<p className="text-amber-100 text-sm mt-1">تم الإضافة: {new Date(property.createdAt).toLocaleDateString('ar-SA')}</p>
|
||
|
|
</div>
|
||
|
|
<button
|
||
|
|
onClick={onClose}
|
||
|
|
className="p-2 hover:bg-white/20 rounded-full transition-colors"
|
||
|
|
>
|
||
|
|
<XCircle className="w-6 h-6" />
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div className="p-6">
|
||
|
|
{property.images && property.images.length > 0 && (
|
||
|
|
<div className="mb-6">
|
||
|
|
<h3 className="text-lg font-bold text-gray-900 mb-3">صور العقار</h3>
|
||
|
|
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
||
|
|
{property.images.map((image, index) => (
|
||
|
|
<div key={index} className="relative aspect-square rounded-lg overflow-hidden border border-gray-200">
|
||
|
|
<img
|
||
|
|
src={image}
|
||
|
|
alt={`${property.title} - صورة ${index + 1}`}
|
||
|
|
className="w-full h-full object-cover"
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
))}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
|
||
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6">
|
||
|
|
<div className="bg-gray-50 p-4 rounded-xl">
|
||
|
|
<h4 className="font-bold text-gray-700 mb-3">معلومات أساسية</h4>
|
||
|
|
<div className="space-y-2">
|
||
|
|
<p className="text-sm">
|
||
|
|
<span className="text-gray-500">نوع العقار:</span>
|
||
|
|
<span className="font-medium text-gray-900 mr-2">
|
||
|
|
{property.propertyType === 'apartment' ? 'شقة' :
|
||
|
|
property.propertyType === 'villa' ? 'فيلا' :
|
||
|
|
property.propertyType === 'suite' ? 'سويت' : 'غرفة ضمن شقة'}
|
||
|
|
</span>
|
||
|
|
</p>
|
||
|
|
<p className="text-sm">
|
||
|
|
<span className="text-gray-500">الحالة:</span>
|
||
|
|
<span className={`font-medium mr-2 ${property.furnished ? 'text-green-600' : 'text-gray-600'}`}>
|
||
|
|
{property.furnished ? 'مفروش' : 'غير مفروش'}
|
||
|
|
</span>
|
||
|
|
</p>
|
||
|
|
<p className="text-sm">
|
||
|
|
<span className="text-gray-500">حالة العقار:</span>
|
||
|
|
<span className={`font-medium mr-2 ${
|
||
|
|
property.status === 'available' ? 'text-green-600' : 'text-yellow-600'
|
||
|
|
}`}>
|
||
|
|
{property.status === 'available' ? 'متاح' : 'مؤجر'}
|
||
|
|
</span>
|
||
|
|
</p>
|
||
|
|
{property.description && (
|
||
|
|
<p className="text-sm mt-2">
|
||
|
|
<span className="text-gray-500">الوصف:</span>
|
||
|
|
<span className="text-gray-700 mr-2">{property.description}</span>
|
||
|
|
</p>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div className="bg-gray-50 p-4 rounded-xl">
|
||
|
|
<h4 className="font-bold text-gray-700 mb-3">التفاصيل</h4>
|
||
|
|
<div className="grid grid-cols-3 gap-2">
|
||
|
|
<div className="text-center">
|
||
|
|
<Bed className="w-5 h-5 text-amber-500 mx-auto mb-1" />
|
||
|
|
<span className="text-sm font-bold">{property.bedrooms}</span>
|
||
|
|
<span className="text-xs text-gray-500 block">غرف</span>
|
||
|
|
</div>
|
||
|
|
<div className="text-center">
|
||
|
|
<Bath className="w-5 h-5 text-amber-500 mx-auto mb-1" />
|
||
|
|
<span className="text-sm font-bold">{property.bathrooms}</span>
|
||
|
|
<span className="text-xs text-gray-500 block">حمامات</span>
|
||
|
|
</div>
|
||
|
|
<div className="text-center">
|
||
|
|
<Square className="w-5 h-5 text-amber-500 mx-auto mb-1" />
|
||
|
|
<span className="text-sm font-bold">{property.area}</span>
|
||
|
|
<span className="text-xs text-gray-500 block">م²</span>
|
||
|
|
</div>
|
||
|
|
{property.livingRooms && (
|
||
|
|
<div className="text-center">
|
||
|
|
<Sofa className="w-5 h-5 text-amber-500 mx-auto mb-1" />
|
||
|
|
<span className="text-sm font-bold">{property.livingRooms}</span>
|
||
|
|
<span className="text-xs text-gray-500 block">صالونات</span>
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div className="bg-gray-50 p-4 rounded-xl mb-6">
|
||
|
|
<h4 className="font-bold text-gray-700 mb-3 flex items-center gap-2">
|
||
|
|
<MapPin className="w-5 h-5 text-amber-500" />
|
||
|
|
الموقع
|
||
|
|
</h4>
|
||
|
|
<p className="text-gray-700">
|
||
|
|
{property.address || 'لم يتم تحديد العنوان'}
|
||
|
|
{property.city && `، ${property.city}`}
|
||
|
|
{property.district && `، ${property.district}`}
|
||
|
|
</p>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{property.services && Object.values(property.services).some(v => v) && (
|
||
|
|
<div className="bg-gray-50 p-4 rounded-xl mb-6">
|
||
|
|
<h4 className="font-bold text-gray-700 mb-3">الخدمات المتوفرة</h4>
|
||
|
|
<div className="flex flex-wrap gap-2">
|
||
|
|
{Object.entries(property.services).map(([key, value]) => {
|
||
|
|
if (!value) return null;
|
||
|
|
const Icon = getServiceIcon(key);
|
||
|
|
return (
|
||
|
|
<span key={key} className="inline-flex items-center gap-1 px-3 py-1 bg-green-100 text-green-800 rounded-full text-sm">
|
||
|
|
{Icon}
|
||
|
|
{key === 'electricity' && 'كهرباء'}
|
||
|
|
{key === 'internet' && 'انترنت'}
|
||
|
|
{key === 'heating' && 'تدفئة'}
|
||
|
|
{key === 'water' && 'ماء'}
|
||
|
|
{key === 'airConditioning' && 'تكييف'}
|
||
|
|
{key === 'parking' && 'موقف سيارات'}
|
||
|
|
{key === 'elevator' && 'مصعد'}
|
||
|
|
</span>
|
||
|
|
);
|
||
|
|
})}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
|
||
|
|
{property.terms && Object.values(property.terms).some(v => v) && (
|
||
|
|
<div className="bg-gray-50 p-4 rounded-xl mb-6">
|
||
|
|
<h4 className="font-bold text-gray-700 mb-3">شروط الاستخدام</h4>
|
||
|
|
<div className="flex flex-wrap gap-2">
|
||
|
|
{Object.entries(property.terms).map(([key, value]) => {
|
||
|
|
if (!value) return null;
|
||
|
|
return (
|
||
|
|
<span key={key} className="inline-flex items-center gap-1 px-3 py-1 bg-amber-100 text-amber-800 rounded-full text-sm">
|
||
|
|
{key === 'noSmoking' && ' ممنوع التدخين'}
|
||
|
|
{key === 'noPets' && ' ممنوع الحيوانات'}
|
||
|
|
{key === 'noParties' && ' ممنوع الحفلات'}
|
||
|
|
{key === 'noAlcohol' && ' ممنوع الكحول'}
|
||
|
|
{key === 'suitableForChildren' && ' مناسب للأطفال'}
|
||
|
|
{key === 'suitableForElderly' && ' مناسب لكبار السن'}
|
||
|
|
</span>
|
||
|
|
);
|
||
|
|
})}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
|
||
|
|
<div className="bg-amber-50 p-4 rounded-xl">
|
||
|
|
<h4 className="font-bold text-amber-700 mb-3">معلومات السعر</h4>
|
||
|
|
{property.purpose === 'rent' ? (
|
||
|
|
<div className="space-y-2">
|
||
|
|
{property.dailyPrice && (
|
||
|
|
<p className="flex justify-between">
|
||
|
|
<span className="text-gray-600">السعر اليومي:</span>
|
||
|
|
<span className="font-bold text-amber-600">{Number(property.dailyPrice).toLocaleString()} ل.س</span>
|
||
|
|
</p>
|
||
|
|
)}
|
||
|
|
{property.monthlyPrice && (
|
||
|
|
<p className="flex justify-between">
|
||
|
|
<span className="text-gray-600">السعر الشهري:</span>
|
||
|
|
<span className="font-bold text-amber-600">{Number(property.monthlyPrice).toLocaleString()} ل.س</span>
|
||
|
|
</p>
|
||
|
|
)}
|
||
|
|
<p className="text-sm text-gray-500 mt-1">
|
||
|
|
نوع الإيجار: {
|
||
|
|
property.rentType === 'daily' ? 'يومي' :
|
||
|
|
property.rentType === 'monthly' ? 'شهري' : 'يومي وشهري'
|
||
|
|
}
|
||
|
|
</p>
|
||
|
|
</div>
|
||
|
|
) : (
|
||
|
|
<p className="flex justify-between">
|
||
|
|
<span className="text-gray-600">سعر البيع:</span>
|
||
|
|
<span className="font-bold text-blue-600">{Number(property.salePrice).toLocaleString()} ل.س</span>
|
||
|
|
</p>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</motion.div>
|
||
|
|
</motion.div>
|
||
|
|
);
|
||
|
|
};
|
||
|
|
|
||
|
|
const PropertyEditModal = ({ isOpen, onClose, property, onSave }) => {
|
||
|
|
const [formData, setFormData] = useState({ ...property });
|
||
|
|
const [isSaving, setIsSaving] = useState(false);
|
||
|
|
const [editingField, setEditingField] = useState(null);
|
||
|
|
const [tempValues, setTempValues] = useState({});
|
||
|
|
|
||
|
|
const sections = [
|
||
|
|
{
|
||
|
|
title: 'معلومات أساسية',
|
||
|
|
fields: [
|
||
|
|
{ id: 'title', label: 'عنوان العقار', type: 'text' },
|
||
|
|
{ id: 'description', label: 'الوصف', type: 'textarea' },
|
||
|
|
{
|
||
|
|
id: 'propertyType',
|
||
|
|
label: 'نوع العقار',
|
||
|
|
type: 'select',
|
||
|
|
options: [
|
||
|
|
{ value: 'apartment', label: 'شقة' },
|
||
|
|
{ value: 'villa', label: 'فيلا' },
|
||
|
|
{ value: 'suite', label: 'سويت' },
|
||
|
|
{ value: 'room', label: 'غرفة ضمن شقة' }
|
||
|
|
],
|
||
|
|
|
||
|
|
},
|
||
|
|
{
|
||
|
|
id: 'furnished',
|
||
|
|
label: 'حالة العقار',
|
||
|
|
type: 'radio',
|
||
|
|
options: [
|
||
|
|
{ value: true, label: 'مفروش' },
|
||
|
|
{ value: false, label: 'غير مفروش' }
|
||
|
|
],
|
||
|
|
|
||
|
|
}
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
title: 'التفاصيل',
|
||
|
|
fields: [
|
||
|
|
{ id: 'bedrooms', label: 'عدد الغرف', type: 'number' },
|
||
|
|
{ id: 'bathrooms', label: 'عدد الحمامات', type: 'number' },
|
||
|
|
{ id: 'livingRooms', label: 'عدد الصالونات', type: 'number' },
|
||
|
|
{ id: 'area', label: 'المساحة (م²)', type: 'number' }
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
title: 'الموقع',
|
||
|
|
fields: [
|
||
|
|
{ id: 'address', label: 'العنوان الكامل', type: 'text' },
|
||
|
|
{ id: 'city', label: 'المدينة', type: 'text' },
|
||
|
|
{ id: 'district', label: 'الحي', type: 'text' }
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
title: 'السعر',
|
||
|
|
fields: formData?.purpose === 'rent' ? [
|
||
|
|
{ id: 'dailyPrice', label: 'السعر اليومي', type: 'number' },
|
||
|
|
{ id: 'monthlyPrice', label: 'السعر الشهري', type: 'number' },
|
||
|
|
{
|
||
|
|
id: 'rentType',
|
||
|
|
label: 'نوع الإيجار',
|
||
|
|
type: 'select',
|
||
|
|
options: [
|
||
|
|
{ value: 'daily', label: 'يومي' },
|
||
|
|
{ value: 'monthly', label: 'شهري' },
|
||
|
|
{ value: 'both', label: 'يومي وشهري' }
|
||
|
|
],
|
||
|
|
}
|
||
|
|
] : [
|
||
|
|
{ id: 'salePrice', label: 'سعر البيع', type: 'number' }
|
||
|
|
]
|
||
|
|
}
|
||
|
|
];
|
||
|
|
|
||
|
|
const startEditing = (fieldId) => {
|
||
|
|
setEditingField(fieldId);
|
||
|
|
setTempValues({ ...tempValues, [fieldId]: formData[fieldId] });
|
||
|
|
};
|
||
|
|
|
||
|
|
const cancelEditing = () => {
|
||
|
|
setEditingField(null);
|
||
|
|
setTempValues({});
|
||
|
|
};
|
||
|
|
|
||
|
|
const saveField = (fieldId) => {
|
||
|
|
setFormData({
|
||
|
|
...formData,
|
||
|
|
[fieldId]: tempValues[fieldId]
|
||
|
|
});
|
||
|
|
setEditingField(null);
|
||
|
|
setTempValues({});
|
||
|
|
const fieldLabel = sections.flatMap(s => s.fields).find(f => f.id === fieldId)?.label;
|
||
|
|
toast.success(`تم تحديث ${fieldLabel}`);
|
||
|
|
};
|
||
|
|
|
||
|
|
const handleTempChange = (fieldId, value) => {
|
||
|
|
setTempValues({ ...tempValues, [fieldId]: value });
|
||
|
|
};
|
||
|
|
|
||
|
|
const handleSave = () => {
|
||
|
|
setIsSaving(true);
|
||
|
|
|
||
|
|
setTimeout(() => {
|
||
|
|
onSave(formData);
|
||
|
|
setIsSaving(false);
|
||
|
|
onClose();
|
||
|
|
toast.success('تم تحديث العقار بنجاح');
|
||
|
|
}, 1000);
|
||
|
|
};
|
||
|
|
|
||
|
|
if (!isOpen || !property) return null;
|
||
|
|
|
||
|
|
return (
|
||
|
|
<motion.div
|
||
|
|
initial={{ opacity: 0 }}
|
||
|
|
animate={{ opacity: 1 }}
|
||
|
|
exit={{ opacity: 0 }}
|
||
|
|
className="fixed inset-0 bg-black/50 backdrop-blur-sm flex items-center justify-center p-4 z-50"
|
||
|
|
onClick={onClose}
|
||
|
|
>
|
||
|
|
<motion.div
|
||
|
|
initial={{ scale: 0.9, y: 20 }}
|
||
|
|
animate={{ scale: 1, y: 0 }}
|
||
|
|
exit={{ scale: 0.9, y: 20 }}
|
||
|
|
className="bg-white rounded-2xl w-full max-w-4xl max-h-[90vh] overflow-y-auto shadow-2xl"
|
||
|
|
onClick={(e) => e.stopPropagation()}
|
||
|
|
>
|
||
|
|
<div className="sticky top-0 bg-gradient-to-r from-amber-500 to-amber-600 p-6 text-white flex justify-between items-center">
|
||
|
|
<div>
|
||
|
|
<h2 className="text-2xl font-bold">تعديل العقار</h2>
|
||
|
|
<p className="text-amber-100 text-sm mt-1">يمكنك تعديل أي حقل بالضغط على أيقونة القلم</p>
|
||
|
|
</div>
|
||
|
|
<button onClick={onClose} className="p-2 hover:bg-white/20 rounded-full">
|
||
|
|
<XCircle className="w-6 h-6" />
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div className="p-6 space-y-8">
|
||
|
|
{formData.images && formData.images.length > 0 && (
|
||
|
|
<div className="bg-gray-50 p-4 rounded-xl">
|
||
|
|
<div className="flex justify-between items-center mb-4">
|
||
|
|
<h3 className="text-lg font-bold text-gray-900">صور العقار</h3>
|
||
|
|
<button className="text-amber-600 hover:text-amber-700 text-sm font-medium">
|
||
|
|
+ إضافة صور
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
||
|
|
{formData.images.map((image, index) => (
|
||
|
|
<div key={index} className="relative group aspect-square">
|
||
|
|
<img
|
||
|
|
src={image}
|
||
|
|
alt={`Property ${index + 1}`}
|
||
|
|
className="w-full h-full object-cover rounded-lg"
|
||
|
|
/>
|
||
|
|
<button className="absolute -top-2 -right-2 w-6 h-6 bg-red-500 rounded-full flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity">
|
||
|
|
<X className="w-4 h-4 text-white" />
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
))}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
|
||
|
|
{sections.map((section, sectionIndex) => (
|
||
|
|
<div key={sectionIndex} className="bg-gray-50 p-4 rounded-xl">
|
||
|
|
<h3 className="text-lg font-bold text-gray-900 mb-4">{section.title}</h3>
|
||
|
|
<div className="space-y-4">
|
||
|
|
{section.fields.map((field) => (
|
||
|
|
<div key={field.id} className="bg-white p-3 rounded-lg group hover:shadow-sm transition-shadow">
|
||
|
|
<div className="flex justify-between items-start mb-2">
|
||
|
|
<label className="text-sm font-medium text-gray-600 flex items-center gap-1">
|
||
|
|
<span>{field.icon}</span>
|
||
|
|
{field.label}
|
||
|
|
</label>
|
||
|
|
{editingField !== field.id && (
|
||
|
|
<button
|
||
|
|
onClick={() => startEditing(field.id)}
|
||
|
|
className="opacity-0 group-hover:opacity-100 transition-opacity text-gray-400 hover:text-amber-500"
|
||
|
|
>
|
||
|
|
<Pencil className="w-4 h-4" />
|
||
|
|
</button>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{editingField === field.id ? (
|
||
|
|
<div className="space-y-2">
|
||
|
|
{field.type === 'textarea' ? (
|
||
|
|
<textarea
|
||
|
|
value={tempValues[field.id] || ''}
|
||
|
|
onChange={(e) => handleTempChange(field.id, e.target.value)}
|
||
|
|
className="w-full px-3 py-2 border border-amber-500 rounded-lg focus:outline-none focus:ring-2 focus:ring-amber-500"
|
||
|
|
rows="3"
|
||
|
|
/>
|
||
|
|
) : field.type === 'select' ? (
|
||
|
|
<select
|
||
|
|
value={tempValues[field.id] || ''}
|
||
|
|
onChange={(e) => handleTempChange(field.id, e.target.value)}
|
||
|
|
className="w-full px-3 py-2 border border-amber-500 rounded-lg focus:outline-none focus:ring-2 focus:ring-amber-500"
|
||
|
|
>
|
||
|
|
{field.options.map((opt) => (
|
||
|
|
<option key={opt.value} value={opt.value}>
|
||
|
|
{opt.label}
|
||
|
|
</option>
|
||
|
|
))}
|
||
|
|
</select>
|
||
|
|
) : field.type === 'radio' ? (
|
||
|
|
<div className="flex gap-4">
|
||
|
|
{field.options.map((opt) => (
|
||
|
|
<label key={opt.value} className="flex items-center gap-2">
|
||
|
|
<input
|
||
|
|
type="radio"
|
||
|
|
name={field.id}
|
||
|
|
value={opt.value}
|
||
|
|
checked={tempValues[field.id] === opt.value}
|
||
|
|
onChange={(e) => handleTempChange(field.id, e.target.value === 'true')}
|
||
|
|
className="w-4 h-4 text-amber-500"
|
||
|
|
/>
|
||
|
|
<span>{opt.label}</span>
|
||
|
|
</label>
|
||
|
|
))}
|
||
|
|
</div>
|
||
|
|
) : (
|
||
|
|
<input
|
||
|
|
type={field.type}
|
||
|
|
value={tempValues[field.id] || ''}
|
||
|
|
onChange={(e) => handleTempChange(field.id, e.target.value)}
|
||
|
|
className="w-full px-3 py-2 border border-amber-500 rounded-lg focus:outline-none focus:ring-2 focus:ring-amber-500"
|
||
|
|
/>
|
||
|
|
)}
|
||
|
|
|
||
|
|
<div className="flex gap-2 justify-end">
|
||
|
|
<button
|
||
|
|
onClick={() => saveField(field.id)}
|
||
|
|
className="px-3 py-1 bg-green-500 text-white rounded-lg text-sm hover:bg-green-600"
|
||
|
|
>
|
||
|
|
حفظ
|
||
|
|
</button>
|
||
|
|
<button
|
||
|
|
onClick={cancelEditing}
|
||
|
|
className="px-3 py-1 bg-gray-500 text-white rounded-lg text-sm hover:bg-gray-600"
|
||
|
|
>
|
||
|
|
إلغاء
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
) : (
|
||
|
|
<div className="text-gray-900">
|
||
|
|
{field.type === 'select' ? (
|
||
|
|
field.options.find(opt => opt.value === formData[field.id])?.label || formData[field.id]
|
||
|
|
) : field.type === 'radio' ? (
|
||
|
|
formData[field.id] ? 'مفروش' : 'غير مفروش'
|
||
|
|
) : field.id.includes('Price') ? (
|
||
|
|
formData[field.id] ? `${Number(formData[field.id]).toLocaleString()} ل.س` : '—'
|
||
|
|
) : (
|
||
|
|
formData[field.id] || '—'
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
))}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
))}
|
||
|
|
|
||
|
|
<div className="bg-gray-50 p-4 rounded-xl">
|
||
|
|
<h3 className="text-lg font-bold text-gray-900 mb-4">الخدمات</h3>
|
||
|
|
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
|
||
|
|
{Object.entries(formData.services || {}).map(([key, value]) => {
|
||
|
|
const serviceLabels = {
|
||
|
|
electricity: 'كهرباء',
|
||
|
|
internet: 'انترنت',
|
||
|
|
heating: 'تدفئة',
|
||
|
|
water: 'ماء',
|
||
|
|
airConditioning: 'تكييف',
|
||
|
|
parking: 'موقف سيارات',
|
||
|
|
elevator: 'مصعد'
|
||
|
|
};
|
||
|
|
return (
|
||
|
|
<label key={key} className="flex items-center gap-2 p-2 border rounded-lg cursor-pointer hover:bg-amber-50">
|
||
|
|
<input
|
||
|
|
type="checkbox"
|
||
|
|
checked={value}
|
||
|
|
onChange={(e) => {
|
||
|
|
setFormData({
|
||
|
|
...formData,
|
||
|
|
services: {
|
||
|
|
...formData.services,
|
||
|
|
[key]: e.target.checked
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}}
|
||
|
|
className="w-4 h-4 text-amber-500"
|
||
|
|
/>
|
||
|
|
<span className="text-sm">{serviceLabels[key]}</span>
|
||
|
|
</label>
|
||
|
|
);
|
||
|
|
})}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div className="bg-gray-50 p-4 rounded-xl">
|
||
|
|
<h3 className="text-lg font-bold text-gray-900 mb-4">شروط الاستخدام</h3>
|
||
|
|
<div className="grid grid-cols-2 md:grid-cols-3 gap-3">
|
||
|
|
{Object.entries(formData.terms || {}).map(([key, value]) => {
|
||
|
|
const termLabels = {
|
||
|
|
noSmoking: ' ممنوع التدخين',
|
||
|
|
noPets: ' ممنوع الحيوانات',
|
||
|
|
noParties: ' ممنوع الحفلات',
|
||
|
|
noAlcohol: ' ممنوع الكحول',
|
||
|
|
suitableForChildren: ' مناسب للأطفال',
|
||
|
|
suitableForElderly: ' مناسب لكبار السن'
|
||
|
|
};
|
||
|
|
return (
|
||
|
|
<label key={key} className="flex items-center gap-2 p-2 border rounded-lg cursor-pointer hover:bg-amber-50">
|
||
|
|
<input
|
||
|
|
type="checkbox"
|
||
|
|
checked={value}
|
||
|
|
onChange={(e) => {
|
||
|
|
setFormData({
|
||
|
|
...formData,
|
||
|
|
terms: {
|
||
|
|
...formData.terms,
|
||
|
|
[key]: e.target.checked
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}}
|
||
|
|
className="w-4 h-4 text-amber-500"
|
||
|
|
/>
|
||
|
|
<span className="text-sm">{termLabels[key]}</span>
|
||
|
|
</label>
|
||
|
|
);
|
||
|
|
})}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div className="flex gap-3 pt-6 border-t">
|
||
|
|
<button
|
||
|
|
onClick={onClose}
|
||
|
|
className="flex-1 py-3 bg-gray-100 text-gray-700 rounded-xl hover:bg-gray-200 transition-colors"
|
||
|
|
>
|
||
|
|
إلغاء
|
||
|
|
</button>
|
||
|
|
<button
|
||
|
|
onClick={handleSave}
|
||
|
|
disabled={isSaving}
|
||
|
|
className="flex-1 bg-gradient-to-r from-amber-500 to-amber-600 text-white py-3 rounded-xl hover:from-amber-600 hover:to-amber-700 transition-all disabled:opacity-50 flex items-center justify-center gap-2"
|
||
|
|
>
|
||
|
|
{isSaving ? (
|
||
|
|
<>
|
||
|
|
<Loader2 className="w-5 h-5 animate-spin" />
|
||
|
|
جاري الحفظ...
|
||
|
|
</>
|
||
|
|
) : (
|
||
|
|
<>
|
||
|
|
<Save className="w-5 h-5" />
|
||
|
|
حفظ جميع التغييرات
|
||
|
|
</>
|
||
|
|
)}
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</motion.div>
|
||
|
|
</motion.div>
|
||
|
|
);
|
||
|
|
};
|
||
|
|
|
||
|
|
export default function OwnerPropertiesPage() {
|
||
|
|
const router = useRouter();
|
||
|
|
const [user, setUser] = useState(null);
|
||
|
|
const [properties, setProperties] = useState([]);
|
||
|
|
const [isLoading, setIsLoading] = useState(true);
|
||
|
|
const [showAddMenu, setShowAddMenu] = useState(false);
|
||
|
|
|
||
|
|
const [deleteModal, setDeleteModal] = useState({ isOpen: false, property: null });
|
||
|
|
const [viewModal, setViewModal] = useState({ isOpen: false, property: null });
|
||
|
|
const [editModal, setEditModal] = useState({ isOpen: false, property: null });
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
const storedUser = localStorage.getItem('user');
|
||
|
|
if (storedUser) {
|
||
|
|
const userData = JSON.parse(storedUser);
|
||
|
|
if (userData.role !== 'owner') {
|
||
|
|
router.push('/');
|
||
|
|
} else {
|
||
|
|
setUser(userData);
|
||
|
|
loadProperties();
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
router.push('/auth/choose-role');
|
||
|
|
}
|
||
|
|
}, [router]);
|
||
|
|
|
||
|
|
const loadProperties = () => {
|
||
|
|
const storedProperties = localStorage.getItem('ownerProperties');
|
||
|
|
if (storedProperties) {
|
||
|
|
setProperties(JSON.parse(storedProperties));
|
||
|
|
} else {
|
||
|
|
const mockProperties = [
|
||
|
|
{
|
||
|
|
id: 1,
|
||
|
|
title: 'فيلا فاخرة في المزة',
|
||
|
|
propertyType: 'villa',
|
||
|
|
purpose: 'rent',
|
||
|
|
rentType: 'both',
|
||
|
|
dailyPrice: 500000,
|
||
|
|
monthlyPrice: 15000000,
|
||
|
|
location: 'دمشق، المزة',
|
||
|
|
bedrooms: 5,
|
||
|
|
bathrooms: 4,
|
||
|
|
area: 450,
|
||
|
|
livingRooms: 3,
|
||
|
|
status: 'available',
|
||
|
|
images: ['/villa1.jpg'],
|
||
|
|
createdAt: new Date().toISOString(),
|
||
|
|
furnished: true,
|
||
|
|
description: 'فيلا فاخرة مع حديقة خاصة ومسبح',
|
||
|
|
address: 'شارع المزة - فيلات غربية',
|
||
|
|
city: 'دمشق',
|
||
|
|
district: 'المزة',
|
||
|
|
services: {
|
||
|
|
electricity: true,
|
||
|
|
internet: true,
|
||
|
|
heating: true,
|
||
|
|
water: true,
|
||
|
|
airConditioning: true,
|
||
|
|
parking: true,
|
||
|
|
elevator: false
|
||
|
|
},
|
||
|
|
terms: {
|
||
|
|
noSmoking: true,
|
||
|
|
noPets: false,
|
||
|
|
noParties: true,
|
||
|
|
noAlcohol: false,
|
||
|
|
suitableForChildren: true,
|
||
|
|
suitableForElderly: true
|
||
|
|
}
|
||
|
|
}
|
||
|
|
];
|
||
|
|
setProperties(mockProperties);
|
||
|
|
localStorage.setItem('ownerProperties', JSON.stringify(mockProperties));
|
||
|
|
}
|
||
|
|
setIsLoading(false);
|
||
|
|
};
|
||
|
|
|
||
|
|
const updatePropertiesInStorage = (newProperties) => {
|
||
|
|
setProperties(newProperties);
|
||
|
|
localStorage.setItem('ownerProperties', JSON.stringify(newProperties));
|
||
|
|
};
|
||
|
|
|
||
|
|
const handleDelete = () => {
|
||
|
|
if (deleteModal.property) {
|
||
|
|
const newProperties = properties.filter(p => p.id !== deleteModal.property.id);
|
||
|
|
updatePropertiesInStorage(newProperties);
|
||
|
|
setDeleteModal({ isOpen: false, property: null });
|
||
|
|
toast.success('تم حذف العقار بنجاح');
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
const handleSaveEdit = (updatedProperty) => {
|
||
|
|
const newProperties = properties.map(p =>
|
||
|
|
p.id === updatedProperty.id ? updatedProperty : p
|
||
|
|
);
|
||
|
|
updatePropertiesInStorage(newProperties);
|
||
|
|
setEditModal({ isOpen: false, property: null });
|
||
|
|
};
|
||
|
|
|
||
|
|
const fadeInUp = {
|
||
|
|
initial: { opacity: 0, y: 20 },
|
||
|
|
animate: { opacity: 1, y: 0 },
|
||
|
|
transition: { duration: 0.5 }
|
||
|
|
};
|
||
|
|
|
||
|
|
if (isLoading) {
|
||
|
|
return (
|
||
|
|
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
|
||
|
|
<div className="text-center">
|
||
|
|
<Loader2 className="w-12 h-12 text-amber-500 animate-spin mx-auto mb-4" />
|
||
|
|
<p className="text-gray-600">جاري تحميل عقاراتك...</p>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div className="min-h-screen bg-gray-50 py-8">
|
||
|
|
<Toaster position="top-center" reverseOrder={false} />
|
||
|
|
|
||
|
|
<DeleteConfirmationModal
|
||
|
|
isOpen={deleteModal.isOpen}
|
||
|
|
onClose={() => setDeleteModal({ isOpen: false, property: null })}
|
||
|
|
onConfirm={handleDelete}
|
||
|
|
propertyTitle={deleteModal.property?.title}
|
||
|
|
/>
|
||
|
|
|
||
|
|
<PropertyViewModal
|
||
|
|
isOpen={viewModal.isOpen}
|
||
|
|
onClose={() => setViewModal({ isOpen: false, property: null })}
|
||
|
|
property={viewModal.property}
|
||
|
|
/>
|
||
|
|
|
||
|
|
<PropertyEditModal
|
||
|
|
isOpen={editModal.isOpen}
|
||
|
|
onClose={() => setEditModal({ isOpen: false, property: null })}
|
||
|
|
property={editModal.property}
|
||
|
|
onSave={handleSaveEdit}
|
||
|
|
/>
|
||
|
|
|
||
|
|
<div className="container mx-auto px-4">
|
||
|
|
<motion.div
|
||
|
|
initial={{ opacity: 0, y: -20 }}
|
||
|
|
animate={{ opacity: 1, y: 0 }}
|
||
|
|
className="flex justify-between items-center mb-8"
|
||
|
|
>
|
||
|
|
<div>
|
||
|
|
<h1 className="text-3xl font-bold text-gray-900 mb-2">عقاراتي</h1>
|
||
|
|
<p className="text-gray-600">مرحباً {user?.name}، لديك {properties.length} عقار</p>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div className="relative">
|
||
|
|
<motion.button
|
||
|
|
onClick={() => setShowAddMenu(!showAddMenu)}
|
||
|
|
className="bg-gradient-to-r from-amber-500 to-amber-600 text-white px-6 py-3 rounded-xl font-medium flex items-center gap-2 shadow-lg hover:shadow-xl transition-all"
|
||
|
|
whileHover={{ scale: 1.05 }}
|
||
|
|
whileTap={{ scale: 0.95 }}
|
||
|
|
>
|
||
|
|
<PlusCircle className="w-5 h-5" />
|
||
|
|
إضافة عقار جديد
|
||
|
|
</motion.button>
|
||
|
|
|
||
|
|
<AnimatePresence>
|
||
|
|
{showAddMenu && (
|
||
|
|
<motion.div
|
||
|
|
initial={{ opacity: 0, y: -10 }}
|
||
|
|
animate={{ opacity: 1, y: 0 }}
|
||
|
|
exit={{ opacity: 0, y: -10 }}
|
||
|
|
className="absolute left-0 mt-2 w-64 bg-white rounded-xl shadow-xl border border-gray-200 overflow-hidden z-50"
|
||
|
|
>
|
||
|
|
<div className="p-2">
|
||
|
|
<Link
|
||
|
|
href="/owner/properties/add?purpose=rent"
|
||
|
|
className="flex items-center gap-3 px-4 py-3 text-gray-700 hover:bg-amber-50 rounded-lg transition-colors"
|
||
|
|
onClick={() => setShowAddMenu(false)}
|
||
|
|
>
|
||
|
|
<div className="w-8 h-8 bg-green-100 rounded-lg flex items-center justify-center">
|
||
|
|
<Home className="w-4 h-4 text-green-600" />
|
||
|
|
</div>
|
||
|
|
<div>
|
||
|
|
<p className="font-medium">عقار للإيجار</p>
|
||
|
|
<p className="text-xs text-gray-500">أضف عقار متاح للإيجار</p>
|
||
|
|
</div>
|
||
|
|
</Link>
|
||
|
|
|
||
|
|
<Link
|
||
|
|
href="/owner/properties/add?purpose=sale"
|
||
|
|
className="flex items-center gap-3 px-4 py-3 text-gray-700 hover:bg-amber-50 rounded-lg transition-colors"
|
||
|
|
onClick={() => setShowAddMenu(false)}
|
||
|
|
>
|
||
|
|
<div className="w-8 h-8 bg-blue-100 rounded-lg flex items-center justify-center">
|
||
|
|
<DollarSign className="w-4 h-4 text-blue-600" />
|
||
|
|
</div>
|
||
|
|
<div>
|
||
|
|
<p className="font-medium">عقار للبيع</p>
|
||
|
|
<p className="text-xs text-gray-500">أضف عقار متاح للبيع</p>
|
||
|
|
</div>
|
||
|
|
</Link>
|
||
|
|
</div>
|
||
|
|
</motion.div>
|
||
|
|
)}
|
||
|
|
</AnimatePresence>
|
||
|
|
</div>
|
||
|
|
</motion.div>
|
||
|
|
|
||
|
|
{properties.length === 0 ? (
|
||
|
|
<motion.div
|
||
|
|
initial={{ opacity: 0, y: 20 }}
|
||
|
|
animate={{ opacity: 1, y: 0 }}
|
||
|
|
className="bg-white rounded-2xl p-12 text-center border-2 border-dashed border-gray-300"
|
||
|
|
>
|
||
|
|
<div className="w-24 h-24 bg-amber-100 rounded-full flex items-center justify-center mx-auto mb-4">
|
||
|
|
<Building className="w-12 h-12 text-amber-600" />
|
||
|
|
</div>
|
||
|
|
<h3 className="text-xl font-bold text-gray-900 mb-2">لا توجد عقارات بعد</h3>
|
||
|
|
<p className="text-gray-600 mb-6">ابدأ بإضافة أول عقار لك الآن</p>
|
||
|
|
<Link
|
||
|
|
href="/owner/properties/add"
|
||
|
|
className="inline-flex items-center gap-2 bg-amber-500 text-white px-6 py-3 rounded-xl font-medium hover:bg-amber-600 transition-colors"
|
||
|
|
>
|
||
|
|
<PlusCircle className="w-5 h-5" />
|
||
|
|
إضافة عقار جديد
|
||
|
|
</Link>
|
||
|
|
</motion.div>
|
||
|
|
) : (
|
||
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||
|
|
{properties.map((property, index) => (
|
||
|
|
<motion.div
|
||
|
|
key={property.id}
|
||
|
|
variants={fadeInUp}
|
||
|
|
initial="initial"
|
||
|
|
animate="animate"
|
||
|
|
transition={{ delay: index * 0.1 }}
|
||
|
|
className="bg-white rounded-2xl shadow-sm hover:shadow-md transition-all overflow-hidden border border-gray-200"
|
||
|
|
>
|
||
|
|
<div className="relative h-48 bg-gray-100">
|
||
|
|
{property.images && property.images.length > 0 ? (
|
||
|
|
<img
|
||
|
|
src={property.images[0]}
|
||
|
|
alt={property.title}
|
||
|
|
className="w-full h-full object-cover"
|
||
|
|
/>
|
||
|
|
) : (
|
||
|
|
<div className="w-full h-full flex items-center justify-center">
|
||
|
|
<ImageIcon className="w-12 h-12 text-gray-400" />
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
<div className="absolute top-2 right-2">
|
||
|
|
<span className={`px-2 py-1 rounded-lg text-xs font-medium ${
|
||
|
|
property.status === 'available'
|
||
|
|
? 'bg-green-100 text-green-800'
|
||
|
|
: 'bg-yellow-100 text-yellow-800'
|
||
|
|
}`}>
|
||
|
|
{property.status === 'available' ? 'متاح' : 'مؤجر'}
|
||
|
|
</span>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div className="p-4">
|
||
|
|
<h3 className="font-bold text-gray-900 mb-2">{property.title}</h3>
|
||
|
|
|
||
|
|
<div className="flex items-center gap-1 text-gray-500 text-sm mb-3">
|
||
|
|
<MapPin className="w-4 h-4" />
|
||
|
|
{property.address || property.location || 'موقع غير محدد'}
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div className="flex items-center gap-3 text-sm text-gray-600 mb-3">
|
||
|
|
<div className="flex items-center gap-1">
|
||
|
|
<Bed className="w-4 h-4" />
|
||
|
|
<span>{property.bedrooms}</span>
|
||
|
|
</div>
|
||
|
|
<div className="flex items-center gap-1">
|
||
|
|
<Bath className="w-4 h-4" />
|
||
|
|
<span>{property.bathrooms}</span>
|
||
|
|
</div>
|
||
|
|
<div className="flex items-center gap-1">
|
||
|
|
<Square className="w-4 h-4" />
|
||
|
|
<span>{property.area}م²</span>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div className="flex justify-between items-center pt-3 border-t border-gray-100">
|
||
|
|
<div>
|
||
|
|
{property.purpose === 'rent' ? (
|
||
|
|
<div>
|
||
|
|
{property.dailyPrice && (
|
||
|
|
<span className="text-lg font-bold text-amber-600">
|
||
|
|
{Number(property.dailyPrice).toLocaleString()} ل.س
|
||
|
|
</span>
|
||
|
|
)}
|
||
|
|
<span className="text-xs text-gray-500 mr-1">
|
||
|
|
{property.rentType === 'daily' ? '/يوم' :
|
||
|
|
property.rentType === 'monthly' ? '/شهر' :
|
||
|
|
property.dailyPrice && property.monthlyPrice ? '/يوم وشهر' : ''}
|
||
|
|
</span>
|
||
|
|
</div>
|
||
|
|
) : (
|
||
|
|
<div>
|
||
|
|
<span className="text-lg font-bold text-blue-600">
|
||
|
|
{Number(property.salePrice).toLocaleString()} ل.س
|
||
|
|
</span>
|
||
|
|
<span className="text-xs text-gray-500 mr-1">للبيع</span>
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div className="flex gap-2">
|
||
|
|
<button
|
||
|
|
onClick={() => setViewModal({ isOpen: true, property })}
|
||
|
|
className="p-2 hover:bg-blue-50 rounded-lg transition-colors group"
|
||
|
|
title="عرض التفاصيل"
|
||
|
|
>
|
||
|
|
<Eye className="w-4 h-4 text-gray-600 group-hover:text-blue-600" />
|
||
|
|
</button>
|
||
|
|
<button
|
||
|
|
onClick={() => setEditModal({ isOpen: true, property })}
|
||
|
|
className="p-2 hover:bg-amber-50 rounded-lg transition-colors group"
|
||
|
|
title="تعديل العقار"
|
||
|
|
>
|
||
|
|
<Edit className="w-4 h-4 text-gray-600 group-hover:text-amber-600" />
|
||
|
|
</button>
|
||
|
|
<button
|
||
|
|
onClick={() => setDeleteModal({ isOpen: true, property })}
|
||
|
|
className="p-2 hover:bg-red-50 rounded-lg transition-colors group"
|
||
|
|
title="حذف العقار"
|
||
|
|
>
|
||
|
|
<Trash2 className="w-4 h-4 text-gray-600 group-hover:text-red-600" />
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</motion.div>
|
||
|
|
))}
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
}
|