"use client";
import { useState, useEffect } from "react";
import { motion, AnimatePresence } from "framer-motion";
import { useRouter } from "next/navigation";
import Link from "next/link";
import { useTranslation } from "react-i18next";
import {
PlusCircle,
Building,
Home,
DollarSign,
MapPin,
Bed,
Bath,
Square,
Edit,
Trash2,
Eye,
Calendar,
Layers,
Sofa,
DoorOpen,
ImageIcon,
CheckCircle,
XCircle,
AlertCircle,
ChevronRight,
ChevronLeft,
Loader2,
Save,
X,
Star,
Ban,
Check,
School,
Hospital,
Store,
GraduationCap,
TreePine,
CreditCard,
Banknote, // تم إضافة Banknote هنا
} from "lucide-react";
import toast, { Toaster } from "react-hot-toast";
import AuthService from "../../services/AuthService";
import {
buildRentPropertyPayload,
getMyRentListings,
getMySaleListings,
editRentProperty,
editSaleProperty,
updateRentPropertyStatus,
updateSalePropertyStatus,
uploadPicture,
getPaymentTypes,
OwnerPaymentForSaleProperite, // تم إضافة استيراد الدالة هنا
} from "../../utils/api";
import { PropertyService } from "../../enums/PropertyService";
import { PropertyTerm } from "../../enums/PropertyTerm";
import Loading from "@/app/loading";
const serviceI18nKeys = {
[PropertyService.ELECTRICITY]: "propertyService.electricity",
[PropertyService.INTERNET]: "propertyService.internet",
[PropertyService.HEATING]: "propertyService.heating",
[PropertyService.WATER]: "propertyService.water",
[PropertyService.POOL]: "propertyService.pool",
[PropertyService.PRIVATE_GARDEN]: "propertyService.privateGarden",
[PropertyService.PARKING]: "propertyService.parking",
[PropertyService.SECURITY_247]: "propertyService.security247",
[PropertyService.CENTRAL_HEATING]: "propertyService.centralHeating",
[PropertyService.CENTRAL_AIR_CONDITIONING]: "propertyService.centralAirConditioning",
[PropertyService.EQUIPPED_KITCHEN]: "propertyService.equippedKitchen",
[PropertyService.MAIDS_ROOM]: "propertyService.maidsRoom",
[PropertyService.ELEVATOR]: "propertyService.elevator",
};
const termI18nKeys = {
[PropertyTerm.NO_SMOKING]: "propertyTerm.noSmoking",
[PropertyTerm.NO_ANIMALS]: "propertyTerm.noAnimals",
[PropertyTerm.NO_PARTIES]: "propertyTerm.noParties",
};
const proximityI18nKeys = {
School: "proximity.school",
Hospital: "proximity.hospital",
Restaurant: "proximity.restaurant",
University: "proximity.university",
Park: "proximity.park",
Mall: "proximity.mall",
Supermarket: "proximity.supermarket",
Pharmacy: "proximity.pharmacy",
Mosque: "proximity.mosque",
Bank: "proximity.bank",
Airport: "proximity.airport",
BusStation: "proximity.busStation",
};
const CommissionPaymentDialog = ({
isOpen,
property,
onClose,
selectedPayment,
onSelectPayment,
onConfirmPay,
payingId,
paymentMethods,
loadingPaymentMethods,
receiptImage,
onSetReceiptImage,
commissionDetails,
setCommissionDetails,
}) => {
const { t } = useTranslation();
if (!isOpen || !property) return null;
const commission = Number(property.commission || 0);
const selectedMethod = paymentMethods?.find((m) => String(m?.id ?? m?.name) === String(selectedPayment));
const isHaram = selectedMethod?.name?.toLowerCase?.() === "haram";
const hasSelectedMethod = !!selectedMethod;
const hasHaramDetails = commissionDetails?.fullName?.trim() && commissionDetails?.governorate?.trim() && commissionDetails?.phoneNumber?.trim();
const canPay = payingId !== property.id && hasSelectedMethod && (!isHaram || (!!receiptImage && !!hasHaramDetails));
return (
e.stopPropagation()}
>
{/* Header */}
{property.title}
{t("commission")}
{/* Commission amount */}
{t("commission")}
{commission.toLocaleString()} {t("syp")}
{/* Payment methods */}
{t("paymentMethod")}
{loadingPaymentMethods ? (
{t("loadingPaymentMethods", {
defaultValue: "جاري تحميل طرق الدفع...",
})}
) : !Array.isArray(paymentMethods) || paymentMethods.length === 0 ? (
{t("noPaymentMethodsAvailable", {
defaultValue: "لا توجد طرق دفع متاحة حالياً.",
})}
) : (
{paymentMethods.map((method, index) => {
const isActive = method?.isActive === true || method?.active === true;
const optionId = method?.id ?? method?.name ?? `payment-method-${index}`;
const label =
method?.name ||
t("paymentMethod", {
defaultValue: "طريقة الدفع",
});
const isSelected = selectedPayment === optionId;
return (
);
})}
)}
{/* Haram payment details */}
{isHaram && (
بيانات الدفع
{commissionDetails.split("\n").map((line, idx) => (
{line}
))}
)}
{/* Receipt */}
{isHaram && (
{t("uploadReceipt", {
defaultValue: "إيصال الدفع",
})}
{receiptImage ? (
) : (
)}
)}
{/* Pay */}
onConfirmPay(property)}
whileHover={{ scale: 1.01 }}
whileTap={{ scale: 0.99 }}
className="w-full bg-amber-500 hover:bg-amber-600 disabled:bg-amber-300 text-white font-bold py-4 px-6 rounded-2xl text-lg transition-all flex items-center justify-center gap-3"
>
{payingId === property.id ? (
<>
{t("processingPayment")}
>
) : (
<>
{t("payCommission", {
defaultValue: "دفع الكمسيون",
})}
>
)}
);
};
const DeleteConfirmationModal = ({ isOpen, onClose, onConfirm, propertyTitle }) => {
const { t } = useTranslation();
if (!isOpen) return null;
return (
e.stopPropagation()}
>
{t("deleteConfirmTitle")}
{t("deleteConfirmMessage", { title: propertyTitle })}
{t("deleteIrreversible")}
);
};
const DeactivateConfirmationModal = ({ isOpen, onClose, onConfirm, propertyTitle, isActivating }) => {
const { t } = useTranslation();
if (!isOpen) return null;
return (
e.stopPropagation()}
>
{isActivating ? t("activateTitle") : t("deactivateTitle")}
"{propertyTitle}"
{!isActivating ? (
⚠️ {t("warning")}
{t("deactivateWarning")}
) : (
✅ {t("activateInfoTitle")}
{t("activateInfo")}
)}
);
};
const PropertyViewModal = ({ isOpen, onClose, property }) => {
const { t } = useTranslation();
if (!isOpen || !property) return null;
return (
e.stopPropagation()}
>
{property.title}
{t("addedOn")}
{new Date(property.createdAt).toLocaleDateString("ar-SA")}
{property.images && property.images.length > 0 && (
{t("propertyImages")}
{property.images.map((image, index) => (
))}
)}
{t("basicInfo")}
{t("propertyTypeLabel")}
{property.propertyTypeLabel || t("offer")}
{t("displayTypeLabel")}
{property.displayType === "Daily rent"
? t("dailyRent")
: property.displayType === "Monthly rent"
? t("monthlyRent")
: property.displayType === "Both"
? t("dailyAndMonthly")
: property.displayType === "For sale"
? t("forSale")
: property.rentType === "daily"
? t("dailyRent")
: property.rentType === "monthly"
? t("monthlyRent")
: property.dailyPrice > 0 && property.monthlyPrice > 0
? t("dailyAndMonthly")
: property.monthlyPrice > 0
? t("monthlyRent")
: property.dailyPrice > 0
? t("dailyRent")
: property.purpose === "sale"
? t("forSale")
: t("offer")}
{property.purpose === "rent" && (
{t("furnishedStatusLabel")}
{property.furnished ? t("furnished") : t("unfurnished")}
)}
{t("propertyStatusLabel")}
{property.status === "available" ? t("available") : t("rented")}
{property.description && (
{t("descriptionLabel")}
{property.description}
)}
{t("basicInfo")}
{property.bedrooms}
{t("rooms")}
{property.bathrooms}
{t("bathrooms")}
{property.area}
{t("sqm")}
{property.floor > 0 && (
{property.floor}
{t("floor")}
)}
{property.salons > 0 && (
{property.salons}
{t("salons")}
)}
{property.balconies > 0 && (
{property.balconies}
{t("balconies")}
)}
{property.bookedCount > 0 && (
{property.bookedCount}
{t("bookings")}
)}
{t("location")}
{property.address || t("addressNotSpecified")}
{property.city && `، ${property.city}`}
{property.district && `، ${property.district}`}
{property.services && (Array.isArray(property.services) ? property.services.length > 0 : Object.keys(property.services).length > 0) && (
{t("availableServices")}
{Array.isArray(property.services)
? property.services.map((svc, i) => (
{t(serviceI18nKeys[svc]) || svc}
))
: Object.entries(property.services).map(([key, value]) => {
if (!value) return null;
const detail = typeof value === "object" && value.detail ? value.detail : typeof value === "string" ? value : null;
return (
{t(serviceI18nKeys[key]) || key}
{detail && · {detail}}
);
})}
)}
{property.proximity && Object.keys(property.proximity).length > 0 && (
{t("proximityToServices")}
{Object.entries(property.proximity).map(([key, val]) => {
if (!val) return null;
const dist = typeof val === "object" ? val.distance : val;
return (
{key === "School" &&
}
{key === "Hospital" &&
}
{key === "Restaurant" &&
}
{key === "University" &&
}
{key === "Park" &&
}
{key === "Mall" &&
}
{!["School", "Hospital", "Restaurant", "University", "Park", "Mall"].includes(key) &&
}
{t(proximityI18nKeys[key]) || key}
{dist} {typeof dist === "number" ? t("km") : ""}
);
})}
)}
{property.terms && Object.keys(property.terms).length > 0 && (
{t("termsOfUse")}
{Object.entries(property.terms).map(([key, value]) => {
if (!value) return null;
return (
{key.startsWith("No") || key.startsWith("Only") ? : }
{t(termI18nKeys[key]) || key}
);
})}
)}
{t("priceInfo")}
{property.purpose === "rent" ? (
{property.dailyPrice > 0 && (
{t("dailyPriceLabel")}
{Number(property.dailyPrice).toLocaleString()} {t("syp")}
)}
{property.monthlyPrice > 0 && (
{t("monthlyPriceLabel")}
{Number(property.monthlyPrice).toLocaleString()} {t("syp")}
)}
{property.deposit > 0 && (
{t("depositLabel")}
{Number(property.deposit).toLocaleString()} {t("syp")}
)}
{t("rentTypeLabel")} {property.rentType === "daily" ? t("daily") : property.rentType === "monthly" ? t("monthly") : t("dailyAndMonthly")}
{property.rating > 0 && (
{t("ratingLabel")}
{Number(property.rating).toFixed(1)}
)}
) : (
{t("salePriceLabel")}
{Number(property.salePrice).toLocaleString()} {t("syp")}
)}
);
};
const PropertyEditModal = ({ isOpen, onClose, property, onSave }) => {
const { t } = useTranslation();
const [formData, setFormData] = useState({
propertyType: "apartment",
furnished: false,
description: "",
bedrooms: 0,
bathrooms: 0,
floor: 0,
salons: 0,
balconies: 0,
livingRooms: 0,
area: 0,
services: {},
serviceDetails: {},
terms: {},
customTerms: [],
nearbySchool: "",
nearbyHospital: "",
nearbyRestaurant: "",
nearbyUniversity: "",
nearbyPark: "",
nearbyMall: "",
nearbyMosque: "",
purpose: "rent",
currencyId: 1,
dailyPrice: 0,
commission: "",
monthlyPrice: 0,
deposit: 0,
rentType: "monthly",
allowedPaymentPeriod: "",
salePrice: 0,
});
const [newCustomTerm, setNewCustomTerm] = useState("");
const [isSaving, setIsSaving] = useState(false);
const [existingImages, setExistingImages] = useState([]);
const [newImages, setNewImages] = useState([]);
const editApiBase = process.env.NEXT_PUBLIC_API_URL || "https://45.93.137.91.nip.io/api";
const editImgUrl = (path) => {
if (!path) return null;
if (path.startsWith("http://") || path.startsWith("https://")) return path;
return `${editApiBase}${path}`;
};
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 propServices = property.services || {};
const services = {};
const serviceDetails = {};
Object.keys(serviceI18nKeys).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] || "";
}
});
const rawSvcArray = Array.isArray(details.services) ? details.services : [];
rawSvcArray.forEach((key) => {
if (!(key in services)) {
services[key] = true;
serviceDetails[key] = details.serviceDetails?.[key] || "";
}
});
const propTerms = property.terms || {};
const terms = {};
Object.keys(termI18nKeys).forEach((key) => {
terms[key] = !!propTerms[key];
});
if (details.terms && typeof details.terms === "object") {
Object.entries(details.terms).forEach(([key, val]) => {
if (val && !(key in terms)) {
terms[key] = true;
}
});
}
const prox = property.proximity || details.nearbyDistances || {};
setFormData({
propertyType: property.propertyType || "apartment",
furnished: property.furnished ?? false,
description: property.description || details.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,
livingRooms: property.livingRooms ?? details.numberOfLivingRooms ?? details.livingRooms ?? 0,
area: property.area || 0,
services,
serviceDetails,
terms,
customTerms: details.customTerms || [],
nearbyMosque: prox.nearbyMosque || prox.neaebymosque || "",
nearbySchool: prox.School ?? prox.school ?? prox.nearbySchool ?? "",
nearbyHospital: prox.Hospital ?? prox.hospital ?? prox.nearbyHospital ?? "",
nearbyRestaurant: prox.Restaurant ?? prox.restaurant ?? prox.nearbyRestaurant ?? "",
nearbyUniversity: prox.University ?? prox.university ?? prox.nearbyUniversity ?? "",
nearbyPark: prox.Park ?? prox.park ?? prox.nearbyPark ?? "",
nearbyMall: prox.Mall ?? prox.mall ?? prox.nearbyMall ?? "",
purpose: property.purpose || "rent",
currencyId: property.currencyId || 1,
commission: property.commission || prox.commission || "",
...(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("");
setNewImages([]);
const rawImages = Array.isArray(property.images) ? property.images : Array.isArray(info.images) ? info.images : [];
setExistingImages(rawImages.filter((p) => p && p.trim()));
}, [property, isOpen]);
const handleChange = (field, value) => {
setFormData((prev) => ({ ...prev, [field]: value }));
};
const handleServiceToggle = (key, checked) => {
setFormData((prev) => ({
...prev,
services: { ...prev.services, [key]: checked },
}));
};
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 handleRemoveExistingImage = (index) => {
setExistingImages((prev) => prev.filter((_, i) => i !== index));
};
const handleMoveExistingImage = (index, direction) => {
setExistingImages((prev) => {
const next = [...prev];
const target = index + direction;
if (target < 0 || target >= next.length) return prev;
[next[index], next[target]] = [next[target], next[index]];
return next;
});
};
const handleAddImages = async (files) => {
const validFiles = Array.from(files).filter((f) => {
if (!f.type.startsWith("image/")) return false;
if (f.size > 5 * 1024 * 1024) {
toast.error(t("fileTooLarge") || "الصورة كبيرة جداً");
return false;
}
return true;
});
if (validFiles.length === 0) return;
const maxTotal = 10;
const currentCount = existingImages.length + newImages.length;
const slots = maxTotal - currentCount;
if (slots <= 0) {
toast.error(t("maxImagesReached") || "الحد الأقصى 10 صور");
return;
}
const toAdd = validFiles.slice(0, slots);
const entries = toAdd.map((file) => ({
file,
preview: URL.createObjectURL(file),
path: null,
uploading: true,
}));
setNewImages((prev) => [...prev, ...entries]);
for (let i = 0; i < entries.length; i++) {
const entry = entries[i];
try {
const result = await uploadPicture(entry.file);
const path = result?.value || result?.path || result?.url || result;
entry.path = path;
} catch {
toast.error(t("uploadFailed") || "فشل رفع الصورة");
} finally {
entry.uploading = false;
setNewImages((prev) => [...prev]);
}
}
};
const handleRemoveNewImage = (index) => {
const entry = newImages[index];
if (entry?.preview) URL.revokeObjectURL(entry.preview);
setNewImages((prev) => prev.filter((_, i) => i !== index));
};
const handleMoveNewImage = (index, direction) => {
setNewImages((prev) => {
const next = [...prev];
const target = index + direction;
if (target < 0 || target >= next.length) return prev;
[next[index], next[target]] = [next[target], next[index]];
return next;
});
};
const handleSave = async () => {
const errors = [];
if (!formData.description?.trim()) errors.push(t("descriptionRequired") || "الوصف مطلوب");
if (formData.bedrooms < 1) errors.push(t("bedroomsRequired") || "يجب أن يكون عدد الغرف على الأقل 1");
if (formData.bathrooms < 1) errors.push(t("bathroomsRequired") || "يجب أن يكون عدد الحمامات على الأقل 1");
if (!formData.area || formData.area <= 0) errors.push(t("areaRequired") || "المساحة مطلوبة");
if (formData.purpose === "rent") {
if ((!formData.dailyPrice || formData.dailyPrice <= 0) && (!formData.monthlyPrice || formData.monthlyPrice <= 0)) {
errors.push(t("priceRequired") || "يجب إدخال سعر الأجر الشهري أو اليومي");
}
} else if (formData.purpose === "sale") {
if (!formData.salePrice || formData.salePrice <= 0) errors.push(t("priceRequired") || "السعر مطلوب");
}
if (errors.length > 0) {
toast.error(errors[0]);
return;
}
setIsSaving(true);
try {
const uploadedPaths = newImages.filter((e) => e.path && !e.uploading).map((e) => e.path);
const allImages = [...existingImages, ...uploadedPaths];
await onSave({ ...formData, _images: allImages });
} catch {
setIsSaving(false);
}
};
if (!isOpen || !property) return null;
return (
e.stopPropagation()}
>
{t("editProperty")}
{t("editSubtitle")}
{/* Basic Info */}
{t("basicInfo")}
{/* Details */}
{t("basicInfo")}
{[
{ id: "bedrooms", label: t("bedroomsCount") },
{ id: "bathrooms", label: t("bathroomsCount") },
{ id: "livingRooms", label: t("livingRoomsCount") },
{ id: "floor", label: t("floorLabel") },
{ id: "salons", label: t("salonsCount") },
{ id: "balconies", label: t("balconiesCount") },
{ id: "area", label: t("areaSqm") },
].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"
/>
))}
{/* Images */}
{t("images")}
{existingImages.map((path, i) => (
{i > 0 && (
)}
{i < existingImages.length - 1 && (
)}
))}
{newImages.map((entry, i) => (
{entry.uploading ? (
) : (

)}
{i > 0 && (
)}
{i < newImages.length - 1 && (
)}
))}
{existingImages.length + newImages.length < 10 && (
)}
{existingImages.length + newImages.length}/10
{/* Services */}
{t("services")}
{Object.entries(serviceI18nKeys).map(([key, i18nKey]) => (
))}
{/* Terms - rent only */}
{formData.purpose === "rent" && (
)}
{/* Nearby Distances */}
{t("proximityToServices")}
{[
{ id: "nearbySchool", label: t("school") },
{ id: "nearbyHospital", label: t("hospital") },
{ id: "nearbyRestaurant", label: t("restaurant") },
{ id: "nearbyUniversity", label: t("university") },
{ id: "nearbyPark", label: t("park") },
{ id: "nearbyMall", label: t("mall") },
{ id: "nearbyMosque", label: t("neaebymosque") },
].map((field) => (
handleChange(field.id, e.target.value)}
placeholder={t("distancePlaceholder")}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-amber-500"
/>
))}
{/* Pricing */}
{formData.purpose === "rent" ? t("priceInfoRent") : t("salePriceSimple")}
{formData.purpose === "rent" ? (
) : (
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"
/>
)}
);
};
export default function OwnerPropertiesPage() {
const { t } = useTranslation();
const router = useRouter();
const [user, setUser] = useState(null);
const [properties, setProperties] = useState([]);
const [isLoading, setIsLoading] = useState(true);
const [showAddMenu, setShowAddMenu] = useState(false);
const [activeTab, setActiveTab] = useState("rent");
const [commissionPayment, setCommissionPayment] = useState({
open: false,
property: null,
});
const [selectedCommissionPayment, setSelectedCommissionPayment] = useState(null);
const [commissionPaymentMethods, setCommissionPaymentMethods] = useState([]);
const [loadingCommissionPaymentMethods, setLoadingCommissionPaymentMethods] = useState(false);
const [commissionReceiptImage, setCommissionReceiptImage] = useState(null);
const [commissionPayingId, setCommissionPayingId] = useState(null);
const [commissionDetails, setCommissionDetails] = useState("");
const [deleteModal, setDeleteModal] = useState({
isOpen: false,
property: null,
});
const [viewModal, setViewModal] = useState({ isOpen: false, property: null });
const [editModal, setEditModal] = useState({ isOpen: false, property: null });
const [deactivateModal, setDeactivateModal] = useState({
isOpen: false,
property: null,
isActivating: false,
});
const filteredProperties = properties.filter((p) => p.purpose === activeTab);
const rentCount = properties.filter((p) => p.purpose === "rent").length;
const saleCount = properties.filter((p) => p.purpose === "sale").length;
useEffect(() => {
const authUser = AuthService.getUser();
if (authUser && AuthService.isOwner()) {
setUser({
name: authUser.name || authUser.email,
email: authUser.email,
role: "owner",
});
loadProperties();
} else {
router.push("/auth/choose-role");
}
}, [router]);
const openCommissionPayment = async (property) => {
setCommissionPayment({
open: true,
property,
});
setSelectedCommissionPayment(null);
setCommissionReceiptImage(null);
setLoadingCommissionPaymentMethods(true);
try {
const result = await getPaymentTypes();
setCommissionDetails(result[0].details);
setCommissionPaymentMethods(result?.value || result?.data || result || []);
} catch (error) {
toast.error(error?.message || "فشل تحميل طرق الدفع");
} finally {
setLoadingCommissionPaymentMethods(false);
}
};
const loadProperties = async () => {
const authUser = AuthService.getUser();
const userId = authUser?.id;
if (!userId) {
console.warn("[OwnerProperties] No user ID found");
setIsLoading(false);
return;
}
try {
const [rentData, saleData] = await Promise.allSettled([getMyRentListings(), getMySaleListings()]);
const rentList = rentData.status === "fulfilled" ? (Array.isArray(rentData.value) ? rentData.value.filter(Boolean) : rentData.value ? [rentData.value] : []) : [];
const saleList = saleData.status === "fulfilled" ? (Array.isArray(saleData.value) ? saleData.value.filter(Boolean) : saleData.value ? [saleData.value] : []) : [];
const normalizeServices = (details) => {
const rawServices = details.services || {};
const serviceList = Array.isArray(rawServices) ? rawServices : Object.keys(rawServices).filter((k) => rawServices[k]);
const serviceDetails = details.serviceDetails || {};
const services = {};
serviceList.forEach((s) => {
services[s] = serviceDetails[s] || true;
});
return services;
};
const normalizeTerms = (terms) => {
if (!terms) return {};
return Array.isArray(terms) ? terms.reduce((acc, t) => ({ ...acc, [t]: true }), {}) : terms;
};
const normalizeProximity = (prox) => {
if (!prox) return {};
const result = {};
Object.entries(prox).forEach(([k, v]) => {
if (!v) return;
result[k.charAt(0).toUpperCase() + k.slice(1)] = v;
});
return result;
};
const mappedRent = rentList.map((item) => {
const info = item.propertyInformation || {};
const details =
typeof info.detailsJSON === "object" && info.detailsJSON
? info.detailsJSON
: (() => {
try {
return JSON.parse(info.detailsJSON || "{}");
} catch {
return {};
}
})();
const apiBase = typeof window !== "undefined" ? process.env.NEXT_PUBLIC_API_URL || "https://45.93.137.91.nip.io/api" : "";
const raw = Array.isArray(info.images) ? info.images : [];
return {
id: item.id,
title: info.address || `عقار #${item.id}`,
propertyType:
{
0: "apartment",
1: "villa",
2: "sweet",
3: "room",
4: "studio",
5: "office",
6: "farms",
7: "shop",
8: "warehouse",
}[info.buildingType] || "apartment",
propertyTypeLabel:
{
0: "شقة",
1: "فيلا",
2: "سويت",
3: "غرفة",
4: "استوديو",
5: "مكتب",
6: "مزرعة",
7: "متجر",
8: "مستودع",
}[info.buildingType] || "عقار",
purpose: "rent",
rentType: item.rentType === 0 ? "monthly" : item.rentType === 1 ? "daily" : "daily",
dailyPrice: item.dailyRent || 0,
monthlyPrice: item.monthlyRent || 0,
salePrice: item.price || 0,
deposit: item.deposit || 0,
commission: item.commission || details.commission || 0,
location: info.address || "",
bedrooms: info.numberOfBedRooms || 0,
bathrooms: info.numberOfBathRooms || 0,
area: info.space || 0,
livingRooms: details.livingRooms || 0,
floor: details.floorNumber ?? details.floor ?? 0,
salons: details.numberOfSalons ?? details.salonsCount ?? details.salons ?? 0,
balconies: details.numberOfBalconies ?? details.balconiesCount ?? details.balconies ?? 0,
bookedCount: details.bookedCount || 0,
status: { 0: "available", 1: "booked", 2: "maintenance" }[info.status] || "available",
images: raw.length > 0 ? raw.map((img) => (img.startsWith("http") ? img : `${apiBase}${img.startsWith("/") ? "" : "/Pictures/"}${img}`)) : ["/property-placeholder.jpg"],
createdAt: item.createdAt || new Date().toISOString(),
furnished: details.furnished || false,
displayType: details.displayType || (item.dailyRent && item.monthlyRent ? "Both" : item.monthlyRent ? "Monthly rent" : item.dailyRent ? "Daily rent" : "Both"),
description: info.description || "",
address: info.address || "",
city: "",
district: "",
services: normalizeServices(details),
terms: normalizeTerms(details.terms),
proximity: normalizeProximity(details.nearbyDistances),
rating: item.rating || 0,
currencyId: item.currencyId,
_raw: item,
status2: item.status,
};
});
const mappedSale = saleList.map((item) => {
const info = item.propertyInformation || {};
const details =
typeof info.detailsJSON === "object" && info.detailsJSON
? info.detailsJSON
: (() => {
try {
return JSON.parse(info.detailsJSON || "{}");
} catch {
return {};
}
})();
const apiBase = typeof window !== "undefined" ? process.env.NEXT_PUBLIC_API_URL || "https://45.93.137.91.nip.io/api" : "";
const raw = Array.isArray(info.images) ? info.images : [];
return {
id: item.id,
title: info.address || `عقار للبيع #${item.id}`,
propertyType:
{
0: "apartment",
1: "villa",
2: "sweet",
3: "room",
4: "studio",
5: "office",
6: "farms",
7: "shop",
8: "warehouse",
}[info.buildingType] || "apartment",
propertyTypeLabel:
{
0: "شقة",
1: "فيلا",
2: "سويت",
3: "غرفة",
4: "استوديو",
5: "مكتب",
6: "مزرعة",
7: "متجر",
8: "مستودع",
}[info.buildingType] || "عقار",
purpose: "sale",
dailyPrice: 0,
monthlyPrice: 0,
salePrice: item.price || 0,
deposit: 0,
commission: item.commission || details.commission || 0,
location: info.address || "",
bedrooms: info.numberOfBedRooms || 0,
bathrooms: info.numberOfBathRooms || 0,
area: info.space || 0,
livingRooms: details.livingRooms || 0,
floor: details.floorNumber ?? details.floor ?? 0,
salons: details.numberOfSalons ?? details.salonsCount ?? details.salons ?? 0,
balconies: details.numberOfBalconies ?? details.balconiesCount ?? details.balconies ?? 0,
bookedCount: details.bookedCount || 0,
status: "available",
images: raw.length > 0 ? raw.map((img) => (img.startsWith("http") ? img : `${apiBase}${img.startsWith("/") ? "" : "/Pictures/"}${img}`)) : ["/property-placeholder.jpg"],
createdAt: item.createdAt || new Date().toISOString(),
furnished: details.furnished || false,
displayType: details.displayType || "For sale",
description: info.description || "",
address: info.address || "",
city: "",
district: "",
services: normalizeServices(details),
terms: normalizeTerms(details.terms),
proximity: normalizeProximity(details.nearbyDistances),
rating: item.rating || 0,
currencyId: item.currencyId,
_raw: item,
status2: item.status,
};
});
setProperties([...mappedRent, ...mappedSale]);
} catch (err) {
console.error("[OwnerProperties] Failed to load properties:", err);
toast.error(t("loadFailed"));
} finally {
setIsLoading(false);
}
};
const updatePropertiesInStorage = (newProperties) => {
setProperties(newProperties);
localStorage.setItem("ownerProperties", JSON.stringify(newProperties));
};
const handleDelete = () => {
if (deleteModal.property) {
const newProperties = properties.filter((p) => p.id !== deleteModal.property.id);
updatePropertiesInStorage(newProperties);
setDeleteModal({ isOpen: false, property: null });
toast.success(t("deleteSuccess"));
}
};
const handleSaveEdit = async (formData) => {
try {
const property = editModal.property;
const raw = property._raw || {};
const rawInfo = raw.propertyInformation || {};
const buildingTypeMap = {
apartment: 0,
villa: 1,
sweet: 2,
room: 3,
studio: 4,
office: 5,
farms: 6,
shop: 7,
warehouse: 8,
};
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 = {
description: formData.description || "",
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,
numberOfLivingRooms: parseInt(formData.livingRooms) || 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: formData._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 = buildRentPropertyPayload({
propertyInformation: propInfo,
city: formData.city || property?.city || property?.governorate || "",
governorate: formData.city || property?.city || property?.governorate || "",
documentType: formData.documentType || property?.documentType || "",
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 || "1.00:00:00",
});
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 === property.id ? updatedProperty : p));
updatePropertiesInStorage(newProperties);
setEditModal({ isOpen: false, property: null });
toast.success(t("updateSuccess"));
} catch (err) {
console.error("[OwnerProperties] Edit failed:", err);
toast.error(t("updateFailed"));
throw err;
}
};
const handleToggleActivation = async () => {
const prop = deactivateModal.property;
if (!prop) return;
const willActivate = deactivateModal.isActivating;
const newStatus = willActivate ? "available" : "notAvailable";
const statusCode = willActivate ? 0 : 1;
setDeactivateModal({ isOpen: false, property: null, isActivating: false });
try {
if (prop.purpose === "rent") {
await updateRentPropertyStatus(prop.id, statusCode);
await updateRentPropertyStatus(prop.id, statusCode);
} else {
await updateSalePropertyStatus(prop.id, statusCode);
}
const newProperties = properties.map((p) => (p.id === prop.id ? { ...p, status: newStatus } : p));
setProperties(newProperties);
localStorage.setItem("ownerProperties", JSON.stringify(newProperties));
toast.success(willActivate ? t("activateSuccess") : t("deactivateSuccess"));
} catch (err) {
console.error("[OwnerProperties] Toggle status failed:", err);
toast.error(t("statusUpdateFailed"));
}
};
const fadeInUp = {
initial: { opacity: 0, y: 20 },
animate: { opacity: 1, y: 0 },
transition: { duration: 0.5 },
};
const handleCommissionPayment = async (property) => {
if (!property) return;
if (!selectedCommissionPayment) {
toast.error("اختر طريقة الدفع");
return;
}
const selectedMethod = commissionPaymentMethods.find((m) => String(m?.id ?? m?.name) === String(selectedCommissionPayment));
const isHaram = selectedMethod?.name?.toLowerCase?.() === "haram";
if (isHaram && !commissionReceiptImage) {
toast.error("يرجى رفع إيصال الدفع");
return;
}
setCommissionPayingId(property.id);
try {
await OwnerPaymentForSaleProperite(property.id, selectedCommissionPayment, 1, commissionReceiptImage);
toast.success("تم إرسال طلب دفع الكمسيون");
await loadProperties();
setCommissionPayment({
open: false,
property: null,
});
setCommissionReceiptImage(null);
setSelectedCommissionPayment(null);
} catch (error) {
console.error("COMMISSION PAYMENT ERROR:", error);
toast.error(error?.message || "فشل دفع الكمسيون");
} finally {
setCommissionPayingId(null);
}
};
if (isLoading) {
return ;
}
return (
setDeleteModal({ isOpen: false, property: null })} onConfirm={handleDelete} propertyTitle={deleteModal.property?.title} />
setViewModal({ isOpen: false, property: null })} property={viewModal.property} />
setEditModal({ isOpen: false, property: null })} property={editModal.property} onSave={handleSaveEdit} />
setDeactivateModal({ isOpen: false, property: null, isActivating: false })}
onConfirm={handleToggleActivation}
propertyTitle={deactivateModal.property?.title}
isActivating={deactivateModal.isActivating}
/>
{t("myProperties")}
{t("welcomeUser", { name: user?.name, count: properties.length })}
setShowAddMenu(!showAddMenu)}
className="bg-gradient-to-r from-amber-500 to-amber-600 text-white px-6 py-3 rounded-xl font-medium flex items-center gap-2 shadow-lg hover:shadow-xl transition-all"
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
>
{t("addNewProperty")}
{showAddMenu && (
setShowAddMenu(false)}
>
{t("rentProperty")}
{t("rentPropertyDesc")}
setShowAddMenu(false)}
>
{t("saleProperty")}
{t("salePropertyDesc")}
)}
{/* Tab Switcher */}
{filteredProperties.length === 0 ? (
{activeTab === "rent" ? t("noRentProperties") : t("noSaleProperties")}
{t("startAdding", { type: activeTab === "rent" ? t("forRent") : t("forSaleTab") })}
{t("addNewProperty")}
) : (
{filteredProperties.map((property, index) => (
{property.images && property.images.length > 0 ? (

) : (
)}
{property.status === "notAvailable" ? t("notAvailable") : property.status === "available" ? t("available") : t("postponed")}
{property.purpose === "rent" && property.furnished !== undefined && (
{property.furnished ? t("furnished") : t("unfurnished")}
)}
{property.propertyTypeLabel ||
(property.propertyType === "apartment"
? t("buildingType.apartment")
: property.propertyType === "villa"
? t("buildingType.villa")
: property.propertyType === "sweet"
? t("buildingType.sweet")
: property.propertyType === "room"
? t("buildingType.room")
: property.propertyType === "studio"
? t("buildingType.studio")
: property.propertyType === "office"
? t("buildingType.office")
: property.propertyType === "farms"
? t("buildingType.farms")
: property.propertyType === "shop"
? t("buildingType.shop")
: property.propertyType === "warehouse"
? t("buildingType.warehouse")
: t("offer"))}
{property.title}
{property.rating > 0 && (
{Number(property.rating).toFixed(1)}
)}
{property.address || property.location || t("locationUnknown")}
{property.description &&
{property.description}
}
{property.bedrooms > 0 && (
{property.bedrooms}
)}
{property.bathrooms > 0 && (
{property.bathrooms}
)}
{property.area > 0 && (
{property.area}
{t("sqm")}
)}
{property.floor > 0 && (
{t("floor")} {property.floor}
)}
{property.salons > 0 && (
{property.salons}
)}
{property.balconies > 0 && (
{property.balconies}
)}
{property.purpose === "rent" ? (
{property.monthlyPrice > 0 && (
{Number(property.monthlyPrice).toLocaleString()}
{t("perMonth")}
)}
{property.dailyPrice > 0 && !property.monthlyPrice && (
{Number(property.dailyPrice).toLocaleString()}
{t("perDay")}
)}
{property.deposit > 0 &&
{t("depositAmount", { amount: Number(property.deposit).toLocaleString() })}
}
) : (
{Number(property.salePrice).toLocaleString()} {t("syp")}
)}
{property.status === "notAvailable" ? (
) : (
)}
{property.purpose === "sale" && Number(property.status2) === 0 && (
)}
{property.purpose === "sale" && Number(property.status2) === 1 && (
قيد الانتظار
)}
{property.purpose === "sale" && Number(property.status2) === 2 && تم التأكيد
}
))}
)}
setCommissionPayment({
open: false,
property: null,
})
}
selectedPayment={selectedCommissionPayment}
onSelectPayment={setSelectedCommissionPayment}
onConfirmPay={handleCommissionPayment}
payingId={commissionPayingId}
paymentMethods={commissionPaymentMethods}
loadingPaymentMethods={loadingCommissionPaymentMethods}
receiptImage={commissionReceiptImage}
onSetReceiptImage={setCommissionReceiptImage}
commissionDetails={commissionDetails}
setCommissionDetails={setCommissionDetails}
/>
);
}