Skip to content

Commit

Permalink
Merge pull request #313 from prgrms-web-devcourse-final-project/feature/
Browse files Browse the repository at this point in the history
#247

feat: 상세페이지 작업 (5차)
  • Loading branch information
ppyom authored Dec 8, 2024
2 parents 9108cdd + c18ce95 commit c937f2e
Show file tree
Hide file tree
Showing 8 changed files with 109 additions and 52 deletions.
2 changes: 1 addition & 1 deletion src/components/atoms/Toast/styled.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export const ToastManagerWrapper: ReturnType<typeof styled.div> = styled.div`
bottom: 80px;
left: 50%;
transform: translateX(-50%);
z-index: 1500;
z-index: 999999;
`;

export const ToastItemWrapper: ReturnType<typeof styled.div> = styled.div`
Expand Down
24 changes: 14 additions & 10 deletions src/components/templates/DetailTemplate/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ import { DetailTemplateWrapper, ImageSliderAndTimer } from "./styled";
import { useBid, useDetailModal } from "hooks";
import { buttonNames, priceNames } from "constants/auctionControlBarNames";
import type { IComment, IProductDetail } from "types";
import { LOGO_PATH } from "constants/imgPath";

interface IBaseDetailTemplateProps extends Omit<IProductDetail, ""> {
interface IBaseDetailTemplateProps
extends Omit<IProductDetail, "winningPrice"> {
/** 판매자 정보 */
seller: {
id: number;
Expand All @@ -35,7 +37,7 @@ interface IBaseDetailTemplateProps extends Omit<IProductDetail, ""> {
uploadTime: string;
/** 거래 희망 장소 */
productLocation: {
longtitude: number;
longitude: number;
latitube: number;
address: string;
location: string;
Expand All @@ -47,9 +49,9 @@ interface IBaseDetailTemplateProps extends Omit<IProductDetail, ""> {
/** 최소 입찰가 */
minimumPrice: number;
/** 내 입찰가 */
myPrice?: number;
myPrice: IProductDetail["myPrice"];
/** 최고 입찰가 */
maximumPrice?: number;
maximumPrice: IProductDetail["winningPrice"];
/** 입찰 취소 */
onCancel: () => void;
/** 조기마감 */
Expand All @@ -72,14 +74,15 @@ export const DetailTemplate = ({
// 판매자 정보
seller: { name, image },
// 거래 희망 장소
productLocation: { longtitude, latitube, address, location },
productLocation: { longitude, latitube, address, location },
onLocationClick,
// 댓글
comments,
// 가격
minimumPrice,
myPrice,
maximumPrice,
myPrice,
myAuctionId,
isEarly,
// hasBuyer,
onCancel,
Expand Down Expand Up @@ -109,9 +112,10 @@ export const DetailTemplate = ({
createdAt={uploadTime}
description={content}
/>
<Profile imgUrl={image} nickname={name} location={address} />
{/* TODO 프로필 사진 없는 경우 Logo 사진 */}
<Profile imgUrl={image || LOGO_PATH} nickname={name} location={address} />
<LocationMap
coord={{ lat: latitube, lng: longtitude }}
coord={{ lat: latitube, lng: longitude }}
location={location}
onClick={onLocationClick}
/>
Expand Down Expand Up @@ -173,8 +177,8 @@ export const DetailTemplate = ({
price={price}
setPrice={setPrice}
minPrice={minimumPrice}
beforePrice={myPrice}
onBid={handleBid}
beforePrice={myPrice || undefined}
onBid={() => handleBid(minimumPrice, myAuctionId || undefined)}
open={open}
onClose={handleCloseBottomSheet}
/>,
Expand Down
2 changes: 1 addition & 1 deletion src/components/templates/DetailTemplate/stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export const Default: Story = {
},
// 거래 희망 장소
productLocation: {
longtitude: 126.9784147,
longitude: 126.9784147,
latitube: 37.5666805,
address: "관악구 신림동",
location: "보라매공원 CU",
Expand Down
3 changes: 3 additions & 0 deletions src/components/templates/DetailTemplate/styled.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,7 @@ export const DetailTemplateWrapper: ReturnType<typeof styled.div> = styled.div`
width: 80px;
}
}
${CommentWrapper} {
padding-bottom: 2rem;
}
`;
78 changes: 49 additions & 29 deletions src/hooks/useBid.ts
Original file line number Diff line number Diff line change
@@ -1,40 +1,69 @@
import { useEffect, useMemo, useState } from "react";
import { useState } from "react";
import { bidding, cancelBidding, editBidding } from "services/apis";
import { useFetchBidding } from "hooks";
import { useFetchProduct } from "hooks";
import { useModalStore } from "stores";
import { Toast } from "components/atoms";

/**
* 입찰 로직
*/
export const useBid = (productId: number) => {
const { biddingList, isLoading } = useFetchBidding();
const myPrice = useMemo(() => {
return (
(!isLoading && biddingList.find((b) => b.productId === productId)) || null
);
}, [isLoading]);
const {
actions: { closeModal },
} = useModalStore();
const { productRefetch } = useFetchProduct(productId.toString());
const [open, setOpen] = useState(false);
const [price, setPrice] = useState("");

const handleOpenBottomSheet = () => {
setOpen(true);
};

const handleCloseBottomSheet = () => {
setOpen(false);
};

/**
* 입찰 버튼 클릭
*/
const handleBid = () => {
if (!myPrice) {
const handleBid = (minimumPrice: number, myAuctionId?: number) => {
// 1. 가격 체크
const priceValue = Number(price.replace(/,/g, ""));
if (priceValue < minimumPrice) {
Toast.show(
`${minimumPrice.toLocaleString()}원 이상으로 입력해주세요.`,
2000,
);
return;
}
// 2. 백엔드 요청
if (!myAuctionId) {
// 현재 입찰중이 아닌 경우
bidding({ productId, price: Number(price.replace(/,/g, "")) })
// TODO 동네 인증 여부
// TODO 입찰 전 경고 모달
bidding({ productId, price: priceValue })
.then((data) => {
// 입찰 완료
console.log(data);
// TODO Refetch 수정
productRefetch().catch(console.error);
handleCloseBottomSheet();
Toast.show("입찰을 성공했습니다.", 2000);
})
.catch(console.error);
} else {
// 현재 입찰중인 경우
editBidding({
productId,
price: Number(price.replace(/,/g, "")),
auctionId: myPrice.auctionId,
price: priceValue,
auctionId: myAuctionId,
})
.then((data) => {
console.log(data);
// TODO Refetch 수정
productRefetch().catch(console.error);
handleCloseBottomSheet();
Toast.show("입찰 가격이 수정되었습니다.", 2000);
})
.catch(console.error);
}
Expand All @@ -43,32 +72,23 @@ export const useBid = (productId: number) => {
/**
* 입찰 취소
*/
const handleCancel = () => {
if (myPrice) {
cancelBidding(myPrice.auctionId)
const handleCancel = (myAuctionId: number) => {
if (myAuctionId) {
cancelBidding(myAuctionId)
.then((data) => {
console.log(data);
// TODO Refetch 수정
productRefetch().catch(console.error);
closeModal();
Toast.show("입찰이 취소되었습니다.", 2000);
})
.catch(console.error);
}
};

const handleOpenBottomSheet = () => {
setOpen(true);
};

const handleCloseBottomSheet = () => {
setOpen(false);
};

useEffect(() => {
console.log(myPrice);
}, []);

return {
open,
price,
myPrice,
setPrice,
handleOpenBottomSheet,
handleCloseBottomSheet,
Expand Down
8 changes: 6 additions & 2 deletions src/hooks/useFetchProduct.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@ import { queries } from "constants/queryKeys";
import { getProduct } from "services/apis";

export const useFetchProduct = (productId: string) => {
const { data, isLoading } = useQuery({
const { data, isLoading, refetch } = useQuery({
queryKey: queries.product.detail(productId),
queryFn: () => getProduct(productId),
select: (data) => data.result,
});

return { product: data, isProductLoading: isLoading };
return {
product: data,
isProductLoading: isLoading,
productRefetch: refetch,
};
};
37 changes: 31 additions & 6 deletions src/pages/DetailPage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ import { DetailTemplate } from "components/templates";
import { KebabMenu } from "components/molecules";
import { KebabIcon } from "components/atoms/Icon";
import { Loading } from "components/molecules/Loading";
import { useSelectedLocationStore, useTopBarStore } from "stores";
import {
useFormDataStore,
useSelectedLocationStore,
useTopBarStore,
} from "stores";
import {
useFetchProduct,
useFetchComment,
Expand All @@ -14,6 +18,7 @@ import {
} from "hooks";
import { KebabWrapper } from "./styled";
import { earlyClose } from "services/apis";
import type { Category } from "../../types";

export const DetailPage = () => {
const navigate = useNavigate();
Expand All @@ -24,8 +29,12 @@ export const DetailPage = () => {
const {
actions: { setCoord, setLocation, setAddress },
} = useSelectedLocationStore();
// TODO
const {
actions: { setFormData },
} = useFormDataStore();
const { open, handleOpen, handleClose, menuRef } = useKebabMenu();
const { handleCancel, myPrice } = useBid(parseInt(productId!));
const { handleCancel } = useBid(parseInt(productId!));
const { todo } = useDetailModal();

/**
Expand All @@ -35,7 +44,7 @@ export const DetailPage = () => {
if (product) {
setCoord({
lat: product.productLocation.latitube,
lng: product.productLocation.longtitude,
lng: product.productLocation.longitude,
});
setLocation(product.productLocation.location);
setAddress(product.productLocation.address);
Expand Down Expand Up @@ -65,7 +74,9 @@ export const DetailPage = () => {
* (구매자) 입찰 취소
*/
const handleCancelBid = () => {
handleCancel();
if (product?.myAuctionId) {
handleCancel(product.myAuctionId);
}
};

/**
Expand All @@ -84,7 +95,20 @@ export const DetailPage = () => {
const handleEdit = () => {
if (product && !product.hasBuyer) {
// 수정 페이지로 이동
navigate(`/product?${productId!}`);
// TODO 확인 필요
setFormData({
title: product.title,
content: product.content,
minimumPrice: product.minimumPrice.toLocaleString(),
category: product.category as Category,
latitude: product.productLocation.latitube,
longitude: product.productLocation.longitude,
address: product.productLocation.address,
location: product.productLocation.location,
imgUrls: product.images.map((img) => ({ url: img, file: null })),
expiredTime: product.expiredTime,
});
navigate(`/product?productId=${productId!}`);
return;
}
};
Expand Down Expand Up @@ -138,14 +162,15 @@ export const DetailPage = () => {
onLocationClick={handleLocationMapClick}
comments={comments}
minimumPrice={product.minimumPrice}
myPrice={myPrice?.bidPrice}
myPrice={product.myPrice}
maximumPrice={product.winningPrice}
isEarly={product.isEarly}
productId={product.productId}
hasBuyer={product.hasBuyer}
onCancel={handleCancelBid}
onEarlyClosing={handleEarlyClosing}
isSeller={product.isSeller}
myAuctionId={product.myAuctionId}
/>
{open && (
<KebabWrapper ref={menuRef}>
Expand Down
7 changes: 4 additions & 3 deletions src/types/response/product.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export interface IProductDetail {
image: string;
};
productLocation: {
longtitude: number;
longitude: number;
latitube: number;
address: string;
location: string;
Expand All @@ -25,8 +25,9 @@ export interface IProductDetail {
expiredTime: string;
isEarly: boolean;
images: string[];
myPrice?: number;
winningPrice?: number;
myAuctionId: number | null;
myPrice: number | null;
winningPrice: number | null;
isSeller: boolean;
}

Expand Down

0 comments on commit c937f2e

Please sign in to comment.