1- import React , { useState , useEffect } from "react" ;
1+ import React , { useState , useEffect , useRef } from "react" ;
22import { useRouter } from "next/router" ;
33import SkeletonUser from "@/shared/skeletonUser" ;
44import Image from "next/image" ;
@@ -7,7 +7,8 @@ import { getMembers } from "@/api/members";
77import { getUserInfo } from "@/api/user" ;
88import { getDashboardById } from "@/api/dashboards" ;
99import { TEAM_ID } from "@/constants/team" ;
10- import RandomProfile from "@/components/table/member/RandomProfile" ;
10+ import { MemberAvatars , UserAvatars } from "@/components/gnb/Avatars" ;
11+ import UserMenu from "@/components/gnb/UserMenu" ;
1112import InviteDashboard from "@/components/modal/InviteDashboard" ;
1213
1314interface HeaderDashboardProps {
@@ -16,24 +17,22 @@ interface HeaderDashboardProps {
1617 dashboardId ?: string | string [ ] ;
1718}
1819
19- const MAX_VISIBLE_MEMBERS = 4 ;
20- const memberIconWrapperClass =
21- "relative flex items-center justify-center w-[34px] h-[34px] md:w-[38px] md:h-[38px] rounded-full border-[2px] border-white" ;
22-
2320const HeaderDashboard : React . FC < HeaderDashboardProps > = ( {
2421 variant,
2522 dashboardId,
2623} ) => {
2724 const router = useRouter ( ) ;
25+ const [ isLoading , setIsLoading ] = useState ( true ) ;
2826 const [ user , setUser ] = useState < UserType | null > ( null ) ;
2927 const [ members , setMembers ] = useState < MemberType [ ] > ( [ ] ) ;
28+ const [ isMenuOpen , setIsMenuOpen ] = useState ( false ) ;
29+ const [ errorMessage , setErrorMessage ] = useState ( "" ) ;
3030 const [ dashboard , setDashboard ] = useState < {
3131 title : string ;
3232 createdByMe : boolean ;
3333 } | null > ( null ) ;
34- const [ errorMessage , setErrorMessage ] = useState ( "" ) ;
35- const [ isLoading , setIsLoading ] = useState ( true ) ;
3634
35+ /*초대하기 모달 상태 관리*/
3736 const [ isModalOpen , setIsModalOpen ] = useState ( false ) ;
3837 const openInviteModal = ( ) => {
3938 setIsModalOpen ( true ) ;
@@ -42,23 +41,6 @@ const HeaderDashboard: React.FC<HeaderDashboardProps> = ({
4241 setIsModalOpen ( false ) ;
4342 } ;
4443
45- /*유저 정보 api 호출*/
46- useEffect ( ( ) => {
47- const fetchUser = async ( ) => {
48- try {
49- const user = await getUserInfo ( { teamId : TEAM_ID } ) ;
50- setUser ( user ) ;
51- } catch ( error ) {
52- console . error ( "유저 정보 불러오기 실패" , error ) ;
53- setErrorMessage ( "유저 정보를 불러오지 못했습니다." ) ;
54- } finally {
55- setIsLoading ( false ) ;
56- }
57- } ;
58-
59- fetchUser ( ) ;
60- } , [ ] ) ;
61-
6244 /*멤버 목록 api 호출*/
6345 useEffect ( ( ) => {
6446 const fetchMembers = async ( ) => {
@@ -77,7 +59,24 @@ const HeaderDashboard: React.FC<HeaderDashboardProps> = ({
7759 }
7860 } , [ dashboardId , variant ] ) ;
7961
80- /*대시보드 이름 api 호출*/
62+ /*유저 정보 api 호출*/
63+ useEffect ( ( ) => {
64+ const fetchUser = async ( ) => {
65+ try {
66+ const user = await getUserInfo ( { teamId : TEAM_ID } ) ;
67+ setUser ( user ) ;
68+ } catch ( error ) {
69+ console . error ( "유저 정보 불러오기 실패" , error ) ;
70+ setErrorMessage ( "유저 정보를 불러오지 못했습니다." ) ;
71+ } finally {
72+ setIsLoading ( false ) ;
73+ }
74+ } ;
75+
76+ fetchUser ( ) ;
77+ } , [ ] ) ;
78+
79+ /*대시보드 api 호출*/
8180 useEffect ( ( ) => {
8281 const fetchDashboard = async ( ) => {
8382 if ( variant === "dashboard" && dashboardId ) {
@@ -129,14 +128,14 @@ const HeaderDashboard: React.FC<HeaderDashboardProps> = ({
129128 </ div >
130129
131130 < div className = "flex items-center" >
132- { /*관리 / 초대하기 버튼*/ }
131+ { /*관리 버튼*/ }
133132 < div className = "flex gap-[6px] md:gap-[16px] pr-[40px]" >
134133 < button
135134 onClick = { ( ) => {
136135 if ( dashboardId ) {
137136 router . push ( `/dashboard/${ dashboardId } /edit` ) ;
138137 } else {
139- router . push ( "/mydashboard " ) ;
138+ router . push ( "/mypage " ) ;
140139 }
141140 } }
142141 className = "flex items-center justify-center w-[49px] h-[30px] md:w-[85px] md:h-[36px] lg:w-[88px] lg:h-[40px] rounded-[8px] border border-[#D9D9D9] gap-[10px] cursor-pointer"
@@ -150,7 +149,7 @@ const HeaderDashboard: React.FC<HeaderDashboardProps> = ({
150149 />
151150 < span className = "text-sm md:text-base text-gray1" > 관리</ span >
152151 </ button >
153-
152+ { /*초대하기 버튼*/ }
154153 < button
155154 onClick = { openInviteModal }
156155 className = "flex items-center justify-center w-[73px] h-[30px] md:w-[109px] md:h-[36px] lg:w-[116px] lg:h-[40px] rounded-[8px] border border-[#D9D9D9] gap-[10px] cursor-pointer"
@@ -167,72 +166,39 @@ const HeaderDashboard: React.FC<HeaderDashboardProps> = ({
167166 { isModalOpen && < InviteDashboard onClose = { closeInviteModal } /> }
168167 </ div >
169168
170- { /*멤버 목록, 나머지 멤버 수 +n 아이콘으로 표시*/ }
171-
172- { variant !== "mydashboard" && (
173- < div className = "flex -space-x-3" >
174- { isLoading ? (
175- < SkeletonUser />
176- ) : (
177- < >
178- { members . slice ( 0 , MAX_VISIBLE_MEMBERS ) . map ( ( member ) => (
179- < div className = { memberIconWrapperClass } key = { member . id } >
180- { member . profileImageUrl ? (
181- < Image
182- src = { member . profileImageUrl }
183- alt = { member . nickname }
184- fill
185- className = "object-cover"
186- />
187- ) : (
188- < RandomProfile name = { member . nickname } />
189- ) }
190- </ div >
191- ) ) }
192- { members . length > MAX_VISIBLE_MEMBERS && (
193- < div
194- className = { `${ memberIconWrapperClass } bg-[#F4D7DA] font-16m text-[#D25B68]` }
195- >
196- +{ members . length - MAX_VISIBLE_MEMBERS }
197- </ div >
198- ) }
199- </ >
200- ) }
201- </ div >
202- ) }
203-
204- { /*구분선*/ }
205- < div className = "pl-[15px] pr-[20px] md:pl-[25px] md:pr-[30px] lg:pl-[30px] lg:pr-[35px]" >
206- < div className = "flex items-center justify-center h-[34px] md:h-[38px] w-[1px] bg-[var(--color-gray3)]" > </ div >
207- </ div >
208-
209- { /*유저 정보*/ }
210- { isLoading ? (
211- < SkeletonUser />
212- ) : (
213- user && (
214- < div
215- onClick = { ( ) => router . push ( "/mypage" ) }
216- className = "flex items-center pr-[10px] md:pr-[30px] lg:pr-[80px] gap-[12px] cursor-default"
217- >
218- < div className = "relative w-[34px] h-[34px] md:w-[38px] md:h-[38px] rounded-full" >
219- { user . profileImageUrl ? (
220- < Image
221- src = { user . profileImageUrl }
222- alt = "유저 프로필 아이콘"
223- fill
224- className = "object-cover"
225- />
226- ) : (
227- < RandomProfile name = { user . nickname } />
228- ) }
169+ { /*멤버 목록*/ }
170+ < MemberAvatars
171+ members = { members }
172+ isLoading = { isLoading }
173+ variant = { variant }
174+ />
175+
176+ { /*드롭다운 메뉴 너비 지정 목적의 섹션 구분용 div*/ }
177+ < div className = "relative flex items-center h-[60px] md:h-[70px] pr-[10px] md:pr-[30px] lg:pr-[80px]" >
178+ { /*구분선*/ }
179+ < div className = "h-[34px] md:h-[38px] w-[1px] bg-[var(--color-gray3)]" />
180+
181+ { /*유저 정보*/ }
182+ { isLoading ? (
183+ < SkeletonUser />
184+ ) : (
185+ user && (
186+ < div
187+ onClick = { ( ) => setIsMenuOpen ( ( prev ) => ! prev ) }
188+ className = "flex items-center gap-[12px] pl-[20px] md:pl-[30px] lg:pl-[35px] cursor-pointer"
189+ >
190+ < UserAvatars user = { user } />
191+ < span className = "hidden md:block text-black3 md:text-base md:font-medium" >
192+ { user . nickname }
193+ </ span >
194+ < UserMenu
195+ isMenuOpen = { isMenuOpen }
196+ setIsMenuOpen = { setIsMenuOpen }
197+ />
229198 </ div >
230- < span className = "hidden md:block text-black3 md:text-base md:font-medium" >
231- { user . nickname }
232- </ span >
233- </ div >
234- )
235- ) }
199+ )
200+ ) }
201+ </ div >
236202 </ div >
237203 </ div >
238204 </ header >
0 commit comments