refactor mylistings and my rentals
This commit is contained in:
701
frontend/src/pages/Owning.tsx
Normal file
701
frontend/src/pages/Owning.tsx
Normal file
@@ -0,0 +1,701 @@
|
||||
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,
|
||||
availability: !item.availability,
|
||||
});
|
||||
setListings(
|
||||
listings.map((i) =>
|
||||
i.id === item.id ? { ...i, availability: !i.availability } : 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">
|
||||
<div className="d-flex justify-content-between align-items-center mb-4">
|
||||
<h1>Owning</h1>
|
||||
<Link to="/create-item" className="btn btn-primary">
|
||||
Add New Item
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{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>
|
||||
Rental Requests
|
||||
</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> {rental.renter.firstName}{" "}
|
||||
{rental.renter.lastName}
|
||||
</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.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>
|
||||
)}
|
||||
|
||||
<h4 className="mb-3">
|
||||
<i className="bi bi-list-ul me-2"></i>
|
||||
Listings
|
||||
</h4>
|
||||
|
||||
{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">
|
||||
<span
|
||||
className={`badge ${
|
||||
item.availability ? "bg-success" : "bg-secondary"
|
||||
}`}
|
||||
>
|
||||
{item.availability ? "Available" : "Not Available"}
|
||||
</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.availability
|
||||
? "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;
|
||||
Reference in New Issue
Block a user