Subversion Repositories SmartDukaan

Rev

Rev 36668 | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 36668 Rev 36681
Line 39... Line 39...
39
            </span>
39
            </span>
40
        </div>
40
        </div>
41
    </div>
41
    </div>
42
 
42
 
43
    <div class="row">
43
    <div class="row">
44
        <div class="col-lg-9">
44
        <div class="col-lg-8">
45
            <div id="blMap" style="width:100%; height:520px; border-radius:8px; background:#eee;"></div>
45
            <div id="blMap" style="width:100%; height:520px; border-radius:8px; background:#eee;"></div>
46
        </div>
46
        </div>
47
        <div class="col-lg-3">
47
        <div class="col-lg-4">
48
            <div style="padding:10px; background:#f8f9fa; border-radius:6px;">
48
            <div style="padding:10px; background:#f8f9fa; border-radius:6px; margin-bottom:12px;">
-
 
49
                <div style="font-weight:600; font-size:13px; margin-bottom:6px;">Add new base location</div>
49
                <div style="font-size:12px; color:#555;">Selected:</div>
50
                <div style="font-size:12px; color:#555;">Selected:</div>
50
                <div id="blPicked" style="font-weight:600; margin-top:4px;">-</div>
51
                <div id="blPicked" style="font-weight:600; margin-top:4px;">-</div>
51
                <div id="blPickedCoords" style="font-size:11px; color:#888; margin-top:4px;"></div>
52
                <div id="blPickedCoords" style="font-size:11px; color:#888; margin-top:4px;"></div>
-
 
53
                <label style="display:block; margin-top:10px; font-size:12px; font-weight:normal;">
-
 
54
                    <input type="checkbox" id="blMakeDefault"> Mark as default location
-
 
55
                </label>
52
                <button id="blSave" class="btn btn-primary" style="width:100%; margin-top:14px;" disabled>
56
                <button id="blSave" class="btn btn-primary btn-sm" style="width:100%; margin-top:10px;" disabled>
53
                    Save Base Location
57
                    Save Location
54
                </button>
58
                </button>
55
                <div id="blMsg" style="margin-top:10px; font-size:12px;"></div>
59
                <div id="blMsg" style="margin-top:10px; font-size:12px;"></div>
56
            </div>
60
            </div>
-
 
61
 
-
 
62
            <div style="background:#fff; border:1px solid #e5e5e5; border-radius:6px;">
-
 
63
                <div style="padding:8px 12px; font-weight:600; font-size:13px; border-bottom:1px solid #eee; background:#f5f5f5;">
-
 
64
                    Saved locations
-
 
65
                </div>
-
 
66
                <div id="blList" style="max-height:400px; overflow-y:auto;">
-
 
67
                    <div style="padding:14px; color:#999; font-size:12px; text-align:center;">
-
 
68
                        Select a user to see their base locations
-
 
69
                    </div>
-
 
70
                </div>
-
 
71
            </div>
57
        </div>
72
        </div>
58
    </div>
73
    </div>
59
</section>
74
</section>
60
 
75
 
61
<script>
76
<script>
62
    // Ensure Google Maps + Places is loaded (works whether page is loaded inline or full)
-
 
63
    function blEnsureMaps(cb) {
77
    function blEnsureMaps(cb) {
64
        if (typeof google !== 'undefined' && google.maps && google.maps.places) {
78
        if (typeof google !== 'undefined' && google.maps && google.maps.places) {
65
            cb();
79
            cb();
66
            return;
80
            return;
67
        }
81
        }
Line 84... Line 98...
84
        };
98
        };
85
        document.head.appendChild(s);
99
        document.head.appendChild(s);
86
    }
100
    }
87
 
101
 
88
    blEnsureMaps(function () {
102
    blEnsureMaps(function () {
89
        var blMap, blMarker, blAutocomplete;
103
        var blMap, blAutocomplete;
-
 
104
        var blMarkers = {};  // id → google.maps.Marker for saved locations
-
 
105
        var pickMarker = null; // the draggable "new" pin
90
        var picked = {lat: null, lng: null, name: '', address: ''};
106
        var picked = {lat: null, lng: null, name: '', address: ''};
91
 
107
 
-
 
108
        function readServerMsg(xhr, fallback) {
-
 
109
            try {
-
 
110
                var err = JSON.parse(xhr.responseText);
-
 
111
                if (err && err.response) {
-
 
112
                    if (typeof err.response === 'string') return err.response;
-
 
113
                    if (err.response.message) return err.response.message;
-
 
114
                }
-
 
115
            } catch (e) {
-
 
116
            }
-
 
117
            return fallback || 'Request failed';
-
 
118
        }
-
 
119
 
92
        function setPicked(lat, lng, name, address) {
120
        function setPicked(lat, lng, name, address) {
93
            picked.lat = lat;
121
            picked.lat = lat;
94
            picked.lng = lng;
122
            picked.lng = lng;
95
            picked.name = name || '';
123
            picked.name = name || '';
96
            picked.address = address || name || '';
124
            picked.address = address || name || '';
97
            $('#blPicked').text(name || '(unnamed pin)');
125
            $('#blPicked').text(name || '(unnamed pin)');
98
            $('#blPickedCoords').text(lat.toFixed(6) + ', ' + lng.toFixed(6));
126
            $('#blPickedCoords').text(lat.toFixed(6) + ', ' + lng.toFixed(6));
99
            $('#blSave').prop('disabled', !$('#blAuthUser').val());
127
            $('#blSave').prop('disabled', !$('#blAuthUser').val());
100
        }
128
        }
101
 
129
 
102
        function placeMarker(lat, lng) {
130
        function placePickMarker(lat, lng) {
103
            var pos = new google.maps.LatLng(lat, lng);
131
            var pos = new google.maps.LatLng(lat, lng);
104
            if (!blMarker) {
132
            if (!pickMarker) {
-
 
133
                pickMarker = new google.maps.Marker({
105
                blMarker = new google.maps.Marker({map: blMap, position: pos, draggable: true});
134
                    map: blMap, position: pos, draggable: true,
-
 
135
                    icon: {
-
 
136
                        path: google.maps.SymbolPath.CIRCLE, scale: 10,
-
 
137
                        fillColor: '#3b82f6', fillOpacity: 0.95, strokeColor: '#fff', strokeWeight: 2
-
 
138
                    },
-
 
139
                    zIndex: 9999
-
 
140
                });
106
                blMarker.addListener('dragend', function (e) {
141
                pickMarker.addListener('dragend', function (e) {
107
                    setPicked(e.latLng.lat(), e.latLng.lng(), picked.name, picked.address);
142
                    setPicked(e.latLng.lat(), e.latLng.lng(), picked.name, picked.address);
108
                });
143
                });
109
            } else {
144
            } else {
110
                blMarker.setPosition(pos);
145
                pickMarker.setPosition(pos);
111
            }
146
            }
112
            blMap.panTo(pos);
147
            blMap.panTo(pos);
113
            if (blMap.getZoom() < 12) blMap.setZoom(14);
148
            if (blMap.getZoom() < 12) blMap.setZoom(14);
114
        }
149
        }
115
 
150
 
-
 
151
        function clearSavedMarkers() {
-
 
152
            Object.keys(blMarkers).forEach(function (id) {
-
 
153
                blMarkers[id].setMap(null);
-
 
154
            });
-
 
155
            blMarkers = {};
-
 
156
        }
-
 
157
 
-
 
158
        function addSavedMarker(loc) {
-
 
159
            var pos = new google.maps.LatLng(parseFloat(loc.latitude), parseFloat(loc.longitude));
-
 
160
            var m = new google.maps.Marker({
116
        // Init map (centered on India)
161
                map: blMap, position: pos,
-
 
162
                title: (loc.isDefault ? '[Default] ' : '') + (loc.locationName || ''),
-
 
163
                icon: {
-
 
164
                    path: google.maps.SymbolPath.CIRCLE, scale: 8,
-
 
165
                    fillColor: loc.isDefault ? '#16a34a' : '#94a3b8', fillOpacity: 0.95,
-
 
166
                    strokeColor: '#fff', strokeWeight: 2
-
 
167
                }
-
 
168
            });
-
 
169
            blMarkers[loc.id] = m;
-
 
170
        }
-
 
171
 
117
        blMap = new google.maps.Map(document.getElementById('blMap'), {
172
        blMap = new google.maps.Map(document.getElementById('blMap'), {
118
            center: {lat: 22.0, lng: 79.0}, zoom: 5
173
            center: {lat: 22.0, lng: 79.0}, zoom: 5
119
        });
174
        });
120
        blMap.addListener('click', function (e) {
175
        blMap.addListener('click', function (e) {
121
            placeMarker(e.latLng.lat(), e.latLng.lng());
176
            placePickMarker(e.latLng.lat(), e.latLng.lng());
122
            setPicked(e.latLng.lat(), e.latLng.lng(), 'Picked location', '');
177
            setPicked(e.latLng.lat(), e.latLng.lng(), 'Picked location', '');
123
        });
178
        });
124
 
179
 
125
        // Places autocomplete
-
 
126
        blAutocomplete = new google.maps.places.Autocomplete(
180
        blAutocomplete = new google.maps.places.Autocomplete(
127
                document.getElementById('blSearch'),
181
                document.getElementById('blSearch'),
128
                {componentRestrictions: {country: 'in'}}
182
                {componentRestrictions: {country: 'in'}}
129
        );
183
        );
130
        blAutocomplete.addListener('place_changed', function () {
184
        blAutocomplete.addListener('place_changed', function () {
131
            var p = blAutocomplete.getPlace();
185
            var p = blAutocomplete.getPlace();
132
            if (!p.geometry) return;
186
            if (!p.geometry) return;
133
            var lat = p.geometry.location.lat(), lng = p.geometry.location.lng();
187
            var lat = p.geometry.location.lat(), lng = p.geometry.location.lng();
134
            placeMarker(lat, lng);
188
            placePickMarker(lat, lng);
135
            setPicked(lat, lng, p.name || p.formatted_address || '', p.formatted_address || '');
189
            setPicked(lat, lng, p.name || p.formatted_address || '', p.formatted_address || '');
136
        });
190
        });
137
 
191
 
138
        // Level change -> load users (direct binding with namespace so re-loading
-
 
139
        // the page doesn't stack duplicate handlers from previous loads)
-
 
140
        $('#blLevel').off('change.bl').on('change.bl', function () {
192
        $('#blLevel').off('change.bl').on('change.bl', function () {
141
            var level = $(this).val();
193
            var level = $(this).val();
142
            $('#blAuthUser').html('<option value="">Select User</option>');
194
            $('#blAuthUser').html('<option value="">Select User</option>');
143
            $('#blCurrent').text('');
195
            $('#blCurrent').text('');
144
            $('#blSave').prop('disabled', true);
196
            $('#blSave').prop('disabled', true);
-
 
197
            renderList(null);
145
            if (!level) return;
198
            if (!level) return;
146
            console.log('[BL] loading users level=' + level + ' cat=' + $('#blCategory').val());
-
 
147
            $.ajax({
199
            $.ajax({
148
                url: context + '/beatPlan/getAuthUsers',
200
                url: context + '/beatPlan/getAuthUsers',
149
                type: 'GET',
-
 
150
                dataType: 'json',
201
                type: 'GET', dataType: 'json',
151
                data: {categoryId: $('#blCategory').val(), escalationType: level},
202
                data: {categoryId: $('#blCategory').val(), escalationType: level},
152
                success: function (r) {
203
                success: function (r) {
153
                    var data = r.response || r;
204
                    var data = r.response || r;
154
                    console.log('[BL] users loaded:', data && data.length);
-
 
155
                    var h = '<option value="">Select User</option>';
205
                    var h = '<option value="">Select User</option>';
156
                    if (data && data.length) {
206
                    if (data && data.length) {
157
                        data.forEach(function (u) {
207
                        data.forEach(function (u) {
158
                            h += '<option value="' + u.id + '">' + u.name + '</option>';
208
                            h += '<option value="' + u.id + '">' + u.name + '</option>';
159
                        });
209
                        });
160
                    } else {
210
                    } else {
161
                        h = '<option value="">No users at this level</option>';
211
                        h = '<option value="">No users at this level</option>';
162
                    }
212
                    }
163
                    $('#blAuthUser').html(h);
213
                    $('#blAuthUser').html(h);
164
                },
214
                },
165
                error: function (xhr) {
215
                error: function () {
166
                    console.error('[BL] getAuthUsers failed', xhr.status, xhr.responseText);
-
 
167
                    $('#blAuthUser').html('<option value="">Error loading users</option>');
216
                    $('#blAuthUser').html('<option value="">Error loading users</option>');
168
                }
217
                }
169
            });
218
            });
170
        });
219
        });
171
 
220
 
172
        // Category change resets level + user
-
 
173
        $('#blCategory').off('change.bl').on('change.bl', function () {
221
        $('#blCategory').off('change.bl').on('change.bl', function () {
174
            $('#blLevel').val('').trigger('change.bl');
222
            $('#blLevel').val('').trigger('change.bl');
175
        });
223
        });
176
 
224
 
177
        // User change -> load current base location (if any)
-
 
178
        $('#blAuthUser').off('change.bl').on('change.bl', function () {
225
        $('#blAuthUser').off('change.bl').on('change.bl', function () {
179
            var uid = $(this).val();
226
            var uid = $(this).val();
180
            $('#blCurrent').text('');
227
            $('#blCurrent').text('');
181
            $('#blSave').prop('disabled', true);
228
            $('#blSave').prop('disabled', true);
-
 
229
            if (pickMarker) {
-
 
230
                pickMarker.setMap(null);
-
 
231
                pickMarker = null;
-
 
232
            }
-
 
233
            picked = {lat: null, lng: null, name: '', address: ''};
-
 
234
            $('#blPicked').text('-');
-
 
235
            $('#blPickedCoords').text('');
-
 
236
            $('#blMakeDefault').prop('checked', false);
182
            if (!uid) return;
237
            if (!uid) {
-
 
238
                renderList(null);
-
 
239
                return;
-
 
240
            }
-
 
241
            loadList(uid);
-
 
242
        });
-
 
243
 
-
 
244
        function loadList(uid) {
183
            $.ajax({
245
            $.ajax({
184
                url: context + '/beatPlan/getBaseLocation',
246
                url: context + '/beatPlan/listBaseLocations',
185
                type: 'GET', dataType: 'json',
247
                type: 'GET', dataType: 'json',
186
                data: {authUserId: uid},
248
                data: {authUserId: uid},
187
                success: function (r) {
249
                success: function (r) {
188
                    var data = r.response || r;
250
                    var data = r.response || r;
189
                    if (data && data.locationName) {
251
                    renderList(data.locations || []);
190
                        $('#blCurrent').html('Current: <strong>' + data.locationName + '</strong> ('
-
 
191
                                + data.latitude + ', ' + data.longitude + ')');
-
 
192
                        placeMarker(parseFloat(data.latitude), parseFloat(data.longitude));
-
 
193
                        setPicked(parseFloat(data.latitude), parseFloat(data.longitude),
-
 
194
                                data.locationName, data.address || '');
-
 
195
                    } else {
252
                },
196
                        $('#blCurrent').html('<em>No base location set yet</em>');
-
 
197
                        $('#blSave').prop('disabled', picked.lat == null);
253
                error: function () {
198
                    }
254
                    renderList([]);
199
                }
255
                }
200
            });
256
            });
-
 
257
        }
-
 
258
 
-
 
259
        function renderList(locations) {
-
 
260
            clearSavedMarkers();
-
 
261
            if (locations === null) {
-
 
262
                $('#blList').html('<div style="padding:14px; color:#999; font-size:12px; text-align:center;">Select a user to see their base locations</div>');
-
 
263
                $('#blCurrent').text('');
-
 
264
                return;
-
 
265
            }
-
 
266
            if (locations.length === 0) {
-
 
267
                $('#blList').html('<div style="padding:14px; color:#999; font-size:12px; text-align:center;">No base locations yet. Pick on the map and Save.</div>');
-
 
268
                $('#blCurrent').html('<em>No base location set yet</em>');
-
 
269
                return;
-
 
270
            }
-
 
271
 
-
 
272
            var html = '';
-
 
273
            var defaultLoc = null;
-
 
274
            var bounds = new google.maps.LatLngBounds();
-
 
275
            locations.forEach(function (l) {
-
 
276
                addSavedMarker(l);
-
 
277
                bounds.extend(new google.maps.LatLng(parseFloat(l.latitude), parseFloat(l.longitude)));
-
 
278
                if (l.isDefault) defaultLoc = l;
-
 
279
 
-
 
280
                var safeName = (l.locationName || '').replace(/</g, '&lt;');
-
 
281
                var badge = l.isDefault
-
 
282
                        ? '<span style="display:inline-block;background:#16a34a;color:#fff;font-size:10px;padding:1px 6px;border-radius:3px;margin-left:4px;">DEFAULT</span>'
-
 
283
                        : '';
-
 
284
                var actions = l.isDefault
-
 
285
                        ? '<button class="btn btn-default btn-xs bl-remove-btn" disabled title="Default location cannot be removed">Remove</button>'
-
 
286
                        : '<button class="btn btn-success btn-xs bl-set-default-btn" data-id="' + l.id + '">Set Default</button> '
-
 
287
                        + '<button class="btn btn-danger btn-xs bl-remove-btn" data-id="' + l.id + '">Remove</button>';
-
 
288
 
-
 
289
                html += '<div style="padding:10px 12px; border-bottom:1px solid #f0f0f0;">'
-
 
290
                        + '<div style="font-weight:600; font-size:13px;">' + safeName + badge + '</div>'
-
 
291
                        + '<div style="font-size:11px; color:#888; margin-top:2px;">' + l.latitude + ', ' + l.longitude + '</div>'
-
 
292
                        + '<div style="margin-top:6px;">' + actions + '</div>'
-
 
293
                        + '</div>';
-
 
294
            });
-
 
295
            $('#blList').html(html);
-
 
296
 
-
 
297
            if (defaultLoc) {
-
 
298
                $('#blCurrent').html('Default: <strong>' + defaultLoc.locationName + '</strong> ('
-
 
299
                        + defaultLoc.latitude + ', ' + defaultLoc.longitude + ')');
-
 
300
            } else {
-
 
301
                $('#blCurrent').html('<em>No default set (legacy)</em>');
-
 
302
            }
-
 
303
 
-
 
304
            if (locations.length === 1) {
-
 
305
                blMap.panTo(new google.maps.LatLng(parseFloat(locations[0].latitude), parseFloat(locations[0].longitude)));
-
 
306
                if (blMap.getZoom() < 12) blMap.setZoom(13);
-
 
307
            } else {
-
 
308
                blMap.fitBounds(bounds);
-
 
309
            }
201
        });
310
        }
202
 
311
 
203
        // Save
-
 
204
        $('#blSave').off('click.bl').on('click.bl', function () {
312
        $('#blSave').off('click.bl').on('click.bl', function () {
205
            var uid = $('#blAuthUser').val();
313
            var uid = $('#blAuthUser').val();
206
            if (!uid) {
314
            if (!uid) {
207
                alert('Pick a user');
315
                alert('Pick a user');
208
                return;
316
                return;
Line 221... Line 329...
221
                data: {
329
                data: {
222
                    authUserId: uid,
330
                    authUserId: uid,
223
                    locationName: picked.name || ('Location ' + picked.lat.toFixed(4) + ',' + picked.lng.toFixed(4)),
331
                    locationName: picked.name || ('Location ' + picked.lat.toFixed(4) + ',' + picked.lng.toFixed(4)),
224
                    latitude: String(picked.lat),
332
                    latitude: String(picked.lat),
225
                    longitude: String(picked.lng),
333
                    longitude: String(picked.lng),
226
                    address: picked.address
334
                    address: picked.address,
-
 
335
                    isDefault: $('#blMakeDefault').is(':checked')
227
                },
336
                },
228
                success: function (r) {
337
                success: function (r) {
229
                    var d = r.response || r;
338
                    var d = r.response || r;
230
                    $('#blMsg').html('<span style="color:#2e7d32;">Saved successfully.</span>');
339
                    $('#blMsg').html('<span style="color:#2e7d32;">' + (d.message || 'Saved.') + '</span>');
231
                    btn.prop('disabled', false).text('Save Base Location');
340
                    btn.prop('disabled', false).text('Save Location');
-
 
341
                    if (pickMarker) {
-
 
342
                        pickMarker.setMap(null);
232
                    // Refresh "Current" line
343
                        pickMarker = null;
-
 
344
                    }
-
 
345
                    picked = {lat: null, lng: null, name: '', address: ''};
233
                    $('#blAuthUser').trigger('change');
346
                    $('#blPicked').text('-');
-
 
347
                    $('#blPickedCoords').text('');
-
 
348
                    $('#blMakeDefault').prop('checked', false);
-
 
349
                    loadList(uid);
234
                },
350
                },
235
                error: function (xhr) {
351
                error: function (xhr) {
236
                    var msg = 'Save failed';
352
                    var msg = readServerMsg(xhr, 'Save failed');
237
                    try {
-
 
238
                        var err = JSON.parse(xhr.responseText);
-
 
239
                        // response may be a plain string (e.g., forbidden) or an object with .message
-
 
240
                        if (err && err.response) {
-
 
241
                            if (typeof err.response === 'string') msg = err.response;
-
 
242
                            else if (err.response.message) msg = err.response.message;
-
 
243
                        }
-
 
244
                    } catch (e) {
-
 
245
                    }
-
 
246
                    if (xhr.status === 403 || xhr.status === 401) {
353
                    if (xhr.status === 403 || xhr.status === 401) msg += ' (HTTP ' + xhr.status + ')';
247
                        msg = msg + ' (HTTP ' + xhr.status + ')';
-
 
248
                    }
-
 
249
                    $('#blMsg').html('<span style="color:#c62828;">' + msg + '</span>');
354
                    $('#blMsg').html('<span style="color:#c62828;">' + msg + '</span>');
250
                    btn.prop('disabled', false).text('Save Base Location');
355
                    btn.prop('disabled', false).text('Save Location');
-
 
356
                }
-
 
357
            });
-
 
358
        });
-
 
359
 
-
 
360
        $(document).off('click.blset').on('click.blset', '.bl-set-default-btn', function () {
-
 
361
            var id = $(this).data('id');
-
 
362
            var uid = $('#blAuthUser').val();
-
 
363
            if (!confirm('Set this location as the default base location?')) return;
-
 
364
            $.ajax({
-
 
365
                url: context + '/beatPlan/setDefaultBaseLocation',
-
 
366
                type: 'POST',
-
 
367
                data: {id: id},
-
 
368
                success: function () {
-
 
369
                    loadList(uid);
-
 
370
                },
-
 
371
                error: function (xhr) {
-
 
372
                    alert(readServerMsg(xhr, 'Could not set default'));
-
 
373
                }
-
 
374
            });
-
 
375
        });
-
 
376
 
-
 
377
        $(document).off('click.blrm').on('click.blrm', '.bl-remove-btn', function () {
-
 
378
            if ($(this).prop('disabled')) return;
-
 
379
            var id = $(this).data('id');
-
 
380
            var uid = $('#blAuthUser').val();
-
 
381
            if (!confirm('Remove this base location? This cannot be undone.')) return;
-
 
382
            $.ajax({
-
 
383
                url: context + '/beatPlan/deleteBaseLocation',
-
 
384
                type: 'POST',
-
 
385
                data: {id: id},
-
 
386
                success: function () {
-
 
387
                    loadList(uid);
-
 
388
                },
-
 
389
                error: function (xhr) {
-
 
390
                    alert(readServerMsg(xhr, 'Could not remove'));
251
                }
391
                }
252
            });
392
            });
253
        });
393
        });
254
    });
394
    });
255
</script>
395
</script>