| Line 1178... |
Line 1178... |
| 1178 |
prompt("Share this link with the prospect:", url);
|
1178 |
prompt("Share this link with the prospect:", url);
|
| 1179 |
}
|
1179 |
}
|
| 1180 |
});
|
1180 |
});
|
| 1181 |
});
|
1181 |
});
|
| 1182 |
|
1182 |
|
| - |
|
1183 |
// Manual geo verification — for cases where the prospect refused to tap the
|
| - |
|
1184 |
// verification link. Team-member confirms lat/lng over phone (or pastes a
|
| - |
|
1185 |
// Maps URL the prospect shared) and writes it APPROVED directly.
|
| - |
|
1186 |
function ensureManualVerifyModal() {
|
| - |
|
1187 |
if ($('#manualVerifyGeoModal').length) return;
|
| - |
|
1188 |
var html =
|
| - |
|
1189 |
'<div id="manualVerifyGeoModal" class="modal" role="dialog" tabindex="-1">' +
|
| - |
|
1190 |
' <div class="modal-dialog modal-lg">' +
|
| - |
|
1191 |
' <div class="modal-content">' +
|
| - |
|
1192 |
' <div class="modal-header">' +
|
| - |
|
1193 |
' <button type="button" class="close" data-dismiss="modal">×</button>' +
|
| - |
|
1194 |
' <h4 class="modal-title">Verify Lead Location Manually</h4>' +
|
| - |
|
1195 |
' </div>' +
|
| - |
|
1196 |
' <div class="modal-body" style="max-height:75vh;overflow-y:auto;">' +
|
| - |
|
1197 |
' <input type="hidden" id="manualVerifyLeadId">' +
|
| - |
|
1198 |
' <div class="form-group">' +
|
| - |
|
1199 |
' <label>Address <small class="text-muted">(include city, state, pincode for best result)</small></label>' +
|
| - |
|
1200 |
' <div class="input-group">' +
|
| - |
|
1201 |
' <input type="text" class="form-control" id="manualVerifyAddress" placeholder="e.g. Shop 12, Sector 18, Noida, UP, 201301">' +
|
| - |
|
1202 |
' <span class="input-group-btn">' +
|
| - |
|
1203 |
' <button type="button" class="btn btn-default" id="manualVerifyGeocodeBtn">Find on map</button>' +
|
| - |
|
1204 |
' </span>' +
|
| - |
|
1205 |
' </div>' +
|
| - |
|
1206 |
' </div>' +
|
| - |
|
1207 |
' <div class="form-group">' +
|
| - |
|
1208 |
' <label>Latitude, Longitude</label>' +
|
| - |
|
1209 |
' <div class="input-group">' +
|
| - |
|
1210 |
' <input type="text" class="form-control" id="manualVerifyLatLng" placeholder="28.6139,77.2090">' +
|
| - |
|
1211 |
' <span class="input-group-btn">' +
|
| - |
|
1212 |
' <button type="button" class="btn btn-default" id="manualVerifyPreviewBtn">Show on map</button>' +
|
| - |
|
1213 |
' </span>' +
|
| - |
|
1214 |
' </div>' +
|
| - |
|
1215 |
' <small class="text-muted">You can also paste a Google Maps URL containing coords.</small>' +
|
| - |
|
1216 |
' </div>' +
|
| - |
|
1217 |
' <div class="form-group" id="manualVerifyMapWrap" style="display:none;">' +
|
| - |
|
1218 |
' <label>Map preview <small class="text-muted">(confirm the pin is right)</small></label>' +
|
| - |
|
1219 |
' <div style="border:1px solid #ddd;border-radius:3px;overflow:hidden;">' +
|
| - |
|
1220 |
' <iframe id="manualVerifyMapFrame" width="100%" height="260" frameborder="0" style="border:0;" allowfullscreen></iframe>' +
|
| - |
|
1221 |
' </div>' +
|
| - |
|
1222 |
' <small><a href="#" id="manualVerifyOpenMap" target="_blank">Open in Google Maps</a></small>' +
|
| - |
|
1223 |
' </div>' +
|
| - |
|
1224 |
' <div class="form-group">' +
|
| - |
|
1225 |
' <label>Store-board photo <span class="text-danger">*</span> <small class="text-muted">(required — proves the verification)</small></label>' +
|
| - |
|
1226 |
' <input type="file" id="manualVerifyPhoto" accept="image/jpeg,image/png" required>' +
|
| - |
|
1227 |
' <div id="manualVerifyPhotoPreview" style="margin-top:6px;display:none;">' +
|
| - |
|
1228 |
' <img id="manualVerifyPhotoImg" src="" style="max-height:120px;border:1px solid #ddd;border-radius:3px;padding:2px;">' +
|
| - |
|
1229 |
' </div>' +
|
| - |
|
1230 |
' </div>' +
|
| - |
|
1231 |
' <div class="form-group">' +
|
| - |
|
1232 |
' <label>Remark <small class="text-muted">(optional)</small></label>' +
|
| - |
|
1233 |
' <input type="text" class="form-control" id="manualVerifyRemark" placeholder="e.g. Confirmed on call with prospect">' +
|
| - |
|
1234 |
' </div>' +
|
| - |
|
1235 |
' <div id="manualVerifyErr" class="text-danger" style="display:none;"></div>' +
|
| - |
|
1236 |
' </div>' +
|
| - |
|
1237 |
' <div class="modal-footer">' +
|
| - |
|
1238 |
' <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>' +
|
| - |
|
1239 |
' <button type="button" class="btn btn-success" id="manualVerifySaveBtn">Save & Verify</button>' +
|
| - |
|
1240 |
' </div>' +
|
| - |
|
1241 |
' </div>' +
|
| - |
|
1242 |
' </div>' +
|
| - |
|
1243 |
'</div>';
|
| - |
|
1244 |
$('body').append(html);
|
| - |
|
1245 |
}
|
| - |
|
1246 |
|
| - |
|
1247 |
function manualVerifyShowMap(lat, lng) {
|
| - |
|
1248 |
var src = 'https://www.google.com/maps?q=' + lat + ',' + lng + '&z=17&output=embed';
|
| - |
|
1249 |
$('#manualVerifyMapFrame').attr('src', src);
|
| - |
|
1250 |
$('#manualVerifyOpenMap').attr('href', 'https://www.google.com/maps?q=' + lat + ',' + lng);
|
| - |
|
1251 |
$('#manualVerifyMapWrap').show();
|
| - |
|
1252 |
}
|
| - |
|
1253 |
|
| - |
|
1254 |
// Parses raw "lat,lng" or a Google Maps URL containing the coords.
|
| - |
|
1255 |
// Maps formats covered: /@lat,lng, /maps?q=lat,lng, ?ll=lat,lng, ?destination=lat,lng.
|
| - |
|
1256 |
function parseLatLngInput(raw) {
|
| - |
|
1257 |
if (!raw) return null;
|
| - |
|
1258 |
var s = String(raw).trim();
|
| - |
|
1259 |
// Strip any leading "lat:" / "lng:" tokens
|
| - |
|
1260 |
var direct = s.match(/(-?\d{1,3}\.\d+)\s*,\s*(-?\d{1,3}\.\d+)/);
|
| - |
|
1261 |
if (direct) return {lat: parseFloat(direct[1]), lng: parseFloat(direct[2])};
|
| - |
|
1262 |
return null;
|
| - |
|
1263 |
}
|
| - |
|
1264 |
|
| - |
|
1265 |
$(document).on('click', '.verifyGeoManual', function (e) {
|
| - |
|
1266 |
e.stopPropagation();
|
| - |
|
1267 |
var lid = $(this).data('leadid');
|
| - |
|
1268 |
ensureManualVerifyModal();
|
| - |
|
1269 |
$('#manualVerifyLeadId').val(lid);
|
| - |
|
1270 |
$('#manualVerifyAddress').val('');
|
| - |
|
1271 |
$('#manualVerifyLatLng').val('');
|
| - |
|
1272 |
$('#manualVerifyRemark').val('');
|
| - |
|
1273 |
$('#manualVerifyErr').hide().text('');
|
| - |
|
1274 |
$('#manualVerifyMapWrap').hide();
|
| - |
|
1275 |
$('#manualVerifyMapFrame').attr('src', '');
|
| - |
|
1276 |
$('#manualVerifyPhoto').val('');
|
| - |
|
1277 |
$('#manualVerifyPhotoImg').attr('src', '');
|
| - |
|
1278 |
$('#manualVerifyPhotoPreview').hide();
|
| - |
|
1279 |
$('#manualVerifyGeoModal').modal('show');
|
| - |
|
1280 |
});
|
| - |
|
1281 |
|
| - |
|
1282 |
// Local thumbnail preview as soon as the user picks a file (no upload yet —
|
| - |
|
1283 |
// upload happens on Save so we don't orphan documents on cancel).
|
| - |
|
1284 |
$(document).on('change', '#manualVerifyPhoto', function () {
|
| - |
|
1285 |
var f = this.files && this.files[0];
|
| - |
|
1286 |
if (!f) {
|
| - |
|
1287 |
$('#manualVerifyPhotoPreview').hide();
|
| - |
|
1288 |
return;
|
| - |
|
1289 |
}
|
| - |
|
1290 |
var reader = new FileReader();
|
| - |
|
1291 |
reader.onload = function (ev) {
|
| - |
|
1292 |
$('#manualVerifyPhotoImg').attr('src', ev.target.result);
|
| - |
|
1293 |
$('#manualVerifyPhotoPreview').show();
|
| - |
|
1294 |
};
|
| - |
|
1295 |
reader.readAsDataURL(f);
|
| - |
|
1296 |
});
|
| - |
|
1297 |
|
| - |
|
1298 |
// Address -> coords via server (uses existing GeocodingService).
|
| - |
|
1299 |
// On success, fills the lat/lng box and renders the map preview.
|
| - |
|
1300 |
$(document).on('click', '#manualVerifyGeocodeBtn', function () {
|
| - |
|
1301 |
var addr = ($('#manualVerifyAddress').val() || '').trim();
|
| - |
|
1302 |
if (!addr) {
|
| - |
|
1303 |
$('#manualVerifyErr').text('Enter an address first.').show();
|
| - |
|
1304 |
return;
|
| - |
|
1305 |
}
|
| - |
|
1306 |
$('#manualVerifyErr').hide();
|
| - |
|
1307 |
var btn = $(this);
|
| - |
|
1308 |
btn.prop('disabled', true).text('Finding...');
|
| - |
|
1309 |
$.ajax({
|
| - |
|
1310 |
url: context + '/lead-geo/geocode-address',
|
| - |
|
1311 |
type: 'POST',
|
| - |
|
1312 |
data: {address: addr},
|
| - |
|
1313 |
success: function (resp) {
|
| - |
|
1314 |
btn.prop('disabled', false).text('Find on map');
|
| - |
|
1315 |
var r = resp && resp.response ? resp.response : resp;
|
| - |
|
1316 |
if (!r || !r.success) {
|
| - |
|
1317 |
$('#manualVerifyErr').text((r && r.message) || 'Could not geocode that address.').show();
|
| - |
|
1318 |
return;
|
| - |
|
1319 |
}
|
| - |
|
1320 |
$('#manualVerifyLatLng').val(r.latitude + ',' + r.longitude);
|
| - |
|
1321 |
manualVerifyShowMap(r.latitude, r.longitude);
|
| - |
|
1322 |
},
|
| - |
|
1323 |
error: function () {
|
| - |
|
1324 |
btn.prop('disabled', false).text('Find on map');
|
| - |
|
1325 |
$('#manualVerifyErr').text('Geocode request failed. Try again.').show();
|
| - |
|
1326 |
}
|
| - |
|
1327 |
});
|
| - |
|
1328 |
});
|
| - |
|
1329 |
|
| - |
|
1330 |
// "Show on map" — re-render the iframe from whatever is in the lat/lng input.
|
| - |
|
1331 |
// Useful after the user nudges the coords manually.
|
| - |
|
1332 |
$(document).on('click', '#manualVerifyPreviewBtn', function () {
|
| - |
|
1333 |
var parsed = parseLatLngInput($('#manualVerifyLatLng').val());
|
| - |
|
1334 |
if (!parsed) {
|
| - |
|
1335 |
$('#manualVerifyErr').text('Enter latitude,longitude first.').show();
|
| - |
|
1336 |
return;
|
| - |
|
1337 |
}
|
| - |
|
1338 |
$('#manualVerifyErr').hide();
|
| - |
|
1339 |
manualVerifyShowMap(parsed.lat, parsed.lng);
|
| - |
|
1340 |
});
|
| - |
|
1341 |
|
| - |
|
1342 |
$(document).on('click', '#manualVerifySaveBtn', function () {
|
| - |
|
1343 |
var lid = $('#manualVerifyLeadId').val();
|
| - |
|
1344 |
var raw = $('#manualVerifyLatLng').val();
|
| - |
|
1345 |
var remark = $('#manualVerifyRemark').val() || '';
|
| - |
|
1346 |
var parsed = parseLatLngInput(raw);
|
| - |
|
1347 |
if (!parsed) {
|
| - |
|
1348 |
$('#manualVerifyErr').text('Could not read latitude/longitude. Paste like 28.6139,77.2090').show();
|
| - |
|
1349 |
return;
|
| - |
|
1350 |
}
|
| - |
|
1351 |
if (parsed.lat < -90 || parsed.lat > 90 || parsed.lng < -180 || parsed.lng > 180
|
| - |
|
1352 |
|| (parsed.lat === 0 && parsed.lng === 0)) {
|
| - |
|
1353 |
$('#manualVerifyErr').text('Latitude/longitude out of range.').show();
|
| - |
|
1354 |
return;
|
| - |
|
1355 |
}
|
| - |
|
1356 |
var fileInput = document.getElementById('manualVerifyPhoto');
|
| - |
|
1357 |
var pickedFile = fileInput && fileInput.files && fileInput.files[0];
|
| - |
|
1358 |
if (!pickedFile) {
|
| - |
|
1359 |
$('#manualVerifyErr').text('Please attach a store-board photo to verify.').show();
|
| - |
|
1360 |
return;
|
| - |
|
1361 |
}
|
| - |
|
1362 |
$('#manualVerifyErr').hide();
|
| - |
|
1363 |
var btn = $('#manualVerifySaveBtn');
|
| - |
|
1364 |
btn.prop('disabled', true).text('Verifying...');
|
| - |
|
1365 |
|
| - |
|
1366 |
function callVerify(docId) {
|
| - |
|
1367 |
$.ajax({
|
| - |
|
1368 |
url: context + '/lead-geo/manual-verify',
|
| - |
|
1369 |
type: 'POST',
|
| - |
|
1370 |
data: {
|
| - |
|
1371 |
leadId: lid,
|
| - |
|
1372 |
latitude: parsed.lat,
|
| - |
|
1373 |
longitude: parsed.lng,
|
| - |
|
1374 |
remark: remark,
|
| - |
|
1375 |
imageDocumentId: docId || 0
|
| - |
|
1376 |
},
|
| - |
|
1377 |
success: function () {
|
| - |
|
1378 |
btn.prop('disabled', false).text('Save & Verify');
|
| - |
|
1379 |
$('#manualVerifyGeoModal').modal('hide');
|
| - |
|
1380 |
// Replace the cell of this lead row with the Verified badge so
|
| - |
|
1381 |
// the user sees the result without reloading the whole list.
|
| - |
|
1382 |
var $cell = $('.verifyGeoManual[data-leadid="' + lid + '"]').closest('td');
|
| - |
|
1383 |
if ($cell.length) {
|
| - |
|
1384 |
var photoBtn = docId > 0
|
| - |
|
1385 |
? ' <a href="' + context + '/open-attachment?documentId=' + docId + '" target="_blank" class="btn btn-xs btn-default" style="margin-bottom:2px;">View Photo</a>'
|
| - |
|
1386 |
: '';
|
| - |
|
1387 |
$cell.html(
|
| - |
|
1388 |
'<div class="btn-group-vertical" style="width:100%">' +
|
| - |
|
1389 |
' <span class="label label-success" style="margin-bottom:4px;">Verified</span>' +
|
| - |
|
1390 |
' <a href="https://www.google.com/maps?q=' + parsed.lat + ',' + parsed.lng + '" target="_blank" class="btn btn-xs btn-default" style="margin-bottom:2px;">View Map</a>' +
|
| - |
|
1391 |
photoBtn +
|
| - |
|
1392 |
' <button class="btn btn-xs btn-danger reject-geo" data-leadid="' + lid + '">Revoke</button>' +
|
| - |
|
1393 |
'</div>'
|
| - |
|
1394 |
);
|
| - |
|
1395 |
}
|
| - |
|
1396 |
},
|
| - |
|
1397 |
error: function (xhr) {
|
| - |
|
1398 |
btn.prop('disabled', false).text('Save & Verify');
|
| - |
|
1399 |
var msg = 'Failed to verify. Please try again.';
|
| - |
|
1400 |
try {
|
| - |
|
1401 |
var j = xhr.responseJSON || JSON.parse(xhr.responseText || '{}');
|
| - |
|
1402 |
if (j && j.response) msg = j.response;
|
| - |
|
1403 |
else if (j && j.message) msg = j.message;
|
| - |
|
1404 |
} catch (e) {
|
| - |
|
1405 |
}
|
| - |
|
1406 |
$('#manualVerifyErr').text(msg).show();
|
| - |
|
1407 |
}
|
| - |
|
1408 |
});
|
| - |
|
1409 |
}
|
| - |
|
1410 |
|
| - |
|
1411 |
// Upload first, then verify with the returned document id. Photo is
|
| - |
|
1412 |
// required (gate at the top of the handler), so we always go through this.
|
| - |
|
1413 |
btn.text('Uploading photo...');
|
| - |
|
1414 |
var formData = new FormData();
|
| - |
|
1415 |
formData.append('file', pickedFile, pickedFile.name || 'store-photo.jpg');
|
| - |
|
1416 |
$.ajax({
|
| - |
|
1417 |
url: context + '/lead-geo/upload-image',
|
| - |
|
1418 |
type: 'POST',
|
| - |
|
1419 |
data: formData,
|
| - |
|
1420 |
processData: false,
|
| - |
|
1421 |
contentType: false,
|
| - |
|
1422 |
success: function (resp) {
|
| - |
|
1423 |
var r = resp && resp.response ? resp.response : resp;
|
| - |
|
1424 |
var docId = r && (r.document_id || r.documentId || r.id);
|
| - |
|
1425 |
if (!docId) {
|
| - |
|
1426 |
btn.prop('disabled', false).text('Save & Verify');
|
| - |
|
1427 |
$('#manualVerifyErr').text('Photo upload failed. Try again or remove the photo.').show();
|
| - |
|
1428 |
return;
|
| - |
|
1429 |
}
|
| - |
|
1430 |
btn.text('Verifying...');
|
| - |
|
1431 |
callVerify(docId);
|
| - |
|
1432 |
},
|
| - |
|
1433 |
error: function () {
|
| - |
|
1434 |
btn.prop('disabled', false).text('Save & Verify');
|
| - |
|
1435 |
$('#manualVerifyErr').text('Photo upload failed. Try again or remove the photo.').show();
|
| - |
|
1436 |
}
|
| - |
|
1437 |
});
|
| - |
|
1438 |
});
|
| - |
|
1439 |
|
| 1183 |
// Geo Review navigation
|
1440 |
// Geo Review navigation
|
| 1184 |
$(document).on('click', '.geo-review', function () {
|
1441 |
$(document).on('click', '.geo-review', function () {
|
| 1185 |
doGetAjaxRequestHandler(context + "/lead-geo/pending-reviews", function (response) {
|
1442 |
doGetAjaxRequestHandler(context + "/lead-geo/pending-reviews", function (response) {
|
| 1186 |
$('#main-content').html(response);
|
1443 |
$('#main-content').html(response);
|
| 1187 |
});
|
1444 |
});
|