Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FE] 브랜치 병합 #221

Merged
merged 14 commits into from
Nov 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion FE/src/components/Login/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import useLoginModalStore from 'store/useLoginModalStore';
import Input from './Input';
import { ChatBubbleOvalLeftIcon } from '@heroicons/react/16/solid';
import { ChatBubbleOvalLeftIcon, XMarkIcon } from '@heroicons/react/16/solid';
import { FormEvent, useEffect, useState } from 'react';
import { login } from 'service/auth';
import useAuthStore from 'store/authStore';
Expand Down Expand Up @@ -95,6 +95,9 @@ export default function Login() {
<ChatBubbleOvalLeftIcon className='size-5' />
카카오 계정으로 로그인
</button>
<button className='absolute right-8 top-8' onClick={toggleModal}>
<XMarkIcon className='h-7 w-7 text-juga-grayscale-500' />
</button>
</section>
</>
);
Expand Down
8 changes: 7 additions & 1 deletion FE/src/components/Mypage/Account.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@ import { useQuery } from '@tanstack/react-query';
import AccountCondition from './AccountCondition';
import MyStocksList from './MyStocksList';
import { getAssets } from 'service/assets';
import { isWithinTimeRange } from 'utils/common';

export default function Account() {
const { data, isLoading, isError } = useQuery(
['account', 'assets'],
() => getAssets(),
{ staleTime: 1000 },
{
staleTime: 1000,
refetchInterval: isWithinTimeRange('09:00', '15:30') ? 5000 : false,
},
);

if (isLoading) return <div>loading</div>;
Expand All @@ -16,6 +20,8 @@ export default function Account() {

const { asset, stocks } = data;

console.log(asset, stocks);

return (
<div className='flex min-h-[500px] flex-col gap-3'>
<AccountCondition asset={asset} />
Expand Down
26 changes: 14 additions & 12 deletions FE/src/components/Mypage/MyInfo.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { PencilSquareIcon } from '@heroicons/react/16/solid';
import { useQuery } from '@tanstack/react-query';
import { getMyProfile } from 'service/user';

Expand All @@ -12,22 +13,23 @@ export default function MyInfo() {
if (!data) return <div>No data</div>;
if (isError) return <div>error</div>;

const { name, email } = data;
const { name } = data;

return (
<div className='flex flex-col items-center p-6 text-lg'>
<div className='w-full px-40'>
<div className='flex items-center justify-between py-2 border-b'>
<p className='font-medium text-left text-jugagrayscale-400 w-28'>
Username
<div className='flex w-[50%] items-center gap-2 py-2'>
<div className='flex items-center justify-between w-full'>
<p className='w-28 min-w-[100px] truncate font-medium text-juga-grayscale-black'>
username
</p>
<p className='font-semibold text-jugagrayscale-500'>{name}</p>
</div>
<div className='flex items-center justify-between py-2'>
<p className='font-medium text-left text-jugagrayscale-400 w-28'>
Email
</p>
<p className='font-semibold text-jugagrayscale-500'>{email}</p>
<div className='flex items-center gap-2'>
<p className='min-w-[50px] truncate font-semibold text-juga-grayscale-500'>
{name}
</p>
<button>
<PencilSquareIcon className='w-5 h-5' />
</button>
</div>
</div>
</div>
</div>
Expand Down
26 changes: 20 additions & 6 deletions FE/src/components/Mypage/MyStocksList.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { MyStockListUnit } from 'types';
import { calcYield } from 'utils/common';

type MyStocksListProps = {
stocks: MyStockListUnit[];
Expand All @@ -9,22 +10,35 @@ export default function MyStocksList({ stocks }: MyStocksListProps) {
<div className='flex flex-col flex-1 w-full p-4 mx-auto bg-white rounded-md shadow-md'>
<div className='flex pb-2 text-sm font-bold border-b'>
<p className='w-1/2 text-left truncate'>종목</p>
<p className='w-1/4 text-center'>보유 수량</p>
<p className='w-1/4 text-right'>평균 가격</p>
<p className='w-1/6 text-center'>보유 수량</p>
<p className='w-1/6 text-center'>현재가</p>
<p className='w-1/6 text-center'>평균 매수가</p>
<p className='w-1/6 text-right'>수익률</p>
</div>

<ul className='flex flex-col text-sm divide-y'>
{stocks.map((stock) => {
const { code, name, avg_price, quantity } = stock;
const { code, name, avg_price, quantity, stck_prpr } = stock;

const stockYield = calcYield(avg_price, +stck_prpr);

return (
<li className='flex py-2' key={code}>
<div className='flex w-1/2 gap-2 text-left truncate'>
<p className='font-semibold'>{name}</p>
<p className='text-gray-500'>{code}</p>
</div>
<p className='w-1/4 text-center'>{quantity}</p>
<p className='w-1/4 text-right truncate'>
{Math.floor(avg_price).toLocaleString()}원
<p className='w-1/6 text-center'>{quantity}</p>
<p className='w-1/6 text-center truncate'>
{(+stck_prpr).toLocaleString()}원
</p>
<p className='w-1/6 text-center'>
{avg_price.toLocaleString()}원
</p>
<p
className={`w-1/6 truncate text-right ${stockYield < 0 ? 'text-juga-blue-50' : 'text-juga-red-60'}`}
>
{stockYield.toFixed(2)}%
</p>
</li>
);
Expand Down
20 changes: 10 additions & 10 deletions FE/src/components/News/Card.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,33 @@
import { NewsMockDataType } from './newsMockData.ts';
import { NewsDataType } from './NewsDataType.ts';
import { formatDate } from '../../utils/formatTime.ts';

type CardWithImageProps = {
data: NewsMockDataType;
data: NewsDataType;
};
export default function Card({ data }: CardWithImageProps) {
return (
<a
className='flex cursor-pointer flex-col rounded-lg border p-4 transition-all hover:bg-juga-grayscale-50'
href={data.link}
href={data.originallink}
target='_blank'
rel='noopener noreferrer'
>
<div className={'mb-2 flex w-full flex-row items-center justify-between'}>
<div className={'flex flex-row items-center gap-3'}>
<span className='rounded-full bg-juga-blue-10 px-2 py-0.5 text-xs text-juga-blue-50'>
증권
</span>
<h3 className='w-[320px] truncate text-left text-base font-medium'>
{data.title}
</h3>
</div>
<span className={'w-fit text-sm text-gray-500'}>{data.date}</span>
<span className={'w-fit text-sm text-gray-500'}>
{formatDate(data.pubDate)}
</span>
</div>
<div className='flex w-full items-center justify-between gap-4'>
<p className='w-96 truncate text-left text-sm text-juga-grayscale-500'>
{data.img}
{data.description}
</p>
<span className='whitespace-nowrap text-sm text-juga-grayscale-500'>
{data.publisher}
<span className='rounded-full bg-juga-blue-10 px-2 py-0.5 text-xs text-juga-blue-50'>
{data.query}
</span>
</div>
</a>
Expand Down
23 changes: 19 additions & 4 deletions FE/src/components/News/News.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,32 @@
import Card from './Card.tsx';
import { newsMockData } from './newsMockData.ts';
import { useQuery } from '@tanstack/react-query';
import { getNewsData } from '../../service/getNewsData.ts';
import { NewsDataType } from './NewsDataType.ts';

export default function News() {
const { data, isLoading, isError } = useQuery({
queryKey: ['News'],
queryFn: () => getNewsData(),
staleTime: 1000 * 60,
});

if (isError) return <div>Error!!</div>;
if (isLoading) return <div>Loading...</div>;

const randomNewsIndex = Math.floor(Math.random() * 16);

return (
<div className='w-full'>
<div className='mb-4 flex items-center justify-between'>
<h2 className='text-xl font-bold'>주요 뉴스</h2>
</div>

<div className='grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-2'>
{newsMockData.slice(0, 4).map((news, index) => (
<Card key={index} data={news} />
))}
{data.news
.slice(randomNewsIndex, randomNewsIndex + 4)
.map((news: NewsDataType, index: number) => (
<Card key={index} data={news} />
))}
</div>
</div>
);
Expand Down
7 changes: 7 additions & 0 deletions FE/src/components/News/NewsDataType.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export type NewsDataType = {
title: string;
description: string;
pubDate: string;
originallink: string;
query: string;
};
4 changes: 3 additions & 1 deletion FE/src/components/Search/SearchCardHighlight.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { formatNoSpecialChar } from '../../utils/formatNoSpecialChar.ts';

type SearchCardHighLightProps = {
text: string;
highlight: string;
Expand All @@ -11,7 +13,7 @@ export const SearchCardHighLight = ({
return <div>{text}</div>;
}

const targetWord = highlight.trim();
const targetWord = formatNoSpecialChar(highlight.trim());

const parts = text.trim().split(new RegExp(`(${targetWord})`, 'gi'));
return (
Expand Down
6 changes: 3 additions & 3 deletions FE/src/components/Search/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import Lottie from 'lottie-react';
import searchAnimation from 'assets/searchAnimation.json';
import { useSearchHistory } from './searchHistoryHook.ts';
import { formatNoSpecialChar } from '../../utils/formatNoSpecialChar.ts';

export default function SearchModal() {
const { isOpen, toggleSearchModal } = useSearchModalStore();
Expand All @@ -23,20 +24,19 @@
shouldSearch ? searchInput : '',
500,
);

const { data, isLoading, isFetching } = useQuery({
queryKey: ['search', debounceValue],
queryFn: () => getSearchResults(debounceValue),
queryFn: () => getSearchResults(formatNoSpecialChar(debounceValue)),
enabled: !!debounceValue && !isDebouncing,
staleTime: 1000,
cacheTime: 1000 * 60,
});

useEffect(() => {
if (data && data.length > 0 && debounceValue && !isLoading) {
addSearchHistory(debounceValue);
addSearchHistory(formatNoSpecialChar(debounceValue));
}
}, [data, debounceValue]);

Check warning on line 39 in FE/src/components/Search/index.tsx

View workflow job for this annotation

GitHub Actions / FE-test-and-build

React Hook useEffect has missing dependencies: 'addSearchHistory' and 'isLoading'. Either include them or remove the dependency array

Check warning on line 39 in FE/src/components/Search/index.tsx

View workflow job for this annotation

GitHub Actions / build-and-deploy (fe, FE, 5173, juga-docker-fe, ENV_FE)

React Hook useEffect has missing dependencies: 'addSearchHistory' and 'isLoading'. Either include them or remove the dependency array

if (!isOpen) return null;

Expand Down
7 changes: 2 additions & 5 deletions FE/src/components/StocksDetail/Chart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
import { drawUpperYAxis } from 'utils/chart/drawUpperYAxis.ts';
import { drawLowerYAxis } from 'utils/chart/drawLowerYAxis.ts';
import { drawChartGrid } from 'utils/chart/drawChartGrid.ts';
import { drawMouseGrid } from '../../utils/chart/drawMouseGrid.ts';
import { drawMouseGrid } from 'utils/chart/drawMouseGrid.ts';

const categories: { label: string; value: TiemCategory }[] = [
{ label: '일', value: 'D' },
Expand Down Expand Up @@ -46,7 +46,6 @@
const upperChartY = useRef<HTMLCanvasElement>(null);
const lowerChartY = useRef<HTMLCanvasElement>(null);
const chartX = useRef<HTMLCanvasElement>(null);
// RAF 관리를 위한 ref
const rafRef = useRef<number>();
const [timeCategory, setTimeCategory] = useState<TiemCategory>('D');
const [charSizeConfig, setChartSizeConfig] = useState<ChartSizeConfigType>({
Expand All @@ -64,14 +63,12 @@
y: 0,
});


const { data, isLoading } = useQuery(
['stocksChartData', code, timeCategory],
() => getStocksChartDataByCode(code, timeCategory),
{ staleTime: 1000 },
);


const handleMouseDown = useCallback((e: MouseEvent) => {
e.preventDefault();
setIsDragging(true);
Expand Down Expand Up @@ -285,7 +282,7 @@
);
}
},
[

Check warning on line 285 in FE/src/components/StocksDetail/Chart.tsx

View workflow job for this annotation

GitHub Actions / FE-test-and-build

React Hook useCallback has unnecessary dependencies: 'drawBarChart', 'drawCandleChart', 'drawChartGrid', 'drawLineChart', 'drawLowerYAxis', 'drawUpperYAxis', 'drawXAxis', and 'padding'. Either exclude them or remove the dependency array. Outer scope values like 'padding' aren't valid dependencies because mutating them doesn't re-render the component

Check warning on line 285 in FE/src/components/StocksDetail/Chart.tsx

View workflow job for this annotation

GitHub Actions / build-and-deploy (fe, FE, 5173, juga-docker-fe, ENV_FE)

React Hook useCallback has unnecessary dependencies: 'drawBarChart', 'drawCandleChart', 'drawChartGrid', 'drawLineChart', 'drawLowerYAxis', 'drawUpperYAxis', 'drawXAxis', and 'padding'. Either exclude them or remove the dependency array. Outer scope values like 'padding' aren't valid dependencies because mutating them doesn't re-render the component
padding,
upperLabelNum,
lowerLabelNum,
Expand Down Expand Up @@ -361,7 +358,7 @@

return (
<div className='box-border flex h-[260px] flex-col items-center rounded-lg bg-white p-3'>
<div className='flex items-center justify-between w-full h-fit'>
<div className='flex h-fit w-full items-center justify-between'>
<p className='font-semibold'>차트</p>
<nav className='flex gap-4 text-sm'>
{categories.map(({ label, value }) => (
Expand Down
5 changes: 2 additions & 3 deletions FE/src/components/StocksDetail/PriceSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,21 +31,20 @@
},
);
},
[id, buttonFlag],

Check warning on line 34 in FE/src/components/StocksDetail/PriceSection.tsx

View workflow job for this annotation

GitHub Actions / FE-test-and-build

React Hook useCallback has a missing dependency: 'queryClient'. Either include it or remove the dependency array

Check warning on line 34 in FE/src/components/StocksDetail/PriceSection.tsx

View workflow job for this annotation

GitHub Actions / build-and-deploy (fe, FE, 5173, juga-docker-fe, ENV_FE)

React Hook useCallback has a missing dependency: 'queryClient'. Either include it or remove the dependency array
);

useEffect(() => {
// 이벤트 리스너 등록
if (!buttonFlag) return;
const handleTradeHistory = (chartData: PriceDataType) => {
addData(chartData);
};

socket.on(`trade-history/${id}`, handleTradeHistory);

return () => {
socket.off(`trade-history/${id}`, handleTradeHistory);
};
}, [id, addData]);
}, [id, addData, buttonFlag]);

useEffect(() => {
const tmpIndex = buttonFlag ? 0 : 1;
Expand Down
4 changes: 2 additions & 2 deletions FE/src/components/StocksDetail/PriceTableColumn.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ export default function PriceTableColumn({ viewMode }: Props) {
return (
<thead className={'z-1 sticky top-0 bg-white'}>
<tr className={'h-10 border-b text-gray-500'}>
<th className={'px-4 py-1 text-left font-medium'}>채결가</th>
<th className={'px-4 py-1 text-right font-medium'}>채결량(주)</th>
<th className={'px-4 py-1 text-left font-medium'}>체결가</th>
<th className={'px-4 py-1 text-right font-medium'}>체결량(주)</th>
<th className={'px-4 py-1 text-right font-medium'}>등락률</th>
{/*<th className={'px-4 py-1 text-right font-medium'}>거래량(주)</th>*/}
<th className={'px-4 py-1 text-right font-medium'}>시간</th>
Expand Down
8 changes: 4 additions & 4 deletions FE/src/components/StocksDetail/TradeSection/SellSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { ChangeEvent, FocusEvent, FormEvent, useRef, useState } from 'react';
import { StockDetailType } from 'types';
import useAuthStore from 'store/authStore';
import useTradeAlertModalStore from 'store/tradeAlertModalStore';
import { isNumericString } from 'utils/common';
import { calcYield, isNumericString } from 'utils/common';
import TradeAlertModal from './TradeAlertModal';

type SellSectionProps = {
Expand Down Expand Up @@ -43,7 +43,7 @@ export default function SellSection({ code, detailInfo }: SellSectionProps) {

const pl = (+currPrice - avg_price) * count;
const totalPrice = +currPrice * count;
const plRate = ((pl / totalPrice) * 100).toFixed(2);
const plRate = calcYield(avg_price, +currPrice);

const handlePriceChange = (e: ChangeEvent<HTMLInputElement>) => {
const s = e.target.value.replace(/,/g, '');
Expand Down Expand Up @@ -170,9 +170,9 @@ export default function SellSection({ code, detailInfo }: SellSectionProps) {
<div className='flex justify-between'>
<p>예상 수익률</p>
<p
className={`${+plRate < 0 ? 'text-juga-blue-50' : 'text-juga-red-60'}`}
className={`${plRate < 0 ? 'text-juga-blue-50' : 'text-juga-red-60'}`}
>
{plRate}%
{plRate.toFixed(2)}%
</p>
</div>
</div>
Expand Down
7 changes: 7 additions & 0 deletions FE/src/service/getNewsData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const getNewsData = async () => {
const response = await fetch(`${import.meta.env.VITE_API_URL}/news`);
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
};
6 changes: 6 additions & 0 deletions FE/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ export type StockChartUnit = {
stck_lwpr: string;
acml_vol: string;
prdy_vrss_sign: string;
mov_avg_5: string;
mov_avg_20: string;
};

export type MypageSectionType = 'account' | 'order' | 'info';
Expand All @@ -55,6 +57,10 @@ export type MyStockListUnit = {
avg_price: number;
code: string;
name: string;
stck_prpr: string;
prdy_vrss: string;
prdy_vrss_sign: string;
prdy_ctrt: string;
quantity: number;
};

Expand Down
Loading
Loading