diff --git a/app/owner/properties/page.js b/app/owner/properties/page.js
index 6cf54d9..6ab98f8 100644
--- a/app/owner/properties/page.js
+++ b/app/owner/properties/page.js
@@ -58,6 +58,7 @@ import {
getMyRentListings,
getMySaleListings,
editRentProperty,
+ editSaleProperty,
} from "../../utils/api";
const DeleteConfirmationModal = ({
@@ -521,114 +522,140 @@ const PropertyViewModal = ({ isOpen, onClose, property }) => {
};
const PropertyEditModal = ({ isOpen, onClose, property, onSave }) => {
- const [formData, setFormData] = useState({ ...property });
+ const [formData, setFormData] = useState({});
+ const [newCustomTerm, setNewCustomTerm] = useState('');
const [isSaving, setIsSaving] = useState(false);
- const [editingField, setEditingField] = useState(null);
- const [tempValues, setTempValues] = useState({});
- const sections = [
- {
- title: "معلومات أساسية",
- fields: [
- { id: "title", label: "عنوان العقار", type: "text" },
- { id: "description", label: "الوصف", type: "textarea" },
- {
- id: "propertyType",
- label: "نوع العقار",
- type: "select",
- options: [
- { value: "apartment", label: "شقة" },
- { value: "villa", label: "فيلا" },
- { value: "suite", label: "سويت" },
- { value: "room", label: "غرفة ضمن شقة" },
- ],
- },
- {
- id: "furnished",
- label: "حالة العقار",
- type: "radio",
- options: [
- { value: true, label: "مفروش" },
- { value: false, label: "غير مفروش" },
- ],
- },
- ],
- },
- {
- title: "التفاصيل",
- fields: [
- { id: "bedrooms", label: "عدد الغرف", type: "number" },
- { id: "bathrooms", label: "عدد الحمامات", type: "number" },
- { id: "livingRooms", label: "عدد الصالونات", type: "number" },
- { id: "area", label: "المساحة (م²)", type: "number" },
- ],
- },
- {
- title: "الموقع",
- fields: [
- { id: "address", label: "العنوان الكامل", type: "text" },
- { id: "city", label: "المدينة", type: "text" },
- { id: "district", label: "الحي", type: "text" },
- ],
- },
- {
- title: "السعر",
- fields:
- formData?.purpose === "rent"
- ? [
- { id: "dailyPrice", label: "السعر اليومي", type: "number" },
- { id: "monthlyPrice", label: "السعر الشهري", type: "number" },
- {
- id: "rentType",
- label: "نوع الإيجار",
- type: "select",
- options: [
- { value: "daily", label: "يومي" },
- { value: "monthly", label: "شهري" },
- { value: "both", label: "يومي وشهري" },
- ],
- },
- ]
- : [{ id: "salePrice", label: "سعر البيع", type: "number" }],
- },
- ];
+ useEffect(() => {
+ if (!property || !isOpen) return;
+ const raw = property._raw || {};
+ const info = raw.propertyInformation || {};
+ let details = {};
+ try {
+ details =
+ typeof info.detailsJSON === 'object' && info.detailsJSON
+ ? info.detailsJSON
+ : JSON.parse(info.detailsJSON || '{}');
+ } catch {
+ details = {};
+ }
- const startEditing = (fieldId) => {
- setEditingField(fieldId);
- setTempValues({ ...tempValues, [fieldId]: formData[fieldId] });
- };
-
- const cancelEditing = () => {
- setEditingField(null);
- setTempValues({});
- };
-
- const saveField = (fieldId) => {
- setFormData({
- ...formData,
- [fieldId]: tempValues[fieldId],
+ const propServices = property.services || {};
+ const services = {};
+ const serviceDetails = {};
+ Object.keys(serviceLabels).forEach((key) => {
+ const val = propServices[key];
+ if (val && typeof val === 'string') {
+ services[key] = true;
+ serviceDetails[key] = val;
+ } else if (val && typeof val === 'object' && val.detail) {
+ services[key] = true;
+ serviceDetails[key] = val.detail;
+ } else {
+ services[key] = !!val;
+ serviceDetails[key] = details.serviceDetails?.[key] || '';
+ }
});
- setEditingField(null);
- setTempValues({});
- const fieldLabel = sections
- .flatMap((s) => s.fields)
- .find((f) => f.id === fieldId)?.label;
- toast.success(`تم تحديث ${fieldLabel}`);
+
+ const propTerms = property.terms || {};
+ const terms = {};
+ Object.keys(termLabels).forEach((key) => {
+ terms[key] = !!propTerms[key];
+ });
+
+ const prox = property.proximity || details.nearbyDistances || {};
+
+ setFormData({
+ propertyType: property.propertyType || 'apartment',
+ furnished: property.furnished ?? false,
+ description: property.description || '',
+ bedrooms: property.bedrooms || 0,
+ bathrooms: property.bathrooms || 0,
+ floor: property.floor ?? details.floorNumber ?? details.floor ?? 0,
+ salons: property.salons ?? details.numberOfSalons ?? details.salons ?? 0,
+ balconies:
+ property.balconies ??
+ details.numberOfBalconies ??
+ details.balconies ??
+ 0,
+ area: property.area || 0,
+ services,
+ serviceDetails,
+ terms,
+ customTerms: details.customTerms || [],
+ nearbySchool: prox.School ?? prox.school ?? '',
+ nearbyHospital: prox.Hospital ?? prox.hospital ?? '',
+ nearbyRestaurant: prox.Restaurant ?? prox.restaurant ?? '',
+ nearbyUniversity: prox.University ?? prox.university ?? '',
+ nearbyPark: prox.Park ?? prox.park ?? '',
+ nearbyMall: prox.Mall ?? prox.mall ?? '',
+ purpose: property.purpose || 'rent',
+ currencyId: property.currencyId || 1,
+ ...(property.purpose === 'rent'
+ ? {
+ dailyPrice: property.dailyPrice || 0,
+ monthlyPrice: property.monthlyPrice || 0,
+ deposit: property.deposit || 0,
+ rentType: property.rentType || 'monthly',
+ allowedPaymentPeriod:
+ property.allowedPaymentPeriod ||
+ details.allowedPaymentPeriod ||
+ '',
+ }
+ : { salePrice: property.salePrice || 0 }),
+ });
+ setNewCustomTerm('');
+ }, [property, isOpen]);
+
+ const handleChange = (field, value) => {
+ setFormData((prev) => ({ ...prev, [field]: value }));
};
- const handleTempChange = (fieldId, value) => {
- setTempValues({ ...tempValues, [fieldId]: value });
+ const handleServiceToggle = (key, checked) => {
+ setFormData((prev) => ({
+ ...prev,
+ services: { ...prev.services, [key]: checked },
+ }));
};
- const handleSave = () => {
+ const handleServiceDetail = (key, value) => {
+ setFormData((prev) => ({
+ ...prev,
+ serviceDetails: { ...prev.serviceDetails, [key]: value },
+ }));
+ };
+
+ const handleTermToggle = (key, checked) => {
+ setFormData((prev) => ({
+ ...prev,
+ terms: { ...prev.terms, [key]: checked },
+ }));
+ };
+
+ const addCustomTerm = () => {
+ const term = newCustomTerm.trim();
+ if (!term) return;
+ setFormData((prev) => ({
+ ...prev,
+ customTerms: [...(prev.customTerms || []), term],
+ }));
+ setNewCustomTerm('');
+ };
+
+ const removeCustomTerm = (index) => {
+ setFormData((prev) => ({
+ ...prev,
+ customTerms: prev.customTerms.filter((_, i) => i !== index),
+ }));
+ };
+
+ const handleSave = async () => {
setIsSaving(true);
-
- setTimeout(() => {
- onSave(formData);
+ try {
+ await onSave(formData);
+ } catch {
setIsSaving(false);
- onClose();
- toast.success("تم تحديث العقار بنجاح");
- }, 1000);
+ }
};
if (!isOpen || !property) return null;
@@ -645,7 +672,7 @@ const PropertyEditModal = ({ isOpen, onClose, property, onSave }) => {
initial={{ scale: 0.9, y: 20 }}
animate={{ scale: 1, y: 0 }}
exit={{ scale: 0.9, y: 20 }}
- className="bg-white rounded-2xl w-full max-w-4xl max-h-[90vh] overflow-y-auto shadow-2xl"
+ className="bg-white rounded-2xl w-full max-w-4xl max-h-[90vh] flex flex-col shadow-2xl"
onClick={(e) => e.stopPropagation()}
>
@@ -663,261 +690,371 @@ const PropertyEditModal = ({ isOpen, onClose, property, onSave }) => {
-
- {formData.images && formData.images.length > 0 && (
-
-
-
صور العقار
-
+
+ {/* Basic Info */}
+
+
+ معلومات أساسية
+
+
+
+
+
-
- {formData.images.map((image, index) => (
-
-

-
+
+
+
+ {[
+ { value: true, label: 'مفروش' },
+ { value: false, label: 'غير مفروش' },
+ ].map((opt) => (
+
+ ))}
+
+
+
+
+
+
+
+
+ {/* Details */}
+
+
التفاصيل
+
+ {[
+ { id: 'bedrooms', label: 'عدد غرف النوم' },
+ { id: 'bathrooms', label: 'عدد الحمامات' },
+ { id: 'floor', label: 'الطابق' },
+ { id: 'salons', label: 'عدد الصالونات' },
+ { id: 'balconies', label: 'عدد البلكونات' },
+ { id: 'area', label: 'المساحة (م²)' },
+ ].map((field) => (
+
+
+
+ handleChange(field.id, Number(e.target.value))
+ }
+ className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-amber-500"
+ />
+
+ ))}
+
+
+
+ {/* Services */}
+
+
الخدمات
+
+ {Object.entries(serviceLabels).map(([key, label]) => (
+
+ ))}
+
+
+
+ {/* Terms - rent only */}
+ {formData.purpose === 'rent' && (
+
+
+ شروط الاستخدام
+
+
+ {Object.entries(termLabels).map(([key, label]) => (
+
))}
+
+
+
+ setNewCustomTerm(e.target.value)}
+ onKeyDown={(e) => e.key === 'Enter' && addCustomTerm()}
+ placeholder="أضف شرطاً مخصصاً..."
+ className="flex-1 px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-amber-500 text-sm"
+ />
+
+
+ {formData.customTerms?.length > 0 && (
+
+ {formData.customTerms.map((term, i) => (
+
+ {term}
+
+
+ ))}
+
+ )}
+
)}
- {sections.map((section, sectionIndex) => (
-
-
- {section.title}
-
-
- {section.fields.map((field) => (
-
-
-
- {editingField !== field.id && (
-
- )}
-
-
- {editingField === field.id ? (
-
- {field.type === "textarea" ? (
-
- ) : (
-
- {field.type === "select"
- ? field.options.find(
- (opt) => opt.value === formData[field.id],
- )?.label || formData[field.id]
- : field.type === "radio"
- ? formData[field.id]
- ? "مفروش"
- : "غير مفروش"
- : field.id.includes("Price")
- ? formData[field.id]
- ? `${Number(formData[field.id]).toLocaleString()} ل.س`
- : "—"
- : formData[field.id] || "—"}
-
- )}
-
- ))}
-
-
- ))}
-
-
-
الخدمات
-
- {Object.entries(formData.services || {}).map(([key, value]) => {
- const serviceLabels = {
- electricity: "كهرباء",
- internet: "انترنت",
- heating: "تدفئة",
- water: "ماء",
- airConditioning: "تكييف",
- parking: "موقف سيارات",
- elevator: "مصعد",
- };
- return (
-
- );
- })}
-
-
-
+ {/* Nearby Distances */}
- شروط الاستخدام
+ القرب من الخدمات
-
- {Object.entries(formData.terms || {}).map(([key, value]) => {
- const termLabels = {
- noSmoking: " ممنوع التدخين",
- noPets: " ممنوع الحيوانات",
- noParties: " ممنوع الحفلات",
- noAlcohol: " ممنوع الكحول",
- suitableForChildren: " مناسب للأطفال",
- suitableForElderly: " مناسب لكبار السن",
- };
- return (
-
-
-
-
+ {/* Pricing */}
+
+
+ {formData.purpose === 'rent'
+ ? 'معلومات السعر (إيجار)'
+ : 'سعر البيع'}
+
+ {formData.purpose === 'rent' ? (
+
+
+
+
+ التأمين
+
+
+ handleChange('deposit', Number(e.target.value))
+ }
+ className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-amber-500"
+ />
+
+
+
+ نوع الإيجار
+
+
+
+
+
+ مدة السداد المسموحة
+
+
+
+
+ ) : (
+
+
+ سعر البيع
+
+
+ handleChange('salePrice', Number(e.target.value))
+ }
+ className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-amber-500"
+ />
+
+ )}
+
+
+
+
+
);
@@ -1239,90 +1376,142 @@ export default function OwnerPropertiesPage() {
}
};
- const handleSaveEdit = async (updatedProperty) => {
+ const handleSaveEdit = async (formData) => {
try {
- if (updatedProperty.purpose === "rent" && updatedProperty._raw) {
- const buildingTypeMap = {
- apartment: 0,
- villa: 1,
- sweet: 2,
- room: 3,
- studio: 4,
- office: 5,
- farms: 6,
- shop: 7,
- warehouse: 8,
- };
- const raw = updatedProperty._raw;
- const rentTypeMap = { daily: 1, monthly: 0, both: 0 };
- const rawServices = updatedProperty.services || {};
- const serviceList = Array.isArray(rawServices)
- ? rawServices
- : Object.keys(rawServices).filter((k) => rawServices[k]);
- const details = {
- services: serviceList,
- serviceDetails: updatedProperty.serviceDetails || {},
- terms: normalizeTerms(updatedProperty.terms),
- displayType: "Both",
- propertyCondition: updatedProperty.furnished
- ? "WithFurniture"
- : "WithoutFurniture",
- };
- const detailsJSON = JSON.stringify(details);
+ const property = editModal.property;
+ const raw = property._raw || {};
+ const rawInfo = raw.propertyInformation || {};
- const payload = {
- propertyInformation: {
- cordsX: raw.propertyInformation?.cordsX || "",
- cordsY: raw.propertyInformation?.cordsY || "",
- address:
- updatedProperty.address || raw.propertyInformation?.address || "",
- description:
- updatedProperty.description ||
- raw.propertyInformation?.description ||
- "",
- numberOfBathRooms:
- updatedProperty.bathrooms ||
- raw.propertyInformation?.numberOfBathRooms ||
- 0,
- numberOfRooms: updatedProperty.bedrooms || 0,
- numberOfBedRooms:
- updatedProperty.bedrooms ||
- raw.propertyInformation?.numberOfBedRooms ||
- 0,
- space:
- parseFloat(updatedProperty.area) ||
- raw.propertyInformation?.space ||
- 0,
- detailsJSON,
- buildingType: buildingTypeMap[updatedProperty.propertyType] ?? 0,
- status: updatedProperty.status === "available" ? 0 : 1,
- propertyType: updatedProperty.furnished ? 0 : 1,
- images: raw.propertyInformation?.images || [],
- },
- deposit: parseFloat(updatedProperty.deposit) || raw.deposit || 0,
- monthlyRent:
- parseFloat(updatedProperty.monthlyPrice) || raw.monthlyRent || 0,
- dailyRent:
- parseFloat(updatedProperty.dailyPrice) || raw.dailyRent || 0,
- rating: 1,
- currencyId: updatedProperty.currencyId || raw.currencyId || 1,
- rentType: rentTypeMap[updatedProperty.rentType] ?? 0,
- type: updatedProperty.furnished ? 0 : 1,
- allowedPaymentPeriod: "",
- };
+ const buildingTypeMap = {
+ apartment: 0,
+ villa: 1,
+ sweet: 2,
+ room: 3,
+ studio: 4,
+ office: 5,
+ farms: 6,
+ shop: 7,
+ warehouse: 8,
+ };
- await editRentProperty(updatedProperty.id, payload);
+ const activeServices = Object.entries(formData.services || {})
+ .filter(([, v]) => v)
+ .map(([k]) => k);
+
+ const activeTerms = Object.entries(formData.terms || {})
+ .filter(([, v]) => v)
+ .reduce((acc, [k]) => ({ ...acc, [k]: true }), {});
+
+ if (formData.customTerms?.length) {
+ formData.customTerms.forEach((t) => {
+ activeTerms[t] = true;
+ });
}
+ const details = {
+ services: activeServices,
+ serviceDetails: Object.fromEntries(
+ Object.entries(formData.serviceDetails || {}).filter(
+ ([k, v]) => activeServices.includes(k) && v,
+ ),
+ ),
+ ...(formData.purpose === 'rent' ? { terms: activeTerms } : {}),
+ displayType:
+ formData.purpose === 'rent'
+ ? formData.rentType === 'both'
+ ? 'Both'
+ : formData.rentType === 'daily'
+ ? 'Daily rent'
+ : 'Monthly rent'
+ : 'For sale',
+ propertyCondition: formData.furnished
+ ? 'WithFurniture'
+ : 'WithoutFurniture',
+ floorNumber: parseInt(formData.floor) || 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 (
+ formData.purpose === 'rent' &&
+ formData.propertyType === 'room'
+ ) {
+ const roomDetails = rawInfo.detailsJSON || {};
+ let parsedDetails = {};
+ try {
+ parsedDetails =
+ typeof roomDetails === 'object'
+ ? roomDetails
+ : JSON.parse(roomDetails);
+ } catch {
+ parsedDetails = {};
+ }
+ details.room = parsedDetails.room || {};
+ }
+
+ const detailsJSON = JSON.stringify(details);
+
+ const propInfo = {
+ cordsX: rawInfo.cordsX || '',
+ cordsY: rawInfo.cordsY || '',
+ images: rawInfo.images || [],
+ address: property.address || rawInfo.address || '',
+ description:
+ formData.description || rawInfo.description || '',
+ numberOfBathRooms: parseInt(formData.bathrooms) || 0,
+ numberOfRooms: parseInt(formData.bedrooms) || 0,
+ numberOfBedRooms: parseInt(formData.bedrooms) || 0,
+ space: parseFloat(formData.area) || 0,
+ detailsJSON,
+ buildingType: buildingTypeMap[formData.propertyType] ?? 0,
+ status: 0,
+ propertyType: formData.furnished ? 0 : 1,
+ };
+
+ if (formData.purpose === 'rent') {
+ const rentTypeMap = { daily: 1, monthly: 0, both: 0 };
+ const payload = {
+ propertyInformation: propInfo,
+ deposit: parseFloat(formData.deposit) || 0,
+ monthlyRent: parseFloat(formData.monthlyPrice) || 0,
+ dailyRent: parseFloat(formData.dailyPrice) || 0,
+ rating: 1,
+ currencyId:
+ formData.currencyId || property.currencyId || 1,
+ rentType: rentTypeMap[formData.rentType] ?? 0,
+ type: formData.furnished ? 0 : 1,
+ allowedPaymentPeriod: formData.allowedPaymentPeriod || '',
+ };
+ await editRentProperty(property.id, payload);
+ } else {
+ const payload = {
+ propInfo,
+ price: parseFloat(formData.salePrice) || 0,
+ currencyId:
+ formData.currencyId || property.currencyId || 1,
+ };
+ await editSaleProperty(property.id, payload);
+ }
+
+ const updatedProperty = { ...property, ...formData };
const newProperties = properties.map((p) =>
- p.id === updatedProperty.id ? updatedProperty : p,
+ p.id === property.id ? updatedProperty : p,
);
updatePropertiesInStorage(newProperties);
setEditModal({ isOpen: false, property: null });
- toast.success("تم تحديث العقار بنجاح");
+ toast.success('تم تحديث العقار بنجاح');
} catch (err) {
- console.error("[OwnerProperties] Edit failed:", err);
- toast.error("فشل تحديث العقار");
+ console.error('[OwnerProperties] Edit failed:', err);
+ toast.error('فشل تحديث العقار');
+ throw err;
}
};
diff --git a/app/utils/api.js b/app/utils/api.js
index e958bf0..aae0b7f 100644
--- a/app/utils/api.js
+++ b/app/utils/api.js
@@ -672,6 +672,14 @@ export async function editRentProperty(id, data) {
});
}
+export async function editSaleProperty(id, data) {
+ console.log('[API] Editing sale property:', id);
+ return apiFetch(`/SaleProperties/EditSaleProperty/${id}`, {
+ method: 'PUT',
+ body: JSON.stringify(data),
+ });
+}
+
export async function addSaleProperty(data) {
console.log('[API] Adding sale property');
return apiFetch('/SaleProperties/AddSaleProperty', {