'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'; import { addRentProperty } from '../../../utils/api'; import { BuildingType, RentPropertyCondition, RentPropertyType, RentType, PropertyService, PropertyServiceLabels, PropertyServicesList, PropertyTerm, PropertyTermLabels, PropertyTermsList, Currency, CurrencyLabels } from '../../../enums'; 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: { [PropertyService.ELECTRICITY]: false, [PropertyService.INTERNET]: false, [PropertyService.HEATING]: false, [PropertyService.WATER]: false, [PropertyService.CENTRAL_AIR_CONDITIONING]: false, [PropertyService.PARKING]: false, [PropertyService.ELEVATOR]: false }, serviceDetails: {}, terms: { [PropertyTerm.NO_SMOKING]: false, [PropertyTerm.NO_ANIMALS]: false, [PropertyTerm.NO_PARTIES]: false }, offerType: 'daily', dailyPrice: '', monthlyPrice: '', city: '', district: '', address: '', lat: null, lng: null, description: '', images: [] }); const [imagePreviews, setImagePreviews] = useState([]); const [uploadedImagePaths, setUploadedImagePaths] = useState([]); const [selectedLocation, setSelectedLocation] = useState(null); const [mapCenter, setMapCenter] = useState([33.5138, 36.2765]); const [mapZoom, setMapZoom] = useState(13); const [searchQuery, setSearchQuery] = useState(''); const [mapLoaded, setMapLoaded] = useState(false); const [selectedCurrencyId, setSelectedCurrencyId] = useState(Currency.SYP); 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: PropertyService.ELECTRICITY, label: PropertyServiceLabels[PropertyService.ELECTRICITY], icon: Zap }, { id: PropertyService.INTERNET, label: PropertyServiceLabels[PropertyService.INTERNET], icon: Wifi }, { id: PropertyService.HEATING, label: PropertyServiceLabels[PropertyService.HEATING], icon: Flame }, { id: PropertyService.WATER, label: PropertyServiceLabels[PropertyService.WATER], icon: Droplets }, { id: PropertyService.CENTRAL_AIR_CONDITIONING, label: PropertyServiceLabels[PropertyService.CENTRAL_AIR_CONDITIONING], icon: Wind }, { id: PropertyService.PARKING, label: PropertyServiceLabels[PropertyService.PARKING], icon: Warehouse }, { id: PropertyService.ELEVATOR, label: PropertyServiceLabels[PropertyService.ELEVATOR], icon: Layers }, ]; const termsList = [ { id: PropertyTerm.NO_SMOKING, label: PropertyTermLabels[PropertyTerm.NO_SMOKING], icon: Cigarette }, { id: PropertyTerm.NO_ANIMALS, label: PropertyTermLabels[PropertyTerm.NO_ANIMALS], icon: Dog }, { id: PropertyTerm.NO_PARTIES, label: PropertyTermLabels[PropertyTerm.NO_PARTIES], icon: Music }, ]; const offerTypes = [ { id: 'daily', label: 'إيجار يومي', icon: Clock }, { id: 'monthly', label: 'إيجار شهري', icon: Calendar }, { id: 'both', label: 'إيجار يومي وشهري', icon: Calendar }, ].filter(Boolean); 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(prev => { const services = { ...prev.services }; services[serviceId] = !services[serviceId]; return { ...prev, services }; }); }; const updateServiceDetail = (serviceId, value) => { setFormData(prev => ({ ...prev, serviceDetails: { ...prev.serviceDetails, [serviceId]: value } })); }; const toggleTerm = (termId) => { setFormData(prev => { const terms = { ...prev.terms }; terms[termId] = !terms[termId]; return { ...prev, terms }; }); }; 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 = 'السعر الشهري مطلوب'; } 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); console.log('[AddProperty] Building RentPropertyDto payload...'); // Map UI property type to API BuildingType enum const buildingTypeMap = { apartment: BuildingType.APARTMENT, villa: BuildingType.VILLA, suite: BuildingType.APARTMENT, room: BuildingType.APARTMENT }; // Map offer type to RentType enum: 0=Monthly, 1=Daily const rentTypeMap = { daily: RentType.DAILY, monthly: RentType.MONTHLY, both: RentType.MONTHLY }; // Services: collect selected service enum names into array const selectedServices = Object.entries(formData.services) .filter(([, v]) => v) .map(([k]) => k); // k is already the enum value (e.g. "Electricity") // Terms: collect selected term enum names into array const selectedTerms = Object.entries(formData.terms) .filter(([, v]) => v) .map(([k]) => k); // k is already the enum value (e.g. "NoSmoking") // Build detailsJSON matching Flutter structure const detailsJSON = JSON.stringify({ services: selectedServices, serviceDetails: selectedServices.reduce((acc, s) => ({ ...acc, [s]: 'in general' }), {}), terms: selectedTerms, displayType: formData.offerType === 'both' ? 'Both' : formData.offerType === 'daily' ? 'Daily' : 'Monthly', propertyCondition: formData.furnished ? 'Furnished' : 'Unfurnished', photos: imagePreviews.map((_, i) => `photo_${i}.jpg`), room: { areaType: formData.propertyType === 'room' ? 'Shared room' : 'Private room', peopleAllowed: String(formData.bedrooms), entranceType: formData.propertyType === 'room' ? 'Shared entrance' : 'Private entrance', bathroomType: formData.bathrooms > 1 ? 'Private' : 'Shared', kitchenType: 'Not available', hasRestrictedOwnerAreas: false, languageDialect: '', hasChildren: false, hasPets: false, dedicatedTo: 'Everyone', visitorsAllowed: true, quietTimesEnabled: false, quietTimes: '', } }); const payload = { propertyInformation: { cordsX: formData.lat ? String(formData.lat) : '', cordsY: formData.lng ? String(formData.lng) : '', address: `${formData.city} - ${formData.district} - ${formData.address}`.trim(), description: formData.description || '', numberOfBathRooms: formData.bathrooms || 0, numberOfRooms: (formData.bedrooms || 0) + (formData.livingRooms || 0), numberOfBedRooms: formData.bedrooms || 0, space: parseFloat(formData.space) || 0, detailsJSON, buildingType: buildingTypeMap[formData.propertyType] ?? BuildingType.APARTMENT, status: 0, propertyType: formData.furnished ? RentPropertyCondition.WITH_FURNITURE : RentPropertyCondition.WITHOUT_FURNITURE, images: uploadedImagePaths, }, deposit: parseFloat(formData.deposit) || 0, monthlyRent: parseFloat(formData.monthlyPrice) || 0, dailyRent: parseFloat(formData.dailyPrice) || 0, rating: 0, currencyId: selectedCurrencyId, rentType: rentTypeMap[formData.offerType] ?? RentType.MONTHLY, isSmokeAllow: !formData.terms[PropertyTerm.NO_SMOKING], specializedFor: false, isVisitorAllow: !formData.terms[PropertyTerm.NO_PARTIES], type: formData.furnished ? RentPropertyType.FURNISHED : RentPropertyType.UNFURNISHED, }; console.log('[AddProperty] Payload:', JSON.stringify(payload, null, 2)); try { const res = await addRentProperty(payload); console.log('[AddProperty] API response:', res); toast.success('تم إضافة العقار بنجاح!'); setTimeout(() => { router.push('/owner/properties'); }, 1500); } catch (err) { console.error('[AddProperty] API error:', err); toast.error(err.message || 'فشل في إضافة العقار'); } finally { setIsLoading(false); } }; const fadeInUp = { initial: { opacity: 0, y: 20 }, animate: { opacity: 1, y: 0 }, transition: { duration: 0.5 } }; return (
العودة للعقارات
خطوة {step} من {totalSteps}
{[1, 2, 3, 4].map((s) => ( ))}
معلومات العقار التفاصيل والخدمات السعر الموقع والصور
{step === 1 && (

معلومات العقار

اختر نوع العقار والحالة

{propertyTypes.map((type) => { const Icon = type.icon; return ( ); })}
{errors.propertyType && (

{errors.propertyType}

)}