Created
May 15, 2025 12:54
-
-
Save fMoro1999/2f664abb6a7cc96f69f4e663ce249f6d to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name Work Clock-Out Calculator | |
// @namespace http://tampermonkey.net/ | |
// @version 0.9 | |
// @description Calculate when to clock out based on clock-in times and lunch break requirements | |
// @author You | |
// @match https://abacus.casale.ch/portal/myabacus/proj_inandout | |
// @grant GM_addStyle | |
// @run-at document-end | |
// ==/UserScript== | |
(function() { | |
'use strict'; | |
// Add CSS for better UI | |
GM_addStyle(` | |
#clock-calculator { | |
position: fixed; | |
bottom: 20px; | |
right: 20px; | |
background-color: #f8f9fa; | |
padding: 15px; | |
border-radius: 8px; | |
box-shadow: 0 3px 10px rgba(0,0,0,0.2); | |
z-index: 9999; | |
width: 320px; | |
font-family: Arial, sans-serif; | |
} | |
#clock-calculator h3 { | |
margin-top: 0; | |
margin-bottom: 10px; | |
color: #333; | |
} | |
#clock-info { | |
margin-bottom: 15px; | |
font-size: 14px; | |
} | |
#clock-out-time { | |
font-weight: bold; | |
margin-bottom: 10px; | |
font-size: 16px; | |
} | |
#time-remaining { | |
color: #0066cc; | |
margin-bottom: 15px; | |
font-size: 15px; | |
} | |
.calculator-btn { | |
background-color: #4CAF50; | |
color: white; | |
border: none; | |
padding: 8px 12px; | |
border-radius: 4px; | |
cursor: pointer; | |
margin-right: 5px; | |
} | |
#close-calculator { | |
background-color: #f44336; | |
} | |
.warning { | |
color: #e65100; | |
} | |
`); | |
// Create the UI for the calculator | |
function createUI() { | |
// Check if the calculator already exists | |
if (document.getElementById('clock-calculator')) { | |
return; // Do not create multiple instances | |
} | |
const container = document.createElement('div'); | |
container.id = 'clock-calculator'; | |
container.innerHTML = ` | |
<h3>Clock-Out Calculator</h3> | |
<div id="clock-info">Loading clock data...</div> | |
<div id="clock-out-time"></div> | |
<div id="time-remaining"></div> | |
<button id="refresh-clock-data" class="calculator-btn">Refresh Data</button> | |
<button id="close-calculator" class="calculator-btn">Close</button> | |
<div style="margin-top: 10px"> | |
<button id="toggle-calculator" class="calculator-btn" style="background-color: #ff9800;">Toggle Calculator</button> | |
</div> | |
`; | |
document.body.appendChild(container); | |
// Add event listeners to buttons | |
document.getElementById('refresh-clock-data').addEventListener('click', calculateClockOutTime); | |
document.getElementById('close-calculator').addEventListener('click', function() { | |
document.getElementById('clock-calculator').style.display = 'none'; | |
}); | |
document.getElementById('toggle-calculator').addEventListener('click', function() { | |
const infoEl = document.getElementById('clock-info'); | |
const clockOutEl = document.getElementById('clock-out-time'); | |
const timeRemainingEl = document.getElementById('time-remaining'); | |
if (infoEl.style.display === 'none') { | |
infoEl.style.display = 'block'; | |
clockOutEl.style.display = 'block'; | |
timeRemainingEl.style.display = 'block'; | |
this.textContent = 'Hide Details'; | |
} else { | |
infoEl.style.display = 'none'; | |
clockOutEl.style.display = 'none'; | |
timeRemainingEl.style.display = 'none'; | |
this.textContent = 'Show Details'; | |
} | |
}); | |
// Initial calculation | |
calculateClockOutTime(); | |
// Set up auto-refresh timer (every minute) | |
setInterval(calculateClockOutTime, 60000); | |
} | |
// Parse time string in 24h format to Date object | |
function parseTime(timeStr) { | |
if (!timeStr) return null; | |
const now = new Date(); | |
const [hours, minutes] = timeStr.trim().split(':').map(Number); | |
const timeDate = new Date(now); | |
timeDate.setHours(hours, minutes, 0, 0); | |
return timeDate; | |
} | |
// Format Date object to HH:MM string | |
function formatTime(date) { | |
if (!date) return 'N/A'; | |
return date.toTimeString().substring(0, 5); | |
} | |
// Calculate time difference in minutes | |
function getMinutesDifference(startTime, endTime) { | |
if (!startTime || !endTime) return 0; | |
return Math.round((endTime - startTime) / (1000 * 60)); | |
} | |
// Main function to calculate clock out time | |
function calculateClockOutTime() { | |
// Get clock times from the page - USING THE EXACT IDs PROVIDED | |
const firstClockInEl = document.getElementById('id_history_from_1'); | |
const secondClockInEl = document.getElementById('id_history_to_1'); | |
const thirdClockInEl = document.getElementById('id_history_from_2'); | |
// Get the values from the elements | |
let firstClockIn = firstClockInEl && firstClockInEl.value ? firstClockInEl.value : null; | |
let secondClockIn = secondClockInEl && secondClockInEl.value ? secondClockInEl.value : null; | |
let thirdClockIn = thirdClockInEl && thirdClockInEl.value ? thirdClockInEl.value : null; | |
// Get the clock info element | |
const clockInfoEl = document.getElementById('clock-info'); | |
const clockOutTimeEl = document.getElementById('clock-out-time'); | |
const timeRemainingEl = document.getElementById('time-remaining'); | |
// If elements don't exist in DOM, show error | |
if (!clockInfoEl || !clockOutTimeEl || !timeRemainingEl) { | |
console.error('Clock calculator elements not found in DOM'); | |
return; | |
} | |
// If we don't have first clock in time, show error message | |
if (!firstClockIn) { | |
clockInfoEl.innerHTML = 'No clock-in times found on this page. Make sure you are on the correct timesheet page.'; | |
clockOutTimeEl.innerHTML = ''; | |
timeRemainingEl.innerHTML = ''; | |
return; | |
} | |
// Parse time strings to Date objects | |
const firstClockInTime = parseTime(firstClockIn); | |
const secondClockInTime = parseTime(secondClockIn); | |
const thirdClockInTime = parseTime(thirdClockIn); | |
const currentTime = new Date(); | |
let clockOutTime; | |
let totalWorkMinutes = 480; // Default to 8 hours (480 minutes) | |
let lunchBreakMinutes = 0; | |
let minimumLunchBreak = 45; // default to 45 minutes | |
let infoText = ''; | |
// If planning to work 9+ hours, minimum lunch break is 60 minutes | |
if (totalWorkMinutes >= 540) { | |
minimumLunchBreak = 60; | |
} | |
// Display the clock in times | |
infoText += `First Clock In: ${formatTime(firstClockInTime)}<br>`; | |
// Calculate based on available clock times | |
if (firstClockInTime && secondClockInTime && thirdClockInTime) { | |
// We have all three clock times, so lunch break is from second to third | |
infoText += `Second Clock In (Lunch Start): ${formatTime(secondClockInTime)}<br>`; | |
infoText += `Third Clock In (Lunch End): ${formatTime(thirdClockInTime)}<br>`; | |
// Calculate actual lunch break taken | |
lunchBreakMinutes = getMinutesDifference(secondClockInTime, thirdClockInTime); | |
infoText += `Actual Lunch Break: ${lunchBreakMinutes} min<br>`; | |
infoText += `Required Lunch Break: ${minimumLunchBreak} min<br>`; | |
// Calculate total work minutes before lunch | |
const morningWorkMinutes = getMinutesDifference(firstClockInTime, secondClockInTime); | |
// Calculate clock-out time | |
clockOutTime = new Date(thirdClockInTime); | |
clockOutTime.setMinutes(clockOutTime.getMinutes() + (totalWorkMinutes - morningWorkMinutes)); | |
// If the actual lunch break was shorter than required, adjust clock-out time | |
if (lunchBreakMinutes < minimumLunchBreak) { | |
const additionalMinutes = minimumLunchBreak - lunchBreakMinutes; | |
clockOutTime.setMinutes(clockOutTime.getMinutes() + additionalMinutes); | |
infoText += `<span class="warning">Short lunch! Adding ${additionalMinutes} min to total time.</span><br>`; | |
} | |
} else if (firstClockInTime && secondClockInTime) { | |
// We have first and second clock times, assume second is lunch start and user hasn't returned yet | |
infoText += `Second Clock In (Lunch Start): ${formatTime(secondClockInTime)}<br>`; | |
infoText += `Third Clock In (Lunch End): Not yet clocked<br>`; | |
// Calculate work minutes before lunch | |
const morningWorkMinutes = getMinutesDifference(firstClockInTime, secondClockInTime); | |
infoText += `Required Lunch Break: ${minimumLunchBreak} min<br>`; | |
// Calculate potential clock-out time if user returned from lunch now | |
const potentialReturnTime = new Date(); | |
lunchBreakMinutes = getMinutesDifference(secondClockInTime, potentialReturnTime); | |
// Calculate clock-out time assuming user returns now | |
clockOutTime = new Date(potentialReturnTime); | |
clockOutTime.setMinutes(clockOutTime.getMinutes() + (totalWorkMinutes - morningWorkMinutes)); | |
// If current lunch break is shorter than required, adjust clock-out time | |
if (lunchBreakMinutes < minimumLunchBreak) { | |
const additionalMinutes = minimumLunchBreak - lunchBreakMinutes; | |
clockOutTime.setMinutes(clockOutTime.getMinutes() + additionalMinutes); | |
infoText += `<span class="warning">If you clock in now, lunch break (${lunchBreakMinutes} min) is shorter than required.</span><br>`; | |
} | |
infoText += `Current Lunch Duration: ${lunchBreakMinutes} min<br>`; | |
} else if (firstClockInTime) { | |
// We only have first clock in, no lunch break yet | |
infoText += `Second Clock In (Lunch Start): Not yet clocked<br>`; | |
infoText += `Third Clock In (Lunch End): Not yet clocked<br>`; | |
infoText += `Required Lunch Break: ${minimumLunchBreak} min<br>`; | |
// Calculate earliest possible clock-out time (assuming minimum lunch break) | |
clockOutTime = new Date(firstClockInTime); | |
clockOutTime.setMinutes(clockOutTime.getMinutes() + totalWorkMinutes + minimumLunchBreak); | |
} | |
// Display clock-out time | |
clockInfoEl.innerHTML = infoText; | |
clockOutTimeEl.innerHTML = `Recommended Clock Out: <span style="font-size: 18px; color: #2E7D32;">${formatTime(clockOutTime)}</span>`; | |
// Calculate and display time remaining | |
const minutesRemaining = getMinutesDifference(currentTime, clockOutTime); | |
if (minutesRemaining > 0) { | |
const hoursRemaining = Math.floor(minutesRemaining / 60); | |
const minsRemaining = minutesRemaining % 60; | |
let timeRemainingText = 'Time remaining: '; | |
if (hoursRemaining > 0) { | |
timeRemainingText += `${hoursRemaining} hr `; | |
} | |
if (minsRemaining > 0 || hoursRemaining === 0) { | |
timeRemainingText += `${minsRemaining} min`; | |
} | |
timeRemainingEl.innerHTML = timeRemainingText; | |
} else { | |
timeRemainingEl.innerHTML = '<span style="color: #2E7D32; font-weight: bold;">You can clock out now!</span>'; | |
} | |
} | |
// Create a floating button to toggle the calculator | |
function createToggleButton() { | |
const button = document.createElement('button'); | |
button.id = 'show-clock-calculator'; | |
button.textContent = 'Calculate Clock-Out'; | |
button.style.cssText = 'position: fixed; bottom: 10px; right: 10px; background-color: #4CAF50; color: white; border: none; padding: 10px; border-radius: 5px; cursor: pointer; z-index: 9998;'; | |
document.body.appendChild(button); | |
button.addEventListener('click', function() { | |
// Check if calculator exists | |
let calculator = document.getElementById('clock-calculator'); | |
if (calculator) { | |
// Toggle visibility | |
calculator.style.display = calculator.style.display === 'none' ? 'block' : 'none'; | |
} else { | |
// Create the calculator | |
createUI(); | |
} | |
// Hide the toggle button | |
this.style.display = 'none'; | |
}); | |
} | |
// Initialize after a slight delay to ensure page loads | |
setTimeout(function() { | |
createToggleButton(); | |
}, 1500); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment