| Line 31... |
Line 31... |
| 31 |
.btn-r{background:var(--brand);border:0;color:#fff;font-weight:650;padding:9px 15px;border-radius:9px;cursor:pointer;font-size:13px;height:36px}
|
31 |
.btn-r{background:var(--brand);border:0;color:#fff;font-weight:650;padding:9px 15px;border-radius:9px;cursor:pointer;font-size:13px;height:36px}
|
| 32 |
.lenses{display:flex;gap:4px;border-bottom:1px solid var(--line);margin-bottom:20px}
|
32 |
.lenses{display:flex;gap:4px;border-bottom:1px solid var(--line);margin-bottom:20px}
|
| 33 |
.lens{padding:11px 4px;margin-right:22px;font-size:14px;font-weight:650;color:var(--muted);cursor:pointer;border:0;background:transparent;border-bottom:2.5px solid transparent;margin-bottom:-1px}
|
33 |
.lens{padding:11px 4px;margin-right:22px;font-size:14px;font-weight:650;color:var(--muted);cursor:pointer;border:0;background:transparent;border-bottom:2.5px solid transparent;margin-bottom:-1px}
|
| 34 |
.lens.active{color:var(--brand);border-bottom-color:var(--brand)}
|
34 |
.lens.active{color:var(--brand);border-bottom-color:var(--brand)}
|
| 35 |
.lens .b{display:inline-block;background:#eef1fb;color:var(--brand);font-size:11px;font-weight:700;border-radius:999px;padding:1px 7px;margin-left:6px}
|
35 |
.lens .b{display:inline-block;background:#eef1fb;color:var(--brand);font-size:11px;font-weight:700;border-radius:999px;padding:1px 7px;margin-left:6px}
|
| 36 |
.kpis{display:grid;grid-template-columns:repeat(6,1fr);gap:12px;margin-bottom:20px}
|
36 |
.kpis{display:grid;grid-template-columns:repeat(4,1fr);gap:12px;margin-bottom:20px}
|
| 37 |
.kpi{background:var(--card);border:1px solid var(--line);border-radius:13px;padding:14px 15px;box-shadow:0 1px 2px rgba(20,30,60,.04)}
|
37 |
.kpi{background:var(--card);border:1px solid var(--line);border-radius:13px;padding:14px 15px;box-shadow:0 1px 2px rgba(20,30,60,.04)}
|
| 38 |
.kpi.clickable{cursor:pointer;transition:border-color .12s,box-shadow .12s,transform .12s}
|
38 |
.kpi.clickable{cursor:pointer;transition:border-color .12s,box-shadow .12s,transform .12s}
|
| 39 |
.kpi.clickable:hover{border-color:#c7d0e8;box-shadow:0 3px 10px rgba(20,30,60,.09);transform:translateY(-1px)}
|
39 |
.kpi.clickable:hover{border-color:#c7d0e8;box-shadow:0 3px 10px rgba(20,30,60,.09);transform:translateY(-1px)}
|
| 40 |
.kpi .top{display:flex;align-items:center;gap:8px}
|
40 |
.kpi .top{display:flex;align-items:center;gap:8px}
|
| 41 |
.kpi .ic{width:28px;height:28px;border-radius:8px;display:flex;align-items:center;justify-content:center;font-size:14px;flex:none}
|
41 |
.kpi .ic{width:28px;height:28px;border-radius:8px;display:flex;align-items:center;justify-content:center;font-size:14px;flex:none}
|
| Line 95... |
Line 95... |
| 95 |
.bdot{display:inline-block;width:9px;height:9px;border-radius:50%;margin-right:6px;vertical-align:middle}
|
95 |
.bdot{display:inline-block;width:9px;height:9px;border-radius:50%;margin-right:6px;vertical-align:middle}
|
| 96 |
.ptime{display:inline-block;background:#ebfbee;color:#2f9e44;font-weight:700;font-size:11px;padding:2px 7px;border-radius:6px;font-variant-numeric:tabular-nums}
|
96 |
.ptime{display:inline-block;background:#ebfbee;color:#2f9e44;font-weight:700;font-size:11px;padding:2px 7px;border-radius:6px;font-variant-numeric:tabular-nums}
|
| 97 |
.potime{display:inline-block;background:#ebfbee;color: #D60303A8;font-weight:700;font-size:11px;padding:2px 7px;border-radius:6px;font-variant-numeric:tabular-nums}
|
97 |
.potime{display:inline-block;background:#ebfbee;color: #D60303A8;font-weight:700;font-size:11px;padding:2px 7px;border-radius:6px;font-variant-numeric:tabular-nums}
|
| 98 |
td.cnt{font-weight:700;font-variant-numeric:tabular-nums}
|
98 |
td.cnt{font-weight:700;font-variant-numeric:tabular-nums}
|
| 99 |
.cgreen{color:#2f9e44}.camber{color:#f08c00}.cred{color:#e03131}.cmuted{color:#69748c}
|
99 |
.cgreen{color:#2f9e44}.camber{color:#f08c00}.cred{color:#e03131}.cmuted{color:#69748c}
|
| 100 |
tr.boardtot td{background:#f4f6fb;border-top:2px solid #cdd3df;font-weight:700}
|
100 |
tr.boardtot td{background:#f4f6fb;border-bottom:2px solid #cdd3df;font-weight:700}
|
| 101 |
.actbtn{display:inline-flex;align-items:center;justify-content:center;width:26px;height:26px;border-radius:7px;border:0;cursor:pointer;color:#fff;font-size:12px;margin-left:3px}
|
101 |
.actbtn{display:inline-flex;align-items:center;justify-content:center;width:26px;height:26px;border-radius:7px;border:0;cursor:pointer;color:#fff;font-size:12px;margin-left:3px}
|
| 102 |
.actbtn.map{background:#12b886}.actbtn.view{background:#3b5bdb}
|
102 |
.actbtn.map{background:#12b886}.actbtn.view{background:#3b5bdb}
|
| 103 |
.actbtn.edit{background:#f08c00}.actbtn.add{background:#2f9e44;font-size:15px}
|
103 |
.actbtn.edit{background:#f08c00}.actbtn.add{background:#2f9e44;font-size:15px}
|
| 104 |
.actbtn:active{transform:translateY(1px)}
|
104 |
.actbtn:active{transform:translateY(1px)}
|
| 105 |
.att{display:flex;align-items:center;gap:12px;padding:11px 12px;border:1px solid var(--line);border-radius:11px;margin-bottom:9px}
|
105 |
.att{display:flex;align-items:center;gap:12px;padding:11px 12px;border:1px solid var(--line);border-radius:11px;margin-bottom:9px}
|
| Line 363... |
Line 363... |
| 363 |
window._canApprove = !!d.canApprove;
|
363 |
window._canApprove = !!d.canApprove;
|
| 364 |
window._mediaUrl = d.mediaUrl || window._mediaUrl || '';
|
364 |
window._mediaUrl = d.mediaUrl || window._mediaUrl || '';
|
| 365 |
window._board = d.board || []; // funnel drill-down filters this
|
365 |
window._board = d.board || []; // funnel drill-down filters this
|
| 366 |
window._boardDate = d.date || '';
|
366 |
window._boardDate = d.date || '';
|
| 367 |
window._leadsList = d.leadsList || []; // leads drill-down
|
367 |
window._leadsList = d.leadsList || []; // leads drill-down
|
| - |
|
368 |
window._execList = d.execList || []; // main-card drill-downs
|
| 368 |
var h='';
|
369 |
var h='';
|
| 369 |
h+='<div class="kpis">';
|
370 |
h+='<div class="kpis">';
|
| 370 |
h+=kpi('👥','#eef1fb','#3b5bdb','Executives scheduled',num(k.scheduled),k.notStarted+' not started yet');
|
371 |
h+=kpi('👥','#eef1fb','#3b5bdb','Total Executives',num(k.totalExecs),'in your team',null,"openCards('total')");
|
| 371 |
h+=kpi('⏱','#ebfbee','#2f9e44','Punched in',num(k.punchedIn)+' <small>/'+k.scheduled+'</small>',k.notStarted+' pending');
|
372 |
h+=kpi('📅','#ebfbee','#2f9e44','Scheduled PJP',num(k.scheduledPjp)+' <small>/'+k.totalExecs+'</small>',k.unscheduled+' unscheduled',null,"openCards('scheduled')");
|
| 372 |
h+=kpi('📍','#e7f5ff','#1098ad','On route now',num(k.onRouteNow),'live journeys');
|
- |
|
| 373 |
h+=kpi('✓','#eef1fb','#3b5bdb','Visit vs Planned',num(k.visitsDone)+' <small>/'+k.visitsPlanned+'</small>',k.inProgress+' in progress');
|
373 |
h+=kpi('✎','#fff4e6','#e8590c','Agenda Filled',num(k.agendaFilled)+' <small>/'+k.scheduledPjp+'</small>',(k.scheduledPjp-k.agendaFilled)+' pending',null,"openCards('agenda')");
|
| 374 |
h+=kpi('↻','#fff4e6','#e8590c','Deferred today',num(k.deferred),'shop closed / unavailable');
|
- |
|
| 375 |
h+=kpi('★','#fff0f6','#d6336c','Leads on route',num(k.leads),'new shops logged',null,'openLeads()');
|
374 |
h+=kpi('❌','#fff0f6','#d6336c','Unscheduled',num(k.unscheduled),'no PJP today',null,"openCards('unscheduled')");
|
| 376 |
h+='</div>';
|
375 |
h+='</div>';
|
| 377 |
|
376 |
|
| 378 |
h+='<div class="sectionlabel">Today\'s Journey Funnel</div><div class="card"><div class="funnel">';
|
377 |
h+='<div class="sectionlabel">Today\'s Journey Funnel</div><div class="card"><div class="funnel">';
|
| 379 |
var sc=f.scheduled||1;
|
378 |
var sp=f.scheduledPjp||1, pin=f.punchedIn||1;
|
| 380 |
h+=fstep('Scheduled executives',f.scheduled,100,'start of day',false,false,'scheduled');
|
379 |
h+=fstep('Punched In',f.punchedIn,pct(f.punchedIn,sp),f.punchedIn+' of '+f.scheduledPjp+' scheduled',false,false,'punchedIn');
|
| 381 |
h+=fstep('Punched in',f.punchedIn,pct(f.punchedIn,sc),'−'+(f.scheduled-f.punchedIn)+' never started',true,false,'punchedIn');
|
380 |
h+=fstep('Traveling',f.travelling,pct(f.travelling,pin),'on the road',false,false,'travelling');
|
| 382 |
h+=fstep('Made a check-in',f.madeCheckin,pct(f.madeCheckin,sc),'−'+(f.punchedIn-f.madeCheckin)+' punched, idle',true,false,'madeCheckin');
|
381 |
h+=fstep('In Store',f.inStore,pct(f.inStore,pin),'at a stop',false,false,'inStore');
|
| 383 |
h+=fstep('Completed all visits',f.completedAll,pct(f.completedAll,sc),(f.madeCheckin-f.completedAll)+' still mid-journey',true,false,'completedAll');
|
382 |
h+=fstep('Idle / Break',f.idle,pct(f.idle,pin),'off-store > 40 min',true,false,'idle');
|
| 384 |
h+=fstep('Punched out',f.punchedOut,pct(f.punchedOut,sc),'journey ended',false,true,'punchedOut');
|
383 |
h+=fstep('Punched Out',f.punchedOut,pct(f.punchedOut,pin),'journey ended',false,true,'punchedOut');
|
| 385 |
h+='</div></div>';
|
384 |
h+='</div></div>';
|
| 386 |
|
385 |
|
| 387 |
window._boardFilter = null; // funnel-card filter (null = all rows)
|
386 |
window._boardFilter = null; // funnel-card filter (null = all rows)
|
| 388 |
h+='<div class="grid g3">';
|
387 |
h+='<div class="grid g3">';
|
| 389 |
h+='<div class="card"><h2>Live executive board</h2><p class="desc">Each executive\'s journey right now. Click a row for the day\'s visit detail.</p><div id="bj-board">'+boardCard()+'</div></div>';
|
388 |
h+='<div class="card"><h2>Live executive board</h2><p class="desc">Each executive\'s journey right now. Click a row for the day\'s visit detail.</p><div id="bj-board">'+boardCard()+'</div></div>';
|
| Line 450... |
Line 449... |
| 450 |
'<th>Agenda</th><th>Executive</th><th>Punch In/Out</th>'+
|
449 |
'<th>Agenda</th><th>Executive</th><th>Punch In/Out</th>'+
|
| 451 |
'<th class="num" title="Completed − Deferred − Pending / Planned">Planned - Visits</th>'+
|
450 |
'<th class="num" title="Completed − Deferred − Pending / Planned">Planned - Visits</th>'+
|
| 452 |
'<th class="num" title="Completed − Deferred − Pending / Planned">Self Assigned Lead</th>'+
|
451 |
'<th class="num" title="Completed − Deferred − Pending / Planned">Self Assigned Lead</th>'+
|
| 453 |
'<th class="num">Total</th><th>State</th><th>Action</th>'+
|
452 |
'<th class="num">Total</th><th>State</th><th>Action</th>'+
|
| 454 |
'</tr></thead><tbody>';
|
453 |
'</tr></thead><tbody>';
|
| - |
|
454 |
// column totals, computed up front so the Total row can lead the table
|
| 455 |
var t={cp:0,dp:0,pp:0,cl:0,dl:0,pl:0,tot:0}; // column totals
|
455 |
var t={cp:0,dp:0,pp:0,cl:0,dl:0,pl:0,tot:0};
|
| 456 |
rows.forEach(function(r){
|
456 |
rows.forEach(function(r){
|
| 457 |
t.cp+=r.completedPartner||0; t.dp+=r.deferredPartner||0; t.pp+=r.plannedPartner||0;
|
457 |
t.cp+=r.completedPartner||0; t.dp+=r.deferredPartner||0; t.pp+=r.plannedPartner||0;
|
| 458 |
t.cl+=r.completedLead||0; t.dl+=r.deferredLead||0; t.pl+=r.plannedLead||0;
|
458 |
t.cl+=r.completedLead||0; t.dl+=r.deferredLead||0; t.pl+=r.plannedLead||0;
|
| 459 |
t.tot+=r.totalVisits||0;
|
459 |
t.tot+=r.totalVisits||0;
|
| - |
|
460 |
});
|
| - |
|
461 |
h+='<tr class="boardtot">'+
|
| - |
|
462 |
'<td></td><td><b>Total</b><br><span style="color:#97a1b5;font-size:11px">'+rows.length+' executives</span></td><td></td>'+
|
| - |
|
463 |
'<td class="num cnt">'+cdp(t.cp,t.dp,t.pp)+'</td>'+
|
| - |
|
464 |
'<td class="num cnt">'+cdp(t.cl,t.dl,t.pl)+'</td>'+
|
| - |
|
465 |
'<td class="num cnt cmuted">'+t.tot+'</td><td></td><td></td>'+
|
| - |
|
466 |
'</tr>';
|
| - |
|
467 |
rows.forEach(function(r){
|
| 460 |
var rowcls = r.state==='NOT_STARTED' ? 'rowbad' : (r.idleAway?'rowwarn':'');
|
468 |
var rowcls = r.state==='NOT_STARTED' ? 'rowbad' : (r.idleAway?'rowwarn':'');
|
| 461 |
var A = r.userId+",'"+js(r.name)+"','"+date+"'"; // openExec args
|
469 |
var A = r.userId+",'"+js(r.name)+"','"+date+"'"; // openExec args
|
| 462 |
var go = 'onclick="openExec('+A+')"';
|
470 |
var go = 'onclick="openExec('+A+')"';
|
| 463 |
h+='<tr class="clickable '+rowcls+'" '+go+'>'+
|
471 |
h+='<tr class="clickable '+rowcls+'" '+go+'>'+
|
| 464 |
'<td style="text-align:center">'+(r.agendaFilled?'<span style="color:#2f9e44;font-size:15px">✓</span>':'<span class="badflag" style="font-size:15px">✗</span>')+'</td>'+
|
472 |
'<td style="text-align:center">'+(r.agendaFilled?'<span style="color:#2f9e44;font-size:15px">✓</span>':'<span class="badflag" style="font-size:15px">✗</span>')+'</td>'+
|
| Line 474... |
Line 482... |
| 474 |
'<button class="actbtn view" title="Journey detail" onclick="openExec('+A+')">👁</button>'+
|
482 |
'<button class="actbtn view" title="Journey detail" onclick="openExec('+A+')">👁</button>'+
|
| 475 |
'<button class="actbtn edit" title="Edit beat in Beat Planner" onclick="openPlanner('+r.authUserId+','+(r.beatId||0)+',\''+date+'\',\'edit\')">✎</button>'+
|
483 |
'<button class="actbtn edit" title="Edit beat in Beat Planner" onclick="openPlanner('+r.authUserId+','+(r.beatId||0)+',\''+date+'\',\'edit\')">✎</button>'+
|
| 476 |
'<button class="actbtn add" title="Assign / add visit in Beat Planner" onclick="openPlanner('+r.authUserId+','+(r.beatId||0)+',\''+date+'\',\'add\')">+</button>'+
|
484 |
'<button class="actbtn add" title="Assign / add visit in Beat Planner" onclick="openPlanner('+r.authUserId+','+(r.beatId||0)+',\''+date+'\',\'add\')">+</button>'+
|
| 477 |
'</td></tr>';
|
485 |
'</td></tr>';
|
| 478 |
});
|
486 |
});
|
| 479 |
h+='</tbody><tfoot><tr class="boardtot">'+
|
- |
|
| 480 |
'<td></td><td><b>Total</b><br><span style="color:#97a1b5;font-size:11px">'+rows.length+' executives</span></td><td></td>'+
|
- |
|
| 481 |
'<td class="num cnt">'+cdp(t.cp,t.dp,t.pp)+'</td>'+
|
- |
|
| 482 |
'<td class="num cnt">'+cdp(t.cl,t.dl,t.pl)+'</td>'+
|
- |
|
| 483 |
'<td class="num cnt cmuted">'+t.tot+'</td><td></td><td></td>'+
|
- |
|
| 484 |
'</tr></tfoot>';
|
- |
|
| 485 |
return h+'</table></div>';
|
487 |
return h+'</tbody></table></div>';
|
| 486 |
}
|
488 |
}
|
| 487 |
// Funnel drill-down: each stage shows the executives who reached it AND the
|
489 |
// Funnel drill-down: each stage shows the executives who reached it AND the
|
| 488 |
// drop-off cohort that fell out at this step (the "−N …" sub-line). Filters
|
490 |
// drop-off cohort that fell out at this step (the "−N …" sub-line). Filters
|
| 489 |
// mirror the backend funnel conditions exactly (rows carry the per-stage flags).
|
491 |
// mirror the backend funnel conditions exactly (rows carry the per-stage flags).
|
| 490 |
var FUNNEL_META = {
|
492 |
var FUNNEL_META = {
|
| 491 |
scheduled: {title:'Scheduled executives', desc:'Have a beat today, or any activity', f:function(r){ return true; }},
|
- |
|
| 492 |
punchedIn: {title:'Punched in', desc:'Started the day (punch-in recorded)', f:function(r){ return r.punchedIn; },
|
493 |
punchedIn: {title:'Punched In', desc:'Started the day (punch-in recorded)', f:function(r){ return r.punchedIn; }},
|
| 493 |
dropLabel:'Never started (scheduled, no punch-in)', drop:function(r){ return !r.punchedIn; }},
|
494 |
travelling: {title:'Traveling', desc:'On the road between stops', f:function(r){ return r.state==='TRAVELLING'; }},
|
| 494 |
madeCheckin: {title:'Made a check-in', desc:'Checked in at one or more stops', f:function(r){ return r.madeCheckin; },
|
495 |
inStore: {title:'In Store', desc:'Currently checked in at a stop', f:function(r){ return r.state==='IN_STORE'; }},
|
| 495 |
dropLabel:'Punched in but idle (no check-in)', drop:function(r){ return r.punchedIn && !r.madeCheckin; }},
|
- |
|
| 496 |
completedAll: {title:'Completed all visits', desc:'Finished every planned stop', f:function(r){ return r.completedAll; },
|
496 |
idle: {title:'Idle / Break', desc:'Idle off-store beyond the threshold', f:function(r){ return r.idleAway; }},
|
| 497 |
dropLabel:'Still mid-journey (not all stops done)', drop:function(r){ return r.madeCheckin && !r.completedAll; }},
|
- |
|
| 498 |
punchedOut: {title:'Punched out', desc:'Ended the day (punch-out recorded)', f:function(r){ return r.punchedOut; },
|
497 |
punchedOut: {title:'Punched Out', desc:'Ended the day (punch-out recorded)', f:function(r){ return r.punchedOut; }}
|
| 499 |
dropLabel:'Still out (punched in, not punched out)', drop:function(r){ return r.punchedIn && !r.punchedOut; }}
|
- |
|
| 500 |
};
|
498 |
};
|
| 501 |
function funnelRows(list, date){
|
499 |
function funnelRows(list, date){
|
| 502 |
if(!list.length) return '<p class="desc">None.</p>';
|
500 |
if(!list.length) return '<p class="desc">None.</p>';
|
| 503 |
var h='<table><thead><tr><th>Executive / Beat</th><th>Punch In/Out</th><th class="num">Completed</th><th class="num">Planned</th><th>State</th></tr></thead><tbody>';
|
501 |
var h='<table><thead><tr><th>Executive / Beat</th><th>Punch In/Out</th><th class="num">Completed</th><th class="num">Planned</th><th>State</th></tr></thead><tbody>';
|
| 504 |
list.slice().sort(function(a,b){ return (''+a.name).localeCompare(b.name); }).forEach(function(r){
|
502 |
list.slice().sort(function(a,b){ return (''+a.name).localeCompare(b.name); }).forEach(function(r){
|
| Line 528... |
Line 526... |
| 528 |
funnelRows(dropped, date);
|
526 |
funnelRows(dropped, date);
|
| 529 |
}
|
527 |
}
|
| 530 |
h+='<p class="desc" style="margin-top:10px">Click a row to open that executive\'s journey detail.</p>';
|
528 |
h+='<p class="desc" style="margin-top:10px">Click a row to open that executive\'s journey detail.</p>';
|
| 531 |
md.innerHTML=h; mb.classList.add('show');
|
529 |
md.innerHTML=h; mb.classList.add('show');
|
| 532 |
}
|
530 |
}
|
| - |
|
531 |
// Main-card drill-downs: roster of executives behind each top KPI card.
|
| - |
|
532 |
// 'drop' (optional) adds a second, highlighted section for the complementary
|
| - |
|
533 |
// cohort — e.g. who's scheduled but hasn't filled the agenda.
|
| - |
|
534 |
var CARD_META = {
|
| - |
|
535 |
total: {title:'Total Executives', desc:'Everyone in your team', f:function(r){ return true; }},
|
| - |
|
536 |
scheduled: {title:'Scheduled PJP', desc:'Executives with a beat today', f:function(r){ return r.hasBeat; }},
|
| - |
|
537 |
agenda: {title:'Agenda Filled', desc:'Beats with the agenda filled', f:function(r){ return r.agendaFilled; },
|
| - |
|
538 |
dropLabel:'Agenda not filled (scheduled)', drop:function(r){ return r.hasBeat && !r.agendaFilled; }},
|
| - |
|
539 |
unscheduled: {title:'Unscheduled', desc:'No PJP scheduled today', f:function(r){ return !r.hasBeat; }}
|
| - |
|
540 |
};
|
| - |
|
541 |
// Renders an executive roster table (used by the card drill-downs).
|
| - |
|
542 |
function execRows(list, date){
|
| - |
|
543 |
if(!list.length) return '<p class="desc">None.</p>';
|
| - |
|
544 |
var h='<table><thead><tr><th>Executive</th><th>Beat</th><th>Agenda</th><th>Status</th></tr></thead><tbody>';
|
| - |
|
545 |
list.forEach(function(r){
|
| - |
|
546 |
var A=r.userId+",'"+js(r.name)+"','"+date+"'";
|
| - |
|
547 |
var go = r.userId ? ' class="clickable" onclick="openExec('+A+')"' : '';
|
| - |
|
548 |
var agenda = !r.hasBeat ? '<span style="color:#97a1b5">—</span>'
|
| - |
|
549 |
: (r.agendaFilled ? '<span style="color:#2f9e44">✓</span>' : '<span class="badflag">✗</span>');
|
| - |
|
550 |
h+='<tr'+go+'>'+
|
| - |
|
551 |
'<td><b>'+esc(r.name)+'</b></td>'+
|
| - |
|
552 |
'<td>'+esc(r.beatName||'-')+'</td>'+
|
| - |
|
553 |
'<td style="text-align:center">'+agenda+'</td>'+
|
| - |
|
554 |
'<td>'+esc(r.statusLabel||'')+'</td></tr>';
|
| - |
|
555 |
});
|
| - |
|
556 |
return h+'</tbody></table>';
|
| - |
|
557 |
}
|
| - |
|
558 |
function openCards(key){
|
| - |
|
559 |
var m=CARD_META[key]; if(!m) return;
|
| - |
|
560 |
var all=(window._execList||[]), date=window._boardDate||'';
|
| - |
|
561 |
var list=all.filter(m.f);
|
| - |
|
562 |
var dropped=m.drop ? all.filter(m.drop) : [];
|
| - |
|
563 |
var mb=el('bj-modal-back'), md=el('bj-modal');
|
| - |
|
564 |
var h='<button class="x" onclick="closeModal()">×</button><h3>'+esc(m.title)+'</h3>'+
|
| - |
|
565 |
'<p class="desc">'+list.length+' '+(list.length===1?'executive':'executives')+' · '+esc(m.desc)+'</p>'+
|
| - |
|
566 |
execRows(list, date);
|
| - |
|
567 |
if(m.drop){
|
| - |
|
568 |
h+='<div class="sectionlabel" style="margin-top:18px;color:#e8590c">'+esc(m.dropLabel)+' · '+dropped.length+'</div>'+
|
| - |
|
569 |
execRows(dropped, date);
|
| - |
|
570 |
}
|
| - |
|
571 |
h+='<p class="desc" style="margin-top:10px">Click a row to open that executive\'s journey detail.</p>';
|
| - |
|
572 |
md.innerHTML=h; mb.classList.add('show');
|
| - |
|
573 |
}
|
| 533 |
// Leads drill-down: lists every lead visit logged today (matches the KPI count).
|
574 |
// Leads drill-down: lists every lead visit logged today (matches the KPI count).
|
| 534 |
function openLeads(){
|
575 |
function openLeads(){
|
| 535 |
var list=window._leadsList||[], date=window._boardDate||'';
|
576 |
var list=window._leadsList||[], date=window._boardDate||'';
|
| 536 |
var mb=el('bj-modal-back'), md=el('bj-modal');
|
577 |
var mb=el('bj-modal-back'), md=el('bj-modal');
|
| 537 |
var h='<button class="x" onclick="closeModal()">×</button><h3>Leads on route</h3>'+
|
578 |
var h='<button class="x" onclick="closeModal()">×</button><h3>Leads on route</h3>'+
|