| Line 2342... |
Line 2342... |
| 2342 |
Loading…
|
2342 |
Loading…
|
| 2343 |
</div>
|
2343 |
</div>
|
| 2344 |
</div>
|
2344 |
</div>
|
| 2345 |
</div>
|
2345 |
</div>
|
| 2346 |
|
2346 |
|
| 2347 |
<!-- Partner Billing Count -->
|
2347 |
<!-- Partner Billing Count — unchanged, always current month MTD -->
|
| 2348 |
<div class="sectionlabel">Partner Billing Count <span
|
2348 |
<div class="sectionlabel">Partner Billing Count <span
|
| 2349 |
style="color:var(--soft);font-weight:600;text-transform:none;letter-spacing:0;margin-left:6px">(Targeted partners only · bucketed by MTD ₹)</span>
|
2349 |
style="color:var(--soft);font-weight:600;text-transform:none;letter-spacing:0;margin-left:6px">(Targeted partners only · bucketed by MTD ₹)</span>
|
| 2350 |
</div>
|
2350 |
</div>
|
| 2351 |
<div class="card">
|
2351 |
<div class="card">
|
| 2352 |
<div class="card-hd">
|
2352 |
<div class="card-hd">
|
| Line 2363... |
Line 2363... |
| 2363 |
Loading…
|
2363 |
Loading…
|
| 2364 |
</div>
|
2364 |
</div>
|
| 2365 |
</div>
|
2365 |
</div>
|
| 2366 |
</div>
|
2366 |
</div>
|
| 2367 |
|
2367 |
|
| 2368 |
<!-- RBM Performance Dashboard (MTD) — extracted from /indent/today_po_rbm without any backend change -->
|
2368 |
<!-- RBM Performance Dashboard — JSON-driven, supports historical months via /rbm-drr/performance?month=YYYY-MM -->
|
| 2369 |
<div class="sectionlabel">RBM Performance Dashboard (MTD)</div>
|
2369 |
<div class="sectionlabel">RBM Performance Dashboard</div>
|
| 2370 |
<div class="card">
|
2370 |
<div class="card">
|
| 2371 |
<div class="card-hd">
|
2371 |
<div class="card-hd">
|
| 2372 |
<div>
|
2372 |
<div>
|
| 2373 |
<h2>Composite MTD Ranking</h2>
|
2373 |
<h2>Composite MTD Ranking</h2>
|
| 2374 |
<p class="desc">Rank of RBMs by weighted score across Talk Time (10%) + MTD Achievement (55%) + Aging (2%) +
|
2374 |
<p class="desc">Rank of RBMs by weighted score across Talk Time (10%) + MTD Achievement (55%) + Aging (2%) +
|
| 2375 |
>1L Partners (29%) + Rating (2%) + Partners Rated (2%).</p>
|
2375 |
>1L Partners (29%) + Rating (2%) + Partners Rated (2%).
|
| - |
|
2376 |
<span style="color:var(--soft)"
|
| - |
|
2377 |
title="Aging denominator uses today's warehouse aging snapshot — for past months aging% is approximate.">Aging denominator is a live snapshot.</span>
|
| - |
|
2378 |
</p>
|
| - |
|
2379 |
</div>
|
| - |
|
2380 |
<div style="display:flex;align-items:center;gap:10px">
|
| - |
|
2381 |
<select id="rdrrMonthPicker" class="ctl" style="min-width:170px;height:30px;padding:4px 10px"
|
| - |
|
2382 |
title="Pick a month to view that month's ranking"></select>
|
| - |
|
2383 |
<span class="badge-period" id="rdrrPerfMonth">MTD</span>
|
| 2376 |
</div>
|
2384 |
</div>
|
| 2377 |
<span class="badge-period" id="rdrrPerfMonth">MTD</span>
|
- |
|
| 2378 |
</div>
|
2385 |
</div>
|
| 2379 |
<div id="rdrrPerfWrap">
|
2386 |
<div id="rdrrPerfWrap">
|
| 2380 |
<div class="loading">
|
2387 |
<div class="loading">
|
| 2381 |
<div class="spinner"></div>
|
2388 |
<div class="spinner"></div>
|
| 2382 |
Loading…
|
2389 |
Loading…
|
| Line 3552... |
Line 3559... |
| 3552 |
.replace(/"/g, """).replace(/'/g, "'");
|
3559 |
.replace(/"/g, """).replace(/'/g, "'");
|
| 3553 |
}
|
3560 |
}
|
| 3554 |
|
3561 |
|
| 3555 |
$("#rdrrPmCategory").on("change", loadPurchaseMilestones);
|
3562 |
$("#rdrrPmCategory").on("change", loadPurchaseMilestones);
|
| 3556 |
|
3563 |
|
| 3557 |
// ------------------------- RBM Performance Dashboard (MTD) -------------------------
|
3564 |
// ------------------------- Month picker + RBM Performance Dashboard -------------------------
|
| 3558 |
// No dedicated backend endpoint exists — the perf-dashboard table is only rendered
|
3565 |
// The Composite MTD Ranking card now reads /rbm-drr/performance?month=YYYY-MM so historical
|
| - |
|
3566 |
// months can be viewed. Partner Billing Count re-fetches /rbm-drr/extras?month=YYYY-MM.
|
| 3559 |
// inline in /indent/today_po_rbm. We fetch that page and extract just the table.
|
3567 |
// Aging/Billing extras aging bits are unaffected by the month picker — they stay driven by
|
| - |
|
3568 |
// the top-of-page date range.
|
| - |
|
3569 |
|
| - |
|
3570 |
function fmtSeconds(sec) {
|
| - |
|
3571 |
sec = Math.max(0, Math.round(Number(sec) || 0));
|
| - |
|
3572 |
var h = Math.floor(sec / 3600);
|
| - |
|
3573 |
var m = Math.floor((sec % 3600) / 60);
|
| - |
|
3574 |
if (h > 0) return h + "h " + m + "m";
|
| - |
|
3575 |
return m + "m";
|
| - |
|
3576 |
}
|
| - |
|
3577 |
|
| - |
|
3578 |
function pctCell(pct) {
|
| - |
|
3579 |
var p = Number(pct) || 0;
|
| - |
|
3580 |
return p.toFixed(1) + "%";
|
| - |
|
3581 |
}
|
| - |
|
3582 |
|
| - |
|
3583 |
function renderPerformance(data) {
|
| - |
|
3584 |
var rows = data.rows || [];
|
| - |
|
3585 |
$("#rdrrPerfMonth").text(data.monthLabel || 'MTD');
|
| - |
|
3586 |
if (rows.length === 0) {
|
| - |
|
3587 |
$("#rdrrPerfWrap").html('<div class="empty"><div class="ei">📊</div>No performance dashboard data for this month.</div>');
|
| - |
|
3588 |
return;
|
| - |
|
3589 |
}
|
| - |
|
3590 |
var html = ['<div style="overflow-x:auto"><table class="xtable" id="rdrrPerfTable"><thead><tr>',
|
| - |
|
3591 |
'<th style="width:48px">Rank</th>',
|
| - |
|
3592 |
'<th>RBM</th>',
|
| - |
|
3593 |
'<th class="num">Partners</th>',
|
| - |
|
3594 |
'<th class="num" title="Talk time / expected (10%)">Talk Time<br><small>(10%)</small></th>',
|
| - |
|
3595 |
'<th class="num" title="MTD achieved / monthly target (55%)">MTD Achievement<br><small>(55%)</small></th>',
|
| - |
|
3596 |
'<th class="num" title="Aging sold / warehouse aging stock. Denominator is a live snapshot (2%).">15-Day Aging<br><small>(2%)</small></th>',
|
| - |
|
3597 |
'<th class="num" title="Partners who crossed ₹1L this month (29%)">>1L Partners<br><small>(29%)</small></th>',
|
| - |
|
3598 |
'<th class="num" title="Average partner rating (2%)">Avg Rating<br><small>(2%)</small></th>',
|
| - |
|
3599 |
'<th class="num" title="Partners who rated / total partners (2%)">Partners Rated<br><small>(2%)</small></th>',
|
| - |
|
3600 |
'<th class="num g-total">Score</th>',
|
| - |
|
3601 |
'</tr></thead><tbody>'];
|
| - |
|
3602 |
rows.forEach(function (r) {
|
| - |
|
3603 |
var rankCell;
|
| - |
|
3604 |
if (r.rank === 1) rankCell = '<img src="/resources/images/king.png" style="width:28px" title="Rank 1">';
|
| - |
|
3605 |
else if (r.rank === 2) rankCell = '<img src="/resources/images/trophy-cup.png" style="width:24px" title="Rank 2">';
|
| - |
|
3606 |
else if (r.rank === 3) rankCell = '<img src="/resources/images/bronze-medal.png" style="width:24px" title="Rank 3">';
|
| - |
|
3607 |
else rankCell = String(r.rank);
|
| - |
|
3608 |
html.push('<tr>' +
|
| - |
|
3609 |
'<td class="num">' + rankCell + '</td>' +
|
| - |
|
3610 |
'<td class="rbm-nm">' + escapeHtml(r.rbmName || '-') + '</td>' +
|
| - |
|
3611 |
'<td class="num">' + (Number(r.partnerCount) || 0) + '</td>' +
|
| - |
|
3612 |
'<td class="num">' + fmtSeconds(r.talkSeconds) + '<br><small style="color:var(--soft)">' + pctCell(r.talkPct) + '</small></td>' +
|
| - |
|
3613 |
'<td class="num">' + pctCell(r.drrAchvPct) + '</td>' +
|
| - |
|
3614 |
'<td class="num">' + formatIndian(r.agingUnitsSold) + '<br><small style="color:var(--soft)">' + pctCell(r.agingPct) + '</small></td>' +
|
| - |
|
3615 |
'<td class="num">' + (Number(r.above1LPartnerCount) || 0) + '<br><small style="color:var(--soft)">' + pctCell(r.above1LPct) + '</small></td>' +
|
| 3560 |
// Trade-off: heavier request (server re-runs full page prep). Consider adding a
|
3616 |
'<td class="num">' + (Number(r.partnerRating) || 0).toFixed(2) + '</td>' +
|
| 3561 |
// /rbm-drr/performance fragment endpoint later if this becomes a hot path.
|
3617 |
'<td class="num">' + (Number(r.ratingPartnerCount) || 0) + '</td>' +
|
| - |
|
3618 |
'<td class="num g-total">' + (Number(r.compositeScore) || 0).toFixed(1) + '</td>' +
|
| - |
|
3619 |
'</tr>');
|
| - |
|
3620 |
});
|
| - |
|
3621 |
html.push('</tbody></table></div>');
|
| - |
|
3622 |
$("#rdrrPerfWrap").html(html.join(""));
|
| - |
|
3623 |
}
|
| - |
|
3624 |
|
| 3562 |
function loadPerformanceDashboard() {
|
3625 |
function loadPerformance(monthKey) {
|
| 3563 |
$("#rdrrPerfWrap").html('<div class="loading"><div class="spinner"></div>Loading…</div>');
|
3626 |
$("#rdrrPerfWrap").html('<div class="loading"><div class="spinner"></div>Loading…</div>');
|
| - |
|
3627 |
var qs = monthKey ? ("?month=" + encodeURIComponent(monthKey)) : "";
|
| - |
|
3628 |
fetch("/rbm-drr/performance" + qs, {credentials: "same-origin"})
|
| 3564 |
$.ajax({
|
3629 |
.then(function (r) {
|
| - |
|
3630 |
return r.json().then(function (b) {
|
| 3565 |
url: "/indent/today_po_rbm",
|
3631 |
return {ok: r.ok, body: b};
|
| 3566 |
type: "GET",
|
3632 |
});
|
| 3567 |
dataType: "html",
|
3633 |
})
|
| 3568 |
success: function (fullHtml) {
|
3634 |
.then(function (res) {
|
| 3569 |
var doc = new DOMParser().parseFromString(fullHtml, "text/html");
|
3635 |
if (!res.ok) {
|
| - |
|
3636 |
$("#rdrrPerfWrap").html('<div class="empty">' + escapeHtml(res.body && res.body.error ? res.body.error : "Failed to load.") + '</div>');
|
| - |
|
3637 |
return;
|
| - |
|
3638 |
}
|
| 3570 |
var tbl = doc.querySelector("#performance-dashboard-table");
|
3639 |
renderPerformance(res.body);
|
| 3571 |
if (!tbl) {
|
3640 |
})
|
| - |
|
3641 |
.catch(function (e) {
|
| 3572 |
$("#rdrrPerfWrap").html('<div class="empty"><div class="ei">📊</div>No performance dashboard data available yet.</div>');
|
3642 |
$("#rdrrPerfWrap").html('<div class="empty">Failed to load: ' + (e && e.message ? e.message : "network error") + '</div>');
|
| 3573 |
return;
|
3643 |
});
|
| 3574 |
}
|
3644 |
}
|
| - |
|
3645 |
|
| 3575 |
// Fix any relative image paths (e.g. king.png / trophy-cup.png) to be root-relative.
|
3646 |
// Note: variable is named `sel` without the leading dollar on purpose.
|
| 3576 |
tbl.querySelectorAll("img").forEach(function (im) {
|
3647 |
// Velocity would parse a leading-dollar name as a template reference and
|
| 3577 |
var src = im.getAttribute("src") || "";
|
3648 |
// choke on the embedded strings in the body.
|
| - |
|
3649 |
function populateMonthPicker() {
|
| 3578 |
if (src.indexOf("resources/") !== -1 && src.charAt(0) !== "/") {
|
3650 |
var sel = $("#rdrrMonthPicker");
|
| 3579 |
im.setAttribute("src", "/" + src.replace(/^\.\.\/+/, ""));
|
3651 |
if (sel.length === 0 || sel.data("populated")) return;
|
| 3580 |
}
|
3652 |
var now = new Date();
|
| 3581 |
});
|
3653 |
var opts = [];
|
| - |
|
3654 |
for (var i = 0; i < 6; i++) {
|
| 3582 |
$("#rdrrPerfWrap").empty().append(tbl);
|
3655 |
var d = new Date(now.getFullYear(), now.getMonth() - i, 1);
|
| 3583 |
},
|
3656 |
var y = d.getFullYear();
|
| - |
|
3657 |
var m = String(d.getMonth() + 1).padStart(2, "0");
|
| 3584 |
error: function () {
|
3658 |
var key = y + "-" + m;
|
| - |
|
3659 |
var label = d.toLocaleString("en-US", {month: "long", year: "numeric"}) + (i === 0 ? " (current)" : "");
|
| 3585 |
$("#rdrrPerfWrap").html('<div class="empty">Failed to load performance dashboard.</div>');
|
3660 |
opts.push('<option value="' + key + '">' + label + '</option>');
|
| 3586 |
}
|
3661 |
}
|
| - |
|
3662 |
sel.html(opts.join(""));
|
| - |
|
3663 |
sel.data("populated", true);
|
| - |
|
3664 |
sel.on("change", function () {
|
| - |
|
3665 |
loadPerformance($(this).val());
|
| 3587 |
});
|
3666 |
});
|
| 3588 |
}
|
3667 |
}
|
| 3589 |
|
3668 |
|
| 3590 |
// ------------------------- RBM Call Target iframe -------------------------
|
3669 |
// ------------------------- RBM Call Target iframe -------------------------
|
| 3591 |
// The /indent/rbm_call_target endpoint returns a Velocity FRAGMENT (no <html>,
|
3670 |
// The /indent/rbm_call_target endpoint returns a Velocity FRAGMENT (no <html>,
|
| Line 3860... |
Line 3939... |
| 3860 |
}
|
3939 |
}
|
| 3861 |
});
|
3940 |
});
|
| 3862 |
|
3941 |
|
| 3863 |
// initial load
|
3942 |
// initial load
|
| 3864 |
loadFilterOptions();
|
3943 |
loadFilterOptions();
|
| - |
|
3944 |
populateMonthPicker();
|
| 3865 |
load();
|
3945 |
load();
|
| 3866 |
loadPurchaseMilestones();
|
3946 |
loadPurchaseMilestones();
|
| 3867 |
loadPerformanceDashboard();
|
3947 |
loadPerformance($("#rdrrMonthPicker").val() || null);
|
| 3868 |
loadCallTargetIframe();
|
3948 |
loadCallTargetIframe();
|
| 3869 |
})();
|
3949 |
})();
|
| 3870 |
</script>
|
3950 |
</script>
|
| 3871 |
</body>
|
3951 |
</body>
|
| 3872 |
</html>
|
3952 |
</html>
|