AtoZ Working time calculator

Working time calculator for AtoZ - English

// ==UserScript==
// @name         AtoZ Working time calculator
// @namespace    https://atoz.amazon.work
// @version      1.0
// @description  Working time calculator for AtoZ - English
// @author       @celvip
// @match        https://atoz.amazon.work/timecard*
// @match        https://atoz.amazon.work/schedule*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    // Helper functions for localStorage
    function setValue(key, value) {
        try {
            localStorage.setItem(key, JSON.stringify(value));
        } catch (e) {
            console.error('Error saving to localStorage:', e);
        }
    }

    function getValue(key, defaultValue) {
        try {
            const value = localStorage.getItem(key);
            return value ? JSON.parse(value) : defaultValue;
        } catch (e) {
            console.error('Error reading from localStorage:', e);
            return defaultValue;
        }
    }

    let breakTime = getValue('breakTime', 30);
    let workTime = getValue('workTime', 8);
    const FIXED_MAX_BREAK = 45; // Fixed break time for maximum working time

    // Extended translations for different languages and variations
    const TRANSLATIONS = {
        IN: ['Einstempeln', 'Clock in', 'Punch in', 'In'],
        OUT: ['Ausstempeln', 'Clock out', 'Punch out', 'Out'],
        MISSING: ['Fehlende Stempelzeit', 'Missing punch', 'Missed Punch', 'Missed punch', '--:--']
    };

    const COLORS = {
        GREEN: {
            bg: '#c6efce',
            text: '#006100'
        },
        YELLOW: {
            bg: '#ffeb9c',
            text: '#9c6500'
        },
        RED: {
            bg: '#ffc7ce',
            text: '#9c0006'
        }
    };

    function findPunchTimes() {
        let punchInTime = null;
        let punchOutTime = null;
        let missingPunch = false;

        console.log("Searching for punch times...");

        // Define search terms for both languages
        const searchTerms = {
            punchIn: ['Einstempeln', 'Clock In', 'Punch In', 'Time In', 'In'],
            punchOut: ['Ausstempeln', 'Clock Out', 'Punch Out', 'Time Out', 'Out'],
            missing: ['Fehlende Stempelzeit', 'Missing Punch', 'Missed Punch', '--:--']
        };

        // Search for all elements containing text
        const elements = document.getElementsByTagName('*');

        for (const element of elements) {
            const text = element.textContent.trim();

            // Search for Clock In
            if (searchTerms.punchIn.some(term => text.includes(term))) {
                const timeMatch = text.match(/\d{1,2}:\d{2}/);
                if (timeMatch) {
                    punchInTime = timeMatch[0].padStart(5, '0');
                    console.log("Punch in time found:", punchInTime);
                }
            }

            // Search for Missing Punch
            if (searchTerms.missing.some(term => text.includes(term))) {
                missingPunch = true;
                console.log("Missing punch found");
            }

            // If no missing punch, search for Clock Out
            if (!missingPunch && searchTerms.punchOut.some(term => text.includes(term))) {
                const timeMatch = text.match(/\d{1,2}:\d{2}/);
                if (timeMatch) {
                    punchOutTime = timeMatch[0].padStart(5, '0');
                    console.log("Punch out time found:", punchOutTime);
                }
            }
        }

        // Alternative search method for more complex cases
        if (!punchInTime) {
            const timeNodes = document.evaluate(
                "//*[contains(text(), ':')]",
                document,
                null,
                XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
                null
            );

            for (let i = 0; i < timeNodes.snapshotLength; i++) {
                const node = timeNodes.snapshotItem(i);
                const text = node.textContent.trim();
                const timeMatch = text.match(/\d{1,2}:\d{2}/);

                if (timeMatch) {
                    const parentText = node.parentElement?.textContent.toLowerCase() || '';
                    if (searchTerms.punchIn.some(term => parentText.toLowerCase().includes(term.toLowerCase()))) {
                        punchInTime = timeMatch[0].padStart(5, '0');
                        console.log("Alternative method - Punch in time found:", punchInTime);
                    } else if (!missingPunch && searchTerms.punchOut.some(term => parentText.toLowerCase().includes(term.toLowerCase()))) {
                        punchOutTime = timeMatch[0].padStart(5, '0');
                        console.log("Alternative method - Punch out time found:", punchOutTime);
                    }
                }
            }
        }

        // If "Missing Punch" was found, reset punch out time
        if (missingPunch) {
            punchOutTime = null;
        }

        console.log("Final times - In:", punchInTime, "Out:", punchOutTime, "Missing:", missingPunch);
        return { punchInTime, punchOutTime };
    }

    function getCurrentWorktime(startTime, endTime) {
        if (endTime) {
            return getTimeDifference(startTime, endTime);
        }

        const now = new Date();
        const nowHours = now.getHours();
        const nowMinutes = now.getMinutes();
        const [startHours, startMinutes] = startTime.split(':').map(Number);

        let hours = nowHours - startHours;
        let minutes = nowMinutes - startMinutes;

        if (minutes < 0) {
            hours--;
            minutes += 60;
        }
        if (hours < 0) {
            hours += 24;
        }

        return hours + (minutes / 60);
    }

    function getTimeDifference(startTime, endTime) {
        const [startHours, startMinutes] = startTime.split(':').map(Number);
        const [endHours, endMinutes] = endTime.split(':').map(Number);

        let hours = endHours - startHours;
        let minutes = endMinutes - startMinutes;

        if (minutes < 0) {
            hours--;
            minutes += 60;
        }
        if (hours < 0) {
            hours += 24;
        }

        return hours + (minutes / 60);
    }

    function getBackgroundColor(hours) {
        if (hours <= 8.5) return COLORS.GREEN;
        if (hours <= 10) return COLORS.YELLOW;
        return COLORS.RED;
    }

    function calculateEndTime(startTime, workHours, breakMinutes) {
        const [hours, minutes] = startTime.split(':').map(Number);
        let endHours = hours + workHours;
        let endMinutes = minutes + breakMinutes;

        if (endMinutes >= 60) {
            endHours += Math.floor(endMinutes / 60);
            endMinutes = endMinutes % 60;
        }
        if (endHours >= 24) {
            endHours -= 24;
        }

        return `${String(endHours).padStart(2, '0')}:${String(endMinutes).padStart(2, '0')}`;
    }

    function formatHoursAndMinutes(hours) {
        if (isNaN(hours)) return "0:00";

        const fullHours = Math.floor(hours);
        const minutes = Math.round((hours - fullHours) * 60);
        return `${fullHours}h ${minutes.toString().padStart(2, '0')}min`;
    }

    function updateDisplay(punchInTime = null, punchOutTime = null) {
        const display = document.getElementById('timeCalculator');

        if (display) {
            let content;
            const currentTime = new Date().toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false });

            if (punchInTime) {
                const currentWorktime = getCurrentWorktime(punchInTime, punchOutTime);
                const colors = getBackgroundColor(currentWorktime);
                const maxEndTime = calculateEndTime(punchInTime, 10, FIXED_MAX_BREAK);

                display.style.backgroundColor = colors.bg;
                content = `
                    <div style="margin-bottom: 12px; font-weight: bold; color: ${colors.text}; font-size: 20px; text-align: center;"><u>Working time calculator</u></div>
                    <div style="margin-bottom: 8px; color: ${colors.text};">Current time: <strong>${currentTime}</strong></div>
                    <div style="margin-bottom: 8px; color: ${colors.text};">Stamp IN: <strong>${punchInTime}</strong></div>
                    ${punchOutTime ? `<div style="margin-bottom: 8px; color: ${colors.text};">Stamp OUT: <strong>${punchOutTime}</strong></div>` : ''}
                    <div style="margin-bottom: 8px; color: ${colors.text};">
                        Working time:
                        <select id="workTimeSelect" style="margin-left: 5px; background-color: white;">
                            <option value="6" ${workTime === 6 ? 'selected' : ''}>6 hours</option>
                            <option value="8" ${workTime === 8 ? 'selected' : ''}>8 hours</option>
                            <option value="9" ${workTime === 9 ? 'selected' : ''}>9 hours</option>
                            <option value="10" ${workTime === 10 ? 'selected' : ''}>10 hours</option>
                        </select>
                    </div>
                    <div style="margin-bottom: 8px; color: ${colors.text};">
                        Break time:
                        <select id="breakTimeSelect" style="margin-left: 5px; background-color: white;">
                            <option value="0" ${breakTime === 0 ? 'selected' : ''}>No break</option>
                            <option value="30" ${breakTime === 30 ? 'selected' : ''}>30 minutes</option>
                            <option value="45" ${breakTime === 45 ? 'selected' : ''}>45 minutes</option>
                            <option value="60" ${breakTime === 60 ? 'selected' : ''}>60 minutes</option>
                        </select>
                    </div>
                    ${!punchOutTime ? `
                    <div style="margin-top: 12px; font-weight: bold; color: ${colors.text};">
                        Planned end of work: <strong>${calculateEndTime(punchInTime, workTime, breakTime)}</strong>
                    </div>
                    <div style="margin-top: 4px; font-weight: bold; color: red;">
                        Latest end of work: <span style="color: red; font-weight: bold; background-color: #ffdddd; padding: 2px 5px; border-radius: 3px;">❗${maxEndTime}❗</span>
                    </div>
                    ` : ''}
                    <div style="font-size: 12px; color: ${colors.text}; margin-top: 8px;">
                        ${punchOutTime ? 'Total' : 'Current'} working time: ${formatHoursAndMinutes(currentWorktime)}
                    </div>
                    ${!punchOutTime ? `
                        <div style="font-size: 12px; color: ${colors.text}; margin-top: 4px;">
                            Total planned time: ${workTime}h ${breakTime}min
                        </div>
                        <div style="font-size: 12px; color: ${colors.text}; margin-top: 4px;">
                            Maximum total time: 10h ${FIXED_MAX_BREAK}min
                        </div>
                    ` : ''}
                `;
            } else {
                display.style.backgroundColor = 'white';
                content = `
                    <div style="margin-bottom: 12px; font-weight: bold; color: #666;">Working time calculator</div>
                    <div style="margin-bottom: 8px; color: #666;">Current time: <strong>${currentTime}</strong></div>
                    <div style="margin-bottom: 8px; color: #666;">Stamp IN: <strong>No punch time found</strong></div>
                    <div style="margin-bottom: 8px; color: #666;">
                        Working time:
                        <select id="workTimeSelect" style="margin-left: 5px; background-color: white;">
                            <option value="6" ${workTime === 6 ? 'selected' : ''}>6 hours</option>
                            <option value="8" ${workTime === 8 ? 'selected' : ''}>8 hours</option>
                            <option value="9" ${workTime === 9 ? 'selected' : ''}>9 hours</option>
                            <option value="10" ${workTime === 10 ? 'selected' : ''}>10 hours</option>
                        </select>
                    </div>
                    <div style="margin-bottom: 8px; color: #666;">
                        Break time:
                        <select id="breakTimeSelect" style="margin-left: 5px; background-color: white;">
                            <option value="30" ${breakTime === 30 ? 'selected' : ''}>30 minutes</option>
                            <option value="45" ${breakTime === 45 ? 'selected' : ''}>45 minutes</option>
                            <option value="60" ${breakTime === 60 ? 'selected' : ''}>60 minutes</option>
                        </select>
                    </div>
                    <div style="font-size: 12px; color: #666; margin-top: 8px;">
                        Waiting for punch time...
                    </div>
                `;
            }

            display.innerHTML = content;

            // Event Listeners
            const breakSelect = document.getElementById('breakTimeSelect');
            if (breakSelect) {
                breakSelect.addEventListener('change', function() {
                    breakTime = parseInt(this.value);
                    setValue('breakTime', breakTime);
                    updateDisplay(punchInTime, punchOutTime);
                });
            }

            const workSelect = document.getElementById('workTimeSelect');
            if (workSelect) {
                workSelect.addEventListener('change', function() {
                    workTime = parseInt(this.value);
                    setValue('workTime', workTime);
                    updateDisplay(punchInTime, punchOutTime);
                });
            }
        }
    }

    function addTimeCalculator() {
        let display = document.getElementById('timeCalculator');
        if (!display) {
            display = document.createElement('div');
            display.id = 'timeCalculator';
            display.style.cssText = `
                position: fixed;
                bottom: 100px;
                right: 10px;
                padding: 15px;
                border: 1px solid #ccc;
                border-radius: 5px;
                z-index: 9999;
                font-family: Arial, sans-serif;
                font-size: 14px;
                box-shadow: 0 2px 5px rgba(0,0,0,0.1);
                min-width: 220px;
                transition: background-color 0.3s ease;
                background-color: white;
            `;
            document.body.appendChild(display);
        }

        const { punchInTime, punchOutTime } = findPunchTimes();
        updateDisplay(punchInTime, punchOutTime);
    }

    // Initial execution
    setTimeout(() => {
        addTimeCalculator();
        setInterval(addTimeCalculator, 5000);
    }, 1000);

})();