346 lines
13 KiB
JavaScript
346 lines
13 KiB
JavaScript
|
|
'use client';
|
||
|
|
|
||
|
|
import { useState } from 'react';
|
||
|
|
import { motion } from 'framer-motion';
|
||
|
|
import { useProperties } from '@/app/contexts/PropertyContext';
|
||
|
|
import { COMMISSION_TYPE, CITIES } from '@/app/utils/constants';
|
||
|
|
import { X, MapPin, Home, DollarSign, Percent } from 'lucide-react';
|
||
|
|
|
||
|
|
export default function AddPropertyForm({ onClose, onSuccess }) {
|
||
|
|
const { addProperty } = useProperties();
|
||
|
|
const [formData, setFormData] = useState({
|
||
|
|
title: '',
|
||
|
|
description: '',
|
||
|
|
city: '',
|
||
|
|
district: '',
|
||
|
|
address: '',
|
||
|
|
latitude: '',
|
||
|
|
longitude: '',
|
||
|
|
|
||
|
|
type: 'apartment',
|
||
|
|
bedrooms: 1,
|
||
|
|
bathrooms: 1,
|
||
|
|
area: 0,
|
||
|
|
floor: 1,
|
||
|
|
|
||
|
|
dailyPrice: 0,
|
||
|
|
commissionRate: 5,
|
||
|
|
commissionType: COMMISSION_TYPE.FROM_OWNER,
|
||
|
|
|
||
|
|
securityDeposit: 0,
|
||
|
|
|
||
|
|
images: [],
|
||
|
|
features: [],
|
||
|
|
|
||
|
|
status: 'available'
|
||
|
|
});
|
||
|
|
|
||
|
|
const [selectedFeatures, setSelectedFeatures] = useState([]);
|
||
|
|
|
||
|
|
const featuresList = [
|
||
|
|
'swimmingPool', 'privateGarden', 'parking', 'superLuxFinish',
|
||
|
|
'equippedKitchen', 'centralHeating', 'balcony', 'securitySystem',
|
||
|
|
'largeGarden', 'receptionHall', 'maidRoom', 'garage',
|
||
|
|
'seaView', 'centralAC', 'fruitGarden', 'storage'
|
||
|
|
];
|
||
|
|
|
||
|
|
const handleSubmit = async (e) => {
|
||
|
|
e.preventDefault();
|
||
|
|
|
||
|
|
const propertyData = {
|
||
|
|
...formData,
|
||
|
|
features: selectedFeatures,
|
||
|
|
priceDisplay: {
|
||
|
|
daily: formData.dailyPrice,
|
||
|
|
monthly: formData.dailyPrice * 30,
|
||
|
|
withCommission: calculateCommissionPrice(formData)
|
||
|
|
},
|
||
|
|
location: {
|
||
|
|
lat: formData.latitude,
|
||
|
|
lng: formData.longitude,
|
||
|
|
address: formData.address
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
try {
|
||
|
|
await addProperty(propertyData);
|
||
|
|
onSuccess?.();
|
||
|
|
onClose();
|
||
|
|
} catch (error) {
|
||
|
|
console.error('Error adding property:', error);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
const calculateCommissionPrice = (data) => {
|
||
|
|
const { dailyPrice, commissionRate, commissionType } = data;
|
||
|
|
const commission = (dailyPrice * commissionRate) / 100;
|
||
|
|
|
||
|
|
switch(commissionType) {
|
||
|
|
case COMMISSION_TYPE.FROM_TENANT:
|
||
|
|
return dailyPrice + commission;
|
||
|
|
case COMMISSION_TYPE.FROM_OWNER:
|
||
|
|
return dailyPrice;
|
||
|
|
case COMMISSION_TYPE.FROM_BOTH:
|
||
|
|
return dailyPrice + (commission / 2);
|
||
|
|
default:
|
||
|
|
return dailyPrice;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
return (
|
||
|
|
<motion.div
|
||
|
|
initial={{ opacity: 0 }}
|
||
|
|
animate={{ opacity: 1 }}
|
||
|
|
className="fixed inset-0 bg-black/50 flex items-center justify-center p-4 z-50"
|
||
|
|
>
|
||
|
|
<motion.div
|
||
|
|
initial={{ scale: 0.9, y: 20 }}
|
||
|
|
animate={{ scale: 1, y: 0 }}
|
||
|
|
className="bg-white rounded-xl w-full max-w-4xl max-h-[90vh] overflow-y-auto"
|
||
|
|
>
|
||
|
|
<div className="sticky top-0 bg-white border-b p-4 flex justify-between items-center">
|
||
|
|
<h2 className="text-xl font-bold">إضافة عقار جديد</h2>
|
||
|
|
<button onClick={onClose} className="p-2 hover:bg-gray-100 rounded-lg">
|
||
|
|
<X className="w-5 h-5" />
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<form onSubmit={handleSubmit} className="p-6 space-y-6">
|
||
|
|
<div className="bg-blue-50 p-4 rounded-lg">
|
||
|
|
<h3 className="font-semibold mb-3 flex items-center gap-2">
|
||
|
|
<MapPin className="w-4 h-4" />
|
||
|
|
موقع العقار (سيظهر على الخريطة)
|
||
|
|
</h3>
|
||
|
|
<div className="grid grid-cols-2 gap-4">
|
||
|
|
<div>
|
||
|
|
<label className="block text-sm font-medium mb-1">المدينة</label>
|
||
|
|
<select
|
||
|
|
value={formData.city}
|
||
|
|
onChange={(e) => setFormData({...formData, city: e.target.value})}
|
||
|
|
className="w-full p-2 border rounded-lg"
|
||
|
|
required
|
||
|
|
>
|
||
|
|
<option value="">اختر المدينة</option>
|
||
|
|
{Object.values(CITIES).map(city => (
|
||
|
|
<option key={city} value={city}>{city}</option>
|
||
|
|
))}
|
||
|
|
</select>
|
||
|
|
</div>
|
||
|
|
<div>
|
||
|
|
<label className="block text-sm font-medium mb-1">الحي</label>
|
||
|
|
<input
|
||
|
|
type="text"
|
||
|
|
value={formData.district}
|
||
|
|
onChange={(e) => setFormData({...formData, district: e.target.value})}
|
||
|
|
className="w-full p-2 border rounded-lg"
|
||
|
|
required
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
<div className="col-span-2">
|
||
|
|
<label className="block text-sm font-medium mb-1">العنوان بالتفصيل</label>
|
||
|
|
<input
|
||
|
|
type="text"
|
||
|
|
value={formData.address}
|
||
|
|
onChange={(e) => setFormData({...formData, address: e.target.value})}
|
||
|
|
className="w-full p-2 border rounded-lg"
|
||
|
|
required
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
<div>
|
||
|
|
<label className="block text-sm font-medium mb-1">خط العرض (Latitude)</label>
|
||
|
|
<input
|
||
|
|
type="number"
|
||
|
|
step="any"
|
||
|
|
value={formData.latitude}
|
||
|
|
onChange={(e) => setFormData({...formData, latitude: e.target.value})}
|
||
|
|
className="w-full p-2 border rounded-lg"
|
||
|
|
required
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
<div>
|
||
|
|
<label className="block text-sm font-medium mb-1">خط الطول (Longitude)</label>
|
||
|
|
<input
|
||
|
|
type="number"
|
||
|
|
step="any"
|
||
|
|
value={formData.longitude}
|
||
|
|
onChange={(e) => setFormData({...formData, longitude: e.target.value})}
|
||
|
|
className="w-full p-2 border rounded-lg"
|
||
|
|
required
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div className="bg-amber-50 p-4 rounded-lg">
|
||
|
|
<h3 className="font-semibold mb-3 flex items-center gap-2">
|
||
|
|
<DollarSign className="w-4 h-4" />
|
||
|
|
السعر ونسبة الربح
|
||
|
|
</h3>
|
||
|
|
<div className="grid grid-cols-2 gap-4">
|
||
|
|
<div>
|
||
|
|
<label className="block text-sm font-medium mb-1">
|
||
|
|
السعر اليومي (ل.س)
|
||
|
|
</label>
|
||
|
|
<input
|
||
|
|
type="number"
|
||
|
|
value={formData.dailyPrice}
|
||
|
|
onChange={(e) => setFormData({...formData, dailyPrice: Number(e.target.value)})}
|
||
|
|
className="w-full p-2 border rounded-lg"
|
||
|
|
required
|
||
|
|
min="0"
|
||
|
|
/>
|
||
|
|
<p className="text-xs text-gray-500 mt-1">
|
||
|
|
هذا السعر سيظهر على الخريطة
|
||
|
|
</p>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div>
|
||
|
|
<label className="block text-sm font-medium mb-1">
|
||
|
|
نسبة ربح المنصة (%)
|
||
|
|
</label>
|
||
|
|
<div className="flex items-center gap-2">
|
||
|
|
<input
|
||
|
|
type="number"
|
||
|
|
value={formData.commissionRate}
|
||
|
|
onChange={(e) => setFormData({...formData, commissionRate: Number(e.target.value)})}
|
||
|
|
className="w-full p-2 border rounded-lg"
|
||
|
|
min="0"
|
||
|
|
max="100"
|
||
|
|
step="0.1"
|
||
|
|
required
|
||
|
|
/>
|
||
|
|
<Percent className="w-4 h-4 text-gray-400" />
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div className="col-span-2">
|
||
|
|
<label className="block text-sm font-medium mb-2">
|
||
|
|
مصدر العمولة (بموافقة الأدمن)
|
||
|
|
</label>
|
||
|
|
<div className="grid grid-cols-3 gap-3">
|
||
|
|
<label className="flex items-center gap-2 p-3 border rounded-lg cursor-pointer hover:bg-gray-50">
|
||
|
|
<input
|
||
|
|
type="radio"
|
||
|
|
name="commissionType"
|
||
|
|
value={COMMISSION_TYPE.FROM_OWNER}
|
||
|
|
checked={formData.commissionType === COMMISSION_TYPE.FROM_OWNER}
|
||
|
|
onChange={(e) => setFormData({...formData, commissionType: e.target.value})}
|
||
|
|
/>
|
||
|
|
<span>من المالك</span>
|
||
|
|
</label>
|
||
|
|
<label className="flex items-center gap-2 p-3 border rounded-lg cursor-pointer hover:bg-gray-50">
|
||
|
|
<input
|
||
|
|
type="radio"
|
||
|
|
name="commissionType"
|
||
|
|
value={COMMISSION_TYPE.FROM_TENANT}
|
||
|
|
checked={formData.commissionType === COMMISSION_TYPE.FROM_TENANT}
|
||
|
|
onChange={(e) => setFormData({...formData, commissionType: e.target.value})}
|
||
|
|
/>
|
||
|
|
<span>من المستأجر</span>
|
||
|
|
</label>
|
||
|
|
<label className="flex items-center gap-2 p-3 border rounded-lg cursor-pointer hover:bg-gray-50">
|
||
|
|
<input
|
||
|
|
type="radio"
|
||
|
|
name="commissionType"
|
||
|
|
value={COMMISSION_TYPE.FROM_BOTH}
|
||
|
|
checked={formData.commissionType === COMMISSION_TYPE.FROM_BOTH}
|
||
|
|
onChange={(e) => setFormData({...formData, commissionType: e.target.value})}
|
||
|
|
/>
|
||
|
|
<span>من الاثنين</span>
|
||
|
|
</label>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div className="col-span-2 bg-white p-3 rounded-lg">
|
||
|
|
<h4 className="font-medium mb-2">تفاصيل السعر بعد العمولة:</h4>
|
||
|
|
<div className="grid grid-cols-3 gap-4 text-sm">
|
||
|
|
<div>
|
||
|
|
<span className="text-gray-600">السعر الأصلي:</span>
|
||
|
|
<span className="block font-bold">{formData.dailyPrice} ل.س</span>
|
||
|
|
</div>
|
||
|
|
<div>
|
||
|
|
<span className="text-gray-600">العمولة:</span>
|
||
|
|
<span className="block font-bold">
|
||
|
|
{(formData.dailyPrice * formData.commissionRate / 100)} ل.س
|
||
|
|
</span>
|
||
|
|
</div>
|
||
|
|
<div>
|
||
|
|
<span className="text-gray-600">السعر النهائي:</span>
|
||
|
|
<span className="block font-bold text-green-600">
|
||
|
|
{calculateCommissionPrice(formData)} ل.س
|
||
|
|
</span>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div className="grid grid-cols-2 gap-4">
|
||
|
|
<div>
|
||
|
|
<label className="block text-sm font-medium mb-1">نوع العقار</label>
|
||
|
|
<select
|
||
|
|
value={formData.type}
|
||
|
|
onChange={(e) => setFormData({...formData, type: e.target.value})}
|
||
|
|
className="w-full p-2 border rounded-lg"
|
||
|
|
>
|
||
|
|
<option value="apartment">شقة</option>
|
||
|
|
<option value="house">بيت</option>
|
||
|
|
<option value="villa">فيلا</option>
|
||
|
|
<option value="studio">استوديو</option>
|
||
|
|
</select>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div>
|
||
|
|
<label className="block text-sm font-medium mb-1">مبلغ الضمان (ل.س)</label>
|
||
|
|
<input
|
||
|
|
type="number"
|
||
|
|
value={formData.securityDeposit}
|
||
|
|
onChange={(e) => setFormData({...formData, securityDeposit: Number(e.target.value)})}
|
||
|
|
className="w-full p-2 border rounded-lg"
|
||
|
|
min="0"
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div>
|
||
|
|
<label className="block text-sm font-medium mb-2">المميزات</label>
|
||
|
|
<div className="grid grid-cols-3 gap-2">
|
||
|
|
{featuresList.map(feature => (
|
||
|
|
<label key={feature} className="flex items-center gap-2 p-2 border rounded-lg">
|
||
|
|
<input
|
||
|
|
type="checkbox"
|
||
|
|
checked={selectedFeatures.includes(feature)}
|
||
|
|
onChange={(e) => {
|
||
|
|
if (e.target.checked) {
|
||
|
|
setSelectedFeatures([...selectedFeatures, feature]);
|
||
|
|
} else {
|
||
|
|
setSelectedFeatures(selectedFeatures.filter(f => f !== feature));
|
||
|
|
}
|
||
|
|
}}
|
||
|
|
/>
|
||
|
|
<span className="text-sm">{feature}</span>
|
||
|
|
</label>
|
||
|
|
))}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div className="flex gap-3 pt-4 border-t">
|
||
|
|
<button
|
||
|
|
type="submit"
|
||
|
|
className="flex-1 bg-blue-600 text-white py-3 rounded-lg font-semibold hover:bg-blue-700 transition-colors"
|
||
|
|
>
|
||
|
|
إضافة العقار
|
||
|
|
</button>
|
||
|
|
<button
|
||
|
|
type="button"
|
||
|
|
onClick={onClose}
|
||
|
|
className="px-6 py-3 border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors"
|
||
|
|
>
|
||
|
|
إلغاء
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
</form>
|
||
|
|
</motion.div>
|
||
|
|
</motion.div>
|
||
|
|
);
|
||
|
|
}
|