Skip to content

Commit

Permalink
Feature/#40 - 주식 가격 상승/하락순, 조회수순 api 호출 (#205)
Browse files Browse the repository at this point in the history
  • Loading branch information
baegyeong authored Nov 21, 2024
2 parents 42ad46b + b5c838b commit 593469e
Show file tree
Hide file tree
Showing 9 changed files with 100 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ import axios from 'axios';
export const instance = axios.create({
baseURL: import.meta.env.VITE_BASE_URL,
timeout: 1000,
withCredentials: true,
});
3 changes: 3 additions & 0 deletions packages/frontend/src/apis/queries/stocks/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './types';
export * from './useGetTopViews';
export * from './useGetStocksByPrice';
12 changes: 12 additions & 0 deletions packages/frontend/src/apis/queries/stocks/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export interface GetStockListRequest {
limit: number;
}

export interface GetStockListResponse {
id: string;
name: string;
currentPrice: number;
changeRate: number;
volume: number;
marketCap: string;
}
29 changes: 29 additions & 0 deletions packages/frontend/src/apis/queries/stocks/useGetStocksByPrice.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import type { GetStockListRequest, GetStockListResponse } from './types';
import { useQuery } from '@tanstack/react-query';
import { instance } from '@/apis/config';

const getTopGainers = async ({
limit,
}: GetStockListRequest): Promise<GetStockListResponse[]> => {
const { data } = await instance.get(`/api/stock/topGainers?limit=${limit}`);
return data;
};

const getTopLosers = async ({
limit,
}: GetStockListRequest): Promise<GetStockListResponse[]> => {
const { data } = await instance.get(`/api/stock/topLosers?limit=${limit}`);
return data;
};

export const useGetStocksByPrice = ({
limit,
isGaining,
}: GetStockListRequest & { isGaining: boolean }) => {
return useQuery<GetStockListResponse[], Error>({
queryKey: ['stocks', isGaining],
queryFn: isGaining
? () => getTopGainers({ limit })
: () => getTopLosers({ limit }),
});
};
17 changes: 17 additions & 0 deletions packages/frontend/src/apis/queries/stocks/useGetTopViews.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import type { GetStockListRequest, GetStockListResponse } from './types';
import { useQuery } from '@tanstack/react-query';
import { instance } from '@/apis/config';

const getTopViews = async ({
limit,
}: GetStockListRequest): Promise<GetStockListResponse[]> => {
const { data } = await instance.get(`/api/stock/topViews?limit=${limit}`);
return data;
};

export const useGetTopViews = ({ limit }: GetStockListRequest) => {
return useQuery<GetStockListResponse[], Error>({
queryKey: ['stocks', 'topViews'],
queryFn: () => getTopViews({ limit }),
});
};
46 changes: 30 additions & 16 deletions packages/frontend/src/pages/stocks/StockRankingTable.tsx
Original file line number Diff line number Diff line change
@@ -1,56 +1,70 @@
import { useState } from 'react';
import { Link } from 'react-router-dom';
import { useGetStocksByPrice } from '@/apis/queries/stocks';
import DownArrow from '@/assets/down-arrow.svg?react';
import stockData from '@/mocks/stock.json';
import { cn } from '@/utils/cn';

export const StockRankingTable = () => {
const LIMIT = 20;
const [isGaining, setIsGaining] = useState(true);

const { data } = useGetStocksByPrice({ limit: LIMIT, isGaining });

return (
<div className="rounded-md bg-white px-6 shadow">
<table className="w-full border-collapse">
<colgroup>
<col className="w-1/4" />
<col className="w-1/5" />
<col className="w-1/5" />
<col className="w-1/5" />
<col className="w-1/5" />
<col className="w-4/12" />
<col className="w-2/12" />
<col className="w-2/12" />
<col className="w-2/12" />
<col className="w-2/12" />
</colgroup>
<thead>
<tr className="display-medium12 text-dark-gray border-light-gray border-b text-left [&>*]:p-4 [&>*]:py-3">
<th>종목</th>
<th className="text-right">현재가</th>
<th className="flex items-center justify-end gap-1 text-right">
<p> 등락률</p>
<DownArrow className="cursor-pointer" />
<p>등락률({isGaining ? '상승순' : '하락순'})</p>
<DownArrow
className={cn(
'cursor-pointer',
isGaining ? 'rotate-0' : 'rotate-180',
)}
onClick={() => setIsGaining((prev) => !prev)}
/>
</th>
<th className="text-right">거래대금</th>
<th className="text-right">거래량</th>
</tr>
</thead>
<tbody>
{stockData.data.map((stock, index) => (
{data?.map((stock, index) => (
<tr
key={stock.id}
className="display-medium14 text-dark-gray text-right [&>*]:p-4"
>
<td className="flex gap-6 text-left">
<span className="text-gray">{index + 1}</span>
<span className="text-gray w-3 flex-shrink-0">{index + 1}</span>
<Link
to={`${stock.id}`}
className="display-bold14 hover:text-orange cursor-pointer text-ellipsis hover:underline"
aria-label={stock.name}
>
{stock.name}
</Link>
</td>
<td>{stock.currentPrice.toLocaleString()}</td>
<td>{stock.currentPrice?.toLocaleString()}</td>
<td
className={cn(stock.changeRate >= 0 ? 'text-red' : 'text-blue')}
className={cn(
+stock.changeRate >= 0 ? 'text-red' : 'text-blue',
)}
>
{stock.changeRate >= 0 && '+'}
{stock.changeRate.toLocaleString()}원 ({stock.changeRatePercent}
%)
{stock.changeRate}%
</td>
<td>{stock.tradingVolume.toLocaleString()}</td>
<td>{stock.tradingValue.toLocaleString()}</td>
<td>{stock.volume?.toLocaleString()}</td>
<td>{stock.marketCap?.toLocaleString()}</td>
</tr>
))}
</tbody>
Expand Down
9 changes: 5 additions & 4 deletions packages/frontend/src/pages/stocks/Stocks.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { StockIndexCard } from './components/StockIndexCard';
import { StockInfoCard } from './components/StockInfoCard';
import { StockRankingTable } from './StockRankingTable';
import { useGetTopViews } from '@/apis/queries/stocks';
import marketData from '@/mocks/market.json';
import stockData from '@/mocks/stock.json';

const TOP_VIEW = 5;
const LIMIT = 5;

export const Stocks = () => {
const kospi = marketData.data.filter((value) => value.name === '코스피')[0];
Expand All @@ -13,6 +13,8 @@ export const Stocks = () => {
(value) => value.name === '달러환율',
)[0];

const { data: topViews } = useGetTopViews({ limit: LIMIT });

return (
<main className="flex flex-col gap-16">
<h1 className="display-bold24">오늘의 투자</h1>
Expand Down Expand Up @@ -49,14 +51,13 @@ export const Stocks = () => {
이 종목은 어떠신가요?
</h2>
<div className="grid w-fit grid-cols-5 gap-5">
{stockData.data.slice(0, TOP_VIEW).map((stock, index) => (
{topViews?.map((stock, index) => (
<StockInfoCard
key={stock.id}
index={index}
name={stock.name}
currentPrice={stock.currentPrice}
changeRate={stock.changeRate}
changeRatePercent={stock.changeRatePercent}
/>
))}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,13 @@ interface StockInfoCardProps {
name: string;
currentPrice: number;
changeRate: number;
changeRatePercent: number;
index: number;
}

export const StockInfoCard = ({
name,
currentPrice,
changeRate,
changeRatePercent,
index,
}: StockInfoCardProps) => {
return (
Expand All @@ -32,14 +30,13 @@ export const StockInfoCard = ({
)}
>
{changeRate >= 0 && '+'}
{changeRate.toLocaleString()}원 ({changeRatePercent}
%)
{changeRate}%
</span>
</div>
<div className="flex items-center gap-5">
<span className="display-bold12 text-dark-gray">현재가</span>
<span className="display-medium12 text-dark-gray">
{currentPrice.toLocaleString()}
{currentPrice?.toLocaleString()}
</span>
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion packages/frontend/src/styles/theme/darkTheme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { ChartTheme } from '.';
import tailwindConfig from '@/../tailwind.config';

const colorConfig = resolveConfig(tailwindConfig).theme.colors;
console.log(colorConfig.red);

export const darkTheme: ChartTheme = {
background: '#1a1a1a',
textColor: '#ffffffe6',
Expand Down

0 comments on commit 593469e

Please sign in to comment.