43 Commits
Author SHA1 Message Date
JianMiauandClaude Fable 5 cb01c14421 功能:結算上傳戰績時自動幫忘記報名的上場球員補報名
摘要:
- POST /api/history 寫入戰績後,檢查上場 4 人是否在當天 attendance 名單,
  沒報名的自動補:有 TG 綁定用 tg_members 的真實 user_id 與名稱,
  沒綁定用名字 md5 前 6 bytes 取負的固定 id(與歷史匯入規則一致,已驗證)
- 名字比對沿用出席統計規則(別名表+大小寫不分),輪空等佔位字不補
- 前端上傳成功後浮出「已幫 ○○ 補報名」提示,6 秒自動消失
- 補報名失敗只記 log,不影響戰績上傳

根本原因:
- 報名靠 TG bot,臨時上場的人常忘了報名,出席統計就少算;
  記分板結算時已經知道誰上場,是補報名最準的時機

影響:
- 跨裝置記分也適用,出席統計(attendance 表)不再漏掉臨時上場的人
- 同一場重複上傳不會重複補(已在名單就跳過)

修法:
- server.mjs 新增 ensureAttendanceSignups / getTaipeiPollDate / syntheticUserId
- types.ts 的 HistoryUploadResponse 增加 addedSignups
- App.tsx 新增 signupNotice 狀態與浮動提示

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-20 13:51:55 +08:00
JianMiauandClaude Fable 5 6f38c155f0 功能:首頁改為 V2 場地排程,載入與手動產生皆為 2 場地各 8 局並依實力配對
摘要:
- 從資料庫讀取 match-hub V2 資料(battlecombination 以場地為 key)時,
  顯示「2 號場地」「3 號場地」各 8 局賽程,挑一個場地帶進記分板
- 手動分組移除 A / B 區,改為「今日出席名單」+「其他區名單」,
  演算法移植 match-hub V2:依 tg_members.skill 實力強弱互補、兩隊平衡,
  搭檔輪完一遍前不重複、每人局數落差最多 1 局;8 人以上排兩場地
- server 新增 GET /api/skills(表名可用 DB_MEMBERS_TABLE 覆寫)
- 選隊面板預設隊伍照局分組並顯示局數標題;修正帶入現有對戰時
  第 2 隊不顯示「已放入」且點擊無法取消的問題(配對比對改為不分順序)
- 舊 V1 三輪資料(personnel 含 B 區)仍照「第 N 組」顯示,自動判別

根本原因:
- 實際打球流程是 2、3 號場地各打 8 局、每局換搭檔輪休,
  舊的三輪配對制不符流程,也完全沒考慮實力;
  選隊面板的隊伍比對寫死正序,帶入現有對戰時右隊兩人反序就對不上

影響:
- 首頁載入與手動產生的結果格式一致(N 號場地 × 8 局),操作流程不變
- 舊版 A / B 區的 localStorage 內容自動合併進今日出席名單

修法:
- match.ts 移除跨區平衡三輪邏輯,移植 buildSchedule/pickBestMatching 等演算法,
  新增 buildCourtGroups 輸出場地格式;RoundGroup 增加 label
- convertDbRecordToGroups 依 personnel 是否含 B 區自動分流 V1 / V2
- ScoreboardPage 的 getPresetTeamSelectionSlot / removePresetTeamFromDraft
  改為兩人同隊即成立、不看順序

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-20 12:19:53 +08:00
JianMiauandClaude Fable 5 6ae1eda7e2 功能:選隊面板新增「同步今日場次」,從 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>
2026-08-18 11:33:54 +08:00
JianMiauandClaude Fable 5 d53ebbd3a8 功能:出席率統計改以 attendance 表(TG 報名名單)為資料來源
摘要:
- 出席統計改讀 attendance 表(每天每人一列),依 poll_date 分組後餵給原本的統計函式
- 新增 DB_ATTENDANCE_TABLE 環境變數,預設 attendance;沒有此表時退回 badminton 分組名單
- 前端說明文字與 README 更新來源說明

根本原因:
- 原本用 badminton 表的每日分組名單,只有名單存進 DB 的日子才有資料,
  當天報名還沒存名單就統計不到,名字寫法也要靠別名表修補

影響:
- 統計天數 121 → 126 天,含當天報名尚未分組的場次
- 名字寫法在 attendance 表已統一,別名合併(卡森→景涵 等)仍生效

修法:
- server.mjs 新增 loadAttendanceDays(),buildAttendanceStats() 改吃 [{ time, names }]
- .env.example / docker-compose.yml / README 補上 DB_ATTENDANCE_TABLE

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-17 13:50:59 +08:00
JianMiauandClaude Opus 4.8 641e51c887 功能:手動分組改為跨區平衡配對,偶數總人數不再補輪空
根本原因:
原本配對只以人數多的一區為基準,缺額全部補「輪空」。例如 A 區 7 人、B 區 5 人會補 2 個輪空,但總人數 12 是偶數,其實可以讓 A 區 1 人跨區支援就湊滿 6 隊真人對打。

影響:
兩區差距 2 以上時,每輪自動抽「差距÷2」人跨區支援,且每輪輪替不同人,跨區負擔平均分攤;只有總人數為奇數時才會補 1 個輪空。等人數時行為不變,三輪之間照舊旋轉配對讓搭檔不同。

修法:
自 badminton-match-hub 8cc0518 移植 balanceZones / pickCrossMembers,buildManualGroups 改為每輪先平衡兩區再建隊伍,補位改在平衡後只補至多 1 個。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-10 15:47:38 +08:00
JianMiauandClaude Opus 4.8 37fa1446af 功能:首頁新增其他區名單,可在記分板臨時選上場但不參與配對
根本原因:
現場常有臨時來打球的人,不屬於 A、B 區的正規輪替名單;原本要讓他們上場只能把名字塞進 A 或 B 區,但這樣會被排進對戰組合,打亂正規配對。

影響:
首頁 A、B 區下方新增跨整行的「其他區名單」輸入框,內容存 localStorage 且不會被資料庫載入蓋掉;分組配對完全不使用其他區。記分板選隊伍時,其他區的人排在清單最後、名字旁掛「其他區」徽章,可手動勾選上場(含與正規名單混搭),今日場次照計;「自動選擇」不會選到他們,自動輪替仍以正規名單為準。

修法:
App 新增 areaOtherInput 狀態與 localStorage 持久化,parseRoster 後以 extraPlayers 傳入 ScoreboardPage;selectablePlayers 併入其他區人選並用 extraPlayerSet 標記,autoPickDraftPlayers 過濾掉其他區,TeamPickerModal 依標記顯示徽章;TeamSelectionPage 新增第三個輸入欄位與說明文字。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-10 15:22:17 +08:00
JianMiauandClaude Opus 4.8 710d04bffe 功能:新增出席率統計,含每日人數分析分頁與名字別名合併
根本原因:
歷史戰績只有逐場比賽紀錄,看不出每個人長期的出席狀況,也不知道每天平均來幾個人;且名單上同一人有多種寫法(大小寫、錯字),直接統計會被拆成多個人而失真。

影響:
歷史戰績頁新增「出席率統計」按鈕,彈窗含兩個分頁——「出席排行」顯示每人出席場次、全期/加入後出席率長條與最近 12 場到場數;「每日人數」顯示全期與最近平均、單場最多最少、年度平均人數、人數分佈與最近 12 場逐日人數。名字自動正規化:英文大小寫視為同一人,中文錯字用別名表合併(景函、卡森→景涵,振華→振驊,阿翔→昱翔),佔位字(輪空、那個、隨機)不列入統計,平均每場人數也一併排除佔位字。

修法:
後端新增 GET /api/attendance,從 badminton 表的每日分組名單統計(history 表只有記分場次會漏算出席),buildAttendanceStats 輸出球員列表、dailyCounts 與含年度平均的 yearTotals;前端 HistoryPage 新增 AttendanceModal 與分頁切換,App.css 補上排行長條與每日分析的樣式。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-10 15:21:37 +08:00
JianMiauandClaude Opus 4.8 68618a8e04 功能:記分板側欄新增得分紀錄清單,並讓左右兩欄底部切齊
根本原因:
計分時只能看到當前比分,沒有逐球紀錄可回顧;且右側欄在「比賽結算」下方留有大片閒置空間。原本 .scoreboard-screen 用 align-items: start,記分板維持自然高度、側欄卻撐滿,導致兩欄底部不齊。

影響:
計分頁右側欄在比賽結算按鈕下方顯示逐球得分表:每列一球,得分隊亮綠標記並顯示當下比分,最新在最上、可上下捲動看舊資料;記分板與側欄底部切齊、不超出畫面,多出的高度分配給上下分數面板,中間橫幅維持原高度。

修法:
App 把既有 pointLog 傳入 ScoreboardPage,新增 pointHistoryRows 反序累計比分並渲染 rail-history 清單;CSS 將 .scoreboard-screen 改 align-items: stretch(配合 align-self: start + min-height 100% 讓內容過高時可自然長高不重疊),.scoreboard-court 以 minmax(min-content, 1fr) 把撐高空間讓給隊伍區與分數面板;行動版清單改跨滿整行並以 32dvh 限高。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 14:11:22 +08:00
JianMiauandClaude Fable 5 540050a991 功能:自動選隊排除「那個」、選隊面板顯示每人今日場次
摘要:
- 記分板「自動選擇」時排除名字為「那個」的球員,手動仍可勾選
- 結算完成(上傳或不上傳)時幫上場 4 人各記一場,輪空不計
- 選球員面板每位球員名字旁顯示「今日 N 場」徽章

根本原因:
- 名單裡的「那個」是佔位用名字,自動選擇會把它當一般球員排進場
- 現場排下一場時沒有依據看誰打得多、誰打得少,容易分配不均

影響:
- src/App.tsx:新增 playCounts state 與 localStorage 持久化(跨日自動歸零)、recordPlayedMatch() 於 skipUpload 與 uploadSettledMatch 成功時計數、傳 playCounts 給記分板
- src/pages/ScoreboardPage.tsx:新增 AUTO_PICK_EXCLUDED_NAMES 常數過濾自動選擇、選隊 modal 每列加「今日 N 場」徽章
- src/App.css:徽章樣式與選項改三欄 grid(含手機版縮小字級)

修法:
- 自動選擇先以排除名單過濾 selectablePlayers 再洗牌取 4 位
- 場次以 {date, counts} 存 localStorage,載入時日期非當天即歸零
- 計數以 activeMatchup 兩隊為準,isPlaceholder 的輪空欄位跳過

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-14 11:18:44 +08:00
JianMiauandClaude Fable 5 1bc42f5bf6 功能:SSL 憑證過期偵測與提醒、歷史戰績改為分頁載入
根本原因:
1. 憑證過期時瀏覽器只回 Failed to fetch,前端無法辨識原因,使用者誤以為是 DB 連不上(本次 7/1 憑證過期即是如此)。
2. 歷史戰績一次撈出全部資料(400+ 筆、逾 100KB),筆數持續成長會越來越慢。

影響:所有 API 的連線錯誤訊息、歷史戰績頁載入速度、Docker 憑證掛載路徑與檔名。

修法:
- 後端讀取 cert.pem 到期日(60 秒快取),由 /api/version 與 /api/health 回傳 certNotAfter
- 前端把到期日存進 localStorage;fetch 失敗時依離線、憑證已過期、其他連線問題分別顯示明確訊息
- 憑證到期前 7 天於頁面跳出「SSL 憑證提醒」通知,可手動關閉
- /api/history 支援 page/pageSize(預設 20 筆、上限 100),回傳 total 與 totalPages
- 歷史戰績頁加入分頁控制列(上一頁/頁碼含省略號/下一頁/總筆數),刪除後自動重新載入並修正超出範圍的頁碼
- 憑證掛載目錄改為 /volume1/docker/certs,檔名改為 cert.pem / chain.pem / privkey.pem,app 容器同步掛載以讀取到期日

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 15:44:44 +08:00
JianMiauandClaude Fable 5 13f3bf5198 修正:changelog 產生器保留既有紀錄,避免部署後更新紀錄被清空
根本原因:Docker build 內沒有 .git,generate-changelog 讀不到 git log 時會把 changelog.json 覆蓋成空清單,導致正式站的更新紀錄分頁變成空白。

影響:正式版更新紀錄分頁的資料完整性;package.json 的 npm start 行為與縮排格式。

修法:
- git 取不到資料(或 0 筆)且既有 changelog.json 非空時,直接沿用不覆蓋
- package.json 的 start 改為 npm run dev,縮排統一為 tab

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 15:44:42 +08:00
JianMiauandClaude Opus 4.8 40cb2f01d7 功能:新增更新紀錄分頁、勝利後鎖記分與結算後自動選隊、報分語音改唸個位數
摘要:
- 新增「更新紀錄」分頁,依 git commit 自動產生日誌(scripts/generate-changelog.mjs → src/data/changelog.json)
- 比賽分出勝負後鎖住記分按鈕,需先結算才能開下一場
- 結算(上傳或不上傳)完成後自動打開選隊伍面板並清空已選,方便直接排下一場
- 得分播報只唸個位數(10→0、19→9、20→0),同分改唸「N 平」

根本原因:
- 想在 App 內直接看到版本更新內容,原本沒有頁面
- 勝利後仍可繼續點加分,容易誤觸超過該局比分
- 一場打完要手動回去重設下一場對戰,流程多一步
- 報分連十位數一起唸(十九、二十)冗長,現場聽不直覺

影響:
- 新增 scripts/generate-changelog.mjs、src/pages/ChangelogPage.tsx;package.json 加 predev/prebuild/gen:changelog;tsconfig.app.json 開 resolveJsonModule;.gitignore 排除產生的 changelog.json
- src/types.ts 新增 ChangelogEntry/ChangelogData;src/App.tsx 加導覽與路由、結算後發 nextMatchSignal、recordPoint 加勝負防呆
- src/lib/match.ts 將 hasWonGame 抽出並 export 供記分板共用
- src/pages/ScoreboardPage.tsx:勝負時 canScore=false、收到 nextMatchSignal 自動清空並打開選隊 picker、報分組字改個位數與「平」
- src/App.css 新增更新紀錄卡片樣式

修法:
- changelog 於 dev/build 前自動產生,正式版讀打包後的 JSON 顯示
- hasWonGame 統一判斷(達標分/Deuce 領先 2 分/30 分上限),App 與記分板共用
- 結算成功才遞增 nextMatchSignal,記分板以「render 期間依 prop 變化調整 state」開啟選隊面板
- 報分以 servingScore % 10 / opponentScore % 10 組字,同分輸出「N 平」

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 12:15:28 +08:00
JianMiauandClaude Opus 4.8 304bcaeedd 修正:dev 環境改善(vite 對網路磁碟啟用 polling、Service Worker 僅正式版註冊)
摘要:
- vite dev server 啟用 watch.usePolling,讓 W: 網路磁碟(RaiDrive)的檔案變更能觸發 HMR
- Service Worker 改為僅在正式版(import.meta.env.PROD)才註冊,dev 不再註冊

根本原因:
- 專案放在 RaiDrive 掛載的網路磁碟,不會發出 fs 事件,vite 預設的檔案監看抓不到變更,HMR 失效
- dev 也註冊 SW 會用快取卡住舊版 bundle,改了程式看不到效果

影響:
- vite.config.ts:server.watch 加上 usePolling 與 interval
- src/main.tsx:SW 註冊條件加上 import.meta.env.PROD

修法:
- 開發時靠 polling 監看檔案;SW 只在 build 後的正式環境啟用

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 17:42:22 +08:00
JianMiauandClaude Opus 4.8 9eb60baa37 功能:記分板語音改為報比分、發球區、賽末點與獲勝隊伍
摘要:
- 得分播報改為「比分(發球方先報)+ 發球者左右發球區」
- 發球方到賽末點(再得 1 分即獲勝)時,比分後加唸「賽末點」
- 賽末點得分獲勝時,整段改播「<獲勝隊伍> 贏得比賽」
- 發球區左右一律用實際球場方向,取消上方隊伍鏡像;畫面「發球區」顯示同步改為不鏡像,與語音一致

根本原因:
- 現場記分需要即時聽到比分與發球位置,原本只唸「誰得分、誰發球」較不直覺
- 先前發球區對上方隊伍做鏡像,導致語音與實際球場方向相反

影響:
- src/App.tsx:recordPoint 計算發球方比分、發球區、賽末點與獲勝隊伍,重整 voiceAnnouncement 欄位
- src/pages/ScoreboardPage.tsx:語音組字改為「X比Y(賽末點)」「OO左/右邊發球」、獲勝改播「贏得比賽」;發球區顯示移除鏡像;語音設定「播報誰得分」更名「播報比分」

修法:
- 發球方分數先報;賽末點僅在發球方再得 1 分就獲勝時觸發;發球區統一用 getServiceCourt 實際方向

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 17:42:05 +08:00
JianMiauandClaude Opus 4.8 091e654bdb 功能:首頁日期預設當天、記分板語音改為報比分與發球區
摘要:
- 首頁「指定日期」每次進入都預設為當天
- 記分板語音得分播報改為「比分(發球方先)+ 發球者左右發球區」,賽末點獲勝時整段改播「贏得比賽」

根本原因:
- 日期原本從 localStorage 讀上次選的值,重開會停在舊日期
- 語音原本只唸「誰得分、誰發球」,現場較不直覺,球敘想聽到即時比分與發球位置

影響:
- src/App.tsx:targetDate 改為直接用當天、移除日期的 localStorage 記憶;recordPoint 算出發球方比分與發球區,重整 voiceAnnouncement 欄位
- src/pages/ScoreboardPage.tsx:語音組字改為「X比Y」「OO左/右邊發球」,獲勝時改播「贏得比賽」;語音設定「播報誰得分」更名「播報比分」

修法:
- 發球方分數先報;上方(左側)隊伍發球區做左右鏡像以對齊畫面,下方隊伍不鏡像

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 15:33:22 +08:00
JianMiau 3677162747 修正記分板在 iPad 尺寸的版面與修復 dev server 啟動問題 2026-05-18 17:40:58 +08:00
JianMiau 30a8e1a44c 調整記分板手機滿版模式並更新說明 2026-04-28 18:17:41 +08:00
JianMiau f3e51ea83d 調整發球鏡像規則並更新說明文件 2026-04-28 08:52:47 +08:00
JianMiau edab74f125 強化房間清理與比賽中分頁限制 2026-04-19 18:05:33 +08:00
JianMiau 2d1ad0600e 補上房間關閉通知與列表重取 2026-04-19 13:02:00 +08:00
JianMiau ab3647587e 調整結算通知與全站防選字 2026-04-19 12:54:18 +08:00
JianMiau 896c24547b 新增即時觀戰房間並整理 README 2026-04-19 12:46:59 +08:00
JianMiau c097ceb9ad 更新圖示與部署更新機制並整理 README 2026-04-16 20:35:31 +08:00
JianMiau 36a39f0b8f 調整比賽結算長按回饋並更新 README 2026-04-16 20:06:26 +08:00
JianMiau 975732017f 新增 PWA 更新提示並整理 README 2026-04-16 19:57:08 +08:00
JianMiau 0cfcdc3b0a 調整歷史戰績刪除提示並更新 README 2026-04-16 18:14:56 +08:00
JianMiau d5fee1d299 補上歷史戰績刪除二次確認 2026-04-16 17:22:23 +08:00
JianMiau b2494fff17 調整語音設定介面並更新 README 2026-04-16 17:11:11 +08:00
JianMiau 860e7adc0e 新增連勝與獲勝特效並更新 README 2026-04-16 16:54:59 +08:00
JianMiau b3809b5d4f 補上先攻勾選顯示並更新 README 2026-04-16 16:49:02 +08:00
JianMiau af7ebda9f4 新增歷史戰績關閉按鈕並更新 README 2026-04-16 15:23:09 +08:00
JianMiau f50b11600e 調整分組卡片版面並更新 README 2026-04-16 14:37:32 +08:00
JianMiau 4a66f37e1f 調整選隊伍頁操作並更新 README 2026-04-16 12:37:38 +08:00
JianMiau 98c289ea5b 精簡選隊伍頁提示並更新 README 2026-04-16 10:55:41 +08:00
JianMiau bbedb70e7e 新增歷史戰績刪除功能並更新 README 2026-04-16 10:26:58 +08:00
JianMiau 31168e830b 整理設定隊伍介面並更新 README 2026-04-16 10:19:23 +08:00
JianMiau a1e0e0f16e 調整設定隊伍流程並支援勝利分數設定 2026-04-16 08:53:05 +08:00
JianMiau e903d3ae52 調整歷史戰績彈窗顯示文字 2026-04-16 07:48:15 +08:00
JianMiau 7965c234df 修正 SSL 憑證拼接換行問題 2026-04-16 07:34:13 +08:00
JianMiau 7a8436db47 調整 Docker SSL 入口與部署說明 2026-04-15 23:15:52 +08:00
JianMiau b0908b4d3c 補上歷史戰績列表與 NAS 部署說明 2026-04-15 23:04:16 +08:00
JianMiau 7fc8e2698b Refine scoreboard flow and update ports 2026-04-15 22:56:50 +08:00
JianMiau 8f4411d97e chore: initialize Vite React TypeScript app 2026-04-15 16:55:54 +08:00