719 lines
26 KiB
TypeScript
719 lines
26 KiB
TypeScript
import React, { useState, useEffect } from "react";
|
|
import { Link, useNavigate } from "react-router-dom";
|
|
import { useAuth } from "../contexts/AuthContext";
|
|
import api from "../services/api";
|
|
import { Item, Rental } from "../types";
|
|
import { rentalAPI, conditionCheckAPI } from "../services/api";
|
|
import ReviewRenterModal from "../components/ReviewRenterModal";
|
|
import RentalCancellationModal from "../components/RentalCancellationModal";
|
|
import DeclineRentalModal from "../components/DeclineRentalModal";
|
|
import ConditionCheckModal from "../components/ConditionCheckModal";
|
|
import ReturnStatusModal from "../components/ReturnStatusModal";
|
|
|
|
const Owning: React.FC = () => {
|
|
// Helper function to format time
|
|
const formatTime = (timeString?: string) => {
|
|
if (!timeString || timeString.trim() === "") return "";
|
|
try {
|
|
const [hour, minute] = timeString.split(":");
|
|
const hourNum = parseInt(hour);
|
|
const hour12 = hourNum === 0 ? 12 : hourNum > 12 ? hourNum - 12 : hourNum;
|
|
const period = hourNum < 12 ? "AM" : "PM";
|
|
return `${hour12}:${minute} ${period}`;
|
|
} catch (error) {
|
|
return "";
|
|
}
|
|
};
|
|
|
|
// Helper function to format date and time together
|
|
const formatDateTime = (dateTimeString: string) => {
|
|
const date = new Date(dateTimeString);
|
|
return date
|
|
.toLocaleDateString("en-US", {
|
|
month: "2-digit",
|
|
day: "2-digit",
|
|
year: "numeric",
|
|
hour: "numeric",
|
|
minute: "2-digit",
|
|
hour12: true,
|
|
})
|
|
.replace(",", "");
|
|
};
|
|
|
|
const { user } = useAuth();
|
|
const navigate = useNavigate();
|
|
const [listings, setListings] = useState<Item[]>([]);
|
|
const [ownerRentals, setOwnerRentals] = useState<Rental[]>([]);
|
|
const [loading, setLoading] = useState(true);
|
|
const [error, setError] = useState("");
|
|
// Owner rental management state
|
|
const [showReviewRenterModal, setShowReviewRenterModal] = useState(false);
|
|
const [selectedRentalForReview, setSelectedRentalForReview] =
|
|
useState<Rental | null>(null);
|
|
const [showCancelModal, setShowCancelModal] = useState(false);
|
|
const [rentalToCancel, setRentalToCancel] = useState<Rental | null>(null);
|
|
const [showDeclineModal, setShowDeclineModal] = useState(false);
|
|
const [rentalToDecline, setRentalToDecline] = useState<Rental | null>(null);
|
|
const [isProcessingPayment, setIsProcessingPayment] = useState<string>("");
|
|
const [processingSuccess, setProcessingSuccess] = useState<string>("");
|
|
const [showConditionCheckModal, setShowConditionCheckModal] = useState(false);
|
|
const [conditionCheckData, setConditionCheckData] = useState<{
|
|
rental: Rental;
|
|
checkType: string;
|
|
} | null>(null);
|
|
const [availableChecks, setAvailableChecks] = useState<any[]>([]);
|
|
const [showReturnStatusModal, setShowReturnStatusModal] = useState(false);
|
|
const [rentalForReturn, setRentalForReturn] = useState<Rental | null>(null);
|
|
|
|
useEffect(() => {
|
|
fetchListings();
|
|
fetchOwnerRentals();
|
|
fetchAvailableChecks();
|
|
}, [user]);
|
|
|
|
const fetchListings = async () => {
|
|
if (!user) return;
|
|
|
|
try {
|
|
setLoading(true);
|
|
setError("");
|
|
const response = await api.get("/items");
|
|
|
|
// Filter items to only show ones owned by current user
|
|
const myItems = response.data.items.filter(
|
|
(item: Item) => item.ownerId === user.id
|
|
);
|
|
setListings(myItems);
|
|
} catch (err: any) {
|
|
console.error("Error fetching listings:", err);
|
|
if (err.response && err.response.status >= 500) {
|
|
setError("Failed to get your listings. Please try again later.");
|
|
}
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
const handleDelete = async (itemId: string) => {
|
|
if (!window.confirm("Are you sure you want to delete this listing?"))
|
|
return;
|
|
|
|
try {
|
|
await api.delete(`/items/${itemId}`);
|
|
setListings(listings.filter((item) => item.id !== itemId));
|
|
} catch (err: any) {
|
|
alert("Failed to delete listing");
|
|
}
|
|
};
|
|
|
|
const toggleAvailability = async (item: Item) => {
|
|
try {
|
|
await api.put(`/items/${item.id}`, {
|
|
...item,
|
|
isAvailable: !item.isAvailable,
|
|
});
|
|
setListings(
|
|
listings.map((i) =>
|
|
i.id === item.id ? { ...i, isAvailable: !i.isAvailable } : i
|
|
)
|
|
);
|
|
} catch (err: any) {
|
|
alert("Failed to update availability");
|
|
}
|
|
};
|
|
|
|
const fetchOwnerRentals = async () => {
|
|
try {
|
|
const response = await rentalAPI.getListings();
|
|
setOwnerRentals(response.data);
|
|
} catch (err: any) {
|
|
console.error("Failed to fetch owner rentals:", err);
|
|
}
|
|
};
|
|
|
|
const fetchAvailableChecks = async () => {
|
|
try {
|
|
const response = await conditionCheckAPI.getAvailableChecks();
|
|
const checks = Array.isArray(response.data.availableChecks)
|
|
? response.data.availableChecks
|
|
: [];
|
|
setAvailableChecks(checks);
|
|
} catch (err: any) {
|
|
console.error("Failed to fetch available checks:", err);
|
|
setAvailableChecks([]);
|
|
}
|
|
};
|
|
|
|
// Owner functionality handlers
|
|
const handleAcceptRental = async (rentalId: string) => {
|
|
try {
|
|
setIsProcessingPayment(rentalId);
|
|
const response = await rentalAPI.updateRentalStatus(
|
|
rentalId,
|
|
"confirmed"
|
|
);
|
|
|
|
// Check if payment processing was successful
|
|
if (response.data.paymentStatus === "paid") {
|
|
// Payment successful, rental confirmed
|
|
setProcessingSuccess(rentalId);
|
|
setTimeout(() => {
|
|
setProcessingSuccess("");
|
|
}, 3000);
|
|
}
|
|
|
|
fetchOwnerRentals();
|
|
fetchAvailableChecks(); // Refresh available checks after rental confirmation
|
|
|
|
// Notify Navbar to update pending count
|
|
window.dispatchEvent(new CustomEvent("rentalStatusChanged"));
|
|
} catch (err: any) {
|
|
console.error("Failed to accept rental request:", err);
|
|
|
|
// Check if it's a payment failure
|
|
if (err.response?.data?.error?.includes("Payment failed")) {
|
|
alert(
|
|
`Payment failed during approval: ${
|
|
err.response.data.details || "Unknown payment error"
|
|
}`
|
|
);
|
|
} else {
|
|
alert("Failed to accept rental request");
|
|
}
|
|
} finally {
|
|
setIsProcessingPayment("");
|
|
}
|
|
};
|
|
|
|
const handleDeclineClick = (rental: Rental) => {
|
|
setRentalToDecline(rental);
|
|
setShowDeclineModal(true);
|
|
};
|
|
|
|
const handleDeclineComplete = (updatedRental: Rental) => {
|
|
// Update the rental in the owner rentals list
|
|
setOwnerRentals((prev) =>
|
|
prev.map((rental) =>
|
|
rental.id === updatedRental.id ? updatedRental : rental
|
|
)
|
|
);
|
|
setShowDeclineModal(false);
|
|
setRentalToDecline(null);
|
|
};
|
|
|
|
const handleCompleteClick = (rental: Rental) => {
|
|
setRentalForReturn(rental);
|
|
setShowReturnStatusModal(true);
|
|
};
|
|
|
|
const handleReturnStatusMarked = async (updatedRental: Rental) => {
|
|
// Update the rental in the list
|
|
setOwnerRentals((prev) =>
|
|
prev.map((rental) =>
|
|
rental.id === updatedRental.id ? updatedRental : rental
|
|
)
|
|
);
|
|
|
|
// Close the return status modal
|
|
setShowReturnStatusModal(false);
|
|
setRentalForReturn(null);
|
|
|
|
// Show review modal (rental is already marked as completed by return status endpoint)
|
|
setSelectedRentalForReview(updatedRental);
|
|
setShowReviewRenterModal(true);
|
|
fetchOwnerRentals();
|
|
};
|
|
|
|
const handleReviewRenterSuccess = () => {
|
|
fetchOwnerRentals();
|
|
};
|
|
|
|
const handleCancelClick = (rental: Rental) => {
|
|
setRentalToCancel(rental);
|
|
setShowCancelModal(true);
|
|
};
|
|
|
|
const handleCancellationComplete = (updatedRental: Rental) => {
|
|
// Update the rental in the owner rentals list
|
|
setOwnerRentals((prev) =>
|
|
prev.map((rental) =>
|
|
rental.id === updatedRental.id ? updatedRental : rental
|
|
)
|
|
);
|
|
setShowCancelModal(false);
|
|
setRentalToCancel(null);
|
|
};
|
|
|
|
const handleConditionCheck = (rental: Rental, checkType: string) => {
|
|
setConditionCheckData({ rental, checkType });
|
|
setShowConditionCheckModal(true);
|
|
};
|
|
|
|
const handleConditionCheckSuccess = () => {
|
|
fetchAvailableChecks();
|
|
alert("Condition check submitted successfully!");
|
|
};
|
|
|
|
const getAvailableChecksForRental = (rentalId: string) => {
|
|
if (!Array.isArray(availableChecks)) return [];
|
|
return availableChecks.filter(
|
|
(check) =>
|
|
check.rentalId === rentalId && check.checkType === "pre_rental_owner" // Only pre-rental; post-rental is in return modal
|
|
);
|
|
};
|
|
|
|
// Filter owner rentals - exclude cancelled (shown in Rental History)
|
|
const allOwnerRentals = ownerRentals
|
|
.filter((r) => ["pending", "confirmed", "active"].includes(r.status))
|
|
.sort((a, b) => {
|
|
const statusOrder = { pending: 0, confirmed: 1, active: 2 };
|
|
return (
|
|
statusOrder[a.status as keyof typeof statusOrder] -
|
|
statusOrder[b.status as keyof typeof statusOrder]
|
|
);
|
|
});
|
|
|
|
if (loading) {
|
|
return (
|
|
<div className="container mt-4">
|
|
<div className="text-center">
|
|
<div className="spinner-border" role="status">
|
|
<span className="visually-hidden">Loading...</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="container mt-4">
|
|
<h1 className="mb-4">Owning</h1>
|
|
|
|
{error && (
|
|
<div className="alert alert-danger" role="alert">
|
|
{error}
|
|
</div>
|
|
)}
|
|
|
|
{/* Rental Requests Section - Moved to top for priority */}
|
|
{allOwnerRentals.length > 0 && (
|
|
<div className="mb-5">
|
|
<h4 className="mb-3">
|
|
<i className="bi bi-calendar-check me-2"></i>
|
|
Rentals
|
|
</h4>
|
|
<div className="row">
|
|
{allOwnerRentals.map((rental) => (
|
|
<div key={rental.id} className="col-md-6 col-lg-4 mb-4">
|
|
<div className="card h-100">
|
|
{rental.item?.images && rental.item.images[0] && (
|
|
<img
|
|
src={rental.item.images[0]}
|
|
className="card-img-top"
|
|
alt={rental.item.name}
|
|
style={{ height: "200px", objectFit: "cover" }}
|
|
/>
|
|
)}
|
|
<div className="card-body">
|
|
<h5 className="card-title text-dark">
|
|
{rental.item ? rental.item.name : "Item Unavailable"}
|
|
</h5>
|
|
|
|
{rental.renter && (
|
|
<p className="mb-1 text-dark small">
|
|
<strong>Renter:</strong>{" "}
|
|
<span
|
|
onClick={() => navigate(`/users/${rental.renterId}`)}
|
|
style={{ cursor: "pointer" }}
|
|
>
|
|
{rental.renter.firstName} {rental.renter.lastName}
|
|
</span>
|
|
</p>
|
|
)}
|
|
|
|
<div className="mb-2">
|
|
<span
|
|
className={`badge ${
|
|
rental.status === "active"
|
|
? "bg-success"
|
|
: rental.status === "pending"
|
|
? "bg-warning"
|
|
: rental.status === "confirmed"
|
|
? "bg-info"
|
|
: "bg-danger"
|
|
}`}
|
|
>
|
|
{rental.status.charAt(0).toUpperCase() +
|
|
rental.status.slice(1)}
|
|
</span>
|
|
</div>
|
|
|
|
<p className="mb-1 text-dark small">
|
|
<strong>Period:</strong>
|
|
<br />
|
|
{formatDateTime(rental.startDateTime)} -{" "}
|
|
{formatDateTime(rental.endDateTime)}
|
|
</p>
|
|
|
|
<p className="mb-1 text-dark small">
|
|
<strong>Total:</strong> ${rental.totalAmount}
|
|
</p>
|
|
|
|
{rental.intendedUse && rental.status === "pending" && (
|
|
<div className="alert alert-light mt-2 mb-2 p-2 small">
|
|
<strong>Intended Use:</strong>
|
|
<br />
|
|
{rental.intendedUse}
|
|
</div>
|
|
)}
|
|
|
|
{rental.status === "cancelled" &&
|
|
rental.refundAmount !== undefined && (
|
|
<div className="alert alert-info mt-2 mb-2 p-2 small">
|
|
<strong>
|
|
<i className="bi bi-arrow-return-left me-1"></i>
|
|
Refund:
|
|
</strong>{" "}
|
|
${Number(rental.refundAmount || 0).toFixed(2)}
|
|
{rental.refundProcessedAt && (
|
|
<small className="d-block text-muted mt-1">
|
|
Processed:{" "}
|
|
{new Date(
|
|
rental.refundProcessedAt
|
|
).toLocaleDateString()}
|
|
</small>
|
|
)}
|
|
{rental.refundReason && (
|
|
<small className="d-block mt-1">
|
|
{rental.refundReason}
|
|
</small>
|
|
)}
|
|
{rental.cancelledBy && (
|
|
<small className="d-block text-muted mt-1">
|
|
Cancelled by: {rental.cancelledBy}
|
|
</small>
|
|
)}
|
|
</div>
|
|
)}
|
|
|
|
{rental.itemPrivateMessage && rental.itemReviewVisible && (
|
|
<div className="alert alert-info mt-2 mb-2 p-2 small">
|
|
<strong>
|
|
<i className="bi bi-envelope-fill me-1"></i>Private
|
|
Note from Renter:
|
|
</strong>
|
|
<br />
|
|
{rental.itemPrivateMessage}
|
|
</div>
|
|
)}
|
|
|
|
<div className="d-flex flex-column gap-2 mt-3">
|
|
<div className="d-flex gap-2">
|
|
{rental.status === "pending" && (
|
|
<>
|
|
<button
|
|
className="btn btn-sm btn-success"
|
|
onClick={() => handleAcceptRental(rental.id)}
|
|
disabled={isProcessingPayment === rental.id}
|
|
>
|
|
{isProcessingPayment === rental.id ? (
|
|
<>
|
|
<div
|
|
className="spinner-border spinner-border-sm me-2"
|
|
role="status"
|
|
>
|
|
<span className="visually-hidden">
|
|
Loading...
|
|
</span>
|
|
</div>
|
|
Confirming...
|
|
</>
|
|
) : processingSuccess === rental.id ? (
|
|
<>
|
|
<i className="bi bi-check-circle me-1"></i>
|
|
Confirmed!
|
|
</>
|
|
) : (
|
|
"Accept"
|
|
)}
|
|
</button>
|
|
<button
|
|
className="btn btn-sm btn-danger"
|
|
onClick={() => handleDeclineClick(rental)}
|
|
>
|
|
Decline
|
|
</button>
|
|
</>
|
|
)}
|
|
{rental.status === "confirmed" && (
|
|
<>
|
|
<button
|
|
className="btn btn-sm btn-success"
|
|
onClick={() => handleCompleteClick(rental)}
|
|
>
|
|
Complete
|
|
</button>
|
|
<button
|
|
className="btn btn-sm btn-outline-danger"
|
|
onClick={() => handleCancelClick(rental)}
|
|
>
|
|
Cancel
|
|
</button>
|
|
</>
|
|
)}
|
|
{rental.status === "active" && (
|
|
<button
|
|
className="btn btn-sm btn-success"
|
|
onClick={() => handleCompleteClick(rental)}
|
|
>
|
|
Complete
|
|
</button>
|
|
)}
|
|
</div>
|
|
|
|
{/* Condition Check Buttons */}
|
|
{getAvailableChecksForRental(rental.id).map((check) => (
|
|
<button
|
|
key={`${rental.id}-${check.checkType}`}
|
|
className="btn btn-sm btn-outline-primary"
|
|
onClick={() =>
|
|
handleConditionCheck(rental, check.checkType)
|
|
}
|
|
>
|
|
<i className="bi bi-camera me-2" />
|
|
{check.checkType === "pre_rental_owner"
|
|
? "Submit Pre-Rental Check"
|
|
: "Submit Post-Rental Check"}
|
|
</button>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
<div className="d-flex justify-content-between align-items-center mb-3">
|
|
<h4 className="mb-0">
|
|
<i className="bi bi-list-ul me-2"></i>
|
|
Listings
|
|
</h4>
|
|
<Link to="/create-item" className="btn btn-primary">
|
|
Add New Item
|
|
</Link>
|
|
</div>
|
|
|
|
{listings.length === 0 ? (
|
|
<div className="text-center py-5">
|
|
<p className="text-muted">You haven't listed any items yet.</p>
|
|
<Link to="/create-item" className="btn btn-primary mt-3">
|
|
List Your First Item
|
|
</Link>
|
|
</div>
|
|
) : (
|
|
<div className="row">
|
|
{listings.map((item) => (
|
|
<div key={item.id} className="col-md-6 col-lg-4 mb-4">
|
|
<div
|
|
className="card h-100"
|
|
style={{ cursor: "pointer" }}
|
|
onClick={(e: React.MouseEvent<HTMLDivElement>) => {
|
|
const target = e.target as HTMLElement;
|
|
if (target.closest("button") || target.closest("a")) {
|
|
return;
|
|
}
|
|
navigate(`/items/${item.id}`);
|
|
}}
|
|
>
|
|
{item.images && item.images[0] && (
|
|
<img
|
|
src={item.images[0]}
|
|
className="card-img-top"
|
|
alt={item.name}
|
|
style={{ height: "200px", objectFit: "cover" }}
|
|
/>
|
|
)}
|
|
<div className="card-body">
|
|
<h5 className="card-title text-dark">{item.name}</h5>
|
|
<p className="card-text text-truncate text-dark">
|
|
{item.description}
|
|
</p>
|
|
|
|
<div className="mb-2 d-flex gap-2 flex-wrap">
|
|
<span
|
|
className={`badge ${
|
|
item.isAvailable ? "bg-success" : "bg-secondary"
|
|
}`}
|
|
>
|
|
{item.isAvailable ? "Available" : "Not Available"}
|
|
</span>
|
|
{item.isDeleted && (
|
|
<span className="badge bg-danger">
|
|
<i className="bi bi-exclamation-triangle-fill me-1"></i>
|
|
Deleted by Admin
|
|
</span>
|
|
)}
|
|
</div>
|
|
|
|
<div className="mb-3">
|
|
{(() => {
|
|
const hasAnyPositivePrice =
|
|
(item.pricePerHour !== undefined &&
|
|
Number(item.pricePerHour) > 0) ||
|
|
(item.pricePerDay !== undefined &&
|
|
Number(item.pricePerDay) > 0) ||
|
|
(item.pricePerWeek !== undefined &&
|
|
Number(item.pricePerWeek) > 0) ||
|
|
(item.pricePerMonth !== undefined &&
|
|
Number(item.pricePerMonth) > 0);
|
|
|
|
const hasAnyZeroPrice =
|
|
(item.pricePerHour !== undefined &&
|
|
Number(item.pricePerHour) === 0) ||
|
|
(item.pricePerDay !== undefined &&
|
|
Number(item.pricePerDay) === 0) ||
|
|
(item.pricePerWeek !== undefined &&
|
|
Number(item.pricePerWeek) === 0) ||
|
|
(item.pricePerMonth !== undefined &&
|
|
Number(item.pricePerMonth) === 0);
|
|
|
|
if (!hasAnyPositivePrice && hasAnyZeroPrice) {
|
|
return (
|
|
<div className="text-success small fw-bold">
|
|
Free to Borrow
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<>
|
|
{item.pricePerDay && Number(item.pricePerDay) > 0 && (
|
|
<div className="text-muted small">
|
|
${item.pricePerDay}/day
|
|
</div>
|
|
)}
|
|
{item.pricePerHour &&
|
|
Number(item.pricePerHour) > 0 && (
|
|
<div className="text-muted small">
|
|
${item.pricePerHour}/hour
|
|
</div>
|
|
)}
|
|
{item.pricePerWeek &&
|
|
Number(item.pricePerWeek) > 0 && (
|
|
<div className="text-muted small">
|
|
${item.pricePerWeek}/week
|
|
</div>
|
|
)}
|
|
{item.pricePerMonth &&
|
|
Number(item.pricePerMonth) > 0 && (
|
|
<div className="text-muted small">
|
|
${item.pricePerMonth}/month
|
|
</div>
|
|
)}
|
|
</>
|
|
);
|
|
})()}
|
|
</div>
|
|
|
|
<div className="d-flex gap-2">
|
|
<Link
|
|
to={`/items/${item.id}/edit`}
|
|
className="btn btn-sm btn-outline-primary"
|
|
>
|
|
Edit
|
|
</Link>
|
|
<button
|
|
onClick={() => toggleAvailability(item)}
|
|
className="btn btn-sm btn-outline-info"
|
|
>
|
|
{item.isAvailable ? "Mark Unavailable" : "Mark Available"}
|
|
</button>
|
|
<button
|
|
onClick={() => handleDelete(item.id)}
|
|
className="btn btn-sm btn-outline-danger"
|
|
>
|
|
Delete
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
|
|
{/* Review Modal */}
|
|
{selectedRentalForReview && (
|
|
<ReviewRenterModal
|
|
show={showReviewRenterModal}
|
|
onClose={() => {
|
|
setShowReviewRenterModal(false);
|
|
setSelectedRentalForReview(null);
|
|
}}
|
|
rental={selectedRentalForReview}
|
|
onSuccess={handleReviewRenterSuccess}
|
|
/>
|
|
)}
|
|
|
|
{/* Cancellation Modal */}
|
|
{rentalToCancel && (
|
|
<RentalCancellationModal
|
|
show={showCancelModal}
|
|
onHide={() => {
|
|
setShowCancelModal(false);
|
|
setRentalToCancel(null);
|
|
}}
|
|
rental={rentalToCancel}
|
|
onCancellationComplete={handleCancellationComplete}
|
|
/>
|
|
)}
|
|
|
|
{/* Decline Modal */}
|
|
{rentalToDecline && (
|
|
<DeclineRentalModal
|
|
show={showDeclineModal}
|
|
onHide={() => {
|
|
setShowDeclineModal(false);
|
|
setRentalToDecline(null);
|
|
}}
|
|
rental={rentalToDecline}
|
|
onDeclineComplete={handleDeclineComplete}
|
|
/>
|
|
)}
|
|
|
|
{/* Condition Check Modal */}
|
|
{conditionCheckData && (
|
|
<ConditionCheckModal
|
|
show={showConditionCheckModal}
|
|
onHide={() => {
|
|
setShowConditionCheckModal(false);
|
|
setConditionCheckData(null);
|
|
}}
|
|
rentalId={conditionCheckData.rental.id}
|
|
checkType={conditionCheckData.checkType}
|
|
itemName={conditionCheckData.rental.item?.name || "Item"}
|
|
onSuccess={handleConditionCheckSuccess}
|
|
/>
|
|
)}
|
|
|
|
{/* Return Status Modal */}
|
|
{rentalForReturn && (
|
|
<ReturnStatusModal
|
|
show={showReturnStatusModal}
|
|
onHide={() => {
|
|
setShowReturnStatusModal(false);
|
|
setRentalForReturn(null);
|
|
}}
|
|
rental={rentalForReturn}
|
|
onReturnMarked={handleReturnStatusMarked}
|
|
onSubmitSuccess={handleReturnStatusMarked}
|
|
/>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default Owning;
|