Enhanced AG Grid Functionality with Clean Styles

Adds vibrant row hover effect, conditional status cell styling for test and sample status, static red background for emergency rows, and cleans up styles for specific elements.

Per 14-04-2025. Zie de nieuwste versie.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey, Greasemonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Userscripts.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een gebruikersscriptbeheerder nodig.

(Ik heb al een user script manager, laat me het downloaden!)

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

(Ik heb al een beheerder - laat me doorgaan met de installatie!)

// ==UserScript==
// @name         Enhanced AG Grid Functionality with Clean Styles
// @version      3.8
// @description  Adds vibrant row hover effect, conditional status cell styling for test and sample status, static red background for emergency rows, and cleans up styles for specific elements.
// @match        *://his.kaauh.org/lab/*
// @author       Hamad AlShegifi
// @grant        GM_addStyle
// @namespace    http://tampermonkey.net/
// ==/UserScript==

(function () {
    'use strict';

    // Inject CSS for hover effect, status cell styling, and emergency highlighting
    GM_addStyle(`
        .ag-row {
            transition: background-color 0.3s ease;
        }
        .vibrant-hover {
            /* Background color and text color are now applied to cells within the hovered row */
        }
        .ag-row.vibrant-hover .ag-cell {
            background-color: #87dced !important; /* Light blue color */
            color: black !important; /* Black text color */
            font-weight: bold !important; /* Bold font */
        }
        .ag-cell[col-id="testStatus"].status-verified-level2,
        .ag-cell[col-id="testStatus"].status-verified-level1 {
            background-color: #90EE90 !important; /* Light green color */
        }
        .ag-cell[col-id="testStatus"].status-ordered {
            background-color: #FFFFE0 !important; /* Light yellow color */
        }
        .ag-cell[col-id="testStatus"].status-resulted {
            background-color: #FFA500 !important; /* Orange color */
            color: black !important; /* Ensure text is readable on orange */
        }
        .ag-cell[col-id="testStatus"].status-cancelled {
            background-color: #000000 !important; /* Brown color */
            color: white !important; /* White text for contrast */
        }
        .emergency-row {
            background-color: #ffe0e0 !important; /* Light red */
        }
        .ag-cell[col-id="sampleStatus"].status-received {
            background-color: #90EE90 !important; /* Light green color for Received sample status */
        }
    `);

    // Clean styles for specific links and spans
    function applyCleanStyles() {
        const anchor = document.querySelector('li > a[href="#/lab-orders/doctor-request"]');
        if (anchor) {
            anchor.style.setProperty('white-space', 'nowrap', 'important');
            anchor.style.setProperty('font-size', '13px', 'important');
            anchor.style.setProperty('overflow', 'visible', 'important');
            anchor.style.setProperty('text-overflow', 'unset', 'important');

            const spans = anchor.querySelectorAll('span');
            spans.forEach(span => {
                span.style.setProperty('display', 'inline', 'important');
                span.style.setProperty('font-size', '13px', 'important');
                span.style.setProperty('white-space', 'nowrap', 'important');
            });
        }

        const simplifySpan = (selector) => {
            const span = document.querySelector(selector);
            if (span) {
                span.style.setProperty('display', 'inline', 'important');
                span.style.setProperty('font-size', '20px', 'important');
                span.style.setProperty('white-space', 'nowrap', 'important');
                span.style.setProperty('overflow', 'visible', 'important');
                span.style.setProperty('text-overflow', 'unset', 'important');
                span.textContent = span.textContent.replace(/\s+/g, ''); // Optional cleanup
            }
        };

        simplifySpan('span.to-do');
        simplifySpan('span.pending-orders');
    }

    // Debounce for applying clean styles
    function debounce(func, wait) {
        let timeout;
        return function(...args) {
            clearTimeout(timeout);
            timeout = setTimeout(() => func.apply(this, args), wait);
        };
    }

    const debouncedStyleUpdater = debounce(applyCleanStyles, 300);
    const observer = new MutationObserver(debouncedStyleUpdater);
    observer.observe(document.body, { childList: true, subtree: true });

    // Apply hover effects
    const targetColumnIdsForHover = [
        "orderNo", "testId", "testDescription", "clusterMrn",
        "hospitalMrn", "patientName", "dob", "nationalIqamaId",
        "department", "clinic", "doctor", "analyzer",
        "orderDateAndTime", "lastUpdatedDate", "sampleStatus",
        "referenceLab", "accessionNo", "barcode", "sequenceNo",
        "primaryPatientId", "referenceLabDesc", "testStatus",
        "orderLastModifiedOnEpoch", "orderCreatedOnEpoch", "equipmentName", "doctorName", "localMrn",
        "dateOfBirth", "idNumber"
    ];

    function applyHoverEffect() {
        document.querySelectorAll('.ag-cell').forEach(cell => {
            const colId = cell.getAttribute('col-id');
            if (colId && targetColumnIdsForHover.includes(colId)) {
                const row = cell.closest('.ag-row'); // Find the parent row
                if (row) {
                    // Add hover effect to the entire row on mouseenter
                    cell.addEventListener('mouseenter', () => {
                        row.classList.add('vibrant-hover'); // Add hover effect to the row
                    });
                    // Remove hover effect on mouseleave
                    cell.addEventListener('mouseleave', () => {
                        row.classList.remove('vibrant-hover'); // Remove hover effect from the row
                    });
                }
            }
        });
    }

    // Apply conditional background color for test and sample statuses
    function applyStatusCellStyle() {
        document.querySelectorAll('.ag-cell[col-id="testStatus"]').forEach(cell => {
            const statusValue = cell.textContent.trim();
            cell.classList.remove('status-verified-level2', 'status-verified-level1', 'status-ordered', 'status-resulted', 'status-cancelled'); // Remove previous styles

            if (statusValue === "VerifiedLevel2" || statusValue === "VerifiedLevel1") {
                cell.classList.add('status-verified-level2');
            } else if (statusValue === "Ordered") {
                cell.classList.add('status-ordered');
            } else if (statusValue === "Resulted") {
                cell.classList.add('status-resulted');
            } else if (statusValue === "Cancelled") {
                cell.classList.add('status-cancelled'); // Apply brown color for Cancelled
            }
        });

        // Apply style for Sample Status = Received
        document.querySelectorAll('.ag-cell[col-id="sampleStatus"]').forEach(cell => {
            if (cell.textContent.trim() === "Received") {
                cell.classList.add('status-received');
            } else {
                cell.classList.remove('status-received'); // Ensure the class is removed if the value changes
            }
        });
    }

    // Highlight emergency rows based on clinic column
    function highlightEmergencyRows() {
        const rows = document.querySelectorAll('div[role="row"]');
        rows.forEach(row => {
            const clinicCell = row.querySelector('div[col-id="clinic"]');
            if (clinicCell && clinicCell.textContent.trim() === 'EMERGENCY') {
                row.classList.add('emergency-row');
            } else {
                row.classList.remove('emergency-row'); // Remove the class if the condition is no longer met
            }
        });
    }

    const observerForGrid = new MutationObserver(() => {
        applyHoverEffect();
        applyStatusCellStyle();
        highlightEmergencyRows(); // Call highlightEmergencyRows on every DOM change
    });

    const gridBodyViewport = document.querySelector('.ag-body-viewport'); // Observe the grid body viewport
    if (gridBodyViewport) {
        observerForGrid.observe(gridBodyViewport, { childList: true, subtree: false }); // subtree should be false here as we are observing direct children (rows)
    } else {
        console.warn("AG Grid body viewport not found. Falling back to body observer.");
        observerForGrid.observe(document.body, { childList: true, subtree: true });
    }

    window.addEventListener('load', () => {
        applyHoverEffect();
        applyStatusCellStyle();
        highlightEmergencyRows(); // Initial call to highlight emergency rows
    });

    // Set the dropdown value for pagination to 100
    function setDropdownValue() {
        const dropdown = document.getElementById("dropdownPaginationPageSize");
        if (dropdown && dropdown.value !== "100") {
            dropdown.value = "100"; // Set the value to "100"

            // Trigger the 'change' event
            const event = new Event('change', { bubbles: true });
            dropdown.dispatchEvent(event);

            console.log("Dropdown value set to 100");
        }
    }

    // Observe changes in the DOM for the dropdown
    function observeDOMForDropdown() {
        const observer = new MutationObserver(() => {
            setDropdownValue(); // Check and set the dropdown value when changes are detected
        });

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

        console.log("MutationObserver is active for dropdown");
    }

    window.addEventListener('load', () => {
        setDropdownValue(); // Initial check
        observeDOMForDropdown(); // Start observing for dynamic changes
    });

})();