Files
wooo 1e082333af
All checks were successful
2026 World Cup Quant Platform - Production Deployment / Code Quality, Security Gate & Testing (push) Successful in 4m25s
2026 World Cup Quant Platform - Production Deployment / Deploy to Production VM via Gitea CD (push) Successful in 5m22s
fix: type missing product pages
2026-06-18 15:28:30 +08:00

48 lines
2.7 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import Link from 'next/link';
const ANALYTICS_BACKEND = process.env.ANALYTICS_BACKEND_URL || 'http://127.0.0.1:8000';
type MatchRow = {
match_id: string;
home_team: string;
away_team: string;
home_score: number | null;
away_score: number | null;
kickoff_utc: string;
status: string;
venue_city?: string | null;
};
async function getMatches(): Promise<MatchRow[]> {
const response = await fetch(`${ANALYTICS_BACKEND}/analytics/matches`, { cache: 'no-store' });
if (!response.ok) throw new Error('賽程 API 暫時無法回應');
return response.json() as Promise<MatchRow[]>;
}
function taipeiDate(value: string) {
return new Intl.DateTimeFormat('zh-TW', { timeZone: 'Asia/Taipei', month: '2-digit', day: '2-digit', weekday: 'short' }).format(new Date(value));
}
function taipeiTime(value: string) {
return new Intl.DateTimeFormat('zh-TW', { timeZone: 'Asia/Taipei', hour: '2-digit', minute: '2-digit', hour12: false }).format(new Date(value));
}
export default async function SchedulePage() {
let matches: MatchRow[] = [];
let error = '';
try { matches = await getMatches(); } catch (err) { error = err instanceof Error ? err.message : '賽程暫時無法讀取'; }
const grouped = matches.reduce<Record<string, MatchRow[]>>((acc, match) => {
const key = taipeiDate(match.kickoff_utc);
acc[key] = [...(acc[key] ?? []), match];
return acc;
}, {});
return (
<div className="space-y-6">
<section className="rounded-[2rem] border border-[#e7c89b] bg-[#fff8e6]/95 p-6 md:p-8"><p className="dot-matrix text-sm font-bold text-[#b83822]"></p><h1 className="mt-3 text-4xl font-black text-[#3f2f25]"></h1><p className="mt-3 text-sm leading-7 text-[#6f4f3c]">便</p></section>
{error ? <p className="rounded-2xl border border-[#e7a49a] bg-[#fff0e8] p-4 text-sm font-bold text-[#b83822]">{error}</p> : null}
{Object.entries(grouped).map(([date, rows]) => <section key={date} className="rounded-3xl border border-[#eadcb9] bg-white/75 p-5"><h2 className="dot-matrix text-lg font-bold text-[#7d2a15]">{date}{rows.length} </h2><div className="mt-4 grid gap-3">{rows.map((match) => <Link key={match.match_id} href={`/matches/${match.match_id}`} className="rounded-2xl border border-[#eadcb9] bg-[#fff8e6] p-4 transition hover:border-[#b83822]"><p className="font-black text-[#3f2f25]">{taipeiTime(match.kickoff_utc)}{match.home_team} vs {match.away_team}</p><p className="mt-1 text-sm text-[#7a5b46]">{match.status}{match.home_score ?? '-'} : {match.away_score ?? '-'}{match.venue_city ?? '場地待定'}</p></Link>)}</div></section>)}
</div>
);
}