"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,
TrendingUp,
Users,
FileText,
Image as ImageIcon,
CheckCircle,
XCircle,
AlertCircle,
ChevronRight,
ChevronLeft,
Loader2,
Clock,
Wifi,
Zap,
Flame,
Droplets,
Cigarette,
Dog,
Music,
Warehouse,
Layers,
Sofa,
DoorOpen,
Wind,
Pencil,
Save,
X,
Star,
Ban,
Check,
School,
Hospital,
Store,
GraduationCap,
TreePine,
} from "lucide-react";
import toast, { Toaster } from "react-hot-toast";
import AuthService from "../../services/AuthService";
import { buildRentPropertyPayload, getMyRentListings, getMySaleListings, editRentProperty, editSaleProperty, updateRentPropertyStatus, updateSalePropertyStatus, uploadPicture } 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 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: "",
purpose: "rent",
currencyId: 1,
dailyPrice: 0,
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 || [],
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,
...(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") },
].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("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"
/>
)}
);
};
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 [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 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,
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,
};
});
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,
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,
};
});
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);
} 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 },
};
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" ? (
) : (
)}
))}
)}
);
}