feat(listings): add featured boost to search and expose isFeatured in API responses
Featured listings now sort first in search results via featuredUntil desc ordering. All listing read DTOs (detail, search, seller) include isFeatured boolean and featuredUntil timestamp. Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
@@ -12,6 +12,8 @@ export interface ListingDetailData {
|
||||
viewCount: number;
|
||||
saveCount: number;
|
||||
inquiryCount: number;
|
||||
isFeatured: boolean;
|
||||
featuredUntil: string | null;
|
||||
publishedAt: string | null;
|
||||
createdAt: string;
|
||||
property: {
|
||||
@@ -63,6 +65,8 @@ export interface ListingSearchItem {
|
||||
transactionType: TransactionType;
|
||||
priceVND: string;
|
||||
pricePerM2: number | null;
|
||||
isFeatured: boolean;
|
||||
featuredUntil: string | null;
|
||||
viewCount: number;
|
||||
publishedAt: string | null;
|
||||
property: {
|
||||
@@ -92,6 +96,8 @@ export interface ListingSellerItem {
|
||||
status: ListingStatus;
|
||||
transactionType: TransactionType;
|
||||
priceVND: string;
|
||||
isFeatured: boolean;
|
||||
featuredUntil: string | null;
|
||||
property: {
|
||||
id: string;
|
||||
title: string;
|
||||
|
||||
@@ -34,6 +34,7 @@ export async function findByIdWithProperty(
|
||||
// location is NOT NULL in the database — geo extraction always succeeds for existing properties
|
||||
const geo = geoRows[0]!;
|
||||
|
||||
const now = new Date();
|
||||
return {
|
||||
id: listing.id,
|
||||
status: listing.status,
|
||||
@@ -45,6 +46,8 @@ export async function findByIdWithProperty(
|
||||
viewCount: listing.viewCount,
|
||||
saveCount: listing.saveCount,
|
||||
inquiryCount: listing.inquiryCount,
|
||||
isFeatured: listing.featuredUntil != null && listing.featuredUntil > now,
|
||||
featuredUntil: listing.featuredUntil?.toISOString() ?? null,
|
||||
publishedAt: listing.publishedAt?.toISOString() ?? null,
|
||||
createdAt: listing.createdAt.toISOString(),
|
||||
property: {
|
||||
@@ -116,7 +119,10 @@ export async function searchListings(
|
||||
where,
|
||||
skip,
|
||||
take: limit,
|
||||
orderBy: { createdAt: 'desc' },
|
||||
orderBy: [
|
||||
{ featuredUntil: { sort: 'desc', nulls: 'last' } },
|
||||
{ createdAt: 'desc' },
|
||||
],
|
||||
include: {
|
||||
property: {
|
||||
include: {
|
||||
@@ -146,6 +152,7 @@ export async function searchListings(
|
||||
}
|
||||
}
|
||||
|
||||
const now = new Date();
|
||||
return {
|
||||
data: data.map((listing) => {
|
||||
// location is NOT NULL — every property in the result set has geo data
|
||||
@@ -156,6 +163,8 @@ export async function searchListings(
|
||||
transactionType: listing.transactionType,
|
||||
priceVND: listing.priceVND.toString(),
|
||||
pricePerM2: listing.pricePerM2,
|
||||
isFeatured: listing.featuredUntil != null && listing.featuredUntil > now,
|
||||
featuredUntil: listing.featuredUntil?.toISOString() ?? null,
|
||||
viewCount: listing.viewCount,
|
||||
publishedAt: listing.publishedAt?.toISOString() ?? null,
|
||||
property: {
|
||||
@@ -213,12 +222,15 @@ export async function findBySellerIdQuery(
|
||||
prisma.listing.count({ where }),
|
||||
]);
|
||||
|
||||
const sellerNow = new Date();
|
||||
return {
|
||||
data: data.map((listing) => ({
|
||||
id: listing.id,
|
||||
status: listing.status,
|
||||
transactionType: listing.transactionType,
|
||||
priceVND: listing.priceVND.toString(),
|
||||
isFeatured: listing.featuredUntil != null && listing.featuredUntil > sellerNow,
|
||||
featuredUntil: listing.featuredUntil?.toISOString() ?? null,
|
||||
property: {
|
||||
id: listing.property.id,
|
||||
title: listing.property.title,
|
||||
|
||||
Reference in New Issue
Block a user