Zendesk — Add link to Azure DevOps work item in header v1.0.1

← Back to User Scripts

Adds a button to the ticket header that links to the Azure DevOps work item.

Script Content

// ==UserScript==
// @name         Zendesk — Add link to Azure DevOps work item in header
// @namespace    http://tampermonkey.net/
// @version      1.0.1
// @description  Adds a button to the ticket header that links to the Azure DevOps work item.
// @author       tjhleeds using Jules
// @match        https://*.zendesk.com/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    const LOG_PREFIX = '[Zendesk to Azure DevOps Userscript]';
    console.log(`${LOG_PREFIX} Script initialized`);

    const AZURE_DEVOPS_SVG = `

    
    
        
            
            
            
            
            
        
    

    `;

    const addLink = () => {
        console.log(`${LOG_PREFIX} addLink() called`);
        
        const devopsLinkField = Array.from(document.querySelectorAll('label')).find(label => label.textContent === 'DevOps Link');
        if (!devopsLinkField) {
            console.log(`${LOG_PREFIX} ❌ DevOps Link field not found`);
            return;
        }
        console.log(`${LOG_PREFIX} ✅ DevOps Link field found`);

        const devopsLinkInput = devopsLinkField.nextElementSibling;
        if (!devopsLinkInput || !devopsLinkInput.value) {
            console.log(`${LOG_PREFIX} ❌ DevOps Link input not found or empty`, devopsLinkInput);
            return;
        }
        console.log(`${LOG_PREFIX} ✅ DevOps Link value: ${devopsLinkInput.value}`);

        const header = document.querySelector('div[data-ticket-id]');
        if (!header) {
            console.log(`${LOG_PREFIX} ❌ Header not found`);
            return;
        }
        
        if (document.querySelector('.azure-devops-link')) {
            console.log(`${LOG_PREFIX} ⏭️ Link already exists, skipping`);
            return;
        }
        console.log(`${LOG_PREFIX} ✅ Header found, data-ticket-id: ${header.getAttribute('data-ticket-id')}`);

        const firstChildOfHeader = header.firstChild;
        if (!firstChildOfHeader) {
            console.log(`${LOG_PREFIX} ❌ Header has no first child`);
            return;
        }

        const targetElement = firstChildOfHeader.firstChild;
        if (!targetElement) {
            console.log(`${LOG_PREFIX} ❌ First child has no first child`);
            return;
        }

        const link = document.createElement('a');
        link.href = devopsLinkInput.value;
        link.target = '_blank';
        link.classList.add('azure-devops-link');
        link.innerHTML = AZURE_DEVOPS_SVG;

        firstChildOfHeader.insertBefore(link, targetElement);
        firstChildOfHeader.style.display = 'flex';
        firstChildOfHeader.style.alignItems = 'center';
        targetElement.style.marginLeft = '10px';
        
        console.log(`${LOG_PREFIX} 🎉 Link successfully added to header!`);
    };

    let mutationCount = 0;
    const observer = new MutationObserver(() => {
        mutationCount++;
        console.log(`${LOG_PREFIX} Mutation #${mutationCount} detected, calling addLink()`);
        addLink();
    });

    observer.observe(document.body, {
        childList: true,
        subtree: true
    });
    
    console.log(`${LOG_PREFIX} MutationObserver started, watching document.body`);
})();