Skip to content
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
8 changes: 4 additions & 4 deletions app/(dashboard)/_ui/strategies-item/area-chart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ const AreaChart = ({ profitRateChartData: data }: Props) => {
},
yAxis: {
visible: false,
min: Math.min(...data.yAxis),
max: Math.max(...data.yAxis),
min: Math.min(...data.profitRates),
max: Math.max(...data.profitRates),
},
legend: { enabled: false },
plotOptions: {
Expand All @@ -56,9 +56,9 @@ const AreaChart = ({ profitRateChartData: data }: Props) => {
series: [
{
type: 'areaspline',
data: data.xAxis.map((x, idx) => ({
data: data.dates.map((x, idx) => ({
x: new Date(x).getTime(),
y: data.yAxis[idx],
y: data.profitRates[idx],
})),
color: {
linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 },
Expand Down
12 changes: 6 additions & 6 deletions app/(dashboard)/_ui/strategies-item/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,16 @@ const StrategiesItem = ({ strategiesData: data }: Props) => {
return (
<Link className={cx('container')} href={`/strategies/${data.strategyId}`}>
<StrategiesSummary
iconUrls={[data.tradeTypeIconUrl, ...data.stockTypeIconUrls]}
iconNames={[data.tradeTypeName, ...data.stockTypeNames]}
iconUrls={[data.tradeTypeIconUrl, ...data.stockTypeInfo.stockTypeIconUrls]}
iconNames={[data.tradeTypeName, ...data.stockTypeInfo.stockTypeNames]}
title={data.strategyName}
profile={{
traderImage: data.traderImage,
traderImage: data.traderImgUrl,
nickname: data.nickname,
}}
subscriptionCount={data.subscriptionCnt}
subscriptionCount={data.subscriptionCount}
averageRating={data.averageRating}
totalReview={data.totalReview}
totalReview={data.totalReviews}
/>
<AreaChart profitRateChartData={data.profitRateChartData} />
<div className={cx('mdd')}>
Expand All @@ -39,7 +39,7 @@ const StrategiesItem = ({ strategiesData: data }: Props) => {
</div>
<div className={cx('profit')}>
<span>누적 수익률</span>
<p>{data.cumulativeProfitLossRate}%</p>
<p>{data.cumulativeProfitRate}%</p>
<span>최근 1년 수익률</span>
<p>{data.recentYearProfitLossRate ? data.recentYearProfitLossRate + '%' : '-'}</p>
</div>
Expand Down
70 changes: 38 additions & 32 deletions app/(dashboard)/_ui/strategies-item/strategies-item.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,16 @@ export const Primary = strategy.bind({})
Primary.args = {
strategiesData: [
{
strategyId: '123',
strategyName: '리치테크 FuturesDay',
nickname: 'MACS',
stockTypeIconUrls: [],
stockTypeNames: [],
strategyId: 1,
strategyName: 'Dynamic ETF 전략',
traderImgUrl: '/images/trader1.png',
nickname: 'AlphaTrader',
stockTypeInfo: {
stockTypeIconUrls: ['/images/stock.png'],
stockTypeNames: ['해외지수선물'],
},
profitRateChartData: {
xAxis: [
dates: [
'2023-01-01',
'2023-01-02',
'2023-01-03',
Expand All @@ -39,27 +42,30 @@ Primary.args = {
'2023-01-08',
'2023-01-09',
],
yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18],
profitRates: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18],
},
tradeTypeIconUrl: '',
tradeTypeName: '',
mdd: '-20,580,856',
smScore: 60.6,
cumulativeProfitLossRate: 120.1,
recentYearProfitLossRate: 30.1,
subscriptionCnt: 23,
tradeTypeIconUrl: '/images/trade.png',
tradeTypeName: '자동',
mdd: -15432567,
smScore: 72.1,
cumulativeProfitRate: 140.5,
recentYearProfitLossRate: 35.2,
subscriptionCount: 45,
averageRating: 4.9,
totalReviews: 34,
isSubscribed: true,
averageRating: 4.8,
totalReview: 12,
},
{
strategyId: '12345',
strategyName: 'ETF 레버리지/인버',
nickname: '수밍',
stockTypeIconUrls: [],
stockTypeNames: [],
strategyId: 2,
strategyName: '고수익 ETF',
traderImgUrl: '/images/trader2.png',
nickname: 'BetaTrader',
stockTypeInfo: {
stockTypeIconUrls: ['/images/stock.png'],
stockTypeNames: ['해외지수선물'],
},
profitRateChartData: {
xAxis: [
dates: [
'2023-01-01',
'2023-01-02',
'2023-01-03',
Expand All @@ -70,18 +76,18 @@ Primary.args = {
'2023-01-08',
'2023-01-09',
],
yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18],
profitRates: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18],
},
tradeTypeIconUrl: '',
tradeTypeName: '',
mdd: '-20,580,856',
smScore: 60.6,
cumulativeProfitLossRate: 120.1,
recentYearProfitLossRate: 30.1,
subscriptionCnt: 23,
tradeTypeIconUrl: '/images/trade.png',
tradeTypeName: '자동',
mdd: -12786543,
smScore: 65.4,
cumulativeProfitRate: 125.3,
recentYearProfitLossRate: 28.4,
subscriptionCount: 67,
averageRating: 4.6,
totalReviews: 19,
isSubscribed: false,
averageRating: 4.8,
totalReview: 12,
},
],
}
Expand Down
34 changes: 34 additions & 0 deletions app/(dashboard)/my/_api/get-favorite-strategy-list.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { strategiesMockData } from '@/mocks/handlers/strategies'

import axiosInstance from '@/shared/api/axios'
import { StrategiesModel } from '@/shared/types/strategy-details-data'

interface Props {
page: number
size: number
}

const getFavoriteStrategyList = async ({
page = 1,
size = 6,
}: Props): Promise<{ strategiesData: StrategiesModel[]; totalPages: number } | undefined> => {
try {
const response = await axiosInstance.get(
`/api/my-strategies/subscribed?page=${page}&size=${size}`
)

const {
content: strategiesData,
totalPages,
}: { content: StrategiesModel[]; totalPages: number } = await response.data.result

return { strategiesData, totalPages }
} catch (err) {
console.error(err)
// throw new Error('구독한 전략 조회에 실패했습니다.')
// 임시 목데이터
return { strategiesData: strategiesMockData.filter((data) => data.isSubscribed), totalPages: 1 }
}
}

export default getFavoriteStrategyList
17 changes: 17 additions & 0 deletions app/(dashboard)/my/_hooks/query/use-get-favorite-strategy-list.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { useQuery } from '@tanstack/react-query'

import getFavoriteStrategyList from '../../_api/get-favorite-strategy-list'

interface Props {
page: number
size: number
}

const useGetFavoriteStrategyList = ({ page, size }: Props) => {
return useQuery({
queryKey: ['favoriteStrategies', page, size],
queryFn: () => getFavoriteStrategyList({ page, size }),
})
}

export default useGetFavoriteStrategyList
44 changes: 44 additions & 0 deletions app/(dashboard)/my/favorites/_ui/favorite-strategy-list/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
'use client'

import ListHeader from '@/app/(dashboard)/_ui/list-header'
import StrategiesItem from '@/app/(dashboard)/_ui/strategies-item'
import classNames from 'classnames/bind'

import { PATH } from '@/shared/constants/path'
import { usePagination } from '@/shared/hooks/custom/use-pagination'
import Pagination from '@/shared/ui/pagination'

import useGetFavoriteStrategyList from '../../../_hooks/query/use-get-favorite-strategy-list'
import styles from './styles.module.scss'

const cx = classNames.bind(styles)

const COUNT_PER_PAGE = 6

const FavoriteStrategyList = () => {
const { page, handlePageChange } = usePagination({
basePath: PATH.FAVORITES,
pageSize: COUNT_PER_PAGE,
})

const { data } = useGetFavoriteStrategyList({ page, size: COUNT_PER_PAGE })

const strategiesData = data?.strategiesData || []
const totalPages = data?.totalPages || null

return (
<>
<ListHeader />
<div className={cx('pagination')}>
{strategiesData?.map((strategy) => (
<StrategiesItem key={strategy.strategyId} strategiesData={strategy} />
))}
{totalPages && (
<Pagination currentPage={page} maxPage={totalPages} onPageChange={handlePageChange} />
)}
</div>
</>
)
}

export default FavoriteStrategyList
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.pagination {
margin-bottom: 24px;
}
5 changes: 5 additions & 0 deletions app/(dashboard)/my/favorites/page.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.container {
h1 {
margin: 80px 0 24px;
}
}
20 changes: 19 additions & 1 deletion app/(dashboard)/my/favorites/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
import { Suspense } from 'react'

import classNames from 'classnames/bind'

import Title from '@/shared/ui/title'

import FavoriteStrategyList from './_ui/favorite-strategy-list'
import styles from './page.module.scss'

const cx = classNames.bind(styles)

const MyFavoritesPage = () => {
return <></>
return (
<div className={cx('container')}>
<Title label="구독한 전략" />
<Suspense fallback={<div>Loading...</div>}>
<FavoriteStrategyList />
</Suspense>
</div>
)
}

export default MyFavoritesPage
16 changes: 9 additions & 7 deletions app/(dashboard)/strategies/_api/get-strategies.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { strategiesMockData } from '@/mocks/handlers/strategies'
import axios from 'axios'

import { StrategiesModel } from '@/shared/types/strategy-details-data'
Expand All @@ -6,23 +7,24 @@ const getStrategiesData = async (
isReady: boolean,
page: number,
size: number
): Promise<{ strategiesData: StrategiesModel[]; totalCount: number } | undefined> => {
if (!isReady) return { strategiesData: [], totalCount: 0 }
): Promise<{ strategiesData: StrategiesModel[]; totalPages: number } | undefined> => {
if (!isReady) return { strategiesData: [], totalPages: 0 }

try {
const response = await axios.get(`/api/strategies?page=${page}&size=${size}`)
if (!response.data) {
console.error('전략 목록 데이터 가져오기 실패')
return { strategiesData: [], totalCount: 0 }
}
const {
strategiesData,
totalCount,
}: { strategiesData: StrategiesModel[]; totalCount: number } = await response.data
content: strategiesData,
totalPages,
}: { content: StrategiesModel[]; totalPages: number } = await response.data.result

return { strategiesData, totalCount }
return { strategiesData, totalPages }
} catch (err) {
console.error(err)
// 임시 목데이터
return { strategiesData: strategiesMockData, totalPages: 2 }
}
}

Expand Down
35 changes: 9 additions & 26 deletions app/(dashboard)/strategies/_ui/strategy-list/index.tsx
Original file line number Diff line number Diff line change
@@ -1,58 +1,41 @@
'use client'

import { useEffect } from 'react'

import { useRouter, useSearchParams } from 'next/navigation'

import ListHeader from '@/app/(dashboard)/_ui/list-header'
import StrategiesItem from '@/app/(dashboard)/_ui/strategies-item'
import classNames from 'classnames/bind'

import { PATH } from '@/shared/constants/path'
import { usePagination } from '@/shared/hooks/custom/use-pagination'
import { useMSWStore } from '@/shared/stores/msw'
import Pagination from '@/shared/ui/pagination'

import useGetStrategiesData from '../../_hooks/query/use-get-strategies-data'
import styles from './styles.module.scss'

/* eslint-disable react-hooks/exhaustive-deps */

const cx = classNames.bind(styles)

const COUNT_PER_PAGE = 8

const StrategyList = () => {
const searchParams = useSearchParams()
const router = useRouter()
const isReady = useMSWStore((state) => state.isReady)
const page = parseInt(searchParams?.get('page') || '1')

const { page, handlePageChange } = usePagination({
basePath: PATH.STRATEGIES,
pageSize: COUNT_PER_PAGE,
})
const { data } = useGetStrategiesData({ isReady, page, size: COUNT_PER_PAGE })
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이것도 다시보니 size도 searchParams로 구하는게 좋겠네요,,,,


useEffect(() => {
if (!searchParams.size) {
router.push(`/strategies?page=1&size=${COUNT_PER_PAGE}`)
}
}, [searchParams])

const strategiesData = data?.strategiesData || []
const totalCount = data?.totalCount || null
const totalPages = data?.totalPages || null

const handlePageChange = (page: number) => {
router.push(`/strategies?page=${page}&size=${COUNT_PER_PAGE}`)
}
return (
<>
<ListHeader />
{strategiesData?.map((strategy) => (
<StrategiesItem key={strategy.strategyId} strategiesData={strategy} />
))}
<div className={cx('pagination')}>
{totalCount && (
<Pagination
currentPage={page}
maxPage={Math.ceil(totalCount / COUNT_PER_PAGE)}
onPageChange={handlePageChange}
/>
{totalPages && (
<Pagination currentPage={page} maxPage={totalPages} onPageChange={handlePageChange} />
)}
</div>
</>
Expand Down
Loading