From 9a24b0542e5dde111d38e50e7c4e8e6aeba24dcd Mon Sep 17 00:00:00 2001 From: seongminn Date: Sun, 26 Nov 2023 22:12:04 +0900 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20sports=20list=EB=A5=BC=20=ED=98=B8?= =?UTF-8?q?=EC=B6=9C=ED=95=98=EB=8A=94=20api=20=EB=B0=8F=20=EC=BF=BC?= =?UTF-8?q?=EB=A6=AC=20=ED=95=A8=EC=88=98=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/league.ts | 17 +++++---- src/api/match.ts | 2 ++ .../useSportsListByLeagueId/Fetcher.tsx | 36 +++++++++++++++++++ src/queries/useSportsListByLeagueId/query.ts | 15 ++++++++ src/types/league.ts | 9 +++++ 5 files changed, 73 insertions(+), 6 deletions(-) create mode 100644 src/queries/useSportsListByLeagueId/Fetcher.tsx create mode 100644 src/queries/useSportsListByLeagueId/query.ts create mode 100644 src/types/league.ts diff --git a/src/api/league.ts b/src/api/league.ts index 5c62d53..aa0bf97 100644 --- a/src/api/league.ts +++ b/src/api/league.ts @@ -1,16 +1,13 @@ import * as Sentry from '@sentry/nextjs'; import { AxiosError } from 'axios'; -import instance from '.'; +import { SportsType } from '@/types/league'; -export type LeagueType = { - name: string; - leagueId: number; -}; +import instance from '.'; export const getAllLeagues = async () => { try { - const response = await instance.get('/leagues'); + const response = await instance.get('/leagues'); return response.data; } catch (error) { @@ -25,3 +22,11 @@ export const getAllLeagues = async () => { } } }; + +export const getSportsListByLeagueId = async (leagueId: string) => { + const { data } = await instance.get( + `/leagues/${leagueId}/sports`, + ); + + return data; +}; diff --git a/src/api/match.ts b/src/api/match.ts index 4180cdf..30c8f5a 100644 --- a/src/api/match.ts +++ b/src/api/match.ts @@ -15,6 +15,8 @@ export type MatchListParams = { sportsId: number | number[]; status: 'playing' | 'scheduled' | 'finished'; leagueId: number; + cursor: number; + size: number; }; export const getMatchList = async (params: MatchListParams) => { diff --git a/src/queries/useSportsListByLeagueId/Fetcher.tsx b/src/queries/useSportsListByLeagueId/Fetcher.tsx new file mode 100644 index 0000000..37c063a --- /dev/null +++ b/src/queries/useSportsListByLeagueId/Fetcher.tsx @@ -0,0 +1,36 @@ +import { ReactNode } from 'react'; + +import { SportsType } from '@/types/league'; + +import useSportsListByLeagueId from './query'; + +type SportsListFetcherProps = { + leagueId: string; + children: (data: SportsType[]) => ReactNode; +}; + +const DUMMY = [ + { + sportId: 1, + name: '축구', + }, + { + sportId: 3, + name: '농구', + }, + { + sportId: 2, + name: '롤', + }, +]; + +export default function SportsListFetcher({ + leagueId, + children, +}: SportsListFetcherProps) { + const { error } = useSportsListByLeagueId(leagueId); + + if (error) throw error; + + return children(DUMMY); +} diff --git a/src/queries/useSportsListByLeagueId/query.ts b/src/queries/useSportsListByLeagueId/query.ts new file mode 100644 index 0000000..c3357d8 --- /dev/null +++ b/src/queries/useSportsListByLeagueId/query.ts @@ -0,0 +1,15 @@ +import { useSuspenseQuery } from '@tanstack/react-query'; + +import { getSportsListByLeagueId } from '@/api/league'; + +export default function useSportsListByLeagueId(leagueId: string) { + const { data, error } = useSuspenseQuery({ + queryKey: ['sports-list', leagueId], + queryFn: () => getSportsListByLeagueId(leagueId), + }); + + return { + sportsList: data, + error, + }; +} diff --git a/src/types/league.ts b/src/types/league.ts new file mode 100644 index 0000000..316d6da --- /dev/null +++ b/src/types/league.ts @@ -0,0 +1,9 @@ +export type LeagueType = { + leagueId: number; + name: string; +}; + +export type SportsType = { + name: string; + sportId: number; +}; From 12ce1e24606a90ae68c3c22465b37e83f2893286 Mon Sep 17 00:00:00 2001 From: seongminn Date: Sun, 26 Nov 2023 22:13:45 +0900 Subject: [PATCH 2/3] =?UTF-8?q?fix:=20sidebar=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=ED=95=98=EB=8A=94=20=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=20=ED=83=80=EC=9E=85=EC=9D=84=20LeagueType=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/league.ts | 4 ++-- src/components/common/Sidebar/index.tsx | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/api/league.ts b/src/api/league.ts index aa0bf97..6cf7adf 100644 --- a/src/api/league.ts +++ b/src/api/league.ts @@ -1,13 +1,13 @@ import * as Sentry from '@sentry/nextjs'; import { AxiosError } from 'axios'; -import { SportsType } from '@/types/league'; +import { LeagueType, SportsType } from '@/types/league'; import instance from '.'; export const getAllLeagues = async () => { try { - const response = await instance.get('/leagues'); + const response = await instance.get('/leagues'); return response.data; } catch (error) { diff --git a/src/components/common/Sidebar/index.tsx b/src/components/common/Sidebar/index.tsx index 29e738d..725938b 100644 --- a/src/components/common/Sidebar/index.tsx +++ b/src/components/common/Sidebar/index.tsx @@ -3,7 +3,8 @@ import Link from 'next/link'; import { useEffect, useState } from 'react'; -import { getAllLeagues, LeagueType } from '@/api/league'; +import { getAllLeagues } from '@/api/league'; +import { LeagueType } from '@/types/league'; type SidebarProps = { onClickSidebar: () => void; From f8b917afd8695a8af94eef002e800aad7fb902e4 Mon Sep 17 00:00:00 2001 From: seongminn Date: Sun, 26 Nov 2023 22:15:27 +0900 Subject: [PATCH 3/3] =?UTF-8?q?feat:=20sports=20list=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EC=83=9D=EC=84=B1=20=EB=B0=8F=20queryPara?= =?UTF-8?q?ms=EB=A5=BC=20=EB=8B=A4=EB=A3=A8=EB=8A=94=20hook=20=ED=95=A8?= =?UTF-8?q?=EC=88=98=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/page.tsx | 46 +++++++++++++++++----- src/components/league/SportsList/index.tsx | 26 ++++++++++++ src/hooks/useQueryParams.ts | 30 ++++++++++++++ 3 files changed, 93 insertions(+), 9 deletions(-) create mode 100644 src/components/league/SportsList/index.tsx create mode 100644 src/hooks/useQueryParams.ts diff --git a/src/app/page.tsx b/src/app/page.tsx index ef50baf..7e22e3a 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -2,21 +2,49 @@ import { Suspense } from 'react'; -import MatchList from '@/components/match/MatchList'; -import MatchListFetcher from '@/queries/useMatchList/Fetcher'; +import SportsList from '@/components/league/SportsList'; +import useQueryParams from '@/hooks/useQueryParams'; +import SportsListFetcher from '@/queries/useSportsListByLeagueId/Fetcher'; export default function Home() { + const { appendToParams, setInParams } = useQueryParams(); + return ( -
-

매치

-
+
+ + + {data => } + + + +
+ + + +
+ + {/*
MatchList 로딩중...
}> - {/* // TODO sportsId, leagueId, status 상태를 동적 주입 */} - + {data => } -
-
+ */} + ); } diff --git a/src/components/league/SportsList/index.tsx b/src/components/league/SportsList/index.tsx new file mode 100644 index 0000000..2b10f53 --- /dev/null +++ b/src/components/league/SportsList/index.tsx @@ -0,0 +1,26 @@ +import { SportsType } from '@/types/league'; + +type SportsListProps = { + sportsList: SportsType[]; + onClick: (key: string, value: string) => void; +}; + +export default function SportsList({ sportsList, onClick }: SportsListProps) { + return ( +
    + {sportsList.map(sports => ( +
  • + +
  • + ))} +
+ ); +} diff --git a/src/hooks/useQueryParams.ts b/src/hooks/useQueryParams.ts new file mode 100644 index 0000000..69b5326 --- /dev/null +++ b/src/hooks/useQueryParams.ts @@ -0,0 +1,30 @@ +import { usePathname, useRouter, useSearchParams } from 'next/navigation'; + +export default function useQueryParams() { + const params = useSearchParams(); + const pathname = usePathname(); + const router = useRouter(); + + const appendToParams = (key: string, value: string) => { + const newParams = new URLSearchParams(params.toString()); + const targetParams = newParams.getAll(key); + + if (targetParams.includes(value)) { + newParams.delete(key, value); + } else { + newParams.append(key, value); + } + router.push(`${pathname}?${newParams.toString()}`); + }; + + const setInParams = (key: string, value: string) => { + const newParams = new URLSearchParams(params.toString()); + + if (newParams.get(key) === value) return; + + newParams.set(key, value); + router.push(`${pathname}?${newParams.toString()}`); + }; + + return { params, appendToParams, setInParams }; +}