Files
SweetHome/app/owner/properties/page.js
mouazkh 4c350d7589
All checks were successful
Build frontend / build (push) Successful in 42s
fixed the details
2026-05-26 01:48:12 +03:00

1707 lines
67 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,
Warehouse,
Layers,
Sofa,
DoorOpen,
Wind,
Pencil,
Save,
X,
Star,
Ban,
Check,
School,
Hospital,
Store,
GraduationCap,
TreePine,
} from "lucide-react";
import toast, { Toaster } from "react-hot-toast";
import AuthService from "../../services/AuthService";
import {
getMyRentListings,
getMySaleListings,
editRentProperty,
} from "../../utils/api";
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 serviceLabels = {
Electricity: "كهرباء",
Internet: "إنترنت",
Heating: "تدفئة",
Water: "ماء",
Pool: "مسبح",
PrivateGarden: "حديقة خاصة",
Parking: "موقف سيارات",
Security247: "حراسة 24 س",
CentralHeating: "تدفئة مركزية",
CentralAirConditioning: "تكييف مركزي",
EquippedKitchen: "مطبخ مجهز",
MaidsRoom: "غرفة خادمة",
Elevator: "مصعد",
Gym: "نادي رياضي",
Sauna: "ساونا",
Jacuzzi: "جاكوزي",
Balcony: "بلكونة",
Rooftop: "سطح",
Furnished: "مفروش",
AirConditioning: "تكييف",
SatelliteTV: "تلفاز",
Fireplace: "مدفأة",
StudyRoom: "غرفة دراسة",
Storage: "مستودع",
Laundry: "غرفة غسيل",
SmartHome: "منزل ذكي",
};
const termLabels = {
NoSmoking: "ممنوع التدخين",
NoAnimals: "ممنوع الحيوانات الأليفة",
NoParties: "ممنوع الحفلات",
NoAlcohol: "ممنوع الكحول",
SuitableForChildren: "مناسب للأطفال",
SuitableForFamilies: "مناسب للعائلات",
SuitableForStudents: "مناسب للطلاب",
SuitableForElderly: "مناسب لكبار السن",
OnlyFemales: "إناث فقط",
OnlyMales: "ذكور فقط",
};
const proximityLabels = {
School: "مدرسة",
Hospital: "مستشفى",
Restaurant: "مطعم",
University: "جامعة",
Park: "حديقة",
Mall: "مركز تسوق",
Supermarket: "سوبر ماركت",
Pharmacy: "صيدلية",
Mosque: "مسجد",
Bank: "بنك",
Airport: "مطار",
BusStation: "موقف باص",
};
const PropertyViewModal = ({ isOpen, onClose, property }) => {
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 z-10 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.propertyTypeLabel || "عقار"}
</span>
</p>
<p className="text-sm">
<span className="text-gray-500">نوع العرض:</span>
<span className="font-medium text-gray-900 mr-2">
{property.displayType === "Daily rent"
? "إيجار يومي"
: property.displayType === "Monthly rent"
? "إيجار شهري"
: property.displayType === "Both"
? "يومي وشهري"
: property.displayType === "For sale"
? "للبيع"
: property.rentType === "daily"
? "إيجار يومي"
: property.rentType === "monthly"
? "إيجار شهري"
: (property.dailyPrice > 0 && property.monthlyPrice > 0)
? "يومي وشهري"
: property.monthlyPrice > 0
? "إيجار شهري"
: property.dailyPrice > 0
? "إيجار يومي"
: property.purpose === "sale" ? "للبيع" : "عرض"}
</span>
</p>
{property.purpose === "rent" && (
<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-4 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.floor > 0 && (
<div className="text-center">
<Layers className="w-5 h-5 text-amber-500 mx-auto mb-1" />
<span className="text-sm font-bold">{property.floor}</span>
<span className="text-xs text-gray-500 block">طابق</span>
</div>
)}
{property.salons > 0 && (
<div className="text-center">
<Sofa className="w-5 h-5 text-amber-500 mx-auto mb-1" />
<span className="text-sm font-bold">{property.salons}</span>
<span className="text-xs text-gray-500 block">صالونات</span>
</div>
)}
{property.balconies > 0 && (
<div className="text-center">
<DoorOpen className="w-5 h-5 text-amber-500 mx-auto mb-1" />
<span className="text-sm font-bold">
{property.balconies}
</span>
<span className="text-xs text-gray-500 block">بلكونات</span>
</div>
)}
{property.bookedCount > 0 && (
<div className="text-center">
<Calendar className="w-5 h-5 text-amber-500 mx-auto mb-1" />
<span className="text-sm font-bold">
{property.bookedCount}
</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 &&
(Array.isArray(property.services)
? property.services.length > 0
: Object.keys(property.services).length > 0) && (
<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">
{Array.isArray(property.services)
? property.services.map((svc, i) => (
<span
key={i}
className="inline-flex items-center gap-1 px-3 py-1 bg-green-100 text-green-800 rounded-full text-sm"
>
{serviceLabels[svc] || svc}
</span>
))
: Object.entries(property.services).map(([key, value]) => {
if (!value) return null;
const detail =
typeof value === "object" && value.detail
? value.detail
: typeof value === "string"
? value
: null;
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"
>
{serviceLabels[key] || key}
{detail && (
<span className="text-green-400">· {detail}</span>
)}
</span>
);
})}
</div>
</div>
)}
{property.proximity && Object.keys(property.proximity).length > 0 && (
<div className="bg-gray-50 p-4 rounded-xl mb-6">
<h4 className="font-bold text-gray-700 mb-3">القرب من الخدمات</h4>
<div className="grid grid-cols-2 md:grid-cols-3 gap-2">
{Object.entries(property.proximity).map(([key, val]) => {
if (!val) return null;
const dist = typeof val === "object" ? val.distance : val;
return (
<div
key={key}
className="flex items-center gap-2 p-2 bg-white rounded-lg"
>
{key === "School" && <School className="w-4 h-4 text-amber-500" />}
{key === "Hospital" && <Hospital className="w-4 h-4 text-amber-500" />}
{key === "Restaurant" && <Store className="w-4 h-4 text-amber-500" />}
{key === "University" && <GraduationCap className="w-4 h-4 text-amber-500" />}
{key === "Park" && <TreePine className="w-4 h-4 text-amber-500" />}
{key === "Mall" && <Building className="w-4 h-4 text-amber-500" />}
{!["School","Hospital","Restaurant","University","Park","Mall"].includes(key) && <MapPin className="w-4 h-4 text-amber-500" />}
<div>
<span className="text-sm text-gray-700">{proximityLabels[key] || key}</span>
<span className="text-xs text-gray-500 mr-1">{dist} {typeof dist === "number" ? "كم" : ""}</span>
</div>
</div>
);
})}
</div>
</div>
)}
{property.terms && Object.keys(property.terms).length > 0 && (
<div className="bg-gray-50 p-4 rounded-xl mb-6">
<h4 className="font-bold text-gray-700 mb-3">شروط الاستخدام</h4>
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
{Object.entries(property.terms).map(([key, value]) => {
if (!value) return null;
return (
<div
key={key}
className="flex items-center gap-2 p-2 bg-white rounded-lg"
>
{key.startsWith("No") || key.startsWith("Only") ? (
<Ban className="w-4 h-4 text-red-500 flex-shrink-0" />
) : (
<Check className="w-4 h-4 text-green-500 flex-shrink-0" />
)}
<span className="text-sm text-gray-700">
{termLabels[key] || key}
</span>
</div>
);
})}
</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 > 0 && (
<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 > 0 && (
<p className="flex justify-between">
<span className="text-gray-600">السعر الشهري:</span>
<span className="font-bold text-amber-600">
{Number(property.monthlyPrice).toLocaleString()} ل.س
</span>
</p>
)}
{property.deposit > 0 && (
<p className="flex justify-between">
<span className="text-gray-600">التأمين:</span>
<span className="font-bold text-gray-700">
{Number(property.deposit).toLocaleString()} ل.س
</span>
</p>
)}
<p className="text-sm text-gray-500 mt-1">
نوع الإيجار:{" "}
{property.rentType === "daily"
? "يومي"
: property.rentType === "monthly"
? "شهري"
: "يومي وشهري"}
</p>
{property.rating > 0 && (
<p className="flex justify-between mt-2 pt-2 border-t border-amber-200">
<span className="text-gray-600">التقييم:</span>
<span className="font-bold text-amber-600 flex items-center gap-1">
{Number(property.rating).toFixed(1)}{" "}
<Star className="w-4 h-4 fill-amber-500" />
</span>
</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 z-10 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 transition-colors"
>
<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 [activeTab, setActiveTab] = useState("rent");
const [deleteModal, setDeleteModal] = useState({
isOpen: false,
property: null,
});
const [viewModal, setViewModal] = useState({ isOpen: false, property: null });
const [editModal, setEditModal] = useState({ isOpen: false, property: null });
const filteredProperties = properties.filter((p) => p.purpose === activeTab);
const rentCount = properties.filter((p) => p.purpose === "rent").length;
const saleCount = properties.filter((p) => p.purpose === "sale").length;
useEffect(() => {
const authUser = AuthService.getUser();
if (authUser && AuthService.isOwner()) {
setUser({
name: authUser.name || authUser.email,
email: authUser.email,
role: "owner",
});
loadProperties();
} else {
router.push("/auth/choose-role");
}
}, [router]);
const loadProperties = async () => {
const authUser = AuthService.getUser();
const userId = authUser?.id;
if (!userId) {
console.warn("[OwnerProperties] No user ID found");
setIsLoading(false);
return;
}
try {
console.log("[OwnerProperties] Fetching listings for user:", userId);
const [rentData, saleData] = await Promise.allSettled([
getMyRentListings(),
getMySaleListings(),
]);
const rentList =
rentData.status === "fulfilled"
? Array.isArray(rentData.value)
? rentData.value.filter(Boolean)
: rentData.value
? [rentData.value]
: []
: [];
const saleList =
saleData.status === "fulfilled"
? Array.isArray(saleData.value)
? saleData.value.filter(Boolean)
: saleData.value
? [saleData.value]
: []
: [];
console.log(
"[OwnerProperties] Rent:",
rentList.length,
"Sale:",
saleList.length,
);
const normalizeServices = (details) => {
const rawServices = details.services || {};
const serviceList = Array.isArray(rawServices)
? rawServices
: Object.keys(rawServices).filter((k) => rawServices[k]);
const serviceDetails = details.serviceDetails || {};
const services = {};
serviceList.forEach((s) => {
services[s] = serviceDetails[s] || true;
});
return services;
};
const normalizeTerms = (terms) => {
if (!terms) return {};
return Array.isArray(terms)
? terms.reduce((acc, t) => ({ ...acc, [t]: true }), {})
: terms;
};
const normalizeProximity = (prox) => {
if (!prox) return {};
const result = {};
Object.entries(prox).forEach(([k, v]) => {
if (!v) return;
result[k.charAt(0).toUpperCase() + k.slice(1)] = v;
});
return result;
};
const mappedRent = rentList.map((item) => {
const info = item.propertyInformation || {};
const details = typeof info.detailsJSON === 'object' && info.detailsJSON ? info.detailsJSON : (() => {
try {
return JSON.parse(info.detailsJSON || "{}");
} catch {
return {};
}
})();
const apiBase =
typeof window !== "undefined"
? process.env.NEXT_PUBLIC_API_URL ||
"https://45.93.137.91.nip.io/api"
: "";
const raw = Array.isArray(info.images) ? info.images : [];
return {
id: item.id,
title: info.address || `عقار #${item.id}`,
propertyType:
{
0: "apartment",
1: "villa",
2: "sweet",
3: "room",
4: "studio",
5: "office",
6: "farms",
7: "shop",
8: "warehouse",
}[info.buildingType] || "apartment",
propertyTypeLabel:
{
0: "شقة",
1: "فيلا",
2: "سويت",
3: "غرفة",
4: "استوديو",
5: "مكتب",
6: "مزرعة",
7: "متجر",
8: "مستودع",
}[info.buildingType] || "عقار",
purpose: "rent",
rentType:
item.rentType === 0
? "monthly"
: item.rentType === 1
? "daily"
: "daily",
dailyPrice: item.dailyRent || 0,
monthlyPrice: item.monthlyRent || 0,
salePrice: item.price || 0,
deposit: item.deposit || 0,
location: info.address || "",
bedrooms: info.numberOfBedRooms || 0,
bathrooms: info.numberOfBathRooms || 0,
area: info.space || 0,
livingRooms: details.livingRooms || 0,
floor: details.floorNumber ?? details.floor ?? 0,
salons:
details.numberOfSalons ??
details.salonsCount ??
details.salons ??
0,
balconies:
details.numberOfBalconies ??
details.balconiesCount ??
details.balconies ??
0,
bookedCount: details.bookedCount || 0,
status:
{ 0: "available", 1: "booked", 2: "maintenance" }[info.status] ||
"available",
images:
raw.length > 0
? raw.map((img) =>
img.startsWith("http")
? img
: `${apiBase}${img.startsWith("/") ? "" : "/Pictures/"}${img}`,
)
: ["/property-placeholder.jpg"],
createdAt: item.createdAt || new Date().toISOString(),
furnished: details.furnished || false,
displayType: details.displayType || (item.dailyRent && item.monthlyRent ? 'Both' : item.monthlyRent ? 'Monthly rent' : item.dailyRent ? 'Daily rent' : 'Both'),
description: info.description || "",
address: info.address || "",
city: "",
district: "",
services: normalizeServices(details),
terms: normalizeTerms(details.terms),
proximity: normalizeProximity(details.nearbyDistances),
rating: item.rating || 0,
currencyId: item.currencyId,
_raw: item,
};
});
const mappedSale = saleList.map((item) => {
const info = item.propertyInformation || {};
const details = typeof info.detailsJSON === 'object' && info.detailsJSON ? info.detailsJSON : (() => {
try {
return JSON.parse(info.detailsJSON || "{}");
} catch {
return {};
}
})();
const apiBase =
typeof window !== "undefined"
? process.env.NEXT_PUBLIC_API_URL ||
"https://45.93.137.91.nip.io/api"
: "";
const raw = Array.isArray(info.images) ? info.images : [];
return {
id: item.id,
title: info.address || `عقار للبيع #${item.id}`,
propertyType:
{
0: "apartment",
1: "villa",
2: "sweet",
3: "room",
4: "studio",
5: "office",
6: "farms",
7: "shop",
8: "warehouse",
}[info.buildingType] || "apartment",
propertyTypeLabel:
{
0: "شقة",
1: "فيلا",
2: "سويت",
3: "غرفة",
4: "استوديو",
5: "مكتب",
6: "مزرعة",
7: "متجر",
8: "مستودع",
}[info.buildingType] || "عقار",
purpose: "sale",
dailyPrice: 0,
monthlyPrice: 0,
salePrice: item.price || 0,
deposit: 0,
location: info.address || "",
bedrooms: info.numberOfBedRooms || 0,
bathrooms: info.numberOfBathRooms || 0,
area: info.space || 0,
livingRooms: details.livingRooms || 0,
floor: details.floorNumber ?? details.floor ?? 0,
salons:
details.numberOfSalons ??
details.salonsCount ??
details.salons ??
0,
balconies:
details.numberOfBalconies ??
details.balconiesCount ??
details.balconies ??
0,
bookedCount: details.bookedCount || 0,
status: "available",
images:
raw.length > 0
? raw.map((img) =>
img.startsWith("http")
? img
: `${apiBase}${img.startsWith("/") ? "" : "/Pictures/"}${img}`,
)
: ["/property-placeholder.jpg"],
createdAt: item.createdAt || new Date().toISOString(),
furnished: details.furnished || false,
displayType: details.displayType || 'For sale',
description: info.description || "",
address: info.address || "",
city: "",
district: "",
services: normalizeServices(details),
terms: normalizeTerms(details.terms),
proximity: normalizeProximity(details.nearbyDistances),
rating: item.rating || 0,
currencyId: item.currencyId,
_raw: item,
};
});
setProperties([...mappedRent, ...mappedSale]);
} catch (err) {
console.error("[OwnerProperties] Failed to load properties:", err);
toast.error("فشل في تحميل العقارات");
} finally {
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 = async (updatedProperty) => {
try {
if (updatedProperty.purpose === "rent" && updatedProperty._raw) {
const buildingTypeMap = {
apartment: 0,
villa: 1,
sweet: 2,
room: 3,
studio: 4,
office: 5,
farms: 6,
shop: 7,
warehouse: 8,
};
const raw = updatedProperty._raw;
const rentTypeMap = { daily: 1, monthly: 0, both: 0 };
const rawServices = updatedProperty.services || {};
const serviceList = Array.isArray(rawServices)
? rawServices
: Object.keys(rawServices).filter((k) => rawServices[k]);
const details = {
services: serviceList,
serviceDetails: updatedProperty.serviceDetails || {},
terms: normalizeTerms(updatedProperty.terms),
displayType: "Both",
propertyCondition: updatedProperty.furnished
? "WithFurniture"
: "WithoutFurniture",
};
const detailsJSON = JSON.stringify(details);
const editBody = {
PropertyInformation: {
CordsX: raw.propertyInformation?.cordsX || "",
CordsY: raw.propertyInformation?.cordsY || "",
Address:
updatedProperty.address || raw.propertyInformation?.address || "",
Description:
updatedProperty.description ||
raw.propertyInformation?.description ||
"",
NumberOfBathRooms:
updatedProperty.bathrooms ||
raw.propertyInformation?.numberOfBathRooms ||
0,
NumberOfRooms: updatedProperty.bedrooms || 0,
NumberOfBedRooms:
updatedProperty.bedrooms ||
raw.propertyInformation?.numberOfBedRooms ||
0,
Space:
parseFloat(updatedProperty.area) ||
raw.propertyInformation?.space ||
0,
DetailsJSON: detailsJSON,
BuildingType: buildingTypeMap[updatedProperty.propertyType] ?? 0,
Status: updatedProperty.status === "available" ? 0 : 1,
PropertyType: updatedProperty.furnished ? 0 : 1,
Images: raw.propertyInformation?.images || [],
},
Deposit: parseFloat(updatedProperty.deposit) || raw.deposit || 0,
MonthlyRent:
parseFloat(updatedProperty.monthlyPrice) || raw.monthlyRent || 0,
DailyRent:
parseFloat(updatedProperty.dailyPrice) || raw.dailyRent || 0,
Rating: 1,
CurrencyId: updatedProperty.currencyId || raw.currencyId || 1,
RentType: rentTypeMap[updatedProperty.rentType] ?? 0,
Type: updatedProperty.furnished ? 0 : 1,
AllowedPaymentPeriod: "01:00:00:00",
};
const payload = { rentPropertyDto: editBody };
await editRentProperty(updatedProperty.id, payload);
}
const newProperties = properties.map((p) =>
p.id === updatedProperty.id ? updatedProperty : p,
);
updatePropertiesInStorage(newProperties);
setEditModal({ isOpen: false, property: null });
toast.success("تم تحديث العقار بنجاح");
} catch (err) {
console.error("[OwnerProperties] Edit failed:", err);
toast.error("فشل تحديث العقار");
}
};
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 relative">
<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>
{/* Tab Switcher */}
<div className="flex gap-1 mb-6 bg-gray-100 rounded-xl p-1 w-fit">
<button
onClick={() => setActiveTab("rent")}
className={`px-6 py-2 rounded-lg text-sm font-medium transition-all ${activeTab === "rent" ? "bg-white text-amber-600 shadow-sm" : "text-gray-600 hover:text-gray-800"}`}
>
<span className="flex items-center gap-2">
<Home className="w-4 h-4" />
للإيجار
{rentCount > 0 && (
<span className="bg-amber-100 text-amber-800 text-xs px-2 py-0.5 rounded-full">
{rentCount}
</span>
)}
</span>
</button>
<button
onClick={() => setActiveTab("sale")}
className={`px-6 py-2 rounded-lg text-sm font-medium transition-all ${activeTab === "sale" ? "bg-white text-blue-600 shadow-sm" : "text-gray-600 hover:text-gray-800"}`}
>
<span className="flex items-center gap-2">
<DollarSign className="w-4 h-4" />
للبيع
{saleCount > 0 && (
<span className="bg-blue-100 text-blue-800 text-xs px-2 py-0.5 rounded-full">
{saleCount}
</span>
)}
</span>
</button>
</div>
{filteredProperties.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">
{activeTab === "rent"
? "لا توجد عقارات للإيجار"
: "لا توجد عقارات للبيع"}
</h3>
<p className="text-gray-600 mb-6">
ابدأ بإضافة أول عقار {activeTab === "rent" ? "للإيجار" : "للبيع"}{" "}
الآن
</p>
<Link
href={`/owner/properties/add?purpose=${activeTab}`}
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">
{filteredProperties.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 aspect-[16/10] bg-gray-100 overflow-hidden">
{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 flex gap-1">
<span
className={`px-2 py-0.5 rounded-md text-xs font-medium shadow-sm backdrop-blur-sm ${
property.status === "available"
? "bg-white/90 text-green-700"
: "bg-white/90 text-yellow-700"
}`}
>
{property.status === "available" ? "متاح" : "مؤجر"}
</span>
{property.purpose === "rent" &&
property.furnished !== undefined && (
<span
className={`px-2 py-0.5 rounded-md text-xs font-medium shadow-sm backdrop-blur-sm ${
property.furnished
? "bg-white/90 text-amber-700"
: "bg-white/90 text-gray-600"
}`}
>
{property.furnished ? "مفروش" : "غير مفروش"}
</span>
)}
</div>
<div className="absolute top-2 left-2">
<span className="px-2 py-0.5 rounded-md text-xs font-medium bg-white/90 text-amber-700 shadow-sm backdrop-blur-sm">
{property.propertyTypeLabel ||
(property.propertyType === "apartment"
? "شقة"
: property.propertyType === "villa"
? "فيلا"
: property.propertyType === "sweet"
? "سويت"
: property.propertyType === "room"
? "غرفة"
: property.propertyType === "studio"
? "استوديو"
: property.propertyType === "office"
? "مكتب"
: property.propertyType === "farms"
? "مزرعة"
: property.propertyType === "shop"
? "متجر"
: property.propertyType === "warehouse"
? "مستودع"
: "عقار")}
</span>
</div>
</div>
<div className="p-3">
<div className="flex items-start justify-between mb-1">
<h3 className="font-bold text-gray-900 truncate flex-1 ml-2">
{property.title}
</h3>
{property.rating > 0 && (
<div className="flex items-center gap-0.5 text-amber-600 shrink-0">
<Star className="w-3 h-3 fill-amber-500" />
<span className="text-xs font-medium">
{Number(property.rating).toFixed(1)}
</span>
</div>
)}
</div>
<div className="flex items-center gap-1 text-gray-500 text-xs mb-2">
<MapPin className="w-3 h-3" />
<span className="truncate">
{property.address || property.location || "موقع غير محدد"}
</span>
</div>
<div className="flex flex-wrap items-center gap-2 text-xs text-gray-600 mb-2">
{property.bedrooms > 0 && (
<div className="flex items-center gap-0.5 bg-gray-50 px-2 py-0.5 rounded-md">
<Bed className="w-3 h-3" />
<span>{property.bedrooms}</span>
</div>
)}
{property.bathrooms > 0 && (
<div className="flex items-center gap-0.5 bg-gray-50 px-2 py-0.5 rounded-md">
<Bath className="w-3 h-3" />
<span>{property.bathrooms}</span>
</div>
)}
{property.area > 0 && (
<div className="flex items-center gap-0.5 bg-gray-50 px-2 py-0.5 rounded-md">
<Square className="w-3 h-3" />
<span>{property.area}م²</span>
</div>
)}
{property.floor > 0 && (
<div className="flex items-center gap-0.5 bg-gray-50 px-2 py-0.5 rounded-md">
<Layers className="w-3 h-3" />
<span>ط {property.floor}</span>
</div>
)}
{property.salons > 0 && (
<div className="flex items-center gap-0.5 bg-gray-50 px-2 py-0.5 rounded-md">
<Sofa className="w-3 h-3" />
<span>{property.salons}</span>
</div>
)}
{property.balconies > 0 && (
<div className="flex items-center gap-0.5 bg-gray-50 px-2 py-0.5 rounded-md">
<DoorOpen className="w-3 h-3" />
<span>{property.balconies}</span>
</div>
)}
</div>
<div className="flex justify-between items-center pt-2 border-t border-gray-100">
<div>
{property.purpose === "rent" ? (
<div>
{property.monthlyPrice > 0 && (
<span className="text-base font-bold text-amber-600">
{Number(property.monthlyPrice).toLocaleString()}
<span className="text-xs text-gray-500 font-normal mr-1">
ل.س /شهر
</span>
</span>
)}
{property.dailyPrice > 0 &&
!property.monthlyPrice && (
<span className="text-base font-bold text-amber-600">
{Number(property.dailyPrice).toLocaleString()}
<span className="text-xs text-gray-500 font-normal mr-1">
ل.س /يوم
</span>
</span>
)}
{property.deposit > 0 && (
<div className="text-[10px] text-gray-400">
تأمين: {Number(property.deposit).toLocaleString()}{" "}
ل.س
</div>
)}
</div>
) : (
<span className="text-base font-bold text-blue-600">
{Number(property.salePrice).toLocaleString()} ل.س
</span>
)}
</div>
<div className="flex gap-1">
<button
onClick={() => setViewModal({ isOpen: true, property })}
className="px-3 py-1.5 bg-gray-50 hover:bg-blue-50 border border-gray-200 hover:border-blue-200 rounded-lg transition-all group flex items-center gap-1.5"
title="عرض التفاصيل"
>
<Eye className="w-3.5 h-3.5 text-gray-400 group-hover:text-blue-600" />
<span className="text-xs text-gray-500 group-hover:text-blue-600 font-medium">
عرض
</span>
</button>
<button
onClick={() => setEditModal({ isOpen: true, property })}
className="p-1.5 hover:bg-amber-50 rounded-lg transition-colors group"
title="تعديل العقار"
>
<Edit className="w-3.5 h-3.5 text-gray-400 group-hover:text-amber-600" />
</button>
<button
onClick={() =>
setDeleteModal({ isOpen: true, property })
}
className="p-1.5 hover:bg-red-50 rounded-lg transition-colors group"
title="حذف العقار"
>
<Trash2 className="w-3.5 h-3.5 text-gray-400 group-hover:text-red-600" />
</button>
</div>
</div>
</div>
</motion.div>
))}
</div>
)}
</div>
</div>
);
}