From 4fc294c3406880214972d4fbdca464810b718001 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=91=ED=98=84?= Date: Tue, 19 Sep 2023 20:50:04 +0900 Subject: [PATCH] =?UTF-8?q?[Feat]=EC=84=9C=EB=B9=84=EC=8A=A4=20=EB=94=94?= =?UTF-8?q?=ED=85=8C=EC=9D=BC=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=EC=9D=B4=20?= =?UTF-8?q?=ED=95=84=EC=9A=94=ED=95=9C=20=EC=84=9C=EB=B9=84=EC=8A=A4?= =?UTF-8?q?=EC=9E=85=EB=8B=88=EB=8B=A4=20=ED=8E=98=EC=9D=B4=EC=A7=80=20?= =?UTF-8?q?=EC=97=B0=EA=B2=B0=20=ED=98=84=EA=B8=88=20=EC=B6=A9=EC=A0=84?= =?UTF-8?q?=EC=9D=B4=20=ED=95=84=EC=9A=94=ED=95=9C=20=EC=84=9C=EB=B9=84?= =?UTF-8?q?=EC=8A=A4=EC=9E=85=EB=8B=88=EB=8B=A4=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=20=EC=97=B0=EA=B2=B0=20=ED=98=84=EA=B8=88=20=EC=B6=A9?= =?UTF-8?q?=EC=A0=84=20=EB=A9=94=EC=8B=9C=EC=A7=80=20=EC=9E=91=EC=84=B1=20?= =?UTF-8?q?Issues=20#15?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/HoldingList/HoldingList.tsx | 59 +++++++++++++------ .../HoldingList/LoginRequestIndicator.tsx | 46 +++++++++++++++ client/src/components/Profile/cashModal.tsx | 30 +++++++++- .../components/StockOrderSection/Index.tsx | 30 +++++++--- client/src/components/watchlist/WatchList.tsx | 8 ++- client/src/page/MainPage.tsx | 7 ++- 6 files changed, 148 insertions(+), 32 deletions(-) create mode 100644 client/src/components/HoldingList/LoginRequestIndicator.tsx diff --git a/client/src/components/HoldingList/HoldingList.tsx b/client/src/components/HoldingList/HoldingList.tsx index d1308062..a990e925 100644 --- a/client/src/components/HoldingList/HoldingList.tsx +++ b/client/src/components/HoldingList/HoldingList.tsx @@ -5,6 +5,9 @@ import StockItem from "./StockItem"; import useGetStockHolds from "../../hooks/useGetStockholds"; import { StockItemProps } from "./StockItem"; import useCompanyData from "../../hooks/useCompanyData"; +import LoginRequestIndicator from "./LoginRequestIndicator"; +import { RootState } from "../../store/config"; +import { useSelector } from "react-redux"; // ๐Ÿ”ด const evalutationProfitText = "ํ‰๊ฐ€ ์ˆ˜์ต๊ธˆ"; @@ -20,9 +23,19 @@ const HoldingList: React.FC = ({ currentListType, onChangeListTy // ๋ชจ๋“  stockReturn์˜ ํ•ฉ์„ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค. let totalEvaluationProfit = 0; - if (stockHolds) { - totalEvaluationProfit = stockHolds.reduce((sum: number, stockHold: StockItemProps["stockData"]) => sum + stockHold.stockReturn, 0); - } +if (Array.isArray(stockHolds) && stockHolds.length > 0) { + totalEvaluationProfit = stockHolds.reduce((sum: number, stockHold: StockItemProps["stockData"]) => sum + stockHold.stockReturn, 0); +} + + // if (stockHolds) { + // totalEvaluationProfit = stockHolds.reduce((sum: number, stockHold: StockItemProps["stockData"]) => sum + stockHold.stockReturn, 0); + // } + const isLogin = useSelector((state: RootState) => state.login); // ๋กœ๊ทธ์ธ ์ƒํƒœ ๊ฐ€์ ธ์˜ค๊ธฐ + + // OAuth ๋ชจ๋‹ฌ์„ ์—ด๊ธฐ ์œ„ํ•œ ํ•จ์ˆ˜ + const openOAuthModal = () => { + // OAuth ๋กœ๊ทธ์ธ ๋ชจ๋‹ฌ์„ ์—ด๊ธฐ ์œ„ํ•œ ๋กœ์ง + }; return ( @@ -40,20 +53,31 @@ const HoldingList: React.FC = ({ currentListType, onChangeListTy {/* */} - {isLoading || isCompanyDataLoading ? ( -
Loading...
- ) : isError || isCompanyDataError ? ( -
Error fetching data
- ) : ( - stockHolds.map((stockHold: StockItemProps["stockData"]) => { - const matchedCompany = companyData ? companyData.find((company) => company.companyId === stockHold.companyId) : undefined; - - return matchedCompany ? : null; - }) - )} -
-
- ); + {isLogin === 0 ? ( + + ) : isLoading || isCompanyDataLoading ? ( +
Loading...
+ ) : isError || isCompanyDataError ? ( +
Error fetching data
+ ) : ( + (Array.isArray(stockHolds) && stockHolds.length > 0) && // ์—ฌ๊ธฐ์— ์กฐ๊ฑด์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค + stockHolds.map((stockHold: StockItemProps["stockData"]) => { + const matchedCompany = companyData ? companyData.find((company) => company.companyId === stockHold.companyId) : undefined; + + return matchedCompany ? ( + + ) : null; + }) + )} + + +); }; export default HoldingList; @@ -61,6 +85,7 @@ export default HoldingList; type WatchListProps = { currentListType: "์ „์ฒด์ข…๋ชฉ" | "๊ด€์‹ฌ์ข…๋ชฉ" | "๋ณด์œ ์ข…๋ชฉ"; onChangeListType: (type: "์ „์ฒด์ข…๋ชฉ" | "๊ด€์‹ฌ์ข…๋ชฉ" | "๋ณด์œ ์ข…๋ชฉ") => void; + openOAuthModal: () => void; }; const WatchListContainer = styled.div` diff --git a/client/src/components/HoldingList/LoginRequestIndicator.tsx b/client/src/components/HoldingList/LoginRequestIndicator.tsx new file mode 100644 index 00000000..83edfff8 --- /dev/null +++ b/client/src/components/HoldingList/LoginRequestIndicator.tsx @@ -0,0 +1,46 @@ +// LoginRequestIndicator.tsx + +import React from "react"; +import styled from "styled-components"; + + +interface LoginRequestIndicatorProps { + openOAuthModal: () => void; +} + +const LoginRequestIndicator: React.FC = ({ openOAuthModal }) => { + return ( + +
๋กœ๊ทธ์ธ์ด ํ•„์š”ํ•œ ์„œ๋น„์Šค์ž…๋‹ˆ๋‹ค.
+ +
+ ); +}; + +const LoginRequestContainer = styled.div` + width: 100%; + height: 100%; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 8px; + + .Notification { + color: #999999; + } + + .LoginButton { + width: 170px; + height: 32px; + font-size: 15px; + font-weight: 400; + color: white; + background-color: #2f4f4f; + border: none; + border-radius: 0.3rem; + cursor: pointer; + } +`; + +export default LoginRequestIndicator; \ No newline at end of file diff --git a/client/src/components/Profile/cashModal.tsx b/client/src/components/Profile/cashModal.tsx index f67f799e..6480b33a 100644 --- a/client/src/components/Profile/cashModal.tsx +++ b/client/src/components/Profile/cashModal.tsx @@ -18,6 +18,8 @@ const CashModal: React.FC<{ onClose: () => void }> = ({ onClose }) => { const resetButtonText = "ํ˜„๊ธˆ ์žฌ์ƒ์„ฑ"; // const refreshButtonText ="์ƒˆ๋กœ๊ณ ์นจ"; + const [errorMessage, setErrorMessage] = useState(null); // ์—๋Ÿฌ ๋ฉ”์‹œ์ง€ ์ƒํƒœ ๋ณ€์ˆ˜ ์ถ”๊ฐ€ + const dispatch = useDispatch(); // useGetCash ํ›…์„ ์‚ฌ์šฉํ•˜์—ฌ ํ˜„๊ธˆ ๋ณด์œ ๋Ÿ‰ ๊ฐ€์ ธ์˜ค๊ธฐ @@ -57,6 +59,15 @@ const CashModal: React.FC<{ onClose: () => void }> = ({ onClose }) => { } }; + const validateCashInput = (inputValue: string) => { + const numericValue = parseInt(inputValue.replace(/,/g, ''), 10); + if (isNaN(numericValue) || numericValue < 1000000 || numericValue > 500000000) { + setErrorMessage("100๋งŒ์—์„œ 5์–ต ์‚ฌ์ด์˜ ์ˆ˜๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”"); + } else { + setErrorMessage(null); + } + }; + const handleEnterPress = (event: React.KeyboardEvent, action: () => void) => { if (event.key === 'Enter') { action(); @@ -75,10 +86,14 @@ return ( setInitialAmount(e.target.value)} + onChange={e => { + setInitialAmount(e.target.value); + validateCashInput(e.target.value); // ์ž…๋ ฅ ๊ฐ’ ๋ณ€๊ฒฝ ์‹œ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ + }} placeholder={cashCreationPlaceholder} onKeyDown={(event) => handleEnterPress(event, handleCreateCash)} /> + {errorMessage && {errorMessage}} {createCashButtonText} )} @@ -89,10 +104,14 @@ return ( setCashInput(e.target.value)} + onChange={e => { + setCashInput(e.target.value); + validateCashInput(e.target.value); // ์ž…๋ ฅ ๊ฐ’ ๋ณ€๊ฒฝ ์‹œ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ + }} placeholder={cashInputPlaceholder} onKeyDown={(event) => handleEnterPress(event, handleCashReset)} /> + {errorMessage && {errorMessage}} {resetButtonText} )} @@ -193,3 +212,10 @@ const Content = styled.p` color: #555; // ์ƒ‰์ƒ ๋ณ€๊ฒฝ text-align: center; // ํ…์ŠคํŠธ ์ค‘์•™ ์ •๋ ฌ `; + +// ์—๋Ÿฌ ๋ฉ”์‹œ์ง€ ์Šคํƒ€์ผ๋ง +const ErrorMessage = styled.div` + color: red; + font-size: 0.8rem; + margin-top: 5px; +`; \ No newline at end of file diff --git a/client/src/components/StockOrderSection/Index.tsx b/client/src/components/StockOrderSection/Index.tsx index 6a9133f0..e6039f68 100644 --- a/client/src/components/StockOrderSection/Index.tsx +++ b/client/src/components/StockOrderSection/Index.tsx @@ -40,7 +40,7 @@ import LGelec from "../../asset/logos/LG์ „์ž.svg"; import LGchem from "../../asset/logos/LGํ™”ํ•™.svg"; import posco from "../../asset/logos/POSCOํ™€๋”ฉ์Šค.svg"; -const StockOrderSection = () => { +const StockOrderSection: React.FC = (props) => { const dispatch = useDispatch(); const isLogin = useSelector((state: StateProps) => state.login); const companyId = useSelector((state: StateProps) => state.companyId); @@ -96,7 +96,7 @@ const StockOrderSection = () => { ✕ - + ); } @@ -149,7 +149,7 @@ const StockOrderSection = () => { ) : ( - + //props์ „๋‹ฌ )} ); @@ -157,26 +157,41 @@ const StockOrderSection = () => { export default StockOrderSection; +interface StockOrderSectionProps { + openOAuthModal: () => void; + openProfileModal: () => void; // Add this line +} + + // ๋ฏธ๋กœ๊ทธ์ธ ์‹œ -> ๋กœ๊ทธ์ธ ์š”์ฒญ ํ™”๋ฉด -const LoginRequestIndicator = () => { +//props ์ „๋‹ฌ +const LoginRequestIndicator: React.FC = ({ openOAuthModal }) => { return (
{loginRequiredText}
- +
); }; +interface LoginRequestIndicatorProps { + openOAuthModal: () => void; +} // ํ˜„๊ธˆ ์ถฉ์ „์š”์ฒญ ํ™”๋ฉด -const MoneyReqireIndicator = () => { +//props ์ „๋‹ฌ +const MoneyReqireIndicator: React.FC = ({ openProfileModal }) => { return (
{moneyRequireText}
- +
); }; +interface MoneyReqireIndicatorProps { + openProfileModal: () => void; +} + // component ์ƒ์„ฑ const Container = styled.aside<{ orderSet: boolean }>` position: fixed; @@ -270,6 +285,7 @@ const LoginRequestContainer = styled.div` background-color: #2f4f4f; border: none; border-radius: 0.3rem; + cursor: pointer; } `; diff --git a/client/src/components/watchlist/WatchList.tsx b/client/src/components/watchlist/WatchList.tsx index d41cc7c2..45bb5c69 100644 --- a/client/src/components/watchlist/WatchList.tsx +++ b/client/src/components/watchlist/WatchList.tsx @@ -7,8 +7,9 @@ import useCompanyData from "../../hooks/useCompanyData"; import useGetStars from "../../hooks/stars/useGetstars.ts"; // useGetStars ํ›…์˜ ๊ฒฝ๋กœ๋ฅผ ์ง€์ •ํ•ด์ฃผ์„ธ์š”. import { useSelector } from "react-redux"; import { RootState } from "../../store/config.ts"; +import LoginRequestIndicator from "../HoldingList/LoginRequestIndicator.tsx" -const WatchList: React.FC = ({ currentListType, onChangeListType }) => { +const WatchList: React.FC = ({ currentListType, onChangeListType, openOAuthModal}) => { const [isMenuOpen, setMenuOpen] = useState(false); const loginStatus = useSelector((state: RootState) => state.login); @@ -28,7 +29,7 @@ const WatchList: React.FC = ({ currentListType, onChangeListType useEffect(() => { console.log("Updated starredCompanyIds:", starredCompanyIds); // ์—ฌ๊ธฐ๊ฐ€ ์ถœ๋ ฅ๋˜๋Š”์ง€ ํ™•์ธ }, [starredCompanyIds]); - + const handleCompanyDelete = (deletedCompanyId: number) => { console.log("Company ID to delete:", deletedCompanyId); // ์—ฌ๊ธฐ๊ฐ€ ์ถœ๋ ฅ๋˜๋Š”์ง€ ํ™•์ธ setStarredCompanyIds(prevState => prevState.filter(id => id !== deletedCompanyId)); @@ -51,7 +52,7 @@ const WatchList: React.FC = ({ currentListType, onChangeListType ) : loginStatus === 1 ? ( companiesList.filter((company) => starredCompanyIds.includes(company.companyId)).map((company) => ) ) : ( -
๋กœ๊ทธ์ธ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.
+ )} @@ -61,6 +62,7 @@ const WatchList: React.FC = ({ currentListType, onChangeListType type WatchListProps = { currentListType: "์ „์ฒด์ข…๋ชฉ" | "๊ด€์‹ฌ์ข…๋ชฉ" | "๋ณด์œ ์ข…๋ชฉ"; onChangeListType: (type: "์ „์ฒด์ข…๋ชฉ" | "๊ด€์‹ฌ์ข…๋ชฉ" | "๋ณด์œ ์ข…๋ชฉ") => void; + openOAuthModal: () => void; // Add this line }; const WatchListContainer = styled.div` diff --git a/client/src/page/MainPage.tsx b/client/src/page/MainPage.tsx index 2325dfa1..768472e6 100644 --- a/client/src/page/MainPage.tsx +++ b/client/src/page/MainPage.tsx @@ -179,13 +179,14 @@ const MainPage = () => { {selectedMenu === "์ „์ฒด์ข…๋ชฉ" ? ( ) : selectedMenu === "๊ด€์‹ฌ์ข…๋ชฉ" ? ( - + ) : selectedMenu === "๋ณด์œ ์ข…๋ชฉ" ? ( - + ) : null} - + {/* props์ „๋‹ฌ */} + {isOAuthModalOpen && (