Added Active or DeActive for owner with API
All checks were successful
Build frontend / build (push) Successful in 1m10s
All checks were successful
Build frontend / build (push) Successful in 1m10s
This commit is contained in:
@ -59,6 +59,8 @@ import {
|
|||||||
getMySaleListings,
|
getMySaleListings,
|
||||||
editRentProperty,
|
editRentProperty,
|
||||||
editSaleProperty,
|
editSaleProperty,
|
||||||
|
updateRentPropertyStatus,
|
||||||
|
updateSalePropertyStatus,
|
||||||
} from "../../utils/api";
|
} from "../../utils/api";
|
||||||
|
|
||||||
const DeleteConfirmationModal = ({
|
const DeleteConfirmationModal = ({
|
||||||
@ -117,6 +119,86 @@ const DeleteConfirmationModal = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const DeactivateConfirmationModal = ({
|
||||||
|
isOpen,
|
||||||
|
onClose,
|
||||||
|
onConfirm,
|
||||||
|
propertyTitle,
|
||||||
|
isActivating,
|
||||||
|
}) => {
|
||||||
|
if (!isOpen) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0 }}
|
||||||
|
animate={{ opacity: 1 }}
|
||||||
|
exit={{ opacity: 0 }}
|
||||||
|
className="fixed inset-0 bg-black/50 backdrop-blur-sm flex items-center justify-center p-4 z-50"
|
||||||
|
onClick={onClose}
|
||||||
|
>
|
||||||
|
<motion.div
|
||||||
|
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-md p-6 shadow-2xl"
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
>
|
||||||
|
<div className="text-center mb-4">
|
||||||
|
<div className="w-16 h-16 bg-amber-100 rounded-full flex items-center justify-center mx-auto mb-3">
|
||||||
|
<AlertCircle className="w-8 h-8 text-amber-600" />
|
||||||
|
</div>
|
||||||
|
<h3 className="text-xl font-bold text-gray-900">
|
||||||
|
{isActivating ? "تأكيد التنشيط" : "تأكيد إلغاء التنشيط"}
|
||||||
|
</h3>
|
||||||
|
<p className="text-sm text-gray-500 mt-2">
|
||||||
|
العقار:{" "}
|
||||||
|
<span className="font-bold text-gray-700">"{propertyTitle}"</span>
|
||||||
|
</p>
|
||||||
|
{!isActivating ? (
|
||||||
|
<div className="mt-4 p-4 bg-red-50 border border-red-200 rounded-xl">
|
||||||
|
<p className="text-sm text-red-700 font-medium">
|
||||||
|
⚠️ تحذير
|
||||||
|
</p>
|
||||||
|
<p className="text-sm text-red-600 mt-1">
|
||||||
|
إذا قمت بإلغاء التنشيط، سيصبح هذا العقار غير متاح للحجز أو الشراء.
|
||||||
|
لا يمكن للعملاء رؤيته أو حجزه حتى تقوم بتنشيطه مرة أخرى.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="mt-4 p-4 bg-green-50 border border-green-200 rounded-xl">
|
||||||
|
<p className="text-sm text-green-700 font-medium">
|
||||||
|
✅ تنشيط العقار
|
||||||
|
</p>
|
||||||
|
<p className="text-sm text-green-600 mt-1">
|
||||||
|
سيصبح العقار متاحاً مرة أخرى للحجز أو الشراء.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex gap-3 pt-3">
|
||||||
|
<button
|
||||||
|
onClick={onClose}
|
||||||
|
className="flex-1 bg-gray-100 text-gray-700 py-3 rounded-xl font-medium hover:bg-gray-200 transition-colors"
|
||||||
|
>
|
||||||
|
إلغاء
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={onConfirm}
|
||||||
|
className={`flex-1 py-3 rounded-xl font-medium transition-colors ${
|
||||||
|
isActivating
|
||||||
|
? "bg-green-600 text-white hover:bg-green-700"
|
||||||
|
: "bg-red-600 text-white hover:bg-red-700"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{isActivating ? "نعم، فعّل" : "نعم، ألغي التنشيط"}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
</motion.div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const serviceLabels = {
|
const serviceLabels = {
|
||||||
Electricity: "كهرباء",
|
Electricity: "كهرباء",
|
||||||
Internet: "إنترنت",
|
Internet: "إنترنت",
|
||||||
@ -1074,6 +1156,11 @@ export default function OwnerPropertiesPage() {
|
|||||||
});
|
});
|
||||||
const [viewModal, setViewModal] = useState({ isOpen: false, property: null });
|
const [viewModal, setViewModal] = useState({ isOpen: false, property: null });
|
||||||
const [editModal, setEditModal] = 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 filteredProperties = properties.filter((p) => p.purpose === activeTab);
|
||||||
const rentCount = properties.filter((p) => p.purpose === "rent").length;
|
const rentCount = properties.filter((p) => p.purpose === "rent").length;
|
||||||
@ -1511,11 +1598,44 @@ export default function OwnerPropertiesPage() {
|
|||||||
toast.success('تم تحديث العقار بنجاح');
|
toast.success('تم تحديث العقار بنجاح');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('[OwnerProperties] Edit failed:', err);
|
console.error('[OwnerProperties] Edit failed:', err);
|
||||||
toast.error('فشل تحديث العقار');
|
toast.error("فشل تحديث العقار");
|
||||||
throw err;
|
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
|
||||||
|
? "تم تنشيط العقار بنجاح"
|
||||||
|
: "تم إلغاء تنشيط العقار بنجاح",
|
||||||
|
);
|
||||||
|
} catch (err) {
|
||||||
|
console.error("[OwnerProperties] Toggle status failed:", err);
|
||||||
|
toast.error("فشل تحديث حالة العقار");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const fadeInUp = {
|
const fadeInUp = {
|
||||||
initial: { opacity: 0, y: 20 },
|
initial: { opacity: 0, y: 20 },
|
||||||
animate: { opacity: 1, y: 0 },
|
animate: { opacity: 1, y: 0 },
|
||||||
@ -1557,6 +1677,16 @@ export default function OwnerPropertiesPage() {
|
|||||||
onSave={handleSaveEdit}
|
onSave={handleSaveEdit}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<DeactivateConfirmationModal
|
||||||
|
isOpen={deactivateModal.isOpen}
|
||||||
|
onClose={() =>
|
||||||
|
setDeactivateModal({ isOpen: false, property: null, isActivating: false })
|
||||||
|
}
|
||||||
|
onConfirm={handleToggleActivation}
|
||||||
|
propertyTitle={deactivateModal.property?.title}
|
||||||
|
isActivating={deactivateModal.isActivating}
|
||||||
|
/>
|
||||||
|
|
||||||
<div className="container mx-auto px-4">
|
<div className="container mx-auto px-4">
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ opacity: 0, y: -20 }}
|
initial={{ opacity: 0, y: -20 }}
|
||||||
@ -1714,10 +1844,16 @@ export default function OwnerPropertiesPage() {
|
|||||||
className={`px-2 py-0.5 rounded-md text-xs font-medium shadow-sm backdrop-blur-sm ${
|
className={`px-2 py-0.5 rounded-md text-xs font-medium shadow-sm backdrop-blur-sm ${
|
||||||
property.status === "available"
|
property.status === "available"
|
||||||
? "bg-white/90 text-green-700"
|
? "bg-white/90 text-green-700"
|
||||||
|
: property.status === "notAvailable"
|
||||||
|
? "bg-red-50/90 text-red-700"
|
||||||
: "bg-white/90 text-yellow-700"
|
: "bg-white/90 text-yellow-700"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{property.status === "available" ? "متاح" : "مؤجر"}
|
{property.status === "notAvailable"
|
||||||
|
? "غير متاح"
|
||||||
|
: property.status === "available"
|
||||||
|
? "متاح"
|
||||||
|
: "مؤجل"}
|
||||||
</span>
|
</span>
|
||||||
{property.purpose === "rent" &&
|
{property.purpose === "rent" &&
|
||||||
property.furnished !== undefined && (
|
property.furnished !== undefined && (
|
||||||
@ -1862,6 +1998,35 @@ export default function OwnerPropertiesPage() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex gap-1">
|
<div className="flex gap-1">
|
||||||
|
{property.status === "notAvailable" ? (
|
||||||
|
<button
|
||||||
|
onClick={() =>
|
||||||
|
setDeactivateModal({
|
||||||
|
isOpen: true,
|
||||||
|
property,
|
||||||
|
isActivating: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
className="p-1.5 hover:bg-green-50 rounded-lg transition-colors group"
|
||||||
|
title="تنشيط العقار"
|
||||||
|
>
|
||||||
|
<CheckCircle className="w-3.5 h-3.5 text-gray-400 group-hover:text-green-600" />
|
||||||
|
</button>
|
||||||
|
) : (
|
||||||
|
<button
|
||||||
|
onClick={() =>
|
||||||
|
setDeactivateModal({
|
||||||
|
isOpen: true,
|
||||||
|
property,
|
||||||
|
isActivating: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
className="p-1.5 hover:bg-red-50 rounded-lg transition-colors group"
|
||||||
|
title="إلغاء تنشيط العقار"
|
||||||
|
>
|
||||||
|
<XCircle className="w-3.5 h-3.5 text-gray-400 group-hover:text-red-600" />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
<button
|
<button
|
||||||
onClick={() => setViewModal({ isOpen: true, property })}
|
onClick={() => setViewModal({ isOpen: true, property })}
|
||||||
className="px-3 py-1.5 bg-gray-50 hover:bg-blue-50 border border-gray-200 hover:border-blue-200 rounded-lg transition-all group flex items-center gap-1.5"
|
className="px-3 py-1.5 bg-gray-50 hover:bg-blue-50 border border-gray-200 hover:border-blue-200 rounded-lg transition-all group flex items-center gap-1.5"
|
||||||
|
|||||||
@ -685,6 +685,20 @@ export async function getSalePropertyById(id) {
|
|||||||
return apiFetch(`/SaleProperties/${id}`);
|
return apiFetch(`/SaleProperties/${id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function updateRentPropertyStatus(id, status) {
|
||||||
|
return apiFetch(`/RentProperties/UpdateStatus/${id}`, {
|
||||||
|
method: 'PUT',
|
||||||
|
body: { status },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateSalePropertyStatus(id, status) {
|
||||||
|
return apiFetch(`/SaleProperties/UpdateStatus/${id}`, {
|
||||||
|
method: 'PUT',
|
||||||
|
body: { status },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// ─── Currencies ───
|
// ─── Currencies ───
|
||||||
|
|
||||||
export async function getCurrencies() {
|
export async function getCurrencies() {
|
||||||
|
|||||||
Reference in New Issue
Block a user