badge when owner gets pending rental request
This commit is contained in:
@@ -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 {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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`),
|
||||||
|
|||||||
Reference in New Issue
Block a user