Skip to content

Commit 75387c3

Browse files
committed
feat: 토글 개고생 시발
1 parent efb39cf commit 75387c3

File tree

6 files changed

+192
-86
lines changed

6 files changed

+192
-86
lines changed

package-lock.json

Lines changed: 26 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
"class-variance-authority": "^0.7.0",
2525
"clsx": "^2.1.1",
2626
"date-fns": "^3.6.0",
27+
"framer-motion": "^11.11.17",
2728
"lucide": "^0.454.0",
2829
"lucide-react": "^0.453.0",
2930
"next": "15.0.1",

src/app/seat/reserve/page.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,21 @@ const SeatStatusArea = ({ onSeatClick }: SeatStatusAreaProps): ReactNode => {
2222
<p
2323
key={seat}
2424
onClick={() => onSeatClick(seat)}
25-
className='flex aspect-square h-full cursor-pointer items-center justify-center bg-swGrayLight text-base font-semibold hover:bg-swGreenLight md:text-xl'
25+
className='flex aspect-square h-full cursor-pointer items-center justify-center rounded-sm bg-swGrayLight text-base font-semibold hover:bg-swGreenLight md:text-xl'
2626
>
2727
{seat}
2828
</p>
2929
))}
3030
</div>
31-
<div className='flex aspect-video w-1/2 items-center justify-center bg-[#FFF495] text-lg font-semibold md:aspect-[5/2] md:text-2xl'>휴게 공간</div>
31+
<div className='flex aspect-video w-1/2 items-center justify-center rounded-lg bg-[#FFF495] text-lg font-semibold md:aspect-[5/2] md:text-2xl'>
32+
휴게 공간
33+
</div>
3234
<div className='mx-4 my-6 grid h-[24%] w-fit grid-cols-6 grid-rows-2 gap-2 self-end md:mx-8 md:h-[30%] md:gap-4'>
3335
{DEST_SEAT_GROUP.map(seat => (
3436
<p
3537
key={seat}
3638
onClick={() => onSeatClick(seat)}
37-
className='flex aspect-square h-full cursor-pointer items-center justify-center bg-swGrayLight text-base font-semibold hover:bg-swGreenLight md:text-xl'
39+
className='flex aspect-square h-full cursor-pointer items-center justify-center rounded-sm bg-swGrayLight text-base font-semibold hover:bg-swGreenLight md:text-xl'
3840
>
3941
{seat}
4042
</p>
@@ -113,7 +115,10 @@ const ReservePage = ({}: ReservePageProps): ReactNode => {
113115
</div>
114116
<div className='relative flex w-full flex-col items-center justify-start gap-1 md:gap-2'>
115117
{SEAT_WARNINGS.map(warning => (
116-
<p key={warning} className='w-full rounded-lg border border-solid border-swGray bg-swGrayLight py-3 text-center text-sm font-medium lg:text-sm'>
118+
<p
119+
key={warning}
120+
className='w-full rounded-lg border border-solid border-swGray bg-swGrayLight py-3 text-center text-sm font-medium lg:text-sm'
121+
>
117122
{warning}
118123
</p>
119124
))}

src/components/common/Header.tsx

Lines changed: 152 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -3,44 +3,55 @@ import Link from 'next/link'
33
import React, { ReactNode } from 'react'
44

55
import { ROUTES } from '@/src/lib/constants/route'
6-
import useAuthStore from '@/src/lib/context/authContext'
6+
import useAuthStore, { AuthState } from '@/src/lib/context/authContext'
77
import ToggleWrapper, { useDropdown } from '@/src/lib/hooks/useToggle'
88
import { cn } from '@/src/lib/utils/cn'
99
import { InstallLink } from '@/src/lib/utils/install-prompt'
10-
import { Nullable } from '@/src/lib/utils/typeUtils'
1110

1211
import LucideIcon from '../provider/LucideIcon'
1312
import { Button } from '../ui/button'
14-
import Backdrop from './Backdrop'
1513
import Logo from './Logo'
1614

1715
const Header = (): ReactNode => {
18-
const { studentId, name } = useAuthStore()
16+
const authData = useAuthStore()
17+
18+
const dropdownData = useDropdown()
1919
return (
2020
<>
21-
<MobileHeader studentId={studentId} name={name} className='flex lg:hidden' />
22-
<DesktopHeader studentId={studentId} name={name} className='hidden lg:flex' />
21+
<MobileHeader dropdownData={dropdownData} authData={authData} className='flex lg:hidden' />
22+
<DesktopHeader dropdownData={dropdownData} authData={authData} className='hidden lg:flex' />
2323
</>
2424
)
2525
}
2626

2727
interface HeaderProps {
28-
studentId: Nullable<string>
29-
name: Nullable<string>
28+
dropdownData: {
29+
refs: React.RefObject<HTMLDivElement>[]
30+
isOpen: boolean
31+
toggleDropdown: () => void
32+
}
33+
authData: AuthState
3034
className?: string
3135
}
3236
export default Header
3337

34-
const DesktopHeader = ({ studentId, name, className }: HeaderProps) => {
38+
const DesktopHeader = ({ dropdownData, authData, className }: HeaderProps) => {
39+
const {
40+
refs: [buttonRef, dropdownRef],
41+
toggleDropdown,
42+
} = dropdownData
43+
const { studentId, name } = authData
44+
3545
let isLogin: boolean = false
3646
if (studentId && name) {
3747
isLogin = true
3848
}
49+
3950
const linkStyle = 'flex items-center justify-center text-lg font-bold hover:border-b hover:border-black hover:border-solid'
4051
return (
4152
<div
4253
className={cn(
43-
'relative z-10 mt-8 flex h-16 w-[90%] max-w-[1800px] items-center justify-between gap-14 rounded-full bg-swWhite px-12 py-7',
54+
'relative z-50 mt-8 flex h-16 w-[90%] max-w-[1800px] items-center justify-between gap-14 rounded-full bg-swWhite px-12 py-7',
4455
className,
4556
)}
4657
>
@@ -59,95 +70,158 @@ const DesktopHeader = ({ studentId, name, className }: HeaderProps) => {
5970
이용 수칙
6071
</Link>
6172
</div>
62-
<div className='flex cursor-pointer items-center justify-center gap-8 px-3 py-1 text-lg font-bold'>
73+
<div className='flex h-full cursor-pointer items-center justify-center gap-8 px-3 py-1 text-lg font-bold'>
6374
<LucideIcon name='Bell' size={20} />
6475
{!isLogin ? (
6576
<Link href={ROUTES.AUTH.LOGIN.url}>로그인</Link>
6677
) : (
67-
<span>
68-
{studentId} / {name}
69-
</span>
78+
<>
79+
<div onClick={toggleDropdown} ref={buttonRef} className='flex h-full items-center justify-center'>
80+
{studentId} / {name}
81+
</div>
82+
</>
7083
)}
7184
</div>
85+
<HeaderToggleMenu
86+
authData={authData}
87+
dropdownData={dropdownData}
88+
className='absolute right-0 top-0 mr-3 mt-20 flex h-fit w-1/2 max-w-56 flex-col items-center justify-start self-end rounded-xl bg-swWhite'
89+
/>
7290
</div>
7391
)
7492
}
7593

76-
const MobileHeader = ({ studentId, name, className }: HeaderProps) => {
94+
const MobileHeader = ({ dropdownData, authData, className }: HeaderProps) => {
7795
const {
78-
refs: [buttonRef, dropdownRef],
79-
isOpen: isOpenMenu,
96+
isOpen,
8097
toggleDropdown,
81-
} = useDropdown()
98+
refs: [buttonRef, dropdownRef],
99+
} = dropdownData
100+
const { studentId, name } = authData
101+
82102
let isLogin: boolean = false
83103
if (studentId && name) {
84104
isLogin = true
85105
}
106+
86107
return (
87-
<>
88-
<div className={cn('fixed z-50 flex h-24 w-screen items-center justify-between bg-swWhite px-8 py-7', className)}>
89-
<Link href={ROUTES.MAIN.url}>
90-
<Logo text='SoKK' className='text-4xl' />
91-
</Link>
92-
{/* Todo: 버거 메뉴 만들기 */}
93-
<LucideIcon name='Menu' size={30} className='cursor-pointer' onClick={toggleDropdown} ref={buttonRef} />
108+
<div className={cn('absolute z-50 flex h-24 w-screen items-center justify-between bg-swWhite px-8 py-7', className)}>
109+
<Link href={ROUTES.MAIN.url}>
110+
<Logo text='SoKK' className='text-4xl' />
111+
</Link>
112+
{/* Todo: 버거 메뉴 만들기 */}
113+
<div
114+
ref={buttonRef}
115+
onClick={e => {
116+
toggleDropdown()
117+
}}
118+
>
119+
<LucideIcon name='Menu' size={30} className='cursor-pointer' />
94120
</div>
95-
{isOpenMenu && <Backdrop className='absolute z-40 mt-24 h-[calc(100%-6rem)] w-screen' />}
121+
{/* {isOpen && <Backdrop className='fixed top-0 z-40 h-screen w-screen' />} */}
122+
<HeaderToggleMenu
123+
authData={authData}
124+
dropdownData={dropdownData}
125+
className='absolute right-0 top-0 z-50 mr-3 mt-28 flex h-fit w-1/2 max-w-56 flex-col items-center justify-start self-end rounded-lg bg-swWhite'
126+
/>
127+
</div>
128+
)
129+
}
96130

97-
<ToggleWrapper
98-
isOpen={isOpenMenu}
99-
ref={dropdownRef}
100-
className='absolute z-40 mr-3 mt-28 flex h-fit w-1/2 flex-col items-center justify-start self-end rounded-lg bg-swWhite'
101-
>
102-
<div className='relative flex w-4/5 flex-col items-center justify-center gap-4 border-b border-solid border-swGray py-8'>
103-
<LucideIcon name='CircleUserRound' size={100} strokeWidth={0.5} />
104-
{isLogin ? (
105-
<>
106-
<p className='mt-4 text-center text-xl font-medium'>{name}</p>
107-
<p className='text-center text-swGrayDark'>{studentId}</p>
108-
</>
109-
) : (
110-
<Link className='font-s relative h-12 w-4/5' href={ROUTES.AUTH.LOGIN.url} onClick={toggleDropdown}>
111-
<Button variant='swLightGreen' className='h-full w-full'>
112-
로그인
113-
</Button>
114-
</Link>
115-
)}
116-
</div>
117-
118-
<div className='flex w-4/5 flex-col items-center justify-start gap-6 border-b border-solid border-swGray py-8 text-lg font-semibold'>
119-
<Link href={ROUTES.SEAT.RESERVE(-1).url} onClick={toggleDropdown}>
120-
좌석 배정
121-
</Link>
122-
<Link href={ROUTES.SEAT.QR.STEP2.url} className='flex items-center justify-center gap-2' onClick={toggleDropdown}>
123-
<LucideIcon name='ScanLine' size={24} />
124-
QR 좌석 배정
125-
</Link>
126-
</div>
131+
interface HeaderToggleMenuProps {
132+
dropdownData: {
133+
refs: React.RefObject<HTMLDivElement>[]
134+
isOpen: boolean
135+
toggleDropdown: () => void
136+
}
137+
authData: AuthState
138+
className: string
139+
}
140+
const HeaderToggleMenu = ({ dropdownData, authData, className }: HeaderToggleMenuProps) => {
141+
// const router = useRouter()
142+
const {
143+
isOpen,
144+
refs: [_, dropdownRef],
145+
toggleDropdown,
146+
} = dropdownData
147+
const { studentId, name } = authData
127148

128-
<div className='flex w-4/5 flex-col items-center justify-start gap-6 border-b border-solid border-swGray py-8 text-lg font-semibold'>
129-
<Link href={ROUTES.ROOM.RESERVE.STEP1.url} onClick={toggleDropdown}>
130-
스터디룸 예약
131-
</Link>
132-
<Link href={ROUTES.ROOM.HISTORY.url} onClick={toggleDropdown}>
133-
스터디룸 예약 내역
134-
</Link>
135-
</div>
149+
let isLogin: boolean = false
150+
if (studentId && name) {
151+
isLogin = true
152+
}
136153

137-
<div className='flex w-4/5 flex-col items-center justify-start gap-6 border-b border-solid border-swGray py-8 text-lg font-normal text-swGrayDark'>
138-
{/* TODO: 아래 3가지 Route 추가하기*/}
139-
<Link href={ROUTES.ETC.LOUNGE_RULES.url} onClick={toggleDropdown} className='w-full text-center'>
140-
이용 수칙
141-
</Link>
142-
<InstallLink className='w-full text-center'>앱 다운로드</InstallLink>
143-
<Link href={ROUTES.ETC.PERSONAL_INFO_RULES.url} target='_blank' onClick={toggleDropdown} className='w-full text-center'>
144-
개인정보 처리방침
145-
</Link>
146-
<Link href={ROUTES.ETC.SERVICE_CENTER.url} target='_blank' onClick={toggleDropdown} className='w-full text-center'>
147-
문의하기
154+
// Functions
155+
const logoutHandler = () => {
156+
localStorage.removeItem('authData')
157+
toggleDropdown()
158+
location.reload()
159+
}
160+
161+
return (
162+
<ToggleWrapper isOpen={isOpen} className={cn(className)}>
163+
<div className='relative flex w-4/5 flex-col items-center justify-center gap-2 border-b border-solid border-swGray py-8'>
164+
<LucideIcon name='CircleUserRound' size={100} strokeWidth={0.5} />
165+
{isLogin ? (
166+
<>
167+
<p className='mt-4 text-center text-xl font-medium'>{name}</p>
168+
<p className='text-center text-swGrayDark'>{studentId}</p>
169+
</>
170+
) : (
171+
<Link className='font-s relative h-12 w-4/5' href={ROUTES.AUTH.LOGIN.url} onClick={toggleDropdown}>
172+
<Button variant='swLightGreen' className='h-full w-full'>
173+
로그인
174+
</Button>
148175
</Link>
149-
</div>
150-
</ToggleWrapper>
151-
</>
176+
)}
177+
</div>
178+
179+
<div className='flex w-4/5 flex-col items-center justify-start gap-6 border-b border-solid border-swGray py-8 text-lg font-semibold'>
180+
<Link href={ROUTES.SEAT.RESERVE(-1).url} onClick={toggleDropdown}>
181+
좌석 배정
182+
</Link>
183+
<Link href={ROUTES.SEAT.QR.STEP2.url} className='flex items-center justify-center gap-2' onClick={toggleDropdown}>
184+
<LucideIcon name='ScanLine' size={24} />
185+
QR 좌석 배정
186+
</Link>
187+
</div>
188+
189+
<div className='flex w-4/5 flex-col items-center justify-start gap-6 border-b border-solid border-swGray py-8 text-lg font-semibold'>
190+
<Link href={ROUTES.ROOM.RESERVE.STEP1.url} onClick={toggleDropdown}>
191+
스터디룸 예약
192+
</Link>
193+
<Link href={ROUTES.ROOM.HISTORY.url} onClick={toggleDropdown}>
194+
스터디룸 예약 내역
195+
</Link>
196+
</div>
197+
198+
<div className='flex w-4/5 flex-col items-center justify-start gap-6 py-8 text-lg font-normal text-swGrayDark'>
199+
<Link href={ROUTES.ETC.LOUNGE_RULES.url} onClick={toggleDropdown} className='w-full text-center hover:text-swBlack'>
200+
이용 수칙
201+
</Link>
202+
<InstallLink className='w-full text-center hover:text-swBlack'>앱 다운로드</InstallLink>
203+
<Link
204+
href={ROUTES.ETC.PERSONAL_INFO_RULES.url}
205+
target='_blank'
206+
onClick={toggleDropdown}
207+
className='w-full text-center hover:text-swBlack'
208+
>
209+
개인정보 처리방침
210+
</Link>
211+
<Link
212+
href={ROUTES.ETC.SERVICE_CENTER.url}
213+
target='_blank'
214+
onClick={toggleDropdown}
215+
className='w-full text-center hover:text-swBlack'
216+
>
217+
문의하기
218+
</Link>
219+
{isLogin && (
220+
<div className='flex w-full cursor-pointer items-center justify-center text-center hover:text-swBlack' onClick={logoutHandler}>
221+
로그아웃
222+
</div>
223+
)}
224+
</div>
225+
</ToggleWrapper>
152226
)
153227
}

src/lib/context/authContext.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import { create } from 'zustand'
33

44
// Zustand 상태 정의
5-
interface AuthState {
5+
export interface AuthState {
66
studentId: string | null
77
name: string | null
88
setAuthData: (studentId: string, studentName: string) => void

0 commit comments

Comments
 (0)