-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: react-hook-form 설치 * fix: 퀴즈 데이터 호출에서 유저 api 호출 방식 변경 * fix: 헤더 뒤로가기 컴포넌트 수정 * chore: 퀴즈 관련 페이지 레이아웃 구성 * feat: 정답 페이지 기본 기능 구성 정답 api 호출 에러 및 로딩 페이지 구성 임시 스타일 적용 * fix: 헤더 유저 프로필 라우팅 변경 * refactor: 테이블 난이도 텍스트 변경 * feat: 댓글 기본 구조 작업 스타일 및 form 적용 * feat: 댓글 가져오기 기능 구현 * feat: 댓글 작성 기능 * style: 줄바꿈 적용 * fix: 쿼리키 및 댓글 추가 로딩 관련 코드 수정 * fix: api 병렬 처리 진행 * fix: 퀴즈 테이블 관련 api 호출 방식 변경
- Loading branch information
Showing
33 changed files
with
604 additions
and
80 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
'use client'; | ||
|
||
import FullButton from '@/components/common/buttons/full-button'; | ||
import { Avatar, AvatarFallback } from '@/components/ui/avatar'; | ||
import { Textarea } from '@/components/ui/textarea'; | ||
import { useForm } from 'react-hook-form'; | ||
import { z } from 'zod'; | ||
import { zodResolver } from '@hookform/resolvers/zod'; | ||
import { Skeleton } from '@/components/ui/skeleton'; | ||
import { | ||
useGetCommentsOfQuiz, | ||
usePostCommentOfQuiz, | ||
} from '@/services/comment/hooks'; | ||
import dayjs from 'dayjs'; | ||
import { useQueryClient } from '@tanstack/react-query'; | ||
import commentOptions from '@/services/comment/options'; | ||
|
||
type CommentsProps = { | ||
disable: boolean; | ||
quizId: number; | ||
userId?: string; | ||
}; | ||
|
||
const FormSchema = z.object({ | ||
content: z.string().min(1, { | ||
message: '댓글을 입력해주세요.', | ||
}), | ||
}); | ||
|
||
export default function Comments({ disable, quizId, userId }: CommentsProps) { | ||
const queryClient = useQueryClient(); | ||
|
||
const { data: comments = [] } = useGetCommentsOfQuiz(quizId); | ||
|
||
const { mutate: postCommentOfQuiz, isPending } = usePostCommentOfQuiz(); | ||
|
||
const { | ||
handleSubmit, | ||
register, | ||
formState: { errors }, | ||
setValue, | ||
} = useForm<z.infer<typeof FormSchema>>({ | ||
resolver: zodResolver(FormSchema), | ||
reValidateMode: 'onSubmit', | ||
}); | ||
|
||
const onSubmit = (data: z.infer<typeof FormSchema>) => { | ||
if (!userId) { | ||
return; | ||
} | ||
|
||
const { content } = data; | ||
postCommentOfQuiz( | ||
{ content, quizId, userId }, | ||
{ | ||
onSuccess: () => { | ||
queryClient.invalidateQueries({ | ||
queryKey: [...commentOptions.quiz(quizId).queryKey], | ||
}); | ||
setValue('content', ''); | ||
}, | ||
} | ||
); | ||
}; | ||
|
||
return ( | ||
<div> | ||
<h3 className="mb-4 text-lg font-semibold">댓글 ({comments?.length})</h3> | ||
|
||
<form className="mb-8" onSubmit={handleSubmit(onSubmit)}> | ||
<Textarea | ||
{...register('content')} | ||
disabled={disable} | ||
placeholder={ | ||
disable ? '회원만 이용 가능합니다.' : '자유롭게 의견을 남겨주세요.' | ||
} | ||
/> | ||
<FullButton disabled={disable || isPending}>제출</FullButton> | ||
<p className="mt-4 text-destructive">{errors.content?.message}</p> | ||
</form> | ||
|
||
<div className="flex flex-col gap-8 whitespace-break-spaces"> | ||
{comments?.map((comment) => ( | ||
<div className="flex flex-col gap-4" key={comment.id}> | ||
<div className="flex items-center justify-between"> | ||
<div className="flex items-center gap-3"> | ||
<Avatar> | ||
<Skeleton className="h-10 w-10 rounded-full" /> | ||
<AvatarFallback> | ||
<Skeleton className="h-10 w-10 rounded-full" /> | ||
</AvatarFallback> | ||
</Avatar> | ||
<span className="max-w-[150px] truncate"> | ||
{comment.users?.name} | ||
</span> | ||
</div> | ||
<span className="text-sm text-slate-500"> | ||
{dayjs('2023-12-26T14:05:45.449Z').format('MMM DD, YYYY')} | ||
</span> | ||
</div> | ||
|
||
<p>{comment.content}</p> | ||
</div> | ||
))} | ||
</div> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
'use client'; | ||
|
||
import MarkDown from '@/components/ui/markdown'; | ||
import { useGetAnswersOfQuiz } from '@/services/quiz/hooks'; | ||
import { dracula } from 'react-syntax-highlighter/dist/cjs/styles/prism'; | ||
|
||
type QuizAnswerProps = { | ||
quizId: number; | ||
}; | ||
|
||
export default function QuizAnswer({ quizId }: QuizAnswerProps) { | ||
const { data: quizAnswer } = useGetAnswersOfQuiz(quizId); | ||
|
||
return ( | ||
<div className="flex flex-col gap-6"> | ||
<div> | ||
<h2 className="text-xl font-bold">정답</h2> | ||
<MarkDown style={dracula}>{quizAnswer.description}</MarkDown> | ||
</div> | ||
|
||
<div> | ||
<h3 className="text-lg font-semibold">해설</h3> | ||
<MarkDown style={dracula}> | ||
{quizAnswer.answer_description ?? ''} | ||
</MarkDown> | ||
</div> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
'use client'; | ||
|
||
import Button from '@/components/common/buttons/button'; | ||
import { useParams, useRouter } from 'next/navigation'; | ||
|
||
export default function Error({ | ||
error, | ||
}: { | ||
error: Error & { digest?: string }; | ||
reset: () => void; | ||
}) { | ||
const router = useRouter(); | ||
const { id: quizId } = useParams(); | ||
|
||
return ( | ||
<div className="flex flex-col items-center gap-6"> | ||
<h2 className="text-xl font-semibold">{error.message}</h2> | ||
<Button onClick={() => router.push(`/quizzes/${quizId}`)}> | ||
문제로 이동하기 | ||
</Button> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 ( | ||
<> | ||
<BackHeader /> | ||
|
||
<section className="p-4">{children}</section> | ||
</> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export default function Loading() { | ||
return <div>loading...</div>; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import quizOptions from '@/services/quiz/options'; | ||
import { | ||
HydrationBoundary, | ||
QueryClient, | ||
dehydrate, | ||
} from '@tanstack/react-query'; | ||
import QuizAnswer from './_components/quiz-answer'; | ||
import Comments from './_components/comments'; | ||
import { cookies } from 'next/headers'; | ||
import { createClient } from '@/utils/supabase/server'; | ||
import { Separator } from '@/components/ui/separator'; | ||
import commentOptions from '@/services/comment/options'; | ||
|
||
export default async function Page({ params }: { params: { id: string } }) { | ||
const queryClient = new QueryClient(); | ||
const quizId = Number(params.id) ?? 0; | ||
|
||
const cookieStore = cookies(); | ||
const supabase = createClient(cookieStore); | ||
|
||
const { | ||
data: { session }, | ||
} = await supabase.auth.getSession(); | ||
|
||
await Promise.all([ | ||
queryClient.prefetchQuery(quizOptions.answers(quizId)), | ||
queryClient.prefetchQuery(commentOptions.quiz(quizId)), | ||
]); | ||
|
||
return ( | ||
<HydrationBoundary state={dehydrate(queryClient)}> | ||
<QuizAnswer quizId={quizId} /> | ||
<Separator className="my-4" /> | ||
<Comments disable={!session} quizId={quizId} userId={session?.user.id} /> | ||
</HydrationBoundary> | ||
); | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
'use client'; | ||
|
||
import Image from 'next/image'; | ||
import BackIcon from '@/assets/images/back-icon.png'; | ||
import { useRouter } from 'next/navigation'; | ||
|
||
export default function BackButton() { | ||
const router = useRouter(); | ||
|
||
return ( | ||
<button className="ml-4 flex items-center" onClick={() => router.back()}> | ||
<Image src={BackIcon} width={30} height={30} alt="뒤로 가기" /> | ||
</button> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.