Unfulfilled Reservation Highlighter

Highlight unfulfilled reservations in red

// ==UserScript==
// @name         Unfulfilled Reservation Highlighter
// @namespace    http://tampermonkey.net/
// @version      1.1
// @description  Highlight unfulfilled reservations in red
// @author       ipsum (with Claude.ai)
// @match        https://tacomatoollibrary.myturn.com/library/orgInventory/listReservations*
// @match        https://*.myturn.com/library/orgInventory/listReservations*
// @grant        none
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    function parseReservationDate(dateString) {
        // Parse dates like "Jun 12, 2025–Jun 26, 2025"
        // We want the start date (before the dash)
        const startDatePart = dateString.split('–')[0].trim();

        // Convert to a Date object
        const date = new Date(startDatePart);
        return date;
    }

    function isUnfulfilled(reservationDate) {
        const today = new Date();
        today.setHours(0, 0, 0, 0); // Set to start of day for comparison

        const resDate = new Date(reservationDate);
        resDate.setHours(0, 0, 0, 0); // Set to start of day for comparison

        return resDate < today;
    }

    function highlightUnfulfilledReservations() {
        // Find all reservation panels
        const reservationPanels = document.querySelectorAll('.panel.reservation');

        reservationPanels.forEach(panel => {
            // Find the date element within each panel
            const dateElement = panel.querySelector('.panel-heading .col-sm-7 strong');

            if (dateElement) {
                const dateText = dateElement.textContent.trim();

                try {
                    const reservationDate = parseReservationDate(dateText);

                    if (isUnfulfilled(reservationDate)) {
                        // Add the unfulfilled class
                        dateElement.classList.add('unfulfilled-reservation-date');
                    } else {
                        // Remove the class if not unfulfilled (in case of dynamic updates)
                        dateElement.classList.remove('unfulfilled-reservation-date');
                    }
                } catch (error) {
                    console.log('Could not parse date:', dateText, error);
                }
            }
        });
    }

    // Initialize when page loads
    function initialize() {
        // Wait a bit for the page to fully load
        setTimeout(() => {
            highlightUnfulfilledReservations();
        }, 1000);
    }

    // Run when DOM is ready
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', initialize);
    } else {
        initialize();
    }

    // Also watch for dynamic content changes (like filtering)
    const observer = new MutationObserver((mutations) => {
        let shouldUpdate = false;
        mutations.forEach((mutation) => {
            if (mutation.type === 'childList' &&
                (mutation.target.classList.contains('portlet-body') ||
                 mutation.target.querySelector('.panel.reservation'))) {
                shouldUpdate = true;
            }
        });

        if (shouldUpdate) {
            setTimeout(highlightUnfulfilledReservations, 500);
        }
    });

    observer.observe(document.body, {
        childList: true,
        subtree: true
    });

})();