From e3b082a299cae247f0414a3c07e6d45d5cff6861 Mon Sep 17 00:00:00 2001 From: ogt Date: Thu, 25 Jun 2026 09:23:37 +0800 Subject: [PATCH] feat: align growth homepage dashboard experience --- config.py | 2 +- docs/AI_INTELLIGENCE_MODULE_SOT.md | 1 + templates/ai_intelligence.html | 654 +++++++++++++++++++- tests/test_pchome_revenue_growth_service.py | 6 + 4 files changed, 659 insertions(+), 4 deletions(-) diff --git a/config.py b/config.py index ec213cb..2cd3e68 100644 --- a/config.py +++ b/config.py @@ -402,7 +402,7 @@ YOUTUBE_API_KEY = os.getenv('YOUTUBE_API_KEY', '') # ========================================== # 系統版本與路徑 # ========================================== -SYSTEM_VERSION = "V10.655" +SYSTEM_VERSION = "V10.656" LOG_FILE_PATH = os.path.join(BASE_DIR, 'logs/system.log') public_url = PUBLIC_URL # 用於模板顯示 diff --git a/docs/AI_INTELLIGENCE_MODULE_SOT.md b/docs/AI_INTELLIGENCE_MODULE_SOT.md index 70e89b4..ed0c646 100644 --- a/docs/AI_INTELLIGENCE_MODULE_SOT.md +++ b/docs/AI_INTELLIGENCE_MODULE_SOT.md @@ -90,6 +90,7 @@ - V10.652 起正式首頁 `/` 必須顯示「PChome 業績成長自動化作戰系統」,舊商品看板僅保留在 `/dashboard` 或 `/product-dashboard`;「今日策略動作」必須放在首屏任務摘要後方,不能只藏在商品明細區;每列必須直接顯示價格證據,至少包含 PChome、MOMO、差距與可信度四格。候選待確認或缺資料時需以待確認/待補呈現,不得要求使用者先打開詳情才知道判斷依據。 - V10.654 起全站側邊欄第一個主入口必須命名為「業績成長指揮台」;舊商品看板只能以「舊商品看板」保留在 `/dashboard`,比價工作台必須直連 `/dashboard?filter=pchome_review...`,不得再使用 `/` query 轉址,避免正式首頁與舊頁混淆。 - V10.655 起正式首頁 `/` 必須以 HTTP 200 原地渲染「業績成長指揮台」,不得 302 跳轉到 `/ai_intelligence`;側邊欄第一個主入口必須直連 `/`。`/ai_intelligence` 只作為相容入口保留,不得成為主導流路由。 +- V10.656 起正式首頁首屏必須是「PChome 業績成長系統」專業儀表板,而非大段說明型頁首;第一屏需直接呈現近 7 天業績、比價可用率、下滑商品、待補比價、最大分類、下滑商品 TOP 5、PChome/MOMO 價格狀態圓環與處理狀態,且全部使用 `/api/ai/pchome-growth/opportunities` 真資料渲染。 ## 零之一、12 Agent 決策信封(2026-05-24) diff --git a/templates/ai_intelligence.html b/templates/ai_intelligence.html index af98a3e..4151eb8 100644 --- a/templates/ai_intelligence.html +++ b/templates/ai_intelligence.html @@ -9,6 +9,365 @@ gap: 18px; } + .ai-intel-hero, + .growth-executive-strip, + #growthActionBoard, + .ops-flow { + display: none !important; + } + + .growth-command-pro { + display: grid; + gap: 14px; + } + + .growth-command-pro-head { + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; + border: 1px solid var(--momo-border-subtle); + border-radius: 8px; + background: rgba(255, 255, 255, 0.92); + box-shadow: var(--momo-shadow-soft); + padding: 16px 18px; + } + + .growth-command-pro-title { + margin: 0; + color: var(--momo-text-strong); + font-family: var(--momo-font-display); + font-size: 1.3rem; + font-weight: 900; + letter-spacing: 0; + } + + .growth-command-pro-subtitle { + margin: 5px 0 0; + color: var(--momo-text-muted); + font-size: 0.82rem; + font-weight: 760; + } + + .growth-command-pro-actions { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: flex-end; + gap: 8px; + } + + .growth-command-status-pill { + display: inline-flex; + align-items: center; + gap: 7px; + min-height: 34px; + border: 1px solid rgba(40, 128, 80, 0.22); + border-radius: 999px; + background: rgba(232, 247, 238, 0.9); + color: #216542; + font-size: 0.78rem; + font-weight: 900; + padding: 7px 12px; + } + + .growth-command-kpi-grid { + display: grid; + grid-template-columns: 1.28fr repeat(4, minmax(0, 1fr)); + gap: 10px; + } + + .growth-command-card { + min-height: 136px; + border: 1px solid var(--momo-border-subtle); + border-radius: 8px; + background: rgba(255, 255, 255, 0.92); + box-shadow: var(--momo-shadow-soft); + padding: 13px; + } + + .growth-command-card.is-clickable { + cursor: pointer; + transition: transform 0.16s ease, border-color 0.16s ease, box-shadow 0.16s ease; + } + + .growth-command-card.is-clickable:hover, + .growth-command-card.is-clickable:focus { + border-color: rgba(49, 113, 234, 0.28); + box-shadow: 0 0 0 3px rgba(49, 113, 234, 0.08), var(--momo-shadow-soft); + outline: none; + transform: translateY(-1px); + } + + .growth-command-kpi-label { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; + color: var(--momo-text-muted); + font-size: 0.74rem; + font-weight: 900; + } + + .growth-command-kpi-label i { + color: var(--momo-warm-rust); + } + + .growth-command-kpi-value { + display: block; + margin-top: 10px; + color: var(--momo-text-strong); + font-family: var(--momo-font-mono); + font-size: 1.75rem; + font-weight: 900; + line-height: 1; + } + + .growth-command-card.is-sales .growth-command-kpi-value { + font-size: 2.05rem; + } + + .growth-command-kpi-note { + display: block; + margin-top: 8px; + color: var(--momo-text-muted); + font-size: 0.76rem; + font-weight: 800; + line-height: 1.35; + } + + .growth-command-spark { + display: block; + width: 100%; + height: 34px; + margin-top: 9px; + } + + .growth-command-progress { + overflow: hidden; + height: 8px; + margin-top: 12px; + border-radius: 999px; + background: rgba(42, 37, 32, 0.08); + } + + .growth-command-progress span { + display: block; + width: 0; + height: 100%; + border-radius: inherit; + background: #3bb273; + transition: width 0.24s ease; + } + + .growth-command-focus { + display: grid; + grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr) minmax(260px, 0.85fr); + gap: 12px; + } + + .growth-command-panel { + border: 1px solid var(--momo-border-subtle); + border-radius: 8px; + background: rgba(255, 255, 255, 0.92); + box-shadow: var(--momo-shadow-soft); + padding: 14px; + } + + .growth-command-panel-head { + display: flex; + align-items: center; + justify-content: space-between; + gap: 10px; + margin-bottom: 10px; + } + + .growth-command-panel-title { + margin: 0; + color: var(--momo-text-strong); + font-size: 0.92rem; + font-weight: 900; + } + + .growth-command-panel-link { + border: 0; + background: transparent; + color: #3171ea; + font-size: 0.74rem; + font-weight: 900; + padding: 0; + } + + .growth-command-table { + width: 100%; + border-collapse: collapse; + } + + .growth-command-table td { + border-top: 1px solid rgba(42, 37, 32, 0.08); + padding: 9px 4px; + vertical-align: middle; + } + + .growth-command-product { + display: block; + max-width: 280px; + overflow: hidden; + color: var(--momo-text-strong); + font-size: 0.82rem; + font-weight: 900; + text-overflow: ellipsis; + white-space: nowrap; + } + + .growth-command-meta { + display: block; + margin-top: 3px; + color: var(--momo-text-muted); + font-size: 0.72rem; + font-weight: 780; + } + + .growth-command-badge { + display: inline-flex; + align-items: center; + justify-content: center; + min-width: 44px; + border-radius: 999px; + background: rgba(255, 239, 232, 0.88); + color: #b94f3a; + font-size: 0.72rem; + font-weight: 900; + padding: 5px 8px; + } + + .growth-command-donut-wrap { + display: grid; + grid-template-columns: 168px minmax(0, 1fr); + gap: 12px; + align-items: center; + } + + .growth-command-donut { + --ready: 0; + --review: 0; + width: 152px; + aspect-ratio: 1; + border-radius: 50%; + background: + conic-gradient(#3bb273 0 calc(var(--ready) * 1%), #f2b25a 0 calc((var(--ready) + var(--review)) * 1%), #d9d5cc 0 100%); + display: grid; + place-items: center; + } + + .growth-command-donut strong { + display: grid; + place-items: center; + width: 92px; + aspect-ratio: 1; + border-radius: 50%; + background: #fff; + color: var(--momo-text-strong); + font-family: var(--momo-font-mono); + font-size: 1.35rem; + font-weight: 900; + text-align: center; + line-height: 1.1; + } + + .growth-command-legend { + display: grid; + gap: 8px; + } + + .growth-command-legend-row { + display: grid; + grid-template-columns: auto minmax(0, 1fr) auto; + gap: 8px; + align-items: center; + color: var(--momo-text-muted); + font-size: 0.78rem; + font-weight: 850; + } + + .growth-command-dot { + width: 9px; + height: 9px; + border-radius: 50%; + background: #d9d5cc; + } + + .growth-command-dot.is-ready { background: #3bb273; } + .growth-command-dot.is-review { background: #f2b25a; } + + .growth-command-mini-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 9px; + } + + .growth-command-mini-card { + min-height: 88px; + border: 1px solid rgba(42, 37, 32, 0.08); + border-radius: 8px; + background: rgba(250, 247, 240, 0.58); + padding: 10px; + } + + .growth-command-mini-card span { + color: var(--momo-text-muted); + font-size: 0.72rem; + font-weight: 900; + } + + .growth-command-mini-card strong { + display: block; + margin-top: 8px; + color: var(--momo-text-strong); + font-family: var(--momo-font-mono); + font-size: 1.45rem; + font-weight: 900; + line-height: 1; + } + + .growth-command-mini-card em { + display: block; + margin-top: 7px; + color: var(--momo-text-muted); + font-size: 0.72rem; + font-style: normal; + font-weight: 780; + } + + .growth-command-alert { + display: grid; + grid-template-columns: auto minmax(0, 1fr) auto; + gap: 10px; + align-items: center; + border: 1px solid rgba(49, 113, 234, 0.16); + border-radius: 8px; + background: rgba(235, 243, 255, 0.82); + padding: 11px 13px; + } + + .growth-command-alert i { + color: #3171ea; + } + + .growth-command-alert strong { + color: var(--momo-text-strong); + font-size: 0.9rem; + font-weight: 900; + } + + .growth-command-alert span { + display: block; + margin-top: 2px; + color: var(--momo-text-muted); + font-size: 0.76rem; + font-weight: 780; + } + .ai-intel-hero { position: relative; overflow: hidden; @@ -2066,6 +2425,161 @@ {% block ewooo_content %}
+
+
+
+

PChome 業績成長系統

+

看清業績、比價壓力與今天要先處理的商品。

+
+
+ + + 讀取中 + + + + + +
+
+ +
+
+
+ PChome 近 7 天業績 + +
+ + + + + 等待資料 +
+
+
+ 比價可用率 + +
+ —% +
+ 等待 MOMO 對應 +
+
+
+ 下滑商品 + +
+ + 等待業績商品數 +
+
+
+ 待補比價 + +
+ + 筆候選待確認 +
+
+
+ 最大業績分類 + +
+ + 等待分類業績 +
+
+ +
+ +
+ 正在整理今天第一件事 + 系統正在讀取 PChome 業績與 MOMO 比價資料。 +
+ +
+ +
+
+
+

下滑商品 TOP 5

+ +
+ + + + +
整理商品中...
+
+ +
+
+

PChome 與 MOMO 價格狀態

+ +
+
+
+ —% +
+
+
+ + PChome 有優勢 + +
+
+ + MOMO 更便宜 + +
+
+ + 待確認 / 待補 + +
+
+
+
+ +
+
+

處理狀態

+ +
+
+
+ 可立即處理 + + 已有外部價格可判斷 +
+
+ 待確認 + + 先確認同款 +
+
+ 待補比價 + + 需要補抓 MOMO +
+
+ 最新業績日 + + 資料更新狀態 +
+
+
+
+
+
@@ -2080,13 +2594,13 @@ 載入中... - - - + + + ${escapeHtml(formatMoney(row.sales_7d || 0))} + ${delta ? `${delta > 0 ? '+' : ''}${delta.toFixed(1)}%` : '趨勢待補'} + + ${escapeHtml(action)} + `; + }).join(''); } function renderNextAction(candidateCount, mappedCount, needsMapping, reviewCandidateCount = 0) { @@ -3044,12 +3690,14 @@ async function loadGrowthOps(forceRefresh = false) { renderGrowthSourceReadiness((scope.source_readiness || {}).sources || []); latestGrowthRows = data.opportunities || []; + renderGrowthCommandCenter(stats, latestGrowthRows); renderGrowthOps(latestGrowthRows); renderGrowthDetail(activeGrowthDetailKind); loadGrowthReviewCandidates(); } catch (error) { console.error(error); latestGrowthRows = []; + renderGrowthCommandCenter({}, []); renderOpsCommandDashboard({}, {}); renderGrowthActionHint({ candidate_count: 0, mapped_count: 0, needs_mapping_count: 0 }); renderGrowthDataSourceSummary({}); diff --git a/tests/test_pchome_revenue_growth_service.py b/tests/test_pchome_revenue_growth_service.py index 2ee93c6..3a6e447 100644 --- a/tests/test_pchome_revenue_growth_service.py +++ b/tests/test_pchome_revenue_growth_service.py @@ -488,6 +488,12 @@ def test_ai_intelligence_template_uses_pchome_growth_name_and_endpoint(): assert "growthActionBoard" in template assert "今日策略動作" in template assert "renderGrowthActionBoard" in template + assert "PChome 業績成長系統" in template + assert "growth-command-pro" in template + assert "commandSales7d" in template + assert "commandTopDecliners" in template + assert "PChome 與 MOMO 價格狀態" in template + assert "renderGrowthCommandCenter" in template assert "growthActionPlanForRow" in template assert "growthActionEvidence" in template assert "growth-action-evidence-chip" in template