From 376d801a6f884b4023bc31f179e7fffa01cff243 Mon Sep 17 00:00:00 2001 From: Francis Anthony Date: Fri, 23 Aug 2024 14:53:27 +0800 Subject: [PATCH] Update listings when new search filter is applied refs #49 --- app/api/listings/route.ts | 7 ++++ app/listings/actions.ts | 1 + app/listings/content.tsx | 18 ++++++++-- app/listings/function.ts | 3 -- app/listings/functions.ts | 67 ++++++++++++++++++++++++++++++++++++++ app/listings/list.tsx | 2 +- app/listings/types.ts | 53 ++++++++---------------------- tests/app/listings.test.ts | 2 +- 8 files changed, 105 insertions(+), 48 deletions(-) create mode 100644 app/api/listings/route.ts delete mode 100644 app/listings/function.ts create mode 100644 app/listings/functions.ts diff --git a/app/api/listings/route.ts b/app/api/listings/route.ts new file mode 100644 index 00000000..12430174 --- /dev/null +++ b/app/api/listings/route.ts @@ -0,0 +1,7 @@ +import { fetchMatchedListings } from "@/app/listings/actions"; + +export async function GET(request: Request) { + // TODO: Read search filters from request, try HTTP headers? + const listings = await fetchMatchedListings(); + return Response.json({ listings }); +} diff --git a/app/listings/actions.ts b/app/listings/actions.ts index 13766da3..38a27089 100644 --- a/app/listings/actions.ts +++ b/app/listings/actions.ts @@ -36,6 +36,7 @@ const prismaListingMapper = (dbListing: PrismaListing) => { return listing; }; +// TODO: Expost this as an endpoint through Route Handlers export async function fetchMatchedListings( filters: ListingsSearchFilters = DEFAULT_LIST_FILTERS ): Promise { diff --git a/app/listings/content.tsx b/app/listings/content.tsx index bb5d6c11..88f562cd 100644 --- a/app/listings/content.tsx +++ b/app/listings/content.tsx @@ -1,21 +1,33 @@ "use client" -import { useContext } from "react" +import { useContext, useEffect } from "react" +import { ListingSortCompareFunctions, makeSearchFiltersRequest } from "./functions" import ListingsList from "./list" import { ListingsListCountSort } from "./list-count-sort" import { ListingsMap } from "./map" import ListingsPagination from "./pagination" import { ListingsContext } from "./provider" -import { Listing, ListingSortCompareFunctions } from "./types" +import { Listing } from "./types" interface ListingsContentInterface { listings: Listing[] } export default function ListingsContent(props: ListingsContentInterface) { - // TODO: Use context filter values, try to use useMemo if applicable + // TODO: try to use useMemo if applicable const context = useContext(ListingsContext); + useEffect(() => { + async function updateListings() { + const searchFiltersRequest = makeSearchFiltersRequest(context.searchFilters); + // TODO: Look for a way to pass search filters into HTTP request, try HTTP headers? + const listings = await fetch("/api/listings"); + const json = await listings.json() + console.log(json); + } + updateListings() + }, [context.searchFilters]) + const sortedListings = props.listings.sort(ListingSortCompareFunctions.choose(context.sort.value)) return ( diff --git a/app/listings/function.ts b/app/listings/function.ts deleted file mode 100644 index 3cf3b2f9..00000000 --- a/app/listings/function.ts +++ /dev/null @@ -1,3 +0,0 @@ -export function countListingsToSkip(currentPage: number, countPerPage: number) { - return (currentPage - 1) * countPerPage; -} diff --git a/app/listings/functions.ts b/app/listings/functions.ts new file mode 100644 index 00000000..41372384 --- /dev/null +++ b/app/listings/functions.ts @@ -0,0 +1,67 @@ +import { + Listing, + ListingSort, + ListingsSearchFilters, + ListingsSearchFiltersRequest, +} from "./types"; + +export function countListingsToSkip(currentPage: number, countPerPage: number) { + return (currentPage - 1) * countPerPage; +} + +export function makeSearchFiltersRequest( + original: ListingsSearchFilters +): ListingsSearchFiltersRequest { + return { + price: { + min: original.price.min.value, + max: original.price.max.value, + }, + beds: original.beds.value, + baths: original.baths.value, + area: { + min: original.area.min.value, + max: original.area.max.value, + }, + }; +} + +type ListingSortCompareFunction = (a: Listing, b: Listing) => number; + +export class ListingSortCompareFunctions { + static #NEWEST: ListingSortCompareFunction = (a, b) => { + if (a.createdDate < b.createdDate) { + return 1; + } + + if (a.createdDate === b.createdDate) { + return 0; + } + + return -1; + }; + + static #PRICE_DESC: ListingSortCompareFunction = (a, b) => { + return b.price.value - a.price.value; + }; + + static #PRICE_ASC: ListingSortCompareFunction = (a, b) => { + return a.price.value - b.price.value; + }; + + static choose(sort: ListingSort): ListingSortCompareFunction { + switch (sort) { + case ListingSort.NEWEST: + return ListingSortCompareFunctions.#NEWEST; + break; + case ListingSort.PRICE_DESC: + return ListingSortCompareFunctions.#PRICE_DESC; + break; + case ListingSort.PRICE_ASC: + return ListingSortCompareFunctions.#PRICE_ASC; + break; + default: + return ListingSortCompareFunctions.#NEWEST; + } + } +} diff --git a/app/listings/list.tsx b/app/listings/list.tsx index 33f2fab7..30db4f79 100644 --- a/app/listings/list.tsx +++ b/app/listings/list.tsx @@ -4,7 +4,7 @@ import CardListing from "@/components/card-listing"; import { CURRENCY_FORMATTER } from "@/lib/formatter/currency"; import { useContext } from "react"; import { LISTINGS_PER_PAGE } from "./constants"; -import { countListingsToSkip } from "./function"; +import { countListingsToSkip } from "./functions"; import { ListingsContext } from "./provider"; import { Listing } from "./types"; diff --git a/app/listings/types.ts b/app/listings/types.ts index c30efd5a..552237f5 100644 --- a/app/listings/types.ts +++ b/app/listings/types.ts @@ -61,46 +61,6 @@ export enum ListingSort { PRICE_ASC = "Price (low to high)", } -type ListingSortCompareFunction = (a: Listing, b: Listing) => number; - -export class ListingSortCompareFunctions { - static #NEWEST: ListingSortCompareFunction = (a, b) => { - if (a.createdDate < b.createdDate) { - return 1; - } - - if (a.createdDate === b.createdDate) { - return 0; - } - - return -1; - }; - - static #PRICE_DESC: ListingSortCompareFunction = (a, b) => { - return b.price.value - a.price.value; - }; - - static #PRICE_ASC: ListingSortCompareFunction = (a, b) => { - return a.price.value - b.price.value; - }; - - static choose(sort: ListingSort): ListingSortCompareFunction { - switch (sort) { - case ListingSort.NEWEST: - return ListingSortCompareFunctions.#NEWEST; - break; - case ListingSort.PRICE_DESC: - return ListingSortCompareFunctions.#PRICE_DESC; - break; - case ListingSort.PRICE_ASC: - return ListingSortCompareFunctions.#PRICE_ASC; - break; - default: - return ListingSortCompareFunctions.#NEWEST; - } - } -} - export type ListingContextType = { value: Type; change: (value: Type) => void; @@ -118,6 +78,19 @@ export interface ListingsSearchFilters { area: ContenTypeNumberRange; } +export interface ListingsSearchFiltersRequest { + price: { + min: number; + max: number; + }; + beds: BedsBathsOption; + baths: BedsBathsOption; + area: { + min: number; + max: number; + }; +} + export enum BedsBathsOption { ANY = "Any", ONE = "1", diff --git a/tests/app/listings.test.ts b/tests/app/listings.test.ts index 88695303..3495d950 100644 --- a/tests/app/listings.test.ts +++ b/tests/app/listings.test.ts @@ -1,4 +1,4 @@ -import { countListingsToSkip } from "../../app/listings/function"; +import { countListingsToSkip } from "../../app/listings/functions"; describe("Listings", () => { test.each([