From afd9b64af2b5587b8350853f4a3e2927c340418c Mon Sep 17 00:00:00 2001 From: jaemin Date: Fri, 8 May 2026 17:32:22 +0900 Subject: [PATCH 1/3] =?UTF-8?q?fix(admin):=20notification=20=EB=B0=9C?= =?UTF-8?q?=EC=86=A1=20=EC=97=90=EB=9F=AC=20=EB=AA=A8=EB=8B=AC=20=ED=91=9C?= =?UTF-8?q?=EC=8B=9C=20=EB=B0=8F=20=EA=B2=B0=EA=B3=BC=20=ED=9B=84=20?= =?UTF-8?q?=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EA=B0=B1=EC=8B=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - /api/admin/notification/send 400 응답의 message를 ErrorModalTemplate으로 노출 - 발송 성공/실패 무관하게 알림 리스트 invalidate Co-Authored-By: Claude Opus 4.7 (1M context) --- .../routes/_GNBLayout/notification/index.tsx | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/apps/admin/src/routes/_GNBLayout/notification/index.tsx b/apps/admin/src/routes/_GNBLayout/notification/index.tsx index afafce498..779ca8d42 100644 --- a/apps/admin/src/routes/_GNBLayout/notification/index.tsx +++ b/apps/admin/src/routes/_GNBLayout/notification/index.tsx @@ -1,6 +1,6 @@ import { useState } from 'react'; import { createFileRoute } from '@tanstack/react-router'; -import { Header, Modal, Input, SegmentedControl } from '@components'; +import { Header, Modal, Input, SegmentedControl, ErrorModalTemplate } from '@components'; import { getNotification, postNotification, getStudent } from '@apis'; import { useModal, useSelectedStudent } from '@hooks'; import { useQueryClient } from '@tanstack/react-query'; @@ -122,6 +122,14 @@ function RouteComponent() { closeModal: closeSendModal, } = useModal(); + // Error modal state + const { + isOpen: isErrorModalOpen, + openModal: openErrorModal, + closeModal: closeErrorModal, + } = useModal(); + const [errorMessage, setErrorMessage] = useState(''); + // Form state for sending notification const [sendMode, setSendMode] = useState<'selected' | 'all' | 'search'>('selected'); const [notificationType, setNotificationType] = useState('SYSTEM'); @@ -171,9 +179,13 @@ function RouteComponent() { setNotificationUrl(''); setSelectedStudentIds([]); closeSendModal(); - queryClient.invalidateQueries(); } catch (error) { console.error('Failed to send notification:', error); + const message = (error as { message?: string })?.message || '알림 발송에 실패했습니다.'; + setErrorMessage(message); + openErrorModal(); + } finally { + queryClient.invalidateQueries(); } }; @@ -523,6 +535,15 @@ function RouteComponent() { + + {/* Error Modal */} + + + ); } From c80c06ed6b98502ddf19d65c5a7ea6bc3dc24751 Mon Sep 17 00:00:00 2001 From: sterdsterd Date: Sun, 10 May 2026 05:50:11 +0900 Subject: [PATCH 2/3] fix(admin): scope notification invalidation --- apps/admin/src/hooks/useInvalidate.ts | 16 ++++++++++++++++ .../src/routes/_GNBLayout/notification/index.tsx | 7 +++---- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/apps/admin/src/hooks/useInvalidate.ts b/apps/admin/src/hooks/useInvalidate.ts index 74e8819a3..150d8c55f 100644 --- a/apps/admin/src/hooks/useInvalidate.ts +++ b/apps/admin/src/hooks/useInvalidate.ts @@ -51,6 +51,21 @@ const useInvalidate = () => { }); }, [queryClient]); + const invalidateNotification = useCallback( + (studentId: number) => { + queryClient.invalidateQueries({ + queryKey: $api.queryOptions('get', '/api/admin/notification', { + params: { + query: { + studentId, + }, + }, + }).queryKey, + }); + }, + [queryClient] + ); + const invalidateQna = useCallback( (qnaId?: number) => { const promises: Promise[] = [ @@ -77,6 +92,7 @@ const useInvalidate = () => { invalidateProblemSet, invalidatePublish, invalidateNotice, + invalidateNotification, invalidateQna, }; }; diff --git a/apps/admin/src/routes/_GNBLayout/notification/index.tsx b/apps/admin/src/routes/_GNBLayout/notification/index.tsx index 779ca8d42..a7d6c60d2 100644 --- a/apps/admin/src/routes/_GNBLayout/notification/index.tsx +++ b/apps/admin/src/routes/_GNBLayout/notification/index.tsx @@ -2,8 +2,7 @@ import { useState } from 'react'; import { createFileRoute } from '@tanstack/react-router'; import { Header, Modal, Input, SegmentedControl, ErrorModalTemplate } from '@components'; import { getNotification, postNotification, getStudent } from '@apis'; -import { useModal, useSelectedStudent } from '@hooks'; -import { useQueryClient } from '@tanstack/react-query'; +import { useInvalidate, useModal, useSelectedStudent } from '@hooks'; import { Bell, Send, @@ -113,7 +112,7 @@ export const Route = createFileRoute('/_GNBLayout/notification/')({ function RouteComponent() { const { selectedStudent } = useSelectedStudent(); - const queryClient = useQueryClient(); + const { invalidateNotification } = useInvalidate(); // Send modal state const { @@ -185,7 +184,7 @@ function RouteComponent() { setErrorMessage(message); openErrorModal(); } finally { - queryClient.invalidateQueries(); + invalidateNotification(selectedStudent?.id || 0); } }; From 8b76ade08dcc5163f64a1dac9ec9d9c889579116 Mon Sep 17 00:00:00 2001 From: sterdsterd Date: Sun, 10 May 2026 05:56:58 +0900 Subject: [PATCH 3/3] fix(admin): close notification modal before error --- apps/admin/src/routes/_GNBLayout/notification/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/admin/src/routes/_GNBLayout/notification/index.tsx b/apps/admin/src/routes/_GNBLayout/notification/index.tsx index a7d6c60d2..2b6e9510a 100644 --- a/apps/admin/src/routes/_GNBLayout/notification/index.tsx +++ b/apps/admin/src/routes/_GNBLayout/notification/index.tsx @@ -182,6 +182,7 @@ function RouteComponent() { console.error('Failed to send notification:', error); const message = (error as { message?: string })?.message || '알림 발송에 실패했습니다.'; setErrorMessage(message); + closeSendModal(); openErrorModal(); } finally { invalidateNotification(selectedStudent?.id || 0);