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([]); const [ownerRentals, setOwnerRentals] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(""); // Owner rental management state const [showReviewRenterModal, setShowReviewRenterModal] = useState(false); const [selectedRentalForReview, setSelectedRentalForReview] = useState(null); const [showCancelModal, setShowCancelModal] = useState(false); const [rentalToCancel, setRentalToCancel] = useState(null); const [showDeclineModal, setShowDeclineModal] = useState(false); const [rentalToDecline, setRentalToDecline] = useState(null); const [isProcessingPayment, setIsProcessingPayment] = useState(""); const [processingSuccess, setProcessingSuccess] = useState(""); const [showConditionCheckModal, setShowConditionCheckModal] = useState(false); const [conditionCheckData, setConditionCheckData] = useState<{ rental: Rental; checkType: string; } | null>(null); const [availableChecks, setAvailableChecks] = useState([]); const [showReturnStatusModal, setShowReturnStatusModal] = useState(false); const [rentalForReturn, setRentalForReturn] = useState(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 (
Loading...
); } return (

Owning

{error && (
{error}
)} {/* Rental Requests Section - Moved to top for priority */} {allOwnerRentals.length > 0 && (

Rentals

{allOwnerRentals.map((rental) => (
{rental.item?.imageFilenames && rental.item.imageFilenames[0] && ( {rental.item.name} )}
{rental.item ? rental.item.name : "Item Unavailable"}
{rental.renter && (

Renter:{" "} navigate(`/users/${rental.renterId}`)} style={{ cursor: "pointer" }} > {rental.renter.firstName} {rental.renter.lastName}

)}
{rental.status.charAt(0).toUpperCase() + rental.status.slice(1)}

Period:
{formatDateTime(rental.startDateTime)} -{" "} {formatDateTime(rental.endDateTime)}

Total: ${rental.totalAmount}

{rental.intendedUse && rental.status === "pending" && (
Intended Use:
{rental.intendedUse}
)} {rental.status === "cancelled" && rental.refundAmount !== undefined && (
Refund: {" "} ${Number(rental.refundAmount || 0).toFixed(2)} {rental.refundProcessedAt && ( Processed:{" "} {new Date( rental.refundProcessedAt ).toLocaleDateString()} )} {rental.refundReason && ( {rental.refundReason} )} {rental.cancelledBy && ( Cancelled by: {rental.cancelledBy} )}
)} {rental.itemPrivateMessage && rental.itemReviewVisible && (
Private Note from Renter:
{rental.itemPrivateMessage}
)}
{rental.status === "pending" && ( <> )} {rental.status === "confirmed" && ( <> )} {rental.status === "active" && ( )}
{/* Condition Check Buttons */} {getAvailableChecksForRental(rental.id).map((check) => ( ))}
))}
)}

Listings

Add New Item
{listings.length === 0 ? (

You haven't listed any items yet.

List Your First Item
) : (
{listings.map((item) => (
) => { const target = e.target as HTMLElement; if (target.closest("button") || target.closest("a")) { return; } navigate(`/items/${item.id}`); }} > {item.imageFilenames && item.imageFilenames[0] && ( {item.name} )}
{item.name}

{item.description}

{item.isAvailable ? "Available" : "Not Available"} {item.isDeleted && ( Deleted by Admin )}
{(() => { 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 (
Free to Borrow
); } return ( <> {item.pricePerDay && Number(item.pricePerDay) > 0 && (
${item.pricePerDay}/day
)} {item.pricePerHour && Number(item.pricePerHour) > 0 && (
${item.pricePerHour}/hour
)} {item.pricePerWeek && Number(item.pricePerWeek) > 0 && (
${item.pricePerWeek}/week
)} {item.pricePerMonth && Number(item.pricePerMonth) > 0 && (
${item.pricePerMonth}/month
)} ); })()}
Edit
))}
)} {/* Review Modal */} {selectedRentalForReview && ( { setShowReviewRenterModal(false); setSelectedRentalForReview(null); }} rental={selectedRentalForReview} onSuccess={handleReviewRenterSuccess} /> )} {/* Cancellation Modal */} {rentalToCancel && ( { setShowCancelModal(false); setRentalToCancel(null); }} rental={rentalToCancel} onCancellationComplete={handleCancellationComplete} /> )} {/* Decline Modal */} {rentalToDecline && ( { setShowDeclineModal(false); setRentalToDecline(null); }} rental={rentalToDecline} onDeclineComplete={handleDeclineComplete} /> )} {/* Condition Check Modal */} {conditionCheckData && ( { setShowConditionCheckModal(false); setConditionCheckData(null); }} rentalId={conditionCheckData.rental.id} checkType={conditionCheckData.checkType} itemName={conditionCheckData.rental.item?.name || "Item"} onSuccess={handleConditionCheckSuccess} /> )} {/* Return Status Modal */} {rentalForReturn && ( { setShowReturnStatusModal(false); setRentalForReturn(null); }} rental={rentalForReturn} onReturnMarked={handleReturnStatusMarked} onSubmitSuccess={handleReturnStatusMarked} /> )}
); }; export default Owning;