dh_demo

DreamHanks demo project
git clone git://git.lair.cx/dh_demo
Log | Files | Refs | README

new.tsx (4507B)


      1 import { SubmitButton } from '@/components/elements/Button'
      2 import Title from '@/components/elements/Title'
      3 import Field from '@/components/form/Field'
      4 import Fields from '@/components/form/Fields'
      5 import Form from '@/components/form/Form'
      6 import Container from '@/components/layout/Container'
      7 import Hero from '@/components/layout/Hero'
      8 import Section from '@/components/layout/Section'
      9 import { isApiError } from '@/lib/apierror'
     10 import { ERR_CODE_DUPLICATED_SLUG, ERR_CODE_INVALID_SLUG, ERR_CODE_INVALID_TITLE } from '@/lib/error_codes'
     11 import { generateRandomName } from '@/lib/random_name'
     12 import { useRouter } from 'next/router'
     13 import { useCallback, useEffect, useState } from 'react'
     14 
     15 export default function WikiNewPage () {
     16   return (
     17     <>
     18       <Hero>
     19         <Title kind="headline">새 위키 만들기</Title>
     20       </Hero>
     21 
     22       <Container>
     23         <Section>
     24           <NewWikiForm/>
     25         </Section>
     26       </Container>
     27     </>
     28   )
     29 }
     30 
     31 function NewWikiForm () {
     32   const router = useRouter()
     33 
     34   const [title, setTitle] = useState('')
     35   const [slug, setSlug] = useState('')
     36   const [description, setDescription] = useState('')
     37 
     38   const [titleError, setTitleError] = useState<string | null>(null)
     39   const [slugError, setSlugError] = useState<string | null>(null)
     40   const [descriptionError, setDescriptionError] = useState<string | null>(null)
     41 
     42   const [isLoading, setLoading] = useState(false)
     43 
     44   const handleSubmit = useCallback(() => {
     45     const handleError = (status: number, err: any) => {
     46       setLoading(false)
     47 
     48       if (!isApiError(err)) {
     49         setTitleError('알 수 없는 오류가 발생했습니다.')
     50         return
     51       }
     52 
     53       switch (status) {
     54         case 400:
     55           switch (err.code) {
     56             case ERR_CODE_INVALID_SLUG:
     57               setSlugError('경로가 유효하지 않습니다.')
     58               return
     59 
     60             case ERR_CODE_INVALID_TITLE:
     61               setTitleError('제목이 유효하지 않습니다.')
     62               return
     63 
     64             case ERR_CODE_DUPLICATED_SLUG:
     65               setSlugError('이미 사용중인 경로입니다.')
     66               return
     67           }
     68           break
     69 
     70         case 401:
     71           setTitleError('로그인이 필요합니다.')
     72           return
     73       }
     74 
     75       setTitleError('알 수 없는 오류가 발생했습니다.')
     76     }
     77 
     78     setLoading(true)
     79     setTitleError(null)
     80     setSlugError(null)
     81     setDescriptionError(null)
     82 
     83     !(async () => {
     84       const res = await fetch('/api/wiki', {
     85         method: 'POST',
     86         headers: {
     87           'Content-Type': 'application/json',
     88         },
     89         body: JSON.stringify({ title, slug, description }),
     90       })
     91 
     92       if (!res.ok) {
     93         const err = await res.json()
     94         handleError(res.status, err)
     95         return
     96       }
     97 
     98       await router.push(`/wiki/${slug}`)
     99     })().catch(err => {
    100       console.error(err)
    101       handleError(err.status, err)
    102     })
    103   }, [router, title, slug, description])
    104 
    105   // Random slug generation
    106 
    107   const [randomSlug, setRandomSlug] = useState('')
    108 
    109   const applyRandomSlug = useCallback(() => {
    110     setSlug(randomSlug)
    111     setRandomSlug(generateRandomName())
    112   }, [randomSlug])
    113 
    114   useEffect(() => {
    115     setRandomSlug(generateRandomName())
    116   }, [])
    117 
    118   return (
    119     <Form onSubmit={handleSubmit}>
    120       <Fields>
    121         <Field
    122           type="text"
    123           placeholder="이름"
    124           value={title}
    125           onValueChange={setTitle}
    126           message={titleError ?? undefined}
    127           color={titleError != null ? 'error' : undefined}
    128           disabled={isLoading}
    129         />
    130 
    131         <Field
    132           type="text"
    133           placeholder="경로"
    134           value={slug}
    135           onValueChange={setSlug}
    136           message={slugError ?? undefined}
    137           color={slugError != null ? 'error' : undefined}
    138           disabled={isLoading}
    139         />
    140 
    141         <p>URL의 일부로써 사용되며, 전체 위키에 대해 고유해야 합니다.<br/>
    142           예를 들어, <a onClick={applyRandomSlug}>&apos;{randomSlug}&apos;</a> 같은 걸 의미합니다.</p>
    143 
    144         <Field
    145           type="text"
    146           placeholder="짧은 설명"
    147           value={description}
    148           onValueChange={setDescription}
    149           message={descriptionError ?? undefined}
    150           color={descriptionError != null ? 'error' : undefined}
    151           disabled={isLoading}
    152         />
    153 
    154         <SubmitButton
    155           color="primary"
    156           value="만들기"
    157           disabled={isLoading}
    158         />
    159       </Fields>
    160     </Form>
    161   )
    162 }