Files
SweetHome/app/owner/properties/add/page.js

1177 lines
42 KiB
JavaScript

'use client';
import { useState, useRef, useEffect } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { useRouter } from 'next/navigation';
import Link from 'next/link';
import Image from 'next/image';
import dynamic from 'next/dynamic';
import 'leaflet/dist/leaflet.css';
import {
ArrowLeft,
MapPin,
Camera,
X,
Home,
Building,
Bed,
Bath,
Square,
DollarSign,
Calendar,
Clock,
CheckCircle,
AlertCircle,
Info,
ChevronRight,
ChevronLeft,
Loader2,
Upload,
FileText,
Shield,
HelpCircle,
Search,
Navigation,
Wifi,
Zap,
Flame,
Droplets,
Cigarette,
Dog,
Music,
Star,
Sofa,
DoorOpen,
Warehouse,
Layers,
Plus,
Minus,
Save,
Wind,
Move
} from 'lucide-react';
import toast, { Toaster } from 'react-hot-toast';
const MapContainer = dynamic(() => import('react-leaflet').then(mod => mod.MapContainer), { ssr: false });
const TileLayer = dynamic(() => import('react-leaflet').then(mod => mod.TileLayer), { ssr: false });
const Marker = dynamic(() => import('react-leaflet').then(mod => mod.Marker), { ssr: false });
const Popup = dynamic(() => import('react-leaflet').then(mod => mod.Popup), { ssr: false });
const useMapEvents = dynamic(() => import('react-leaflet').then(mod => mod.useMapEvents), { ssr: false });
function MapClickHandler({ onMapClick }) {
const map = useMapEvents({
click: (e) => {
const { lat, lng } = e.latlng;
onMapClick([lat, lng]);
},
});
return null;
}
export default function AddPropertyPage() {
const router = useRouter();
const [step, setStep] = useState(1);
const totalSteps = 4;
const [formData, setFormData] = useState({
propertyType: 'apartment', // apartment, villa, suite, room
furnished: false,
bedrooms: 1,
bathrooms: 1,
livingRooms: 1,
services: {
electricity: false,
internet: false,
heating: false,
water: false,
airConditioning: false,
parking: false,
elevator: false
},
terms: {
noSmoking: false,
noPets: false,
noParties: false,
noAlcohol: false,
suitableForChildren: true,
suitableForElderly: true
},
offerType: 'daily',
dailyPrice: '',
monthlyPrice: '',
salePrice: '',
city: '',
district: '',
address: '',
lat: null,
lng: null,
description: '',
images: []
});
const [imagePreviews, setImagePreviews] = useState([]);
const [selectedLocation, setSelectedLocation] = useState(null);
const [mapCenter, setMapCenter] = useState([33.5138, 36.2765]);
const [searchQuery, setSearchQuery] = useState('');
const [mapLoaded, setMapLoaded] = useState(false);
const [errors, setErrors] = useState({});
const [isLoading, setIsLoading] = useState(false);
const fileInputRef = useRef(null);
const propertyTypes = [
{ id: 'apartment', label: 'شقة', icon: Building },
{ id: 'villa', label: 'فيلا', icon: Home },
{ id: 'suite', label: 'سويت', icon: Sofa },
{ id: 'room', label: 'غرفة ضمن شقة', icon: DoorOpen }
];
const serviceList = [
{ id: 'electricity', label: 'كهرباء', icon: Zap },
{ id: 'internet', label: 'انترنت', icon: Wifi },
{ id: 'heating', label: 'تدفئة', icon: Flame },
{ id: 'water', label: 'ماء', icon: Droplets },
{ id: 'airConditioning', label: 'تكييف', icon: Wind },
{ id: 'parking', label: 'موقف سيارات', icon: Warehouse },
{ id: 'elevator', label: 'مصعد', icon: Layers }
];
const termsList = [
{ id: 'noSmoking', label: 'ممنوع التدخين', icon: Cigarette },
{ id: 'noPets', label: 'ممنوع الحيوانات', icon: Dog },
{ id: 'noParties', label: 'عدم إقامة حفلات', icon: Music },
{ id: 'noAlcohol', label: 'ممنوع الكحول', icon: X },
{ id: 'suitableForChildren', label: 'مناسب للأطفال', icon: Star },
{ id: 'suitableForElderly', label: 'مناسب لكبار السن', icon: Star }
];
const offerTypes = [
{ id: 'daily', label: 'إيجار يومي', icon: Clock },
{ id: 'monthly', label: 'إيجار شهري', icon: Calendar },
{ id: 'both', label: 'إيجار يومي وشهري', icon: Calendar },
{ id: 'sale', label: 'للبيع', icon: DollarSign }
];
useEffect(() => {
if (typeof window !== 'undefined') {
const L = require('leaflet');
delete L.Icon.Default.prototype._getIconUrl;
L.Icon.Default.mergeOptions({
iconRetinaUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-icon-2x.png',
iconUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-icon.png',
shadowUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-shadow.png',
});
}
setMapLoaded(true);
}, []);
const handleSearch = async () => {
if (!searchQuery) return;
toast.loading('جاري البحث...', { id: 'search' });
try {
const response = await fetch(
`https://nominatim.openstreetmap.org/search?format=json&q=${encodeURIComponent(searchQuery)}&limit=1&accept-language=ar`
);
const data = await response.json();
if (data && data.length > 0) {
const result = data[0];
const lat = parseFloat(result.lat);
const lng = parseFloat(result.lon);
setMapCenter([lat, lng]);
setMapZoom(18);
const addressResponse = await fetch(
`https://nominatim.openstreetmap.org/reverse?format=json&lat=${lat}&lon=${lng}&accept-language=ar`
);
const addressData = await addressResponse.json();
setSelectedLocation({
lat: lat,
lng: lng,
address: addressData.display_name || result.display_name
});
toast.success('تم العثور على الموقع', { id: 'search' });
} else {
toast.error('لم يتم العثور على العنوان', { id: 'search' });
}
} catch (error) {
console.error('خطأ في البحث:', error);
toast.error('حدث خطأ في البحث', { id: 'search' });
}
};
const handleGeolocation = () => {
if (!navigator.geolocation) {
toast.error('المتصفح لا يدعم تحديد الموقع');
return;
}
toast.loading('جاري تحديد موقعك...', { id: 'geolocation' });
navigator.geolocation.getCurrentPosition(
async (position) => {
const { latitude, longitude } = position.coords;
setMapCenter([latitude, longitude]);
setMapZoom(18);
try {
const response = await fetch(
`https://nominatim.openstreetmap.org/reverse?format=json&lat=${latitude}&lon=${longitude}&accept-language=ar`
);
const data = await response.json();
setSelectedLocation({
lat: latitude,
lng: longitude,
address: data.display_name || 'موقعك الحالي'
});
toast.success('تم تحديد موقعك', { id: 'geolocation' });
} catch (error) {
setSelectedLocation({
lat: latitude,
lng: longitude,
address: 'موقعك الحالي'
});
toast.success('تم تحديد موقعك', { id: 'geolocation' });
}
},
(error) => {
toast.error('فشل في تحديد الموقع', { id: 'geolocation' });
}
);
};
const handleMapClick = async (coords) => {
try {
const [lat, lng] = coords;
toast.loading('جاري تحديد الموقع...', { id: 'location' });
const response = await fetch(
`https://nominatim.openstreetmap.org/reverse?format=json&lat=${lat}&lon=${lng}&accept-language=ar`
);
const data = await response.json();
setSelectedLocation({
lat: lat,
lng: lng,
address: data.display_name || 'موقع محدد'
});
setMapZoom(18);
toast.success('تم تحديد الموقع بنجاح!', { id: 'location' });
} catch (error) {
console.error('خطأ في تحديد الموقع:', error);
const [lat, lng] = coords;
setSelectedLocation({
lat: lat,
lng: lng,
address: 'موقع محدد'
});
setMapZoom(18);
toast.success('تم تحديد الموقع', { id: 'location' });
}
};
const confirmLocation = () => {
if (selectedLocation) {
setFormData({
...formData,
lat: selectedLocation.lat,
lng: selectedLocation.lng,
address: selectedLocation.address
});
toast.success('تم تأكيد الموقع بنجاح');
}
};
const resetLocation = () => {
setSelectedLocation(null);
setFormData({
...formData,
lat: null,
lng: null,
address: ''
});
setMapZoom(15);
toast.info('تم إلغاء تحديد الموقع');
};
const handleImageUpload = (files) => {
const newImages = Array.from(files);
if (formData.images.length + newImages.length > 10) {
toast.error('يمكنك رفع 10 صور كحد أقصى');
return;
}
newImages.forEach(file => {
if (!file.type.startsWith('image/')) {
toast.error('الرجاء اختيار صور صالحة فقط');
return;
}
if (file.size > 5 * 1024 * 1024) {
toast.error('حجم الصورة يجب أن يكون أقل من 5 ميجابايت');
return;
}
const reader = new FileReader();
reader.onloadend = () => {
setImagePreviews(prev => [...prev, reader.result]);
};
reader.readAsDataURL(file);
setFormData({
...formData,
images: [...formData.images, file]
});
});
};
const removeImage = (index) => {
const newImages = [...formData.images];
newImages.splice(index, 1);
const newPreviews = [...imagePreviews];
newPreviews.splice(index, 1);
setFormData({
...formData,
images: newImages
});
setImagePreviews(newPreviews);
};
const toggleService = (serviceId) => {
setFormData({
...formData,
services: {
...formData.services,
[serviceId]: !formData.services[serviceId]
}
});
};
const toggleTerm = (termId) => {
setFormData({
...formData,
terms: {
...formData.terms,
[termId]: !formData.terms[termId]
}
});
};
const incrementBedrooms = () => {
setFormData({
...formData,
bedrooms: formData.bedrooms + 1
});
};
const decrementBedrooms = () => {
if (formData.bedrooms > 1) {
setFormData({
...formData,
bedrooms: formData.bedrooms - 1
});
}
};
const incrementBathrooms = () => {
setFormData({
...formData,
bathrooms: formData.bathrooms + 1
});
};
const decrementBathrooms = () => {
if (formData.bathrooms > 1) {
setFormData({
...formData,
bathrooms: formData.bathrooms - 1
});
}
};
const incrementLivingRooms = () => {
setFormData({
...formData,
livingRooms: formData.livingRooms + 1
});
};
const decrementLivingRooms = () => {
if (formData.livingRooms > 1) {
setFormData({
...formData,
livingRooms: formData.livingRooms - 1
});
}
};
const validateStep = () => {
const newErrors = {};
switch(step) {
case 1:
if (!formData.propertyType) {
newErrors.propertyType = 'نوع العقار مطلوب';
}
break;
case 2:
if (!formData.bedrooms) {
newErrors.bedrooms = 'عدد الغرف مطلوب';
}
if (!formData.bathrooms) {
newErrors.bathrooms = 'عدد الحمامات مطلوب';
}
if (!formData.livingRooms) {
newErrors.livingRooms = 'عدد الصالونات مطلوب';
}
break;
case 3:
if (formData.offerType === 'daily' && !formData.dailyPrice) {
newErrors.dailyPrice = 'السعر اليومي مطلوب';
}
if (formData.offerType === 'monthly' && !formData.monthlyPrice) {
newErrors.monthlyPrice = 'السعر الشهري مطلوب';
}
if (formData.offerType === 'both') {
if (!formData.dailyPrice) newErrors.dailyPrice = 'السعر اليومي مطلوب';
if (!formData.monthlyPrice) newErrors.monthlyPrice = 'السعر الشهري مطلوب';
}
if (formData.offerType === 'sale' && !formData.salePrice) {
newErrors.salePrice = 'سعر البيع مطلوب';
}
break;
case 4:
if (!formData.lat || !formData.lng) {
newErrors.location = 'الرجاء تحديد موقع العقار على الخريطة';
}
if (formData.images.length === 0) {
newErrors.images = 'يجب رفع صورة واحدة على الأقل';
}
break;
}
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
};
const handleNext = () => {
if (validateStep()) {
setStep(step + 1);
window.scrollTo({ top: 0, behavior: 'smooth' });
}
};
const handleBack = () => {
setStep(step - 1);
window.scrollTo({ top: 0, behavior: 'smooth' });
};
const handleSubmit = async () => {
if (!validateStep()) return;
setIsLoading(true);
setTimeout(() => {
console.log('Property Data:', formData);
setIsLoading(false);
toast.success('تم إضافة العقار بنجاح!');
setTimeout(() => {
router.push('/owner/properties');
}, 1500);
}, 2000);
};
const fadeInUp = {
initial: { opacity: 0, y: 20 },
animate: { opacity: 1, y: 0 },
transition: { duration: 0.5 }
};
function MapClickHandler({ onMapClick }) {
const map = useMapEvents({
dblclick: (e) => {
const { lat, lng } = e.latlng;
onMapClick([lat, lng]);
},
});
return null;
}
return (
<div className="min-h-screen bg-gray-50 py-8">
<Toaster position="top-center" reverseOrder={false} />
<div className="container mx-auto px-4 max-w-4xl">
<div className="mb-8">
<div className="flex items-center justify-between mb-4">
<Link
href="/owner/properties"
className="flex items-center gap-2 text-gray-600 hover:text-amber-600 transition-colors group"
>
<ArrowLeft className="w-4 h-4 group-hover:-translate-x-1 transition-transform" />
<span>العودة للعقارات</span>
</Link>
<div className="flex items-center gap-2">
<span className="text-sm font-medium text-amber-600">خطوة {step} من {totalSteps}</span>
</div>
</div>
<div className="flex gap-2">
{[1, 2, 3, 4].map((s) => (
<motion.div
key={s}
className={`h-2 flex-1 rounded-full ${
s <= step ? 'bg-amber-500' : 'bg-gray-200'
}`}
animate={{ scaleX: s <= step ? 1 : 0.5 }}
/>
))}
</div>
<div className="flex justify-between mt-2 text-xs text-gray-500">
<span>معلومات العقار</span>
<span>التفاصيل والخدمات</span>
<span>السعر</span>
<span>الموقع والصور</span>
</div>
</div>
<motion.div
key={step}
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -20 }}
transition={{ duration: 0.3 }}
className="bg-white rounded-2xl shadow-xl p-6 md:p-8"
>
{step === 1 && (
<motion.div variants={fadeInUp} className="space-y-8">
<div className="text-center mb-6">
<div className="w-20 h-20 bg-amber-100 rounded-2xl flex items-center justify-center mx-auto mb-4">
<Home className="w-10 h-10 text-amber-600" />
</div>
<h2 className="text-2xl font-bold text-gray-900 mb-2">معلومات العقار</h2>
<p className="text-gray-600">اختر نوع العقار والحالة</p>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-3">
نوع العقار <span className="text-red-500">*</span>
</label>
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
{propertyTypes.map((type) => {
const Icon = type.icon;
return (
<button
key={type.id}
type="button"
onClick={() => setFormData({...formData, propertyType: type.id})}
className={`p-4 border rounded-xl flex flex-col items-center gap-2 transition-all ${
formData.propertyType === type.id
? 'border-amber-500 bg-amber-50 text-amber-700'
: 'border-gray-200 hover:border-amber-200 hover:bg-amber-50/50'
}`}
>
<Icon className="w-6 h-6" />
<span className="text-sm font-medium">{type.label}</span>
</button>
);
})}
</div>
{errors.propertyType && (
<p className="text-red-500 text-sm mt-2">{errors.propertyType}</p>
)}
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-3">
حالة العقار
</label>
<div className="flex gap-4">
<label className="flex items-center gap-2 p-3 border rounded-xl cursor-pointer hover:bg-gray-50 flex-1">
<input
type="radio"
name="furnished"
checked={formData.furnished === true}
onChange={() => setFormData({...formData, furnished: true})}
className="w-4 h-4 text-amber-500"
/>
<span className="text-gray-700">مفروش</span>
</label>
<label className="flex items-center gap-2 p-3 border rounded-xl cursor-pointer hover:bg-gray-50 flex-1">
<input
type="radio"
name="furnished"
checked={formData.furnished === false}
onChange={() => setFormData({...formData, furnished: false})}
className="w-4 h-4 text-amber-500"
/>
<span className="text-gray-700">غير مفروش</span>
</label>
</div>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
وصف إضافي (اختياري)
</label>
<textarea
value={formData.description}
onChange={(e) => setFormData({...formData, description: e.target.value})}
rows="4"
className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500"
placeholder="أضف وصفاً إضافياً للعقار..."
/>
</div>
</motion.div>
)}
{step === 2 && (
<motion.div variants={fadeInUp} className="space-y-8">
<div className="text-center mb-6">
<div className="w-20 h-20 bg-amber-100 rounded-2xl flex items-center justify-center mx-auto mb-4">
<Layers className="w-10 h-10 text-amber-600" />
</div>
<h2 className="text-2xl font-bold text-gray-900 mb-2">تفاصيل العقار</h2>
<p className="text-gray-600">أدخل التفاصيل والخدمات المتاحة</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
عدد الغرف <span className="text-red-500">*</span>
</label>
<div className="flex items-center gap-2">
<button
type="button"
onClick={decrementBedrooms}
className="w-10 h-10 bg-gray-100 rounded-lg flex items-center justify-center hover:bg-gray-200 transition-colors"
>
<Minus className="w-4 h-4" />
</button>
<div className="flex-1 text-center font-bold text-xl">
{formData.bedrooms}
</div>
<button
type="button"
onClick={incrementBedrooms}
className="w-10 h-10 bg-gray-100 rounded-lg flex items-center justify-center hover:bg-gray-200 transition-colors"
>
<Plus className="w-4 h-4" />
</button>
</div>
{errors.bedrooms && (
<p className="text-red-500 text-sm mt-1">{errors.bedrooms}</p>
)}
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
عدد الحمامات <span className="text-red-500">*</span>
</label>
<div className="flex items-center gap-2">
<button
type="button"
onClick={decrementBathrooms}
className="w-10 h-10 bg-gray-100 rounded-lg flex items-center justify-center hover:bg-gray-200"
>
<Minus className="w-4 h-4" />
</button>
<div className="flex-1 text-center font-bold text-xl">
{formData.bathrooms}
</div>
<button
type="button"
onClick={incrementBathrooms}
className="w-10 h-10 bg-gray-100 rounded-lg flex items-center justify-center hover:bg-gray-200"
>
<Plus className="w-4 h-4" />
</button>
</div>
{errors.bathrooms && (
<p className="text-red-500 text-sm mt-1">{errors.bathrooms}</p>
)}
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
عدد الصالونات <span className="text-red-500">*</span>
</label>
<div className="flex items-center gap-2">
<button
type="button"
onClick={decrementLivingRooms}
className="w-10 h-10 bg-gray-100 rounded-lg flex items-center justify-center hover:bg-gray-200"
>
<Minus className="w-4 h-4" />
</button>
<div className="flex-1 text-center font-bold text-xl">
{formData.livingRooms}
</div>
<button
type="button"
onClick={incrementLivingRooms}
className="w-10 h-10 bg-gray-100 rounded-lg flex items-center justify-center hover:bg-gray-200"
>
<Plus className="w-4 h-4" />
</button>
</div>
{errors.livingRooms && (
<p className="text-red-500 text-sm mt-1">{errors.livingRooms}</p>
)}
</div>
</div>
<div>
<h3 className="text-lg font-bold text-gray-900 mb-4">الخدمات المتوفرة</h3>
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
{serviceList.map((service) => {
const Icon = service.icon;
return (
<label
key={service.id}
className={`flex items-center gap-2 p-3 border rounded-xl cursor-pointer transition-all ${
formData.services[service.id]
? 'border-amber-500 bg-amber-50'
: 'border-gray-200 hover:border-amber-200 hover:bg-amber-50/50'
}`}
>
<input
type="checkbox"
checked={formData.services[service.id]}
onChange={() => toggleService(service.id)}
className="hidden"
/>
<Icon className={`w-5 h-5 ${
formData.services[service.id] ? 'text-amber-600' : 'text-gray-400'
}`} />
<span className={`text-sm ${
formData.services[service.id] ? 'text-amber-700' : 'text-gray-600'
}`}>
{service.label}
</span>
</label>
);
})}
</div>
</div>
<div>
<h3 className="text-lg font-bold text-gray-900 mb-4">شروط استخدام العقار</h3>
<div className="grid grid-cols-2 md:grid-cols-3 gap-3">
{termsList.map((term) => {
const Icon = term.icon;
return (
<label
key={term.id}
className={`flex items-center gap-2 p-3 border rounded-xl cursor-pointer transition-all ${
formData.terms[term.id]
? 'border-amber-500 bg-amber-50'
: 'border-gray-200 hover:border-amber-200 hover:bg-amber-50/50'
}`}
>
<input
type="checkbox"
checked={formData.terms[term.id]}
onChange={() => toggleTerm(term.id)}
className="hidden"
/>
<Icon className={`w-5 h-5 ${
formData.terms[term.id] ? 'text-amber-600' : 'text-gray-400'
}`} />
<span className={`text-sm ${
formData.terms[term.id] ? 'text-amber-700' : 'text-gray-600'
}`}>
{term.label}
</span>
</label>
);
})}
</div>
</div>
</motion.div>
)}
{step === 3 && (
<motion.div variants={fadeInUp} className="space-y-8">
<div className="text-center mb-6">
<div className="w-20 h-20 bg-amber-100 rounded-2xl flex items-center justify-center mx-auto mb-4">
<DollarSign className="w-10 h-10 text-amber-600" />
</div>
<h2 className="text-2xl font-bold text-gray-900 mb-2">نوع العرض والسعر</h2>
<p className="text-gray-600">اختر نوع العرض وحدد السعر المناسب</p>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-3">
نوع العرض <span className="text-red-500">*</span>
</label>
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
{offerTypes.map((type) => {
const Icon = type.icon;
return (
<button
key={type.id}
type="button"
onClick={() => setFormData({...formData, offerType: type.id})}
className={`p-4 border rounded-xl flex flex-col items-center gap-2 transition-all ${
formData.offerType === type.id
? 'border-amber-500 bg-amber-50 text-amber-700'
: 'border-gray-200 hover:border-amber-200 hover:bg-amber-50/50'
}`}
>
<Icon className="w-6 h-6" />
<span className="text-sm font-medium">{type.label}</span>
</button>
);
})}
</div>
</div>
<AnimatePresence mode="wait">
{(formData.offerType === 'daily' || formData.offerType === 'both') && (
<motion.div
key="daily"
initial={{ opacity: 0, height: 0 }}
animate={{ opacity: 1, height: 'auto' }}
exit={{ opacity: 0, height: 0 }}
className="space-y-4"
>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
السعر اليومي (ل.س) <span className="text-red-500">*</span>
</label>
<div className="relative">
<DollarSign className="absolute right-3 top-1/2 transform -translate-y-1/2 w-5 h-5 text-gray-400" />
<input
type="number"
value={formData.dailyPrice}
onChange={(e) => setFormData({...formData, dailyPrice: e.target.value})}
className={`w-full pr-12 pl-4 py-3 border rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500 ${
errors.dailyPrice ? 'border-red-500' : 'border-gray-300'
}`}
placeholder="مثال: 50000"
/>
</div>
{errors.dailyPrice && (
<p className="text-red-500 text-sm mt-1">{errors.dailyPrice}</p>
)}
</div>
</motion.div>
)}
{(formData.offerType === 'monthly' || formData.offerType === 'both') && (
<motion.div
key="monthly"
initial={{ opacity: 0, height: 0 }}
animate={{ opacity: 1, height: 'auto' }}
exit={{ opacity: 0, height: 0 }}
className="space-y-4"
>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
السعر الشهري (ل.س) <span className="text-red-500">*</span>
</label>
<div className="relative">
<DollarSign className="absolute right-3 top-1/2 transform -translate-y-1/2 w-5 h-5 text-gray-400" />
<input
type="number"
value={formData.monthlyPrice}
onChange={(e) => setFormData({...formData, monthlyPrice: e.target.value})}
className={`w-full pr-12 pl-4 py-3 border rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500 ${
errors.monthlyPrice ? 'border-red-500' : 'border-gray-300'
}`}
placeholder="مثال: 1000000"
/>
</div>
{errors.monthlyPrice && (
<p className="text-red-500 text-sm mt-1">{errors.monthlyPrice}</p>
)}
</div>
</motion.div>
)}
{formData.offerType === 'sale' && (
<motion.div
key="sale"
initial={{ opacity: 0, height: 0 }}
animate={{ opacity: 1, height: 'auto' }}
exit={{ opacity: 0, height: 0 }}
className="space-y-4"
>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
سعر البيع (ل.س) <span className="text-red-500">*</span>
</label>
<div className="relative">
<DollarSign className="absolute right-3 top-1/2 transform -translate-y-1/2 w-5 h-5 text-gray-400" />
<input
type="number"
value={formData.salePrice}
onChange={(e) => setFormData({...formData, salePrice: e.target.value})}
className={`w-full pr-12 pl-4 py-3 border rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500 ${
errors.salePrice ? 'border-red-500' : 'border-gray-300'
}`}
placeholder="أدخل السعر المطلوب"
/>
</div>
{errors.salePrice && (
<p className="text-red-500 text-sm mt-1">{errors.salePrice}</p>
)}
</div>
</motion.div>
)}
</AnimatePresence>
</motion.div>
)}
{step === 4 && (
<motion.div variants={fadeInUp} className="space-y-8">
<div className="text-center mb-6">
<div className="w-20 h-20 bg-amber-100 rounded-2xl flex items-center justify-center mx-auto mb-4">
<MapPin className="w-10 h-10 text-amber-600" />
</div>
<h2 className="text-2xl font-bold text-gray-900 mb-2">الموقع والصور</h2>
<p className="text-gray-600">حدد موقع العقار وأضف الصور</p>
</div>
<div>
<h3 className="text-lg font-bold text-gray-900 mb-4">حدد موقع العقار على الخريطة</h3>
<div className="flex gap-2 mb-4">
<div className="flex-1 relative">
<input
type="text"
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
onKeyPress={(e) => e.key === 'Enter' && handleSearch()}
placeholder="ابحث عن عنوان..."
className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500 pr-12"
/>
<Search className="absolute left-4 top-1/2 transform -translate-y-1/2 w-5 h-5 text-gray-400" />
</div>
<button
onClick={handleSearch}
className="px-6 py-3 bg-amber-500 text-white rounded-xl hover:bg-amber-600 transition-colors"
>
بحث
</button>
</div>
<div className="relative w-full h-96 rounded-xl overflow-hidden border-2 border-gray-200 mb-4">
{mapLoaded && (
<MapContainer
center={mapCenter}
zoom={mapZoom}
style={{ height: '100%', width: '100%' }}
className="z-0"
doubleClickZoom={false}
>
<TileLayer
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>'
/>
<MapClickHandler onMapClick={handleMapClick} />
{selectedLocation && (
<Marker
position={[selectedLocation.lat, selectedLocation.lng]}
draggable={true}
eventHandlers={{
dragend: async (e) => {
const marker = e.target;
const position = marker.getLatLng();
try {
const response = await fetch(
`https://nominatim.openstreetmap.org/reverse?format=json&lat=${position.lat}&lon=${position.lng}&accept-language=ar`
);
const data = await response.json();
setSelectedLocation({
lat: position.lat,
lng: position.lng,
address: data.display_name || 'موقع محدد'
});
} catch (error) {
setSelectedLocation({
lat: position.lat,
lng: position.lng,
address: 'موقع محدد'
});
}
}
}}
>
<Popup>
<div className="text-right p-2 max-w-xs">
<p className="font-bold text-sm mb-1">موقع العقار</p>
<p className="text-xs text-gray-600">{selectedLocation.address}</p>
</div>
</Popup>
</Marker>
)}
</MapContainer>
)}
</div>
{selectedLocation && !formData.lat && (
<button
onClick={confirmLocation}
className="w-full mb-4 bg-green-500 text-white py-3 rounded-xl font-medium hover:bg-green-600 transition-colors flex items-center justify-center gap-2"
>
<CheckCircle className="w-5 h-5" />
تأكيد هذا الموقع
</button>
)}
{formData.lat && (
<div className="bg-green-50 border border-green-200 rounded-xl p-4 mb-4">
<div className="flex items-center gap-2">
<CheckCircle className="w-5 h-5 text-green-600" />
<span className="text-green-800 font-medium">تم تأكيد الموقع:</span>
</div>
<p className="text-green-700 text-sm mt-2 line-clamp-2">{formData.address}</p>
</div>
)}
{errors.location && (
<p className="text-red-500 text-sm text-center mb-4">{errors.location}</p>
)}
</div>
<div>
<h3 className="text-lg font-bold text-gray-900 mb-4">صور العقار</h3>
<div
onClick={() => fileInputRef.current?.click()}
className={`border-2 border-dashed rounded-xl p-8 text-center cursor-pointer transition-all ${
errors.images ? 'border-red-500 bg-red-50' : 'border-gray-300 hover:border-amber-500 hover:bg-amber-50'
}`}
>
<input
ref={fileInputRef}
type="file"
multiple
accept="image/*"
onChange={(e) => handleImageUpload(e.target.files)}
className="hidden"
/>
<Upload className="w-12 h-12 text-gray-400 mx-auto mb-3" />
<p className="text-gray-600 font-medium">اضغط لرفع الصور</p>
<p className="text-xs text-gray-500 mt-2">
JPEG, PNG, JPG حتى 5MB 800x600 بكسل حد أقصى 10 صور
</p>
</div>
{errors.images && (
<p className="text-red-500 text-sm text-center mt-2">{errors.images}</p>
)}
{imagePreviews.length > 0 && (
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 mt-6">
{imagePreviews.map((preview, index) => (
<motion.div
key={index}
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }}
className="relative group aspect-square"
>
<Image
src={preview}
alt={`Property ${index + 1}`}
fill
className="object-cover rounded-lg"
/>
<button
onClick={() => removeImage(index)}
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>
</motion.div>
))}
</div>
)}
</div>
</motion.div>
)}
<div className="flex gap-3 mt-8 pt-6 border-t border-gray-200">
{step > 1 && (
<button
onClick={handleBack}
className="flex-1 py-3 px-4 bg-gray-100 text-gray-700 rounded-xl font-medium hover:bg-gray-200 transition-colors flex items-center justify-center gap-2"
>
<ChevronRight className="w-5 h-5" />
السابق
</button>
)}
{step < totalSteps ? (
<button
onClick={handleNext}
className={`flex-1 py-3 px-4 bg-amber-500 text-white rounded-xl font-medium hover:bg-amber-600 transition-colors flex items-center justify-center gap-2 ${
step === 1 ? 'w-full' : ''
}`}
>
التالي
<ChevronLeft className="w-5 h-5" />
</button>
) : (
<button
onClick={handleSubmit}
disabled={isLoading}
className="flex-1 py-3 px-4 bg-gradient-to-r from-amber-500 to-amber-600 text-white rounded-xl font-medium hover:from-amber-600 hover:to-amber-700 transition-all disabled:opacity-50 flex items-center justify-center gap-2"
>
{isLoading ? (
<>
<Loader2 className="w-5 h-5 animate-spin" />
جاري الحفظ...
</>
) : (
<>
<Save className="w-5 h-5" />
حفظ العقار
</>
)}
</button>
)}
</div>
</motion.div>
</div>
</div>
);
}