diff --git a/CONSTITUTION.md b/CONSTITUTION.md index fa985df..f0c1bb6 100644 --- a/CONSTITUTION.md +++ b/CONSTITUTION.md @@ -2,7 +2,7 @@ > 本文件定義專案開發的核心準則與不可違反的規範 > **建立日期**: 2026-01-12 -> **當前版本**: V10.35 (Frontend v2 web static assets mounted) +> **當前版本**: V10.36 (EDM dashboard v2 feature flag) > **最後更新**: 2026-04-30 --- diff --git a/app.py b/app.py index 21b2a03..29e8747 100644 --- a/app.py +++ b/app.py @@ -95,8 +95,8 @@ except Exception as e: sys_log.error(f"無法檢測磁碟空間: {e}") # 🚩 系統版本定義 (備份與顯示用) -# 🚩 2026-04-30 V10.35: Frontend v2 web static assets mounted -SYSTEM_VERSION = "V10.35" +# 🚩 2026-04-30 V10.36: EDM dashboard v2 feature flag +SYSTEM_VERSION = "V10.36" # ========================================== # 🔒 SQL Injection 防護函數 diff --git a/config.py b/config.py index 0867842..8364057 100644 --- a/config.py +++ b/config.py @@ -254,7 +254,7 @@ YOUTUBE_API_KEY = os.getenv('YOUTUBE_API_KEY', '') # ========================================== # 系統版本與路徑 # ========================================== -SYSTEM_VERSION = "V10.35" +SYSTEM_VERSION = "V10.36" LOG_FILE_PATH = os.path.join(BASE_DIR, 'logs/system.log') public_url = PUBLIC_URL # 用於模板顯示 diff --git a/docs/memory/history_logs.md b/docs/memory/history_logs.md index cb336c4..34ca92d 100644 --- a/docs/memory/history_logs.md +++ b/docs/memory/history_logs.md @@ -58,6 +58,7 @@ - **Frontend V2 Phase 0 assets**: 新增 `static/css/ewoooc-tokens.css`、`static/css/ewoooc-shell.css`、`templates/ewoooc_base.html` 與 `_ewoooc_shell.html`,先建立可重用 shell,不替換既有頁面;憲章補「真實資料與真實頁面」紅線,禁止假資料冒充完成。 - **Dashboard V2 feature flag**: `/` 預設仍走既有 `dashboard.html`,`/?ui=v2` 才渲染 `dashboard_v2.html`;新版頁沿用既有 dashboard 真實資料與篩選/排序參數,不建立 mock 商品或假 KPI。 - **Frontend V2 static mount**: `momo-app` 補 `./web/static:/app/web/static:ro`,確保 Flask `url_for('static', filename='css/ewoooc-*.css')` 會從實際 `STATIC_DIR=/app/web/static` 讀到 V2 CSS;保留 `./static:/app/static:ro` 供 local-dev Nginx/舊資產相容。 +- **EDM Dashboard V2 feature flag**: 五個活動看板入口預設仍走既有 `edm_dashboard.html`,加上 `?ui=v2` 才渲染 `edm_dashboard_v2.html`;新版頁沿用既有活動真實 `grouped_items/slot_stats/scheduler_stats`。 ### 2026-04-28~29:Phase 3e 重構大戰 + daily_sales cache 隱形 bug 根除 - **app.py 縮減 -10.8%**: 7,386 → 6,590 行,11 commits 全綠零 502。 diff --git a/routes/edm_routes.py b/routes/edm_routes.py index 3b4bded..fed91d4 100644 --- a/routes/edm_routes.py +++ b/routes/edm_routes.py @@ -321,7 +321,9 @@ def edm_dashboard(): scheduler_stats = load_scheduler_stats() now_taipei = datetime.now(TAIPEI_TZ) - return render_template('edm_dashboard.html', + template_name = 'edm_dashboard_v2.html' if request.args.get('ui') == 'v2' else 'edm_dashboard.html' + + return render_template(template_name, promo_pages=promo_pages, current_promo_page='edm', page_title='MOMO 限時搶購', @@ -337,7 +339,8 @@ def edm_dashboard(): current_sort=sort_by, current_order=order, slugify=slugify, - datetime_now=now_taipei.strftime('%Y-%m-%d %H:%M:%S')) + datetime_now=now_taipei.strftime('%Y-%m-%d %H:%M:%S'), + active_page='edm') except Exception as e: sys_log.error(f"EDM Dashboard 渲染錯誤: {e}") return f"系統錯誤: {e}" @@ -372,7 +375,9 @@ def festival_dashboard(): scheduler_stats = load_scheduler_stats() - return render_template('edm_dashboard.html', + template_name = 'edm_dashboard_v2.html' if request.args.get('ui') == 'v2' else 'edm_dashboard.html' + + return render_template(template_name, promo_pages=promo_pages, current_promo_page='festival', page_title=PAGE_NAME, @@ -386,7 +391,8 @@ def festival_dashboard(): scheduler_stats=scheduler_stats, current_sort=sort_by, current_order=order, - slugify=slugify) + slugify=slugify, + active_page='edm') except Exception as e: sys_log.error(f"{PAGE_NAME} Dashboard 渲染錯誤: {e}") return f"系統錯誤: {e}" @@ -421,7 +427,9 @@ def mothers_day_dashboard(): scheduler_stats = load_scheduler_stats() - return render_template('edm_dashboard.html', + template_name = 'edm_dashboard_v2.html' if request.args.get('ui') == 'v2' else 'edm_dashboard.html' + + return render_template(template_name, promo_pages=promo_pages, current_promo_page='mothers_day', page_title=PAGE_NAME, @@ -435,7 +443,8 @@ def mothers_day_dashboard(): scheduler_stats=scheduler_stats, current_sort=sort_by, current_order=order, - slugify=slugify) + slugify=slugify, + active_page='edm') except Exception as e: sys_log.error(f"{PAGE_NAME} Dashboard 渲染錯誤: {e}") return f"系統錯誤: {e}" @@ -470,7 +479,9 @@ def valentine_520_dashboard(): scheduler_stats = load_scheduler_stats() - return render_template('edm_dashboard.html', + template_name = 'edm_dashboard_v2.html' if request.args.get('ui') == 'v2' else 'edm_dashboard.html' + + return render_template(template_name, promo_pages=promo_pages, current_promo_page='valentine_520', page_title=PAGE_NAME, @@ -484,7 +495,8 @@ def valentine_520_dashboard(): scheduler_stats=scheduler_stats, current_sort=sort_by, current_order=order, - slugify=slugify) + slugify=slugify, + active_page='edm') except Exception as e: sys_log.error(f"{PAGE_NAME} Dashboard 渲染錯誤: {e}") return f"系統錯誤: {e}" @@ -519,7 +531,9 @@ def labor_day_dashboard(): scheduler_stats = load_scheduler_stats() - return render_template('edm_dashboard.html', + template_name = 'edm_dashboard_v2.html' if request.args.get('ui') == 'v2' else 'edm_dashboard.html' + + return render_template(template_name, promo_pages=promo_pages, current_promo_page='labor_day', page_title=PAGE_NAME, @@ -533,7 +547,8 @@ def labor_day_dashboard(): scheduler_stats=scheduler_stats, current_sort=sort_by, current_order=order, - slugify=slugify) + slugify=slugify, + active_page='edm') except Exception as e: sys_log.error(f"{PAGE_NAME} Dashboard 渲染錯誤: {e}") return f"系統錯誤: {e}" diff --git a/templates/edm_dashboard_v2.html b/templates/edm_dashboard_v2.html new file mode 100644 index 0000000..9813bb3 --- /dev/null +++ b/templates/edm_dashboard_v2.html @@ -0,0 +1,747 @@ +{% extends 'ewoooc_base.html' %} + +{% block title %}EwoooC 活動看板{% endblock %} + +{% block extra_css %} + +{% endblock %} + +{% block ewooo_content %} +{% set current_endpoint = 'edm.' ~ current_promo_page ~ '_dashboard' %} +{% set current_stats = {'new': 0, 'up': 0, 'down': 0, 'delisted_last_run': 0, 'on_shelf': 0} %} +{% if active_tab in slot_stats %} + {% set current_stats = slot_stats[active_tab] %} +{% elif slot_stats %} + {% set current_stats = slot_stats.values() | list | first %} +{% endif %} +{% set task_key = 'festival_task' if current_promo_page == 'festival' else 'edm_task' %} +{% set run_list = scheduler_stats.get(task_key, []) %} +{% set latest_run = run_list[0] if run_list else None %} + +
| 分類 | ++ {% set next_order_name = 'asc' if current_sort == 'name' and current_order == 'desc' else 'desc' %} + 商品資訊 + | ++ {% set next_order_price = 'asc' if current_sort == 'price' and current_order == 'desc' else 'desc' %} + 價格 + | ++ {% if current_promo_page == 'edm' %} + {% set next_order_qty = 'asc' if current_sort == 'remain_qty' and current_order == 'desc' else 'desc' %} + 倒數組數 + {% else %} + 狀態 + {% endif %} + | +
|---|---|---|---|
| + {{ item.main_category or '未分類' }} + | +
+
+ {% if item.image_url %}
+
+ 無圖
+ {% endif %}
+
+ {{ item.name }}
+
+ ID {{ item.i_code }}
+
+ {% if item.status_change == 'NEW' %}
+ NEW
+ {% elif item.status_change == 'PRICE_DOWN' %}
+ 降價
+ {% elif item.status_change == 'PRICE_UP' %}
+ 漲價
+ {% elif item.status_change in ['DELISTED', 'SLOT_END'] %}
+ 下架
+ {% endif %}
+ {% if item.discount_text %}
+ {{ item.discount_text }}
+ {% endif %}
+
+ |
+
+ {% if item.previous_price and item.price and item.previous_price != item.price %}
+ {% set diff = item.price - item.previous_price %}
+ {% if diff < 0 %}
+ ▼ {{ (diff|abs) | number_format }}
+ {% else %}
+ ▲ {{ diff | number_format }}
+ {% endif %}
+ ${{ item.previous_price | number_format }}
+ ${{ item.price | number_format }}
+ {% elif item.price is not none %}
+ ${{ item.price | number_format }}
+ {% else %}
+ --
+ {% endif %}
+ |
+ + {% if current_promo_page == 'edm' %} + {% if item.remain_qty is not none %} + 剩 {{ item.remain_qty | number_format }} 組 + {% else %} + -- + {% endif %} + {% else %} + {{ item.status_change or '活動中' }} + {% endif %} + | +
|
+ 此時段目前沒有商品資料
+ |
+ |||