From a4f721e79f5ee0d3db87bfa6b96a6fe74bfc46c7 Mon Sep 17 00:00:00 2001 From: HyeonSik Choi Date: Mon, 26 Aug 2024 22:24:23 +0900 Subject: [PATCH 01/17] =?UTF-8?q?feat:=20=EA=B2=BD=EB=A7=A4=20=EA=B0=80?= =?UTF-8?q?=EA=B2=A9=20=EA=B2=BD=EC=8B=A0=20=EB=A1=9C=EC=A7=81=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pages/auction/detail/AuctionDetail.tsx | 51 +++++++++++---- .../auction/detail/PricePolicyElement.tsx | 65 ++++++++++--------- 2 files changed, 76 insertions(+), 40 deletions(-) diff --git a/front/src/pages/auction/detail/AuctionDetail.tsx b/front/src/pages/auction/detail/AuctionDetail.tsx index ed811224..c6fd5a81 100644 --- a/front/src/pages/auction/detail/AuctionDetail.tsx +++ b/front/src/pages/auction/detail/AuctionDetail.tsx @@ -1,4 +1,9 @@ -import {formatVariationDuration, getKrDateFormat} from "../../../util/DateUtil"; +import { + formatVariationDuration, + getAuctionStatus, + getKrDateFormat, + getMsFromIso8601Duration, getTimeDifferenceInMs +} from "../../../util/DateUtil"; import {useEffect, useState} from "react"; import {AuctionDetailInfo} from "./type"; import PricePolicyElement from "./PricePolicyElement"; @@ -16,6 +21,8 @@ function AuctionDetail({auctionId}: { auctionId?: number }) { const {showAlert} = useAlert(); const [auction, setAuction] = useState(null); const [quantity, setQuantity] = useState(1); + const [leftInfo, setLeftInfo] = useState("불러오는 중..."); + const [isFinished, setIsFinished] = useState(true); const increaseQuantity = (maximum: number) => { if (quantity >= maximum) { @@ -63,8 +70,30 @@ function AuctionDetail({auctionId}: { auctionId?: number }) { showAlert("상품 정보를 가져오는데 실패했습니다."); } ); + }, []); + useEffect(() => { + if (!auction) { + return; + } + + // 현재 가격 갱신 타이머 + const intervalId = setInterval(() => { + const { status, timeInfo } = getAuctionStatus(auction.startedAt, auction.finishedAt); + if (status === "종료") { + let krDateFormat = "경매 종료 (" + getKrDateFormat(auction.finishedAt) + ")"; + setLeftInfo(krDateFormat); + } else { + setLeftInfo(status + " (" + timeInfo + ")"); + setIsFinished(false); // 입찰 버튼 활성화 + } + + }, 1000); + + return () => clearInterval(intervalId); + }, [auction]); + const onClickBidButton = () => { requestAuctionBid( baseUrl, @@ -122,15 +151,15 @@ function AuctionDetail({auctionId}: { auctionId?: number }) {
-

경매 종료 시간

-

{getKrDateFormat(auction.finishedAt)}

+

경매 진행 상황

+

{leftInfo}

-
-

변동 주기

-

{formatVariationDuration(auction.variationDuration)}

-
+ {/*
*/} + {/*

변동 주기

*/} + {/*

{formatVariationDuration(auction.variationDuration)}

*/} + {/*
*/}
@@ -187,15 +216,15 @@ function AuctionDetail({auctionId}: { auctionId?: number }) {
-
- +
diff --git a/front/src/pages/auction/detail/PricePolicyElement.tsx b/front/src/pages/auction/detail/PricePolicyElement.tsx index 6704c2cf..e9542a63 100644 --- a/front/src/pages/auction/detail/PricePolicyElement.tsx +++ b/front/src/pages/auction/detail/PricePolicyElement.tsx @@ -1,7 +1,12 @@ import {AuctionDetailInfo, ConstantPricePolicy, PercentagePricePolicy} from "./type"; import {getPriceFormatted} from "../../../util/NumberUtil"; -import {formatVariationDuration, getMsFromIso8601Duration, getTimeDifferenceInMs} from "../../../util/DateUtil"; -import {useEffect} from "react"; +import { getAuctionStatus } from "../../../util/DateUtil"; +import { + formatVariationDuration, + getMsFromIso8601Duration, + getTimeDifferenceInMs +} from "../../../util/DateUtil"; +import {useEffect, useState} from "react"; interface PricePolicyElementProps { priceLimit: number; @@ -16,44 +21,45 @@ function PricePolicyElement( setAuction }: PricePolicyElementProps) { - useEffect(() => { - - const durationMs = getMsFromIso8601Duration(auction.variationDuration); - const diffMsBetweenStartedAndNow = getTimeDifferenceInMs(auction.startedAt, new Date()); - const diffMs = getTimeDifferenceInMs(auction.startedAt, auction.finishedAt); + const [nowPrice, setNowPrice] = useState(0); - const times = Math.floor(diffMsBetweenStartedAndNow / durationMs); + // const { status, timeInfo } = getAuctionStatus(auction.startedAt, new Date()); - let currentPrice = auction.originPrice; - for (let i = 0; i < times; i++) { - const nextPrice = calculateNextPrice(); - if (priceLimit <= nextPrice) { - currentPrice = nextPrice; - } else { - currentPrice = priceLimit; - } - } - setAuction({...auction, currentPrice: currentPrice}); + useEffect(() => { + // 시작 가격을 설정한다. + setNowPrice(auction.originPrice); + // 현재 가격 갱신 타이머 const intervalId = setInterval(() => { - if (diffMsBetweenStartedAndNow % durationMs === 0) { - const nextPrice = calculateNextPrice(); - if (priceLimit <= nextPrice) { - setAuction({...auction, currentPrice: nextPrice}); + // 현재 가격 계산 로직 + const durationMs = getMsFromIso8601Duration(auction.variationDuration); + const diffMsBetweenStartedAndNow = getTimeDifferenceInMs(auction.startedAt, new Date()); + const times = Math.floor(diffMsBetweenStartedAndNow / durationMs); + + let currentPrice = auction.originPrice; + for (let i = 0; i < times; i++) { // times번 만큼 할인된 가격을 구하는 로직 + const calculatePrice = calculateNextPrice(currentPrice); + if (priceLimit <= calculatePrice) { + currentPrice = calculatePrice; + } else { + currentPrice = priceLimit; } } + // 가격 설정 + setNowPrice(currentPrice); + }, 1000); return () => clearInterval(intervalId); }, []); - function calculateNextPrice(): number { + function calculateNextPrice(currentPrice: number): number { if (auction.pricePolicy.type === "CONSTANT") { - return auction.currentPrice - (auction.pricePolicy as ConstantPricePolicy).variationWidth; + return currentPrice - (auction.pricePolicy as ConstantPricePolicy).variationWidth; } else if (auction.pricePolicy.type === "PERCENTAGE") { - return auction.currentPrice - (auction.currentPrice * (auction.pricePolicy as PercentagePricePolicy).discountRate / 100); + return currentPrice - (currentPrice * (auction.pricePolicy as PercentagePricePolicy).discountRate / 100); } else { return -1; } @@ -64,14 +70,14 @@ function PricePolicyElement( case "CONSTANT": return (
- {formatVariationDuration(auction.variationDuration)}후 {auction.pricePolicy.variationWidth}원 할인이 적용됩니다.
) case "PERCENTAGE": return (
- {formatVariationDuration(auction.variationDuration)}후 ${auction.pricePolicy.discountRate}% 할인이 적용됩니다.
) @@ -88,17 +94,18 @@ function PricePolicyElement(

할인 정책

+
{component()}

현재 가격

-

{getPriceFormatted(auction.currentPrice)}

+

{getPriceFormatted(nowPrice)}

다음 가격

-

{getPriceFormatted(calculateNextPrice())}

+

{getPriceFormatted(calculateNextPrice(nowPrice))}

From 4dd3f9844ff5e637753f12a40f9559e966a75183 Mon Sep 17 00:00:00 2001 From: Basak Date: Tue, 27 Aug 2024 00:58:37 +0900 Subject: [PATCH 02/17] =?UTF-8?q?fix:=20=EA=B2=BD=EB=A7=A4=20=EA=B0=80?= =?UTF-8?q?=EA=B2=A9=20=ED=95=98=EB=9D=BD=20=EB=A1=9C=EC=A7=81=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auction/detail/PricePolicyElement.tsx | 30 ++++++++++++++----- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/front/src/pages/auction/detail/PricePolicyElement.tsx b/front/src/pages/auction/detail/PricePolicyElement.tsx index e9542a63..7b73bf0b 100644 --- a/front/src/pages/auction/detail/PricePolicyElement.tsx +++ b/front/src/pages/auction/detail/PricePolicyElement.tsx @@ -22,8 +22,7 @@ function PricePolicyElement( }: PricePolicyElementProps) { const [nowPrice, setNowPrice] = useState(0); - - // const { status, timeInfo } = getAuctionStatus(auction.startedAt, new Date()); + const [isLastPrice, setIsLastPrice] = useState(false); useEffect(() => { // 시작 가격을 설정한다. @@ -32,9 +31,19 @@ function PricePolicyElement( // 현재 가격 갱신 타이머 const intervalId = setInterval(() => { + // 가격 하락 정책이 종료되었는지 체크 + const now = new Date(); + let isLastTime = false; + if (now >= auction.finishedAt) { + isLastTime = true; + setIsLastPrice(true); + } + // 현재 가격 계산 로직 const durationMs = getMsFromIso8601Duration(auction.variationDuration); - const diffMsBetweenStartedAndNow = getTimeDifferenceInMs(auction.startedAt, new Date()); + const diffMsBetweenStartedAndNow = getTimeDifferenceInMs( + auction.startedAt, isLastTime ? auction.finishedAt : now + ); const times = Math.floor(diffMsBetweenStartedAndNow / durationMs); let currentPrice = auction.originPrice; @@ -103,10 +112,17 @@ function PricePolicyElement(

현재 가격

{getPriceFormatted(nowPrice)}

-
-

다음 가격

-

{getPriceFormatted(calculateNextPrice(nowPrice))}

-
+ { + isLastPrice + ?
+

최종 가격

+

{getPriceFormatted(nowPrice)}

+
+ :
+

다음 가격

+

{getPriceFormatted(calculateNextPrice(nowPrice))}

+
+ } From 0bc6b5052875cb33c35117c6ee6ef617904dade3 Mon Sep 17 00:00:00 2001 From: Basak Date: Tue, 27 Aug 2024 01:14:25 +0900 Subject: [PATCH 03/17] =?UTF-8?q?feat:=20=EC=9E=AC=EA=B3=A0=20=EC=88=98?= =?UTF-8?q?=EB=9F=89=EC=9D=84=20=ED=8F=B4=EB=A7=81=ED=95=98=EB=8A=94=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80=20(=EC=8B=9C=EC=97=B0?= =?UTF-8?q?=EC=9A=A9)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pages/auction/detail/AuctionDetail.tsx | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/front/src/pages/auction/detail/AuctionDetail.tsx b/front/src/pages/auction/detail/AuctionDetail.tsx index c6fd5a81..0018f57a 100644 --- a/front/src/pages/auction/detail/AuctionDetail.tsx +++ b/front/src/pages/auction/detail/AuctionDetail.tsx @@ -73,6 +73,28 @@ function AuctionDetail({auctionId}: { auctionId?: number }) { }, []); + useEffect(() => { + + if (auctionId === undefined) { + return; + } + + // 재고 갱신 + const intervalId = setInterval(() => { + requestAuctionDetail(baseUrl, auctionId, + (auctionDetailItem) => { + setAuction(prevAuction => + prevAuction ? {...prevAuction, currentStock: auctionDetailItem.currentStock} : null + ); + console.log("현재 재고: " + auctionDetailItem.currentStock); + }, + () => {console.log("현재 재고량을 가져오는데 실패하였습니다.")} + ); + }, 1000); + + return () => clearInterval(intervalId); + }, []); + useEffect(() => { if (!auction) { return; From dca5dc176c660d310d985766faaeb255028469d6 Mon Sep 17 00:00:00 2001 From: Basak Date: Tue, 27 Aug 2024 01:36:57 +0900 Subject: [PATCH 04/17] =?UTF-8?q?feat:=20=EC=A0=95=EB=A7=90=EB=A1=9C=20?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=95=84=EC=9B=83=ED=95=A0=EA=B9=8C=EC=9A=94?= =?UTF-8?q?=3F=20=EB=AC=B8=EA=B5=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- front/src/pages/Footer.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/front/src/pages/Footer.tsx b/front/src/pages/Footer.tsx index 50cf6d23..f86b646b 100644 --- a/front/src/pages/Footer.tsx +++ b/front/src/pages/Footer.tsx @@ -18,6 +18,9 @@ function Footer() { }; const logout = () => { + if(!window.confirm("정말로 로그아웃할까요?")) { + return; + } signOut( baseUrl, () => { From bc89efac3bc4a60634b7526f5c5831ef1a2c8a82 Mon Sep 17 00:00:00 2001 From: Basak Date: Tue, 27 Aug 2024 01:37:26 +0900 Subject: [PATCH 05/17] =?UTF-8?q?feat:=20=EA=B2=BD=EB=A7=A4=20=EC=9E=85?= =?UTF-8?q?=EC=B0=B0=20=EC=84=B1=EA=B3=B5=EC=9D=84=20=EC=8B=9C=EA=B0=81?= =?UTF-8?q?=EC=A0=81=EC=9C=BC=EB=A1=9C=20=ED=91=9C=EC=8B=9C.=20=EB=B0=8F?= =?UTF-8?q?=20=EB=94=B0=EB=8B=A5=20=EB=B0=A9=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- front/package-lock.json | 22 ++++ front/package.json | 1 + .../pages/auction/detail/AuctionDetail.tsx | 108 +++++++++++++++++- 3 files changed, 125 insertions(+), 6 deletions(-) diff --git a/front/package-lock.json b/front/package-lock.json index a8f055ef..71910bc9 100644 --- a/front/package-lock.json +++ b/front/package-lock.json @@ -16,6 +16,7 @@ "@types/react": "^18.3.1", "@types/react-dom": "^18.3.0", "react": "^18.3.1", + "react-confetti": "^6.1.0", "react-dom": "^18.3.1", "react-scripts": "5.0.1", "typescript": "^4.9.5", @@ -14053,6 +14054,21 @@ "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", "license": "MIT" }, + "node_modules/react-confetti": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/react-confetti/-/react-confetti-6.1.0.tgz", + "integrity": "sha512-7Ypx4vz0+g8ECVxr88W9zhcQpbeujJAVqL14ZnXJ3I23mOI9/oBVTQ3dkJhUmB0D6XOtCZEM6N0Gm9PMngkORw==", + "license": "MIT", + "dependencies": { + "tween-functions": "^1.2.0" + }, + "engines": { + "node": ">=10.18" + }, + "peerDependencies": { + "react": "^16.3.0 || ^17.0.1 || ^18.0.0" + } + }, "node_modules/react-dev-utils": { "version": "12.0.1", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", @@ -16494,6 +16510,12 @@ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "license": "0BSD" }, + "node_modules/tween-functions": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/tween-functions/-/tween-functions-1.2.0.tgz", + "integrity": "sha512-PZBtLYcCLtEcjL14Fzb1gSxPBeL7nWvGhO5ZFPGqziCcr8uvHp0NDmdjBchp6KHL+tExcg0m3NISmKxhU394dA==", + "license": "BSD" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", diff --git a/front/package.json b/front/package.json index cc6f7625..73dc6310 100644 --- a/front/package.json +++ b/front/package.json @@ -11,6 +11,7 @@ "@types/react": "^18.3.1", "@types/react-dom": "^18.3.0", "react": "^18.3.1", + "react-confetti": "^6.1.0", "react-dom": "^18.3.1", "react-scripts": "5.0.1", "typescript": "^4.9.5", diff --git a/front/src/pages/auction/detail/AuctionDetail.tsx b/front/src/pages/auction/detail/AuctionDetail.tsx index 0018f57a..0de1e394 100644 --- a/front/src/pages/auction/detail/AuctionDetail.tsx +++ b/front/src/pages/auction/detail/AuctionDetail.tsx @@ -5,13 +5,14 @@ import { getMsFromIso8601Duration, getTimeDifferenceInMs } from "../../../util/DateUtil"; import {useEffect, useState} from "react"; -import {AuctionDetailInfo} from "./type"; +import {AuctionDetailInfo, ConstantPricePolicy, PercentagePricePolicy} from "./type"; import PricePolicyElement from "./PricePolicyElement"; import {requestAuctionBid, requestAuctionDetail} from "../../../api/auction/api"; import {usePageStore} from "../../../store/PageStore"; import useAlert from "../../../hooks/useAlert"; import {getAuctionProgress} from "../../../util/NumberUtil" import arrowLeftIcon from '../../../img/arrow-left.svg'; +import Confetti from 'react-confetti'; function AuctionDetail({auctionId}: { auctionId?: number }) { @@ -22,7 +23,11 @@ function AuctionDetail({auctionId}: { auctionId?: number }) { const [auction, setAuction] = useState(null); const [quantity, setQuantity] = useState(1); const [leftInfo, setLeftInfo] = useState("불러오는 중..."); + const [isFinished, setIsFinished] = useState(true); + const [isButtonDisabled, setIsButtonDisabled] = useState(false); + const [countdown, setCountdown] = useState(0); + const [showConfetti, setShowConfetti] = useState(false); const increaseQuantity = (maximum: number) => { if (quantity >= maximum) { @@ -116,13 +121,83 @@ function AuctionDetail({auctionId}: { auctionId?: number }) { return () => clearInterval(intervalId); }, [auction]); + useEffect(() => { + if (isButtonDisabled) { + const timer = setInterval(() => { + setCountdown((prevCount) => { + if (prevCount <= 1) { + clearInterval(timer); + setIsButtonDisabled(false); + return 0; + } + return prevCount - 1; + }); + }, 1000); + + return () => clearInterval(timer); + } + }, [isButtonDisabled]); + + useEffect(() => { + if (showConfetti) { + const timer = setTimeout(() => setShowConfetti(false), 5000); // 5초 후에 confetti 효과 종료 + return () => clearTimeout(timer); + } + }, [showConfetti]); + + function getCurrentPrice(): number { + if (!auction) { + return 1; + } + + // 가격 하락 정책이 종료되었는지 체크 + const now = new Date(); + let isLastTime = false; + if (now >= auction.finishedAt) { + isLastTime = true; + } + + // 현재 가격 계산 로직 + const durationMs = getMsFromIso8601Duration(auction.variationDuration); + const diffMsBetweenStartedAndNow = getTimeDifferenceInMs( + auction.startedAt, isLastTime ? auction.finishedAt : now + ); + const times = Math.floor(diffMsBetweenStartedAndNow / durationMs); + + let currentPrice = auction.originPrice; + for (let i = 0; i < times; i++) { // times번 만큼 할인된 가격을 구하는 로직 + const calculatePrice = calculateNextPrice(currentPrice); + currentPrice = calculatePrice; + } + + return currentPrice; + } + + function calculateNextPrice(currentPrice: number): number { + if (!auction) { + return 1; + } + + if (auction.pricePolicy.type === "CONSTANT") { + return currentPrice - (auction.pricePolicy as ConstantPricePolicy).variationWidth; + } else if (auction.pricePolicy.type === "PERCENTAGE") { + return currentPrice - (currentPrice * (auction.pricePolicy as PercentagePricePolicy).discountRate / 100); + } else { + return -1; + } + } + const onClickBidButton = () => { + const currentPrice = getCurrentPrice(); + requestAuctionBid( baseUrl, auction?.auctionId!, - {quantity: quantity, price: auction!.currentPrice}, + {quantity: quantity, price: currentPrice}, () => { - setPage('home'); + setShowConfetti(true); // 성공 시 confetti 효과 시작 + setIsButtonDisabled(true); // 버튼 비활성화 + setCountdown(5); // 5초 카운트다운 시작 }, () => { showAlert("입찰에 실패했습니다."); @@ -130,6 +205,12 @@ function AuctionDetail({auctionId}: { auctionId?: number }) { ); } + const getButtonText = () => { + if (isFinished) return '경매 종료'; + if (isButtonDisabled) return `${countdown}초 남음`; + return '입찰하기'; + } + const onClickBackButton = () => { setPage('home'); } @@ -146,6 +227,14 @@ function AuctionDetail({auctionId}: { auctionId?: number }) { return ( <> + {showConfetti && ( + + )}
From b1013f40ff530ef7393303b8c497b45d2237f270 Mon Sep 17 00:00:00 2001 From: Basak Date: Tue, 27 Aug 2024 01:47:01 +0900 Subject: [PATCH 06/17] =?UTF-8?q?style:=20list=20=EB=AF=B8=EB=A6=AC?= =?UTF-8?q?=EB=B3=B4=EA=B8=B0=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- front/src/pages/auction/list/AuctionListElement.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/front/src/pages/auction/list/AuctionListElement.tsx b/front/src/pages/auction/list/AuctionListElement.tsx index 86d56c4d..e2da294b 100644 --- a/front/src/pages/auction/list/AuctionListElement.tsx +++ b/front/src/pages/auction/list/AuctionListElement.tsx @@ -54,7 +54,7 @@ const AuctionListElement: React.FC = ({
{/* 이미지 영역 */} Auction Item From b93e9abe490a3801b125c6debeb5d7ffe4b18448 Mon Sep 17 00:00:00 2001 From: Basak Date: Tue, 27 Aug 2024 01:51:25 +0900 Subject: [PATCH 07/17] =?UTF-8?q?style:=20=EB=AA=A9=EB=A1=9D=EC=97=90?= =?UTF-8?q?=EC=84=9C=EB=8A=94=20=ED=98=84=EC=9E=AC=EA=B0=80=EA=B2=A9?= =?UTF-8?q?=EC=9D=84=20=EC=B6=94=EC=A0=95=ED=95=A0=20=EC=88=98=20=EC=97=86?= =?UTF-8?q?=EC=9C=BC=EB=AF=80=EB=A1=9C=20'=EC=8B=9C=EC=9E=91=20=EA=B0=80?= =?UTF-8?q?=EA=B2=A9'=EC=9C=BC=EB=A1=9C=20=EB=AC=B8=EA=B5=AC=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- front/src/pages/auction/list/AuctionListElement.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/front/src/pages/auction/list/AuctionListElement.tsx b/front/src/pages/auction/list/AuctionListElement.tsx index e2da294b..1c00f4f0 100644 --- a/front/src/pages/auction/list/AuctionListElement.tsx +++ b/front/src/pages/auction/list/AuctionListElement.tsx @@ -67,7 +67,7 @@ const AuctionListElement: React.FC = ({

{title}

시작 시간: {getKrDateFormat(startedAt)}

종료 시간: {getKrDateFormat(endedAt)}

-

현재가: {getPriceFormatted(price)}

+

시작 가격: {getPriceFormatted(price)}

{timeInfo}

From deea588336b7ff9680f1b976cc7bf6fcba868dca Mon Sep 17 00:00:00 2001 From: Basak Date: Tue, 27 Aug 2024 01:57:52 +0900 Subject: [PATCH 08/17] =?UTF-8?q?feat:=20=ED=83=80=EC=9D=B4=EB=A8=B8?= =?UTF-8?q?=EB=A5=BC=20=ED=86=B5=ED=95=B4=20=EB=8B=A4=EC=9D=8C=20=EA=B0=80?= =?UTF-8?q?=EA=B2=A9=20=EB=B3=80=EB=8F=99=20=EC=8B=9C=EC=A0=90=EC=9D=84=20?= =?UTF-8?q?=EB=85=B8=EC=B6=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/pages/auction/detail/PricePolicyElement.tsx | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/front/src/pages/auction/detail/PricePolicyElement.tsx b/front/src/pages/auction/detail/PricePolicyElement.tsx index 7b73bf0b..2f384445 100644 --- a/front/src/pages/auction/detail/PricePolicyElement.tsx +++ b/front/src/pages/auction/detail/PricePolicyElement.tsx @@ -23,6 +23,7 @@ function PricePolicyElement( const [nowPrice, setNowPrice] = useState(0); const [isLastPrice, setIsLastPrice] = useState(false); + const [timeUntilNextChange, setTimeUntilNextChange] = useState({ minutes: 0, seconds: 0 }); useEffect(() => { // 시작 가격을 설정한다. @@ -59,6 +60,16 @@ function PricePolicyElement( // 가격 설정 setNowPrice(currentPrice); + // 다음 가격 변동까지 남은 시간 계산 + const msUntilNextChange = durationMs - (diffMsBetweenStartedAndNow % durationMs); + const minutesUntilNextChange = Math.floor(msUntilNextChange / 60000); + const secondsUntilNextChange = Math.floor((msUntilNextChange % 60000) / 1000); + + setTimeUntilNextChange({ + minutes: minutesUntilNextChange, + seconds: secondsUntilNextChange + }); + }, 1000); return () => clearInterval(intervalId); @@ -119,7 +130,7 @@ function PricePolicyElement(

{getPriceFormatted(nowPrice)}

:
-

다음 가격

+

{timeUntilNextChange.minutes}분 {timeUntilNextChange.seconds}초 뒤

{getPriceFormatted(calculateNextPrice(nowPrice))}

} From f88602808e3c8a72ecf1a85c630d89d906e4fbce Mon Sep 17 00:00:00 2001 From: HyeonSik Choi Date: Tue, 27 Aug 2024 10:34:52 +0900 Subject: [PATCH 09/17] =?UTF-8?q?feat:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=EC=8B=9C=20=EC=95=84=EC=9D=B4=EB=94=94,=20?= =?UTF-8?q?=ED=8C=A8=EC=8A=A4=EC=9B=8C=EB=93=9C=20=EA=B7=9C=EA=B2=A9?= =?UTF-8?q?=EC=9D=84=20=EC=82=AC=EC=9A=A9=EC=9E=90=EC=97=90=EA=B2=8C=20?= =?UTF-8?q?=EC=95=88=EB=82=B4=ED=95=9C=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- front/src/pages/SignUp.tsx | 58 ++++++++++++++++++++++++++++++++++---- 1 file changed, 53 insertions(+), 5 deletions(-) diff --git a/front/src/pages/SignUp.tsx b/front/src/pages/SignUp.tsx index 2f74238e..e9faf44e 100644 --- a/front/src/pages/SignUp.tsx +++ b/front/src/pages/SignUp.tsx @@ -15,17 +15,28 @@ function SignUpPage() { password: '', userRole: 'BUYER', }); + const [idError, setIdError] = useState(" "); + const [passwordError, setPasswordError] = useState(" "); + const [isFormValid, setIsFormValid] = useState(false); const handleUserTypeChange = (e: React.ChangeEvent) => { setRequest({...request, userRole: e.target.value,}); }; const handleNameChange = (e: React.ChangeEvent) => { - setRequest({...request, signUpId: e.target.value,}); + const newSignUpId = e.target.value; + setRequest({...request, signUpId: newSignUpId}); + const idError = validateSignUpId(newSignUpId); + setIdError(idError); + validateForm(idError, passwordError); }; const handlePasswordChange = (e: React.ChangeEvent) => { - setRequest({...request, password: e.target.value,}); + const newPassword = e.target.value; + setRequest({...request, password: newPassword}); + const passwordError = validatePassword(newPassword); + setPasswordError(passwordError); + validateForm(idError, passwordError); }; const requestSignUp = () => { @@ -41,6 +52,37 @@ function SignUpPage() { ); } + const validateForm = (idError: string, passwordError: string) => { + if (idError === "" && passwordError === "") { + setIsFormValid(true); + } else { + setIsFormValid(false); + } + }; + + const validateSignUpId = (signUpId: string): string => { + if (signUpId.length < 2 || signUpId.length > 20) { + return "아이디는 2자 이상, 20자 이하로 입력해야 합니다."; + } + return ""; + }; + + const validatePassword = (password: string): string => { + if (password.length < 8 || password.length > 20) { + return "비밀번호는 8자 이상, 20자 이하로 입력해야 합니다."; + } + if (!/[0-9]/.test(password)) { + return "비밀번호에는 최소한 하나의 숫자가 포함되어야 합니다."; + } + if (!/[a-z]/.test(password)) { + return "비밀번호에는 최소한 하나의 소문자가 포함되어야 합니다."; + } + if (!/^[a-zA-Z0-9]*$/.test(password)) { + return "비밀번호는 영문 대소문자와 숫자만 사용할 수 있습니다."; + } + return ""; + }; + return (
@@ -77,17 +119,18 @@ function SignUpPage() {
- + + {idError &&

{idError}

}
@@ -102,16 +145,21 @@ function SignUpPage() { onChange={handlePasswordChange} required /> + {passwordError &&

{passwordError}

}
+
+
From 9e53facf7f06fc8b583613d6cfe71b66891eea4a Mon Sep 17 00:00:00 2001 From: HyeonSik Choi Date: Tue, 27 Aug 2024 13:22:57 +0900 Subject: [PATCH 10/17] =?UTF-8?q?feat:=20API=20=EC=9A=94=EC=B2=AD=20?= =?UTF-8?q?=EC=8B=A4=ED=8C=A8=20=EC=8B=9C,=20=EC=9D=BC=EB=B6=80=20?= =?UTF-8?q?=EC=BC=80=EC=9D=B4=EC=8A=A4=EC=97=90=EC=84=9C=20=EC=84=9C?= =?UTF-8?q?=EB=B2=84=EA=B0=80=20=EC=A0=84=EB=8B=AC=ED=95=9C=20=EB=A9=94?= =?UTF-8?q?=EC=84=B8=EC=A7=80=EB=A5=BC=20=ED=91=9C=EC=8B=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- front/src/api/auction/api.ts | 14 ++++++---- front/src/api/payments/api.ts | 7 +++-- front/src/api/receipt/api.ts | 7 +++-- front/src/api/user/api.ts | 28 +++++++++++-------- front/src/pages/ChargePoint.tsx | 4 +-- front/src/pages/Footer.tsx | 6 +++- front/src/pages/Login.tsx | 4 +-- front/src/pages/SignUp.tsx | 4 +-- .../pages/auction/detail/AuctionDetail.tsx | 9 ++++-- .../pages/receipt/detail/ReceiptDetail.tsx | 7 +++-- 10 files changed, 55 insertions(+), 35 deletions(-) diff --git a/front/src/api/auction/api.ts b/front/src/api/auction/api.ts index 303119a1..128e8737 100644 --- a/front/src/api/auction/api.ts +++ b/front/src/api/auction/api.ts @@ -30,7 +30,7 @@ async function requestAuctionDetail( baseUrl: string, auctionId: number, onSuccess: (auctionDetail: AuctionDetailItem) => void, - onFailure: () => void + onFailure: (message: string) => void ) { try { const response = await fetch(`${baseUrl}/auctions/${auctionId}`, { @@ -45,11 +45,12 @@ async function requestAuctionDetail( const auctionDetail: AuctionDetailItem = await response.json(); onSuccess(auctionDetail); } else { - onFailure(); + const errorMessage = await response.text(); + onFailure(errorMessage); } } catch (error) { console.error('Failed to fetch auction detail.', error); - onFailure(); + onFailure("REQUEST AUCTION DETAIL."); } } @@ -58,7 +59,7 @@ async function requestAuctionBid( auctionId: number, request: AuctionPurchaseRequest, onSuccess: () => void, - onFailure: () => void + onFailure: (message: string) => void ) { try { const response = await fetch(`${baseUrl}/auctions/${auctionId}/purchase`, { @@ -78,12 +79,13 @@ async function requestAuctionBid( if (response.ok) { onSuccess(); } else { - onFailure(); + const errorMessage = await response.text(); + onFailure(errorMessage); } } catch (error) { console.error('Failed to bid auction.', error); - onFailure(); + onFailure("BID REQUEST FAILED"); } } diff --git a/front/src/api/payments/api.ts b/front/src/api/payments/api.ts index 3097272d..d4a0f1ce 100644 --- a/front/src/api/payments/api.ts +++ b/front/src/api/payments/api.ts @@ -4,7 +4,7 @@ async function chargePointsApi( baseUrl: string, data: ChargePointsRequest, onSuccess: () => void, - onFailure: () => void + onFailure: (message: string) => void ) { try { const response = await fetch(`${baseUrl}/payments/points/charge`, { @@ -21,10 +21,11 @@ async function chargePointsApi( if (response.ok) { onSuccess(); } else { - onFailure(); + const errorMessage = await response.text(); + onFailure(errorMessage); } } catch (error) { - onFailure(); + onFailure("CHARGE POINT ERROR."); } } diff --git a/front/src/api/receipt/api.ts b/front/src/api/receipt/api.ts index 627ed5eb..618a6281 100644 --- a/front/src/api/receipt/api.ts +++ b/front/src/api/receipt/api.ts @@ -61,7 +61,7 @@ async function requestRefund( baseUrl: string, receiptId: number, onSuccess: () => void, - onFailure: () => void + onFailure: (message: string) => void ) { try { const response = await fetch(`${baseUrl}/receipts/${receiptId}/refund`, { @@ -76,10 +76,11 @@ async function requestRefund( if (response.ok) { onSuccess(); } else { - onFailure(); + const errorMessage = await response.text(); + onFailure(errorMessage); } } catch (error) { - onFailure(); + onFailure("REQUEST REFUND ERROR"); } } diff --git a/front/src/api/user/api.ts b/front/src/api/user/api.ts index 86b5fe59..723fd00b 100644 --- a/front/src/api/user/api.ts +++ b/front/src/api/user/api.ts @@ -1,10 +1,10 @@ -import {SignInRequest, SignUpRequest} from "./type"; +import { SignInRequest, SignUpRequest } from "./type"; async function signUpApi( baseUrl: string, data: SignUpRequest, onSuccess: () => void, - onFailure: () => void + onFailure: (message: string) => void ) { try { const response = await fetch(`${baseUrl}/auth/signup`, { @@ -18,10 +18,12 @@ async function signUpApi( if (response.ok) { onSuccess(); } else { - onFailure(); + const errorMessage = await response.text(); + onFailure(`Sign up failed: ${errorMessage}`); } } catch (error) { - onFailure(); + console.error(error); + onFailure("An unexpected error occurred during sign-up."); } } @@ -29,7 +31,7 @@ async function signInApi( baseUrl: string, data: SignInRequest, onSuccess: () => void, - onFailure: () => void + onFailure: (message: string) => void ) { try { const response = await fetch(`${baseUrl}/auth/signin`, { @@ -41,21 +43,23 @@ async function signInApi( }, body: JSON.stringify(data), }); + if (response.ok) { onSuccess(); } else { - onFailure(); + const errorMessage = await response.text(); + onFailure(`Sign in failed: ${errorMessage}`); } } catch (error) { console.error(error); - onFailure(); + onFailure("An unexpected error occurred during sign-in."); } } async function signOut( baseUrl: string, onSuccess: () => void, - onFailure: () => void + onFailure: (message: string) => void ) { try { const response = await fetch(`${baseUrl}/auth/signout`, { @@ -70,11 +74,13 @@ async function signOut( if (response.ok) { onSuccess(); } else { - onFailure(); + const errorMessage = await response.text(); + onFailure(`Sign out failed: ${errorMessage}`); } } catch (error) { - onFailure(); + console.error(error); + onFailure("An unexpected error occurred during sign-out."); } } -export {signUpApi, signInApi, signOut}; +export { signUpApi, signInApi, signOut }; diff --git a/front/src/pages/ChargePoint.tsx b/front/src/pages/ChargePoint.tsx index 816b930f..9da793ea 100644 --- a/front/src/pages/ChargePoint.tsx +++ b/front/src/pages/ChargePoint.tsx @@ -20,8 +20,8 @@ function ChargePointPage() { showAlert("포인트 충전 성공!") setRequest({amount: 0}); }, - () => { - showAlert("포인트 충전 실패!") + (message) => { + showAlert("포인트 충전 실패! " + message) } ); } diff --git a/front/src/pages/Footer.tsx b/front/src/pages/Footer.tsx index f86b646b..c9979a49 100644 --- a/front/src/pages/Footer.tsx +++ b/front/src/pages/Footer.tsx @@ -7,8 +7,11 @@ import creditCardIcon from '../img/credit-card.svg'; import listIcon from '../img/list.svg'; import logInIcon from '../img/log-in.svg'; import logOutIcon from '../img/log-out.svg'; +import useAlert from "../hooks/useAlert"; function Footer() { + const {showAlert} = useAlert(); + const { currentPage, setPage } = usePageStore(); const { isLogin, setIsLogin } = useLoginStore(); const baseUrl = process.env.REACT_APP_API_URL || ''; @@ -27,8 +30,9 @@ function Footer() { setIsLogin(false); setPage('home'); }, - () => { + (message) => { console.log('Failed to sign out.'); + showAlert(message); } ); }; diff --git a/front/src/pages/Login.tsx b/front/src/pages/Login.tsx index c3160516..dcce8424 100644 --- a/front/src/pages/Login.tsx +++ b/front/src/pages/Login.tsx @@ -29,9 +29,9 @@ function LoginPage() { setIsLogin(true); showAlert('로그인 성공하였습니다.'); }, - () => { + (response) => { setIsLogin(false); - showAlert('로그인에 실패하였습니다.'); + showAlert(response); setRequest({signInId: '', password: ''}); } ); diff --git a/front/src/pages/SignUp.tsx b/front/src/pages/SignUp.tsx index e9faf44e..07e0aba8 100644 --- a/front/src/pages/SignUp.tsx +++ b/front/src/pages/SignUp.tsx @@ -46,8 +46,8 @@ function SignUpPage() { () => { setPage('login'); }, - () => { - showAlert('회원가입에 실패했습니다.'); + (message) => { + showAlert(message); } ); } diff --git a/front/src/pages/auction/detail/AuctionDetail.tsx b/front/src/pages/auction/detail/AuctionDetail.tsx index 0de1e394..33a6ba60 100644 --- a/front/src/pages/auction/detail/AuctionDetail.tsx +++ b/front/src/pages/auction/detail/AuctionDetail.tsx @@ -111,6 +111,10 @@ function AuctionDetail({auctionId}: { auctionId?: number }) { if (status === "종료") { let krDateFormat = "경매 종료 (" + getKrDateFormat(auction.finishedAt) + ")"; setLeftInfo(krDateFormat); + } if (status === "진행 예정") { + setLeftInfo(status + " (" + timeInfo + ")"); + } if (status === "곧 시작") { + setLeftInfo(status + " (" + timeInfo + ")"); } else { setLeftInfo(status + " (" + timeInfo + ")"); setIsFinished(false); // 입찰 버튼 활성화 @@ -199,14 +203,13 @@ function AuctionDetail({auctionId}: { auctionId?: number }) { setIsButtonDisabled(true); // 버튼 비활성화 setCountdown(5); // 5초 카운트다운 시작 }, - () => { - showAlert("입찰에 실패했습니다."); + (message) => { + showAlert(message); } ); } const getButtonText = () => { - if (isFinished) return '경매 종료'; if (isButtonDisabled) return `${countdown}초 남음`; return '입찰하기'; } diff --git a/front/src/pages/receipt/detail/ReceiptDetail.tsx b/front/src/pages/receipt/detail/ReceiptDetail.tsx index 8d2b0be7..28e54e7f 100644 --- a/front/src/pages/receipt/detail/ReceiptDetail.tsx +++ b/front/src/pages/receipt/detail/ReceiptDetail.tsx @@ -5,8 +5,10 @@ import {ReceiptDetailItem} from "../../../api/receipt/type"; import {getPriceFormatted} from "../../../util/NumberUtil"; import {getKrDateFormat} from "../../../util/DateUtil"; import {usePageStore} from "../../../store/PageStore"; +import useAlert from "../../../hooks/useAlert"; function ReceiptDetailPage() { + const {showAlert} = useAlert(); const {receiptId, setReceiptId} = useReceiptStore(); const {currentPage, setPage} = usePageStore(); @@ -50,8 +52,9 @@ function ReceiptDetailPage() { console.log('Refund success.'); setPage('home'); }, - () => { - console.log('Refund failed.'); + (message) => { + console.log('Refund failed. ' + message); + showAlert(message); } ) } From 622c4804aad2832a417f719ae8c7e6bc941af481 Mon Sep 17 00:00:00 2001 From: HyeonSik Choi Date: Tue, 27 Aug 2024 13:31:37 +0900 Subject: [PATCH 11/17] =?UTF-8?q?feat:=20=ED=99=9C=EC=84=B1=ED=95=AD?= =?UTF-8?q?=EB=AA=A9=EC=9D=84=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=EC=97=90=20?= =?UTF-8?q?=ED=91=9C=EC=8B=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- front/src/pages/auction/list/AuctionListElement.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/front/src/pages/auction/list/AuctionListElement.tsx b/front/src/pages/auction/list/AuctionListElement.tsx index 1c00f4f0..3552b2e4 100644 --- a/front/src/pages/auction/list/AuctionListElement.tsx +++ b/front/src/pages/auction/list/AuctionListElement.tsx @@ -44,7 +44,8 @@ const AuctionListElement: React.FC = ({ } // 상태에 따라 현재가의 색상 설정 - const priceColor = status === '종료' ? 'text-gray-400' : 'text-[#62CBC6]'; + const priceColor = status !== '진행 중' ? 'text-gray-400' : 'text-[#62CBC6]'; + const greyScale = status !== '진행 중' ? 'grayscale' : ''; return (
= ({ Auction Item {/* 상태 태그 */} @@ -68,7 +69,7 @@ const AuctionListElement: React.FC = ({

시작 시간: {getKrDateFormat(startedAt)}

종료 시간: {getKrDateFormat(endedAt)}

시작 가격: {getPriceFormatted(price)}

-

{timeInfo}

+

{status}! {timeInfo}

); From 5f1e26a4eb47c7fa9b0e5f4c651e3ce8904a6ded Mon Sep 17 00:00:00 2001 From: HyeonSik Choi Date: Tue, 27 Aug 2024 14:08:03 +0900 Subject: [PATCH 12/17] =?UTF-8?q?refactor:=20=EC=A7=84=ED=96=89=20?= =?UTF-8?q?=EC=A4=91=EC=9D=B8=20=EA=B2=BD=EB=A7=A4=EC=97=90=20=EC=83=89?= =?UTF-8?q?=EC=83=81=EC=9D=84=20=ED=91=9C=EC=8B=9C=ED=95=9C=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pages/auction/detail/AuctionDetail.tsx | 11 +++++---- .../auction/detail/PricePolicyElement.tsx | 12 ++++++++-- .../pages/auction/list/AuctionListElement.tsx | 7 +++--- front/src/util/DateUtil.ts | 24 +++++++++++-------- 4 files changed, 34 insertions(+), 20 deletions(-) diff --git a/front/src/pages/auction/detail/AuctionDetail.tsx b/front/src/pages/auction/detail/AuctionDetail.tsx index 33a6ba60..4716e14f 100644 --- a/front/src/pages/auction/detail/AuctionDetail.tsx +++ b/front/src/pages/auction/detail/AuctionDetail.tsx @@ -24,7 +24,7 @@ function AuctionDetail({auctionId}: { auctionId?: number }) { const [quantity, setQuantity] = useState(1); const [leftInfo, setLeftInfo] = useState("불러오는 중..."); - const [isFinished, setIsFinished] = useState(true); + const [isNotRunning, setIsNotRunning] = useState(true); const [isButtonDisabled, setIsButtonDisabled] = useState(false); const [countdown, setCountdown] = useState(0); const [showConfetti, setShowConfetti] = useState(false); @@ -106,6 +106,7 @@ function AuctionDetail({auctionId}: { auctionId?: number }) { } // 현재 가격 갱신 타이머 + setIsNotRunning(true); const intervalId = setInterval(() => { const { status, timeInfo } = getAuctionStatus(auction.startedAt, auction.finishedAt); if (status === "종료") { @@ -117,13 +118,13 @@ function AuctionDetail({auctionId}: { auctionId?: number }) { setLeftInfo(status + " (" + timeInfo + ")"); } else { setLeftInfo(status + " (" + timeInfo + ")"); - setIsFinished(false); // 입찰 버튼 활성화 + setIsNotRunning(false); // 입찰 버튼 활성화 } }, 1000); return () => clearInterval(intervalId); - }, [auction]); + }, []); useEffect(() => { if (isButtonDisabled) { @@ -337,9 +338,9 @@ function AuctionDetail({auctionId}: { auctionId?: number }) {
diff --git a/front/src/pages/auction/detail/PricePolicyElement.tsx b/front/src/pages/auction/detail/PricePolicyElement.tsx index 2f384445..ab1e33f6 100644 --- a/front/src/pages/auction/detail/PricePolicyElement.tsx +++ b/front/src/pages/auction/detail/PricePolicyElement.tsx @@ -24,6 +24,7 @@ function PricePolicyElement( const [nowPrice, setNowPrice] = useState(0); const [isLastPrice, setIsLastPrice] = useState(false); const [timeUntilNextChange, setTimeUntilNextChange] = useState({ minutes: 0, seconds: 0 }); + const [isStarted, setIsStarted] = useState(false); useEffect(() => { // 시작 가격을 설정한다. @@ -31,7 +32,6 @@ function PricePolicyElement( // 현재 가격 갱신 타이머 const intervalId = setInterval(() => { - // 가격 하락 정책이 종료되었는지 체크 const now = new Date(); let isLastTime = false; @@ -70,6 +70,10 @@ function PricePolicyElement( seconds: secondsUntilNextChange }); + // 경매 시작했는지 확인하는 로직 + if (now >= auction.startedAt) { + setIsStarted(true); + } }, 1000); return () => clearInterval(intervalId); @@ -130,7 +134,11 @@ function PricePolicyElement(

{getPriceFormatted(nowPrice)}

:
-

{timeUntilNextChange.minutes}분 {timeUntilNextChange.seconds}초 뒤

+ { + isStarted + ?

{timeUntilNextChange.minutes}분 {timeUntilNextChange.seconds}초 뒤

+ :

첫 할인가!

+ }

{getPriceFormatted(calculateNextPrice(nowPrice))}

} diff --git a/front/src/pages/auction/list/AuctionListElement.tsx b/front/src/pages/auction/list/AuctionListElement.tsx index 3552b2e4..f61a7120 100644 --- a/front/src/pages/auction/list/AuctionListElement.tsx +++ b/front/src/pages/auction/list/AuctionListElement.tsx @@ -24,12 +24,14 @@ const AuctionListElement: React.FC = ({ const [status, setStatus] = useState(''); const [timeInfo, setTimeInfo] = useState(''); + const [tagColor, setTagColor] = useState('bg-gray-500'); useEffect(() => { const updateStatus = () => { - const { status, timeInfo } = getAuctionStatus(startedAt, endedAt); + const { status, timeInfo, color } = getAuctionStatus(startedAt, endedAt); setStatus(status); setTimeInfo(timeInfo); + setTagColor(color); }; updateStatus(); @@ -59,8 +61,7 @@ const AuctionListElement: React.FC = ({ alt="Auction Item" className={`w-full h-40 object-cover ${greyScale}`} // Apply grayscale based on status /> - {/* 상태 태그 */} - + {status} diff --git a/front/src/util/DateUtil.ts b/front/src/util/DateUtil.ts index 407a9c76..f4709e72 100644 --- a/front/src/util/DateUtil.ts +++ b/front/src/util/DateUtil.ts @@ -88,14 +88,14 @@ const getAuctionStatus = (startedAt: Date, endedAt: Date): { status: string; tim return { status: '곧 시작', timeInfo: formatTime(timeUntilStart), - color: 'text-red-500' + color: 'bg-red-500' }; } else { // 5분 이상 return { status: '진행 예정', timeInfo: formatTime(timeUntilStart), - color: 'text-blue-500' + color: 'bg-blue-500' }; } } else if (now >= startedAt && now <= endedAt) { @@ -104,7 +104,7 @@ const getAuctionStatus = (startedAt: Date, endedAt: Date): { status: string; tim return { status: '진행 중', timeInfo: `남은 시간: ${formatTime(timeRemaining)}`, - color: 'text-red-500' + color: 'bg-red-500' }; } else { // 경매 종료 @@ -112,7 +112,7 @@ const getAuctionStatus = (startedAt: Date, endedAt: Date): { status: string; tim return { status: '종료', timeInfo: formatEndTime(timeElapsed), - color: 'text-gray-500' + color: 'bg-gray-500' }; } }; @@ -142,14 +142,18 @@ function getMsFromIso8601Duration(duration: string): number { } function getTimeDifferenceInMs(date1: Date, date2: Date): number { - const timeDifference = date2.getTime() - date1.getTime(); - if (timeDifference < 0) { - return -timeDifference; - } else { - return timeDifference; - } + return date2.getTime() - date1.getTime(); } +// function getTimeDifferenceInMs(date1: Date, date2: Date): number { +// const timeDifference = date2.getTime() - date1.getTime(); +// if (timeDifference < 0) { +// return -timeDifference; +// } else { +// return timeDifference; +// } +// } + export { getKrDateFormat, getRelativeTime, From b6c02ca3740e41c4556c0f75b6b310bc545c0fe6 Mon Sep 17 00:00:00 2001 From: HyeonSik Choi Date: Tue, 27 Aug 2024 14:09:21 +0900 Subject: [PATCH 13/17] =?UTF-8?q?feat:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=EC=84=B1=EA=B3=B5=EC=8B=9C=20=EC=84=B1=EA=B3=B5=20?= =?UTF-8?q?=EB=A9=98=ED=8A=B8=20=ED=91=9C=EC=8B=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- front/src/pages/SignUp.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/front/src/pages/SignUp.tsx b/front/src/pages/SignUp.tsx index 07e0aba8..73773a79 100644 --- a/front/src/pages/SignUp.tsx +++ b/front/src/pages/SignUp.tsx @@ -44,6 +44,7 @@ function SignUpPage() { baseUrl, request, () => { + showAlert("회원가입에 성공했습니다!"); setPage('login'); }, (message) => { From 1981b76fcb74dc5952589edd1ccc990d1b9058b5 Mon Sep 17 00:00:00 2001 From: HyeonSik Choi Date: Tue, 27 Aug 2024 14:11:00 +0900 Subject: [PATCH 14/17] =?UTF-8?q?refactor:=20=EA=B1=B0=EB=9E=98=EB=82=B4?= =?UTF-8?q?=EC=97=AD=EC=97=90=EC=84=9C=20=EC=83=9D=EC=84=B1=EC=9D=BC?= =?UTF-8?q?=EA=B3=BC=20=EC=88=98=EC=A0=95=EC=9D=BC=EC=9D=84=20=ED=91=9C?= =?UTF-8?q?=EC=8B=9C=ED=95=98=EC=A7=80=20=EC=95=8A=EC=8A=B5=EB=8B=88?= =?UTF-8?q?=EB=8B=A4.=20(=EC=84=9C=EB=B2=84=EC=97=90=EC=84=9C=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=ED=95=98=EC=A7=80=20=EC=95=8A=EC=9D=8C)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pages/receipt/detail/ReceiptDetail.tsx | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/front/src/pages/receipt/detail/ReceiptDetail.tsx b/front/src/pages/receipt/detail/ReceiptDetail.tsx index 28e54e7f..d6805af8 100644 --- a/front/src/pages/receipt/detail/ReceiptDetail.tsx +++ b/front/src/pages/receipt/detail/ReceiptDetail.tsx @@ -105,17 +105,17 @@ function ReceiptDetailPage() {

{receiptDetail?.buyerId}

-
- -

{getKrDateFormat(new Date(receiptDetail!.createdAt))}

-
- -
- -

- {getKrDateFormat(new Date(receiptDetail!.updatedAt))} -

-
+ {/*
*/} + {/* */} + {/*

{getKrDateFormat(new Date(receiptDetail!.createdAt))}

*/} + {/*
*/} + + {/*
*/} + {/* */} + {/*

*/} + {/* {getKrDateFormat(new Date(receiptDetail!.updatedAt))}*/} + {/*

*/} + {/*
*/} { receiptDetail?.receiptStatus === 'REFUND' ? '' From 12fd1d41995db9ba1c7fbfab50118ae61dd52bd2 Mon Sep 17 00:00:00 2001 From: HyeonSik Choi Date: Tue, 27 Aug 2024 14:12:16 +0900 Subject: [PATCH 15/17] =?UTF-8?q?refactor:=20=ED=8C=90=EB=A7=A4=EC=9E=90?= =?UTF-8?q?=20=EA=B0=80=EC=9E=85=20=EA=B8=B0=EB=8A=A5=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- front/src/pages/SignUp.tsx | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/front/src/pages/SignUp.tsx b/front/src/pages/SignUp.tsx index 73773a79..3975ea87 100644 --- a/front/src/pages/SignUp.tsx +++ b/front/src/pages/SignUp.tsx @@ -104,18 +104,20 @@ function SignUpPage() { /> 구매자 - + + {/**/} + From 58616247a5a0f0a811b501027e4355fba8b94bef Mon Sep 17 00:00:00 2001 From: HyeonSik Choi Date: Tue, 27 Aug 2024 14:37:18 +0900 Subject: [PATCH 16/17] =?UTF-8?q?fix:=20=EA=B2=BD=EB=A7=A4=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=ED=8E=98=EC=9D=B4=EC=A7=80=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EA=B0=80=EA=B2=A9=EC=9D=84=20=EA=B0=B1=EC=8B=A0=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=ED=83=80=EC=9D=B4=EB=A8=B8=20=EC=98=A4=EB=A5=98=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pages/auction/detail/AuctionDetail.tsx | 36 +++++++++---------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/front/src/pages/auction/detail/AuctionDetail.tsx b/front/src/pages/auction/detail/AuctionDetail.tsx index 4716e14f..51265e78 100644 --- a/front/src/pages/auction/detail/AuctionDetail.tsx +++ b/front/src/pages/auction/detail/AuctionDetail.tsx @@ -79,7 +79,6 @@ function AuctionDetail({auctionId}: { auctionId?: number }) { }, []); useEffect(() => { - if (auctionId === undefined) { return; } @@ -105,26 +104,23 @@ function AuctionDetail({auctionId}: { auctionId?: number }) { return; } - // 현재 가격 갱신 타이머 + // 남은 시간 갱신 타이머 setIsNotRunning(true); - const intervalId = setInterval(() => { - const { status, timeInfo } = getAuctionStatus(auction.startedAt, auction.finishedAt); - if (status === "종료") { - let krDateFormat = "경매 종료 (" + getKrDateFormat(auction.finishedAt) + ")"; - setLeftInfo(krDateFormat); - } if (status === "진행 예정") { - setLeftInfo(status + " (" + timeInfo + ")"); - } if (status === "곧 시작") { - setLeftInfo(status + " (" + timeInfo + ")"); - } else { - setLeftInfo(status + " (" + timeInfo + ")"); - setIsNotRunning(false); // 입찰 버튼 활성화 - } - - }, 1000); - - return () => clearInterval(intervalId); - }, []); + const {status, timeInfo} = getAuctionStatus(auction.startedAt, auction.finishedAt); + if (status === "종료") { + let krDateFormat = "경매 종료 (" + getKrDateFormat(auction.finishedAt) + ")"; + setLeftInfo(krDateFormat); + } + if (status === "진행 예정") { + setLeftInfo(status + " (" + timeInfo + ")"); + } + if (status === "곧 시작") { + setLeftInfo(status + " (" + timeInfo + ")"); + } else { + setLeftInfo(status + " (" + timeInfo + ")"); + setIsNotRunning(false); // 입찰 버튼 활성화 + } + }, [auction]); useEffect(() => { if (isButtonDisabled) { From 54a297cc2c88bc2393e6cf5977159117bf4e8189 Mon Sep 17 00:00:00 2001 From: HyeonSik Choi Date: Tue, 27 Aug 2024 14:42:38 +0900 Subject: [PATCH 17/17] =?UTF-8?q?fix:=20=EC=A2=85=EB=A3=8C=EB=90=9C=20?= =?UTF-8?q?=EA=B2=BD=EB=A7=A4=EC=97=90=EC=84=9C=20=EC=9E=85=EC=B0=B0?= =?UTF-8?q?=EB=B2=84=ED=8A=BC=EC=9D=B4=20=ED=99=9C=EC=84=B1=ED=99=94?= =?UTF-8?q?=EB=90=98=EB=8D=98=20=EB=AC=B8=EC=A0=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- front/src/pages/auction/detail/AuctionDetail.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/front/src/pages/auction/detail/AuctionDetail.tsx b/front/src/pages/auction/detail/AuctionDetail.tsx index 51265e78..9c0fe3fc 100644 --- a/front/src/pages/auction/detail/AuctionDetail.tsx +++ b/front/src/pages/auction/detail/AuctionDetail.tsx @@ -105,16 +105,16 @@ function AuctionDetail({auctionId}: { auctionId?: number }) { } // 남은 시간 갱신 타이머 - setIsNotRunning(true); + // setIsNotRunning(true); const {status, timeInfo} = getAuctionStatus(auction.startedAt, auction.finishedAt); if (status === "종료") { let krDateFormat = "경매 종료 (" + getKrDateFormat(auction.finishedAt) + ")"; setLeftInfo(krDateFormat); } - if (status === "진행 예정") { + else if (status === "진행 예정") { setLeftInfo(status + " (" + timeInfo + ")"); } - if (status === "곧 시작") { + else if (status === "곧 시작") { setLeftInfo(status + " (" + timeInfo + ")"); } else { setLeftInfo(status + " (" + timeInfo + ")");