kalpdev.1 / Precise ASIN Ticket Helper

// ==UserScript==
// @name         Precise ASIN Ticket Helper
// @version      2.0
// @namespace   kalpdev
// @description  Provide a list of open tickets related to a provided ASIN
// @include      https://t.corp.amazon.com/*
// @include      https://sim-ticketing-fleet-*.*.proxy.amazon.com/*
// @updateURL https://openuserjs.org/meta/kalpdev.1/Precise_ASIN_Ticket_Helper.meta.js
// @downloadURL https://openuserjs.org/install/kalpdev.1/Precise_ASIN_Ticket_Helper.user.js
// @grant        GM_addStyle
// @grant        GM_xmlhttpRequest
// @license MIT
// @icon         data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjUiIGhlaWdodD0iMjUiIHZpZXdCb3g9IjAgMCAyNSAyNSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxyZWN0IGZpbGw9IiM0NzUzNjMiIHdpZHRoPSIyNSIgaGVpZ2h0PSIyNSIgcng9IjIiLz48cGF0aCBkPSJNMTAuMTM3IDNoMy42NTF2NC4xNTJoNC4xOHYzLjI5aC00LjE4djcuODYyYy4wNTUuNzk0LjQ5NyAxLjE5MSAxLjMyNSAxLjE5MWgyLjg1NXYyLjgxMmMtMS4yMjIuNTMzLTIuNDcuNzU4LTMuNzQzLjY3Ny0xLjkxLS4xMjItNC45MDYtLjc5Ny00LjkwNi00LjMyNHYtOC4yMThIN1Y3LjY3NmwyLjQ2NS0uNTI0TDEwLjEzNyAzeiIgZmlsbD0iI0Y5MCIvPjwvZz48L3N2Zz4K
// ==/UserScript==

(function() {
    'use strict';

    //global values
    let observer;//to hold the observer of page changes
    let queryTime = 0;//the last time maxis was queried
    let ticketUUID;//to hold the UUID of the ticket being looked at
    let cachedResults;//to hold any cached results to keep from querying maxis more than needed
    let cachedSearch = '';//to hold the string last entered into the search box

    observer = new MutationObserver(updatePage);//create a new observer

    //observe the page body for changes
    observer.observe(document.body, {
        childList: true,
        subtree: true
    });

    //update the elements on the page for this script
    function updatePage() {

        addStyle();
        setGlobalValues();
        addPageSection();
    }

    //must do this in an interval because for some reason the mutation observer will not notice the menu opening if the user is not on the overview tab...
    setInterval(function() {
        addCommentReminder();
    }, 2000);

    //add script stylesheets to page
    function addStyle() {

        //if the better search style tag has not been added yet
        if (!document.querySelector('head [asin-ticket-helper-style]')) {

            //add the custom CSS stylesheet
            GM_addStyle('.asin-table-title {background-color:var(--awsui-color-background-container-header,#21252c); padding:var(--space-container-header-vertical-_wk0ky,12px) var(--space-container-horizontal-AstNGj,20px); border-bottom:1px solid var(--awsui-color-border-divider-default); border-top:1rem solid var(--awsui-color-background-layout-main, #16191f);}' +
                        '#asin-ticket-table {padding:var(--space-scaled-l-1U9HoF,20px) var(--space-container-horizontal-AstNGj,20px);}' +
                        '.asin-ticket-table table {width:100%;}' +
                        '.asin-ticket-table td {border-top:1px solid var(--awsui-color-border-divider-default);}' +
                        '.asin-ticket-table th, .asin-ticket-table td {text-align:left; padding:0 .5rem;}' +
                        '#asin-input {margin:1rem; padding-left:1rem; background-color:var(--awsui-color-background-input-default,#fff); ' +
                                      'color:var(--awsui-color-text-heading-default,#16191f); border:1px solid var(--awsui-color-border-button-normal-default,#545b64); ' +
                                      'line-height: var(--awsui-font-body-m-line-height,22px); padding:4px 20px; border-radius:2px;}' +
                        '#asin-button {margin:1rem; background-color:var(--awsui-color-background-button-normal-default, #fff); ' +
                                      'color:var(--awsui-color-text-interactive-default,#545b64); border-color:var(--awsui-color-border-button-normal-default,#545b64); ' +
                                      'line-height: var(--awsui-font-body-m-line-height,22px); padding:4px 20px; border-radius:2px; font-weight:700;}' +
                        '#asin-button:hover {margin:1rem; background-color:var(--awsui-color-background-button-normal-hover, #fafafa); ' +
                                      'color:var(--awsui-color-text-interactive-hover); border-color:var(--awsui-color-text-interactive-hover); cursor:pointer;}'
                       ).setAttribute('asin-ticket-helper-style', 'true');
        }
    }

    //get the global values used throughout this ticket
    function setGlobalValues() {
        ticketUUID = document.querySelector('.ticket-id')?.getAttribute('data-id');
    }

    function addPageSection() {

        //if the section has been added already, or there is no place to put it
        if (document.querySelector('.asin-ticket-section') || !document.querySelector('.issue-tabs > div > div:first-child')) {
            return;
        }

        let sectionHTML =
            '<div class="asin-table-title">' +
              '<h2>ASIN Search</h2>' +
            '</div>' +
            '<div class="asin-ticket-section">' +
              '<input id="asin-input" maxlength="20" placeholder="Search Text" value="' + cachedSearch + '"/>' +
              '<button id="asin-button">Find Related Tickets</button>' +
            '</div>' +
            '<div id="asin-ticket-table">' +
            '</div>';

        document.querySelector('.issue-tabs > div > div:first-child > div')?.insertAdjacentHTML('beforeend', sectionHTML);//add the section to the overview
        document.querySelector('#asin-button').addEventListener('click', initiateSearch, false);//add an event listener to get results when the button is clicked
        document.querySelector('#asin-input').addEventListener("change", function(event) {
            cachedSearch = document.querySelector('#asin-input').value;
        });//add event listener to cache the search text when it changes
        document.querySelector('#asin-input').addEventListener("keyup", function(event) {
            //make sure the enter button is the one being pressed
            if (event.keyCode === 13) {
                event.preventDefault();//cancel any other enter button events
                initiateSearch();//start the search
            }
        });//add event listener to search when the enter button is pressed after typing in an ASIN

        //if there are cached results
        if (cachedResults) {
            addResultsTable(cachedResults);//add any cached results
        }
    }

    //start getting search results for the provided ASIN
    function initiateSearch() {
        getRelatedTicketsByType(document.querySelector('#asin-input').value);//search for whatever is in the input
    }

    //get all tickets related to the ASIN and in supported types
    function getRelatedTicketsByType(ASIN) {

        //if the asin is not provided
        if (!ASIN) {

            //if there is no cached result
            if (!cachedResults) {
                return;

            //use the cached results
            } else {
                addResultsTable(cachedResults);//add the cached tickets to the page
            }

        //use the provided ASIN
        } else {

            document.querySelector('#asin-ticket-table').innerHTML = '<div>Searching for related tickets...</div>';//show the searching text

            //request a new ticket list. timeout in 15 seconds
            GM_xmlhttpRequest({
                method: 'GET',
                url: 'https://maxis-service-prod-iad.amazon.com/issues' +
                '?q=extensions.tt.status%3A(Assigned OR "Work In Progress" OR Researching OR Pending OR Resolved OR Closed)' +
                ' AND full_text%3A("' + ASIN + '")' +
                ' AND extensions.tt.type%3A(' +
                '"Classification CLP - DE" OR "Classification CSC - AE" OR "Classification CSC - AU" OR "Classification CSC - BR" OR "Classification CSC - CA"' +
                ' OR "Classification CSC - CN" OR "Classification CSC - DE" OR "Classification CSC - ES" OR "Classification CSC - FR" OR "Classification CSC - IN"' +
                ' OR "Classification CSC - IT" OR "Classification CSC - JP" OR "Classification CSC - MX" OR "Classification CSC - PL" OR "Classification CSC - SA"' +
                ' OR "Classification CSC - SG" OR "Classification CSC - SE" OR "Classification CSC - TR" OR "Classification CSC - UK" OR "Classification CSC - US"' +
                ' OR "FLEX-BLR-AE-HAZMAT" OR "FLEX-BLR-AU-HAZMAT" OR "FLEX-BLR-CA-HAZMAT" OR "FLEX-BLR-EG-HAZMAT" OR "FLEX-BLR-IN-HAZMAT"' +
                ' OR "FLEX-BLR-SA-HAZMAT" OR "FLEX-BLR-SG-HAZMAT" OR "FLEX-BLR-UK-HAZMAT" OR "FLEX-BLR-US-HAZMAT" OR "FLEX-SJO-MX-HAZMAT")',
                timeout: 15000,
                onload: function(result) {

                    cachedResults = JSON.parse(result.response).documents;//get array of newly returned tickets
                    queryTime = new Date();//update the time of the last query

                    getRelatedTicketsByGroup(ASIN);
                }
            });
        }
    }

     //get all tickets related to the ASIN and in supported groups
    function getRelatedTicketsByGroup(ASIN) {

        //if the asin is not provided
        if (!ASIN) {

            //if there is no cached result
            if (!cachedResults) {
                return;

            //use the cached results
            } else {
                addResultsTable(cachedResults);//add the cached tickets to the page
            }

        //use the provided ASIN
        } else {

            //request a new ticket list. timeout in 15 seconds
            GM_xmlhttpRequest({
                method: 'GET',
                url: 'https://maxis-service-prod-iad.amazon.com/issues' +
                '?q=extensions.tt.status%3A(Assigned OR "Work In Progress" OR Researching OR Pending OR Resolved OR Closed)' +
                ' AND full_text%3A("' + ASIN + '")' +
                ' AND extensions.tt.assignedGroup%3A("Product Compliance WW Hazmat" OR "RBS-Flex-Hazmat Support" OR "Cops-Hazmat-seller follow up" OR "US/CA Operations"' +
                ' OR "Seller Support Hazmat Team" OR "RIVER TT FC" OR "ISS-IN-RBS" OR "PC Classification UTC5" OR "CSC Classification" OR "CSC Classification PL"' +
                ' OR "FBA Ops MP" OR "PC Classification team" OR "Flex-RBS MX" OR "Dangerous Goods-BR" OR "Vendor follow-up MX" OR "mx-hazmat"' +
                ' OR "Product Compliance PO Hazmat" OR "Supplier Follow Up DE" OR "Product Compliance RO Hazmat" OR "DG Ops Vendor Follow-up" OR "DG Ops Seller Follow-up" OR "EU DG Operations"' +
                ' OR "Product Compliance TR Hazmat" OR "Supplier Follow Up TR" OR "cn-hazmat classification" OR "CN Operations" OR "Hazmat Flag Solutions JP" OR "Hazmat Flag Solutions JP SFU"' +
                ' OR "Special Hazmat review JP" OR "PC DG Team JP" OR "Amazon Easy Ship Program" OR "IN DG Compliance" OR "SupplierFollowUp DG BR" OR "Product Compliance PL Hazmat"' +
                ' OR "Product Compliance NL Hazmat" OR "Supplier Follow Up PL" OR "Product Compliance SE Hazmat" OR "Supplier Follow Up SE")',
                timeout: 15000,
                onload: function(result) {

                    cachedResults = cachedResults.concat(JSON.parse(result.response).documents);//append the array of newly returned tickets

                    addResultsTable(cachedResults);//add the tickets to the page
                }
            });
        }
    }

    //add the related thckets to the page
    function addResultsTable(ticketList) {

        //if the custom table has been added already or and there is no place to put it
        if (document.querySelector('.asin-ticket-table') || !document.querySelector('#asin-ticket-table')) {
            return;
        }

        const ticketMap = new Map();//map to keep track of which tickets have been added already

        let tableHTML =
            '<div class="asin-ticket-table">' +
              '<table>' +
                '<tr>' +
                  '<th>Short ID</th>' +
                  '<th>Status</th>' +
                  '<th>Category</th>' +
                  '<th>Type</th>' +
                  '<th>Item</th>' +
                  '<th>Assigned Group</th>' +
                  '<th>Attachments</th>' +
                '</tr>';

        //add each ticket to the table
        ticketList.forEach(function(ticket) {

            //if the ticket has already been added to the table
            if (ticketMap.get(ticket.id)) {
                return;//do not add it again
            }

            ticketMap.set(ticket.id, true);//add the ticket UUID to the map

            tableHTML +=
                '<tr>' +
                  '<td>' +
                    '<a href="https://t.corp.amazon.com/' + ticket.id + '">';

            //if the ticket being added to the table is the ticket currently being looked at
            if (ticket.id == ticketUUID) {
                tableHTML += 'This Ticket - ';
            }

            tableHTML +=
                    ticket?.aliases[0]?.id + '</a>' +
                  '</td>' +
                  '<td>' + ticket.extensions.tt.status + '</td>' +
                  '<td>' + ticket.extensions.tt.category + '</td>' +
                  '<td>' + ticket.extensions.tt.type + '</td>' +
                  '<td>' + ticket.extensions.tt.item + '</td>' +
                  '<td>' + ticket.extensions.tt.assignedGroup + '</td>'+
                  '<td>';

            //for each attachment on the ticket
            ticket.attachments.forEach(function(attachment) {
                tableHTML +=
                    '<div>' +
                      '<a target="_blank" href="https://maxis-file-service-prod-iad.iad.proxy.amazon.com/issues/' +
                      ticket.id + '/attachments/' + attachment.id + '">' + attachment.fileName + '</a>' +
                    '</div>';
            });

            let overviewLinks = ticket.description.match(/(http(s)?:\/\/.)[-a-zA-Z0-9@:%._\+~#=]{0,256}\.[a-z]{2,4}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)/g);//get any links from the ticket overview

            //if there is any link in the overview
            if (overviewLinks) {

                //for each link in the overview of the ticket
                overviewLinks.forEach(function(link) {
                    tableHTML +=
                        '<div>' +
                        '<a target="_blank" href="' + link + '">' + link + '</a>' +
                        '</div>';
                });
            }

            tableHTML +=
                  '</td>' +
                '</tr>'
        });

        tableHTML +=
              '</table>' +
            '</div>';
        document.querySelector('#asin-ticket-table').innerHTML = tableHTML;//add the table to the page
    }

    function addCommentReminder() {
        const saveButton = document.querySelector('.cti-folder-search-modal .save-button');

        if (saveButton && !saveButton.getAttribute('ASIN-Click')) {
            saveButton.setAttribute('ASIN-Click', 'true');
            saveButton.addEventListener("click", remindIfNeeded);
        }
     }

    function remindIfNeeded() {
        const group = document.querySelector('#assigned-group');

        if (group && ['CSC Classification RO', 'CSC Classification CN', 'CSC Classification PL', 'CSC Classification IN', 'CSC Ticketing SME', 'PC Classification UTC5'].includes(group.innerText)) {
            alert('Remember to update the root cause in the work log.');
            document.querySelector('[data-testid="communication"]')?.click();

            setTimeout(function() {
                document.querySelectorAll('[aria-label="Select a communication thread"] button')[2].click();

                setTimeout(function() {
                    let commentBox = document.querySelector('#sim-communicationActions--createComment');

                    //if the SIM-T comment box is found
                    if (commentBox) {
                        let nativeTextAreaValueSetter = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, "value").set;
                        nativeTextAreaValueSetter.call(commentBox, 'Root cause: ');
                        commentBox.dispatchEvent(new Event('change', { bubbles: true}));
                    }
                }, 500);
            }, 500);
        }
    }

})();