功能:選隊面板新增「同步今日場次」,從 history 表撈今天資料覆蓋本機場次

摘要:
- 選隊伍面板新增「同步今日場次」按鈕,按下後把每個人的今日場次更新成 DB 的數字
- 後端新增 GET /api/history/play-counts?from=&to=,回傳區間內每人上場場數與比賽數
- 按鈕上方顯示同步結果或錯誤訊息;手機版同步按鈕獨占一列

根本原因:
- 今日場次只存在本機 localStorage,跨裝置記分時其他裝置上傳的場次本機看不到,
  自動選擇與「今日 N 場」提示就不準

影響:
- 任一裝置按同步後,今日場次會與 DB history 表一致(以 DB 為準,覆蓋本機)
- 輪空/那個等佔位字不計場次

修法:
- server.mjs 新增 play-counts 端點,依 time 區間查 history.players 累計
- api.ts 新增 loadHistoryPlayCounts;App.tsx 以本機時區當天 00:00 起算 24 小時區間並 setPlayCounts
- ScoreboardPage 加入同步狀態與按鈕,App.css 調整按鈕列為三欄、≤720px 改兩列

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-18 11:33:54 +08:00
co-authored by Claude Fable 5
parent d53ebbd3a8
commit 6ae1eda7e2
7 changed files with 186 additions and 1 deletions
+57
View File
@@ -528,6 +528,63 @@ app.get('/api/history', async (request, response) => {
}
})
// 指定時間區間內(通常是「今天」)每個人在 history 表打了幾場,
// 讓不同裝置記分後可以把本機的今日場次同步成 DB 的數字。
app.get('/api/history/play-counts', async (request, response) => {
if (!pool) {
response.status(500).json({
ok: false,
message: `DB 尚未設定完成,缺少 ${missingEnv.join(', ')}`,
})
return
}
const from = Number.parseInt(request.query.from, 10)
const to = Number.parseInt(request.query.to, 10)
if (!Number.isFinite(from) || !Number.isFinite(to) || from >= to) {
response.status(400).json({
ok: false,
message: '時間區間格式不正確。',
})
return
}
try {
await ensureHistoryTable(pool, historyTableName)
const [rows] = await pool.execute(
`SELECT players FROM \`${historyTableName}\` WHERE time >= ? AND time < ?`,
[from, to],
)
const counts = {}
rows.forEach((row) => {
parseAttendanceNames(row.players).forEach((name) => {
// 輪空等佔位字不是真的有人上場。
if (ATTENDANCE_IGNORED_NAMES.has(name)) {
return
}
counts[name] = (counts[name] ?? 0) + 1
})
})
response.json({
ok: true,
data: {
counts,
matches: rows.length,
},
})
} catch (error) {
console.error('history play-counts error:', error)
response.status(500).json({
ok: false,
message: error instanceof Error ? error.message : '讀取今日場次失敗。',
})
}
})
app.delete('/api/history/:id', async (request, response) => {
if (!pool) {
response.status(500).json({