ETA Dashboard

ETA Widget Code

Create a dashboard widget

<div class="container-fluid p-0">
    <div class="row">
        <div class="col-md-12">
            <div class="mapdata" id="googleMapLocation" style="width:100%"></div>

        </div>
        <div class="col-md-12 mt-2">
             <h5 class="pull-left">Total Records: <span class="totalCount"></span></h5>
             <select class="form-control form-control-sm pull-right" id="geoFilter" placeholder="Select Geofence" onchange="loadGeoList()" style="width:200px;"></select>

             <table id="geofenceTable" class="table table-bordered" cellspacing="0">
            
            </table>
        </div>
    </div>
</div>
/********************************************
 Pre Defined variables to access in the widget 
********************************************/

// API_BASE_PATH - to get boodskap api base path
// DOMAIN_KEY - to get domain key
// API_KEY - to get api key
// API_TOKEN - to get api token
// MESSAGE_ID - to get message id
// DEVICE_ID - to get device id
// ASSET_ID - to get asset id
// RECORD_ID - to get record id
// WIDGET_ID - to get current widget id
// USER_ID - to get user id (In public share this will be empty)


var initialLng =  51.507351;
var initialLat =  -0.127758;

var ZOOM_LEVEL = 5;

var DATE_TIME_FORMAT = 'MM/DD/YYYY hh:mm:ss A';

var record_size = 1000;
var AUTO_REFRESH_INTERVAL = 10; //Seconds

var DEVICE_ID_FIELD ='member';
var GEOFECNE_ID_FIELD ='destination';
var ETA_FIELD ='eta';
var LAT_FIELD ='latitude';
var LON_FIELD ='longitude';
var DISTANCE_FIELD ='distance';

var SORTING_FIELD = 'distance';
var SORTING_FIELD_INDEX = 4;




var drawingManager = null;
var geofenceTable = null;
var infowindow = null;
var locationMap = null;
var markerObj = null;
var infoWindow = null;
var geofence_list = [];

$(document).ready(function () {
    
    getGoogleApiKey()
    
    $("#googleMapLocation").css('height',$(window).height()/2)
    


});





// function loadGoefenceList() {
//      var queryParams={
//       "query": {
//         "bool": {
//             "must": [{
//                  "match": {
//                       "domainKey": DOMAIN_KEY
//                       }
//                   }]
//                 }
//              },
//              "size":record_size
//     }
    
    

// 	var searchQuery = {
//     	"extraPath": "",
//         "query": JSON.stringify(queryParams),
//         "params": [],
//         "type": "GEOFENCE"
// 	};
// 	searchByQuery(searchQuery, function (status, data) {
	   
// 		if (status) {
		 
// 			var result = searchQueryFormatter(data);
			
// // 		
// 			if (result.total > 0) {
// 			    var resultData = result.data.data;
	
		
// 			} else {
				
// 			}
// 		} else {
			
// 		}
// 	});
// }

var allmarker = [];

function loadGoogleMap(resultdata) {

 var iconBase = 'https://maps.google.com/mapfiles/kml/shapes/';
 
  locationMap = new google.maps.Map(document.getElementById('googleMapLocation'), {
        center: new google.maps.LatLng(initialLat,initialLng),
        zoom: ZOOM_LEVEL,
   
    });
    
    var colorList = ['yellow','blue','red','green','orange','gray','purple','white','black','brown']
    

   
    for(var j=0;j<resultdata.length;j++){
        
      
      
        marker = new google.maps.Marker({
            position: new google.maps.LatLng(resultdata[j][LAT_FIELD], resultdata[j][LON_FIELD]),
            icon: 'http://labs.google.com/ridefinder/images/mm_20_'+colorList[j]+'.png',
            map : locationMap
    
        });
        
        marker.setMap(locationMap);
        
         locationMap.panTo(new google.maps.LatLng(resultdata[j][LAT_FIELD], resultdata[j][LON_FIELD]));
         
         locationMap.setZoom(10)
         
         
         const contentString =
            '<div id="content">' +
            '<div id="siteNotice">' +
            "</div>" +
            '<h5 id="firstHeading" class="firstHeading"><small>Estimate Time of Arrival (ETA)</small><br> '+moment(resultdata[j]).format(DATE_TIME_FORMAT)+'</h1>' +
            '<div id="bodyContent">' +
            "<p><small>Device Id</small><br>"+resultdata[j][DEVICE_ID_FIELD]+"</p>" +
            "<p><small>Geofence Id</small><br>"+resultdata[j][GEOFECNE_ID_FIELD]+"</p>" +
            "</div>" +
            "</div>";

          const infowindow = new google.maps.InfoWindow({
            content: contentString,
          });


          marker.addListener("click", () => {
            infowindow.open({
              anchor: marker,
              locationMap,
              shouldFocus: false,
            });
          });
        
                   
    }

   

  
 }
 
 
 
 function searchByQuery(data, cbk) {
       	$.ajax({
        	url: API_BASE_PATH + "/elastic/search/query/" + API_TOKEN,
            data: JSON.stringify(data),
             contentType: "application/json",
            type: 'POST',
            success: function (data) {
             //called when successful
               cbk(true, data);
            },
        error: function (e) {
         //called when there is an error
          cbk(false, e);
        }
	});
}


// searchqueryformatter----------

function searchQueryFormatter(data) {

      var resultObj = {
         total: 0,
         data: {},
         aggregations: {}
        };

      if (data.httpCode === 200) {
      var arrayData = JSON.parse(data.result);
      var totalRecords = arrayData.hits.total.value;
      var records = arrayData.hits.hits;
      var aggregations = arrayData.aggregations ? arrayData.aggregations : {};
      for (var i = 0; i < records.length; i++) {
      records[i]['_source']['_id'] = records[i]['_id'];
      }

       resultObj = {
         "total": totalRecords,
          "data": {
              "recordsTotal": totalRecords,
              "recordsFiltered": totalRecords,
               "data": _.pluck(records, '_source')
            },
            aggregations: aggregations
         };

        return resultObj;
  
    } else {
 
   return resultObj;
   }
}

// api dynamic-------------------

function getGoogleApiKey(datas) {
	$.ajax({
		url: API_BASE_PATH + "/domain/property/get/" + API_TOKEN+"/google.map.key",
        // ata: JSON.stringify(data),
		contentType: "application/json",
		type: 'GET',
		success: function (data) {
			//called when successful
			var key=JSON.parse(data.value)
		
			  var script_tag = document.createElement('script');
            script_tag.setAttribute("type", "text/javascript");
	    	script_tag.setAttribute("src", "https://maps.googleapis.com/maps/api/js?key="+key.apiKey+"&callback=loadGeoList&libraries=drawing&v=weekly");
	  		  (document.getElementsByTagName("head")[0] || document.documentElement).appendChild(script_tag);

		   $("#geoFilter").html('<option value="">All Geofences</option>')
		 },
		 error: function (e) {
			//called when there is an error
			console.log(false, e);
		}
	});
}

 
 
function loadGeoList(){
    
    const self = this;
    if (geofenceTable) {
        geofenceTable.destroy();
        $("#geofenceTable").html("");
    }

    var fields = [

        {
            mData: DEVICE_ID_FIELD,
            sTitle: 'Device Id',
            orderable: false,
            mRender: function(data, type, row) {
                return data;
            }
        },

        {
            mData: GEOFECNE_ID_FIELD,
            sTitle: 'Geofence Id',
            orderable: false,
            mRender: function(data, type, row) {
                return data;
            }
        },
        {
            mData: LAT_FIELD,
            sTitle: 'Latitude',
            orderable: false,
            mRender: function(data, type, row) {
                var str ='<span class="">'+data+'</span>'
                return str;
            }
        },
        {
            mData: LON_FIELD,
            sTitle: 'Longitude',
            orderable: false,
            mRender: function(data, type, row) {
                var str ='<span class="">'+data+'</span>'
                return str;
            }
        },
        {
            mData: ETA_FIELD,
            sTitle: 'ETA',
            orderable: true,
            mRender: function(data, type, row) {

                return moment(data).format(DATE_TIME_FORMAT);
            }
        },
        {
            mData: DISTANCE_FIELD,
            sTitle: 'Distance in KMs',
            orderable: true,
            mRender: function(data, type, row) {
                return data/1000;
            }
        },
         {
            mData: SORTING_FIELD,
            sTitle: 'Last Updated Time',
            mRender: function(data, type, row) {

                return moment(data).format(DATE_TIME_FORMAT);
            }
        }
 

    ];


    var domainKeyJson = { "match": { "domainKey": DOMAIN_KEY } };

    var queryParams = {
        query: {
            "bool": {
                "must": [],
            }
        },
        sort: [],
        aggs:{
            "geo":{
                "terms":{
                    "field":GEOFECNE_ID_FIELD,
                    "size":100
                }
            }
        }
    };

    var tableOption = {

        "language": {
            "processing": '<i class="fa fa-spinner fa-spin"></i> Processing',
            "sLengthMenu": "Show _MENU_ records",
            "info": "Showing _START_ to _END_ of _TOTAL_ records",
            "infoFiltered": ""

        },
        responsive: true,
        paging: true,
        searching: false,
        aaSorting: [
            [SORTING_FIELD_INDEX, 'desc']
        ],
        "ordering": true,
        iDisplayLength: 5,
        lengthMenu: [
            [5, 10],
            [5, 10]
        ],
        aoColumns: fields,
        "bProcessing": true,
        "bServerSide": true,
        "sAjaxSource": API_BASE_PATH + '/elastic/search/query/' + API_TOKEN ,

        "fnServerData": function(sSource, aoData, fnCallback, oSettings) {


            var keyName = fields[oSettings.aaSorting[0][0]]

            var sortingJson = {};
            sortingJson[keyName['mData']] = { "order": oSettings.aaSorting[0][1] };
            queryParams.sort = [sortingJson];
            queryParams.query['bool']['must'] = [];
            queryParams.query["bool"]["should"] = [];
            delete queryParams.query["bool"]["minimum_should_match"];
            queryParams['size'] = oSettings._iDisplayLength;
            queryParams['from'] = oSettings._iDisplayStart;

            var searchText = oSettings.oPreviousSearch.sSearch;

            queryParams.query['bool']['must'].push(domainKeyJson);
            if($("#geoFilter").val()){
                queryParams.query.bool.must.push({match:{geofenceid:$("#geoFilter").val()}})
            }


            var ajaxObj = {
                "method": "GET",
               	"extraPath": "",
                "query": JSON.stringify(queryParams),
                "params": [],
                "specId":RECORD_ID,
                "type": "RECORD"
            };



            oSettings.jqXHR = $.ajax({
                "dataType": 'json',
                "contentType": 'application/json',
                "type": "POST",
                "url": sSource,
                "data": JSON.stringify(ajaxObj),
                success: function (data) {

                    var fullObj = searchQueryFormatter(data);
                    
                    var aggsObj = fullObj.aggregations.geo.buckets;
                    
                     if($("#geoFilter").val() == ""){
                    
                        for(var i =0;i<aggsObj.length;i++){
                            $("#geoFilter").append('<option value="'+aggsObj[i].key+'">'+aggsObj[i].key +' ('+aggsObj[i].doc_count+')</option>')
                        }
                     }
                    
                    var resultData = fullObj.data;
                    
                    geofence_list =resultData.data;
                    
                    loadGoogleMap(resultData.data)
                    
                    $('.totalCount').html(fullObj.total)
                

                    resultData['draw'] = oSettings.iDraw;

                    fnCallback(resultData);
                }
            });
        }

    };

    geofenceTable = $("#geofenceTable").DataTable(tableOption);
    
     

}

//  setInterval(function(){ 
//      loadGeoList()
//  }, AUTO_REFRESH_INTERVAL*1000);
.dataTable td{
    font-size: 12px;
}
.dataTable th{
    font-size: 13px;
    font-weight: 500 !important;
    color: #fff !important;
}
table.dataTable thead .sorting, 
table.dataTable thead .sorting_asc, 
table.dataTable thead .sorting_desc {
    background : none;
}
.dataTables_processing{
    background: #fff !important;
    /*height : 50px !important;*/
    box-shadow: 1px 1px 4px #ccc;
}
.table_processing{
 
}
table.dataTable thead .sorting:after, table.dataTable thead .sorting_asc:after, table.dataTable thead .sorting_desc:after, table.dataTable thead .sorting_asc_disabled:after, table.dataTable thead .sorting_desc_disabled:after {
    position: absolute;
    bottom: 8px;
    right: 8px;
    display: block;
    opacity: 0.5;
}

table.dataTable thead .sorting:after, table.dataTable thead .sorting_asc:after, table.dataTable thead .sorting_asc_disabled:after, table.dataTable thead .sorting_desc:after, table.dataTable thead .sorting_desc_disabled:after {
    font-family: 'FontAwesome' !important;
    font-weight: 900;
    font-style: normal;
    font-variant: normal;
    text-rendering: auto;
}

table.dataTable thead .sorting_asc:after {
    content: '\f0dd'!important;
}
table.dataTable thead .sorting_desc:after {
    content: "\e156";
}
table.dataTable thead .sorting:after, table.dataTable thead .sorting_asc:after, table.dataTable thead .sorting_desc:after, table.dataTable thead .sorting_asc_disabled:after, table.dataTable thead .sorting_desc_disabled:after {
    position: absolute;
    /*bottom: 8px;*/
    /*right: 8px;*/
    display: block;
    opacity: 0.5;
}
table.dataTable thead .sorting_desc:after {
    content: '\f0de'!important;
}
table.dataTable thead .sorting:after {
    content: '\f0dc'!important;
}

table thead tr{
    background-color: #6d6d6d !important;
    color: #fff !important;
}

.pagination .page-item.disabled>.page-link{
    border: 0.001rem solid #e1e2e4 !important;
    /*margin: 0 !important;*/
    border-radius: 0 !important;
    box-shadow: none;
    background-color: #ffffff;
    color: #424242;
    opacity: 1;
    text-transform: capitalize;
}
body div.dataTables_wrapper div.dataTables_paginate ul.pagination {
    /*margin: 9px 0;*/
    display: inline-block;
}
div.dataTables_wrapper div.dataTables_info {
    /*margin: 8px 0;*/
}
div.dataTables_wrapper div.dataTables_paginate{
    margin: 0 !important;
}

.paging_simple_numbers .pagination .paginate_button.active a, .paging_simple_numbers .pagination .paginate_button:hover a{
    background-color: #6d6d6d  !important;
    border-radius: 0 !important;
    /*margin: 0 !important;*/
    box-shadow: none !important;
    border-color: #6d6d6d  !important;
}
div.table-responsive > div.dataTables_wrapper > div.row > div[class^="col-"]:first-child {
    padding-left: 0;
    margin: auto;
}

.unsort::before {
    content: none !important;
}
.unsort::after {
    content: none !important;
}
.table-bordered thead td, .table-bordered thead th{
    /*border-bottom-width: 0px !important;*/
}

div.dataTables_wrapper div.dataTables_filter input{
    border: 1px solid #e9ecef;
    border-radius: 4px;
    /*height: 34px;*/
    /*width: 220px;*/
}
select.custom-select.custom-select-sm.form-control.form-control-sm {
    border: 1px solid #dddddd !important;
    background-color: #ffffff !important;
    /*height: 34px !important;*/
    border-radius: 4px;

}

table.table-bordered.dataTable tbody th, table.table-bordered.dataTable tbody td:focus{
    outline: none !important;
}


.pagination > li > a{
    border-radius : 0 !important;
}

div.dataTables_wrapper div.dataTables_paginate .pagination > li > a:hover{
    margin: 0;
}
.dataTables_wrapper .dataTables_length {
    float: left;
}
 
::-webkit-scrollbar {
  width: 5px;
  height:5px;
}
.over{
    overflow:auto;
    height:auto;
    width:auto;
}

Sample Output

3574

Estimated Time of Arrival