Project/Airbnb Clone
[Airbnb 클론코딩] Fetching listings whith server components
hu6r1s
2023. 8. 7. 08:48
2023.07.11 - [Project/Airbnb Clone] - [Airbnb 클론코딩] Listing creation
[Airbnb 클론코딩] Listing creation
2023.07.02 - [Project/Airbnb Clone] - [Airbnb 클론코딩] Category UI [Airbnb 클론코딩] Category UI fmf2023.06.25 - [Project/Airbnb Clone] - [Airbnb 클론코딩] Register & Login functionality [Airbnb 클론코딩] Register & Login functionality 202
hu-bris.tistory.com
Listing
이번에 하는 것은 생성된 방 목록을 띄워주는 것이다.
먼저 방이 없을 때 나오는 리스트를 띄워주게 한다.
EmptyState.tsx
page.tsx 파일에서 리스트가 비었을 때, EmptyState 컴포넌트를 띄워준다.
const isEmpty = true
if (isEmpty) {
return (
<EmptyState showReset />
)
}
이제 EmptyState 컴포넌트를 만들어보자.
'use client';
import { useRouter } from "next/navigation";
import Button from "./Button";
import Heading from "./Heading";
interface EmptyState {
title?: string;
subtitle?: string;
showReset?: boolean;
}
const EmptyState: React.FC<EmptyState> = ({
title = "No exact matches",
subtitle = "Try changing or removing some of your filters",
showReset
}) => {
const router = useRouter();
return (
<div className="
h-[60vh]
flex
flex-col
gap-2
justify-center
items-center
">
<Heading
center
title={title}
subtitle={subtitle}
/>
<div className="w-48 mt-4">
{showReset && (
<Button
outline
label="Remove all filters"
onClick={() => router.push("/")}
/>
)}
</div>
</div>
);
};
export default EmptyState;
showReset가 true일 때, 홈으로 갈 수 있는 버튼을 만들어 놨다.
ListingCard
이제 생성된 방 목록을 하나 하나 띄워주도록 해보자.
import getCurrentUser from "./actions/getCurrentUser";
import getListings from "./actions/getListings";
import Container from "./components/Container";
import EmptyState from "./components/EmptyState";
import ListingCard from "./components/listings/ListingCard";
export default async function Home() {
const listings = await getListings();
const currentUser = await getCurrentUser();
if (listings.length === 0) {
return (
<EmptyState showReset />
)
}
return (
<Container>
<div className="
pt-24
grid
grid-cols-1
sm:grid-cols-2
md:grid-cols-3
lg:grid-cols-4
xl:grid-cols-5
2xl:grid-cols-6
gap-8
">
{listings.map((listing: any) => {
return (
<ListingCard
currentUser={currentUser}
key={listing.id}
data={listing}
/>
)
})}
</div>
</Container>
)
}
prisma를 통해 Listing 테이블에 있는 데이터를 가져와 반환해주는 getListings과 map 함수를 사용하여 하나씩 출력시켜 주면 된다.
"use client";
import useCountries from "@/app/hooks/useCountries";
import { SafeUser } from "@/app/types";
import { Listing, Reservation } from "@prisma/client";
import { useRouter } from "next/navigation";
import React, { useCallback, useMemo } from "react";
import { format } from "date-fns";
import Image from "next/image";
import HeartButton from "../HeartButton";
import Button from "../Button";
interface ListingCardProps {
data: Listing;
reservation?: Reservation;
onAction?: (id: string) => void;
disabled?: boolean;
actionLabel?: string;
actionId?: string;
currentUser?: SafeUser | null;
}
const ListingCard: React.FC<ListingCardProps> = ({
data,
reservation,
onAction,
disabled,
actionLabel,
actionId = "",
currentUser
}) => {
const router = useRouter();
const { getByValue } = useCountries();
const location = getByValue(data.locationValue);
const handleCancel = useCallback(
(e: React.MouseEvent<HTMLButtonElement>) => {
e.stopPropagation();
if (disabled) {
return;
}
onAction?.(actionId);
}, [onAction, actionId, disabled]);
const price = useMemo(() => {
if (reservation) {
return reservation.totalPrice;
}
return data.price;
}, [reservation, data.price]);
const reservationDate = useMemo(() => {
if (!reservation) {
return null;
}
const start = new Date(reservation.startDate);
const end = new Date(reservation.endDate);
return `${format(start, "PP")} - ${format(end, "PP")}`
}, [reservation])
return (
<div
onClick={() => router.push(`/listings/${data.id}`)}
className="
col-span-1 cursor-pointer group
">
<div className="flex flex-col gap-2 w-full">
<div
className="
aspect-square
w-full
relative
overflow-hidden
rounded-xl
"
>
<Image
fill
alt="Listing"
src={data.imageSrc}
className="
object-cover
h-full
w-full
group-hover:scale-110
transition
"
/>
<div className="absolute top-3 right-3">
<HeartButton
listingId={data.id}
currentUser={currentUser}
/>
</div>
</div>
<div className="font-semibold text-lg">
{location?.region}, {location?.label}
</div>
<div className="font-right text-neutral-500">
{reservationDate || data.category}
</div>
<div className="flex flex-row items-center gap-1">
<div className="font-semibold">
$ {price}
</div>
{!reservation && (
<div className="font-light">night</div>
)}
</div>
{onAction && actionLabel && (
<Button
disabled={disabled}
small
label={actionLabel}
onClick={handleCancel}
/>
)}
</div>
</div >
);
};
export default ListingCard;
이걸로 방 목록 카드 하나의 컴포넌트를 만들어 모든 리스트 목록을 출력시켜준다.