Localize tracker for international audience
This commit is contained in:
+16
-18
@@ -1,5 +1,5 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
@@ -12,14 +12,13 @@
|
||||
@media(prefers-color-scheme:dark){:root{--bg:#0c1220;--panel:#121a2a;--ink:#eef3fb;--muted:#9ba9bd;--line:#28354a;--soft:#182743;--shadow:none}}
|
||||
</style>
|
||||
</head>
|
||||
<body><div id="login" class="login-shell"><form id="loginForm" class="login-card" autocomplete="on"><div class="eyebrow">Restricted Report</div><h1>登录查看报告</h1><p>请输入获授权的邮箱与密码。</p><label class="field">邮箱<input id="email" type="email" autocomplete="username" required></label><label class="field">密码<input id="password" type="password" autocomplete="current-password" required></label><button id="loginButton" type="submit">登录</button><div id="loginError" class="login-error" role="alert" aria-live="polite"></div><p class="access-note">仅授权名单内的邮箱可以访问。</p></form></div><main id="report" hidden>
|
||||
<header class="hero"><div class="eyebrow">Daily Delivery · ZA</div><h1>Amazon - ZA daily tracker</h1><p>包名:com.amazon.mShop.android.shopping。聚焦 repostback 交付、E1+E2 转化与 Midas / Payable spend。</p><div class="status"><span class="dot"></span><span id="range">正在载入数据…</span></div></header>
|
||||
<section><h2>核心指标</h2><div class="summary" id="cards"></div></section>
|
||||
<section><h2>8 月预算消耗</h2><div id="budgetText" class="takeaway"></div><div class="summary" id="budgetCards" style="margin-top:14px"></div><div class="panel"><div class="chart-title">月度预算使用进度</div><div class="chart-sub">预算按 E1+E2 × 49.50 ZAR 扣减</div><div class="progress"><div id="budgetProgress" class="progress-fill"></div></div><div class="progress-labels"><span id="budgetUsed"></span><span id="budgetTotal"></span></div></div></section>
|
||||
<section><h2>每日交付与转化</h2><div class="takeaway"><strong>如何阅读:</strong>Install 仅取交付 repostback,不包含 app open;消耗趋势对比每日 Midas 与 Payable spend,转化趋势用于定位 Event rate 波动。</div><div class="panel chart"><div class="chart-head"><div class="chart-title">本月每日消耗趋势</div><div class="chart-sub">USD;Midas spend 与 Payable spend</div></div><div id="spendTrend"></div><div class="legend"><span><i class="swatch" style="background:var(--blue)"></i>Midas spend</span><span><i class="swatch" style="background:var(--gold)"></i>Payable spend</span></div></div><div class="charts"><div class="panel chart"><div class="chart-head"><div class="chart-title">每日 Install</div><div class="chart-sub">仅取 delivery repostback,排除 af_app_opened</div></div><div id="installChart" class="bars"></div></div><div class="panel chart"><div class="chart-head"><div class="chart-title">E1+E2% 趋势</div><div class="chart-sub">(Event1 + Event2) ÷ Install</div></div><div id="rateTrend"></div></div></div></section>
|
||||
<section><h2>8 月每日明细</h2><div class="panel table-wrap"><table><thead><tr><th>日期</th><th>Install</th><th>Event1</th><th>Event2</th><th>Total</th><th>Event rate</th><th>Payable (ZAR)</th><th>日预算</th><th>FFR</th><th>Midas (USD)</th></tr></thead><tbody id="rows"></tbody></table><div class="footer-note">口径:Install 仅取交付 repostback;Total = E1+E2;FFR = Total ÷ Daily cap。Event rate 较前一日波动 ≥5 个百分点时突出显示;灰色日期为数据截止日之后。</div></div></section>
|
||||
<section><h2>自然周汇总</h2><div class="panel table-wrap"><table><thead><tr><th>自然周(周一至周日)</th><th>Install</th><th>Event1+2</th><th>Event1+2%</th><th>Payable spend</th><th>Midas spend</th></tr></thead><tbody id="weeklyRows"></tbody></table><div class="footer-note">金额为 USD;首周和末周按 8 月自然月边界截取,当前周统计至数据截止日。</div></div></section>
|
||||
<section><h2>建议关注</h2><div class="panel"><ul><li>优先关注 FFR 明显低于 100% 的日期,判断是流量不足、事件率下降还是日 cap 执行问题。</li><li>剩余预算需要的日均消耗已高于当前日预算;如目标是完整消耗月预算,需要确认是否提高每日 cap。</li><li>对比 Payable spend 与 Midas spend 的日级差额,排查差额扩大日期的事件质量和计费口径。</li></ul></div></section>
|
||||
<body><div id="login" class="login-shell"><form id="loginForm" class="login-card" autocomplete="on"><div class="eyebrow">Restricted Report</div><h1>Sign in to view</h1><p>Enter your authorized email and password.</p><label class="field">Email<input id="email" type="email" autocomplete="username" required></label><label class="field">Password<input id="password" type="password" autocomplete="current-password" required></label><button id="loginButton" type="submit">Sign in</button><div id="loginError" class="login-error" role="alert" aria-live="polite"></div><p class="access-note">Access is limited to authorized email addresses.</p></form></div><main id="report" hidden>
|
||||
<header class="hero"><div class="eyebrow">Daily Delivery · ZA</div><h1>Amazon - ZA daily tracker</h1><div class="status"><span class="dot"></span><span id="range">Loading data…</span></div></header>
|
||||
<section><h2>Key Metrics</h2><div class="summary" id="cards"></div></section>
|
||||
<section><h2>August Budget Pacing</h2><div id="budgetText" class="takeaway"></div><div class="summary" id="budgetCards" style="margin-top:14px"></div><div class="panel"><div class="chart-title">Monthly Budget Utilization</div><div class="chart-sub">Payable spend = (E1 + E2) × $3, converted at 16.50 ZAR/USD</div><div class="progress"><div id="budgetProgress" class="progress-fill"></div></div><div class="progress-labels"><span id="budgetUsed"></span><span id="budgetTotal"></span></div></div></section>
|
||||
<section><h2>Daily Delivery & Conversion</h2><div class="panel chart"><div class="chart-head"><div class="chart-title">Daily Spend Trend</div><div class="chart-sub">USD · Midas spend vs. payable spend</div></div><div id="spendTrend"></div><div class="legend"><span><i class="swatch" style="background:var(--blue)"></i>Midas spend</span><span><i class="swatch" style="background:var(--gold)"></i>Payable spend</span></div></div><div class="panel chart"><div class="chart-head"><div class="chart-title">E1+E2% Trend</div><div class="chart-sub">(Event1 + Event2) ÷ Install</div></div><div id="rateTrend"></div></div></section>
|
||||
<section><h2>August Daily Detail</h2><div class="panel table-wrap"><table><thead><tr><th>Date</th><th>Install</th><th>Event1</th><th>Event2</th><th>Total</th><th>Event rate</th><th>Payable (USD)</th><th>Daily budget</th><th>FFR</th><th>Midas (USD)</th></tr></thead><tbody id="rows"></tbody></table><div class="footer-note">Definitions: Install includes delivery repostbacks only; Total = E1+E2; Payable spend = Total × $3; FFR = Total ÷ daily cap. Event-rate changes of ≥5 percentage points versus the previous day are highlighted. Grey rows are after the data cutoff.</div></div></section>
|
||||
<section><h2>Calendar Week Summary</h2><div class="panel table-wrap"><table><thead><tr><th>Calendar week (Mon–Sun)</th><th>Install</th><th>Event1+2</th><th>Event1+2%</th><th>Payable spend</th><th>Midas spend</th></tr></thead><tbody id="weeklyRows"></tbody></table><div class="footer-note">Amounts are in USD. The first and last weeks are clipped to August; the current week runs through the data cutoff.</div></div></section>
|
||||
</main><script>
|
||||
const fmt=n=>new Intl.NumberFormat('en-US').format(n), money=n=>'$'+new Intl.NumberFormat('en-US',{minimumFractionDigits:2,maximumFractionDigits:2}).format(n), pct=n=>n==null?'—':n.toFixed(2)+'%';
|
||||
const label=d=>d.slice(5).replace('-','/');
|
||||
@@ -29,17 +28,16 @@ const ACCESS={emails:['a91dc567bcdb6d054cf121d7e4016454ee3d316e3b452b851ae4d3087
|
||||
async function sha256(value){const bytes=new TextEncoder().encode(value);const digest=await crypto.subtle.digest('SHA-256',bytes);return [...new Uint8Array(digest)].map(x=>x.toString(16).padStart(2,'0')).join('')}
|
||||
function showReport(){document.getElementById('login').hidden=true;document.getElementById('report').hidden=false;loadReport()}
|
||||
async function loadReport(){fetch('data.json',{cache:'no-store'}).then(r=>{if(!r.ok)throw Error('data.json '+r.status);return r.json()}).then(data=>{
|
||||
const t=data.totals,b=data.budget,all=data.daily,d=all.filter(x=>x.is_actual);document.getElementById('range').textContent=`2026-08-01 至 2026-08-31 · 数据截至 ${data.meta.data_cutoff}`;
|
||||
const cards=[['Install',fmt(t.install),'仅 delivery repostback;排除 app open'],['E1 + E2',fmt(t.e1e2),`${fmt(t.event1)} E1 · ${fmt(t.event2)} E2`],['E1+E2%',pct(t.e1e2_rate),'累计 E1+E2 ÷ 累计 Install'],['Payable spend',money(t.payable_spend),`Midas spend ${money(t.midas_spend)}`]];document.getElementById('cards').innerHTML=cards.map(x=>`<div class="card"><div class="label">${x[0]}</div><div class="value">${x[1]}</div><div class="note">${x[2]}</div></div>`).join('');
|
||||
const budgetCards=[['月度预算','R '+fmt(b.monthly_budget_zar),money(b.monthly_budget_usd)],['已用预算','R '+fmt(b.used_budget_zar),`${pct(b.used_rate)} used · MTD expected FFR ${pct(b.mtd_expected_ffr)}`],['剩余预算','R '+fmt(b.remaining_budget_zar),money(b.remaining_budget_usd)],['剩余日均需求','R '+fmt(b.required_daily_budget_zar),`${fmt(b.required_daily_events)} E1+E2 / day`]];document.getElementById('budgetCards').innerHTML=budgetCards.map(x=>`<div class="card"><div class="label">${x[0]}</div><div class="value">${x[1]}</div><div class="note">${x[2]}</div></div>`).join('');
|
||||
document.getElementById('budgetText').innerHTML=`<strong>预算消耗低于截至当前日期的均匀进度。</strong> 已用 R ${fmt(b.used_budget_zar)},同期预算应为 R ${fmt(b.expected_budget_to_date_zar)};剩余 ${b.remaining_days} 天需要日均 R ${fmt(b.required_daily_budget_zar)},高于当前日预算 R ${fmt(b.daily_budget_zar)}。`;
|
||||
document.getElementById('budgetProgress').style.width=Math.min(b.used_rate,100)+'%';document.getElementById('budgetUsed').textContent=`已用 ${pct(b.used_rate)} · R ${fmt(b.used_budget_zar)}`;document.getElementById('budgetTotal').textContent=`总预算 R ${fmt(b.monthly_budget_zar)}`;
|
||||
const t=data.totals,b=data.budget,all=data.daily,d=all.filter(x=>x.is_actual);document.getElementById('range').textContent=`Aug 1–31, 2026 · Data through ${data.meta.data_cutoff}`;
|
||||
const cards=[['Install',fmt(t.install),'Delivery repostbacks only · app opens excluded'],['E1 + E2',fmt(t.e1e2),`${fmt(t.event1)} E1 · ${fmt(t.event2)} E2`],['E1+E2%',pct(t.e1e2_rate),'Cumulative E1+E2 ÷ cumulative Install'],['Payable spend',money(t.payable_spend),`Midas spend ${money(t.midas_spend)}`]];document.getElementById('cards').innerHTML=cards.map(x=>`<div class="card"><div class="label">${x[0]}</div><div class="value">${x[1]}</div><div class="note">${x[2]}</div></div>`).join('');
|
||||
const budgetCards=[['Monthly budget','R '+fmt(b.monthly_budget_zar),money(b.monthly_budget_usd)],['Budget used','R '+fmt(b.used_budget_zar),`${pct(b.used_rate)} used · MTD expected FFR ${pct(b.mtd_expected_ffr)}`],['Budget remaining','R '+fmt(b.remaining_budget_zar),money(b.remaining_budget_usd)],['Required daily run rate','R '+fmt(b.required_daily_budget_zar),`${fmt(b.required_daily_events)} E1+E2 / day`]];document.getElementById('budgetCards').innerHTML=budgetCards.map(x=>`<div class="card"><div class="label">${x[0]}</div><div class="value">${x[1]}</div><div class="note">${x[2]}</div></div>`).join('');
|
||||
document.getElementById('budgetText').innerHTML=`<strong>Budget consumption is below even pacing through the current date.</strong> R ${fmt(b.used_budget_zar)} has been used versus an expected R ${fmt(b.expected_budget_to_date_zar)}. With ${b.remaining_days} days remaining, the required daily run rate is R ${fmt(b.required_daily_budget_zar)}, above the current daily budget of R ${fmt(b.daily_budget_zar)}.`;
|
||||
document.getElementById('budgetProgress').style.width=Math.min(b.used_rate,100)+'%';document.getElementById('budgetUsed').textContent=`Used ${pct(b.used_rate)} · R ${fmt(b.used_budget_zar)}`;document.getElementById('budgetTotal').textContent=`Total budget R ${fmt(b.monthly_budget_zar)}`;
|
||||
lineChart(document.getElementById('spendTrend'),d,[{key:'midas_spend',name:'Midas spend',color:'var(--blue)'},{key:'payable_spend',name:'Payable spend',color:'var(--gold)'}],v=>'$'+Math.round(v));
|
||||
bars(document.getElementById('installChart'),d,[{key:'install',color:'var(--blue)',format:fmt}],Math.max(...d.map(x=>x.install),1));
|
||||
lineChart(document.getElementById('rateTrend'),d,[{key:'e1e2_rate',name:'E1+E2%',color:'var(--orange)'}],v=>v.toFixed(0)+'%');
|
||||
document.getElementById('rows').innerHTML=all.map((x,i)=>{const prev=i?all[i-1]:null,spike=x.is_actual&&prev&&prev.is_actual&&Math.abs((x.e1e2_rate||0)-(prev.e1e2_rate||0))>=5;return `<tr class="${x.is_actual?'':'future'}"><td>${x.date}</td><td>${fmt(x.install)}</td><td>${fmt(x.event1)}</td><td>${fmt(x.event2)}</td><td>${fmt(x.e1e2)}</td><td class="${spike?'rate-spike':''}">${pct(x.e1e2_rate)}</td><td>R ${fmt(x.payable_spend_zar)}</td><td>R ${fmt(x.daily_budget_zar)}</td><td>${pct(x.ffr_rate)}</td><td>${money(x.midas_spend)}</td></tr>`}).join('');
|
||||
document.getElementById('rows').innerHTML=all.map((x,i)=>{const prev=i?all[i-1]:null,spike=x.is_actual&&prev&&prev.is_actual&&Math.abs((x.e1e2_rate||0)-(prev.e1e2_rate||0))>=5;return `<tr class="${x.is_actual?'':'future'}"><td>${x.date}</td><td>${fmt(x.install)}</td><td>${fmt(x.event1)}</td><td>${fmt(x.event2)}</td><td>${fmt(x.e1e2)}</td><td class="${spike?'rate-spike':''}">${pct(x.e1e2_rate)}</td><td>${money(x.payable_spend)}</td><td>R ${fmt(x.daily_budget_zar)}</td><td>${pct(x.ffr_rate)}</td><td>${money(x.midas_spend)}</td></tr>`}).join('');
|
||||
document.getElementById('weeklyRows').innerHTML=data.weekly.map(w=>`<tr><td>${w.label}</td><td>${fmt(w.install)}</td><td>${fmt(w.e1e2)}</td><td>${pct(w.e1e2_rate)}</td><td>${money(w.payable_spend)}</td><td>${money(w.midas_spend)}</td></tr>`).join('');
|
||||
}).catch(e=>{document.getElementById('range').textContent='数据载入失败:'+e.message})}
|
||||
document.getElementById('loginForm').addEventListener('submit',async e=>{e.preventDefault();const button=document.getElementById('loginButton'),error=document.getElementById('loginError');button.disabled=true;error.textContent='';try{const email=document.getElementById('email').value.trim().toLowerCase(),password=document.getElementById('password').value;const [emailHash,passwordHash]=await Promise.all([sha256(email),sha256(ACCESS.salt+':'+password)]);if(ACCESS.emails.includes(emailHash)&&passwordHash===ACCESS.password){sessionStorage.setItem('amazonZaAccess','granted');showReport()}else{error.textContent='邮箱或密码不正确。'}}catch(_){error.textContent='当前浏览器无法完成验证,请更换现代浏览器。'}finally{button.disabled=false}});
|
||||
}).catch(e=>{document.getElementById('range').textContent='Failed to load data: '+e.message})}
|
||||
document.getElementById('loginForm').addEventListener('submit',async e=>{e.preventDefault();const button=document.getElementById('loginButton'),error=document.getElementById('loginError');button.disabled=true;error.textContent='';try{const email=document.getElementById('email').value.trim().toLowerCase(),password=document.getElementById('password').value;const [emailHash,passwordHash]=await Promise.all([sha256(email),sha256(ACCESS.salt+':'+password)]);if(ACCESS.emails.includes(emailHash)&&passwordHash===ACCESS.password){sessionStorage.setItem('amazonZaAccess','granted');showReport()}else{error.textContent='Incorrect email or password.'}}catch(_){error.textContent='This browser cannot complete verification. Please use a modern browser.'}finally{button.disabled=false}});
|
||||
if(sessionStorage.getItem('amazonZaAccess')==='granted')showReport();
|
||||
</script></body></html>
|
||||
|
||||
Reference in New Issue
Block a user