dh_demo

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

[...path].tsx (4470B)


      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 Divider from '@/components/layout/Divider'
      8 import Hero from '@/components/layout/Hero'
      9 import Section from '@/components/layout/Section'
     10 import { Pagination } from '@/components/Pagination'
     11 import ThreadList from '@/components/threads/ThreadList'
     12 import WikiBase from '@/components/wiki/WikiBase'
     13 import { ApiError } from '@/lib/apierror'
     14 import { withConnection } from '@/lib/model_helpers'
     15 import { Thread } from '@/lib/models/thread'
     16 import { getWikiAndPageACLViaPath } from '@/lib/models/wiki_acl'
     17 import { countWikiTalks, listWikiTalks } from '@/lib/models/wiki_talk'
     18 import { parseIntOrDefault } from '@/lib/utils/number'
     19 import { CreateTalkResponse } from '@/pages/api/talks/[slug]/[...path]'
     20 import { GetServerSideProps } from 'next'
     21 import { useRouter } from 'next/router'
     22 import { useState } from 'react'
     23 
     24 const PAGE_SIZE = 10
     25 
     26 export interface TestPageProps {
     27   wikiSlug: string
     28   pageId: number
     29   pagePath: string
     30   talks: Thread[]
     31   counts: number
     32   page: number
     33 }
     34 
     35 export const getServerSideProps: GetServerSideProps<TestPageProps> = async (context) => {
     36   const { slug, path: paths } = context.query
     37   if (typeof slug !== 'string' || !Array.isArray(paths)) {
     38     return {
     39       notFound: true,
     40     }
     41   }
     42 
     43   const path = paths.join('/')
     44 
     45   const page = Math.max(1, parseIntOrDefault(context.query.page, 1))
     46 
     47   return await withConnection(async (conn) => {
     48     const aclInfo = await getWikiAndPageACLViaPath(conn, [slug, path])
     49     if (aclInfo == null || aclInfo.pageId == null) {
     50       return { notFound: true }
     51     }
     52 
     53     const talks = await listWikiTalks(conn, [aclInfo.pageId, PAGE_SIZE, (page - 1) * PAGE_SIZE])
     54     const counts = await countWikiTalks(conn, [aclInfo.pageId])
     55 
     56     return {
     57       props: {
     58         wikiSlug: slug,
     59         pageId: aclInfo.pageId,
     60         pagePath: path,
     61         talks: talks,
     62         counts: counts,
     63         page: page,
     64       },
     65     }
     66   })
     67 }
     68 
     69 export default function TestPage (props: TestPageProps) {
     70   return (
     71     <WikiBase pageKind={'talk'} title={props.pagePath}>
     72       <Section>
     73         {props.talks.length === 0 ? (
     74           <div>No talks</div>
     75         ) : (
     76           <>
     77             <ThreadList threads={props.talks}/>
     78             <Pagination
     79               page={props.page}
     80               totalCount={props.counts}
     81               pageSize={PAGE_SIZE}
     82             />
     83           </>
     84         )}
     85       </Section>
     86 
     87       <Divider/>
     88 
     89       <Section>
     90         <Title>새 주제 만들기</Title>
     91         <NewTalk {...props} />
     92       </Section>
     93     </WikiBase>
     94   )
     95 }
     96 
     97 function NewTalk (props: TestPageProps) {
     98   const router = useRouter()
     99 
    100   const [title, setTitle] = useState('')
    101   const [content, setContent] = useState('')
    102 
    103   const [isLoading, setLoading] = useState(false)
    104   const [submitError, setSubmitError] = useState<ApiError | null>(null)
    105 
    106   const handleSubmit = () => {
    107     setLoading(true)
    108 
    109     !(async () => {
    110       const res = await fetch(`/api/talks/${props.wikiSlug}/${props.pagePath}`, {
    111         method: 'POST',
    112         headers: {
    113           'Content-Type': 'application/json',
    114         },
    115         body: JSON.stringify({
    116           title: title,
    117           content: content,
    118         }),
    119       })
    120 
    121       const data = await res.json()
    122 
    123       if (!res.ok) {
    124         setLoading(false)
    125         setSubmitError(data)
    126         return
    127       }
    128 
    129       await router.push(`/threads/${(data as CreateTalkResponse).thread}`)
    130     })().catch((err) => {
    131       setLoading(false)
    132       console.error(err)
    133     })
    134   }
    135 
    136   return (
    137     <>
    138       <Form onSubmit={handleSubmit}>
    139         <Fields>
    140           <Field
    141             type="text"
    142             placeholder="제목"
    143             value={title}
    144             onValueChange={setTitle}
    145             disabled={isLoading}
    146             color={submitError ? 'error' : undefined}
    147           />
    148 
    149           <Field
    150             type="textarea"
    151             placeholder="내용"
    152             value={content}
    153             onValueChange={setContent}
    154             disabled={isLoading}
    155             color={submitError ? 'error' : undefined}
    156             message={submitError ? submitError.message : undefined}
    157           />
    158 
    159           <SubmitButton value="만들기"/>
    160         </Fields>
    161       </Form>
    162     </>
    163   )
    164 }