badge when owner gets pending rental request

This commit is contained in:
jackiettran
2025-10-31 12:18:40 -04:00
parent 71ce2c63fb
commit 99aa0b3bdc
5 changed files with 95 additions and 3 deletions

View File

@@ -109,6 +109,28 @@ router.get("/my-listings", authenticateToken, async (req, res) => {
} }
}); });
// Get count of pending rental requests for owner
router.get("/pending-requests-count", authenticateToken, async (req, res) => {
try {
const count = await Rental.count({
where: {
ownerId: req.user.id,
status: "pending",
},
});
res.json({ count });
} catch (error) {
const reqLogger = logger.withRequestId(req.id);
reqLogger.error("Error getting pending rental count", {
error: error.message,
stack: error.stack,
userId: req.user.id,
});
res.status(500).json({ error: "Failed to get pending rental count" });
}
});
// Get rental by ID // Get rental by ID
router.get("/:id", authenticateToken, async (req, res) => { router.get("/:id", authenticateToken, async (req, res) => {
try { try {

View File

@@ -51,6 +51,9 @@ const DeclineRentalModal: React.FC<DeclineRentalModalProps> = ({
// Call parent callback with updated rental data if we have it // Call parent callback with updated rental data if we have it
if (updatedRental) { if (updatedRental) {
onDeclineComplete(updatedRental); onDeclineComplete(updatedRental);
// Notify Navbar to update pending count
window.dispatchEvent(new CustomEvent("rentalStatusChanged"));
} }
// Reset all states when closing // Reset all states when closing

View File

@@ -1,6 +1,7 @@
import React, { useState } from "react"; import React, { useState, useEffect } from "react";
import { Link, useNavigate } from "react-router-dom"; import { Link, useNavigate } from "react-router-dom";
import { useAuth } from "../contexts/AuthContext"; import { useAuth } from "../contexts/AuthContext";
import { rentalAPI } from "../services/api";
const Navbar: React.FC = () => { const Navbar: React.FC = () => {
const { user, logout, openAuthModal } = useAuth(); const { user, logout, openAuthModal } = useAuth();
@@ -9,6 +10,36 @@ const Navbar: React.FC = () => {
search: "", search: "",
location: "", location: "",
}); });
const [pendingRequestsCount, setPendingRequestsCount] = useState(0);
// Fetch pending rental requests count when user logs in
useEffect(() => {
const fetchPendingCount = async () => {
if (user) {
try {
const response = await rentalAPI.getPendingRequestsCount();
setPendingRequestsCount(response.data.count);
} catch (error) {
console.error("Failed to fetch pending requests count:", error);
}
} else {
setPendingRequestsCount(0);
}
};
fetchPendingCount();
// Listen for rental status changes to refresh count
const handleRentalStatusChange = () => {
fetchPendingCount();
};
window.addEventListener("rentalStatusChanged", handleRentalStatusChange);
return () => {
window.removeEventListener("rentalStatusChanged", handleRentalStatusChange);
};
}, [user]);
const handleLogout = () => { const handleLogout = () => {
logout(); logout();
@@ -123,8 +154,35 @@ const Navbar: React.FC = () => {
data-bs-toggle="dropdown" data-bs-toggle="dropdown"
aria-expanded="false" aria-expanded="false"
> >
<span style={{ display: "flex", alignItems: "center", position: "relative" }}>
{pendingRequestsCount > 0 && (
<span
style={{
position: "absolute",
left: "-0.9em",
top: "50%",
transform: "translateY(-50%)",
backgroundColor: "#dc3545",
color: "white",
borderRadius: "50%",
width: "1.5em",
height: "1.5em",
display: "flex",
alignItems: "center",
justifyContent: "center",
fontSize: "0.85em",
fontWeight: "bold",
border: "2px solid white",
opacity: 1,
zIndex: 1,
}}
>
{pendingRequestsCount}
</span>
)}
<i className="bi bi-person-circle me-1"></i> <i className="bi bi-person-circle me-1"></i>
{user.firstName} {user.firstName}
</span>
</a> </a>
<ul <ul
className="dropdown-menu" className="dropdown-menu"
@@ -144,6 +202,11 @@ const Navbar: React.FC = () => {
<li> <li>
<Link className="dropdown-item" to="/my-listings"> <Link className="dropdown-item" to="/my-listings">
<i className="bi bi-list-ul me-2"></i>Owning <i className="bi bi-list-ul me-2"></i>Owning
{pendingRequestsCount > 0 && (
<span className="badge bg-danger rounded-pill ms-2">
{pendingRequestsCount}
</span>
)}
</Link> </Link>
</li> </li>
<li> <li>

View File

@@ -164,6 +164,9 @@ const MyListings: React.FC = () => {
fetchOwnerRentals(); fetchOwnerRentals();
fetchAvailableChecks(); // Refresh available checks after rental confirmation fetchAvailableChecks(); // Refresh available checks after rental confirmation
// Notify Navbar to update pending count
window.dispatchEvent(new CustomEvent("rentalStatusChanged"));
} catch (err: any) { } catch (err: any) {
console.error("Failed to accept rental request:", err); console.error("Failed to accept rental request:", err);

View File

@@ -205,6 +205,7 @@ export const rentalAPI = {
createRental: (data: any) => api.post("/rentals", data), createRental: (data: any) => api.post("/rentals", data),
getMyRentals: () => api.get("/rentals/my-rentals"), getMyRentals: () => api.get("/rentals/my-rentals"),
getMyListings: () => api.get("/rentals/my-listings"), getMyListings: () => api.get("/rentals/my-listings"),
getPendingRequestsCount: () => api.get("/rentals/pending-requests-count"),
updateRentalStatus: (id: string, status: string) => updateRentalStatus: (id: string, status: string) =>
api.put(`/rentals/${id}/status`, { status }), api.put(`/rentals/${id}/status`, { status }),
markAsCompleted: (id: string) => api.post(`/rentals/${id}/mark-completed`), markAsCompleted: (id: string) => api.post(`/rentals/${id}/mark-completed`),