Skip to content

Commit

Permalink
feat: #55 - 회원가입 페이지 구현
Browse files Browse the repository at this point in the history
회원가입 페이지 구현
  • Loading branch information
bomi8489 authored Nov 10, 2023
2 parents d6df312 + c89be2d commit 9da5e75
Show file tree
Hide file tree
Showing 7 changed files with 241 additions and 8 deletions.
8 changes: 7 additions & 1 deletion src/app/(routes)/register/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import UserInfoForm from '@/components/UserInfoForm/UserInfoForm'

const RegisterPage = () => {
return <></>
return (
<div>
<UserInfoForm />
</div>
)
}

export default RegisterPage
189 changes: 189 additions & 0 deletions src/components/UserInfoForm/UserInfoForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
'use client'

import { ChangeEvent, useEffect, useRef, useState } from 'react'
import { useForm } from 'react-hook-form'
import { Avatar, CategoryList, Input } from '@/components'
import { User } from '@/types'
import { cls } from '@/utils'
import { CheckIcon } from '@heroicons/react/24/solid'
import Button from '../common/Button/Button'
import useToggle from '../common/Toggle/hooks/useToggle'
import { useRegister } from './hooks/useRegister'

interface FormValues {
image: File | null
nickName: string
introduce: string
email: string
category: string
newsLetter: boolean
}

const UserInfoForm = ({ userData }: { userData?: User }) => {
const selectUserImage = useRef<HTMLInputElement | null>(null)
const [thumnail, setThumnail] = useState(userData?.profile)
const [isEmailAuthOpen, setIsEmailAuthOpen] = useState(false)
const [checked, toggle] = useToggle(false)
const { registerLinkHub } = useRegister()

useEffect(() => {
setThumnail(userData?.profile)
}, [userData?.profile])

const emailRegex =
/^[a-zA-Z0-9.!#$%&'*+/=?&_`{|}~-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-]/

const {
register,
setValue,
handleSubmit,
formState: { errors },
} = useForm<FormValues>({
defaultValues: {
nickName: userData?.name || '',
introduce: userData?.introduce || '',
category: userData?.category || '엔터테인먼트•예술',
newsLetter: userData?.newsLetter || false,
},
})

const handleFileChange = (e?: ChangeEvent<HTMLInputElement>) => {
e?.preventDefault()

if (e?.target.files) {
const blob = new Blob([e.target.files[0]], {
type: e.target.files[0].type,
})

const thumbNailImage = URL.createObjectURL(blob)
setThumnail(thumbNailImage)
setValue('image', e.target.files[0])
}
}

const handleEmailAuth = () => {
// Todo: 이메일 로직

setIsEmailAuthOpen(true)
}

const handleCheckAuthNum = () => {
// Todo: 인증번호 확인 로직
}

const handleClickCheckButton = () => {
toggle()
setValue('newsLetter', !checked)
}

return (
<form
className="flex flex-col gap-3 px-4 pt-8"
onSubmit={handleSubmit((data) => {
registerLinkHub(data)
})}>
<div className="flex justify-center">
<input
{...register('image')}
type="file"
ref={selectUserImage}
onChange={handleFileChange}
hidden
/>
<div onClick={() => selectUserImage?.current?.click()}>
<Avatar
// Todo: 기본 이미지로 변경
src={thumnail || '/duck.jpg'}
width={80}
height={80}
alt="프로필"
className="h-20"
/>
</div>
</div>
<div>
<Input
{...register('nickName', {
required: '닉네임을 입력해 주세요',
})}
label="닉네임"
placeholder="Nickname"
validation={errors.nickName?.message}
/>
</div>
<div>
<Input
{...register('introduce')}
label="한줄 소개"
placeholder="Introduce"
/>
</div>
<div>
<Input
{...register('email', {
required: '이메일을 입력해 주세요',
pattern: emailRegex,
})}
validation={
errors.email?.type === 'pattern'
? '이메일 양식에 맞게 입력해 주세요'
: errors.email?.message
}
label="이메일"
placeholder="Email"
inputButton
buttonText="인증번호 전송"
buttonColor="gray"
onButtonClick={handleEmailAuth}
/>
</div>
{isEmailAuthOpen && (
<div>
<Input
label="이메일 인증"
placeholder="인증번호를 입력해 주세요"
inputButton
buttonText="인증번호 확인"
buttonColor="gray"
onButtonClick={handleCheckAuthNum}
/>
</div>
)}
<div className="flex flex-col">
<div className="py-2 text-sm font-semibold text-gray9">
관심 카테고리
</div>
<CategoryList
type="default"
horizontal={false}
onChange={(e) => setValue('category', e?.currentTarget.value || '')}
/>
</div>
<div className="flex items-center gap-3 py-2">
<div>관심 카테고리로 뉴스레터 받아보기 동의하시겠습니까?</div>
<label>
<input
type="checkBox"
hidden
onChange={handleClickCheckButton}
/>
<CheckIcon
className={cls(
'h-5 w-5 rounded-sm text-white',
checked ? 'bg-emerald5 ' : 'bg-gray9',
)}
/>
</label>
</div>
<div className="py-6">
<Button
type="submit"
className="button button-lg button-gray px-4 py-2.5">
가입하기
</Button>
</div>
</form>
)
}

export default UserInfoForm
17 changes: 17 additions & 0 deletions src/components/UserInfoForm/hooks/useRegister.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
interface RegisterReqBody {
nickName: string
introduce: string
email: string
category: string
newsLetter: boolean
}

const useRegister = () => {
const registerLinkHub = (data: RegisterReqBody) => {
console.log('회원가입 로직', data)
}

return { registerLinkHub }
}

export { useRegister }
9 changes: 7 additions & 2 deletions src/components/common/Avatar/Avatar.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { CSSProperties } from 'react'
import { cls } from '@/utils'
import Image from 'next/image'

export interface AvatarProps {
Expand All @@ -7,17 +8,21 @@ export interface AvatarProps {
height: number
alt: string
style?: CSSProperties
className?: string
}

const Avatar = ({ src, width, height, alt }: AvatarProps) => {
const Avatar = ({ src, width, height, alt, className }: AvatarProps) => {
return (
<div className="inline-block">
<Image
src={src}
width={width}
height={height}
alt={alt}
className="border-slate3 rounded-full border object-cover"
className={cls(
className,
'rounded-full border border-slate3 object-cover',
)}
/>
</div>
)
Expand Down
1 change: 1 addition & 0 deletions src/components/common/Input/Input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ const Input = forwardRef(
/>
{inputButton && (
<button
type="button"
className={cls(
'absolute right-0 top-0 flex rounded-r-md border px-4 py-2.5 text-sm font-semibold text-white',
buttonColor === 'green'
Expand Down
3 changes: 3 additions & 0 deletions src/data/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,10 @@ export const mock_memberData = [
export const mock_userData = {
id: 3,
name: '프롱이',
introduce: '반갑습니다!!',
profile: '/duck.jpg',
category: '엔터테인먼트•예술',
newsLetter: false,
mySpaces: [
{
name: 'My Space 1',
Expand Down
22 changes: 17 additions & 5 deletions src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,27 @@ export interface Space {
comment: boolean
}

export interface User {
id: string
profile: string
}

export interface UserData {
id: number
name: string
profile: string
mySpaces: { name: string; id: string }[]
favoriteSpaces: { name: string; id: string }[]
}

export interface User {
id?: string
name?: string
introduce?: string
profile?: string
category?: string
newsLetter?: boolean
mySpaces?: {
name?: string
id?: string
}[]
favoriteSpaces?: {
name?: string
id?: string
}[]
}

0 comments on commit 9da5e75

Please sign in to comment.