diff --git a/client/src/asset/images/menu.png b/client/src/asset/images/menu.png
new file mode 100644
index 00000000..06afba7a
Binary files /dev/null and b/client/src/asset/images/menu.png differ
diff --git a/client/src/asset/logos/LG_logo.svg b/client/src/asset/logos/LG_logo.svg
new file mode 100644
index 00000000..28ef44b3
--- /dev/null
+++ b/client/src/asset/logos/LG_logo.svg
@@ -0,0 +1,186 @@
+
+
+
+
diff --git a/client/src/asset/logos/POSCO_logo.svg b/client/src/asset/logos/POSCO_logo.svg
new file mode 100644
index 00000000..ae93452e
--- /dev/null
+++ b/client/src/asset/logos/POSCO_logo.svg
@@ -0,0 +1,19 @@
+
+
+
+
+
diff --git a/client/src/asset/logos/SK_logo.png b/client/src/asset/logos/SK_logo.png
new file mode 100644
index 00000000..701c350d
Binary files /dev/null and b/client/src/asset/logos/SK_logo.png differ
diff --git a/client/src/asset/logos/Samsung_logo.svg b/client/src/asset/logos/Samsung_logo.svg
new file mode 100644
index 00000000..06f0f3e1
--- /dev/null
+++ b/client/src/asset/logos/Samsung_logo.svg
@@ -0,0 +1,99 @@
+
+
+
+
\ No newline at end of file
diff --git a/client/src/components/Logins/OAuthLogin.tsx b/client/src/components/Logins/OAuthLogin.tsx
index 665385ee..78e1b1c2 100644
--- a/client/src/components/Logins/OAuthLogin.tsx
+++ b/client/src/components/Logins/OAuthLogin.tsx
@@ -74,6 +74,8 @@ interface LoginModalProps {
onClose: () => void;
onEmailLoginClick: () => void;
onEmailSignupClick: () => void; // 추가
+ onWatchListClick: () => void; // 추가
+ onHoldingsClick: () => void; // 추가
}
const OrText = styled.span`
diff --git a/client/src/components/watchlist/Holdings.tsx b/client/src/components/watchlist/Holdings.tsx
new file mode 100644
index 00000000..6c51b0cd
--- /dev/null
+++ b/client/src/components/watchlist/Holdings.tsx
@@ -0,0 +1,246 @@
+import React, { useState } from 'react';
+import styled from 'styled-components';
+import Samsung_logo from "../../asset/logos/Samsung_logo.svg"
+import Menu_icon from "../../asset/images/menu.png"
+
+const Holdings: React.FC = ({ currentListType, onChangeListType }) => {
+ const [isMenuOpen, setMenuOpen] = useState(false);
+
+
+ const holdingsData = [
+ { name: "삼성전자", code: "005930", price: "71,000원", change: "+6.13%",
+ profit: "수익", holding: "보유", profitAmount: "+10,000원", purchasePrice: "61,000원",
+ rateOfReturn: "+15%", stocksHeld: "100주", logo: Samsung_logo
+ },
+ // ... (다른 종목들의 더미 데이터도 추가 가능)
+ ];
+
+ const [showChangePrice, setShowChangePrice] = useState(false);
+
+ return (
+
+
+ setMenuOpen(!isMenuOpen)}
+ />
+ {currentListType}
+ {isMenuOpen && (
+
+
+
+
+ )}
+
+
+ 평가 수익금: +5,000,000원 {/* 임의의 평가 수익금 */}
+
+ {holdingsData.map(stock => (
+ <>
+
+
+
+ {stock.name}
+ {stock.code}
+
+
+ {stock.price}
+ setShowChangePrice(true)}
+ onMouseLeave={() => setShowChangePrice(false)}
+ >
+ {showChangePrice ? stock.profitAmount : stock.change}
+
+
+
+
+
+ {stock.profit}
+ {stock.holding}
+
+
+ {stock.profitAmount}
+ {stock.purchasePrice}
+
+
+ {stock.rateOfReturn}
+ {stock.stocksHeld}
+
+
+
+ >
+ ))}
+
+ );
+};
+
+type HoldingsProps = {
+ currentListType: "관심목록" | "투자목록";
+ onChangeListType: (type: "관심목록" | "투자목록") => void;
+};
+
+const getColorByChange = (change: string) => {
+ if (change.startsWith('+')) return 'red';
+ if (change.startsWith('-')) return 'blue';
+ return 'black';
+ };
+
+ const HoldingsContainer = styled.div`
+ padding: 16px;
+ `;
+
+ const Header = styled.div`
+ display: flex;
+ align-items: center;
+ position: relative;
+ `;
+
+ const Icon = styled.img`
+ width: 24px;
+ height: 24px;
+ cursor: pointer;
+ margin-right: 10px;
+ `;
+
+ const HeaderText = styled.span`
+ font-size: 18px;
+ `;
+
+ const SlideMenu = styled.div`
+ position: absolute;
+ top: 100%;
+ left: 0;
+ width: 248px;
+ background-color: #f7f7f7;
+ border: 1px solid #e0e0e0;
+ display: flex;
+ flex-direction: column;
+ `;
+
+ const MenuItem = styled.button`
+ padding: 8px 16px;
+ border: none;
+ background-color: transparent;
+ cursor: pointer;
+ text-align: left;
+
+ &:hover {
+ background-color: #e0e0e0;
+ }
+ `;
+
+ const StockItem = styled.button`
+ display: flex;
+ justify-content: space-between;
+ align-items: flex-start; // 시작 위치 정렬 추가
+ padding: 8px 0;
+ border-bottom: 1px solid #e0e0e0;
+ width: 100%;
+ background-color: transparent;
+ cursor: pointer;
+ border: none;
+ text-align: left;
+ `;
+
+ const Logo = styled.img`
+ border-radius: 50%;
+ width: 40px;
+ height: 40px;
+ margin-right: 12px;
+ `;
+
+ const StockInfo = styled.div`
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start; // 시작 위치 정렬 추가
+ margin-right: 16px; // 간격 추가
+ `;
+
+ const StockName = styled.span`
+ font-weight: bold;
+ `;
+
+ const StockCode = styled.span`
+ color: gray;
+ `;
+
+ const StockPriceSection = styled.div`
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start; // 시작 위치 정렬 추가
+ `;
+
+ const StockPrice = styled.span.attrs<{ change: string }>(({ change }) => ({
+ style: {
+ color: getColorByChange(change),
+ },
+ }))``;
+
+ const StockChange = styled.span.attrs<{ change: string }>(({ change }) => ({
+ style: {
+ color: getColorByChange(change),
+ },
+ }))`
+ cursor: pointer; // 마우스 포인터 변경 추가
+ `;
+
+ const Divider = styled.div`
+ height: 1px;
+ background-color: #aaa; // 회색으로 설정
+ margin: 8px 0; // 상하 여백 추가
+ `;
+
+
+const EvaluationProfit = styled.div`
+ font-size: 16px;
+ font-weight: bold;
+ margin: 10px 0;
+ text-align: center;
+ color: red; // 수익금이 플러스일 경우 초록색으로 표시
+`;
+
+const StockDetails = styled.div`
+ display: flex;
+ justify-content: space-between;
+ padding: 8px 0;
+ width: 100%;
+`;
+
+const DetailSection = styled.div`
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+`;
+
+const DetailTitle = styled.span`
+ font-weight: light;
+ font-size : 14px;
+`;
+
+const DetailData = styled.span`
+ font-size: 14px; // Setting standardized font size for all data
+`;
+
+const getColorByValue = (value: string) => {
+ if (value.startsWith('+')) return 'red';
+ if (value.startsWith('-')) return 'blue';
+ return 'black';
+};
+
+const ColoredDetailData = styled.span.attrs<{ value: string }>(({ value }) => ({
+ style: {
+ color: getColorByValue(value),
+ },
+}))`
+ font-size: 14px; // Setting standardized font size for all data
+`;
+
+const ThickDivider = styled.div`
+ height: 2px;
+ background-color: #aaa;
+ margin: 8px 0;
+`;
+
+export default Holdings;
diff --git a/client/src/components/watchlist/WatchList.tsx b/client/src/components/watchlist/WatchList.tsx
new file mode 100644
index 00000000..5fcee4b4
--- /dev/null
+++ b/client/src/components/watchlist/WatchList.tsx
@@ -0,0 +1,197 @@
+import React, { useState } from 'react';
+import styled from 'styled-components';
+import Samsung_logo from "../../asset/logos/Samsung_logo.svg"
+import LG_logo from "../../asset/logos/LG_logo.svg"
+import Sk_logo from "../../asset/logos/Sk_logo.png"
+import POSCO_logo from "../../asset/logos/POSCO_logo.svg"
+import Menu_icon from "../../asset/images/menu.png"
+
+const WatchList: React.FC = ({ currentListType, onChangeListType }) => {
+
+ const [isMenuOpen, setMenuOpen] = useState(false);
+
+
+ const favoriteStocks = [
+ { name: "삼성전자", code: "005930", price: "71,000원", change: "+6.13%", changePrice: "+4,100원", logo: Samsung_logo },
+ { name: "LG에너지솔루션", code: "373220", price: "522,000원", change: "-4.04%", changePrice: "-22,000원", logo: LG_logo },
+ { name: "SK하이닉스", code: "000660", price: "120,000원", change: "-1.48%", changePrice: "-1,800원", logo: Sk_logo },
+ { name: "삼성바이오로직스", code: "207940", price: "733,000원", change: "-0.54%", changePrice: "-4,000원", logo: Samsung_logo },
+ { name: "POSCO홀딩스", code: "005490", price: "560,000원", change: "-3.28%", changePrice: "-19,000원", logo: POSCO_logo },
+ { name: "삼성전자우", code: "005935", price: "56,900원", change: "+5.37%", changePrice: "+2,900원", logo: Samsung_logo },
+ { name: "삼성SDI", code: "006400", price: "596,000원", change: "-2.93%", changePrice: "-18,000원", logo: Samsung_logo }
+ ];
+
+ const [showChangePrice, setShowChangePrice] = useState(false);
+
+ return (
+
+
+ setMenuOpen(!isMenuOpen)}
+ />
+ {currentListType}
+ {isMenuOpen && (
+
+
+
+
+ )}
+
+
+ { /* 종목 추가 로직 */ }}>종목 추가
+
+ {favoriteStocks.map(stock => (
+
+
+
+ {stock.name}
+ {stock.code}
+
+
+ {stock.price}
+ setShowChangePrice(true)}
+ onMouseLeave={() => setShowChangePrice(false)}
+ >
+ {showChangePrice ? stock.changePrice : stock.change}
+
+
+
+ ))}
+
+ );
+};
+
+type WatchListProps = {
+ currentListType: "관심목록" | "투자목록";
+ onChangeListType: (type: "관심목록" | "투자목록") => void;
+};
+
+const getColorByChange = (change: string) => {
+ if (change.startsWith('+')) return 'red';
+ if (change.startsWith('-')) return 'blue';
+ return 'black';
+};
+
+const WatchListContainer = styled.div`
+ padding: 16px;
+`;
+
+const Header = styled.div`
+ display: flex;
+ align-items: center;
+ position: relative;
+`;
+
+const Icon = styled.img`
+ width: 24px; // 너비를 설정합니다. 원하는 크기로 조절 가능합니다.
+ height: 24px; // 높이를 설정합니다. 원하는 크기로 조절 가능합니다.
+ cursor: pointer;
+ margin-right: 10px;
+`;
+
+const HeaderText = styled.span`
+ font-size: 18px;
+`;
+
+const SlideMenu = styled.div`
+ position: absolute;
+ top: 100%;
+ left: 0;
+ width: 248px;
+ background-color: #f7f7f7;
+ border: 1px solid #e0e0e0;
+ display: flex;
+ flex-direction: column;
+`;
+
+const MenuItem = styled.button`
+ padding: 8px 16px;
+ border: none;
+ background-color: transparent;
+ cursor: pointer;
+ text-align: left;
+
+ &:hover {
+ background-color: #e0e0e0;
+ }
+`;
+
+const StockItem = styled.button`
+ display: flex;
+ justify-content: space-between;
+ align-items: flex-start;
+ padding: 8px 0;
+ border-bottom: 1px solid #e0e0e0;
+ width: 100%;
+ background-color: transparent;
+ cursor: pointer;
+ border: none;
+ text-align: left;
+`;
+
+const Logo = styled.img`
+ border-radius: 50%;
+ width: 40px;
+ height: 40px;
+ margin-right: 12px;
+`;
+
+const StockInfo = styled.div`
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ margin-right: 16px;
+`;
+
+const StockName = styled.span`
+ font-weight: bold;
+`;
+
+const StockCode = styled.span`
+ color: gray;
+`;
+
+const StockPriceSection = styled.div`
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+`;
+
+const StockPrice = styled.span.attrs<{ change: string }>(({ change }) => ({
+ style: {
+ color: getColorByChange(change),
+ },
+}))``;
+
+const StockChange = styled.span.attrs<{ change: string }>(({ change }) => ({
+ style: {
+ color: getColorByChange(change),
+ },
+ }))`
+ cursor: pointer;
+ `;
+
+const Divider = styled.div`
+ height: 1px;
+ background-color: #aaa;
+ margin: 8px 0;
+`;
+
+const AddStockButton = styled.button`
+ padding: 10px 20px;
+ border: none;
+ background-color: transparent;
+ cursor: pointer;
+ text-align: left;
+ color: black;
+
+ &:hover {
+ background-color: #e0e0e0;
+ }
+`;
+
+export default WatchList;
diff --git a/client/src/page/MainPage.tsx b/client/src/page/MainPage.tsx
index 59c868c0..b342917d 100644
--- a/client/src/page/MainPage.tsx
+++ b/client/src/page/MainPage.tsx
@@ -7,8 +7,9 @@ import EmailLoginModal from "../components/Logins/EmailLogin";
import EmailSignupModal from "../components/Signups/EmailSignup";
import EmailVerificationModal from "../components/Signups/EmailCertify";
import PasswordSettingModal from "../components/Signups/Password";
-
import CentralChartSection from "../components/CentralChartSection/Index";
+import WatchList from "../components/watchlist/WatchList";
+import Holdings from "../components/watchlist/Holdings"; // Assuming you have a Holdings component
import CompareChartSection from "../components/CompareChartSection/Index";
import StockOrderSection from "../components/StockOrderSection/Index";
@@ -71,6 +72,12 @@ const MainPage = () => {
setIsLoggedIn(true);
};
+ const [selectedMenu, setSelectedMenu] = useState<"관심목록" | "투자목록">("투자목록"); // Default menu is 관심목록
+
+ const handleMenuChange = (menu: "관심목록" | "투자목록") => {
+ setSelectedMenu(menu);
+ };
+
const handleLogout = () => {
setIsLoggedIn(false);
};
@@ -78,39 +85,57 @@ const MainPage = () => {
return (
{isLoggedIn ? (
- // 로그아웃 버튼 클릭 핸들러 추가
+
) : (
)}
-
+
+ {selectedMenu === "관심목록" ? (
+
+ ) : (
+
+ )}
+
- {isOAuthModalOpen && }
+ {isOAuthModalOpen && (
+ handleMenuChange("관심목록")}
+ onHoldingsClick={() => handleMenuChange("투자목록")}
+ />
+ )}
{isEmailLoginModalOpen && (
)}
-
{isEmailSignupModalOpen && (
)}
-
{isEmailVerificationModalOpen && (
)}
-
{isPasswordSettingModalOpen && (
{
@@ -150,4 +175,4 @@ export const RightSection = styled.section`
min-width: 400px;
height: 100%;
border: 1px solid black;
-`;
+`;
\ No newline at end of file