Skip to main content
VIP Day Snapshot Wed–Fri onlyi
Daily GMV Trend Per-store · VIP days marked with amber ring
Daily Orders & ATV Orders (blue = normal, amber = VIP) + ATV line
Weekly GMV Week starting Monday
Weekly Orders & ATV Per-week combined
GMV Share by Store Share of GMV
Top 10 Brands by GMV
Top 10 Delivery Districts by Orders
Promo Performance Snapshot Mix & Match + Add-on promotions · Current filter
Top 10 Promos by GMV · dynamic to filter
Top 10 Subcategories by GMV · dynamic to filter
Promo Detail Rank / Name / Store split / Orders / GMV / Disc%
Subcategory Detail Rank / Subcategory / Store split / Qty / GMV
Top 10 SKUs by GMV
Store × Date Breakdown Daily per store
Daily Combined Both stores
Weekly Summary Per-store + combined + VIP-only slice
| Week Starting (Mon) | Range | Days | Store |
Orders | GMV | ATV | Qty | Sub-orders |
▶ 系統更新 · 最後更新 2026-05-02 08:37
2026-05-02 08:37 修復
- **S-4:CSP `unsafe-inline` 移除(nonce-based script-src)**:`functions/_middleware.ts` 重寫 CSP 方案
- 每個請求生成獨立的 16-byte 加密 nonce(base64url 編碼)
- `HTMLRewriter` 自動為所有 HTML 回應中的每個 `<script>` 和 `<style>` 標籤注入 `nonce=...` 屬性
- `script-src` 從 `'unsafe-inline'` 改為 `'nonce-{nonce}'`,封堵所有未授權 inline script 注入(XSS 核心防線)
- `style-src` 保留 `'unsafe-inline'`(因大量 `style="..."` 屬性需要)
- `script-src` 額外允許 `cdn.jsdelivr.net`(Chart.js CDN fallback 用)
- `applySecurityHeaders()` 改為接受 nonce 參數,`injectNonce()` 僅對 text/html 回應執行 HTMLRewriter transform
2026-05-02 08:25 修復
- **v9 Phase 2 安全盲區修復(S-37/S-30/S-31/S-32/S-35/S-20)**:
- **S-37(P1)** `upload-stock.ts` 新增 `sanitizeStoreKey()`,merchant_id 只允許英數字 + 連字符 + 下劃線(最多 64 字元),徹底封堵 `../` 注入及非預期 R2 key
- **S-30** catch 錯誤訊息改為通用「Failed to save stock data.」,不再暴露 R2 內部細節
- **S-31** 新增 `MAX_STOCK_ROWS = 50000` 上限,超出返回 413
- **S-32** 新增 `MAX_RAW_CSV_BYTES = 10MB` 限制,超出返回 413
- **S-35** `strip-raw.ts`、`consolidate-shards.ts`、`preagg-months.ts` 三個破壞性維護端點從 `requireRole("editor")` 升級為 `requireRole("admin")`,防止低權限用戶誤觸
- **S-20/S-27** `storage.ts` `newUploadId()` 改用 `crypto.randomUUID()` 替代 `Math.random()`,upload ID 熵提升至 128-bit
- **v9 Phase 2 UX 修復(U-46/U-48)**:
- **U-46** Inventory SKU Detail 新增「⬇ Export CSV」全量匯出按鈕,匯出目前篩選店鋪的所有 SKU(含 Stock / Safety Stock / Daily Sales / Days Cover / OOS Days / Status),檔名自動帶入店鋪名稱與日期
- **U-48** Inventory SKU Detail 表頭全部改為可點擊排序(SKU ID / SKU Name / Category / Stock / Safety Stock / Daily Sales / Days Cover),點擊切換升序/降序,排序指示符 ▲/▼ 顯示;同時新增 Days Cover 欄位
2026-05-02 07:56 優化
- **重建進度 Banner 改進**:Stale data banner 新增 GitHub Actions 自動重建說明 + 實時倒計時(~30s 倒數至 0 後顯示 "almost done…");正常重建時隱藏「Rebuild Now」按鈕(僅出錯時顯示);倒計時基於上傳時間計算,即使用戶延遲打開頁面也能顯示準確剩餘時間
- 部署:https://8bcc8d76.hktv-dashboard.pages.dev
▶ 更多歷史記錄 (78 條)
2026-05-02 00:10 新功能
- **GitHub Actions 自動 Rebuild(終極方案)**:CF Worker Free Plan CPU 10ms 限制無法完成 aggregate,改為上傳成功後 CF Worker 通過 `repository_dispatch` 觸發 GitHub Actions 在雲端運行 rebuild
- 新增 `.github/workflows/rebuild.yml`:觸發類型 `repository_dispatch`(type: rebuild)+ `workflow_dispatch`(手動);步驟:Checkout → Setup Node 22 → npm ci → `npx tsx rebuild_local_node.mjs`
- `functions/api/upload.ts` 修改:waitUntil 邏輯改為雙層 —— Primary: 通過 GitHub API `POST /repos/{owner}/{repo}/dispatches` 觸發 Actions;Fallback: CF Worker 同步/異步 rebuild(GitHub dispatch 失敗時)
- `functions/lib/auth.ts` 新增 Env interface 欄位:`GITHUB_TOKEN`、`GITHUB_OWNER`、`GITHUB_REPO`(需在 CF Pages Dashboard → Settings → Environment variables 設定)
- 修復過程中解決的錯誤:`refusing to allow PAT...workflow scope`(Token 權限不足,重新生成包含 workflow scope)、`npm ci` 失敗(Mac 生成的 lock 檔案缺 Linux 套件,改用 `npm install` 更新)、`ERR_REQUIRE_CYCLE_MODULE`(Node.js 22 ESM 循環依賴,改用 `npx tsx` 替代 `node --import=tsx/esm`)
- 部署:https://d035ab61.hktv-dashboard.pages.dev
2026-05-01 14:57 新功能
- **Stock Level 多文件批量上傳**:`upload.html` Stock Level 上傳區改為支持多文件選擇(`<input multiple>`)。可同時選取兩個或以上店鋪的 CSV 文件,系統會依次串行上傳每個文件,每步顯示進度 `[1/2]` / `[2/2]`,最後彙總成功/失敗數量。拖拽也支持多文件。
- 部署:https://b91b19d8.hktv-dashboard.pages.dev
2026-05-01 14:39 新功能
- **多店铺库存数据支持**:修复上传 Pretties 店铺库存报告会覆盖 Wagaya 店铺数据的问题。后端 `upload-stock.ts` 改为将 `stock_level` 存储为按店铺 ID 索引的对象(`stock_level[store_id] = storeData`),支持多店铺数据并存。前端 `inventory.html` 改为遍历所有店铺数据,合并 SKU 列表并显示汇总。控制台日志现在会显示 "Loaded X SKUs from Y stores"
- 部署:https://81c9ceae.hktv-dashboard.pages.dev
2026-04-29 22:00 新功能
- **SmartRebuild 上傳即更新**:`storage.ts` 新增 `smartRebuild(env, uploads, affectedMonths)`,只 load 受影響月份嘅 consolidated shards(~8-15K rows/月),同步在 upload Worker 內完成 aggregate
- upload.html 移除多餘嘅 /api/rebuild batch call,直接讀 upload response 嘅 rebuild status
- 效果:upload 1-5 天數據後 dashboard 即時更新,無需另按 Rebuild
2026-04-28 01:33 新功能
- Upload 頁 Upload History 新增**日期篩選**:最近 7 日 / 30 日 / 90 日 / 全部 快捷 pill + 自訂日期範圍(from → to)
- 右側顯示「N / 總數 筆」計數,切換篩選即時刷新唔使重新 request API
- 數據一次載入後 client-side filter,零額外網絡延遲
2026-04-26 23:50 新功能
- Monthly Report 新增 **MoM 趨勢對比 + Category Breakdown**
- KPI Grid 加 `↑ +X.X%` / `↓ -X.X%` MoM badge,Daily Rhythm 加上月同期灰色虛線
- Store Compare 加 GMV/Orders/ATV 上月值 + MoM badge
- 新增 Section 13 Category Breakdown:品類 card grid + 橫向 bar chart + GMV↔Qty toggle
2026-04-26 23:16 新功能
- Monthly Report 7 條 Action Plan 全面改為 **data-driven**:Top SKU / 品牌獨家分析 / MoM 品牌環比全部用真實數據生成
- 新增 Action 08(退貨率最高品牌),從 `cancel.by_brand` 抽取異常品牌
2026-04-26 23:49 新功能
- 全新 **🚚 Operation 頁面**(`/operation.html`),5 個 section:
- ① Fulfillment Speed Panel — Order-to-Door / Pickup / Delivery 時效 + SLA breach
- ② Delivery Mode 分析 — Express vs Pickup 佔比 & ATV
- ③ Warehouse 容量 & 錯配 — 主倉佔比 + Cross-warehouse %
- ④ Status Lifecycle Donut — 全狀態 donut + per-store stacked bar
- ⑤ Warehouse Heatmap — 每日 × 倉庫 stacked area chart
- 後端新增 `aggregate-operation.ts`,upload.html 加 9 個新欄位 mapping
- 所有 5 頁導航加 🚚 Operation 連結
2026-04-24 20:20 新功能
- Index 新增 **Hero KPI Strip**:大字 GMV + 3 個 mini KPI(Orders/ATV/Items)+ delta badge
- **Controls Bar**:store chips + date bar 合成一行 compact pill
- KPI grid 由 6 卡精簡為 4 卡(Sub-orders / Avg Items / Avg Sub / Best Day GMV)
- Hero strip 歷經 3 輪用戶 feedback 迭代:移除 sparkline→compact single-row→padding 微調
2026-04-24 20:00 新功能
- Compare 頁新增 **Quick Compare preset**:Last 7D vs Prev / Last 30D vs Prev / This Week vs Last / MTD vs Last Month
- URL param 支援:`?preset=last7&store=H0958013`(shareable link)
2026-04-26 18:12 新功能
- Insights 頁新增 **Section 7:Discount Depth + ROI**(100% 前端計算,無需 rebuild)
- 折扣深度分佈直方圖:6 個區間(0–5% / 5–10% / 10–20% / 20–30% / 30–40% / 40%+),雙軸(訂單數 + GMV)
- Discount Depth vs ROI Bubble 圖:每個 promo 一粒泡,橫=折扣率、縱=ROI、泡大=訂單量,3色分 ROI 區間
- Promo Cannibalization Panel:VIP Day vs 非 VIP Day 平均日 GMV / 訂單 / Uplift %,估算蠶食率
- 4 個 KPI cards:平均折扣率、有折扣訂單佔比、最高 ROI 促銷、最高 ATV 折扣區間
2026-04-26 16:42 新功能
- Insights 頁新增 **Section 6:Basket Combo Matrix**,自動識別最常共購 SKU 組合
- 演算法:Approach B Lift Score(市場籃分析標準);指標:Lift / Confidence / Co-count
- 噪音過濾:MIN_SUPPORT=3 / MIN_LIFT=1.2;輸出 Top 50 pairs(lift 降序)
- 前端:cross-brand toggle + min support selector,Lift pill 顏色分級(≥5 紅 / ≥2 黃 / ≥1.5 綠)
- 自動建議:🔥 強 Bundle / ⭐ Mix & Match / 💡 觀察,附 top 3 actionable insight 文案
- 後端 `aggregate-insights.ts` 新增 `buildComboMatrix()`,`aggregate.ts` 輸出 `insights.basket_combo`
2026-04-24 16:42 新功能
- Insights 頁新增 3 個 section:AI 智能洞察(自動 derive top 3-5 insight)、Promo ROI Leaderboard(識別賺錢 vs 蝕本促銷)、Day-of-Week Performance(VIP Day uplift 量化)
- Promo ROI 完全跟時段 + 店舖篩選,含整體 ROI / 客單提升 / Top 15 promo 排名
- DOW 顯示每個 weekday 平均 GMV/單量/ATV,VIP Day 標記 + 最強/最弱日 insight
2026-04-24 14:25 新功能
- 全新 **💡 Insights 頁面**(`/insights.html`):獨立分析頁,營運效率 + 收入提升盲點
- **Cancel Rate Leaderboard**:SKU / Brand / District 三維度,最少 10 單觸發,揾出取消率異常高嘅 SKU 即時跟進
- **Order Timing Heatmap**:星期 × 小時 7×24 grid,識別 peak slot 排 promo launch、避開 dead hour
- **Basket Size Distribution**:單 SKU vs 多 SKU 訂單拆分,顯示 cross-sell gap + AOV 機會(單 vs 多嘅 AOV 差距)
- 後端 `aggregate.ts` 新增 `insights` 區塊,rebuild 後即可用
2026-04-24 14:15 新功能
- Admin 頁新增 **User Activity** 面板,睇到邊位用戶而家喺線、睇緊邊個頁面、揀咗邊個 time period
- 每行可點擊展開 drill-down,顯示最近幾次 filter 變動(preset、日期範圍、附加 extra)
- 24h / 7d / 30d 時段切換 · 每 60 秒自動刷新 online 狀態
- 前端每頁加 heartbeat(只喺 tab 可見 + 近 5 分鐘有活動時發送,減低 R2 write)
2026-04-24 12:29 新功能
- Top SKUs card 加 GMV / Qty 切換,可以改變排名基準(之前只支援 GMV)
- 切 Qty 模式時,表格按銷量由高至低排序,subtitle 顯示 "by Qty"
2026-04-24 11:55 新功能
- Admin 頁新增 Role Reference 卡,清楚列出 Viewer / Editor / Super Admin 嘅能力範圍,方便指派角色
2026-04-24 10:26 新功能
- 頁腳新增可摺疊嘅系統更新日誌,用戶可隨時查閱改動歷史
2026-04-24 10:06 新功能
- 日期跨度超過 30 日嘅圖表,自動切換做週度匯總,更易睇出趨勢
- 所有趨勢圖加 ⛶ 放大按鈕,全螢幕細看都得
- Top Brands 加 **GMV / 銷量** 切換
- Top SKUs 加 **10 / 20 / 50 / 100** 數量篩選,表格可 scroll
- Promo 同 Sub-category 圖表改成並排 grouped bar,更緊湊
2026-04-24 02:02 新功能
- 預設時段由 All time 改成 **本月至今**(新增 MTD 快捷按鈕)
2026-04-24 01:40 新功能
- 新增 **促銷表現** 模組:Top 促銷、加購附加率、平均折扣率
- 新增 **子類別** 分析,支援逐日 drilldown
2026-04-24 09:57 新功能
- 新增 **🔄 Compare periods** 頁面(MoM / 自訂區間 / Movers & Shakers)
2026-05-02 07:46 優化
- **GitHub Actions Rebuild 性能優化**:`rebuild_local_node.mjs` Shards 下載改為並發(CONCURRENCY=10),`data.json` + `data-meta.json` 並行上傳;`.github/workflows/rebuild.yml` `npm ci` 改為 `npm ci --prefer-offline` 利用 GitHub Actions npm 快取;預期 rebuild 時間從 ~40s 降至 ~20-25s
- 部署:https://968fb516.hktv-dashboard.pages.dev
2026-05-01 14:45 優化
- **Inventory SKU 過濾:排除下架且隱藏的 SKU**:`inventory.html` 在加載 stock_level 數據後,過濾掉 `online_status = Offline` 且 `Invisible = Y` 的 SKU,令這些已下架且設為不可見的產品不計入任何庫存統計、圖表和 SKU 列表。控制台日誌新增提示:"excluded X offline+invisible SKUs"
- 部署:https://fbcb7070.hktv-dashboard.pages.dev
2026-04-30 22:30 優化
- **P-13 IDB 連接單例**:`api-client.js` 的 `idbOpen()` 改為快取 `_dbInstance`,複用 IndexedDB 連接而非每次新建,自動處理 `onclose` 事件重連
- **P-18 xlsx lazy-load**:upload.html 移除 862KB xlsx 全局 `<script defer>`,改為 `loadXlsx()` 動態加載——僅在用戶上傳文件時才下載,首屏 -862KB
- **P-19 html2canvas lazy-load**:monthly-report.html 移除 199KB html2canvas 全局 `<script defer>`,改為 `loadHtml2Canvas()` 動態加載——僅在用戶點擊「導出圖片」時才下載
- **P-28/29 innerHTML 批次更新**:`dashboard-utils.js` 新增 `queueWrite(id, html)` + `flushWritesSync()` 機制,insights / monthly-report / operation / index 四頁熱路徑 innerHTML 改為 `batchUpdate()` 批量寫入,消除 reflow thrashing
2026-04-30 22:00 優化
- **v7 Phase 1 Quick Wins(8/8 完成)**:
- S-14:auth.ts HMAC 比較從 `!==` 改為 `crypto.subtle.timingSafeEqual`,修復 timing attack
- S-21:AccessLogEntry 加 `full_reset` 事件類型,reset.ts 從 `upload_created` 改為 `full_reset`
- S-22:login.html `?next=` 增加 `//` 檢查,阻止開放重定向
- P-25:admin.html 3 個 vendor 腳本加 defer + inline script 移入 DOMContentLoaded
- P-30:4 個數據頁面 `cache: 'none'` → `cache: 'idb'` 啟用 IndexedDB 緩存
- U-34:upload.html + admin.html 加載 shared.css(統一 focus-visible 等樣式)
- U-35:upload.html + admin.html 導航從自定義 topnav/links 改為統一 app-nav
- U-41:刪除 upload.html 殘留 `input[type="password"]` CSS 和無用註釋
2026-04-30 20:00 優化
- **P-1:所有 vendor 腳本加 defer**,消除渲染阻塞——chart.umd.min.js / dashboard-utils / chart-helpers / api-client / modal 等 `<script>` 全加 `defer`,下載與 HTML 解析並行
- compare / operation / insights 三頁新增 `DOMContentLoaded` 包裹主腳本
- insights.html 兩個獨立 `<script>` IIFE 合併為同一 `DOMContentLoaded` 回調(消除 chartRegistry 跨 scope 問題)
- `activateAppNav()` 從獨立 inline `<script>` 移入各頁 DOMContentLoaded
2026-04-30 15:00 優化
- **v7 Phase 2 P1 修復(全部完成)**:
- P-4/5:12 個裸 `new Chart()` 全部遷移為 `CH.newChart(chartRegistry, id, cfg)` 統一生命週期管理
- P-6:`dashboard-utils.js` 新增 `batchUpdate()` helper(rAF 批量 innerHTML)
- P-7:`aggregate-insights.ts` combo matrix 加 `MAX_PAIRS_PER_ORDER=30` 限制 O(k²) 配對
- P-8:`/api/data` 添加 `encodeBody: "auto"`(後發現 CF Pages 不支持,已移除)
- P-10:`track/view.ts` 滑動窗口 purge 閾值 200→50
- U-4:upload + admin 導航統一
- U-6:22 個 `<canvas>` 加 aria-label
- U-7:新建 `/vendor/modal.js`(HKTVModal),替換 6 處原生 alert/confirm
- U-8:shared.css 加全局 `:focus-visible` 焦點環
- U-10:operation.html 加 Admin 連結 + 鑑權
- U-11:index.html 加 loading skeleton(shimmer 動畫)
2026-04-29 21:00 優化
- **v6 Phase 1 P0 修復(6/6 完成)**:
- P-1:index.html 外部化 Chart.js(-205KB inline → 外部 vendor 腳本)
- P-2:`data-status.ts` 改讀 `data-meta.json`(~100 bytes)而非 5-15MB 完整 data.json
- P-3:`buildFulfillmentStats` 雙遍歷合併為單次 + 消除 6 個多餘數組
- U-1:7 個頁面加 `<main>` 地標
- U-2:8 個頁面加 Skip-Navigation link + shared.css `.skip-nav` 樣式
- U-3:login.html `<label>` 加 `for` 屬性
2026-04-28 01:15 優化
- 跨頁前端共用 helper 重構:新增 `/vendor/dashboard-utils.js`(fmtHKD / fmtInt / fmtPct / fmtMoney / fmtBytes / escapeHtml / 日期 period helpers / isoAdd / isoDiff / isoMinusMonth / datesBetween / fmtMonthLabel / fmtDateTimeHK / fmtCompact / WKD)
- 新增 `/vendor/chart-helpers.js`(storeColor / storeName / destroyChart / newChart / applyChartDefaults / activateAppNav)
- 新增 `/vendor/api-client.js`(fetch wrapper / JSON error handling / CSRF+JSON headers / `/api/data` IndexedDB stale-while-revalidate cache)
- 已遷移 7 個頁面(index / insights / compare / operation / monthly-report / upload / admin)嘅重複 helpers,改用共用 vendor 模組
- 各頁 `esc()` / `escapeHtml()` / `fmtHKD` / `fmtInt` / `_addDaysISO` / `storeColor` / `storeName` / `destroyChart` / nav active 邏輯 / `/api/data` fetch 全部統一引用
2026-04-27 22:30 優化
- Monthly Report export 畫質優化:重寫 setupExport capture 流程,從固定 scale:1.5 改為按裝置與畫布尺寸自適應 DPI(hero 優先更高、full 在像素 budget 內動態降級)
- 下載由 `toDataURL` 改為 `toBlob + object URL`,避免大圖 base64 導致 Safari / 手機記憶體問題,同時提升輸出清晰度
2026-04-27 15:10 優化
- `/api/data` cache 時間大幅縮短:`s-maxage` 300s→60s、`stale-while-revalidate` 600s→120s
- Rebuild 後最長等待由 ~15 分鐘縮短至 ~3 分鐘即可見到新數據
2026-04-27 00:30 優化
- Dashboard 首頁刪除 Order Status Mix card(已有 Operation 頁替代),grid-3→grid-2
- Insights 頁標題由「Operator Insights」改為「Insights」
- Operation 頁 5 個 section 全加「🚧 Coming Soon」overlay(等待新數據重新上傳)
2026-04-26 23:20 優化
- Monthly Report Executive Summary 改為 **Signal Detection** 架構:7 張 card 自動判斷 signal-alert(紅)/ signal-warn(橙)/ signal-ok,取代原本 flat number cards
- Lead paragraph + Footer 改為自動抽出最重要 signal 文案
2026-04-26 23:20 優化
- Insights TOC badge 標注日期篩選覆蓋範圍(Cancel Rate→部分 YTD / Basket Size→YTD only / Combo→YTD only)
- Insights 非 period-aware section 加 `⚠️ 此 section 不跟隨日期篩選` warning
2026-04-24 21:54 優化
- Index Hero GMV 字體加大 clamp(32-42px) fw800,KPI/VIP cards 縮細 padding/value
- VIP 卡片獨立 override(比 KPI 細一級)
- VIP banner 改為 ⓘ icon tooltip(移除整條橙色提示欄)
2026-04-24 19:30 優化
- **UX Audit Batch A**(5 quick wins):
- Index chart legend bar(🔵 Follows filter / 🟡 YTD)
- Insights Promo empty state 改為黃底 banner + fix 建議
- Insights period-kpi-bar 加 label + blue stripe
- Insights 5-section TOC pills(①~⑤)
- Compare skeleton shimmer animation
2026-04-24 18:35 優化
- Session TTL 由 24h 縮短至 **12h**,閒置超過 12 小時自動登出
- Sliding refresh threshold 同步調整:12h→6h
2026-04-26 20:42 優化
- P2/P3 收尾 Jira:輸出 3 張工程債票:Top Brands Qty period-aware(P2)、deprecated saveAllowedEmails 路徑清理(P2)、CSP nonce rollout 替換 unsafe-inline(P3)
2026-04-26 19:08 優化
- 全面 re-audit:typecheck + vitest 全綠(6 files / 118 tests),上輪 P1 核心項基本收斂
- 交付 `AUDIT_RECHECK_2026-04-26.md`,記錄現存 P2/P3 風險清單
2026-04-26 16:28 優化
- **H-13 記憶體佔用優化**:`aggregate-insights.ts` 中 `CancelBucketAcc.byStoreEff` / `byStoreCancel` 從 `Set<string>` 改為 `number` 計數器
- 180k rows / 500 brands / 2 stores 場景估計釋放 ~10–20 MB heap(~1,000 Set objects freed)
- `buildBasketStats()` finalization 後加 `b.skus = new Set()` 釋放 GC 引用
- 新增 `aggregate-memory.test.ts`(18 tests)覆蓋 counter arithmetic / end-to-end cancel rate / 500-brand 壓測
- **H-14 Single-pass 確認**:`aggregate.ts` 係唯一 for-loop orchestrator,Ticket 直接 close(無需改動)
2026-04-26 16:17 優化
- **H-19 Sliding-window rate limit**:`track/view.ts` 新增 count-based 滑動窗口(1 min / 6 req per user)
- Throttle 時 response 含 `throttled_reason: "sliding_window_exceeded"` + CF tail log 可見
- Feature flag:`TRACK_VIEW_RATE_LIMIT_ENABLED != "0"` 才啟用,支援 rollback 無需 redeploy
- 新增 7 條滑動窗口測試覆蓋(6req pass / 7th throttled / 窗外恢復 / per-user 獨立)
2026-04-26 14:51 優化
- **B 線:Aggregate 拆模組完成**,`aggregate.ts` 從 1084 行縮減至 401 行(純 orchestrator)
- 新增 4 個子模組:`aggregate-kpi.ts`(144 行)/ `aggregate-insights.ts`(268 行)/ `aggregate-promo.ts`(307 行)/ `aggregate-output.ts`(212 行)
- 新增 `aggregate-snapshot.test.ts`(18 tests)鎖定 data.json 24 個 top-level key + schema contract
- 全套 100 tests / 5 files / 0 typecheck errors ✅
2026-04-26 14:41 優化
- C 線補充:新增 `upload-rebuild.smoke.test.ts`(18 tests)驗證 sanitizeFileName / UploadRecord schema / aggregate→data.json 24 key contract
- 新增 `auth-me.smoke.test.ts`(28 tests)驗證 Session HMAC round-trip / session refresh threshold / roleMeets 三層 hierarchy / parseCookies
2026-04-26 14:28 優化
- **C 線:測試 + CI 基線**:新增 `tsconfig.json`(strict mode)、`vitest.config.ts`、`package.json` test/typecheck/predeploy scripts
- 新增 `aggregate.test.ts`(23 tests)覆蓋 cancel taxonomy / KPI math / brand normalize / dailyCancelAcc / basket
- 新增 `track-view.smoke.test.ts`(13 tests)覆蓋 sanitize / ALLOWED_PATHS / rate-limit logic
- 新增 `.github/workflows/ci.yml`:Node 22 + typecheck + vitest run on push/PR
2026-04-26 14:14 優化
- **A 線:Activity Shard 模式**:`activity-store.ts` 全面重寫,採用 per-user-per-day shard schema(`activity/<YYYY-MM-DD>/<emailKey>.json`)
- CAS 只鎖單一 shard,消除跨用戶 hot-blob contention;legacy path 保留,rollback 只需 `ACTIVITY_SHARD_MODE=0`
- `track/view.ts` response 新增 `write_ok` / `mode` / `cas_conflict` 觀測欄位
- `auth.ts` Env interface 加 `ACTIVITY_SHARD_MODE?: string`,移除 `(env as any)` cast
- 新增 `activity-store.test.ts`(12 tests)覆蓋 emailKey / dateKey / shardKey / compaction / dedup
2026-04-26 16:19 優化
- 系統更新 Log 顯示重構:預設只顯示最近 3 條,其餘打包入「更多歷史記錄」摺疊按鈕
- `build_changelog.py` 更新:常數 `RECENT_COUNT=3`,自動分組 RECENT / HISTORY 兩段輸出
- Order Status Mix、Top 10 Brands、Top 10 Delivery Districts 三個 card 現已跟隨日期篩選(之前係全期 YTD 靜態數據)
- Backend 新增 `daily_status` / `brand_daily` / `district_daily` 三個 per-date breakdown(需 Rebuild data.json 生效)
- 三個 card 標頭新增 scope note:已 rebuild 顯示「跟隨日期篩選 · N 日」(綠色),未 rebuild 顯示「全期 YTD · 請 Rebuild」(橙色)
- Top 10 Brands GMV metric 用 `brand_daily` 精確計算;Qty metric 仍用全期 top_brands(per-date qty breakdown 留 Phase 2)
2026-04-24 12:03 優化
- Reset All Data 按鈕加強防呆:彈出 modal 解釋後果,必須輸入「RESET」先可以確認
- 舊嘅 `confirm()` 對話框換走,改用設計清晰嘅紅色警告 modal
2026-04-24 12:00 優化
- Admin 頁嘅角色權限說明改用中文顯示,方便本地團隊理解
- 明確標註 Editor 唔能 Reset 全部數據(呢個只有 Super Admin 有權)
2026-04-24 10:56 優化
- 後台數據重建:Top SKUs 擴大至 100 條、品牌銷量數據齊全,WAGAYA 單店篩選唔會再空白
- 加入 `/api/rebuild` 內部觸發機制(需 REBUILD_KEY)畀 CI / 腳本喺部署後自動重建
2026-04-24 10:30 優化
- 更新日誌改用半自動模式,由 CHANGELOG.md 統一管理,deploy 時自動同步
- 日誌支援顯示日期 + 具體時間
2026-04-23 優化
- 上傳架構重構成 sharded,支援約 2 年數據而唔會 rebuild timeout
- 新增上傳歷史,Admin 頁可查看同刪除過往上傳
2026-05-02 07:35 修復
- **GitHub API 403 錯誤(User-Agent 缺失)**:CF Worker 發出的 GitHub API 請求缺少 `User-Agent` header,導致所有上傳後 GitHub Actions 無法自動觸發(`/api/debug-gh` 顯示 `token_valid: false, token_status: 403, "Request forbidden by administrative rules"`)
- 修復:`functions/api/upload.ts` dispatch fetch 請求加入 `"User-Agent": "hktv-dashboard-cf-worker/1.0"`;`functions/api/debug-gh.ts` 測試請求同步加 User-Agent
- 部署:https://983b4410.hktv-dashboard.pages.dev
2026-05-02 00:12 修復
- **Inventory Turnover / SKU Daily Sales 修復**:Inventory 頁面原本只讀 stock report,缺少歷史銷售數據,導致 Turnover 和 SKU daily sales 顯示為「—」或 0。現改為並行載入 `/api/stock-level` 庫存快照 + `loadDashboardData()` 銷售數據,建立 `storeId|skuId` / `skuId` 查找表,優先用實際銷售 qty 計算日均銷量與庫存周轉,fallback 到 stock report 的 `avg_daily_sales`
- **Top SKUs 全量輸出**:修復 `top_skus` / `top_skus_by_month` 只輸出 Top 100,導致大部分 inventory SKU 無法匹配銷售數據的問題;`aggregate.ts` 將 `buildTopSkus` / `buildTopSkusByMonth` 提升至全量輸出(99999),前端同時讀取 `top_skus` 和 `top_skus_by_month` 做精確匹配
- **Inventory Store Filter 改善**:新增 `ACTIVE_SKUS` 狀態,店舖篩選後 Overview / Alerts / Turnover / SKU Table / Charts 全部跟隨當前店舖,不再只影響部分列表
- 部署:https://143287bd.hktv-dashboard.pages.dev
2026-05-01 15:20 修復
- **Stock Level 多店舖終極存儲方案**:`upload-stock.ts` 改為每間店舖獨立寫入 `stock-level/store-{storeKey}.json`,避免多店舖上傳互相覆蓋或 CAS 衝突;新增 `/api/stock-level` endpoint 並行讀取所有 `store-*.json`,Inventory 頁統一從此 endpoint 載入庫存快照
- **CSV Parser Merchant ID 錯誤修復**:HKTVmall CSV header 行 `Merchant ID,Merchant Product ID,...` 曾被誤解析成 `meta.merchant_id = "Merchant Product ID"`,導致不同店舖生成相同/錯誤 storeKey。修復為先偵測 header row,並只從 `<=2` 欄的 metadata row 解析 `date` / `merchant_id` / `merchant_name`
- **Stock Level 調試與清理**:`/api/stock-level` 會自動忽略並清理包含空格的錯誤舊 key(例如 `store-Merchant Product ID.json`);Stock upload 成功 response / access log 顯示目前 R2 中所有店舖 key,方便確認多店舖資料已並存
- 部署:https://06fd831a.hktv-dashboard.pages.dev
2026-05-01 02:37 修復
- **Inventory 頁面數據結構不匹配修復**:修復 inventory 頁面顯示 "—" 無數據的問題。後端上傳的 stock_level 是對象結構(SKU 數據在 `rows` 數組中),但前端 render 函數期望直接是數組。添加數據轉換層,從 `D.stock_level.rows` 提取 SKU 數組,並將字段名映射:`sku_id` → `id`,`stock_qty` → `stock`,`avg_daily_sales` → `dailySales` 等。同時添加調試日志輸出 SKU 加載數量
- 部署:https://e823b144.hktv-dashboard.pages.dev
2026-05-01 02:17 修復
- **Inventory 頁面 IDB 缓存清除修复**:修复上传 stock_level 数据后 inventory 页面仍显示空白的问题。上传成功后现在会自动调用 `window.HKTVApi.idbDelete(DATA_CACHE_KEY)` 清除 IndexedDB 缓存,确保后续页面加载能获取包含最新 stock_level 数据的 data.json
- 部署:https://79cdea02.hktv-dashboard.pages.dev
2026-05-01 02:10 修復
- **monthly-report.html 語法錯誤修復**:`.catch()` 回調末尾缺少 `)` 關閉方法調用(應為 `})` 而非 `}`),導致 JS 引擎解析第 2102 行時拋出 `SyntaxError: Unexpected identifier 'window'`,頁面永遠卡在 "Loading..."。同時修正 `.catch()` block 的 `})` 閉合
- 部署:https://4988845e.hktv-dashboard.pages.dev
2026-04-30 21:00 修復
- Monthly Report MoM KPI 對比口徑修正:MTD(如 4/1-22)vs 上月同期(3/1-22),不再 vs 上月整月(3/1-31)
- `prevAllDates` 改為只取與當月天數相同的前 N 天,實現同期對比
- KPI 卡片 + Store Compare 自動顯示「vs 上月同期」或「vs 上月」標籤
2026-04-30 19:00 修復
- Monthly 頁 chartRegistry 跨 IIFE 作用域錯誤:`var chartRegistry` 在第一個 IIFE 內聲明,第二個 IIFE 無法訪問
- 修復:改為 `window._monthlyChartRegistry = {}`,4 處引用全部統一
2026-04-30 18:00 修復
- **Safari JSON parse 修復**:Safari 遇非 JSON 內容拋 "The string did not match the expected pattern"(DOM Exception 12),比 Chrome 的具體 SyntaxError 更模糊
- `fetchDashboardData` 從 `res.json()` 改為 `res.text()` + try-catch `JSON.parse`,檢測 HTML 響應自動導向登錄頁
- 所有 5 個頁面(index / compare / insights / operation / monthly-report)catch handler 統一增加 `isHtmlResponse`、`TypeError`(redirect:"error" 觸發)和 `session may have expired` 檢測
2026-04-30 17:00 修復
- Compare 頁缺少 `const CH = window.HKTVCharts` 別名,導致 `CH.newChart()` 拋 ReferenceError → "Failed to load data"
- Monthly 頁 catch handler 改為顯示具體 `error.message` 便於診斷
2026-04-30 16:00 修復
- **/api/data Error 1102**:`data.ts` 舊代碼將 5.2MB data.json 用 `obj.text()` 緩衝 + SHA-1 ETag 計算,CF Worker CPU 超時
- 修復:改為流式傳輸 R2 body(`new Response(obj.body, ...)`),使用 R2 內置 ETag
- **Error 1101**:`encodeBody: "auto"` 在 CF Pages Functions 中不支持(僅純 CF Workers 有效),移除此選項
- `cache-control` 改為 `s-maxage=0, must-revalidate`,CF 邊緣不緩存 `/api/data`
- `api-client.js` loadDashboardData 返回 IDB 緩存前先做 ETag 檢查,ETag 變了就強制獲取新數據
2026-04-30 01:00 修復
- login.html `.skip-nav` 缺少 CSS 樣式,在 `display:flex` body 下與 `<main>` 並排顯示破壞佈局
- 加 `.skip-nav { position:absolute; left:-9999px }` + `:focus { left:0 }` 標準隱藏模式
2026-04-29 23:30 修復
- **Rebuild 後 dashboard 不更新 — 3 個 bug 修復**:
- `rebuild.ts` 從 `ctx.waitUntil()` fire-and-forget 改為同步 await,完成後才返回 200/500
- `data-status.ts` 同時檢查 `_auto_rebuild_error.json` 和 `_rebuild_error.json`
- `api-client.js` pollRebuild reload 前先 `idbDelete()` 清除 IndexedDB 緩存
- `data.ts` s-maxage 從 60 降到 10,CF 邊緣最快 10s 刷新
2026-04-27 23:40 修復
- Operation Delivery Mode 卡片顯示全「—%/HK$0」但後端有數據:mode 名稱不一定含 express/pickup
- 修復 `renderDeliveryMode()`:加入多語關鍵字匹配(delivery / 送貨 / 速遞 / 宅配 / standard / nextday / pickup / 自取 / 門市 / 門店 / 到店)
- match 唔到時自動 fallback 用 top2 mode,摘要卡 label 改為動態顯示實際 mode 名 + ATV
- 改為優先用 `delivery_mode_stats.daily` 按當前 period 聚合,再 fallback 舊 schema `by_store`;加入 `s.modes || s.by_mode` 兼容
2026-04-27 00:56 修復
- 折扣深度 vs ROI 散點圖按 ⛶ 放大後空白:根因 `JSON.parse(JSON.stringify(chart.config))` 無法序列化 Chart.js Config 實例
- 改用 `buildDiscountScatterCfg()` 從 `_lastScatterData` 重建 config(與 index.html 的 `buildOverlayConfig()` 模式一致)
2026-04-26 21:01 修復
- Insights + Monthly Report **Mobile/iPad Responsive** 修復
- Insights 孤兒 `@media` block 修復 + 加入 480px iPhone breakpoint
- Monthly Report 加入 600px + 480px breakpoints,修復 heatmap grid 手機擠壓
2026-04-26 20:44 修復
- **P2:Top Brands Qty period-aware**:新增 `brand_daily_qty` accumulator,Qty metric 不再 fallback 全期 YTD
- `buildBrandsPool(metric)` 統一函數,GMV/Qty 都走 period-aware 路徑
- aggregate-snapshot.test.ts required keys 24→25
2026-04-24 22:00 修復
- `daily_status` TDZ Bug:`drIso` 變數於宣告前使用導致永遠 undefined,rebuild 無法寫入 daily_status 數據
- 移除頂部 legend bar(Follows date filter / All-time YTD)+ 3 個 scope note span,改由 card 內顯示
2026-04-24 18:28 修復
- **C-01 XSS**:monthly-report.html `esc()` 覆蓋率 90%→100%,6 個 innerHTML sink 全部修復
- **H-03 R2 Race**:allowed-emails.ts 改用 `updateAllowedEmails()` CAS retry(熱點④修復,5 熱點全清)
- **H-10 Activity Tracker**:確認已在 v3 完成(heartbeat 120s + in-memory rate limit)
2026-04-24 17:10 修復
- **v3 Medium 12 條全修**:timezone / YTD fallback / brand dedupe / inline handler / html2canvas scale / filterKey / session sliding / migrate log / brand normalize / file_name sanitize / basket assumption / upload ordering / popover leak
- H-15 cancelReasonStats 改用 `.trim().toUpperCase()` normalize
2026-04-26 14:22 修復
- Resend API Key 失效:更新 `RESEND_API_KEY` + 設定 `OTP_SENDER=HKTV Dashboard <noreply@nylimited.com>`
- `SESSION_SECRET` 空字串導致 HMAC key length=0 錯誤,已換用 64-char hex secret
- `SUPER_ADMIN` secret 重設(移除多餘空格),`sales@nylimited.com` 登入恢復正常
2026-04-24 10:45 修復
- GMV Share 百分比 label 改用動態量度:slice 弧長唔夠容納字就自動用外部 callout(解決仲係貼邊嘅 case)
- Top Brands 加返 Qty 資料(backend 原本只計 GMV,今次補返銷量);切 Qty 時 x 軸顯示數量而唔係金額
- Promo / Subcategory Detail card:Store Split 欄擴闊 + 支援換行,兩間店金額永遠顯示齊
- Promo / Subcategory Detail card:Store Split 嘅 HK$ 簡化為 $(騰空間畀完整數字)
- Top SKUs 10/20/50/100 真正生效(backend 之前只返 10 條,今次提升到 100)
2026-04-24 10:41 修復
- Daily GMV / Daily Orders 圖表唔再會喺 > 30 日時自動變週度 bar(避免同 Weekly GMV card 重複)
- Daily 圖表永遠維持 daily line,保留每日 rhythm 資訊;要睇週度匯總請用下方 Weekly GMV card
2026-04-24 02:05 修復
- 批量 upload 自動跳過冇訂單嘅空 xlsx 檔,唔再當 error
2026-04-24 00:20 修復
- GMV Share 百分比 label 位置調整,唔再貼近環邊緣
- 細 slice(< 6%)改用外部 callout,避免重疊
- Daily GMV Trend legend 顏色修正(唔再兩個都啡色)
- Daily Orders & ATV 加返 legend,Orders / VIP / ATV 三個 dataset 清晰分開
2026-04-24 00:04 修復
- 同一日重複 upload 時新檔覆蓋舊檔,唔會重複 count