Pending Barcodes (Dashboard Box - Optimized Edition)

Collects barcodes, adds a 'Pending' button to sidebar, and extracts sample counts from navbar (Fixes panel disappearing, closes on click-outside)

Stan na 11-11-2025. Zobacz najnowsza wersja.

Aby zainstalować ten skrypt, wymagana jest instalacje jednego z następujących rozszerzeń: Tampermonkey, Greasemonkey lub Violentmonkey.

Aby zainstalować ten skrypt, wymagana jest instalacje jednego z następujących rozszerzeń: Tampermonkey, Violentmonkey.

Aby zainstalować ten skrypt, wymagana jest instalacje jednego z następujących rozszerzeń: Tampermonkey, Violentmonkey.

Aby zainstalować ten skrypt, wymagana będzie instalacja rozszerzenia Tampermonkey lub Userscripts.

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

Aby zainstalować ten skrypt, musisz zainstalować rozszerzenie menedżera skryptów użytkownika.

(Mam już menedżera skryptów użytkownika, pozwól mi to zainstalować!)

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.

Będziesz musiał zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

Będziesz musiał zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

Musisz zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

(Mam już menedżera stylów użytkownika, pozwól mi to zainstalować!)

// ==UserScript==
// @name         Pending Barcodes (Dashboard Box - Optimized Edition)
// @namespace    http://tampermonkey.net/
// @version     4.7
// @description  Collects barcodes, adds a 'Pending' button to sidebar, and extracts sample counts from navbar (Fixes panel disappearing, closes on click-outside)
// @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 DASHBOARD_BOX_ID = 'my-pending-box-container'; // Removed
    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 = 250;
    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 dashboardBoxInjected = false; // Removed
    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();
    }

    // --- NEW: 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 };
    }

    // --- NEW: 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.");
    }

    // --- NEW: 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;
    }

    // --- NEW: 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) {
            // Yes. The observer will run, and the page will build.
            // Let's wait a moment for the grid to *likely* be there.
            // The `observerCallback` itself is already debounced (250ms).
            // Let's add another short delay.
            setTimeout(async () => {
                console.log(`Barcode Collector: Applying post-redirect filter for ${barcodeToFilter}`);
                await enterBarcodeInFilter(barcodeToFilter);
            }, 750); // Wait 750ms after the observer fires, hoping the grid is built.
        } 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);
        });
        mainObserver.observe(document.body, { childList: true, subtree: true });
        window.addEventListener('beforeunload', cleanup);

        // --- NEW: 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 doesn't exist, do nothing
            if (!panel) return;

            // Check if the panel is open
            if (panel.style.display === 'flex') {
                // Check if the click was *outside* the panel
                // and also *not* on the toggle button itself (to avoid double-toggling)
                const isClickInsidePanel = panel.contains(event.target);
                const isClickOnButton = toggleButton ? toggleButton.contains(event.target) : false;

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

    async function observerCallback() {
        checkAndApplyPostRedirectFilter(); // ADDED THIS
        
        // Inject Stats Box in Navbar
        injectStatsBox();
        
        // Sidebar Button
        const sidebarMenu = document.querySelector(SIDEBAR_INJECTION_POINT_SELECTOR);
        if (sidebarMenu && !sidebarButtonInjected) {
            injectSidebarButton(sidebarMenu);
        }
        
        // Dashboard Box - REMOVED
        
        // Update/Insert Table
        // MODIFIED: We now update the table on *every* observer tick,
        // regardless of which page we are on. The function itself
        // will create the (hidden) panel if it doesn't exist.
        // This stops the observer from hiding the panel if the user opened it.
        await updateOrInsertBarcodeTable();

        // Patient Barcode Logic
        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.");
    }

    // REMOVED: This function was causing the panel to hide
    /*
    function hidePanelAndClearInterval() {
        const existingTable = document.getElementById(IN_PAGE_TABLE_ID);
        if (existingTable) {
            existingTable.style.display = 'none';
            if (timeSinceInterval) clearInterval(timeSinceInterval);
        }
    }
    */

    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) {
                // MODIFIED: Use flex to match CSS
                panel.style.display = panel.style.display === 'flex' ? 'none' : 'flex';
            }
        });
        sidebarButtonInjected = true;
        console.log("Barcode Collector: Sidebar button injected.");
    }

    // function injectDashboardBox(parentRow) { ... } // REMOVED ENTIRE FUNCTION

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

    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) {
                const headerMeta = summaryContainer.querySelector('.header-meta');
                const actionHeader = summaryContainer.querySelector('h2');
                const isPending = (headerMeta && headerMeta.textContent.includes('Pending')) ||
                                  (actionHeader && actionHeader.textContent.includes('ACTION REQUIRED'));
                if (!isPending) await markBarcodeAsFoundAndUpdateStorage(barcodeOnPage);
            }
        } else {
            lastCheckedPatientBarcode = null;
        }
    }

    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 {
            // --- MODIFIED LOGIC ---
            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;
            }
            // --- END MODIFIED LOGIC ---
        }
    }

    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;
        }
        
        /* NEW: Stats Box in Navbar */
        #bc-stats-container {
            display: flex !important;
            align-items: center;
            padding: 0 15px;
            margin-right: auto; /* MODIFIED: Aligns to the left */
        }
        .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;
        }
        
        /* Removed .bc-pending-box styles */
        
        #${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; 
        }
        
        /* NEW: Top row for title and close button */
        .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; }
        
        /* NEW: Container for filters and buttons */
        .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();
})();