補上歷史戰績列表與 NAS 部署說明

This commit is contained in:
2026-04-15 23:04:16 +08:00
parent 7fc8e2698b
commit b0908b4d3c
8 changed files with 472 additions and 72 deletions

View File

@@ -1,49 +1,162 @@
import type { MatchHistoryItem } from '../types'
import { useEffect, useState } from 'react'
import { loadHistoryList } from '../lib/api'
import type { HistoryListItem } from '../types'
type HistoryPageProps = {
history: MatchHistoryItem[]
}
export function HistoryPage() {
const [history, setHistory] = useState<HistoryListItem[]>([])
const [loading, setLoading] = useState(true)
const [error, setError] = useState('')
const [selectedItem, setSelectedItem] = useState<HistoryListItem | null>(null)
useEffect(() => {
let active = true
const run = async () => {
setLoading(true)
setError('')
try {
const nextHistory = await loadHistoryList()
if (!active) {
return
}
setHistory(nextHistory)
} catch (fetchError) {
if (!active) {
return
}
setError(fetchError instanceof Error ? fetchError.message : '無法讀取歷史戰績。')
} finally {
if (active) {
setLoading(false)
}
}
}
void run()
return () => {
active = false
}
}, [])
export function HistoryPage({ history }: HistoryPageProps) {
return (
<section className="page-grid">
<article className="panel panel-hero">
<p className="panel-kicker">History</p>
<h2></h2>
<p className="panel-copy"> DB </p>
</article>
<>
<section className="page-grid">
<article className="panel panel-hero">
<p className="panel-kicker">History</p>
<h2></h2>
<p className="panel-copy">
DB `history`
</p>
</article>
<article className="panel full-span">
{history.length === 0 ? (
<div className="empty-state">
<h3></h3>
<p> DB </p>
</div>
) : (
<div className="history-list">
{history.map((item) => (
<article className="history-card" key={item.id}>
<div className="history-head">
<div>
<p className="panel-kicker">{item.playedAt}</p>
<h3>
{item.leftTeamName} vs {item.rightTeamName}
</h3>
<article className="panel full-span">
{loading ? (
<div className="empty-state">
<h3></h3>
<p> DB </p>
</div>
) : error ? (
<div className="empty-state">
<h3></h3>
<p>{error}</p>
</div>
) : history.length === 0 ? (
<div className="empty-state">
<h3></h3>
<p>DB `history` </p>
</div>
) : (
<div className="history-list">
{history.map((item) => (
<button
className="history-card history-card-button"
key={item.id}
type="button"
onClick={() => setSelectedItem(item)}
>
<div className="history-head">
<div>
<p className="panel-kicker">
{item.playedAt} / {item.dayLabel}
</p>
<h3>
{item.leftTeamName} vs {item.rightTeamName}
</h3>
</div>
<span className="winner-badge">{item.winnerTeamName}</span>
</div>
<span className="winner-badge">{item.winner}</span>
</div>
<div className="history-meta">
<span>{item.matchDate || '-'}</span>
<span>{item.source === 'db' ? 'DB' : item.source === 'manual' ? '手動' : '-'}</span>
<span> {item.groupId} </span>
<span>{item.scoreLeft} - {item.scoreRight}</span>
</div>
</article>
))}
</div>
)}
</article>
</section>
<div className="history-meta">
<span>{item.score[0]} - {item.score[1]}</span>
<span>{item.typeLabel}</span>
<span>{item.winScore}</span>
</div>
</button>
))}
</div>
)}
</article>
</section>
{selectedItem ? (
<HistoryReplayModal item={selectedItem} onClose={() => setSelectedItem(null)} />
) : null}
</>
)
}
type HistoryReplayModalProps = {
item: HistoryListItem
onClose: () => void
}
function HistoryReplayModal({ item, onClose }: HistoryReplayModalProps) {
return (
<div className="history-modal-overlay" role="presentation" onClick={onClose}>
<div aria-modal="true" className="history-modal" role="dialog">
<p className="panel-kicker"></p>
<h3>
{item.leftTeamName} vs {item.rightTeamName}
</h3>
<div className="history-modal-score">
<div>
<strong>{item.score[0]}</strong>
<span>{item.leftTeamName}</span>
</div>
<div className="history-modal-score-divider">:</div>
<div>
<strong>{item.score[1]}</strong>
<span>{item.rightTeamName}</span>
</div>
</div>
<div className="history-modal-summary">
<span>{item.playedAt}</span>
<span>{item.typeLabel}</span>
<span>{item.winnerTeamName}</span>
</div>
<div className="history-replay-list">
{item.scoreList.length === 0 ? (
<p className="history-replay-empty"></p>
) : (
item.scoreList.map(([round, starter, winCount, winner]) => (
<div className="history-replay-row" key={`${item.id}-${round}`}>
<span> {round + 1} </span>
<span> #{starter + 1}</span>
<span> {winCount + 1}</span>
<strong>{winner === 0 ? item.leftTeamName : item.rightTeamName}</strong>
</div>
))
)}
</div>
</div>
</div>
)
}