From 4008b5ed60a80922bbbf507f728d521ad6310c52 Mon Sep 17 00:00:00 2001 From: gummmmmy0v0 Date: Mon, 20 Oct 2025 21:55:00 +0900 Subject: [PATCH 01/10] =?UTF-8?q?=F0=9F=90=9Bfix:=20approved=20->=20accept?= =?UTF-8?q?ed=20=EC=88=98=EC=A0=95=20#99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/my-profile/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/my-profile/index.tsx b/src/pages/my-profile/index.tsx index 60b531b..7986733 100644 --- a/src/pages/my-profile/index.tsx +++ b/src/pages/my-profile/index.tsx @@ -43,7 +43,7 @@ export default function MyProfileDetailPage() { return applications.map((app: ApiResponse) => { const a = app.item; const status = - a.status === 'accepted' ? 'approved' : a.status === 'rejected' ? 'rejected' : 'pending'; + a.status === 'accepted' ? 'accepted' : a.status === 'rejected' ? 'rejected' : 'pending'; return { id: a.id, From caa50c670f2f120a3e29778430c6b36e946c05c5 Mon Sep 17 00:00:00 2001 From: gummmmmy0v0 Date: Tue, 21 Oct 2025 00:14:23 +0900 Subject: [PATCH 02/10] =?UTF-8?q?=F0=9F=90=9Bfix:=20=EA=B3=B5=EA=B3=A0=20?= =?UTF-8?q?=EB=93=B1=EB=A1=9D=20=EB=82=A0=EC=A7=9C=20=EC=98=A4=EB=A5=98=20?= =?UTF-8?q?#100?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../shops/[shopId]/notices/register/index.tsx | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/pages/employer/shops/[shopId]/notices/register/index.tsx b/src/pages/employer/shops/[shopId]/notices/register/index.tsx index 0900a43..b2e87ae 100644 --- a/src/pages/employer/shops/[shopId]/notices/register/index.tsx +++ b/src/pages/employer/shops/[shopId]/notices/register/index.tsx @@ -133,12 +133,27 @@ const EmployerNoticeRegisterPage = () => { requiredMark value={time ? { date: time, period: time.getHours() >= 12 ? '오후' : '오전' } : null} onChange={(value: TimeValue | null) => { - if (value && value.date < new Date()) { + if (!value) return; + + // dateInput에서 선택한 날짜 + const selectedDate = date ?? new Date(); + + // timeInput에서 선택한 시간 + const hours = value.date.getHours(); + const minutes = value.date.getMinutes(); + + // date + time 합치기 + const combinedDateTime = new Date(selectedDate); + combinedDateTime.setHours(hours, minutes, 0, 0); + + // 과거 시간인지 체크 + if (combinedDateTime < new Date()) { setPastTimeModal(true); setTime(null); return; } - setTime(value?.date ?? null); + + setTime(combinedDateTime); }} /> From ea84793554b8fa3b1ed275a25a5dbe31428cdd06 Mon Sep 17 00:00:00 2001 From: gummmmmy0v0 Date: Tue, 21 Oct 2025 00:19:13 +0900 Subject: [PATCH 03/10] =?UTF-8?q?=F0=9F=90=9Bfix:=20=EB=82=A0=EC=A7=9C=20#?= =?UTF-8?q?101?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/employer/shops/[shopId]/notices/register/index.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/pages/employer/shops/[shopId]/notices/register/index.tsx b/src/pages/employer/shops/[shopId]/notices/register/index.tsx index b2e87ae..e6326a4 100644 --- a/src/pages/employer/shops/[shopId]/notices/register/index.tsx +++ b/src/pages/employer/shops/[shopId]/notices/register/index.tsx @@ -135,18 +135,14 @@ const EmployerNoticeRegisterPage = () => { onChange={(value: TimeValue | null) => { if (!value) return; - // dateInput에서 선택한 날짜 const selectedDate = date ?? new Date(); - // timeInput에서 선택한 시간 const hours = value.date.getHours(); const minutes = value.date.getMinutes(); - // date + time 합치기 const combinedDateTime = new Date(selectedDate); combinedDateTime.setHours(hours, minutes, 0, 0); - // 과거 시간인지 체크 if (combinedDateTime < new Date()) { setPastTimeModal(true); setTime(null); From 2df1437d908856ff9a59c1263a68e48a64e6d2f7 Mon Sep 17 00:00:00 2001 From: BaeZzi813 Date: Tue, 21 Oct 2025 00:35:47 +0900 Subject: [PATCH 04/10] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refact:=20=ED=86=A0?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EB=A9=94=EC=8B=9C=EC=A7=80=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EC=9D=B4=EB=8F=99=EC=8B=9C=20null=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/context/toastContext.tsx | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/context/toastContext.tsx b/src/context/toastContext.tsx index 02dd415..3e99567 100644 --- a/src/context/toastContext.tsx +++ b/src/context/toastContext.tsx @@ -1,3 +1,4 @@ +import { useRouter } from 'next/router'; import { createContext, ReactNode, useContext, useEffect, useState } from 'react'; import { createPortal } from 'react-dom'; @@ -15,6 +16,7 @@ export const useToast = () => { const ToastProvider = ({ children }: { children: ReactNode }) => { const [message, setMessage] = useState(null); + const router = useRouter(); const showToast = (msg: string) => { setMessage(msg); @@ -30,7 +32,18 @@ const ToastProvider = ({ children }: { children: ReactNode }) => { return () => { clearTimeout(timer); }; - }); + }, [message]); + + useEffect(() => { + const handleRouteChange = () => { + setMessage(null); + }; + + router.events.on('routeChangeStart', handleRouteChange); + return () => { + router.events.off('routeChangeStart', handleRouteChange); + }; + }, [router]); const toastRoot = typeof window !== 'undefined' ? document.getElementById('toast-root') : null; @@ -43,7 +56,7 @@ const ToastProvider = ({ children }: { children: ReactNode }) => { message ? (
{message}
From 8f1ff88bd4f8cb83a18bb39667c16f2fd7e6ade5 Mon Sep 17 00:00:00 2001 From: jeschun Date: Tue, 21 Oct 2025 00:34:31 +0900 Subject: [PATCH 05/10] =?UTF-8?q?=E2=9C=A8=20feat:=20=EC=95=8C=EB=A6=BC=20?= =?UTF-8?q?=EC=9D=BD=EC=9D=8C=20=EC=B2=98=EB=A6=AC=20API=20=EC=97=B0?= =?UTF-8?q?=EA=B2=B0,=20notificationOn=20=ED=99=9C=EC=84=B1=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/layout/header/nav.tsx | 149 +++++++++++++++++---------- src/pages/my-profile/index.tsx | 2 +- 2 files changed, 96 insertions(+), 55 deletions(-) diff --git a/src/components/layout/header/nav.tsx b/src/components/layout/header/nav.tsx index d67102d..597d7ad 100644 --- a/src/components/layout/header/nav.tsx +++ b/src/components/layout/header/nav.tsx @@ -1,3 +1,4 @@ +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 +6,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,43 +23,80 @@ 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]); - - const handleRead = (id: string) => { - setReadIds(prev => { - const next = new Set(prev); - next.add(id); - return next; - }); - }; + }, [applications, role, readIds]); - // role이 초기 undefined일 수 있어 방어 - const currentRole: UserRole = (role ?? 'guest') as UserRole; + // 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 unreadCount = alerts.filter(a => !a.read).length; const bellIcon: 'notificationOn' | 'notificationOff' = open ? '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))); + } + }; + + const currentRole: UserRole = (role ?? 'guest') as UserRole; return ( ); diff --git a/src/pages/my-profile/index.tsx b/src/pages/my-profile/index.tsx index 7986733..b323d44 100644 --- a/src/pages/my-profile/index.tsx +++ b/src/pages/my-profile/index.tsx @@ -187,7 +187,7 @@ export default function MyProfileDetailPage() { title='신청 내역' content='마음에 드는 공고를 찾아 지원해 보세요.' buttonText='공고 보러가기' - href='/notices' + href='/' /> ) : ( From fe29f78119ba5c28f2938b5521e1d566f61918ce Mon Sep 17 00:00:00 2001 From: jeschun Date: Tue, 21 Oct 2025 01:37:17 +0900 Subject: [PATCH 06/10] =?UTF-8?q?=F0=9F=92=84=20style:=20=EC=95=8C?= =?UTF-8?q?=EB=A6=BC=20=EB=B2=84=ED=8A=BC=20=EC=A4=91=EB=B3=B5=20=EC=9B=90?= =?UTF-8?q?=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/layout/header/nav.tsx | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/components/layout/header/nav.tsx b/src/components/layout/header/nav.tsx index 597d7ad..3148d81 100644 --- a/src/components/layout/header/nav.tsx +++ b/src/components/layout/header/nav.tsx @@ -77,9 +77,8 @@ const Nav = () => { }, [apiAlerts, fallbackAlertsForEmployee, readIds]); const unreadCount = alerts.filter(a => !a.read).length; - const bellIcon: 'notificationOn' | 'notificationOff' = open - ? 'notificationOn' - : 'notificationOff'; + const bellIcon: 'notificationOn' | 'notificationOff' = + open || unreadCount > 0 ? 'notificationOn' : 'notificationOff'; const bellColor = open || unreadCount > 0 ? 'bg-red-400' : 'bg-black'; // 알림 읽음 처리(서버 + 로컬 동기화) @@ -136,9 +135,6 @@ const Nav = () => { ariaLabel='알림' className={bellColor} /> - {unreadCount > 0 && ( - - )} {open && ( From 010eaa751d1d304b04369df90ab0d5596f783847 Mon Sep 17 00:00:00 2001 From: jeschun Date: Tue, 21 Oct 2025 01:57:10 +0900 Subject: [PATCH 07/10] =?UTF-8?q?=F0=9F=90=9B=20fix:=20=EC=A7=81=EC=9B=90?= =?UTF-8?q?=EC=97=90=EA=B2=8C=EB=A7=8C=20=EC=95=84=EC=9D=B4=EC=BD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/layout/header/nav.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/layout/header/nav.tsx b/src/components/layout/header/nav.tsx index 3148d81..2a4b60f 100644 --- a/src/components/layout/header/nav.tsx +++ b/src/components/layout/header/nav.tsx @@ -32,7 +32,7 @@ const Nav = () => { // 1) 서버 알림 불러오기 (사장님/알바 공통) useEffect(() => { - if (!isLogin || !user?.id) { + if (!isLogin || !user?.id || role !== 'employer') { setApiAlerts([]); return; } @@ -105,7 +105,7 @@ const Nav = () => { ))} - {isLogin && ( + {isLogin && role === 'employee' && ( <> + )} + + {/* ✅ 사장님(employer)에게만 알림 버튼 숨김 */} + {isLogin && role !== 'employer' && ( +
- {/* 로그인 사용자는 누구나 알림 버튼 노출 (사장님 포함) */} -
- - - {open && ( - setOpen(false)} - /> - )} -
- + {open && ( + setOpen(false)} + /> + )} +
)} ); From 2c9c2fc5152023a5ed272699947a031f9ee6658a Mon Sep 17 00:00:00 2001 From: celine Date: Tue, 21 Oct 2025 03:35:50 +0900 Subject: [PATCH 09/10] =?UTF-8?q?=E2=9C=A8=20feat:=20=EC=A0=95=EB=A0=AC=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20=EC=8B=9C=20=ED=8E=98=EC=9D=B4=EC=A7=80?= =?UTF-8?q?=EB=84=A4=EC=9D=B4=EC=85=98=201=EB=B2=88=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99=20#97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/features/noticeList/noticeListSection.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/features/noticeList/noticeListSection.tsx b/src/components/features/noticeList/noticeListSection.tsx index 6d18a56..5bc23c4 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 })} /> Date: Tue, 21 Oct 2025 03:37:12 +0900 Subject: [PATCH 10/10] =?UTF-8?q?=F0=9F=92=84=20design:=20=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EC=95=84=EC=9B=83=20=EC=8B=9C=ED=94=84=ED=8A=B8=20?= =?UTF-8?q?=EB=B0=A9=EC=A7=80=20#97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../features/noticeList/noticeListSection.tsx | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/components/features/noticeList/noticeListSection.tsx b/src/components/features/noticeList/noticeListSection.tsx index 5bc23c4..8fd5a62 100644 --- a/src/components/features/noticeList/noticeListSection.tsx +++ b/src/components/features/noticeList/noticeListSection.tsx @@ -40,15 +40,16 @@ const NoticeListSection = ({ q, initialFilters }: NoticeListSectionProps) => { isInitialized={isInitialized} reset={reset} /> - {!isLoading && ( - fetchNotices({ offset: next })} - className='mt-8 tablet:mt-10' - /> - )} +
+ {!isLoading && ( + fetchNotices({ offset: next })} + /> + )} +
); };