Skip to content

Commit 630ce6e

Browse files
장아영장아영
authored andcommitted
[#119] 🐛 resoleve conflict
2 parents 02733c2 + fa979cb commit 630ce6e

File tree

14 files changed

+425
-13
lines changed

14 files changed

+425
-13
lines changed

.env

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
NEXT_PUBLIC_API_BASE_URL=http://43.202.50.174:8080/
1+
# NEXT_PUBLIC_API_BASE_URL=http://localhost:3000

src/app/(pages)/sign-in/page.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
'use client'
22

3+
import { useRouter } from 'next/navigation'
4+
35
import { SubmitHandler, useForm } from 'react-hook-form'
46

57
import { SignInRequest } from '@/types/auth.types'
68

79
export default function LoginPage(): JSX.Element {
10+
const router = useRouter()
811
const {
912
register,
1013
handleSubmit,
@@ -28,6 +31,7 @@ export default function LoginPage(): JSX.Element {
2831
const result = await response.json()
2932
console.log('Login successful:', result)
3033
alert('로그인 성공')
34+
router.push('/')
3135
} catch (error) {
3236
console.error('Login error', error)
3337
alert('로그인 요청 중 오류 발생')

src/app/(pages)/sign-up/page.tsx

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
'use client'
2+
3+
import { useRouter } from 'next/navigation'
4+
5+
import { SubmitHandler, useForm } from 'react-hook-form'
6+
7+
import { SignUpRequest } from '@/types/auth.types'
8+
9+
import { SignUp } from '@/services/auth/auth'
10+
11+
export default function SignUpPage(): JSX.Element {
12+
const router = useRouter()
13+
const {
14+
register,
15+
handleSubmit,
16+
formState: { errors },
17+
} = useForm<SignUpRequest>()
18+
19+
const onSubmit: SubmitHandler<SignUpRequest> = async data => {
20+
try {
21+
const response = await SignUp(data)
22+
if (!response.ok) {
23+
console.error('회원가입 실패')
24+
alert('회원가입 실패')
25+
return
26+
}
27+
const result = await response.json()
28+
console.log('회원가입 성공:', result)
29+
30+
alert('회원가입이 완료되었습니다!')
31+
router.push('/sign-in')
32+
} catch (error) {
33+
console.error('회원가입 요청 중 오류 발생', error)
34+
alert('회원가입 중 오류 발생')
35+
}
36+
}
37+
38+
return (
39+
<div>
40+
<h1>회원가입</h1>
41+
<form onSubmit={handleSubmit(onSubmit)}>
42+
<div>
43+
<label htmlFor='email'>이메일</label>
44+
<input
45+
id='email'
46+
type='email'
47+
{...register('email', { required: '이메일을 입력하세요.' })}
48+
/>
49+
</div>
50+
<div>
51+
<label htmlFor='name'>이름</label>
52+
<input
53+
id='name'
54+
type='name'
55+
{...register('name', { required: '이름을 입력하세요.' })}
56+
/>
57+
</div>
58+
<div>
59+
<label htmlFor='password'>비밀번호</label>
60+
<input
61+
id='password'
62+
type='password'
63+
{...register('password', { required: '비밀번호를 입력하세요.' })}
64+
/>
65+
</div>
66+
<div>
67+
<label htmlFor='gitHub'>깃허브 주소</label>
68+
<input
69+
id='gitHub'
70+
type='gitHub'
71+
{...register('gitHub', { required: '깃허브 주소를 입력하세요.' })}
72+
/>
73+
</div>
74+
<button type='submit'>회원가입</button>
75+
</form>
76+
</div>
77+
)
78+
}

src/assets/icons/ic-caret-up.svg

Lines changed: 1 addition & 1 deletion
Loading

src/components/common/button/Clickable.test.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import React from 'react'
2-
31
import '@testing-library/jest-dom'
42
import { render, screen } from '@testing-library/react'
53

src/components/common/dropdown/Dropdown.test.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import React from 'react'
2-
31
import '@testing-library/jest-dom'
42
import { fireEvent, render, screen } from '@testing-library/react'
53

src/components/common/dropdown/Dropdown.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,9 @@ interface DropdownContextType {
1515

1616
const DropdownContext = createContext<DropdownContextType | null>(null)
1717

18-
const useDropdownContext = () => {
18+
export const useDropdownContext = (): DropdownContextType => {
1919
const context = useContext(DropdownContext)
20+
2021
if (!context) {
2122
throw new Error('useDropdownContext must be used within a DropdownProvider')
2223
}
@@ -46,7 +47,7 @@ const Dropdown = ({ children, className }: BaseProps): JSX.Element => {
4647
return () => {
4748
document.removeEventListener('mousedown', handleClickOutside)
4849
}
49-
}, [])
50+
})
5051

5152
return (
5253
<DropdownContext.Provider value={{ isOpen, toggle, close }}>
@@ -58,15 +59,15 @@ const Dropdown = ({ children, className }: BaseProps): JSX.Element => {
5859
}
5960

6061
const Trigger = ({ children, className }: BaseProps) => {
61-
const { toggle } = useDropdownContext()
62+
const { isOpen, toggle } = useDropdownContext()
6263

6364
return (
6465
<button
6566
type='button'
6667
className={className}
6768
onClick={toggle}
6869
aria-haspopup='listbox'
69-
aria-expanded={true}
70+
aria-expanded={isOpen}
7071
>
7172
{children}
7273
</button>
@@ -112,7 +113,7 @@ interface ItemProps extends BaseProps {
112113

113114
const getItemStyle = (className: string) =>
114115
clsx(
115-
'flex h-40 w-full items-center rounded-8 px-12 text-body2 text-gray-800 hover:bg-gray-100',
116+
'flex h-40 w-full items-center rounded-8 px-12 text-body2 font-medium text-gray-800 hover:bg-gray-100',
116117
className
117118
)
118119

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
import { Dropdown } from './Dropdown'
1+
import { Dropdown, useDropdownContext } from './Dropdown'
22

3-
export { Dropdown }
3+
export { Dropdown, useDropdownContext }
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import { IcCaretDown, IcCaretUp } from '@/assets/IconList'
2+
import clsx from 'clsx'
3+
4+
import { Box } from '@/components/common/containers'
5+
import { Dropdown, useDropdownContext } from '@/components/common/dropdown'
6+
import { CheckboxInput } from '@/components/common/input'
7+
8+
type Option = {
9+
label: string
10+
value: string
11+
}
12+
13+
interface SelectProps {
14+
options: Option[]
15+
values: string[]
16+
onChange: React.Dispatch<React.SetStateAction<string[]>>
17+
placeholder?: string
18+
disabled?: boolean
19+
}
20+
export const MultiSelect = ({
21+
options,
22+
values,
23+
onChange,
24+
placeholder = 'Select an option',
25+
disabled = false,
26+
}: SelectProps): JSX.Element => {
27+
const selectedOptions = options.filter(option =>
28+
values.includes(option.value)
29+
)
30+
const selectedLabel = selectedOptions[0]?.value
31+
? `${selectedOptions[0]?.value}` +
32+
(selectedOptions.length - 1
33+
? ` 외 ${selectedOptions.length - 1}개 선택`
34+
: '')
35+
: ''
36+
37+
const handleSelect = (value: string) => {
38+
onChange(prev =>
39+
prev.includes(value) ? prev.filter(v => v !== value) : [...prev, value]
40+
)
41+
}
42+
43+
const triggerStyle = clsx({
44+
'pointer-events-none cursor-not-allowed': disabled,
45+
})
46+
47+
return (
48+
<Dropdown>
49+
<Dropdown.Trigger className={triggerStyle} aria-disabled={disabled}>
50+
<DropdownTriggerBox
51+
label={selectedLabel || placeholder}
52+
isSelected={!!selectedLabel}
53+
isDisabled={disabled}
54+
/>
55+
</Dropdown.Trigger>
56+
<Dropdown.Menu>
57+
{options.map(option => (
58+
<Dropdown.Item
59+
key={option.value}
60+
closeOnSelect={false}
61+
onClick={() => handleSelect(option.value)}
62+
>
63+
<CheckboxInput
64+
label={option.label}
65+
onClick={() => handleSelect(option.value)}
66+
variant='checkbox'
67+
checked={selectedOptions.some(
68+
selectedOption => selectedOption.value === option.value
69+
)}
70+
className='ml-4 cursor-pointer'
71+
/>
72+
</Dropdown.Item>
73+
))}
74+
</Dropdown.Menu>
75+
</Dropdown>
76+
)
77+
}
78+
79+
interface DropdownTriggerBoxProps {
80+
label: string
81+
isSelected: boolean
82+
isDisabled: boolean
83+
}
84+
85+
const DropdownTriggerBox = ({
86+
label,
87+
isSelected,
88+
isDisabled,
89+
}: DropdownTriggerBoxProps) => {
90+
const { isOpen } = useDropdownContext()
91+
const triggerBoxClass = clsx(
92+
'h-48 w-210 justify-between p-12 text-body1 font-medium text-gray-500 focus:border-primary-normal',
93+
{ 'text-gray-800': isSelected },
94+
{ 'bg-gray-200 text-gray-400': isDisabled }
95+
)
96+
97+
return (
98+
<Box className={triggerBoxClass} rounded={8}>
99+
{label}
100+
{isOpen ? <IcCaretUp /> : <IcCaretDown />}
101+
</Box>
102+
)
103+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import { IcCaretDown, IcCaretUp } from '@/assets/IconList'
2+
import clsx from 'clsx'
3+
4+
import { Box } from '@/components/common/containers'
5+
import { Dropdown, useDropdownContext } from '@/components/common/dropdown'
6+
7+
type Options = {
8+
label: string
9+
value: string
10+
}
11+
12+
interface SelectProps {
13+
options: Options[]
14+
value: string
15+
onChange: (value: string) => void
16+
placeholder?: string
17+
disabled?: boolean
18+
}
19+
export const Select = ({
20+
options,
21+
value,
22+
onChange,
23+
placeholder = 'Select an option',
24+
disabled = false,
25+
}: SelectProps): JSX.Element => {
26+
const selectedOption = options.find(option => option.value === value)
27+
const selectedLabel = selectedOption?.label || ''
28+
29+
const handleSelect = (value: string) => {
30+
onChange(value)
31+
}
32+
33+
const triggerStyle = clsx({
34+
'pointer-events-none cursor-not-allowed': disabled,
35+
})
36+
37+
return (
38+
<Dropdown>
39+
<Dropdown.Trigger className={triggerStyle} aria-disabled={disabled}>
40+
<DropdownTriggerBox
41+
label={selectedLabel || placeholder}
42+
isSelected={!!selectedLabel}
43+
isDisabled={disabled}
44+
/>
45+
</Dropdown.Trigger>
46+
<Dropdown.Menu>
47+
{options.map(option => (
48+
<Dropdown.Item
49+
key={option.value}
50+
onClick={() => handleSelect(option.value)}
51+
>
52+
{option.label}
53+
</Dropdown.Item>
54+
))}
55+
</Dropdown.Menu>
56+
</Dropdown>
57+
)
58+
}
59+
60+
interface DropdownTriggerBoxProps {
61+
label: string
62+
isSelected: boolean
63+
isDisabled: boolean
64+
}
65+
66+
const DropdownTriggerBox = ({
67+
label,
68+
isSelected,
69+
isDisabled,
70+
}: DropdownTriggerBoxProps) => {
71+
const { isOpen } = useDropdownContext()
72+
const triggerBoxClass = clsx(
73+
'h-48 w-210 justify-between p-12 text-body1 font-medium text-gray-500 focus:border-primary-normal',
74+
{ 'text-gray-800': isSelected },
75+
{ 'bg-gray-200 text-gray-400': isDisabled }
76+
)
77+
78+
return (
79+
<Box className={triggerBoxClass} rounded={8}>
80+
{label}
81+
{isOpen ? <IcCaretUp /> : <IcCaretDown />}
82+
</Box>
83+
)
84+
}

0 commit comments

Comments
 (0)