"use client"; import { useState, useRef, useEffect } from "react"; import { motion, AnimatePresence } from "framer-motion"; import { useRouter, useSearchParams } from "next/navigation"; import Link from "next/link"; import Image from "next/image"; import "leaflet/dist/leaflet.css"; import { useTranslation } from "react-i18next"; 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, Trees, } from "lucide-react"; import toast, { Toaster } from "react-hot-toast"; import { addRentProperty, addSaleProperty, buildRentPropertyPayload, getCurrencies, uploadPicture } from "../../../utils/api"; import { BuildingType, RentPropertyCondition, RentPropertyType, RentType, PropertyService, PropertyServiceLabels, PropertyServicesList, PropertyTerm, PropertyTermLabels, PropertyTermsList, DocumentType, DocumentTypeList, Governorate, GovernorateLabels, GovernorateList, Currency, CurrencyLabels, } from "../../../enums"; import dynamic from "next/dynamic"; import { CityEnum, GovernorateToBackendCity } from "@/app/enums/City"; const AddPropertyMap = dynamic(() => import("@/app/components/property/AddPropertyMap"), { ssr: false }); export default function AddPropertyPage() { const router = useRouter(); const searchParams = useSearchParams(); const purpose = searchParams.get("purpose") || "rent"; const { t } = useTranslation(); const [step, setStep] = useState(1); const totalSteps = purpose === "sale" ? 4 : 4; const [formData, setFormData] = useState({ propertyType: "apartment", furnished: false, documentType: DocumentType.PASSPORT, bedrooms: 1, bathrooms: 1, livingRooms: 1, floorNumber: "", salons: "", balconies: "", space: "", 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: "", dailyPrice: "", monthlyPrice: "", deposit: "", allowedPaymentPeriod: "", city: Governorate.DAMASCUS, district: "", address: "", lat: null, lng: null, description: "", images: [], nearbySchool: "", nearbyHospital: "", nearbyRestaurant: "", nearbyUniversity: "", nearbyPark: "", nearbyMall: "", neaebymosque: "", roomAreaType: "Private room", roomPeopleAllowed: "", roomFurniture: "", roomEntrance: "Shared entrance", roomBathroom: "Shared", roomKitchen: "Not available", roomRestrictedAreas: false, roomResidents: "", roomGender: "Family", roomLanguage: "", roomChildren: false, roomPets: false, roomDedicatedTo: "Everyone", roomVisitors: true, roomQuietTimes: false, roomQuietTimesDetails: "", }); const [imagePreviews, setImagePreviews] = useState([]); const [uploadedImagePaths, setUploadedImagePaths] = useState([]); const [customTerms, setCustomTerms] = useState([]); const [customTermInput, setCustomTermInput] = useState(""); const [selectedLocation, setSelectedLocation] = useState(null); const [mapCenter, setMapCenter] = useState([33.5138, 36.2765]); const [mapZoom, setMapZoom] = useState(13); const [searchQuery, setSearchQuery] = useState(""); const [currencies, setCurrencies] = useState([]); const [selectedCurrencyId, setSelectedCurrencyId] = useState(Currency.SYP); const [errors, setErrors] = useState({}); const [isLoading, setIsLoading] = useState(false); const [isUploadingImages, setIsUploadingImages] = useState(false); const [agreeToTerms, setAgreeToTerms] = useState(false); const fileInputRef = useRef(null); const propertyTypes = [ { id: "apartment", label: t("buildingType.apartment"), icon: Building }, { id: "villa", label: t("buildingType.villa"), icon: Home }, { id: "sweet", label: t("buildingType.sweet"), icon: Sofa }, { id: "room", label: t("buildingType.room"), icon: DoorOpen }, { id: "studio", label: t("buildingType.studio"), icon: Sofa }, { id: "office", label: t("buildingType.office"), icon: Building }, { id: "farms", label: t("buildingType.farms"), icon: Trees }, { id: "shop", label: t("buildingType.shop"), icon: Warehouse }, { id: "warehouse", label: t("buildingType.warehouse"), icon: Warehouse }, ]; 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: t("dailyRent"), icon: Clock }, { id: "monthly", label: t("monthlyRent"), icon: Calendar }, { id: "both", label: t("addProperty.dailyAndMonthly"), icon: Calendar }, ].filter(Boolean); const getDocumentTypeLabel = (type) => { switch (type) { case DocumentType.PASSPORT: return t("documentType.passport"); case DocumentType.IDCARD: return t("documentType.idCard"); case DocumentType.BOTH: return t("documentType.both"); default: return type; } }; useEffect(() => { getCurrencies() .then((data) => { if (Array.isArray(data) && data.length > 0) { setCurrencies(data); } }) .catch((err) => { console.warn("[AddProperty] Failed to load currencies:", err); }); }, []); const handleSearch = async () => { if (!searchQuery) return; toast.loading(t("toast.searching"), { 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(t("toast.locationFound"), { id: "search" }); } else { toast.error(t("toast.locationNotFound"), { id: "search" }); } } catch (error) { console.error(t("search.error"), error); toast.error(t("search.error"), { id: "search" }); } }; const handleGeolocation = () => { if (!navigator.geolocation) { toast.error(t("toast.geolocationNotSupported")); return; } toast.loading(t("toast.geolocationLoading"), { 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 || t("addProperty.currentLocation"), }); toast.success(t("toast.geolocationSuccess"), { id: "geolocation" }); } catch (error) { setSelectedLocation({ lat: latitude, lng: longitude, address: t("addProperty.currentLocation"), }); toast.success(t("toast.geolocationSuccess"), { id: "geolocation" }); } }, (error) => { toast.error(t("toast.geolocationFailed"), { id: "geolocation" }); }, ); }; const handleMapClick = async (coords) => { try { const [lat, lng] = coords; toast.loading(t("addProperty.locationSelecting"), { 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 || t("addProperty.defaultAddress"), }); setMapZoom(18); toast.success(t("toast.locationSelectedSuccess"), { id: "location" }); } catch (error) { console.error(t("addProperty.locationError"), error); const [lat, lng] = coords; setSelectedLocation({ lat: lat, lng: lng, address: t("addProperty.defaultAddress"), }); setMapZoom(18); toast.success(t("toast.locationConfirmed"), { id: "location" }); } }; const handleMarkerDragEnd = async (lat, lng) => { try { 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, lng, address: data.display_name || t("addProperty.defaultAddress"), }); } catch (error) { setSelectedLocation({ lat, lng, address: t("addProperty.defaultAddress"), }); } }; const confirmLocation = () => { if (selectedLocation) { setFormData({ ...formData, lat: selectedLocation.lat, lng: selectedLocation.lng, address: selectedLocation.address, }); toast.success(t("toast.locationConfirmed")); } }; const resetLocation = () => { setSelectedLocation(null); setFormData({ ...formData, lat: null, lng: null, address: "", }); setMapZoom(15); toast.info(t("toast.clearLocation")); }; const handleImageUpload = async (files) => { const newImages = Array.from(files); if (formData.images.length + newImages.length > 5) { toast.error(t("toast.maxImages")); return; } for (const file of newImages) { if (!file.type.startsWith("image/")) { toast.error(t("toast.imageRequired")); continue; } if (file.size > 5 * 1024 * 1024) { toast.error(t("toast.imageSize")); continue; } const reader = new FileReader(); reader.onloadend = () => { setImagePreviews((prev) => [...prev, reader.result]); }; reader.readAsDataURL(file); setFormData((prev) => ({ ...prev, images: [...prev.images, file], })); try { const path = await uploadPicture(file); setUploadedImagePaths((prev) => [...prev, path]); } catch (err) { console.error("[AddProperty] Image upload failed:", err); toast.error(`${t("toast.imageUploadFailed")}: ${file.name}`); } } }; const removeImage = (index) => { const newImages = [...formData.images]; newImages.splice(index, 1); const newPreviews = [...imagePreviews]; newPreviews.splice(index, 1); const newPaths = [...uploadedImagePaths]; newPaths.splice(index, 1); setFormData((prev) => ({ ...prev, images: newImages })); setImagePreviews(newPreviews); setUploadedImagePaths(newPaths); }; 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 addCustomTerm = () => { const val = customTermInput.trim(); if (!val) return; if (customTerms.includes(val)) return; setCustomTerms((prev) => [...prev, val]); setCustomTermInput(""); }; const removeCustomTerm = (term) => { setCustomTerms((prev) => prev.filter((t) => t !== term)); }; 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 = t("addProperty.propertyTypeRequired"); } break; case 2: if (!formData.bedrooms) { newErrors.bedrooms = t("addProperty.bedroomsRequired"); } if (!formData.bathrooms) { newErrors.bathrooms = t("addProperty.bathroomsRequired"); } if (!formData.livingRooms) { newErrors.livingRooms = t("addProperty.livingRoomsRequired"); } break; case 3: if (purpose === "sale") { if (!formData.salePrice) newErrors.salePrice = t("addProperty.salePriceRequired"); } else { if (!formData.offerType) { newErrors.offerType = t("addProperty.offerTypeRequired"); } else if (formData.offerType === "daily" && !formData.dailyPrice) { newErrors.dailyPrice = t("addProperty.dailyPriceRequired"); } else if (formData.offerType === "monthly" && !formData.monthlyPrice) { newErrors.monthlyPrice = t("addProperty.monthlyPriceRequired"); } else if (formData.offerType === "both") { if (!formData.dailyPrice) newErrors.dailyPrice = t("addProperty.dailyPriceRequired"); if (!formData.monthlyPrice) newErrors.monthlyPrice = t("addProperty.monthlyPriceRequired"); } } break; case 4: if (!formData.lat || !formData.lng) { newErrors.location = t("addProperty.locationRequired"); } if (formData.images.length < 2) { newErrors.images = t("addProperty.minImagesRequired"); } if (formData.images.length > 5) { newErrors.images = t("addProperty.maxImagesError"); } 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 ensureImagesUploaded = async () => { if (formData.images.length === 0) { return []; } setIsUploadingImages(true); const finalPaths = []; for (let index = 0; index < formData.images.length; index += 1) { const file = formData.images[index]; const existingPath = uploadedImagePaths[index]; if (existingPath) { finalPaths.push(existingPath); continue; } try { const path = await uploadPicture(file); finalPaths.push(path); } catch (err) { console.error("[AddProperty] Image upload failed:", err); toast.error(`${t("toast.imageUploadFailed")}: ${file.name}`); setIsUploadingImages(false); return []; } } setUploadedImagePaths(finalPaths); setIsUploadingImages(false); return finalPaths; }; const handleSubmit = async () => { if (!validateStep()) return; setIsLoading(true); const finalImagePaths = await ensureImagesUploaded(); if (finalImagePaths.length < 2) { toast.error(t("addProperty.minImagesRequired")); setIsLoading(false); return; } const GovernorateToBackendCity = { [Governorate.DAMASCUS]: "Damascus", [Governorate.ALEPPO]: "Aleppo", [Governorate.HOMS]: "Homs", [Governorate.LATAKIA]: "Latakia", [Governorate.DARAA]: "Daraa", [Governorate.TARTOUS]: "Tartous", [Governorate.SUWEIDA]: "Suweida", [Governorate.DEIR_EZZOR]: "DeirEzzor", [Governorate.RAQQA]: "Raqqa", [Governorate.IDLIB]: "Idlib", [Governorate.HASAKAH]: "Hasakah", [Governorate.QAMISHLI]: "Qamishli", [Governorate.RURAL_DAMASCUS]: "RuralDamascus", }; const buildingTypeMap = { apartment: BuildingType.APARTMENT, villa: BuildingType.VILLA, sweet: BuildingType.SWEET, suite: BuildingType.SWEET, room: BuildingType.ROOM, studio: BuildingType.STUDIO, office: BuildingType.OFFICE, farms: BuildingType.FARMS, shop: BuildingType.SHOP, warehouse: BuildingType.WAREHOUSE, }; const selectedServices = Object.entries(formData.services) .filter(([, v]) => v) .map(([k]) => k); const selectedTerms = Object.entries(formData.terms) .filter(([, v]) => v) .map(([k]) => k); const allTerms = [...new Set([...selectedTerms, ...customTerms])]; const isRent = purpose === "rent"; const details = { description: formData.description || "", services: selectedServices, serviceDetails: selectedServices.reduce((acc, s) => ({ ...acc, [s]: formData.serviceDetails[s] || "in general" }), {}), propertyCondition: formData.furnished ? "WithFurniture" : "WithoutFurniture", floorNumber: parseInt(formData.floorNumber) || 0, numberOfSalons: parseInt(formData.salons) || 0, numberOfBalconies: parseInt(formData.balconies) || 0, nearbyDistances: { school: formData.nearbySchool || "", hospital: formData.nearbyHospital || "", restaurant: formData.nearbyRestaurant || "", university: formData.nearbyUniversity || "", park: formData.nearbyPark || "", mall: formData.nearbyMall || "", mosque: formData.neaebymosque || "", }, }; if (isRent) { details.terms = allTerms.reduce((acc, k) => ({ ...acc, [k]: true }), {}); details.displayType = formData.offerType === "both" ? "Both" : formData.offerType === "daily" ? "Daily" : "Monthly"; } if (isRent && formData.propertyType === "room") { details.room = { areaType: formData.roomAreaType || "Private room", peopleAllowed: formData.roomPeopleAllowed || String(formData.bedrooms), furnitureDetails: formData.roomFurniture || "", entranceType: formData.roomEntrance || "Shared entrance", bathroomType: formData.roomBathroom || "Shared", kitchenType: formData.roomKitchen || "Not available", hasRestrictedOwnerAreas: formData.roomRestrictedAreas || false, homeResidentsCount: formData.roomResidents || "", currentPopulationGender: formData.roomGender || "Family", languageDialect: formData.roomLanguage || "", hasChildren: formData.roomChildren || false, hasPets: formData.roomPets || false, dedicatedTo: formData.roomDedicatedTo || "Everyone", visitorsAllowed: formData.roomVisitors ?? true, quietTimesEnabled: formData.roomQuietTimes ?? false, quietTimes: formData.roomQuietTimesDetails || "", }; } const detailsJSON = JSON.stringify(details); const propInfo = { cordsX: formData.lat ? String(formData.lat) : "", cordsY: formData.lng ? String(formData.lng) : "", // activityStatus: 1, images: finalImagePaths, address: `${formData.city} - ${formData.district} - ${formData.address}`.trim(), description: formData.description || "", numberOfBathRooms: formData.bathrooms || 0, numberOfRooms: formData.bedrooms || 0, numberOfBedRooms: formData.bedrooms || 0, space: parseFloat(formData.space) || 0, detailsJSON: detailsJSON || "{}", buildingType: buildingTypeMap[formData.propertyType] ?? BuildingType.APARTMENT, status: 0, propertyType: formData.furnished ? RentPropertyCondition.WITH_FURNITURE : RentPropertyCondition.WITHOUT_FURNITURE, documentType: formData.documentType, governorate: formData.city, city: CityEnum[formData.city], }; try { if (purpose === "sale") { const payload = { propInfo, price: parseFloat(formData.salePrice), deposit: parseFloat(formData.deposit) || 0, }; console.log("ff", payload); const res = await addSaleProperty(payload); console.log(res); toast.success(t("toast.salePropertySuccess")); } else { const rentTypeMap = { daily: RentType.DAILY, monthly: RentType.MONTHLY, both: RentType.MONTHLY }; const payload = buildRentPropertyPayload({ propertyInformation: propInfo, governorate: formData.city, city: GovernorateToBackendCity[formData.city] ?? formData.city, documentType: formData.documentType, deposit: parseFloat(formData.deposit) || 0, monthlyRent: parseFloat(formData.monthlyPrice) || 0, dailyRent: parseFloat(formData.dailyPrice) || 0, rating: 1, currencyId: selectedCurrencyId, rentType: rentTypeMap[formData.offerType] ?? RentType.MONTHLY, type: formData.furnished ? RentPropertyType.FURNISHED : RentPropertyType.UNFURNISHED, allowedPaymentPeriod: formData.allowedPaymentPeriod || "1.00:00:00", }); const res = await addRentProperty(payload); toast.success(t("toast.rentPropertySuccess")); } setTimeout(() => { router.push("/owner/properties"); }, 1500); } catch (err) { console.error("[AddProperty] API error:", err); toast.error(err.message || t("toast.propertyAddFailed")); } finally { setIsLoading(false); } }; const fadeInUp = { initial: { opacity: 0, y: 20 }, animate: { opacity: 1, y: 0 }, transition: { duration: 0.5 }, }; const currencySymbol = selectedCurrencyId === Currency.USD ? "$" : "SP"; return (
{isUploadingImages && (
{t("addProperty.uploadingImagesMessage") || "جاري رفع الصور... يرجى الانتظار"}
)}
{t("addProperty.backToProperties")}
{t("addProperty.stepCounter", { step, total: totalSteps })}
{[1, 2, 3, 4].map((s) => ( ))}
{t("addProperty.stepInfo")} {t("addProperty.stepDetails")} {purpose === "sale" ? t("addProperty.stepSalePrice") : t("addProperty.stepRentPrice")} {t("addProperty.stepLocation")}
{step === 1 && (

{t("addProperty.stepInfo")}

{t("addProperty.propertyInfoSubtitle")}

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

{errors.propertyType}

}