Files
SweetHomeWeb/app/owner/properties/add/page.js
2026-07-16 10:56:59 -07:00

1847 lines
75 KiB
JavaScript

'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';
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: '',
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 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 || '',
},
};
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) : '',
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: formData.city,
};
try {
if (purpose === 'sale') {
const payload = {
propInfo,
price: parseFloat(formData.salePrice) || 0,
currencyId: selectedCurrencyId,
};
const res = await addSaleProperty(payload);
toast.success(t('toast.salePropertySuccess'));
} else {
const rentTypeMap = { daily: RentType.DAILY, monthly: RentType.MONTHLY, both: RentType.MONTHLY };
const payload = buildRentPropertyPayload({
propertyInformation: propInfo,
city: formData.city,
governorate: 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 (
<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">
{isUploadingImages && (
<div className="mb-4 rounded-2xl border border-amber-200 bg-amber-50 px-4 py-3 text-sm text-amber-800 flex items-center justify-center gap-2">
<Loader2 className="w-4 h-4 animate-spin" />
<span>{t('addProperty.uploadingImagesMessage') || 'جاري رفع الصور... يرجى الانتظار'}</span>
</div>
)}
<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>{t('addProperty.backToProperties')}</span>
</Link>
<div className="flex items-center gap-2">
<span className="text-sm font-medium text-amber-600">{t('addProperty.stepCounter', { step, total: 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>{t('addProperty.stepInfo')}</span>
<span>{t('addProperty.stepDetails')}</span>
<span>{purpose === 'sale' ? t('addProperty.stepSalePrice') : t('addProperty.stepRentPrice')}</span>
<span>{t('addProperty.stepLocation')}</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">{t('addProperty.stepInfo')}</h2>
<p className="text-gray-600">{t('addProperty.propertyInfoSubtitle')}</p>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-3">
{t('addProperty.propertyTypeLabel')} <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">
{t('addProperty.furnishedStatus')}
</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">{t('addProperty.furnished')}</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">{t('addProperty.unfurnished')}</span>
</label>
</div>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
{t('addProperty.documentTypeLabel')} <span className="text-red-500">*</span>
</label>
<select
value={formData.documentType}
onChange={(e) => setFormData({...formData, documentType: e.target.value})}
className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500"
>
{DocumentTypeList.map((type) => (
<option key={type} value={type}>
{getDocumentTypeLabel(type)}
</option>
))}
</select>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
{t('addProperty.additionalDescription')}
</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={t('addProperty.descriptionPlaceholder')}
/>
</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">{t('addProperty.propertyDetailsTitle')}</h2>
<p className="text-gray-600">{t('addProperty.propertyDetailsSubtitle')}</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-4 gap-6">
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
{t('addProperty.space')}
</label>
<div className="relative">
<Square className="absolute right-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-gray-400" />
<input
type="number"
min="0"
value={formData.space}
onChange={(e) => setFormData({...formData, space: e.target.value})}
className="w-full pr-10 pl-3 py-3 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500"
placeholder={t('addProperty.spacePlaceholder')}
/>
</div>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
{t('addProperty.bedrooms')} <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">
{t('addProperty.bathrooms')} <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">
{t('addProperty.livingRooms')} <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 className="grid grid-cols-1 md:grid-cols-3 gap-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">{t('addProperty.floorNumber')}</label>
<input
type="number"
value={formData.floorNumber}
onChange={(e) => setFormData({...formData, floorNumber: e.target.value})}
className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500"
placeholder={t('addProperty.floorPlaceholder')}
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">{t('addProperty.salons')}</label>
<input
type="number"
min="0"
value={formData.salons}
onChange={(e) => setFormData({...formData, salons: e.target.value})}
className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500"
placeholder={t('addProperty.salonsPlaceholder')}
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">{t('addProperty.balconies')}</label>
<input
type="number"
min="0"
value={formData.balconies}
onChange={(e) => setFormData({...formData, balconies: e.target.value})}
className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500"
placeholder={t('addProperty.balconiesPlaceholder')}
/>
</div>
</div>
<div>
<h3 className="text-lg font-bold text-gray-900 mb-4">{t('addProperty.servicesLabel')} <span className="text-red-500">*</span></h3>
<div className="space-y-3">
{serviceList.map((service) => {
const Icon = service.icon;
const isSelected = formData.services[service.id];
return (
<div key={service.id} className={`border rounded-xl transition-all ${isSelected ? 'border-amber-500 bg-amber-50' : 'border-gray-200'}`}>
<label className="flex items-center gap-3 p-3 cursor-pointer">
<input
type="checkbox"
checked={isSelected}
onChange={() => toggleService(service.id)}
className="w-4 h-4 text-amber-500 rounded"
/>
<Icon className={`w-5 h-5 ${isSelected ? 'text-amber-600' : 'text-gray-400'}`} />
<span className={`text-sm font-medium ${isSelected ? 'text-amber-700' : 'text-gray-600'}`}>
{service.label}
</span>
</label>
{isSelected && (
<div className="px-3 pb-3">
<input
type="text"
value={formData.serviceDetails[service.id] || ''}
onChange={(e) => updateServiceDetail(service.id, e.target.value)}
className="w-full px-3 py-2 border border-gray-200 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-amber-500"
placeholder={t('addProperty.serviceDetailPlaceholder')}
/>
</div>
)}
</div>
);
})}
</div>
</div>
<div className="bg-white rounded-xl p-6 border border-gray-200 mt-6">
<h3 className="text-lg font-bold text-gray-800 mb-4 flex items-center gap-2">
<MapPin className="w-5 h-5 text-amber-600" />
{t('addProperty.nearbyServicesTitle')}
</h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">{t('addProperty.nearbySchoolLabel')}</label>
<input
type="number"
min="0"
step="0.1"
value={formData.nearbySchool}
onChange={(e) => setFormData({...formData, nearbySchool: e.target.value})}
className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500"
placeholder={t('addProperty.nearbyPlaceholder')}
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">{t('addProperty.nearbyHospitalLabel')}</label>
<input
type="number"
min="0"
step="0.1"
value={formData.nearbyHospital}
onChange={(e) => setFormData({...formData, nearbyHospital: e.target.value})}
className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500"
placeholder={t('addProperty.nearbyPlaceholder')}
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">{t('addProperty.nearbyRestaurantLabel')}</label>
<input
type="number"
min="0"
step="0.1"
value={formData.nearbyRestaurant}
onChange={(e) => setFormData({...formData, nearbyRestaurant: e.target.value})}
className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500"
placeholder={t('addProperty.nearbyPlaceholder')}
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">{t('addProperty.nearbyUniversityLabel')}</label>
<input
type="number"
min="0"
step="0.1"
value={formData.nearbyUniversity}
onChange={(e) => setFormData({...formData, nearbyUniversity: e.target.value})}
className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500"
placeholder={t('addProperty.nearbyPlaceholder')}
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">{t('addProperty.nearbyParkLabel')}</label>
<input
type="number"
min="0"
step="0.1"
value={formData.nearbyPark}
onChange={(e) => setFormData({...formData, nearbyPark: e.target.value})}
className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500"
placeholder={t('addProperty.nearbyPlaceholder')}
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">{t('addProperty.nearbyMallLabel')}</label>
<input
type="number"
min="0"
step="0.1"
value={formData.nearbyMall}
onChange={(e) => setFormData({...formData, nearbyMall: e.target.value})}
className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500"
placeholder={t('addProperty.nearbyPlaceholder')}
/>
</div>
</div>
</div>
{purpose === 'rent' && (
<div>
<h3 className="text-lg font-bold text-gray-900 mb-4">{t('addProperty.termsTitle')}</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 className="mt-4 p-4 border border-dashed border-gray-300 rounded-xl">
<p className="text-sm font-medium text-gray-700 mb-2">{t('addProperty.customTermsTitle')}</p>
<div className="flex gap-2">
<input
type="text"
value={customTermInput}
onChange={(e) => setCustomTermInput(e.target.value)}
onKeyDown={(e) => { if (e.key === 'Enter') { e.preventDefault(); addCustomTerm(); } }}
placeholder={t('addProperty.customTermPlaceholder')}
className="flex-1 px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-amber-500 focus:border-transparent outline-none"
/>
<button
type="button"
onClick={addCustomTerm}
disabled={!customTermInput.trim()}
className="px-4 py-2 bg-amber-500 text-white rounded-lg text-sm font-medium hover:bg-amber-600 disabled:bg-gray-300 disabled:cursor-not-allowed transition-colors"
>
{t('addProperty.add')}
</button>
</div>
{customTerms.length > 0 && (
<div className="flex flex-wrap gap-2 mt-3">
{customTerms.map((term) => (
<span
key={term}
className="inline-flex items-center gap-1 px-3 py-1 bg-amber-100 text-amber-800 rounded-full text-sm"
>
{term}
<button
type="button"
onClick={() => removeCustomTerm(term)}
className="hover:text-red-600 transition-colors"
>
<X className="w-3.5 h-3.5" />
</button>
</span>
))}
</div>
)}
</div>
</div>
)}
</motion.div>
)}
{step === 3 && purpose === 'sale' && (
<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">{t('addProperty.salePriceTitle')}</h2>
<p className="text-gray-600">{t('addProperty.salePriceSubtitle')}</p>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
{t('addProperty.salePriceLabel')} <span className="text-red-500">*</span>
</label>
<div className="relative">
<span className="absolute right-3 top-1/2 transform -translate-y-1/2 w-5 h-5 text-gray-400 font-bold text-sm flex items-center justify-center">{currencySymbol}</span>
<input
type="number"
min="0"
value={formData.salePrice || ''}
onChange={(e) => {
const val = e.target.value;
if (val === '' || parseFloat(val) >= 0) {
setFormData({...formData, salePrice: val});
}
}}
onKeyDown={(e) => { if (['-', 'e', 'E', '+'].includes(e.key)) e.preventDefault() }}
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={t('addProperty.salePricePlaceholder')}
/>
</div>
{errors.salePrice && <p className="text-red-500 text-sm mt-1">{errors.salePrice}</p>}
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
{t('addProperty.currencyLabel')} <span className="text-red-500">*</span>
</label>
<select
value={selectedCurrencyId}
onChange={(e) => setSelectedCurrencyId(parseInt(e.target.value))}
className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500"
>
<option value={Currency.USD}>{t('addProperty.usdOption')}</option>
<option value={Currency.SYP}>{t('addProperty.sypOption')}</option>
</select>
</div>
</motion.div>
)}
{step === 3 && purpose === 'rent' && (
<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">{t('addProperty.offerAndPriceTitle')}</h2>
<p className="text-gray-600">{t('addProperty.offerAndPriceSubtitle')}</p>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-3">
{t('addProperty.offerTypeLabel')} <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>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
{t('addProperty.currencyLabel')} <span className="text-red-500">*</span>
</label>
<select
value={selectedCurrencyId}
onChange={(e) => setSelectedCurrencyId(parseInt(e.target.value))}
className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500"
>
<option value={Currency.USD}>{t('addProperty.usdOption')}</option>
<option value={Currency.SYP}>{t('addProperty.sypOption')}</option>
</select>
</div>
{errors.offerType && (
<p className="text-red-500 text-sm mt-1">{errors.offerType}</p>
)}
{!formData.offerType && (
<div className="border-2 border-dashed border-gray-200 rounded-2xl p-8 text-center">
<DollarSign className="w-8 h-8 text-gray-300 mx-auto mb-3" />
<p className="text-gray-400 font-medium">{t('addProperty.selectOfferTypeFirst')}</p>
</div>
)}
<AnimatePresence mode="wait">
{formData.offerType === 'daily' && (
<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">
{t('addProperty.dailyPriceLabel')} ({currencySymbol}) <span className="text-red-500">*</span>
</label>
<div className="relative">
<span className="absolute right-3 top-1/2 transform -translate-y-1/2 w-5 h-5 text-gray-400 font-bold text-sm flex items-center justify-center">{currencySymbol}</span>
<input
type="number"
min="0"
value={formData.dailyPrice}
onChange={(e) => {
const val = e.target.value;
if (val === '' || parseFloat(val) >= 0) {
setFormData({...formData, dailyPrice: val});
}
}}
onKeyDown={(e) => { if (['-', 'e', 'E', '+'].includes(e.key)) e.preventDefault() }}
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={t('addProperty.dailyPricePlaceholder')}
/>
</div>
{errors.dailyPrice && (
<p className="text-red-500 text-sm mt-1">{errors.dailyPrice}</p>
)}
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
{t('addProperty.depositLabel')} ({currencySymbol})
</label>
<div className="relative">
<span className="absolute right-3 top-1/2 transform -translate-y-1/2 w-5 h-5 text-gray-400 font-bold text-sm flex items-center justify-center">{currencySymbol}</span>
<input
type="number"
min="0"
value={formData.deposit || ''}
onChange={(e) => {
const val = e.target.value;
if (val === '' || parseFloat(val) >= 0) {
setFormData({...formData, deposit: val});
}
}}
onKeyDown={(e) => { if (['-', 'e', 'E', '+'].includes(e.key)) e.preventDefault() }}
className="w-full pr-12 pl-4 py-3 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500"
placeholder={t('addProperty.depositPlaceholder')}
/>
</div>
</div>
</motion.div>
)}
{formData.offerType === 'monthly' && (
<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">
{t('addProperty.monthlyPriceLabel')} ({currencySymbol}) <span className="text-red-500">*</span>
</label>
<div className="relative">
<span className="absolute right-3 top-1/2 transform -translate-y-1/2 w-5 h-5 text-gray-400 font-bold text-sm flex items-center justify-center">{currencySymbol}</span>
<input
type="number"
min="0"
value={formData.monthlyPrice}
onChange={(e) => {
const val = e.target.value;
if (val === '' || parseFloat(val) >= 0) {
setFormData({...formData, monthlyPrice: val});
}
}}
onKeyDown={(e) => { if (['-', 'e', 'E', '+'].includes(e.key)) e.preventDefault() }}
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={t('addProperty.monthlyPricePlaceholder')}
/>
</div>
{errors.monthlyPrice && (
<p className="text-red-500 text-sm mt-1">{errors.monthlyPrice}</p>
)}
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
{t('addProperty.depositLabel')} ({currencySymbol})
</label>
<div className="relative">
<span className="absolute right-3 top-1/2 transform -translate-y-1/2 w-5 h-5 text-gray-400 font-bold text-sm flex items-center justify-center">{currencySymbol}</span>
<input
type="number"
min="0"
value={formData.deposit || ''}
onChange={(e) => {
const val = e.target.value;
if (val === '' || parseFloat(val) >= 0) {
setFormData({...formData, deposit: val});
}
}}
onKeyDown={(e) => { if (['-', 'e', 'E', '+'].includes(e.key)) e.preventDefault() }}
className="w-full pr-12 pl-4 py-3 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500"
placeholder={t('addProperty.depositPlaceholder')}
/>
</div>
</div>
</motion.div>
)}
{formData.offerType === 'both' && (
<motion.div
key="both"
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">
{t('addProperty.dailyPriceLabel')} ({currencySymbol}) <span className="text-red-500">*</span>
</label>
<div className="relative">
<span className="absolute right-3 top-1/2 transform -translate-y-1/2 w-5 h-5 text-gray-400 font-bold text-sm flex items-center justify-center">{currencySymbol}</span>
<input
type="number"
min="0"
value={formData.dailyPrice}
onChange={(e) => {
const val = e.target.value;
if (val === '' || parseFloat(val) >= 0) {
setFormData({...formData, dailyPrice: val});
}
}}
onKeyDown={(e) => { if (['-', 'e', 'E', '+'].includes(e.key)) e.preventDefault() }}
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={t('addProperty.dailyPricePlaceholder')}
/>
</div>
{errors.dailyPrice && (
<p className="text-red-500 text-sm mt-1">{errors.dailyPrice}</p>
)}
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
{t('addProperty.monthlyPriceLabel')} ({currencySymbol}) <span className="text-red-500">*</span>
</label>
<div className="relative">
<span className="absolute right-3 top-1/2 transform -translate-y-1/2 w-5 h-5 text-gray-400 font-bold text-sm flex items-center justify-center">{currencySymbol}</span>
<input
type="number"
min="0"
value={formData.monthlyPrice}
onChange={(e) => {
const val = e.target.value;
if (val === '' || parseFloat(val) >= 0) {
setFormData({...formData, monthlyPrice: val});
}
}}
onKeyDown={(e) => { if (['-', 'e', 'E', '+'].includes(e.key)) e.preventDefault() }}
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={t('addProperty.monthlyPricePlaceholder')}
/>
</div>
{errors.monthlyPrice && (
<p className="text-red-500 text-sm mt-1">{errors.monthlyPrice}</p>
)}
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
{t('addProperty.depositLabel')} ({currencySymbol})
</label>
<div className="relative">
<span className="absolute right-3 top-1/2 transform -translate-y-1/2 w-5 h-5 text-gray-400 font-bold text-sm flex items-center justify-center">{currencySymbol}</span>
<input
type="number"
min="0"
value={formData.deposit || ''}
onChange={(e) => {
const val = e.target.value;
if (val === '' || parseFloat(val) >= 0) {
setFormData({...formData, deposit: val});
}
}}
onKeyDown={(e) => { if (['-', 'e', 'E', '+'].includes(e.key)) e.preventDefault() }}
className="w-full pr-12 pl-4 py-3 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500"
placeholder={t('addProperty.depositPlaceholder')}
/>
</div>
</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">{t('addProperty.locationAndImagesTitle')}</h2>
<p className="text-gray-600">{t('addProperty.locationAndImagesSubtitle')}</p>
</div>
<div className="rounded-3xl border border-amber-100 bg-linear-to-br from-amber-50 via-white to-orange-50 p-4 sm:p-5 md:p-6 shadow-sm">
<div className="flex flex-col gap-3 md:flex-row md:items-start md:justify-between mb-5">
<div>
<div className="flex items-center gap-2 text-amber-700 font-semibold mb-1">
<Navigation className="w-4 h-4" />
{t('addProperty.selectLocationTitle')}
</div>
<p className="text-sm text-gray-600">{t('addProperty.clickMapHint')}</p>
</div>
<div className="inline-flex items-center gap-2 rounded-full border border-amber-200 bg-white px-3 py-1.5 text-sm font-medium text-amber-700 shadow-sm">
<MapPin className="w-4 h-4" />
{t('addProperty.cityLabel')}
</div>
</div>
<div className="grid grid-cols-1 xl:grid-cols-[minmax(220px,260px)_1fr] gap-4 mb-5">
<div className="rounded-2xl border border-white/80 bg-white/90 p-4 shadow-sm">
<label className="block text-sm font-medium text-gray-700 mb-2">
{t('addProperty.cityLabel')} <span className="text-red-500">*</span>
</label>
<select
value={formData.city}
onChange={(e) => setFormData({...formData, city: e.target.value})}
className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500 bg-white"
>
{GovernorateList.map((city) => (
<option key={city} value={city}>
{GovernorateLabels[city] || city}
</option>
))}
</select>
<p className="text-xs text-gray-500 mt-2">{t('addProperty.searchHint')}</p>
</div>
<div className="rounded-2xl border border-white/80 bg-white/90 p-4 shadow-sm">
<label className="block text-sm font-medium text-gray-700 mb-2">
{t('addProperty.searchAddress')}
</label>
<div className="flex flex-col sm:flex-row gap-2">
<div className="relative flex-1">
<input
type="text"
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
onKeyPress={(e) => e.key === 'Enter' && handleSearch()}
placeholder={t('addProperty.searchAddress')}
className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500 pr-10"
/>
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 w-5 h-5 text-gray-400" />
</div>
<button
onClick={handleSearch}
className="px-5 py-3 bg-amber-500 text-white rounded-xl hover:bg-amber-600 transition-colors font-medium flex items-center justify-center gap-2"
>
<Search className="w-4 h-4" />
{t('addProperty.searchButton')}
</button>
</div>
<div className="mt-3 flex items-start gap-2 text-sm text-amber-700">
<Info className="w-4 h-4 mt-0.5 shrink-0" />
<span>{t('addProperty.clickMapHint')}</span>
</div>
</div>
</div>
<div className="relative w-full h-96 rounded-2xl overflow-hidden border border-gray-200 shadow-inner">
<AddPropertyMap
mapCenter={mapCenter}
mapZoom={mapZoom}
selectedLocation={selectedLocation}
onMapClick={handleMapClick}
onMarkerDragEnd={handleMarkerDragEnd}
/>
</div>
<div className="mt-4 flex flex-col gap-3 md:flex-row md:items-center md:justify-between">
{selectedLocation && !formData.lat ? (
<button
onClick={confirmLocation}
className="w-full md:w-auto bg-green-600 text-white py-3 px-5 rounded-xl font-medium hover:bg-green-700 transition-colors flex items-center justify-center gap-2 shadow-sm"
>
<CheckCircle className="w-5 h-5" />
{t('addProperty.confirmLocation')}
</button>
) : (
<div className="rounded-2xl border border-amber-100 bg-amber-50 px-4 py-3 text-sm text-amber-800 flex items-start gap-2">
<Info className="w-4 h-4 mt-0.5 shrink-0" />
<span>{t('addProperty.clickMapHint')}</span>
</div>
)}
{formData.lat && (
<div className="flex-1 rounded-2xl border border-green-200 bg-green-50 p-4">
<div className="flex items-center gap-2">
<CheckCircle className="w-5 h-5 text-green-600" />
<span className="text-green-800 font-medium">{t('addProperty.locationConfirmed')}:</span>
</div>
<p className="text-green-700 text-sm mt-2 line-clamp-2">{formData.address}</p>
</div>
)}
</div>
{errors.location && (
<p className="text-red-500 text-sm text-center mt-4">{errors.location}</p>
)}
</div>
<div>
<h3 className="text-lg font-bold text-gray-900 mb-4">{t('addProperty.imagesTitle')}</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">{t('addProperty.uploadImages')}</p>
<p className="text-xs text-gray-500 mt-2">
{t('addProperty.imageFormatHint')}
</p>
</div>
<p className="text-sm text-gray-500 mt-2 mb-4 text-center">
{t('addProperty.uploadMultipleHintLine1')}
<br />
{t('addProperty.uploadMultipleHintLine2')}
</p>
{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>
{step === 4 && (
<div className="border-t border-gray-200 pt-6 mt-8">
<div className="bg-gray-50 rounded-2xl p-5 border border-gray-200">
<p className="text-sm text-gray-700 leading-relaxed mb-4">
{t('addProperty.ownerAgreement')}
</p>
<label className="flex items-start gap-3 cursor-pointer">
<input
type="checkbox"
checked={agreeToTerms}
onChange={(e) => setAgreeToTerms(e.target.checked)}
className="w-5 h-5 mt-0.5 text-amber-500 rounded shrink-0"
/>
<span className="text-sm text-gray-600">
{t('addProperty.agreeToText')}{' '}
<Link href="/terms" target="_blank" className="text-amber-600 underline hover:text-amber-700">
{t('addProperty.termsOfUseText')}
</Link>
</span>
</label>
</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" />
{t('addProperty.previous')}
</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' : ''
}`}
>
{t('addProperty.next')}
<ChevronLeft className="w-5 h-5" />
</button>
) : (
<button
onClick={handleSubmit}
disabled={isLoading || !agreeToTerms}
className="flex-1 py-3 px-4 bg-linear-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" />
{t('addProperty.saving')}
</>
) : (
<>
<Save className="w-5 h-5" />
{t('addProperty.saveProperty')}
</>
)}
</button>
)}
</div>
</motion.div>
</div>
</div>
);
}