| Line 53... |
Line 53... |
| 53 |
</div>
|
53 |
</div>
|
| 54 |
#set($completedCount = 0)
|
54 |
#set($completedCount = 0)
|
| 55 |
#set($pendingCount = 0)
|
55 |
#set($pendingCount = 0)
|
| 56 |
#set($deferredCount = 0)
|
56 |
#set($deferredCount = 0)
|
| 57 |
#foreach($v in $visits)
|
57 |
#foreach($v in $visits)
|
| 58 |
#if($v.getMarkType() == "CHECKIN-CHECKOUT" || $v.getMarkType() == "CHECKOUT")
|
58 |
#if($v.getMarkType() == "CHECKIN-CHECKOUT" || $v.getMarkType() == "CHECKIN_CHECKOUT" || $v.getMarkType() == "CHECKOUT")
|
| 59 |
#set($completedCount = $completedCount + 1)
|
59 |
#set($completedCount = $completedCount + 1)
|
| 60 |
#elseif($v.getMarkType() == "PENDING" || $v.getMarkType() == "CHECKIN")
|
60 |
#elseif($v.getMarkType() == "PENDING" || $v.getMarkType() == "CHECKIN")
|
| 61 |
#set($pendingCount = $pendingCount + 1)
|
61 |
#set($pendingCount = $pendingCount + 1)
|
| 62 |
#elseif($v.getMarkType() == "DEFERRED")
|
62 |
#elseif($v.getMarkType() == "DEFERRED")
|
| 63 |
#set($deferredCount = $deferredCount + 1)
|
63 |
#set($deferredCount = $deferredCount + 1)
|
| Line 123... |
Line 123... |
| 123 |
<td>#if($v.getTaskDescription() && $v.getTaskDescription() != "")#if($v.getTaskDescription().length() > 50)<span class="desc-short">$v.getTaskDescription().substring(0, 50)... <a href="javascript:void(0);" class="read-more-link" style="color:#3498db;">read more</a></span><span class="desc-full" style="display:none;">$v.getTaskDescription() <a href="javascript:void(0);" class="read-less-link" style="color:#3498db;">read less</a></span>#else$v.getTaskDescription()#end#else--#end</td>
|
123 |
<td>#if($v.getTaskDescription() && $v.getTaskDescription() != "")#if($v.getTaskDescription().length() > 50)<span class="desc-short">$v.getTaskDescription().substring(0, 50)... <a href="javascript:void(0);" class="read-more-link" style="color:#3498db;">read more</a></span><span class="desc-full" style="display:none;">$v.getTaskDescription() <a href="javascript:void(0);" class="read-less-link" style="color:#3498db;">read less</a></span>#else$v.getTaskDescription()#end#else--#end</td>
|
| 124 |
<td>
|
124 |
<td>
|
| 125 |
#if($v.getAttachment() && $v.getAttachment() != "")
|
125 |
#if($v.getAttachment() && $v.getAttachment() != "")
|
| 126 |
<img src="/download-attachment?documentId=$v.getAttachment()" class="view-attachment-link" alt="IMAGE" width="50" data-attachment="$v.getAttachment()">
|
126 |
<img src="/download-attachment?documentId=$v.getAttachment()" class="view-attachment-link" alt="IMAGE" width="50" data-attachment="$v.getAttachment()">
|
| 127 |
#end
|
127 |
#end
|
| 128 |
#if($v.getMarkType() == "CHECKIN-CHECKOUT" || $v.getMarkType() == "CHECKOUT")
|
128 |
#if($v.getMarkType() == "CHECKIN-CHECKOUT" || $v.getMarkType() == "CHECKIN_CHECKOUT" || $v.getMarkType() == "CHECKOUT")
|
| 129 |
<span class="label label-success">$v.getMarkType()</span>
|
129 |
<span class="label label-success">$v.getMarkType()</span>
|
| 130 |
#elseif($v.getMarkType() == "PENDING" || $v.getMarkType() == "CHECKIN")
|
130 |
#elseif($v.getMarkType() == "PENDING" || $v.getMarkType() == "CHECKIN")
|
| 131 |
<span class="label label-warning">$v.getMarkType()</span>
|
131 |
<span class="label label-warning">$v.getMarkType()</span>
|
| 132 |
#elseif($v.getMarkType() == "DEFERRED")
|
132 |
#elseif($v.getMarkType() == "DEFERRED")
|
| 133 |
<span class="label label-danger">$v.getMarkType()</span>
|
133 |
<span class="label label-danger">$v.getMarkType()</span>
|
| Line 327... |
Line 327... |
| 327 |
var liveTrackingMap = null;
|
327 |
var liveTrackingMap = null;
|
| 328 |
var liveTrackingUserId = null;
|
328 |
var liveTrackingUserId = null;
|
| 329 |
var liveTrackingInterval = null;
|
329 |
var liveTrackingInterval = null;
|
| 330 |
|
330 |
|
| 331 |
function getMarkerColor(markType) {
|
331 |
function getMarkerColor(markType) {
|
| 332 |
if (markType === 'CHECKIN-CHECKOUT' || markType === 'CHECKOUT') return '#0F9D58';
|
332 |
if (markType === 'CHECKIN-CHECKOUT' || markType === 'CHECKIN_CHECKOUT' || markType === 'CHECKOUT') return '#0F9D58';
|
| 333 |
if (markType === 'PENDING' || markType === 'CHECKIN') return '#4285F4';
|
333 |
if (markType === 'PENDING' || markType === 'CHECKIN') return '#4285F4';
|
| 334 |
if (markType === 'DEFERRED') return '#DB4437';
|
334 |
if (markType === 'DEFERRED') return '#DB4437';
|
| 335 |
if (markType === 'PUNCHIN') return '#1abc9c';
|
335 |
if (markType === 'PUNCHIN') return '#1abc9c';
|
| 336 |
if (markType === 'PUNCHOUT') return '#e74c3c';
|
336 |
if (markType === 'PUNCHOUT') return '#e74c3c';
|
| 337 |
return '#F4B400';
|
337 |
return '#F4B400';
|
| Line 472... |
Line 472... |
| 472 |
var bounds = new google.maps.LatLngBounds();
|
472 |
var bounds = new google.maps.LatLngBounds();
|
| 473 |
var hasPoints = false;
|
473 |
var hasPoints = false;
|
| 474 |
var visitIdx = 0;
|
474 |
var visitIdx = 0;
|
| 475 |
var visitCoords = [];
|
475 |
var visitCoords = [];
|
| 476 |
|
476 |
|
| - |
|
477 |
// Pre-compute first/last valid path point as fallback for punch records with no coordinates
|
| - |
|
478 |
var firstPathLat = null, firstPathLng = null;
|
| - |
|
479 |
var lastPathLat = null, lastPathLng = null;
|
| - |
|
480 |
for (var pi = 0; pi < pathPoints.length; pi++) {
|
| - |
|
481 |
var ppLat = parseFloat(pathPoints[pi].lat), ppLng = parseFloat(pathPoints[pi].lng);
|
| - |
|
482 |
if (!isNaN(ppLat) && !isNaN(ppLng) && !(ppLat === 0 && ppLng === 0)) {
|
| - |
|
483 |
if (firstPathLat === null) { firstPathLat = ppLat; firstPathLng = ppLng; }
|
| - |
|
484 |
lastPathLat = ppLat; lastPathLng = ppLng;
|
| - |
|
485 |
}
|
| - |
|
486 |
}
|
| - |
|
487 |
|
| 477 |
for (var i = 0; i < visits.length; i++) {
|
488 |
for (var i = 0; i < visits.length; i++) {
|
| 478 |
var v = visits[i];
|
489 |
var v = visits[i];
|
| 479 |
if (!v.lat || !v.lng) continue;
|
- |
|
| 480 |
var lat = parseFloat(v.lat);
|
490 |
var lat = v.lat ? parseFloat(v.lat) : NaN;
|
| 481 |
var lng = parseFloat(v.lng);
|
491 |
var lng = v.lng ? parseFloat(v.lng) : NaN;
|
| 482 |
if (isNaN(lat) || isNaN(lng) || (lat === 0 && lng === 0)) continue;
|
492 |
if (isNaN(lat) || isNaN(lng) || (lat === 0 && lng === 0)) {
|
| - |
|
493 |
if (v.markType === 'PUNCHIN' && firstPathLat !== null) {
|
| - |
|
494 |
lat = firstPathLat; lng = firstPathLng;
|
| - |
|
495 |
} else if (v.markType === 'PUNCHOUT' && lastPathLat !== null) {
|
| - |
|
496 |
lat = lastPathLat; lng = lastPathLng;
|
| - |
|
497 |
} else {
|
| - |
|
498 |
continue;
|
| - |
|
499 |
}
|
| - |
|
500 |
}
|
| 483 |
|
501 |
|
| 484 |
hasPoints = true;
|
502 |
hasPoints = true;
|
| 485 |
var pos = { lat: lat, lng: lng };
|
503 |
var pos = { lat: lat, lng: lng };
|
| 486 |
bounds.extend(pos);
|
504 |
bounds.extend(pos);
|
| 487 |
visitCoords.push(pos);
|
505 |
visitCoords.push(pos);
|
| Line 701... |
Line 719... |
| 701 |
liveTrackingMap.fitBounds(bounds);
|
719 |
liveTrackingMap.fitBounds(bounds);
|
| 702 |
}
|
720 |
}
|
| 703 |
|
721 |
|
| 704 |
// Info bar
|
722 |
// Info bar
|
| 705 |
var totalVisits = visits.filter(function (v) { return v.markType !== 'PUNCHIN' && v.markType !== 'PUNCHOUT'; }).length;
|
723 |
var totalVisits = visits.filter(function (v) { return v.markType !== 'PUNCHIN' && v.markType !== 'PUNCHOUT'; }).length;
|
| 706 |
var completedVisits = visits.filter(function (v) { return v.markType === 'CHECKIN-CHECKOUT' || v.markType === 'CHECKOUT'; }).length;
|
724 |
var completedVisits = visits.filter(function (v) { return v.markType === 'CHECKIN-CHECKOUT' || v.markType === 'CHECKIN_CHECKOUT' || v.markType === 'CHECKOUT'; }).length;
|
| 707 |
var now = new Date();
|
725 |
var now = new Date();
|
| 708 |
var timeStr = now.getHours().toString().padStart(2, '0') + ':' + now.getMinutes().toString().padStart(2, '0') + ':' + now.getSeconds().toString().padStart(2, '0');
|
726 |
var timeStr = now.getHours().toString().padStart(2, '0') + ':' + now.getMinutes().toString().padStart(2, '0') + ':' + now.getSeconds().toString().padStart(2, '0');
|
| 709 |
|
727 |
|
| 710 |
var avgAccuracy = accuracyCount > 0 ? (accuracySum / accuracyCount).toFixed(0) : null;
|
728 |
var avgAccuracy = accuracyCount > 0 ? (accuracySum / accuracyCount).toFixed(0) : null;
|
| 711 |
var accuracyLabel = '';
|
729 |
var accuracyLabel = '';
|
| Line 717... |
Line 735... |
| 717 |
else { accuracyLabel = 'Low'; accuracyBadge = 'background:#e74c3c;'; }
|
735 |
else { accuracyLabel = 'Low'; accuracyBadge = 'background:#e74c3c;'; }
|
| 718 |
}
|
736 |
}
|
| 719 |
|
737 |
|
| 720 |
var totalGpsKm = (totalGpsDistanceM / 1000).toFixed(2);
|
738 |
var totalGpsKm = (totalGpsDistanceM / 1000).toFixed(2);
|
| 721 |
|
739 |
|
| - |
|
740 |
var punchInRecord = visits.filter(function (v) { return v.markType === 'PUNCHIN'; })[0];
|
| - |
|
741 |
var punchOutRecord = visits.filter(function (v) { return v.markType === 'PUNCHOUT'; })[0];
|
| - |
|
742 |
|
| 722 |
var infoHtml = '<strong>' + username + '</strong> | ' +
|
743 |
var infoHtml = '<strong>' + username + '</strong> | ';
|
| - |
|
744 |
if (punchInRecord && punchInRecord.checkInTime) {
|
| - |
|
745 |
infoHtml += '<i class="fa fa-sign-in" style="color:#1abc9c;"></i> In: <strong>' + punchInRecord.checkInTime + '</strong> | ';
|
| - |
|
746 |
}
|
| - |
|
747 |
if (punchOutRecord && punchOutRecord.checkInTime) {
|
| - |
|
748 |
infoHtml += '<i class="fa fa-sign-out" style="color:#e74c3c;"></i> Out: <strong>' + punchOutRecord.checkInTime + '</strong> | ';
|
| - |
|
749 |
}
|
| 723 |
'Visits: <strong>' + totalVisits + '</strong> | ' +
|
750 |
infoHtml += 'Visits: <strong>' + totalVisits + '</strong> | ' +
|
| 724 |
'Completed: <strong>' + completedVisits + '</strong>';
|
751 |
'Completed: <strong>' + completedVisits + '</strong>';
|
| 725 |
|
752 |
|
| 726 |
if (pathPoints.length > 0) {
|
753 |
if (pathPoints.length > 0) {
|
| 727 |
infoHtml += ' | GPS Distance: <strong>' + totalGpsKm + ' km</strong>';
|
754 |
infoHtml += ' | GPS Distance: <strong>' + totalGpsKm + ' km</strong>';
|
| 728 |
infoHtml += ' | Points: <strong>' + pathPoints.length + '</strong>';
|
755 |
infoHtml += ' | Points: <strong>' + pathPoints.length + '</strong>';
|
| Line 739... |
Line 766... |
| 739 |
|
766 |
|
| 740 |
infoHtml += ' | <span style="color:#e74c3c;"><i class="fa fa-arrow-right" style="font-size:10px;"></i></span> GPS Trail';
|
767 |
infoHtml += ' | <span style="color:#e74c3c;"><i class="fa fa-arrow-right" style="font-size:10px;"></i></span> GPS Trail';
|
| 741 |
if (visitCoords.length > 1) {
|
768 |
if (visitCoords.length > 1) {
|
| 742 |
infoHtml += ' | <span style="background:#8e44ad; display:inline-block; width:14px; height:3px; vertical-align:middle; margin:0 2px;"></span> Route';
|
769 |
infoHtml += ' | <span style="background:#8e44ad; display:inline-block; width:14px; height:3px; vertical-align:middle; margin:0 2px;"></span> Route';
|
| 743 |
}
|
770 |
}
|
| - |
|
771 |
var battery = data.batteryLevel;
|
| - |
|
772 |
if (battery !== null && battery !== undefined) {
|
| - |
|
773 |
var batteryColor = battery > 50 ? '#27ae60' : (battery > 20 ? '#f39c12' : '#e74c3c');
|
| - |
|
774 |
var batteryIcon = battery > 50 ? 'fa-battery-full' : (battery > 20 ? 'fa-battery-half' : 'fa-battery-quarter');
|
| - |
|
775 |
infoHtml += ' | <span style="color:' + batteryColor + ';"><i class="fa ' + batteryIcon + '"></i> Battery: <strong>' + battery + '%</strong></span>';
|
| - |
|
776 |
}
|
| - |
|
777 |
|
| 744 |
var locToggle = data.locationToggleCount || 0;
|
778 |
var locToggle = data.locationToggleCount || 0;
|
| 745 |
if (locToggle > 0) {
|
779 |
if (locToggle > 0) {
|
| 746 |
infoHtml += ' | <span class="label" style="background:#e74c3c; color:white; padding:2px 8px; border-radius:3px; font-size:11px;"><i class="fa fa-power-off"></i> Loc Off: ' + locToggle + '</span>';
|
780 |
infoHtml += ' | <span class="label" style="background:#e74c3c; color:white; padding:2px 8px; border-radius:3px; font-size:11px;"><i class="fa fa-power-off"></i> Loc Off: ' + locToggle + '</span>';
|
| 747 |
}
|
781 |
}
|
| 748 |
|
782 |
|
| Line 799... |
Line 833... |
| 799 |
|
833 |
|
| 800 |
setTimeout(function () {
|
834 |
setTimeout(function () {
|
| 801 |
liveTrackingMap = null;
|
835 |
liveTrackingMap = null;
|
| 802 |
ensureGoogleMaps(function () {
|
836 |
ensureGoogleMaps(function () {
|
| 803 |
loadLiveTracking(liveTrackingUserId, liveTrackingUsername);
|
837 |
loadLiveTracking(liveTrackingUserId, liveTrackingUsername);
|
| 804 |
startAutoRefresh();
|
- |
|
| 805 |
});
|
838 |
});
|
| 806 |
}, 300);
|
839 |
}, 300);
|
| 807 |
});
|
840 |
});
|
| 808 |
|
841 |
|
| 809 |
$(document).on('click.livetrack', '#refreshTrackingBtn', function () {
|
842 |
$(document).on('click.livetrack', '#refreshTrackingBtn', function () {
|