128 lines
4.8 KiB
TypeScript
128 lines
4.8 KiB
TypeScript
import Link from "next/link";
|
|
import { matchesByDate, matchPhase, type MatchPhase } from "@/lib/schedule";
|
|
import { getPredictions, getCrowd } from "@/lib/mockData";
|
|
import { dateHeading, timeOnly } from "@/lib/format";
|
|
import { type Lang, dict, teamShort, roundLabel as tRound } from "@/lib/i18n";
|
|
import type { Match } from "@/lib/types";
|
|
import TeamFlag from "./TeamFlag";
|
|
|
|
const STROKE = "#94FBE0"; // 일정 텍스트·선택 아웃라인 스트로크 컬러
|
|
|
|
const PHASE_CLS: Record<MatchPhase, string> = {
|
|
open: "border-[#94FBE0] text-[#94FBE0]",
|
|
scheduled: "border-[var(--line-d)] text-[var(--ink-muted)]",
|
|
locked: "border-[var(--line-d)] text-[var(--ink-muted)]",
|
|
finished: "border-white/15 text-white/55",
|
|
};
|
|
|
|
export default function ScheduleBoard({ lang = "ko" }: { lang?: Lang }) {
|
|
const t = dict(lang);
|
|
const groups = matchesByDate();
|
|
return (
|
|
<section className="mt-6">
|
|
<div className="mb-3 flex items-baseline gap-2.5">
|
|
<h2 className="shrink-0 text-[22px] font-extrabold">{t.schedTitle}</h2>
|
|
<span className="whitespace-nowrap text-[12px] text-white/55">
|
|
{t.schedGuide}
|
|
</span>
|
|
</div>
|
|
|
|
<div className="flex flex-col gap-5">
|
|
{groups.map((g) => (
|
|
<div key={g.date}>
|
|
<div className="mb-2 text-[13px] font-bold" style={{ color: STROKE }}>
|
|
{dateHeading(g.date, lang)}
|
|
</div>
|
|
<div className="flex flex-col gap-2.5">
|
|
{g.matches.map((m) => (
|
|
<MatchCard key={m.matchId} match={m} lang={lang} />
|
|
))}
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|
|
|
|
function MatchCard({ match, lang }: { match: Match; lang: Lang }) {
|
|
const t = dict(lang);
|
|
const phase = matchPhase(match);
|
|
const preds = getPredictions(match, lang);
|
|
const crowd = getCrowd(match);
|
|
|
|
// AI 픽 갈림 요약
|
|
const tally = { a: 0, d: 0, b: 0 };
|
|
for (const p of preds) {
|
|
if (p.outcome === "TEAM_A_WIN") tally.a++;
|
|
else if (p.outcome === "DRAW") tally.d++;
|
|
else tally.b++;
|
|
}
|
|
const split: string[] = [];
|
|
if (tally.a) split.push(`${teamShort(match.teamA, lang)} ${tally.a}`);
|
|
if (tally.d) split.push(`${t.draw} ${tally.d}`);
|
|
if (tally.b) split.push(`${teamShort(match.teamB, lang)} ${tally.b}`);
|
|
|
|
// 종료 경기: 결과 스코어 + 승자 라벨 (예: "멕시코 승")
|
|
const result = match.result;
|
|
const winLabel = result
|
|
? result.outcome === "TEAM_A_WIN"
|
|
? `${teamShort(match.teamA, lang)} ${t.win}`
|
|
: result.outcome === "TEAM_B_WIN"
|
|
? `${teamShort(match.teamB, lang)} ${t.win}`
|
|
: t.drawLabel
|
|
: null;
|
|
|
|
const href = `/match/${match.matchId}${lang === "en" ? "?lang=en" : ""}`;
|
|
|
|
return (
|
|
<Link
|
|
href={href}
|
|
className="block rounded-2xl border-2 border-[#94FBE0]/45 bg-[#171b21] p-4 transition active:scale-[0.99] hover:border-[#94FBE0]"
|
|
>
|
|
<div className="flex items-center justify-between text-[11px]">
|
|
<span className="font-mono font-bold text-white/70">
|
|
{timeOnly(match.kickoffKst)} <span className="text-white/40">KST</span> ·{" "}
|
|
{tRound(match.roundLabel, lang)}
|
|
{winLabel && (
|
|
<span className="ml-1 font-semibold text-[#94FBE0]">· {winLabel}</span>
|
|
)}
|
|
</span>
|
|
<span className={`rounded-md border px-2 py-0.5 font-semibold ${PHASE_CLS[phase]}`}>
|
|
{t.phase[phase]}
|
|
</span>
|
|
</div>
|
|
|
|
<div className="mt-3 grid grid-cols-[1fr_auto_1fr] items-center gap-2">
|
|
<div className="flex items-center gap-2">
|
|
<TeamFlag team={match.teamA} className="h-6 w-9 shrink-0" />
|
|
<span className="truncate text-[15px] font-extrabold">{teamShort(match.teamA, lang)}</span>
|
|
</div>
|
|
{result ? (
|
|
<span className="flex items-center gap-1.5 font-impact text-[18px] italic">
|
|
<span className="tabular-nums text-white">{result.scoreA}</span>
|
|
<span className="text-[13px] text-[#94FBE0]">VS</span>
|
|
<span className="tabular-nums text-white">{result.scoreB}</span>
|
|
</span>
|
|
) : (
|
|
<span className="font-impact text-[18px] italic text-[#94FBE0]">VS</span>
|
|
)}
|
|
<div className="flex items-center justify-end gap-2">
|
|
<span className="truncate text-right text-[15px] font-extrabold">{teamShort(match.teamB, lang)}</span>
|
|
<TeamFlag team={match.teamB} className="h-6 w-9 shrink-0" />
|
|
</div>
|
|
</div>
|
|
|
|
<div className="mt-3 flex items-center justify-between text-[11px] font-bold text-[#F4F3FE]">
|
|
<span>
|
|
<span className="font-semibold text-white/55">{t.aiPicks}</span> {split.join(" · ")}
|
|
</span>
|
|
<span className="flex items-center gap-2">
|
|
<span>{t.joined(crowd.total.toLocaleString())}</span>
|
|
<span className="text-[#94FBE0]">→</span>
|
|
</span>
|
|
</div>
|
|
</Link>
|
|
);
|
|
}
|