95 lines
3.0 KiB
TypeScript
95 lines
3.0 KiB
TypeScript
'use client';
|
||
|
||
import { useEffect, useState } from 'react';
|
||
import Link from 'next/link';
|
||
|
||
type CachedOddsEntry = {
|
||
match_id: string;
|
||
home_team?: string;
|
||
away_team?: string;
|
||
odds: number;
|
||
market_type?: string;
|
||
selection?: string;
|
||
captured_at: string;
|
||
};
|
||
|
||
const OFFLINE_STORAGE_KEY = 'wc2026:lastOddsSnapshot';
|
||
|
||
function formatTs(ts: string) {
|
||
try {
|
||
return new Date(ts).toLocaleTimeString('zh-TW', {
|
||
hour: '2-digit',
|
||
minute: '2-digit',
|
||
second: '2-digit',
|
||
});
|
||
} catch {
|
||
return ts;
|
||
}
|
||
}
|
||
|
||
export default function OfflinePage() {
|
||
const [snapshot, setSnapshot] = useState<CachedOddsEntry[]>([]);
|
||
|
||
useEffect(() => {
|
||
try {
|
||
const raw = window.localStorage.getItem(OFFLINE_STORAGE_KEY);
|
||
if (!raw) {
|
||
return;
|
||
}
|
||
const data = JSON.parse(raw);
|
||
if (Array.isArray(data)) {
|
||
setSnapshot(data as CachedOddsEntry[]);
|
||
}
|
||
} catch {
|
||
setSnapshot([]);
|
||
}
|
||
}, []);
|
||
|
||
return (
|
||
<div className="flex min-h-[70vh] items-center justify-center p-4">
|
||
<section className="max-w-xl w-full rounded-2xl border border-[#9b2e19] bg-[#1a100a] p-6 text-[#ffd5a8] shadow-2xl shadow-[#9b2e19]/35">
|
||
<p className="dot-matrix text-2xl text-[#ffb67a]">OFFLINE: CONNECTION LOST</p>
|
||
<h1 className="mt-2 text-3xl text-[#ffe2bd]">你已進入離線保護模式</h1>
|
||
<p className="mt-3 text-sm text-[#f6c48f]">
|
||
網路暫時中斷,已為你保留最後可用賠率快取,並暫時停用即時更新。
|
||
</p>
|
||
|
||
<div className="mt-4 rounded-xl border border-[#6d391f] bg-[#27160f] p-3">
|
||
<p className="dot-matrix text-sm text-[#f8b66d]">快取賠率(最近)</p>
|
||
{snapshot.length === 0 ? (
|
||
<p className="mt-2 text-sm text-[#e6ba95]">目前沒有可顯示的快取資料。</p>
|
||
) : (
|
||
<ul className="mt-2 space-y-2">
|
||
{snapshot.map((item) => (
|
||
<li key={`${item.match_id}-${item.selection}`} className="text-sm">
|
||
<span className="font-semibold text-[#ffd79b]">{item.home_team || item.match_id}</span>
|
||
{item.away_team ? ` vs ${item.away_team}` : ''} | {item.market_type || 'market'}
|
||
{item.selection ? ` ${item.selection}` : ''}:
|
||
<span className="font-semibold text-[#ff9f4c]"> {item.odds.toFixed(2)} </span>
|
||
<span className="text-[#f4c89c]">({formatTs(item.captured_at)})</span>
|
||
</li>
|
||
))}
|
||
</ul>
|
||
)}
|
||
</div>
|
||
|
||
<div className="mt-4 flex flex-wrap gap-2">
|
||
<Link
|
||
href="/"
|
||
className="dot-matrix rounded-full bg-[#ab3f23] px-4 py-2 text-white"
|
||
>
|
||
返回首頁
|
||
</Link>
|
||
<button
|
||
type="button"
|
||
className="rounded-full bg-white/90 px-4 py-2 text-[#5f2c1a]"
|
||
onClick={() => window.location.reload()}
|
||
>
|
||
重新連線
|
||
</button>
|
||
</div>
|
||
</section>
|
||
</div>
|
||
);
|
||
}
|