From 4b2c6ada68e1b4cbee714d706e694d0736c9114a Mon Sep 17 00:00:00 2001 From: James Date: Thu, 28 Nov 2024 12:33:57 +0900 Subject: [PATCH 1/3] =?UTF-8?q?fix:=20=EC=9C=A0=EC=A0=80=20=EB=A1=A4=20?= =?UTF-8?q?=ED=83=80=EC=9E=85=20=EB=B3=80=EC=88=98=20=EC=88=98=EC=A0=95(#1?= =?UTF-8?q?25)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/types/auth.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shared/types/auth.ts b/shared/types/auth.ts index 1025dc33..6b24d3cf 100644 --- a/shared/types/auth.ts +++ b/shared/types/auth.ts @@ -53,10 +53,10 @@ export interface TokenStatusModel { export const isAdmin = (user: UserModel | null): boolean => { if (!user) return false - return user.role.includes('admin') + return user.role.includes('ADMIN') } export const isTrader = (user: UserModel | null): boolean => { if (!user) return false - return user.role.includes('trader') + return user.role.includes('TRADER') } From fcbf6ed9cab13e0ee8df9651743ac643029b53d0 Mon Sep 17 00:00:00 2001 From: James Date: Thu, 28 Nov 2024 12:36:19 +0900 Subject: [PATCH 2/3] =?UTF-8?q?feat:=20=EB=82=98=EC=9D=98=20=EC=A0=84?= =?UTF-8?q?=EB=9E=B5=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#125)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/_ui/list-header/index.tsx | 15 +++++-- .../_ui/list-header/styles.module.scss | 4 ++ .../my/_api/get-my-strategy-list.ts | 19 +++++++++ .../custom/use-intersection-observer.ts | 37 +++++++++++++++++ .../_hooks/query/use-get-my-strategy-list.ts | 24 +++++++++++ .../strategies/_ui/my-strategy-list/index.tsx | 41 +++++++++++++++++++ app/(dashboard)/my/strategies/page.tsx | 22 +++++++++- .../my/strategies/styles.module.scss | 3 ++ 8 files changed, 160 insertions(+), 5 deletions(-) create mode 100644 app/(dashboard)/my/_api/get-my-strategy-list.ts create mode 100644 app/(dashboard)/my/_hooks/custom/use-intersection-observer.ts create mode 100644 app/(dashboard)/my/_hooks/query/use-get-my-strategy-list.ts create mode 100644 app/(dashboard)/my/strategies/_ui/my-strategy-list/index.tsx create mode 100644 app/(dashboard)/my/strategies/styles.module.scss diff --git a/app/(dashboard)/_ui/list-header/index.tsx b/app/(dashboard)/_ui/list-header/index.tsx index c1edf917..b8ace106 100644 --- a/app/(dashboard)/_ui/list-header/index.tsx +++ b/app/(dashboard)/_ui/list-header/index.tsx @@ -4,12 +4,19 @@ import styles from './styles.module.scss' const cx = classNames.bind(styles) -const LIST_HEADER = ['전략', '분석', 'MDD', 'SM SCORE', '수익률', '구독'] +const LIST_HEADER = { + default: ['전략', '분석', 'MDD', 'SM SCORE', '수익률', '구독'], + my: ['전략', '분석', 'MDD', 'SM SCORE', '수익률', '공개', '관리'], +} + +interface Props { + type?: 'default' | 'my' +} -const ListHeader = () => { +const ListHeader = ({ type = 'default' }: Props) => { return ( -
- {LIST_HEADER.map((category) => ( +
+ {LIST_HEADER[type].map((category) => (
{category}
diff --git a/app/(dashboard)/_ui/list-header/styles.module.scss b/app/(dashboard)/_ui/list-header/styles.module.scss index 0050d4c6..ee5053ef 100644 --- a/app/(dashboard)/_ui/list-header/styles.module.scss +++ b/app/(dashboard)/_ui/list-header/styles.module.scss @@ -5,6 +5,10 @@ height: 42px; margin: 20px 0 10px; + &.my { + grid-template-columns: 3fr 1.5fr 1.7fr 1.3fr 1.5fr 1.2fr 1.2fr; + } + .category { display: flex; align-items: center; diff --git a/app/(dashboard)/my/_api/get-my-strategy-list.ts b/app/(dashboard)/my/_api/get-my-strategy-list.ts new file mode 100644 index 00000000..3c072bcc --- /dev/null +++ b/app/(dashboard)/my/_api/get-my-strategy-list.ts @@ -0,0 +1,19 @@ +import axiosInstance from '@/shared/api/axios' +import { StrategiesModel } from '@/shared/types/strategy-details-data' + +// 실제 api 나오면 수정 필요함 +// totalElements 사용해서 hasmore값 계산해야될 것 같음 + +interface StrategiesResponseModel { + result: { + strategies: StrategiesModel[] + hasMore: boolean + } +} + +export const getMyStrategyList = async ({ page = 1, size = 4 }: { page: number; size: number }) => { + const response = await axiosInstance.get( + `/api/my-strategies/page=${page}&size=${size}` + ) + return response.data.result +} diff --git a/app/(dashboard)/my/_hooks/custom/use-intersection-observer.ts b/app/(dashboard)/my/_hooks/custom/use-intersection-observer.ts new file mode 100644 index 00000000..0ffbf5bf --- /dev/null +++ b/app/(dashboard)/my/_hooks/custom/use-intersection-observer.ts @@ -0,0 +1,37 @@ +import { RefObject, useEffect } from 'react' + +interface UseIntersectionObserverProps { + ref: RefObject + onIntersect: (entry: IntersectionObserverEntry) => void + threshold?: number + rootMargin?: string +} + +export const useIntersectionObserver = ({ + ref, + onIntersect, + threshold = 0.1, + rootMargin = '0px', +}: UseIntersectionObserverProps) => { + useEffect(() => { + if (!ref.current) return + + const observer = new IntersectionObserver( + (entries) => { + entries.forEach((entry) => onIntersect(entry)) + }, + { + threshold, + rootMargin, + } + ) + + observer.observe(ref.current) + + return () => { + if (ref.current) { + observer.unobserve(ref.current) + } + } + }, [ref, threshold, rootMargin, onIntersect]) +} diff --git a/app/(dashboard)/my/_hooks/query/use-get-my-strategy-list.ts b/app/(dashboard)/my/_hooks/query/use-get-my-strategy-list.ts new file mode 100644 index 00000000..8b87cc7c --- /dev/null +++ b/app/(dashboard)/my/_hooks/query/use-get-my-strategy-list.ts @@ -0,0 +1,24 @@ +import { getMyStrategyList } from '@/app/(dashboard)/my/_api/get-my-strategy-list' +import { useInfiniteQuery } from '@tanstack/react-query' + +import { StrategiesModel } from '@/shared/types/strategy-details-data' + +interface StrategiesPageModel { + strategies: StrategiesModel[] + hasMore: boolean +} + +export const useGetMyStrategyList = () => { + return useInfiniteQuery({ + queryKey: ['myStrategies'], + queryFn: async ({ pageParam = 1 }) => { + const page = typeof pageParam === 'number' ? pageParam : 1 + return getMyStrategyList({ page, size: 4 }) + }, + getNextPageParam: (lastPage, pages) => { + if (!lastPage.hasMore) return undefined + return pages.length + 1 + }, + initialPageParam: 1, + }) +} diff --git a/app/(dashboard)/my/strategies/_ui/my-strategy-list/index.tsx b/app/(dashboard)/my/strategies/_ui/my-strategy-list/index.tsx new file mode 100644 index 00000000..79aace9a --- /dev/null +++ b/app/(dashboard)/my/strategies/_ui/my-strategy-list/index.tsx @@ -0,0 +1,41 @@ +'use client' + +import { useCallback, useRef } from 'react' + +import StrategiesItem from '@/app/(dashboard)/_ui/strategies-item' +import { useIntersectionObserver } from '@/app/(dashboard)/my/_hooks/custom/use-intersection-observer' +import { useGetMyStrategyList } from '@/app/(dashboard)/my/_hooks/query/use-get-my-strategy-list' + +const MyStrategyList = () => { + const { data, fetchNextPage, hasNextPage, isFetchingNextPage } = useGetMyStrategyList() + + const loadMoreRef = useRef(null) + + const onIntersect = useCallback( + (entry: IntersectionObserverEntry) => { + if (entry.isIntersecting && hasNextPage && !isFetchingNextPage) { + fetchNextPage() + } + }, + [fetchNextPage, hasNextPage, isFetchingNextPage] + ) + + useIntersectionObserver({ + ref: loadMoreRef, + onIntersect, + }) + + const strategies = data?.pages.flatMap((page) => page.strategies) || [] + + return ( + <> + {strategies.map((strategy) => ( + + ))} +
+ {isFetchingNextPage &&
로딩 중...
} + + ) +} + +export default MyStrategyList diff --git a/app/(dashboard)/my/strategies/page.tsx b/app/(dashboard)/my/strategies/page.tsx index 6f7e77b5..7010a8a5 100644 --- a/app/(dashboard)/my/strategies/page.tsx +++ b/app/(dashboard)/my/strategies/page.tsx @@ -1,5 +1,25 @@ +import { Suspense } from 'react' + +import classNames from 'classnames/bind' + +import Title from '@/shared/ui/title' + +import ListHeader from '../../_ui/list-header' +import MyStrategyList from './_ui/my-strategy-list' +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + const MyStrategiesPage = () => { - return <> + return ( +
+ + <ListHeader type="my" /> + <Suspense fallback={<div>Loading...</div>}> + <MyStrategyList /> + </Suspense> + </div> + ) } export default MyStrategiesPage diff --git a/app/(dashboard)/my/strategies/styles.module.scss b/app/(dashboard)/my/strategies/styles.module.scss new file mode 100644 index 00000000..f481a8ff --- /dev/null +++ b/app/(dashboard)/my/strategies/styles.module.scss @@ -0,0 +1,3 @@ +.container { + margin-top: 80px; +} From a024de175e75fc119fa2d3a10e61b9a448aa663b Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Thu, 28 Nov 2024 14:25:29 +0900 Subject: [PATCH 3/3] =?UTF-8?q?rename:=20=EB=AC=B4=ED=95=9C=EC=8A=A4?= =?UTF-8?q?=ED=81=AC=EB=A1=A4=20=EA=B4=80=EB=A0=A8=20=ED=9B=85=20shared=20?= =?UTF-8?q?=EB=94=94=EB=A0=89=ED=86=A0=EB=A6=AC=20=EB=82=B4=EB=B6=80?= =?UTF-8?q?=EB=A1=9C=20=EC=9D=B4=EB=8F=99=20(#125)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/my/strategies/_ui/my-strategy-list/index.tsx | 3 ++- .../hooks}/custom/use-intersection-observer.ts | 0 2 files changed, 2 insertions(+), 1 deletion(-) rename {app/(dashboard)/my/_hooks => shared/hooks}/custom/use-intersection-observer.ts (100%) diff --git a/app/(dashboard)/my/strategies/_ui/my-strategy-list/index.tsx b/app/(dashboard)/my/strategies/_ui/my-strategy-list/index.tsx index 79aace9a..29e5edc1 100644 --- a/app/(dashboard)/my/strategies/_ui/my-strategy-list/index.tsx +++ b/app/(dashboard)/my/strategies/_ui/my-strategy-list/index.tsx @@ -3,9 +3,10 @@ import { useCallback, useRef } from 'react' import StrategiesItem from '@/app/(dashboard)/_ui/strategies-item' -import { useIntersectionObserver } from '@/app/(dashboard)/my/_hooks/custom/use-intersection-observer' import { useGetMyStrategyList } from '@/app/(dashboard)/my/_hooks/query/use-get-my-strategy-list' +import { useIntersectionObserver } from '@/shared/hooks/custom/use-intersection-observer' + const MyStrategyList = () => { const { data, fetchNextPage, hasNextPage, isFetchingNextPage } = useGetMyStrategyList() diff --git a/app/(dashboard)/my/_hooks/custom/use-intersection-observer.ts b/shared/hooks/custom/use-intersection-observer.ts similarity index 100% rename from app/(dashboard)/my/_hooks/custom/use-intersection-observer.ts rename to shared/hooks/custom/use-intersection-observer.ts