Files
ewoooc/templates/dashboard_v2.html
OoO 2c47a79f05
All checks were successful
CD Pipeline / deploy (push) Successful in 1m7s
[V10.328] 強化 PChome 比價診斷與狀態分流
2026-05-20 13:24:38 +08:00

683 lines
51 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{% extends 'ewoooc_base.html' %}
{% block title %}EwoooC 商品看板{% endblock %}
{% block extra_css %}
<link rel="stylesheet" href="{{ url_for('static', filename='css/page-dashboard-v2.css') }}">
{% endblock %}
{% block ewooo_content %}
<div class="dashboard-v2-stack">
{% set overview = competitor_overview | default({}) %}
<section>
<div class="dashboard-section-label">
<span class="num momo-mono">01</span>
<span class="title">比價監控總覽</span>
<span class="meta momo-mono">LIVE · 更新於 {{ datetime_now }}</span>
</div>
<div class="dashboard-kpi-grid">
<div class="dashboard-kpi">
<div class="dashboard-kpi-label momo-mono">比對覆蓋率</div>
<div class="dashboard-kpi-value momo-mono">{{ overview.match_rate | default(0) }}%</div>
<div class="dashboard-kpi-sub momo-mono">{{ overview.matched_count | default(0) | number_format }} / {{ overview.total_active | default(total_products) | number_format }} ACTIVE</div>
</div>
<div class="dashboard-kpi is-accent">
<div class="dashboard-kpi-label momo-mono">PChome 優勢</div>
<div class="dashboard-kpi-value momo-mono">{{ overview.pchome_advantage_count | default(0) | number_format }}</div>
<div class="dashboard-kpi-sub momo-mono">平均價差 +{{ overview.avg_advantage_gap | default(0) }}%</div>
</div>
<div class="dashboard-kpi">
<div class="dashboard-kpi-label momo-mono">MOMO 威脅</div>
<div class="dashboard-kpi-value momo-mono is-danger">{{ overview.momo_threat_count | default(0) | number_format }}</div>
<div class="dashboard-kpi-sub momo-mono">MOMO 價格低於 PChome</div>
</div>
<div class="dashboard-kpi">
<div class="dashboard-kpi-label momo-mono">AI 挑品</div>
<div class="dashboard-kpi-value momo-mono is-success">{{ overview.ai_pick_count | default(0) | number_format }}</div>
<div class="dashboard-kpi-sub momo-mono">
<a class="dashboard-kpi-sub-link" href="{{ url_for('dashboard.index', filter='ai_picks', category=current_category, q=search_query, sort_by='timestamp', order='desc') }}">查看 {{ ai_pick_list_limit }} 品清單</a>
</div>
</div>
<div class="dashboard-kpi">
<div class="dashboard-kpi-label momo-mono">比價覆核</div>
<div class="dashboard-kpi-value momo-mono is-warning">{{ overview.review_queue_count | default(0) | number_format }}</div>
<div class="dashboard-kpi-sub momo-mono">
<a class="dashboard-kpi-sub-link" href="{{ url_for('dashboard.index', filter='pchome_review', category=current_category, q=search_query, sort_by='pchome_review', order='desc') }}">需單位價覆核 {{ overview.unit_comparable_count | default(0) | number_format }}</a>
· 待補抓 {{ overview.pending_match_count | default(0) | number_format }}
</div>
</div>
<div class="dashboard-kpi">
<div class="dashboard-kpi-label momo-mono">資料新鮮度</div>
<div class="dashboard-kpi-value momo-mono is-small">{{ '已更新' if overview.last_pchome_crawled else '待更新' }}</div>
<div class="dashboard-kpi-sub momo-mono">{{ overview.last_pchome_crawled or '尚無 PChome 抓取紀錄' }}</div>
</div>
</div>
</section>
<section>
<div class="dashboard-section-label">
<span class="num momo-mono">02</span>
<span class="title">比價決策焦點</span>
<span class="meta momo-mono">{{ today_date }}</span>
</div>
<div class="dashboard-focus-grid">
<div class="dashboard-focus-card">
<div class="dashboard-focus-label momo-mono">今日優先銷售</div>
{% if overview.top_picks %}
<div class="dashboard-focus-list">
{% for pick in overview.top_picks %}
<div class="dashboard-focus-row">
<a class="dashboard-focus-row-title momo-tracked-link" href="{{ pick.momo_url or '#' }}" data-momo-original-url="{{ pick.momo_url or '#' }}" target="_blank" rel="noopener noreferrer"
data-track-platform="momo"
data-track-source="dashboard-v2-overview-top-picks"
data-track-product-id="{{ pick.sku }}"
data-track-icode="{{ pick.sku }}"
data-track-product-name="{{ pick.name|e }}">{{ pick.name }}</a>
<div class="dashboard-focus-row-meta momo-mono">
<span class="dashboard-focus-chip is-win">AI {{ (pick.confidence * 100) | round(0) | int if pick.confidence else 0 }}%</span>
<span>證據 {{ pick.evidence_quality | round(0) | int }}%</span>
<span>機會 {{ pick.opportunity_score | round(0) | int }}</span>
<span>MOMO ${{ pick.momo_price | int | number_format }}</span>
<span>PChome ${{ pick.pchome_price | int | number_format }}</span>
<span>+{{ pick.gap_pct | round(1) }}%</span>
</div>
{% if pick.missing_evidence %}
<div class="dashboard-ai-evidence-line">
{% for evidence in pick.missing_evidence[:2] %}
<span class="dashboard-ai-evidence-chip">{{ evidence }}</span>
{% endfor %}
</div>
{% endif %}
<div class="dashboard-focus-row-links">
<a class="dashboard-platform-link is-momo momo-tracked-link" href="{{ pick.momo_url or '#' }}" data-momo-original-url="{{ pick.momo_url or '#' }}" target="_blank" rel="noopener noreferrer"
data-track-platform="momo"
data-track-source="dashboard-v2-overview-top-picks"
data-track-product-id="{{ pick.sku }}"
data-track-icode="{{ pick.sku }}"
data-track-product-name="{{ pick.name|e }}">MOMO {{ pick.sku }}</a>
{% if pick.pchome_url %}
<a class="dashboard-platform-link is-pchome" href="{{ pick.pchome_url }}" target="_blank" rel="noopener noreferrer">PChome {{ pick.pchome_id }}</a>
{% endif %}
</div>
</div>
{% endfor %}
</div>
{% else %}
<div class="dashboard-focus-title">尚無 AI 挑品</div>
<div class="dashboard-focus-sub momo-mono">請先讓 PChome 比對與挑品 Agent 累積資料</div>
{% endif %}
</div>
<div class="dashboard-focus-card">
<div class="dashboard-focus-label momo-mono">價格威脅</div>
{% if overview.top_momo_threats %}
<div class="dashboard-focus-list">
{% for item in overview.top_momo_threats %}
<div class="dashboard-focus-row">
<a class="dashboard-focus-row-title momo-tracked-link" href="{{ item.momo_url or '#' }}" data-momo-original-url="{{ item.momo_url or '#' }}" target="_blank" rel="noopener noreferrer"
data-track-platform="momo"
data-track-source="dashboard-v2-overview-top-momo-threats"
data-track-product-id="{{ item.sku }}"
data-track-icode="{{ item.sku }}"
data-track-product-name="{{ item.name|e }}">{{ item.name }}</a>
<div class="dashboard-focus-row-meta momo-mono">
<span class="dashboard-focus-chip is-risk">{{ item.gap_pct | round(1) }}%</span>
<span>MOMO ${{ item.momo_price | int | number_format }}</span>
<span>PChome ${{ item.pchome_price | int | number_format }}</span>
</div>
<div class="dashboard-focus-row-links">
<a class="dashboard-platform-link is-momo momo-tracked-link" href="{{ item.momo_url or '#' }}" data-momo-original-url="{{ item.momo_url or '#' }}" target="_blank" rel="noopener noreferrer"
data-track-platform="momo"
data-track-source="dashboard-v2-overview-top-momo-threats"
data-track-product-id="{{ item.sku }}"
data-track-icode="{{ item.sku }}"
data-track-product-name="{{ item.name|e }}">MOMO {{ item.sku }}</a>
{% if item.pchome_url %}
<a class="dashboard-platform-link is-pchome" href="{{ item.pchome_url }}" target="_blank" rel="noopener noreferrer">PChome {{ item.pchome_id }}</a>
{% endif %}
</div>
</div>
{% endfor %}
</div>
{% else %}
<div class="dashboard-focus-title">尚無明顯威脅</div>
<div class="dashboard-focus-sub momo-mono">目前沒有 MOMO 低於 PChome 5% 以上的配對商品</div>
{% endif %}
</div>
<div class="dashboard-focus-card">
<div class="dashboard-focus-label momo-mono">覆核與補資料</div>
{% if overview.review_queue %}
<div class="dashboard-focus-list">
{% for item in overview.review_queue %}
<div class="dashboard-focus-row">
<a class="dashboard-focus-row-title" href="{{ url_for('dashboard.index', filter='pchome_review', q=item.sku, sort_by='pchome_review', order='desc') }}">{{ item.name }}</a>
<div class="dashboard-focus-row-meta momo-mono">
<span class="dashboard-focus-chip is-review">{{ item.status_label }}</span>
<span>MOMO ${{ item.momo_price | int | number_format }}</span>
{% if item.candidate_pc_price %}
<span>候選 PChome ${{ item.candidate_pc_price | int | number_format }}</span>
{% endif %}
{% if item.best_match_score %}
<span>match {{ (item.best_match_score * 100) | round(0) | int }}%</span>
{% endif %}
</div>
<div class="dashboard-focus-sub">{{ item.action_label }}</div>
{% if item.diagnostic_reasons %}
<div class="dashboard-review-reasons" aria-label="比對診斷原因">
{% for reason in item.diagnostic_reasons[:4] %}
<span>{{ reason.label }}</span>
{% endfor %}
</div>
{% endif %}
{% if item.unit_comparison and item.unit_comparison.summary %}
<div class="dashboard-review-note momo-mono">{{ item.unit_comparison.summary }}</div>
{% endif %}
<div class="dashboard-focus-row-links">
<a class="dashboard-platform-link is-momo momo-tracked-link" href="{{ build_momo_product_url(item.sku) if build_momo_product_url is defined else '#' }}" data-momo-original-url="{{ build_momo_product_url(item.sku) if build_momo_product_url is defined else '#' }}" target="_blank" rel="noopener noreferrer"
data-track-platform="momo"
data-track-source="dashboard-v2-overview-review-queue"
data-track-product-id="{{ item.sku }}"
data-track-icode="{{ item.sku }}"
data-track-product-name="{{ item.name|e }}">MOMO {{ item.sku }}</a>
{% if item.candidate_pc_id %}
<a class="dashboard-platform-link is-pchome" href="https://24h.pchome.com.tw/prod/{{ item.candidate_pc_id }}" target="_blank" rel="noopener noreferrer">PChome {{ item.candidate_pc_id }}</a>
{% endif %}
</div>
</div>
{% endfor %}
</div>
<a class="dashboard-focus-action" href="{{ url_for('dashboard.index', filter='pchome_review', category=current_category, q=search_query, sort_by='pchome_review', order='desc') }}">查看高優先覆核隊列</a>
{% elif overview.pending_priority %}
<div class="dashboard-focus-list">
{% for item in overview.pending_priority %}
<div class="dashboard-focus-row">
<a class="dashboard-focus-row-title momo-tracked-link" href="{{ item.momo_url or '#' }}" data-momo-original-url="{{ item.momo_url or '#' }}" target="_blank" rel="noopener noreferrer"
data-track-platform="momo"
data-track-source="dashboard-v2-overview-pending-priority"
data-track-product-id="{{ item.sku }}"
data-track-icode="{{ item.sku }}"
data-track-product-name="{{ item.name|e }}">{{ item.name }}</a>
<div class="dashboard-focus-row-meta momo-mono">
<span class="dashboard-focus-chip is-neutral">待比對</span>
<span>MOMO ${{ item.momo_price | int | number_format }}</span>
<span>{{ item.category or '未分類' }}</span>
</div>
<div class="dashboard-focus-row-links">
<a class="dashboard-platform-link is-momo momo-tracked-link" href="{{ item.momo_url or '#' }}" data-momo-original-url="{{ item.momo_url or '#' }}" target="_blank" rel="noopener noreferrer"
data-track-platform="momo"
data-track-source="dashboard-v2-overview-pending-priority"
data-track-product-id="{{ item.sku }}"
data-track-icode="{{ item.sku }}"
data-track-product-name="{{ item.name|e }}">MOMO {{ item.sku }}</a>
<span class="dashboard-platform-muted">PChome 待比對</span>
</div>
</div>
{% endfor %}
</div>
{% else %}
<div class="dashboard-focus-title">覆核隊列已清空</div>
<div class="dashboard-focus-sub momo-mono">目前 ACTIVE 商品沒有高優先 PChome 覆核項目</div>
{% endif %}
</div>
</div>
</section>
<section>
<div class="dashboard-section-label">
<span class="num momo-mono">03</span>
<span class="title">篩選</span>
</div>
<div class="dashboard-filter-card">
<form class="dashboard-filter-form" method="GET" action="/">
<input class="dashboard-search" type="text" name="q" value="{{ search_query }}" placeholder="搜尋商品名稱或品號...">
<select class="dashboard-select" name="category" data-dashboard-auto-submit>
<option value="all">所有分類</option>
{% for cat in categories %}
<option value="{{ cat }}" {% if current_category == cat %}selected{% endif %}>{{ cat }}</option>
{% endfor %}
</select>
<input type="hidden" name="filter" value="{{ current_filter }}">
<input type="hidden" name="review_status" value="{{ current_review_status }}">
<input type="hidden" name="sort_by" value="{{ current_sort }}">
<input type="hidden" name="order" value="{{ current_order }}">
<button class="dashboard-action-button" type="submit">
<i class="fas fa-search"></i> 搜尋
</button>
<div class="dashboard-segmented">
<a class="{% if current_filter == 'all' %}is-active{% endif %}" href="{{ url_for('dashboard.index', filter='all', category=current_category, q=search_query, sort_by=current_sort, order=current_order) }}">全部</a>
<a class="{% if current_filter == 'ai_picks' %}is-active{% endif %}" href="{{ url_for('dashboard.index', filter='ai_picks', category=current_category, q=search_query, sort_by='timestamp', order='desc') }}">AI挑品</a>
<a class="{% if current_filter == 'pchome_review' %}is-active{% endif %}" href="{{ url_for('dashboard.index', filter='pchome_review', review_status='all', category=current_category, q=search_query, sort_by='pchome_review', order='desc') }}">比價覆核</a>
<a class="{% if current_filter == 'new' %}is-active{% endif %}" href="{{ url_for('dashboard.index', filter='new', category=current_category, q=search_query, sort_by=current_sort, order=current_order) }}">新上架</a>
<a class="{% if current_filter == 'increase' %}is-active{% endif %}" href="{{ url_for('dashboard.index', filter='increase', category=current_category, q=search_query, sort_by=current_sort, order=current_order) }}">漲價</a>
<a class="{% if current_filter == 'decrease' %}is-active{% endif %}" href="{{ url_for('dashboard.index', filter='decrease', category=current_category, q=search_query, sort_by=current_sort, order=current_order) }}">降價</a>
<a class="{% if current_filter == 'delisted' %}is-active{% endif %}" href="{{ url_for('dashboard.index', filter='delisted', category=current_category, q=search_query, sort_by=current_sort, order=current_order) }}">下架</a>
</div>
<button class="dashboard-action-button" type="button" data-dashboard-task="crawler">
<i class="fas fa-rotate"></i> 更新
</button>
<button class="dashboard-action-button is-primary" type="button" data-dashboard-task="notification">
<i class="fas fa-bell"></i> 發送通知
</button>
</form>
</div>
</section>
<section>
<div class="dashboard-table-card">
<div class="dashboard-table-head">
<span class="dashboard-section-index momo-mono">04</span>
<span class="dashboard-table-title">{{ 'AI 挑品清單' if current_filter == 'ai_picks' else ('比價覆核隊列' if current_filter == 'pchome_review' else '商品列表') }}</span>
<span class="dashboard-table-meta momo-mono">
{% if current_filter == 'ai_picks' %}
{{ total_items | number_format }} / {{ ai_pick_list_limit }} 品
{% elif current_filter == 'pchome_review' %}
{{ total_items | number_format }} / {{ overview.review_queue_count | default(0) | number_format }} 待處理
{% else %}
{{ total_items | number_format }} 筆
{% endif %}
</span>
<div class="momo-topbar-spacer"></div>
<a class="dashboard-action-link" href="/api/export/excel/all">
<i class="fas fa-download"></i> 匯出全部
</a>
<a class="dashboard-action-link" href="/api/export/excel/changes">
<i class="fas fa-arrow-trend-up"></i> 匯出漲跌
</a>
{% if current_filter == 'ai_picks' %}
<a class="dashboard-action-link" href="/api/export/excel/ai-picks">
<i class="fas fa-file-excel"></i> 匯出 AI 挑品
</a>
{% elif current_filter == 'pchome_review' %}
<a class="dashboard-action-link" href="{{ url_for('export.export_excel_pchome_review', review_status=current_review_status, category=current_category, q=search_query) }}">
<i class="fas fa-file-excel"></i> 匯出覆核
</a>
{% endif %}
</div>
{% if current_filter == 'ai_picks' and ai_pick_summary %}
<div class="dashboard-ai-summary-grid">
<div class="dashboard-ai-summary-item">
<div class="dashboard-ai-summary-label">PICK COUNT</div>
<div class="dashboard-ai-summary-value momo-mono">{{ ai_pick_summary.count | number_format }}</div>
<div class="dashboard-ai-summary-sub">目前清單上限 {{ ai_pick_list_limit }} 品</div>
</div>
<div class="dashboard-ai-summary-item">
<div class="dashboard-ai-summary-label">AVG CONFIDENCE</div>
<div class="dashboard-ai-summary-value momo-mono">{{ (ai_pick_summary.avg_confidence * 100) | round(0) | int }}%</div>
<div class="dashboard-ai-summary-sub">高信心 {{ ai_pick_summary.high_confidence_count | number_format }} 品</div>
</div>
<div class="dashboard-ai-summary-item">
<div class="dashboard-ai-summary-label">EVIDENCE</div>
<div class="dashboard-ai-summary-value momo-mono">{{ ai_pick_summary.avg_evidence_quality | round(0) | int }}%</div>
<div class="dashboard-ai-summary-sub">需補證據 {{ ai_pick_summary.needs_evidence_count | number_format }} 品</div>
</div>
<div class="dashboard-ai-summary-item">
<div class="dashboard-ai-summary-label">AVG GAP</div>
<div class="dashboard-ai-summary-value momo-mono">+{{ ai_pick_summary.avg_gap_pct | round(1) }}%</div>
<div class="dashboard-ai-summary-sub">PChome 相對 MOMO 價差</div>
</div>
<div class="dashboard-ai-summary-item">
<div class="dashboard-ai-summary-label">BEST GAP</div>
<div class="dashboard-ai-summary-value momo-mono">+{{ ai_pick_summary.max_gap_pct | round(1) }}%</div>
<div class="dashboard-ai-summary-sub">清單內最大價格優勢</div>
</div>
<div class="dashboard-ai-summary-item">
<div class="dashboard-ai-summary-label">EVIDENCE GAP</div>
<div class="dashboard-ai-summary-value momo-mono">
{% if ai_pick_summary.top_missing_evidence %}
{{ ai_pick_summary.top_missing_evidence[0].count | number_format }}
{% else %}
0
{% endif %}
</div>
<div class="dashboard-ai-summary-sub">
{% if ai_pick_summary.top_missing_evidence %}
{{ ai_pick_summary.top_missing_evidence[0].label }}
{% else %}
暫無待補證據
{% endif %}
</div>
</div>
</div>
{% endif %}
{% if current_filter == 'pchome_review' %}
<div class="dashboard-review-segments">
{% for option in review_status_options %}
<a class="{% if current_review_status == option.key %}is-active{% endif %}" href="{{ url_for('dashboard.index', page=1, filter='pchome_review', review_status=option.key, category=current_category, q=search_query, sort_by='pchome_review', order='desc') }}">
<span>{{ option.label }}</span>
<span class="momo-mono">{{ option.count | number_format }}</span>
</a>
{% endfor %}
</div>
{% endif %}
<div class="dashboard-table-wrap">
<table class="dashboard-table {% if current_filter == 'ai_picks' %}is-ai-picks{% elif current_filter == 'pchome_review' %}is-review{% endif %}">
<thead>
<tr>
<th>分類</th>
<th>商品名稱</th>
<th class="text-end">
<a href="{{ url_for('dashboard.index', page=1, sort_by='price', order='asc' if current_sort == 'price' and current_order == 'desc' else 'desc', category=current_category, filter=current_filter, q=search_query) }}">MOMO 價格</a>
</th>
<th class="text-end">PChome 價格</th>
<th>競價判讀</th>
{% if current_filter == 'ai_picks' %}
<th>AI 建議</th>
{% elif current_filter == 'pchome_review' %}
<th>覆核動作</th>
{% endif %}
<th class="text-end">
<a href="{{ url_for('dashboard.index', page=1, sort_by='yesterday_change', order='asc' if current_sort == 'yesterday_change' and current_order == 'desc' else 'desc', category=current_category, filter=current_filter, q=search_query) }}">昨日漲跌</a>
</th>
<th class="text-end">
<a href="{{ url_for('dashboard.index', page=1, sort_by='week_change', order='asc' if current_sort == 'week_change' and current_order == 'desc' else 'desc', category=current_category, filter=current_filter, q=search_query) }}">週漲跌</a>
</th>
<th class="text-end">
<a href="{{ url_for('dashboard.index', page=1, sort_by='timestamp', order='asc' if current_sort == 'timestamp' and current_order == 'desc' else 'desc', category=current_category, filter=current_filter, q=search_query) }}">更新時間</a>
</th>
<th class="text-end">上架時間</th>
</tr>
</thead>
<tbody>
{% for item in items %}
{% set product = item.record.product %}
{% set competitor = item.pchome_competitor %}
{% set decision = item.competitor_decision %}
{% set match_status = item.pchome_match_status %}
{% set image_url = product.image_url or ('https://m.momoshop.com.tw/moscdn/goods/' ~ product.i_code ~ '_m.webp') %}
<tr class="is-history-enabled" data-product-id="{{ product.id }}" data-product-name="{{ product.name|e }}" title="點擊查看歷史價格圖表">
<td><span class="dashboard-category">{{ product.category or '未分類' }}</span></td>
<td>
<div class="dashboard-product-cell">
<img class="dashboard-product-thumb" src="{{ image_url }}" alt="{{ product.name }}" loading="lazy" referrerpolicy="no-referrer">
{% set safe_product_url = item.safe_momo_url or '#' %}
<a class="dashboard-product-name momo-tracked-link" href="{{ safe_product_url or '#' }}" target="_blank" rel="noopener noreferrer"
data-momo-original-url="{{ safe_product_url or '#' }}"
data-track-platform="momo"
data-track-source="dashboard-v2-table-main"
data-track-product-id="{{ product.id }}"
data-track-icode="{{ product.i_code }}"
data-track-product-name="{{ product.name|e }}">{{ product.name }}</a>
<div>
<div class="dashboard-platform-links">
<a class="dashboard-platform-link is-momo momo-tracked-link" href="{{ safe_product_url or '#' }}" target="_blank" rel="noopener noreferrer"
data-momo-original-url="{{ safe_product_url or '#' }}"
data-track-platform="momo"
data-track-source="dashboard-v2-table-main"
data-track-product-id="{{ product.id }}"
data-track-icode="{{ product.i_code }}"
data-track-product-name="{{ product.name|e }}">
MOMO {{ product.i_code }}
</a>
{% if competitor and competitor.product_url %}
<a class="dashboard-platform-link is-pchome" href="{{ competitor.product_url }}" target="_blank" rel="noopener noreferrer">
PChome {{ competitor.product_id }}
</a>
{% elif competitor and competitor.product_id %}
<span class="dashboard-platform-muted">PChome {{ competitor.product_id }}</span>
{% else %}
<span class="dashboard-platform-status is-{{ match_status.tone | default('neutral') }}" title="{{ match_status.summary | default('尚未搜尋 PChome 同款') }}">
PChome {{ match_status.label | default('尚未搜尋') }}
</span>
{% endif %}
</div>
{% if competitor and competitor.product_name %}
<div class="dashboard-product-id momo-mono" title="{{ competitor.product_name }}">PChome{{ competitor.product_name }}</div>
{% elif item.pchome_match_attempt and item.pchome_match_attempt.best_competitor_product_name %}
<div class="dashboard-product-id momo-mono" title="{{ item.pchome_match_attempt.best_competitor_product_name }}">候選:{{ item.pchome_match_attempt.best_competitor_product_name }}</div>
{% if item.pchome_match_attempt.competitor_product_url %}
<a class="dashboard-product-id dashboard-candidate-link momo-mono" href="{{ item.pchome_match_attempt.competitor_product_url }}" target="_blank" rel="noopener noreferrer">
PChome 候選 {{ item.pchome_match_attempt.best_competitor_product_id }}
</a>
{% endif %}
{% endif %}
{% if item.ai_pick %}
<div class="dashboard-product-id momo-mono" title="{{ item.ai_pick.reason }}">
AI挑品 #{{ item.ai_pick.rank }} · 信心 {{ (item.ai_pick.confidence * 100) | round(0) | int }}% · 證據 {{ item.ai_pick.evidence_quality | round(0) | int }}% · 價差 {{ item.ai_pick.gap_pct | round(1) }}%
</div>
{% endif %}
</div>
</div>
</td>
<td class="text-end">
<button
class="dashboard-history-button"
type="button"
data-history-trigger
data-product-id="{{ product.id }}"
data-product-name="{{ product.name|e }}"
aria-label="查看歷史價格圖表"
>
<span class="dashboard-price momo-mono">${{ item.record.price | int | number_format }}</span>
<i class="fas fa-chart-line" aria-hidden="true"></i>
</button>
</td>
<td class="text-end momo-mono">
{% if competitor and competitor.price %}
<div class="dashboard-pchome-price">${{ competitor.price | int | number_format }}</div>
{% if competitor.match_score %}
<div class="dashboard-price-sub">match {{ (competitor.match_score * 100) | round(0) | int }}%</div>
{% endif %}
{% elif item.pchome_match_attempt and item.pchome_match_attempt.best_competitor_price and match_status.label == '需單位價比較' %}
<div class="dashboard-pchome-price">${{ item.pchome_match_attempt.best_competitor_price | int | number_format }}</div>
<div class="dashboard-price-sub">候選價,需單位換算</div>
{% else %}
<span class="dashboard-match-state is-{{ match_status.tone | default('neutral') }}" title="{{ match_status.summary | default('尚未搜尋 PChome 同款') }}">
{{ match_status.label | default('尚未搜尋') }}
</span>
{% if match_status.detail %}
<div class="dashboard-price-sub">{{ match_status.detail }}</div>
{% endif %}
{% if match_status.summary %}
<div class="dashboard-price-sub">{{ match_status.summary }}</div>
{% endif %}
{% endif %}
</td>
<td>
<div class="dashboard-competition-card">
<span class="dashboard-competition-badge is-{{ decision.tone }}">{{ decision.label }}</span>
{% if decision.gap_pct is not none %}
<span class="dashboard-competition-meta momo-mono">
MOMO - PChome
{% if decision.gap_amount > 0 %}+{% endif %}${{ decision.gap_amount | round(0) | int | number_format }}
/ {% if decision.gap_pct > 0 %}+{% endif %}{{ decision.gap_pct | round(1) }}%
</span>
{% endif %}
<span class="dashboard-competition-meta">{{ decision.summary }}</span>
</div>
</td>
{% if current_filter == 'ai_picks' %}
<td>
{% if item.ai_pick %}
<div class="dashboard-ai-pick-card">
<div class="dashboard-ai-pick-head">
<span class="dashboard-ai-pick-rank">#{{ item.ai_pick.rank }}</span>
<span class="dashboard-ai-pick-confidence is-{{ item.ai_pick.confidence_band | replace('_', '-') }}">信心 {{ (item.ai_pick.confidence * 100) | round(0) | int }}%</span>
</div>
<div class="dashboard-ai-evidence-line">
<span>機會 {{ item.ai_pick.opportunity_score | round(0) | int }}</span>
<span>證據 {{ item.ai_pick.evidence_quality | round(0) | int }}%</span>
{% if item.ai_pick.margin_rate is not none %}
<span>毛利 {{ item.ai_pick.margin_rate | round(1) }}%</span>
{% endif %}
</div>
<div class="dashboard-ai-pick-reason">{{ item.ai_pick.reason }}</div>
{% if item.ai_pick.missing_evidence %}
<div class="dashboard-ai-evidence-line" title="{{ item.ai_pick.missing_evidence_text }}">
{% for evidence in item.ai_pick.missing_evidence[:3] %}
<span class="dashboard-ai-evidence-chip">{{ evidence }}</span>
{% endfor %}
</div>
{% endif %}
</div>
{% else %}
<span class="dashboard-muted">尚無建議理由</span>
{% endif %}
</td>
{% elif current_filter == 'pchome_review' %}
<td>
{% set review = item.pchome_review %}
<div class="dashboard-review-card">
<div class="dashboard-ai-pick-head">
<span class="dashboard-ai-pick-rank">{{ review.status_label if review else match_status.label }}</span>
{% if review and review.best_match_score %}
<span class="dashboard-ai-pick-confidence is-medium">match {{ (review.best_match_score * 100) | round(0) | int }}%</span>
{% endif %}
</div>
<div class="dashboard-ai-pick-reason">{{ review.action_label if review else decision.summary }}</div>
{% if review %}
{% if review.candidate_pc_name or review.candidate_pc_price %}
<div class="dashboard-review-note">
{% if review.candidate_pc_name %}
{{ review.candidate_pc_name[:42] }}
{% endif %}
{% if review.candidate_pc_price %}
<span class="momo-mono">候選 ${{ review.candidate_pc_price | number_format }}</span>
{% endif %}
</div>
{% endif %}
{% if review.diagnostic_reasons %}
<div class="dashboard-review-reasons" aria-label="比對診斷原因">
{% for reason in review.diagnostic_reasons[:4] %}
<span>{{ reason.label }}</span>
{% endfor %}
</div>
{% endif %}
<div class="dashboard-ai-evidence-line">
{% if review.candidate_count %}
<span>{{ review.candidate_count }} 筆候選</span>
{% endif %}
{% if review.candidate_pc_id %}
<span>PChome {{ review.candidate_pc_id }}</span>
{% endif %}
{% if review.attempted_at %}
<span>{{ review.attempted_at }}</span>
{% endif %}
</div>
{% if review.unit_comparison and review.unit_comparison.summary %}
<div class="dashboard-review-note momo-mono">{{ review.unit_comparison.summary }}</div>
{% endif %}
<div class="dashboard-review-actions" aria-label="人工覆核決策">
{% if review.candidate_pc_id and review.candidate_pc_price %}
<button class="dashboard-review-action is-accept" type="button"
data-pchome-review-action
data-review-action="accept_identity"
data-review-sku="{{ review.sku }}"
data-review-confirm="確認採用這筆 PChome 候選為正式同款配對?">
採用同款
</button>
{% endif %}
<button class="dashboard-review-action" type="button"
data-pchome-review-action
data-review-action="reject_identity"
data-review-sku="{{ review.sku }}"
data-review-confirm="確認否決這筆 PChome 候選?">
否決候選
</button>
<button class="dashboard-review-action" type="button"
data-pchome-review-action
data-review-action="unit_price_required"
data-review-sku="{{ review.sku }}"
data-review-confirm="確認標記為需單位價比較?">
標記單位價
</button>
</div>
{% endif %}
</div>
</td>
{% endif %}
<td class="text-end momo-mono">
{% if item.yesterday_diff > 0 %}
<span class="dashboard-change-up">▲ +{{ item.yesterday_diff | abs | int | number_format }}</span>
{% elif item.yesterday_diff < 0 %}
<span class="dashboard-change-down">▼ -{{ item.yesterday_diff | abs | int | number_format }}</span>
{% else %}
<span class="dashboard-muted">--</span>
{% endif %}
</td>
<td class="text-end momo-mono">
{% set week_diff = item.stats.get('7d_diff', 0) %}
{% if week_diff > 0 %}
<span class="dashboard-change-up">+{{ week_diff | int | number_format }}</span>
{% elif week_diff < 0 %}
<span class="dashboard-change-down">-{{ week_diff | abs | int | number_format }}</span>
{% else %}
<span class="dashboard-muted">--</span>
{% endif %}
</td>
<td class="dashboard-table-time text-end momo-mono">
{{ item.record.timestamp.strftime('%m-%d %H:%M') if item.record.timestamp else '--' }}
</td>
<td class="dashboard-table-time text-end momo-mono">
{{ item.safe_created_at.strftime('%m-%d %H:%M') if item.safe_created_at else '--' }}
</td>
</tr>
{% else %}
<tr>
<td colspan="{{ 10 if current_filter in ['ai_picks', 'pchome_review'] else 9 }}">
<div class="dashboard-empty">
{% if search_query %}
找不到與「{{ search_query }}」相關的商品
{% else %}
目前沒有符合條件的商品
{% endif %}
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% if total_pages > 1 %}
<div class="dashboard-pagination">
{% if current_page > 1 %}
<a class="dashboard-action-link" href="{{ url_for('dashboard.index', page=current_page - 1, category=current_category, filter=current_filter, review_status=current_review_status, q=search_query, sort_by=current_sort, order=current_order) }}">上一頁</a>
{% endif %}
<span class="dashboard-table-meta momo-mono">第 {{ current_page }} / {{ total_pages }} 頁</span>
{% if current_page < total_pages %}
<a class="dashboard-action-link" href="{{ url_for('dashboard.index', page=current_page + 1, category=current_category, filter=current_filter, review_status=current_review_status, q=search_query, sort_by=current_sort, order=current_order) }}">下一頁</a>
{% endif %}
</div>
{% endif %}
</div>
</section>
</div>
<div class="modal fade dashboard-history-modal" id="historyModal" tabindex="-1" aria-labelledby="historyModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<div>
<h5 class="modal-title" id="historyModalLabel">歷史價格走勢</h5>
<div class="dashboard-history-subtitle momo-mono" id="historyModalSubtitle">真實價格紀錄</div>
<div class="dashboard-history-range" aria-label="價格歷史區間">
<button type="button" data-history-range="week"></button>
<button class="is-active" type="button" data-history-range="month"></button>
<button type="button" data-history-range="quarter"></button>
<button type="button" data-history-range="year"></button>
</div>
</div>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="關閉"></button>
</div>
<div class="modal-body">
<div class="dashboard-chart-shell">
<div class="dashboard-chart-state" id="historyChartState">載入價格歷史中...</div>
<canvas class="dashboard-chart-canvas is-hidden" id="priceChart"></canvas>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block extra_js %}
<script src="{{ url_for('static', filename='js/analysis-chart-theme.js') }}"></script>
<script src="{{ url_for('static', filename='js/page-dashboard-v2.js') }}"></script>
{% endblock %}