@miraiva_test/miravia-order-report 0.1.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cli.js +61 -0
- package/dist/.keypair.json +5 -0
- package/dist/README.md +249 -0
- package/dist/SKILL.md +329 -0
- package/dist/VERSION +1 -0
- package/dist/executive_summary_prompt_v2.md +574 -0
- package/dist/reference.md +409 -0
- package/dist/requirements.txt +6 -0
- package/dist/scripts/auth_cli.py +1442 -0
- package/dist/scripts/config.py +216 -0
- package/dist/scripts/cred_crypto.py +235 -0
- package/dist/scripts/estimate_time.py +244 -0
- package/dist/scripts/export_orders.py +1549 -0
- package/dist/scripts/i18n.py +1162 -0
- package/dist/scripts/inject_insights.py +96 -0
- package/dist/scripts/login.bat +14 -0
- package/dist/scripts/miravia_client.py +468 -0
- package/dist/scripts/report_v4.py +1694 -0
- package/dist/scripts/run.bat +15 -0
- package/dist/scripts/skill_crypto.py +86 -0
- package/dist/scripts/skill_keypair.py +86 -0
- package/dist/scripts/skill_logger.py +262 -0
- package/dist/scripts/test_v4_render.py +322 -0
- package/dist/scripts/trace_logger.py +97 -0
- package/dist/scripts/validate_render.py +101 -0
- package/dist/static/chart.umd.min.js +20 -0
- package/dist/static/chartjs-plugin-datalabels.min.js +7 -0
- package/dist/static/miravia-logo.svg +61 -0
- package/dist/templates/report_v4.html +1005 -0
- package/lib/build-dist.js +86 -0
- package/lib/clean-dist.js +18 -0
- package/lib/installer.js +444 -0
- package/package.json +34 -0
|
@@ -0,0 +1,1005 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="{{ t.lang_code }}">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>{{ t.page_title }}</title>
|
|
7
|
+
<script>{{ chartjs_inline|safe }}</script>
|
|
8
|
+
<script>{{ chartjs_datalabels_inline|safe }}</script>
|
|
9
|
+
<style>
|
|
10
|
+
:root{--pri:#6b5ff2;--pri-l:#f5f3ff;--bg:#f8f7fc;--card:#fff;--txt:#1f2329;--mut:#86909c;--ok:#10b981;--wn:#f59e0b;--dg:#ef4444;--info:#6b5ff2;--r:12px;--sh:0 2px 12px rgba(107,95,242,.08)}
|
|
11
|
+
*{box-sizing:border-box}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"PingFang SC","Helvetica Neue",Arial,sans-serif;background:var(--bg);color:var(--txt)}
|
|
12
|
+
.hdr{background:linear-gradient(135deg,#3730a3 0%,#4f46e5 50%,#6d5ed8 100%);color:#fff;padding:24px 28px 14px;position:relative}
|
|
13
|
+
.hdr-top{display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:8px}
|
|
14
|
+
.hdr h1{margin:0;font-size:20px;font-weight:600}.hdr .meta{opacity:.8;font-size:12px}
|
|
15
|
+
.hdr .badge{background:rgba(255,255,255,.18);padding:4px 10px;border-radius:12px;font-size:11px}
|
|
16
|
+
.hdr .brand-logo{height:22px;opacity:.9}
|
|
17
|
+
.tf{display:flex;align-items:center;gap:6px;margin-top:12px;flex-wrap:wrap}
|
|
18
|
+
.tf-label{font-size:11px;opacity:.6;margin-right:2px}
|
|
19
|
+
.tf-btn{padding:3px 10px;border:1px solid rgba(255,255,255,.2);background:transparent;color:rgba(255,255,255,.75);border-radius:12px;font-size:11px;cursor:pointer;font-family:inherit;transition:all .15s}
|
|
20
|
+
.tf-btn:hover{background:rgba(255,255,255,.12)}.tf-btn.on{background:rgba(255,255,255,.25);color:#fff;border-color:rgba(255,255,255,.4);font-weight:500}
|
|
21
|
+
.tf-sep{width:1px;height:16px;background:rgba(255,255,255,.15);margin:0 2px}
|
|
22
|
+
.tf input[type=date]{background:rgba(255,255,255,.1);border:1px solid rgba(255,255,255,.15);color:#fff;padding:2px 6px;border-radius:6px;font-size:11px;font-family:inherit;width:110px}
|
|
23
|
+
.kpis{display:grid;grid-template-columns:repeat(auto-fit,minmax(155px,1fr));gap:8px;margin-top:10px}
|
|
24
|
+
.kpi{background:rgba(255,255,255,.08);border:1px solid rgba(255,255,255,.1);border-radius:8px;padding:10px 12px}
|
|
25
|
+
.kpi .lb{font-size:10px;opacity:.85;margin-bottom:1px}.kpi .vl{font-size:18px;font-weight:700}
|
|
26
|
+
.kpi .sub{display:flex;gap:10px;font-size:10px;margin-top:3px;opacity:.9}.kpi .sub .pm{color:#86efac}.kpi .sub .pa{color:#ffb366}
|
|
27
|
+
.es{background:var(--card);margin:14px 28px;padding:16px 20px;border-radius:var(--r);box-shadow:var(--sh);border-left:4px solid var(--pri)}
|
|
28
|
+
.es-t{font-size:14px;font-weight:600;margin-bottom:8px;color:var(--pri)}.es-body{font-size:13px;line-height:1.8;margin-bottom:12px}
|
|
29
|
+
.es-grid{display:grid;grid-template-columns:1fr 1fr;gap:12px}
|
|
30
|
+
.es-box{background:#fafbfc;border-radius:8px;padding:10px 12px}.es-box h4{margin:0 0 6px;font-size:12px;color:var(--mut);font-weight:500}
|
|
31
|
+
.ai{display:flex;align-items:flex-start;gap:6px;padding:4px 0;font-size:12px;border-bottom:1px solid #eef0f3}.ai:last-child{border-bottom:none}
|
|
32
|
+
.ad{width:7px;height:7px;border-radius:50%;margin-top:4px;flex-shrink:0}.ad-r{background:var(--dg)}.ad-y{background:var(--wn)}.ad-g{background:var(--ok)}
|
|
33
|
+
.ac{display:flex;align-items:center;gap:6px;padding:4px 0;font-size:12px;border-bottom:1px solid #eef0f3}.ac:last-child{border-bottom:none}
|
|
34
|
+
.ap{font-size:10px;padding:1px 6px;border-radius:3px;font-weight:500;flex-shrink:0}
|
|
35
|
+
.p0{background:#ffeceb;color:var(--dg)}.p1{background:#fff7e8;color:var(--wn)}.p2{background:#e8ffea;color:var(--ok)}
|
|
36
|
+
.tc{margin:0 28px 16px}.tn{display:flex;gap:3px;background:var(--card);padding:4px;border-radius:var(--r) var(--r) 0 0;box-shadow:var(--sh);border-bottom:1px solid #eef0f3;overflow-x:auto;position:sticky;top:0;z-index:100}
|
|
37
|
+
.tb{padding:8px 16px;border:none;background:transparent;color:var(--mut);font-size:13px;cursor:pointer;border-radius:6px;white-space:nowrap;transition:all .2s;font-family:inherit}
|
|
38
|
+
.tb:hover{color:var(--txt);background:#f5f6f8}.tb.on{color:var(--pri);background:var(--pri-l);font-weight:600}
|
|
39
|
+
.tp{display:none}.tp.on{display:block}
|
|
40
|
+
.sec{background:var(--card);border-radius:var(--r);box-shadow:var(--sh);padding:18px;margin-bottom:12px}
|
|
41
|
+
.sec-t{font-size:15px;font-weight:600;margin:0 0 2px;display:flex;align-items:center;gap:6px}.sec-s{font-size:12px;color:var(--mut);margin-bottom:14px}
|
|
42
|
+
.ib{background:var(--pri-l);border-left:3px solid var(--pri);border-radius:0 6px 6px 0;padding:8px 12px;font-size:12px;line-height:1.6;color:#4c3d99;margin-top:12px}
|
|
43
|
+
.g2{display:grid;grid-template-columns:1fr 1fr;gap:12px}.g3{display:grid;grid-template-columns:repeat(3,1fr);gap:12px}
|
|
44
|
+
@media(max-width:900px){.g2,.g3{grid-template-columns:1fr}.es-grid{grid-template-columns:1fr}.hdr{padding:14px 14px}.tc,.es{margin:8px 14px}}
|
|
45
|
+
.cc{position:relative}.cc h4{margin:0 0 8px;font-size:12px;font-weight:600}.cw{position:relative;height:250px}.cwl{height:300px}
|
|
46
|
+
.pf{display:flex;gap:4px;margin-bottom:8px}
|
|
47
|
+
.pf button{padding:3px 10px;border:1px solid #dfe1e6;background:#fff;color:var(--mut);border-radius:12px;font-size:11px;cursor:pointer;font-family:inherit;transition:all .15s}
|
|
48
|
+
.pf button:hover{border-color:var(--pri);color:var(--pri)}.pf button.on{background:var(--pri);color:#fff;border-color:var(--pri);font-weight:500}
|
|
49
|
+
table{width:100%;border-collapse:collapse;font-size:12px}th,td{padding:7px 10px;text-align:left;border-bottom:1px solid #eef0f3}
|
|
50
|
+
th{background:#fafbfc;color:var(--mut);font-weight:500;font-size:11px}tr:hover td{background:#f4f7ff}
|
|
51
|
+
.n{text-align:right;font-variant-numeric:tabular-nums}
|
|
52
|
+
.ts{color:var(--ok)}.tw{color:var(--wn)}.td{color:var(--dg)}.tm{color:var(--mut)}
|
|
53
|
+
.tag{display:inline-block;padding:1px 6px;border-radius:3px;font-size:10px;font-weight:500}
|
|
54
|
+
.sev{display:inline-flex;align-items:center;gap:3px;padding:1px 6px;border-radius:3px;font-size:11px;font-weight:500}
|
|
55
|
+
.sh{background:#ffeceb;color:var(--dg)}.sm{background:#fff7e8;color:var(--wn)}.sl{background:#e8ffea;color:var(--ok)}
|
|
56
|
+
.at th,.at td{padding:8px 10px}.at tr.ah td{background:#fff1f0}.at tr.am td{background:#fff7e6}
|
|
57
|
+
.ms{display:flex;gap:4px;margin-bottom:8px}
|
|
58
|
+
.ms button{padding:3px 10px;border:1px solid #dfe1e6;background:#fff;color:var(--mut);border-radius:12px;font-size:11px;cursor:pointer;font-family:inherit;transition:all .15s}
|
|
59
|
+
.ms button:hover{border-color:var(--info);color:var(--info)}.ms button.on{background:var(--info);color:#fff;border-color:var(--info)}
|
|
60
|
+
.mq{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-top:12px}
|
|
61
|
+
.mqq{background:#fafbfc;border-radius:8px;padding:12px;border:2px solid transparent}
|
|
62
|
+
.mqq.q1{border-color:var(--ok)}.mqq.q2{border-color:var(--wn)}.mqq.q3{border-color:var(--mut)}.mqq.q4{border-color:var(--dg)}
|
|
63
|
+
.mt{font-size:12px;font-weight:600;margin-bottom:4px}.mi{padding:2px 0;border-bottom:1px solid #eef0f3;font-size:11px}
|
|
64
|
+
.mr{background:#fafbfc;border-radius:8px;padding:12px;border-left:3px solid var(--pri);margin-bottom:10px}
|
|
65
|
+
.mr h4{margin:0 0 6px;font-size:13px}.mk{display:grid;grid-template-columns:repeat(3,1fr);gap:6px;margin-bottom:8px}
|
|
66
|
+
.mk-i{text-align:center}.mk-i .v{font-size:15px;font-weight:600}.mk-i .l{font-size:10px;color:var(--mut)}
|
|
67
|
+
.ft{text-align:center;color:var(--mut);font-size:11px;padding:16px}
|
|
68
|
+
.modal-bg{display:none;position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,.45);z-index:1000;justify-content:center;align-items:center}
|
|
69
|
+
.modal-bg.show{display:flex}
|
|
70
|
+
.modal-box{background:#fff;border-radius:12px;width:90%;max-width:680px;max-height:80vh;overflow-y:auto;box-shadow:0 8px 32px rgba(0,0,0,.18);animation:modalIn .2s ease}
|
|
71
|
+
@keyframes modalIn{from{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}
|
|
72
|
+
.modal-hd{display:flex;justify-content:space-between;align-items:center;padding:16px 20px;border-bottom:1px solid #eef0f3}
|
|
73
|
+
.modal-hd h3{margin:0;font-size:16px;font-weight:600}
|
|
74
|
+
.modal-close{background:none;border:none;font-size:20px;cursor:pointer;color:var(--mut);padding:4px 8px;border-radius:4px}
|
|
75
|
+
.modal-close:hover{background:#f5f6f8;color:var(--txt)}
|
|
76
|
+
.modal-bd{padding:16px 20px}.modal-bd table{margin-bottom:12px}.modal-bd .ib{margin-top:8px}
|
|
77
|
+
.clickable{cursor:pointer;transition:background .15s}.clickable:hover td{background:#e8f0fe!important}
|
|
78
|
+
.click-hint{font-size:11px;color:var(--info);margin-left:8px}
|
|
79
|
+
.no-data{text-align:center;color:var(--mut);padding:40px;font-size:13px}
|
|
80
|
+
.no-data-inline{text-align:center;color:var(--mut);padding:10px 0 6px;font-size:13px}
|
|
81
|
+
.sec-empty>.sec-s{display:none}
|
|
82
|
+
.sec-empty>.cw,.sec-empty>.mq,.sec-empty>.ms,.sec-empty>.g2,.sec-empty>.g3{display:none!important}
|
|
83
|
+
.sec-empty>table{display:none!important}
|
|
84
|
+
.lang-sel{background:rgba(255,255,255,.18);color:#fff;border:1px solid rgba(255,255,255,.25);padding:3px 8px;border-radius:8px;font-size:11px;cursor:pointer;font-family:inherit;outline:none;appearance:none;-webkit-appearance:none}
|
|
85
|
+
.lang-sel:hover{background:rgba(255,255,255,.28)}
|
|
86
|
+
.lang-sel option{color:#1f2329;background:#fff}
|
|
87
|
+
.ai-badge{display:inline-flex;align-items:center;gap:3px;background:linear-gradient(135deg,#e8e0ff 0%,#f0e8ff 100%);color:#6b5ff2;font-size:10px;font-weight:500;padding:2px 7px;border-radius:10px;border:1px solid rgba(107,95,242,.2);vertical-align:middle;margin-left:6px}
|
|
88
|
+
</style>
|
|
89
|
+
</head>
|
|
90
|
+
<body>
|
|
91
|
+
<div class="hdr">
|
|
92
|
+
<div class="hdr-top">
|
|
93
|
+
<div><h1 data-i18n="title">{{ t.title }}</h1><div class="meta" data-i18n="subtitle">{{ t.subtitle }}</div></div>
|
|
94
|
+
<div style="display:flex;align-items:center;gap:10px"><select id="langSel" class="lang-sel">{% for l in supported_langs %}<option value="{{ l }}"{% if l == current_lang %} selected{% endif %}>{{ l|upper }}</option>{% endfor %}</select><div class="badge" id="rangeBadge"></div><img class="brand-logo" src="data:image/svg+xml;base64,{{ logo_base64 }}" alt="Miravia"></div>
|
|
95
|
+
</div>
|
|
96
|
+
<div class="tf" id="tfBox"></div>
|
|
97
|
+
<div class="kpis" id="hk"></div>
|
|
98
|
+
</div>
|
|
99
|
+
<div class="es">
|
|
100
|
+
<div class="es-t" data-i18n="exec_summary">{{ t.exec_summary }} <span class="ai-badge" data-i18n="ai_generated_badge">{{ t.ai_generated_badge }}</span></div>
|
|
101
|
+
<div class="es-body" id="esBody"></div>
|
|
102
|
+
<div class="es-grid">
|
|
103
|
+
<div class="es-box"><h4 data-i18n="top_alerts">{{ t.top_alerts }}</h4><div id="esAlerts"></div></div>
|
|
104
|
+
<div class="es-box"><h4 data-i18n="suggested_actions">{{ t.suggested_actions }}</h4><div id="esActions"></div></div>
|
|
105
|
+
</div>
|
|
106
|
+
</div>
|
|
107
|
+
<div class="tc">
|
|
108
|
+
<div class="tn">
|
|
109
|
+
<button class="tb on" data-t="overview" data-i18n="tab_overview">{{ t.tab_overview }}</button>
|
|
110
|
+
<button class="tb" data-t="sales" data-i18n="tab_sales">{{ t.tab_sales }}</button>
|
|
111
|
+
<button class="tb" data-t="refund" data-i18n="tab_refund">{{ t.tab_refund }}</button>
|
|
112
|
+
<button class="tb" data-t="product" data-i18n="tab_product">{{ t.tab_product }}</button>
|
|
113
|
+
<button class="tb" data-t="buyer" data-i18n="tab_buyer">{{ t.tab_buyer }}</button>
|
|
114
|
+
<button class="tb" data-t="logistics" data-i18n="tab_logistics">{{ t.tab_logistics }}</button>
|
|
115
|
+
</div>
|
|
116
|
+
<div class="tp on" id="t-overview">
|
|
117
|
+
<div class="sec"><div class="sec-t" data-i18n="sec_kpi_dashboard">{{ t.sec_kpi_dashboard }}</div><div class="sec-s" data-i18n="sec_kpi_sub">{{ t.sec_kpi_sub }}</div><div id="ovTable"></div></div>
|
|
118
|
+
<div class="g2">
|
|
119
|
+
<div class="sec"><div class="sec-t" data-i18n="sec_channel_compare">{{ t.sec_channel_compare }}</div><div class="sec-s" data-i18n="sec_channel_sub">{{ t.sec_channel_sub }}</div>
|
|
120
|
+
<div style="display:grid;grid-template-columns:1fr 1fr;gap:12px">
|
|
121
|
+
<div class="cc"><h4 data-i18n="chart_scale">{{ t.chart_scale }}</h4><div class="cw"><canvas id="chCompare"></canvas></div></div>
|
|
122
|
+
<div class="cc"><h4 data-i18n="chart_rate">{{ t.chart_rate }}</h4><div class="cw"><canvas id="chCompareRate"></canvas></div></div>
|
|
123
|
+
</div><div class="ib" id="ibCompare"></div></div>
|
|
124
|
+
<div class="sec"><div class="sec-t" data-i18n="sec_country_gmv">{{ t.sec_country_gmv }}</div><div class="sec-s" data-i18n="sec_country_sub">{{ t.sec_country_sub }}</div>
|
|
125
|
+
<div class="pf" data-chart="chCountry" data-mode="country"></div>
|
|
126
|
+
<div class="cw" style="height:270px"><canvas id="chCountry"></canvas></div><div class="ib" id="ibCountry"></div></div>
|
|
127
|
+
</div>
|
|
128
|
+
<div class="sec"><div class="sec-t" data-i18n="sec_risk_alerts">{{ t.sec_risk_alerts }} <span class="ai-badge" data-i18n="ai_generated_badge">{{ t.ai_generated_badge }}</span></div><div class="sec-s" data-i18n="sec_risk_sub">{{ t.sec_risk_sub }}</div>
|
|
129
|
+
<table class="at"><thead><tr><th data-i18n="th_alert_item">{{ t.th_alert_item }}</th><th data-i18n="th_severity">{{ t.th_severity }}</th><th data-i18n="th_trigger">{{ t.th_trigger }}</th></tr></thead><tbody id="alertTb"></tbody></table></div>
|
|
130
|
+
</div>
|
|
131
|
+
<div class="tp" id="t-sales">
|
|
132
|
+
<div class="sec"><div class="sec-t" data-i18n="sec_trend">{{ t.sec_trend }}</div><div class="sec-s" data-i18n="sec_trend_sub">{{ t.sec_trend_sub }}</div>
|
|
133
|
+
<div class="ms" id="trendMs"></div><div class="pf" id="trendPf"></div>
|
|
134
|
+
<div class="cw cwl"><canvas id="chTrend"></canvas></div><div class="ib" id="ibTrend"></div></div>
|
|
135
|
+
<div class="g2">
|
|
136
|
+
<div class="sec"><div class="sec-t" data-i18n="sec_net_rev">{{ t.sec_net_rev }}</div><div class="sec-s" data-i18n="sec_net_rev_sub">{{ t.sec_net_rev_sub }}</div>
|
|
137
|
+
<div class="pf" data-chart="chNetRev"></div><div class="cw" style="height:260px"><canvas id="chNetRev"></canvas></div><div class="ib" id="ibNetRev"></div></div>
|
|
138
|
+
<div class="sec"><div class="sec-t" data-i18n="sec_aov">{{ t.sec_aov }}</div><div class="sec-s" data-i18n="sec_aov_sub">{{ t.sec_aov_sub }}</div>
|
|
139
|
+
<div class="pf" data-chart="chAOV"></div><div class="cw" style="height:260px"><canvas id="chAOV"></canvas></div><div class="ib" id="ibAOV"></div></div>
|
|
140
|
+
</div>
|
|
141
|
+
<div class="g2">
|
|
142
|
+
<div class="sec"><div class="sec-t" data-i18n="sec_pay">{{ t.sec_pay }}</div><div class="sec-s" data-i18n="sec_pay_sub">{{ t.sec_pay_sub }}</div>
|
|
143
|
+
<div class="pf" data-chart="chPay"></div><div class="cw" style="height:260px"><canvas id="chPay"></canvas></div><div class="ib" id="ibPay"></div></div>
|
|
144
|
+
<div class="sec"><div class="sec-t" data-i18n="sec_voucher">{{ t.sec_voucher }}</div><div class="sec-s" data-i18n="sec_voucher_sub">{{ t.sec_voucher_sub }}</div>
|
|
145
|
+
<div class="cw" style="height:260px"><canvas id="chVoucher"></canvas></div><div class="ib" id="ibVoucher"></div></div>
|
|
146
|
+
</div>
|
|
147
|
+
<div class="sec"><div class="sec-t" data-i18n="sec_shipping">{{ t.sec_shipping }}</div><div class="sec-s" data-i18n="sec_shipping_sub">{{ t.sec_shipping_sub }}</div>
|
|
148
|
+
<table><thead><tr><th data-i18n="th_dimension">{{ t.th_dimension }}</th><th class="n" data-i18n="th_total_shipping">{{ t.th_total_shipping }}</th><th class="n" data-i18n="th_avg_shipping">{{ t.th_avg_shipping }}</th><th class="n" data-i18n="th_seller_disc">{{ t.th_seller_disc }}</th><th class="n" data-i18n="th_disc_ratio">{{ t.th_disc_ratio }}</th><th class="n" data-i18n="th_free_ratio">{{ t.th_free_ratio }}</th></tr></thead><tbody id="shipTb"></tbody></table>
|
|
149
|
+
<div class="ib" id="ibShip"></div></div>
|
|
150
|
+
</div>
|
|
151
|
+
<div class="tp" id="t-refund">
|
|
152
|
+
<div class="sec"><div class="sec-t" data-i18n="sec_status">{{ t.sec_status }}</div><div class="sec-s" data-i18n="sec_status_sub">{{ t.sec_status_sub }}</div>
|
|
153
|
+
<div class="pf" data-chart="chStatus"></div><div class="cw" style="height:270px"><canvas id="chStatus"></canvas></div><div class="ib" id="ibStatus"></div></div>
|
|
154
|
+
<div class="sec"><div class="sec-t" data-i18n="sec_cancel_init">{{ t.sec_cancel_init }}</div><div class="sec-s" data-i18n="sec_cancel_init_sub">{{ t.sec_cancel_init_sub }}</div>
|
|
155
|
+
<div class="g2"><div><div class="pf" data-chart="chCancelInit"></div><div class="cw" style="height:250px"><canvas id="chCancelInit"></canvas></div></div>
|
|
156
|
+
<div class="ib" id="ibCancelInit" style="align-self:center;margin-top:0"></div></div></div>
|
|
157
|
+
<div class="sec"><div class="sec-t" data-i18n="sec_cancel_agree">{{ t.sec_cancel_agree }}</div><div class="sec-s" data-i18n="sec_cancel_agree_sub">{{ t.sec_cancel_agree_sub }}</div>
|
|
158
|
+
<div class="g2"><div><div class="pf" data-chart="chCancelAgree"></div><div class="cw" style="height:250px"><canvas id="chCancelAgree"></canvas></div></div>
|
|
159
|
+
<div class="ib" id="ibCancelAgree" style="align-self:center;margin-top:0"></div></div></div>
|
|
160
|
+
<div class="sec"><div class="sec-t" data-i18n="sec_cancel_reason">{{ t.sec_cancel_reason }}</div><div class="sec-s" data-i18n="sec_cancel_reason_sub">{{ t.sec_cancel_reason_sub }}</div>
|
|
161
|
+
<div class="pf" data-chart="chCancelReason"></div><div class="cw" style="height:270px"><canvas id="chCancelReason"></canvas></div><div class="ib" id="ibCancelReason"></div></div>
|
|
162
|
+
<div class="g2">
|
|
163
|
+
<div class="sec"><div class="sec-t" data-i18n="sec_ref_type">{{ t.sec_ref_type }}</div><div class="sec-s" data-i18n="sec_ref_type_sub">{{ t.sec_ref_type_sub }}</div>
|
|
164
|
+
<div class="pf" data-chart="chRefType"></div><div class="cw" style="height:260px"><canvas id="chRefType"></canvas></div><div class="ib" id="ibRefType"></div></div>
|
|
165
|
+
<div class="sec"><div class="sec-t" data-i18n="sec_duty">{{ t.sec_duty }}</div><div class="sec-s" data-i18n="sec_duty_sub">{{ t.sec_duty_sub }}</div>
|
|
166
|
+
<div class="pf" data-chart="chDuty"></div><div class="cw" style="height:260px"><canvas id="chDuty"></canvas></div><div class="ib" id="ibDuty"></div></div>
|
|
167
|
+
</div>
|
|
168
|
+
<div class="sec"><div class="sec-t">{{ t.sec_ref_reason }}<span class="click-hint" data-i18n="sec_ref_reason_click">{{ t.sec_ref_reason_click }}</span></div><div class="sec-s" data-i18n="sec_ref_reason_sub">{{ t.sec_ref_reason_sub }}</div>
|
|
169
|
+
<table><thead><tr><th data-i18n="th_refund_reason">{{ t.th_refund_reason }}</th><th class="n" data-i18n="th_ratio">{{ t.th_ratio }}</th><th class="n" data-i18n="th_amount">{{ t.th_amount }}</th><th data-i18n="th_duty_party">{{ t.th_duty_party }}</th><th data-i18n="th_features">{{ t.th_features }}</th></tr></thead><tbody id="reasonTb"></tbody></table><div class="ib" id="ibReason"></div></div>
|
|
170
|
+
<div class="sec"><div class="sec-t" data-i18n="sec_fast_ref">{{ t.sec_fast_ref }}</div><div class="sec-s" data-i18n="sec_fast_ref_sub">{{ t.sec_fast_ref_sub }}</div>
|
|
171
|
+
<div class="pf" data-chart="chFastRef"></div><div class="cw" style="height:260px"><canvas id="chFastRef"></canvas></div><div class="ib" id="ibFastRef"></div></div>
|
|
172
|
+
<div class="sec"><div class="sec-t" data-i18n="sec_ref_sku">{{ t.sec_ref_sku }}</div><div class="sec-s" data-i18n="sec_ref_sku_sub">{{ t.sec_ref_sku_sub }}</div>
|
|
173
|
+
<table><thead><tr><th data-i18n="th_sku">{{ t.th_sku }}</th><th data-i18n="th_product_name">{{ t.th_product_name }}</th><th class="n" data-i18n="th_miravia_refund">{{ t.th_miravia_refund }}</th><th class="n" data-i18n="th_m2a_refund">{{ t.th_m2a_refund }}</th><th class="n" data-i18n="th_total_refund">{{ t.th_total_refund }}</th><th class="n" data-i18n="th_refund_rate">{{ t.th_refund_rate }}</th></tr></thead><tbody id="refSkuTb"></tbody></table><div class="ib" id="ibRefSku"></div></div>
|
|
174
|
+
</div>
|
|
175
|
+
<div class="tp" id="t-product">
|
|
176
|
+
<div class="sec"><div class="sec-t" data-i18n="sec_top_sku">{{ t.sec_top_sku }}</div><div class="sec-s" data-i18n="sec_top_sku_sub">{{ t.sec_top_sku_sub }}</div>
|
|
177
|
+
<table><thead><tr><th data-i18n="th_sku">{{ t.th_sku }}</th><th data-i18n="th_product_name">{{ t.th_product_name }}</th><th class="n" data-i18n="th_miravia_sales">{{ t.th_miravia_sales }}</th><th class="n" data-i18n="th_m2a_sales">{{ t.th_m2a_sales }}</th><th class="n" data-i18n="th_total_sales">{{ t.th_total_sales }}</th><th class="n" data-i18n="th_refund_rate">{{ t.th_refund_rate }}</th></tr></thead><tbody id="topSkuTb"></tbody></table><div class="ib" id="ibTopSku"></div></div>
|
|
178
|
+
<div class="sec"><div class="sec-t" data-i18n="sec_matrix">{{ t.sec_matrix }}</div><div class="sec-s" data-i18n="sec_matrix_sub">{{ t.sec_matrix_sub }}</div>
|
|
179
|
+
<div class="cw cwl"><canvas id="chMatrix"></canvas></div>
|
|
180
|
+
<div class="mq">
|
|
181
|
+
<div class="mqq q1"><div class="mt" style="color:var(--ok)" data-i18n="q1_label">{{ t.q1_label }}</div><div id="mq1"></div></div>
|
|
182
|
+
<div class="mqq q2"><div class="mt" style="color:var(--wn)" data-i18n="q2_label">{{ t.q2_label }}</div><div id="mq2"></div></div>
|
|
183
|
+
<div class="mqq q3"><div class="mt" style="color:var(--mut)" data-i18n="q3_label">{{ t.q3_label }}</div><div id="mq3"></div></div>
|
|
184
|
+
<div class="mqq q4"><div class="mt" style="color:var(--dg)" data-i18n="q4_label">{{ t.q4_label }}</div><div id="mq4"></div></div>
|
|
185
|
+
</div><div class="ib" id="ibMatrix"></div></div>
|
|
186
|
+
<div class="g2">
|
|
187
|
+
<div class="sec"><div class="sec-t" data-i18n="sec_cat">{{ t.sec_cat }}</div><div class="sec-s" data-i18n="sec_cat_sub">{{ t.sec_cat_sub }}</div>
|
|
188
|
+
<div class="pf" data-chart="chCat"></div><div class="cw" style="height:280px"><canvas id="chCat"></canvas></div><div class="ib" id="ibCat"></div></div>
|
|
189
|
+
<div class="sec"><div class="sec-t" data-i18n="sec_discount">{{ t.sec_discount }}</div><div class="sec-s" data-i18n="sec_discount_sub">{{ t.sec_discount_sub }}</div>
|
|
190
|
+
<div class="pf" data-chart="chDiscount"></div><div class="cw" style="height:280px"><canvas id="chDiscount"></canvas></div><div class="ib" id="ibDiscount"></div></div>
|
|
191
|
+
</div>
|
|
192
|
+
<div class="g2">
|
|
193
|
+
<div class="sec"><div class="sec-t" data-i18n="sec_fbm">{{ t.sec_fbm }}</div><div class="sec-s" data-i18n="sec_fbm_sub">{{ t.sec_fbm_sub }}</div>
|
|
194
|
+
<div class="pf" data-chart="chFBM"></div><div class="cw" style="height:260px"><canvas id="chFBM"></canvas></div><div class="ib" id="ibFBM"></div></div>
|
|
195
|
+
<div class="sec"><div class="sec-t" data-i18n="sec_digital">{{ t.sec_digital }}</div><div class="sec-s" data-i18n="sec_digital_sub">{{ t.sec_digital_sub }}</div>
|
|
196
|
+
<div class="pf" data-chart="chDigital"></div><div class="cw" style="height:260px"><canvas id="chDigital"></canvas></div><div class="ib" id="ibDigital"></div></div>
|
|
197
|
+
</div>
|
|
198
|
+
</div>
|
|
199
|
+
<div class="tp" id="t-buyer">
|
|
200
|
+
<div class="sec"><div class="sec-t" data-i18n="sec_buyer_overview">{{ t.sec_buyer_overview }}</div><div class="sec-s" data-i18n="sec_buyer_sub">{{ t.sec_buyer_sub }}</div><div class="g3" id="buyerKPIs"></div></div>
|
|
201
|
+
<div class="g2">
|
|
202
|
+
<div class="sec"><div class="sec-t" data-i18n="sec_country_platform">{{ t.sec_country_platform }}</div><div class="sec-s" data-i18n="sec_country_platform_sub">{{ t.sec_country_platform_sub }}</div>
|
|
203
|
+
<table><thead><tr><th data-i18n="th_country">{{ t.th_country }}</th><th class="n" data-i18n="th_miravia_orders">{{ t.th_miravia_orders }}</th><th class="n" data-i18n="th_m2a_orders">{{ t.th_m2a_orders }}</th><th class="n" data-i18n="th_total_orders">{{ t.th_total_orders }}</th><th class="n" data-i18n="th_total_gmv">{{ t.th_total_gmv }}</th><th class="n" data-i18n="th_refund_rate">{{ t.th_refund_rate }}</th></tr></thead><tbody id="countryTb"></tbody></table><div class="ib" id="ibCountryTb"></div></div>
|
|
204
|
+
<div class="sec"><div class="sec-t" data-i18n="sec_city_top10">{{ t.sec_city_top10 }}</div><div class="sec-s" data-i18n="sec_city_sub">{{ t.sec_city_sub }}</div>
|
|
205
|
+
<div class="cw" style="height:280px"><canvas id="chCity"></canvas></div><div class="ib" id="ibCity"></div></div>
|
|
206
|
+
</div>
|
|
207
|
+
<div class="sec"><div class="sec-t" data-i18n="sec_country_mini">{{ t.sec_country_mini }}</div><div class="sec-s" data-i18n="sec_country_mini_sub">{{ t.sec_country_mini_sub }}</div><div class="g3" id="countryMini"></div></div>
|
|
208
|
+
<div class="g2">
|
|
209
|
+
<div class="sec"><div class="sec-t" data-i18n="sec_pay_country">{{ t.sec_pay_country }}</div><div class="sec-s" data-i18n="sec_pay_country_sub">{{ t.sec_pay_country_sub }}</div>
|
|
210
|
+
<div class="cw" style="height:280px"><canvas id="chPayCountry"></canvas></div><div class="ib" id="ibPayCountry"></div></div>
|
|
211
|
+
<div class="sec"><div class="sec-t" data-i18n="sec_ab_buyer">{{ t.sec_ab_buyer }}</div><div class="sec-s" data-i18n="sec_ab_buyer_sub">{{ t.sec_ab_buyer_sub }}</div>
|
|
212
|
+
<table><thead><tr><th data-i18n="th_buyer_id">{{ t.th_buyer_id }}</th><th class="n" data-i18n="th_order_count">{{ t.th_order_count }}</th><th class="n" data-i18n="th_refund_count">{{ t.th_refund_count }}</th><th class="n" data-i18n="th_refund_rate">{{ t.th_refund_rate }}</th><th data-i18n="th_risk_level">{{ t.th_risk_level }}</th></tr></thead><tbody id="abBuyerTb"></tbody></table><div class="ib" id="ibAbBuyer"></div></div>
|
|
213
|
+
</div>
|
|
214
|
+
</div>
|
|
215
|
+
<div class="tp" id="t-logistics">
|
|
216
|
+
<div class="sec"><div class="sec-t" data-i18n="sec_carrier">{{ t.sec_carrier }}</div><div class="sec-s" data-i18n="sec_carrier_sub">{{ t.sec_carrier_sub }}</div>
|
|
217
|
+
<table><thead><tr><th data-i18n="th_carrier">{{ t.th_carrier }}</th><th class="n" data-i18n="th_miravia_orders">{{ t.th_miravia_orders }}</th><th class="n" data-i18n="th_m2a_orders">{{ t.th_m2a_orders }}</th><th class="n" data-i18n="th_total_orders">{{ t.th_total_orders }}</th><th class="n" data-i18n="th_share">{{ t.th_share }}</th><th class="n" data-i18n="th_cancel_rate">{{ t.th_cancel_rate }}</th><th class="n" data-i18n="th_refund_rate">{{ t.th_refund_rate }}</th></tr></thead><tbody id="carrierTb"></tbody></table><div class="ib" id="ibCarrier"></div></div>
|
|
218
|
+
<div class="g2">
|
|
219
|
+
<div class="sec"><div class="sec-t" data-i18n="sec_fbm2">{{ t.sec_fbm2 }}</div><div class="sec-s" data-i18n="sec_fbm2_sub">{{ t.sec_fbm2_sub }}</div>
|
|
220
|
+
<div class="pf" data-chart="chFBM2"></div><div class="cw" style="height:260px"><canvas id="chFBM2"></canvas></div><div class="ib" id="ibFBM2"></div></div>
|
|
221
|
+
<div class="sec"><div class="sec-t" data-i18n="sec_ship_type">{{ t.sec_ship_type }}</div><div class="sec-s" data-i18n="sec_ship_type_sub">{{ t.sec_ship_type_sub }}</div>
|
|
222
|
+
<div class="pf" data-chart="chShipType"></div><div class="cw" style="height:260px"><canvas id="chShipType"></canvas></div><div class="ib" id="ibShipType"></div></div>
|
|
223
|
+
</div>
|
|
224
|
+
</div>
|
|
225
|
+
</div>
|
|
226
|
+
<div class="modal-bg" id="modalBg" onclick="if(event.target===this)closeModal()">
|
|
227
|
+
<div class="modal-box"><div class="modal-hd"><h3 id="modalTitle"></h3><button class="modal-close" onclick="closeModal()">×</button></div><div class="modal-bd" id="modalBody"></div></div>
|
|
228
|
+
</div>
|
|
229
|
+
<div class="ft" data-i18n="footer"><img src="data:image/svg+xml;base64,{{ logo_base64 }}" alt="Miravia" style="height:18px;opacity:.5;margin-bottom:6px;display:block;margin-left:auto;margin-right:auto">{{ t.footer }}</div>
|
|
230
|
+
<script>
|
|
231
|
+
// ================================================================
|
|
232
|
+
// DATA INJECTION (replaces mock data generation)
|
|
233
|
+
// ================================================================
|
|
234
|
+
const DATA = {{ data_json | safe }};
|
|
235
|
+
const TRANSLATIONS = {{ all_t_json | safe }};
|
|
236
|
+
let currentLang = {{ current_lang | tojson }};
|
|
237
|
+
let T = TRANSLATIONS[currentLang] || {{ t_json | safe }};
|
|
238
|
+
const dailyData = DATA.dailyData || [];
|
|
239
|
+
let SKUS = DATA.skus || [];
|
|
240
|
+
let CITIES = DATA.cities || [];
|
|
241
|
+
let CARRIERS = DATA.carriers || [];
|
|
242
|
+
let REF_REASONS = DATA.refReasons || [];
|
|
243
|
+
let AB_BUYERS = DATA.abBuyers || [];
|
|
244
|
+
const CUR = DATA.meta ? DATA.meta.currency : 'EUR';
|
|
245
|
+
const DAYS = dailyData.length;
|
|
246
|
+
// Check if buyer_id is available (when false, 'bu' metric uses order_id count as fallback)
|
|
247
|
+
const BUYER_ID_AVAIL = !(DATA.meta && DATA.meta.buyer_id_available === false);
|
|
248
|
+
|
|
249
|
+
// Preset dimensional data switcher
|
|
250
|
+
const _DIM_KEYS=['skus','cities','carriers','refReasons','abBuyers','countries','payCountry',
|
|
251
|
+
'payMethods','cancelReasons','cancelInitiator','cancelAgree','refundTypes','dutyPartyDist',
|
|
252
|
+
'fastRefund','fbmDist','digitalDist','aovBuckets','statusDist','shipTypeDist','voucherStats',
|
|
253
|
+
'shippingStats','discountDepth','catDist','buyerStats','overall'];
|
|
254
|
+
let _currentPresetKey = '0';
|
|
255
|
+
function getCurrentPresetKey(){ return _currentPresetKey; }
|
|
256
|
+
function switchPreset(key){
|
|
257
|
+
_currentPresetKey = String(key === 0 ? '0' : key);
|
|
258
|
+
const p=DATA.presetData&&DATA.presetData[String(key)];
|
|
259
|
+
if(!p)return;
|
|
260
|
+
_DIM_KEYS.forEach(k=>{if(p[k]!==undefined)DATA[k]=p[k];});
|
|
261
|
+
SKUS=DATA.skus||[];CITIES=DATA.cities||[];CARRIERS=DATA.carriers||[];
|
|
262
|
+
REF_REASONS=DATA.refReasons||[];AB_BUYERS=DATA.abBuyers||[];
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// ================================================================
|
|
266
|
+
// STATE & UTILITIES
|
|
267
|
+
// ================================================================
|
|
268
|
+
let S = {s: 0, e: Math.max(0, DAYS - 1)};
|
|
269
|
+
const CH = {};
|
|
270
|
+
const fmt = (n, d = 0) => (n || 0).toLocaleString('en-US', {minimumFractionDigits: d, maximumFractionDigits: d});
|
|
271
|
+
const fm = n => CUR + ' ' + fmt(n, 2);
|
|
272
|
+
const fp = (n, d = 1) => (n * 100).toFixed(d) + '%';
|
|
273
|
+
|
|
274
|
+
function sel() { return dailyData.slice(S.s, S.e + 1); }
|
|
275
|
+
function agg() {
|
|
276
|
+
const d = sel();
|
|
277
|
+
if (!d.length) return {o:0,mO:0,aO:0,g:0,mG:0,aG:0,aov:0,mAov:0,aAov:0,cr:0,mCr:0,aCr:0,rr:0,mRr:0,aRr:0,net:0,mNet:0,aNet:0,bu:0,rAmt:0,mC:0,aC:0,mR:0,aR:0};
|
|
278
|
+
const mO=d.reduce((a,b)=>a+b.mO,0),aO=d.reduce((a,b)=>a+b.aO,0),o=mO+aO;
|
|
279
|
+
const mI=d.reduce((a,b)=>a+(b.mI||0),0),aI=d.reduce((a,b)=>a+(b.aI||0),0),items=mI+aI;
|
|
280
|
+
const mG=d.reduce((a,b)=>a+b.mG,0),aG=d.reduce((a,b)=>a+b.aG,0);
|
|
281
|
+
const mR=d.reduce((a,b)=>a+b.mR,0),aR=d.reduce((a,b)=>a+b.aR,0);
|
|
282
|
+
const mC=d.reduce((a,b)=>a+b.mC,0),aC=d.reduce((a,b)=>a+b.aC,0);
|
|
283
|
+
const bu=d.reduce((a,b)=>a+b.bu,0);
|
|
284
|
+
const rA=d.reduce((a,b)=>a+b.rAmt,0);
|
|
285
|
+
const mRA=d.reduce((a,b)=>a+(b.mRA||0),0),aRA=d.reduce((a,b)=>a+(b.aRA||0),0);
|
|
286
|
+
const net=mG+aG-rA;
|
|
287
|
+
const mNet=mG-mRA,aNet=aG-aRA;
|
|
288
|
+
return{o,mO,aO,g:mG+aG,mG,aG,aov:o?(mG+aG)/o:0,mAov:mO?mG/mO:0,aAov:aO?aG/aO:0,
|
|
289
|
+
cr:items?(mC+aC)/items:0,mCr:mI?mC/mI:0,aCr:aI?aC/aI:0,rr:items?(mR+aR)/items:0,mRr:mI?mR/mI:0,aRr:aI?aR/aI:0,
|
|
290
|
+
net,mNet,aNet,bu,rAmt:rA,mC,aC,mR,aR};
|
|
291
|
+
}
|
|
292
|
+
function destroy(id){if(CH[id]){CH[id].destroy();delete CH[id];}}
|
|
293
|
+
function noData(id,opts){const el=document.getElementById(id);if(!el)return;if(opts&&opts.compact){const sec=el.closest&&el.closest('.sec');if(sec){if(sec.querySelector('.pf[data-chart]')){sec.querySelectorAll('.cw').forEach(e=>e.style.display='none');}else{sec.querySelectorAll('.cw,.mq,.ms').forEach(e=>e.style.display='none');sec.classList.add('sec-empty');}}el.innerHTML='<div class="no-data-inline">'+(opts.msg||T.no_data)+'</div>';}else{el.innerHTML='<div class="no-data">'+T.no_data+'</div>';}}
|
|
294
|
+
|
|
295
|
+
// ================================================================
|
|
296
|
+
// CHART DEFAULTS
|
|
297
|
+
// ================================================================
|
|
298
|
+
Chart.register(ChartDataLabels);
|
|
299
|
+
Chart.defaults.plugins.datalabels = {display: false};
|
|
300
|
+
const COLORS = ['#6b5ff2','#a78bfa','#10b981','#f59e0b','#ef4444','#86909c','#ec4899','#06b6d4'];
|
|
301
|
+
|
|
302
|
+
// Platform filter
|
|
303
|
+
const pfState = {};
|
|
304
|
+
function initPf(container){
|
|
305
|
+
const chartId=container.dataset.chart;
|
|
306
|
+
if(!chartId)return;
|
|
307
|
+
pfState[chartId]='all';
|
|
308
|
+
container.innerHTML='<button class="on" data-m="all">'+T.pf_all+'</button><button data-m="miravia">'+T.pf_miravia+'</button><button data-m="ae">'+T.pf_m2a+'</button>';
|
|
309
|
+
container.querySelectorAll('button').forEach(b=>{
|
|
310
|
+
b.addEventListener('click',()=>{
|
|
311
|
+
container.querySelectorAll('button').forEach(x=>x.classList.remove('on'));
|
|
312
|
+
b.classList.add('on');pfState[chartId]=b.dataset.m;
|
|
313
|
+
const sec=container.closest('.sec');
|
|
314
|
+
if(sec){sec.classList.remove('sec-empty');sec.querySelectorAll('.cw,.mq,.ms').forEach(e=>{e.style.display='';});sec.querySelectorAll('table').forEach(t=>{if(t.style.display==='none')t.style.display='';});}
|
|
315
|
+
const fn=pfRenderers[chartId];if(fn)fn();
|
|
316
|
+
});
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
const pfRenderers={};
|
|
320
|
+
|
|
321
|
+
// ================================================================
|
|
322
|
+
// TIME FILTER
|
|
323
|
+
// ================================================================
|
|
324
|
+
function initTF(){
|
|
325
|
+
const box=document.getElementById('tfBox');
|
|
326
|
+
if(DAYS===0){box.innerHTML='<span class="tf-label">'+T.no_data+'</span>';document.getElementById('rangeBadge').textContent=T.no_data;return;}
|
|
327
|
+
box.innerHTML='<span class="tf-label">'+T.time_range+'</span>';
|
|
328
|
+
const presets=[];
|
|
329
|
+
if(DAYS>=7)presets.push({lb:T.preset_1w,d:7});
|
|
330
|
+
if(DAYS>=30)presets.push({lb:T.preset_1m,d:30});
|
|
331
|
+
if(DAYS>=90)presets.push({lb:T.preset_3m,d:90});
|
|
332
|
+
if(DAYS>=180)presets.push({lb:T.preset_6m,d:180});
|
|
333
|
+
if(DAYS>=365)presets.push({lb:T.preset_1y,d:365});
|
|
334
|
+
presets.push({lb:T.preset_all,d:0});
|
|
335
|
+
presets.forEach((p,i)=>{
|
|
336
|
+
const b=document.createElement('button');
|
|
337
|
+
b.className='tf-btn'+(i===presets.length-1?' on':'');
|
|
338
|
+
b.textContent=p.lb;
|
|
339
|
+
b.onclick=()=>{
|
|
340
|
+
const days=p.d||DAYS;
|
|
341
|
+
S.s=Math.max(0,DAYS-days);S.e=DAYS-1;
|
|
342
|
+
switchPreset(p.d);
|
|
343
|
+
box.querySelectorAll('.tf-btn').forEach(x=>x.classList.remove('on'));b.classList.add('on');
|
|
344
|
+
renderAll();
|
|
345
|
+
};
|
|
346
|
+
box.appendChild(b);
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// ================================================================
|
|
351
|
+
// RENDER ALL
|
|
352
|
+
// ================================================================
|
|
353
|
+
function resetAllSections(){
|
|
354
|
+
document.querySelectorAll('.sec-empty').forEach(sec=>{
|
|
355
|
+
sec.classList.remove('sec-empty');
|
|
356
|
+
sec.querySelectorAll('.cw,.mq,.ms').forEach(e=>{e.style.display='';});
|
|
357
|
+
sec.querySelectorAll('table').forEach(t=>{if(t.style.display==='none')t.style.display='';});
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
function renderAll(){
|
|
361
|
+
resetAllSections();
|
|
362
|
+
const a=agg();
|
|
363
|
+
document.getElementById('rangeBadge').textContent=DAYS>0?`${dailyData[S.s].ds} ~ ${dailyData[S.e].ds} (${S.e-S.s+1} ${T.days_suffix})`:T.no_data;
|
|
364
|
+
renderKPIs(a);renderES(a);renderOV(a);
|
|
365
|
+
renderCompare(a);renderCountry();
|
|
366
|
+
renderTrend();renderNetRev();renderAOV();renderPay();renderVoucher();renderShip(a);
|
|
367
|
+
renderStatus();renderCancelInit();renderCancelAgree();renderCancelReason();
|
|
368
|
+
renderRefType();renderDuty();renderReason();renderFastRef();renderRefSku();
|
|
369
|
+
renderTopSku();renderMatrix();renderCat();renderDiscount();renderFBM();renderDigital();
|
|
370
|
+
renderBuyerKPIs(a);renderCountryTable();renderCity();renderCountryMini();renderPayCountry();renderAbBuyer();
|
|
371
|
+
renderCarrier();renderFBM2();renderShipType();
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// KPIs
|
|
375
|
+
function renderKPIs(a){
|
|
376
|
+
const items=[
|
|
377
|
+
{lb:T.kpi_orders,v:a.o,mv:a.mO,av:a.aO},{lb:T.kpi_gmv,v:a.g,mv:a.mG,av:a.aG,money:true},
|
|
378
|
+
{lb:T.kpi_aov,v:a.aov,mv:a.mAov,av:a.aAov,money:true},{lb:T.kpi_cancel_rate,v:a.cr,mv:a.mCr,av:a.aCr,pct:true},
|
|
379
|
+
{lb:T.kpi_refund_rate,v:a.rr,mv:a.mRr,av:a.aRr,pct:true},{lb:T.kpi_net_rev,v:a.net,mv:a.mNet,av:a.aNet,money:true}];
|
|
380
|
+
document.getElementById('hk').innerHTML=items.map(k=>{
|
|
381
|
+
const fv=k.money?fm(k.v):(k.pct?fp(k.v):fmt(k.v));
|
|
382
|
+
const fmv=k.money?fm(k.mv):(k.pct?fp(k.mv):fmt(k.mv));
|
|
383
|
+
const fav=k.money?fm(k.av):(k.pct?fp(k.av):fmt(k.av));
|
|
384
|
+
return`<div class="kpi"><div class="lb">${k.lb}</div><div class="vl">${fv}</div><div class="sub"><span class="pm">Miravia:${fmv}</span><span class="pa">M2A:${fav}</span></div></div>`;
|
|
385
|
+
}).join('');
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// Executive Summary (Agent-generated insights, multi-language aware)
|
|
389
|
+
// Resolve per-lang insight: presets.{pk}.{lang} > presets.{pk}.en > legacy flat
|
|
390
|
+
// With validation: if AI insights are malformed, fall back to rule-engine generated content
|
|
391
|
+
|
|
392
|
+
// Validate that insight object has the correct structure
|
|
393
|
+
function _validateInsight(insight){
|
|
394
|
+
if(!insight || typeof insight !== 'object') return false;
|
|
395
|
+
if(typeof insight.summary !== 'string' || !insight.summary.trim()) return false;
|
|
396
|
+
// highlights must be array of {title, detail, severity}
|
|
397
|
+
if(insight.highlights && Array.isArray(insight.highlights)){
|
|
398
|
+
for(const h of insight.highlights){
|
|
399
|
+
if(!h || typeof h !== 'object' || typeof h.title !== 'string' || typeof h.detail !== 'string') return false;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
// actions must be array of {text, priority}
|
|
403
|
+
if(insight.actions && Array.isArray(insight.actions)){
|
|
404
|
+
for(const ac of insight.actions){
|
|
405
|
+
if(!ac || typeof ac !== 'object' || typeof ac.text !== 'string' || typeof ac.priority !== 'string') return false;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
return true;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// Rule-engine fallback: generate basic insights from DATA metrics
|
|
412
|
+
function _buildFallbackInsight(pk){
|
|
413
|
+
const p = DATA.presetData && DATA.presetData[pk];
|
|
414
|
+
const ov = p && p.overall ? p.overall : (DATA.overall || {});
|
|
415
|
+
if(!ov || !ov.orders) return null;
|
|
416
|
+
const cr = ov.cancel_rate||0, rr = ov.refund_rate||0, gmv = ov.gmv||0, aov = ov.aov||0;
|
|
417
|
+
const net = ov.net_revenue||0, refAmt = ov.refund_amount||0;
|
|
418
|
+
const highlights = [];
|
|
419
|
+
const actions = [];
|
|
420
|
+
// Highlight: refund rate
|
|
421
|
+
if(rr > 0.1){
|
|
422
|
+
highlights.push({title: T.kpi_refund_rate||'Refund Rate', detail: (rr*100).toFixed(1)+'% — '+( T.fallback_high_refund||'Above threshold, needs attention'), severity:'high'});
|
|
423
|
+
} else if(rr > 0.05){
|
|
424
|
+
highlights.push({title: T.kpi_refund_rate||'Refund Rate', detail: (rr*100).toFixed(1)+'% — '+(T.fallback_medium_refund||'Moderate level, monitor trend'), severity:'medium'});
|
|
425
|
+
} else {
|
|
426
|
+
highlights.push({title: T.kpi_refund_rate||'Refund Rate', detail: (rr*100).toFixed(1)+'% — '+(T.fallback_low_refund||'Healthy level'), severity:'low'});
|
|
427
|
+
}
|
|
428
|
+
// Highlight: cancel rate
|
|
429
|
+
if(cr > 0.1){
|
|
430
|
+
highlights.push({title: T.kpi_cancel_rate||'Cancel Rate', detail: (cr*100).toFixed(1)+'% — '+(T.fallback_high_cancel||'Above threshold'), severity:'high'});
|
|
431
|
+
} else if(cr > 0.05){
|
|
432
|
+
highlights.push({title: T.kpi_cancel_rate||'Cancel Rate', detail: (cr*100).toFixed(1)+'% — '+(T.fallback_medium_cancel||'Moderate level'), severity:'medium'});
|
|
433
|
+
} else {
|
|
434
|
+
highlights.push({title: T.kpi_cancel_rate||'Cancel Rate', detail: (cr*100).toFixed(1)+'% — '+(T.fallback_low_cancel||'Healthy level'), severity:'low'});
|
|
435
|
+
}
|
|
436
|
+
// Highlight: GMV
|
|
437
|
+
highlights.push({title: 'GMV', detail: CUR+' '+gmv.toLocaleString(undefined,{minimumFractionDigits:2,maximumFractionDigits:2}), severity:'low'});
|
|
438
|
+
// Highlight: top refund reason if available
|
|
439
|
+
const reasons = (p && p.refReasons) || DATA.refReasons || [];
|
|
440
|
+
if(reasons.length > 0){
|
|
441
|
+
const top = reasons[0];
|
|
442
|
+
highlights.push({title: T.fallback_top_refund_reason||'Top Refund Reason', detail: (top.r||top.reason||'—')+' ('+(top.pc!==undefined?(top.pc*100).toFixed(0):top.pct!==undefined?(top.pct*100).toFixed(0):'?')+'%)', severity: rr>0.05?'medium':'low'});
|
|
443
|
+
}
|
|
444
|
+
// Actions
|
|
445
|
+
if(rr > 0.05){
|
|
446
|
+
actions.push({priority:'P0', text: T.fallback_action_refund||'Investigate top refund reasons and optimize product/logistics quality'});
|
|
447
|
+
}
|
|
448
|
+
if(cr > 0.05){
|
|
449
|
+
actions.push({priority:'P1', text: T.fallback_action_cancel||'Review cancellation patterns — check pricing competitiveness and stock availability'});
|
|
450
|
+
}
|
|
451
|
+
actions.push({priority:'P2', text: T.fallback_action_monitor||'Continue monitoring weekly trends for GMV, refund rate, and cancel rate'});
|
|
452
|
+
// Summary
|
|
453
|
+
const summary = (T.fallback_summary_tpl||'Period overview: {0} orders, GMV {1}, AOV {2}, refund rate {3}, cancel rate {4}, net revenue {5}.')
|
|
454
|
+
.replace('{0}', ov.orders).replace('{1}', CUR+' '+gmv.toFixed(2)).replace('{2}', CUR+' '+aov.toFixed(2))
|
|
455
|
+
.replace('{3}', (rr*100).toFixed(1)+'%').replace('{4}', (cr*100).toFixed(1)+'%').replace('{5}', CUR+' '+net.toFixed(2));
|
|
456
|
+
return {summary, highlights, actions};
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
function _getInsight(pk){
|
|
460
|
+
const ai=DATA.agentInsights&&DATA.agentInsights.presets&&DATA.agentInsights.presets[pk];
|
|
461
|
+
if(!ai) return _buildFallbackInsight(pk);
|
|
462
|
+
// Try language-specific first
|
|
463
|
+
let resolved = null;
|
|
464
|
+
if(ai[currentLang]) resolved = ai[currentLang];
|
|
465
|
+
else if(ai['en']) resolved = ai['en'];
|
|
466
|
+
else { for(const l of ['es','it','zh']){if(ai[l]){resolved=ai[l];break;}} }
|
|
467
|
+
if(!resolved && ai.summary!==undefined) resolved = ai;
|
|
468
|
+
// Validate resolved insight — if malformed, use fallback
|
|
469
|
+
if(!_validateInsight(resolved)) return _buildFallbackInsight(pk);
|
|
470
|
+
return resolved;
|
|
471
|
+
}
|
|
472
|
+
function renderES(a){
|
|
473
|
+
const pk = getCurrentPresetKey();
|
|
474
|
+
const ai = _getInsight(pk);
|
|
475
|
+
|
|
476
|
+
// Summary body
|
|
477
|
+
document.getElementById('esBody').innerHTML = ai && ai.summary ? ai.summary : '';
|
|
478
|
+
|
|
479
|
+
// Highlights / alerts
|
|
480
|
+
const alertMap={'high':'ad-r','medium':'ad-y','low':'ad-g'};
|
|
481
|
+
document.getElementById('esAlerts').innerHTML = ai && ai.highlights && ai.highlights.length
|
|
482
|
+
? ai.highlights.map(h =>
|
|
483
|
+
`<div class="ai"><span class="ad ${alertMap[h.severity]||'ad-g'}"></span><span><b>${h.title}</b>\uff1a${h.detail}</span></div>`
|
|
484
|
+
).join('')
|
|
485
|
+
: '';
|
|
486
|
+
|
|
487
|
+
// Actions
|
|
488
|
+
document.getElementById('esActions').innerHTML = ai && ai.actions && ai.actions.length
|
|
489
|
+
? ai.actions.map(ac =>
|
|
490
|
+
`<div class="ac"><span class="ap p${ac.priority==='P0'?0:ac.priority==='P1'?1:2}">${ac.priority}</span><span>${ac.text}</span></div>`
|
|
491
|
+
).join('') : '';
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// Overview
|
|
495
|
+
function renderOV(a){
|
|
496
|
+
const bs=DATA.buyerStats||{all:{repurchase_rate:0,top5_share:0}};
|
|
497
|
+
const rows=[
|
|
498
|
+
[T.ov_orders,a.o,a.mO,a.aO,fmt],[T.ov_gmv,a.g,a.mG,a.aG,fm],[T.ov_aov,a.aov,a.mAov,a.aAov,fm],
|
|
499
|
+
[T.ov_cancel_rate,a.cr,a.mCr,a.aCr,fp],[T.ov_refund_rate,a.rr,a.mRr,a.aRr,fp],[T.ov_net_rev,a.net,a.mNet,a.aNet,fm],
|
|
500
|
+
[BUYER_ID_AVAIL ? T.ov_buyers : (T.ov_buyers + '*'),(bs.all?bs.all.buyer_count:0),(bs.miravia?bs.miravia.buyer_count:0),(bs.ae?bs.ae.buyer_count:0),fmt],[T.ov_repurchase,(bs.all?bs.all.repurchase_rate/100:0),(bs.miravia?bs.miravia.repurchase_rate/100:0),(bs.ae?bs.ae.repurchase_rate/100:0),fp]];
|
|
501
|
+
document.getElementById('ovTable').innerHTML=`<table><thead><tr><th>${T.th_metric}</th><th class="n">${T.th_overall}</th><th class="n">${T.pf_miravia}</th><th class="n">${T.pf_m2a}</th></tr></thead><tbody>${rows.map(r=>`<tr><td>${r[0]}</td><td class="n">${r[4](r[1])}</td><td class="n">${r[4](r[2])}</td><td class="n">${r[4](r[3])}</td></tr>`).join('')}</tbody></table>`;
|
|
502
|
+
// Alert table now driven by agentInsights highlights (multi-lang)
|
|
503
|
+
const pk2 = getCurrentPresetKey();
|
|
504
|
+
const ai2 = _getInsight(pk2);
|
|
505
|
+
const alertRows = ai2 && ai2.highlights ? ai2.highlights : [];
|
|
506
|
+
const sevMap={high:'sh',medium:'sm',low:'sl'};
|
|
507
|
+
document.getElementById('alertTb').innerHTML=alertRows.length?alertRows.map(al=>
|
|
508
|
+
`<tr class="${al.severity==='high'?'ah':al.severity==='medium'?'am':''}"><td><b>${al.title}</b></td><td><span class="sev ${sevMap[al.severity]||'sl'}">${al.severity==='high'?T.sev_high:al.severity==='medium'?T.sev_medium:T.sev_low}</span></td><td>${al.detail||''}</td></tr>`
|
|
509
|
+
).join(''):'<tr><td colspan="3" style="text-align:center;color:var(--mut)">'+T.no_alerts+'</td></tr>';
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
// Channel Compare
|
|
513
|
+
function renderCompare(a){
|
|
514
|
+
destroy('chCompare');destroy('chCompareRate');
|
|
515
|
+
if(a.o===0){noData('ibCompare',{compact:true});return;}
|
|
516
|
+
CH.chCompare=new Chart(document.getElementById('chCompare'),{type:'bar',
|
|
517
|
+
data:{labels:[T.chart_gmv,T.kpi_aov,T.chart_net_rev],datasets:[
|
|
518
|
+
{label:'Miravia',data:[a.mG,a.mAov,a.mNet],backgroundColor:'#1f6feb'},
|
|
519
|
+
{label:'M2A',data:[a.aG,a.aAov,a.aNet],backgroundColor:'#ff6a00'}]},
|
|
520
|
+
options:{responsive:true,maintainAspectRatio:false,
|
|
521
|
+
plugins:{legend:{position:'top',labels:{font:{size:11}}},datalabels:{display:true,anchor:'end',align:'top',font:{size:9},formatter:v=>fm(v)}},
|
|
522
|
+
scales:{y:{type:'linear',position:'left',title:{display:true,text:CUR,font:{size:11}}}}}});
|
|
523
|
+
CH.chCompareRate=new Chart(document.getElementById('chCompareRate'),{type:'bar',
|
|
524
|
+
data:{labels:[T.kpi_cancel_rate,T.kpi_refund_rate],datasets:[
|
|
525
|
+
{label:'Miravia',data:[a.mCr*100,a.mRr*100],backgroundColor:'#1f6feb',yAxisID:'y1'},
|
|
526
|
+
{label:'M2A',data:[a.aCr*100,a.aRr*100],backgroundColor:'#ff6a00',yAxisID:'y1'}]},
|
|
527
|
+
options:{responsive:true,maintainAspectRatio:false,
|
|
528
|
+
plugins:{legend:{position:'top',labels:{font:{size:11}}},datalabels:{display:true,anchor:'end',align:'top',font:{size:11,weight:'bold'},formatter:v=>v.toFixed(1)+'%'}},
|
|
529
|
+
scales:{y1:{type:'linear',position:'right',title:{display:true,text:'%',font:{size:11}},min:0,max:Math.max(50,Math.ceil(Math.max(a.mCr,a.aCr,a.mRr,a.aRr)*100/10)*10+10)}}}});
|
|
530
|
+
document.getElementById('ibCompare').innerHTML=T.insight_compare.replace('{0}',fmt(a.mO)).replace('{1}',fmt(a.aO)).replace('{2}',fp(a.mCr)).replace('{3}',fp(a.aCr)).replace('{4}',fp(a.mRr)).replace('{5}',fp(a.aRr));
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
// Country
|
|
534
|
+
function renderCountry(){
|
|
535
|
+
destroy('chCountry');
|
|
536
|
+
const m=pfState.chCountry||'all';
|
|
537
|
+
const co=DATA.countries||[];
|
|
538
|
+
if(!co.length){noData('ibCountry',{compact:true});return;}
|
|
539
|
+
const data=co.slice(0,8).map(c=>({nm:c.nm,v:m==='all'?c.all_gmv:m==='miravia'?c.miravia_gmv:c.ae_gmv}));
|
|
540
|
+
const total=data.reduce((a,b)=>a+b.v,0);
|
|
541
|
+
CH.chCountry=new Chart(document.getElementById('chCountry'),{type:'doughnut',
|
|
542
|
+
data:{labels:data.map(d=>d.nm),datasets:[{data:data.map(d=>d.v),backgroundColor:COLORS}]},
|
|
543
|
+
options:{responsive:true,maintainAspectRatio:false,
|
|
544
|
+
plugins:{legend:{position:'right',labels:{font:{size:11}}},datalabels:{display:true,color:'#fff',font:{size:11,weight:'bold'},formatter:(v)=>{const p=(v/total*100).toFixed(1);return v>total*.04?[fmt(v,0),p+'%']:'';}}}}});
|
|
545
|
+
const top=data.reduce((a,b)=>b.v>a.v?b:a,data[0]);
|
|
546
|
+
document.getElementById('ibCountry').innerHTML=top&&total>0?T.insight_top_market.replace('{0}',top.nm).replace('{1}',(top.v/total*100).toFixed(1)):'';}
|
|
547
|
+
pfRenderers.chCountry=renderCountry;
|
|
548
|
+
|
|
549
|
+
// Trend
|
|
550
|
+
let trendMetric='gmv',trendPlatform='all';
|
|
551
|
+
function renderTrend(){
|
|
552
|
+
destroy('chTrend');
|
|
553
|
+
const d=sel();
|
|
554
|
+
if(!d.length){noData('ibTrend',{compact:true});return;}
|
|
555
|
+
const labels=d.map(dd=>dd.ds);
|
|
556
|
+
let data,lb,clr;
|
|
557
|
+
if(trendMetric==='gmv'){data=d.map(dd=>trendPlatform==='miravia'?dd.mG:trendPlatform==='ae'?dd.aG:dd.mG+dd.aG);lb='GMV ('+CUR+')';clr='#1f6feb';
|
|
558
|
+
}else if(trendMetric==='orders'){data=d.map(dd=>trendPlatform==='miravia'?dd.mO:trendPlatform==='ae'?dd.aO:dd.mO+dd.aO);lb=T.chart_orders;clr='#ff6a00';
|
|
559
|
+
}else if(trendMetric==='aov'){data=d.map(dd=>{const g2=trendPlatform==='miravia'?dd.mG:trendPlatform==='ae'?dd.aG:dd.mG+dd.aG;const o2=trendPlatform==='miravia'?dd.mO:trendPlatform==='ae'?dd.aO:dd.mO+dd.aO;return o2?g2/o2:0;});lb='AOV ('+CUR+')';clr='#00b42a';
|
|
560
|
+
}else if(trendMetric==='cr'){data=d.map(dd=>{const c2=trendPlatform==='miravia'?dd.mC:trendPlatform==='ae'?dd.aC:dd.mC+dd.aC;const i2=trendPlatform==='miravia'?(dd.mI||dd.mO):trendPlatform==='ae'?(dd.aI||dd.aO):(dd.mI||dd.mO)+(dd.aI||dd.aO);return i2?(c2/i2*100):0;});lb=T.kpi_cancel_rate+' (%)';clr='#f53f3f';
|
|
561
|
+
}else{data=d.map(dd=>{const r2=trendPlatform==='miravia'?dd.mR:trendPlatform==='ae'?dd.aR:dd.mR+dd.aR;const i2=trendPlatform==='miravia'?(dd.mI||dd.mO):trendPlatform==='ae'?(dd.aI||dd.aO):(dd.mI||dd.mO)+(dd.aI||dd.aO);return i2?(r2/i2*100):0;});lb=T.kpi_refund_rate+' (%)';clr='#ff7d00';}
|
|
562
|
+
const n2=data.length;let _sx=0,_sy=0,_sxy=0,_sxx=0;for(let i=0;i<n2;i++){_sx+=i;_sy+=data[i];_sxy+=i*data[i];_sxx+=i*i;}
|
|
563
|
+
const slope=n2>1?(n2*_sxy-_sx*_sy)/(n2*_sxx-_sx*_sx):0;const intercept=n2>0?(_sy-slope*_sx)/n2:0;
|
|
564
|
+
CH.chTrend=new Chart(document.getElementById('chTrend'),{type:'line',
|
|
565
|
+
data:{labels,datasets:[
|
|
566
|
+
{label:lb,data,borderColor:clr,backgroundColor:clr+'20',fill:true,tension:.3,pointRadius:d.length>90?0:2,borderWidth:1.5},
|
|
567
|
+
{label:T.chart_trendline,data:data.map((_,i)=>Math.max(0,intercept+slope*i)),borderColor:'#ff7d00',borderDash:[6,4],borderWidth:2,pointRadius:0,fill:false}]},
|
|
568
|
+
options:{responsive:true,maintainAspectRatio:false,
|
|
569
|
+
plugins:{legend:{display:true,position:'top',labels:{font:{size:11},usePointStyle:true}},datalabels:{display:false},tooltip:{mode:'index',intersect:false}},
|
|
570
|
+
scales:{x:{ticks:{maxTicksLimit:12,font:{size:10}}},y:{min:0,title:{display:true,text:lb,font:{size:11}}}},
|
|
571
|
+
interaction:{mode:'nearest',intersect:false}}});
|
|
572
|
+
const avg=data.reduce((a,b)=>a+b,0)/data.length||0;
|
|
573
|
+
const maxI=data.indexOf(Math.max(...data)),minI=data.indexOf(Math.min(...data));
|
|
574
|
+
const trendDir=slope>0?T.insight_trend_up:T.insight_trend_down;
|
|
575
|
+
const insightMap={gmv:'GMV',orders:T.chart_orders,aov:T.kpi_aov,cr:T.kpi_cancel_rate,rr:T.kpi_refund_rate};
|
|
576
|
+
document.getElementById('ibTrend').innerHTML=T.insight_trend_avg.replace('{0}',insightMap[trendMetric]).replace('{1}',trendMetric==='cr'||trendMetric==='rr'?avg.toFixed(2)+'%':trendMetric==='aov'?fm(avg):fmt(Math.round(avg))).replace('{2}',trendDir).replace('{3}',labels[maxI]).replace('{4}',labels[minI]);
|
|
577
|
+
}
|
|
578
|
+
document.getElementById('trendMs').innerHTML=['gmv','orders','aov','cr','rr'].map((m,i)=>{
|
|
579
|
+
const lbs={gmv:'GMV',orders:T.chart_orders,aov:'AOV',cr:T.kpi_cancel_rate,rr:T.kpi_refund_rate};
|
|
580
|
+
return`<button class="${i===0?'on':''}" data-m="${m}">${lbs[m]}</button>`;}).join('');
|
|
581
|
+
document.getElementById('trendMs').addEventListener('click',e=>{
|
|
582
|
+
if(e.target.tagName!=='BUTTON')return;
|
|
583
|
+
document.querySelectorAll('#trendMs button').forEach(b=>b.classList.remove('on'));
|
|
584
|
+
e.target.classList.add('on');trendMetric=e.target.dataset.m;renderTrend();});
|
|
585
|
+
document.getElementById('trendPf').innerHTML='<button class="on" data-m="all">'+T.pf_all+'</button><button data-m="miravia">'+T.pf_miravia+'</button><button data-m="ae">'+T.pf_m2a+'</button>';
|
|
586
|
+
document.getElementById('trendPf').addEventListener('click',e=>{
|
|
587
|
+
if(e.target.tagName!=='BUTTON')return;
|
|
588
|
+
document.querySelectorAll('#trendPf button').forEach(b=>b.classList.remove('on'));
|
|
589
|
+
e.target.classList.add('on');trendPlatform=e.target.dataset.m;renderTrend();});
|
|
590
|
+
|
|
591
|
+
// Net Revenue
|
|
592
|
+
function renderNetRev(){
|
|
593
|
+
destroy('chNetRev');const a=agg();const m=pfState.chNetRev||'all';
|
|
594
|
+
const gv=m==='miravia'?a.mG:m==='ae'?a.aG:a.g;
|
|
595
|
+
const nv=m==='miravia'?a.mNet:m==='ae'?a.aNet:a.net;
|
|
596
|
+
if(gv===0&&nv===0){noData('ibNetRev',{compact:true});return;}
|
|
597
|
+
CH.chNetRev=new Chart(document.getElementById('chNetRev'),{type:'bar',
|
|
598
|
+
data:{labels:['GMV',T.chart_net_rev],datasets:[{data:[gv,nv],backgroundColor:['#1f6feb','#00b42a']}]},
|
|
599
|
+
options:{responsive:true,maintainAspectRatio:false,plugins:{legend:{display:false},datalabels:{display:true,anchor:'end',font:{size:11},formatter:v=>fm(v)}}}});
|
|
600
|
+
document.getElementById('ibNetRev').innerHTML=gv>0?T.insight_net_rev_rate.replace('{0}',(nv/gv*100).toFixed(1)):T.no_data+'.';}
|
|
601
|
+
pfRenderers.chNetRev=renderNetRev;
|
|
602
|
+
|
|
603
|
+
// AOV Segments
|
|
604
|
+
function renderAOV(){
|
|
605
|
+
destroy('chAOV');const m=pfState.chAOV||'all';
|
|
606
|
+
const buckets=DATA.aovBuckets?(DATA.aovBuckets[m]||[]):[];
|
|
607
|
+
if(!buckets.length){noData('ibAOV',{compact:true});return;}
|
|
608
|
+
CH.chAOV=new Chart(document.getElementById('chAOV'),{type:'bar',
|
|
609
|
+
data:{labels:buckets.map(b=>b.range),datasets:[{data:buckets.map(b=>b.count),backgroundColor:'#ff6a00'}]},
|
|
610
|
+
options:{responsive:true,maintainAspectRatio:false,plugins:{legend:{display:false},datalabels:{display:true,anchor:'end',font:{size:10},formatter:(v,ctx)=>{const t=buckets.reduce((a,b)=>a+b.count,0)||1;const p=(v/t*100).toFixed(1);return fmt(v)+' ('+p+'%)';}}}}});
|
|
611
|
+
const total=buckets.reduce((a,b)=>a+b.count,0)||1;
|
|
612
|
+
const top=buckets.reduce((a,b)=>b.count>a.count?b:a,buckets[0]);
|
|
613
|
+
document.getElementById('ibAOV').innerHTML=T.insight_aov_main.replace('{0}',top.range).replace('{1}',(top.count/total*100).toFixed(1));}
|
|
614
|
+
pfRenderers.chAOV=renderAOV;
|
|
615
|
+
|
|
616
|
+
// Payment
|
|
617
|
+
function renderPay(){
|
|
618
|
+
destroy('chPay');const m=pfState.chPay||'all';
|
|
619
|
+
const data=(DATA.payMethods&&DATA.payMethods[m])?DATA.payMethods[m].map(d=>({n:d.name,v:d.count})):[];
|
|
620
|
+
if(!data.length){noData('ibPay',{compact:true});return;}
|
|
621
|
+
const total=data.reduce((a,b)=>a+b.v,0)||1;
|
|
622
|
+
CH.chPay=new Chart(document.getElementById('chPay'),{type:'doughnut',
|
|
623
|
+
data:{labels:data.map(d=>d.n),datasets:[{data:data.map(d=>d.v),backgroundColor:COLORS}]},
|
|
624
|
+
options:{responsive:true,maintainAspectRatio:false,plugins:{legend:{position:'right',labels:{font:{size:11}}},datalabels:{display:true,color:'#fff',font:{size:10,weight:'bold'},formatter:v=>(v/total*100).toFixed(1)+'%'}}}});
|
|
625
|
+
const top=data.reduce((a,b)=>b.v>a.v?b:a,data[0]);
|
|
626
|
+
document.getElementById('ibPay').innerHTML=T.insight_pay_main.replace('{0}',top.n).replace('{1}',(top.v/total*100).toFixed(1));}
|
|
627
|
+
pfRenderers.chPay=renderPay;
|
|
628
|
+
|
|
629
|
+
// Voucher
|
|
630
|
+
function renderVoucher(){
|
|
631
|
+
destroy('chVoucher');
|
|
632
|
+
const vs=DATA.voucherStats||{usage_rate:{all:0,miravia:0,ae:0},avg_amount:{all:0,miravia:0,ae:0}};
|
|
633
|
+
CH.chVoucher=new Chart(document.getElementById('chVoucher'),{type:'bar',
|
|
634
|
+
data:{labels:[T.th_overall,'Miravia','M2A'],datasets:[
|
|
635
|
+
{label:T.chart_usage_rate,data:[vs.usage_rate.all,vs.usage_rate.miravia,vs.usage_rate.ae],backgroundColor:'#ff7d00',yAxisID:'y'},
|
|
636
|
+
{label:T.chart_avg_disc+'('+CUR+')',data:[vs.avg_amount.all,vs.avg_amount.miravia,vs.avg_amount.ae],backgroundColor:'#1f6feb',yAxisID:'y1'}]},
|
|
637
|
+
options:{responsive:true,maintainAspectRatio:false,
|
|
638
|
+
plugins:{legend:{position:'top',labels:{font:{size:11}}},datalabels:{display:true,anchor:'end',font:{size:10},formatter:(v,ctx)=>ctx.datasetIndex===0?v+'%':CUR+' '+v.toFixed(1)}},
|
|
639
|
+
scales:{y:{type:'linear',position:'left',title:{display:true,text:T.chart_usage_axis},min:0,max:(function(){const maxRate=Math.max(vs.usage_rate.all||0,vs.usage_rate.miravia||0,vs.usage_rate.ae||0);return maxRate<=0?10:Math.ceil(maxRate*1.5);}())},
|
|
640
|
+
y1:{type:'linear',position:'right',title:{display:true,text:CUR},grid:{drawOnChartArea:false},min:0,max:Math.max(5,vs.avg_amount.all*2)}}}});
|
|
641
|
+
document.getElementById('ibVoucher').innerHTML=T.insight_voucher.replace('{0}',vs.usage_rate.all).replace('{1}',CUR).replace('{2}',vs.avg_amount.all.toFixed(2));}
|
|
642
|
+
|
|
643
|
+
// Shipping
|
|
644
|
+
function renderShip(a){
|
|
645
|
+
const ss=DATA.shippingStats||{all:{},miravia:{},ae:{}};
|
|
646
|
+
const rows=[[T.th_overall,ss.all],['Miravia',ss.miravia],['M2A',ss.ae]];
|
|
647
|
+
document.getElementById('shipTb').innerHTML=rows.map(r=>{
|
|
648
|
+
const d=r[1]||{};
|
|
649
|
+
return`<tr><td><b>${r[0]}</b></td><td class="n">${d.total!=null?fm(d.total):'-'}</td><td class="n">${d.avg!=null?fm(d.avg):'-'}</td><td class="n">${d.disc!=null?fm(d.disc):'-'}</td><td class="n">${d.disc_pct!=null?d.disc_pct+'%':'-'}</td><td class="n">${d.free_pct!=null?d.free_pct+'%':'-'}</td></tr>`;}).join('');
|
|
650
|
+
document.getElementById('ibShip').innerHTML=ss.all&&ss.all.total?T.insight_shipping.replace('{0}',ss.all.disc_pct).replace('{1}',ss.all.free_pct):'';}
|
|
651
|
+
|
|
652
|
+
// Order Status
|
|
653
|
+
function renderStatus(){
|
|
654
|
+
destroy('chStatus');const m=pfState.chStatus||'all';
|
|
655
|
+
const data=(DATA.statusDist&&DATA.statusDist[m])||[];
|
|
656
|
+
if(!data.length){noData('ibStatus',{compact:true});return;}
|
|
657
|
+
const total=data.reduce((a,b)=>a+b.count,0)||1;
|
|
658
|
+
CH.chStatus=new Chart(document.getElementById('chStatus'),{type:'doughnut',
|
|
659
|
+
data:{labels:data.map(d=>d.name),datasets:[{data:data.map(d=>d.count),backgroundColor:COLORS}]},
|
|
660
|
+
options:{responsive:true,maintainAspectRatio:false,plugins:{legend:{position:'right',labels:{font:{size:11}}},datalabels:{display:true,color:'#fff',font:{size:10,weight:'bold'},formatter:v=>(v/total*100).toFixed(1)+'%'}}}});
|
|
661
|
+
const top=data.reduce((a,b)=>b.count>a.count?b:a,data[0]);
|
|
662
|
+
document.getElementById('ibStatus').innerHTML=T.insight_status_main.replace('{0}',top.name).replace('{1}',(top.count/total*100).toFixed(1));}
|
|
663
|
+
pfRenderers.chStatus=renderStatus;
|
|
664
|
+
|
|
665
|
+
// Cancel Initiator
|
|
666
|
+
function renderCancelInit(){
|
|
667
|
+
destroy('chCancelInit');const m=pfState.chCancelInit||'all';
|
|
668
|
+
const data=(DATA.cancelInitiator&&DATA.cancelInitiator[m])||[];
|
|
669
|
+
if(!data.length){noData('ibCancelInit',{compact:true});return;}
|
|
670
|
+
const total=data.reduce((a,b)=>a+b.count,0)||1;
|
|
671
|
+
CH.chCancelInit=new Chart(document.getElementById('chCancelInit'),{type:'doughnut',
|
|
672
|
+
data:{labels:data.map(d=>d.name),datasets:[{data:data.map(d=>d.count),backgroundColor:['#1f6feb','#ff6a00','#86909c']}]},
|
|
673
|
+
options:{responsive:true,maintainAspectRatio:false,plugins:{legend:{position:'bottom',labels:{font:{size:11}}},datalabels:{display:true,color:'#fff',font:{size:11,weight:'bold'},formatter:v=>(v/total*100).toFixed(1)+'%'}}}});
|
|
674
|
+
const top=data.reduce((a,b)=>b.count>a.count?b:a,data[0]);
|
|
675
|
+
document.getElementById('ibCancelInit').innerHTML=T.insight_cancel_init_main.replace('{0}',top.name).replace('{1}',(top.count/total*100).toFixed(1));}
|
|
676
|
+
pfRenderers.chCancelInit=renderCancelInit;
|
|
677
|
+
|
|
678
|
+
// Cancel Agree
|
|
679
|
+
function renderCancelAgree(){
|
|
680
|
+
destroy('chCancelAgree');const m=pfState.chCancelAgree||'all';
|
|
681
|
+
const d=(DATA.cancelAgree&&DATA.cancelAgree[m])||{agreed:0,rejected:0};
|
|
682
|
+
if(d.agreed===0&&d.rejected===0){noData('ibCancelAgree',{compact:true});return;}
|
|
683
|
+
CH.chCancelAgree=new Chart(document.getElementById('chCancelAgree'),{type:'bar',
|
|
684
|
+
data:{labels:[T.seller_agree,T.seller_reject],datasets:[{data:[d.agreed,d.rejected],backgroundColor:['#00b42a','#f53f3f']}]},
|
|
685
|
+
options:{responsive:true,maintainAspectRatio:false,indexAxis:'y',
|
|
686
|
+
plugins:{legend:{display:false},datalabels:{display:true,anchor:'end',font:{size:12,weight:'bold'},formatter:v=>v+'%'}},
|
|
687
|
+
scales:{x:{max:100,ticks:{callback:v=>v+'%'}}}}});
|
|
688
|
+
document.getElementById('ibCancelAgree').innerHTML=T.insight_cancel_agree.replace('{0}',d.agreed);}
|
|
689
|
+
pfRenderers.chCancelAgree=renderCancelAgree;
|
|
690
|
+
|
|
691
|
+
// Cancel Reason
|
|
692
|
+
function renderCancelReason(){
|
|
693
|
+
destroy('chCancelReason');const m=pfState.chCancelReason||'all';
|
|
694
|
+
const data=(DATA.cancelReasons&&DATA.cancelReasons[m])||[];
|
|
695
|
+
if(!data.length){noData('ibCancelReason',{compact:true});return;}
|
|
696
|
+
const total=data.reduce((a,b)=>a+b.count,0)||1;
|
|
697
|
+
CH.chCancelReason=new Chart(document.getElementById('chCancelReason'),{type:'bar',
|
|
698
|
+
data:{labels:data.map(d=>d.name),datasets:[{data:data.map(d=>d.count),backgroundColor:COLORS}]},
|
|
699
|
+
options:{responsive:true,maintainAspectRatio:false,
|
|
700
|
+
plugins:{legend:{display:false},datalabels:{display:true,anchor:'end',font:{size:10},formatter:v=>v+' ('+(v/total*100).toFixed(1)+'%)'}},
|
|
701
|
+
scales:{y:{title:{display:true,text:T.chart_qty}}}}});
|
|
702
|
+
const top=data.reduce((a,b)=>b.count>a.count?b:a,data[0]);
|
|
703
|
+
document.getElementById('ibCancelReason').innerHTML=T.insight_cancel_reason_main.replace('{0}',top.name).replace('{1}',(top.count/total*100).toFixed(1));}
|
|
704
|
+
pfRenderers.chCancelReason=renderCancelReason;
|
|
705
|
+
|
|
706
|
+
// Refund Type
|
|
707
|
+
function renderRefType(){
|
|
708
|
+
destroy('chRefType');const m=pfState.chRefType||'all';
|
|
709
|
+
const data=(DATA.refundTypes&&DATA.refundTypes[m])||[];
|
|
710
|
+
if(!data.length){noData('ibRefType',{compact:true,msg:T.no_refund_data});return;}
|
|
711
|
+
const total=data.reduce((a,b)=>a+b.count,0)||1;
|
|
712
|
+
CH.chRefType=new Chart(document.getElementById('chRefType'),{type:'doughnut',
|
|
713
|
+
data:{labels:data.map(d=>d.name),datasets:[{data:data.map(d=>d.count),backgroundColor:['#f53f3f','#ff7d00','#1f6feb']}]},
|
|
714
|
+
options:{responsive:true,maintainAspectRatio:false,plugins:{legend:{position:'right',labels:{font:{size:11}}},datalabels:{display:true,color:'#fff',font:{size:11,weight:'bold'},formatter:v=>(v/total*100).toFixed(1)+'%'}}}});
|
|
715
|
+
document.getElementById('ibRefType').innerHTML=data.length?T.insight_ref_type_main.replace('{0}',data[0].name).replace('{1}',(data[0].count/total*100).toFixed(1)):'';}
|
|
716
|
+
pfRenderers.chRefType=renderRefType;
|
|
717
|
+
|
|
718
|
+
// Duty Party
|
|
719
|
+
function renderDuty(){
|
|
720
|
+
destroy('chDuty');const m=pfState.chDuty||'all';
|
|
721
|
+
const data=(DATA.dutyPartyDist&&DATA.dutyPartyDist[m])||[];
|
|
722
|
+
if(!data.length){noData('ibDuty',{compact:true,msg:T.no_refund_data});return;}
|
|
723
|
+
const total=data.reduce((a,b)=>a+b.count,0)||1;
|
|
724
|
+
CH.chDuty=new Chart(document.getElementById('chDuty'),{type:'doughnut',
|
|
725
|
+
data:{labels:data.map(d=>d.name),datasets:[{data:data.map(d=>d.count),backgroundColor:['#ff6a00','#1f6feb','#00b42a','#86909c']}]},
|
|
726
|
+
options:{responsive:true,maintainAspectRatio:false,plugins:{legend:{position:'right',labels:{font:{size:11}}},datalabels:{display:true,color:'#fff',font:{size:11,weight:'bold'},formatter:v=>(v/total*100).toFixed(1)+'%'}}}});
|
|
727
|
+
document.getElementById('ibDuty').innerHTML=data.length?T.insight_duty_main.replace('{0}',data[0].name).replace('{1}',(data[0].count/total*100).toFixed(1)):'';}
|
|
728
|
+
pfRenderers.chDuty=renderDuty;
|
|
729
|
+
|
|
730
|
+
// Refund Reason table
|
|
731
|
+
function renderReason(){
|
|
732
|
+
if(!REF_REASONS.length){const _ibR=document.getElementById('ibReason');const _secR=_ibR&&_ibR.closest&&_ibR.closest('.sec');if(_secR){const _tR=_secR.querySelector('table');if(_tR)_tR.style.display='none';_secR.classList.add('sec-empty');}document.getElementById('reasonTb').innerHTML='';if(_ibR)_ibR.innerHTML='<div class="no-data-inline">'+T.no_refund_data+'</div>';return;}
|
|
733
|
+
document.getElementById('reasonTb').innerHTML=REF_REASONS.map((r,i)=>
|
|
734
|
+
`<tr class="clickable" onclick="openReasonDetail(${i})"><td>${r.r}</td><td class="n">${fp(r.pc)}</td><td class="n">${fm(r.amt)}</td><td>${r.dp}</td><td>${r.ft}</td></tr>`).join('');
|
|
735
|
+
document.getElementById('ibReason').innerHTML=REF_REASONS.length?T.insight_ref_reason_top.replace('{0}',REF_REASONS[0].r).replace('{1}',(REF_REASONS[0].pc*100).toFixed(1))+'<b>'+T.insight_ref_reason_click+'</b>':'';}
|
|
736
|
+
function openReasonDetail(idx){
|
|
737
|
+
const r=REF_REASONS[idx];const d=r.detail||{};
|
|
738
|
+
document.getElementById('modalTitle').textContent=r.r;
|
|
739
|
+
let html=`<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:10px;margin-bottom:14px">
|
|
740
|
+
<div style="text-align:center;background:#fafbfc;padding:10px;border-radius:8px"><div style="font-size:18px;font-weight:700;color:var(--info)">${fp(r.pc)}</div><div style="font-size:11px;color:var(--mut)">${T.modal_ratio}</div></div>
|
|
741
|
+
<div style="text-align:center;background:#fafbfc;padding:10px;border-radius:8px"><div style="font-size:18px;font-weight:700;color:var(--wn)">${fm(r.amt)}</div><div style="font-size:11px;color:var(--mut)">${T.modal_refund_amount}</div></div>
|
|
742
|
+
<div style="text-align:center;background:#fafbfc;padding:10px;border-radius:8px"><div style="font-size:18px;font-weight:700">${r.dp}</div><div style="font-size:11px;color:var(--mut)">${T.modal_duty_party}</div></div></div>`;
|
|
743
|
+
html+=`<table><thead><tr><th>${T.modal_dimension}</th><th>${T.modal_detail}</th></tr></thead><tbody>
|
|
744
|
+
<tr><td>${T.modal_related_sku}</td><td>${(d.skus||[]).join(', ')||'-'}</td></tr>
|
|
745
|
+
<tr><td>${T.modal_related_carrier}</td><td>${d.carriers||'N/A'}</td></tr></tbody></table>`;
|
|
746
|
+
html+=`<div class="ib"><b>${T.modal_suggestion}</b>: ${d.rec||'-'}</div>`;
|
|
747
|
+
document.getElementById('modalBody').innerHTML=html;
|
|
748
|
+
document.getElementById('modalBg').classList.add('show');}
|
|
749
|
+
function closeModal(){document.getElementById('modalBg').classList.remove('show');}
|
|
750
|
+
document.addEventListener('keydown',function(e){if(e.key==='Escape')closeModal();});
|
|
751
|
+
|
|
752
|
+
// Fast Refund
|
|
753
|
+
function renderFastRef(){
|
|
754
|
+
destroy('chFastRef');const m=pfState.chFastRef||'all';
|
|
755
|
+
const data=(DATA.fastRefund&&DATA.fastRefund[m])||[];
|
|
756
|
+
if(!data.length){noData('ibFastRef',{compact:true,msg:T.no_refund_data});return;}
|
|
757
|
+
CH.chFastRef=new Chart(document.getElementById('chFastRef'),{type:'bar',
|
|
758
|
+
data:{labels:data.map(d=>d.name),datasets:[{data:data.map(d=>d.pct),backgroundColor:['#1f6feb','#00b42a','#ff7d00','#f53f3f']}]},
|
|
759
|
+
options:{responsive:true,maintainAspectRatio:false,
|
|
760
|
+
plugins:{legend:{display:false},datalabels:{display:true,anchor:'end',font:{size:11,weight:'bold'},formatter:v=>v+'%'}},
|
|
761
|
+
scales:{y:{max:100,ticks:{callback:v=>v+'%'},title:{display:true,text:T.chart_share_pct}}}}});
|
|
762
|
+
document.getElementById('ibFastRef').innerHTML=data.length?T.insight_fast_ref.replace('{0}',data[0].pct):'';}
|
|
763
|
+
pfRenderers.chFastRef=renderFastRef;
|
|
764
|
+
|
|
765
|
+
// Refund SKU
|
|
766
|
+
function renderRefSku(){
|
|
767
|
+
const refSkus=SKUS.filter(s=>(s.mRA||0)+(s.aRA||0)>0).sort((a,b)=>((b.mRA||0)+(b.aRA||0))-((a.mRA||0)+(a.aRA||0)));
|
|
768
|
+
if(!refSkus.length){const _ibS=document.getElementById('ibRefSku');const _secS=_ibS&&_ibS.closest&&_ibS.closest('.sec');if(_secS){const _tS=_secS.querySelector('table');if(_tS)_tS.style.display='none';_secS.classList.add('sec-empty');}document.getElementById('refSkuTb').innerHTML='';if(_ibS)_ibS.innerHTML='<div class="no-data-inline">'+(T.no_refund_sku||T.no_refund_data)+'</div>';return;}
|
|
769
|
+
document.getElementById('refSkuTb').innerHTML=refSkus.slice(0,10).map(s=>{
|
|
770
|
+
const mR=s.mRA||0,aR=s.aRA||0;
|
|
771
|
+
const cls=s.rr>.12?'td':s.rr>.07?'tw':'ts';
|
|
772
|
+
return`<tr><td>${s.s}</td><td>${s.nm}</td><td class="n">${fm(mR)}</td><td class="n">${fm(aR)}</td><td class="n">${fm(mR+aR)}</td><td class="n ${cls}">${fp(s.rr)}</td></tr>`;}).join('');
|
|
773
|
+
const top=refSkus[0];
|
|
774
|
+
document.getElementById('ibRefSku').innerHTML=top?T.insight_ref_sku_top.replace('{0}',top.s).replace('{1}',top.nm).replace('{2}',fp(top.rr)):'';}
|
|
775
|
+
|
|
776
|
+
|
|
777
|
+
// Top SKU
|
|
778
|
+
function renderTopSku(){
|
|
779
|
+
if(!SKUS.length){noData('ibTopSku',{compact:true});return;}
|
|
780
|
+
document.getElementById('topSkuTb').innerHTML=SKUS.slice(0,10).map(s=>{
|
|
781
|
+
const t=s.mq+s.aq,cls=s.rr>.12?'td':s.rr>.07?'tw':'ts';
|
|
782
|
+
return`<tr><td>${s.s}</td><td>${s.nm}</td><td class="n">${fmt(s.mq)}</td><td class="n">${fmt(s.aq)}</td><td class="n">${fmt(t)}</td><td class="n ${cls}">${fp(s.rr)}</td></tr>`;}).join('');
|
|
783
|
+
const top=SKUS[0];
|
|
784
|
+
document.getElementById('ibTopSku').innerHTML=top?T.insight_top_sku.replace('{0}',top.s).replace('{1}',top.nm).replace('{2}',fmt(top.mq+top.aq)):'';}
|
|
785
|
+
|
|
786
|
+
// Matrix
|
|
787
|
+
function renderMatrix(){
|
|
788
|
+
destroy('chMatrix');
|
|
789
|
+
if(!SKUS.length){noData('ibMatrix',{compact:true});return;}
|
|
790
|
+
const datasets=SKUS.map(s=>{const t=s.mq+s.aq;return{
|
|
791
|
+
label:s.nm.substring(0,15),data:[{x:t,y:s.rr*100,r:Math.max(3,Math.sqrt(t)/5)}],
|
|
792
|
+
backgroundColor:s.rr>.12?'#f53f3f':s.rr>.07?'#ff7d00':'#00b42a'};});
|
|
793
|
+
CH.chMatrix=new Chart(document.getElementById('chMatrix'),{type:'bubble',
|
|
794
|
+
data:{datasets},
|
|
795
|
+
options:{responsive:true,maintainAspectRatio:false,
|
|
796
|
+
plugins:{legend:{display:false},datalabels:{display:true,font:{size:9},anchor:'end',align:'top',formatter:(v,ctx)=>ctx.dataset.label.substring(0,10)},
|
|
797
|
+
tooltip:{callbacks:{label:c=>`${c.dataset.label}: ${c.raw.x}${T.mini_orders}, ${c.raw.y.toFixed(1)}%`}}},
|
|
798
|
+
scales:{x:{title:{display:true,text:T.chart_orders}},y:{title:{display:true,text:T.chart_refund_rate_pct},min:0,max:Math.max(20,Math.max(...SKUS.map(s=>s.rr*100))+5)}}}});
|
|
799
|
+
const grp={q1:[],q2:[],q3:[],q4:[]};
|
|
800
|
+
const medQ=SKUS.map(s=>s.mq+s.aq).sort((a,b)=>a-b)[Math.floor(SKUS.length/2)]||1000;
|
|
801
|
+
SKUS.forEach(s=>{const t=s.mq+s.aq;const hs=t>=medQ,hr=s.rr>.08;
|
|
802
|
+
if(hs&&!hr)grp.q1.push(s);else if(hs&&hr)grp.q2.push(s);else if(!hs&&!hr)grp.q3.push(s);else grp.q4.push(s);});
|
|
803
|
+
['q1','q2','q3','q4'].forEach(q=>{
|
|
804
|
+
document.getElementById('m'+q).innerHTML=grp[q].length?
|
|
805
|
+
grp[q].map(s=>`<div class="mi">${s.nm} · ${fmt(s.mq+s.aq)}${T.mini_orders} · ${fp(s.rr)}</div>`).join(''):
|
|
806
|
+
'<div class="mi" style="color:var(--mut)">'+T.no_data+'</div>';});
|
|
807
|
+
document.getElementById('ibMatrix').innerHTML=T.insight_matrix.replace('{0}',grp.q1.length).replace('{1}',grp.q4.length);}
|
|
808
|
+
|
|
809
|
+
// Category
|
|
810
|
+
function renderCat(){
|
|
811
|
+
destroy('chCat');const m=pfState.chCat||'all';
|
|
812
|
+
const cats=(DATA.catDist&&DATA.catDist[m])||[];
|
|
813
|
+
if(!cats.length){noData('ibCat',{compact:true});return;}
|
|
814
|
+
CH.chCat=new Chart(document.getElementById('chCat'),{type:'bar',
|
|
815
|
+
data:{labels:cats.map(c=>c.name),datasets:[
|
|
816
|
+
{label:'GMV('+CUR+')',data:cats.map(c=>c.gmv),backgroundColor:'#1f6feb',yAxisID:'y'},
|
|
817
|
+
{label:T.chart_refund_rate_pct,data:cats.map(c=>c.rr*100),backgroundColor:'#f53f3f',yAxisID:'y1'}]},
|
|
818
|
+
options:{responsive:true,maintainAspectRatio:false,
|
|
819
|
+
plugins:{datalabels:{display:true,anchor:'end',font:{size:9},formatter:(v,ctx)=>ctx.datasetIndex===1?v.toFixed(1)+'%':CUR+' '+fmt(v)}},
|
|
820
|
+
scales:{y:{type:'linear',position:'left',title:{display:true,text:'GMV('+CUR+')'}},
|
|
821
|
+
y1:{type:'linear',position:'right',title:{display:true,text:T.chart_refund_rate_pct},grid:{drawOnChartArea:false}}}}});
|
|
822
|
+
const top=cats.reduce((a,b)=>b.gmv>a.gmv?b:a,cats[0]);
|
|
823
|
+
document.getElementById('ibCat').innerHTML=T.insight_cat_top.replace('{0}',top.name).replace('{1}',fm(top.gmv)).replace('{2}',fp(top.rr));}
|
|
824
|
+
pfRenderers.chCat=renderCat;
|
|
825
|
+
|
|
826
|
+
// Discount
|
|
827
|
+
function renderDiscount(){
|
|
828
|
+
destroy('chDiscount');const m=pfState.chDiscount||'all';
|
|
829
|
+
const data=(DATA.discountDepth&&DATA.discountDepth[m])||[];
|
|
830
|
+
if(!data.length){noData('ibDiscount',{compact:true});return;}
|
|
831
|
+
const total=data.reduce((a,b)=>a+b.count,0)||1;
|
|
832
|
+
CH.chDiscount=new Chart(document.getElementById('chDiscount'),{type:'bar',
|
|
833
|
+
data:{labels:data.map(d=>d.range),datasets:[{data:data.map(d=>d.count),backgroundColor:'#ff6a00'}]},
|
|
834
|
+
options:{responsive:true,maintainAspectRatio:false,
|
|
835
|
+
plugins:{legend:{display:false},datalabels:{display:true,anchor:'end',font:{size:10},formatter:v=>v+' ('+(v/total*100).toFixed(1)+'%)'}},
|
|
836
|
+
scales:{y:{title:{display:true,text:T.chart_orders}}}}});
|
|
837
|
+
const top=data.reduce((a,b)=>b.count>a.count?b:a,data[0]);
|
|
838
|
+
document.getElementById('ibDiscount').innerHTML=T.insight_discount_top.replace('{0}',top.range).replace('{1}',(top.count/total*100).toFixed(1));}
|
|
839
|
+
pfRenderers.chDiscount=renderDiscount;
|
|
840
|
+
|
|
841
|
+
// FBM
|
|
842
|
+
function renderFBM(){
|
|
843
|
+
destroy('chFBM');const m=pfState.chFBM||'all';
|
|
844
|
+
const d=(DATA.fbmDist&&DATA.fbmDist[m])||[0,0];
|
|
845
|
+
const total=(d[0]+d[1])||1;
|
|
846
|
+
if(total===0){noData('ibFBM',{compact:true});return;}
|
|
847
|
+
CH.chFBM=new Chart(document.getElementById('chFBM'),{type:'doughnut',
|
|
848
|
+
data:{labels:[T.fbm_label,T.non_fbm_label],datasets:[{data:d,backgroundColor:['#00b42a','#86909c']}]},
|
|
849
|
+
options:{responsive:true,maintainAspectRatio:false,plugins:{legend:{position:'right',labels:{font:{size:11}}},datalabels:{display:true,color:'#fff',font:{size:11,weight:'bold'},formatter:v=>[fmt(v),(v/total*100).toFixed(1)+'%']}}}});
|
|
850
|
+
document.getElementById('ibFBM').innerHTML=T.insight_fbm.replace('{0}',(d[0]/total*100).toFixed(1));}
|
|
851
|
+
pfRenderers.chFBM=renderFBM;
|
|
852
|
+
|
|
853
|
+
// Digital
|
|
854
|
+
function renderDigital(){
|
|
855
|
+
destroy('chDigital');const m=pfState.chDigital||'all';
|
|
856
|
+
const d=(DATA.digitalDist&&DATA.digitalDist[m])||[100,0];
|
|
857
|
+
CH.chDigital=new Chart(document.getElementById('chDigital'),{type:'doughnut',
|
|
858
|
+
data:{labels:[T.physical_goods,T.digital_goods],datasets:[{data:d,backgroundColor:['#1f6feb','#ff7d00']}]},
|
|
859
|
+
options:{responsive:true,maintainAspectRatio:false,plugins:{legend:{position:'right',labels:{font:{size:11}}},datalabels:{display:true,color:'#fff',font:{size:11,weight:'bold'},formatter:v=>v+'%'}}}});
|
|
860
|
+
document.getElementById('ibDigital').innerHTML=T.insight_digital.replace('{0}',d[1]);}
|
|
861
|
+
pfRenderers.chDigital=renderDigital;
|
|
862
|
+
|
|
863
|
+
// Buyer KPIs
|
|
864
|
+
function renderBuyerKPIs(a){
|
|
865
|
+
const bs=DATA.buyerStats||{all:{buyer_count:0,repurchase_rate:0,top5_share:0},miravia:{buyer_count:0,repurchase_rate:0,top5_share:0},ae:{buyer_count:0,repurchase_rate:0,top5_share:0}};
|
|
866
|
+
const items=[
|
|
867
|
+
{lb:T.buyer_total,v:bs.all.buyer_count,mv:bs.miravia.buyer_count,av:bs.ae.buyer_count},
|
|
868
|
+
{lb:T.buyer_repurchase,v:bs.all.repurchase_rate/100,mv:bs.miravia.repurchase_rate/100,av:bs.ae.repurchase_rate/100,pct:true},
|
|
869
|
+
{lb:T.buyer_top5,v:bs.all.top5_share/100,mv:bs.miravia.top5_share/100,av:bs.ae.top5_share/100,pct:true}];
|
|
870
|
+
document.getElementById('buyerKPIs').innerHTML=items.map(k=>{
|
|
871
|
+
const fv=k.pct?fp(k.v):fmt(k.v),fmv=k.pct?fp(k.mv):fmt(k.mv),fav=k.pct?fp(k.av):fmt(k.av);
|
|
872
|
+
return`<div class="kpi" style="background:#fafbfc;border:1px solid #eef0f3"><div class="lb">${k.lb}</div><div class="vl" style="font-size:18px">${fv}</div><div class="sub"><span class="pm">Miravia:${fmv}</span><span class="pa">M2A:${fav}</span></div></div>`;}).join('');}
|
|
873
|
+
|
|
874
|
+
// Country Table
|
|
875
|
+
function renderCountryTable(){
|
|
876
|
+
const co=DATA.countries||[];
|
|
877
|
+
if(!co.length){noData('ibCountryTb',{compact:true});return;}
|
|
878
|
+
document.getElementById('countryTb').innerHTML=co.slice(0,10).map(c=>
|
|
879
|
+
`<tr><td>${c.nm}</td><td class="n">${fmt(c.miravia_orders)}</td><td class="n">${fmt(c.ae_orders)}</td><td class="n">${fmt(c.all_orders)}</td><td class="n">${fm(c.all_gmv)}</td><td class="n">${fp(c.rr)}</td></tr>`).join('');
|
|
880
|
+
document.getElementById('ibCountryTb').innerHTML=co[0]?T.insight_country_dominant.replace('{0}',co[0].nm):'';}
|
|
881
|
+
|
|
882
|
+
// City
|
|
883
|
+
function renderCity(){
|
|
884
|
+
destroy('chCity');
|
|
885
|
+
if(!CITIES.length){noData('ibCity',{compact:true});return;}
|
|
886
|
+
CH.chCity=new Chart(document.getElementById('chCity'),{type:'bar',
|
|
887
|
+
data:{labels:CITIES.map(c=>c.n),datasets:[{data:CITIES.map(c=>c.o),backgroundColor:'#1f6feb'}]},
|
|
888
|
+
options:{responsive:true,maintainAspectRatio:false,indexAxis:'y',
|
|
889
|
+
plugins:{legend:{display:false},datalabels:{display:true,anchor:'end',font:{size:10},formatter:v=>fmt(v)}},
|
|
890
|
+
scales:{x:{title:{display:true,text:T.chart_orders}}}}});
|
|
891
|
+
document.getElementById('ibCity').innerHTML=CITIES[0]?T.insight_city_top.replace('{0}',CITIES[0].n).replace('{1}',fmt(CITIES[0].o)):'';}
|
|
892
|
+
|
|
893
|
+
// Country Mini
|
|
894
|
+
function renderCountryMini(){
|
|
895
|
+
const co=(DATA.countries||[]).slice(0,6);
|
|
896
|
+
if(!co.length){noData('countryMini',{compact:true});return;}
|
|
897
|
+
document.getElementById('countryMini').innerHTML=co.map(c=>
|
|
898
|
+
`<div class="mr"><h4>${c.nm} <span style="font-weight:400;color:var(--mut)">${c.cd}</span></h4>
|
|
899
|
+
<div class="mk"><div class="mk-i"><div class="v">${fmt(c.all_orders)}</div><div class="l">${T.mini_orders}</div></div><div class="mk-i"><div class="v">${fm(c.all_gmv)}</div><div class="l">${T.mini_gmv}</div></div><div class="mk-i"><div class="v">${fp(c.rr)}</div><div class="l">${T.mini_refund_rate}</div></div></div></div>`).join('');}
|
|
900
|
+
|
|
901
|
+
// Pay × Country
|
|
902
|
+
function renderPayCountry(){
|
|
903
|
+
destroy('chPayCountry');
|
|
904
|
+
const pc=DATA.payCountry||{countries:[],payments:[],data:[]};
|
|
905
|
+
if(!pc.countries.length){noData('ibPayCountry',{compact:true});return;}
|
|
906
|
+
const datasets=pc.payments.map((pm,i)=>({label:pm,data:pc.data.map(row=>row[pm]||0),backgroundColor:COLORS[i%COLORS.length]}));
|
|
907
|
+
CH.chPayCountry=new Chart(document.getElementById('chPayCountry'),{type:'bar',
|
|
908
|
+
data:{labels:pc.countries,datasets},
|
|
909
|
+
options:{responsive:true,maintainAspectRatio:false,
|
|
910
|
+
plugins:{datalabels:{display:true,anchor:'end',font:{size:10},formatter:v=>v+'%'}}}});
|
|
911
|
+
document.getElementById('ibPayCountry').innerHTML=T.insight_pay_country;}
|
|
912
|
+
|
|
913
|
+
// Abnormal Buyers
|
|
914
|
+
function renderAbBuyer(){
|
|
915
|
+
if(!AB_BUYERS.length){noData('ibAbBuyer',{compact:true,msg:T.no_abnormal_buyer});return;}
|
|
916
|
+
document.getElementById('abBuyerTb').innerHTML=AB_BUYERS.map(b=>{
|
|
917
|
+
const t=b.risk==='high'?'<span class="tag" style="background:#ffeceb;color:var(--dg)">'+T.risk_high+'</span>':b.risk==='mid'?'<span class="tag" style="background:#fff7e8;color:var(--wn)">'+T.risk_mid+'</span>':'<span class="tag" style="background:#e8ffea;color:var(--ok)">'+T.risk_low+'</span>';
|
|
918
|
+
return`<tr><td>${b.id}</td><td class="n">${b.o}</td><td class="n">${b.rf}</td><td class="n td">${fp(b.rt)}</td><td>${t}</td></tr>`;}).join('');
|
|
919
|
+
document.getElementById('ibAbBuyer').innerHTML=T.insight_ab_buyer.replace('{0}',AB_BUYERS.length);}
|
|
920
|
+
|
|
921
|
+
// Carrier
|
|
922
|
+
function renderCarrier(){
|
|
923
|
+
if(!CARRIERS.length){noData('ibCarrier',{compact:true});return;}
|
|
924
|
+
const total=CARRIERS.reduce((a,b)=>a+b.mo+b.ao,0)||1;
|
|
925
|
+
document.getElementById('carrierTb').innerHTML=CARRIERS.map(c=>{
|
|
926
|
+
const t=c.mo+c.ao;
|
|
927
|
+
return`<tr><td><b>${c.nm}</b></td><td class="n">${fmt(c.mo)}</td><td class="n">${fmt(c.ao)}</td><td class="n">${fmt(t)}</td><td class="n">${(t/total*100).toFixed(1)}%</td><td class="n">${fp(c.cr)}</td><td class="n">${fp(c.rr)}</td></tr>`;}).join('');
|
|
928
|
+
document.getElementById('ibCarrier').innerHTML=CARRIERS[0]?T.insight_carrier_top.replace('{0}',CARRIERS[0].nm).replace('{1}',((CARRIERS[0].mo+CARRIERS[0].ao)/total*100).toFixed(1)):'';}
|
|
929
|
+
|
|
930
|
+
// FBM2 (logistics tab)
|
|
931
|
+
function renderFBM2(){
|
|
932
|
+
destroy('chFBM2');const m=pfState.chFBM2||'all';
|
|
933
|
+
const d=(DATA.fbmDist&&DATA.fbmDist[m])||[0,0];
|
|
934
|
+
const total=(d[0]+d[1])||1;
|
|
935
|
+
if(total===0){noData('ibFBM2',{compact:true});return;}
|
|
936
|
+
CH.chFBM2=new Chart(document.getElementById('chFBM2'),{type:'doughnut',
|
|
937
|
+
data:{labels:['FBM','Non-FBM'],datasets:[{data:d,backgroundColor:['#00b42a','#86909c']}]},
|
|
938
|
+
options:{responsive:true,maintainAspectRatio:false,plugins:{legend:{position:'right'},datalabels:{display:true,color:'#fff',font:{size:11,weight:'bold'},formatter:v=>[fmt(v),(v/total*100).toFixed(1)+'%']}}}});
|
|
939
|
+
document.getElementById('ibFBM2').innerHTML=T.insight_fbm.replace('{0}',(d[0]/total*100).toFixed(1));}
|
|
940
|
+
pfRenderers.chFBM2=renderFBM2;
|
|
941
|
+
|
|
942
|
+
// Ship Type
|
|
943
|
+
function renderShipType(){
|
|
944
|
+
destroy('chShipType');const m=pfState.chShipType||'all';
|
|
945
|
+
const data=(DATA.shipTypeDist&&DATA.shipTypeDist[m])||[];
|
|
946
|
+
if(!data.length){noData('ibShipType',{compact:true});return;}
|
|
947
|
+
const total=data.reduce((a,b)=>a+b.count,0)||1;
|
|
948
|
+
CH.chShipType=new Chart(document.getElementById('chShipType'),{type:'bar',
|
|
949
|
+
data:{labels:data.map(d=>d.name),datasets:[{data:data.map(d=>d.count),backgroundColor:COLORS}]},
|
|
950
|
+
options:{responsive:true,maintainAspectRatio:false,
|
|
951
|
+
plugins:{legend:{display:false},datalabels:{display:true,anchor:'end',font:{size:10},formatter:v=>fmt(v)+' ('+(v/total*100).toFixed(1)+'%)'}},
|
|
952
|
+
scales:{y:{title:{display:true,text:T.chart_orders}}}}});
|
|
953
|
+
const top=data.reduce((a,b)=>b.count>a.count?b:a,data[0]);
|
|
954
|
+
document.getElementById('ibShipType').innerHTML=T.insight_ship_type_main.replace('{0}',top.name).replace('{1}',(top.count/total*100).toFixed(1));}
|
|
955
|
+
pfRenderers.chShipType=renderShipType;
|
|
956
|
+
|
|
957
|
+
// ================================================================
|
|
958
|
+
// TAB SWITCH & INIT
|
|
959
|
+
// ================================================================
|
|
960
|
+
document.querySelectorAll('.tb').forEach(b=>b.addEventListener('click',()=>{
|
|
961
|
+
document.querySelectorAll('.tb').forEach(x=>x.classList.remove('on'));
|
|
962
|
+
document.querySelectorAll('.tp').forEach(x=>x.classList.remove('on'));
|
|
963
|
+
b.classList.add('on');document.getElementById('t-'+b.dataset.t).classList.add('on');
|
|
964
|
+
}));
|
|
965
|
+
function initPlatformFilters(){document.querySelectorAll('.pf[data-chart]').forEach(c=>initPf(c));}
|
|
966
|
+
|
|
967
|
+
// ================================================================
|
|
968
|
+
// LANGUAGE SWITCH
|
|
969
|
+
// ================================================================
|
|
970
|
+
function switchLang(lang){
|
|
971
|
+
if(!TRANSLATIONS[lang])return;
|
|
972
|
+
currentLang=lang;
|
|
973
|
+
T=TRANSLATIONS[lang];
|
|
974
|
+
document.documentElement.lang=T.lang_code||lang;
|
|
975
|
+
document.title=T.page_title||document.title;
|
|
976
|
+
// Update all static elements with data-i18n attribute
|
|
977
|
+
document.querySelectorAll('[data-i18n]').forEach(el=>{
|
|
978
|
+
const key=el.dataset.i18n;
|
|
979
|
+
if(T[key]!=null)el.textContent=T[key];
|
|
980
|
+
});
|
|
981
|
+
// Re-init time filter buttons & platform filters with new labels
|
|
982
|
+
initTF();initPlatformFilters();
|
|
983
|
+
// Re-init trend metric/platform buttons
|
|
984
|
+
document.getElementById('trendMs').innerHTML=['gmv','orders','aov','cr','rr'].map((m,i)=>{
|
|
985
|
+
const lbs={gmv:'GMV',orders:T.chart_orders,aov:'AOV',cr:T.kpi_cancel_rate,rr:T.kpi_refund_rate};
|
|
986
|
+
return'<button class="'+(m===trendMetric?'on':'')+'" data-m="'+m+'">'+lbs[m]+'</button>';}).join('');
|
|
987
|
+
document.getElementById('trendMs').addEventListener('click',e=>{
|
|
988
|
+
if(e.target.tagName!=='BUTTON')return;
|
|
989
|
+
document.querySelectorAll('#trendMs button').forEach(b=>b.classList.remove('on'));
|
|
990
|
+
e.target.classList.add('on');trendMetric=e.target.dataset.m;renderTrend();});
|
|
991
|
+
document.getElementById('trendPf').innerHTML='<button class="'+(trendPlatform==='all'?'on':'')+'" data-m="all">'+T.pf_all+'</button><button class="'+(trendPlatform==='miravia'?'on':'')+'" data-m="miravia">'+T.pf_miravia+'</button><button class="'+(trendPlatform==='ae'?'on':'')+'" data-m="ae">'+T.pf_m2a+'</button>';
|
|
992
|
+
document.getElementById('trendPf').addEventListener('click',e=>{
|
|
993
|
+
if(e.target.tagName!=='BUTTON')return;
|
|
994
|
+
document.querySelectorAll('#trendPf button').forEach(b=>b.classList.remove('on'));
|
|
995
|
+
e.target.classList.add('on');trendPlatform=e.target.dataset.m;renderTrend();});
|
|
996
|
+
// Re-render all dynamic content
|
|
997
|
+
renderAll();
|
|
998
|
+
}
|
|
999
|
+
document.getElementById('langSel').addEventListener('change',function(){switchLang(this.value);});
|
|
1000
|
+
|
|
1001
|
+
function init(){initTF();initPlatformFilters();renderAll();}
|
|
1002
|
+
init();
|
|
1003
|
+
</script>
|
|
1004
|
+
</body>
|
|
1005
|
+
</html>
|