diff --git a/src/components/layout/header/nav.tsx b/src/components/layout/header/nav.tsx index 49f0ea2..772c815 100644 --- a/src/components/layout/header/nav.tsx +++ b/src/components/layout/header/nav.tsx @@ -24,6 +24,7 @@ const NAV_ITEMS: Record = { const Nav = () => { const { role, isLogin, logout } = useAuth(); const { applications } = useUserApplications(); + const [open, setOpen] = useState(false); // 읽음 처리한 알림 ID들 (간단 로컬 상태) const [readIds, setReadIds] = useState>(new Set()); @@ -43,10 +44,6 @@ const Nav = () => { })); }, [applications, readIds]); - const unreadCount = alerts.filter(a => !a.read).length; - const bellIcon: 'notificationOn' | 'notificationOff' = - unreadCount > 0 ? 'notificationOn' : 'notificationOff'; - const handleRead = (id: string) => { setReadIds(prev => { const next = new Set(prev); @@ -55,9 +52,17 @@ const Nav = () => { }); }; + // ✅ role이 초기 undefined일 수 있어 방어 + const currentRole: UserRole = (role ?? 'guest') as UserRole; + + // ✅ 아이콘은 "패널 열림 상태"로만 토글 + const bellIcon: 'notificationOn' | 'notificationOff' = open + ? 'notificationOn' + : 'notificationOff'; + return ( ); }; + export default Nav; diff --git a/src/pages/my-profile/index.tsx b/src/pages/my-profile/index.tsx index 4c5ef50..b28a440 100644 --- a/src/pages/my-profile/index.tsx +++ b/src/pages/my-profile/index.tsx @@ -1,3 +1,4 @@ +// src/pages/my-profile/index.tsx import Image from 'next/image'; import Link from 'next/link'; import { useEffect, useMemo, useState } from 'react'; @@ -46,124 +47,158 @@ export default function MyProfileDetailPage() { startsAt: a.notice.item.startsAt, workhour: a.notice.item.workhour, status, - // employee 표에서는 미사용 — 타입만 충족 bio: '', phone: '', }; }); }, [applications]); - const pagedRows = useMemo(() => rows.slice(offset, offset + limit), [rows, offset]); + // 로딩 중에도 마지막 성공 데이터를 유지 (화면 흔들림 방지) + const [stableRows, setStableRows] = useState([]); + const [stableTotal, setStableTotal] = useState(0); + useEffect(() => { + if (rows.length > 0) { + setStableRows(rows); + setStableTotal(applications.length); + } + }, [rows, applications.length]); - // rows 변화 시 첫 페이지로 리셋 (페이지네이션 UX 보강) + // rows 변화 시 첫 페이지로 리셋 useEffect(() => { setOffset(0); }, [rows.length]); - return ( -
-
-

내 프로필

+ const currentRows = rows.length > 0 ? rows : stableRows; + const currentTotal = rows.length > 0 ? applications.length : stableTotal; + + const pagedRows = useMemo(() => currentRows.slice(offset, offset + limit), [currentRows, offset]); - {/* 프로필이 없으면 등록 프레임 */} + return ( +
+ {/* 공통 컨테이너: Table과 좌측선/폭 동일 */} +
{profileIsEmpty ? ( - + <> +

내 프로필

+
+ +
+ ) : ( - // 프로필 카드(피그마 스타일) -
-
-
-

이름

-

- {user?.name || '—'} -

- - {/* 연락처 */} -
- 전화 - {user?.phone || '—'} -
- - {/* 선호 지역 */} -
- 지도 - 선호 지역: {(user?.address as string) || '—'} -
+ // ✅ 데스크탑에서 제목과 카드가 같은 flex 라인에 놓이도록 +
+

+ 내 프로필 +

- {/* 소개 */} - {user?.bio && ( -

- {user.bio} +

+
+
+

이름

+

+ {user?.name || '—'}

- )} -
- {/* 우상단 편집 버튼 */} -
- + {/* 연락처 */} +
+ 전화 + {user?.phone || '—'} +
+ + {/* 선호 지역 */} +
+ 지도 + + 선호 지역: {(user?.address as string) || '—'} + +
+ + {/* 소개 */} + {user?.bio && ( +

+ {user.bio} +

+ )} +
+ + {/* 우상단 편집 버튼 */} +
+ +
-
-
+ +
)}
- {/* 신청 내역 — 프로필 있고 로그인 상태일 때만 */} + {/* 하단: 신청 내역 — 프로필 있고 로그인 상태일 때만 */} {!profileIsEmpty && isLogin && ( -
- {isLoading ? ( -
불러오는 중…
- ) : rows.length === 0 ? ( -
+
+
+ {isLoading && currentRows.length === 0 ? ( + <> +
+

신청 내역

+
+
+
+ {[...Array(5)].map((_, i) => ( +
+ ))} +
+
+ + ) : currentRows.length === 0 ? ( -
- ) : ( -
- - - )} + ) : ( +
+
+ + )} + )} diff --git a/src/pages/my-profile/register.tsx b/src/pages/my-profile/register.tsx index 3a68053..4e5db9a 100644 --- a/src/pages/my-profile/register.tsx +++ b/src/pages/my-profile/register.tsx @@ -9,12 +9,11 @@ import { useEffect, useState } from 'react'; import { ADDRESS_CODE, type AddressCode } from '@/constants/dropdown'; -/** 폼 타입 */ type ProfileForm = { name: string; phone: string; region: AddressCode | ''; - bio: string; + bio: string; // bio는 선택 }; export default function MyProfileRegisterPage() { @@ -33,15 +32,13 @@ export default function MyProfileRegisterPage() { const [regionErrorMessage, setRegionErrorMessage] = useState(null); const [isSubmitting, setIsSubmitting] = useState(false); - const [isDoneOpen, setIsDoneOpen] = useState(false); // 완료 모달 - const [isCancelOpen, setIsCancelOpen] = useState(false); // 취소 확인 모달 + const [isDoneOpen, setIsDoneOpen] = useState(false); + const [isCancelOpen, setIsCancelOpen] = useState(false); - // 로그인 가드 useEffect(() => { if (!isLogin) router.replace('/login'); }, [isLogin, router]); - // 기존 값 프리필(컨텍스트 user 사용) useEffect(() => { if (!isLogin || !user) return; setFormState({ @@ -76,12 +73,11 @@ export default function MyProfileRegisterPage() { setIsSubmitting(true); try { - // 서버 반영 + 컨텍스트 동기화 await updateUser({ name: formState.name.trim(), phone: formState.phone.trim(), address: formState.region, - bio: formState.bio, + bio: formState.bio, // 선택값: 빈 문자열 가능 }); setIsDoneOpen(true); } finally { @@ -91,7 +87,7 @@ export default function MyProfileRegisterPage() { return (
- {/* 우상단 닫기(X) 버튼 */} + {/* 닫기 */} +

내 프로필

{ @@ -126,13 +123,14 @@ export default function MyProfileRegisterPage() { required error={nameErrorMessage ?? undefined} /> + {/* ❌ 중복 메시지 제거: 아래 커스텀

삭제 */} {/* 연락처 */}

{ @@ -149,12 +147,13 @@ export default function MyProfileRegisterPage() { required error={phoneErrorMessage ?? undefined} /> + {/* ❌ 중복 메시지 제거: 아래 커스텀

삭제 */}

{/* 선호 지역 */}
name='region' @@ -170,12 +169,14 @@ export default function MyProfileRegisterPage() { className='w-full' /> {regionErrorMessage && ( -

{regionErrorMessage}

+

+ {regionErrorMessage} +

)}
- {/* 소개(선택) */} + {/* 소개(선택) → 소개 */}
- {/* 등록 버튼 */}