Zendesk View Agent Name latest version (currently v1.0.0)

← Back to User Scripts

Script Content

// ==UserScript==
// @name         Zendesk: View Agent Name
// @namespace    https://www.timhilton.xyz/user-scripts
// @version      1.0.0
// @description  Displays the first name of agents below their avatar in the agent collision viewer list.
// @author       Tim Hilton using GitHub Copilot
// @match        https://*.zendesk.com/agent/tickets/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    const LOG_PREFIX = '[Zendesk: View Agent Name]';
    let observerFireCount = 0;

    console.debug(`${LOG_PREFIX} Script initialised`);

    // Add styles for the name labels
    const style = document.createElement('style');
    style.textContent = `
        .agent-name-label {
            position: absolute;
            bottom: -20px;
            left: 50%;
            transform: translateX(-50%);
            background: green;
            color: white;
            text-align: center;
            padding: 2px 6px;
            font-size: 11px;
            white-space: nowrap;
            border-radius: 2px;
            z-index: 1000;
        }
        
        #agentCollisionViewerList li {
            position: relative;
        }
    `;
    document.head.appendChild(style);

    let listObserver = null;
    let documentObserver = null;

    // Process collision avatars and add name labels
    function processCollisionAvatars() {
        console.debug(`${LOG_PREFIX} Processing collision avatars`);
        const buttons = document.querySelectorAll('#agentCollisionViewerList button[data-test-id="collision-avatar"]');
        console.debug(`${LOG_PREFIX} Found ${buttons.length} collision avatar button(s)`);
        
        let processedCount = 0;
        buttons.forEach(button => {
            // Check if we've already processed this button
            if (button.dataset.nameProcessed === 'true') {
                return;
            }
            
            const ariaLabel = button.getAttribute('aria-label');
            if (!ariaLabel) {
                console.debug(`${LOG_PREFIX} Button has no aria-label, skipping`);
                return;
            }
            
            // Extract the first word (first name) from the aria-label
            const firstName = ariaLabel.trim().split(/\s+/)[0];
            
            if (!firstName) {
                console.debug(`${LOG_PREFIX} Could not extract first name from aria-label: "${ariaLabel}"`);
                return;
            }
            
            console.debug(`${LOG_PREFIX} Extracted first name: "${firstName}" from aria-label: "${ariaLabel}"`);
            
            // Find the parent li element
            const parentLi = button.closest('li');
            if (!parentLi) {
                console.debug(`${LOG_PREFIX} Could not find parent li element`);
                return;
            }
            
            // Remove any existing label
            const existingLabel = parentLi.querySelector('.agent-name-label');
            if (existingLabel) {
                existingLabel.remove();
            }
            
            // Create and add the name label
            const nameLabel = document.createElement('div');
            nameLabel.className = 'agent-name-label';
            nameLabel.textContent = firstName;
            parentLi.appendChild(nameLabel);
            
            // Mark as processed
            button.dataset.nameProcessed = 'true';
            processedCount++;
            console.debug(`${LOG_PREFIX} Added name label for: ${firstName}`);
        });
        
        if (processedCount > 0) {
            console.log(`${LOG_PREFIX} ✅ Successfully added ${processedCount} name label(s)`);
        }
    }

    // Observe mutations within the agentCollisionViewerList
    function observeList() {
        console.debug(`${LOG_PREFIX} Entering observeList()`);
        const list = document.getElementById('agentCollisionViewerList');
        if (!list) {
            console.debug(`${LOG_PREFIX} agentCollisionViewerList not found, exiting`);
            return;
        }

        console.debug(`${LOG_PREFIX} agentCollisionViewerList found, processing existing avatars`);
        // Process existing avatars
        processCollisionAvatars();

        // Create observer for the list
        if (listObserver) {
            console.debug(`${LOG_PREFIX} Disconnecting existing list observer`);
            listObserver.disconnect();
        }

        listObserver = new MutationObserver((mutations) => {
            observerFireCount++;
            console.debug(`${LOG_PREFIX} List MutationObserver fired (count: ${observerFireCount})`);
            
            let shouldProcess = false;

            for (const mutation of mutations) {
                if (mutation.type === 'childList') {
                    // Check if buttons were added or removed
                    if (mutation.addedNodes.length > 0 || mutation.removedNodes.length > 0) {
                        console.debug(`${LOG_PREFIX} childList mutation detected: ${mutation.addedNodes.length} added, ${mutation.removedNodes.length} removed`);
                        shouldProcess = true;
                        break;
                    }
                } else if (mutation.type === 'attributes' && mutation.attributeName === 'aria-label') {
                    // If aria-label changes, we need to reprocess
                    console.debug(`${LOG_PREFIX} aria-label attribute changed`);
                    shouldProcess = true;
                    break;
                }
            }

            if (shouldProcess) {
                console.debug(`${LOG_PREFIX} Mutations require reprocessing`);
                processCollisionAvatars();
            } else {
                console.debug(`${LOG_PREFIX} No relevant mutations, skipping reprocessing`);
            }
        });

        listObserver.observe(list, {
            childList: true,
            subtree: true,
            attributes: true,
            attributeFilter: ['aria-label']
        });

        console.debug(`${LOG_PREFIX} Now observing agentCollisionViewerList for mutations`);
    }

    // Observe the document for when agentCollisionViewerList appears
    function observeDocument() {
        console.debug(`${LOG_PREFIX} Entering observeDocument()`);
        
        if (documentObserver) {
            console.debug(`${LOG_PREFIX} Disconnecting existing document observer`);
            documentObserver.disconnect();
        }

        documentObserver = new MutationObserver((mutations) => {
            console.debug(`${LOG_PREFIX} Document MutationObserver fired`);
            const list = document.getElementById('agentCollisionViewerList');
            
            if (list) {
                // Found the list, stop observing document
                console.debug(`${LOG_PREFIX} agentCollisionViewerList appeared in DOM`);
                documentObserver.disconnect();
                
                // Start observing the list
                observeList();
                
                // Also observe for when the list disappears
                observeListDisappearance();
            }
        });

        documentObserver.observe(document.body, {
            childList: true,
            subtree: true
        });

        console.debug(`${LOG_PREFIX} Now observing document.body for agentCollisionViewerList`);
        
        // Check if the list already exists
        const list = document.getElementById('agentCollisionViewerList');
        if (list) {
            console.debug(`${LOG_PREFIX} agentCollisionViewerList already exists in DOM`);
            documentObserver.disconnect();
            observeList();
            observeListDisappearance();
        } else {
            console.debug(`${LOG_PREFIX} agentCollisionViewerList not yet in DOM, waiting for it to appear`);
        }
    }

    // Observe for when the list disappears
    function observeListDisappearance() {
        console.debug(`${LOG_PREFIX} Setting up disappearance observer`);
        
        const disappearanceObserver = new MutationObserver((mutations) => {
            console.debug(`${LOG_PREFIX} Disappearance MutationObserver fired`);
            const list = document.getElementById('agentCollisionViewerList');
            
            if (!list) {
                // List has disappeared
                console.debug(`${LOG_PREFIX} agentCollisionViewerList has disappeared from DOM`);
                
                // Stop all observers
                if (listObserver) {
                    console.debug(`${LOG_PREFIX} Disconnecting list observer`);
                    listObserver.disconnect();
                    listObserver = null;
                }
                disappearanceObserver.disconnect();
                
                // Resume observing the document
                console.debug(`${LOG_PREFIX} Resuming document observation`);
                observeDocument();
            }
        });

        disappearanceObserver.observe(document.body, {
            childList: true,
            subtree: true
        });
        
        console.debug(`${LOG_PREFIX} Disappearance observer active`);
    }

    // Initialise
    console.debug(`${LOG_PREFIX} Starting initialisation`);
    observeDocument();
    console.log(`${LOG_PREFIX} 🎉 Script ready and monitoring for agent collision list`);
})();