From 8da8b7e7bd43fc685e15d3c8173ff844c7f615b9 Mon Sep 17 00:00:00 2001 From: jwkim Date: Tue, 25 Aug 2026 09:07:31 +0900 Subject: [PATCH] =?UTF-8?q?=EC=9D=BC=EC=A0=95=20=EC=B9=B4=EB=93=9C=20AI=20?= =?UTF-8?q?=ED=94=BD=EC=9D=84=20=ED=8C=80=20=EC=95=84=EB=9E=98=20=EB=AA=A8?= =?UTF-8?q?=EB=8D=B8=20=EC=95=84=EC=9D=B4=EC=BD=98=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=ED=91=9C=EC=8B=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 기존 "AI 픽 삼성 3" 텍스트 줄 대신 GPT/Claude/Gemini 아이콘을 각자 픽한 팀 쪽 아래에 배치 (무승부 픽은 가운데). 참여수는 우측 유지. Co-Authored-By: Claude Fable 5 --- frontend/src/components/ScheduleBoard.tsx | 60 ++++++++++++++++++++--- 1 file changed, 53 insertions(+), 7 deletions(-) diff --git a/frontend/src/components/ScheduleBoard.tsx b/frontend/src/components/ScheduleBoard.tsx index 439784e..863875f 100644 --- a/frontend/src/components/ScheduleBoard.tsx +++ b/frontend/src/components/ScheduleBoard.tsx @@ -3,7 +3,7 @@ import { Link } from "react-router-dom"; import { timeOnly, dateKey } from "@/lib/format"; import { getLive, type LiveData } from "@/lib/api"; import { type Lang, dict, teamShort, roundLabel as tRound, dayName, monthEn } from "@/lib/i18n"; -import type { League, Match, MatchPhase } from "@/lib/types"; +import type { League, Match, MatchPhase, ModelName, Outcome } from "@/lib/types"; import TeamFlag from "./TeamFlag"; import StandingsTable from "./StandingsTable"; @@ -435,6 +435,29 @@ function RankBadge({ rank, lang = "ko" }: { rank?: number | null; lang?: Lang }) ); } +// AI 픽 아이콘 (일정 카드용 — Arena 와 동일 공식 로고) +const MODEL_ICON: Record = { + GPT: "/icons/gpt.png", + Claude: "/icons/claude.jpg", + Gemini: "/icons/gemini.jpeg", +}; + +function AiPickIcons({ models }: { models: ModelName[] }) { + return ( + <> + {models.map((m) => ( + {m} + ))} + + ); +} + function MatchCard({ match, lang, @@ -490,6 +513,15 @@ function MatchCard({ const rightSt = flip ? match.extras?.standings?.a : match.extras?.standings?.b; const flagCls = isClub ? "h-7 w-7 shrink-0" : "h-6 w-9 shrink-0"; + // AI 픽: 픽한 팀 아래에 모델 아이콘 배치 (무승부 픽은 가운데) + const picksFor = (o: Outcome) => + match.predictions.filter((p) => p.outcome === o).map((p) => p.model); + const leftPicks = picksFor(flip ? "TEAM_B_WIN" : "TEAM_A_WIN"); + const rightPicks = picksFor(flip ? "TEAM_A_WIN" : "TEAM_B_WIN"); + const drawPicks = picksFor("DRAW"); + const showAiRow = + match.votable && phase !== "cancelled" && match.predictions.length > 0; + // 비투표(타 조)는 클릭 비활성, 투표 가능 조(A)는 상세로 이동 const inner = ( <> @@ -558,6 +590,24 @@ function MatchCard({ + {/* AI 픽: 각 모델 아이콘을 픽한 팀 아래에 표시 (무승부 픽은 가운데) */} + {showAiRow && ( +
+
+ +
+
+ +
+
+ +
+
+ )} + {/* 야구: 선발 매치업 (프리뷰 캐시 있을 때만) */} {isBaseball && !match.result && (match.extras?.starterA || match.extras?.starterB) && (
@@ -578,13 +628,9 @@ function MatchCard({
)} - {/* 투표 가능 조만: AI 픽 갈림 + 참여수 + 이동 화살표 (취소 경기는 숨김) */} + {/* 투표 가능 조만: 참여수 + 이동 화살표 (AI 픽은 위의 아이콘 행이 담당, 취소 경기는 숨김) */} {match.votable && phase !== "cancelled" && ( -
- - {t.aiPicks}{" "} - {aiSplit(match, lang).join(" · ")} - +
{t.joined((match.crowd?.total ?? 0).toLocaleString())}