Subversion Repositories SmartDukaan

Rev

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

Rev 36656 Rev 36664
Line 9... Line 9...
9
            </ol>
9
            </ol>
10
        </div>
10
        </div>
11
    </div>
11
    </div>
12
    <br>
12
    <br>
13
    <div class="row col-lg-12" style="float: right;">
13
    <div class="row col-lg-12" style="float: right;">
-
 
14
        <div class="col-lg-2">
-
 
15
            <label>Level</label>
-
 
16
            <select id="user-detail-level" class="form-control" data-selected="$!userLevel">
-
 
17
                <option value="">All Levels</option>
-
 
18
                #foreach($et in $escalationTypes)
-
 
19
                    <option value="$et" #if($et.name() == $userLevel)selected#end>$et</option>
-
 
20
                #end
-
 
21
            </select>
-
 
22
        </div>
-
 
23
        <div class="col-lg-3">
-
 
24
            <label>User</label>
-
 
25
            <select id="user-detail-user" class="form-control" data-selected-auth="$!authUserId">
-
 
26
                <option value="$!authUserId">$username</option>
-
 
27
            </select>
-
 
28
        </div>
14
        <div class="col-lg-3">
29
        <div class="col-lg-3">
15
            <label>Select Date</label>
30
            <label>Select Date</label>
16
            <div class="input-group">
31
            <div class="input-group">
17
                <input type="date" id="user-detail-date" class="form-control" value="$reportDate" data-userid="$userId">
32
                <input type="date" id="user-detail-date" class="form-control" value="$reportDate" data-userid="$userId">
18
                <span class="input-group-btn">
33
                <span class="input-group-btn">
19
                    <button type="button" class="btn btn-info user-detail-refresh" data-userid="$userId" style="margin-left:10px;">
34
                    <button type="button" class="btn btn-info user-detail-refresh" data-userid="$userId" style="margin-left:10px;">
20
                        <i class="fa fa-refresh"></i> Refresh
35
                        <i class="fa fa-refresh"></i> Refresh
21
                    </button>
36
                    </button>
-
 
37
                    <button type="button" class="btn btn-success live-tracking-btn" data-userid="$userId" data-username="$username" style="margin-left:5px;">
-
 
38
                        <i class="fa fa-map-marker"></i> Live Tracking
-
 
39
                    </button>
22
                </span>
40
                </span>
23
            </div>
41
            </div>
24
        </div>
42
        </div>
25
    </div>
43
    </div>
26
    <br><br><br>
44
    <br><br><br>
Line 168... Line 186...
168
            </div>
186
            </div>
169
        </div>
187
        </div>
170
    </div>
188
    </div>
171
</div>
189
</div>
172
 
190
 
-
 
191
<!-- Live Tracking Modal -->
-
 
192
<div class="modal fade" id="liveTrackingModal" tabindex="-1" role="dialog">
-
 
193
    <div class="modal-dialog modal-lg" role="document" style="width:90%;">
-
 
194
        <div class="modal-content">
-
 
195
            <div class="modal-header" style="background:#27ae60; color:white;">
-
 
196
                <button type="button" class="close" data-dismiss="modal" style="color:white;">&times;</button>
-
 
197
                <h4 class="modal-title" id="liveTrackingModalTitle"><i class="fa fa-map-marker"></i> Live Tracking</h4>
-
 
198
            </div>
-
 
199
            <div class="modal-body" style="padding:0;">
-
 
200
                <div id="liveTrackingInfo" style="padding:8px 15px; background:#f8f9fa; border-bottom:1px solid #ddd; font-size:13px;"></div>
-
 
201
                <div id="liveTrackingMap" style="height:500px; width:100%;"></div>
-
 
202
            </div>
-
 
203
            <div class="modal-footer">
-
 
204
                <button type="button" class="btn btn-primary" id="refreshTrackingBtn"><i class="fa fa-refresh"></i> Refresh</button>
-
 
205
                <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
-
 
206
            </div>
-
 
207
        </div>
-
 
208
    </div>
-
 
209
</div>
-
 
210
 
173
<script>
211
<script>
174
    $('#userDetailTable').DataTable({
212
    $('#userDetailTable').DataTable({
175
        "scrollX": true,
213
        "scrollX": true,
176
        "bPaginate": true,
214
        "bPaginate": true,
177
        "bLengthChange": true,
215
        "bLengthChange": true,
178
        "bFilter": true,
216
        "bFilter": true,
179
        "bInfo": true,
217
        "bInfo": true,
180
        "bAutoWidth": false
218
        "bAutoWidth": false
181
    });
219
    });
182
 
220
 
-
 
221
    (function () {
-
 
222
        var level = $('#user-detail-level').data('selected');
-
 
223
        var selectedAuth = $('#user-detail-user').data('selected-auth');
-
 
224
        if (level && selectedAuth) {
-
 
225
            $.ajax({
-
 
226
                url: context + "/beatPlan/getAuthUsers", type: "GET", dataType: "json",
-
 
227
                data: { categoryId: 4, escalationType: level },
-
 
228
                success: function (r) {
-
 
229
                    var data = r.response || r;
-
 
230
                    var html = '<option value="">Select User</option>';
-
 
231
                    data.forEach(function (u) {
-
 
232
                        html += '<option value="' + u.id + '"' + (u.id == selectedAuth ? ' selected' : '') + '>' + u.name + '</option>';
-
 
233
                    });
-
 
234
                    $('#user-detail-user').html(html);
-
 
235
                }
-
 
236
            });
-
 
237
        }
-
 
238
    })();
-
 
239
 
-
 
240
    $(document).off('click.beatdetail').off('click.livetrack');
-
 
241
 
183
    $(document).on('click', '.beat-history-link', function () {
242
    $(document).on('click.beatdetail', '.beat-history-link', function () {
184
        var userId = $(this).data('userid');
243
        var userId = $(this).data('userid');
185
        var taskId = $(this).data('taskid');
244
        var taskId = $(this).data('taskid');
186
        var taskType = $(this).data('tasktype');
245
        var taskType = $(this).data('tasktype');
187
        var taskName = $(this).data('taskname');
246
        var taskName = $(this).data('taskname');
188
 
247
 
Line 193... Line 252...
193
        doGetAjaxRequestHandler(context + "/beat-report/task-history?userId=" + userId + "&taskId=" + taskId + "&taskType=" + encodeURIComponent(taskType),
252
        doGetAjaxRequestHandler(context + "/beat-report/task-history?userId=" + userId + "&taskId=" + taskId + "&taskType=" + encodeURIComponent(taskType),
194
            function (response) {
253
            function (response) {
195
                $('#taskHistoryModalBody').html(response);
254
                $('#taskHistoryModalBody').html(response);
196
            });
255
            });
197
    });
256
    });
-
 
257
 
-
 
258
    var liveTrackingMap = null;
-
 
259
    var liveTrackingUserId = null;
-
 
260
    var liveTrackingInterval = null;
-
 
261
 
-
 
262
    function getMarkerColor(markType) {
-
 
263
        if (markType === 'CHECKIN-CHECKOUT' || markType === 'CHECKOUT') return '#0F9D58';
-
 
264
        if (markType === 'PENDING' || markType === 'CHECKIN') return '#4285F4';
-
 
265
        if (markType === 'DEFERRED') return '#DB4437';
-
 
266
        if (markType === 'PUNCHIN') return '#1abc9c';
-
 
267
        if (markType === 'PUNCHOUT') return '#e74c3c';
-
 
268
        return '#F4B400';
-
 
269
    }
-
 
270
 
-
 
271
    function createTrackingMarkerIcon(color, label) {
-
 
272
        var svg = '<svg xmlns="http://www.w3.org/2000/svg" width="28" height="40" viewBox="0 0 28 40">' +
-
 
273
            '<path d="M14 0C6.268 0 0 6.268 0 14c0 10.5 14 26 14 26s14-15.5 14-26C28 6.268 21.732 0 14 0z" fill="' + color + '"/>' +
-
 
274
            '<circle cx="14" cy="14" r="7" fill="white"/>' +
-
 
275
            '<text x="14" y="18" text-anchor="middle" font-size="10" font-weight="bold" fill="' + color + '">' + label + '</text></svg>';
-
 
276
        return 'data:image/svg+xml;charset=UTF-8,' + encodeURIComponent(svg);
-
 
277
    }
-
 
278
 
-
 
279
    function loadLiveTracking(userId, username) {
-
 
280
        var selectedDate = $('#user-detail-date').val();
-
 
281
        $.ajax({
-
 
282
            url: context + "/beat-report/live-tracking",
-
 
283
            type: "GET",
-
 
284
            dataType: "json",
-
 
285
            data: { userId: userId, date: selectedDate },
-
 
286
            success: function (data) {
-
 
287
                renderLiveTrackingMap(data, username);
-
 
288
            },
-
 
289
            error: function () {
-
 
290
                $('#liveTrackingInfo').html('<span style="color:red;">Failed to load tracking data.</span>');
-
 
291
            }
-
 
292
        });
-
 
293
    }
-
 
294
 
-
 
295
    function renderLiveTrackingMap(data, username) {
-
 
296
        var visits = data.visits || [];
-
 
297
        var pathPoints = data.pathPoints || [];
-
 
298
 
-
 
299
        if (!liveTrackingMap) {
-
 
300
            liveTrackingMap = new google.maps.Map(document.getElementById('liveTrackingMap'), {
-
 
301
                zoom: 12,
-
 
302
                center: { lat: 20.5937, lng: 78.9629 },
-
 
303
                mapTypeControl: true,
-
 
304
                streetViewControl: false
-
 
305
            });
-
 
306
        }
-
 
307
 
-
 
308
        if (liveTrackingMap._markers) {
-
 
309
            liveTrackingMap._markers.forEach(function (m) { m.setMap(null); });
-
 
310
        }
-
 
311
        if (liveTrackingMap._polyline) {
-
 
312
            liveTrackingMap._polyline.setMap(null);
-
 
313
        }
-
 
314
        if (liveTrackingMap._currentMarker) {
-
 
315
            liveTrackingMap._currentMarker.setMap(null);
-
 
316
        }
-
 
317
        liveTrackingMap._markers = [];
-
 
318
 
-
 
319
        var bounds = new google.maps.LatLngBounds();
-
 
320
        var hasPoints = false;
-
 
321
        var visitIdx = 0;
-
 
322
 
-
 
323
        for (var i = 0; i < visits.length; i++) {
-
 
324
            var v = visits[i];
-
 
325
            if (!v.lat || !v.lng) continue;
-
 
326
            var lat = parseFloat(v.lat);
-
 
327
            var lng = parseFloat(v.lng);
-
 
328
            if (isNaN(lat) || isNaN(lng) || (lat === 0 && lng === 0)) continue;
-
 
329
 
-
 
330
            hasPoints = true;
-
 
331
            var pos = { lat: lat, lng: lng };
-
 
332
            bounds.extend(pos);
-
 
333
 
-
 
334
            var label = v.markType === 'PUNCHIN' ? 'P' : (v.markType === 'PUNCHOUT' ? 'X' : String(++visitIdx));
-
 
335
            var marker = new google.maps.Marker({
-
 
336
                position: pos,
-
 
337
                map: liveTrackingMap,
-
 
338
                icon: {
-
 
339
                    url: createTrackingMarkerIcon(getMarkerColor(v.markType), label),
-
 
340
                    scaledSize: new google.maps.Size(28, 40),
-
 
341
                    anchor: new google.maps.Point(14, 40)
-
 
342
                },
-
 
343
                title: v.taskName + ' (' + v.markType + ')'
-
 
344
            });
-
 
345
 
-
 
346
            var infoContent = '<div style="font-size:13px; max-width:250px;">' +
-
 
347
                '<strong>' + (v.taskName || '') + '</strong><br>' +
-
 
348
                '<span class="label" style="background:' + getMarkerColor(v.markType) + '; color:white;">' + v.markType + '</span><br>' +
-
 
349
                (v.checkInTime ? '<i class="fa fa-sign-in"></i> ' + v.checkInTime + '<br>' : '') +
-
 
350
                (v.checkOutTime ? '<i class="fa fa-sign-out"></i> ' + v.checkOutTime + '<br>' : '') +
-
 
351
                (v.address ? '<i class="fa fa-map-marker"></i> ' + v.address : '') +
-
 
352
                '</div>';
-
 
353
            (function (m, c) {
-
 
354
                var iw = new google.maps.InfoWindow({ content: c });
-
 
355
                m.addListener('click', function () { iw.open(liveTrackingMap, m); });
-
 
356
            })(marker, infoContent);
-
 
357
 
-
 
358
            liveTrackingMap._markers.push(marker);
-
 
359
        }
-
 
360
 
-
 
361
        if (pathPoints.length > 0) {
-
 
362
            var pathCoords = [];
-
 
363
            for (var j = 0; j < pathPoints.length; j++) {
-
 
364
                var pt = pathPoints[j];
-
 
365
                var pLat = parseFloat(pt.lat);
-
 
366
                var pLng = parseFloat(pt.lng);
-
 
367
                if (isNaN(pLat) || isNaN(pLng) || (pLat === 0 && pLng === 0)) continue;
-
 
368
                pathCoords.push({ lat: pLat, lng: pLng });
-
 
369
                bounds.extend({ lat: pLat, lng: pLng });
-
 
370
                hasPoints = true;
-
 
371
            }
-
 
372
 
-
 
373
            if (pathCoords.length > 0) {
-
 
374
                liveTrackingMap._polyline = new google.maps.Polyline({
-
 
375
                    path: pathCoords,
-
 
376
                    geodesic: true,
-
 
377
                    strokeColor: '#3498db',
-
 
378
                    strokeOpacity: 0.8,
-
 
379
                    strokeWeight: 3,
-
 
380
                    map: liveTrackingMap
-
 
381
                });
-
 
382
 
-
 
383
                var lastPoint = pathCoords[pathCoords.length - 1];
-
 
384
                liveTrackingMap._currentMarker = new google.maps.Marker({
-
 
385
                    position: lastPoint,
-
 
386
                    map: liveTrackingMap,
-
 
387
                    icon: {
-
 
388
                        path: google.maps.SymbolPath.CIRCLE,
-
 
389
                        scale: 10,
-
 
390
                        fillColor: '#e74c3c',
-
 
391
                        fillOpacity: 1,
-
 
392
                        strokeColor: '#ffffff',
-
 
393
                        strokeWeight: 3
-
 
394
                    },
-
 
395
                    title: 'Last Known Location',
-
 
396
                    zIndex: 999
-
 
397
                });
-
 
398
 
-
 
399
                var lastTs = pathPoints[pathPoints.length - 1].timestamp || 'N/A';
-
 
400
                var currentIw = new google.maps.InfoWindow({
-
 
401
                    content: '<div style="font-size:13px;"><strong><i class="fa fa-crosshairs"></i> Last Known Location</strong><br>Time: ' + lastTs + '</div>'
-
 
402
                });
-
 
403
                liveTrackingMap._currentMarker.addListener('click', function () {
-
 
404
                    currentIw.open(liveTrackingMap, liveTrackingMap._currentMarker);
-
 
405
                });
-
 
406
            }
-
 
407
        }
-
 
408
 
-
 
409
        if (hasPoints) {
-
 
410
            liveTrackingMap.fitBounds(bounds);
-
 
411
        }
-
 
412
 
-
 
413
        var totalVisits = visits.filter(function (v) { return v.markType !== 'PUNCHIN' && v.markType !== 'PUNCHOUT'; }).length;
-
 
414
        var completedVisits = visits.filter(function (v) { return v.markType === 'CHECKIN-CHECKOUT' || v.markType === 'CHECKOUT'; }).length;
-
 
415
        $('#liveTrackingInfo').html(
-
 
416
            '<strong>' + username + '</strong> | ' +
-
 
417
            'Visits: <strong>' + totalVisits + '</strong> | ' +
-
 
418
            'Completed: <strong>' + completedVisits + '</strong> | ' +
-
 
419
            'Path Points: <strong>' + pathPoints.length + '</strong>' +
-
 
420
            (pathPoints.length > 0 ? ' | <span style="color:#e74c3c;"><i class="fa fa-circle"></i></span> Last Known Location' : '')
-
 
421
        );
-
 
422
    }
-
 
423
 
-
 
424
    function ensureGoogleMaps(callback) {
-
 
425
        if (window.google && window.google.maps) {
-
 
426
            callback();
-
 
427
            return;
-
 
428
        }
-
 
429
        var s = document.createElement('script');
-
 
430
        s.src = 'https://maps.googleapis.com/maps/api/js?key=AIzaSyAckO0y4Z6WhBOuMjNjioWLSYZDhGEvGBc&v=weekly';
-
 
431
        s.onload = callback;
-
 
432
        document.head.appendChild(s);
-
 
433
    }
-
 
434
 
-
 
435
    $(document).on('click.livetrack', '.live-tracking-btn', function () {
-
 
436
        liveTrackingUserId = $(this).data('userid');
-
 
437
        var username = $(this).data('username');
-
 
438
        $('#liveTrackingModalTitle').html('<i class="fa fa-map-marker"></i> Live Tracking - ' + username);
-
 
439
        $('#liveTrackingInfo').html('<div class="text-center"><i class="fa fa-spinner fa-spin"></i> Loading...</div>');
-
 
440
        $('#liveTrackingModal').modal('show');
-
 
441
 
-
 
442
        setTimeout(function () {
-
 
443
            liveTrackingMap = null;
-
 
444
            ensureGoogleMaps(function () {
-
 
445
                loadLiveTracking(liveTrackingUserId, username);
-
 
446
            });
-
 
447
        }, 300);
-
 
448
    });
-
 
449
 
-
 
450
    $(document).on('click.livetrack', '#refreshTrackingBtn', function () {
-
 
451
        if (liveTrackingUserId) {
-
 
452
            var username = $('#liveTrackingModalTitle').text().replace('Live Tracking - ', '').trim();
-
 
453
            $('#liveTrackingInfo').html('<div class="text-center"><i class="fa fa-spinner fa-spin"></i> Refreshing...</div>');
-
 
454
            loadLiveTracking(liveTrackingUserId, username);
-
 
455
        }
-
 
456
    });
-
 
457
 
-
 
458
    $('#liveTrackingModal').on('hidden.bs.modal', function () {
-
 
459
        if (liveTrackingInterval) {
-
 
460
            clearInterval(liveTrackingInterval);
-
 
461
            liveTrackingInterval = null;
-
 
462
        }
-
 
463
    });
198
</script>
464
</script>