Auto Refresh Interface for Hotsauce

Auto refresh for HotSOS

目前为 2025-04-28 提交的版本。查看 最新版本

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