Olympus Timesheet Reminder Hider v1.0.0

← Back to User Scripts

Initial release of the Olympus Timesheet Reminder Hider user script.

Script Content

// ==UserScript==
// @name         Olympus: Timesheet Reminder Hider
// @namespace    https://www.timhilton.xyz/user-scripts
// @version      1.0.1
// @description  Hide missing timesheet badges for specific dates or today's date
// @author       Tim Hilton using Claude
// @match        https://olympus.audacia.co.uk/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    const LOG_PREFIX = '[Olympus: Timesheet Reminder Hider]';
    const STORAGE_KEY = 'olympus-timesheet-reminder-hider-ignored-dates';
    const API_URL = 'https://olympus-api.audacia.co.uk/api/timesheets/missing';
    const STYLE_ID = 'timesheet-reminder-hider-style';

    console.debug(`${LOG_PREFIX} Script initialized`);

    /**
     * Get today's date in YYYY-MM-DD format
     */
    function getTodayDate() {
        const today = new Date();
        return today.toISOString().split('T')[0];
    }

    /**
     * Get the list of ignored dates from localStorage
     */
    function getIgnoredDates() {
        const stored = localStorage.getItem(STORAGE_KEY);
        return stored ? JSON.parse(stored) : [];
    }

    /**
     * Save the list of ignored dates to localStorage
     */
    function saveIgnoredDates(dates) {
        localStorage.setItem(STORAGE_KEY, JSON.stringify(dates));
    }

    /**
     * Add a date to the ignore list
     */
    function ignoreDate(date) {
        const dates = getIgnoredDates();
        if (!dates.includes(date)) {
            dates.push(date);
            saveIgnoredDates(dates);
            console.log(`${LOG_PREFIX} ✅ Added ${date} to ignore list`);
        } else {
            console.debug(`${LOG_PREFIX} ${date} already in ignore list`);
        }
    }

    /**
     * Remove a date from the ignore list
     */
    function unignoreDate(date) {
        const dates = getIgnoredDates();
        const index = dates.indexOf(date);
        if (index > -1) {
            dates.splice(index, 1);
            saveIgnoredDates(dates);
            console.log(`${LOG_PREFIX} ✅ Removed ${date} from ignore list`);
        } else {
            console.debug(`${LOG_PREFIX} ${date} not in ignore list`);
        }
    }

    /**
     * List all ignored dates
     */
    function listIgnoredDates() {
        const dates = getIgnoredDates();
        if (dates.length === 0) {
            console.log(`${LOG_PREFIX} No dates in ignore list`);
        } else {
            console.log(`${LOG_PREFIX} Ignored dates: ${dates.join(', ')}`);
        }
        return dates;
    }

    /**
     * Clear all ignored dates
     */
    function clearIgnoredDates() {
        saveIgnoredDates([]);
        console.log(`${LOG_PREFIX} ✅ Cleared all ignored dates`);
    }

    /**
     * Inject CSS to hide the badge
     */
    function hideBadge() {
        if (document.getElementById(STYLE_ID)) {
            return; // Already hidden
        }
        const style = document.createElement('style');
        style.id = STYLE_ID;
        style.textContent = `
            span.mud-badge-wrapper span.missing-timesheet-badge {
                display: none;
            }
        `;
        document.head.appendChild(style);
        console.log(`${LOG_PREFIX} 🎉 Badge hidden`);
    }

    /**
     * Remove the CSS that hides the badge
     */
    function showBadge() {
        const style = document.getElementById(STYLE_ID);
        if (style) {
            style.remove();
            console.log(`${LOG_PREFIX} ✅ Badge shown`);
        }
    }

    /**
     * Filter missing dates and update badge visibility
     */
    function processMissingDates(dates) {
        const today = getTodayDate();
        const ignoredDates = getIgnoredDates();

        // Filter out today and any ignored dates
        const visibleDates = dates.filter(date => {
            if (date === today) return false;
            if (ignoredDates.includes(date)) return false;
            return true;
        });

        console.debug(`${LOG_PREFIX} Original dates: ${dates.length}, visible after filtering: ${visibleDates.length}`);

        if (visibleDates.length === 0) {
            hideBadge();
        } else {
            showBadge();
        }
    }

    /**
     * Override fetch to intercept API responses
     */
    const originalFetch = window.fetch;
    window.fetch = async function(...args) {
        const response = await originalFetch.apply(this, args);

        // Check if this is the missing timesheets API
        const url = args[0]?.url || args[0];
        if (typeof url === 'string' && url.includes('/api/timesheets/missing')) {
            console.debug(`${LOG_PREFIX} Intercepted missing timesheets API call`);
            // Clone the response so we can read it without consuming it
            const clonedResponse = response.clone();
            try {
                const data = await clonedResponse.json();
                if (Array.isArray(data)) {
                    processMissingDates(data);
                }
            } catch (e) {
                console.log(`${LOG_PREFIX} ❌ Error parsing fetch response: ${e.message}`);
            }
        }

        return response;
    };

    /**
     * Override XMLHttpRequest to intercept API responses
     */
    const originalXHROpen = XMLHttpRequest.prototype.open;
    const originalXHRSend = XMLHttpRequest.prototype.send;

    XMLHttpRequest.prototype.open = function(method, url, ...rest) {
        this._timesheetHiderUrl = url;
        return originalXHROpen.apply(this, [method, url, ...rest]);
    };

    XMLHttpRequest.prototype.send = function(...args) {
        if (this._timesheetHiderUrl && this._timesheetHiderUrl.includes('/api/timesheets/missing')) {
            console.debug(`${LOG_PREFIX} Intercepted missing timesheets XHR call`);
            this.addEventListener('load', function() {
                try {
                    const data = JSON.parse(this.responseText);
                    if (Array.isArray(data)) {
                        processMissingDates(data);
                    }
                } catch (e) {
                    console.log(`${LOG_PREFIX} ❌ Error parsing XHR response: ${e.message}`);
                }
            });
        }
        return originalXHRSend.apply(this, args);
    };

    // Expose functions globally for console access
    window.timesheetHider = {
        ignore: ignoreDate,
        unignore: unignoreDate,
        list: listIgnoredDates,
        clear: clearIgnoredDates,
        help: function() {
            console.log(`${LOG_PREFIX} Console Commands:
  timesheetHider.ignore('YYYY-MM-DD')   - Add a date to the ignore list
  timesheetHider.unignore('YYYY-MM-DD') - Remove a date from the ignore list
  timesheetHider.list()                 - Show all ignored dates
  timesheetHider.clear()                - Clear all ignored dates
  timesheetHider.help()                 - Show this help message

Note: Today's date is always ignored automatically.`);
        }
    };

    console.debug(`${LOG_PREFIX} Ready. Type timesheetHider.help() for commands`);
})();