Skip to content

Commit

Permalink
Refactor data fetching composition
Browse files Browse the repository at this point in the history
refs #49
  • Loading branch information
franthormel committed Aug 7, 2024
1 parent 04c086f commit 976e52a
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 79 deletions.
72 changes: 31 additions & 41 deletions app/listings/actions.ts
Original file line number Diff line number Diff line change
@@ -1,52 +1,42 @@
"use server";

import prisma from "@/lib/db";
import { LISTINGS_PER_PAGE } from "./constants";
import { countListingsToSkip } from "./function";
import { Listing } from "./types";
import { Listing, PrismaListing } from "./types";

export async function countMatchedListings(): Promise<number> {
// TODO: Add filter param values
return await prisma.listing.count();
}
const prismaListingMapper = (dbListing: PrismaListing) => {
const dbListingPrice = dbListing.prices
.filter((dbListingPrice) => dbListingPrice.isCurrent)
.at(0)!
.value.toNumber();
const dbListingAddress = dbListing.address!;
const listing: Listing = {
id: dbListing.id,
imageUrls: dbListing.imageUrls,
beds: dbListing.beds,
baths: dbListing.baths,
area: dbListing.area.toNumber(),
createdDate: dbListing.createdDate,
address: {
addressLine: dbListingAddress.addressLine,
city: dbListingAddress.city,
state: dbListingAddress.state,
longitude: dbListingAddress.longitude.toNumber(),
latitude: dbListingAddress.latitude.toNumber(),
},
price: {
value: dbListingPrice,
},
};
return listing;
};

export async function fetchListingsByPage(
currentPage: number
): Promise<Listing[]> {
const dbListings = await prisma.listing.findMany({
skip: countListingsToSkip(currentPage, LISTINGS_PER_PAGE),
take: LISTINGS_PER_PAGE,
export async function fetchMatchedListings(): Promise<Listing[]> {
// TODO: Add filter param values
const prismaListings = await prisma.listing.findMany({
include: {
address: true,
prices: true,
},
});
const processedListings = dbListings.map((dbListing) => {
const dbListingPrice = dbListing.prices
.filter((dbListingPrice) => dbListingPrice.isCurrent)
.at(0)!
.value.toNumber();
const dbListingAddress = dbListing.address!;
const listing: Listing = {
id: dbListing.id,
imageUrls: dbListing.imageUrls,
beds: dbListing.beds,
baths: dbListing.baths,
area: dbListing.area.toNumber(),
createdDate: dbListing.createdDate,
address: {
addressLine: dbListingAddress.addressLine,
city: dbListingAddress.city,
state: dbListingAddress.state,
longitude: dbListingAddress.longitude.toNumber(),
latitude: dbListingAddress.latitude.toNumber(),
},
price: {
value: dbListingPrice,
},
};
return listing;
});

return processedListings;
return prismaListings.map(prismaListingMapper);
}
43 changes: 9 additions & 34 deletions app/listings/content.tsx
Original file line number Diff line number Diff line change
@@ -1,47 +1,22 @@
"use client"

import { useContext, useEffect, useState } from "react"
import { countMatchedListings, fetchListingsByPage } from "./actions"
import ListingsList from "./list"
import { ListingsListTop } from "./list-top"
import { ListingsMap } from "./map"
import ListingsPagination from "./pagination"
import { ListingsContext } from "./provider"
import { Listing } from "./types"

export default function ListingsContent() {
const [listings, setListings] = useState<Listing[]>([])
const [listingsCount, setListingsCount] = useState<number>(0)

const context = useContext(ListingsContext)
const currentPage = context.pagination.currentPage.value

// Change pagination
useEffect(() => {
async function updateListings() {
const newListings = await fetchListingsByPage(currentPage)
setListings(newListings)
}
updateListings()
}, [currentPage])


// Count how many listings
useEffect(() => {
async function countListings() {
const newListingsCount = await countMatchedListings()
setListingsCount(newListingsCount)
}
countListings()
}, [])
interface ListingsContentInterface {
listings: Listing[]
}

export default function ListingsContent(props: ListingsContentInterface) {
return (
<div className="flex h-[36rem]">
<ListingsMap />
{/* TODO: Display listings as pins */}
<ListingsMap listings={props.listings} />
<div className="flex basis-1/2 flex-col">
<ListingsListTop listingsCount={listingsCount} />
<ListingsList listings={listings} />
<ListingsPagination listingsCount={listingsCount} />
<ListingsListTop listingsCount={props.listings.length} />
<ListingsList listings={props.listings} />
<ListingsPagination listingsCount={props.listings.length} />
</div>
</div>
)
Expand Down
16 changes: 15 additions & 1 deletion app/listings/list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,31 @@

import CardListing from "@/components/card-listing";
import { CURRENCY_FORMATTER } from "@/lib/formatter/currency";
import { useContext, useEffect, useMemo, useState } from "react";
import { LISTINGS_PER_PAGE } from "./constants";
import { countListingsToSkip } from "./function";
import { ListingsContext } from "./provider";
import { Listing } from "./types";

export interface ListingsListProps {
listings: Listing[]
}

export default function ListingsList(props: ListingsListProps) {
const { pagination } = useContext(ListingsContext);
const currentPage = pagination.currentPage.value;

const memoListings = useMemo<Listing[]>(() => {
const start = countListingsToSkip(currentPage, LISTINGS_PER_PAGE);
const end = start + LISTINGS_PER_PAGE;
const newListings = props.listings.slice(start, end);
return newListings;
}, [currentPage])

return (
<div className="flex basis-full flex-col items-center overflow-x-auto p-4">
<div className="grid-cols-auto grid gap-4 xl:grid-cols-2 xl:gap-6">
{props.listings.map((listing) => {
{memoListings.map((listing) => {
const priceFormatted = CURRENCY_FORMATTER.format(listing.price.value)
return (
<CardListing
Expand Down
7 changes: 5 additions & 2 deletions app/listings/map.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import VectorSource from "ol/source/Vector";
import { Fill, Stroke, Style, Text } from "ol/style";
import CircleStyle from "ol/style/Circle";
import { useEffect } from "react";
import { Listing } from "./types";

const ICON_DEFAULT_STYLE = new Style({
image: new CircleStyle({
Expand Down Expand Up @@ -70,8 +71,11 @@ const TEXT_SELECTED_STYLE = new Style({
}),
});

export interface ListingsMapInterface {
listings: Listing[]
}

export function ListingsMap() {
export function ListingsMap(props: ListingsMapInterface) {

// Map icon feature
const iconFeature = new Feature(new Point(fromLonLat([0, 0])))
Expand Down Expand Up @@ -157,7 +161,6 @@ export function ListingsMap() {
}, []);
// TODO: Think about the problem when icon or text features are dynamically added/removed. Need to think about the side effect dependencies


return (
<div id="map" className="basis-1/2 w-full h-full" tabIndex={0} />
)
Expand Down
4 changes: 3 additions & 1 deletion app/listings/page.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { fetchMatchedListings } from "./actions";
import ListingsContent from "./content";
import ListingsProvider from "./provider";
import { ListingsSearchFilters } from "./search-filters";

export default async function Listings() {
const listings = await fetchMatchedListings();
return (
<ListingsProvider>
<div className="flex flex-col gap-y-8">
<ListingsSearchFilters />
<ListingsContent />
<ListingsContent listings={listings} />
</div>
</ListingsProvider>
);
Expand Down
32 changes: 32 additions & 0 deletions app/listings/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,37 @@
import { Decimal } from "@prisma/client/runtime/library";

export interface PrismaListing {
id: string;
deposit: Decimal | null;
imageUrls: string[];
description: string;
beds: number;
baths: number;
area: Decimal;
availableDate: Date | null;
createdDate: Date;
userId: string;
address: {
id: string;
addressLine: string;
city: string;
state: string;
zipcode: string | null;
country: string;
latitude: Decimal;
longitude: Decimal;
listingId: string;
} | null;
prices: {
id: string;
value: Decimal;
startDate: Date;
endDate: Date;
isCurrent: boolean;
listingId: string;
}[];
}

interface ListingPrice {
value: number;
}
Expand Down

0 comments on commit 976e52a

Please sign in to comment.