From 51352c7c5c54c28921966bd0e6cc1f4022662cdf Mon Sep 17 00:00:00 2001 From: hbyang Date: Thu, 11 Jun 2026 17:22:02 +0900 Subject: [PATCH] =?UTF-8?q?feat(ui):=20100=EB=A7=8C=EC=9B=90=20=EC=B1=8C?= =?UTF-8?q?=EB=A6=B0=EC=A7=80=20=EB=A3=B0=20=EC=84=A4=EB=AA=85=20=EB=AA=A8?= =?UTF-8?q?=EB=8B=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit '100만 원에 도전하기' 버튼 클릭 시 모달 표시 — 배점표(정확5/근접3/승패2/부분1/빗나감0), 누적 1위 상금, 이메일 기준 참여·집계 안내. KO/EN 지원. Co-Authored-By: Claude Opus 4.8 (1M context) --- frontend/src/components/Arena.tsx | 60 ++++++++++++++++++++++++++++++- frontend/src/lib/i18n.ts | 29 +++++++++++++++ 2 files changed, 88 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/Arena.tsx b/frontend/src/components/Arena.tsx index 4b1866d..85f81c4 100644 --- a/frontend/src/components/Arena.tsx +++ b/frontend/src/components/Arena.tsx @@ -49,6 +49,7 @@ export default function Arena({ const [copied, setCopied] = useState(false); const [submitting, setSubmitting] = useState(false); const [error, setError] = useState(null); + const [showRules, setShowRules] = useState(false); // 점수 선택 시 승/무/패 자동 선택 (스코어가 결과의 소스) useEffect(() => { @@ -359,12 +360,69 @@ export default function Arena({ {t.goldDesc}

- + {/* ===== 룰 설명 모달 ===== */} + {showRules && ( +
setShowRules(false)} + > +
e.stopPropagation()} + > +
+ {t.rulesTitle} + +
+

{t.rulesCumulative}

+ +
+ {( + [ + [t.rulesExact, 5], + [t.rulesCloseGrade, 3], + [t.rulesOutcome, 2], + [t.rulesPartial, 1], + [t.rulesMiss, 0], + ] as [string, number][] + ).map(([label, pt]) => ( +
+ {label} + + {pt} + {lang === "en" ? " pt" : "점"} + +
+ ))} +
+ +

+ {t.rulesEmailRequired} +

+ + +
+
+ )} + {/* ===== Crowd Pick (003) — 보팅 비율(≠ AI 승리 확률) ===== */}
diff --git a/frontend/src/lib/i18n.ts b/frontend/src/lib/i18n.ts index f952e09..ca35972 100644 --- a/frontend/src/lib/i18n.ts +++ b/frontend/src/lib/i18n.ts @@ -74,6 +74,15 @@ type Dict = { copied: string; goldDesc: string; goldBtn: string; + rulesTitle: string; + rulesCumulative: string; + rulesExact: string; + rulesCloseGrade: string; + rulesOutcome: string; + rulesPartial: string; + rulesMiss: string; + rulesEmailRequired: string; + rulesCloseBtn: string; crowdSub: string; finalResult: string; hit: string; @@ -133,6 +142,16 @@ export const DICT: Record = { copied: "복사됨 ✓", goldDesc: "경기마다 승패·스코어를 맞혀 포인트를 쌓고, 누적 1위에게 최종 100만원", goldBtn: "100만 원에\n도전하기", + rulesTitle: "100만 원 챌린지 룰", + rulesCumulative: "경기마다 적중 포인트를 쌓아, 대회 누적 포인트 1위 한 분에게 최종 100만 원을 드립니다.", + rulesExact: "정확 스코어 적중", + rulesCloseGrade: "근접 (승패 + 득실차 일치)", + rulesOutcome: "승패만 적중", + rulesPartial: "부분 (한 팀 스코어만 일치)", + rulesMiss: "빗나감", + rulesEmailRequired: + "픽 제출 시 입력한 이메일로 챌린지에 참여됩니다. 같은 이메일의 모든 경기 포인트가 합산되어 누적 순위가 매겨집니다.", + rulesCloseBtn: "확인", crowdSub: "| 참여자들의 선택", finalResult: "최종 결과", hit: "적중 ✓", @@ -191,6 +210,16 @@ export const DICT: Record = { copied: "Copied ✓", goldDesc: "Earn points each match by nailing the result & score — the overall #1 wins ₩1,000,000.", goldBtn: "Go for\n₩1,000,000", + rulesTitle: "₩1,000,000 Challenge Rules", + rulesCumulative: "Earn points each match — the player with the highest cumulative total wins ₩1,000,000.", + rulesExact: "Exact score", + rulesCloseGrade: "Close (result + goal difference)", + rulesOutcome: "Correct result only", + rulesPartial: "Partial (one team's score)", + rulesMiss: "Miss", + rulesEmailRequired: + "You enter the challenge with the email you provide when submitting a pick. Points from all matches under the same email are summed for the overall ranking.", + rulesCloseBtn: "Got it", crowdSub: "| What the crowd picked", finalResult: "Final result", hit: "Hit ✓",