From a68242af6fbd65c524784a7cdb2c7cf892c08af2 Mon Sep 17 00:00:00 2001 From: hyoribogo <97094709+hyoribogo@users.noreply.github.com> Date: Thu, 28 Dec 2023 22:30:18 +0900 Subject: [PATCH] =?UTF-8?q?=EC=9C=A0=EC=A0=80=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=20=EC=8A=A4=ED=83=80=EC=9D=BC=20=EC=9E=91=EC=97=85=20?= =?UTF-8?q?(#52)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: 프로필 링크 user/id로 통일 * feat: 레이아웃 추가 * feat: 퀴즈 테이블 컴포넌트 적용 * feat: 유저 페이지 테이블 필터링 * feat: users 테이블 get 로직 구현 * refactor: 유저 정보 컴포넌트 구현 * refactor: my 폴더 삭제 * refactor: return 타입 변경 * feat: 서버 컴포넌트로 변경 * feat: not found 페이지 추가 * refactor: userInfo로 prop 이름 변경 * feat: user 페이지로 링크 처리 --- app/my/page.tsx | 52 ------------------------ app/quizzes/[id]/page.tsx | 3 +- app/user/[id]/_components/quiz-table.tsx | 15 +++++++ app/user/[id]/_components/user-info.tsx | 9 ++++ app/user/[id]/layout.tsx | 16 ++++++++ app/user/[id]/not-found.tsx | 20 +++++++++ app/user/[id]/page.tsx | 36 ++++++++++++++++ components/common/headers/menu.tsx | 2 +- components/common/headers/profile.tsx | 2 +- services/quiz/api.ts | 6 +-- services/user/api.ts | 19 +++++++++ services/user/hook.ts | 6 +++ services/user/options.ts | 14 +++++++ 13 files changed, 141 insertions(+), 59 deletions(-) delete mode 100644 app/my/page.tsx create mode 100644 app/user/[id]/_components/quiz-table.tsx create mode 100644 app/user/[id]/_components/user-info.tsx create mode 100644 app/user/[id]/layout.tsx create mode 100644 app/user/[id]/not-found.tsx create mode 100644 app/user/[id]/page.tsx create mode 100644 services/user/api.ts create mode 100644 services/user/hook.ts create mode 100644 services/user/options.ts diff --git a/app/my/page.tsx b/app/my/page.tsx deleted file mode 100644 index d1b0b16..0000000 --- a/app/my/page.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import { QuizCard } from '@/components/quiz/quiz-card'; -import quizOptions from '@/services/quiz/options'; -import { createClient } from '@/utils/supabase/server'; -import { - HydrationBoundary, - QueryClient, - dehydrate, -} from '@tanstack/react-query'; -import { cookies } from 'next/headers'; - -export default async function Page() { - const cookieStore = cookies(); - const supabase = createClient(cookieStore); - const { - data: { user }, - } = await supabase.auth.getUser(); - - const queryClient = new QueryClient(); - - const submittedQuiz = await queryClient.fetchQuery( - quizOptions.submitted(user?.id ?? '') - ); - - return ( - <> -

- {user?.user_metadata.user_name} -

-

풀었던 퀴즈

- - {submittedQuiz && submittedQuiz.length ? ( - - ) : ( -

제출한 퀴즈가 없습니다.

- )} -
- - ); -} diff --git a/app/quizzes/[id]/page.tsx b/app/quizzes/[id]/page.tsx index efd33e6..9522746 100644 --- a/app/quizzes/[id]/page.tsx +++ b/app/quizzes/[id]/page.tsx @@ -37,8 +37,7 @@ export default async function Page({ params }: { params: { id: string } }) {

By{' '} - - {/* TODO: 추후 상세 유저 페이지 라우팅 경로로 변경하기 */} + {quiz?.users?.name}

diff --git a/app/user/[id]/_components/quiz-table.tsx b/app/user/[id]/_components/quiz-table.tsx new file mode 100644 index 0000000..2d89af7 --- /dev/null +++ b/app/user/[id]/_components/quiz-table.tsx @@ -0,0 +1,15 @@ +import { columns } from '@/components/quiz/table/columns'; +import DataTable from '@/components/quiz/table/data-table'; +import { QuizTable } from '../../../../libs/models'; + +type QuizTableProps = { + quiz: QuizTable[]; +}; + +export default function QuizTable({ quiz }: QuizTableProps) { + return ( +
+ +
+ ); +} diff --git a/app/user/[id]/_components/user-info.tsx b/app/user/[id]/_components/user-info.tsx new file mode 100644 index 0000000..11f6d80 --- /dev/null +++ b/app/user/[id]/_components/user-info.tsx @@ -0,0 +1,9 @@ +type UserInfoProps = { + userInfo: { + name: string; + }; +}; + +export default function UserInfo({ userInfo }: UserInfoProps) { + return

{userInfo.name}

; +} diff --git a/app/user/[id]/layout.tsx b/app/user/[id]/layout.tsx new file mode 100644 index 0000000..ce3b4fd --- /dev/null +++ b/app/user/[id]/layout.tsx @@ -0,0 +1,16 @@ +import BackHeader from '@/components/common/headers/back-header'; +import React from 'react'; + +type LayoutProps = { + children: React.ReactNode; +}; + +export default function Layout({ children }: LayoutProps) { + return ( + <> + + +
{children}
+ + ); +} diff --git a/app/user/[id]/not-found.tsx b/app/user/[id]/not-found.tsx new file mode 100644 index 0000000..ae28c4d --- /dev/null +++ b/app/user/[id]/not-found.tsx @@ -0,0 +1,20 @@ +import Image from 'next/image'; +import Link from 'next/link'; +import TypeTime from '@/assets/images/type-time.png'; + +export default function NotFound() { + return ( + <> +
+ 로고 +

페이지를 찾을 수 없습니다.

+ + 홈으로 이동하기 + +
+ + ); +} diff --git a/app/user/[id]/page.tsx b/app/user/[id]/page.tsx new file mode 100644 index 0000000..feab581 --- /dev/null +++ b/app/user/[id]/page.tsx @@ -0,0 +1,36 @@ +import { + HydrationBoundary, + QueryClient, + dehydrate, +} from '@tanstack/react-query'; +import quizOptions from '@/services/quiz/options'; +import userOptions from '@/services/user/options'; +import { Separator } from '@/components/ui/separator'; +import UserInfo from './_components/user-info'; +import QuizTable from './_components/quiz-table'; +import NotFound from './not-found'; + +export default async function Page({ params }: { params: { id: string } }) { + const { id } = params; + + const queryClient = new QueryClient(); + + const [userInfo, submittedQuiz] = await Promise.all([ + queryClient.fetchQuery(userOptions.info(id)), + queryClient.fetchQuery(quizOptions.submitted(id)), + ]); + + if (userInfo) { + return ( + + {userInfo && } + + +

제출한 퀴즈

+ +
+ ); + } + + return ; +} diff --git a/components/common/headers/menu.tsx b/components/common/headers/menu.tsx index f2d5154..0c9015a 100644 --- a/components/common/headers/menu.tsx +++ b/components/common/headers/menu.tsx @@ -48,7 +48,7 @@ export default async function Menu() { diff --git a/components/common/headers/profile.tsx b/components/common/headers/profile.tsx index 079070b..3dc6c05 100644 --- a/components/common/headers/profile.tsx +++ b/components/common/headers/profile.tsx @@ -13,7 +13,7 @@ export default async function Profile() { } = await supabase.auth.getUser(); return ( - +
{ diff --git a/services/user/api.ts b/services/user/api.ts new file mode 100644 index 0000000..f8d76d8 --- /dev/null +++ b/services/user/api.ts @@ -0,0 +1,19 @@ +import { createClient } from '@/utils/supabase/client'; +import { SupabaseClient } from '@supabase/supabase-js'; + +const userAPI = { + getUserInfo: async (id: string) => { + const supabase: SupabaseClient = createClient(); + + const { data } = await supabase + .from('users') + .select('name') + .eq('id', id) + .limit(1) + .single(); + + return data; + }, +}; + +export default userAPI; diff --git a/services/user/hook.ts b/services/user/hook.ts new file mode 100644 index 0000000..58b7d52 --- /dev/null +++ b/services/user/hook.ts @@ -0,0 +1,6 @@ +import { useSuspenseQuery } from '@tanstack/react-query'; +import userOptions from './options'; + +export function useGetUserInfo(id: string) { + return useSuspenseQuery(userOptions.info(id)); +} diff --git a/services/user/options.ts b/services/user/options.ts new file mode 100644 index 0000000..46a122c --- /dev/null +++ b/services/user/options.ts @@ -0,0 +1,14 @@ +import { queryOptions } from '@tanstack/react-query'; +import userAPI from './api'; + +const userOptions = { + default: ['users'] as const, + + info: (id: string) => + queryOptions({ + queryKey: [...userOptions.default, 'info', id], + queryFn: () => userAPI.getUserInfo(id), + }), +}; + +export default userOptions;