Subversion Repositories SmartDukaan

Rev

Rev 37020 | Rev 37032 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 37020 Rev 37028
Line 674... Line 674...
674
function pScoreTable(rows){
674
function pScoreTable(rows){
675
  if(!rows.length) return '<p class="desc">No executives in scope.</p>';
675
  if(!rows.length) return '<p class="desc">No executives in scope.</p>';
676
  var h='<table><thead><tr><th>Executive</th><th class="num">Plan</th><th class="num">Done</th><th class="num">Adher.</th>'+
676
  var h='<table><thead><tr><th>Executive</th><th class="num">Plan</th><th class="num">Done</th><th class="num">Adher.</th>'+
677
    '<th class="num" title="Avg lead visits per active (punched-in) day">Lead/day</th>'+
677
    '<th class="num" title="Avg lead visits per active (punched-in) day">Lead/day</th>'+
678
    '<th class="num" title="Avg franchisee visits per active (punched-in) day">Fr/day</th>'+
678
    '<th class="num" title="Avg franchisee visits per active (punched-in) day">Fr/day</th>'+
679
    '<th class="num">Disc.</th><th class="num">Hrs</th><th class="num">Leads</th><th class="num">Geo&#9888;</th></tr></thead><tbody>';
679
    '<th class="num">Disc.</th><th class="num">Hrs</th><th class="num">Leads</th></tr></thead><tbody>';
680
  rows.forEach(function(r){
680
  rows.forEach(function(r){
681
    var c=adhColor(r.adherencePct);
681
    var c=adhColor(r.adherencePct);
682
    h+='<tr class="clickable" onclick="openExecPeriod('+r.authUserId+',\''+js(r.name)+'\')">'+
682
    h+='<tr class="clickable" onclick="openExecPeriod('+r.authUserId+',\''+js(r.name)+'\')">'+
683
       '<td><span class="lvl">'+esc(r.level)+'</span> <b>'+esc(r.name)+'</b></td>'+
683
       '<td><span class="lvl">'+esc(r.level)+'</span> <b>'+esc(r.name)+'</b></td>'+
684
       '<td class="num">'+r.planned+'</td><td class="num">'+r.done+'</td>'+
684
       '<td class="num">'+r.planned+'</td><td class="num">'+r.done+'</td>'+
685
       '<td class="num"><b style="color:'+c+'">'+r.adherencePct+'%</b></td>'+
685
       '<td class="num"><b style="color:'+c+'">'+r.adherencePct+'%</b></td>'+
686
       '<td class="num">'+(r.avgLeadVisitsPerDay||'-')+'</td>'+
686
       '<td class="num">'+(r.avgLeadVisitsPerDay||'-')+'</td>'+
687
       '<td class="num">'+(r.avgFranchiseeVisitsPerDay||'-')+'</td>'+
687
       '<td class="num">'+(r.avgFranchiseeVisitsPerDay||'-')+'</td>'+
688
       '<td class="num'+(r.discussionMin>0&&r.discussionMin<10?' warnflag':'')+'">'+(r.discussionMin||'-')+(r.discussionMin?'m':'')+'</td>'+
688
       '<td class="num'+(r.discussionMin>0&&r.discussionMin<10?' warnflag':'')+'">'+(r.discussionMin||'-')+(r.discussionMin?'m':'')+'</td>'+
689
       '<td class="num">'+r.workingHrs+'</td><td class="num">'+r.leads+'</td>'+
689
       '<td class="num">'+r.workingHrs+'</td><td class="num">'+r.leads+'</td></tr>';
690
       '<td class="num'+(r.geoFlags>0?' badflag':'')+'">'+r.geoFlags+'</td></tr>';
-
 
691
  });
690
  });
692
  return h+'</tbody></table>';
691
  return h+'</tbody></table>';
693
}
692
}
694
// Funnel stage → the executives behind it (the scorecard, focused).
693
// Funnel stage → the executives behind it (the scorecard, focused).
695
function openPFunnel(key){
694
function openPFunnel(key){
Line 947... Line 946...
947
  var h='<table id="scTbl"><thead><tr>'+
946
  var h='<table id="scTbl"><thead><tr>'+
948
    '<th onclick="sortCard(\'name\')">Executive</th>'+
947
    '<th onclick="sortCard(\'name\')">Executive</th>'+
949
    '<th class="num" onclick="sortCard(\'planned\')">Plan</th>'+
948
    '<th class="num" onclick="sortCard(\'planned\')">Plan</th>'+
950
    '<th class="num" onclick="sortCard(\'done\')">Done</th>'+
949
    '<th class="num" onclick="sortCard(\'done\')">Done</th>'+
951
    '<th class="num" onclick="sortCard(\'adherencePct\')">Adher.</th>'+
950
    '<th class="num" onclick="sortCard(\'adherencePct\')">Adher.</th>'+
-
 
951
    '<th class="num" onclick="sortCard(\'avgLeadVisitsPerDay\')" title="Avg lead visits per active (punched-in) day">Lead/day</th>'+
-
 
952
    '<th class="num" onclick="sortCard(\'avgFranchiseeVisitsPerDay\')" title="Avg franchisee visits per active (punched-in) day">Fr/day</th>'+
952
    '<th class="num" onclick="sortCard(\'discussionMin\')">Disc.</th>'+
953
    '<th class="num" onclick="sortCard(\'discussionMin\')">Disc.</th>'+
953
    '<th class="num" onclick="sortCard(\'workingHrs\')">Hrs</th>'+
954
    '<th class="num" onclick="sortCard(\'workingHrs\')">Hrs</th>'+
954
    '<th class="num" onclick="sortCard(\'leads\')">Leads</th>'+
-
 
955
    '<th class="num" onclick="sortCard(\'geoFlags\')">Geo&#9888;</th></tr></thead><tbody>';
955
    '<th class="num" onclick="sortCard(\'leads\')">Leads</th></tr></thead><tbody>';
956
  rows.forEach(function(r){
956
  rows.forEach(function(r){
957
    var c=adhColor(r.adherencePct);
957
    var c=adhColor(r.adherencePct);
958
    h+='<tr class="clickable" onclick="openExecPeriod('+r.authUserId+',\''+js(r.name)+'\')"><td>'+esc(r.name)+' <span class="lvl">'+esc(r.level)+'</span></td>'+
958
    h+='<tr class="clickable" onclick="openExecPeriod('+r.authUserId+',\''+js(r.name)+'\')"><td><span class="lvl">'+esc(r.level)+'</span>'+esc(r.name)+'</td>'+
959
       '<td class="num">'+r.planned+'</td><td class="num">'+r.done+'</td>'+
959
       '<td class="num">'+r.planned+'</td><td class="num">'+r.done+'</td>'+
960
       '<td class="num"><b style="color:'+c+'">'+r.adherencePct+'%</b><span class="meter"><i style="width:'+Math.min(100,r.adherencePct)+'%;background:'+c+'"></i></span></td>'+
960
       '<td class="num"><b style="color:'+c+'">'+r.adherencePct+'%</b><span class="meter"><i style="width:'+Math.min(100,r.adherencePct)+'%;background:'+c+'"></i></span></td>'+
-
 
961
       '<td class="num">'+(r.avgLeadVisitsPerDay||'-')+'</td>'+
-
 
962
       '<td class="num">'+(r.avgFranchiseeVisitsPerDay||'-')+'</td>'+
961
       '<td class="num'+(r.discussionMin>0&&r.discussionMin<10?' warnflag':'')+'">'+(r.discussionMin||'-')+(r.discussionMin?'m':'')+'</td>'+
963
       '<td class="num'+(r.discussionMin>0&&r.discussionMin<10?' warnflag':'')+'">'+(r.discussionMin||'-')+(r.discussionMin?'m':'')+'</td>'+
962
       '<td class="num">'+r.workingHrs+'</td><td class="num">'+r.leads+'</td>'+
964
       '<td class="num">'+r.workingHrs+'</td><td class="num">'+r.leads+'</td></tr>';
963
       '<td class="num'+(r.geoFlags>0?' badflag':'')+'">'+r.geoFlags+'</td></tr>';
-
 
964
  });
965
  });
965
  return h+'</tbody></table>';
966
  return h+'</tbody></table>';
966
}
967
}
967
function coverageBars(rows){
968
function coverageBars(rows){
968
  if(!rows||!rows.length) return '<p class="desc">No coverage data.</p>';
969
  if(!rows||!rows.length) return '<p class="desc">No coverage data.</p>';
Line 1232... Line 1233...
1232
      'Punch out'+(data.punchOut.time?' · '+data.punchOut.time:''),
1233
      'Punch out'+(data.punchOut.time?' · '+data.punchOut.time:''),
1233
      '<div style="font-size:12px"><b>&#9873; Punch out</b>'+(data.punchOut.time?'<br>'+esc(data.punchOut.time):'')+'</div>');
1234
      '<div style="font-size:12px"><b>&#9873; Punch out</b>'+(data.punchOut.time?'<br>'+esc(data.punchOut.time):'')+'</div>');
1234
  }
1235
  }
1235
 
1236
 
1236
  // 5) routes — actual road taken vs Google-suggested optimal, for comparison.
1237
  // 5) routes — actual road taken vs Google-suggested optimal, for comparison.
-
 
1238
  // Suggested-route checkpoints = the whole day's stops (planned + visited), not
-
 
1239
  // just the ones already visited, so the suggestion shows from punch-in onward
-
 
1240
  // (and even before the first check-in). optimizeWaypoints reorders them, so the
1237
  // Checkpoints in journey order: punch in → visits (by check-in time) → punch out.
1241
  // input order only needs to keep punch-in first / punch-out last; visited stops
-
 
1242
  // are listed by check-in time and still-pending stops after them.
1238
  var cps=[];
1243
  var cps=[];
1239
  if(data.punchIn) cps.push({lat:data.punchIn.lat,lng:data.punchIn.lng});
1244
  if(data.punchIn) cps.push({lat:data.punchIn.lat,lng:data.punchIn.lng});
1240
  (data.visits||[]).slice()
1245
  (data.visits||[]).slice()
1241
    .filter(function(v){ return v.lat && v.lng && v.checkInTime; })   // visited stops only
1246
    .filter(function(v){ return v.lat && v.lng; })                   // every stop with coords
1242
    .sort(function(a,b){ return tlSecs(a.checkInTime)-tlSecs(b.checkInTime); })
1247
    .sort(function(a,b){ return tlSecs(a.checkInTime)-tlSecs(b.checkInTime); })
1243
    .forEach(function(v){ cps.push({lat:v.lat,lng:v.lng}); });
1248
    .forEach(function(v){ cps.push({lat:v.lat,lng:v.lng}); });
1244
  if(data.punchOut) cps.push({lat:data.punchOut.lat,lng:data.punchOut.lng});
1249
  if(data.punchOut) cps.push({lat:data.punchOut.lat,lng:data.punchOut.lng});
1245
  // Actual time window of the GPS trail (first → last breadcrumb timestamp),
1250
  // Actual time window of the GPS trail (first → last breadcrumb timestamp),
1246
  // shown on the taken-route click detail alongside the driving estimate.
1251
  // shown on the taken-route click detail alongside the driving estimate.