fix: price sorting by period type, pagination, avgRating on dashboard
- Sort by monthlyPrice when periodType=MONTHLY instead of always dailyPrice - Fix client-side pagination in MyBookingsPage and LandlordListingsPage - Compute avgRating in /rentals/mine endpoint so dashboard shows ratings - Fix falsy filter for avgRating=0 on landlord dashboard Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -17,7 +17,6 @@ const STATUS_TABS: (BookingStatus | 'ALL')[] = ['ALL', 'PENDING', 'CONFIRMED', '
|
|||||||
export function MyBookingsPage() {
|
export function MyBookingsPage() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [bookings, setBookings] = useState<Booking[]>([]);
|
const [bookings, setBookings] = useState<Booking[]>([]);
|
||||||
const [total, setTotal] = useState(0);
|
|
||||||
const [page, setPage] = useState(1);
|
const [page, setPage] = useState(1);
|
||||||
const [tab, setTab] = useState<BookingStatus | 'ALL'>('ALL');
|
const [tab, setTab] = useState<BookingStatus | 'ALL'>('ALL');
|
||||||
const [, setLoading] = useState(true);
|
const [, setLoading] = useState(true);
|
||||||
@@ -37,7 +36,6 @@ export function MyBookingsPage() {
|
|||||||
try {
|
try {
|
||||||
const res = await api.get<Booking[]>(`/bookings?${params}`);
|
const res = await api.get<Booking[]>(`/bookings?${params}`);
|
||||||
setBookings(res);
|
setBookings(res);
|
||||||
setTotal(res.length);
|
|
||||||
} catch {
|
} catch {
|
||||||
setBookings([]);
|
setBookings([]);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -170,8 +168,8 @@ export function MyBookingsPage() {
|
|||||||
render: (b: Booking) => <BookingStatusBadge status={b.status} />,
|
render: (b: Booking) => <BookingStatusBadge status={b.status} />,
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
data={bookings}
|
data={bookings.slice((page - 1) * 20, page * 20)}
|
||||||
total={total}
|
total={bookings.length}
|
||||||
page={page}
|
page={page}
|
||||||
pageSize={20}
|
pageSize={20}
|
||||||
onPageChange={setPage}
|
onPageChange={setPage}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ export function LandlordDashboardPage() {
|
|||||||
.filter((b) => b.status === 'COMPLETED')
|
.filter((b) => b.status === 'COMPLETED')
|
||||||
.reduce((sum, b) => sum + b.totalAmount, 0);
|
.reduce((sum, b) => sum + b.totalAmount, 0);
|
||||||
|
|
||||||
const ratings = rentalsRes.filter((r) => r.avgRating).map((r) => r.avgRating!);
|
const ratings = rentalsRes.filter((r) => r.avgRating !== undefined && r.avgRating !== null).map((r) => r.avgRating!);
|
||||||
const avgRating = ratings.length > 0 ? ratings.reduce((a, b) => a + b, 0) / ratings.length : 0;
|
const avgRating = ratings.length > 0 ? ratings.reduce((a, b) => a + b, 0) / ratings.length : 0;
|
||||||
|
|
||||||
setStats({ totalRentals, activeBookings, revenue, avgRating });
|
setStats({ totalRentals, activeBookings, revenue, avgRating });
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import type { RentalListing } from '../../types/rental';
|
|||||||
|
|
||||||
export function LandlordListingsPage() {
|
export function LandlordListingsPage() {
|
||||||
const [listings, setListings] = useState<RentalListing[]>([]);
|
const [listings, setListings] = useState<RentalListing[]>([]);
|
||||||
const [total, setTotal] = useState(0);
|
|
||||||
const [page, setPage] = useState(1);
|
const [page, setPage] = useState(1);
|
||||||
const [search, setSearch] = useState('');
|
const [search, setSearch] = useState('');
|
||||||
|
|
||||||
@@ -19,7 +18,6 @@ export function LandlordListingsPage() {
|
|||||||
if (search) params.set('search', search);
|
if (search) params.set('search', search);
|
||||||
const res = await api.get<RentalListing[]>(`/rentals/mine?${params}`);
|
const res = await api.get<RentalListing[]>(`/rentals/mine?${params}`);
|
||||||
setListings(res);
|
setListings(res);
|
||||||
setTotal(res.length);
|
|
||||||
} catch {
|
} catch {
|
||||||
// silently fail
|
// silently fail
|
||||||
}
|
}
|
||||||
@@ -143,8 +141,8 @@ export function LandlordListingsPage() {
|
|||||||
render: (l: RentalListing) => l._count?.bookings ?? 0,
|
render: (l: RentalListing) => l._count?.bookings ?? 0,
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
data={listings}
|
data={listings.slice((page - 1) * 20, page * 20)}
|
||||||
total={total}
|
total={listings.length}
|
||||||
page={page}
|
page={page}
|
||||||
pageSize={20}
|
pageSize={20}
|
||||||
onPageChange={setPage}
|
onPageChange={setPage}
|
||||||
|
|||||||
@@ -37,10 +37,16 @@ router.get('/mine', authenticate, async (req, res, next) => {
|
|||||||
|
|
||||||
const listings = await prisma.rentalListing.findMany({
|
const listings = await prisma.rentalListing.findMany({
|
||||||
where,
|
where,
|
||||||
select: rentalSelect,
|
select: { ...rentalSelect, reviews: { select: { rating: true } } },
|
||||||
orderBy: { createdAt: 'desc' },
|
orderBy: { createdAt: 'desc' },
|
||||||
});
|
});
|
||||||
res.json(listings);
|
const withRatings = listings.map(l => ({
|
||||||
|
...l,
|
||||||
|
avgRating: l.reviews.length > 0
|
||||||
|
? l.reviews.reduce((sum, r) => sum + r.rating, 0) / l.reviews.length
|
||||||
|
: undefined,
|
||||||
|
}));
|
||||||
|
res.json(withRatings);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
next(error);
|
next(error);
|
||||||
}
|
}
|
||||||
@@ -111,8 +117,9 @@ router.get('/', optionalAuth, async (req, res, next) => {
|
|||||||
where.dailyPrice = priceFilter;
|
where.dailyPrice = priceFilter;
|
||||||
}
|
}
|
||||||
|
|
||||||
const orderBy = sort === 'price_asc' ? { dailyPrice: 'asc' as const }
|
const priceField = periodType === 'MONTHLY' ? 'monthlyPrice' : 'dailyPrice';
|
||||||
: sort === 'price_desc' ? { dailyPrice: 'desc' as const }
|
const orderBy = sort === 'price_asc' ? { [priceField]: 'asc' as const }
|
||||||
|
: sort === 'price_desc' ? { [priceField]: 'desc' as const }
|
||||||
: sort === 'popular' ? { viewCount: 'desc' as const }
|
: sort === 'popular' ? { viewCount: 'desc' as const }
|
||||||
: { createdAt: 'desc' as const };
|
: { createdAt: 'desc' as const };
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user