// ==UserScript==
// @name Enhanced AG Grid Functionality with Clean Styles and Persistent Column Management
// @version 4.0
// @description Adds vibrant row hover effect, conditional status cell styling for test and sample status, static red background for emergency rows, cleans up styles for specific elements, and persistently manages default column visibility on a specific page.
// @match *://his.kaauh.org/lab/*
// @author Hamad AlShegifi
// @grant GM_addStyle
// @namespace http://tampermonkey.net/
// ==/UserScript==
(function () {
'use strict';
const columnsToUncheck = [
'Lab Order No', 'Hospital MRN', 'DOB', 'Test ID', 'National/Iqama Id',
'Department', 'Doctor', 'Analyzer', 'Reference Lab',
'Accession No', 'Sequence No','Age','Container Type','Storage Condition'
];
// Inject CSS for hover effect, status cell styling, and emergency highlighting
GM_addStyle(`
.ag-row {
transition: background-color 0.3s ease;
}
.vibrant-hover {
/* Background color and text color are now applied to cells within the hovered row */
}
.ag-row.vibrant-hover .ag-cell {
background-color: #87dced !important; /* Light blue color */
color: black !important; /* Black text color */
font-weight: bold !important; /* Bold font */
}
.ag-cell[col-id="testStatus"].status-verified-level2,
.ag-cell[col-id="testStatus"].status-verified-level1 {
background-color: #90EE90 !important; /* Light green color */
}
.ag-cell[col-id="testStatus"].status-ordered {
background-color: #FFFFE0 !important; /* Light yellow color */
}
.ag-cell[col-id="testStatus"].status-resulted {
background-color: #FFA500 !important; /* Orange color */
color: black !important; /* Ensure text is readable on orange */
}
.ag-cell[col-id="testStatus"].status-cancelled {
background-color: #000000 !important; /* Brown color */
color: white !important; /* White text for contrast */
}
.emergency-row {
background-color: #ffe0e0 !important; /* Light red */
}
.ag-cell[col-id="sampleStatus"].status-received {
background-color: #90EE90 !important; /* Light green color for Received sample status */
}
`);
// Clean styles for specific links and spans
function applyCleanStyles() {
const anchor = document.querySelector('li > a[href="#/lab-orders/doctor-request"]');
if (anchor) {
anchor.style.setProperty('white-space', 'nowrap', 'important');
anchor.style.setProperty('font-size', '13px', 'important');
anchor.style.setProperty('overflow', 'visible', 'important');
anchor.style.setProperty('text-overflow', 'unset', 'important');
const spans = anchor.querySelectorAll('span');
spans.forEach(span => {
span.style.setProperty('display', 'inline', 'important');
span.style.setProperty('font-size', '13px', 'important');
span.style.setProperty('white-space', 'nowrap', 'important');
});
}
const simplifySpan = (selector) => {
const span = document.querySelector(selector);
if (span) {
span.style.setProperty('display', 'inline', 'important');
span.style.setProperty('font-size', '20px', 'important');
span.style.setProperty('white-space', 'nowrap', 'important');
span.style.setProperty('overflow', 'visible', 'important');
span.style.setProperty('text-overflow', 'unset', 'important');
span.textContent = span.textContent.replace(/\s+/g, ''); // Optional cleanup
}
};
simplifySpan('span.to-do');
simplifySpan('span.pending-orders');
}
// Debounce for applying clean styles
function debounce(func, wait) {
let timeout;
return function(...args) {
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(this, args), wait);
};
}
const debouncedStyleUpdater = debounce(applyCleanStyles, 300);
const observer = new MutationObserver(debouncedStyleUpdater);
observer.observe(document.body, { childList: true, subtree: true });
// Apply hover effects
const targetColumnIdsForHover = [
"orderNo", "testId", "testDescription", "clusterMrn",
"hospitalMrn", "patientName", "dob", "nationalIqamaId",
"department", "clinic", "doctor", "analyzer",
"orderDateAndTime", "lastUpdatedDate", "sampleStatus",
"referenceLab", "accessionNo", "barcode", "sequenceNo",
"primaryPatientId", "referenceLabDesc", "testStatus",
"orderLastModifiedOnEpoch", "orderCreatedOnEpoch", "equipmentName", "doctorName", "localMrn",
"dateOfBirth", "idNumber"
];
function applyHoverEffect() {
document.querySelectorAll('.ag-cell').forEach(cell => {
const colId = cell.getAttribute('col-id');
if (colId && targetColumnIdsForHover.includes(colId)) {
const row = cell.closest('.ag-row'); // Find the parent row
if (row) {
// Add hover effect to the entire row on mouseenter
cell.addEventListener('mouseenter', () => {
row.classList.add('vibrant-hover'); // Add hover effect to the row
});
// Remove hover effect on mouseleave
cell.addEventListener('mouseleave', () => {
row.classList.remove('vibrant-hover'); // Remove hover effect from the row
});
}
}
});
}
// Apply conditional background color for test and sample statuses
function applyStatusCellStyle() {
document.querySelectorAll('.ag-cell[col-id="testStatus"]').forEach(cell => {
const statusValue = cell.textContent.trim();
cell.classList.remove('status-verified-level2', 'status-verified-level1', 'status-ordered', 'status-resulted', 'status-cancelled'); // Remove previous styles
if (statusValue === "VerifiedLevel2" || statusValue === "VerifiedLevel1") {
cell.classList.add('status-verified-level2');
} else if (statusValue === "Ordered") {
cell.classList.add('status-ordered');
} else if (statusValue === "Resulted") {
cell.classList.add('status-resulted');
} else if (statusValue === "Cancelled") {
cell.classList.add('status-cancelled'); // Apply brown color for Cancelled
}
});
// Apply style for Sample Status = Received
document.querySelectorAll('.ag-cell[col-id="sampleStatus"]').forEach(cell => {
if (cell.textContent.trim() === "Received") {
cell.classList.add('status-received');
} else {
cell.classList.remove('status-received'); // Ensure the class is removed if the value changes
}
});
}
// Highlight emergency rows based on clinic column
function highlightEmergencyRows() {
const rows = document.querySelectorAll('div[role="row"]');
rows.forEach(row => {
const clinicCell = row.querySelector('div[col-id="clinic"]');
if (clinicCell && clinicCell.textContent.trim() === 'EMERGENCY') {
row.classList.add('emergency-row');
} else {
row.classList.remove('emergency-row'); // Remove the class if the condition is no longer met
}
});
}
const observerForGrid = new MutationObserver(() => {
applyHoverEffect();
applyStatusCellStyle();
highlightEmergencyRows(); // Call highlightEmergencyRows on every DOM change
});
const gridBodyViewport = document.querySelector('.ag-body-viewport'); // Observe the grid body viewport
if (gridBodyViewport) {
observerForGrid.observe(gridBodyViewport, { childList: true, subtree: false }); // subtree should be false here as we are observing direct children (rows)
} else {
console.warn("AG Grid body viewport not found. Falling back to body observer.");
observerForGrid.observe(document.body, { childList: true, subtree: true });
}
window.addEventListener('load', () => {
applyHoverEffect();
applyStatusCellStyle();
highlightEmergencyRows(); // Initial call to highlight emergency rows
});
// Set the dropdown value for pagination to 100
function setDropdownValue() {
const dropdown = document.getElementById("dropdownPaginationPageSize");
if (dropdown && dropdown.value !== "100") {
dropdown.value = "100"; // Set the value to "100"
// Trigger the 'change' event
const event = new Event('change', { bubbles: true });
dropdown.dispatchEvent(event);
console.log("Dropdown value set to 100");
}
}
function isSpecificPage() {
return window.location.href.endsWith('/#/lab-orders/lab-test-analyzer');
}
function areColumnsChecked() {
const labels = document.querySelectorAll('.ag-column-tool-panel-column-label');
for (const column of columnsToUncheck) {
if (isColumnChecked(column, labels)) {
return true;
}
}
return false;
}
function isColumnChecked(labelText, labels) {
for (const label of labels) {
if (label.textContent.trim() === labelText) {
const checkbox = label.parentElement.querySelector('.ag-icon-checkbox-checked');
if (checkbox) return true; // Column is checked
}
}
return false; // Column is not checked
}
function clickColumnLabel(labelText, labels) {
if (!isSpecificPage()) return;
labels.forEach(label => {
if (label.textContent.trim() === labelText) {
const checkbox = label.parentElement.querySelector('.ag-icon-checkbox-checked');
if (checkbox) {
label.click(); // Click only if checked
}
}
});
}
function ensureColumnsUnchecked() {
if (!isSpecificPage()) return;
const labels = document.querySelectorAll('.ag-column-tool-panel-column-label');
columnsToUncheck.forEach(column => {
clickColumnLabel(column, labels);
});
}
function ensureOtherColumnsChecked() {
if (!isSpecificPage()) return;
const allLabels = document.querySelectorAll('.ag-column-tool-panel-column-label');
allLabels.forEach(label => {
const labelText = label.textContent.trim();
if (!columnsToUncheck.includes(labelText)) {
const checkbox = label.parentElement.querySelector('.ag-icon-checkbox-unchecked');
if (checkbox) {
label.click(); // Click to check the column if unchecked
}
}
});
}
// Observe changes in the DOM, specifically looking for the column tool panel
function observeDOMForColumnChanges() {
const observer = new MutationObserver(() => {
if (isSpecificPage()) {
ensureColumnsUnchecked();
ensureOtherColumnsChecked();
}
});
// Observe the body for changes, including subtree
observer.observe(document.body, { childList: true, subtree: true });
console.log("MutationObserver is active for persistent column management");
}
// Observe changes in the DOM for the dropdown
function observeDOMForDropdown() {
const observer = new MutationObserver(() => {
setDropdownValue(); // Check and set the dropdown value when changes are detected
});
// Observe the entire document for changes
observer.observe(document.body, {
childList: true,
subtree: true,
});
console.log("MutationObserver is active for dropdown");
}
window.addEventListener('load', () => {
setDropdownValue(); // Initial check
observeDOMForDropdown(); // Start observing for dynamic changes in dropdown
observeDOMForColumnChanges(); // Start observing for column changes
});
})();