|
// ==UserScript== |
|
// @name Autofill OneView Timesheet |
|
// @namespace http://tampermonkey.net/ |
|
// @version 1.1.5 |
|
// @description Help you save some time filling out your timesheet |
|
// @author mbruno-kr, scottw-kr, cmcculloh-kr |
|
// @match http://oneview.kroger.com/niku/nu |
|
// @icon https://www.google.com/s2/favicons?sz=64&domain=kroger.com |
|
// @grant none |
|
// ==/UserScript== |
|
|
|
(function () { |
|
("use strict"); |
|
|
|
const taskTypes = [ |
|
{ |
|
id: "work", |
|
description: "KDS", |
|
guidance: "This is your primary billable category.", |
|
}, |
|
{ |
|
id: "plan", |
|
description: "OKR Discovery & Agile Ceremonies", |
|
guidance: |
|
"All team events such as refinement, scrum events, standup, retro etc.", |
|
}, |
|
{ |
|
id: "admin", |
|
description: "General Admin", |
|
guidance: |
|
"Town Hall, OKR Kickoff, 1:1, Interviews, Web-Guild, organizational emails/newsletters, Machine updates, On-boarding, Town Halls, All-hands, Benefits sessions, Company Webinars, Quarterly/Annual review, VTO, Jury Duty, Travel", |
|
}, |
|
{ |
|
id: "train", |
|
description: "Non Project Training", |
|
guidance: |
|
"L&D Day, MyInfo learning, LUMA, Pluralsight and other Kroger related", |
|
}, |
|
{ |
|
id: "pto", |
|
description: "PTO/ Vacation/ Approved Absences", |
|
guidance: |
|
"Health & Wellness days, Vacation, Approve Absence, Personal days, Bereavement.", |
|
}, |
|
{ |
|
id: "hol", |
|
description: "Holiday", |
|
guidance: "Corporate Holidays", |
|
}, |
|
]; |
|
|
|
// Fill in your default week here. "A" will be "Auto" assigned any remaining hours, assuming an 8 hour day. |
|
const defaultWeek = { |
|
work: ["A", "A", "A", "A", "A"], |
|
plan: [1.5, 1.5, 1.5, 1.5, 1], |
|
admin: [2.0, 0.5, 0.5, 0.5, 0.5], |
|
train: [0, 0, 0, 0, 0], |
|
pto: [0, 0, 0, 0, 0], |
|
hol: [0, 0, 0, 0, 0], |
|
}; |
|
|
|
const expectedOutcome = { |
|
work: [5.5, 5.5, 7, 4.5, 0], |
|
plan: [1.5, 1, 0, 3, 1], |
|
admin: [1, 1.5, 1, 0.5, 1.5], |
|
train: [0, 0, 0, 0, 0], |
|
pto: [0, 0, 0, 0, 0], |
|
hol: [0, 0, 0, 0, 6], |
|
}; |
|
|
|
const appendGuidance = () => { |
|
taskTypes.forEach((taskType) => { |
|
const link = document.querySelector( |
|
`a[title*="${taskType.description}"]` |
|
); |
|
if (link) { |
|
link.innerHTML += ` - ${taskType.guidance}`; |
|
} |
|
}); |
|
}; |
|
|
|
const totalForDayAcrossCategories = (week, day) => { |
|
let total = 0; |
|
Object.keys(week).forEach((category) => { |
|
console.log("week, category", category, day, week[category][day]); |
|
total += +week[category][day] > 0 ? +week[category][day] : 0; |
|
}); |
|
console.log("totalForDayAcrossCategories", total); |
|
return total; |
|
}; |
|
|
|
const doAutoCalculation = (week) => { |
|
const autoWeek = {}; |
|
Object.keys(week).forEach((category, i) => { |
|
autoWeek[category] = []; |
|
for (let i = 0; i < week[category].length; i++) { |
|
autoWeek[category][i] = |
|
week[category][i] === "A" |
|
? 8 - totalForDayAcrossCategories(week, i) |
|
: week[category][i]; |
|
} |
|
return autoWeek[category]; |
|
}); |
|
return autoWeek; |
|
}; |
|
|
|
// UNIT TESTS |
|
// console.log("expectedOutcome", expectedOutcome); |
|
// console.assert(compareWeeks(finalizedWeek, expectedOutcome), "Weeks do not match"); |
|
// console.assert(totalForDayAcrossCategories(finalizedWeek, 0) === 5, "Total for day 0 is not 5"); |
|
// console.assert(false, "This is a test"); |
|
|
|
// const compareWeeks = (week1, week2) => { |
|
// let result = true; |
|
// Object.keys(week1).forEach((category) => { |
|
// for (let i = 0; i < week1[category].length; i++) { |
|
// if (week1[category][i] !== week2[category][i]) { |
|
// console.log( |
|
// "Weeks do not match at " + |
|
// category + |
|
// " " + |
|
// i + |
|
// " " + |
|
// week1[category][i] + |
|
// " " + |
|
// week2[category][i] |
|
// ); |
|
// result = false; |
|
// } |
|
// } |
|
// }); |
|
// return result; |
|
// }; |
|
|
|
function getDays() { |
|
let days = []; |
|
document.querySelectorAll('[data-columnid="day"]').forEach((e) => { |
|
let content = e.innerText; |
|
if (content.length > 0) { |
|
days.push(content.replace(/\n/g, " ").trim().replace(" ", ", ")); |
|
} |
|
}); |
|
return days; |
|
} |
|
|
|
const getCurrentValues = () => { |
|
let days = getDays(); |
|
let weekDays = days.slice(1, 6); |
|
|
|
// For each weekday, fill each task's values |
|
let weekValues = {}; |
|
taskTypes.forEach((task) => { |
|
weekValues[task.id] = []; |
|
}); |
|
weekDays.forEach((dayOfWeek) => { |
|
taskTypes.forEach((task) => { |
|
let day = document.querySelector( |
|
`input[alt*='${dayOfWeek}'][title*='${task.description}']` |
|
); |
|
weekValues[task.id].push(day.value); |
|
}); |
|
}); |
|
console.log("weekValues", weekValues); |
|
return weekValues; |
|
}; |
|
|
|
const getWeekValues = () => { |
|
const mergedWeek = getCurrentValues(); |
|
const finalizedWeek = doAutoCalculation(mergedWeek); |
|
|
|
return finalizedWeek; |
|
}; |
|
|
|
const autoFillWithDefaultValues = () => { |
|
autoFill(defaultWeek); |
|
}; |
|
|
|
const autoFillWithOverrides = () => { |
|
const weekValues = getWeekValues(); |
|
autoFill(weekValues); |
|
}; |
|
|
|
function autoFill(weekvalues) { |
|
let days = getDays(); |
|
let weekDays = days.slice(1, 6); |
|
|
|
// For each weekday, fill each task's values |
|
weekDays.forEach((dayOfWeek) => { |
|
taskTypes.forEach((task) => { |
|
let day = document.querySelector( |
|
`input[alt*='${dayOfWeek}'][title*='${task.description}']` |
|
); |
|
if (!day) { |
|
const msg = `Cannot find activity "${task.description}" on ${dayOfWeek}`; |
|
alert(msg); |
|
throw Error(msg); |
|
} |
|
// Add weekvalues value for day & id |
|
const newValueForDay = weekvalues[task.id][weekDays.indexOf(dayOfWeek)]; |
|
day.value = newValueForDay; |
|
}); |
|
}); |
|
|
|
// submitForm("page", "timeadmin.saveTimesheet"); |
|
} |
|
|
|
function addAutofillButton() { |
|
let container = document.querySelector('div[class*="ppm_button_bar"]'); |
|
if (!container) { |
|
setTimeout(main, 200); |
|
return; |
|
} |
|
|
|
if (isEntryPage()) { |
|
// if there is no button |
|
if (!container.querySelector('button[id="auto-fill"]')) { |
|
let button = document.createElement("button"); |
|
button.id = "auto-fill"; |
|
button.innerText = "Auto Fill"; |
|
button.className = "ppm_button"; |
|
button.style = "box-shadow: 0 0 15px 3px rgb(182 148 92, 1);"; |
|
button.onclick = autoFillWithOverrides; |
|
container.appendChild(button); |
|
} else { |
|
const input = document.querySelectorAll( |
|
'input[name="actuals_hours"]' |
|
)[1]; |
|
// if the form is still empty |
|
if (input && input.value === "") { |
|
const xpath = "//button[text()='Auto Fill']"; |
|
const matchingElement = document.evaluate( |
|
xpath, |
|
document, |
|
null, |
|
XPathResult.FIRST_ORDERED_NODE_TYPE, |
|
null |
|
).singleNodeValue; |
|
matchingElement.click(); |
|
} |
|
} |
|
} |
|
} |
|
|
|
const isEntryPage = () => |
|
document.querySelectorAll('img[title="Delete"]')[0] !== undefined; |
|
|
|
function main() { |
|
const onTimeSheetPage = |
|
window.location.hash.indexOf("editTimesheet") !== -1; |
|
|
|
if (onTimeSheetPage) { |
|
setTimeout(() => { |
|
console.log("AutoFill Running"); |
|
setTimeout(appendGuidance, 100); |
|
setTimeout(addAutofillButton, 100); |
|
// autofill with default values |
|
setTimeout(autoFillWithDefaultValues, 100); |
|
}, 500); |
|
} |
|
} |
|
|
|
main(); |
|
})(); |