diff --git a/src/components/features/noticeList/noticeListSection.tsx b/src/components/features/noticeList/noticeListSection.tsx index 6d18a56..8fd5a62 100644 --- a/src/components/features/noticeList/noticeListSection.tsx +++ b/src/components/features/noticeList/noticeListSection.tsx @@ -28,8 +28,8 @@ const NoticeListSection = ({ q, initialFilters }: NoticeListSectionProps) => { fetchNotices({ sort })} - onFilterSubmit={filter => fetchNotices(filter)} + onSortChange={sort => fetchNotices({ sort, offset: 0 })} + onFilterSubmit={filter => fetchNotices({ ...filter, offset: 0 })} /> { isInitialized={isInitialized} reset={reset} /> - {!isLoading && ( - fetchNotices({ offset: next })} - className='mt-8 tablet:mt-10' - /> - )} +
+ {!isLoading && ( + fetchNotices({ offset: next })} + /> + )} +
); }; diff --git a/src/components/layout/header/nav.tsx b/src/components/layout/header/nav.tsx index d67102d..12bfe39 100644 --- a/src/components/layout/header/nav.tsx +++ b/src/components/layout/header/nav.tsx @@ -1,3 +1,5 @@ +// src/components/layout/header/nav.tsx (예시 경로) +import { getUserAlerts, markAlertRead } from '@/api/alerts'; import { Icon } from '@/components/ui'; import Notification, { type Alert } from '@/components/ui/modal/notification/Notification'; import { useUserApplications } from '@/context/userApplicationsProvider'; @@ -5,7 +7,7 @@ import useAuth from '@/hooks/useAuth'; import { cn } from '@/lib/utils/cn'; import { UserRole } from '@/types/user'; import Link from 'next/link'; -import { useMemo, useState } from 'react'; +import { useEffect, useMemo, useState } from 'react'; interface NavItems { href: string; @@ -22,44 +24,79 @@ const NAV_ITEMS: Record = { }; const Nav = () => { - const { role, isLogin, logout } = useAuth(); + const { role, isLogin, logout, user } = useAuth(); const { applications } = useUserApplications(); const [open, setOpen] = useState(false); - // 읽음 처리한 알림 ID들 (간단 로컬 상태) const [readIds, setReadIds] = useState>(new Set()); + const [apiAlerts, setApiAlerts] = useState([]); - // 알바님 알림: 승인/거절만 표시 - const alerts: Alert[] = useMemo(() => { + // 1) 서버 알림 불러오기 (사장님/알바 공통) + useEffect(() => { + if (!isLogin || !user?.id) { + setApiAlerts([]); + return; + } + (async () => { + try { + const res = await getUserAlerts(user.id, { offset: 0, limit: 50 }); + const mapped: Alert[] = (res.items ?? []).map(({ item }) => ({ + id: item.id, + createdAt: item.createdAt, + result: item.result, + read: item.read, + shop: { item: item.shop.item }, + notice: { item: item.notice.item }, + })); + setApiAlerts(mapped); + } catch { + setApiAlerts([]); // 실패해도 UI는 동작(직원 fallback) + } + })(); + }, [isLogin, user?.id]); + + // 2) (직원 전용) 지원내역 기반 fallback 알림 + const fallbackAlertsForEmployee: Alert[] = useMemo(() => { + if (role !== 'employee') return []; return applications .filter(a => a.item.status !== 'pending') .map(a => ({ id: a.item.id, createdAt: a.item.createdAt ?? new Date().toISOString(), result: a.item.status === 'accepted' ? 'accepted' : 'rejected', - // ▶ 읽음: 사용자가 메시지를 클릭했을 때만 true read: readIds.has(a.item.id), shop: { item: a.item.shop.item, href: `/shops/${a.item.shop.item.id}` }, notice: { item: a.item.notice.item, href: `/notices/${a.item.notice.item.id}` }, })); - }, [applications, readIds]); + }, [applications, role, readIds]); + + // 3) 실제 표시할 알림: 서버 결과가 있으면 우선, 없으면(특히 직원) fallback + const alerts: Alert[] = useMemo(() => { + const base = apiAlerts.length > 0 ? apiAlerts : fallbackAlertsForEmployee; + return base.map(a => (readIds.has(a.id) ? { ...a, read: true } : a)); + }, [apiAlerts, fallbackAlertsForEmployee, readIds]); - const handleRead = (id: string) => { - setReadIds(prev => { - const next = new Set(prev); - next.add(id); - return next; - }); + const unreadCount = alerts.filter(a => !a.read).length; + const bellIcon: 'notificationOn' | 'notificationOff' = + open || unreadCount > 0 ? 'notificationOn' : 'notificationOff'; + const bellColor = open || unreadCount > 0 ? 'bg-red-400' : 'bg-black'; + + // 알림 읽음 처리(서버 + 로컬 동기화) + const handleRead = async (id: string) => { + try { + if (user?.id) await markAlertRead(user.id, id); + } finally { + setReadIds(prev => { + const next = new Set(prev); + next.add(id); + return next; + }); + setApiAlerts(prev => prev.map(a => (a.id === id ? { ...a, read: true } : a))); + } }; - // role이 초기 undefined일 수 있어 방어 const currentRole: UserRole = (role ?? 'guest') as UserRole; - // 아이콘은 "패널 열림 상태"로만 토글 - const bellIcon: 'notificationOn' | 'notificationOff' = open - ? 'notificationOn' - : 'notificationOff'; - return (