121 lines
6.2 KiB
HTML
121 lines
6.2 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>EG Nagwa Daily Report - August 2026</title>
|
|
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
|
|
<style>
|
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #f0f2f5; color: #333; padding: 24px; }
|
|
.container { max-width: 1200px; margin: 0 auto; }
|
|
h1 { font-size: 22px; color: #1a1a2e; margin-bottom: 4px; }
|
|
.subtitle { color: #666; font-size: 13px; margin-bottom: 24px; }
|
|
.summary-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 14px; margin-bottom: 28px; }
|
|
.card { background: #fff; border-radius: 10px; padding: 16px; box-shadow: 0 1px 6px rgba(0,0,0,0.06); }
|
|
.card .label { font-size: 11px; color: #888; text-transform: uppercase; letter-spacing: 0.5px; }
|
|
.card .value { font-size: 20px; font-weight: 700; margin-top: 4px; }
|
|
.card .value.blue { color: #3b82f6; }
|
|
.card .value.green { color: #10b981; }
|
|
.card .value.orange { color: #f59e0b; }
|
|
.card .value.purple { color: #8b5cf6; }
|
|
.charts { display: grid; grid-template-columns: 1fr; gap: 20px; margin-bottom: 28px; }
|
|
@media(min-width:900px) { .charts { grid-template-columns: 1fr 1fr; } }
|
|
.chart-box { background: #fff; border-radius: 10px; padding: 18px; box-shadow: 0 1px 6px rgba(0,0,0,0.06); }
|
|
.chart-box.full { grid-column: 1 / -1; }
|
|
.chart-box h3 { font-size: 14px; color: #555; margin-bottom: 10px; }
|
|
.table-wrap { background: #fff; border-radius: 10px; padding: 18px; box-shadow: 0 1px 6px rgba(0,0,0,0.06); overflow-x: auto; }
|
|
.table-wrap h3 { font-size: 14px; color: #555; margin-bottom: 10px; }
|
|
table { width: 100%; border-collapse: collapse; font-size: 12px; }
|
|
th, td { padding: 8px 10px; text-align: right; border-bottom: 1px solid #f0f0f0; }
|
|
th { background: #f8fafc; color: #666; font-weight: 600; position: sticky; top: 0; }
|
|
td:first-child, th:first-child { text-align: left; }
|
|
tr:hover td { background: #f5f9ff; }
|
|
.footer { text-align: center; color: #aaa; font-size: 11px; margin-top: 20px; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<h1>EG Nagwa Daily Report</h1>
|
|
<p class="subtitle">Egypt · August 2026 · Install / Enroll Session / Revenue</p>
|
|
<div class="summary-cards" id="summary"></div>
|
|
<div class="charts">
|
|
<div class="chart-box full">
|
|
<h3>Daily Install vs Enroll Session</h3>
|
|
<canvas id="chartMain" height="70"></canvas>
|
|
</div>
|
|
<div class="chart-box">
|
|
<h3>Enroll Session Rate (%)</h3>
|
|
<canvas id="chartRate" height="90"></canvas>
|
|
</div>
|
|
<div class="chart-box">
|
|
<h3>Daily Spend ($)</h3>
|
|
<canvas id="chartSpend" height="90"></canvas>
|
|
</div>
|
|
</div>
|
|
<div class="table-wrap">
|
|
<h3>Daily Detail</h3>
|
|
<table>
|
|
<thead><tr><th>Date</th><th>Install</th><th>Spend</th><th>Enroll Session</th><th>Enroll %</th></tr></thead>
|
|
<tbody id="tbody"></tbody>
|
|
</table>
|
|
</div>
|
|
<p class="footer" id="foot"></p>
|
|
</div>
|
|
<script>
|
|
fetch('data.json?t='+Date.now()).then(r=>r.json()).then(({meta, daily})=>{
|
|
const valid = daily.filter(d=>d.install>0);
|
|
const totalInstall = valid.reduce((s,d)=>s+d.install,0);
|
|
const totalSpend = valid.reduce((s,d)=>s+d.payable_spend,0);
|
|
const totalEnroll = valid.reduce((s,d)=>s+d.enroll_session,0);
|
|
const avgRate = (totalEnroll/totalInstall*100).toFixed(1);
|
|
|
|
document.getElementById('summary').innerHTML = `
|
|
<div class="card"><div class="label">Total Install (MTD)</div><div class="value blue">${totalInstall.toLocaleString()}</div></div>
|
|
<div class="card"><div class="label">Total Enroll Session</div><div class="value green">${totalEnroll.toLocaleString()}</div></div>
|
|
<div class="card"><div class="label">Avg Enroll Rate</div><div class="value orange">${avgRate}%</div></div>
|
|
<div class="card"><div class="label">Total Spend</div><div class="value purple">$${totalSpend.toFixed(2)}</div></div>
|
|
<div class="card"><div class="label">Budget</div><div class="value">$${meta.aug_budget.toLocaleString()}</div></div>
|
|
<div class="card"><div class="label">Budget Left</div><div class="value green">$${meta.left.toLocaleString()}</div></div>
|
|
<div class="card"><div class="label">Budget Used</div><div class="value orange">${meta.spend_pct}</div></div>
|
|
<div class="card"><div class="label">Rec. Daily Install</div><div class="value blue">${meta.rec_daily_install.toLocaleString()}</div></div>
|
|
`;
|
|
|
|
const labels = valid.map(d=>d.date.slice(5));
|
|
const installs = valid.map(d=>d.install);
|
|
const enrolls = valid.map(d=>d.enroll_session);
|
|
const rates = valid.map(d=>d.enroll_session_pct);
|
|
const spends = valid.map(d=>d.payable_spend);
|
|
|
|
new Chart(document.getElementById('chartMain'),{
|
|
type:'line',
|
|
data:{labels, datasets:[
|
|
{label:'Install', data:installs, borderColor:'#3b82f6', backgroundColor:'rgba(59,130,246,0.07)', tension:0.3, fill:true, pointRadius:3},
|
|
{label:'Enroll Session', data:enrolls, borderColor:'#10b981', backgroundColor:'rgba(16,185,129,0.07)', tension:0.3, fill:true, pointRadius:3}
|
|
]},
|
|
options:{responsive:true, plugins:{legend:{position:'top'}}, scales:{y:{beginAtZero:false}}}
|
|
});
|
|
|
|
new Chart(document.getElementById('chartRate'),{
|
|
type:'bar',
|
|
data:{labels, datasets:[{label:'Enroll %', data:rates, backgroundColor:'rgba(245,158,11,0.55)', borderColor:'#f59e0b', borderWidth:1}]},
|
|
options:{responsive:true, plugins:{legend:{display:false}}, scales:{y:{ticks:{callback:v=>v+'%'}}}}
|
|
});
|
|
|
|
new Chart(document.getElementById('chartSpend'),{
|
|
type:'line',
|
|
data:{labels, datasets:[{label:'Spend', data:spends, borderColor:'#8b5cf6', backgroundColor:'rgba(139,92,246,0.08)', tension:0.3, fill:true, pointRadius:3}]},
|
|
options:{responsive:true, plugins:{legend:{display:false}}, scales:{y:{ticks:{callback:v=>'$'+v}}}}
|
|
});
|
|
|
|
const tbody = document.getElementById('tbody');
|
|
valid.slice().reverse().forEach(d=>{
|
|
tbody.innerHTML+=`<tr><td>${d.date}</td><td>${d.install}</td><td>$${d.payable_spend.toFixed(2)}</td><td>${d.enroll_session}</td><td>${d.enroll_session_pct.toFixed(2)}%</td></tr>`;
|
|
});
|
|
|
|
document.getElementById('foot').textContent='Last updated: '+new Date().toLocaleString();
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|