Skip to content

Commit

Permalink
feat: system view (#297)
Browse files Browse the repository at this point in the history
* feat: 시스템 화면 - not found

* feat: system error view

* feat: 퀴즈 생성 로딩 페이지 => 라우트 이동 없이 구현
  • Loading branch information
rabyeoljji authored Dec 5, 2024
1 parent 40af215 commit b6c8181
Show file tree
Hide file tree
Showing 11 changed files with 133 additions and 79 deletions.
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

0 comments on commit b6c8181

Please sign in to comment.