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, ConditionCheck } from "../types"; import { rentalAPI, conditionCheckAPI } from "../services/api"; import { getImageUrl } from "../services/uploadService"; import ReviewRenterModal from "../components/ReviewRenterModal"; import RentalCancellationModal from "../components/RentalCancellationModal"; import DeclineRentalModal from "../components/DeclineRentalModal"; import ConditionCheckModal from "../components/ConditionCheckModal"; import ConditionCheckViewerModal from "../components/ConditionCheckViewerModal"; 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 [conditionChecks, setConditionChecks] = useState([]); const [showConditionCheckViewer, setShowConditionCheckViewer] = useState(false); const [selectedConditionCheck, setSelectedConditionCheck] = useState(null); const [showReturnStatusModal, setShowReturnStatusModal] = useState(false); const [rentalForReturn, setRentalForReturn] = useState(null); const [showDeleteModal, setShowDeleteModal] = useState(false); const [itemToDelete, setItemToDelete] = useState(null); useEffect(() => { fetchListings(); fetchOwnerRentals(); fetchAvailableChecks(); }, [user]); useEffect(() => { if (ownerRentals.length > 0) { fetchConditionChecks(); } }, [ownerRentals]); 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 handleDeleteClick = (item: Item) => { setItemToDelete(item); setShowDeleteModal(true); }; const handleDeleteConfirm = async () => { if (!itemToDelete) return; try { await api.delete(`/items/${itemToDelete.id}`); setListings(listings.filter((item) => item.id !== itemToDelete.id)); setShowDeleteModal(false); setItemToDelete(null); } catch (err: any) { setError("Failed to delete listing"); setShowDeleteModal(false); setItemToDelete(null); } }; 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([]); } }; const fetchConditionChecks = async () => { try { const allChecks: ConditionCheck[] = []; for (const rental of ownerRentals) { try { const response = await conditionCheckAPI.getConditionChecks( rental.id ); if (response.data.conditionChecks) { allChecks.push(...response.data.conditionChecks); } } catch (err) { // Skip rentals with no condition checks } } setConditionChecks(allChecks); } catch (err) { console.error("Failed to fetch condition checks:", err); setConditionChecks([]); } }; // 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(); fetchConditionChecks(); }; const handleViewConditionCheck = (check: ConditionCheck) => { setSelectedConditionCheck(check); setShowConditionCheckViewer(true); }; 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 ); }; const getCompletedChecksForRental = (rentalId: string) => { if (!Array.isArray(conditionChecks)) return []; return conditionChecks.filter((check) => check.rentalId === rentalId); }; // Filter owner rentals - exclude cancelled (shown in Rental History) // Use displayStatus for filtering/sorting as it includes computed "active" status const allOwnerRentals = ownerRentals .filter((r) => ["pending", "confirmed", "active"].includes(r.displayStatus || r.status)) .sort((a, b) => { const statusOrder = { pending: 0, confirmed: 1, active: 2 }; const aStatus = a.displayStatus || a.status; const bStatus = b.displayStatus || b.status; return ( statusOrder[aStatus as keyof typeof statusOrder] - statusOrder[bStatus 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} { const target = e.currentTarget; if (!target.dataset.fallback && rental.item) { target.dataset.fallback = 'true'; target.src = getImageUrl(rental.item.imageFilenames[0], 'original'); } }} style={{ height: "200px", objectFit: "contain", backgroundColor: "#f8f9fa", }} /> )}
{rental.item ? rental.item.name : "Item Unavailable"}
{rental.renter && (

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

)}
{(rental.displayStatus || rental.status).charAt(0).toUpperCase() + (rental.displayStatus || 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.displayStatus || rental.status) === "confirmed" && ( )} {(rental.displayStatus || rental.status) === "active" && ( )}
{/* Condition Check Status */} {getCompletedChecksForRental(rental.id).length > 0 && (
{getCompletedChecksForRental(rental.id).map( (check) => ( ) )}
)} {/* 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} { const target = e.currentTarget; if (!target.dataset.fallback) { target.dataset.fallback = 'true'; target.src = getImageUrl(item.imageFilenames[0], 'original'); } }} style={{ height: "200px", objectFit: "contain", backgroundColor: "#f8f9fa", }} /> )}
{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} /> )} {/* Condition Check Viewer Modal */} { setShowConditionCheckViewer(false); setSelectedConditionCheck(null); }} conditionCheck={selectedConditionCheck} /> {/* Delete Confirmation Modal */} {showDeleteModal && (
Delete Listing

Are you sure you want to delete {itemToDelete?.name}?

This action cannot be undone.

)}
); }; export default Owning;