Enhanced AG Grid

Adds a unified summary table, improves UI styling, and adds QoL features.

As of 2025-06-21. See the latest version.

// ==UserScript==
// @name         Enhanced AG Grid
// @version      13.9.1.0
// @description  Adds a unified summary table, improves UI styling, and adds QoL features.
// @match        https://his.kaauh.org/lab/*
// @author       Hamad AlShegifi
// @grant        GM_addStyle
// @namespace http://tampermonkey.net/
// ==/UserScript==

(function () {
    'use strict';

    // Create a global namespace for inter-IIFE communication
    window.enhancedGrid = window.enhancedGrid || {};

    // --- Global Constants ---
    const GRID_WIDTH = '54vw'; // Target width for main content areas (AG-Grids, static tables, card headers)
    const RESIZED_FLAG = 'data-ag-resized'; // Custom attribute to mark AG-Grids as already resized
    const INTERVAL = 300; // Interval (ms) for periodic scanning and resizing
    const CLICKED_ROW_EXPIRY_PREFIX = 'clicked_row_expiry_'; // Prefix for localStorage keys for row highlights
    const CLICK_DURATION_MS = 60 * 1000; // Duration (ms) for row highlight persistence (1 minute)

    // --- Utility Functions ---
    const debounce = (func, wait) => {
        let timeout;
        return function executedFunction(...args) {
            const later = () => {
                clearTimeout(timeout);
                func(...args);
            };
            clearTimeout(timeout);
            timeout = setTimeout(later, wait);
        };
    };

    // --- Inject CSS Styles ---
    GM_addStyle(`
        @import url('https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css');

        /* --- General Layout & Resizing for Main Content Area --- */
        .results .ag-theme-balham,
        .accd-details,
        .accd-details-table-static,
        .card-header {
            width: ${GRID_WIDTH} !important;
            margin-left: auto !important;
            margin-right: 0 !important;
            box-sizing: border-box;
        }
        .accd-details table,
        .accd-details-table-static table {
            width: 100% !important;
            table-layout: fixed !important;
            border-collapse: collapse !important;
        }
        .accd-details table th, .accd-details table td,
        .accd-details-table-static table th, .accd-details-table-static table td {
            padding: 8px !important;
            word-wrap: break-word !important;
        }
        .results .ag-theme-balham {
            height: 220px !important;
        }

        /* --- AG-Grid Row and Cell Specific Styles --- */
        .ag-row {
            transition: background-color 0.3s ease;
        }
        .ag-row.clicked-row-green .ag-cell {
            background-color: #A0ECA0 !important;
        }
    `);

    // --- AG-Grid Column ID Definitions ---
    const columnsToUncheck = [
        'Lab Order No', 'Hospital MRN', 'DOB', 'Test ID', 'National/Iqama Id', 'Department',
        'Doctor', 'Analyzer', 'Reference Lab', 'Accession No', 'Sequence No','Age',
        'Container Type','Storage Condition'
    ];
    let hasRunOnce = false;

    // --- AG-Grid Row Highlight and Persistence Logic ---
    function handleRowClickForPersistentGreen(event) {
        const cellElement = event.target.closest('.ag-cell');
        if (!cellElement) return;
        const rowElement = cellElement.closest('.ag-row[role="row"]');
        if (!rowElement) return;
        const barcodeCell = rowElement.querySelector('div[col-id="barcode"]');
        if (!barcodeCell || !barcodeCell.textContent) return;
        const barcode = barcodeCell.textContent.trim();
        if (!barcode) return;
        const expiryTimestamp = Date.now() + CLICK_DURATION_MS;
        try { localStorage.setItem(CLICKED_ROW_EXPIRY_PREFIX + barcode, expiryTimestamp.toString()); }
        catch (e) { console.error("Error saving to localStorage:", e); }
        applyPersistentRowStyles();
    }

    function applyPersistentRowStyles() {
        const rows = document.querySelectorAll('.ag-center-cols-container div[role="row"], .ag-pinned-left-cols-container div[role="row"], .ag-pinned-right-cols-container div[role="row"]');
        const now = Date.now();
        rows.forEach(row => {
            const barcodeCell = row.querySelector('div[col-id="barcode"]');
            let rowBarcode = barcodeCell?.textContent?.trim();
            if (rowBarcode) {
                const expiryKey = CLICKED_ROW_EXPIRY_PREFIX + rowBarcode;
                const expiryTimestampStr = localStorage.getItem(expiryKey);
                if (expiryTimestampStr) {
                    if (now < parseInt(expiryTimestampStr, 10)) row.classList.add('clicked-row-green');
                    else {
                        localStorage.removeItem(expiryKey);
                        row.classList.remove('clicked-row-green');
                    }
                } else row.classList.remove('clicked-row-green');
            } else row.classList.remove('clicked-row-green');
        });
        try {
            for (let i = 0; i < localStorage.length; i++) {
                const key = localStorage.key(i);
                if (key?.startsWith(CLICKED_ROW_EXPIRY_PREFIX)) {
                    if (now >= parseInt(localStorage.getItem(key), 10)) localStorage.removeItem(key);
                }
            }
        } catch (e) { console.error("Error during localStorage cleanup:", e); }
    }

    function setupPersistentRowStylesListener() {
        const gridRoot = document.querySelector('.ag-root-wrapper');
        const listenerTarget = gridRoot || document.querySelector('.ag-body-viewport') || document.body;
        if (listenerTarget && !listenerTarget.dataset.persistentClickListenerAttached) {
            listenerTarget.addEventListener('click', handleRowClickForPersistentGreen, true);
            listenerTarget.dataset.persistentClickListenerAttached = 'true';
        }
    }

    // --- AG-Grid Column Visibility Toggle Logic ---
    function isSpecificPageForColumns() {
        return window.location.href.endsWith('/#/lab-orders/lab-test-analyzer');
    }
    function areColumnsChecked() {
        return columnsToUncheck.some(column => isColumnChecked(column));
    }
    function isColumnChecked(labelText) {
        for (const label of document.querySelectorAll('.ag-column-tool-panel-column-label')) {
            if (label.textContent.trim() === labelText && label.parentElement.querySelector('.ag-icon-checkbox-checked')) return true;
        }
        return false;
    }
    function ensureColumnsUnchecked() {
        if (hasRunOnce || !isSpecificPageForColumns() || !areColumnsChecked()) return;
        hasRunOnce = true;
        setTimeout(() => columnsToUncheck.forEach(clickColumnLabel), 1000);
    }
    function ensureOtherColumnsChecked() {
        if (!isSpecificPageForColumns()) return;
        document.querySelectorAll('.ag-column-tool-panel-column-label').forEach(label => {
            if (!columnsToUncheck.includes(label.textContent.trim()) && label.parentElement.querySelector('.ag-icon-checkbox-unchecked')) label.click();
        });
    }
    function clickColumnLabel(labelText) {
        if (!isSpecificPageForColumns()) return;
        document.querySelectorAll('.ag-column-tool-panel-column-label').forEach(label => {
            if (label.textContent.trim() === labelText && label.parentElement.querySelector('.ag-icon-checkbox-checked')) label.click();
        });
    }
    function initColumnToggle() {
        if (!isSpecificPageForColumns()) return;
        let attempts = 0;
        const interval = setInterval(() => {
            if (document.querySelector('.ag-side-buttons') || ++attempts > 10) {
                if (document.querySelector('.ag-side-buttons')) {
                    ensureColumnsUnchecked();
                    ensureOtherColumnsChecked();
                }
                clearInterval(interval);
            }
        }, 500);
    }

    // --- Layout and Main Update Orchestration ---
    const performLayoutAdjustments = () => {
        document.querySelectorAll('ag-grid-angular').forEach(grid => {
            if (grid.getAttribute(RESIZED_FLAG)) return;
            const api = grid.agGrid?.api || grid.__agGridComp?.api;
            if (api && typeof api.sizeColumnsToFit === 'function') {
                grid.style.width = GRID_WIDTH;
                grid.style.marginLeft = 'auto';
                grid.style.marginRight = '0';
                api.sizeColumnsToFit();
                grid.setAttribute(RESIZED_FLAG, 'true');
            }
        });
        document.querySelectorAll('div.accd-details-table-static.test-open, .card-header').forEach(el => {
            el.style.width = GRID_WIDTH;
            el.style.marginLeft = 'auto';
            el.style.marginRight = '0';
        });
        document.querySelectorAll('h6[translateid="test-results.Results"]').forEach(el => el.remove());
    };

    const fullPageUpdate = debounce(() => {
        performLayoutAdjustments();
        applyPersistentRowStyles();
        setupPersistentRowStylesListener();
        if (window.enhancedGrid && typeof window.enhancedGrid.triggerSummaryUpdate === 'function') {
            window.enhancedGrid.triggerSummaryUpdate();
        }
        if (isSpecificPageForColumns()) {
            hasRunOnce = false;
            initColumnToggle();
        }
    }, 100);

    // --- Main Execution Flow ---
    fullPageUpdate();
    setInterval(fullPageUpdate, INTERVAL);

    // --- SPA Navigation Handling ---
    let lastUrl = location.href;
    const spaUrlObserver = new MutationObserver(() => {
        if (location.href !== lastUrl) {
            lastUrl = location.href;
            document.querySelectorAll('ag-grid-angular').forEach(grid => grid.removeAttribute(RESIZED_FLAG));
            setTimeout(fullPageUpdate, 500);
        }
    });
    spaUrlObserver.observe(document.body, { childList: true, subtree: true });
})();

// IIFE for Barcode display box functionality
(function () {
    'use strict';
    const BARCODE_KEY = 'selectedBarcode';
    let currentUrl = location.href;
    function loadJsBarcode(callback) {
        if (window.JsBarcode) {
            callback();
            return;
        }
        const script = document.createElement('script');
        script.src = 'https://cdn.jsdelivr.net/npm/[email protected]/dist/JsBarcode.all.min.js';
        script.onload = callback;
        document.head.appendChild(script);
    }
    function insertBarcodeBox(barcode) {
        let insertionTarget = document.querySelector('.btn-area.stickey-btnset') || document.querySelector('.test-open.mt-2');
        if (!barcode || !insertionTarget || document.getElementById('barcode-display-box')) return;
        const box = document.createElement('div');
        box.id = 'barcode-display-box';
        box.style.cssText = 'padding:8px 12px;background:#f7f7f7;border-radius:8px;display:flex;align-items:center;gap:10px;border:1px solid #ccc;';
        const label = document.createElement('div');
        label.textContent = 'Sample Barcode:';
        label.style.cssText = 'font-weight:bold;font-size:14px;color:#333;';
        const text = document.createElement('div');
        text.textContent = barcode;
        text.style.cssText = 'font-size: 16px; color: #444; font-weight: bold;';
        const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
        svg.id = "barcode-svg";
        svg.style.cssText = 'height:40px;width:120px;border:1px solid #ccc;border-radius:4px;padding:2px;';
        box.append(label, text, svg);
        insertionTarget.insertAdjacentElement('afterend', box);
        try {
            const buttonBarStyles = window.getComputedStyle(insertionTarget);
            if (buttonBarStyles.position === 'sticky' || buttonBarStyles.position === 'fixed') {
                const buttonBarHeight = insertionTarget.offsetHeight;
                const buttonBarTop = parseInt(buttonBarStyles.top, 10) || 0;
                const desiredTopForBarcode = buttonBarTop + buttonBarHeight + 5;
                Object.assign(box.style, {
                    position: 'sticky',
                    top: `${desiredTopForBarcode}px`,
                    zIndex: '99',
                    boxShadow: '0 3px 5px rgba(0,0,0,0.08)',
                });
            } else box.style.marginTop = '10px';
        } catch (e) {
            console.error("Enhanced AG Grid: Error applying sticky style to barcode box.", e);
            box.style.marginTop = '10px';
        }
        loadJsBarcode(() => {
            try {
                JsBarcode(svg, barcode, { format: "CODE128", displayValue: false, height: 40, width: 2, margin: 0 });
            } catch (err) {
                console.warn('Barcode render error:', err);
                svg.outerHTML = "<span>Invalid Barcode</span>";
            }
        });
    }
    function watchGridClicksForBarcodeBox() {
        document.body.addEventListener('click', e => {
            const cell = e.target.closest('.ag-row')?.querySelector('[col-id="barcode"]');
            if (cell?.textContent.trim()) localStorage.setItem(BARCODE_KEY, cell.textContent.trim());
        });
    }
    function waitAndShowBarcode() {
        const barcode = localStorage.getItem(BARCODE_KEY);
        const urlPattern = /\/0\/.*\/undefined$/;
        if (!barcode || !urlPattern.test(location.href)) return;
        const interval = setInterval(() => {
            const ready = document.querySelector('.btn-area.stickey-btnset') || document.querySelector('.test-open.mt-2');
            if (ready) {
                clearInterval(interval);
                insertBarcodeBox(barcode);
            }
        }, 300);
    }
    function observeSPA() {
        const bodyObserver = new MutationObserver(() => {
            if (location.href !== currentUrl) {
                currentUrl = location.href;
                waitAndShowBarcode();
            }
        });
        bodyObserver.observe(document.body, { childList: true, subtree: true });
    }
    watchGridClicksForBarcodeBox();
    observeSPA();
    waitAndShowBarcode();
})();

// IIFE for Dropdown pagination
(function() {
    'use strict';
    const setDropdownValue = () => {
        const dropdown = document.getElementById("dropdownPaginationPageSize");
        if (dropdown && dropdown.value !== "100") {
            dropdown.value = "100";
            dropdown.dispatchEvent(new Event('change', { bubbles: true }));
        }
    };
    new MutationObserver(setDropdownValue).observe(document.body, { childList: true, subtree: true });
    window.addEventListener('load', setDropdownValue);
})();

// IIFE for Unified Test Summary Table functionality
(function() {
    'use strict';
    GM_addStyle(`
        #test-summary-container {
            width: 40%; float: left; margin: 25px 10px 10px 0;
            background-color: #fff; border: 1px solid #ddd; border-radius: 6px;
            box-shadow: 0 2px 8px rgba(0,0,0,0.08); display: flex; flex-direction: column;
        }
       .card-body::after { content: ""; display: table; clear: both; }
        #test-summary-container .summary-content { padding: 0; position: relative; flex-grow: 1; }
        .summary-header {
            font-weight: 500; font-size: 12px; padding: 8px 10px;
            border-bottom: 1px solid #e0e0e0; display: flex; align-items: center; gap: 10px;
            color: #000000; cursor: pointer; user-select: none; flex-shrink: 0;
        }
        .summary-header > span {
           flex-grow: 1; /* Allow text to take available space */
        }
        .summary-status-icon {
            margin-left: auto; /* Push icon to the far right */
            margin-right: 5px;
            font-size: 1.2em; /* Make icon slightly larger */
        }
        /* --- Custom Icon Colors for Summary Statuses --- */
        .summary-header i.vicon.fa-check-circle { color: #28a745; }
        .summary-header i.vicon.fa-star { color: #6c757d; }
        .summary-header i.vicon.fa-arrow-circle-up.ord { color: #17a2b8; }
        .summary-header i.vicon.fa-arrow-circle-up:not(.ord) { color: #007bff; }

        .collapse-icon {
            transition: color 0.2s ease-in-out;
            color: #28a745;
        }
        .collapse-icon.collapsed {
            color: #dc3545;
        }
        .no-tests { padding: 20px; color: #6c757d; text-align: center; }
        .hidden-note { color: #dc3545; font-style: italic; }
        .test-list-container { display: contents; }
        .hidden-note-container { display: none; }
        #test-summary-container .summary-content.collapsed { display: none; }
        #test-summary-container .summary-content.collapsed .test-list-container { display: none; }
        #test-summary-container .summary-content.collapsed .hidden-note-container { display: block; }
        .summary-grid-container {
            display: grid;
            grid-template-columns: 1fr minmax(80px, auto) minmax(80px, auto) min-content min-content;
            align-items: start;
        }
        .summary-grid-header { display: contents; font-weight: bold; }
        .summary-grid-header > div {
            background-color: #f0f0f0; padding: 6px 15px; border-bottom: 1px solid #e0e0e0;
            white-space: nowrap; color: #555; font-size: 12px;
            position: sticky; top: 0; z-index: 1;
        }
        .summary-grid-header > div.test-name-header { text-align: left; }
        .summary-grid-header > div.test-value-header { text-align: right; }
        .summary-grid-header > div.range-header { text-align: left; }
        .summary-grid-header > div.uom-header { text-align: left; }
        .summary-grid-header > div.flag-header { text-align: center; }
        .test-item { display: contents; }
        .grid-cell {
            padding: 4px 15px; border-bottom: 1px solid #f0f0f0; font-size: 13px;
            display: flex; align-items: center; min-height: 28px;
            overflow: hidden; text-overflow: ellipsis;
        }
        .test-item:last-of-type > .grid-cell { border-bottom: none; }
        .grid-cell.test-details { gap: 8px; }
        .grid-cell.pending-value { justify-content: right; color: #999; }
        .test-name { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
        .test-icon { color: #28a745; }
        .test-bullet { color: #dc3545; }
        .test-value {
            font-weight: bold; color: #000000; border-radius: 4px;
            font-size: 13px; text-align: right; padding: 2px 4px;
            justify-content: flex-end; width: 100%;
        }
        .test-value.highlight-H { background-color: #ffe0e0 !important; }
        .test-value.highlight-L { background-color: #e0f2f7 !important; }
        .test-uom { font-size: 12px; color: #555; }
        .grid-cell.flag-cell { justify-content: center; }
        .flag {
            font-weight: bold; border-radius: 50%; width: 16px; height: 16px;
            display: inline-flex; align-items: center; justify-content: center;
            font-size: 11px; color: white; flex-shrink: 0;
        }
        .flag-placeholder { width: 16px; height: 16px; visibility: hidden; flex-shrink: 0; }
        .flag-H { background-color: #d32f2f !important; }
        .flag-L { background-color: #1976d2 !important; }
        .summary-footer {
            display: flex; justify-content: space-between; align-items: center; background: #f8f9fa;
            border-top: 1px solid #e0e0e0; padding: 10px 15px; font-size: 12px; color: #6c757d;
            flex-shrink: 0;
        }
    `);

    const CONFIG = {
        COLUMN_PATTERNS: {
            TEST_DESC: ['testdesc', 'testdescription', 'name'],
            STATUS: ['resultstatus', 'teststatus', 'status', 'state'],
            RESULT_VALUE: ['testresult'],
            UOM: ['uom', 'uomvalue'],
            REFERENCE_RANGE: ['referencerange', 'range']
        },
        STATUS_PROPERTIES: {
            resulted:       { color: '#ffca77', className: 'fa fa-check-circle vicon' },
            ordered:        { color: '#E0E0E0', className: 'fa fa-star vicon' },
            verifiedlevel1: { color: '#90EE90', className: 'fa fa-arrow-circle-up ord vicon' },
            verifiedlevel2: { color: '#28a745', className: 'fa fa-arrow-circle-up vicon' },
            default:        { color: '#E0E0E0', className: null }
        }
    };
    const collapseState = {};

    // Defines the desired sort order for tests within a summary group.
    const testSortOrder = [
      // User Rule: Na -> K (Electrolytes)
      'sodium',
      'potassium',
      'chloride',
      // Kidney Function
      'urea',
      'creatinine',
      // User Rule: Lipid Panel
      'cholesterol',
      'triglycerides',
      'high-density lipoprotein',
      'low-density lipoprotein',
      // Liver Function
      'alkaline phosphatase',
      'alanine amino transferase',
      'aspartate amino transferase',
      'gamma-glutamyl transferase',
      // User Rule: TBIL -> DBIL
      'bilirubin - total',
      'bilirubin - direct',
      'total protein',
      'albumin',
      // Minerals
      'calcium',
      'phosphate'
    ];

    /**
     * Returns a numeric index for a given test name based on the testSortOrder array.
     * Lower numbers have higher sort priority. Tests not in the list get a high number (Infinity).
     * @param {string} testName - The name of the test.
     * @returns {number} The sort index.
     */
    function getSortIndex(testName) {
        const lowerTestName = testName.toLowerCase();
        const index = testSortOrder.findIndex(sortedName => lowerTestName.includes(sortedName));
        return index === -1 ? Infinity : index;
    }


    const findCellByPatterns = (row, patterns) => {
        for (const cell of row.querySelectorAll('[col-id]')) {
            const colId = cell.getAttribute('col-id')?.toLowerCase();
            if (colId && patterns.includes(colId)) return cell;
        }
        return null;
    };
    const parseRangeAndCompare = (rangeStr, value) => {
        if (!rangeStr || value === null || isNaN(parseFloat(value))) return null;
        const numVal = parseFloat(value);
        const stdRange = rangeStr.match(/(\d+\.?\d*)\s*-\s*(\d+\.?\d*)/);
        if (stdRange) {
            const lower = parseFloat(stdRange[1]);
            const upper = parseFloat(stdRange[2]);
            if (numVal < lower) return 'L';
            if (numVal > upper) return 'H';
        }
        const ltRange = rangeStr.match(/(?:<|Less than)\s*(\d+\.?\d*)/i);
        if (ltRange) { if (numVal >= parseFloat(ltRange[1])) return 'H'; }
        const gtRange = rangeStr.match(/(?:>|Greater than)\s*(\d+\.?\d*)/i);
        if (gtRange) { if (numVal <= parseFloat(gtRange[1])) return 'L'; }
        return null;
    };
    const capitalize = (s) => {
        if (typeof s !== 'string' || s.length === 0) return '';
        const spaced = s.replace(/([A-Z])|([0-9]+)/g, ' $1$2').trim();
        return spaced.charAt(0).toUpperCase() + spaced.slice(1);
    };
    const getNormalizedStatusKey = (status) => {
        const normalized = status.toLowerCase().replace(/\s+/g, '');
        if (normalized.includes('verified') && normalized.includes('1')) return 'verifiedlevel1';
        if (normalized.includes('verified') && normalized.includes('2')) return 'verifiedlevel2';
        if (normalized === 'resulted') return 'resulted';
        if (normalized === 'ordered') return 'ordered';
        return 'default';
    };

    function getAllTests() {
        const testsByStatus = {};
        const leftRows = document.querySelectorAll('.ag-pinned-left-cols-container .ag-row');
        const centerRows = document.querySelectorAll('.ag-center-cols-container .ag-row');
        const rightRows = document.querySelectorAll('.ag-pinned-right-cols-container .ag-row');
        const combinedRows = [];
        for (let i = 0; i < centerRows.length; i++) {
            const combined = document.createElement('div');
            [leftRows[i], centerRows[i], rightRows[i]].forEach(p => {
                if (p) p.querySelectorAll('[col-id]').forEach(c => combined.appendChild(c.cloneNode(true)));
            });
            combinedRows.push(combined);
        }
        const uniqueTests = new Map();
        for (const combined of combinedRows) {
            const nameCell = findCellByPatterns(combined, CONFIG.COLUMN_PATTERNS.TEST_DESC);
            const testName = nameCell?.textContent?.trim();
            if (!testName) continue;
            const statusCell = findCellByPatterns(combined, CONFIG.COLUMN_PATTERNS.STATUS);
            const status = statusCell?.textContent?.trim().toLowerCase() || 'unknown';
            let data = { name: testName, status: status, value: null, uom: null, range: null, flag: null };
            const valCell = findCellByPatterns(combined, CONFIG.COLUMN_PATTERNS.RESULT_VALUE);
            const rangeCell = findCellByPatterns(combined, CONFIG.COLUMN_PATTERNS.REFERENCE_RANGE);
            const uomCell = findCellByPatterns(combined, CONFIG.COLUMN_PATTERNS.UOM);
            data.value = valCell?.textContent?.trim().replace('-', '') || null;
            data.range = rangeCell?.textContent?.trim().replace('-', '') || null;
            data.uom = uomCell?.textContent?.trim().replace('-', '') || null;
            data.flag = parseRangeAndCompare(data.range, data.value);
            uniqueTests.set(testName, data);
        }
        uniqueTests.forEach(data => {
            if (!testsByStatus[data.status]) testsByStatus[data.status] = [];
            testsByStatus[data.status].push(data);
        });
        return testsByStatus;
    }

    const renderList = (list) => {
        if (list.length === 0) return `<div class="no-tests" style="grid-column: 1 / -1;">No tests in this category.</div>`;
        const headerHtml = `
            <div class="summary-grid-header">
                <div class="test-name-header">Test Name</div>
                <div class="test-value-header">Result</div>
                <div class="range-header">Ref Range</div>
                <div class="uom-header">UOM</div>
                <div class="flag-header">H/L</div>
            </div>`;
        const itemsHtml = list.map(t => {
            const iconHtml = t.status.includes('result') ? `<span class="test-icon">✓</span>` : `<span class="test-bullet">●</span>`;
            const resultCellHtml = (t.value !== null && t.value !== undefined) ? `<div class="grid-cell test-value ${t.flag ? 'highlight-' + t.flag : ''}">${t.value}</div>` : `<div class="grid-cell pending-value">${capitalize(t.status)}</div>`;
            return `
                <div class="test-item" title="${t.name} (${capitalize(t.status)})">
                    <div class="grid-cell test-details">${iconHtml}<span class="test-name">${t.name}</span></div>
                    ${resultCellHtml}
                    <div class="grid-cell test-range">${t.range || '-'}</div>
                    <div class="grid-cell test-uom">${t.uom || '-'}</div>
                    <div class="grid-cell flag-cell"><span class="flag ${t.flag ? 'flag-' + t.flag : 'flag-placeholder'}">${t.flag || ''}</span></div>
                </div>`;
        }).join('');
        return headerHtml + itemsHtml;
    };

    function updateSummaryContent(containerEl) {
        if (!containerEl) return;
        const testsByStatus = getAllTests();
        // ** FIX: Corrected the group sort order **
        const statusOrder = ['resulted', 'ordered', 'verifiedlevel1', 'verifiedlevel2'];
        const sortedStatuses = Object.keys(testsByStatus).sort((a, b) => {
            const keyA = getNormalizedStatusKey(a);
            const keyB = getNormalizedStatusKey(b);
            const indexA = statusOrder.indexOf(keyA);
            const indexB = statusOrder.indexOf(keyB);
            if (indexA === -1 && indexB === -1) return a.localeCompare(b);
            if (indexA === -1) return 1;
            if (indexB === -1) return -1;
            return indexA - indexB;
        });

        let contentHtml = '';
        let totalTests = 0;
        if (sortedStatuses.length === 0) {
            contentHtml = `<div class="no-tests">No tests found on this page.</div>`;
        } else {
            sortedStatuses.forEach(status => {
                const testList = testsByStatus[status];
                if (testList.length === 0) return;

                // Sort the test list based on the custom order
                testList.sort((a, b) => {
                    const indexA = getSortIndex(a.name);
                    const indexB = getSortIndex(b.name);
                    // If both tests are in the custom sort list, sort by that order
                    if (indexA !== Infinity || indexB !== Infinity) {
                        return indexA - indexB;
                    }
                    // Otherwise, fall back to alphabetical sorting
                    return a.name.localeCompare(b.name);
                });

                totalTests += testList.length;
                collapseState[status] = collapseState[status] ?? false;
                const isCollapsed = collapseState[status];
                const collapseIconClass = isCollapsed ? 'fa-arrow-circle-right' : 'fa-arrow-circle-down';
                const statusDisplayName = capitalize(status);
                const normalizedKey = getNormalizedStatusKey(status);
                const properties = CONFIG.STATUS_PROPERTIES[normalizedKey] || CONFIG.STATUS_PROPERTIES.default;
                const headerColor = properties.color;
                const statusIconHtml = properties.className ? `<i class="${properties.className} summary-status-icon"></i>` : '';
                contentHtml += `
                    <div class="summary-header" data-section="${status}" style="background-color: ${headerColor};">
                        <i class="fas ${collapseIconClass} collapse-icon ${isCollapsed ? 'collapsed' : ''}"></i>
                        <span>${statusDisplayName} (${testList.length})</span>
                        ${statusIconHtml}
                    </div>
                    <div id="${status.replace(/\s+/g, '-')}-tests-content" class="summary-content ${isCollapsed ? 'collapsed' : ''}">
                         <div class="summary-grid-container test-list-container">${renderList(testList)}</div>
                         <div class="hidden-note-container">
                             ${testList.length > 0 ? `<div class="no-tests hidden-note">${testList.length} ${statusDisplayName} tests hidden</div>` : ''}
                         </div>
                    </div>`;
            });
        }
        const now = new Date();
        containerEl.innerHTML = contentHtml + `
            <div class="summary-footer">
                <span>Total Tests: ${totalTests}</span>
                <span>Updated: ${now.toLocaleDateString([], {month: 'short', day: 'numeric'})} ${now.toLocaleTimeString([], { hour: '2-digit', minute:'2-digit' })}</span>
            </div>`;
    }

    const summaryContainer = document.createElement('div');
    summaryContainer.id = 'test-summary-container';
    summaryContainer.addEventListener('click', e => {
        const header = e.target.closest('.summary-header');
        if (!header) return;
        const section = header.dataset.section;
        if (section) {
            collapseState[section] = !collapseState[section];
            const content = document.getElementById(`${section.replace(/\s+/g, '-')}-tests-content`);
            const icon = header.querySelector('.collapse-icon');
            if (content && icon) {
                content.classList.toggle('collapsed', collapseState[section]);
                icon.classList.toggle('collapsed', collapseState[section]);
                icon.classList.toggle('fa-arrow-circle-down', !collapseState[section]);
                icon.classList.toggle('fa-arrow-circle-right', collapseState[section]);
            }
        }
    });

    let insertionIntervalId = null;
    const isTargetPageForSummary = () => /\/0\/.*\/undefined$/.test(window.location.href);
    const removeSummaryContainer = () => {
        const container = document.getElementById(summaryContainer.id);
        if (container) {
            Object.assign(container.style, { position: '', top: '', zIndex: '' });
            container.remove();
        }
    };
    const attemptInsertion = () => {
        let targetElement = document.getElementById('barcode-display-box') || document.querySelector('.test-open.mt-2');
        if (!targetElement || document.getElementById(summaryContainer.id)) return false;
        targetElement.insertAdjacentElement('afterend', summaryContainer);
        try {
            const barcodeBox = document.getElementById('barcode-display-box');
            if (barcodeBox) {
                const barcodeBoxStyles = window.getComputedStyle(barcodeBox);
                if (barcodeBoxStyles.position === 'sticky') {
                    const barcodeBoxHeight = barcodeBox.offsetHeight;
                    const barcodeBoxTop = parseInt(barcodeBoxStyles.top, 10) || 0;
                    const summaryTopPosition = barcodeBoxTop + barcodeBoxHeight + 10;
                    Object.assign(summaryContainer.style, { position: 'sticky', top: `${summaryTopPosition}px`, zIndex: '98' });
                }
            }
        } catch(e) { console.error("Enhanced AG Grid: Error applying sticky style to summary container.", e); }
        return true;
    };
    const startInsertionPolling = () => {
        if (insertionIntervalId !== null) return;
        insertionIntervalId = setInterval(() => {
            if (attemptInsertion()) {
                clearInterval(insertionIntervalId);
                insertionIntervalId = null;
                if (window.enhancedGrid && window.enhancedGrid.triggerSummaryUpdate) window.enhancedGrid.triggerSummaryUpdate();
            }
        }, 500);
    };
    const stopInsertionPolling = () => {
        if (insertionIntervalId !== null) {
            clearInterval(insertionIntervalId);
            insertionIntervalId = null;
        }
    };
    const manageSummaryTableLifecycle = () => {
        if (isTargetPageForSummary()) startInsertionPolling();
        else {
            stopInsertionPolling();
            removeSummaryContainer();
        }
    };
    window.enhancedGrid.triggerSummaryUpdate = () => {
        const container = document.getElementById(summaryContainer.id);
        if (container && isTargetPageForSummary()) updateSummaryContent(container);
    };
    const observeSpaUrlChangesForSummary = () => {
        let lastHandledUrl = location.href;
        const checkUrlChange = () => {
            if (location.href !== lastHandledUrl) {
                lastHandledUrl = location.href;
                manageSummaryTableLifecycle();
            }
        };
        const bodyObserver = new MutationObserver(checkUrlChange);
        bodyObserver.observe(document.body, { childList: true, subtree: true });
    };
    observeSpaUrlChangesForSummary();
    manageSummaryTableLifecycle();
})();