Skip to content

Commit

Permalink
[Feat]보유종목 구현
Browse files Browse the repository at this point in the history
주식 정보 api와 보유주식 정보 api를 hook으로 호출
보유종목에서 위는 주식정보, 아래는 보유주식 정보를 출력
기업로고 대신 스톡홀름 로고로 대체
Issues #19
  • Loading branch information
김병현 authored and 김병현 committed Sep 15, 2023
1 parent 424b30b commit 50f1a2d
Show file tree
Hide file tree
Showing 6 changed files with 205 additions and 95 deletions.
Binary file added client/src/asset/images/StockHolmImage.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion client/src/components/EntireList/StockItem.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import styled from 'styled-components';
import logo from '../../asset/logos/SK_logo.png';
import logo from '../../asset/images/StockHolmImage.png';

const StockItem: React.FC<StockItemProps> = ({ company, setShowChangePrice, showChangePrice }) => {
const isPositiveChange = parseFloat(company.stockChangeRate) > 0;
Expand Down
84 changes: 41 additions & 43 deletions client/src/components/HoldingList/HoldingList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,16 @@ import React, { useState } from 'react';
import styled from 'styled-components';
import Header from './Header';
import StockItem from './StockItem';
import useGetStockHolds from '../../hooks/useGetStockholds';
import { StockItemProps } from './StockItem';
import useCompanyData from '../../hooks/useCompanyData';

const WatchList: React.FC<WatchListProps> = ({ currentListType, onChangeListType }) => {
const HoldingList: React.FC<WatchListProps> = ({ currentListType, onChangeListType }) => {
const [isMenuOpen, setMenuOpen] = useState(false);
const [showChangePrice, setShowChangePrice] = useState(false);

// useCompanyData 훅 사용하여 데이터 가져오기
const { data: companies, isLoading, isError } = useCompanyData(1, 14);

// 'companies'가 'undefined'인 경우를 처리하기 위해 빈 배열로 초기화
const companiesList = companies || [];
const { stockHolds, stockHoldsLoading: isLoading, stockHoldsError: isError } = useGetStockHolds();
const { data: companyData, isLoading: isCompanyDataLoading, isError: isCompanyDataError } = useCompanyData(1, 14);

return (
<WatchListContainer>
Expand All @@ -23,74 +22,73 @@ const WatchList: React.FC<WatchListProps> = ({ currentListType, onChangeListType
setMenuOpen={setMenuOpen}
/>
<Divider1 />
<EvaluationProfit>평가 수익금: +5,000,000원</EvaluationProfit> {/* 임의의 평가 수익금 */}
<EvaluationProfit>평가 수익금: +5,000,000원</EvaluationProfit>
<Divider2 />
<StockList>
{isLoading ? (
{isLoading || isCompanyDataLoading ? (
<div>Loading...</div>
) : isError ? (
) : isError || isCompanyDataError ? (
<div>Error fetching data</div>
) : (
companiesList.map((company) => (
<StockItem
key={company.companyId}
company={company}
setShowChangePrice={setShowChangePrice}
showChangePrice={showChangePrice}
/>
stockHolds.map((stockHold: StockItemProps['stockData']) => (
companyData ? (
<StockItem
key={stockHold.companyId}
stockData={stockHold}
companyData={companyData}
setShowChangePrice={setShowChangePrice}
showChangePrice={showChangePrice}
/>
) : null
))
)}
</StockList>
</WatchListContainer>
);
};

// Props와 상태에 대한 타입 정의
export default HoldingList;

type WatchListProps = {
currentListType: '전체종목' | '관심종목' | '보유종목';
onChangeListType: (type: '전체종목' | '관심종목' | '보유종목') => void;
};

// WatchList 컴포넌트에 대한 스타일드 컴포넌트 정의
const WatchListContainer = styled.div`
display: flex;
flex-direction: column;
align-items: flex-start;
`;

const Divider1 = styled.div`
margin:0px;
padding:0px;
width: 100%;
height: 10px;
display: flex;
flex-direction: row;
border-bottom: 1px solid #2f4f4f;
margin: 0px;
padding: 0px;
width: 100%;
height: 10px;
display: flex;
flex-direction: row;
border-bottom: 1px solid #2f4f4f;
`;

const Divider2 = styled.div`
margin:0px;
padding:0px;
width: 100%;
height: 4.5px;
display: flex;
flex-direction: row;
border-bottom: 1px solid #2f4f4f;
margin: 0px;
padding: 0px;
width: 100%;
height: 4.5px;
display: flex;
flex-direction: row;
border-bottom: 1px solid #2f4f4f;
`;



const EvaluationProfit = styled.div`
font-size: 16px;
font-weight: bold;
margin: 8px 0;
text-align: center;
color: red; // 수익금이 플러스일 경우 초록색으로 표시
font-size: 16px;
font-weight: bold;
margin: 8px 0;
text-align: center;
color: red;
`;
const StockList = styled.div`
width: 90%;
max-height: 800px; /* 스크롤이 발생할 최대 높이를 지정하세요 */
overflow-y: auto; /* 세로 스크롤을 활성화합니다 */
max-height: 800px;
overflow-y: auto;
`;

export default WatchList;
189 changes: 139 additions & 50 deletions client/src/components/HoldingList/StockItem.tsx
Original file line number Diff line number Diff line change
@@ -1,57 +1,95 @@
import React from 'react';
import styled from 'styled-components';
import logo from '../../asset/logos/SK_logo.png';
import logo from '../../asset/images/StockHolmImage.png';

const StockItem: React.FC<StockItemProps> = ({ company, setShowChangePrice, showChangePrice }) => {
const isPositiveChange = parseFloat(company.stockChangeRate) > 0;
const priceColor = isPositiveChange ? 'red' : 'blue';

return (
<StockItemWrapper>
<Logo src={logo} alt="stock logo" />
<StockInfo>
<StockName>{company.korName}</StockName>
<StockCode>{company.code}</StockCode>
</StockInfo>
<StockPriceSection>
<StockPrice change={priceColor}>{company.stockPrice}</StockPrice>
<StockChange
change={priceColor}
onMouseEnter={() => setShowChangePrice(true)}
onMouseLeave={() => setShowChangePrice(false)}
>
{showChangePrice ? `${company.stockChangeAmount}%` : `${company.stockChangeRate}%`}
</StockChange>
</StockPriceSection>
</StockItemWrapper>
);
export type StockItemProps = {
stockData: {
stockHoldId: number;
memberId: number;
companyId: number;
companyKorName: string;
stockCount: number;
totalPrice: number;
percentage: number;
stockReturn: number;
reserveSellStockCount: number;
};
companyData?: {
companyId: number;
code: string;
korName: string;
stockPrice: string;
stockChangeAmount: string;
stockChangeRate: string;
}[];
setShowChangePrice: (value: boolean) => void;
showChangePrice: boolean;
};

type NewCompanyData = {
companyId: number;
code: string;
korName: string;
stockPrice: string;
stockChangeAmount: string;
stockChangeRate: string;
};

type StockItemProps = {
company: NewCompanyData;
setShowChangePrice: React.Dispatch<React.SetStateAction<boolean>>;
showChangePrice: boolean;

const StockItem: React.FC<StockItemProps> = ({ companyData, stockData, setShowChangePrice, showChangePrice }) => {
const { stockCount, reserveSellStockCount, totalPrice, percentage, stockReturn } = stockData;
const totalStocksHeld = stockCount + reserveSellStockCount;
const company = companyData ? companyData[0] : undefined;

const {
code = '',
korName = '',
stockPrice='',
stockChangeAmount = '',
stockChangeRate = ''
} = company || {};

// Format percentage to two decimal places
const formattedPercentage = parseFloat(percentage.toFixed(2));


return (
<>
<ItemContainer>
<Logo src={logo} alt="stock logo" />
<StockInfo>
<StockName>{korName}</StockName>
<StockCode>{code}</StockCode>
</StockInfo>
<StockPriceSection>
<StockPrice change={`${stockChangeRate}%`}>{stockPrice.toLocaleString()}</StockPrice>
<StockChange
change={`${stockChangeRate}%`}
onMouseEnter={() => setShowChangePrice(true)}
onMouseLeave={() => setShowChangePrice(false)}
>
{showChangePrice ? stockChangeAmount.toLocaleString() : `${stockChangeRate}%`}
</StockChange>
</StockPriceSection>
</ItemContainer>
<StockDetails>
<DetailSection>
<DetailTitle>수익</DetailTitle>
<DetailTitle>보유</DetailTitle>
</DetailSection>
<DetailSection>
<ColoredDetailData value={stockReturn.toString()}>{stockReturn.toLocaleString()}</ColoredDetailData>
<DetailData>{totalPrice.toLocaleString()}</DetailData>
</DetailSection>
<DetailSection>
<ColoredDetailData value={`${formattedPercentage}%`}>{formattedPercentage}%</ColoredDetailData>
<DetailTitle>{totalStocksHeld}</DetailTitle>
</DetailSection>
</StockDetails>
<ThickDivider />
</>
);
};

const StockItemWrapper = styled.div`
const ItemContainer = styled.div`
display: flex;
flex-direction: row; /* 수평으로 정렬 */
justify-content: flex-start; /* 왼쪽 정렬 */
align-items: flex-start; /* 위로 정렬 */
padding: 8px 0;
border-bottom: 1px solid #e0e0e0;
justify-content: space-between;
align-items: center;
width: 100%;
background-color: transparent;
cursor: pointer;
padding: 8px 0;
border-bottom: 1px solid #e0e0e0; // Holdings에서의 스타일 추가
`;

const Logo = styled.img`
Expand Down Expand Up @@ -80,16 +118,67 @@ const StockPriceSection = styled.div`
display: flex;
flex-direction: column;
align-items: flex-start;
margin-left: auto; /* 자동으로 왼쪽 여백 추가 */
`;

const StockPrice = styled.span<{ change: string }>`
color: ${(props) => props.change};
`;
const getColorByChange = (change: string) => {
if (change.startsWith('')) return 'red';
if (change.startsWith('-')) return 'blue';
return 'black';
};

const StockPrice = styled.span.attrs<{ change: string }>(({ change }) => ({
style: {
color: getColorByChange(change),
},
}))``;

const StockChange = styled.span<{ change: string }>`
color: ${(props) => props.change};
const StockChange = styled.span.attrs<{ change: string }>(({ change }) => ({
style: {
color: getColorByChange(change),
},
}))`
cursor: pointer;
`;

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: 3px;
background-color: #aaa;
margin: 8px 0;
`;
export default StockItem;
2 changes: 1 addition & 1 deletion client/src/components/watchlist/StockItem.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import styled from 'styled-components';
import logo from '../../asset/logos/SK_logo.png';
import logo from '../../asset/images/StockHolmImage.png';

const StockItem: React.FC<StockItemProps> = ({ company, setShowChangePrice, showChangePrice }) => {
const isPositiveChange = parseFloat(company.stockChangeRate) > 0;
Expand Down
23 changes: 23 additions & 0 deletions client/src/hooks/useGetStockholds.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { useQuery } from "react-query";
import axios from "axios";

const useGetStockHolds = () => {
const { data, isLoading, error } = useQuery("stockHolds", getStockHolds, {});

return { stockHolds: data, stockHoldsLoading: isLoading, stockHoldsError: error };
};

export default useGetStockHolds;

// 서버에서 StockHolds 목록 fetch 하는 함수
const getStockHolds = async () => {
const token = localStorage.getItem('Authorization'); // 로컬 스토리지에서 토큰 가져오기
const res = await axios.get("http://ec2-13-125-246-160.ap-northeast-2.compute.amazonaws.com:8080/stock/stockholds", {
headers: {
'Authorization': token
}
});
const stockHoldsList = await res.data;

return stockHoldsList;
};

0 comments on commit 50f1a2d

Please sign in to comment.