let prevMonthBtn, nextMonthBtn, month let prevWeekBtn, nextWeekBtn, week let curYear = new Date().getFullYear() let curMonth = new Date().getMonth() let curWeekDate = new Date() function prevMonth() { curMonth = curMonth - 1 if (curMonth < 0) { curMonth += 12 curYear -= 1 } updateTableMonthly() } function nextMonth() { curMonth = curMonth + 1 if (curMonth >= 12) { curMonth -= 12 curYear += 1 } updateTableMonthly() } function prevWeek() { curWeekDate = new Date(curWeekDate.valueOf() - 7 * DAY_MS) updateTableWeekly() } function nextWeek() { curWeekDate = new Date(curWeekDate.valueOf() + 7 * DAY_MS) updateTableWeekly() } function updateTableMonthly() { let year = curYear.toString().padStart(4, "0") let month = (curMonth + 1).toString().padStart(2, "0") let today = new Date() let txt = MONTHS[curMonth] if (today.getFullYear() !== curYear) { txt += " " + year.toString().padStart(4, "0") } document.getElementById("month").innerText = txt fetch(`stats/by-month/${year}/${month}/`).then(res => { return res.json() }).then(res => { if (res.status !== "success") { return } updateTable(res.data) }) } function updateTableWeekly() { let weekNum = curWeekDate.getWeek() let weekDay = (curWeekDate.getDay() + 6) % 7 let startDate = new Date(curWeekDate.valueOf() - weekDay * DAY_MS) let endDate = new Date(startDate.valueOf() + 6 * DAY_MS) let today = new Date() let date = `${MONTHS[startDate.getMonth()]} ${startDate.getDate()}` if (startDate.getFullYear() !== today.getFullYear()) { date += " " + startDate.getFullYear().toString().padStart(4, "0") } document.getElementById("week").innerText = `Week ${weekNum} (${date})` fetch(`stats/between/${formatDate(startDate)}/${formatDate(endDate)}/`).then(res => { return res.json() }).then(res => { if (res.status !== "success") { return } updateTable(res.data) }) } function updateTable(data) { let totalWorked = data.clockings.map(c => c.total).reduce((a, b) => a + b, 0) let parents = data.parents.filter(parent => parent.duration !== null && parent.is_productive) let headers = document.getElementById("headers-table") let table = document.getElementById("projects-table") let projNames = table.querySelector(".project-names") let projNums = table.querySelector(".project-nums") let totDurations = table.querySelector(".total-durations") let workingRatios = table.querySelector(".working-time-ratios") let imputedRatios = table.querySelector(".imputed-time-ratios") let sagexHours = table.querySelector(".sagex-hours") let realSagexHours = table.querySelector(".real-sagex-hours") let differences = table.querySelector(".sagex-diff") table.querySelectorAll("tr").forEach(row => row.innerHTML = "") let totalImputed = 0 parents.forEach(parent => { totalImputed += +parent.duration }) headers.querySelector(".total-clocking").innerText = formatDuration(totalWorked) headers.querySelector(".total-duration").innerText = formatDuration(totalImputed) let sagexHoursTemplate = getTemplate("sagex-hours") let totalSagex = 0 let totalRealSagex = 0 parents.forEach(parent => { let duration = +parent.duration let name = document.createElement("th") name.innerText = parent.name projNames.appendChild(name) projNums.insertCell(-1).innerText = parent.project_num table.querySelector(".clocking").insertCell(-1) totDurations.insertCell(-1).innerText = formatDuration(duration) let ratioWorking = duration / totalWorked let ratioImputed = duration / totalImputed workingRatios.insertCell(-1).innerText = formatPercentage(ratioWorking) imputedRatios.insertCell(-1).innerText = formatPercentage(ratioImputed) let sagexDuration = duration * totalWorked / totalImputed totalSagex += sagexDuration let sagexCell = sagexHours.insertCell(-1) sagexCell.innerText = formatDuration(sagexDuration) sagexCell.dataset.duration = sagexDuration let td = sagexHoursTemplate.cloneNode(true) let [h, m, s] = parent.real_sagex_hours.split(":") let hoursInput = td.querySelector("input.hours") let minutesInput = td.querySelector("input.minutes") hoursInput.value = h minutesInput.value = m let timeout = null minutesInput.addEventListener("input", () => reformatTime(minutesInput)) td.addEventListener("change", () => { if (timeout !== null) { clearTimeout(timeout) } timeout = setTimeout(() => { updateSagex(parent.id, td) }, 1000) }) realSagexHours.appendChild(td) let real = (+h)*60 + (+m) let diff = real - sagexDuration differences.insertCell(-1).innerText = formatDuration(diff) totalRealSagex += real }) headers.querySelector(".sagex-hours-total").innerText = formatDuration(totalSagex) headers.querySelector(".real-sagex-hours-total").innerText = formatDuration(totalRealSagex) headers.querySelector(".sagex-diff-total").innerText = formatDuration(totalRealSagex - totalSagex) } function reformatTime(input) { let value = input.value if (value.length === 1) { input.value = "0" + value } else if (value.length > 2) { input.value = value.slice(value.length - 2) } } function getDuration(td) { let hours = +td.querySelector(".hours").value let minutes = +td.querySelector(".minutes").value return hours * 60 + minutes } function updateSagex(id, cell) { let minutesInput = cell.querySelector(".minutes") reformatTime(minutesInput) let newDuration = getDuration(cell) let year = curYear.toString().padStart(4, "0") let month = (curMonth + 1).toString().padStart(2, "0") let date = `${year}-${month}` let fd = new FormData() fd.set("minutes", newDuration) req(`/sagex/${id}/${date}/`, { method: "POST", body: fd }).then(res => { return res.json() }).then(res => { if (res.status === "success") { cell.classList.add("saved") cell.addEventListener("animationend", () => { cell.classList.remove("saved") }, {once: true}) let theoreticalRow = document.querySelector("#projects-table tr.sagex-hours") let realRow = document.querySelector("#projects-table tr.real-sagex-hours") let diffRow = document.querySelector("#projects-table tr.sagex-diff") let totalRealCell = document.querySelector("#headers-table tr.real-sagex-hours .real-sagex-hours-total") let totalDiffCell = document.querySelector("#headers-table tr.sagex-diff .sagex-diff-total") let durationsTheory = Array.from(theoreticalRow.cells).map(c => +c.dataset.duration) let durationsReal = Array.from(realRow.cells).map(getDuration) let totalTheory = durationsTheory.reduce((a, b) => a + b, 0) let totalReal = durationsReal.reduce((a, b) => a + b, 0) let totalDiff = totalReal - totalTheory totalRealCell.innerText = formatDuration(totalReal) totalDiffCell.innerText = formatDuration(totalDiff) let theory = +theoreticalRow.cells[cell.cellIndex].dataset.duration let diff = newDuration - theory diffRow.cells[cell.cellIndex].innerText = formatDuration(diff) } else { alert(res.error) } }) } window.addEventListener("load", () => { prevMonthBtn = document.getElementById("prev-month") nextMonthBtn = document.getElementById("next-month") month = document.getElementById("month") /*prevWeekBtn = document.getElementById("prev-week") nextWeekBtn = document.getElementById("next-week") week = document.getElementById("week")*/ prevMonthBtn.addEventListener("click", () => prevMonth()) nextMonthBtn.addEventListener("click", () => nextMonth()) /*prevWeekBtn.addEventListener("click", () => prevWeek()) nextWeekBtn.addEventListener("click", () => nextWeek())*/ let monthGrp = document.getElementById("month-sel") //let weekGrp = document.getElementById("week-sel") /* rangeSel = document.getElementById("range-sel") rangeSel.addEventListener("change", () => { let mode = rangeSel.value if (mode === "weekly") { monthGrp.classList.add("hidden") weekGrp.classList.remove("hidden") updateTableWeekly() } else { monthGrp.classList.remove("hidden") weekGrp.classList.add("hidden") updateTableMonthly() } }) */ updateTableMonthly() })