Subversion Repositories SmartDukaan

Rev

Rev 37444 | Rev 37468 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
37444 ranu 1
<!DOCTYPE html>
2
<html lang="en">
3
<head>
4
  <meta charset="utf-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1">
6
  <title>RBM DRR Dashboard</title>
7
  <style>
8
    :root {
9
      --bg: #f4f6fb;
10
      --card: #ffffff;
11
      --ink: #141a2b;
12
      --muted: #5d6a83;
13
      --soft: #97a1b5;
14
      --line: #e6e9f1;
15
      --line2: #eef1f6;
16
      --brand: #3b5bdb;
17
      --brand2: #5c7cfa;
18
      --teal: #0ca678;
19
      --cyan: #1098ad;
20
      --amber: #f59f00;
21
      --orange: #e8590c;
22
      --red: #e03131;
23
      --violet: #7048e8;
24
      --pink: #d6336c;
25
      --good: #2f9e44;
26
      --warn: #e8590c;
27
      --bad: #e03131;
28
      --hid: #7048e8;
29
      --fast: #3b5bdb;
30
      --run: #0ca678;
31
      --slow: #e8590c;
32
      --other: #868e96;
33
      --hid-bg: #f2edff;
34
      --fast-bg: #eaf0ff;
35
      --run-bg: #e5f7ee;
36
      --slow-bg: #ffefe0;
37
      --other-bg: #eef0f4;
38
    }
39
 
40
    * {
41
      box-sizing: border-box
42
    }
43
 
44
    html, body {
45
      margin: 0;
46
      background: var(--bg);
47
      color: var(--ink);
48
      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
49
      -webkit-font-smoothing: antialiased;
50
      font-size: 14px
51
    }
52
 
53
    .wrap {
54
      max-width: 1440px;
55
      margin: 0 auto;
56
      padding: 22px 26px 100px
57
    }
58
 
59
    a {
60
      color: var(--brand);
61
      text-decoration: none
62
    }
63
 
64
    a:hover {
65
      text-decoration: underline
66
    }
67
 
68
    /* header */
69
    header.top {
70
      display: flex;
71
      align-items: flex-end;
72
      justify-content: space-between;
73
      gap: 18px;
74
      flex-wrap: wrap;
75
      margin-bottom: 20px
76
    }
77
 
78
    .brand .crumb {
79
      font-size: 12px;
80
      color: var(--soft);
81
      font-weight: 600
82
    }
83
 
84
    .brand h1 {
85
      margin: 4px 0 0;
86
      font-size: 26px;
87
      font-weight: 770;
88
      letter-spacing: -.015em;
89
      display: flex;
90
      align-items: center;
91
      gap: 12px
92
    }
93
 
94
    .brand h1 .live {
95
      font-size: 11px;
96
      font-weight: 700;
97
      color: var(--teal);
98
      background: #e6fcf5;
99
      border: 1px solid #c3fae8;
100
      border-radius: 999px;
101
      padding: 3px 10px
102
    }
103
 
104
    .brand h1 .live i {
105
      display: inline-block;
106
      width: 7px;
107
      height: 7px;
108
      border-radius: 50%;
109
      background: var(--teal);
110
      margin-right: 5px;
111
      animation: pulse 1.6s infinite;
112
      vertical-align: middle
113
    }
114
 
115
    @keyframes pulse {
116
      0%, 100% {
117
        opacity: 1
118
      }
119
      50% {
120
        opacity: .25
121
      }
122
    }
123
 
124
    .hd-right {
125
      display: flex;
126
      align-items: center;
127
      gap: 10px;
128
      flex-wrap: wrap
129
    }
130
 
131
    .ctl {
132
      background: var(--card);
133
      border: 1px solid var(--line);
134
      border-radius: 10px;
135
      padding: 9px 13px;
136
      font-size: 13px;
137
      color: var(--ink);
138
      font-weight: 600;
139
      box-shadow: 0 1px 2px rgba(20, 30, 60, .04)
140
    }
141
 
142
    input.ctl {
143
      appearance: none;
144
      cursor: pointer;
145
      height: 38px;
146
      min-width: 240px
147
    }
148
 
149
    .btn-r {
150
      background: var(--brand);
151
      border: 0;
152
      color: #fff;
153
      font-weight: 670;
154
      padding: 9px 18px;
155
      border-radius: 10px;
156
      cursor: pointer;
157
      font-size: 13px;
158
      height: 38px;
159
      box-shadow: 0 1px 2px rgba(59, 91, 219, .35);
160
      transition: transform .1s, box-shadow .1s
161
    }
162
 
163
    .btn-r:hover {
164
      background: var(--brand2);
165
      box-shadow: 0 3px 10px rgba(59, 91, 219, .35)
166
    }
167
 
168
    .btn-r:active {
169
      transform: translateY(1px)
170
    }
171
 
172
    /* KPI cards */
173
    .kpis {
174
      display: grid;
175
      grid-template-columns:repeat(4, 1fr);
176
      gap: 14px;
177
      margin-bottom: 22px
178
    }
179
 
180
    .kpi {
181
      position: relative;
182
      background: var(--card);
183
      border: 1px solid var(--line);
184
      border-radius: 14px;
185
      padding: 16px 18px;
186
      box-shadow: 0 1px 2px rgba(20, 30, 60, .05);
187
      display: flex;
188
      flex-direction: column;
189
      min-height: 118px;
190
      overflow: hidden
191
    }
192
 
193
    .kpi::before {
194
      content: "";
195
      position: absolute;
196
      top: 0;
197
      left: 0;
198
      height: 3px;
199
      width: 100%;
200
      border-top-left-radius: 14px;
201
      border-top-right-radius: 14px
202
    }
203
 
204
    .kpi.a::before {
205
      background: linear-gradient(90deg, var(--brand), var(--brand2))
206
    }
207
 
208
    .kpi.b::before {
209
      background: linear-gradient(90deg, var(--teal), #20c997)
210
    }
211
 
212
    .kpi.c::before {
213
      background: linear-gradient(90deg, var(--amber), #ffc078)
214
    }
215
 
216
    .kpi.d::before {
217
      background: linear-gradient(90deg, var(--violet), #9775fa)
218
    }
219
 
220
    .kpi .top {
221
      display: flex;
222
      align-items: center;
223
      gap: 10px
224
    }
225
 
226
    .kpi .ic {
227
      width: 34px;
228
      height: 34px;
229
      border-radius: 10px;
230
      display: flex;
231
      align-items: center;
232
      justify-content: center;
233
      flex: none;
234
      color: #fff;
235
      box-shadow: 0 2px 6px rgba(0, 0, 0, .08)
236
    }
237
 
238
    .kpi.a .ic {
239
      background: linear-gradient(135deg, var(--brand), var(--brand2))
240
    }
241
 
242
    .kpi.b .ic {
243
      background: linear-gradient(135deg, var(--teal), #20c997)
244
    }
245
 
246
    .kpi.c .ic {
247
      background: linear-gradient(135deg, var(--amber), #ffc078);
248
      color: #3a2500
249
    }
250
 
251
    .kpi.d .ic {
252
      background: linear-gradient(135deg, var(--violet), #9775fa)
253
    }
254
 
255
    .kpi .ic svg {
256
      width: 18px;
257
      height: 18px
258
    }
259
 
260
    .kpi .label {
261
      font-size: 12px;
262
      color: var(--muted);
263
      font-weight: 650;
264
      line-height: 1.2;
265
      letter-spacing: .005em
266
    }
267
 
268
    .kpi .val {
269
      font-size: 28px;
270
      font-weight: 780;
271
      margin-top: auto;
272
      padding-top: 10px;
273
      letter-spacing: -.02em;
274
      line-height: 1;
275
      font-variant-numeric: tabular-nums
276
    }
277
 
278
    .kpi .val .u {
279
      font-size: 13px;
280
      color: var(--soft);
281
      font-weight: 600;
282
      margin-left: 3px
283
    }
284
 
285
    .kpi .foot {
286
      font-size: 11.5px;
287
      color: var(--soft);
288
      margin-top: 8px;
289
      font-weight: 500
290
    }
291
 
292
    .kpi .val .pct {
293
      margin-left: 0;
294
      font-size: 20px;
295
      padding: 4px 12px
296
    }
297
 
298
    .kpi.clickable {
299
      cursor: pointer;
300
      transition: transform .1s, box-shadow .12s, border-color .12s
301
    }
302
 
303
    .kpi.clickable:hover {
304
      transform: translateY(-2px);
305
      box-shadow: 0 6px 18px rgba(20, 30, 60, .08);
306
      border-color: #c8d2f4
307
    }
308
 
309
    .kpi.clickable .hint {
310
      position: absolute;
311
      bottom: 8px;
312
      right: 12px;
313
      color: var(--soft);
314
      font-size: 11px;
315
      font-weight: 600;
316
      opacity: .7
317
    }
318
 
319
    .kpi.clickable:hover .hint {
320
      opacity: 1;
321
      color: var(--brand)
322
    }
323
 
324
    /* Cards & section labels */
325
    .card {
326
      background: var(--card);
327
      border: 1px solid var(--line);
328
      border-radius: 15px;
329
      padding: 18px 20px;
330
      margin-bottom: 16px;
331
      box-shadow: 0 1px 2px rgba(20, 30, 60, .04)
332
    }
333
 
334
    .card .card-hd {
335
      display: flex;
336
      align-items: baseline;
337
      justify-content: space-between;
338
      gap: 14px;
339
      margin-bottom: 14px
340
    }
341
 
342
    .card h2 {
343
      margin: 0;
344
      font-size: 16px;
345
      font-weight: 730;
346
      letter-spacing: -.01em
347
    }
348
 
349
    .card .desc {
350
      color: var(--muted);
351
      font-size: 12.5px;
352
      margin: 2px 0 0
353
    }
354
 
355
    .card .badge-period {
356
      background: #eef2ff;
357
      border: 1px solid #d7defb;
358
      color: var(--brand);
359
      border-radius: 999px;
360
      padding: 3px 12px;
361
      font-size: 12px;
362
      font-weight: 650;
363
      letter-spacing: .01em
364
    }
365
 
366
    .sectionlabel {
367
      font-size: 11px;
368
      letter-spacing: .14em;
369
      text-transform: uppercase;
370
      color: var(--soft);
371
      font-weight: 700;
372
      margin: 20px 2px 10px
373
    }
374
 
375
    /* Legend under section label */
376
    .legend {
377
      display: flex;
378
      gap: 14px;
379
      flex-wrap: wrap;
380
      margin: 0 2px 10px;
381
      align-items: center;
382
      color: var(--soft);
383
      font-size: 11.5px;
384
      font-weight: 600
385
    }
386
 
387
    .legend .lg {
388
      display: inline-flex;
389
      align-items: center;
390
      gap: 5px
391
    }
392
 
393
    .legend .lg i {
394
      width: 8px;
395
      height: 8px;
396
      border-radius: 50%;
397
      display: inline-block
398
    }
399
 
400
    .legend .lg.hid i {
401
      background: var(--hid)
402
    }
403
 
404
    .legend .lg.fast i {
405
      background: var(--fast)
406
    }
407
 
408
    .legend .lg.run i {
409
      background: var(--run)
410
    }
411
 
412
    .legend .lg.slow i {
413
      background: var(--slow)
414
    }
415
 
416
    .legend .lg.other i {
417
      background: var(--other)
418
    }
419
 
420
    .legend .lg .lgt {
421
      color: var(--muted)
422
    }
423
 
424
    /* Table */
425
    table {
426
      width: 100%;
427
      border-collapse: separate;
428
      border-spacing: 0;
429
      font-size: 13px
430
    }
431
 
432
    th, td {
433
      text-align: left;
434
      padding: 11px 10px;
435
      border-bottom: 1px solid var(--line2)
436
    }
437
 
438
    thead th {
439
      font-size: 10.5px;
440
      text-transform: uppercase;
441
      letter-spacing: .06em;
442
      color: var(--soft);
443
      font-weight: 700;
444
      white-space: nowrap;
445
      user-select: none;
446
      position: sticky;
447
      top: 0;
448
      background: #fff;
449
      z-index: 1;
450
      border-bottom: 1px solid var(--line)
451
    }
452
 
453
    th.num, td.num {
454
      text-align: right
455
    }
456
 
457
    th.g-hid {
458
      color: var(--hid);
459
      border-top: 3px solid var(--hid)
460
    }
461
 
462
    th.g-fast {
463
      color: var(--fast);
464
      border-top: 3px solid var(--fast)
465
    }
466
 
467
    th.g-run {
468
      color: var(--run);
469
      border-top: 3px solid var(--run)
470
    }
471
 
472
    th.g-slow {
473
      color: var(--slow);
474
      border-top: 3px solid var(--slow)
475
    }
476
 
477
    th.g-other {
478
      color: var(--other);
479
      border-top: 3px solid var(--other)
480
    }
481
 
482
    th.g-total {
483
      color: var(--brand);
484
      border-top: 3px solid var(--brand)
485
    }
486
 
487
    tbody tr:hover {
488
      background: #f6f8fd
489
    }
490
 
491
    tbody tr.clickable {
492
      cursor: pointer;
493
      transition: background .1s
494
    }
495
 
496
    tbody tr.clickable:hover {
497
      background: #eef2ff
498
    }
499
 
500
    tr.totalrow {
501
      background: #fafbfe;
502
      border-top: 2px solid var(--line)
503
    }
504
 
505
    tr.totalrow td {
506
      font-weight: 770;
507
      color: var(--ink)
508
    }
509
 
510
    tr.totalrow .cell-val {
511
      font-size: 14px
512
    }
513
 
514
    .rbm-cell {
515
      display: flex;
516
      flex-direction: column;
517
      gap: 2px
518
    }
519
 
520
    .rbm-cell .rn {
521
      font-weight: 700;
522
      color: var(--ink);
523
      font-size: 13.5px
524
    }
525
 
526
    .rbm-cell .wh {
527
      font-size: 11.5px;
528
      color: var(--muted);
529
      font-weight: 600
530
    }
531
 
532
    .cell-val {
533
      font-variant-numeric: tabular-nums;
534
      font-weight: 650;
535
      color: var(--ink);
536
      white-space: nowrap
537
    }
538
 
539
    .cell-val small {
540
      color: var(--soft);
541
      font-weight: 500;
542
      margin-left: 3px
543
    }
544
 
545
    .pct {
546
      display: inline-flex;
547
      align-items: center;
548
      justify-content: center;
549
      min-width: 56px;
550
      padding: 3px 9px;
551
      border-radius: 7px;
552
      font-size: 11.5px;
553
      font-weight: 700;
554
      text-align: center;
555
      font-variant-numeric: tabular-nums;
556
      line-height: 1.35
557
    }
558
 
559
    .pct.good {
560
      background: #e5f7ee;
561
      color: #177a3a
562
    }
563
 
564
    .pct.warn {
565
      background: #fff0e0;
566
      color: #a04600
567
    }
568
 
569
    .pct.bad {
570
      background: #ffeaea;
571
      color: #b8272b
572
    }
573
 
574
    .pct.na {
575
      background: transparent;
576
      color: var(--soft);
577
      font-weight: 600;
578
      min-width: auto;
579
      padding: 0
580
    }
581
 
582
    .bar {
583
      height: 5px;
584
      border-radius: 3px;
585
      background: #eef1f6;
586
      overflow: hidden;
587
      margin-top: 6px
588
    }
589
 
590
    .bar > i {
591
      display: block;
592
      height: 100%;
593
      border-radius: 3px;
594
      transition: width .3s ease
595
    }
596
 
597
    .bar > i.good {
598
      background: linear-gradient(90deg, #51cf66, #2f9e44)
599
    }
600
 
601
    .bar > i.warn {
602
      background: linear-gradient(90deg, #ffa94d, #e8590c)
603
    }
604
 
605
    .bar > i.bad {
606
      background: linear-gradient(90deg, #ff8787, #e03131)
607
    }
608
 
609
    .chev {
610
      color: #c5cce0;
611
      font-size: 16px;
612
      padding-left: 6px;
613
      transition: transform .12s, color .12s
614
    }
615
 
616
    tr.clickable:hover .chev {
617
      color: var(--brand);
618
      transform: translateX(2px)
619
    }
620
 
621
    .loading {
622
      padding: 70px;
623
      text-align: center;
624
      color: var(--muted)
625
    }
626
 
627
    .spinner {
628
      width: 32px;
629
      height: 32px;
630
      border: 3px solid #e3e8f5;
631
      border-top-color: var(--brand);
632
      border-radius: 50%;
633
      animation: spin 1s linear infinite;
634
      margin: 0 auto 12px
635
    }
636
 
637
    @keyframes spin {
638
      to {
639
        transform: rotate(360deg)
640
      }
641
    }
642
 
643
    .empty {
644
      padding: 56px 20px;
645
      text-align: center;
646
      color: var(--soft);
647
      font-size: 13.5px
648
    }
649
 
650
    .empty .ei {
651
      font-size: 36px;
652
      margin-bottom: 8px;
653
      opacity: .5
654
    }
655
 
656
    .foot-note {
657
      color: var(--soft);
658
      font-size: 11.5px;
659
      margin-top: 14px;
660
      padding: 10px 12px;
661
      background: #fafbfe;
662
      border: 1px dashed var(--line);
663
      border-radius: 10px;
664
      line-height: 1.5
665
    }
666
 
667
    .foot-note code {
668
      background: #eef1f6;
669
      color: var(--muted);
670
      padding: 1px 5px;
671
      border-radius: 4px;
672
      font-size: 11px
673
    }
674
 
675
    /* drill-down panel */
676
    .rdrr-back {
677
      position: fixed;
678
      inset: 0;
679
      background: rgba(15, 22, 40, .5);
680
      backdrop-filter: blur(2px);
681
      z-index: 1200;
682
      display: none
683
    }
684
 
685
    .rdrr-back.show {
686
      display: block
687
    }
688
 
689
    .rdrr-panel {
690
      position: absolute;
691
      top: 0;
692
      right: 0;
693
      height: 100%;
694
      width: min(760px, 96vw);
695
      background: #fff;
696
      box-shadow: -12px 0 40px rgba(20, 30, 60, .22);
697
      display: flex;
698
      flex-direction: column;
699
      padding: 24px 26px 20px;
700
      transform: translateX(100%);
701
      transition: transform .22s ease;
702
      overflow-y: auto
703
    }
704
 
705
    .rdrr-back.show .rdrr-panel {
706
      transform: translateX(0)
707
    }
708
 
709
    .rdrr-panel .x {
710
      position: absolute;
711
      top: 16px;
712
      right: 18px;
713
      cursor: pointer;
714
      color: var(--soft);
715
      font-size: 24px;
716
      line-height: 1;
717
      border: 0;
718
      background: none;
719
      width: 32px;
720
      height: 32px;
721
      border-radius: 8px;
722
      transition: background .1s, color .1s
723
    }
724
 
725
    .rdrr-panel .x:hover {
726
      background: #f1f3f9;
727
      color: var(--ink)
728
    }
729
 
730
    .rdrr-panel h3 {
731
      margin: 0 0 3px;
732
      font-size: 19px;
733
      font-weight: 750;
734
      letter-spacing: -.01em;
735
      padding-right: 40px
736
    }
737
 
738
    .rdrr-panel > .desc {
739
      margin: 0 0 18px;
740
      font-size: 12.5px;
741
      color: var(--muted);
742
      font-weight: 600
743
    }
744
 
745
    .rdrr-total {
746
      display: flex;
747
      gap: 14px;
748
      padding: 14px 16px;
749
      background: linear-gradient(135deg, #f8f9fc, #eef2ff);
750
      border: 1px solid #dbe2f6;
751
      border-radius: 12px;
752
      margin-bottom: 18px
753
    }
754
 
755
    .rdrr-total > div {
756
      flex: 1;
757
      text-align: center;
758
      border-right: 1px solid #dbe2f6;
759
      padding: 0 4px
760
    }
761
 
762
    .rdrr-total > div:last-child {
763
      border-right: 0
764
    }
765
 
766
    .rdrr-total .k {
767
      font-size: 10.5px;
768
      color: var(--muted);
769
      text-transform: uppercase;
770
      letter-spacing: .06em;
771
      font-weight: 700
772
    }
773
 
774
    .rdrr-total .v {
775
      font-size: 20px;
776
      font-weight: 770;
777
      color: var(--ink);
778
      font-variant-numeric: tabular-nums;
779
      margin-top: 5px;
780
      letter-spacing: -.01em
781
    }
782
 
783
    .rdrr-total .v .pct {
784
      font-size: 16px;
785
      padding: 3px 10px;
786
      margin-top: 0
787
    }
788
 
789
    .bbars {
790
      display: flex;
791
      flex-direction: column;
792
      gap: 10px;
793
      margin-bottom: 20px
794
    }
795
 
796
    .bbar {
797
      display: grid;
798
      grid-template-columns:82px 1fr 145px 60px;
799
      gap: 14px;
800
      align-items: center;
801
      padding: 12px 14px;
802
      background: #fff;
803
      border: 1px solid var(--line);
804
      border-radius: 11px;
805
      transition: box-shadow .12s
806
    }
807
 
808
    .bbar:hover {
809
      box-shadow: 0 3px 10px rgba(20, 30, 60, .06)
810
    }
811
 
812
    .bbar .bname {
813
      font-size: 11.5px;
814
      font-weight: 750;
815
      color: var(--ink);
816
      letter-spacing: .04em;
817
      text-transform: uppercase;
818
      display: flex;
819
      align-items: center;
820
      gap: 6px
821
    }
822
 
823
    .bbar .bname i {
824
      display: inline-block;
825
      width: 8px;
826
      height: 8px;
827
      border-radius: 50%
828
    }
829
 
830
    .bbar.hid .bname {
831
      color: var(--hid)
832
    }
833
 
834
    .bbar.fast .bname {
835
      color: var(--fast)
836
    }
837
 
838
    .bbar.run .bname {
839
      color: var(--run)
840
    }
841
 
842
    .bbar.slow .bname {
843
      color: var(--slow)
844
    }
845
 
846
    .bbar.other .bname {
847
      color: var(--other)
848
    }
849
 
850
    .bbar.hid .bname i {
851
      background: var(--hid)
852
    }
853
 
854
    .bbar.fast .bname i {
855
      background: var(--fast)
856
    }
857
 
858
    .bbar.run .bname i {
859
      background: var(--run)
860
    }
861
 
862
    .bbar.slow .bname i {
863
      background: var(--slow)
864
    }
865
 
866
    .bbar.other .bname i {
867
      background: var(--other)
868
    }
869
 
870
    .bbar .btrack {
871
      height: 14px;
872
      border-radius: 8px;
873
      overflow: hidden;
874
      position: relative
875
    }
876
 
877
    .bbar.hid .btrack {
878
      background: var(--hid-bg)
879
    }
880
 
881
    .bbar.fast .btrack {
882
      background: var(--fast-bg)
883
    }
884
 
885
    .bbar.run .btrack {
886
      background: var(--run-bg)
887
    }
888
 
889
    .bbar.slow .btrack {
890
      background: var(--slow-bg)
891
    }
892
 
893
    .bbar.other .btrack {
894
      background: var(--other-bg)
895
    }
896
 
897
    .bbar .btrack > i {
898
      display: block;
899
      height: 100%;
900
      border-radius: 8px;
901
      transition: width .3s ease;
902
      position: relative
903
    }
904
 
905
    .bbar.hid .btrack > i {
906
      background: linear-gradient(90deg, #9775fa, var(--hid))
907
    }
908
 
909
    .bbar.fast .btrack > i {
910
      background: linear-gradient(90deg, var(--brand2), var(--fast))
911
    }
912
 
913
    .bbar.run .btrack > i {
914
      background: linear-gradient(90deg, #20c997, var(--run))
915
    }
916
 
917
    .bbar.slow .btrack > i {
918
      background: linear-gradient(90deg, #ffa94d, var(--slow))
919
    }
920
 
921
    .bbar.other .btrack > i {
922
      background: linear-gradient(90deg, #adb5bd, var(--other))
923
    }
924
 
925
    .bbar .btgt {
926
      font-size: 12px;
927
      color: var(--muted);
928
      font-variant-numeric: tabular-nums;
929
      font-weight: 600;
930
      text-align: right
931
    }
932
 
933
    .bbar .btgt b {
934
      color: var(--ink);
935
      font-weight: 700
936
    }
937
 
938
    .bbar .bpct {
939
      font-size: 13px;
940
      font-weight: 750;
941
      text-align: right;
942
      font-variant-numeric: tabular-nums
943
    }
944
 
945
    .bbar .bpct.good {
946
      color: var(--good)
947
    }
948
 
949
    .bbar .bpct.warn {
950
      color: var(--warn)
951
    }
952
 
953
    .bbar .bpct.bad {
954
      color: var(--bad)
955
    }
956
 
957
    .bbar .bpct.na {
958
      color: var(--soft);
959
      font-weight: 600
960
    }
961
 
962
    /* brand mix bars in drill-down */
963
    .brands {
964
      display: flex;
965
      flex-direction: column;
966
      gap: 8px;
967
      margin-bottom: 20px
968
    }
969
 
970
    .brand-card {
971
      background: #fff;
972
      border: 1px solid var(--line);
973
      border-radius: 11px;
974
      overflow: hidden;
975
      transition: box-shadow .12s
976
    }
977
 
978
    .brand-card:hover {
979
      box-shadow: 0 3px 10px rgba(20, 30, 60, .06)
980
    }
981
 
982
    .brand-card.open {
983
      box-shadow: 0 4px 14px rgba(59, 91, 219, .14);
984
      border-color: #c8d2f4
985
    }
986
 
987
    .brand-head {
988
      display: grid;
37460 ranu 989
      grid-template-columns:24px 130px 1fr 96px 100px 60px;
37444 ranu 990
      gap: 12px;
991
      align-items: center;
992
      padding: 10px 14px;
993
      cursor: pointer;
994
      user-select: none
995
    }
996
 
37460 ranu 997
    .brand-head .bpc {
998
      display: inline-flex;
999
      align-items: center;
1000
      justify-content: center;
1001
      gap: 4px;
1002
      font-size: 11.5px;
1003
      font-weight: 700;
1004
      color: var(--brand);
1005
      background: #eef2ff;
1006
      border: 1px solid #d7defb;
1007
      border-radius: 999px;
1008
      padding: 3px 9px;
1009
      font-variant-numeric: tabular-nums;
1010
      white-space: nowrap;
1011
      justify-self: center
1012
    }
1013
 
1014
    .brand-head .bpc.zero {
1015
      color: var(--soft);
1016
      background: #f1f3f9;
1017
      border-color: var(--line)
1018
    }
1019
 
1020
    .brand-head .bpc svg {
1021
      width: 11px;
1022
      height: 11px
1023
    }
1024
 
37444 ranu 1025
    .brand-head .cx {
1026
      color: #c5cce0;
1027
      font-size: 14px;
1028
      transition: transform .18s ease, color .12s
1029
    }
1030
 
1031
    .brand-card.open .brand-head .cx {
1032
      transform: rotate(90deg);
1033
      color: var(--brand)
1034
    }
1035
 
1036
    .brand-head .bn {
1037
      font-size: 13px;
1038
      font-weight: 700;
1039
      color: var(--ink);
1040
      white-space: nowrap;
1041
      overflow: hidden;
1042
      text-overflow: ellipsis
1043
    }
1044
 
1045
    .brand-head .btrack {
1046
      height: 10px;
1047
      background: #eef1f6;
1048
      border-radius: 6px;
1049
      overflow: hidden
1050
    }
1051
 
1052
    .brand-head .btrack > i {
1053
      display: block;
1054
      height: 100%;
1055
      border-radius: 6px;
1056
      background: linear-gradient(90deg, var(--brand2), var(--brand));
1057
      transition: width .3s ease
1058
    }
1059
 
1060
    .brand-head .bval {
1061
      font-size: 13px;
1062
      font-weight: 700;
1063
      color: var(--ink);
1064
      font-variant-numeric: tabular-nums;
1065
      text-align: right
1066
    }
1067
 
1068
    .brand-head .bshare {
1069
      font-size: 12px;
1070
      font-weight: 700;
1071
      color: var(--muted);
1072
      font-variant-numeric: tabular-nums;
1073
      text-align: right
1074
    }
1075
 
1076
    .brand-card.top .brand-head .btrack > i {
1077
      background: linear-gradient(90deg, #38b2ac, var(--teal))
1078
    }
1079
 
1080
    .brand-card.top .brand-head .bshare {
1081
      color: var(--teal)
1082
    }
1083
 
1084
    .brand-body {
1085
      display: none;
1086
      padding: 6px 14px 14px;
1087
      border-top: 1px dashed var(--line);
1088
      background: #fafbfe
1089
    }
1090
 
1091
    .brand-card.open .brand-body {
1092
      display: block
1093
    }
1094
 
1095
    .brand-body .bhdr {
1096
      font-size: 10.5px;
1097
      letter-spacing: .06em;
1098
      text-transform: uppercase;
1099
      color: var(--soft);
1100
      font-weight: 700;
1101
      margin: 8px 0 8px
1102
    }
1103
 
1104
    .mbars {
1105
      display: flex;
1106
      flex-direction: column;
1107
      gap: 6px
1108
    }
1109
 
1110
    .mbar {
1111
      display: grid;
1112
      grid-template-columns:90px 1fr 100px 60px;
1113
      gap: 10px;
1114
      align-items: center;
1115
      padding: 6px 10px;
1116
      background: #fff;
1117
      border: 1px solid var(--line2);
1118
      border-radius: 8px
1119
    }
1120
 
1121
    .mbar .mname {
1122
      font-size: 11px;
1123
      font-weight: 700;
1124
      letter-spacing: .04em;
1125
      text-transform: uppercase;
1126
      display: flex;
1127
      align-items: center;
1128
      gap: 5px
1129
    }
1130
 
1131
    .mbar .mname i {
1132
      display: inline-block;
1133
      width: 7px;
1134
      height: 7px;
1135
      border-radius: 50%
1136
    }
1137
 
1138
    .mbar.hid .mname {
1139
      color: var(--hid)
1140
    }
1141
 
1142
    .mbar.hid .mname i {
1143
      background: var(--hid)
1144
    }
1145
 
1146
    .mbar.fastmoving .mname {
1147
      color: var(--fast)
1148
    }
1149
 
1150
    .mbar.fastmoving .mname i {
1151
      background: var(--fast)
1152
    }
1153
 
1154
    .mbar.running .mname {
1155
      color: var(--run)
1156
    }
1157
 
1158
    .mbar.running .mname i {
1159
      background: var(--run)
1160
    }
1161
 
1162
    .mbar.slowmoving .mname {
1163
      color: var(--slow)
1164
    }
1165
 
1166
    .mbar.slowmoving .mname i {
1167
      background: var(--slow)
1168
    }
1169
 
1170
    .mbar.other .mname {
1171
      color: var(--other)
1172
    }
1173
 
1174
    .mbar.other .mname i {
1175
      background: var(--other)
1176
    }
1177
 
1178
    .mbar .mtrack {
1179
      height: 10px;
1180
      border-radius: 5px;
1181
      overflow: hidden
1182
    }
1183
 
1184
    .mbar.hid .mtrack {
1185
      background: var(--hid-bg)
1186
    }
1187
 
1188
    .mbar.fastmoving .mtrack {
1189
      background: var(--fast-bg)
1190
    }
1191
 
1192
    .mbar.running .mtrack {
1193
      background: var(--run-bg)
1194
    }
1195
 
1196
    .mbar.slowmoving .mtrack {
1197
      background: var(--slow-bg)
1198
    }
1199
 
1200
    .mbar.other .mtrack {
1201
      background: var(--other-bg)
1202
    }
1203
 
1204
    .mbar .mtrack > i {
1205
      display: block;
1206
      height: 100%;
1207
      border-radius: 5px;
1208
      transition: width .3s ease
1209
    }
1210
 
1211
    .mbar.hid .mtrack > i {
1212
      background: linear-gradient(90deg, #9775fa, var(--hid))
1213
    }
1214
 
1215
    .mbar.fastmoving .mtrack > i {
1216
      background: linear-gradient(90deg, var(--brand2), var(--fast))
1217
    }
1218
 
1219
    .mbar.running .mtrack > i {
1220
      background: linear-gradient(90deg, #20c997, var(--run))
1221
    }
1222
 
1223
    .mbar.slowmoving .mtrack > i {
1224
      background: linear-gradient(90deg, #ffa94d, var(--slow))
1225
    }
1226
 
1227
    .mbar.other .mtrack > i {
1228
      background: linear-gradient(90deg, #adb5bd, var(--other))
1229
    }
1230
 
1231
    .mbar .mval {
1232
      font-size: 12px;
1233
      font-weight: 650;
1234
      color: var(--ink);
1235
      font-variant-numeric: tabular-nums;
1236
      text-align: right
1237
    }
1238
 
1239
    .mbar .mshare {
1240
      font-size: 12px;
1241
      font-weight: 700;
1242
      font-variant-numeric: tabular-nums;
1243
      text-align: right;
1244
      color: var(--muted)
1245
    }
1246
 
1247
    /* status list modal (achieved vs pending) */
1248
    .status-back {
1249
      position: fixed;
1250
      inset: 0;
1251
      background: rgba(15, 22, 40, .5);
1252
      backdrop-filter: blur(2px);
1253
      z-index: 1300;
1254
      display: none;
1255
      align-items: flex-start;
1256
      justify-content: center;
1257
      padding: 60px 20px;
1258
      overflow-y: auto
1259
    }
1260
 
1261
    .status-back.show {
1262
      display: flex
1263
    }
1264
 
1265
    .status-modal {
1266
      background: #fff;
1267
      border-radius: 16px;
1268
      max-width: 1000px;
1269
      width: 100%;
1270
      box-shadow: 0 20px 60px rgba(20, 30, 60, .28);
1271
      padding: 22px 24px 20px;
1272
      position: relative
1273
    }
1274
 
1275
    .status-modal .x {
1276
      position: absolute;
1277
      top: 14px;
1278
      right: 16px;
1279
      cursor: pointer;
1280
      color: var(--soft);
1281
      font-size: 24px;
1282
      line-height: 1;
1283
      border: 0;
1284
      background: none;
1285
      width: 32px;
1286
      height: 32px;
1287
      border-radius: 8px;
1288
      transition: background .1s
1289
    }
1290
 
1291
    .status-modal .x:hover {
1292
      background: #f1f3f9;
1293
      color: var(--ink)
1294
    }
1295
 
1296
    .status-modal h3 {
1297
      margin: 0 0 3px;
1298
      font-size: 19px;
1299
      font-weight: 760;
1300
      letter-spacing: -.01em;
1301
      padding-right: 40px
1302
    }
1303
 
1304
    .status-modal > .desc {
1305
      margin: 0 0 18px;
1306
      font-size: 12.5px;
1307
      color: var(--muted);
1308
      font-weight: 600
1309
    }
1310
 
1311
    .status-grid {
1312
      display: grid;
1313
      grid-template-columns:1fr 1fr;
1314
      gap: 18px
1315
    }
1316
 
1317
    .status-col {
1318
      border: 1px solid var(--line);
1319
      border-radius: 12px;
1320
      padding: 14px 16px;
1321
      background: #fbfcfe
1322
    }
1323
 
1324
    .status-col.good {
1325
      border-color: #c3e8d0;
1326
      background: #f4fbf6
1327
    }
1328
 
1329
    .status-col.bad {
1330
      border-color: #f2c4c4;
1331
      background: #fdf5f5
1332
    }
1333
 
1334
    .status-col h4 {
1335
      margin: 0 0 10px;
1336
      font-size: 13px;
1337
      font-weight: 730;
1338
      display: flex;
1339
      align-items: center;
1340
      gap: 8px;
1341
      letter-spacing: -.005em
1342
    }
1343
 
1344
    .status-col h4 .cnt {
1345
      background: #fff;
1346
      border: 1px solid var(--line);
1347
      color: var(--ink);
1348
      font-size: 11px;
1349
      font-weight: 700;
1350
      padding: 2px 9px;
1351
      border-radius: 999px
1352
    }
1353
 
1354
    .status-col.good h4 {
1355
      color: #177a3a
1356
    }
1357
 
1358
    .status-col.good h4 .cnt {
1359
      background: #e5f7ee;
1360
      border-color: #c3e8d0;
1361
      color: #177a3a
1362
    }
1363
 
1364
    .status-col.bad h4 {
1365
      color: #b8272b
1366
    }
1367
 
1368
    .status-col.bad h4 .cnt {
1369
      background: #ffeaea;
1370
      border-color: #f2c4c4;
1371
      color: #b8272b
1372
    }
1373
 
1374
    .status-list {
1375
      display: flex;
1376
      flex-direction: column;
1377
      gap: 6px;
1378
      max-height: 60vh;
1379
      overflow-y: auto;
1380
      padding-right: 4px
1381
    }
1382
 
1383
    .status-item {
1384
      display: grid;
1385
      grid-template-columns:1fr 100px 68px;
1386
      gap: 10px;
1387
      align-items: center;
1388
      padding: 9px 12px;
1389
      background: #fff;
1390
      border: 1px solid var(--line2);
1391
      border-radius: 9px;
1392
      transition: box-shadow .12s
1393
    }
1394
 
1395
    .status-item:hover {
1396
      box-shadow: 0 2px 8px rgba(20, 30, 60, .06)
1397
    }
1398
 
1399
    .status-item .rn {
1400
      font-weight: 700;
1401
      color: var(--ink);
1402
      font-size: 12.5px;
1403
      line-height: 1.3
1404
    }
1405
 
1406
    .status-item .wh {
1407
      font-size: 11px;
1408
      color: var(--muted);
1409
      font-weight: 600;
1410
      display: block;
1411
      margin-top: 2px
1412
    }
1413
 
1414
    .status-item .vv {
1415
      font-size: 12px;
1416
      color: var(--muted);
1417
      font-variant-numeric: tabular-nums;
1418
      font-weight: 600;
1419
      text-align: right;
1420
      white-space: nowrap
1421
    }
1422
 
1423
    .status-item .vv b {
1424
      color: var(--ink);
1425
      font-weight: 700
1426
    }
1427
 
1428
    .status-item .pp {
1429
      font-size: 12px;
1430
      font-weight: 700;
1431
      font-variant-numeric: tabular-nums;
1432
      text-align: right
1433
    }
1434
 
1435
    .status-item .pp.good {
1436
      color: var(--good)
1437
    }
1438
 
1439
    .status-item .pp.warn {
1440
      color: var(--warn)
1441
    }
1442
 
1443
    .status-item .pp.bad {
1444
      color: var(--bad)
1445
    }
1446
 
1447
    .status-empty {
1448
      padding: 24px;
1449
      text-align: center;
1450
      color: var(--soft);
1451
      font-size: 12.5px
1452
    }
1453
 
37460 ranu 1454
    /* extras: aging + partner-billing tables */
1455
    .xtable {
1456
      width: 100%;
1457
      border-collapse: separate;
1458
      border-spacing: 0;
1459
      font-size: 13px
1460
    }
1461
 
1462
    .xtable th {
1463
      font-size: 10.5px;
1464
      text-transform: uppercase;
1465
      letter-spacing: .06em;
1466
      color: var(--soft);
1467
      font-weight: 700;
1468
      white-space: nowrap;
1469
      padding: 10px 10px;
1470
      border-bottom: 1px solid var(--line);
1471
      text-align: left;
1472
      background: #fff
1473
    }
1474
 
1475
    .xtable th.num {
1476
      text-align: right
1477
    }
1478
 
1479
    .xtable td {
1480
      padding: 10px;
1481
      border-bottom: 1px solid var(--line2);
1482
      color: var(--ink);
1483
      font-weight: 500
1484
    }
1485
 
1486
    .xtable td.num {
1487
      text-align: right;
1488
      font-variant-numeric: tabular-nums;
1489
      font-weight: 650
1490
    }
1491
 
1492
    .xtable tbody tr:hover {
1493
      background: #f6f8fd
1494
    }
1495
 
1496
    .xtable .rbm-nm {
1497
      font-weight: 700;
1498
      color: var(--ink)
1499
    }
1500
 
1501
    .xtable tr.tot td {
1502
      font-weight: 770;
1503
      background: #fafbfe;
1504
      border-top: 2px solid var(--line);
1505
      color: var(--ink)
1506
    }
1507
 
1508
    .xtable tr.bal td {
1509
      font-weight: 700;
1510
      background: #fffbf2;
1511
      border-top: 1px dashed #f0d68a;
1512
      color: #7a5a00
1513
    }
1514
 
1515
    .xtable tr.bal td.hdr {
1516
      font-weight: 770
1517
    }
1518
 
1519
    .xtable td.g-hid {
1520
      border-left: 2px solid transparent
1521
    }
1522
 
1523
    /* placeholder for future column tint */
1524
    .xtable th.g-hid, .xtable td.g-hid {
1525
      color: var(--hid)
1526
    }
1527
 
1528
    .xtable th.g-fast, .xtable td.g-fast {
1529
      color: var(--fast)
1530
    }
1531
 
1532
    .xtable th.g-run, .xtable td.g-run {
1533
      color: var(--run)
1534
    }
1535
 
1536
    .xtable th.g-slow, .xtable td.g-slow {
1537
      color: var(--slow)
1538
    }
1539
 
1540
    .xtable th.g-other, .xtable td.g-other {
1541
      color: var(--other)
1542
    }
1543
 
1544
    .xtable th.g-total, .xtable td.g-total {
1545
      color: var(--brand);
1546
      font-weight: 700
1547
    }
1548
 
1549
    .xtable td.g-hid, .xtable td.g-fast, .xtable td.g-run, .xtable td.g-slow, .xtable td.g-other {
1550
      opacity: .95
1551
    }
1552
 
1553
    .xtable td.zero {
1554
      color: var(--soft);
1555
      font-weight: 500
1556
    }
1557
 
1558
    .xtable .cnt-pill {
1559
      display: inline-block;
1560
      min-width: 32px;
1561
      padding: 2px 8px;
1562
      border-radius: 6px;
1563
      font-size: 11.5px;
1564
      font-weight: 700;
1565
      font-variant-numeric: tabular-nums;
1566
      background: #eef2ff;
1567
      color: var(--brand)
1568
    }
1569
 
1570
    .xtable .cnt-pill.zero {
1571
      background: #f1f3f9;
1572
      color: var(--soft)
1573
    }
1574
 
1575
    .xtable .cnt-pill.warn {
1576
      background: #fff0e0;
1577
      color: #a04600
1578
    }
1579
 
1580
    .xtable .cnt-pill.good {
1581
      background: #e5f7ee;
1582
      color: #177a3a
1583
    }
1584
 
37444 ranu 1585
    @media (max-width: 960px) {
1586
      .kpis {
1587
        grid-template-columns:repeat(2, 1fr)
1588
      }
1589
 
1590
      .brand h1 {
1591
        font-size: 22px
1592
      }
1593
 
1594
      .status-grid {
1595
        grid-template-columns:1fr
1596
      }
1597
    }
1598
  </style>
1599
  <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/daterangepicker/daterangepicker.css"/>
1600
</head>
1601
<body>
1602
<div class="wrap">
1603
  <header class="top">
1604
    <div class="brand">
1605
      <div class="crumb"><a href="/indent/today_po_rbm">&larr; Today PO / RBM</a> &middot; Sales &middot; Indent</div>
1606
      <h1>RBM DRR Dashboard <span class="live"><i></i>Live</span></h1>
1607
    </div>
1608
    <div class="hd-right">
1609
      <input class="ctl" id="rdrrPeriod" type="text" placeholder="Select date range"/>
1610
      <button class="btn-r" id="rdrrApply">Apply</button>
1611
    </div>
1612
  </header>
1613
 
1614
  <div class="kpis">
1615
    <div class="kpi a">
1616
      <div class="top">
1617
        <div class="ic">
1618
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round"
1619
               stroke-linejoin="round">
1620
            <path d="M12 2v20M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6"/>
1621
          </svg>
1622
        </div>
1623
        <div class="label">Total Achieved</div>
1624
      </div>
1625
      <div class="val" id="rdrrKpiAch">&mdash;</div>
1626
      <div class="foot" id="rdrrKpiAchSub">Actual billed across all RBMs</div>
1627
    </div>
1628
    <div class="kpi b">
1629
      <div class="top">
1630
        <div class="ic">
1631
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round"
1632
               stroke-linejoin="round">
1633
            <circle cx="12" cy="12" r="9"/>
1634
            <circle cx="12" cy="12" r="5"/>
1635
            <circle cx="12" cy="12" r="1.5" fill="currentColor"/>
1636
          </svg>
1637
        </div>
1638
        <div class="label">Total Target</div>
1639
      </div>
1640
      <div class="val" id="rdrrKpiTgt">&mdash;</div>
1641
      <div class="foot" id="rdrrKpiTgtSub">Sum of daily target snapshots</div>
1642
    </div>
1643
    <div class="kpi c">
1644
      <div class="top">
1645
        <div class="ic">
1646
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round"
1647
               stroke-linejoin="round">
1648
            <path d="M3 17l6-6 4 4 8-8"/>
1649
            <path d="M17 7h4v4"/>
1650
          </svg>
1651
        </div>
1652
        <div class="label">Achievement %</div>
1653
      </div>
1654
      <div class="val" id="rdrrKpiPct">&mdash;</div>
1655
      <div class="foot" id="rdrrKpiPctSub">Total ACH / Total TGT</div>
1656
    </div>
1657
    <div class="kpi d clickable" id="rdrrKpiTopCard" title="Click to see achieved vs pending RBMs">
1658
      <div class="top">
1659
        <div class="ic">
1660
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"
1661
               stroke-linejoin="round">
1662
            <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"/>
1663
          </svg>
1664
        </div>
1665
        <div class="label">RBMs ≥ 100% Target</div>
1666
      </div>
1667
      <div class="val" id="rdrrKpiTop">&mdash;</div>
1668
      <div class="foot" id="rdrrKpiTopSub">Rows meeting or beating target</div>
1669
      <span class="hint">Click for list &rsaquo;</span>
1670
    </div>
1671
  </div>
1672
 
1673
  <div class="sectionlabel">RBM &times; Warehouse &times; Movement Bucket</div>
1674
  <div class="legend">
1675
    <span class="lg hid"><i></i><span class="lgt">HID</span></span>
1676
    <span class="lg fast"><i></i><span class="lgt">FAST</span></span>
1677
    <span class="lg run"><i></i><span class="lgt">RUN</span></span>
1678
    <span class="lg slow"><i></i><span class="lgt">SLOW</span></span>
1679
    <span class="lg other"><i></i><span class="lgt">OTHER</span></span>
1680
  </div>
1681
  <div class="card">
1682
    <div class="card-hd">
1683
      <div>
1684
        <h2>Achievement vs Target</h2>
1685
        <p class="desc">Click any row for a per-bucket breakdown and the catalogs sold.</p>
1686
      </div>
1687
      <span class="badge-period" id="rdrrPeriodLabel">&mdash;</span>
1688
    </div>
1689
    <div id="rdrrTableWrap">
1690
      <div class="loading">
1691
        <div class="spinner"></div>
1692
        Loading&hellip;
1693
      </div>
1694
    </div>
1695
    <div class="foot-note">
1696
      <b>TGT</b> is summed from persisted per-day snapshots (<code>inventory.rbm_targets</code>) — days with no
1697
      scheduler-written snapshot contribute 0.
1698
      <b>ACH</b> is computed from source orders (<code>transaction.order</code>) for the range.
1699
    </div>
1700
  </div>
37460 ranu 1701
 
1702
  <!-- >15 Days Aging Sold -->
1703
  <div class="sectionlabel">&gt; 15 Days Aging Sold</div>
1704
  <div class="card">
1705
    <div class="card-hd">
1706
      <div>
1707
        <h2>Aging Inventory Cleared</h2>
1708
        <p class="desc">Billed value of items that were &gt; 15 days old at sale, per RBM, for the picked range.</p>
1709
      </div>
1710
      <span class="badge-period" id="rdrrAgingPeriod">&mdash;</span>
1711
    </div>
1712
    <div id="rdrrAgingWrap">
1713
      <div class="loading">
1714
        <div class="spinner"></div>
1715
        Loading&hellip;
1716
      </div>
1717
    </div>
1718
  </div>
1719
 
1720
  <!-- Partner Billing Count -->
1721
  <div class="sectionlabel">Partner Billing Count <span
1722
          style="color:var(--soft);font-weight:600;text-transform:none;letter-spacing:0;margin-left:6px">(Target: &gt;1L, Non-Target: &gt;20K)</span>
1723
  </div>
1724
  <div class="card">
1725
    <div class="card-hd">
1726
      <div>
1727
        <h2>Partners Who Billed This Month</h2>
1728
        <p class="desc">Counts partners meeting the billing threshold in each week bucket. Always month-to-date (weekly
1729
          buckets are inherently monthly).</p>
1730
      </div>
1731
      <span class="badge-period" id="rdrrBillingPeriod">&mdash;</span>
1732
    </div>
1733
    <div id="rdrrBillingWrap">
1734
      <div class="loading">
1735
        <div class="spinner"></div>
1736
        Loading&hellip;
1737
      </div>
1738
    </div>
1739
  </div>
37444 ranu 1740
</div>
1741
 
1742
<!-- achieved-vs-pending modal (opens from the RBMs ≥ 100% Target KPI card) -->
1743
<div class="status-back" id="rdrrStatusBack">
1744
  <div class="status-modal">
1745
    <button class="x" id="rdrrStatusClose" aria-label="Close">&times;</button>
1746
    <h3>Target Status by RBM</h3>
1747
    <p class="desc" id="rdrrStatusDesc">&mdash;</p>
1748
    <div class="status-grid">
1749
      <div class="status-col good">
1750
        <h4>✅ Achieved <span class="cnt" id="rdrrStatusAchCnt">0</span></h4>
1751
        <div class="status-list" id="rdrrStatusAchList"></div>
1752
      </div>
1753
      <div class="status-col bad">
1754
        <h4>⚠ Pending <span class="cnt" id="rdrrStatusPndCnt">0</span></h4>
1755
        <div class="status-list" id="rdrrStatusPndList"></div>
1756
      </div>
1757
    </div>
1758
  </div>
1759
</div>
1760
 
1761
<!-- drill-down panel -->
1762
<div class="rdrr-back" id="rdrrBack">
1763
  <div class="rdrr-panel">
1764
    <button class="x" id="rdrrClose" aria-label="Close">&times;</button>
1765
    <h3 id="rdrrDdTitle">&mdash;</h3>
1766
    <p class="desc" id="rdrrDdDesc">&mdash;</p>
1767
 
1768
    <div class="rdrr-total">
1769
      <div>
1770
        <div class="k">Achieved</div>
1771
        <div class="v" id="rdrrDdAch">&mdash;</div>
1772
      </div>
1773
      <div>
1774
        <div class="k">Target</div>
1775
        <div class="v" id="rdrrDdTgt">&mdash;</div>
1776
      </div>
1777
      <div>
1778
        <div class="k">Achievement</div>
1779
        <div class="v" id="rdrrDdPct">&mdash;</div>
1780
      </div>
1781
    </div>
1782
 
1783
    <div class="sectionlabel" style="margin-top:2px">Movement Split — Achievement vs Target</div>
1784
    <div class="bbars" id="rdrrDdBars"></div>
1785
 
1786
    <div class="sectionlabel">Brand Mix — Share of Achievement <span
1787
            style="color:var(--soft);font-weight:600;text-transform:none;letter-spacing:0;margin-left:6px">(click a brand for movement split)</span>
1788
    </div>
1789
    <div id="rdrrDdBrands">
1790
      <div class="loading">
1791
        <div class="spinner"></div>
1792
        Loading brand mix&hellip;
1793
      </div>
1794
    </div>
1795
  </div>
1796
</div>
1797
 
1798
<script src="https://cdn.jsdelivr.net/jquery/latest/jquery.min.js"></script>
1799
<script src="https://cdn.jsdelivr.net/momentjs/latest/moment.min.js"></script>
1800
<script src="https://cdn.jsdelivr.net/npm/daterangepicker/daterangepicker.min.js"></script>
1801
<script>
1802
  (function () {
1803
    var DEFAULT_START = "$defaultStartDate";
1804
    var DEFAULT_END = "$defaultEndDate";
1805
 
1806
    // Prefer query-string range if the caller provided one (e.g. from today_po_rbm's date picker).
1807
    var url = new URL(window.location.href);
1808
    var qsStart = url.searchParams.get("startDate");
1809
    var qsEnd = url.searchParams.get("endDate");
1810
    var startDate = qsStart || DEFAULT_START;
1811
    var endDate = qsEnd || DEFAULT_END;
1812
 
1813
    function fmtDate(s) {
1814
      return moment(s).format("DD MMM");
1815
    }
1816
 
1817
    function fmtLakh(v) {
1818
      v = Number(v) || 0;
1819
      return (v / 100000).toFixed(1) + "L";
1820
    }
1821
 
1822
    function pctClass(p) {
1823
      if (p >= 95) return "good";
1824
      if (p >= 41) return "warn";
1825
      return "bad";
1826
    }
1827
 
1828
    function pctLabel(ach, tgt) {
1829
      if (!tgt || tgt <= 0) return {text: "-", cls: "na", num: null};
1830
      var p = (ach / tgt) * 100;
1831
      return {text: p.toFixed(1) + "%", cls: pctClass(p), num: p};
1832
    }
1833
 
1834
    function pctBarWidth(ach, tgt) {
1835
      if (!tgt || tgt <= 0) return 0;
1836
      return Math.min(100, (ach / tgt) * 100);
1837
    }
1838
 
1839
    // Initialise the range picker.
1840
    $("#rdrrPeriod").daterangepicker({
1841
      startDate: moment(startDate),
1842
      endDate: moment(endDate),
1843
      locale: {format: "YYYY-MM-DD", cancelLabel: "Clear"}
1844
    });
1845
    $("#rdrrPeriod").on("apply.daterangepicker", function (ev, picker) {
1846
      startDate = picker.startDate.format("YYYY-MM-DD");
1847
      endDate = picker.endDate.format("YYYY-MM-DD");
1848
    });
1849
 
1850
    // Defensive: read the picker's current state at click time in case
1851
    // the apply.daterangepicker event was swallowed for any reason.
1852
    $("#rdrrApply").on("click", function () {
1853
      var p = $("#rdrrPeriod").data("daterangepicker");
1854
      if (p) {
1855
        startDate = p.startDate.format("YYYY-MM-DD");
1856
        endDate = p.endDate.format("YYYY-MM-DD");
1857
      }
1858
      load();
1859
    });
1860
 
1861
    function load() {
1862
      $("#rdrrTableWrap").html('<div class="loading"><div class="spinner"></div>Loading&hellip;</div>');
37460 ranu 1863
      $("#rdrrAgingWrap").html('<div class="loading"><div class="spinner"></div>Loading&hellip;</div>');
1864
      $("#rdrrBillingWrap").html('<div class="loading"><div class="spinner"></div>Loading&hellip;</div>');
37444 ranu 1865
      $("#rdrrPeriodLabel").text(fmtDate(startDate) + " – " + fmtDate(endDate));
37460 ranu 1866
      $("#rdrrAgingPeriod").text(fmtDate(startDate) + " – " + fmtDate(endDate));
37444 ranu 1867
      fetch("/rbm-drr/data?startDate=" + startDate + "&endDate=" + endDate, {credentials: "same-origin"})
1868
              .then(function (r) {
1869
                return r.json().then(function (b) {
1870
                  return {ok: r.ok, body: b};
1871
                });
1872
              })
1873
              .then(function (res) {
1874
                if (!res.ok) {
1875
                  $("#rdrrTableWrap").html('<div class="empty">' + escapeHtml(res.body && res.body.error ? res.body.error : "Failed to load.") + '</div>');
1876
                  return;
1877
                }
1878
                render(res.body);
1879
              })
1880
              .catch(function (e) {
1881
                $("#rdrrTableWrap").html('<div class="empty">Failed to load: ' + (e && e.message ? e.message : "network error") + '</div>');
1882
              });
37460 ranu 1883
 
1884
      fetch("/rbm-drr/extras?startDate=" + startDate + "&endDate=" + endDate, {credentials: "same-origin"})
1885
              .then(function (r) {
1886
                return r.json().then(function (b) {
1887
                  return {ok: r.ok, body: b};
1888
                });
1889
              })
1890
              .then(function (res) {
1891
                if (!res.ok) {
1892
                  var msg = '<div class="empty">' + escapeHtml(res.body && res.body.error ? res.body.error : "Failed to load.") + '</div>';
1893
                  $("#rdrrAgingWrap").html(msg);
1894
                  $("#rdrrBillingWrap").html(msg);
1895
                  return;
1896
                }
1897
                renderAging(res.body);
1898
                renderPartnerBilling(res.body);
1899
              })
1900
              .catch(function (e) {
1901
                var msg = '<div class="empty">Failed to load: ' + (e && e.message ? e.message : "network error") + '</div>';
1902
                $("#rdrrAgingWrap").html(msg);
1903
                $("#rdrrBillingWrap").html(msg);
1904
              });
37444 ranu 1905
    }
1906
 
37460 ranu 1907
    function renderAging(data) {
1908
      var rows = data.aging || [];
1909
      var totals = data.agingTotals || {};
1910
      var balance = data.balance || {};
1911
      if (rows.length === 0) {
1912
        $("#rdrrAgingWrap").html('<div class="empty"><div class="ei">📦</div>No aging inventory sold in this range.</div>');
1913
        return;
1914
      }
1915
      var html = ['<table class="xtable"><thead><tr>',
1916
        '<th>RBM</th>',
1917
        '<th class="num g-hid">HID</th>',
1918
        '<th class="num g-fast">FAST</th>',
1919
        '<th class="num g-run">RUN</th>',
1920
        '<th class="num g-slow">SLOW</th>',
1921
        '<th class="num g-other">OTHER</th>',
1922
        '<th class="num g-total">TOTAL</th>',
1923
        '</tr></thead><tbody>'];
1924
      rows.forEach(function (r) {
1925
        html.push('<tr>' +
1926
                '<td class="rbm-nm">' + escapeHtml(r.rbmName || '-') + '</td>' +
1927
                cell(r.hid, 'g-hid') + cell(r.fast, 'g-fast') + cell(r.run, 'g-run') +
1928
                cell(r.slow, 'g-slow') + cell(r.other, 'g-other') + cell(r.total, 'g-total') +
1929
                '</tr>');
1930
      });
1931
      html.push('<tr class="tot">' +
1932
              '<td>Total</td>' +
1933
              cell(totals.hid, 'g-hid') + cell(totals.fast, 'g-fast') + cell(totals.run, 'g-run') +
1934
              cell(totals.slow, 'g-slow') + cell(totals.other, 'g-other') + cell(totals.total, 'g-total') +
1935
              '</tr>');
1936
      html.push('<tr class="bal">' +
1937
              '<td class="hdr">15-Day Balance Value</td>' +
1938
              cell(balance.hid) + cell(balance.fast) + cell(balance.run) +
1939
              cell(balance.slow) + cell(balance.other) + cell(balance.total) +
1940
              '</tr>');
1941
      html.push('</tbody></table>');
1942
      $("#rdrrAgingWrap").html(html.join(""));
1943
    }
1944
 
1945
    function cell(v, cls) {
1946
      v = Number(v) || 0;
1947
      var zero = v === 0 ? ' zero' : '';
1948
      var extra = cls ? ' ' + cls : '';
1949
      return '<td class="num' + extra + zero + '">' + formatIndian(v) + '</td>';
1950
    }
1951
 
1952
    function formatIndian(n) {
1953
      n = Math.round(Number(n) || 0);
1954
      if (n === 0) return '0';
1955
      var s = n.toString();
1956
      var lastThree = s.substring(s.length - 3);
1957
      var otherNums = s.substring(0, s.length - 3);
1958
      if (otherNums !== '') lastThree = ',' + lastThree;
1959
      return otherNums.replace(/\B(?=(\d{2})+(?!\d))/g, ',') + lastThree;
1960
    }
1961
 
1962
    function renderPartnerBilling(data) {
1963
      var rows = data.partnerBilling || [];
1964
      var totals = data.partnerBillingTotals || {};
1965
      $("#rdrrBillingPeriod").text(data.partnerBillingMonth || '');
1966
      if (rows.length === 0) {
1967
        $("#rdrrBillingWrap").html('<div class="empty"><div class="ei">👥</div>No targeted partners this month.</div>');
1968
        return;
1969
      }
1970
      var html = ['<table class="xtable"><thead><tr>',
1971
        '<th>RBM</th>',
1972
        '<th class="num">Targeted</th>',
1973
        '<th class="num">Week 1<br><small style="color:var(--soft);font-weight:600">(1–7)</small></th>',
1974
        '<th class="num">Week 2<br><small style="color:var(--soft);font-weight:600">(8–15)</small></th>',
1975
        '<th class="num">Week 3<br><small style="color:var(--soft);font-weight:600">(16–22)</small></th>',
1976
        '<th class="num">Week 4<br><small style="color:var(--soft);font-weight:600">(23–end)</small></th>',
1977
        '<th class="num g-total">MTD</th>',
1978
        '<th class="num">Zero Billed<br><small style="color:var(--soft);font-weight:600">(&lt;1L / &lt;20K)</small></th>',
1979
        '</tr></thead><tbody>'];
1980
      rows.forEach(function (r) {
1981
        html.push('<tr>' +
1982
                '<td class="rbm-nm">' + escapeHtml(r.rbmName || '-') + '</td>' +
1983
                '<td class="num">' + pill(r.targeted, '') + '</td>' +
1984
                '<td class="num">' + pill(r.w1, ratio(r.w1, r.targeted)) + '</td>' +
1985
                '<td class="num">' + pill(r.w2, ratio(r.w2, r.targeted)) + '</td>' +
1986
                '<td class="num">' + pill(r.w3, ratio(r.w3, r.targeted)) + '</td>' +
1987
                '<td class="num">' + pill(r.w4, ratio(r.w4, r.targeted)) + '</td>' +
1988
                '<td class="num g-total">' + pill(r.mtd, ratio(r.mtd, r.targeted)) + '</td>' +
1989
                '<td class="num">' + pill(r.zero, r.zero > 0 ? 'warn' : 'zero') + '</td>' +
1990
                '</tr>');
1991
      });
1992
      html.push('<tr class="tot">' +
1993
              '<td>Total</td>' +
1994
              '<td class="num">' + formatIndian(totals.targeted) + '</td>' +
1995
              '<td class="num">' + formatIndian(totals.w1) + '</td>' +
1996
              '<td class="num">' + formatIndian(totals.w2) + '</td>' +
1997
              '<td class="num">' + formatIndian(totals.w3) + '</td>' +
1998
              '<td class="num">' + formatIndian(totals.w4) + '</td>' +
1999
              '<td class="num g-total">' + formatIndian(totals.mtd) + '</td>' +
2000
              '<td class="num">' + formatIndian(totals.zero) + '</td>' +
2001
              '</tr>');
2002
      html.push('</tbody></table>');
2003
      $("#rdrrBillingWrap").html(html.join(""));
2004
    }
2005
 
2006
    function pill(v, cls) {
2007
      v = Number(v) || 0;
2008
      var c = cls || (v === 0 ? 'zero' : '');
2009
      return '<span class="cnt-pill ' + c + '">' + formatIndian(v) + '</span>';
2010
    }
2011
 
2012
    function ratio(part, total) {
2013
      part = Number(part) || 0;
2014
      total = Number(total) || 0;
2015
      if (total === 0) return 'zero';
2016
      var p = (part / total) * 100;
2017
      if (p >= 60) return 'good';
2018
      if (p >= 30) return '';
2019
      if (p > 0) return 'warn';
2020
      return 'zero';
2021
    }
2022
 
37444 ranu 2023
    // Cached latest payload so the "Target Status by RBM" modal can render
2024
    // without a re-fetch when the KPI card is clicked.
2025
    var LAST_PAYLOAD = null;
2026
 
2027
    function render(data) {
2028
      LAST_PAYLOAD = data;
2029
      var rows = data.rows || [];
2030
      var t = data.totals || {};
2031
 
2032
      $("#rdrrKpiAch").html('&#8377;&nbsp;' + fmtLakh(t.totalAch));
2033
      $("#rdrrKpiTgt").html('&#8377;&nbsp;' + fmtLakh(t.totalTgt));
2034
      var pk = pctLabel(t.totalAch, t.totalTgt);
2035
      $("#rdrrKpiPct").html(pk.cls === "na" ? '<span class="u">n/a</span>' :
2036
              '<span class="pct ' + pk.cls + '">' + pk.text + '</span>');
2037
      $("#rdrrKpiTop").html((t.rbmsAtOrAbove100 || 0) + ' <span class="u">/ ' + (t.rbmRowCount || 0) + '</span>');
2038
 
2039
      if (rows.length === 0) {
2040
        $("#rdrrTableWrap").html('<div class="empty"><div class="ei">📭</div>No RBM rows in this range.</div>');
2041
        return;
2042
      }
2043
 
2044
      var buckets = [
2045
        {key: "hid", label: "HID"},
2046
        {key: "fast", label: "FAST"},
2047
        {key: "run", label: "RUN"},
2048
        {key: "slow", label: "SLOW"},
2049
        {key: "other", label: "OTHER"}
2050
      ];
2051
 
2052
      var html = ['<table id="rdrrGrid"><thead><tr>',
2053
        '<th>RBM &middot; Warehouse</th>',
2054
        '<th class="num g-total">Total<br><small style="color:var(--soft);font-weight:600">ACH / TGT</small></th>',
2055
        '<th class="num g-total">%</th>'];
2056
      buckets.forEach(function (b) {
2057
        html.push('<th class="num g-' + b.key + '">' + b.label + '<br><small style="color:var(--soft);font-weight:600">ACH / TGT</small></th>');
2058
        html.push('<th class="num g-' + b.key + '">%</th>');
2059
      });
2060
      html.push('<th></th></tr></thead><tbody>');
2061
 
2062
      rows.forEach(function (r) {
2063
        var tr = ['<tr class="clickable" data-authid="' + r.authId + '" data-warehouseid="' + r.warehouseId + '" data-rbmname="' + escapeHtml(r.rbmName) + '" data-warehousename="' + escapeHtml(r.warehouseName) + '">'];
2064
        tr.push('<td><div class="rbm-cell"><span class="rn">' + escapeHtml(r.rbmName) + '</span><span class="wh">' + escapeHtml(r.warehouseName) + '</span></div></td>');
2065
        tr.push(cellPair(r.totalAch, r.totalTgt));
2066
        buckets.forEach(function (b) {
2067
          tr.push(cellPair(r[b.key + "Ach"], r[b.key + "Tgt"]));
2068
        });
2069
        tr.push('<td class="chev">&rsaquo;</td>');
2070
        tr.push('</tr>');
2071
        html.push(tr.join(""));
2072
      });
2073
 
2074
      // totals row
2075
      var totTr = ['<tr class="totalrow"><td><strong>Total</strong></td>'];
2076
      totTr.push(cellPair(t.totalAch, t.totalTgt, true));
2077
      buckets.forEach(function (b) {
2078
        totTr.push(cellPair(t[b.key + "Ach"], t[b.key + "Tgt"], true));
2079
      });
2080
      totTr.push('<td></td></tr>');
2081
      html.push(totTr.join(""));
2082
 
2083
      html.push('</tbody></table>');
2084
      $("#rdrrTableWrap").html(html.join(""));
2085
 
2086
      // Cache the parsed row on each <tr> so drill-down can render bars immediately.
2087
      rows.forEach(function (r) {
2088
        $("#rdrrGrid tbody tr").filter(function () {
2089
          return +this.dataset.authid === r.authId && +this.dataset.warehouseid === r.warehouseId;
2090
        }).each(function () {
2091
          $(this).data("cached", r);
2092
        });
2093
      });
2094
 
2095
      $("#rdrrGrid tbody tr.clickable").on("click", function () {
2096
        var d = this.dataset;
2097
        var cached = $(this).data("cached");
2098
        openDrilldown(+d.authid, +d.warehouseid, d.rbmname, d.warehousename, cached);
2099
      });
2100
    }
2101
 
2102
    function cellPair(ach, tgt, isTotal) {
2103
      var p = pctLabel(ach, tgt);
2104
      var vhtml = '<td class="num"><div class="cell-val">' + fmtLakh(ach) + ' <small>/ ' + fmtLakh(tgt) + '</small></div></td>';
2105
      var barCls = p.cls === "na" ? "" : p.cls;
2106
      var barW = pctBarWidth(ach, tgt);
2107
      var pctHtml = '<td class="num"><span class="pct ' + p.cls + '">' + p.text + '</span>' +
2108
              (barCls ? '<div class="bar"><i class="' + barCls + '" style="width:' + barW.toFixed(1) + '%"></i></div>' : '') +
2109
              '</td>';
2110
      return vhtml + pctHtml;
2111
    }
2112
 
2113
    // --------------------------- drill-down ---------------------------
2114
 
2115
    function openDrilldown(authId, warehouseId, rbmName, warehouseName, cachedRow) {
2116
      var back = $("#rdrrBack");
2117
      $("#rdrrDdTitle").text(rbmName + " — " + warehouseName);
2118
      $("#rdrrDdDesc").text(fmtDate(startDate) + " – " + fmtDate(endDate));
2119
      $("#rdrrDdBars").empty();
2120
      back.addClass("show");
2121
 
2122
      if (cachedRow) {
2123
        renderBars(cachedRow);
2124
      } else {
2125
        $("#rdrrDdAch").text("—");
2126
        $("#rdrrDdTgt").text("—");
2127
        $("#rdrrDdPct").text("—");
2128
      }
2129
      $("#rdrrDdBrands").html('<div class="loading"><div class="spinner"></div>Loading brand mix&hellip;</div>');
2130
 
2131
      fetch("/rbm-drr/rbm-detail?authId=" + authId + "&warehouseId=" + warehouseId +
2132
              "&startDate=" + startDate + "&endDate=" + endDate, {credentials: "same-origin"})
2133
              .then(function (r) {
2134
                return r.json();
2135
              })
2136
              .then(function (data) {
2137
                renderBrands(data);
2138
              })
2139
              .catch(function (e) {
2140
                $("#rdrrDdBrands").html('<div class="empty">Failed to load: ' + (e && e.message ? e.message : "network error") + '</div>');
2141
              });
2142
    }
2143
 
2144
    function renderBrands(data) {
2145
      var brands = (data && data.brands) || [];
2146
      if (brands.length === 0) {
2147
        $("#rdrrDdBrands").html('<div class="empty"><div class="ei">🏷️</div>No brand sales in this range.</div>');
2148
        return;
2149
      }
2150
      var maxShare = brands[0].sharePct || 0.001;
2151
      var html = '<div class="brands">';
2152
      brands.forEach(function (b, idx) {
2153
        var barW = maxShare > 0 ? Math.min(100, (b.sharePct / maxShare) * 100) : 0;
2154
        var topCls = idx === 0 ? ' top' : '';
37460 ranu 2155
        var pc = Number(b.partnerCount) || 0;
2156
        var pcCls = pc === 0 ? ' zero' : '';
2157
        var pcLabel = pc + (pc === 1 ? ' partner' : ' partners');
37444 ranu 2158
        html += '<div class="brand-card' + topCls + '" data-idx="' + idx + '">' +
2159
                '<div class="brand-head">' +
2160
                '<div class="cx">&rsaquo;</div>' +
2161
                '<div class="bn" title="' + escapeHtml(b.brand) + '">' + escapeHtml(b.brand) + '</div>' +
2162
                '<div class="btrack"><i style="width:' + barW.toFixed(1) + '%"></i></div>' +
37460 ranu 2163
                '<div class="bpc' + pcCls + '" title="Distinct partners of this RBM who billed ' + escapeHtml(b.brand) + ' in the range">' +
2164
                '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M22 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>' +
2165
                pcLabel +
2166
                '</div>' +
37444 ranu 2167
                '<div class="bval">' + fmtLakh(b.valueSold) + '</div>' +
2168
                '<div class="bshare">' + (b.sharePct || 0).toFixed(1) + '%</div>' +
2169
                '</div>' +
2170
                '<div class="brand-body">' +
2171
                '<div class="bhdr">Movement Split within ' + escapeHtml(b.brand) + '</div>' +
2172
                renderBrandBuckets(b.buckets || []) +
2173
                '</div>' +
2174
                '</div>';
2175
      });
2176
      html += '</div>';
2177
      $("#rdrrDdBrands").html(html);
2178
 
2179
      $("#rdrrDdBrands .brand-head").on("click", function () {
2180
        $(this).closest(".brand-card").toggleClass("open");
2181
      });
2182
    }
2183
 
2184
    function renderBrandBuckets(buckets) {
2185
      if (!buckets || buckets.length === 0) {
2186
        return '<div style="color:var(--soft);font-size:12px;padding:8px 4px">No movement split available.</div>';
2187
      }
2188
      var labels = {HID: "HID", FASTMOVING: "FAST", RUNNING: "RUN", SLOWMOVING: "SLOW", OTHER: "OTHER"};
2189
      var html = '<div class="mbars">';
2190
      buckets.forEach(function (bk) {
2191
        var cls = (bk.status || "OTHER").toLowerCase();
2192
        var label = labels[bk.status] || bk.status || "OTHER";
2193
        var sp = bk.sharePctOfBrand || 0;
2194
        var barW = Math.min(100, sp);
2195
        html += '<div class="mbar ' + cls + '">' +
2196
                '<div class="mname"><i></i>' + label + '</div>' +
2197
                '<div class="mtrack"><i style="width:' + barW.toFixed(1) + '%"></i></div>' +
2198
                '<div class="mval">' + fmtLakh(bk.valueSold) + '</div>' +
2199
                '<div class="mshare">' + sp.toFixed(1) + '%</div>' +
2200
                '</div>';
2201
      });
2202
      html += '</div>';
2203
      return html;
2204
    }
2205
 
2206
    function renderBars(row) {
2207
      var totalAch = row.totalAch, totalTgt = row.totalTgt;
2208
      $("#rdrrDdAch").html('&#8377;&nbsp;' + fmtLakh(totalAch));
2209
      $("#rdrrDdTgt").html('&#8377;&nbsp;' + fmtLakh(totalTgt));
2210
      var pt = pctLabel(totalAch, totalTgt);
2211
      $("#rdrrDdPct").html(pt.cls === "na" ? '<span style="color:var(--soft);font-size:14px">n/a</span>' :
2212
              '<span class="pct ' + pt.cls + '">' + pt.text + '</span>');
2213
 
2214
      var buckets = [
2215
        {key: "hid", label: "HID"},
2216
        {key: "fast", label: "FAST"},
2217
        {key: "run", label: "RUN"},
2218
        {key: "slow", label: "SLOW"},
2219
        {key: "other", label: "OTHER"}
2220
      ];
2221
      var html = "";
2222
      buckets.forEach(function (b) {
2223
        var ach = row[b.key + "Ach"], tgt = row[b.key + "Tgt"];
2224
        var p = pctLabel(ach, tgt);
2225
        var w = pctBarWidth(ach, tgt);
2226
        html += '<div class="bbar ' + b.key + '">' +
2227
                '<div class="bname"><i></i>' + b.label + '</div>' +
2228
                '<div class="btrack"><i style="width:' + w.toFixed(1) + '%"></i></div>' +
2229
                '<div class="btgt"><b>' + fmtLakh(ach) + '</b> / ' + fmtLakh(tgt) + '</div>' +
2230
                '<div class="bpct ' + p.cls + '">' + p.text + '</div>' +
2231
                '</div>';
2232
      });
2233
      $("#rdrrDdBars").html(html);
2234
    }
2235
 
2236
    function escapeHtml(s) {
2237
      if (s == null) return "";
2238
      return String(s).replace(/[&<>"']/g, function (c) {
2239
        return {"&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;"}[c];
2240
      });
2241
    }
2242
 
2243
    $("#rdrrClose").on("click", function () {
2244
      $("#rdrrBack").removeClass("show");
2245
    });
2246
    $("#rdrrBack").on("click", function (e) {
2247
      if (e.target === this) $(this).removeClass("show");
2248
    });
2249
 
2250
    // Target-status modal (achieved vs pending) — opens from the KPI card.
2251
    $("#rdrrKpiTopCard").on("click", function () {
2252
      openStatusModal();
2253
    });
2254
    $("#rdrrStatusClose").on("click", function () {
2255
      $("#rdrrStatusBack").removeClass("show");
2256
    });
2257
    $("#rdrrStatusBack").on("click", function (e) {
2258
      if (e.target === this) $(this).removeClass("show");
2259
    });
2260
 
2261
    function openStatusModal() {
2262
      if (!LAST_PAYLOAD) return;
2263
      var rows = (LAST_PAYLOAD.rows || []).slice();
2264
      var achieved = [], pending = [];
2265
      rows.forEach(function (r) {
2266
        if (!r.totalTgt || r.totalTgt <= 0) return;
2267
        var pct = (r.totalAch / r.totalTgt) * 100;
2268
        var enriched = {row: r, pct: pct};
2269
        if (r.totalAch >= r.totalTgt) achieved.push(enriched);
2270
        else pending.push(enriched);
2271
      });
2272
      achieved.sort(function (a, b) {
2273
        return b.pct - a.pct;
2274
      });
2275
      pending.sort(function (a, b) {
2276
        return a.pct - b.pct;
2277
      });
2278
 
2279
      $("#rdrrStatusDesc").text(fmtDate(startDate) + " – " + fmtDate(endDate));
2280
      $("#rdrrStatusAchCnt").text(achieved.length);
2281
      $("#rdrrStatusPndCnt").text(pending.length);
2282
      $("#rdrrStatusAchList").html(renderStatusList(achieved, "achieved"));
2283
      $("#rdrrStatusPndList").html(renderStatusList(pending, "pending"));
2284
      $("#rdrrStatusBack").addClass("show");
2285
 
2286
      // Bind click on each item to open the same drill-down slide-in.
2287
      $("#rdrrStatusBack .status-item").on("click", function () {
2288
        var d = this.dataset;
2289
        var cachedRow = null;
2290
        (LAST_PAYLOAD.rows || []).some(function (r) {
2291
          if (r.authId == d.authid && r.warehouseId == d.warehouseid) {
2292
            cachedRow = r;
2293
            return true;
2294
          }
2295
          return false;
2296
        });
2297
        $("#rdrrStatusBack").removeClass("show");
2298
        openDrilldown(+d.authid, +d.warehouseid, d.rbmname, d.warehousename, cachedRow);
2299
      });
2300
    }
2301
 
2302
    function renderStatusList(items, kind) {
2303
      if (items.length === 0) {
2304
        return '<div class="status-empty">' +
2305
                (kind === "achieved" ? "No RBM has met target in this range." : "All RBMs met target — nice!") +
2306
                '</div>';
2307
      }
2308
      var html = "";
2309
      items.forEach(function (it) {
2310
        var r = it.row;
2311
        var p = pctClass(it.pct);
2312
        html += '<div class="status-item" title="Click to view drill-down"' +
2313
                ' data-authid="' + r.authId + '" data-warehouseid="' + r.warehouseId + '"' +
2314
                ' data-rbmname="' + escapeHtml(r.rbmName) + '" data-warehousename="' + escapeHtml(r.warehouseName) + '"' +
2315
                ' style="cursor:pointer">' +
2316
                '<div><span class="rn">' + escapeHtml(r.rbmName) + '</span>' +
2317
                '<span class="wh">' + escapeHtml(r.warehouseName) + '</span></div>' +
2318
                '<div class="vv"><b>' + fmtLakh(r.totalAch) + '</b> / ' + fmtLakh(r.totalTgt) + '</div>' +
2319
                '<div class="pp ' + p + '">' + it.pct.toFixed(1) + '%</div>' +
2320
                '</div>';
2321
      });
2322
      return html;
2323
    }
2324
 
2325
    // initial load
2326
    load();
2327
  })();
2328
</script>
2329
</body>
2330
</html>