@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.
@@ -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()">&times;</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>