Auto Refresh Interface for Hotsauce

Auto refresh for HotSOS

目前為 2025-04-28 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

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

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Auto Refresh Interface for Hotsauce
// @namespace    http://tampermonkey.net/
// @version      1.0.0
// @description  Auto refresh for HotSOS
// @author       PC
// @match        https://na4.m-tech.com/service-optimization/operations/service-orders/*
// @run-at       document-idle
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_addStyle
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // CSS Styles - Modified for better spacing between sections
    GM_addStyle(`
        #auto-refresh-container {
            position: fixed;
            top: 20px;
            left: 20px;
            background-color: #f5f5f5;
            border: 1px solid #ddd;
            border-radius: 6px;
            box-shadow: 0 2px 5px rgba(0,0,0,0.1);
            padding: 8px 10px;
            z-index: 9999;
            font-family: Arial, sans-serif;
            min-width: 150px;
            max-width: 200px;
            user-select: none;
            font-size: 12px;
        }
        #auto-refresh-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 10px; /* Increased margin */
            cursor: move;
            padding-bottom: 6px; /* Added padding */
            border-bottom: 1px solid #eee; /* Added separator */
        }
        #auto-refresh-title {
            font-weight: bold;
            color: #444;
            font-size: 12px;
        }
        #auto-refresh-controls {
            display: flex;
            align-items: center;
            gap: 8px;
        }
        #auto-refresh-collapse {
            cursor: pointer;
            font-size: 14px;
            color: #666;
            width: 16px;
            height: 16px;
            display: flex;
            align-items: center;
            justify-content: center;
            border-radius: 50%;
        }
        #auto-refresh-collapse:hover {
            background-color: #eee;
        }
        #auto-refresh-body {
            overflow: hidden;
            transition: max-height 0.3s ease;
        }
        #auto-refresh-presets {
            padding-bottom: 8px; /* Added padding */
            margin-bottom: 8px; /* Added margin */
            border-bottom: 1px solid #eee; /* Added separator */
        }
        .auto-refresh-preset {
            display: inline-block;
            background-color: #e9e9e9;
            border: none;
            border-radius: 4px;
            padding: 4px 8px;
            margin: 3px;
            cursor: pointer;
            transition: all 0.2s ease;
            font-size: 11px;
        }
        .auto-refresh-preset:hover {
            background-color: #d9d9d9;
        }
        .auto-refresh-preset.active {
            background-color: #4a89dc;
            color: white;
        }
        .auto-refresh-disabled .auto-refresh-preset {
            opacity: 0.5;
            cursor: default;
        }
        #auto-refresh-status {
            margin-top: 6px;
            font-size: 11px;
            color: #666;
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
            padding-top: 2px; /* Added padding */
        }
        #auto-refresh-status.warning {
            color: #f44336;
        }
        #auto-refresh-status.info {
            color: #9e9e9e;
        }
        #auto-refresh-status.success {
            color: #4caf50;
        }
        #auto-refresh-edit-overlay {
            position: fixed;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background-color: rgba(0,0,0,0.5);
            z-index: 10000;
            display: flex;
            align-items: center;
            justify-content: center;
        }
        #auto-refresh-edit-panel {
            background-color: white;
            border-radius: 6px;
            padding: 16px;
            width: 250px;
            box-shadow: 0 3px 14px rgba(0,0,0,0.2);
        }
        .auto-refresh-form-group {
            margin-bottom: 12px;
        }
        .auto-refresh-form-label {
            display: block;
            margin-bottom: 4px;
            font-weight: bold;
            font-size: 12px;
        }
        .auto-refresh-form-input {
            width: 100%;
            padding: 6px;
            border: 1px solid #ddd;
            border-radius: 4px;
            font-size: 12px;
        }
        .auto-refresh-form-buttons {
            display: flex;
            justify-content: flex-end;
        }
        .auto-refresh-form-button {
            padding: 6px 12px;
            border: none;
            border-radius: 4px;
            margin-left: 8px;
            cursor: pointer;
            font-size: 12px;
        }
        .auto-refresh-form-button.cancel {
            background-color: #f5f5f5;
        }
        .auto-refresh-form-button.save {
            background-color: #4a89dc;
            color: white;
        }
        .switch {
            position: relative;
            display: inline-block;
            width: 32px;
            height: 16px;
        }
        .switch input {
            opacity: 0;
            width: 0;
            height: 0;
        }
        .slider {
            position: absolute;
            cursor: pointer;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background-color: #ccc;
            transition: .3s;
            border-radius: 16px;
        }
        .slider:before {
            position: absolute;
            content: "";
            height: 12px;
            width: 12px;
            left: 2px;
            bottom: 2px;
            background-color: white;
            transition: .3s;
            border-radius: 50%;
        }
        input:checked + .slider {
            background-color: #4a89dc;
        }
        input:checked + .slider:before {
            transform: translateX(16px);
        }
    `);

    // Default presets with proper formatting
    const DEFAULT_PRESETS = [
        { name: '15s', seconds: 15 },
        { name: '30s', seconds: 30 },
        { name: '1min', seconds: 60 },
        { name: '5min', seconds: 300 }
    ];

    // State management
    let state = {
        enabled: true,
        collapsed: false,
        activePreset: null,
        presets: GM_getValue('autoRefreshPresets', DEFAULT_PRESETS),
        position: GM_getValue('autoRefreshPosition', { x: 20, y: 20 }),
        refreshTimer: null,
        lastRefreshTime: null,
        editingPreset: null,
        onServiceOrdersPage: false,
        initialized: false,
        observerDebounce: false,
        refreshButtonObserved: false,
        isDragging: false
    };

    // Check if we're on the service orders page
    function checkIfOnServiceOrdersPage() {
        try {
            // Check if URL matches the service orders page pattern
            const isServiceOrdersURL = window.location.href.includes('/service-optimization/operations/service-orders');

            // Also check for refresh button as a fallback
            const refreshButton = findRefreshButton();

            state.onServiceOrdersPage = isServiceOrdersURL && !!refreshButton;

            // If we're on the service orders page, observe the refresh button for manual clicks
            if (state.onServiceOrdersPage && !state.refreshButtonObserved && refreshButton) {
                observeRefreshButton(refreshButton);
            }

            return state.onServiceOrdersPage;
        } catch (error) {
            console.error('Error checking page type:', error);
            return false;
        }
    }

    // Observe the refresh button for manual clicks
    function observeRefreshButton(button) {
        try {
            if (!button || state.refreshButtonObserved) return;

            // Add click listener to track manual refreshes
            button.addEventListener('click', function manualRefreshHandler() {
                // Only update if auto-refresh is enabled
                if (state.enabled) {
                    // Update time
                    state.lastRefreshTime = new Date();
                    updateStatus('success', `Last checked: ${formatTime(state.lastRefreshTime)}`);

                    // Reset timer if we have an active preset
                    if (state.activePreset && state.refreshTimer) {
                        clearAutoRefresh();
                        state.refreshTimer = setInterval(() => {
                            triggerRefresh();
                        }, state.activePreset.seconds * 1000);
                    }
                }
            });

            state.refreshButtonObserved = true;
            console.log('Refresh button observed for manual clicks');
        } catch (error) {
            console.error('Error observing refresh button:', error);
        }
    }

    // Find refresh button with more detailed search
    function findRefreshButton() {
        try {
            // Try different button selectors
            const selectors = [
                'button[soe-data-cy="refresh"]',
                'button[mat-icon-button] soe-icon[icon="refresh-dot"]',
                'button[mat-icon-button] soe-icon[icon="refresh"]',
                'button[aria-label="refresh"]',
                'button.mat-icon-button mat-ripple.mat-button-ripple'
            ];

            for (const selector of selectors) {
                const button = document.querySelector(selector);
                if (button) {
                    return button;
                }
            }

            // If we can't find with specific selectors, try a broader approach
            const buttons = document.querySelectorAll('button');
            for (const button of buttons) {
                const buttonHTML = button.innerHTML.toLowerCase();
                if (buttonHTML.includes('refresh') ||
                    buttonHTML.includes('reload') ||
                    buttonHTML.includes('refresh-dot')) {
                    return button;
                }
            }

            return null;
        } catch (error) {
            console.error('Error finding refresh button:', error);
            return null;
        }
    }

    // Format time as HH:MM:SS
    function formatTime(date) {
        if (!date) return 'Never';
        return date.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit', second:'2-digit'});
    }

    // Update status message - More concise now
    function updateStatus(type, message) {
        try {
            const statusEl = document.getElementById('auto-refresh-status');
            if (statusEl) {
                statusEl.className = type || '';
                statusEl.textContent = message || '';
            }
        } catch (error) {
            console.error('Error updating status:', error);
        }
    }

    // Trigger refresh click
    function triggerRefresh() {
        try {
            const refreshButton = findRefreshButton();
            if (refreshButton) {
                // If we found the button itself, click it
                refreshButton.click();
                state.lastRefreshTime = new Date();
                updateStatus('success', `Last checked: ${formatTime(state.lastRefreshTime)}`);
                return true;
            } else {
                // If we couldn't find the button, show an error
                updateStatus('warning', 'Refresh button not found');
                return false;
            }
        } catch (error) {
            console.error('Error triggering refresh:', error);
            updateStatus('warning', 'Error refreshing');
            return false;
        }
    }

    // Start auto refresh with given preset
    function startAutoRefresh(preset) {
        try {
            // Clear existing timer
            clearAutoRefresh();

            // Set active preset
            state.activePreset = preset;

            // Update UI
            updateActivePreset();

            // Trigger a refresh immediately when setting a new preset
            if (state.onServiceOrdersPage) {
                triggerRefresh();
            }

            // Only start timer if enabled and on service orders page
            if (state.enabled && state.onServiceOrdersPage) {
                state.refreshTimer = setInterval(() => {
                    triggerRefresh();
                }, preset.seconds * 1000);
            }

            // Save state
            saveState();
        } catch (error) {
            console.error('Error starting auto refresh:', error);
        }
    }

    // Clear auto refresh timer
    function clearAutoRefresh() {
        try {
            if (state.refreshTimer) {
                clearInterval(state.refreshTimer);
                state.refreshTimer = null;
            }
        } catch (error) {
            console.error('Error clearing auto refresh:', error);
        }
    }

    // Update active preset highlighting
    function updateActivePreset() {
        try {
            // Remove active class from all presets
            document.querySelectorAll('.auto-refresh-preset').forEach(btn => {
                btn.classList.remove('active');
            });

            // Add active class to current preset
            if (state.activePreset) {
                const activeBtn = document.querySelector(`.auto-refresh-preset[data-seconds="${state.activePreset.seconds}"]`);
                if (activeBtn) {
                    activeBtn.classList.add('active');
                }
            }
        } catch (error) {
            console.error('Error updating active preset:', error);
        }
    }

    // Toggle auto refresh enabled state
    function toggleEnabled() {
        try {
            state.enabled = !state.enabled;

            // Update UI
            const container = document.getElementById('auto-refresh-container');
            if (container) {
                if (state.enabled) {
                    container.classList.remove('auto-refresh-disabled');
                    if (state.activePreset && state.onServiceOrdersPage) {
                        startAutoRefresh(state.activePreset);
                    }
                } else {
                    container.classList.add('auto-refresh-disabled');
                    clearAutoRefresh();
                    updateStatus('info', 'Auto refresh is off');
                }
            }

            // Save state
            saveState();
        } catch (error) {
            console.error('Error toggling enabled state:', error);
        }
    }

    // Toggle collapsed state
    function toggleCollapsed() {
        try {
            state.collapsed = !state.collapsed;

            // Update UI
            const body = document.getElementById('auto-refresh-body');
            const collapseBtn = document.getElementById('auto-refresh-collapse');

            if (body && collapseBtn) {
                if (state.collapsed) {
                    body.style.maxHeight = '0';
                    collapseBtn.textContent = '+';
                } else {
                    body.style.maxHeight = '500px';
                    collapseBtn.textContent = '-';
                }
            }

            // Save state
            saveState();
        } catch (error) {
            console.error('Error toggling collapsed state:', error);
        }
    }

    // Save state to GM storage
    function saveState() {
        try {
            GM_setValue('autoRefreshPresets', state.presets);
            GM_setValue('autoRefreshPosition', state.position);
            GM_setValue('autoRefreshEnabled', state.enabled);
            GM_setValue('autoRefreshCollapsed', state.collapsed);
            GM_setValue('autoRefreshActivePreset', state.activePreset);
        } catch (error) {
            console.error('Error saving state:', error);
        }
    }

    // Load state from GM storage
    function loadState() {
        try {
            state.presets = GM_getValue('autoRefreshPresets', DEFAULT_PRESETS);
            state.position = GM_getValue('autoRefreshPosition', { x: 20, y: 20 });
            state.enabled = GM_getValue('autoRefreshEnabled', true);
            state.collapsed = GM_getValue('autoRefreshCollapsed', false);

            // Get the previously saved active preset, or default to 30s preset on first launch
            const savedActivePreset = GM_getValue('autoRefreshActivePreset', null);
            if (savedActivePreset) {
                state.activePreset = savedActivePreset;
            } else {
                // Find the 30s preset and set it as default
                state.activePreset = state.presets.find(preset => preset.seconds === 30) || state.presets[1];
            }
        } catch (error) {
            console.error('Error loading state:', error);
            // Fallback to defaults
            state.presets = DEFAULT_PRESETS;
            state.position = { x: 20, y: 20 };
            state.enabled = true;
            state.collapsed = false;
            // Set default preset to 30s
            state.activePreset = state.presets.find(preset => preset.seconds === 30) || state.presets[1];
        }
    }

    // Format preset name based on seconds with improved logic
    function formatPresetName(seconds) {
        if (seconds < 60) {
            return `${seconds}s`;
        } else {
            return `${Math.floor(seconds / 60)}min`;
        }
    }

    // Show preset edit panel
    function showEditPanel(preset) {
        try {
            state.editingPreset = preset;

            // Define your exact preset values
            const timeOptions = [5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 120, 180, 240, 300, 360, 420, 480, 540, 600];

            // Create and append overlay
            const overlay = document.createElement('div');
            overlay.id = 'auto-refresh-edit-overlay';

            const panel = document.createElement('div');
            panel.id = 'auto-refresh-edit-panel';

            // Create options HTML
            const optionsHTML = timeOptions.map(value => {
                const selected = value === preset.seconds ? 'selected' : '';
                const label = value < 60 ? `${value}s` : `${Math.floor(value/60)}min`;
                return `<option value="${value}" ${selected}>${label}</option>`;
            }).join('');

            panel.innerHTML = `
        <h3 style="font-size: 14px; margin-top: 0;">Edit Preset</h3>
        <div class="auto-refresh-form-group">
            <label class="auto-refresh-form-label">Select Interval:</label>
            <select id="edit-preset-seconds" class="auto-refresh-form-input" style="appearance: auto; background-color: white;">
                ${optionsHTML}
            </select>
            <div id="edit-preset-error" style="color: #f44336; font-size: 11px; margin: 8px 0; display: none;"></div>
        </div>
        <div class="auto-refresh-form-buttons">
            <button class="auto-refresh-form-button cancel">Cancel</button>
            <button class="auto-refresh-form-button save">Save</button>
        </div>
        `;

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

            // Add event listeners
            overlay.querySelector('.cancel').addEventListener('click', hideEditPanel);
            overlay.querySelector('.save').addEventListener('click', saveEditedPreset);

            // Handle pressing Enter key
            const selectInput = document.getElementById('edit-preset-seconds');
            selectInput.addEventListener('keydown', (e) => {
                if (e.key === 'Enter') {
                    e.preventDefault();
                    saveEditedPreset();
                }
            });

            // Focus the select field
            selectInput.focus();
        } catch (error) {
            console.error('Error showing edit panel:', error);
        }
    }
    // Save edited preset with duplicate check
    function saveEditedPreset() {
        try {
            const secondsInput = document.getElementById('edit-preset-seconds');
            const errorElement = document.getElementById('edit-preset-error') ||
                  (() => {
                      // Create error element if it doesn't exist
                      const el = document.createElement('div');
                      el.id = 'edit-preset-error';
                      el.style.cssText = 'color: #f44336; font-size: 11px; margin: 8px 0; display: none;';
                      secondsInput.parentNode.insertBefore(el, secondsInput.nextSibling);
                      return el;
                  })();

            if (!secondsInput) return;

            const seconds = parseInt(secondsInput.value, 10);

            if (isNaN(seconds)) {
                return;
            }

            // Check if another preset already has this value
            const duplicatePreset = state.presets.find(p =>
                                                       p.seconds === seconds &&
                                                       !(p.name === state.editingPreset.name && p.seconds === state.editingPreset.seconds)
                                                      );

            if (duplicatePreset) {
                errorElement.textContent = `Preset "${duplicatePreset.name}" already uses this interval. Please choose another value.`;
                errorElement.style.display = 'block';
                return;
            }

            // Auto-format name based on seconds value
            const name = formatPresetName(seconds);

            // Find and update the preset
            const presetIndex = state.presets.findIndex(p =>
                                                        p.name === state.editingPreset.name && p.seconds === state.editingPreset.seconds
                                                       );

            if (presetIndex >= 0) {
                state.presets[presetIndex] = { name, seconds };

                // If this was the active preset, update it
                if (state.activePreset &&
                    state.activePreset.name === state.editingPreset.name &&
                    state.activePreset.seconds === state.editingPreset.seconds) {
                    state.activePreset = { name, seconds };
                    if (state.enabled && state.onServiceOrdersPage) {
                        startAutoRefresh(state.activePreset);
                    }
                }

                // Update UI
                createOrUpdateUI();

                // Save state
                saveState();
            }

            hideEditPanel();
        } catch (error) {
            console.error('Error saving edited preset:', error);
        }
    }
    // Hide preset edit panel
    function hideEditPanel() {
        try {
            const overlay = document.getElementById('auto-refresh-edit-overlay');
            if (overlay) {
                overlay.remove();
            }
            state.editingPreset = null;
        } catch (error) {
            console.error('Error hiding edit panel:', error);
        }
    }

    // Make element draggable - Enhanced for both mouse and touch devices
    function makeDraggable(element, handleElement) {
        try {
            let startX, startY, initialX, initialY;
            let isDragging = false;

            const onStart = (e) => {
                // Don't initiate drag if it's a button or control
                if (e.target.id === 'auto-refresh-collapse' ||
                    e.target.id === 'auto-refresh-toggle' ||
                    e.target.closest('button') ||
                    e.target.closest('.switch')) {
                    return;
                }

                isDragging = true;
                state.isDragging = true;

                // Get starting positions
                if (e.type === 'mousedown') {
                    startX = e.clientX;
                    startY = e.clientY;
                } else if (e.type === 'touchstart') {
                    startX = e.touches[0].clientX;
                    startY = e.touches[0].clientY;
                }

                initialX = element.offsetLeft;
                initialY = element.offsetTop;

                // Add move and end event listeners
                if (e.type === 'mousedown') {
                    document.addEventListener('mousemove', onMove);
                    document.addEventListener('mouseup', onEnd);
                } else if (e.type === 'touchstart') {
                    document.addEventListener('touchmove', onMove, { passive: false });
                    document.addEventListener('touchend', onEnd);
                }

                // Prevent default behavior only for the handle
                if (e.target === handleElement || handleElement.contains(e.target)) {
                    if (e.preventDefault) e.preventDefault();
                }
            };

            const onMove = (e) => {
                if (!isDragging) return;

                // Calculate new position
                let clientX, clientY;
                if (e.type === 'mousemove') {
                    clientX = e.clientX;
                    clientY = e.clientY;
                } else if (e.type === 'touchmove') {
                    clientX = e.touches[0].clientX;
                    clientY = e.touches[0].clientY;
                    e.preventDefault(); // Prevent scrolling when dragging
                }

                // Calculate new position
                const deltaX = clientX - startX;
                const deltaY = clientY - startY;

                const newLeft = initialX + deltaX;
                const newTop = initialY + deltaY;

                // Ensure the element stays within viewport bounds
                const maxTop = window.innerHeight - element.offsetHeight;
                const maxLeft = window.innerWidth - element.offsetWidth;

                element.style.top = `${Math.min(Math.max(0, newTop), maxTop)}px`;
                element.style.left = `${Math.min(Math.max(0, newLeft), maxLeft)}px`;
            };

            const onEnd = (e) => {
                isDragging = false;

                // Wait a short moment before setting isDragging to false to prevent accidental clicks
                setTimeout(() => {
                    state.isDragging = false;
                }, 50);

                // Remove move and end event listeners
                document.removeEventListener('mousemove', onMove);
                document.removeEventListener('mouseup', onEnd);
                document.removeEventListener('touchmove', onMove);
                document.removeEventListener('touchend', onEnd);

                // Save the new position
                state.position = {
                    x: parseInt(element.style.left, 10) || 20,
                    y: parseInt(element.style.top, 10) || 20
                };
                saveState();
            };

        handleElement.addEventListener('mousedown', onStart);
        handleElement.addEventListener('touchstart', onStart, { passive: true });
    } catch (error) {
        console.error('Error making element draggable:', error);
    }
}
   // Create or update UI
    function createOrUpdateUI() {
        try {
            // Check if UI already exists
            let container = document.getElementById('auto-refresh-container');

            if (!container) {
                // Create container
                container = document.createElement('div');
                container.id = 'auto-refresh-container';
                document.body.appendChild(container);

                // Set initial position
                container.style.left = `${state.position.x}px`;
                container.style.top = `${state.position.y}px`;

                // Create UI structure - Updated for more compact layout with better spacing
                container.innerHTML = `
                <div id="auto-refresh-header">
                    <span id="auto-refresh-title">Auto Refresh</span>
                    <div id="auto-refresh-controls">
                        <label class="switch">
                            <input type="checkbox" id="auto-refresh-toggle" ${state.enabled ? 'checked' : ''}>
                            <span class="slider"></span>
                        </label>
                        <span id="auto-refresh-collapse">${state.collapsed ? '+' : '-'}</span>
                    </div>
                </div>
                <div id="auto-refresh-body" style="max-height: ${state.collapsed ? '0' : '500px'};">
                    <div id="auto-refresh-presets"></div>
                    <div id="auto-refresh-status" class="info">Initializing...</div>
                </div>
            `;

                // Add event listeners
                document.getElementById('auto-refresh-toggle').addEventListener('change', toggleEnabled);

                // IMPORTANT FIX: Add event listener directly without cloning
                const collapseBtn = document.getElementById('auto-refresh-collapse');
                if (collapseBtn) {
                    // Remove previous listeners if any (just in case)
                    const newCollapseBtn = collapseBtn.cloneNode(true);
                    if (collapseBtn.parentNode) {
                        collapseBtn.parentNode.replaceChild(newCollapseBtn, collapseBtn);
                    }

                    // Get fresh reference to the button that's now in the DOM
                    const domCollapseBtn = document.getElementById('auto-refresh-collapse');
                    if (domCollapseBtn) {
                        // Use normal click handler - simpler is better
                        domCollapseBtn.onclick = function(e) {
                            // Stop propagation to prevent draggable from interfering
                            if (e) {
                                e.stopPropagation();
                                e.preventDefault();
                            }
                            toggleCollapsed();
                            return false;
                        };
                    }
                }

                // Make draggable - with special handling for the collapse button
                makeDraggable(container, document.getElementById('auto-refresh-header'));

                // If disabled, add class
                if (!state.enabled) {
                    container.classList.add('auto-refresh-disabled');
                }
            }

            // Update presets
            const presetsContainer = document.getElementById('auto-refresh-presets');
            if (presetsContainer) {
                presetsContainer.innerHTML = '';

                state.presets.forEach(preset => {
                    const presetBtn = document.createElement('button');
                    presetBtn.className = 'auto-refresh-preset';
                    presetBtn.textContent = preset.name;
                    presetBtn.dataset.seconds = preset.seconds;

                    // If this is the active preset, add active class
                    if (state.activePreset &&
                        state.activePreset.name === preset.name &&
                        state.activePreset.seconds === preset.seconds) {
                        presetBtn.classList.add('active');
                    }

                    // Add click event
                    presetBtn.addEventListener('click', (e) => {
                        // Only process click if we're not in a drag operation
                        if (!state.isDragging && state.enabled) {
                            startAutoRefresh(preset);
                        }
                    });

                    // Add context menu / long press
                    presetBtn.addEventListener('contextmenu', (e) => {
                        e.preventDefault();
                        showEditPanel(preset);
                    });

                    // Long press detection for touch devices
                    let longPressTimer;
                    let longPressStarted = false;

                    presetBtn.addEventListener('touchstart', (e) => {
                        longPressStarted = true;
                        longPressTimer = setTimeout(() => {
                            if (longPressStarted) {
                                showEditPanel(preset);
                            }
                        }, 800);
                    });

                    presetBtn.addEventListener('touchmove', () => {
                        // Cancel long press if user moves finger
                        longPressStarted = false;
                        clearTimeout(longPressTimer);
                    });

                    presetBtn.addEventListener('touchend', () => {
                        // Only process click if we're not in a drag operation and not a long press
                        if (!state.isDragging && longPressStarted && state.enabled) {
                            startAutoRefresh(preset);
                        }
                        longPressStarted = false;
                        clearTimeout(longPressTimer);
                    });

                    presetsContainer.appendChild(presetBtn);
                });
            }

            // Update status based on current state - Simplified status messages
            updatePageStatus();
        } catch (error) {
            console.error('Error creating or updating UI:', error);
        }
    }

    // Update page status based on current state
    function updatePageStatus() {
        if (!state.onServiceOrdersPage) {
            updateStatus('warning', 'Please navigate to Service Orders page');
        } else if (!state.enabled) {
            updateStatus('info', 'Auto refresh is off');
        } else if (state.lastRefreshTime) {
            updateStatus('success', `Last checked: ${formatTime(state.lastRefreshTime)}`);
        } else {
            updateStatus('info', 'Select a refresh interval');
        }
    }

    // Check page and update UI accordingly
    function checkPageAndUpdateUI() {
        try {
            const wasOnServiceOrdersPage = state.onServiceOrdersPage;
            state.onServiceOrdersPage = checkIfOnServiceOrdersPage();

            // Reset refresh button observed state if we're not on the service orders page
            if (!state.onServiceOrdersPage) {
                state.refreshButtonObserved = false;
            }

            // Update status based on current state
            updatePageStatus();

            // If we just arrived at the service orders page
            if (!wasOnServiceOrdersPage && state.onServiceOrdersPage) {
                // Trigger an immediate refresh
                if (state.enabled) {
                    triggerRefresh();

                    // If we have an active preset, start auto refresh
                    if (state.activePreset) {
                        startAutoRefresh(state.activePreset);
                    }
                }
            } else if (wasOnServiceOrdersPage && !state.onServiceOrdersPage) {
                // If we've left the service orders page
                clearAutoRefresh();
            }
        } catch (error) {
            console.error('Error checking page and updating UI:', error);
        }
    }

    // Setup observer
    function setupObserver() {
        try {
            const observer = new MutationObserver(mutations => {
                // Only check once every second at most to avoid performance issues
                if (!state.observerDebounce) {
                    state.observerDebounce = true;
                    setTimeout(() => {
                        checkPageAndUpdateUI();
                        state.observerDebounce = false;
                    }, 1000);
                }
            });

            // Start observing
            observer.observe(document.body, { childList: true, subtree: true });
        } catch (error) {
            console.error('Error setting up observer:', error);
        }
    }

    // Initialize the script
    function init() {
        try {
            // Load saved state
            loadState();

            // Create initial UI
            createOrUpdateUI();

            // Setup observer to detect page changes
            setupObserver();

            // Check if we're on service orders page initially
            checkPageAndUpdateUI();

            // If we have an active preset and are on service orders page, start auto refresh
            if (state.activePreset && state.onServiceOrdersPage && state.enabled) {
                startAutoRefresh(state.activePreset);
            }

            state.initialized = true;
        } catch (error) {
            console.error('Error initializing Auto Refresh Tool:', error);
        }
    }

    // Handle errors globally to prevent leakage to user
    window.addEventListener('error', function(event) {
        // Check if error is from our script
        if (event.filename && event.filename.includes('Auto Refresh Tool')) {
            console.error('Auto Refresh Tool error:', event.error);
            event.preventDefault();
            return true;
        }
        return false;
    }, true);

    // Try multiple initialization attempts
    let initAttempts = 0;
    const maxInitAttempts = 3;

    function attemptInit() {
        if (initAttempts >= maxInitAttempts) {
            console.error('Failed to initialize Auto Refresh Tool after multiple attempts');
            return;
        }

        if (!state.initialized) {
            initAttempts++;
            init();

            // Schedule another attempt if not successful
            if (!state.initialized) {
                setTimeout(attemptInit, 2000);
            }
        }
    }

    // Start the initialization process with a slight delay
    setTimeout(attemptInit, 1000);

    // Backup initialization - forcefully create UI after page has likely loaded
    setTimeout(() => {
        if (!document.getElementById('auto-refresh-container')) {
            createOrUpdateUI();
            checkPageAndUpdateUI();
        }
    }, 5000);
})();