refactor mylistings and my rentals
This commit is contained in:
@@ -53,7 +53,7 @@ const checkAndUpdateReviewVisibility = async (rental) => {
|
|||||||
return rental;
|
return rental;
|
||||||
};
|
};
|
||||||
|
|
||||||
router.get("/my-rentals", authenticateToken, async (req, res) => {
|
router.get("/renting", authenticateToken, async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const rentals = await Rental.findAll({
|
const rentals = await Rental.findAll({
|
||||||
where: { renterId: req.user.id },
|
where: { renterId: req.user.id },
|
||||||
@@ -72,7 +72,7 @@ router.get("/my-rentals", authenticateToken, async (req, res) => {
|
|||||||
res.json(rentals);
|
res.json(rentals);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const reqLogger = logger.withRequestId(req.id);
|
const reqLogger = logger.withRequestId(req.id);
|
||||||
reqLogger.error("Error in my-rentals route", {
|
reqLogger.error("Error in renting route", {
|
||||||
error: error.message,
|
error: error.message,
|
||||||
stack: error.stack,
|
stack: error.stack,
|
||||||
userId: req.user.id,
|
userId: req.user.id,
|
||||||
@@ -81,7 +81,7 @@ router.get("/my-rentals", authenticateToken, async (req, res) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get("/my-listings", authenticateToken, async (req, res) => {
|
router.get("/owning", authenticateToken, async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const rentals = await Rental.findAll({
|
const rentals = await Rental.findAll({
|
||||||
where: { ownerId: req.user.id },
|
where: { ownerId: req.user.id },
|
||||||
@@ -100,7 +100,7 @@ router.get("/my-listings", authenticateToken, async (req, res) => {
|
|||||||
res.json(rentals);
|
res.json(rentals);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const reqLogger = logger.withRequestId(req.id);
|
const reqLogger = logger.withRequestId(req.id);
|
||||||
reqLogger.error("Error in my-listings route", {
|
reqLogger.error("Error in owning route", {
|
||||||
error: error.message,
|
error: error.message,
|
||||||
stack: error.stack,
|
stack: error.stack,
|
||||||
userId: req.user.id,
|
userId: req.user.id,
|
||||||
|
|||||||
@@ -442,7 +442,7 @@ class EmailService {
|
|||||||
<p><strong>Rental Period:</strong> {{startDate}} to {{endDate}}</p>
|
<p><strong>Rental Period:</strong> {{startDate}} to {{endDate}}</p>
|
||||||
{{earningsSection}}
|
{{earningsSection}}
|
||||||
{{stripeSection}}
|
{{stripeSection}}
|
||||||
<p><a href="{{myListingsUrl}}" class="button">View My Listings</a></p>
|
<p><a href="{{owningUrl}}" class="button">View My Listings</a></p>
|
||||||
`
|
`
|
||||||
),
|
),
|
||||||
|
|
||||||
@@ -697,7 +697,7 @@ class EmailService {
|
|||||||
|
|
||||||
async sendRentalRequestEmail(rental) {
|
async sendRentalRequestEmail(rental) {
|
||||||
const frontendUrl = process.env.FRONTEND_URL || "http://localhost:3000";
|
const frontendUrl = process.env.FRONTEND_URL || "http://localhost:3000";
|
||||||
const approveUrl = `${frontendUrl}/my-listings?rentalId=${rental.id}`;
|
const approveUrl = `${frontendUrl}/owning?rentalId=${rental.id}`;
|
||||||
|
|
||||||
// Fetch owner details
|
// Fetch owner details
|
||||||
const owner = await User.findByPk(rental.ownerId, {
|
const owner = await User.findByPk(rental.ownerId, {
|
||||||
@@ -754,7 +754,7 @@ class EmailService {
|
|||||||
|
|
||||||
async sendRentalRequestConfirmationEmail(rental) {
|
async sendRentalRequestConfirmationEmail(rental) {
|
||||||
const frontendUrl = process.env.FRONTEND_URL || "http://localhost:3000";
|
const frontendUrl = process.env.FRONTEND_URL || "http://localhost:3000";
|
||||||
const viewRentalsUrl = `${frontendUrl}/my-rentals`;
|
const viewRentalsUrl = `${frontendUrl}/renting`;
|
||||||
|
|
||||||
// Fetch renter details
|
// Fetch renter details
|
||||||
const renter = await User.findByPk(rental.renterId, {
|
const renter = await User.findByPk(rental.renterId, {
|
||||||
@@ -1539,7 +1539,7 @@ class EmailService {
|
|||||||
paymentMessage: paymentMessage,
|
paymentMessage: paymentMessage,
|
||||||
earningsSection: earningsSection,
|
earningsSection: earningsSection,
|
||||||
stripeSection: stripeSection,
|
stripeSection: stripeSection,
|
||||||
rentalDetailsUrl: `${frontendUrl}/my-listings?rentalId=${rental.id}`,
|
rentalDetailsUrl: `${frontendUrl}/owning?rentalId=${rental.id}`,
|
||||||
};
|
};
|
||||||
|
|
||||||
const htmlContent = this.renderTemplate(
|
const htmlContent = this.renderTemplate(
|
||||||
@@ -1615,7 +1615,7 @@ class EmailService {
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<p style="text-align: center;">
|
<p style="text-align: center;">
|
||||||
<a href="${frontendUrl}/my-rentals?rentalId=${rental.id}&action=review" class="button">Leave a Review</a>
|
<a href="${frontendUrl}/renting?rentalId=${rental.id}&action=review" class="button">Leave a Review</a>
|
||||||
</p>
|
</p>
|
||||||
`;
|
`;
|
||||||
} else {
|
} else {
|
||||||
@@ -1749,7 +1749,7 @@ class EmailService {
|
|||||||
returnedDate: returnedDate,
|
returnedDate: returnedDate,
|
||||||
earningsSection: earningsSection,
|
earningsSection: earningsSection,
|
||||||
stripeSection: stripeSection,
|
stripeSection: stripeSection,
|
||||||
myListingsUrl: `${frontendUrl}/my-listings`,
|
owningUrl: `${frontendUrl}/owning`,
|
||||||
};
|
};
|
||||||
|
|
||||||
const ownerHtmlContent = this.renderTemplate(
|
const ownerHtmlContent = this.renderTemplate(
|
||||||
|
|||||||
@@ -355,7 +355,7 @@
|
|||||||
<p>Have more items sitting idle? Turn them into income! List camping gear, tools, party supplies, sports equipment, or anything else that others might need.</p>
|
<p>Have more items sitting idle? Turn them into income! List camping gear, tools, party supplies, sports equipment, or anything else that others might need.</p>
|
||||||
|
|
||||||
<p style="text-align: center;">
|
<p style="text-align: center;">
|
||||||
<a href="{{myListingsUrl}}" class="button">View My Listings</a>
|
<a href="{{owningUrl}}" class="button">View My Listings</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>Thank you for being an excellent host on RentAll!</p>
|
<p>Thank you for being an excellent host on RentAll!</p>
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ describe('Rentals Routes', () => {
|
|||||||
jest.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('GET /my-rentals', () => {
|
describe('GET /renting', () => {
|
||||||
it('should get rentals for authenticated user', async () => {
|
it('should get rentals for authenticated user', async () => {
|
||||||
const mockRentals = [
|
const mockRentals = [
|
||||||
{
|
{
|
||||||
@@ -86,7 +86,7 @@ describe('Rentals Routes', () => {
|
|||||||
mockRentalFindAll.mockResolvedValue(mockRentals);
|
mockRentalFindAll.mockResolvedValue(mockRentals);
|
||||||
|
|
||||||
const response = await request(app)
|
const response = await request(app)
|
||||||
.get('/rentals/my-rentals');
|
.get('/rentals/renting');
|
||||||
|
|
||||||
expect(response.status).toBe(200);
|
expect(response.status).toBe(200);
|
||||||
expect(response.body).toEqual(mockRentals);
|
expect(response.body).toEqual(mockRentals);
|
||||||
@@ -108,14 +108,14 @@ describe('Rentals Routes', () => {
|
|||||||
mockRentalFindAll.mockRejectedValue(new Error('Database error'));
|
mockRentalFindAll.mockRejectedValue(new Error('Database error'));
|
||||||
|
|
||||||
const response = await request(app)
|
const response = await request(app)
|
||||||
.get('/rentals/my-rentals');
|
.get('/rentals/renting');
|
||||||
|
|
||||||
expect(response.status).toBe(500);
|
expect(response.status).toBe(500);
|
||||||
expect(response.body).toEqual({ error: 'Failed to fetch rentals' });
|
expect(response.body).toEqual({ error: 'Failed to fetch rentals' });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('GET /my-listings', () => {
|
describe('GET /owning', () => {
|
||||||
it('should get listings for authenticated user', async () => {
|
it('should get listings for authenticated user', async () => {
|
||||||
const mockListings = [
|
const mockListings = [
|
||||||
{
|
{
|
||||||
@@ -129,7 +129,7 @@ describe('Rentals Routes', () => {
|
|||||||
mockRentalFindAll.mockResolvedValue(mockListings);
|
mockRentalFindAll.mockResolvedValue(mockListings);
|
||||||
|
|
||||||
const response = await request(app)
|
const response = await request(app)
|
||||||
.get('/rentals/my-listings');
|
.get('/rentals/owning');
|
||||||
|
|
||||||
expect(response.status).toBe(200);
|
expect(response.status).toBe(200);
|
||||||
expect(response.body).toEqual(mockListings);
|
expect(response.body).toEqual(mockListings);
|
||||||
@@ -151,7 +151,7 @@ describe('Rentals Routes', () => {
|
|||||||
mockRentalFindAll.mockRejectedValue(new Error('Database error'));
|
mockRentalFindAll.mockRejectedValue(new Error('Database error'));
|
||||||
|
|
||||||
const response = await request(app)
|
const response = await request(app)
|
||||||
.get('/rentals/my-listings');
|
.get('/rentals/owning');
|
||||||
|
|
||||||
expect(response.status).toBe(500);
|
expect(response.status).toBe(500);
|
||||||
expect(response.body).toEqual({ error: 'Failed to fetch listings' });
|
expect(response.body).toEqual({ error: 'Failed to fetch listings' });
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ import ItemDetail from './pages/ItemDetail';
|
|||||||
import EditItem from './pages/EditItem';
|
import EditItem from './pages/EditItem';
|
||||||
import RentItem from './pages/RentItem';
|
import RentItem from './pages/RentItem';
|
||||||
import CreateItem from './pages/CreateItem';
|
import CreateItem from './pages/CreateItem';
|
||||||
import MyRentals from './pages/MyRentals';
|
import Renting from './pages/Renting';
|
||||||
import MyListings from './pages/MyListings';
|
import Owning from './pages/Owning';
|
||||||
import Profile from './pages/Profile';
|
import Profile from './pages/Profile';
|
||||||
import PublicProfile from './pages/PublicProfile';
|
import PublicProfile from './pages/PublicProfile';
|
||||||
import Messages from './pages/Messages';
|
import Messages from './pages/Messages';
|
||||||
@@ -117,18 +117,18 @@ const AppContent: React.FC = () => {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<Route
|
<Route
|
||||||
path="/my-rentals"
|
path="/renting"
|
||||||
element={
|
element={
|
||||||
<PrivateRoute>
|
<PrivateRoute>
|
||||||
<MyRentals />
|
<Renting />
|
||||||
</PrivateRoute>
|
</PrivateRoute>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<Route
|
<Route
|
||||||
path="/my-listings"
|
path="/owning"
|
||||||
element={
|
element={
|
||||||
<PrivateRoute>
|
<PrivateRoute>
|
||||||
<MyListings />
|
<Owning />
|
||||||
</PrivateRoute>
|
</PrivateRoute>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -194,13 +194,13 @@ const Navbar: React.FC = () => {
|
|||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<Link className="dropdown-item" to="/my-rentals">
|
<Link className="dropdown-item" to="/renting">
|
||||||
<i className="bi bi-calendar-check me-2"></i>
|
<i className="bi bi-calendar-check me-2"></i>
|
||||||
Renting
|
Renting
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<Link className="dropdown-item" to="/my-listings">
|
<Link className="dropdown-item" to="/owning">
|
||||||
<i className="bi bi-list-ul me-2"></i>Owning
|
<i className="bi bi-list-ul me-2"></i>Owning
|
||||||
{pendingRequestsCount > 0 && (
|
{pendingRequestsCount > 0 && (
|
||||||
<span className="badge bg-danger rounded-pill ms-2">
|
<span className="badge bg-danger rounded-pill ms-2">
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ const EarningsDashboard: React.FC = () => {
|
|||||||
const fetchEarningsData = async () => {
|
const fetchEarningsData = async () => {
|
||||||
try {
|
try {
|
||||||
// Get completed rentals where user is the owner
|
// Get completed rentals where user is the owner
|
||||||
const response = await rentalAPI.getMyListings();
|
const response = await rentalAPI.getListings();
|
||||||
const rentals = response.data || [];
|
const rentals = response.data || [];
|
||||||
|
|
||||||
// Filter for completed rentals with earnings data
|
// Filter for completed rentals with earnings data
|
||||||
|
|||||||
@@ -173,7 +173,7 @@ const EditItem: React.FC = () => {
|
|||||||
|
|
||||||
const fetchAcceptedRentals = async () => {
|
const fetchAcceptedRentals = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await rentalAPI.getMyListings();
|
const response = await rentalAPI.getListings();
|
||||||
const rentals: Rental[] = response.data;
|
const rentals: Rental[] = response.data;
|
||||||
// Filter for accepted rentals for this specific item
|
// Filter for accepted rentals for this specific item
|
||||||
const itemRentals = rentals.filter(
|
const itemRentals = rentals.filter(
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ const ItemDetail: React.FC = () => {
|
|||||||
|
|
||||||
const checkIfAlreadyRenting = async () => {
|
const checkIfAlreadyRenting = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await rentalAPI.getMyRentals();
|
const response = await rentalAPI.getRentals();
|
||||||
const rentals: Rental[] = response.data;
|
const rentals: Rental[] = response.data;
|
||||||
// Check if user has an active rental for this item
|
// Check if user has an active rental for this item
|
||||||
const hasActiveRental = rentals.some(
|
const hasActiveRental = rentals.some(
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import DeclineRentalModal from "../components/DeclineRentalModal";
|
|||||||
import ConditionCheckModal from "../components/ConditionCheckModal";
|
import ConditionCheckModal from "../components/ConditionCheckModal";
|
||||||
import ReturnStatusModal from "../components/ReturnStatusModal";
|
import ReturnStatusModal from "../components/ReturnStatusModal";
|
||||||
|
|
||||||
const MyListings: React.FC = () => {
|
const Owning: React.FC = () => {
|
||||||
// Helper function to format time
|
// Helper function to format time
|
||||||
const formatTime = (timeString?: string) => {
|
const formatTime = (timeString?: string) => {
|
||||||
if (!timeString || timeString.trim() === "") return "";
|
if (!timeString || timeString.trim() === "") return "";
|
||||||
@@ -66,12 +66,12 @@ const MyListings: React.FC = () => {
|
|||||||
const [rentalForReturn, setRentalForReturn] = useState<Rental | null>(null);
|
const [rentalForReturn, setRentalForReturn] = useState<Rental | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchMyListings();
|
fetchListings();
|
||||||
fetchOwnerRentals();
|
fetchOwnerRentals();
|
||||||
fetchAvailableChecks();
|
fetchAvailableChecks();
|
||||||
}, [user]);
|
}, [user]);
|
||||||
|
|
||||||
const fetchMyListings = async () => {
|
const fetchListings = async () => {
|
||||||
if (!user) return;
|
if (!user) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -124,7 +124,7 @@ const MyListings: React.FC = () => {
|
|||||||
|
|
||||||
const fetchOwnerRentals = async () => {
|
const fetchOwnerRentals = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await rentalAPI.getMyListings();
|
const response = await rentalAPI.getListings();
|
||||||
setOwnerRentals(response.data);
|
setOwnerRentals(response.data);
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
console.error("Failed to fetch owner rentals:", err);
|
console.error("Failed to fetch owner rentals:", err);
|
||||||
@@ -698,4 +698,4 @@ const MyListings: React.FC = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default MyListings;
|
export default Owning;
|
||||||
@@ -155,7 +155,7 @@ const Profile: React.FC = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Fetch rentals where user is the owner (rentals on user's items)
|
// Fetch rentals where user is the owner (rentals on user's items)
|
||||||
const ownerRentalsResponse = await rentalAPI.getMyListings();
|
const ownerRentalsResponse = await rentalAPI.getListings();
|
||||||
const ownerRentals: Rental[] = ownerRentalsResponse.data;
|
const ownerRentals: Rental[] = ownerRentalsResponse.data;
|
||||||
|
|
||||||
const acceptedRentals = ownerRentals.filter((r) =>
|
const acceptedRentals = ownerRentals.filter((r) =>
|
||||||
@@ -194,14 +194,14 @@ const Profile: React.FC = () => {
|
|||||||
const fetchRentalHistory = async () => {
|
const fetchRentalHistory = async () => {
|
||||||
try {
|
try {
|
||||||
// Fetch past rentals as a renter
|
// Fetch past rentals as a renter
|
||||||
const renterResponse = await rentalAPI.getMyRentals();
|
const renterResponse = await rentalAPI.getRentals();
|
||||||
const pastRenterRentals = renterResponse.data.filter((r: Rental) =>
|
const pastRenterRentals = renterResponse.data.filter((r: Rental) =>
|
||||||
["completed", "cancelled"].includes(r.status)
|
["completed", "cancelled"].includes(r.status)
|
||||||
);
|
);
|
||||||
setPastRenterRentals(pastRenterRentals);
|
setPastRenterRentals(pastRenterRentals);
|
||||||
|
|
||||||
// Fetch past rentals as an owner
|
// Fetch past rentals as an owner
|
||||||
const ownerResponse = await rentalAPI.getMyListings();
|
const ownerResponse = await rentalAPI.getListings();
|
||||||
const pastOwnerRentals = ownerResponse.data.filter((r: Rental) =>
|
const pastOwnerRentals = ownerResponse.data.filter((r: Rental) =>
|
||||||
["completed", "cancelled"].includes(r.status)
|
["completed", "cancelled"].includes(r.status)
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -223,7 +223,7 @@ const RentItem: React.FC = () => {
|
|||||||
<div className="d-grid gap-2 d-md-block">
|
<div className="d-grid gap-2 d-md-block">
|
||||||
<button
|
<button
|
||||||
className="btn btn-primary me-2"
|
className="btn btn-primary me-2"
|
||||||
onClick={() => navigate("/my-rentals")}
|
onClick={() => navigate("/renting")}
|
||||||
>
|
>
|
||||||
View My Rentals
|
View My Rentals
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import ReviewItemModal from "../components/ReviewModal";
|
|||||||
import RentalCancellationModal from "../components/RentalCancellationModal";
|
import RentalCancellationModal from "../components/RentalCancellationModal";
|
||||||
import ConditionCheckModal from "../components/ConditionCheckModal";
|
import ConditionCheckModal from "../components/ConditionCheckModal";
|
||||||
|
|
||||||
const MyRentals: React.FC = () => {
|
const Renting: React.FC = () => {
|
||||||
// Helper function to format time
|
// Helper function to format time
|
||||||
const formatTime = (timeString?: string) => {
|
const formatTime = (timeString?: string) => {
|
||||||
if (!timeString || timeString.trim() === "") return "";
|
if (!timeString || timeString.trim() === "") return "";
|
||||||
@@ -66,7 +66,7 @@ const MyRentals: React.FC = () => {
|
|||||||
|
|
||||||
const fetchRentals = async () => {
|
const fetchRentals = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await rentalAPI.getMyRentals();
|
const response = await rentalAPI.getRentals();
|
||||||
setRentals(response.data);
|
setRentals(response.data);
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
setError(err.response?.data?.message || "Failed to fetch rentals");
|
setError(err.response?.data?.message || "Failed to fetch rentals");
|
||||||
@@ -470,4 +470,4 @@ const MyRentals: React.FC = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default MyRentals;
|
export default Renting;
|
||||||
@@ -203,8 +203,8 @@ export const itemAPI = {
|
|||||||
|
|
||||||
export const rentalAPI = {
|
export const rentalAPI = {
|
||||||
createRental: (data: any) => api.post("/rentals", data),
|
createRental: (data: any) => api.post("/rentals", data),
|
||||||
getMyRentals: () => api.get("/rentals/my-rentals"),
|
getRentals: () => api.get("/rentals/renting"),
|
||||||
getMyListings: () => api.get("/rentals/my-listings"),
|
getListings: () => api.get("/rentals/owning"),
|
||||||
getPendingRequestsCount: () => api.get("/rentals/pending-requests-count"),
|
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 }),
|
||||||
|
|||||||
Reference in New Issue
Block a user