Skip to content

Commit

Permalink
[v2] add custom page features (#19)
Browse files Browse the repository at this point in the history
  • Loading branch information
nabi-chan authored May 24, 2024
1 parent 91ecd48 commit 21363db
Show file tree
Hide file tree
Showing 23 changed files with 868 additions and 43 deletions.
Binary file modified bun.lockb
Binary file not shown.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"@supabase/supabase-js": "^2.43.1",
"@tanstack/react-query": "^5.35.1",
"axios": "^1.6.8",
"cuid": "^3.0.0",
"formik": "^2.4.6",
"immutable": "^4.3.6",
"lodash": "^4.17.21",
Expand Down
15 changes: 15 additions & 0 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,16 @@ model Article {
stage Stage? @default(DRAFT)
}

model CustomPage {
id BigInt @id @default(autoincrement())
created_at DateTime @default(now()) @db.Timestamptz(6)
updated_at DateTime @default(now()) @db.Timestamptz(6)
title String
description String?
content String?
type PageType @default(MARKDOWN)
}

enum Stage {
PRIVATE
PUBLIC
Expand All @@ -31,3 +41,8 @@ enum BlogCategory {
JOURNAL
NOTE
}

enum PageType {
MARKDOWN
HTML
}
66 changes: 66 additions & 0 deletions src/features/Editor/component/Select.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import type { SelectRef } from '@channel.io/bezier-react'
import { Select as BaseSelect, ListItem } from '@channel.io/bezier-react'
import type { ReactNode } from 'react'
import { useEffect, useMemo, useRef, useState } from 'react'

export type SelectItem<V extends string> = {
leftContent?: ReactNode
label: string
value: V
}

interface SelectProps<V extends string> {
items: SelectItem<V>[]
value: V
name?: string
defaultValue?: V
placeholder?: string
onChange: (event: { target: { name?: string; value: V } }) => void
}

export function Select<V extends string>({
items,
value,
name,
defaultValue,
placeholder,
onChange,
}: SelectProps<V>) {
const selectRef = useRef<SelectRef>(null)
const [valueState, setValueState] = useState<V | undefined>(defaultValue)

useEffect(
function updateValueStateWhenValueChanged() {
setValueState(value)
},
[value]
)

const handleClickItem = (item: SelectItem<V>) => {
selectRef.current?.handleHideDropdown()
onChange?.({ target: { name, value: item.value } })
}

const selectedItem = useMemo(
() => items.find((item) => item.value === valueState),
[items, valueState]
)

return (
<BaseSelect
placeholder={placeholder}
text={selectedItem?.label}
ref={selectRef}
dropdownStyle={{ width: '100%', padding: 8 }}
>
{items.map((item) => (
<ListItem
key={item.value}
leftContent={item.leftContent}
content={item.label}
onClick={() => handleClickItem(item)}
/>
))}
</BaseSelect>
)
}
1 change: 0 additions & 1 deletion src/features/blog/component/PostListError/index.ts

This file was deleted.

77 changes: 77 additions & 0 deletions src/features/custom-pages/components/CustomPageLayout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import type { PropsWithChildren } from 'react'
import { Box } from '@channel.io/bezier-react'
import type { Tables } from '@/supabase/types'
import { BaseLayout } from '@/layouts/BaseLayout/BaseLayout'
import { SiteLayout } from '@/layouts/SiteLayout/SiteLayout'

interface CustomPageLayoutProps {
title: string
description: string | null
layout: Tables<'CustomPage'>['layout']
}

export function CustomPageLayout({
title,
description,
layout,
children,
}: PropsWithChildren<CustomPageLayoutProps>) {
switch (layout) {
case 'SITE': {
return (
<SiteLayout
title={title}
description={description ?? undefined}
>
{children}
</SiteLayout>
)
}

case 'WIDE': {
return (
<BaseLayout
title={title}
description={description ?? undefined}
>
<Box
as="main"
padding={24}
>
{children}
</Box>
</BaseLayout>
)
}

case 'BASE': {
return (
<BaseLayout
title={title}
description={description ?? undefined}
>
<Box
as="main"
padding={24}
maxWidth="72rem"
marginHorizontal="auto"
>
{children}
</Box>
</BaseLayout>
)
}

case 'EMPTY':
default: {
return (
<BaseLayout
title={title}
description={description ?? undefined}
>
{children}
</BaseLayout>
)
}
}
}
44 changes: 44 additions & 0 deletions src/features/custom-pages/queries/useUpdatePageMutation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { useMutation } from '@tanstack/react-query'
import { merge } from 'immutable'
import { useToast } from '@channel.io/bezier-react'
import axios from 'axios'
import assert from 'assert'
import { supabase } from '@/supabase/client'
import type { Tables } from '@/supabase/types'

interface UpdatePagePayload {
pageId: Tables<'CustomPage'>['id']
page: Partial<Omit<Tables<'CustomPage'>, 'id' | 'created_at' | 'updated_at'>>
}
export function useUpdatePageMutation() {
const { addToast } = useToast()

return useMutation({
mutationFn: async ({ page, pageId }: UpdatePagePayload) =>
supabase
.from('CustomPage')
.update(merge(page, { updated_at: new Date() }))
.eq('id', pageId)
.select('id')
.single()
.throwOnError(),
onSuccess: async (_, { pageId }) => {
const {
data: { session },
} = await supabase.auth.getSession()
assert(session?.access_token, 'session.access_token is undefined')
await axios.patch<'ok'>('/api/page/invalidate', undefined, {
params: {
pageId,
},
headers: {
Authorization: `Bearer ${session.access_token}`,
},
})
addToast('성공적으로 수정되었습니다.', { preset: 'success' })
},
onError: async () => {
addToast('수정에 실패했습니다.', { preset: 'error' })
},
})
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { VStack, Text } from '@channel.io/bezier-react'
import type { PropsWithChildren } from 'react'

export function PostListError({ children }: PropsWithChildren) {
export function ListError({ children }: PropsWithChildren) {
return (
<VStack paddingVertical={24}>
<Text
Expand Down
1 change: 1 addition & 0 deletions src/features/errors/ListError/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { ListError as PostListError } from './ListError'
5 changes: 3 additions & 2 deletions src/layouts/AdminLayout/AdminGuard.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { useSuspenseQuery } from '@tanstack/react-query'
import type { PropsWithChildren } from 'react'
import { useGetUserQueryObject } from '@/features/User/queries/useGetUserQueryObject'
import { Redirect } from '@/components/Redirect/Redirect'

export function AdminGuard() {
export function AdminGuard({ children }: PropsWithChildren) {
const { data } = useSuspenseQuery(useGetUserQueryObject())

if (!data || data.email !== '[email protected]') {
return <Redirect to="/nabi/login" />
}

return null
return <>{children}</>
}
46 changes: 26 additions & 20 deletions src/layouts/AdminLayout/AdminLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import { Suspense, type PropsWithChildren } from 'react'
import { Box, HStack, Spinner } from '@channel.io/bezier-react'
import dynamic from 'next/dynamic'
import type { BaseLayoutProps } from '@/layouts/BaseLayout/BaseLayout'
import { BaseLayout } from '@/layouts/BaseLayout/BaseLayout'
import { Sidebar } from './Sidebar'
import { AdminGuard } from './AdminGuard'

const AdminGuard = dynamic(
() => import('./AdminGuard').then((m) => m.AdminGuard),
{ ssr: false }
)

export interface AdminLayoutProps extends BaseLayoutProps {}

Expand All @@ -28,25 +33,26 @@ export function AdminLayout({
</HStack>
}
>
<AdminGuard />
<BaseLayout
noindex
title={`고양이집 : ${title}`}
{...props}
>
<HStack>
<Sidebar />
<Box
as="main"
maxWidth="72rem"
width="100%"
marginHorizontal="auto"
padding={16}
>
{children}
</Box>
</HStack>
</BaseLayout>
<AdminGuard>
<BaseLayout
noindex
title={`고양이집 : ${title}`}
{...props}
>
<HStack>
<Sidebar />
<Box
as="main"
maxWidth="72rem"
width="100%"
marginHorizontal="auto"
padding={16}
>
{children}
</Box>
</HStack>
</BaseLayout>
</AdminGuard>
</Suspense>
)
}
17 changes: 16 additions & 1 deletion src/layouts/AdminLayout/constants.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { PencilIcon, type BezierIcon } from '@channel.io/bezier-icons'
import { PageIcon, PencilIcon, type BezierIcon } from '@channel.io/bezier-icons'

type SidebarLink = {
icon: BezierIcon
Expand Down Expand Up @@ -26,4 +26,19 @@ export const ADMIN_SIDEBAR_LINKS: SidebarLink[] = [
},
],
},
{
icon: PageIcon,
title: '커스텀 페이지',
slug: '/nabi/custom-pages',
links: [
{
href: '/list',
title: '페이지 목록',
},
{
href: '/create',
title: '페이지 작성',
},
],
},
]
Loading

0 comments on commit 21363db

Please sign in to comment.