Rev 37460 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>RBM DRR Dashboard</title><style>:root {--bg: #f4f6fb;--card: #ffffff;--ink: #141a2b;--muted: #5d6a83;--soft: #97a1b5;--line: #e6e9f1;--line2: #eef1f6;--brand: #3b5bdb;--brand2: #5c7cfa;--teal: #0ca678;--cyan: #1098ad;--amber: #f59f00;--orange: #e8590c;--red: #e03131;--violet: #7048e8;--pink: #d6336c;--good: #2f9e44;--warn: #e8590c;--bad: #e03131;--hid: #7048e8;--fast: #3b5bdb;--run: #0ca678;--slow: #e8590c;--other: #868e96;--hid-bg: #f2edff;--fast-bg: #eaf0ff;--run-bg: #e5f7ee;--slow-bg: #ffefe0;--other-bg: #eef0f4;}* {box-sizing: border-box}html, body {margin: 0;background: var(--bg);color: var(--ink);font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;-webkit-font-smoothing: antialiased;font-size: 14px}.wrap {max-width: 1440px;margin: 0 auto;padding: 22px 26px 100px}a {color: var(--brand);text-decoration: none}a:hover {text-decoration: underline}/* header */header.top {display: flex;align-items: flex-end;justify-content: space-between;gap: 18px;flex-wrap: wrap;margin-bottom: 20px}.brand .crumb {font-size: 12px;color: var(--soft);font-weight: 600}.brand h1 {margin: 4px 0 0;font-size: 26px;font-weight: 770;letter-spacing: -.015em;display: flex;align-items: center;gap: 12px}.brand h1 .live {font-size: 11px;font-weight: 700;color: var(--teal);background: #e6fcf5;border: 1px solid #c3fae8;border-radius: 999px;padding: 3px 10px}.brand h1 .live i {display: inline-block;width: 7px;height: 7px;border-radius: 50%;background: var(--teal);margin-right: 5px;animation: pulse 1.6s infinite;vertical-align: middle}@keyframes pulse {0%, 100% {opacity: 1}50% {opacity: .25}}.hd-right {display: flex;align-items: center;gap: 10px;flex-wrap: wrap}.ctl {background: var(--card);border: 1px solid var(--line);border-radius: 10px;padding: 9px 13px;font-size: 13px;color: var(--ink);font-weight: 600;box-shadow: 0 1px 2px rgba(20, 30, 60, .04)}input.ctl {appearance: none;cursor: pointer;height: 38px;min-width: 240px}.btn-r {background: var(--brand);border: 0;color: #fff;font-weight: 670;padding: 9px 18px;border-radius: 10px;cursor: pointer;font-size: 13px;height: 38px;box-shadow: 0 1px 2px rgba(59, 91, 219, .35);transition: transform .1s, box-shadow .1s}.btn-r:hover {background: var(--brand2);box-shadow: 0 3px 10px rgba(59, 91, 219, .35)}.btn-r:active {transform: translateY(1px)}/* KPI cards */.kpis {display: grid;grid-template-columns:repeat(4, 1fr);gap: 14px;margin-bottom: 22px}.kpi {position: relative;background: var(--card);border: 1px solid var(--line);border-radius: 14px;padding: 16px 18px;box-shadow: 0 1px 2px rgba(20, 30, 60, .05);display: flex;flex-direction: column;min-height: 118px;overflow: hidden}.kpi::before {content: "";position: absolute;top: 0;left: 0;height: 3px;width: 100%;border-top-left-radius: 14px;border-top-right-radius: 14px}.kpi.a::before {background: linear-gradient(90deg, var(--brand), var(--brand2))}.kpi.b::before {background: linear-gradient(90deg, var(--teal), #20c997)}.kpi.c::before {background: linear-gradient(90deg, var(--amber), #ffc078)}.kpi.d::before {background: linear-gradient(90deg, var(--violet), #9775fa)}.kpi .top {display: flex;align-items: center;gap: 10px}.kpi .ic {width: 34px;height: 34px;border-radius: 10px;display: flex;align-items: center;justify-content: center;flex: none;color: #fff;box-shadow: 0 2px 6px rgba(0, 0, 0, .08)}.kpi.a .ic {background: linear-gradient(135deg, var(--brand), var(--brand2))}.kpi.b .ic {background: linear-gradient(135deg, var(--teal), #20c997)}.kpi.c .ic {background: linear-gradient(135deg, var(--amber), #ffc078);color: #3a2500}.kpi.d .ic {background: linear-gradient(135deg, var(--violet), #9775fa)}.kpi .ic svg {width: 18px;height: 18px}.kpi .label {font-size: 12px;color: var(--muted);font-weight: 650;line-height: 1.2;letter-spacing: .005em}.kpi .val {font-size: 28px;font-weight: 780;margin-top: auto;padding-top: 10px;letter-spacing: -.02em;line-height: 1;font-variant-numeric: tabular-nums}.kpi .val .u {font-size: 13px;color: var(--soft);font-weight: 600;margin-left: 3px}.kpi .foot {font-size: 11.5px;color: var(--soft);margin-top: 8px;font-weight: 500}.kpi .val .pct {margin-left: 0;font-size: 20px;padding: 4px 12px}.kpi.clickable {cursor: pointer;transition: transform .1s, box-shadow .12s, border-color .12s}.kpi.clickable:hover {transform: translateY(-2px);box-shadow: 0 6px 18px rgba(20, 30, 60, .08);border-color: #c8d2f4}.kpi.clickable .hint {position: absolute;bottom: 8px;right: 12px;color: var(--soft);font-size: 11px;font-weight: 600;opacity: .7}.kpi.clickable:hover .hint {opacity: 1;color: var(--brand)}/* Cards & section labels */.card {background: var(--card);border: 1px solid var(--line);border-radius: 15px;padding: 18px 20px;margin-bottom: 16px;box-shadow: 0 1px 2px rgba(20, 30, 60, .04)}.card .card-hd {display: flex;align-items: baseline;justify-content: space-between;gap: 14px;margin-bottom: 14px}.card h2 {margin: 0;font-size: 16px;font-weight: 730;letter-spacing: -.01em}.card .desc {color: var(--muted);font-size: 12.5px;margin: 2px 0 0}.card .badge-period {background: #eef2ff;border: 1px solid #d7defb;color: var(--brand);border-radius: 999px;padding: 3px 12px;font-size: 12px;font-weight: 650;letter-spacing: .01em}.sectionlabel {font-size: 11px;letter-spacing: .14em;text-transform: uppercase;color: var(--soft);font-weight: 700;margin: 20px 2px 10px}/* Legend under section label */.legend {display: flex;gap: 14px;flex-wrap: wrap;margin: 0 2px 10px;align-items: center;color: var(--soft);font-size: 11.5px;font-weight: 600}.legend .lg {display: inline-flex;align-items: center;gap: 5px}.legend .lg i {width: 8px;height: 8px;border-radius: 50%;display: inline-block}.legend .lg.hid i {background: var(--hid)}.legend .lg.fast i {background: var(--fast)}.legend .lg.run i {background: var(--run)}.legend .lg.slow i {background: var(--slow)}.legend .lg.other i {background: var(--other)}.legend .lg .lgt {color: var(--muted)}/* Table */table {width: 100%;border-collapse: separate;border-spacing: 0;font-size: 13px}th, td {text-align: left;padding: 11px 10px;border-bottom: 1px solid var(--line2)}thead th {font-size: 10.5px;text-transform: uppercase;letter-spacing: .06em;color: var(--soft);font-weight: 700;white-space: nowrap;user-select: none;position: sticky;top: 0;background: #fff;z-index: 1;border-bottom: 1px solid var(--line)}th.num, td.num {text-align: right}th.g-hid {color: var(--hid);border-top: 3px solid var(--hid)}th.g-fast {color: var(--fast);border-top: 3px solid var(--fast)}th.g-run {color: var(--run);border-top: 3px solid var(--run)}th.g-slow {color: var(--slow);border-top: 3px solid var(--slow)}th.g-other {color: var(--other);border-top: 3px solid var(--other)}th.g-total {color: var(--brand);border-top: 3px solid var(--brand)}tbody tr:hover {background: #f6f8fd}tbody tr.clickable {cursor: pointer;transition: background .1s}tbody tr.clickable:hover {background: #eef2ff}tr.totalrow {background: #fafbfe;border-top: 2px solid var(--line)}tr.totalrow td {font-weight: 770;color: var(--ink)}tr.totalrow .cell-val {font-size: 14px}.rbm-cell {display: flex;flex-direction: column;gap: 2px}.rbm-cell .rn {font-weight: 700;color: var(--ink);font-size: 13.5px}.rbm-cell .wh {font-size: 11.5px;color: var(--muted);font-weight: 600}.cell-val {font-variant-numeric: tabular-nums;font-weight: 650;color: var(--ink);white-space: nowrap}.cell-val small {color: var(--soft);font-weight: 500;margin-left: 3px}.pct {display: inline-flex;align-items: center;justify-content: center;min-width: 56px;padding: 3px 9px;border-radius: 7px;font-size: 11.5px;font-weight: 700;text-align: center;font-variant-numeric: tabular-nums;line-height: 1.35}.pct.good {background: #e5f7ee;color: #177a3a}.pct.warn {background: #fff0e0;color: #a04600}.pct.bad {background: #ffeaea;color: #b8272b}.pct.na {background: transparent;color: var(--soft);font-weight: 600;min-width: auto;padding: 0}.bar {height: 5px;border-radius: 3px;background: #eef1f6;overflow: hidden;margin-top: 6px}.bar > i {display: block;height: 100%;border-radius: 3px;transition: width .3s ease}.bar > i.good {background: linear-gradient(90deg, #51cf66, #2f9e44)}.bar > i.warn {background: linear-gradient(90deg, #ffa94d, #e8590c)}.bar > i.bad {background: linear-gradient(90deg, #ff8787, #e03131)}.chev {color: #c5cce0;font-size: 16px;padding-left: 6px;transition: transform .12s, color .12s}tr.clickable:hover .chev {color: var(--brand);transform: translateX(2px)}.loading {padding: 70px;text-align: center;color: var(--muted)}.spinner {width: 32px;height: 32px;border: 3px solid #e3e8f5;border-top-color: var(--brand);border-radius: 50%;animation: spin 1s linear infinite;margin: 0 auto 12px}@keyframes spin {to {transform: rotate(360deg)}}.empty {padding: 56px 20px;text-align: center;color: var(--soft);font-size: 13.5px}.empty .ei {font-size: 36px;margin-bottom: 8px;opacity: .5}.foot-note {color: var(--soft);font-size: 11.5px;margin-top: 14px;padding: 10px 12px;background: #fafbfe;border: 1px dashed var(--line);border-radius: 10px;line-height: 1.5}.foot-note code {background: #eef1f6;color: var(--muted);padding: 1px 5px;border-radius: 4px;font-size: 11px}/* drill-down panel */.rdrr-back {position: fixed;inset: 0;background: rgba(15, 22, 40, .5);backdrop-filter: blur(2px);z-index: 1200;display: none}.rdrr-back.show {display: block}.rdrr-panel {position: absolute;top: 0;right: 0;height: 100%;width: min(760px, 96vw);background: #fff;box-shadow: -12px 0 40px rgba(20, 30, 60, .22);display: flex;flex-direction: column;padding: 24px 26px 20px;transform: translateX(100%);transition: transform .22s ease;overflow-y: auto}.rdrr-back.show .rdrr-panel {transform: translateX(0)}.rdrr-panel .x {position: absolute;top: 16px;right: 18px;cursor: pointer;color: var(--soft);font-size: 24px;line-height: 1;border: 0;background: none;width: 32px;height: 32px;border-radius: 8px;transition: background .1s, color .1s}.rdrr-panel .x:hover {background: #f1f3f9;color: var(--ink)}.rdrr-panel h3 {margin: 0 0 3px;font-size: 19px;font-weight: 750;letter-spacing: -.01em;padding-right: 40px}.rdrr-panel > .desc {margin: 0 0 18px;font-size: 12.5px;color: var(--muted);font-weight: 600}.rdrr-total {display: flex;gap: 14px;padding: 14px 16px;background: linear-gradient(135deg, #f8f9fc, #eef2ff);border: 1px solid #dbe2f6;border-radius: 12px;margin-bottom: 18px}.rdrr-total > div {flex: 1;text-align: center;border-right: 1px solid #dbe2f6;padding: 0 4px}.rdrr-total > div:last-child {border-right: 0}.rdrr-total .k {font-size: 10.5px;color: var(--muted);text-transform: uppercase;letter-spacing: .06em;font-weight: 700}.rdrr-total .v {font-size: 20px;font-weight: 770;color: var(--ink);font-variant-numeric: tabular-nums;margin-top: 5px;letter-spacing: -.01em}.rdrr-total .v .pct {font-size: 16px;padding: 3px 10px;margin-top: 0}.bbars {display: flex;flex-direction: column;gap: 10px;margin-bottom: 20px}.bbar {display: grid;grid-template-columns:82px 1fr 145px 60px;gap: 14px;align-items: center;padding: 12px 14px;background: #fff;border: 1px solid var(--line);border-radius: 11px;transition: box-shadow .12s}.bbar:hover {box-shadow: 0 3px 10px rgba(20, 30, 60, .06)}.bbar .bname {font-size: 11.5px;font-weight: 750;color: var(--ink);letter-spacing: .04em;text-transform: uppercase;display: flex;align-items: center;gap: 6px}.bbar .bname i {display: inline-block;width: 8px;height: 8px;border-radius: 50%}.bbar.hid .bname {color: var(--hid)}.bbar.fast .bname {color: var(--fast)}.bbar.run .bname {color: var(--run)}.bbar.slow .bname {color: var(--slow)}.bbar.other .bname {color: var(--other)}.bbar.hid .bname i {background: var(--hid)}.bbar.fast .bname i {background: var(--fast)}.bbar.run .bname i {background: var(--run)}.bbar.slow .bname i {background: var(--slow)}.bbar.other .bname i {background: var(--other)}.bbar .btrack {height: 14px;border-radius: 8px;overflow: hidden;position: relative}.bbar.hid .btrack {background: var(--hid-bg)}.bbar.fast .btrack {background: var(--fast-bg)}.bbar.run .btrack {background: var(--run-bg)}.bbar.slow .btrack {background: var(--slow-bg)}.bbar.other .btrack {background: var(--other-bg)}.bbar .btrack > i {display: block;height: 100%;border-radius: 8px;transition: width .3s ease;position: relative}.bbar.hid .btrack > i {background: linear-gradient(90deg, #9775fa, var(--hid))}.bbar.fast .btrack > i {background: linear-gradient(90deg, var(--brand2), var(--fast))}.bbar.run .btrack > i {background: linear-gradient(90deg, #20c997, var(--run))}.bbar.slow .btrack > i {background: linear-gradient(90deg, #ffa94d, var(--slow))}.bbar.other .btrack > i {background: linear-gradient(90deg, #adb5bd, var(--other))}.bbar .btgt {font-size: 12px;color: var(--muted);font-variant-numeric: tabular-nums;font-weight: 600;text-align: right}.bbar .btgt b {color: var(--ink);font-weight: 700}.bbar .bpct {font-size: 13px;font-weight: 750;text-align: right;font-variant-numeric: tabular-nums}.bbar .bpct.good {color: var(--good)}.bbar .bpct.warn {color: var(--warn)}.bbar .bpct.bad {color: var(--bad)}.bbar .bpct.na {color: var(--soft);font-weight: 600}/* brand mix bars in drill-down */.brands {display: flex;flex-direction: column;gap: 8px;margin-bottom: 20px}.brand-card {background: #fff;border: 1px solid var(--line);border-radius: 11px;overflow: hidden;transition: box-shadow .12s}.brand-card:hover {box-shadow: 0 3px 10px rgba(20, 30, 60, .06)}.brand-card.open {box-shadow: 0 4px 14px rgba(59, 91, 219, .14);border-color: #c8d2f4}.brand-head {display: grid;grid-template-columns:24px 130px 1fr 100px 60px;gap: 12px;align-items: center;padding: 10px 14px;cursor: pointer;user-select: none}.brand-head .cx {color: #c5cce0;font-size: 14px;transition: transform .18s ease, color .12s}.brand-card.open .brand-head .cx {transform: rotate(90deg);color: var(--brand)}.brand-head .bn {font-size: 13px;font-weight: 700;color: var(--ink);white-space: nowrap;overflow: hidden;text-overflow: ellipsis}.brand-head .btrack {height: 10px;background: #eef1f6;border-radius: 6px;overflow: hidden}.brand-head .btrack > i {display: block;height: 100%;border-radius: 6px;background: linear-gradient(90deg, var(--brand2), var(--brand));transition: width .3s ease}.brand-head .bval {font-size: 13px;font-weight: 700;color: var(--ink);font-variant-numeric: tabular-nums;text-align: right}.brand-head .bshare {font-size: 12px;font-weight: 700;color: var(--muted);font-variant-numeric: tabular-nums;text-align: right}.brand-card.top .brand-head .btrack > i {background: linear-gradient(90deg, #38b2ac, var(--teal))}.brand-card.top .brand-head .bshare {color: var(--teal)}.brand-body {display: none;padding: 6px 14px 14px;border-top: 1px dashed var(--line);background: #fafbfe}.brand-card.open .brand-body {display: block}.brand-body .bhdr {font-size: 10.5px;letter-spacing: .06em;text-transform: uppercase;color: var(--soft);font-weight: 700;margin: 8px 0 8px}.mbars {display: flex;flex-direction: column;gap: 6px}.mbar {display: grid;grid-template-columns:90px 1fr 100px 60px;gap: 10px;align-items: center;padding: 6px 10px;background: #fff;border: 1px solid var(--line2);border-radius: 8px}.mbar .mname {font-size: 11px;font-weight: 700;letter-spacing: .04em;text-transform: uppercase;display: flex;align-items: center;gap: 5px}.mbar .mname i {display: inline-block;width: 7px;height: 7px;border-radius: 50%}.mbar.hid .mname {color: var(--hid)}.mbar.hid .mname i {background: var(--hid)}.mbar.fastmoving .mname {color: var(--fast)}.mbar.fastmoving .mname i {background: var(--fast)}.mbar.running .mname {color: var(--run)}.mbar.running .mname i {background: var(--run)}.mbar.slowmoving .mname {color: var(--slow)}.mbar.slowmoving .mname i {background: var(--slow)}.mbar.other .mname {color: var(--other)}.mbar.other .mname i {background: var(--other)}.mbar .mtrack {height: 10px;border-radius: 5px;overflow: hidden}.mbar.hid .mtrack {background: var(--hid-bg)}.mbar.fastmoving .mtrack {background: var(--fast-bg)}.mbar.running .mtrack {background: var(--run-bg)}.mbar.slowmoving .mtrack {background: var(--slow-bg)}.mbar.other .mtrack {background: var(--other-bg)}.mbar .mtrack > i {display: block;height: 100%;border-radius: 5px;transition: width .3s ease}.mbar.hid .mtrack > i {background: linear-gradient(90deg, #9775fa, var(--hid))}.mbar.fastmoving .mtrack > i {background: linear-gradient(90deg, var(--brand2), var(--fast))}.mbar.running .mtrack > i {background: linear-gradient(90deg, #20c997, var(--run))}.mbar.slowmoving .mtrack > i {background: linear-gradient(90deg, #ffa94d, var(--slow))}.mbar.other .mtrack > i {background: linear-gradient(90deg, #adb5bd, var(--other))}.mbar .mval {font-size: 12px;font-weight: 650;color: var(--ink);font-variant-numeric: tabular-nums;text-align: right}.mbar .mshare {font-size: 12px;font-weight: 700;font-variant-numeric: tabular-nums;text-align: right;color: var(--muted)}/* status list modal (achieved vs pending) */.status-back {position: fixed;inset: 0;background: rgba(15, 22, 40, .5);backdrop-filter: blur(2px);z-index: 1300;display: none;align-items: flex-start;justify-content: center;padding: 60px 20px;overflow-y: auto}.status-back.show {display: flex}.status-modal {background: #fff;border-radius: 16px;max-width: 1000px;width: 100%;box-shadow: 0 20px 60px rgba(20, 30, 60, .28);padding: 22px 24px 20px;position: relative}.status-modal .x {position: absolute;top: 14px;right: 16px;cursor: pointer;color: var(--soft);font-size: 24px;line-height: 1;border: 0;background: none;width: 32px;height: 32px;border-radius: 8px;transition: background .1s}.status-modal .x:hover {background: #f1f3f9;color: var(--ink)}.status-modal h3 {margin: 0 0 3px;font-size: 19px;font-weight: 760;letter-spacing: -.01em;padding-right: 40px}.status-modal > .desc {margin: 0 0 18px;font-size: 12.5px;color: var(--muted);font-weight: 600}.status-grid {display: grid;grid-template-columns:1fr 1fr;gap: 18px}.status-col {border: 1px solid var(--line);border-radius: 12px;padding: 14px 16px;background: #fbfcfe}.status-col.good {border-color: #c3e8d0;background: #f4fbf6}.status-col.bad {border-color: #f2c4c4;background: #fdf5f5}.status-col h4 {margin: 0 0 10px;font-size: 13px;font-weight: 730;display: flex;align-items: center;gap: 8px;letter-spacing: -.005em}.status-col h4 .cnt {background: #fff;border: 1px solid var(--line);color: var(--ink);font-size: 11px;font-weight: 700;padding: 2px 9px;border-radius: 999px}.status-col.good h4 {color: #177a3a}.status-col.good h4 .cnt {background: #e5f7ee;border-color: #c3e8d0;color: #177a3a}.status-col.bad h4 {color: #b8272b}.status-col.bad h4 .cnt {background: #ffeaea;border-color: #f2c4c4;color: #b8272b}.status-list {display: flex;flex-direction: column;gap: 6px;max-height: 60vh;overflow-y: auto;padding-right: 4px}.status-item {display: grid;grid-template-columns:1fr 100px 68px;gap: 10px;align-items: center;padding: 9px 12px;background: #fff;border: 1px solid var(--line2);border-radius: 9px;transition: box-shadow .12s}.status-item:hover {box-shadow: 0 2px 8px rgba(20, 30, 60, .06)}.status-item .rn {font-weight: 700;color: var(--ink);font-size: 12.5px;line-height: 1.3}.status-item .wh {font-size: 11px;color: var(--muted);font-weight: 600;display: block;margin-top: 2px}.status-item .vv {font-size: 12px;color: var(--muted);font-variant-numeric: tabular-nums;font-weight: 600;text-align: right;white-space: nowrap}.status-item .vv b {color: var(--ink);font-weight: 700}.status-item .pp {font-size: 12px;font-weight: 700;font-variant-numeric: tabular-nums;text-align: right}.status-item .pp.good {color: var(--good)}.status-item .pp.warn {color: var(--warn)}.status-item .pp.bad {color: var(--bad)}.status-empty {padding: 24px;text-align: center;color: var(--soft);font-size: 12.5px}@media (max-width: 960px) {.kpis {grid-template-columns:repeat(2, 1fr)}.brand h1 {font-size: 22px}.status-grid {grid-template-columns:1fr}}</style><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/daterangepicker/daterangepicker.css"/></head><body><div class="wrap"><header class="top"><div class="brand"><div class="crumb"><a href="/indent/today_po_rbm">← Today PO / RBM</a> · Sales · Indent</div><h1>RBM DRR Dashboard <span class="live"><i></i>Live</span></h1></div><div class="hd-right"><input class="ctl" id="rdrrPeriod" type="text" placeholder="Select date range"/><button class="btn-r" id="rdrrApply">Apply</button></div></header><div class="kpis"><div class="kpi a"><div class="top"><div class="ic"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round"stroke-linejoin="round"><path d="M12 2v20M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6"/></svg></div><div class="label">Total Achieved</div></div><div class="val" id="rdrrKpiAch">—</div><div class="foot" id="rdrrKpiAchSub">Actual billed across all RBMs</div></div><div class="kpi b"><div class="top"><div class="ic"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round"stroke-linejoin="round"><circle cx="12" cy="12" r="9"/><circle cx="12" cy="12" r="5"/><circle cx="12" cy="12" r="1.5" fill="currentColor"/></svg></div><div class="label">Total Target</div></div><div class="val" id="rdrrKpiTgt">—</div><div class="foot" id="rdrrKpiTgtSub">Sum of daily target snapshots</div></div><div class="kpi c"><div class="top"><div class="ic"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round"stroke-linejoin="round"><path d="M3 17l6-6 4 4 8-8"/><path d="M17 7h4v4"/></svg></div><div class="label">Achievement %</div></div><div class="val" id="rdrrKpiPct">—</div><div class="foot" id="rdrrKpiPctSub">Total ACH / Total TGT</div></div><div class="kpi d clickable" id="rdrrKpiTopCard" title="Click to see achieved vs pending RBMs"><div class="top"><div class="ic"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"stroke-linejoin="round"><path d="M12 2l3 6.5 7 1-5 5 1.5 7L12 18l-6.5 3.5L7 14.5 2 9.5l7-1L12 2z"/></svg></div><div class="label">RBMs ≥ 100% Target</div></div><div class="val" id="rdrrKpiTop">—</div><div class="foot" id="rdrrKpiTopSub">Rows meeting or beating target</div><span class="hint">Click for list ›</span></div></div><div class="sectionlabel">RBM × Warehouse × Movement Bucket</div><div class="legend"><span class="lg hid"><i></i><span class="lgt">HID</span></span><span class="lg fast"><i></i><span class="lgt">FAST</span></span><span class="lg run"><i></i><span class="lgt">RUN</span></span><span class="lg slow"><i></i><span class="lgt">SLOW</span></span><span class="lg other"><i></i><span class="lgt">OTHER</span></span></div><div class="card"><div class="card-hd"><div><h2>Achievement vs Target</h2><p class="desc">Click any row for a per-bucket breakdown and the catalogs sold.</p></div><span class="badge-period" id="rdrrPeriodLabel">—</span></div><div id="rdrrTableWrap"><div class="loading"><div class="spinner"></div>Loading…</div></div><div class="foot-note"><b>TGT</b> is summed from persisted per-day snapshots (<code>inventory.rbm_targets</code>) — days with noscheduler-written snapshot contribute 0.<b>ACH</b> is computed from source orders (<code>transaction.order</code>) for the range.</div></div></div><!-- achieved-vs-pending modal (opens from the RBMs ≥ 100% Target KPI card) --><div class="status-back" id="rdrrStatusBack"><div class="status-modal"><button class="x" id="rdrrStatusClose" aria-label="Close">×</button><h3>Target Status by RBM</h3><p class="desc" id="rdrrStatusDesc">—</p><div class="status-grid"><div class="status-col good"><h4>✅ Achieved <span class="cnt" id="rdrrStatusAchCnt">0</span></h4><div class="status-list" id="rdrrStatusAchList"></div></div><div class="status-col bad"><h4>⚠ Pending <span class="cnt" id="rdrrStatusPndCnt">0</span></h4><div class="status-list" id="rdrrStatusPndList"></div></div></div></div></div><!-- drill-down panel --><div class="rdrr-back" id="rdrrBack"><div class="rdrr-panel"><button class="x" id="rdrrClose" aria-label="Close">×</button><h3 id="rdrrDdTitle">—</h3><p class="desc" id="rdrrDdDesc">—</p><div class="rdrr-total"><div><div class="k">Achieved</div><div class="v" id="rdrrDdAch">—</div></div><div><div class="k">Target</div><div class="v" id="rdrrDdTgt">—</div></div><div><div class="k">Achievement</div><div class="v" id="rdrrDdPct">—</div></div></div><div class="sectionlabel" style="margin-top:2px">Movement Split — Achievement vs Target</div><div class="bbars" id="rdrrDdBars"></div><div class="sectionlabel">Brand Mix — Share of Achievement <spanstyle="color:var(--soft);font-weight:600;text-transform:none;letter-spacing:0;margin-left:6px">(click a brand for movement split)</span></div><div id="rdrrDdBrands"><div class="loading"><div class="spinner"></div>Loading brand mix…</div></div></div></div><script src="https://cdn.jsdelivr.net/jquery/latest/jquery.min.js"></script><script src="https://cdn.jsdelivr.net/momentjs/latest/moment.min.js"></script><script src="https://cdn.jsdelivr.net/npm/daterangepicker/daterangepicker.min.js"></script><script>(function () {var DEFAULT_START = "$defaultStartDate";var DEFAULT_END = "$defaultEndDate";// Prefer query-string range if the caller provided one (e.g. from today_po_rbm's date picker).var url = new URL(window.location.href);var qsStart = url.searchParams.get("startDate");var qsEnd = url.searchParams.get("endDate");var startDate = qsStart || DEFAULT_START;var endDate = qsEnd || DEFAULT_END;function fmtDate(s) {return moment(s).format("DD MMM");}function fmtLakh(v) {v = Number(v) || 0;return (v / 100000).toFixed(1) + "L";}function pctClass(p) {if (p >= 95) return "good";if (p >= 41) return "warn";return "bad";}function pctLabel(ach, tgt) {if (!tgt || tgt <= 0) return {text: "-", cls: "na", num: null};var p = (ach / tgt) * 100;return {text: p.toFixed(1) + "%", cls: pctClass(p), num: p};}function pctBarWidth(ach, tgt) {if (!tgt || tgt <= 0) return 0;return Math.min(100, (ach / tgt) * 100);}// Initialise the range picker.$("#rdrrPeriod").daterangepicker({startDate: moment(startDate),endDate: moment(endDate),locale: {format: "YYYY-MM-DD", cancelLabel: "Clear"}});$("#rdrrPeriod").on("apply.daterangepicker", function (ev, picker) {startDate = picker.startDate.format("YYYY-MM-DD");endDate = picker.endDate.format("YYYY-MM-DD");});// Defensive: read the picker's current state at click time in case// the apply.daterangepicker event was swallowed for any reason.$("#rdrrApply").on("click", function () {var p = $("#rdrrPeriod").data("daterangepicker");if (p) {startDate = p.startDate.format("YYYY-MM-DD");endDate = p.endDate.format("YYYY-MM-DD");}load();});function load() {$("#rdrrTableWrap").html('<div class="loading"><div class="spinner"></div>Loading…</div>');$("#rdrrPeriodLabel").text(fmtDate(startDate) + " – " + fmtDate(endDate));fetch("/rbm-drr/data?startDate=" + startDate + "&endDate=" + endDate, {credentials: "same-origin"}).then(function (r) {return r.json().then(function (b) {return {ok: r.ok, body: b};});}).then(function (res) {if (!res.ok) {$("#rdrrTableWrap").html('<div class="empty">' + escapeHtml(res.body && res.body.error ? res.body.error : "Failed to load.") + '</div>');return;}render(res.body);}).catch(function (e) {$("#rdrrTableWrap").html('<div class="empty">Failed to load: ' + (e && e.message ? e.message : "network error") + '</div>');});}// Cached latest payload so the "Target Status by RBM" modal can render// without a re-fetch when the KPI card is clicked.var LAST_PAYLOAD = null;function render(data) {LAST_PAYLOAD = data;var rows = data.rows || [];var t = data.totals || {};$("#rdrrKpiAch").html('₹ ' + fmtLakh(t.totalAch));$("#rdrrKpiTgt").html('₹ ' + fmtLakh(t.totalTgt));var pk = pctLabel(t.totalAch, t.totalTgt);$("#rdrrKpiPct").html(pk.cls === "na" ? '<span class="u">n/a</span>' :'<span class="pct ' + pk.cls + '">' + pk.text + '</span>');$("#rdrrKpiTop").html((t.rbmsAtOrAbove100 || 0) + ' <span class="u">/ ' + (t.rbmRowCount || 0) + '</span>');if (rows.length === 0) {$("#rdrrTableWrap").html('<div class="empty"><div class="ei">📭</div>No RBM rows in this range.</div>');return;}var buckets = [{key: "hid", label: "HID"},{key: "fast", label: "FAST"},{key: "run", label: "RUN"},{key: "slow", label: "SLOW"},{key: "other", label: "OTHER"}];var html = ['<table id="rdrrGrid"><thead><tr>','<th>RBM · Warehouse</th>','<th class="num g-total">Total<br><small style="color:var(--soft);font-weight:600">ACH / TGT</small></th>','<th class="num g-total">%</th>'];buckets.forEach(function (b) {html.push('<th class="num g-' + b.key + '">' + b.label + '<br><small style="color:var(--soft);font-weight:600">ACH / TGT</small></th>');html.push('<th class="num g-' + b.key + '">%</th>');});html.push('<th></th></tr></thead><tbody>');rows.forEach(function (r) {var tr = ['<tr class="clickable" data-authid="' + r.authId + '" data-warehouseid="' + r.warehouseId + '" data-rbmname="' + escapeHtml(r.rbmName) + '" data-warehousename="' + escapeHtml(r.warehouseName) + '">'];tr.push('<td><div class="rbm-cell"><span class="rn">' + escapeHtml(r.rbmName) + '</span><span class="wh">' + escapeHtml(r.warehouseName) + '</span></div></td>');tr.push(cellPair(r.totalAch, r.totalTgt));buckets.forEach(function (b) {tr.push(cellPair(r[b.key + "Ach"], r[b.key + "Tgt"]));});tr.push('<td class="chev">›</td>');tr.push('</tr>');html.push(tr.join(""));});// totals rowvar totTr = ['<tr class="totalrow"><td><strong>Total</strong></td>'];totTr.push(cellPair(t.totalAch, t.totalTgt, true));buckets.forEach(function (b) {totTr.push(cellPair(t[b.key + "Ach"], t[b.key + "Tgt"], true));});totTr.push('<td></td></tr>');html.push(totTr.join(""));html.push('</tbody></table>');$("#rdrrTableWrap").html(html.join(""));// Cache the parsed row on each <tr> so drill-down can render bars immediately.rows.forEach(function (r) {$("#rdrrGrid tbody tr").filter(function () {return +this.dataset.authid === r.authId && +this.dataset.warehouseid === r.warehouseId;}).each(function () {$(this).data("cached", r);});});$("#rdrrGrid tbody tr.clickable").on("click", function () {var d = this.dataset;var cached = $(this).data("cached");openDrilldown(+d.authid, +d.warehouseid, d.rbmname, d.warehousename, cached);});}function cellPair(ach, tgt, isTotal) {var p = pctLabel(ach, tgt);var vhtml = '<td class="num"><div class="cell-val">' + fmtLakh(ach) + ' <small>/ ' + fmtLakh(tgt) + '</small></div></td>';var barCls = p.cls === "na" ? "" : p.cls;var barW = pctBarWidth(ach, tgt);var pctHtml = '<td class="num"><span class="pct ' + p.cls + '">' + p.text + '</span>' +(barCls ? '<div class="bar"><i class="' + barCls + '" style="width:' + barW.toFixed(1) + '%"></i></div>' : '') +'</td>';return vhtml + pctHtml;}// --------------------------- drill-down ---------------------------function openDrilldown(authId, warehouseId, rbmName, warehouseName, cachedRow) {var back = $("#rdrrBack");$("#rdrrDdTitle").text(rbmName + " — " + warehouseName);$("#rdrrDdDesc").text(fmtDate(startDate) + " – " + fmtDate(endDate));$("#rdrrDdBars").empty();back.addClass("show");if (cachedRow) {renderBars(cachedRow);} else {$("#rdrrDdAch").text("—");$("#rdrrDdTgt").text("—");$("#rdrrDdPct").text("—");}$("#rdrrDdBrands").html('<div class="loading"><div class="spinner"></div>Loading brand mix…</div>');fetch("/rbm-drr/rbm-detail?authId=" + authId + "&warehouseId=" + warehouseId +"&startDate=" + startDate + "&endDate=" + endDate, {credentials: "same-origin"}).then(function (r) {return r.json();}).then(function (data) {renderBrands(data);}).catch(function (e) {$("#rdrrDdBrands").html('<div class="empty">Failed to load: ' + (e && e.message ? e.message : "network error") + '</div>');});}function renderBrands(data) {var brands = (data && data.brands) || [];if (brands.length === 0) {$("#rdrrDdBrands").html('<div class="empty"><div class="ei">🏷️</div>No brand sales in this range.</div>');return;}var maxShare = brands[0].sharePct || 0.001;var html = '<div class="brands">';brands.forEach(function (b, idx) {var barW = maxShare > 0 ? Math.min(100, (b.sharePct / maxShare) * 100) : 0;var topCls = idx === 0 ? ' top' : '';html += '<div class="brand-card' + topCls + '" data-idx="' + idx + '">' +'<div class="brand-head">' +'<div class="cx">›</div>' +'<div class="bn" title="' + escapeHtml(b.brand) + '">' + escapeHtml(b.brand) + '</div>' +'<div class="btrack"><i style="width:' + barW.toFixed(1) + '%"></i></div>' +'<div class="bval">' + fmtLakh(b.valueSold) + '</div>' +'<div class="bshare">' + (b.sharePct || 0).toFixed(1) + '%</div>' +'</div>' +'<div class="brand-body">' +'<div class="bhdr">Movement Split within ' + escapeHtml(b.brand) + '</div>' +renderBrandBuckets(b.buckets || []) +'</div>' +'</div>';});html += '</div>';$("#rdrrDdBrands").html(html);$("#rdrrDdBrands .brand-head").on("click", function () {$(this).closest(".brand-card").toggleClass("open");});}function renderBrandBuckets(buckets) {if (!buckets || buckets.length === 0) {return '<div style="color:var(--soft);font-size:12px;padding:8px 4px">No movement split available.</div>';}var labels = {HID: "HID", FASTMOVING: "FAST", RUNNING: "RUN", SLOWMOVING: "SLOW", OTHER: "OTHER"};var html = '<div class="mbars">';buckets.forEach(function (bk) {var cls = (bk.status || "OTHER").toLowerCase();var label = labels[bk.status] || bk.status || "OTHER";var sp = bk.sharePctOfBrand || 0;var barW = Math.min(100, sp);html += '<div class="mbar ' + cls + '">' +'<div class="mname"><i></i>' + label + '</div>' +'<div class="mtrack"><i style="width:' + barW.toFixed(1) + '%"></i></div>' +'<div class="mval">' + fmtLakh(bk.valueSold) + '</div>' +'<div class="mshare">' + sp.toFixed(1) + '%</div>' +'</div>';});html += '</div>';return html;}function renderBars(row) {var totalAch = row.totalAch, totalTgt = row.totalTgt;$("#rdrrDdAch").html('₹ ' + fmtLakh(totalAch));$("#rdrrDdTgt").html('₹ ' + fmtLakh(totalTgt));var pt = pctLabel(totalAch, totalTgt);$("#rdrrDdPct").html(pt.cls === "na" ? '<span style="color:var(--soft);font-size:14px">n/a</span>' :'<span class="pct ' + pt.cls + '">' + pt.text + '</span>');var buckets = [{key: "hid", label: "HID"},{key: "fast", label: "FAST"},{key: "run", label: "RUN"},{key: "slow", label: "SLOW"},{key: "other", label: "OTHER"}];var html = "";buckets.forEach(function (b) {var ach = row[b.key + "Ach"], tgt = row[b.key + "Tgt"];var p = pctLabel(ach, tgt);var w = pctBarWidth(ach, tgt);html += '<div class="bbar ' + b.key + '">' +'<div class="bname"><i></i>' + b.label + '</div>' +'<div class="btrack"><i style="width:' + w.toFixed(1) + '%"></i></div>' +'<div class="btgt"><b>' + fmtLakh(ach) + '</b> / ' + fmtLakh(tgt) + '</div>' +'<div class="bpct ' + p.cls + '">' + p.text + '</div>' +'</div>';});$("#rdrrDdBars").html(html);}function escapeHtml(s) {if (s == null) return "";return String(s).replace(/[&<>"']/g, function (c) {return {"&": "&", "<": "<", ">": ">", '"': """, "'": "'"}[c];});}$("#rdrrClose").on("click", function () {$("#rdrrBack").removeClass("show");});$("#rdrrBack").on("click", function (e) {if (e.target === this) $(this).removeClass("show");});// Target-status modal (achieved vs pending) — opens from the KPI card.$("#rdrrKpiTopCard").on("click", function () {openStatusModal();});$("#rdrrStatusClose").on("click", function () {$("#rdrrStatusBack").removeClass("show");});$("#rdrrStatusBack").on("click", function (e) {if (e.target === this) $(this).removeClass("show");});function openStatusModal() {if (!LAST_PAYLOAD) return;var rows = (LAST_PAYLOAD.rows || []).slice();var achieved = [], pending = [];rows.forEach(function (r) {if (!r.totalTgt || r.totalTgt <= 0) return;var pct = (r.totalAch / r.totalTgt) * 100;var enriched = {row: r, pct: pct};if (r.totalAch >= r.totalTgt) achieved.push(enriched);else pending.push(enriched);});achieved.sort(function (a, b) {return b.pct - a.pct;});pending.sort(function (a, b) {return a.pct - b.pct;});$("#rdrrStatusDesc").text(fmtDate(startDate) + " – " + fmtDate(endDate));$("#rdrrStatusAchCnt").text(achieved.length);$("#rdrrStatusPndCnt").text(pending.length);$("#rdrrStatusAchList").html(renderStatusList(achieved, "achieved"));$("#rdrrStatusPndList").html(renderStatusList(pending, "pending"));$("#rdrrStatusBack").addClass("show");// Bind click on each item to open the same drill-down slide-in.$("#rdrrStatusBack .status-item").on("click", function () {var d = this.dataset;var cachedRow = null;(LAST_PAYLOAD.rows || []).some(function (r) {if (r.authId == d.authid && r.warehouseId == d.warehouseid) {cachedRow = r;return true;}return false;});$("#rdrrStatusBack").removeClass("show");openDrilldown(+d.authid, +d.warehouseid, d.rbmname, d.warehousename, cachedRow);});}function renderStatusList(items, kind) {if (items.length === 0) {return '<div class="status-empty">' +(kind === "achieved" ? "No RBM has met target in this range." : "All RBMs met target — nice!") +'</div>';}var html = "";items.forEach(function (it) {var r = it.row;var p = pctClass(it.pct);html += '<div class="status-item" title="Click to view drill-down"' +' data-authid="' + r.authId + '" data-warehouseid="' + r.warehouseId + '"' +' data-rbmname="' + escapeHtml(r.rbmName) + '" data-warehousename="' + escapeHtml(r.warehouseName) + '"' +' style="cursor:pointer">' +'<div><span class="rn">' + escapeHtml(r.rbmName) + '</span>' +'<span class="wh">' + escapeHtml(r.warehouseName) + '</span></div>' +'<div class="vv"><b>' + fmtLakh(r.totalAch) + '</b> / ' + fmtLakh(r.totalTgt) + '</div>' +'<div class="pp ' + p + '">' + it.pct.toFixed(1) + '%</div>' +'</div>';});return html;}// initial loadload();})();</script></body></html>