Refine ranking placement, visibility, and consistency
- Move cumulative ranking above the gold ₩1M card (ranking → challenge flow) - Drop redundant "전체 보기" link (inline folding shows full Top 10); /leaderboard route kept for direct access - Make fold toggle purple + match its size to the tab buttons; enlarge title/tab/row text for readability - Match email input & submit button radius to the ranking box (rounded-xl) - Leaderboard full page: top button = "투표로 돌아가기" (history back, home fallback) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>main
parent
965bd374c3
commit
996d5faaf4
|
|
@ -20,12 +20,12 @@ export default async function LeaderboardPage({
|
|||
|
||||
return (
|
||||
<main className="shell">
|
||||
<Hero back />
|
||||
<Hero back backHistory />
|
||||
<p className="mt-6 text-center text-[13px] leading-relaxed text-[var(--ink-muted)]">
|
||||
{t.goldDesc}
|
||||
</p>
|
||||
{/* 전체 페이지: 펼친 상태(폴딩 토글·전체보기 링크 숨김) */}
|
||||
<Leaderboard lang={lang} defaultOpen showAllLink={false} />
|
||||
{/* 전체 페이지: 펼친 상태(폴딩 토글 숨김) */}
|
||||
<Leaderboard lang={lang} defaultOpen />
|
||||
<Footer />
|
||||
</main>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -340,7 +340,7 @@ export default function Arena({
|
|||
value={email}
|
||||
onChange={(e) => onEmailChange(e.target.value)}
|
||||
placeholder={t.emailPh}
|
||||
className="w-full rounded-2xl border border-[var(--share)] bg-[#0f1217] px-4 py-3 text-[16px] text-white shadow-[0_0_0_2px_rgba(166,94,255,0.12)] outline-none focus:shadow-[0_0_0_2px_rgba(166,94,255,0.3)]"
|
||||
className="w-full rounded-xl border border-[var(--share)] bg-[#0f1217] px-4 py-3 text-[16px] text-white shadow-[0_0_0_2px_rgba(166,94,255,0.12)] outline-none focus:shadow-[0_0_0_2px_rgba(166,94,255,0.3)]"
|
||||
/>
|
||||
<label className="mt-2.5 flex items-start gap-2 text-[13px] leading-snug text-[var(--ink-muted)]">
|
||||
<input
|
||||
|
|
@ -354,7 +354,7 @@ export default function Arena({
|
|||
<button
|
||||
onClick={confirmSubmit}
|
||||
disabled={!emailValid}
|
||||
className="btn-share mt-3.5 flex w-full items-center justify-center gap-1.5 rounded-2xl py-3 text-[15px] font-extrabold transition active:scale-[0.99] disabled:opacity-[0.72]"
|
||||
className="btn-share mt-3.5 flex w-full items-center justify-center gap-1.5 rounded-xl py-3 text-[15px] font-extrabold transition active:scale-[0.99] disabled:opacity-[0.72]"
|
||||
>
|
||||
{t.submit} <span aria-hidden>→</span>
|
||||
</button>
|
||||
|
|
@ -459,6 +459,9 @@ export default function Arena({
|
|||
</section>
|
||||
)}
|
||||
|
||||
{/* ===== 누적 랭킹 TOP 10 (골드 카드 위 · 폴딩) — 랭킹→상금 도전 흐름 ===== */}
|
||||
<Leaderboard lang={lang} />
|
||||
|
||||
{/* ===== 골드 상금 (003) ===== */}
|
||||
<section className="gold-card mt-5 rounded-2xl p-5">
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
|
|
@ -477,9 +480,6 @@ export default function Arena({
|
|||
</div>
|
||||
</section>
|
||||
|
||||
{/* ===== 누적 랭킹 TOP 10 (골드 카드 직후 · 폴딩) ===== */}
|
||||
<Leaderboard lang={lang} />
|
||||
|
||||
{/* ===== Crowd Pick (003) — 보팅 비율(≠ AI 승리 확률) ===== */}
|
||||
<section className="mt-6">
|
||||
<div className="mb-2.5 text-[20px] font-extrabold">
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
"use client";
|
||||
|
||||
import { useRouter } from "next/navigation";
|
||||
|
||||
// 뒤로가기 — 직전 페이지(예: 투표하던 매치 페이지)로 복귀.
|
||||
// 히스토리가 없으면(직접 진입) 홈으로 폴백.
|
||||
export default function BackButton({
|
||||
label,
|
||||
home,
|
||||
}: {
|
||||
label: string;
|
||||
home: string;
|
||||
}) {
|
||||
const router = useRouter();
|
||||
const goBack = () => {
|
||||
if (typeof window !== "undefined" && window.history.length > 1) router.back();
|
||||
else router.push(home);
|
||||
};
|
||||
return (
|
||||
<button
|
||||
onClick={goBack}
|
||||
className="flex items-center gap-1.5 rounded-full border border-white/12 bg-white/8 px-[18px] py-[9px] text-[18px] font-bold text-white/85 active:scale-95"
|
||||
>
|
||||
{label}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
import Link from "next/link";
|
||||
import ShareButton from "./ShareButton";
|
||||
import LangSwitch from "./LangSwitch";
|
||||
import BackButton from "./BackButton";
|
||||
import { type Lang, dict } from "@/lib/i18n";
|
||||
|
||||
type Share = { url: string; title: string; text: string };
|
||||
|
|
@ -8,10 +9,12 @@ type Share = { url: string; title: string; text: string };
|
|||
// 브랜드 헤더 (대시보드·상세 공용). 경기별 후킹 카피는 상세 페이지에서 별도 노출.
|
||||
export default function Hero({
|
||||
back = false,
|
||||
backHistory = false,
|
||||
share,
|
||||
lang = "ko",
|
||||
}: {
|
||||
back?: boolean;
|
||||
backHistory?: boolean; // true면 홈 대신 직전 페이지로 복귀(뒤로가기)
|
||||
share?: Share;
|
||||
lang?: Lang;
|
||||
}) {
|
||||
|
|
@ -21,12 +24,16 @@ export default function Hero({
|
|||
<header className="pt-5 text-center">
|
||||
{back && (
|
||||
<div className="mb-3 flex items-center justify-between">
|
||||
<Link
|
||||
href={home}
|
||||
className="flex items-center gap-1.5 rounded-full border border-white/12 bg-white/8 px-[18px] py-[9px] text-[18px] font-bold text-white/85 active:scale-95"
|
||||
>
|
||||
{t.back}
|
||||
</Link>
|
||||
{backHistory ? (
|
||||
<BackButton label={t.backPrev} home={home} />
|
||||
) : (
|
||||
<Link
|
||||
href={home}
|
||||
className="flex items-center gap-1.5 rounded-full border border-white/12 bg-white/8 px-[18px] py-[9px] text-[18px] font-bold text-white/85 active:scale-95"
|
||||
>
|
||||
{t.back}
|
||||
</Link>
|
||||
)}
|
||||
{share && (
|
||||
<ShareButton {...share} label={t.share} copiedLabel={t.shareCopied} />
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import Link from "next/link";
|
||||
import { type Lang, dict } from "@/lib/i18n";
|
||||
import {
|
||||
fetchLeaderboard,
|
||||
|
|
@ -14,11 +13,9 @@ import {
|
|||
export default function Leaderboard({
|
||||
lang = "ko",
|
||||
defaultOpen = false,
|
||||
showAllLink = true,
|
||||
}: {
|
||||
lang?: Lang;
|
||||
defaultOpen?: boolean;
|
||||
showAllLink?: boolean;
|
||||
}) {
|
||||
const t = dict(lang);
|
||||
const [open, setOpen] = useState(defaultOpen);
|
||||
|
|
@ -37,17 +34,7 @@ export default function Leaderboard({
|
|||
|
||||
return (
|
||||
<section className="mt-5 rounded-2xl border border-[var(--line-d)] bg-[var(--bg2)] p-4">
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<h3 className="text-[16px] font-extrabold">{t.lbTitle}</h3>
|
||||
{showAllLink && (
|
||||
<Link
|
||||
href={lang === "en" ? "/leaderboard?lang=en" : "/leaderboard"}
|
||||
className="shrink-0 text-[12px] font-bold text-[var(--share)]"
|
||||
>
|
||||
{t.lbAll} →
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
<h3 className="text-[19px] font-extrabold">{t.lbTitle}</h3>
|
||||
|
||||
{open && (
|
||||
<>
|
||||
|
|
@ -62,7 +49,7 @@ export default function Leaderboard({
|
|||
<button
|
||||
key={val}
|
||||
onClick={() => setTab(val)}
|
||||
className={`rounded-xl py-2.5 text-[14px] font-extrabold transition ${
|
||||
className={`rounded-xl py-2.5 text-[15px] font-extrabold transition ${
|
||||
tab === val
|
||||
? "bg-[var(--share)] text-white"
|
||||
: "border border-[var(--line-d)] text-[var(--ink-muted)]"
|
||||
|
|
@ -74,7 +61,7 @@ export default function Leaderboard({
|
|||
</div>
|
||||
|
||||
{/* 헤더 라벨 */}
|
||||
<div className="mt-3 flex items-center justify-between px-3 text-[11px] font-bold text-[var(--ink-muted)]">
|
||||
<div className="mt-3 flex items-center justify-between px-3 text-[12px] font-bold text-[var(--ink-muted)]">
|
||||
<span>{tab === "ai" ? t.lbTabAI : t.lbTabVoters}</span>
|
||||
<span>{t.lbPoints}</span>
|
||||
</div>
|
||||
|
|
@ -93,16 +80,16 @@ export default function Leaderboard({
|
|||
}`}
|
||||
>
|
||||
<span
|
||||
className={`w-6 shrink-0 text-center font-mono text-[15px] font-extrabold tabular-nums ${
|
||||
className={`w-6 shrink-0 text-center font-mono text-[16px] font-extrabold tabular-nums ${
|
||||
top3 ? "text-[var(--share)]" : "text-[var(--ink-muted)]"
|
||||
}`}
|
||||
>
|
||||
{i + 1}
|
||||
</span>
|
||||
<span className="min-w-0 flex-1 truncate text-[15px] font-bold text-white">
|
||||
<span className="min-w-0 flex-1 truncate text-[16px] font-bold text-white">
|
||||
{r.label}
|
||||
</span>
|
||||
<span className="shrink-0 font-mono text-[15px] font-extrabold tabular-nums text-white">
|
||||
<span className="shrink-0 font-mono text-[16px] font-extrabold tabular-nums text-white">
|
||||
{r.points.toLocaleString()}
|
||||
<span className="ml-0.5 text-[12px] text-[var(--ink-muted)]">
|
||||
{t.lbUnit}
|
||||
|
|
@ -119,7 +106,7 @@ export default function Leaderboard({
|
|||
{!defaultOpen && (
|
||||
<button
|
||||
onClick={() => setOpen((v) => !v)}
|
||||
className="mt-3 flex w-full items-center justify-center gap-1 rounded-xl border border-[var(--line-d)] py-2.5 text-[13px] font-bold text-[var(--ink-muted)] transition active:scale-[0.99]"
|
||||
className="mt-3 flex w-full items-center justify-center gap-1.5 rounded-xl border border-[var(--share)] bg-[rgba(166,94,255,0.12)] py-2.5 text-[15px] font-extrabold text-[var(--share)] transition active:scale-[0.99]"
|
||||
>
|
||||
{open ? t.lbClose : t.lbOpen}
|
||||
<span aria-hidden>{open ? "▲" : "▼"}</span>
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ export function roundLabel(label: string, lang: Lang): string {
|
|||
type Dict = {
|
||||
heroPill: string;
|
||||
back: string;
|
||||
backPrev: string;
|
||||
share: string;
|
||||
shareCopied: string;
|
||||
introLead: string;
|
||||
|
|
@ -113,6 +114,7 @@ export const DICT: Record<Lang, Dict> = {
|
|||
ko: {
|
||||
heroPill: "AI와 함께하는 2026 글로벌 축구 축전 승부예측 챌린지",
|
||||
back: "← 전체 일정",
|
||||
backPrev: "← 투표로 돌아가기",
|
||||
share: "투표 공유하기",
|
||||
shareCopied: "링크 복사됨 ✓",
|
||||
introLead: "GPT · Claude · Gemini가 매 경기를\n서로 다르게 예측합니다.",
|
||||
|
|
@ -186,6 +188,7 @@ export const DICT: Record<Lang, Dict> = {
|
|||
en: {
|
||||
heroPill: "AI-powered Global Football Festival 2026 match predictions",
|
||||
back: "← All matches",
|
||||
backPrev: "← Back to voting",
|
||||
share: "Share vote",
|
||||
shareCopied: "Link copied ✓",
|
||||
introLead: "GPT · Claude · Gemini predict\nevery match differently.",
|
||||
|
|
|
|||
Loading…
Reference in New Issue