diff --git a/app/adminpage/dashboard/_components/OrderCard.tsx b/app/adminpage/dashboard/_components/OrderCard.tsx index b1691db..4dba4e7 100644 --- a/app/adminpage/dashboard/_components/OrderCard.tsx +++ b/app/adminpage/dashboard/_components/OrderCard.tsx @@ -143,26 +143,43 @@ export default function OrderCard({ order }: OrderCardProps) { - {/* 티켓 수량 + 가격 */} + {/* 구매 수량 (조건부) + 가격 */}
-
- -
-

티켓

-
- {order.matchingTicketQty > 0 && ( - - 매칭 {order.matchingTicketQty} - - )} - {order.optionTicketQty > 0 && ( - - 옵션 {order.optionTicketQty} - - )} -
-
-
+ {(() => { + // STOMP 실시간 데이터는 productCode가 "UNKNOWN"이므로 수량 필드로 단품 여부 유추 + const isMatchingSingle = + order.productCode === "MATCHING_TICKET_1" || + (order.productCode === "UNKNOWN" && + order.matchingTicketQty > 0 && + order.optionTicketQty === 0); + + const isOptionSingle = + order.productCode === "OPTION_TICKET_1" || + (order.productCode === "UNKNOWN" && + order.optionTicketQty > 0 && + order.matchingTicketQty === 0); + + if (isMatchingSingle || isOptionSingle) { + return ( +
+ +
+

구매 수량

+

+ {isMatchingSingle + ? order.matchingTicketQty + : order.optionTicketQty} + 개 +

+
+
+ ); + } + return null; + })()}
diff --git a/app/profile-image/_components/ProfileImageSelection.tsx b/app/profile-image/_components/ProfileImageSelection.tsx index 790b693..6d5fdf6 100644 --- a/app/profile-image/_components/ProfileImageSelection.tsx +++ b/app/profile-image/_components/ProfileImageSelection.tsx @@ -81,17 +81,24 @@ const ProfileImageSelection = ({ if (targetFile.type.startsWith("image/")) { onFileChange?.(targetFile); - const reader = new FileReader(); - reader.onloadend = () => { - onCustomImageChange(reader.result as string); - }; - reader.readAsDataURL(targetFile); + + // 기존 브라우저 메모리에 캐싱된 Blob URL이 있다면 해제 (메모리 누수 방지) + if (customImage && customImage.startsWith("blob:")) { + URL.revokeObjectURL(customImage); + } + + // FileReader 대신 createObjectURL을 사용하여 빠르고 가볍게 미리보기 생성 + const objectUrl = URL.createObjectURL(targetFile); + onCustomImageChange(objectUrl); } } catch (error) { console.error("Image processing failed:", error); alert( "이미지 처리 중 오류가 발생했습니다. 다른 이미지 형식을 사용해 주세요.", ); + } finally { + // 동일한 파일을 다시 선택해도 onChange가 트리거되도록 값 초기화 + e.target.value = ""; } }; diff --git a/app/roulette/_components/Roulette.tsx b/app/roulette/_components/Roulette.tsx index 9fb4c75..c6b68f5 100644 --- a/app/roulette/_components/Roulette.tsx +++ b/app/roulette/_components/Roulette.tsx @@ -41,9 +41,9 @@ const ROULETTE_CONFIG: Record< imageSrc: "/roulette/special_roulette.png", items: [ { id: 1, label: "옵션권 2장" }, - { id: 2, label: "옵션권 5장" }, - { id: 3, label: "뽑기권 1장" }, - { id: 4, label: "풀세트" }, + { id: 2, label: "뽑기권 1장" }, + { id: 3, label: "풀세트" }, + { id: 4, label: "옵션권 5장" }, { id: 5, label: "뽑기권 5장" }, { id: 6, label: "뽑기권 10장" }, { id: 7, label: "1만원권 상품권" }, diff --git a/app/roulette/_components/RouletteResultModal.tsx b/app/roulette/_components/RouletteResultModal.tsx index f17523d..259f59a 100644 --- a/app/roulette/_components/RouletteResultModal.tsx +++ b/app/roulette/_components/RouletteResultModal.tsx @@ -18,31 +18,17 @@ const ITEM_IMAGE_MAP: Record = { "옵션권 1장": "/roulette/item/option_ticket_1.png", "옵션권 2장": "/roulette/item/option_ticket_2.png", "옵션권 5장": "/roulette/item/option_ticket_5.png", - 꽝: "/roulette/item/miss.png", // TODO: 꽝 이미지 추가 필요 + 꽝: "/roulette/item/no-luck.png", "뽑기권 1장": "/roulette/item/draw_ticket_1.png", "뽑기권 5장": "/roulette/item/draw_ticket_5.png", "뽑기권 10장": "/roulette/item/draw_ticket_10.png", 풀세트: "/roulette/item/full_set.png", - "1만원권 상품권": "/roulette/item/gift_card_10000.png", // TODO: 상품권 이미지 추가 필요 - "2만원권 상품권": "/roulette/item/gift_card_20000.png", // TODO: 상품권 이미지 추가 필요 -}; - -// 아이템 설명 텍스트 -const ITEM_DESCRIPTION_MAP: Record = { - "옵션권 1장": "매칭 시 옵션 1개를 선택할 수 있어요", - "옵션권 2장": "매칭 시 옵션 2개를 선택할 수 있어요", - "옵션권 5장": "매칭 시 옵션 5개를 선택할 수 있어요", - 꽝: "아쉽지만 다음 기회에!", - "뽑기권 1장": "새로운 매칭 기회가 생겼어요", - "뽑기권 5장": "새로운 매칭 기회 5번이 생겼어요", - "뽑기권 10장": "새로운 매칭 기회 10번이 생겼어요", - 풀세트: "뽑기권 1장 + 옵션권 3장 획득!", - "1만원권 상품권": "1만원 상품권이 지급됩니다", - "2만원권 상품권": "2만원 상품권이 지급됩니다", + "1만원권 상품권": "/roulette/item/gift_card_10000.png", + "2만원권 상품권": "/roulette/item/gift_card_20000.png", }; // ========================================== -// 컨페티 조각 (좌/우) +// 컨페티 조각 (장식용) // ========================================== const ConfettiLeft = () => (
@@ -84,8 +70,7 @@ export default function RouletteResultModal({ }: RouletteResultModalProps) { if (!item) return null; - const imageSrc = ITEM_IMAGE_MAP[item.label] ?? "/roulette/prizes/miss.png"; - const description = ITEM_DESCRIPTION_MAP[item.label] ?? ""; + const imageSrc = ITEM_IMAGE_MAP[item.label] ?? "/roulette/item/no-luck.png"; return ( !o && onClose()}> @@ -95,19 +80,29 @@ export default function RouletteResultModal({ > 룰렛 당첨 결과 - 당첨된 보상을 확인하세요. + 당첨된 보상을 확인하세요 - {/* 찐 모달 본체 */} + {/* 모달 본체 */}
- {/* 컨페티 (장식용이므로 얘네만 absolute 유지) */} - - + {/* 컨페티 (장식이므로 부모는 absolute 아님) */} + {item.label !== "꽝" && ( + <> + + + + )} {/* 상단 텍스트 영역 */}
- - 축하해요! + + {item.label === "꽝" ? "아쉬워요!" : "축하해요!"}
@@ -129,10 +124,12 @@ export default function RouletteResultModal({ {/* 하단 텍스트 영역 */}
- {item.label} + {item.label === "꽝" ? "이번엔 꽝이에요" : `${item.label} 당첨`} - {item.label === "꽝" ? description : "보상이 바로 지급되었어요."} + {item.label === "꽝" + ? "내일 다시 도전해 보세요!" + : "보상이 바로 지급되었어요."}
@@ -140,16 +137,9 @@ export default function RouletteResultModal({
{item.label !== "꽝" && ( - + + 충전내역에서 확인할 수 있어요. + )}
diff --git a/app/roulette/special/_components/SpecialRouletteChanceCard.tsx b/app/roulette/special/_components/SpecialRouletteChanceCard.tsx index d9acb16..c76633e 100644 --- a/app/roulette/special/_components/SpecialRouletteChanceCard.tsx +++ b/app/roulette/special/_components/SpecialRouletteChanceCard.tsx @@ -2,6 +2,7 @@ import React from "react"; import Image from "next/image"; +import { Check } from "lucide-react"; import { cn } from "@/lib/utils"; export interface SpecialRouletteChanceCardProps { @@ -9,11 +10,13 @@ export interface SpecialRouletteChanceCardProps { currentAmount?: number; /** 다음 룰렛 기회까지의 목표/토탈 금액 (기본값: 3000) */ targetAmount?: number; - /** 상단 누적 결제 정보 텍스트 (옵션, 기본값: "누적 결제 {currentAmount}원") */ + /** 오늘 이미 룰렛에 참여했는지 여부 */ + isParticipated?: boolean; + /** 상단 누적 결제 정보 텍스트 (옵션) */ accumulatedText?: string; - /** 상단 추가 결제 안내 텍스트 (옵션, 기본값: "{남은금액}원 추가 결제 시 1회") */ + /** 상단 추가 결제 안내 텍스트 (옵션) */ additionalText?: string; - /** 우측 하단 목표 금액 텍스트 (옵션, 기본값: "{targetAmount}원") */ + /** 우측 하단 목표 금액 텍스트 (옵션) */ targetText?: string; className?: string; } @@ -21,6 +24,7 @@ export interface SpecialRouletteChanceCardProps { export default function SpecialRouletteChanceCard({ currentAmount = 1000, targetAmount = 3000, + isParticipated = false, accumulatedText, additionalText, targetText, @@ -32,10 +36,28 @@ export default function SpecialRouletteChanceCard({ Math.max(0, Math.round((currentAmount / targetAmount) * 100)), ); - const displayAccumulated = - accumulatedText ?? `누적 결제 ${currentAmount.toLocaleString()}원`; - const displayAdditional = - additionalText ?? `${diff.toLocaleString()}원 추가 결제 시 1회`; + const isTargetReached = currentAmount >= targetAmount; + + let displayAccumulated = ""; + let displayAdditional = ""; + + if (isParticipated) { + displayAccumulated = accumulatedText ?? "오늘 참여 완료"; + displayAdditional = additionalText ?? "내일 또 도전해보세요!"; + } else { + displayAccumulated = + accumulatedText ?? + (isTargetReached + ? "오늘 1회 참여 가능" + : `누적 결제 ${currentAmount.toLocaleString()}원`); + + displayAdditional = + additionalText ?? + (isTargetReached + ? `누적 결제 ${targetAmount.toLocaleString()}원 달성` + : `${diff.toLocaleString()}원 추가 결제 시 1회`); + } + const displayTarget = targetText ?? `${targetAmount.toLocaleString()}원`; return ( @@ -45,23 +67,42 @@ export default function SpecialRouletteChanceCard({ className, )} > - {/* Chances container (상단 티켓 아이콘 + 결제 정보) */} + {/* Chances container (상단 아이콘 + 텍스트 정보) */}
- {/* Ticket Image */} + {/* Icon (Ticket or Check) */}
- ticket + {isParticipated ? ( +
+ +
+ ) : ( + ticket + )}
{/* Info container */}
- + {displayAccumulated} diff --git a/public/roulette/item/no-luck.png b/public/roulette/item/no-luck.png new file mode 100644 index 0000000..c106e63 Binary files /dev/null and b/public/roulette/item/no-luck.png differ diff --git a/public/roulette/special_roulette.png b/public/roulette/special_roulette.png index 590d5bc..41474d7 100644 Binary files a/public/roulette/special_roulette.png and b/public/roulette/special_roulette.png differ