84 lines
3.4 KiB
TypeScript
84 lines
3.4 KiB
TypeScript
'use client';
|
||
import Link from 'next/link';
|
||
import { useEffect, useState } from 'react';
|
||
import { formatToTaipeiTime } from '@/lib/timezone';
|
||
import { getAllMatches, type MatchListItem } from '@/lib/analytics-api';
|
||
import { matchStatusLabel, sortMatchesForProfessionalDisplay } from '@/lib/match-order';
|
||
|
||
function scoreLine(match: MatchListItem): string {
|
||
if (typeof match.home_score === 'number' && typeof match.away_score === 'number') {
|
||
return `${match.home_team} ${match.home_score} - ${match.away_score} ${match.away_team}`;
|
||
}
|
||
return `${match.home_team} vs ${match.away_team}`;
|
||
}
|
||
|
||
export default function MatchesPage() {
|
||
const [matches, setMatches] = useState<MatchListItem[]>([]);
|
||
const [loading, setLoading] = useState(true);
|
||
const [error, setError] = useState<string | null>(null);
|
||
|
||
useEffect(() => {
|
||
async function loadMatches() {
|
||
try {
|
||
const data = await getAllMatches();
|
||
setMatches(sortMatchesForProfessionalDisplay(data || []));
|
||
setError(null);
|
||
} catch (error) {
|
||
console.error('Failed to load matches', error);
|
||
setError('賽事資料暫時無法同步,系統正在等待資料源回補。請先到資料健康頁確認目前狀態。');
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
}
|
||
loadMatches();
|
||
const interval = window.setInterval(loadMatches, 60_000);
|
||
return () => window.clearInterval(interval);
|
||
}, []);
|
||
|
||
if (loading) {
|
||
return <div className="p-8 text-[#8a6b58] dot-matrix">載入即時賽事中...</div>;
|
||
}
|
||
|
||
if (matches.length === 0) {
|
||
return (
|
||
<div className="p-8 text-[#7d2a15] dot-matrix">
|
||
{error || '目前沒有可顯示的賽事資料。'}
|
||
</div>
|
||
);
|
||
}
|
||
|
||
return (
|
||
<div className="space-y-4">
|
||
<h2 className="dot-matrix text-2xl text-[#7d2a15]">賽事中心</h2>
|
||
<section className="grid gap-4">
|
||
{matches.map((match) => (
|
||
<Link
|
||
key={match.match_id}
|
||
href={`/matches/${encodeURIComponent(match.match_id)}`}
|
||
className="panel-glow block rounded-2xl p-5 transition hover:-translate-y-0.5 hover:border-[#c58b63] hover:bg-[#fff8e6]"
|
||
>
|
||
<div className="flex flex-col gap-2 md:flex-row md:items-center md:justify-between">
|
||
<p className="text-lg font-semibold text-[#6b3f2d]">{scoreLine(match)}</p>
|
||
<span className="w-fit rounded-full bg-[#fff4df] px-3 py-1 text-xs font-semibold text-[#7d2a15] ring-1 ring-[#e8cead]">
|
||
{matchStatusLabel(match)}
|
||
</span>
|
||
</div>
|
||
<p className="mt-3 text-sm text-[#8a6b58]">開賽:{formatToTaipeiTime(match.kickoff_utc)}</p>
|
||
<p className="text-sm text-[#7c5340]">
|
||
場地:{match.venue_name || '未定'} ({match.venue_city || '未定'})
|
||
</p>
|
||
<p className="mt-3 text-xs font-semibold text-[#b83822]">查看單場詳情與量化資料 →</p>
|
||
</Link>
|
||
))}
|
||
</section>
|
||
|
||
<section className="panel-glow rounded-2xl border-dashed p-5">
|
||
<h3 className="dot-matrix text-xl text-[#7d2a15]">即時事件與預期進球熱區資料</h3>
|
||
<p className="mt-2 text-sm leading-6 text-[#7a5b46]">
|
||
目前尚未接入可驗證的逐分鐘事件源,因此不再顯示假時間軸、假預期進球或假熱區。接入 Opta、FIFA 或授權事件源後,這裡才會開啟即時圖表。
|
||
</p>
|
||
</section>
|
||
</div>
|
||
);
|
||
}
|