Pending Barcodes (Dashboard Box - Optimized Edition)

Marks "All Clear" OR "Action Required" as the complete status.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey, το Greasemonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Userscripts για να εγκαταστήσετε αυτόν τον κώδικα.

You will need to install an extension such as Tampermonkey to install this script.

Θα χρειαστεί να εγκαταστήσετε μια επέκταση διαχείρισης κώδικα χρήστη για να εγκαταστήσετε αυτόν τον κώδικα.

(Έχω ήδη έναν διαχειριστή κώδικα χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(Έχω ήδη έναν διαχειριστή στυλ χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

// ==UserScript==
// @name         Pending Barcodes (Dashboard Box - Optimized Edition)
// @namespace    http://tampermonkey.net/
// @version      5.3
// @description  Marks "All Clear" OR "Action Required" as the complete status.
// @author       Hamad AlShegifi
// @match        *://his.kaauh.org/lab/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_addStyle
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // --- Configuration & Constants ---
    const TABLE_BODY_SELECTOR = 'tbody[formarrayname="TubeTypeList"]';
    const BARCODE_DISPLAY_SELECTOR = '#barcode-display-box';
    const STORAGE_KEY = 'collectedBarcodes_storage_v2';
    const IN_PAGE_TABLE_ID = 'barcode-inpage-container';
    const INJECTION_POINT_SELECTOR = '.row.labordertab';
    const GRID_CONTAINER_SELECTOR = '.ag-center-cols-container';
    const SIDEBAR_INJECTION_POINT_SELECTOR = '.sidebar-bottom csi-list-menu';
    
    // NEW: Navbar selectors for sample counting
    const NAVBAR_SELECTOR = 'ul.navbar-right';
    const NOTIFICATION_BADGE_SELECTOR = '.counter.header_notification';

    const DEBOUNCE_DELAY = 200;
    const GRID_UPDATE_TIMEOUT = 2000;
    const SLEEP_DURATION = 50;
    const TIME_UPDATE_INTERVAL = 5000;
    const CACHE_DURATION = 1000;

    // --- State Flags & Cache ---
    const collectedBarcodesThisSession = new Set();
    let lastCheckedPatientBarcode = null;
    let timeSinceInterval = null;
    let sortState = { key: 'timestamp', direction: 'desc' };
    let sidebarButtonInjected = false;
    let statsBoxInjected = false;
    let mainObserver = null;
    let observerDebounceTimer = null;

    // --- Storage Cache ---
    let cachedBarcodes = null;
    let cacheTimestamp = 0;

    async function getCachedBarcodes() {
        const now = Date.now();
        if (!cachedBarcodes || (now - cacheTimestamp) > CACHE_DURATION) {
            cachedBarcodes = await GM_getValue(STORAGE_KEY, []);
            cacheTimestamp = now;
        }
        return cachedBarcodes;
    }

    async function saveBarcodesCached(barcodes) {
        await GM_setValue(STORAGE_KEY, barcodes);
        cachedBarcodes = barcodes;
        cacheTimestamp = Date.now();
    }

    // --- Extract Sample Counts ---
    function extractSampleCounts() {
        const barcodes = cachedBarcodes || [];
        const totalSamples = barcodes.length;
        const completedSamples = barcodes.filter(b => b.found).length;
        const pendingSamples = totalSamples - completedSamples;
        
        return { totalSamples, completedSamples, pendingSamples };
    }

    // --- Inject Statistics Box in Navbar ---
    function injectStatsBox() {
        if (statsBoxInjected) return;
        
        const navbar = document.querySelector(NAVBAR_SELECTOR);
        if (!navbar) return;

        const statsContainer = document.createElement('li');
        statsContainer.className = 'nav-item';
        statsContainer.id = 'bc-stats-container';
        statsContainer.innerHTML = `
            <div class="bc-stats-box">
                <div class="bc-stat-item">
                    <span class="bc-stat-label">Total:</span>
                    <span id="bc-total-count" class="bc-stat-value">0</span>
                </div>
                <div class="bc-stat-item">
                    <span class="bc-stat-label">Completed:</span>
                    <span id="bc-completed-count" class="bc-stat-value bc-completed">0</span>
                </div>
                <div class="bc-stat-item">
                    <span class="bc-stat-label">Pending:</span>
                    <span id="bc-pending-count" class="bc-stat-value bc-pending">0</span>
                </div>
            </div>
        `;

        // Insert before the clock element
        const clockElement = navbar.querySelector('.clock-nav');
        if (clockElement) {
            navbar.insertBefore(statsContainer, clockElement);
        } else {
            navbar.insertBefore(statsContainer, navbar.firstChild);
        }

        statsBoxInjected = true;
        console.log("Barcode Collector: Stats box injected into navbar.");
    }

    // --- Update Statistics Display ---
    function updateStatsDisplay(stats) {
        const totalEl = document.getElementById('bc-total-count');
        const completedEl = document.getElementById('bc-completed-count');
        const pendingEl = document.getElementById('bc-pending-count');

        if (totalEl) totalEl.textContent = stats.totalSamples;
        if (completedEl) completedEl.textContent = stats.completedSamples;
        if (pendingEl) pendingEl.textContent = stats.pendingSamples;
    }

    // --- Check for post-redirect filter ---
    function checkAndApplyPostRedirectFilter() {
        const barcodeToFilter = sessionStorage.getItem('barcodeToFilterAfterRedirect');
        if (!barcodeToFilter) return;

        // We have a barcode. Clear the flag immediately.
        sessionStorage.removeItem('barcodeToFilterAfterRedirect');

        // Are we on the right page?
        const targetPage = 'https://his.kaauh.org/lab/#/lab-orders/lab-test-analyzer';
        if (window.location.href === targetPage) {
            setTimeout(async () => {
                console.log(`Barcode Collector: Applying post-redirect filter for ${barcodeToFilter}`);
                await enterBarcodeInFilter(barcodeToFilter);
            }, 750);
        } else {
            console.warn(`Barcode Collector: Post-redirect filter for ${barcodeToFilter} ignored, user is not on the correct page.`);
        }
    }

    // --- Main Logic ---
    function initialize() {
        mainObserver = new MutationObserver((mutations, obs) => {
            if (observerDebounceTimer) clearTimeout(observerDebounceTimer);
            observerDebounceTimer = setTimeout(observerCallback, DEBOUNCE_DELAY);
        });
        
        // Added characterData: true to watch for text changes (e.g., status updates)
        mainObserver.observe(document.body, { childList: true, subtree: true, characterData: true });
        
        window.addEventListener('beforeunload', cleanup);

        // Click outside to close panel
        window.addEventListener('click', (event) => {
            const panel = document.getElementById(IN_PAGE_TABLE_ID);
            const toggleButton = document.getElementById('pending-barcodes-toggle');

            if (!panel) return;

            if (panel.style.display === 'flex') {
                const isClickInsidePanel = panel.contains(event.target);
                const isClickOnButton = toggleButton ? toggleButton.contains(event.target) : false;

                if (!isClickInsidePanel && !isClickOnButton) {
                    panel.style.display = 'none';
                }
            }
        });
    }

    async function observerCallback() {
        checkAndApplyPostRedirectFilter();
        
        // Inject Stats Box in Navbar
        injectStatsBox();
        
        // Sidebar Button
        const sidebarMenu = document.querySelector(SIDEBAR_INJECTION_POINT_SELECTOR);
        if (sidebarMenu && !sidebarButtonInjected) {
            injectSidebarButton(sidebarMenu);
        }
        
        // Update/Insert Table
        await updateOrInsertBarcodeTable();

        // Patient Barcode Logic - FIXED
        checkAndMarkPatientBarcode();

        // Collect New Barcodes
        const allBarcodeRows = document.querySelectorAll(`${TABLE_BODY_SELECTOR} tr`);
        if (allBarcodeRows.length > 0) {
            for (const row of allBarcodeRows) {
                const barcodeInput = row.querySelector('input[formcontrolname="Barcode"]');
                const workbenchInput = row.querySelector('input[formcontrolname="TestSection"]');
                if (barcodeInput && barcodeInput.value) {
                    const barcode = barcodeInput.value.trim();
                    const workbench = workbenchInput && workbenchInput.value ? workbenchInput.value.trim() : 'N/A';
                    await saveBarcode(barcode, workbench);
                }
            }
        }
        
        // Update statistics display
        const stats = extractSampleCounts();
        updateStatsDisplay(stats);
    }

    function cleanup() {
        if (timeSinceInterval) {
            clearInterval(timeSinceInterval);
            timeSinceInterval = null;
        }
        if (mainObserver) {
            mainObserver.disconnect();
            mainObserver = null;
        }
        console.log("Barcode Collector: Cleaned up resources.");
    }

    function injectSidebarButton(sidebarMenu) {
        if (document.getElementById('pending-barcodes-toggle')) return;
        const newMenuItemContainer = document.createElement('div');
        newMenuItemContainer.innerHTML = `
            <div>
                <csi-main-menu>
                    <a id="pending-barcodes-toggle" title="Toggle Pending Barcodes Panel">
                        <span class="icon-holder csi-menu-icon">
                            <i class="nova-icon-scan"></i>
                        </span>
                        <div class="csi-menu-text-wrapper">
                            <span class="csi-menu-text sidemenu-title" title="Pending Barcodes">Pending</span>
                            <span id="pending-count-badge" class="bc-sidebar-badge">0</span>
                        </div>
                    </a>
                </csi-main-menu>
            </div>
        `;
        sidebarMenu.appendChild(newMenuItemContainer);
        document.getElementById('pending-barcodes-toggle').addEventListener('click', (e) => {
            e.preventDefault();
            const panel = document.getElementById(IN_PAGE_TABLE_ID);
            if (panel) {
                panel.style.display = panel.style.display === 'flex' ? 'none' : 'flex';
            }
        });
        sidebarButtonInjected = true;
        console.log("Barcode Collector: Sidebar button injected.");
    }

    async function updatePendingCounts(count) {
        const badge = document.getElementById('pending-count-badge');
        if (badge) {
            badge.textContent = count;
            badge.classList.toggle('visible', count > 0);
        }
    }

    async function saveBarcode(barcode, workbench) {
        if (collectedBarcodesThisSession.has(barcode)) return;
        try {
            let barcodes = await getCachedBarcodes();
            if (barcodes.some(entry => entry.barcode === barcode)) {
                collectedBarcodesThisSession.add(barcode);
                return;
            }
            const newEntry = {
                count: barcodes.length + 1,
                barcode: barcode,
                workbench: workbench,
                timestamp: new Date().toISOString(),
                found: false
            };
            barcodes.push(newEntry);
            await saveBarcodesCached(barcodes);
            collectedBarcodesThisSession.add(barcode);
            await updateOrInsertBarcodeTable();
        } catch (error) {
            console.error(`Barcode Collector: Error saving barcode ${barcode}:`, error);
        }
    }

    function formatTimeSince(isoTimestamp) {
        const date = new Date(isoTimestamp);
        const now = new Date();
        const totalMinutes = Math.floor((now - date) / (1000 * 60));
        if (totalMinutes < 1) return "00:00 ago";
        const hours = Math.floor(totalMinutes / 60);
        const minutes = totalMinutes % 60;
        return `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')} ago`;
    }

    async function markBarcodeAsFoundAndUpdateStorage(barcodeToMark) {
        let barcodes = await getCachedBarcodes();
        const entry = barcodes.find(b => b.barcode === barcodeToMark);
        if (entry && !entry.found) {
            entry.found = true;
            await saveBarcodesCached(barcodes);
            await updateOrInsertBarcodeTable();
        }
    }

    // --- THIS FUNCTION IS MODIFIED ---
    async function checkAndMarkPatientBarcode() {
        const patientBarcodeBox = document.querySelector(BARCODE_DISPLAY_SELECTOR);
        const barcodeOnPage = patientBarcodeBox ?
            Array.from(patientBarcodeBox.querySelectorAll('div'))
                .find(div => div.textContent.includes('Sample Barcode:'))?.nextElementSibling?.textContent.trim()
            : null;

        if (barcodeOnPage && barcodeOnPage !== lastCheckedPatientBarcode) {
            lastCheckedPatientBarcode = barcodeOnPage;
        }

        if (barcodeOnPage) {
            const summaryContainer = document.querySelector('#test-summary-container');
            if (summaryContainer) {
                // The status text is in the H2 tag
                const statusHeader = summaryContainer.querySelector('h2'); 
                let statusText = '';
                
                if (statusHeader) {
                    // Normalize whitespace just in case
                    statusText = statusHeader.textContent.replace(/\s+/g, ' ').trim();
                }

                // --- THIS IS THE CHANGED LINE ---
                // Check if the status is one of the two "complete" messages
                const isCompleteStatus = (statusText === 'All Clear: No Pending Actions' || statusText === 'ACTION REQUIRED! : Verification Required');
                // --- END OF CHANGE ---

                if (isCompleteStatus) {
                    console.log(`Barcode Collector: Marking ${barcodeOnPage} as completed (Status: ${statusText})`);
                    await markBarcodeAsFoundAndUpdateStorage(barcodeOnPage);
                } else {
                    // Log the status we actually found
                    console.log(`Barcode Collector: Barcode ${barcodeOnPage} not marked (status: [${statusText ? statusText : 'unknown'}])`);
                }
            }
        } else {
            lastCheckedPatientBarcode = null;
        }
    }
    // --- END OF MODIFIED FUNCTION ---

    function findFloatingFilterInputByHeader(headerText) {
        const headerViewport = document.querySelector('.ag-header-viewport');
        if (!headerViewport) return null;
        const allTitleCells = Array.from(headerViewport.querySelectorAll('.ag-header-row[aria-rowindex="1"] .ag-header-cell'));
        let targetColumnIndex = allTitleCells.findIndex(cell => {
            const cellTextElement = cell.querySelector('.ag-header-cell-text');
            return cellTextElement && cellTextElement.textContent.trim().toLowerCase() === headerText.toLowerCase();
        });
        if (targetColumnIndex === -1) return null;
        const filterRow = headerViewport.querySelector('.ag-header-row[aria-rowindex="2"]');
        if (!filterRow) return null;
        const filterCell = filterRow.children[targetColumnIndex];
        if (!filterCell) return null;
        return filterCell.querySelector('input.ag-floating-filter-input');
    }

    function waitForGridUpdateAndClick() {
        return new Promise((resolve, reject) => {
            const gridContainer = document.querySelector(GRID_CONTAINER_SELECTOR);
            if (!gridContainer) return reject("AG-Grid container not found.");
            const timeout = setTimeout(() => { observer.disconnect(); reject("Timeout: AG-Grid did not update."); }, GRID_UPDATE_TIMEOUT);
            const observer = new MutationObserver((mutations, obs) => {
                const firstRow = gridContainer.querySelector('.ag-row[row-index="0"]');
                if (firstRow) {
                    firstRow.click();
                    clearTimeout(timeout);
                    obs.disconnect();
                    resolve();
                }
            });
            observer.observe(gridContainer, { childList: true, subtree: true });
        });
    }

    function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); }

    async function enterBarcodeInFilter(barcode) {
        const targetInput = findFloatingFilterInputByHeader('Barcode');
        if (!targetInput) { console.error('Barcode Collector: Could not find "Barcode" filter input.'); return; }
        try {
            targetInput.focus(); await sleep(SLEEP_DURATION);
            targetInput.value = barcode;
            targetInput.dispatchEvent(new Event('input', { bubbles: true, cancelable: true })); await sleep(100);
            targetInput.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', code: 'Enter', keyCode: 13, bubbles: true }));
            targetInput.dispatchEvent(new KeyboardEvent('keyup', { key: 'Enter', code: 'Enter', keyCode: 13, bubbles: true }));
            await waitForGridUpdateAndClick();
        } catch (error) {
            console.error("Barcode Collector: Error while filtering/clicking.", error);
        } finally {
            if (targetInput) targetInput.blur();
        }
    }

    function handleSortClick() {
        sortState.direction = sortState.direction === 'desc' ? 'asc' : 'desc';
        updateOrInsertBarcodeTable();
    }

    async function updateOrInsertBarcodeTable() {
        try {
            const barcodes = await getCachedBarcodes();
            const pendingCount = barcodes.filter(b => !b.found).length;
            await updatePendingCounts(pendingCount);
            
            // Update stats display
            const stats = extractSampleCounts();
            updateStatsDisplay(stats);
            
            let container = document.getElementById(IN_PAGE_TABLE_ID);

            if (sortState.key === 'timestamp') {
                barcodes.sort((a, b) => {
                    const dateA = new Date(a.timestamp);
                    const dateB = new Date(b.timestamp);
                    return sortState.direction === 'asc' ? dateA - dateB : dateB - dateA;
                });
            }

            const uniqueWorkbenches = ['All', ...new Set(barcodes.map(b => b.workbench).filter(Boolean))];

            if (!container) {
                container = document.createElement('div');
                container.id = IN_PAGE_TABLE_ID;
                container.style.display = 'none';
                document.body.appendChild(container);

                container.innerHTML = `
                    <div class="bc-table-header">
                        <div class="bc-header-top-row">
                            <h2>Pending</h2>
                            <span id="close-bc-panel-btn">&times;</span>
                        </div>
                        <div class="bc-table-controls">
                            <div class="bc-filter-container"><label for="workbench-filter">Workbench:</label><select id="workbench-filter"></select></div>
                            <div class="bc-button-group">
                                 <button id="delete-completed-btn" class="bc-btn bc-btn-completed">Clear Completed</button>
                                 <button id="clear-all-btn" class="bc-btn bc-btn-clear-all">Clear All</button>
                            </div>
                        </div>
                    </div>
                    <div class="bc-table-body">
                        <table>
                            <thead>
                                <tr>
                                    <th>#</th><th>Barcode</th><th>Workbench</th>
                                    <th id="sort-by-time-header" class="sortable-header">Added <span id="sort-indicator"></span></th>
                                    <th>Pending</th><th>Actions</th>
                                </tr>
                            </thead>
                            <tbody></tbody>
                        </table>
                    </div>
                `;
                container.querySelector('#clear-all-btn').addEventListener('click', async () => {
                    if (confirm("Are you sure you want to delete ALL pending barcodes? This cannot be undone.")) {
                        await saveBarcodesCached([]);
                        await updateOrInsertBarcodeTable();
                    }
                });
                container.querySelector('#delete-completed-btn').addEventListener('click', deleteCompletedBarcodes);
                container.querySelector('#workbench-filter').addEventListener('change', updateOrInsertBarcodeTable);
                container.querySelector('#sort-by-time-header').addEventListener('click', handleSortClick);
                container.querySelector('#close-bc-panel-btn').addEventListener('click', () => {
                    container.style.display = 'none';
                });
            }

            const sortIndicator = container.querySelector('#sort-indicator');
            if (sortIndicator) {
                sortIndicator.textContent = sortState.direction === 'asc' ? '▲' : '▼';
            }

            const filterDropdown = container.querySelector('#workbench-filter');
            const currentFilterValue = filterDropdown.value;
            filterDropdown.innerHTML = uniqueWorkbenches.map(wb => `<option value="${wb}">${wb}</option>`).join('');
            if (uniqueWorkbenches.includes(currentFilterValue)) filterDropdown.value = currentFilterValue;

            const selectedWorkbench = filterDropdown.value;
            const filteredBarcodes = selectedWorkbench === 'All' ? barcodes : barcodes.filter(b => b.workbench === selectedWorkbench);

            let tableRows = filteredBarcodes.map(entry => `
                <tr data-barcode-row="${entry.barcode}" class="${entry.found ? 'barcode-found' : ''}">
                    <td>${entry.count}</td><td>${entry.barcode}</td><td>${entry.workbench || 'N/A'}</td>
                    <td>${new Date(entry.timestamp).toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', hour12: true })}</td>
                    <td data-timestamp="${entry.timestamp}">${formatTimeSince(entry.timestamp)}</td>
                    <td class="action-cell-bc"><span class="delete-barcode-btn" data-barcode="${entry.barcode}" title="Delete">&times;</span></td>
                </tr>
            `).join('');
            if (filteredBarcodes.length === 0) {
                tableRows = '<tr><td colspan="6">No pending barcodes match the filter.</td></tr>';
            }

            const tableBody = container.querySelector('tbody');
            if (tableBody.innerHTML !== tableRows) tableBody.innerHTML = tableRows;
            tableBody.removeEventListener('click', handleTableClick);
            tableBody.addEventListener('click', handleTableClick);

            if (timeSinceInterval) clearInterval(timeSinceInterval);
            timeSinceInterval = setInterval(() => {
                container.querySelectorAll('td[data-timestamp]').forEach(cell => {
                    cell.textContent = formatTimeSince(cell.dataset.timestamp);
                });
            }, TIME_UPDATE_INTERVAL);
        } catch (error) {
            console.error("Barcode Collector: Error updating table:", error);
        }
    }

    async function handleTableClick(event) {
        const row = event.target.closest('tr');
        if (!row || !row.dataset.barcodeRow) return;
        if (event.target.classList.contains('delete-barcode-btn')) {
            await deleteBarcode(event.target.dataset.barcode);
        } else {
            const barcodeToFilter = row.dataset.barcodeRow;
            const targetPage = 'https://his.kaauh.org/lab/#/lab-orders/lab-test-analyzer';
            
            // Hide the panel
            document.getElementById(IN_PAGE_TABLE_ID).style.display = 'none';

            // Check if we are on the right page
            if (window.location.href === targetPage) {
                // We are already on the page, just filter
                await enterBarcodeInFilter(barcodeToFilter);
            } else {
                // We are on the wrong page. Set storage and redirect.
                console.log(`Barcode Collector: Wrong page. Storing ${barcodeToFilter} and redirecting...`);
                sessionStorage.setItem('barcodeToFilterAfterRedirect', barcodeToFilter);
                window.location.href = targetPage;
            }
        }
    }

    async function deleteCompletedBarcodes() {
        if (confirm("Are you sure you want to delete all completed (green) barcodes?")) {
            let barcodes = await getCachedBarcodes();
            let updatedBarcodes = barcodes.filter(entry => !entry.found);
            updatedBarcodes.forEach((entry, index) => { entry.count = index + 1; });
            await saveBarcodesCached(updatedBarcodes);
            await updateOrInsertBarcodeTable();
        }
    }

    async function deleteBarcode(barcodeToDelete) {
        let barcodes = await getCachedBarcodes();
        let updatedBarcodes = barcodes.filter(entry => entry.barcode !== barcodeToDelete);
        updatedBarcodes.forEach((entry, index) => { entry.count = index + 1; });
        await saveBarcodesCached(updatedBarcodes);
        await updateOrInsertBarcodeTable();
    }

    // --- Optimized CSS ---
    GM_addStyle(`
        :root {
            --bc-primary-color: #ef5350;
            --bc-primary-dark: #d32f2f;
            --bc-secondary-color: #0288d1;
            --bc-success-color: #4caf50;
        }
        
        /* Stats Box in Navbar */
        #bc-stats-container {
            display: flex !important;
            align-items: center;
            padding: 0 15px;
            margin-right: auto;
        }
        .bc-stats-box {
            display: flex;
            gap: 15px;
            align-items: center;
            background-color: rgba(0, 0, 0, 0.05);
            padding: 8px 15px;
            border-radius: 6px;
            font-size: 13px;
        }
        .bc-stat-item {
            display: flex;
            align-items: center;
            gap: 5px;
        }
        .bc-stat-label {
            color: #444;
            font-weight: 500;
        }
        .bc-stat-value {
            color: #111;
            font-weight: bold;
            font-size: 15px;
            min-width: 25px;
            text-align: center;
            padding: 2px 8px;
            border-radius: 4px;
            background-color: rgba(0, 0, 0, 0.1);
        }
        .bc-stat-value.bc-completed {
            background-color: var(--bc-success-color);
        }
        .bc-stat-value.bc-pending {
            background-color: var(--bc-primary-color);
        }
        .bc-stat-value.bc-completed,
        .bc-stat-value.bc-pending {
            color: #fff !important;
        }
        
        #pending-barcodes-toggle {
            cursor: pointer;
            display: flex;
            align-items: center;
        }
        #pending-barcodes-toggle .csi-menu-text-wrapper {
            display: flex;
            width: 100%;
            align-items: center;
        }
        .bc-sidebar-badge {
            background-color: var(--bc-primary-color);
            color: white;
            border-radius: 10px;
            padding: 2px 6px;
            font-size: 11px;
            font-weight: bold;
            margin-left: auto;
            margin-right: 10px;
            display: none;
            line-height: 1;
        }
        .bc-sidebar-badge.visible {
            display: inline-block;
        }
        
        #${IN_PAGE_TABLE_ID} {
            position: fixed;
            top: 55px;
            left: 70px;
            width: 500px;
            height: 85vh;
            z-index: 2000;
            display: none;
            margin: 0;
            border: 1px solid #ccc;
            border-radius: 8px;
            box-shadow: 0 5px 15px rgba(0,0,0,0.3);
            background-color: #fff;
            flex-direction: column;
        }
        .bc-table-header {
            padding: 10px 16px; 
            background-color: #f7f7f7; 
            border-bottom: 1px solid #ccc;
            border-top-left-radius: 8px; 
            border-top-right-radius: 8px;
            display: flex; 
            flex-direction: column; 
            align-items: center;
            gap: 8px; 
        }
        
        .bc-header-top-row {
            display: flex;
            justify-content: space-between;
            align-items: center;
            width: 100%;
        }
        .bc-table-header h2 { 
            margin: 0; 
            font-size: 1.1em; 
            color: #333;
        }
        #close-bc-panel-btn {
            font-size: 24px;
            font-weight: bold;
            color: #aaa;
            cursor: pointer;
            padding: 0 5px;
        }
        #close-bc-panel-btn:hover { color: #333; }
        
        .bc-table-controls {
            display: flex;
            justify-content: space-between;
            align-items: center;
            width: 100%;
        }
        .bc-filter-container { 
            display: flex; 
            align-items: center; 
            gap: 8px; 
        }
        .bc-filter-container label { font-weight: bold; font-size: 0.9em; }
        #workbench-filter { padding: 4px; border-radius: 4px; border: 1px solid #ccc; }
        .bc-button-group { display: flex; gap: 8px; flex-shrink: 0; }
        .bc-btn {
            border: none; padding: 6px 12px; border-radius: 5px; cursor: pointer;
            font-weight: bold; font-size: 0.9em; color: white; transition: background-color 0.2s;
        }
        .bc-btn:hover { opacity: 0.9; }
        .bc-btn-clear-all { background-color: var(--bc-primary-color); }
        .bc-btn-clear-all:hover { background-color: var(--bc-primary-dark); }
        .bc-btn-completed { background-color: var(--bc-secondary-color); }
        .bc-btn-completed:hover { background-color: #0277bd; }
        .bc-table-body {
            padding: 8px; overflow-y: auto; flex-grow: 1; min-height: 0;
        }
        .bc-table-body table { width: 100%; border-collapse: collapse; }
        .bc-table-body th, .bc-table-body td {
            border: 1px solid #ddd; padding: 4px 8px; text-align: left; font-size: 0.9em;
        }
        .bc-table-body th { background-color: #f2f2f2; }
        .bc-table-body .sortable-header { cursor: pointer; }
        .bc-table-body .sortable-header:hover { background-color: #e0e0e0; }
        #sort-indicator { font-size: 0.8em; margin-left: 4px; }
        .bc-table-body tbody tr { cursor: pointer; }
        .bc-table-body tbody tr:hover { background-color: #e8eaf6; }
        .bc-table-body tbody tr.barcode-found { background-color: #a5d6a7 !important; color: #1b5e20; }
        .bc-table-body tbody tr.barcode-found:hover { background-color: #81c784 !important; }
        .action-cell-bc { text-align: center !important; }
        .delete-barcode-btn {
            cursor: pointer; font-weight: bold; font-size: 18px; color: var(--bc-primary-color);
            padding: 0 4px; border-radius: 4px;
        }
        .delete-barcode-btn:hover { color: white; background-color: var(--bc-primary-dark); }
    `);

    // Boot
    initialize();
})();