KAAUH Lab Suite - Verification, Alerts & Enhancements

Combines verification buttons (F7/F8), dynamic alerts (>, NO RESULT, X-NORESULT, CL/CH) showing only the *last* alert per scan, checkbox automation, toggle back-nav, and improved scrolling.

当前为 2025-05-10 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

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

(function() {
    'use strict';

    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, ''); // optional
            }
        };
        simplifySpan('span.to-do');
        simplifySpan('span.pending-orders');
    }

    // Debounce function to limit how often applySingleLineStyles is called
    function debounce(func, wait) {
        let timeout;
        return function(...args) {
            clearTimeout(timeout);
            timeout = setTimeout(() => func.apply(this, args), wait);
        };
    }

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

    // Initial run
    applySingleLineStyles();
})();

(function () {
  'use strict';

  /*** CONFIGURATION (Main Script) ***/
  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',
      }
    },
    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'
    ]
  };

  /*** STATE FLAGS ***/
  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;

  /*** UTILITY FUNCTIONS ***/
  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();
  }

  const addFontAwesome = () => {
    if (!document.querySelector('link[href*="font-awesome"]')) {
      logDebugMain("Injecting Font Awesome.");
      const link = document.createElement('link');
      link.rel = 'stylesheet';
      link.href = 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css';
      document.head.appendChild(link);
    }
  };

  /*** BUTTON + ICON CREATORS ***/
  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" aria-hidden="true" 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) {
    logDebugMain(`Toggling auto-back for ${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;
  }

  /*** BUTTONS INJECTION ***/
  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;
    }

    logDebugMain("Adding custom buttons to page.");
    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', () => {
      logDebugMain('VERIFY1 (F7) clicked.');
      verify1Clicked = true;
      verify2Clicked = false;
      checkAllVisibleBoxesWithoutDuplicates();
      setTimeout(clickCompleteTechnicalVerificationButton, 500);
    }, 'verify1-btn');
    const verify2Button = createVerifyButton('VERIFY2 (F8)', 'btn btn-primary btn-sm', () => {
      logDebugMain('VERIFY2 (F8) clicked.');
      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);
    logDebugMain("Custom buttons added.");
  }

  /*** CORE ACTIONS ***/
  function checkAllVisibleBoxesWithoutDuplicates() {
    logDebugMain("Checking all visible boxes...");
    const selectedTests = new Set();
    const boxes = document.querySelectorAll(CONFIG_MAIN.SELECTORS.UNCHECKED_BOX);
    let count = 0;
    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();
                count++;
             } else if (selectedTests.has(descText)) {
             } else if (isExcluded) {
             }
        } else {
            logDebugMain("Found checkbox in row without valid test description.");
        }
      }
    });
    logDebugMain(`Clicked ${count} checkboxes.`);
  }

  const clickButton = (selector, callback) => {
      const btn = document.querySelector(selector);
      if (btn && !btn.disabled) {
          logDebugMain(`Clicking button: ${selector}`);
          btn.click();
          if (callback) {
              setTimeout(callback, 500);
          }
          return true;
      } else if (btn && btn.disabled) {
          logDebugMain(`Button found but disabled: ${selector}`);
          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.`);
      } else {
          logDebugMain(`Button not found: ${selector}`);
      }
      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) {
      logDebugMain("VERIFY1 button is disabled");
      showDisabledButtonAlert("Verification is disabled. Make sure tests not already verified.");
    }

    if (verify2Btn && verify2Btn.disabled) {
      logDebugMain("VERIFY2 button is disabled");
      showDisabledButtonAlert("Verification is disabled. Make sure tests not already verified.");
    }
  }

  function checkUrlAndTriggerClickForUndefined() {
    if (window.location.href.endsWith('/undefined')) {
        logDebugMain("Detected '/undefined' URL. Attempting to close toast.");
        const closeBtn = document.querySelector(`${CONFIG_MAIN.SELECTORS.TOAST.CONTAINER} ${CONFIG_MAIN.SELECTORS.TOAST.CLOSE_BTN}`);
        if (closeBtn) {
            closeBtn.click();
            logDebugMain("Clicked toast close button.");
        }
    }
  }

  function monitorOrderedStatus() {
    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')) {
            logDebugMain("Found 'Ordered' status, updating VERIFY1 button.");
            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) {
          logDebugMain("Scrolling to first 'Ordered' row.");
          document.documentElement.style.scrollBehavior = 'smooth';
          firstOrderedRow.scrollIntoView({ behavior: 'auto', block: 'center' });
          hasScrolledToOrderedRow = true;
          setTimeout(() => {
            document.documentElement.style.scrollBehavior = 'auto';
            logDebugMain("Scroll complete, scroll behavior reset.");
          }, 1000);
        }
      } else {
         if (btn.classList.contains('btn-warning')) {
            logDebugMain("No 'Ordered' status found, resetting VERIFY1 button.");
            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);

  /*** TOAST OBSERVER (for Auto-Back) ***/
  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) {
                logDebugMain("Success toast detected.");
                successToast.addEventListener('click', () => {
                    logDebugMain("Success toast clicked.");
                    if ((verify1Clicked && verify1Toggle) || (verify2Clicked && verify2Toggle)) {
                        logDebugMain("Auto-back triggered.");
                        window.history.back();
                    }
                    verify1Clicked = false;
                    verify2Clicked = false;
                 }, { once: true });
            }
        }
      });
    });
  });
  toastObserver.observe(document.body, { childList: true, subtree: true });

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

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

  /*** MAIN PAGE WATCHER (for SPA Navigation) ***/
  const pageObserver = new MutationObserver(() => {
    if (isCorrectPage()) {
      addFontAwesome();
      if (!document.getElementById('custom-script-buttons')) {
           addButtons();
           hasScrolledToOrderedRow = false;
      }
    } else {
      const customButtons = document.getElementById('custom-script-buttons');
      if (customButtons) {
        logDebugMain("Navigated away from edit page, removing custom buttons.");
        customButtons.remove();
      }
    }
  });
  pageObserver.observe(document.body, { childList: true, subtree: true });

  /*** INITIAL BOOTSTRAP ***/
  logDebugMain("KAAUH Lab Suite script initializing...");
  if (isCorrectPage()) {
    addFontAwesome();
    addButtons();
  }
  setInterval(checkUrlAndTriggerClickForUndefined, CONFIG_MAIN.CHECK_INTERVALS.UNDEFINED_URL);
  setInterval(checkForDisabledButtons, CONFIG_MAIN.CHECK_INTERVALS.DISABLED_BTN_CHECK);
})();


// --- Start of Integrated Alerts Scanner Code ---
(function () {
    'use strict';

    const CONFIG_ALERTS = {
        SCAN_INTERVAL: 150,
        FLASH_COLOR: "pink",
        FLASH_INTERVAL: 500,
        RESULT_CELL_SELECTOR: 'div[role="gridcell"][col-id="TestResult"] app-result-value-render div',
        CRITICAL_FLAG_SELECTOR: 'div[role="gridcell"][col-id="LTFlag"] app-ref-high-low div span.critial-alret-indication',
        CHECKBOX_PARENT_ROW_SELECTOR: '.ag-row',
        NO_RESULT_MESSAGE: "NO-RESULT DETECTED!!",
        SOUND_URL: 'https://notificationsounds.com/storage/sounds/download/48088/file-sounds-916-light.mp3',
    };

    let isScanningActive = false;
    let issueScanIntervalId = null;
    let alertAudio = null;

    function logDebugAlerts(message) {
        console.log(`[Alerts Scanner] ${message}`);
    }

    function playAlertSound() {
        if (!alertAudio) {
            if (!CONFIG_ALERTS.SOUND_URL) {
                 console.error("[Alerts Scanner] CONFIG_ALERTS.SOUND_URL is not defined!");
                 return;
            }
            alertAudio = new Audio(CONFIG_ALERTS.SOUND_URL);
            alertAudio.loop = true;
        }
        alertAudio.pause();
        alertAudio.currentTime = 0;
        alertAudio.play().catch(e => console.warn("[Alerts Scanner] Sound playback blocked by browser:", e));
        setTimeout(() => {
            if (alertAudio) {
                alertAudio.pause();
                alertAudio.currentTime = 0;
            }
        }, 3000);
    }

    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;
    }

    function stopFlashingEffect(row) {
        if (row && row.dataset.flashing === 'true') {
            row.dataset.flashing = 'false';
        }
    }

    function getNotificationSessionKey(type, identifier = 'general') {
        return `labAlertNotified_${window.location.pathname}_${type}_${identifier}`;
    }

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

    function setNotificationFlag(type, identifier = 'general') {
        sessionStorage.setItem(getNotificationSessionKey(type, identifier), 'true');
    }

    function createCustomAlert(message, callback) {
        if (document.getElementById('custom-alert-modal-overlay')) return;
        const overlay = document.createElement('div');
        overlay.id = 'custom-alert-modal-overlay';
        Object.assign(overlay.style, {
            position: 'fixed', top: 0, left: 0, width: '100vw', height: '100vh',
            backgroundColor: 'rgba(0, 0, 0, 0.5)', display: 'flex',
            justifyContent: 'center', alignItems: 'center', zIndex: 9999
        });
        const modal = document.createElement('div');
        Object.assign(modal.style, {
            backgroundColor: '#fff', borderRadius: '12px', padding: '24px 28px',
            boxShadow: '0 8px 20px rgba(0, 0, 0, 0.15)', width: 'auto', minWidth: '300px',
            maxWidth: '90%', textAlign: 'left', position: 'relative',
            fontFamily: 'Segoe UI, sans-serif', animation: 'fadeIn 0.2s ease-out', overflow: 'hidden'
        });
        const closeBtn = document.createElement('span');
        closeBtn.innerHTML = '&times;';
        Object.assign(closeBtn.style, {
            position: 'absolute', top: '12px', right: '16px', fontSize: '20px',
            color: '#999', cursor: 'pointer'
        });
        closeBtn.onclick = () => {
            document.body.removeChild(overlay);
            if (callback) callback();
        };

        const title = document.createElement('h2');
        title.textContent = 'Lab Alert';
        Object.assign(title.style, {
            margin: '0 0 12px', fontSize: '20px', color: '#d9534f'
        });
        const msg = document.createElement('div');
        msg.innerHTML = message.split('\n').map(testLine => {
            const parts = testLine.split(' ');
            if (parts.length > 1) {
                const trigger = parts.pop();
                const restOfLine = parts.join(' ');
                if (trigger.length <= 3 && trigger.match(/^[A-Z]{1,2}$/)) {
                    return `<span style="display: inline-block; margin-right: 10px; font-weight: bold;">${restOfLine} <span style="color: red;">${trigger}</span></span>`;
                } else {
                    return `<span style="display: inline-block; margin-right: 10px; font-weight: bold;">${testLine}</span>`;
                }
            } else {
                return `<span style="display: inline-block; margin-right: 10px; font-weight: bold;">${testLine}</span>`;
            }
        }).join('<br>');
        Object.assign(msg.style, {
            fontSize: '16px', color: '#333', marginBottom: '20px', whiteSpace: 'normal',
            wordBreak: 'break-word', maxWidth: '100%', textAlign: 'left', overflowX: 'auto'
        });

        const okBtn = document.createElement('button');
        okBtn.textContent = 'OK';
        Object.assign(okBtn.style, {
            padding: '10px 20px', fontSize: '16px', backgroundColor: '#5cb85c',
            color: '#fff', border: 'none', borderRadius: '6px', cursor: 'pointer', width: '100%'
        });
        okBtn.onclick = () => {
            document.body.removeChild(overlay);
            if (callback) callback();
        };

        modal.append(closeBtn, title, msg, okBtn);
        overlay.appendChild(modal);
        document.body.appendChild(overlay);
        okBtn.focus();
    }

    function scrollToRowNearest(row) {
        if (!row) return false;
        document.documentElement.style.scrollBehavior = 'smooth';
        row.scrollIntoView({ behavior: 'auto', block: 'center' });
        setTimeout(() => document.documentElement.style.scrollBehavior = 'auto', 500);
        return true;
    }

    function checkForIssues() {
        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) return;

            const resultDiv = centerRow.querySelector(CONFIG_ALERTS.RESULT_CELL_SELECTOR);
            const flagSpan = centerRow.querySelector(CONFIG_ALERTS.CRITICAL_FLAG_SELECTOR);
            const testDesc = pinnedRow.querySelector('div[col-id="TestDesc"]')?.textContent.trim() || 'Unknown Test';
            const rowId = centerRow.getAttribute('row-id') || `${index}`;
            const resultText = resultDiv?.textContent.trim().toLowerCase() || '';
            const flagText = flagSpan?.textContent.trim().toUpperCase() || '';

            const isNoResult = ["no result", "no-xresult", "x-noresult"].includes(resultText);
            const isDilution = resultText.includes(">");
            const isCritical = (flagText === "CL" || flagText === "CH");

            if (!isCritical && centerRow.dataset.flashing === 'true') {
                stopFlashingEffect(centerRow);
            }

            if (isCritical) {
                const key = `critical_${rowId}`;
                if (!hasAlreadyNotified("critical", key)) {
                    potentialAlerts.push({
                        rowElement: centerRow, type: "critical", rowId: key,
                        message: `CRITICAL ${flagText === "CL" ? "LOW" : "HIGH"} RESULT DETECTED !!`,
                        testName: testDesc, result: resultText, flag: flagText
                    });
                    setNotificationFlag("critical", key);
                }
                applyFlashingEffect(centerRow);
            }

            if (isNoResult && !isCritical) {
                 const key = `noresult_${rowId}`;
                 if (!hasAlreadyNotified("noresult", key)) {
                    potentialAlerts.push({
                        rowElement: centerRow, type: "noresult", rowId: key,
                        message: CONFIG_ALERTS.NO_RESULT_MESSAGE,
                        testName: testDesc, result: resultText, flag: ''
                    });
                    setNotificationFlag("noresult", key);
                }
                applyFlashingEffect(centerRow);
            }

             if (isDilution && !isCritical) {
                 const key = `dilution_${rowId}`;
                 if (!hasAlreadyNotified("greaterThan", key)) {
                    potentialAlerts.push({
                        rowElement: centerRow, type: "greaterThan", rowId: key,
                        message: "Dilution is required for this test (> detected)!",
                        testName: testDesc, result: resultText, flag: ''
                    });
                    setNotificationFlag("greaterThan", key);
                }
                applyFlashingEffect(centerRow);
            }
        });

        if (potentialAlerts.length > 0) {
            const firstAlert = potentialAlerts.find(alert => alert.type === 'critical') || potentialAlerts[0];
            playAlertSound();
            scrollToRowNearest(firstAlert.rowElement);
            let messageText;
            if (firstAlert.type === 'critical') {
                 messageText = `${firstAlert.message}\n\n🔴 ${firstAlert.testName} = ${firstAlert.result} ${firstAlert.flag}`;
            } else {
                 messageText = `${firstAlert.message}\n\n🔴 ${firstAlert.testName} =\n${firstAlert.result}`;
            }
            createCustomAlert(messageText);
            potentialAlerts.forEach(alert => applyFlashingEffect(alert.rowElement));
            return true;
        } else {
            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() {
        if (!isScanningActive) {
            logDebugAlerts("Starting scan for alerts...");
            isScanningActive = true;
            checkForIssues();
            issueScanIntervalId = setInterval(checkForIssues, CONFIG_ALERTS.SCAN_INTERVAL);
        } else {
            logDebugAlerts("Scanner is already active (internal call).");
        }
    }

    function stopAlertsScannerInternal() {
        if (isScanningActive) {
            logDebugAlerts("Stopping scan for alerts (internal call).");
            clearInterval(issueScanIntervalId);
            issueScanIntervalId = null;
            isScanningActive = false;
            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);
             if (alertAudio && !alertAudio.paused) {
                 alertAudio.pause();
                 alertAudio.currentTime = 0;
             }
        } else {
            logDebugAlerts("Scanner is not active (internal call).");
        }
    }

    window.startAlertsScanner = function() { // Exposed to window for external calls if needed
        startAlertsScannerInternal();
    };

    window.stopAlertsScanner = function() { // Exposed to window for external calls if needed
        stopAlertsScannerInternal();
    };

    // Observer to automatically start/stop scanning when results grid appears/disappears
    const gridObserver = new MutationObserver(() => {
        const resultsGridExists = document.querySelector(CONFIG_ALERTS.RESULT_CELL_SELECTOR) || document.querySelector(CONFIG_ALERTS.CRITICAL_FLAG_SELECTOR);
        if (resultsGridExists) {
            if (!isScanningActive) {
                setTimeout(startAlertsScannerInternal, 300); // Use internal function
            }
        } else {
            if (isScanningActive) {
                stopAlertsScannerInternal(); // Use internal function
            }
        }
    });
    gridObserver.observe(document.body, { childList: true, subtree: true });

    // Initial check in case the grid is already present on script load
    if (document.querySelector(CONFIG_ALERTS.RESULT_CELL_SELECTOR) || document.querySelector(CONFIG_ALERTS.CRITICAL_FLAG_SELECTOR)) {
        startAlertsScannerInternal(); // Use internal function
    }
})();
// --- End of Integrated Alerts Scanner Code ---

(function () {
    'use strict';

    const counterId = 'inline-patientid-counter';

    function createInlineCounter(container) {
        if (document.getElementById(counterId)) return;

        const wrapper = document.createElement('span');
        wrapper.id = counterId;
        wrapper.style.marginLeft = '12px';
        wrapper.style.fontSize = '18px';
        wrapper.style.fontWeight = 'bold';
        wrapper.style.display = 'inline-flex';
        wrapper.style.alignItems = 'center';

        const label = document.createElement('span');
        label.textContent = 'SAMPLES COUNT: ';
        label.style.marginRight = '6px';
        label.style.color = '#333';

        const badge = document.createElement('span');
        badge.className = 'sample-count-badge';
        badge.textContent = '0';
        badge.style.backgroundColor = '#6c757d';
        badge.style.color = '#fff';
        badge.style.padding = '2px 8px';
        badge.style.borderRadius = '12px';
        badge.style.fontSize = '18px';
        badge.style.minWidth = '24px';
        badge.style.textAlign = 'center';

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

    function updateCounter() {
        const badge = document.querySelector('#' + counterId + ' .sample-count-badge');
        if (!badge) return;

        const inputs = document.querySelectorAll(
            'modal-container.show td input[formcontrolname="PatientID"]'
        );
        const count = inputs.length;
        badge.textContent = count;
        badge.style.backgroundColor = count > 0 ? '#28a745' : '#6c757d';
    }

    function observeModal() {
        const observer = new MutationObserver(() => {
            const closeBtn = document.getElementById('closebtn-smplrecieve');
            const modal = document.querySelector('modal-container.show');

            if (closeBtn && modal && !document.getElementById(counterId)) {
                createInlineCounter(closeBtn.parentElement);
                updateCounter();

                const interval = setInterval(() => {
                    if (!document.body.contains(modal)) {
                        clearInterval(interval);
                        const existing = document.getElementById(counterId);
                        if (existing) existing.remove();
                    } else {
                        updateCounter();
                    }
                }, 800);
            }
        });
        observer.observe(document.body, { childList: true, subtree: true });
    }
    window.addEventListener('load', observeModal);
})();