Advertisement
BryanFongiee

SSP Util

Jan 21st, 2022
1,126
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // ==UserScript==
  2. // @name         SSP Util
  3. // @namespace    trans-logistics.amazon.com
  4. // @version      2021.05.12
  5. // @description  Puts barcode and FMC search links in each VRID. Makes tooltips for driver plates, seals, scacs and preload/live. Gets assigned driver phone numbers from FMC. Bulk search selected loads in FMC. Fixes printing in Chrome, and moves Hazmat special permit to first page.
  6. // @author       bjerkt@
  7. // @downloadURL  https://axzile.corp.amazon.com/-/carthamus/download_script/ssp-util.user.js
  8. // @updateURL    https://axzile.corp.amazon.com/-/carthamus/download_script/ssp-util.user.js
  9. // @match        https://trans-logistics.amazon.com/ssp/dock/*
  10. // @match        https://trans-logistics-eu.amazon.com/ssp/dock/*
  11. // @grant        GM_addStyle
  12. // ==/UserScript==
  13.  
  14. (function() {
  15.     'use strict';
  16.     GM_addStyle(`
  17. .search-pane {
  18.   border: 1px solid black;
  19.   display: inline-block;
  20.   padding: 5px;
  21.   border-radius: 10px;
  22. }
  23. .search-btn {
  24.   margin: 3px;
  25.   font-weight: bold;
  26. }
  27. /* Tooltip prep */
  28. .tooltip {
  29.   position: relative;
  30.   display: inline-flex;
  31. }
  32. .tooltip-carrier{
  33.   position: relative;
  34. }
  35. .icon-seal {
  36.   width: 11px;
  37. }
  38. .icon-driver {
  39.   width: 22px;
  40.   height: 18px;
  41. }
  42. .icon-carrier {
  43. }
  44. .icon-phone {
  45.   width: 22px;
  46.   height: 18px;
  47.   margin-left: -1px;
  48.   margin-bottom: -6px;
  49.   background: transparent url(https://m.media-amazon.com/images/G/01/Help/pg-gacd-phone._V324592851_.png) no-repeat center;
  50.   background-size: 30px;
  51. }
  52. .icon-phone-pointer {
  53.   cursor: pointer;
  54. }
  55. /* Tooltip text */
  56. .tooltip .tooltip-text {
  57.   font-size: 11px;
  58.   visibility: hidden;
  59.   display: inline;
  60.   background-color: black;
  61.   color: #fff;
  62.   text-align: center;
  63.   padding: 2px 7px 2px 7px;
  64.   border-radius: 4px;
  65.   position: absolute;
  66.   width: max-content;
  67. }
  68. .tooltip .seal-tooltip-text {
  69.   bottom: 120%;
  70.   right: -85%;
  71. }
  72. .tooltip .driver-tooltip-text {
  73.   bottom: 125%;
  74.   right: 1%;
  75. }
  76. .tooltip .carrier-tooltip-text {
  77.   bottom: 125%;
  78.   right: 10%;
  79. }
  80. .tooltip .driver-phone-tooltip-text {
  81.   bottom: 110%;
  82.   right: 5%;
  83. }
  84. .tooltip .optype-tooltip-text {
  85.   bottom: 125%;
  86.   right: 1%
  87. }
  88.  
  89. /* Add speech bubble arrow thing */
  90. .tooltip .tooltip-text::after {
  91.   content: " ";
  92.   position: absolute;
  93.   top: 100%;
  94.   right: 10%;
  95.   border-width: 5px;
  96.   border-style: solid;
  97.   border-color: black transparent transparent transparent;
  98. }
  99. /* Show the tooltip text when you mouse over the tooltip container */
  100. .tooltip:hover .tooltip-text {
  101.   visibility: visible;
  102. }
  103.  
  104. @page iframe {size: portrait !important;}
  105. `);
  106.     // Handle Tampermonkey running the script too late to catch the loading events
  107.     if (document.readyState != 'complete') {
  108.         window.addEventListener('load', windowLoadedCallback);
  109.     } else {
  110.         windowLoadedCallback();
  111.     }
  112.     // Determine region
  113.     const urlRegion = window.location.href.indexOf('trans-logistics-eu') > 0 ? '-eu' : '';
  114.  
  115.     function windowLoadedCallback() {
  116.         // Override JsBarcode with a Chrome-compatible version
  117.         // NOTE: Content Security Policy blocks the fetch (not an Amazon domain), causing some console spam, but it still works for some reason.
  118.         var script = document.createElement('script');
  119.         script.type = "text/javascript";
  120.         fetch('https://cdn.jsdelivr.net/npm/jsbarcode@3.11.0/dist/barcodes/JsBarcode.code128.min.js')
  121.         .then(response => response.text())
  122.         .then(txt => {script.innerHTML = txt; document.getElementsByTagName('head')[0].appendChild(script);});
  123.         // Tap into ajax requests
  124.         $.ajaxSetup({
  125.             dataFilter: function(data, type) {
  126.                 let newStr = data.replace('@page {size: landscape}', '@page {size: auto}')
  127.                     .replace('http://media-services.integ.amazon.com:8888/images/G/01/TransCentral/images/transportation/logo_amazon_fulfillment.jpg', 'https://drive-render.corp.amazon.com/view/bjerkt@/Tools%20-%20Mine/Userscripts/res/logo_amazon_fulfillment.jpg');
  128.                 // Move the Hazmat qr code to the first page
  129.                 const matches = [...newStr.matchAll(/(of the DOT.+?n)(.+?noted.+?tr>)(.+?)(<img.+?png.+?")( width.+?height.+?px.+?").+?(<p.+?p>).+?(<p.+?pdf.+?p>).+?\/tr>/g)][0];
  130.                 if (matches) {
  131.                     const fixedStr = matches[1]+"<br>"+matches[4]+' width=\\"70px\\" height=\\"70px\\">'+matches[6]+matches[7]+matches[2];
  132.                     newStr = newStr.replace(matches[0], fixedStr);
  133.                 }
  134.                 return newStr;
  135.             }
  136.         })
  137.         const observer = new MutationObserver(elemChangeCallback);
  138.         const obsConfig = { attributes: true, attributeFilter:["class"], attributeOldValue: true };
  139.         const targetNode = document.getElementById('block-ui-container');
  140.         observer.observe(targetNode, obsConfig);
  141.         addBulkSearchButton();
  142.         // Set up mutation observer to watch when refresh dialog is shown & cleared
  143.         function elemChangeCallback (mutationsList, observer) {
  144.             for (let mutation of mutationsList) {
  145.                 if (mutation.target.classList.contains('hidden') && mutation.oldValue == '') {
  146.                     addLinksTooltips();
  147.                 }
  148.             }
  149.         }
  150.     }
  151.  
  152.     ///////////////////////////////////////////////////////////////////
  153.  
  154.     async function addLinksTooltips() {
  155.         const vridContainers = document.querySelectorAll('td.loadIdCol');
  156.         let carrierMap = {};
  157.         let driverPlateMap = {};
  158.         let driverIdMap = {};
  159.         let VRIDOperationTypeMap = {};
  160.         let vridList = [];
  161.         ///////////////////////
  162.         // Add links
  163.         ///////////////////////
  164.         for (let container of vridContainers) {
  165.             const vrid = container.innerText.slice(0,9);
  166.             vridList.push(vrid);
  167.             // Don't make a double
  168.             if (container.querySelector('a') === null) {
  169.                 addLinks(container, vrid);
  170.             }
  171.         }
  172.         ///////////////////////
  173.         // Add tooltips
  174.         ///////////////////////
  175.         if(vridList.length > 0) {
  176.             addSealTooltips();
  177.             [carrierMap, driverPlateMap, driverIdMap, VRIDOperationTypeMap] = await getFMCData(vridList);
  178.             const driverIconSpans = document.querySelectorAll('table#dashboard > tbody > tr > td.loadIdCol > span.driverPresent');
  179.             const carrierElems = document.querySelectorAll('.loadIdCol + td + td');
  180.             const loadIdElems = document.querySelectorAll('td.loadIdCol > span.loadId');
  181.             requestAnimationFrame(() => {
  182.                 // Operation Type
  183.                 for (let elem of loadIdElems) {
  184.                     elem.classList.add('tooltip');
  185.                     const tooltip = document.createElement('SPAN');
  186.                     tooltip.classList.add('tooltip-text', 'optype-tooltip-text');
  187.                     tooltip.innerHTML = VRIDOperationTypeMap[elem.textContent]
  188.                     elem.appendChild(tooltip);
  189.                 }
  190.                 // Driver plates
  191.                 for (let theSpan of driverIconSpans) {
  192.                     if (theSpan.children.length == 0) {
  193.                         theSpan.removeAttribute('title');
  194.                         theSpan.classList.add('tooltip', 'icon-driver');
  195.                         const tooltip = document.createElement('SPAN');
  196.                         tooltip.classList.add('tooltip-text', 'driver-tooltip-text');
  197.                         tooltip.innerHTML = driverPlateMap[theSpan.previousElementSibling.innerText];
  198.                         theSpan.appendChild(tooltip);
  199.                     }
  200.                 }
  201.                 // Carrier names
  202.                 for (let elem of carrierElems) {
  203.                     if (elem.children.length == 1) {
  204.                         const idWrapper = document.createElement('SPAN');
  205.                         idWrapper.classList.add('tooltip', 'icon-carrier');
  206.                         const carrierIdNode = elem.childNodes[0];
  207.                         carrierIdNode.textContent = carrierIdNode.textContent.slice(0,-1);
  208.                         const tooltip = document.createElement('SPAN');
  209.                         tooltip.classList.add('tooltip-text', 'carrier-tooltip-text');
  210.                         tooltip.innerHTML = carrierMap[elem.innerHTML.split('<',1)[0]];
  211.                         idWrapper.appendChild(carrierIdNode);
  212.                         idWrapper.appendChild(tooltip);
  213.                         elem.insertBefore(idWrapper, elem.children[0]);
  214.  
  215.                         // Phone numbers
  216.                         const driverId = driverIdMap[elem.previousElementSibling.previousElementSibling.firstElementChild.innerText];
  217.                         if (driverId) {
  218.                             const phoneWrapper = document.createElement('SPAN');
  219.                             phoneWrapper.classList.add('tooltip', 'icon-phone', 'icon-phone-pointer');
  220.                             phoneWrapper.title = 'Get phone number from FMC';
  221.                             phoneWrapper.addEventListener('click', function(e) {
  222.                                 e.stopPropagation();
  223.                                 createDriverPhoneTooltip(e.target, carrierIdNode.textContent, driverId);
  224.                                 phoneWrapper.title = '';
  225.                             }, {once: true});
  226.                             elem.insertBefore(phoneWrapper, elem.children[1]);
  227.                         }
  228.                     }
  229.                 }
  230.             });
  231.         }
  232.     }
  233.     async function addSealTooltips() {
  234.         try {
  235.             let sealNums = {};
  236.             const nodeSelectorOptions = document.getElementById('availableNodeName').options
  237.             const nodeId = nodeSelectorOptions[nodeSelectorOptions.selectedIndex].text;
  238.             // At very beginning of page load, date picker is blank. Use default. If not, overwrite with date range.
  239.             let postData = {
  240.                 entity: 'getDefaultOutboundDockView',
  241.                 nodeId: nodeId
  242.             };
  243.             let dayStart = $("input[name\x3dfromDate]").val()
  244.             if (dayStart != '') {
  245.                 let dayEnd = $("input[name\x3dtoDate]").val()
  246.                 , timeStart = $('#searchPanelTable [dataname\x3d"fromTime"] option:selected').val()
  247.                 , timeEnd = $('#searchPanelTable [dataname\x3d"toTime"] option:selected').val();
  248.                 if ("" != timeStart) {
  249.                     var p = timeStart.split("-");
  250.                     timeStart = p[0] + ":" + p[1];
  251.                 }
  252.                 else {
  253.                     timeStart = "00:00";
  254.                 }
  255.                 "" != timeEnd ? (p = timeEnd.split("-"),
  256.                                  timeEnd = p[0] + ":" + p[1]) : timeEnd = "00:00";
  257.                 let dateStart = new Date(dayStart + " " + timeStart + ":00 GMT")
  258.                 , startEpoch = dateStart.getTime()
  259.                 , dateEnd = new Date(dayEnd + " " + timeEnd + ":00 GMT")
  260.                 , endEpoch = dateEnd.getTime();
  261.                 postData = {
  262.                     entity: 'getOutboundDockView',
  263.                     nodeId: nodeId,
  264.                     startDate: startEpoch,
  265.                     endDate: endEpoch,
  266.                     loadCategories: 'outboundScheduled,outboundInProgress,outboundReadyToDepart,outboundDeparted,outboundCancelled',
  267.                     shippingPurposeType: 'TRANSSHIPMENT,NON-TRANSSHIPMENT'
  268.                 }
  269.             }
  270.             const response = await jQuery.ajax({
  271.                 url: 'https://trans-logistics' + urlRegion + '.amazon.com/ssp/dock/hrz/ob/fetchdata',
  272.                 type: 'POST',
  273.                 data: postData,
  274.                 dataType: 'json'
  275.             });
  276.             for (let elem of response.ret.aaData) {
  277.                 if(elem.load.sealId) {
  278.                     sealNums[elem.load.planId] = elem.load.sealId;
  279.                 }
  280.             }
  281.             const sealCheckSpans = document.querySelectorAll('table#dashboard > tbody > tr > td.trailerNumCol > span.sealIndicator');
  282.             requestAnimationFrame(function() {
  283.                 for (let theSpan of sealCheckSpans) {
  284.                     if (theSpan.children.length == 0) {
  285.                         theSpan.removeAttribute('title');
  286.                         theSpan.classList.add('tooltip', 'icon-seal');
  287.                         const tooltip = document.createElement('SPAN');
  288.                         tooltip.classList.add('tooltip-text', 'seal-tooltip-text');
  289.                         tooltip.innerHTML = sealNums[theSpan.parentElement.parentElement.id];
  290.                         theSpan.appendChild(tooltip);
  291.                     }
  292.                 }
  293.             });
  294.         } catch(e) {
  295.             console.group('SSP Util');
  296.             console.log('Error getting seals from SSP:');
  297.             console.error(e);
  298.             console.groupEnd();}
  299.     }
  300.     async function getFMCData(vridList) {
  301.         try {
  302.             let vridListString = "";
  303.             // Create query string
  304.             for (let str of vridList) {
  305.                 const tmp = '"' + str + '",'
  306.                 vridListString += tmp;
  307.             }
  308.             // Chop off last comma
  309.             vridListString = vridListString.slice(0,-1);
  310.             let retCarrierMap = {};
  311.             let retDriverPlateMap = {};
  312.             let retDriverIdMap = {};
  313.             let retVRIDOperationTypeMap = {};
  314.             const postUrl = 'https://trans-logistics' + urlRegion + '.amazon.com/fmc/search/execution/by-id';
  315.             const postData = {
  316.                 "searchIds": vridList,
  317.                 "page": 0,
  318.                 "pageSize": 100,
  319.                 "bookmarkedSavedSearch": false,
  320.                 "executionViewModePreference": "vrs",
  321.                 "dashboardPreferences": "{\"length\":100,\"order\":[[12,\"asc\"]],\"search\":{\"search\":\"\",\"smart\":true,\"regex\":false,\"caseInsensitive\":true},\"columns\":[{\"visible\":true,\"search\":{\"search\":\"\",\"smart\":true,\"regex\":false,\"caseInsensitive\":true}},{\"visible\":false,\"search\":{\"search\":\"\",\"smart\":true,\"regex\":false,\"caseInsensitive\":true}},{\"visible\":true,\"search\":{\"search\":\"\",\"smart\":true,\"regex\":false,\"caseInsensitive\":true}},{\"visible\":true,\"search\":{\"search\":\"\",\"smart\":true,\"regex\":false,\"caseInsensitive\":true}},{\"visible\":true,\"search\":{\"search\":\"\",\"smart\":true,\"regex\":false,\"caseInsensitive\":true}},{\"visible\":true,\"search\":{\"search\":\"\",\"smart\":true,\"regex\":false,\"caseInsensitive\":true}},{\"visible\":true,\"search\":{\"search\":\"\",\"smart\":true,\"regex\":false,\"caseInsensitive\":true}},{\"visible\":true,\"search\":{\"search\":\"\",\"smart\":true,\"regex\":false,\"caseInsensitive\":true}},{\"visible\":true,\"search\":{\"search\":\"\",\"smart\":false,\"regex\":false,\"caseInsensitive\":true}},{\"visible\":false,\"search\":{\"search\":\"\",\"smart\":true,\"regex\":false,\"caseInsensitive\":true}},{\"visible\":true,\"search\":{\"search\":\"\",\"smart\":true,\"regex\":false,\"caseInsensitive\":true}},{\"visible\":false,\"search\":{\"search\":\"\",\"smart\":true,\"regex\":false,\"caseInsensitive\":true}},{\"visible\":true,\"search\":{\"search\":\"\",\"smart\":true,\"regex\":false,\"caseInsensitive\":true}},{\"visible\":false,\"search\":{\"search\":\"\",\"smart\":true,\"regex\":false,\"caseInsensitive\":true}},{\"visible\":true,\"search\":{\"search\":\"\",\"smart\":true,\"regex\":false,\"caseInsensitive\":true}},{\"visible\":false,\"search\":{\"search\":\"\",\"smart\":true,\"regex\":false,\"caseInsensitive\":true}},{\"visible\":true,\"search\":{\"search\":\"\",\"smart\":true,\"regex\":false,\"caseInsensitive\":true}},{\"visible\":true,\"search\":{\"search\":\"\",\"smart\":true,\"regex\":false,\"caseInsensitive\":true}},{\"visible\":false,\"search\":{\"search\":\"\",\"smart\":true,\"regex\":false,\"caseInsensitive\":true}},{\"visible\":true,\"search\":{\"search\":\"\",\"smart\":true,\"regex\":false,\"caseInsensitive\":true}},{\"visible\":true,\"search\":{\"search\":\"\",\"smart\":true,\"regex\":false,\"caseInsensitive\":true}},{\"visible\":true,\"search\":{\"search\":\"\",\"smart\":true,\"regex\":false,\"caseInsensitive\":true}},{\"visible\":true,\"search\":{\"search\":\"\",\"smart\":true,\"regex\":false,\"caseInsensitive\":true}},{\"visible\":true,\"search\":{\"search\":\"\",\"smart\":true,\"regex\":false,\"caseInsensitive\":true}},{\"visible\":false,\"search\":{\"search\":\"\",\"smart\":true,\"regex\":false,\"caseInsensitive\":true}},{\"visible\":false,\"search\":{\"search\":\"\",\"smart\":true,\"regex\":false,\"caseInsensitive\":true}},{\"visible\":false,\"search\":{\"search\":\"\",\"smart\":true,\"regex\":false,\"caseInsensitive\":true}},{\"visible\":false,\"search\":{\"search\":\"\",\"smart\":true,\"regex\":false,\"caseInsensitive\":true}},{\"visible\":false,\"search\":{\"search\":\"\",\"smart\":true,\"regex\":false,\"caseInsensitive\":true}},{\"visible\":true,\"search\":{\"search\":\"\",\"smart\":true,\"regex\":false,\"caseInsensitive\":true}}],\"childTable\":{\"hiddenColumns\":[\"estimatedArrival\",\"estimatedDelay\"],\"shownColumns\":[]},\"columnNames\":[{\"name\":\"collapsed_state\",\"index\":0},{\"name\":\"tour_id\",\"index\":1},{\"name\":\"vr_id\",\"index\":2},{\"name\":\"vr_map\",\"index\":3},{\"name\":\"vr_status\",\"index\":4},{\"name\":\"comments\",\"index\":5},{\"name\":\"tp_id\",\"index\":6},{\"name\":\"tp_actions\",\"index\":7},{\"name\":\"facility_sequence\",\"index\":8},{\"name\":\"disruptions\",\"index\":9},{\"name\":\"first_dock_arrival_time\",\"index\":10},{\"name\":\"first_yard_arrival_time\",\"index\":11},{\"name\":\"first_dock_departure_time\",\"index\":12},{\"name\":\"first_yard_departure_time\",\"index\":13},{\"name\":\"final_dock_arrival_time\",\"index\":14},{\"name\":\"final_yard_arrival_time\",\"index\":15},{\"name\":\"cpt\",\"index\":16},{\"name\":\"alerts\",\"index\":17},{\"name\":\"carrier_group\",\"index\":18},{\"name\":\"carrier\",\"index\":19},{\"name\":\"subcarrier\",\"index\":20},{\"name\":\"cr_id\",\"index\":21},{\"name\":\"shipper_accounts\",\"index\":22},{\"name\":\"equipment_type\",\"index\":23},{\"name\":\"client_contract\",\"index\":24},{\"name\":\"vr_tendering\",\"index\":25},{\"name\":\"tender_status\",\"index\":26},{\"name\":\"operator_id\",\"index\":27},{\"name\":\"driver\",\"index\":28},{\"name\":\"cases\",\"index\":29}]}",
  322.                 "originalCriteria": "{\"searchIds\":[" + vridListString + "],\"pageSize\":100}"
  323.             };
  324.             const response = await jQuery.ajax({
  325.                 url: postUrl,
  326.                 type: 'POST',
  327.                 data: JSON.stringify(postData),
  328.                 contentType: 'application/json',
  329.                 processData: false,
  330.                 dataType: 'json'
  331.             });
  332.             for (let record of response.returnedObject.records) {
  333.                // debugger;
  334.                 retVRIDOperationTypeMap[record.vehicleRunId] = record.aggregatedStops[0].actions[0].operationType;
  335.                 retCarrierMap[record.carrierId] = record.carrierName;
  336.                 // Also have choice of plan id, tour id...
  337.                 // stop 0 is pickup, asset 1 is tractor (0 trailer, 2 driver) !!Box truck, etc breaks this!
  338.                // let tractorIndex = 1;
  339.                 //if (record.aggregatedStops[0].assets[0].category != 'LOADABLE') {
  340.                //     tractorIndex = 0;
  341.                // }
  342.                 let tractorAsset = {};
  343.                 for (let asset of record.aggregatedStops[0].assets) {
  344.                     if (asset.category == "ENGINE") {
  345.                         tractorAsset = asset;
  346.                         break;
  347.                     }
  348.                 }
  349.                 retDriverPlateMap[record.vehicleRunId] = tractorAsset.assetId;
  350.                 if (record.assignedDrivers[0]) {
  351.                     retDriverIdMap[record.vehicleRunId] = record.assignedDrivers[0].assetId;
  352.                 }
  353.             }
  354.             return [retCarrierMap, retDriverPlateMap, retDriverIdMap, retVRIDOperationTypeMap];
  355.         } catch(e) {
  356.             console.group('SSP Util');
  357.             console.log('Error getting data from FMC:');
  358.             console.error(e);
  359.             console.groupEnd();
  360.         }
  361.     }
  362.  
  363.     async function addLinks(container, vrid) {
  364.         // Add FMC link
  365.         const a = document.createElement('a');
  366.         const linkText = document.createTextNode("FMC");
  367.         a.appendChild(linkText);
  368.         a.title = "Open VRID in FMC";
  369.         a.href = "https://trans-logistics" + urlRegion + ".amazon.com/fmc/execution/search/" + vrid;
  370.         a.target = "_blank";
  371.         a.addEventListener('click', function(e) {
  372.             e.stopPropagation();
  373.         });
  374.         container.appendChild(a);
  375.         // Add barcode link
  376.         const b = document.createElement('a');
  377.         const bc = document.createTextNode('∣∥∥');
  378.         b.appendChild(bc);
  379.         b.title = "Open QR code";
  380.         b.href = "https://chart.googleapis.com/chart?chl=" + vrid + "&chs=200x200&cht=qr&chld=H%7C1";
  381.         b.target = "_blank";
  382.         b.style.marginLeft = "5px";
  383.         b.style.fontSize = "15px";
  384.         b.addEventListener('click', function(e) {
  385.             e.stopPropagation();
  386.         });
  387.         container.appendChild(b);
  388.     }
  389.  
  390.     async function createDriverPhoneTooltip(node, scac, driverId) {
  391.         // Pop up tooltip with spinny placeholder
  392.         const tooltip = document.createElement('SPAN');
  393.         tooltip.classList.add('tooltip-text', 'driver-phone-tooltip-text');
  394.         tooltip.innerHTML = '<span class="a-spinner a-spinner-small"></span>';
  395.         node.appendChild(tooltip);
  396.         // Get driver number
  397.         try {
  398.             const response = await jQuery.ajax({
  399.                 url: 'https://trans-logistics' + urlRegion + '.amazon.com/fmc/driver/detail/'+scac+'/'+driverId,
  400.                 type: 'GET',
  401.             });
  402.             const phoneNum = response.returnedObject.phone;
  403.             tooltip.innerHTML = formatPhoneNumber(phoneNum);
  404.             node.classList.remove('icon-phone-pointer');
  405.  
  406.             function formatPhoneNumber(phoneNumberString) {
  407.                 const cleaned = ('' + phoneNumberString).replace(/\D/g, '')
  408.                 const match = cleaned.match(/^(1|)?(\d{3})(\d{3})(\d{4})$/)
  409.                 if (match) {
  410.                     const intlCode = (match[1] ? '+1 ' : '')
  411.                     return [intlCode, '(', match[2], ') ', match[3], '-', match[4]].join('')
  412.                 }
  413.                 return null
  414.             }
  415.         } catch (e) {
  416.             console.error(e);
  417.         }
  418.     }
  419.  
  420.     async function addBulkSearchButton() {
  421.         const topPane = document.getElementById('topPaneContent');
  422.         const searchPane = document.createElement('DIV');
  423.         searchPane.classList.add('search-pane');
  424.         const searchDesc = document.createElement('SPAN');
  425.         searchDesc.innerHTML = 'Search all selected VRIDs on FMC:';
  426.         const searchBtn = document.createElement('BUTTON');
  427.         searchBtn.classList.add('search-btn');
  428.         searchBtn.addEventListener('click', searchSelectedVrids);
  429.         searchBtn.innerHTML = 'Search';
  430.         searchPane.appendChild(searchDesc);
  431.         searchPane.appendChild(searchBtn);
  432.         topPane.appendChild(searchPane);
  433.         async function searchSelectedVrids() {
  434.             const selectedVridRows = document.querySelectorAll('tr.selectedTableRow');
  435.             // Push list of vrids to FMC, get bulk search url
  436.             let vridList = [];
  437.             let vridListString = '';
  438.             if(selectedVridRows.length) {
  439.                 searchBtn.innerHTML = '<span class="a-spinner a-spinner-small"></span>';
  440.                 for (let tableRow of selectedVridRows) {
  441.                     const vrid = tableRow.children[6].firstElementChild.innerText;
  442.                     vridList.push(vrid);
  443.                     vridListString += '"' + vrid + '",';
  444.                 }
  445.                 // Cut off last comma
  446.                 vridListString = vridListString.slice(0,-1);
  447.                 // Go!
  448.                 const postUrl = 'https://trans-logistics' + urlRegion + '.amazon.com/fmc/search/execution/by-id';
  449.                 const postData = {
  450.                     "searchIds": vridList,
  451.                     "page": 0,
  452.                     "pageSize": 100,
  453.                     "bookmarkedSavedSearch": false,
  454.                     "executionViewModePreference": "vrs",
  455.                     "dashboardPreferences": "{\"length\":100,\"order\":[[12,\"asc\"]],\"search\":{\"search\":\"\",\"smart\":true,\"regex\":false,\"caseInsensitive\":true},\"columns\":[{\"visible\":true,\"search\":{\"search\":\"\",\"smart\":true,\"regex\":false,\"caseInsensitive\":true}},{\"visible\":false,\"search\":{\"search\":\"\",\"smart\":true,\"regex\":false,\"caseInsensitive\":true}},{\"visible\":true,\"search\":{\"search\":\"\",\"smart\":true,\"regex\":false,\"caseInsensitive\":true}},{\"visible\":true,\"search\":{\"search\":\"\",\"smart\":true,\"regex\":false,\"caseInsensitive\":true}},{\"visible\":true,\"search\":{\"search\":\"\",\"smart\":true,\"regex\":false,\"caseInsensitive\":true}},{\"visible\":true,\"search\":{\"search\":\"\",\"smart\":true,\"regex\":false,\"caseInsensitive\":true}},{\"visible\":true,\"search\":{\"search\":\"\",\"smart\":true,\"regex\":false,\"caseInsensitive\":true}},{\"visible\":true,\"search\":{\"search\":\"\",\"smart\":true,\"regex\":false,\"caseInsensitive\":true}},{\"visible\":true,\"search\":{\"search\":\"\",\"smart\":false,\"regex\":false,\"caseInsensitive\":true}},{\"visible\":false,\"search\":{\"search\":\"\",\"smart\":true,\"regex\":false,\"caseInsensitive\":true}},{\"visible\":true,\"search\":{\"search\":\"\",\"smart\":true,\"regex\":false,\"caseInsensitive\":true}},{\"visible\":false,\"search\":{\"search\":\"\",\"smart\":true,\"regex\":false,\"caseInsensitive\":true}},{\"visible\":true,\"search\":{\"search\":\"\",\"smart\":true,\"regex\":false,\"caseInsensitive\":true}},{\"visible\":false,\"search\":{\"search\":\"\",\"smart\":true,\"regex\":false,\"caseInsensitive\":true}},{\"visible\":true,\"search\":{\"search\":\"\",\"smart\":true,\"regex\":false,\"caseInsensitive\":true}},{\"visible\":false,\"search\":{\"search\":\"\",\"smart\":true,\"regex\":false,\"caseInsensitive\":true}},{\"visible\":true,\"search\":{\"search\":\"\",\"smart\":true,\"regex\":false,\"caseInsensitive\":true}},{\"visible\":true,\"search\":{\"search\":\"\",\"smart\":true,\"regex\":false,\"caseInsensitive\":true}},{\"visible\":false,\"search\":{\"search\":\"\",\"smart\":true,\"regex\":false,\"caseInsensitive\":true}},{\"visible\":true,\"search\":{\"search\":\"\",\"smart\":true,\"regex\":false,\"caseInsensitive\":true}},{\"visible\":true,\"search\":{\"search\":\"\",\"smart\":true,\"regex\":false,\"caseInsensitive\":true}},{\"visible\":true,\"search\":{\"search\":\"\",\"smart\":true,\"regex\":false,\"caseInsensitive\":true}},{\"visible\":true,\"search\":{\"search\":\"\",\"smart\":true,\"regex\":false,\"caseInsensitive\":true}},{\"visible\":true,\"search\":{\"search\":\"\",\"smart\":true,\"regex\":false,\"caseInsensitive\":true}},{\"visible\":false,\"search\":{\"search\":\"\",\"smart\":true,\"regex\":false,\"caseInsensitive\":true}},{\"visible\":false,\"search\":{\"search\":\"\",\"smart\":true,\"regex\":false,\"caseInsensitive\":true}},{\"visible\":false,\"search\":{\"search\":\"\",\"smart\":true,\"regex\":false,\"caseInsensitive\":true}},{\"visible\":false,\"search\":{\"search\":\"\",\"smart\":true,\"regex\":false,\"caseInsensitive\":true}},{\"visible\":false,\"search\":{\"search\":\"\",\"smart\":true,\"regex\":false,\"caseInsensitive\":true}},{\"visible\":true,\"search\":{\"search\":\"\",\"smart\":true,\"regex\":false,\"caseInsensitive\":true}}],\"childTable\":{\"hiddenColumns\":[\"estimatedArrival\",\"estimatedDelay\"],\"shownColumns\":[]},\"columnNames\":[{\"name\":\"collapsed_state\",\"index\":0},{\"name\":\"tour_id\",\"index\":1},{\"name\":\"vr_id\",\"index\":2},{\"name\":\"vr_map\",\"index\":3},{\"name\":\"vr_status\",\"index\":4},{\"name\":\"comments\",\"index\":5},{\"name\":\"tp_id\",\"index\":6},{\"name\":\"tp_actions\",\"index\":7},{\"name\":\"facility_sequence\",\"index\":8},{\"name\":\"disruptions\",\"index\":9},{\"name\":\"first_dock_arrival_time\",\"index\":10},{\"name\":\"first_yard_arrival_time\",\"index\":11},{\"name\":\"first_dock_departure_time\",\"index\":12},{\"name\":\"first_yard_departure_time\",\"index\":13},{\"name\":\"final_dock_arrival_time\",\"index\":14},{\"name\":\"final_yard_arrival_time\",\"index\":15},{\"name\":\"cpt\",\"index\":16},{\"name\":\"alerts\",\"index\":17},{\"name\":\"carrier_group\",\"index\":18},{\"name\":\"carrier\",\"index\":19},{\"name\":\"subcarrier\",\"index\":20},{\"name\":\"cr_id\",\"index\":21},{\"name\":\"shipper_accounts\",\"index\":22},{\"name\":\"equipment_type\",\"index\":23},{\"name\":\"client_contract\",\"index\":24},{\"name\":\"vr_tendering\",\"index\":25},{\"name\":\"tender_status\",\"index\":26},{\"name\":\"operator_id\",\"index\":27},{\"name\":\"driver\",\"index\":28},{\"name\":\"cases\",\"index\":29}]}",
  456.                     "originalCriteria": "{\"searchIds\":[" + vridListString + "],\"pageSize\":100}"
  457.                 };
  458.                 try {
  459.                     const response = await jQuery.ajax({
  460.                         url: postUrl,
  461.                         type: 'POST',
  462.                         data: JSON.stringify(postData),
  463.                         contentType: 'application/json',
  464.                         processData: false,
  465.                         dataType: 'json'
  466.                     });
  467.                     searchBtn.innerHTML = 'Search';
  468.                     window.open('https://trans-logistics' + urlRegion + '.amazon.com'+response.suggestedUrl,'_blank')
  469.                 } catch(e) {
  470.                     console.error(e);
  471.                 }
  472.             }
  473.         }
  474.     }
  475. })();
Advertisement
Advertisement
Advertisement
RAW Paste Data Copied
Advertisement