Compare commits

...

3 Commits

Author SHA1 Message Date
jackiettran
9c258177ae home page styling changes 2025-11-04 17:17:44 -05:00
jackiettran
6ec7858bbd added spacing 2025-11-03 15:41:37 -05:00
jackiettran
6d0beccea0 refactor mylistings and my rentals 2025-11-01 22:33:59 -04:00
18 changed files with 291 additions and 443 deletions

View File

@@ -53,7 +53,7 @@ const checkAndUpdateReviewVisibility = async (rental) => {
return rental;
};
router.get("/my-rentals", authenticateToken, async (req, res) => {
router.get("/renting", authenticateToken, async (req, res) => {
try {
const rentals = await Rental.findAll({
where: { renterId: req.user.id },
@@ -72,7 +72,7 @@ router.get("/my-rentals", authenticateToken, async (req, res) => {
res.json(rentals);
} catch (error) {
const reqLogger = logger.withRequestId(req.id);
reqLogger.error("Error in my-rentals route", {
reqLogger.error("Error in renting route", {
error: error.message,
stack: error.stack,
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 {
const rentals = await Rental.findAll({
where: { ownerId: req.user.id },
@@ -100,7 +100,7 @@ router.get("/my-listings", authenticateToken, async (req, res) => {
res.json(rentals);
} catch (error) {
const reqLogger = logger.withRequestId(req.id);
reqLogger.error("Error in my-listings route", {
reqLogger.error("Error in owning route", {
error: error.message,
stack: error.stack,
userId: req.user.id,

View File

@@ -442,7 +442,7 @@ class EmailService {
<p><strong>Rental Period:</strong> {{startDate}} to {{endDate}}</p>
{{earningsSection}}
{{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) {
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
const owner = await User.findByPk(rental.ownerId, {
@@ -754,7 +754,7 @@ class EmailService {
async sendRentalRequestConfirmationEmail(rental) {
const frontendUrl = process.env.FRONTEND_URL || "http://localhost:3000";
const viewRentalsUrl = `${frontendUrl}/my-rentals`;
const viewRentalsUrl = `${frontendUrl}/renting`;
// Fetch renter details
const renter = await User.findByPk(rental.renterId, {
@@ -1539,7 +1539,7 @@ class EmailService {
paymentMessage: paymentMessage,
earningsSection: earningsSection,
stripeSection: stripeSection,
rentalDetailsUrl: `${frontendUrl}/my-listings?rentalId=${rental.id}`,
rentalDetailsUrl: `${frontendUrl}/owning?rentalId=${rental.id}`,
};
const htmlContent = this.renderTemplate(
@@ -1615,7 +1615,7 @@ class EmailService {
</ul>
</div>
<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>
`;
} else {
@@ -1749,7 +1749,7 @@ class EmailService {
returnedDate: returnedDate,
earningsSection: earningsSection,
stripeSection: stripeSection,
myListingsUrl: `${frontendUrl}/my-listings`,
owningUrl: `${frontendUrl}/owning`,
};
const ownerHtmlContent = this.renderTemplate(

View File

@@ -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 style="text-align: center;">
<a href="{{myListingsUrl}}" class="button">View My Listings</a>
<a href="{{owningUrl}}" class="button">View My Listings</a>
</p>
<p>Thank you for being an excellent host on RentAll!</p>

View File

@@ -66,7 +66,7 @@ describe('Rentals Routes', () => {
jest.clearAllMocks();
});
describe('GET /my-rentals', () => {
describe('GET /renting', () => {
it('should get rentals for authenticated user', async () => {
const mockRentals = [
{
@@ -86,7 +86,7 @@ describe('Rentals Routes', () => {
mockRentalFindAll.mockResolvedValue(mockRentals);
const response = await request(app)
.get('/rentals/my-rentals');
.get('/rentals/renting');
expect(response.status).toBe(200);
expect(response.body).toEqual(mockRentals);
@@ -108,14 +108,14 @@ describe('Rentals Routes', () => {
mockRentalFindAll.mockRejectedValue(new Error('Database error'));
const response = await request(app)
.get('/rentals/my-rentals');
.get('/rentals/renting');
expect(response.status).toBe(500);
expect(response.body).toEqual({ error: 'Failed to fetch rentals' });
});
});
describe('GET /my-listings', () => {
describe('GET /owning', () => {
it('should get listings for authenticated user', async () => {
const mockListings = [
{
@@ -129,7 +129,7 @@ describe('Rentals Routes', () => {
mockRentalFindAll.mockResolvedValue(mockListings);
const response = await request(app)
.get('/rentals/my-listings');
.get('/rentals/owning');
expect(response.status).toBe(200);
expect(response.body).toEqual(mockListings);
@@ -151,7 +151,7 @@ describe('Rentals Routes', () => {
mockRentalFindAll.mockRejectedValue(new Error('Database error'));
const response = await request(app)
.get('/rentals/my-listings');
.get('/rentals/owning');
expect(response.status).toBe(500);
expect(response.body).toEqual({ error: 'Failed to fetch listings' });

View File

@@ -15,8 +15,8 @@ import ItemDetail from './pages/ItemDetail';
import EditItem from './pages/EditItem';
import RentItem from './pages/RentItem';
import CreateItem from './pages/CreateItem';
import MyRentals from './pages/MyRentals';
import MyListings from './pages/MyListings';
import Renting from './pages/Renting';
import Owning from './pages/Owning';
import Profile from './pages/Profile';
import PublicProfile from './pages/PublicProfile';
import Messages from './pages/Messages';
@@ -117,18 +117,18 @@ const AppContent: React.FC = () => {
}
/>
<Route
path="/my-rentals"
path="/renting"
element={
<PrivateRoute>
<MyRentals />
<Renting />
</PrivateRoute>
}
/>
<Route
path="/my-listings"
path="/owning"
element={
<PrivateRoute>
<MyListings />
<Owning />
</PrivateRoute>
}
/>

View File

@@ -6,9 +6,10 @@ interface AuthButtonProps {
className?: string;
children: React.ReactNode;
asLink?: boolean;
style?: React.CSSProperties;
}
const AuthButton: React.FC<AuthButtonProps> = ({ mode, className = '', children, asLink = false }) => {
const AuthButton: React.FC<AuthButtonProps> = ({ mode, className = '', children, asLink = false, style }) => {
const { openAuthModal } = useAuth();
const handleClick = (e: React.MouseEvent) => {
@@ -22,6 +23,7 @@ const AuthButton: React.FC<AuthButtonProps> = ({ mode, className = '', children,
href="#"
onClick={handleClick}
className={className}
style={style}
>
{children}
</a>
@@ -32,6 +34,7 @@ const AuthButton: React.FC<AuthButtonProps> = ({ mode, className = '', children,
<button
onClick={handleClick}
className={className}
style={style}
>
{children}
</button>

View File

@@ -4,139 +4,51 @@ import { Link } from 'react-router-dom';
const Footer: React.FC = () => {
return (
<footer className="bg-dark text-white">
<div className="container-fluid py-4" style={{ maxWidth: '1800px' }}>
<div className="row">
<div className="col-lg-3">
<h5 className="mb-3">
<i className="bi bi-box-seam me-2"></i>
CommunityRentals.App
</h5>
<p className="small text-white-50">
The marketplace for renting anything, from anyone, anywhere.
</p>
<div className="container py-4">
<div className="text-center">
{/* Social Media Icons */}
<div className="mb-3">
<a href="#" className="text-white-50 me-3">
<i className="bi bi-facebook"></i>
</a>
<a href="#" className="text-white-50 me-3">
<i className="bi bi-twitter"></i>
</a>
<a href="#" className="text-white-50 me-3">
<i className="bi bi-instagram"></i>
</a>
<a href="#" className="text-white-50">
<i className="bi bi-linkedin"></i>
</a>
</div>
<div className="col-lg-2 col-md-6">
<h6 className="mb-3">FAQ</h6>
<ul className="list-unstyled small">
<li className="mb-2">
<Link to="/faq/renters" className="text-decoration-none text-white-50">
Renter FAQ
</Link>
</li>
<li className="mb-2">
<Link to="/faq/owners" className="text-decoration-none text-white-50">
Owner FAQ
</Link>
</li>
<li className="mb-2">
<Link to="/faq/payments" className="text-decoration-none text-white-50">
Payments
</Link>
</li>
<li className="mb-2">
<Link to="/help" className="text-decoration-none text-white-50">
Help Center
</Link>
</li>
</ul>
</div>
<div className="col-lg-2 col-md-6">
<h6 className="mb-3">For Renters</h6>
<ul className="list-unstyled small">
<li className="mb-2">
<Link to="/items" className="text-decoration-none text-white-50">
Browse Items
</Link>
</li>
<li className="mb-2">
<Link to="/how-it-works" className="text-decoration-none text-white-50">
How It Works
</Link>
</li>
<li className="mb-2">
<Link to="/safety" className="text-decoration-none text-white-50">
Safety Tips
</Link>
</li>
</ul>
</div>
<div className="col-lg-2 col-md-6">
<h6 className="mb-3">For Owners</h6>
<ul className="list-unstyled small">
<li className="mb-2">
<Link to="/create-item" className="text-decoration-none text-white-50">
List an Item
</Link>
</li>
<li className="mb-2">
<Link to="/owner-guide" className="text-decoration-none text-white-50">
Owner Guide
</Link>
</li>
<li className="mb-2">
<Link to="/insurance" className="text-decoration-none text-white-50">
Insurance
</Link>
</li>
</ul>
</div>
<div className="col-lg-2 col-md-6">
<h6 className="mb-3">Company</h6>
<ul className="list-unstyled small">
<li className="mb-2">
<Link to="/about" className="text-decoration-none text-white-50">
About Us
</Link>
</li>
<li className="mb-2">
<Link to="/contact" className="text-decoration-none text-white-50">
Contact
</Link>
</li>
<li className="mb-2">
<Link to="/careers" className="text-decoration-none text-white-50">
Careers
</Link>
</li>
</ul>
</div>
<div className="col-lg-1 col-md-6">
<h6 className="mb-3">Follow Us</h6>
<div className="d-flex gap-3">
<a href="#" className="text-white-50">
<i className="bi bi-facebook"></i>
</a>
<a href="#" className="text-white-50">
<i className="bi bi-twitter"></i>
</a>
<a href="#" className="text-white-50">
<i className="bi bi-instagram"></i>
</a>
<a href="#" className="text-white-50">
<i className="bi bi-linkedin"></i>
</a>
</div>
</div>
</div>
<hr className="my-4 bg-secondary" />
<div className="row">
<div className="col-md-6">
<p className="small text-white-50 mb-0">
© 2025 CommunityRentals.App. All rights reserved.
</p>
</div>
<div className="col-md-6 text-md-end">
<Link to="/privacy" className="text-decoration-none text-white-50 small me-3">
{/* Essential Links */}
<div className="mb-3 small">
<Link to="/about" className="text-decoration-none text-white-50 me-2">
About
</Link>
<span className="text-white-50"></span>
<Link to="/contact" className="text-decoration-none text-white-50 mx-2">
Contact
</Link>
<span className="text-white-50"></span>
<Link to="/privacy" className="text-decoration-none text-white-50 mx-2">
Privacy Policy
</Link>
<Link to="/terms" className="text-decoration-none text-white-50 small">
<span className="text-white-50"></span>
<Link to="/terms" className="text-decoration-none text-white-50 ms-2">
Terms of Service
</Link>
</div>
{/* Copyright */}
<p className="small text-white-50 mb-0">
© 2025 CommunityRentals.App. All rights reserved.
</p>
</div>
</div>
</footer>
);
};
export default Footer;
export default Footer;

View File

@@ -111,10 +111,11 @@ const Navbar: React.FC = () => {
}
/>
<span
className="input-group-text bg-white text-muted"
className="input-group-text text-muted"
style={{
borderLeft: "0",
borderRight: "1px solid #dee2e6",
backgroundColor: "#f8f9fa",
}}
>
in
@@ -127,7 +128,6 @@ const Navbar: React.FC = () => {
onChange={(e) =>
handleSearchInputChange("location", e.target.value)
}
style={{ borderLeft: "0" }}
/>
<button className="btn btn-outline-secondary" type="submit">
<i className="bi bi-search"></i>
@@ -194,13 +194,13 @@ const Navbar: React.FC = () => {
</Link>
</li>
<li>
<Link className="dropdown-item" to="/my-rentals">
<Link className="dropdown-item" to="/renting">
<i className="bi bi-calendar-check me-2"></i>
Renting
</Link>
</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
{pendingRequestsCount > 0 && (
<span className="badge bg-danger rounded-pill ms-2">

View File

@@ -36,7 +36,7 @@ const EarningsDashboard: React.FC = () => {
const fetchEarningsData = async () => {
try {
// Get completed rentals where user is the owner
const response = await rentalAPI.getMyListings();
const response = await rentalAPI.getListings();
const rentals = response.data || [];
// Filter for completed rentals with earnings data

View File

@@ -173,7 +173,7 @@ const EditItem: React.FC = () => {
const fetchAcceptedRentals = async () => {
try {
const response = await rentalAPI.getMyListings();
const response = await rentalAPI.getListings();
const rentals: Rental[] = response.data;
// Filter for accepted rentals for this specific item
const itemRentals = rentals.filter(

View File

@@ -1,10 +1,10 @@
import React, { useState, useEffect } from 'react';
import { Link } from 'react-router-dom';
import { useAuth } from '../contexts/AuthContext';
import { itemAPI } from '../services/api';
import { Item } from '../types';
import ItemCard from '../components/ItemCard';
import AuthButton from '../components/AuthButton';
import React, { useState, useEffect } from "react";
import { Link } from "react-router-dom";
import { useAuth } from "../contexts/AuthContext";
import { itemAPI } from "../services/api";
import { Item } from "../types";
import ItemCard from "../components/ItemCard";
import AuthButton from "../components/AuthButton";
const Home: React.FC = () => {
const { user } = useAuth();
@@ -17,7 +17,7 @@ const Home: React.FC = () => {
const response = await itemAPI.getItems({ limit: 8 });
setFeaturedItems(response.data.items);
} catch (error) {
console.error('Error fetching items:', error);
console.error("Error fetching items:", error);
} finally {
setLoading(false);
}
@@ -28,70 +28,56 @@ const Home: React.FC = () => {
return (
<div>
{/* Hero Section */}
<div className="bg-primary text-white py-3">
<div className="container-fluid" style={{ maxWidth: '1800px' }}>
<div className="row align-items-center">
<div className="col-lg-6">
<h1 className="fs-2 fw-bold mb-2">
Rent Anything, From Anyone, Anywhere
</h1>
<p className="mb-3">
Join the sharing economy. Rent items you need for a fraction of the cost,
or earn money from things you already own.
</p>
<div className="d-flex gap-3 flex-wrap">
<Link to="/items" className="btn btn-light">
Start Renting
</Link>
{user ? (
<Link to="/create-item" className="btn btn-outline-light">
List Your Items
</Link>
) : (
<AuthButton mode="signup" className="btn btn-outline-light">
Start Earning
</AuthButton>
)}
</div>
</div>
<div className="col-lg-6 text-center d-none d-lg-block">
<i className="bi bi-box-seam" style={{ fontSize: '8rem', opacity: 0.3 }}></i>
</div>
</div>
</div>
</div>
{/* Featured Items Section */}
<div className="py-4 bg-light">
<div className="container-fluid" style={{ maxWidth: '1800px' }}>
<div className="d-flex justify-content-between align-items-center mb-4">
<h2 className="mb-0">Items for Rent</h2>
<Link to="/items" className="btn btn-link">
View All <i className="bi bi-arrow-right"></i>
</Link>
</div>
<div className="py-4" style={{ backgroundColor: "#2C3E50" }}>
<div className="container-fluid" style={{ maxWidth: "1800px" }}>
{loading ? (
<div className="text-center py-4">
<div className="spinner-border text-primary" role="status">
<div
className="spinner-border"
role="status"
style={{ color: "#4A90E2" }}
>
<span className="visually-hidden">Loading...</span>
</div>
</div>
) : (
<div className="row g-4">
{featuredItems.map((item) => (
<div key={item.id} className="col-md-6 col-lg-4 col-xl-3">
<ItemCard item={item} variant="compact" />
<>
<div className="row g-4">
{featuredItems.map((item) => (
<div key={item.id} className="col-md-6 col-lg-4 col-xl-3">
<ItemCard item={item} variant="compact" />
</div>
))}
</div>
{featuredItems.length > 0 && (
<div className="text-center mt-4">
<Link
to="/items"
className="btn btn-link"
style={{ color: "#4A90E2" }}
>
View All <i className="bi bi-arrow-right"></i>
</Link>
</div>
))}
</div>
)}
</>
)}
{!loading && featuredItems.length === 0 && (
<div className="text-center py-5">
<p className="text-muted">No items available for rent yet.</p>
<Link to="/create-item" className="btn btn-primary">
<p style={{ color: "rgba(255, 255, 255, 0.7)" }}>
No items available for rent yet.
</p>
<Link
to="/create-item"
className="btn"
style={{
backgroundColor: "#4A90E2",
color: "white",
borderColor: "#4A90E2",
}}
>
Be the first to list an item!
</Link>
</div>
@@ -99,40 +85,164 @@ const Home: React.FC = () => {
</div>
</div>
{/* Benefits Section */}
<div className="py-5" style={{ backgroundColor: "#34495e" }}>
<div className="container-fluid" style={{ maxWidth: "1800px" }}>
<div className="row align-items-center">
<div className="col-lg-6">
<h2 className="mb-4" style={{ color: "white" }}>
Why Choose CommunityRentals.App?
</h2>
<div className="d-flex mb-3">
<i
className="bi bi-check-circle-fill me-3"
style={{ fontSize: "1.5rem", color: "#7FB069" }}
></i>
<div>
<h5 style={{ color: "white" }}>Save Money</h5>
<p style={{ color: "rgba(255, 255, 255, 0.8)" }}>
Why buy when you can rent? Access items for a fraction of
the purchase price.
</p>
</div>
</div>
<div className="d-flex mb-3">
<i
className="bi bi-check-circle-fill me-3"
style={{ fontSize: "1.5rem", color: "#7FB069" }}
></i>
<div>
<h5 style={{ color: "white" }}>Earn Extra Income</h5>
<p style={{ color: "rgba(255, 255, 255, 0.8)" }}>
Turn your unused items into a revenue stream. Your garage
could be a goldmine.
</p>
</div>
</div>
<div className="d-flex mb-3">
<i
className="bi bi-check-circle-fill me-3"
style={{ fontSize: "1.5rem", color: "#7FB069" }}
></i>
<div>
<h5 style={{ color: "white" }}>Build Community</h5>
<p style={{ color: "rgba(255, 255, 255, 0.8)" }}>
Connect with neighbors and help each other. Sharing builds
stronger communities.
</p>
</div>
</div>
<div className="d-flex">
<i
className="bi bi-check-circle-fill me-3"
style={{ fontSize: "1.5rem", color: "#7FB069" }}
></i>
<div>
<h5 style={{ color: "white" }}>Reduce Waste</h5>
<p style={{ color: "rgba(255, 255, 255, 0.8)" }}>
Share instead of everyone buying. It's better for your
wallet and the planet.
</p>
</div>
</div>
</div>
<div className="col-lg-6 text-center">
<h2 className="mb-4" style={{ color: "white" }}>
Ready to Get Started?
</h2>
<p
className="lead mb-4"
style={{ color: "rgba(255, 255, 255, 0.9)" }}
>
Join thousands of people sharing and renting in your community
</p>
<div className="d-flex gap-3 justify-content-center flex-wrap">
{user ? (
<Link
to="/create-item"
className="btn btn-lg"
style={{
backgroundColor: "#7FB069",
color: "white",
borderColor: "#7FB069",
}}
>
Start Earning
</Link>
) : (
<AuthButton
mode="signup"
className="btn btn-lg"
style={{
backgroundColor: "#7FB069",
color: "white",
borderColor: "#7FB069",
}}
>
Start Earning
</AuthButton>
)}
<Link
to="/items"
className="btn btn-lg"
style={{
backgroundColor: "#4A90E2",
color: "white",
borderColor: "#4A90E2",
}}
>
Browse Rentals
</Link>
</div>
</div>
</div>
</div>
</div>
{/* How It Works - For Renters */}
<div className="py-5">
<div className="container-fluid" style={{ maxWidth: '1800px' }}>
<h2 className="text-center mb-5">For Renters: Get What You Need, When You Need It</h2>
<div className="container-fluid" style={{ maxWidth: "1800px" }}>
<h2 className="text-center mb-5">
For Renters: Get What You Need, When You Need It
</h2>
<div className="row g-4">
<div className="col-md-4 text-center">
<div className="mb-3">
<i className="bi bi-search text-primary" style={{ fontSize: '3rem' }}></i>
<i
className="bi bi-search"
style={{ fontSize: "3rem", color: "#4A90E2" }}
></i>
</div>
<h4>1. Find What You Need</h4>
<p className="text-muted">
Browse our marketplace for tools, equipment, electronics, and more.
Filter by location, price, and availability.
Browse our marketplace for tools, equipment, electronics, and
more. Filter by location, price, and availability.
</p>
</div>
<div className="col-md-4 text-center">
<div className="mb-3">
<i className="bi bi-calendar-check text-primary" style={{ fontSize: '3rem' }}></i>
<i
className="bi bi-calendar-check"
style={{ fontSize: "3rem", color: "#4A90E2" }}
></i>
</div>
<h4>2. Book Your Rental</h4>
<p className="text-muted">
Select your rental dates, choose delivery or pickup, and pay securely.
Your payment is held until the owner confirms.
Select your rental dates, choose delivery or pickup, and pay
securely. Your payment is held until the owner confirms.
</p>
</div>
<div className="col-md-4 text-center">
<div className="mb-3">
<i className="bi bi-box-arrow-right text-primary" style={{ fontSize: '3rem' }}></i>
<i
className="bi bi-box-arrow-right"
style={{ fontSize: "3rem", color: "#4A90E2" }}
></i>
</div>
<h4>3. Enjoy & Return</h4>
<p className="text-muted">
Use the item for your project or event, then return it as agreed.
Rate your experience to help the community.
Use the item for your project or event, then return it as
agreed. Rate your experience to help the community.
</p>
</div>
</div>
@@ -140,168 +250,56 @@ const Home: React.FC = () => {
</div>
{/* How It Works - For Owners */}
<div className="py-5 bg-light">
<div className="container-fluid" style={{ maxWidth: '1800px' }}>
<h2 className="text-center mb-5">For Owners: Turn Your Idle Items Into Income</h2>
<div className="py-5" style={{ backgroundColor: "#F7F9FB" }}>
<div className="container-fluid" style={{ maxWidth: "1800px" }}>
<h2 className="text-center mb-5">
For Owners: Turn Your Idle Items Into Income
</h2>
<div className="row g-4">
<div className="col-md-4 text-center">
<div className="mb-3">
<i className="bi bi-camera text-success" style={{ fontSize: '3rem' }}></i>
<i
className="bi bi-camera"
style={{ fontSize: "3rem", color: "#7FB069" }}
></i>
</div>
<h4>1. List Your Items</h4>
<p className="text-muted">
Take photos, set your price, and choose when your items are available.
List anything from power tools to party supplies.
Take photos, set your price, and choose when your items are
available. List anything from power tools to party supplies.
</p>
</div>
<div className="col-md-4 text-center">
<div className="mb-3">
<i className="bi bi-bell text-success" style={{ fontSize: '3rem' }}></i>
<i
className="bi bi-bell"
style={{ fontSize: "3rem", color: "#7FB069" }}
></i>
</div>
<h4>2. Accept Requests</h4>
<p className="text-muted">
Review rental requests and accept the ones that work for you.
Review rental requests and accept the ones that work for you.
Set your own rules and requirements.
</p>
</div>
<div className="col-md-4 text-center">
<div className="mb-3">
<i className="bi bi-cash-stack text-success" style={{ fontSize: '3rem' }}></i>
<i
className="bi bi-cash-stack"
style={{ fontSize: "3rem", color: "#7FB069" }}
></i>
</div>
<h4>3. Get Paid</h4>
<p className="text-muted">
Earn money from items sitting in your garage.
Payments are processed securely after each rental.
Earn money from items sitting in your garage. Payments are
processed securely after each rental.
</p>
</div>
</div>
</div>
</div>
{/* Popular Categories */}
<div className="py-5">
<div className="container-fluid" style={{ maxWidth: '1800px' }}>
<h2 className="text-center mb-5">Popular Rental Categories</h2>
<div className="row g-3">
<div className="col-6 col-md-4 col-lg-2">
<div className="card h-100 text-center border-0 shadow-sm">
<div className="card-body">
<i className="bi bi-tools text-primary mb-2" style={{ fontSize: '2rem' }}></i>
<h6 className="mb-0">Tools</h6>
</div>
</div>
</div>
<div className="col-6 col-md-4 col-lg-2">
<div className="card h-100 text-center border-0 shadow-sm">
<div className="card-body">
<i className="bi bi-camera-fill text-primary mb-2" style={{ fontSize: '2rem' }}></i>
<h6 className="mb-0">Electronics</h6>
</div>
</div>
</div>
<div className="col-6 col-md-4 col-lg-2">
<div className="card h-100 text-center border-0 shadow-sm">
<div className="card-body">
<i className="bi bi-bicycle text-primary mb-2" style={{ fontSize: '2rem' }}></i>
<h6 className="mb-0">Sports</h6>
</div>
</div>
</div>
<div className="col-6 col-md-4 col-lg-2">
<div className="card h-100 text-center border-0 shadow-sm">
<div className="card-body">
<i className="bi bi-music-note-beamed text-primary mb-2" style={{ fontSize: '2rem' }}></i>
<h6 className="mb-0">Music</h6>
</div>
</div>
</div>
<div className="col-6 col-md-4 col-lg-2">
<div className="card h-100 text-center border-0 shadow-sm">
<div className="card-body">
<i className="bi bi-balloon text-primary mb-2" style={{ fontSize: '2rem' }}></i>
<h6 className="mb-0">Party</h6>
</div>
</div>
</div>
<div className="col-6 col-md-4 col-lg-2">
<div className="card h-100 text-center border-0 shadow-sm">
<div className="card-body">
<i className="bi bi-tree text-primary mb-2" style={{ fontSize: '2rem' }}></i>
<h6 className="mb-0">Outdoor</h6>
</div>
</div>
</div>
</div>
</div>
</div>
{/* Benefits Section */}
<div className="py-5 bg-light">
<div className="container-fluid" style={{ maxWidth: '1800px' }}>
<div className="row align-items-center">
<div className="col-lg-6">
<h2 className="mb-4">Why Choose CommunityRentals.App?</h2>
<div className="d-flex mb-3">
<i className="bi bi-check-circle-fill text-success me-3" style={{ fontSize: '1.5rem' }}></i>
<div>
<h5>Save Money</h5>
<p className="text-muted">Why buy when you can rent? Access expensive items for a fraction of the purchase price.</p>
</div>
</div>
<div className="d-flex mb-3">
<i className="bi bi-check-circle-fill text-success me-3" style={{ fontSize: '1.5rem' }}></i>
<div>
<h5>Earn Extra Income</h5>
<p className="text-muted">Turn your unused items into a revenue stream. Your garage could be a goldmine.</p>
</div>
</div>
<div className="d-flex mb-3">
<i className="bi bi-check-circle-fill text-success me-3" style={{ fontSize: '1.5rem' }}></i>
<div>
<h5>Build Community</h5>
<p className="text-muted">Connect with neighbors and help each other. Sharing builds stronger communities.</p>
</div>
</div>
<div className="d-flex">
<i className="bi bi-check-circle-fill text-success me-3" style={{ fontSize: '1.5rem' }}></i>
<div>
<h5>Reduce Waste</h5>
<p className="text-muted">Share instead of everyone buying. It's better for your wallet and the planet.</p>
</div>
</div>
</div>
<div className="col-lg-6 text-center">
<i className="bi bi-shield-check" style={{ fontSize: '15rem', color: '#e0e0e0' }}></i>
</div>
</div>
</div>
</div>
{/* CTA Section */}
<div className="bg-primary text-white py-5">
<div className="container text-center">
<h2 className="mb-4">Ready to Get Started?</h2>
<p className="lead mb-4">
Join thousands of people sharing and renting in your community
</p>
<div className="d-flex gap-3 justify-content-center flex-wrap">
<Link to="/items" className="btn btn-light btn-lg">
Browse Rentals
</Link>
{user ? (
<Link to="/create-item" className="btn btn-outline-light btn-lg">
List an Item
</Link>
) : (
<AuthButton mode="signup" className="btn btn-outline-light btn-lg">
Sign Up Free
</AuthButton>
)}
</div>
</div>
</div>
</div>
);
};
export default Home;
export default Home;

View File

@@ -48,7 +48,7 @@ const ItemDetail: React.FC = () => {
const checkIfAlreadyRenting = async () => {
try {
const response = await rentalAPI.getMyRentals();
const response = await rentalAPI.getRentals();
const rentals: Rental[] = response.data;
// Check if user has an active rental for this item
const hasActiveRental = rentals.some(

View File

@@ -10,7 +10,7 @@ import DeclineRentalModal from "../components/DeclineRentalModal";
import ConditionCheckModal from "../components/ConditionCheckModal";
import ReturnStatusModal from "../components/ReturnStatusModal";
const MyListings: React.FC = () => {
const Owning: React.FC = () => {
// Helper function to format time
const formatTime = (timeString?: string) => {
if (!timeString || timeString.trim() === "") return "";
@@ -66,12 +66,12 @@ const MyListings: React.FC = () => {
const [rentalForReturn, setRentalForReturn] = useState<Rental | null>(null);
useEffect(() => {
fetchMyListings();
fetchListings();
fetchOwnerRentals();
fetchAvailableChecks();
}, [user]);
const fetchMyListings = async () => {
const fetchListings = async () => {
if (!user) return;
try {
@@ -124,7 +124,7 @@ const MyListings: React.FC = () => {
const fetchOwnerRentals = async () => {
try {
const response = await rentalAPI.getMyListings();
const response = await rentalAPI.getListings();
setOwnerRentals(response.data);
} catch (err: any) {
console.error("Failed to fetch owner rentals:", err);
@@ -698,4 +698,4 @@ const MyListings: React.FC = () => {
);
};
export default MyListings;
export default Owning;

View File

@@ -155,7 +155,7 @@ const Profile: React.FC = () => {
);
// 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 acceptedRentals = ownerRentals.filter((r) =>
@@ -194,14 +194,14 @@ const Profile: React.FC = () => {
const fetchRentalHistory = async () => {
try {
// Fetch past rentals as a renter
const renterResponse = await rentalAPI.getMyRentals();
const renterResponse = await rentalAPI.getRentals();
const pastRenterRentals = renterResponse.data.filter((r: Rental) =>
["completed", "cancelled"].includes(r.status)
);
setPastRenterRentals(pastRenterRentals);
// Fetch past rentals as an owner
const ownerResponse = await rentalAPI.getMyListings();
const ownerResponse = await rentalAPI.getListings();
const pastOwnerRentals = ownerResponse.data.filter((r: Rental) =>
["completed", "cancelled"].includes(r.status)
);
@@ -608,7 +608,7 @@ const Profile: React.FC = () => {
}
return (
<div className="container mt-4">
<div className="container mt-4 mb-5">
<h1 className="mb-4">Profile</h1>
{error && (
@@ -664,33 +664,6 @@ const Profile: React.FC = () => {
<i className="bi bi-person me-2"></i>
Personal Information
</button>
<button
className={`list-group-item list-group-item-action ${
activeSection === "notifications" ? "active" : ""
}`}
onClick={() => setActiveSection("notifications")}
>
<i className="bi bi-bell me-2"></i>
Notification Settings
</button>
<button
className={`list-group-item list-group-item-action ${
activeSection === "privacy" ? "active" : ""
}`}
onClick={() => setActiveSection("privacy")}
>
<i className="bi bi-shield-lock me-2"></i>
Privacy & Security
</button>
<button
className={`list-group-item list-group-item-action ${
activeSection === "payment" ? "active" : ""
}`}
onClick={() => setActiveSection("payment")}
>
<i className="bi bi-credit-card me-2"></i>
Payment Methods
</button>
<button
className="list-group-item list-group-item-action text-danger"
onClick={logout}
@@ -804,12 +777,6 @@ const Profile: React.FC = () => {
{profileData?.firstName} {profileData?.lastName}
</h5>
<p className="text-muted">@{profileData?.username}</p>
{profileData?.isVerified && (
<span className="badge bg-success mb-3">
<i className="bi bi-check-circle-fill"></i>{" "}
Verified
</span>
)}
<div>
<button
type="button"
@@ -1431,33 +1398,6 @@ const Profile: React.FC = () => {
</div>
</div>
)}
{/* Placeholder sections for other menu items */}
{activeSection === "notifications" && (
<div>
<h4 className="mb-4">Notification Settings</h4>
<div className="card">
<div className="card-body">
<p className="text-muted">
Notification preferences coming soon...
</p>
</div>
</div>
</div>
)}
{activeSection === "privacy" && (
<div>
<h4 className="mb-4">Privacy & Security</h4>
<div className="card">
<div className="card-body">
<p className="text-muted">
Privacy and security settings coming soon...
</p>
</div>
</div>
</div>
)}
</div>
</div>

View File

@@ -63,7 +63,7 @@ const PublicProfile: React.FC = () => {
}
return (
<div className="container mt-4">
<div className="container mt-4 mb-5">
<div className="row justify-content-center">
<div className="col-md-8">
<div className="card">
@@ -86,11 +86,6 @@ const PublicProfile: React.FC = () => {
)}
<h3>{user.firstName} {user.lastName}</h3>
<p className="text-muted">@{user.username}</p>
{user.isVerified && (
<span className="badge bg-success">
<i className="bi bi-check-circle-fill"></i> Verified User
</span>
)}
{currentUser && currentUser.id !== user.id && (
<button
className="btn btn-primary mt-3"

View File

@@ -223,7 +223,7 @@ const RentItem: React.FC = () => {
<div className="d-grid gap-2 d-md-block">
<button
className="btn btn-primary me-2"
onClick={() => navigate("/my-rentals")}
onClick={() => navigate("/renting")}
>
View My Rentals
</button>

View File

@@ -7,7 +7,7 @@ import ReviewItemModal from "../components/ReviewModal";
import RentalCancellationModal from "../components/RentalCancellationModal";
import ConditionCheckModal from "../components/ConditionCheckModal";
const MyRentals: React.FC = () => {
const Renting: React.FC = () => {
// Helper function to format time
const formatTime = (timeString?: string) => {
if (!timeString || timeString.trim() === "") return "";
@@ -66,7 +66,7 @@ const MyRentals: React.FC = () => {
const fetchRentals = async () => {
try {
const response = await rentalAPI.getMyRentals();
const response = await rentalAPI.getRentals();
setRentals(response.data);
} catch (err: any) {
setError(err.response?.data?.message || "Failed to fetch rentals");
@@ -470,4 +470,4 @@ const MyRentals: React.FC = () => {
);
};
export default MyRentals;
export default Renting;

View File

@@ -203,8 +203,8 @@ export const itemAPI = {
export const rentalAPI = {
createRental: (data: any) => api.post("/rentals", data),
getMyRentals: () => api.get("/rentals/my-rentals"),
getMyListings: () => api.get("/rentals/my-listings"),
getRentals: () => api.get("/rentals/renting"),
getListings: () => api.get("/rentals/owning"),
getPendingRequestsCount: () => api.get("/rentals/pending-requests-count"),
updateRentalStatus: (id: string, status: string) =>
api.put(`/rentals/${id}/status`, { status }),