Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: system view #297

Merged
merged 4 commits into from
Dec 5, 2024
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
Binary file added public/images/network-error.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 1 addition & 5 deletions src/app/(routes)/quiz/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,8 @@ interface Props {
id: string
}
searchParams: {
quizType: 'today' | 'document' | 'collection'
quizType: 'today' | 'document' | 'collection' | 'create'
createdAt: string
// 문제 생성일 경우
isFirst?: boolean
// 문서 퀴즈일 경우
documentName?: string
directoryEmoji?: string
Expand All @@ -26,7 +24,6 @@ const QuizDetailPage = async ({ params, searchParams }: Props) => {
const {
quizType,
createdAt,
isFirst,
documentName,
directoryEmoji,
collectionId,
Expand Down Expand Up @@ -59,7 +56,6 @@ const QuizDetailPage = async ({ params, searchParams }: Props) => {
<IntroAndQuizView
quizType={quizType}
createdAt={createdAt}
isFirst={isFirst}
quizzes={quizSet.quizzes}
documentInfo={documentInfo}
collectionInfo={collectionInfo}
Expand Down
12 changes: 0 additions & 12 deletions src/app/(routes)/quiz/layout.tsx

This file was deleted.

26 changes: 0 additions & 26 deletions src/app/(routes)/quiz/page.tsx

This file was deleted.

57 changes: 57 additions & 0 deletions src/app/error.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
'use client'

import Icon from '@/shared/components/custom/icon'
import { Button } from '@/shared/components/ui/button'
import Text from '@/shared/components/ui/text'
import { NextPageContext } from 'next'
import Image from 'next/image'
import React from 'react'

interface Props {
statusCode: number
}

function Error({ statusCode }: Props) {
if (statusCode === 500)
return (
<div className="center flex-center h-dvh w-dvw max-w-mobile flex-col bg-background-base-02">
<Image src={'/images/network-error.png'} alt="" width={135} height={140} />
<div className="mt-[22.4px] flex flex-col items-center gap-[10px] lg:mt-[15.7px] lg:gap-[16px] lg:py-0">
<Text typography="title3" className="text-center">
네트워크 문제로 <br /> 연결이 지연되고 있습니다
</Text>
<Text typography="text1-medium" color="sub" className="px-[45px] text-center">
네트워크 연결 상태를 확인하신 후, <br />
새로고침 버튼을 눌러주세요
</Text>
</div>
<div className="mt-[54px] flex w-full max-w-[480px] flex-col gap-[16px] px-[20px]">
<div className="flex-center">
<Button
variant={'mediumRound'}
className="pl-[16px] pr-[24px]"
onClick={() => window.location.reload()}
>
<Icon name="refresh" className="mr-[8px] size-[20px]" />
새로고침
</Button>
</div>
</div>
</div>
)
}

Error.getInitialProps = ({ res, err }: NextPageContext) => {
if (
err &&
(err.message?.includes('NetworkError') ||
err.message?.includes('Failed to fetch') ||
err.message?.includes('ECONNREFUSED'))
) {
return { statusCode: 500 }
}
const statusCode = res?.statusCode || err?.statusCode || 404
return { statusCode }
}

export default Error
31 changes: 19 additions & 12 deletions src/app/not-found.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,27 @@
'use client'

import Text from '@/shared/components/ui/text'
import { useRouter } from 'next/navigation'

export default function NotFound() {
const router = useRouter()

return (
<div className="center flex w-full flex-col items-center">
<NotFoundIcon className="h-[128px] w-[204px] lg:h-[215px] lg:w-[341px]" />
<div className="center flex-center h-dvh w-dvw max-w-mobile flex-col bg-background-base-02">
<NotFoundIcon className="h-[128px] w-[204px]" />
<div className="mt-[22.4px] flex flex-col items-center gap-[10px] lg:mt-[15.7px] lg:gap-[16px] lg:py-0">
<div className="text-h4-bold text-gray-08 lg:text-h2-bold">페이지를 찾을 수 없습니다.</div>
<div className="text-text-medium text-gray-07 lg:text-body1-medium px-[45px] text-center">
존재하지 않는 주소를 입력했거나, 요청하신 페이지의 주소가 변경, 삭제되어 찾을 수 없습니다.
</div>
<Text typography="title3">페이지를 찾을 수 없습니다.</Text>
<Text typography="text1-medium" color="sub" className="px-[45px] text-center">
존재하지 않는 주소를 입력했거나, 요청하신 페이지의 <br /> 주소가 변경, 삭제되어 찾을 수
없습니다.
</Text>
</div>
<div className="mt-[54px] flex w-full max-w-[480px] flex-col gap-[16px] px-[20px]">
<div className="flex items-center justify-center gap-[8px]">
<div className="text-h2-bold text-orange-06">Q</div>
<div className="text-h4-bold text-gray-09">다음 중 이동을 원하는 페이지는?</div>
<Text typography="question" color="accent">
Q.
</Text>
<Text typography="question">다음 중 이동을 원하는 페이지는?</Text>
</div>
<div className="flex flex-col gap-[8px]">
<RedirectOption
Expand All @@ -44,12 +48,15 @@ function RedirectOption({
return (
<button
onClick={onClick}
className="border-gray-02 text-text-medium text-gray-08 flex w-full items-center gap-[16px] rounded-[12px] border bg-white px-[11px] py-[9px] text-start"
className="flex w-full items-center gap-[16px] rounded-[12px] bg-white px-[11px] py-[9px] text-start text-subtitle2-medium text-gray-800"
>
<div className="bg-gray-02 text-body1-bold-eng flex size-[36px] items-center justify-center rounded-full">
<Text
typography="subtitle2-bold"
className="flex size-[36px] items-center justify-center rounded-full bg-gray-100 text-gray-500"
>
{order}
</div>
<div>{label}</div>
</Text>
<Text>{label}</Text>
</button>
)
}
Expand Down
8 changes: 3 additions & 5 deletions src/features/quiz/screen/ai-creating-quiz/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,7 @@ const AiCreatingQuiz = ({ documentId, documentName, directoryEmoji }: Props) =>
router.push(
'/quiz/' +
data.quizSetId +
'?quizType=document' +
'&' +
'isFirst=true' +
'?quizType=create' +
'&' +
`createdAt=${data.createdAt}` +
'&' +
Expand All @@ -82,10 +80,10 @@ const AiCreatingQuiz = ({ documentId, documentName, directoryEmoji }: Props) =>

return (
<>
<div className="fixed top-0 h-dvh w-dvw max-w-mobile bg-black opacity-60"></div>
<div className="absolute h-dvh w-dvw max-w-mobile bg-black opacity-60"></div>

{quizIsReady ? (
<div className="flex-center fixed top-0 z-10 h-dvh w-dvw max-w-mobile flex-col">
<div className="flex-center absolute z-10 h-dvh w-dvw max-w-mobile flex-col">
<Image src={'/images/question-quiz-card.png'} alt="" width={87} height={106} />

<Text typography="title2" color="primary-inverse" className="mt-[31px]">
Expand Down
8 changes: 3 additions & 5 deletions src/features/quiz/screen/intro-and-quiz-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,9 @@ import QuizView from './quiz-view'
import QuizIntro from './intro'

interface Props {
quizType: 'today' | 'document' | 'collection'
quizType: 'today' | 'document' | 'collection' | 'create'
quizzes: QuizWithMetadata[]
createdAt: string
isFirst: boolean | undefined
documentInfo?: { name: string; directoryEmoji: string }
collectionInfo?: { name: string; emoji: string }
}
Expand All @@ -17,7 +16,6 @@ const IntroAndQuizView = ({
quizType,
quizzes,
createdAt,
isFirst,
documentInfo,
collectionInfo,
}: Props) => {
Expand All @@ -34,7 +32,7 @@ const IntroAndQuizView = ({
if (!finishedIntro) {
return (
<QuizIntro
quizType={quizType}
quizType={quizType === 'create' ? 'document' : quizType}
createdAt={createdAt}
documentInfo={documentInfo}
collectionInfo={collectionInfo}
Expand All @@ -43,7 +41,7 @@ const IntroAndQuizView = ({
)
}

return <QuizView quizzes={quizzes} isFirst={isFirst} />
return <QuizView quizzes={quizzes} isFirst={quizType === 'create'} />
}

export default IntroAndQuizView
2 changes: 1 addition & 1 deletion src/features/quiz/screen/intro/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import CollectionQuizIntro from './components/collection-quiz-intro'
import { formatDateKorean } from '@/shared/utils/date'

interface Props {
quizType: 'today' | 'document' | 'collection'
quizType: 'today' | 'document' | 'collection' | 'create'
createdAt: string
documentInfo?: { name: string; directoryEmoji: string }
collectionInfo?: { name: string; emoji: string }
Expand Down
5 changes: 4 additions & 1 deletion src/features/quiz/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export const getAnswerText = (answer: string) => {
return answer
}

export const getQuizSetTypeEnum = (quizSetType: 'today' | 'document' | 'collection') => {
export const getQuizSetTypeEnum = (quizSetType: 'today' | 'document' | 'collection' | 'create') => {
let enumQuizType: QuizSetType

switch (quizSetType) {
Expand All @@ -46,6 +46,9 @@ export const getQuizSetTypeEnum = (quizSetType: 'today' | 'document' | 'collecti
case 'collection':
enumQuizType = 'COLLECTION_QUIZ_SET'
break
case 'create':
enumQuizType = 'FIRST_QUIZ_SET'
break
}

return enumQuizType
Expand Down
57 changes: 45 additions & 12 deletions src/features/write/pages/write-document-page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client'

import { useState } from 'react'
import { useEffect, useState } from 'react'
import TitleInput from '../components/title-input'
import Icon from '@/shared/components/custom/icon'
import Text from '@/shared/components/ui/text'
Expand All @@ -10,6 +10,7 @@ import { useCreateDocument } from '@/requests/document/hooks'
import { MAX_CHARACTERS, MIN_CHARACTERS } from '@/features/document/config'
import { useDirectoryContext } from '@/features/directory/contexts/directory-context'
import CreateQuizDrawer from '../components/create-quiz-drawer'
import AiCreatingQuiz from '@/features/quiz/screen/ai-creating-quiz'
import { useRouter } from 'next/navigation'

const Editor = dynamic(() => import('../components/editor'), {
Expand All @@ -22,6 +23,8 @@ const WriteDocumentPage = () => {
const { selectedDirectory, selectDirectoryId, globalDirectoryId } = useDirectoryContext()
const [title, setTitle] = useState('')
const [content, setContent] = useState('')
const [documentId, setDocumentId] = useState<number | null>(null)
const [showCreatePopup, setShowCreatePopup] = useState(false)

const { mutate: createDocumentMutate } = useCreateDocument()

Expand All @@ -46,22 +49,52 @@ const WriteDocumentPage = () => {
},
{
onSuccess: ({ id }) => {
router.push(
'/quiz' +
'?documentId=' +
id +
'&' +
'documentName=' +
title +
'&' +
'directoryEmoji=' +
selectedDirectory.emoji
)
setDocumentId(id)
setShowCreatePopup(true)
},
}
)
}

useEffect(() => {
const handlePopState = (event: PopStateEvent) => {
// ai 퀴즈 생성 팝업이 열려 있는 상태에서는 뒤로 가기 이벤트를 확인
if (showCreatePopup) {
event.preventDefault()
window.history.pushState(null, '', window.location.href)
const userConfirm = window.confirm(
'현재 화면에서 나가시겠습니까? 지금 나가더라도 AI 퀴즈 생성이 중단되지는 않습니다.'
)
if (userConfirm && documentId) {
router.push(`/document/${documentId}`)
}
}
}

if (showCreatePopup) {
window.history.pushState(null, '', window.location.href)
window.addEventListener('popstate', handlePopState)
}

return () => {
window.removeEventListener('popstate', handlePopState)
}
}, [showCreatePopup, router, documentId])

if (documentId !== null && showCreatePopup) {
return (
<div className="h-dvh w-full max-w-mobile">
<div className="fixed right-1/2 z-[9999] h-dvh w-dvw max-w-mobile translate-x-1/2 bg-background-base-01">
<AiCreatingQuiz
documentId={documentId}
documentName={title}
directoryEmoji={selectedDirectory?.emoji ?? ''}
/>
</div>
</div>
)
}

return (
<div className="w-full max-w-mobile">
<TitleInput value={title} handleChange={(value: string) => setTitle(value)} />
Expand Down
Loading