From d8a681637215ce4eaec413d40efb463cfc69f439 Mon Sep 17 00:00:00 2001 From: Yun Seongmin Date: Sat, 23 May 2026 20:56:18 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EC=9D=B8=EB=A9=94=EB=AA=A8=EB=A6=AC=20?= =?UTF-8?q?Set=EC=9C=BC=EB=A1=9C=20QR=20=EC=A4=91=EB=B3=B5=20=EC=B6=9C?= =?UTF-8?q?=EC=84=9D=20=EB=B0=A9=EC=A7=80=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/QRScanner.tsx | 21 ++++++++++++++------- src/utils/alert.tsx | 6 ++++++ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/components/QRScanner.tsx b/src/components/QRScanner.tsx index f24b86b..806952d 100644 --- a/src/components/QRScanner.tsx +++ b/src/components/QRScanner.tsx @@ -6,7 +6,7 @@ import { PostMemberActivities } from "@/api/activity/post-member-activities"; import { Button } from "@/components/ui/button"; import { useSessionKeepAlive } from "@/hooks/useSessionKeepAlive"; import { resolveAdminErrorMessage } from "@/lib/errors/admin-error"; -import { showError, showSuccess } from "@/utils/alert"; +import { showError, showInfo, showSuccess } from "@/utils/alert"; interface QRScannerProps { activityId: number; @@ -26,6 +26,7 @@ const QRScannerComponent: React.FC = ({ const lastProcessedQR = useRef(""); const lastScanTime = useRef(0); const isThrottled = useRef(false); + const processedMemberIdsRef = useRef(new Set()); const resetProcessingState = useCallback((delayMs: number) => { window.setTimeout(() => { @@ -77,6 +78,12 @@ const QRScannerComponent: React.FC = ({ return; } + if (processedMemberIdsRef.current.has(qrResult.data.memberId)) { + showInfo(`${qrResult.data.name}님은 이미 출석 처리되었습니다.`); + resetProcessingState(1000); + return; + } + const submitResponse = await PostMemberActivities( activityId, qrResult.data.memberId @@ -84,15 +91,14 @@ const QRScannerComponent: React.FC = ({ if (!submitResponse.ok) { switch (submitResponse.status) { case 400: - showError("잘못된 요청입니다. 관리자에게 문의해주세요."); + showError("출석 처리에 실패했습니다. 관리자에게 문의해주세요."); break; case 404: - showError( - "해당 활동 또는 회원을 찾을 수 없습니다. 관리자에게 문의해주세요." - ); + showError("회원 또는 행사 정보를 찾을 수 없습니다. 관리자에게 문의해주세요."); break; case 409: - showError("이미 참여한 활동입니다."); + showInfo(`${qrResult.data.name}님은 이미 출석 처리되었습니다.`); + processedMemberIdsRef.current.add(qrResult.data.memberId); break; default: showError( @@ -107,7 +113,8 @@ const QRScannerComponent: React.FC = ({ return; } - showSuccess(`${qrResult.data.name}님이 참석했습니다.`); + processedMemberIdsRef.current.add(qrResult.data.memberId); + showSuccess(`${qrResult.data.name}님 출석이 완료되었습니다.`); resetProcessingState(1000); } catch (error) { console.error("QR 코드 처리 중 오류 발생:", error); diff --git a/src/utils/alert.tsx b/src/utils/alert.tsx index 4515cee..676e4ea 100644 --- a/src/utils/alert.tsx +++ b/src/utils/alert.tsx @@ -12,6 +12,12 @@ export const showSuccess = (message: string) => { }); }; +export const showInfo = (message: string) => { + toast.info(message, { + duration: 2500, + }); +}; + export const showConfirm = async (message: string): Promise => { return window.confirm(message); };