fix: refine growth dashboard summary data
All checks were successful
CD Pipeline / deploy (push) Successful in 1m3s

This commit is contained in:
ogt
2026-06-25 09:25:52 +08:00
parent e3b082a299
commit e8f9dfeba4
2 changed files with 14 additions and 9 deletions

View File

@@ -402,7 +402,7 @@ YOUTUBE_API_KEY = os.getenv('YOUTUBE_API_KEY', '')
# ==========================================
# 系統版本與路徑
# ==========================================
SYSTEM_VERSION = "V10.656"
SYSTEM_VERSION = "V10.657"
LOG_FILE_PATH = os.path.join(BASE_DIR, 'logs/system.log')
public_url = PUBLIC_URL # 用於模板顯示

View File

@@ -3414,11 +3414,12 @@ function renderGrowthCommandCenter(stats = {}, rows = []) {
const salesDeltaPct = Number(stats.overall_sales_delta_pct || 0);
const matchRate = Number(stats.mapping_rate || (candidateCount ? (mappedCount / candidateCount) * 100 : 0));
const latestSalesDate = String(stats.overall_latest_sales_date || stats.latest_sales_date || '').slice(0, 10);
const pressureRows = safeRows.filter((row) => row?.recommended_action?.code === 'review_price_or_promo' || Number(row?.external_price?.gap_pct || 0) > 0);
const advantageRows = safeRows.filter((row) => row?.recommended_action?.code === 'amplify_price_advantage' || Number(row?.external_price?.gap_pct || 0) < 0);
const actionCounts = stats.action_code_counts || {};
const pressureCount = Number(actionCounts.review_price_or_promo || 0);
const advantageCount = Number(actionCounts.amplify_price_advantage || 0);
const pendingCount = Math.max(0, needsMapping + reviewCandidateCount);
const readyShare = candidateCount ? (advantageRows.length / candidateCount) * 100 : 0;
const pressureShare = candidateCount ? (pressureRows.length / candidateCount) * 100 : 0;
const readyShare = candidateCount ? (advantageCount / candidateCount) * 100 : 0;
const pressureShare = candidateCount ? (pressureCount / candidateCount) * 100 : 0;
setText('commandSales7d', sales7d ? formatMoney(sales7d) : '—');
setText('commandSalesDelta', prevSales7d
@@ -3436,8 +3437,8 @@ function renderGrowthCommandCenter(stats = {}, rows = []) {
setText('commandStatusReview', formatCount(reviewCandidateCount));
setText('commandStatusNeeds', formatCount(needsMapping));
setText('commandLatestSalesDate', latestSalesDate || '—');
setText('commandPriceAdvantage', `${formatCount(advantageRows.length)}`);
setText('commandPricePressure', `${formatCount(pressureRows.length)}`);
setText('commandPriceAdvantage', `${formatCount(advantageCount)}`);
setText('commandPricePressure', `${formatCount(pressureCount)}`);
setText('commandPricePending', `${formatCount(pendingCount)}`);
setText('commandDonutText', `${Math.round(matchRate)}%`);
@@ -3491,7 +3492,8 @@ function renderGrowthCommandTopDecliners(rows) {
const tbody = document.getElementById('commandTopDecliners');
if (!tbody) return;
const sortedRows = [...rows]
.sort((a, b) => Math.abs(Number(b.sales_delta_pct || 0)) - Math.abs(Number(a.sales_delta_pct || 0)))
.filter((row) => Number(row.sales_7d || 0) > 0)
.sort((a, b) => Number(b.sales_7d || 0) - Number(a.sales_7d || 0))
.slice(0, 5);
if (!sortedRows.length) {
@@ -3502,6 +3504,9 @@ function renderGrowthCommandTopDecliners(rows) {
tbody.innerHTML = sortedRows.map((row, index) => {
const delta = Number(row.sales_delta_pct || 0);
const action = row.recommended_action?.label || '查看商品';
const trendText = delta
? `${delta > 0 ? '+' : ''}${delta.toFixed(1)}%`
: '趨勢待補';
return `<tr>
<td>
<span class="growth-command-meta momo-mono">#${index + 1} · ${escapeHtml(row.category || '未分類')}</span>
@@ -3511,7 +3516,7 @@ function renderGrowthCommandTopDecliners(rows) {
</td>
<td class="text-end">
<strong class="momo-mono">${escapeHtml(formatMoney(row.sales_7d || 0))}</strong>
<span class="growth-command-meta">${delta ? `${delta > 0 ? '+' : ''}${delta.toFixed(1)}%` : '趨勢待補'}</span>
<span class="growth-command-meta">${escapeHtml(trendText)}</span>
</td>
<td class="text-end"><span class="growth-command-badge">${escapeHtml(action)}</span></td>
</tr>`;