⚡
cache-first 渲染
先讀 localStorage 立即畫月曆,背景靜默更新後再 re-render。
體感秒開,又不會看到過期資料超過 5 分鐘。
🧮
狀態前端算
「成團狀態」不存在 Sheet,由前端用 min/max/booked 即時算。教練只要維護「最低/最高人數」,狀態自動更新。
🔌
單 endpoint 多 sheet 路由
一支 GAS 用 ?sheet=xxx 派發到不同分頁;POST 用 action 區分 insert/update/delete。
不用每個表單一支 GAS。
🧩
可注入式條款模組
agreement-flow.js 把「同意 → 解鎖區塊」抽成獨立元件,自帶 CSS。
任何表單頁加兩行就能套上。
📞
電話當主鍵
查詢、繳費、刪除全靠電話。
還處理 Sheet 把 0912 存成 912 的問題(自動補 0)。
💸
繳費 = 改狀態
不串金流,繳費只是把 Sheet 上一列從「未繳費 → 待確認」,教練看到匯款後手動改成「已確認」。
零金流串接成本。
🪄
資料分動靜兩種
會變的(行事曆/預約)放 Sheet;不太變的(泳池清單/價格)放 pools-data.js 純前端。
減少 GAS 讀取量。
🛡️
no-cors fire-and-forget
POST 用 mode:'no-cors' + text/plain,避開 CORS preflight。
代價:拿不到 response,只能假設成功。
⚠️
弱驗證的隱憂
SF_TOKEN = 'sf_9Kx7mBpQ4rL2nWv' 寫在前端 → 任何人都能拿去打 GAS,可惡意刷單或洗 Sheet。
改進方案:GAS 端加 rate limit(同 IP 60s 內最多 N 筆),或用 Cloudflare Worker 當 proxy 套 Turnstile。
🔒
並發寫入隱患
週末晚上同時 5 個人按「確認預約」,GAS 沒加 LockService.getScriptLock() 會同列覆寫。
也沒做「滿團就拒絕」server-side 檢查,全靠前端 isFull。
📨
缺通知
學生報名 / 繳費後沒收到 Email / LINE 確認,只能去查詢頁自己看。
GAS 可內建 MailApp.sendEmail(),或接 LINE Notify webhook(這你最熟)。
🎯
許願 → 排課 的設計很聰明
不是直接讓學生開團,而是收集需求,由教練彙整後排出有勝算的場次。
避免「一堆冷門時段都開了沒人來」的浪費,這個 pattern 你做 MF 課程也可以用。
🎨
UI 風格統一
所有頁面共用 6 個 CSS variable(--bg --surface --border --text --muted --accent),加上紫色漸層 shimmer 動畫當品牌記憶點。
但 CSS 沒抽出來複用,每頁 inline 一份。
📦
build-less 的代價
group.html 已經 104KB(inline JS 47KB)。
再大下去就要拆 module,或乾脆上 Vite/Astro。
你目前所有專案還沒到這個量,繼續 vanilla 沒問題。