Skip to content

Commit

Permalink
Merge branch 'feat/#51' into feat/#43
Browse files Browse the repository at this point in the history
  • Loading branch information
lsy20140 committed Sep 10, 2024
2 parents aa6a48e + e5f6a91 commit ea3a7a9
Show file tree
Hide file tree
Showing 7 changed files with 219 additions and 2 deletions.
44 changes: 44 additions & 0 deletions src/app/admin/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import Image from 'next/image'
import Link from 'next/link'

const MENUS = [
{ name: '용어 목록', path: '/admin/words' },
{ name: '용어 등록', path: '/admin/word/add' },
{ name: '신고 댓글 관리', path: '/admin/reports' },
]

export default function AdminLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<div className="w-full h-full bg-gray-100 text-background">
<nav className="flex gap-8 justify-around py-4 bg-white shadow-sm items-center">
<div className="flex gap-2 items-center">
<Image
alt="logo.svg"
src={'/images/logo.svg'}
width={32}
height={32}
/>
<p className="text-h2">Space D</p>
</div>
<div className="flex gap-8">
{MENUS.map((menu, idx) => (
<Link key={idx} href={menu.path} className="hover:text-h3">
{menu.name}
</Link>
))}
</div>
<Link
href={'/home/learning'}
className="text-gray-400 hover:text-gray-600"
>
서비스 메인으로
</Link>
</nav>
{children}
</div>
)
}
5 changes: 5 additions & 0 deletions src/app/admin/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import Link from 'next/link'

export default function AdminMagePage() {
return <></>
}
133 changes: 133 additions & 0 deletions src/app/admin/word/add/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
'use client'
import Button from '@/components/common/Button'
import Input from '@/components/common/Input'
import RadioButton from '@/components/common/RadioButton'
import Textarea from '@/components/common/Textarea'
import { useState } from 'react'

type AdminWordType = {
name: string
meaning: string
pronunciation: {
english: string
}
category: string
example: string
}

const CATEGORY = [
{ id: 0, name: '비즈니스' },
{ id: 1, name: '디자인' },
{ id: 2, name: '개발' },
]

export default function AddWordPage() {
const [word, setWord] = useState<AdminWordType>({
name: '',
meaning: '',
pronunciation: { english: '' },
category: '',
example: '',
})
const handleChange = (
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
) => {
const { name, value, type } = e.target
if (type === 'radio') {
const category = CATEGORY.find((item) => item.id === parseInt(value))
const categoryName = category ? category.name : ''
setWord((prev) => ({
...prev,
category: categoryName,
}))
} else if (name === 'pronunciation') {
setWord((prev) => ({ ...prev, pronunciation: { english: value } }))
} else {
setWord((prev) => ({ ...prev, [name]: value }))
}
console.log(word)
}
function hasEmptyField() {
const { name, meaning, pronunciation, category, example } = word
return (
name === '' ||
meaning === '' ||
pronunciation.english === '' ||
category === '' ||
example === ''
)
}

const handleSubmit = () => {
if (hasEmptyField()) {
alert('모두 입력해주세요')
return
}
alert(`${word.name} 용어 등록 완료`)
}

return (
<>
<div className="mx-72 mt-8">
<p className="text-h2 mb-6">용어 등록</p>
<div className="flex flex-col gap-6">
<label>
용어 이름{' '}
<Input
name="name"
value={word.name ?? ''}
onChange={handleChange}
/>
</label>

<label>
용어 뜻{' '}
<Textarea
name="meaning"
value={word.meaning ?? ''}
onChange={handleChange}
className="bg-white"
/>
</label>
<label>
예문{' '}
<Textarea
name="example"
value={word.example ?? ''}
onChange={handleChange}
className="bg-white"
/>
</label>

<label>
발음
<Input
name="pronunciation"
value={word.pronunciation.english ?? ''}
onChange={handleChange}
/>
</label>
<fieldset>
<label>카테고리</label>
{CATEGORY.map((item) => (
<RadioButton
key={item.id}
id={item.id}
item={item.name}
onChange={handleChange}
isChecked={word.category === item.name}
/>
))}
</fieldset>
<Button
type={hasEmptyField() ? 'disabled' : 'gradient'}
isFullWidth
onClick={handleSubmit}
>
용어 등록
</Button>
</div>
</div>
</>
)
}
9 changes: 8 additions & 1 deletion src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import './globals.css'
import localFont from 'next/font/local'
import { Noto_Sans_KR } from 'next/font/google'
import QueryProvider from '@/provider/QueryProvider'
import { headers } from 'next/headers'

const pretendard = localFont({
src: '../../public/fonts/PretendardVariable.woff2',
Expand All @@ -28,12 +29,18 @@ export default function RootLayout({
}: Readonly<{
children: React.ReactNode
}>) {
const heads = headers()
const pathname = heads.get('x-current-path')
const isAdminPage = pathname?.startsWith('/admin')

return (
<html lang="en">
<body
className={`flex justify-center min-h-dvh ${pretendard.variable} font-pretendard ${notoSansKr.variable}`}
>
<div className="w-full max-w-[430px] bg-background whitespace-pre-wrap">
<div
className={`w-full ${isAdminPage ? '' : 'max-w-[430px]'} bg-black whitespace-pre-wrap`}
>
<QueryProvider>{children}</QueryProvider>
</div>
</body>
Expand Down
2 changes: 1 addition & 1 deletion src/components/common/Button/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export default function Button({
'bg-btn-gradient hover:bg-btn-gradient-hover text-background':
type === 'gradient',
'bg-background': type === 'black',
'bg-gray-800 text-onSurface-100': type === 'disabled',
'bg-gray-800 text-onSurface-100 cursor-default': type === 'disabled',
'bg-gray-600 hover:bg-gray-500': type === 'light',
'w-full': isFullWidth,
},
Expand Down
21 changes: 21 additions & 0 deletions src/components/common/Input/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { cn } from '@/lib/core'
import { InputHTMLAttributes, forwardRef } from 'react'

interface InputProps extends InputHTMLAttributes<HTMLInputElement> {}

export default forwardRef<HTMLInputElement, InputProps>(function Input(
{ className, ...props },
ref,
) {
return (
<input
ref={ref}
{...props}
className={cn(
'resize-none w-full flex items-center justify-center py-4 px-4 rounded-lg outline outline-[0.5px] outline-primary-400 focus:outline-[1.5px] ',
className,
)}
onChange={props.onChange}
/>
)
})
7 changes: 7 additions & 0 deletions src/middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { NextRequest, NextResponse } from 'next/server'

export function middleware(req: NextRequest) {
const headers = new Headers(req.headers)
headers.set('x-current-path', req.nextUrl.pathname)
return NextResponse.next({ headers })
}

0 comments on commit ea3a7a9

Please sign in to comment.