KAAUH Lab Suite - Verification, Alerts & Enhancements

Combines verification buttons (F7/F8), dynamic alerts (>, NO RESULT, X-NORESULT, CL/CH) showing alerts once per trigger per page visit, checkbox automation, toggle back-nav,

目前為 2025-05-16 提交的版本,檢視 最新版本

// ==UserScript==
// @name         KAAUH Lab Suite - Verification, Alerts & Enhancements
// @namespace    Violentmonkey Scripts
// @version      7.0.20
// @description  Combines verification buttons (F7/F8), dynamic alerts (>, NO RESULT, X-NORESULT, CL/CH) showing alerts once per trigger per page visit, checkbox automation, toggle back-nav, 
// @match        *://his.kaauh.org/lab/*
// @grant        none
// @author       Hamad AlShegifi
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // --- Styling Adjustments (Global) ---
    function applySingleLineStyles() {
        const anchor = document.querySelector('li > a[href="#/lab-orders/doctor-request"]');
        if (anchor) {
            anchor.style.setProperty('white-space', 'nowrap', '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, '');
            }
        };
        simplifySpan('span.to-do');
        simplifySpan('span.pending-orders');
    }

    function debounce(func, wait) {
        let timeout;
        return function(...args) {
            clearTimeout(timeout);
            timeout = setTimeout(() => func.apply(this, args), wait);
        };
    }

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

// Main Userscript Logic (Alerts, F7/F8 buttons, etc.)
(function () {
    'use strict';

    const CONFIG_MAIN = {
        URLS: {
            EDIT_PAGE_PREFIX: 'https://his.kaauh.org/lab/#/lab-orders/edit-lab-order/',
        },
        SELECTORS: {
            VERIFY1_BTN: '#custom-script-buttons button.verify1-btn',
            VERIFY2_BTN: '#custom-script-buttons button.verify2-btn',
            COMPLETE_TECH: 'button.dropdown-item[translateid="test-results.CompleteTechnicalVerification"]',
            COMPLETE_MED: 'button.dropdown-item[translateid="test-results.CompleteMedicalVerification"]',
            FINAL_VERIFY: 'button.btn-success.btn-sm.min-width[translateid="test-verification.Verify"]',
            NEXT_BTN: 'button#btnNext',
            UNCHECKED_BOX: 'span.ag-icon-checkbox-unchecked[unselectable="on"]',
            CHECKBOX_ROW: '.ag-row',
            TEST_DESC_CELL: '[col-id="TestDesc"]',
            ORDERED_STATUS_CELL: 'div[col-id="ResultStatus"]',
            TOAST: {
                CONTAINER: '#toast-container',
                CLOSE_BTN: 'button.toast-close-button',
                SUCCESS: '.toast-success',
            },
            SAMPLE_RECEIVE_MODAL: 'modal-container.show',
        },
        CHECK_INTERVALS: {
            UNDEFINED_URL: 200,
            ORDERED_SCAN: 500,
            DISABLED_BTN_CHECK: 1000,
        },
        EXCLUDE_WORDS: [
            'culture', "gram's stain", 'stain', 'bacterial', 'fungal',
            'pcr', 'meningitis', 'mrsa', 'mid', 'stream', 'cryptococcus'
        ]
    };

    let verify1Clicked = false;
    let verify2Clicked = false;
    let verify1Toggle = localStorage.getItem('verify1Toggle') === 'true';
    let verify2Toggle = localStorage.getItem('verify2Toggle') === 'true';
    let hasScrolledToOrderedRow = false;
    let lastDisabledButtonAlertTime = 0;
    const DISABLED_ALERT_COOLDOWN = 30000;

    const logDebugMain = msg => console.debug(`[LabScript Main] ${msg}`);
    const isCorrectPage = () => window.location.href.startsWith(CONFIG_MAIN.URLS.EDIT_PAGE_PREFIX);

    function showDisabledButtonAlert(message) {
        const now = Date.now();
        if (now - lastDisabledButtonAlertTime < DISABLED_ALERT_COOLDOWN) return;
        lastDisabledButtonAlertTime = now;

        const modalOverlay = document.createElement('div');
        modalOverlay.id = 'disabled-button-alert-overlay';
        Object.assign(modalOverlay.style, {
            position: 'fixed', top: '0', left: '0', width: '100vw', height: '100vh',
            backgroundColor: 'rgba(0, 0, 0, 0.7)', zIndex: '10000', display: 'flex',
            justifyContent: 'center', alignItems: 'center'
        });

        const modalBox = document.createElement('div');
        Object.assign(modalBox.style, {
            backgroundColor: '#fff', padding: '25px', borderRadius: '8px',
            boxShadow: '0 5px 15px rgba(0,0,0,0.3)', width: 'auto', maxWidth: '80%',
            textAlign: 'center', borderTop: '5px solid #f0ad4e'
        });

        const title = document.createElement('h3');
        title.textContent = 'Button Disabled';
        title.style.color = '#d9534f';
        title.style.marginTop = '0';
        modalBox.appendChild(title);

        const messageElem = document.createElement('p');
        messageElem.textContent = message;
        messageElem.style.fontSize = '16px';
        messageElem.style.marginBottom = '20px';
        modalBox.appendChild(messageElem);

        const okButton = document.createElement('button');
        okButton.textContent = 'OK';
        Object.assign(okButton.style, {
            padding: '8px 20px', borderRadius: '5px', backgroundColor: '#5cb85c',
            color: '#fff', border: 'none', cursor: 'pointer', fontSize: '16px'
        });
        okButton.onclick = () => document.body.removeChild(modalOverlay);
        modalBox.appendChild(okButton);

        modalOverlay.appendChild(modalBox);
        document.body.appendChild(modalOverlay);
        okButton.focus();
    }

    function addFontAwesome() {
        const fontAwesomeLink = 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css';
        if (!document.querySelector(`link[href="${fontAwesomeLink}"]`)) {
            const link = document.createElement('link');
            link.rel = 'stylesheet';
            link.href = fontAwesomeLink;
            const head = document.head || document.body;
            if (head) head.appendChild(link);
        }
    }

    function createVerifyButton(label, className, onClick, id) {
        const button = document.createElement('button');
        button.type = 'button';
        button.className = className;
        button.classList.add(id);
        button.innerText = label;
        const styles = {
            'font-family': 'Arial, sans-serif',
            'font-size': '14px',
            'font-weight': 'normal',
            'color': '#ffffff',
            'background-color': className.includes('success') ? '#28a745' : '#2594d9',
            'padding': '8px 16px',
            'border': 'none',
            'border-radius': '5px',
            'text-shadow': 'none',
            'cursor': 'pointer',
            'margin-right': '5px',
            'line-height': '1.5',
            'vertical-align': 'middle',
        };
        for (const [prop, value] of Object.entries(styles)) {
            button.style.setProperty(prop, value, 'important');
        }
        button.onclick = onClick;
        return button;
    }

    function createToggleIcon(id, isActive, onClick) {
        const icon = document.createElement('span');
        icon.id = id;
        icon.innerHTML = `<i class="fas fa-arrow-circle-left" style="color: ${isActive ? '#008000' : '#d1cfcf'}; font-size: 1.3em; vertical-align: middle;"></i>`;
        icon.style.cursor = 'pointer';
        icon.style.marginRight = '10px';
        icon.style.marginLeft = '-1px';
        icon.title = "Toggle: Go back automatically after this verification success toast is clicked";
        icon.onclick = onClick;
        return icon;
    }

    function handleVerifyToggle(type) {
        const toggle = type === 'verify1' ? !verify1Toggle : !verify2Toggle;
        localStorage.setItem(type + 'Toggle', toggle);
        const icon = document.querySelector(`#${type}Icon i`);
        if (icon) icon.style.setProperty('color', toggle ? '#008000' : '#d1cfcf', 'important');

        if (type === 'verify1') verify1Toggle = toggle;
        else verify2Toggle = toggle;
    }

    function addButtons() {
        if (document.getElementById('custom-script-buttons') || !isCorrectPage()) return;
        const nextButton = document.querySelector(CONFIG_MAIN.SELECTORS.NEXT_BTN);
        if (!nextButton || !nextButton.parentNode) {
            setTimeout(addButtons, 500);
            return;
        }

        const buttonDiv = document.createElement('div');
        buttonDiv.id = 'custom-script-buttons';
        buttonDiv.style.setProperty('display', 'inline-block', 'important');
        buttonDiv.style.setProperty('margin-left', '10px', 'important');
        buttonDiv.style.setProperty('vertical-align', 'middle', 'important');

        const verify1Button = createVerifyButton('VERIFY1 (F7)', 'btn btn-success btn-sm', () => {
            verify1Clicked = true;
            verify2Clicked = false;
            checkAllVisibleBoxesWithoutDuplicates();
            setTimeout(clickCompleteTechnicalVerificationButton, 500);
        }, 'verify1-btn');

        const verify2Button = createVerifyButton('VERIFY2 (F8)', 'btn btn-primary btn-sm', () => {
            verify2Clicked = true;
            verify1Clicked = false;
            checkAllVisibleBoxesWithoutDuplicates();
            setTimeout(clickCompleteMedicalVerificationButton, 500);
        }, 'verify2-btn');

        const verify1Icon = createToggleIcon('verify1Icon', verify1Toggle, () => handleVerifyToggle('verify1'));
        const verify2Icon = createToggleIcon('verify2Icon', verify2Toggle, () => handleVerifyToggle('verify2'));

        const credit = document.createElement('span');
        credit.textContent = "Modded by: Hamad AlShegifi";
        credit.style.cssText =
            `font-size: 11px !important;
            font-weight: bold !important;
            color: #e60000 !important;
            margin-left: 15px !important;
            border: 1px solid #e60000 !important;
            border-radius: 5px !important;
            padding: 3px 6px !important;
            background-color: #ffffff !important;
            vertical-align: middle !important;
            `;

        buttonDiv.append(verify1Button, verify1Icon, verify2Button, verify2Icon, credit);
        nextButton.parentNode.insertBefore(buttonDiv, nextButton.nextSibling);
    }

    function checkAllVisibleBoxesWithoutDuplicates() {
        const selectedTests = new Set();
        const boxes = document.querySelectorAll(CONFIG_MAIN.SELECTORS.UNCHECKED_BOX);
        boxes.forEach(box => {
            const row = box.closest(CONFIG_MAIN.SELECTORS.CHECKBOX_ROW);
            if (row && row.offsetParent !== null) {
                const testDescElement = row.querySelector(CONFIG_MAIN.SELECTORS.TEST_DESC_CELL);
                const descText = testDescElement?.textContent.trim().toLowerCase();

                if (descText) {
                    const isExcluded = CONFIG_MAIN.EXCLUDE_WORDS.some(word => descText.includes(word));
                    if (!selectedTests.has(descText) && !isExcluded) {
                        selectedTests.add(descText);
                        box.click();
                    }
                }
            }
        });
    }

    const clickButton = (selector, callback) => {
        const btn = document.querySelector(selector);
        if (btn && !btn.disabled) {
            btn.click();
            if (callback) setTimeout(callback, 500);
            return true;
        } else if (btn && btn.disabled) {
            const buttonName = selector.includes('CompleteTechnicalVerification') ?
                'Complete Technical Verification' :
                selector.includes('CompleteMedicalVerification') ?
                'Complete Medical Verification' :
                selector.includes('Verify') ?
                'Final Verify' : 'Button';
            showDisabledButtonAlert(`${buttonName} button is disabled. Please check if you have selected all required tests or if verification is already done.`);
        }
        return false;
    };

    const clickCompleteTechnicalVerificationButton = () => clickButton(CONFIG_MAIN.SELECTORS.COMPLETE_TECH, clickFinalVerifyButton);
    const clickCompleteMedicalVerificationButton = () => clickButton(CONFIG_MAIN.SELECTORS.COMPLETE_MED, clickFinalVerifyButton);
    const clickFinalVerifyButton = () => clickButton(CONFIG_MAIN.SELECTORS.FINAL_VERIFY);

    function checkForDisabledButtons() {
        if (!isCorrectPage()) return;
        const verify1Btn = document.querySelector(CONFIG_MAIN.SELECTORS.VERIFY1_BTN);
        const verify2Btn = document.querySelector(CONFIG_MAIN.SELECTORS.VERIFY2_BTN);
        if (verify1Btn && verify1Btn.disabled) {
            showDisabledButtonAlert("Verification is disabled. Make sure tests not already verified.");
        }
        if (verify2Btn && verify2Btn.disabled) {
            showDisabledButtonAlert("Verification is disabled. Make sure tests not already verified.");
        }
    }

    function checkUrlAndTriggerClickForUndefined() {
        if (window.location.href.endsWith('/undefined')) {
            const closeBtn = document.querySelector(`${CONFIG_MAIN.SELECTORS.TOAST.CONTAINER} ${CONFIG_MAIN.SELECTORS.TOAST.CLOSE_BTN}`);
            if (closeBtn) closeBtn.click();
        }
    }

    function monitorOrderedStatus() {
        if (!isCorrectPage()) return;
        const rows = document.querySelectorAll('div[role="row"]');
        let hasOrdered = false;
        let firstOrderedRow = null;

        rows.forEach(row => {
            if (row.offsetParent !== null) {
                const cell = row.querySelector(CONFIG_MAIN.SELECTORS.ORDERED_STATUS_CELL);
                if (cell?.textContent.includes('Ordered')) {
                    if (!firstOrderedRow) firstOrderedRow = row;
                    hasOrdered = true;
                }
            }
        });

        const btn = document.querySelector(CONFIG_MAIN.SELECTORS.VERIFY1_BTN);
        if (btn) {
            if (hasOrdered) {
                if (!btn.classList.contains('btn-warning')) {
                    btn.className = 'btn btn-warning verify1-btn';
                    btn.innerText = 'VERIFY1 (F7)';
                    btn.style.setProperty('background-color', '#fab641', 'important');
                    btn.style.setProperty('color', '#050505', 'important');
                }
                if (firstOrderedRow && !hasScrolledToOrderedRow) {
                    document.documentElement.style.scrollBehavior = 'smooth';
                    firstOrderedRow.scrollIntoView({ behavior: 'auto', block: 'center' });
                    hasScrolledToOrderedRow = true;
                    setTimeout(() => document.documentElement.style.scrollBehavior = 'auto', 1000);
                }
            } else {
                if (btn.classList.contains('btn-warning')) {
                    btn.className = 'btn btn-success btn-sm verify1-btn';
                    btn.innerText = 'VERIFY1 (F7)';
                    btn.style.setProperty('background-color', '#28a745', 'important');
                    btn.style.setProperty('color', '#ffffff', 'important');
                }
                hasScrolledToOrderedRow = false;
            }
        }
    }
    setInterval(monitorOrderedStatus, CONFIG_MAIN.CHECK_INTERVALS.ORDERED_SCAN);


    const toastObserver = new MutationObserver(mutations => {
        mutations.forEach(mutation => {
            mutation.addedNodes.forEach(node => {
                if (node.nodeType === Node.ELEMENT_NODE) {
                    let successToast = null;
                    if (node.matches(CONFIG_MAIN.SELECTORS.TOAST.SUCCESS)) {
                        successToast = node;
                    } else if (node.querySelector) {
                        successToast = node.querySelector(CONFIG_MAIN.SELECTORS.TOAST.SUCCESS);
                    }

                    if (successToast) {
                        successToast.addEventListener('click', () => {
                            if ((verify1Clicked && verify1Toggle) || (verify2Clicked && verify2Toggle)) {
                                window.history.back();
                            }
                            verify1Clicked = false;
                            verify2Clicked = false;
                        }, { once: true });
                    }
                }
            });
        });
    });
    toastObserver.observe(document.body, { childList: true, subtree: true });

    document.addEventListener('keydown', e => {
        if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA' || e.target.tagName === 'SELECT') {
            return;
        }

        if (e.key === 'F7') {
            e.preventDefault();
            document.querySelector(CONFIG_MAIN.SELECTORS.VERIFY1_BTN)?.click();
        } else if (e.key === 'F8') {
            e.preventDefault();
            document.querySelector(CONFIG_MAIN.SELECTORS.VERIFY2_BTN)?.click();
        }
    });


    // --- Integrated Alerts Scanner Code (Critical, Dilution, No-Result) ---
    if (typeof XLSX === 'undefined') {
        console.warn("[Alerts Scanner] SheetJS library (XLSX) not found. Local XLSX saving will not work.");
    }

    const CONFIG_ALERTS = {
        SCAN_INTERVAL: 750,
        FLASH_COLOR: "pink",
        FLASH_INTERVAL: 700,
        RESULT_CELL_SELECTOR: 'div[role="gridcell"][col-id="TestResult"] app-result-value-render div',
        UOM_CELL_SELECTOR: 'div[col-id="UomValue"]',
        CRITICAL_FLAG_SELECTOR: 'div[role="gridcell"][col-id="LTFlag"] app-ref-high-low div span.critial-alret-indication',
        TEST_DESC_PINNED_SELECTOR: '.ag-pinned-left-cols-container .ag-row div[col-id="TestDesc"]',
        NO_RESULT_MESSAGE: "NO-RESULT DETECTED!",
        DILUTION_MESSAGE: "DILUTION REQUIRED!",
        PATIENT_NAME_SELECTOR: 'div.patient-name-full',
        PATIENT_MRN_SELECTOR: 'div.mid.renal-demography span',
        PATIENT_LOCATION_SELECTOR: 'span[title*="UNIT/"]',
        USER_NAME_SELECTOR: 'div.profile-wrapper span.csi-dropdown-btn-text',
        SAMPLE_BARCODE_SELECTOR: 'div[style*="font-size: 13px; color: rgb(68, 68, 68);"]',
        LOCAL_SERVER_SAVE_URL: 'http://localhost:5000/save-alerts'
    };

    let isScanningActive = false;
    let issueScanIntervalId = null;
    let isAlertModalOpen = false;

    const logAlertDebug = msg => console.debug(`[Alerts Scanner DEBUG] ${msg}`);
    const logAlertError = msg => console.error(`[Alerts Scanner ERROR] ${msg}`);

    function applyFlashingEffect(row) {
        if (!row || row.dataset.flashing === 'true') return;
        row.dataset.flashing = 'true';
        const originalBg = row.style.backgroundColor || 'transparent';
        row.dataset.originalBg = originalBg;
        row.style.transition = "background-color 0.5s ease";
        let isPink = false;
        const intervalId = setInterval(() => {
            if (!document.body.contains(row) || row.dataset.flashing === 'false') {
                clearInterval(intervalId);
                row.style.transition = '';
                row.style.setProperty("background-color", row.dataset.originalBg || 'transparent', "important");
                delete row.dataset.flashing;
                delete row.dataset.originalBg;
                delete row.dataset.flashIntervalId;
                return;
            }
            isPink = !isPink;
            row.style.setProperty("background-color", isPink ? CONFIG_ALERTS.FLASH_COLOR : originalBg, "important");
        }, CONFIG_ALERTS.FLASH_INTERVAL);
        row.dataset.flashIntervalId = intervalId.toString();
    }

    function stopFlashingEffect(row) {
        if (row && row.dataset.flashing === 'true') {
            row.dataset.flashing = 'false';
            const intervalId = parseInt(row.dataset.flashIntervalId, 10);
            if (!isNaN(intervalId)) {
                clearInterval(intervalId);
            }
        }
    }


    function getNotificationSessionKey(type, identifier = 'general') {
        const safeIdentifier = String(identifier).replace(/[^a-zA-Z0-9_-]/g, '');
        return `labAlertNotified_${window.location.pathname}${window.location.hash}_${type}_${safeIdentifier}`;
    }

    function hasAlreadyNotified(type, identifier = 'general') {
        const key = getNotificationSessionKey(type, identifier);
        return sessionStorage.getItem(key) === 'true';
    }

    function setNotificationFlag(type, identifier = 'general') {
        const key = getNotificationSessionKey(type, identifier);
        logAlertDebug(`Setting notification flag for key: "${key}"`);
        sessionStorage.setItem(key, 'true');
    }

    function getNextEntryID() {
        const counterKey = 'labAlertEntryCounter';
        let currentID = parseInt(localStorage.getItem(counterKey), 10) || 0;
        currentID++;
        localStorage.setItem(counterKey, String(currentID));
        logAlertDebug(`Next Entry ID: ${currentID}`);
        return currentID;
    }

    async function sendAlertDataToServer(alertData) {
        logAlertDebug("Attempting to send data to local server.");
        if (!CONFIG_ALERTS.LOCAL_SERVER_SAVE_URL) {
            logAlertError("LOCAL_SERVER_SAVE_URL is not configured.");
            console.error("Local server save URL is not configured for alerts.");
            return false;
        }

        const notifiedPersonNameInput = document.getElementById('notifiedPersonNameInput');
        const notifiedPersonTelExtInput = document.getElementById('notifiedPersonTelExtInput');
        const readBackCheckbox = document.getElementById('readBackCheckbox');
        const userIdInput = document.getElementById('userIdInput');

        const now = new Date();
        const date = `${String(now.getDate()).padStart(2, '0')}/${String(now.getMonth() + 1).padStart(2, '0')}/${now.getFullYear()}`;
        const hours = now.getHours();
        const minutes = String(now.getMinutes()).padStart(2, '0');
        const ampm = hours >= 12 ? 'pm' : 'am';
        const formattedHours = hours % 12 || 12;
        const time = `${formattedHours}:${minutes} ${ampm}`;
        const entryID = getNextEntryID();

        const dataToSend = {
            entryID: entryID,
            date: date,
            time: time,
            patientName: alertData.patientName || 'N/A',
            patientMRN: alertData.patientId || 'N/A',
            patientLocation: alertData.patientLocation || 'N/A',
            sampleBarcode: alertData.sampleBarcode || 'N/A',
            userName: userIdInput ? userIdInput.value : (alertData.userId || 'N/A'),
            notifiedPersonName: notifiedPersonNameInput ? notifiedPersonNameInput.value : '',
            notifiedPersonTelExt: notifiedPersonTelExtInput ? notifiedPersonTelExtInput.value : '',
            readBack: readBackCheckbox ? readBackCheckbox.checked : false,
            alerts: alertData.alertsToList.map(alert => ({
                testName: alert.testName,
                result: alert.result,
                uom: alert.uom || '',
                flag: alert.flag,
                type: alert.type,
                comment: alert.comment || ''
            })),
        };

        try {
            const response = await fetch(CONFIG_ALERTS.LOCAL_SERVER_SAVE_URL, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify(dataToSend)
            });

            if (response.ok) {
                logAlertDebug("Data successfully sent to local server.");
                return true;
            } else {
                const errorText = await response.text();
                logAlertError(`Failed to send data to local server: ${response.status} ${response.statusText} - ${errorText}`);
                console.error(`Failed to save alert data to local server. Status: ${response.status}. Details: ${errorText}`);
                return false;
            }
        } catch (error) {
            logAlertError("Error sending data to local server:", error);
            console.error("An error occurred while trying to send data to the local server. Ensure the server is running and accessible.");
            return false;
        }
    }


    function closeAlertModalAction() {
        logAlertDebug("closeAlertModalAction triggered.");
        const overlay = document.getElementById('custom-alert-modal-overlay');
        if (overlay) {
            const modalContent = overlay.querySelector('#custom-alert-modal-content');
            if (modalContent && modalContent.dataset.alerts) {
                try {
                    const alertsToMark = JSON.parse(modalContent.dataset.alerts);
                    alertsToMark.forEach(alert => {
                        if (alert.type && alert.rowId) {
                            const parts = alert.rowId.split('_');
                            const type = parts[0];
                            const baseIdentifier = parts.slice(1).join('_');
                            setNotificationFlag(type, baseIdentifier);
                            logAlertDebug(`Marked alert as notified: Type=${type}, Identifier=${baseIdentifier} (from full key ${alert.rowId})`);
                        }
                    });
                } catch (e) {
                    console.error("Error parsing alerts data from modal dataset:", e);
                }
            }

            overlay.style.opacity = '0';
            if (modalContent) {
                modalContent.style.opacity = '0';
                modalContent.style.transform = 'translateY(-30px) scale(0.95)';
            }

            overlay.addEventListener('transitionend', () => {
                if (overlay.parentNode) {
                    overlay.parentNode.removeChild(overlay);
                    isAlertModalOpen = false;
                    document.body.style.overflow = '';
                    logAlertDebug("Alert modal overlay removed, isAlertModalOpen = false");
                }
            }, { once: true });
            setTimeout(() => {
                if (overlay && overlay.parentNode) {
                    overlay.parentNode.removeChild(overlay);
                    isAlertModalOpen = false;
                    document.body.style.overflow = '';
                    logAlertDebug("Alert modal overlay removed by fallback, isAlertModalOpen = false");
                }
            }, 400);
        } else {
            isAlertModalOpen = false;
            document.body.style.overflow = '';
            logAlertDebug("closeAlertModalAction called but overlay not found, isAlertModalOpen = false");
        }
    }

    function createCustomAlert(alertData, alertsDisplayedInModal) {
        logAlertDebug(`Attempting to create alert modal. isAlertModalOpen: ${isAlertModalOpen}, Existing overlay: ${document.getElementById('custom-alert-modal-overlay')}`);
        if (document.getElementById('custom-alert-modal-overlay') || isAlertModalOpen) {
            logAlertDebug("Alert modal already open or overlay element exists. Aborting creation.");
            return;
        }
        isAlertModalOpen = true;
        logDebugMain("Alert modal creation initiated. isAlertModalOpen set to true.");

        const overlay = document.createElement('div');
        overlay.id = 'custom-alert-modal-overlay';
        Object.assign(overlay.style, {
            position: 'fixed', top: '0', left: '0', width: '100%', height: '100%',
            backgroundColor: 'rgba(0, 0, 0, 0.65)',
            zIndex: '10001',
            display: 'flex', justifyContent: 'center', alignItems: 'center',
            opacity: '0', transition: 'opacity 0.3s ease-out',
            padding: '20px'
        });
        document.body.style.overflow = 'hidden';

        const modalBox = document.createElement('div');
        modalBox.id = 'custom-alert-modal-content';
        Object.assign(modalBox.style, {
            backgroundColor: '#ffffff',
            borderRadius: '12px',
            boxShadow: '0 16px 32px rgba(0, 0, 0, 0.1), 0 8px 16px rgba(0, 0, 0, 0.08)',
            width: 'calc(100% - 40px)',
            maxWidth: '1100px',
            maxHeight: '90vh',
            overflowY: 'auto',
            padding: '32px',
            position: 'relative',
            opacity: '0', transform: 'translateY(-20px) scale(0.98)',
            transition: 'opacity 0.3s ease-out, transform 0.3s ease-out',
            fontFamily: "'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif",
            color: '#343a40',
            display: 'flex', flexDirection: 'column', gap: '16px'
        });

        const closeButton = document.createElement('button');
        closeButton.innerHTML = '&times;';
        Object.assign(closeButton.style, {
            position: 'absolute', top: '18px', right: '18px',
            fontSize: '30px', lineHeight: '1', cursor: 'pointer', color: '#adb5bd',
            background: 'transparent', border: 'none', padding: '0', width: '30px', height: '30px',
            display: 'flex', alignItems: 'center', justifyContent: 'center'
        });
        closeButton.onmouseover = () => closeButton.style.color = '#495057';
        closeButton.onmouseout = () => closeButton.style.color = '#adb5bd';
        closeButton.onclick = closeAlertModalAction;
        modalBox.appendChild(closeButton);

        const headerDiv = document.createElement('div');
        Object.assign(headerDiv.style, {
            display: 'flex', alignItems: 'center',
            gap: '12px',
            paddingBottom: '18px', borderBottom: '1px solid #dee2e6', marginBottom: '16px'
        });

        const alertIcon = document.createElement('i');
        alertIcon.className = 'fas fa-exclamation-triangle';
        let iconColor = '#17a2b8';

        let alertTitleText = 'Lab Alert Detected';
        if (alertData.overallSeverity === 'critical') {
            alertTitleText = 'Critical Lab Alert!'; iconColor = '#d9534f';
        } else if (alertData.overallSeverity === 'noresult') {
            alertTitleText = 'No Result Detected'; iconColor = '#ffc107';
        } else if (alertData.overallSeverity === 'greaterThan') {
            alertTitleText = 'Dilution Required'; iconColor = '#ffc107';
        }
        alertIcon.style.fontSize = '26px';
        alertIcon.style.color = iconColor;
        headerDiv.appendChild(alertIcon);

        const title = document.createElement('h2');
        title.textContent = alertTitleText;
        Object.assign(title.style, { fontSize: '26px', fontWeight: '600', color: iconColor, margin: '0' });
        headerDiv.appendChild(title);

        const dateTime = document.createElement('p');
        dateTime.id = 'currentDateTime';
        const now = new Date();
        dateTime.textContent = now.toLocaleString([], { dateStyle: 'medium', timeStyle: 'short' });
        Object.assign(dateTime.style, { fontSize: '14px', color: '#6c757d', margin: '0', marginLeft: 'auto' });
        headerDiv.appendChild(dateTime);
        modalBox.appendChild(headerDiv);

        const allPatientInfoContainer = document.createElement('div');
        Object.assign(allPatientInfoContainer.style, {
            display: 'flex',
            flexDirection: 'column',
            gap: '18px',
            paddingBottom: '18px',
            borderBottom: '1px solid #dee2e6',
            marginBottom: '16px'
        });

        const createInfoItem = (label, value, iconClass) => {
            if (!value) value = 'N/A';
            const itemDiv = document.createElement('div');
            Object.assign(itemDiv.style, {
                backgroundColor: '#f8f9fa', borderRadius: '8px', padding: '16px',
                border: '1px solid #e9ecef', boxShadow: '0 3px 6px rgba(0,0,0,0.06)',
                display: 'flex', flexDirection: 'column', gap: '10px'
            });

            const header = document.createElement('div');
            Object.assign(header.style, { display: 'flex', alignItems: 'center', gap: '12px', color: '#495057' });

            if (iconClass) {
                const iconElem = document.createElement('i');
                iconElem.className = `fas ${iconClass}`;
                Object.assign(iconElem.style, { fontSize: '1.4em', color: '#007bff' });
                header.appendChild(iconElem);
            }

            const labelElem = document.createElement('strong');
            labelElem.textContent = `${label}:`;
            labelElem.style.fontSize = '15px'; labelElem.style.fontWeight = '600';
            header.appendChild(labelElem);
            itemDiv.appendChild(header);

            const valueElem = document.createElement('span');
            valueElem.textContent = value;
            Object.assign(valueElem.style, {
                fontSize: '16px', color: '#212529', wordBreak: 'break-word',
                paddingLeft: iconClass ? '32px' : '0'
            });
            itemDiv.appendChild(valueElem);
            return itemDiv;
        };

        const pNameItem = createInfoItem('Patient Name', alertData.patientName, 'fa-user');
        if (pNameItem) {
            allPatientInfoContainer.appendChild(pNameItem);
        }

        const otherPatientInfoRow = document.createElement('div');
        Object.assign(otherPatientInfoRow.style, {
            display: 'grid',
            gridTemplateColumns: 'repeat(auto-fit, minmax(240px, 1fr))',
            gap: '18px',
        });

        const pIdElem = createInfoItem('MRN', alertData.patientId, 'fa-id-card');
        if (pIdElem) { otherPatientInfoRow.appendChild(pIdElem); }

        const pLocElem = createInfoItem('Location', alertData.patientLocation, 'fa-hospital-alt');
        if (pLocElem) { otherPatientInfoRow.appendChild(pLocElem); }

        const pBarcodeElem = createInfoItem('Sample Barcode', alertData.sampleBarcode, 'fa-barcode');
        if (pBarcodeElem) { otherPatientInfoRow.appendChild(pBarcodeElem); }

        if (otherPatientInfoRow.hasChildNodes()){
            allPatientInfoContainer.appendChild(otherPatientInfoRow);
        }

        if (allPatientInfoContainer.hasChildNodes()){
            modalBox.appendChild(allPatientInfoContainer);
        }

        if (alertData.alertsToList && alertData.alertsToList.length > 0) {
            const alertsListTitle = document.createElement('h3');
            alertsListTitle.textContent = 'Alert Details:';
            Object.assign(alertsListTitle.style, {
                fontSize: '19px', fontWeight: '600', color: '#343a40',
                margin: '0 0 12px 0',
            });
            modalBox.appendChild(alertsListTitle);

            const alertsContainer = document.createElement('div');
            Object.assign(alertsContainer.style, {
                display: 'grid',
                gridTemplateColumns: 'repeat(auto-fit, minmax(280px, 1fr))',
                gap: '18px',
                padding: '5px 0'
            });

            alertData.alertsToList.forEach(alert => {
                const alertCard = document.createElement('div');
                let borderColor = '#6c757d', bgColor = '#f8f9fa', textColor = '#343a40';

                if (alert.type === 'critical') {
                    if (alert.flag === 'CL') {
                        borderColor = '#007bff'; bgColor = '#e7f3ff';
                    } else if (alert.flag === 'CH') {
                        borderColor = '#dc3545'; bgColor = '#f8d7da';
                    } else {
                        borderColor = '#d9534f'; bgColor = '#fdf7f7';
                    }
                } else if (alert.type === 'greaterThan') {
                    borderColor = '#ffc107'; bgColor = '#fff9e6';
                } else if (alert.type === 'noresult') {
                    borderColor = '#6c757d'; bgColor = '#f8f9fa';
                }

                Object.assign(alertCard.style, {
                    borderLeft: `6px solid ${borderColor}`, padding: '18px', borderRadius: '10px',
                    backgroundColor: bgColor, color: textColor, boxShadow: '0 5px 10px rgba(0,0,0,0.08)',
                    display: 'flex', flexDirection: 'column', justifyContent: 'space-between', minHeight: '130px'
                });

                const testNameElem = document.createElement('p');
                testNameElem.innerHTML = `<strong style="font-size: 17px; color: #212529;">${alert.testName}</strong>`;
                testNameElem.style.margin = '0 0 10px 0';
                alertCard.appendChild(testNameElem);

                const resultElem = document.createElement('p');
                const resultText = alert.result || 'N/A';
                const uomText = alert.uom ? ` ${alert.uom}` : '';
                resultElem.textContent = `Result: ${resultText}${uomText}`;
                resultElem.style.margin = '0 0 10px 0';
                resultElem.style.fontSize = '15px';
                alertCard.appendChild(resultElem);

                if (alert.flag) {
                    const flagElem = document.createElement('p');
                    let flagDescription = `Flag: ${alert.flag}`;
                    if (alert.flag === 'CL') flagDescription = 'Flag: CL (Critical Low)';
                    else if (alert.flag === 'CH') flagDescription = 'Flag: CH (Critical High)';
                    flagElem.textContent = flagDescription;
                    Object.assign(flagElem.style, {margin: '0', fontSize: '15px', fontWeight: '600', color: borderColor });
                    alertCard.appendChild(flagElem);
                }
                 if (alert.comment) {
                    const commentElem = document.createElement('p');
                    commentElem.textContent = alert.comment;
                    Object.assign(commentElem.style, {
                        fontSize: '13px', fontStyle: 'italic', color: '#555',
                        marginTop: '8px', paddingTop: '8px', borderTop: '1px dashed #ccc'
                    });
                    alertCard.appendChild(commentElem);
                }

                alertsContainer.appendChild(alertCard);
            });
            modalBox.appendChild(alertsContainer);
        } else if (alertData.primaryMessage) {
            const primaryMessageElem = document.createElement('p');
            primaryMessageElem.textContent = alertData.primaryMessage;
            Object.assign(primaryMessageElem.style, { fontSize: '16px', fontWeight: '500', margin: '12px 0', color: '#555' });
            modalBox.appendChild(primaryMessageElem);
        }

        if (alertData.overallSeverity === 'critical') {
            const notifiedDetailsDiv = document.createElement('div');
            Object.assign(notifiedDetailsDiv.style, {
                marginTop: '24px', paddingTop: '20px', borderTop: '1px solid #dee2e6'
            });

            const h3 = document.createElement('h3');
            h3.textContent = 'Notification Details (Critical)';
            Object.assign(h3.style, { fontSize: '17px', fontWeight: '600', marginBottom: '16px', color: '#343a40'});
            notifiedDetailsDiv.appendChild(h3);

            const formGrid = document.createElement('div');
            Object.assign(formGrid.style, { display: 'grid', gridTemplateColumns: '1fr', gap: '14px' });

            const createFormGroup = (label, inputId, placeholder, type = 'text') => {
                const formGroup = document.createElement('div');
                const formLabel = document.createElement('label');
                formLabel.textContent = label;
                formLabel.htmlFor = inputId;
                Object.assign(formLabel.style, { display: 'block', fontSize: '14px', fontWeight: '500', marginBottom: '6px', color: '#495057' });
                formGroup.appendChild(formLabel);

                const formInput = document.createElement('input');
                formInput.type = type;
                formInput.id = inputId;
                formInput.placeholder = placeholder;
                Object.assign(formInput.style, {
                    width: '100%', padding: '12px', border: '1px solid #ced4da',
                    borderRadius: '6px', fontSize: '14px', boxSizing: 'border-box',
                    transition: 'border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out'
                });
                formInput.onfocus = () => { formInput.style.borderColor = '#80bdff'; formInput.style.boxShadow = '0 0 0 0.2rem rgba(0,123,255,.25)';};
                formInput.onblur = () => { formInput.style.borderColor = '#ced4da'; formInput.style.boxShadow = 'none';};
                formGroup.appendChild(formInput);
                return formGroup;
            };

            const userIdGroup = createFormGroup('User ID', 'userIdInput', 'Enter your User ID');
            formGrid.appendChild(userIdGroup);
            const userIdInputElem = userIdGroup.querySelector('#userIdInput');
            if (userIdInputElem && alertData.userId && alertData.userId !== 'N/A') {
                userIdInputElem.value = alertData.userId;
            }

            formGrid.appendChild(createFormGroup('Notified Person Name', 'notifiedPersonNameInput', 'Name of person notified'));
            formGrid.appendChild(createFormGroup('Extension / Contact', 'notifiedPersonTelExtInput', 'Phone or extension'));

            const readBackGroup = document.createElement('div');
            Object.assign(readBackGroup.style, { display: 'flex', alignItems: 'center', marginTop: '10px'});
            const readBackCheckbox = document.createElement('input');
            readBackCheckbox.type = 'checkbox'; readBackCheckbox.id = 'readBackCheckbox';
            Object.assign(readBackCheckbox.style, { marginRight: '10px', height: '18px', width: '18px', cursor: 'pointer', accentColor: '#007bff'});
            const readBackLabel = document.createElement('label');
            readBackLabel.textContent = 'Read-Back Confirmed'; readBackLabel.htmlFor = 'readBackCheckbox';
            Object.assign(readBackLabel.style, { fontSize: '14px', fontWeight: '500', color: '#495057', cursor: 'pointer'});
            readBackGroup.appendChild(readBackCheckbox); readBackGroup.appendChild(readBackLabel);
            formGrid.appendChild(readBackGroup);

            notifiedDetailsDiv.appendChild(formGrid);
            modalBox.appendChild(notifiedDetailsDiv);
        }

        const buttonContainer = document.createElement('div');
        Object.assign(buttonContainer.style, {
            display: 'flex', justifyContent: 'flex-end',
            paddingTop: '24px', marginTop: 'auto',
            gap: '12px', borderTop: '1px solid #dee2e6'
        });

        const createModalButton = (text, styleType = 'primary') => {
            const button = document.createElement('button');
            button.textContent = text;
            Object.assign(button.style, {
                padding: '12px 24px', borderRadius: '8px', fontWeight: '600', fontSize: '15px',
                border: 'none', cursor: 'pointer',
                transition: 'background-color 0.2s ease, box-shadow 0.2s ease, transform 0.1s ease'
            });
            if (styleType === 'primary') {
                button.style.backgroundColor = '#007bff'; button.style.color = 'white';
                button.onmouseover = () => button.style.backgroundColor = '#0069d9';
                button.onmouseout = () => button.style.backgroundColor = '#007bff';
            } else if (styleType === 'success') {
                 button.style.backgroundColor = '#28a745'; button.style.color = 'white';
                button.onmouseover = () => button.style.backgroundColor = '#218838';
                button.onmouseout = () => button.style.backgroundColor = '#28a745';
            } else {
                button.style.backgroundColor = '#6c757d'; button.style.color = 'white';
                button.onmouseover = () => button.style.backgroundColor = '#5a6268';
                button.onmouseout = () => button.style.backgroundColor = '#6c757d';
            }
            button.onmousedown = () => button.style.transform = 'scale(0.98)';
            button.onmouseup = () => button.style.transform = 'scale(1)';
            return button;
        };


        if (alertData.overallSeverity === 'critical') {
            const acknowledgeButton = createModalButton('Acknowledge & Save', 'success');
            acknowledgeButton.onclick = async () => {
                logAlertDebug("Acknowledge & Save button clicked.");
                const success = await sendAlertDataToServer(alertData);
                if (success) {
                    logAlertDebug("Data sent successfully. Closing modal.");
                    closeAlertModalAction();
                } else {
                    logAlertDebug("Data send failed. Modal remains open.");
                    const errorMsgElem = document.getElementById('sendErrorMsg');
                    if (errorMsgElem) errorMsgElem.textContent = 'Failed to save. Check server or try again.';
                }
            };
            buttonContainer.appendChild(acknowledgeButton);
            const sendErrorMsg = document.createElement('p');
            sendErrorMsg.id = 'sendErrorMsg';
            Object.assign(sendErrorMsg.style, {color: 'red', fontSize: '13px', margin: '0 auto 0 0', flexGrow: 1, alignSelf: 'center'});
            buttonContainer.insertBefore(sendErrorMsg, acknowledgeButton);

        } else {
            const okButton = createModalButton('OK', 'primary');
            okButton.onclick = closeAlertModalAction;
            buttonContainer.appendChild(okButton);
        }

        modalBox.appendChild(buttonContainer);
        modalBox.dataset.alerts = JSON.stringify(alertsDisplayedInModal.map(a => ({ type: a.type, rowId: a.rowId })));


        overlay.appendChild(modalBox);
        document.body.appendChild(overlay);

        setTimeout(() => {
            overlay.style.opacity = '1';
            modalBox.style.opacity = '1';
            modalBox.style.transform = 'translateY(0) scale(1)';
            logAlertDebug("Alert modal fade-in and animation triggered.");
        }, 10);

        overlay.addEventListener('click', (event) => {
            if (event.target === overlay) {
                closeAlertModalAction();
            }
        });
        const escapeKeyListener = (event) => {
            if (event.key === 'Escape' && document.body.contains(overlay)) {
                closeAlertModalAction();
                document.removeEventListener('keydown', escapeKeyListener);
            }
        };
        document.addEventListener('keydown', escapeKeyListener);

    }

    function scrollToRowNearest(row) {
        if (!row) return false;
        let rowToScroll = row;
        if (row.closest('.ag-center-cols-container')) {
            const rowIndex = row.getAttribute('row-index');
            if (rowIndex) {
                const pinnedRow = document.querySelector(`.ag-pinned-left-cols-container .ag-row[row-index="${rowIndex}"]`);
                if (pinnedRow) rowToScroll = pinnedRow;
            }
        }

        document.documentElement.style.scrollBehavior = 'smooth';
        rowToScroll.scrollIntoView({ behavior: 'auto', block: 'center' });
        setTimeout(() => document.documentElement.style.scrollBehavior = 'auto', 500);
        return true;
    }

    async function getPatientDataWithRetry(selectors, maxWaitTime = 2000, checkInterval = 100) {
        let elapsedTime = 0;
        let patientData = { patientName: 'N/A', patientId: 'N/A', patientLocation: 'N/A', sampleBarcode: 'N/A' };

        function elementsPresent() {
            return document.querySelector(selectors.name) &&
                   document.querySelector(selectors.mrn) &&
                   document.querySelector(selectors.location);
        }

        while (elapsedTime < maxWaitTime) {
            if (elementsPresent()) {
                logAlertDebug("Essential patient data elements detected.");
                break;
            }
            await new Promise(resolve => setTimeout(resolve, checkInterval));
            elapsedTime += checkInterval;
            if (elapsedTime % 500 === 0) {
                logAlertDebug(`Waiting for patient data elements... ${elapsedTime}ms`);
            }
        }

        if (!elementsPresent() && elapsedTime >= maxWaitTime) {
            logAlertDebug(`Essential patient data elements not found after ${maxWaitTime}ms. Using N/A for some fields.`);
        }

        const nameEl = document.querySelector(selectors.name);
        patientData.patientName = nameEl ? nameEl.textContent.trim().replace(/\s+/g, ' ') : 'N/A';

        const mrnEl = document.querySelector(selectors.mrn);
        patientData.patientId = mrnEl ? mrnEl.textContent.trim() : 'N/A';

        const locElement = document.querySelector(selectors.location);
        patientData.patientLocation = locElement ? (locElement.title?.trim() || locElement.textContent?.trim()) : 'N/A';
        if (patientData.patientLocation === '') patientData.patientLocation = 'N/A';

        const barcodeEl = document.querySelector(selectors.barcode);
        patientData.sampleBarcode = barcodeEl ? barcodeEl.textContent.trim() : 'N/A';
        if (patientData.sampleBarcode === '') patientData.sampleBarcode = 'N/A';

        return patientData;
    }


    async function checkForIssues() {
        if (!isScanningActive || isAlertModalOpen) return false;

        const patientDataSelectors = {
            name: CONFIG_ALERTS.PATIENT_NAME_SELECTOR,
            mrn: CONFIG_ALERTS.PATIENT_MRN_SELECTOR,
            location: CONFIG_ALERTS.PATIENT_LOCATION_SELECTOR,
            barcode: CONFIG_ALERTS.SAMPLE_BARCODE_SELECTOR
        };

        const patientDataSource = await getPatientDataWithRetry(patientDataSelectors);

        const patientName = patientDataSource.patientName;
        const patientId = patientDataSource.patientId;
        const patientLocation = patientDataSource.patientLocation;
        const sampleBarcode = patientDataSource.sampleBarcode;

        const userIdElement = document.querySelector(CONFIG_ALERTS.USER_NAME_SELECTOR);
        const currentUserId = userIdElement ? userIdElement.textContent.trim() : 'N/A';

        const centerRows = document.querySelectorAll('.ag-center-cols-container .ag-row');
        const pinnedRows = document.querySelectorAll('.ag-pinned-left-cols-container .ag-row');
        const potentialAlerts = [];

        centerRows.forEach((centerRow, index) => {
            const pinnedRow = pinnedRows[index];
            if (!centerRow || !pinnedRow || centerRow.offsetParent === null) return;

            const testDescElement = pinnedRow.querySelector('div[col-id="TestDesc"]');
            const testDesc = testDescElement?.textContent?.trim() || 'Unknown Test';
            const resultDiv = centerRow.querySelector(CONFIG_ALERTS.RESULT_CELL_SELECTOR);
            const flagSpan = centerRow.querySelector(CONFIG_ALERTS.CRITICAL_FLAG_SELECTOR);
            const uomCell = centerRow.querySelector(CONFIG_ALERTS.UOM_CELL_SELECTOR);

            const baseRowIdentifier = centerRow.getAttribute('row-id') || centerRow.getAttribute('row-index') || `idx_${index}`;
            const resultText = resultDiv?.textContent?.trim() || '';
            const uomText = uomCell?.textContent?.trim() || '';
            const normalizedResultText = resultText.toLowerCase().replace(/[- ]/g, '');
            const flagText = flagSpan?.textContent?.trim()?.toUpperCase() || '';

            const isNoResult = ["noresult", "noxresult", "x-noresult"].includes(normalizedResultText);
            const isDilution = resultText.includes(">");
            const isCritical = (flagSpan && (flagText === "CL" || flagText === "CH"));

            let alertType = null;
            let alertMessage = "";
            let alertComment = null;

            if (isCritical) {
                alertType = "critical";
                alertMessage = `CRITICAL ${flagText === "CL" ? "LOW" : "HIGH"} RESULT!`;
            } else if (isNoResult) {
                alertType = "noresult";
                alertMessage = CONFIG_ALERTS.NO_RESULT_MESSAGE;
            } else if (isDilution) {
                alertType = "greaterThan";
                alertMessage = CONFIG_ALERTS.DILUTION_MESSAGE;
                alertComment = "Sample needs dilution";
            }

            if (alertType) {
                if (!hasAlreadyNotified(alertType, baseRowIdentifier)) {
                    potentialAlerts.push({
                        rowElement: centerRow, type: alertType,
                        rowId: `${alertType}_${baseRowIdentifier}`,
                        baseIdentifier: baseRowIdentifier,
                        message: alertMessage,
                        comment: alertComment,
                        testName: testDesc, result: resultText, uom: uomText, flag: flagText
                    });
                }
                applyFlashingEffect(centerRow);
                applyFlashingEffect(pinnedRow);
            } else {
                stopFlashingEffect(centerRow);
                stopFlashingEffect(pinnedRow);
            }
        });

        if (potentialAlerts.length > 0 && !isAlertModalOpen && !document.getElementById('custom-alert-modal-overlay')) {
            const firstSignificantAlert =
                potentialAlerts.find(a => a.type === 'critical') ||
                potentialAlerts.find(a => a.type === 'noresult') ||
                potentialAlerts.find(a => a.type === 'greaterThan') ||
                potentialAlerts[0];

            scrollToRowNearest(firstSignificantAlert.rowElement);

            const modalData = {
                alertsToList: potentialAlerts.map(a => ({
                    testName: a.testName, result: a.result, uom: a.uom, flag: a.flag, type: a.type,
                    rowId: a.rowId,
                    comment: a.comment
                })),
                overallSeverity: firstSignificantAlert.type,
                primaryMessage: firstSignificantAlert.message,
                patientName: patientName,
                patientId: patientId,
                patientLocation: patientLocation,
                sampleBarcode: sampleBarcode,
                userId: currentUserId
            };
            createCustomAlert(modalData, potentialAlerts);

            // Pause scanner after showing alerts for this patient
            logAlertDebug("Alerts shown for patient. Pausing scanner.");
            if (window.stopAlertsScanner) window.stopAlertsScanner();

            return true;
        } else if (potentialAlerts.length === 0) {
            document.querySelectorAll('.ag-center-cols-container .ag-row[data-flashing="true"]').forEach(stopFlashingEffect);
            document.querySelectorAll('.ag-pinned-left-cols-container .ag-row[data-flashing="true"]').forEach(stopFlashingEffect);
        }
        return false;
    }

    function startAlertsScannerInternal() {
        logAlertDebug("startAlertsScannerInternal called.");
        if (!isScanningActive && issueScanIntervalId === null) {
            logAlertDebug("Alert Scanner starting/resuming...");
            isScanningActive = true;
            if (issueScanIntervalId) clearInterval(issueScanIntervalId);
            checkForIssues();
            issueScanIntervalId = setInterval(checkForIssues, CONFIG_ALERTS.SCAN_INTERVAL);
            logAlertDebug(`Alert Scanner interval set: ${issueScanIntervalId}`);
        } else {
            logAlertDebug(`Alert Scanner already active or interval set. Active: ${isScanningActive}, IntervalID: ${issueScanIntervalId}`);
        }
    }

    function stopAlertsScannerInternal() {
        logAlertDebug("stopAlertsScannerInternal called (pause).");
        if (isScanningActive) {
            logAlertDebug("Alert Scanner pausing...");
            clearInterval(issueScanIntervalId);
            issueScanIntervalId = null; // Keep isScanningActive true, but clear interval
            // Do not set isScanningActive = false here if we want it to resume on new patient
            // Flashing rows will stop naturally or when modal is closed / page changes.
            logAlertDebug("Scanner interval cleared for pause. isScanningActive remains true.");
        } else {
             logAlertDebug("Alert Scanner not active, no action taken by stop/pause.");
        }
    }

    window.startAlertsScanner = startAlertsScannerInternal;
    window.stopAlertsScanner = stopAlertsScannerInternal; // This is now more of a "pause"

    let previousObservedUrl = window.location.href;
    let currentPatientProcessed = false; // Flag to manage scanner pause/resume per patient

    const pageUrlObserver = new MutationObserver(() => {
        const newObservedUrl = window.location.href;

        if (newObservedUrl !== previousObservedUrl) {
            logDebugMain(`URL changed from: ${previousObservedUrl} to: ${newObservedUrl}.`);
            currentPatientProcessed = false; // Reset flag for new URL

            const wasOnEditPage = previousObservedUrl.startsWith(CONFIG_MAIN.URLS.EDIT_PAGE_PREFIX);
            const nowOnEditPage = newObservedUrl.startsWith(CONFIG_MAIN.URLS.EDIT_PAGE_PREFIX);

            if (wasOnEditPage && !nowOnEditPage) { // Navigated away from an edit page
                logDebugMain(`Left an edit page (${previousObservedUrl}). Fully stopping scanner.`);
                isScanningActive = false; // Set to false to truly stop
                if (window.stopAlertsScanner) window.stopAlertsScanner();
                // Clear session storage flags for the *specific page* being left
                 try {
                    const oldUrlObject = new URL(previousObservedUrl, window.location.origin);
                    const oldPageKeyPrefix = `labAlertNotified_${oldUrlObject.pathname}${oldUrlObject.hash}_`;
                    logAlertDebug(`Attempting to clear session flags with prefix: "${oldPageKeyPrefix}"`);
                    Object.keys(sessionStorage)
                        .filter(key => key.startsWith(oldPageKeyPrefix))
                        .forEach(key => {
                            sessionStorage.removeItem(key);
                            logAlertDebug(`Cleared session flag: ${key}`);
                        });
                    logAlertDebug(`Session flags for prefix "${oldPageKeyPrefix}" cleared.`);
                } catch (e) {
                    logAlertError(`Error processing old URL (${previousObservedUrl}) for flag clearing: ${e}`);
                }
            }

            if (nowOnEditPage) {
                logDebugMain(`Now on a new edit page (${newObservedUrl}). Setting up features and scanner.`);
                hasScrolledToOrderedRow = false;
                isAlertModalOpen = false;
                setTimeout(() => {
                    // Only start scanner if not already paused for this new patient (currentPatientProcessed is false)
                    if (!currentPatientProcessed && window.startAlertsScanner) {
                         window.startAlertsScanner();
                    }
                    addButtons();
                    addFontAwesome();
                    monitorOrderedStatus();
                }, 700);
            } else {
                logDebugMain(`Not on an edit page (${newObservedUrl}). Ensuring UI cleanup.`);
                const customButtons = document.getElementById('custom-script-buttons');
                if (customButtons) customButtons.remove();
                // If truly leaving edit pages, ensure scanner is fully stopped
                if (isScanningActive && window.stopAlertsScanner) {
                    isScanningActive = false; // Ensure it's marked as not active
                    window.stopAlertsScanner();
                }
            }
            previousObservedUrl = newObservedUrl;
        } else {
            const stillOnEditPage = newObservedUrl.startsWith(CONFIG_MAIN.URLS.EDIT_PAGE_PREFIX);
            if (stillOnEditPage) {
                if (!document.getElementById('custom-script-buttons')) {
                    logDebugMain("Buttons missing on edit page (no URL change), re-adding.");
                    addButtons();
                }
                addFontAwesome();
                // If scanner was paused and it's still the same patient page, it should remain paused.
                // If it's a page refresh for a new patient (somehow URL didn't change initially),
                // startAlertsScanner will handle it if currentPatientProcessed is false.
                if (!isScanningActive && !currentPatientProcessed && window.startAlertsScanner) {
                     // This case might be rare, but handles if scanner was stopped and needs restart on same URL
                     logDebugMain("Scanner not active on current edit page, attempting restart.");
                     window.startAlertsScanner();
                }
            }
        }
    });
    pageUrlObserver.observe(document.body, { childList: true, subtree: true });

    if (isCorrectPage()) {
        logDebugMain("Initial page load on correct page. Setting up features.");
        currentPatientProcessed = false; // Reset for initial load
        setTimeout(() => {
            if (window.startAlertsScanner) window.startAlertsScanner();
            addFontAwesome();
            addButtons();
            monitorOrderedStatus();
        }, 700);
    } else {
        logDebugMain("Initial page load not on correct page. No features started.");
    }

    setInterval(checkUrlAndTriggerClickForUndefined, CONFIG_MAIN.CHECK_INTERVALS.UNDEFINED_URL);
    setInterval(checkForDisabledButtons, CONFIG_MAIN.CHECK_INTERVALS.DISABLED_BTN_CHECK);

})();


// User's SAMPLES COUNT Logic (IIFE) - Integrated and Styles Updated
(function () {
    'use strict';

    function createCounterElement(id) {
        const wrapper = document.createElement('span');
        wrapper.id = id;
        Object.assign(wrapper.style, {
            display: 'inline-flex',
            alignItems: 'center',
            padding: '6px 12px',
            backgroundColor: '#e9ecef',
            borderRadius: '20px',
            boxShadow: '0 2px 5px rgba(0,0,0,0.1)',
            marginRight: 'auto',
            marginLeft: '10px',
            fontSize: '14px',
            fontWeight: '500',
            color: '#495057',
            verticalAlign: 'middle'
        });

        const label = document.createElement('span');
        label.textContent = 'SAMPLES COUNT: ';
        label.style.marginRight = '8px';

        const badge = document.createElement('span');
        badge.className = 'sample-count-badge';
        badge.textContent = '0';
        Object.assign(badge.style, {
            backgroundColor: '#6c757d',
            color: '#ffffff',
            padding: '4px 10px',
            borderRadius: '12px',
            fontSize: '14px',
            fontWeight: 'bold',
            minWidth: '26px',
            textAlign: 'center',
            lineHeight: '1'
        });

        wrapper.appendChild(label);
        wrapper.appendChild(badge);
        return wrapper;
    }

    function updateSpecificCounter(modalElement, counterElement, inputSelector) {
        const badge = counterElement.querySelector('.sample-count-badge');
        if (!badge || !modalElement || !document.body.contains(modalElement) || !modalElement.contains(counterElement)) {
            return;
        }
        const inputs = modalElement.querySelectorAll(inputSelector);
        const count = inputs.length;
        badge.textContent = count;
        badge.style.backgroundColor = count > 0 ? '#28a745' : '#6c757d';
    }

    function observeModals() {
        const activeModalIntervals = new Map();

        const observer = new MutationObserver(() => {
            const originalModal = document.querySelector('modal-container.show');

            // Selectors for WB Modal (Original Modal)
            const wbModalFooterSelector = '.modal-footer'; // Target the footer
            // **IMPORTANT**: Verify this input selector for the WB modal.
            // Inspect the input fields in the WB modal that should be counted.
            const wbInputSelectorForCount = 'td input[formcontrolname="PatientID"]';

            let originalModalFooter = null;

            if (originalModal) {
                originalModalFooter = originalModal.querySelector(wbModalFooterSelector);
            }

            const originalCounterId = 'inline-patientid-counter-original-wb'; // Unique ID for WB counter

            // Check if this is the "WB" modal (heuristic: contains app-sample-receive but not app-sample-receive-receptionist)
            const isLikelyWBModal = originalModal && originalModal.querySelector('app-sample-receive') && !originalModal.querySelector('app-sample-receive-receptionist');

            if (isLikelyWBModal && originalModalFooter && !originalModalFooter.querySelector('#' + originalCounterId)) {
                console.debug("[SAMPLES COUNT WB DEBUG] WB Modal identified and Footer found:", originalModalFooter);

                const counter = createCounterElement(originalCounterId);
                originalModalFooter.insertBefore(counter, originalModalFooter.firstChild); // Insert at the beginning of the footer
                console.log("[SAMPLES COUNT] Original modal (WB) counter element created and inserted into footer.");

                const interval = setInterval(() => {
                    const currentFooterForCheck = document.getElementById(originalCounterId)?.closest(wbModalFooterSelector) || (originalModal ? originalModal.querySelector(wbModalFooterSelector) : null);
                    if (!document.body.contains(originalModal) || (currentFooterForCheck && !currentFooterForCheck.contains(counter))) {
                        clearInterval(activeModalIntervals.get(originalModal));
                        activeModalIntervals.delete(originalModal);
                        const existingCounter = document.getElementById(originalCounterId);
                        if (existingCounter) existingCounter.remove();
                        console.log("[SAMPLES COUNT] Original modal (WB) counter cleaned up.");
                        return;
                    }
                    updateSpecificCounter(originalModal, counter, wbInputSelectorForCount);
                }, 500);
                activeModalIntervals.set(originalModal, interval);

            } else if (isLikelyWBModal && !originalModalFooter && !document.getElementById(originalCounterId)) {
                console.warn("[SAMPLES COUNT WB DEBUG] WB Modal found, but Footer NOT identified using selector '" + wbModalFooterSelector + "'. Counter cannot be placed.");
                console.debug("[SAMPLES COUNT WB DEBUG] WB Modal (for context):", originalModal);
            }


            // --- Observe New Modal (Sample Receive by CPL/Receptionist) --- (This part seems to be working)
            const newModalApp = document.querySelector('.modal-dialog.modal-xlg-fullwidth app-sample-receive-receptionist');
            const newModal = newModalApp ? newModalApp.closest('.modal-dialog') : null;
            const newModalFooter = newModalApp ? newModalApp.querySelector('.modal-footer') : null;
            const newCounterId = 'inline-patientid-counter-new-cpl'; // Unique ID for CPL counter

            if (newModal && newModalFooter && !newModalFooter.querySelector('#' + newCounterId)) {
                const counter = createCounterElement(newCounterId);
                newModalFooter.insertBefore(counter, newModalFooter.firstChild);
                console.debug("[SAMPLES COUNT] New modal (CPL) counter created.");

                const interval = setInterval(() => {
                    if (!document.body.contains(newModal) || !newModalFooter.contains(counter)) {
                        clearInterval(activeModalIntervals.get(newModal));
                        activeModalIntervals.delete(newModal);
                        const existingCounter = document.getElementById(newCounterId);
                        if (existingCounter) existingCounter.remove();
                        console.debug("[SAMPLES COUNT] New modal (CPL) counter cleaned up.");
                    } else {
                        const inputSelector = 'tbody[formarrayname="TubeTypeList"] input[formcontrolname="PatientID"]';
                        updateSpecificCounter(newModal, counter, inputSelector);
                    }
                }, 500);
                activeModalIntervals.set(newModal, interval);
            }
        });
        observer.observe(document.body, { childList: true, subtree: true });
        console.debug("[SAMPLES COUNT] Modal observer started.");
    }

    window.addEventListener('load', () => {
        console.debug("[SAMPLES COUNT] Window loaded, initializing modal observer.");
        observeModals();
    });
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址