added clockings/remote weekly total + minor improvements
This commit is contained in:
parent
ee96598d92
commit
d13127a273
@ -1,3 +1,77 @@
|
|||||||
|
const SEC_MS = 1000
|
||||||
|
const MIN_MS = SEC_MS * 60
|
||||||
|
const HOUR_MS = MIN_MS * 60
|
||||||
|
const DAY_MS = HOUR_MS * 24
|
||||||
|
|
||||||
|
const DAYS_SHORT = [
|
||||||
|
"Mon",
|
||||||
|
"Tue",
|
||||||
|
"Wed",
|
||||||
|
"Thu",
|
||||||
|
"Fri",
|
||||||
|
"Sat",
|
||||||
|
"Sun"
|
||||||
|
]
|
||||||
|
|
||||||
|
const MONTHS = [
|
||||||
|
"January",
|
||||||
|
"February",
|
||||||
|
"March",
|
||||||
|
"April",
|
||||||
|
"May",
|
||||||
|
"June",
|
||||||
|
"July",
|
||||||
|
"August",
|
||||||
|
"September",
|
||||||
|
"October",
|
||||||
|
"November",
|
||||||
|
"December"
|
||||||
|
]
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////
|
||||||
|
// //
|
||||||
|
// Taken from: https://weeknumber.com/how-to/javascript //
|
||||||
|
// //
|
||||||
|
//////////////////////////////////////////////////////////
|
||||||
|
Date.prototype.getWeek = function() {
|
||||||
|
let date = new Date(this.getTime())
|
||||||
|
date.setHours(0, 0, 0, 0)
|
||||||
|
|
||||||
|
// Thursday in current week decides the year.
|
||||||
|
date.setDate(date.getDate() + 3 - (date.getDay() + 6) % 7)
|
||||||
|
|
||||||
|
// January 4 is always in week 1.
|
||||||
|
let week1 = new Date(date.getFullYear(), 0, 4)
|
||||||
|
|
||||||
|
// Adjust to Thursday in week 1 and count number of weeks from date to week1.
|
||||||
|
return 1 + Math.round(((date.getTime() - week1.getTime()) / 86400000 - 3 + (week1.getDay() + 6) % 7) / 7)
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatDate(date) {
|
||||||
|
let year = date.getFullYear().toString().padStart(4, "0")
|
||||||
|
let month = (date.getMonth() + 1).toString().padStart(2, "0")
|
||||||
|
let day = date.getDate().toString().padStart(2, "0")
|
||||||
|
return `${year}-${month}-${day}`
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatDuration(duration) {
|
||||||
|
let hours = Math.floor(duration / 60)
|
||||||
|
duration -= hours * 60
|
||||||
|
let res = ""
|
||||||
|
if (hours > 0) {
|
||||||
|
res += hours.toString() + "h"
|
||||||
|
res += duration.toString().padStart(2, "0")
|
||||||
|
} else {
|
||||||
|
res += duration.toString() + "min"
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatPercentage(ratio) {
|
||||||
|
let percentage = Math.round(ratio * 100)
|
||||||
|
return percentage + "%"
|
||||||
|
}
|
||||||
|
|
||||||
function req(url, options) {
|
function req(url, options) {
|
||||||
let headers = options.headers || {}
|
let headers = options.headers || {}
|
||||||
let csrftoken = document.querySelector("input[name='csrfmiddlewaretoken']").value
|
let csrftoken = document.querySelector("input[name='csrfmiddlewaretoken']").value
|
||||||
@ -5,3 +79,9 @@ function req(url, options) {
|
|||||||
options.headers = headers
|
options.headers = headers
|
||||||
return fetch(url, options)
|
return fetch(url, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getTemplate(cls) {
|
||||||
|
let elmt = document.querySelector(".template." + cls).cloneNode(true)
|
||||||
|
elmt.classList.remove("template")
|
||||||
|
return elmt
|
||||||
|
}
|
@ -4,46 +4,6 @@ let curYear = new Date().getFullYear()
|
|||||||
let curMonth = new Date().getMonth()
|
let curMonth = new Date().getMonth()
|
||||||
let curWeekDate = new Date()
|
let curWeekDate = new Date()
|
||||||
|
|
||||||
const SEC_MS = 1000
|
|
||||||
const MIN_MS = SEC_MS * 60
|
|
||||||
const HOUR_MS = MIN_MS * 60
|
|
||||||
const DAY_MS = HOUR_MS * 24
|
|
||||||
|
|
||||||
const MONTHS = [
|
|
||||||
"January",
|
|
||||||
"February",
|
|
||||||
"March",
|
|
||||||
"April",
|
|
||||||
"May",
|
|
||||||
"June",
|
|
||||||
"July",
|
|
||||||
"August",
|
|
||||||
"September",
|
|
||||||
"October",
|
|
||||||
"November",
|
|
||||||
"December"
|
|
||||||
]
|
|
||||||
|
|
||||||
function formatDate(date) {
|
|
||||||
let year = date.getFullYear().toString().padStart(4, "0")
|
|
||||||
let month = (date.getMonth() + 1).toString().padStart(2, "0")
|
|
||||||
let day = date.getDate().toString().padStart(2, "0")
|
|
||||||
return `${year}-${month}-${day}`
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatDuration(duration) {
|
|
||||||
let hours = Math.floor(duration / 60)
|
|
||||||
duration -= hours * 60
|
|
||||||
let res = ""
|
|
||||||
if (hours > 0) {
|
|
||||||
res += hours.toString() + "h"
|
|
||||||
res += duration.toString().padStart(2, "0")
|
|
||||||
} else {
|
|
||||||
res += duration.toString() + "min"
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
function prevMonth() {
|
function prevMonth() {
|
||||||
curMonth = curMonth - 1
|
curMonth = curMonth - 1
|
||||||
if (curMonth < 0) {
|
if (curMonth < 0) {
|
||||||
@ -90,16 +50,17 @@ function updateListMonthly() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function updateListWeekly() {
|
function updateListWeekly() {
|
||||||
|
let weekNum = curWeekDate.getWeek()
|
||||||
let weekDay = (curWeekDate.getDay() + 6) % 7
|
let weekDay = (curWeekDate.getDay() + 6) % 7
|
||||||
let startDate = new Date(curWeekDate.valueOf() - weekDay * DAY_MS)
|
let startDate = new Date(curWeekDate.valueOf() - weekDay * DAY_MS)
|
||||||
let endDate = new Date(startDate.valueOf() + 6 * DAY_MS)
|
let endDate = new Date(startDate.valueOf() + 6 * DAY_MS)
|
||||||
|
|
||||||
let today = new Date()
|
let today = new Date()
|
||||||
let txt = `Week of ${MONTHS[startDate.getMonth()]} ${startDate.getDate()}`
|
let date = `${MONTHS[startDate.getMonth()]} ${startDate.getDate()}`
|
||||||
if (startDate.getFullYear() !== today.getFullYear()) {
|
if (startDate.getFullYear() !== today.getFullYear()) {
|
||||||
txt += " " + startDate.getFullYear().toString().padStart(4, "0")
|
date += " " + startDate.getFullYear().toString().padStart(4, "0")
|
||||||
}
|
}
|
||||||
document.getElementById("week").innerText = txt
|
document.getElementById("week").innerText = `Week ${weekNum} (${date})`
|
||||||
|
|
||||||
fetch(`stats/between/${formatDate(startDate)}/${formatDate(endDate)}/`).then(res => {
|
fetch(`stats/between/${formatDate(startDate)}/${formatDate(endDate)}/`).then(res => {
|
||||||
return res.json()
|
return res.json()
|
||||||
|
@ -35,6 +35,8 @@
|
|||||||
input {
|
input {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
|
background: none;
|
||||||
|
color: var(--light1);
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type="time"]::-webkit-calendar-picker-indicator {
|
input[type="time"]::-webkit-calendar-picker-indicator {
|
||||||
@ -44,6 +46,14 @@
|
|||||||
|
|
||||||
&.total {
|
&.total {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
|
border-top: solid #71717185 1px;
|
||||||
|
td {
|
||||||
|
padding: 0.4em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.total2, &.remote-total {
|
||||||
|
text-align: center;
|
||||||
td {
|
td {
|
||||||
padding: 0.4em;
|
padding: 0.4em;
|
||||||
}
|
}
|
||||||
@ -127,7 +137,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.separator {
|
.separator {
|
||||||
height: 2em;
|
height: 4em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,73 +1,6 @@
|
|||||||
let table
|
let table
|
||||||
let prevMonthBtn, nextMonthBtn, month
|
let prevMonthBtn, nextMonthBtn, month
|
||||||
|
|
||||||
const DAYS_SHORT = [
|
|
||||||
"Mon",
|
|
||||||
"Tue",
|
|
||||||
"Wed",
|
|
||||||
"Thu",
|
|
||||||
"Fri",
|
|
||||||
"Sat",
|
|
||||||
"Sun"
|
|
||||||
]
|
|
||||||
|
|
||||||
const MONTHS = [
|
|
||||||
"January",
|
|
||||||
"February",
|
|
||||||
"March",
|
|
||||||
"April",
|
|
||||||
"May",
|
|
||||||
"June",
|
|
||||||
"July",
|
|
||||||
"August",
|
|
||||||
"September",
|
|
||||||
"October",
|
|
||||||
"November",
|
|
||||||
"December"
|
|
||||||
]
|
|
||||||
|
|
||||||
const SEC_MS = 1000
|
|
||||||
const MIN_MS = SEC_MS * 60
|
|
||||||
const HOUR_MS = MIN_MS * 60
|
|
||||||
const DAY_MS = HOUR_MS * 24
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////
|
|
||||||
// //
|
|
||||||
// Taken from: https://weeknumber.com/how-to/javascript //
|
|
||||||
// //
|
|
||||||
//////////////////////////////////////////////////////////
|
|
||||||
Date.prototype.getWeek = function() {
|
|
||||||
let date = new Date(this.getTime())
|
|
||||||
date.setHours(0, 0, 0, 0)
|
|
||||||
|
|
||||||
// Thursday in current week decides the year.
|
|
||||||
date.setDate(date.getDate() + 3 - (date.getDay() + 6) % 7)
|
|
||||||
|
|
||||||
// January 4 is always in week 1.
|
|
||||||
let week1 = new Date(date.getFullYear(), 0, 4)
|
|
||||||
|
|
||||||
// Adjust to Thursday in week 1 and count number of weeks from date to week1.
|
|
||||||
return 1 + Math.round(((date.getTime() - week1.getTime()) / 86400000 - 3 + (week1.getDay() + 6) % 7) / 7)
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatDate(date) {
|
|
||||||
let year = date.getFullYear().toString().padStart(4, "0")
|
|
||||||
let month = (date.getMonth() + 1).toString().padStart(2, "0")
|
|
||||||
let day = date.getDate().toString().padStart(2, "0")
|
|
||||||
return `${year}-${month}-${day}`
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatPercentage(ratio) {
|
|
||||||
let percentage = Math.round(ratio * 100)
|
|
||||||
return percentage + "%"
|
|
||||||
}
|
|
||||||
|
|
||||||
function getTemplate(cls) {
|
|
||||||
let elmt = document.querySelector(".template." + cls).cloneNode(true)
|
|
||||||
elmt.classList.remove("template")
|
|
||||||
return elmt
|
|
||||||
}
|
|
||||||
|
|
||||||
class Table {
|
class Table {
|
||||||
constructor(elmt) {
|
constructor(elmt) {
|
||||||
this.table = elmt
|
this.table = elmt
|
||||||
@ -90,6 +23,7 @@ class Table {
|
|||||||
|
|
||||||
this.totalProjects = 0
|
this.totalProjects = 0
|
||||||
this.clockingTotals = []
|
this.clockingTotals = []
|
||||||
|
this.clockingRemotes = []
|
||||||
|
|
||||||
this.update()
|
this.update()
|
||||||
}
|
}
|
||||||
@ -148,8 +82,11 @@ class Table {
|
|||||||
let clockings = this.clockings.querySelectorAll(".clocking")
|
let clockings = this.clockings.querySelectorAll(".clocking")
|
||||||
|
|
||||||
clockings.forEach((clocking, i) => {
|
clockings.forEach((clocking, i) => {
|
||||||
|
if (!(clocking.dataset.type || clocking.classList.contains("total"))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
let cell = clocking.insertCell(this.nDays + 1)
|
let cell = clocking.insertCell(this.nDays + 1)
|
||||||
if (i === 5) {
|
if (clocking.classList.contains("total")) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,7 +101,6 @@ class Table {
|
|||||||
|
|
||||||
addWeek(startDate, length=7) {
|
addWeek(startDate, length=7) {
|
||||||
let weekNum = startDate.getWeek()
|
let weekNum = startDate.getWeek()
|
||||||
|
|
||||||
let weekName = this.weekNames.insertCell(-1)
|
let weekName = this.weekNames.insertCell(-1)
|
||||||
weekName.innerText = `Week n° ${weekNum}`
|
weekName.innerText = `Week n° ${weekNum}`
|
||||||
weekName.colSpan = length
|
weekName.colSpan = length
|
||||||
@ -185,6 +121,10 @@ class Table {
|
|||||||
this.columns.appendChild(col)
|
this.columns.appendChild(col)
|
||||||
this.nDays++
|
this.nDays++
|
||||||
}
|
}
|
||||||
|
let weekRemoteTotalCell = this.clockings.querySelector(".clocking.remote-total").insertCell(-1)
|
||||||
|
weekRemoteTotalCell.colSpan = length
|
||||||
|
let weekTotalCell = this.clockings.querySelector(".clocking.total2").insertCell(-1)
|
||||||
|
weekTotalCell.colSpan = length
|
||||||
}
|
}
|
||||||
|
|
||||||
addProject(id, name, sagexNum, times, isParent=false) {
|
addProject(id, name, sagexNum, times, isParent=false) {
|
||||||
@ -245,7 +185,7 @@ class Table {
|
|||||||
monday = nextMonday
|
monday = nextMonday
|
||||||
}
|
}
|
||||||
|
|
||||||
this.clockings.querySelector(".clocking.total").insertCell(-1)
|
this.clockings.querySelector(".clocking.total").insertCell(-1).rowSpan = 2
|
||||||
this.endDate = new Date(monday.valueOf() - monday.getDate() * DAY_MS)
|
this.endDate = new Date(monday.valueOf() - monday.getDate() * DAY_MS)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -302,6 +242,7 @@ class Table {
|
|||||||
|
|
||||||
displayClockings(clockings) {
|
displayClockings(clockings) {
|
||||||
this.clockingTotals = Array(this.nDays).fill(0)
|
this.clockingTotals = Array(this.nDays).fill(0)
|
||||||
|
this.clockingRemotes = Array(this.nDays).fill(0)
|
||||||
clockings.forEach(clocking => {
|
clockings.forEach(clocking => {
|
||||||
let date = new Date(clocking.date)
|
let date = new Date(clocking.date)
|
||||||
if (date < this.startDate || date > this.endDate) {
|
if (date < this.startDate || date > this.endDate) {
|
||||||
@ -320,6 +261,15 @@ class Table {
|
|||||||
let hours = +clocking.total / 60
|
let hours = +clocking.total / 60
|
||||||
this.clockingTotals[i] = hours
|
this.clockingTotals[i] = hours
|
||||||
totalCell.innerText = hours === 0 ? "" : Math.round(hours * 100) / 100
|
totalCell.innerText = hours === 0 ? "" : Math.round(hours * 100) / 100
|
||||||
|
|
||||||
|
if (clocking.remote !== null) {
|
||||||
|
let remoteParts = clocking.remote.split(":").map(v => +v)
|
||||||
|
let remoteHours = remoteParts[0] + remoteParts[1] / 60
|
||||||
|
if (remoteParts.length >= 3) {
|
||||||
|
remoteHours += remoteParts[2] / 3600
|
||||||
|
}
|
||||||
|
this.clockingRemotes[i] = remoteHours
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -343,6 +293,22 @@ class Table {
|
|||||||
let totalProjects = this.totalProjects
|
let totalProjects = this.totalProjects
|
||||||
this.clockings.querySelector(".clocking.total").cells[this.nDays + 1].innerText = Math.round(totalClockings * 100) / 100
|
this.clockings.querySelector(".clocking.total").cells[this.nDays + 1].innerText = Math.round(totalClockings * 100) / 100
|
||||||
|
|
||||||
|
let startI = 0
|
||||||
|
Array.from(this.clockings.querySelector(".clocking.remote-total").cells).forEach((cell, i) => {
|
||||||
|
let endI = startI + cell.colSpan
|
||||||
|
let remote = this.clockingRemotes.slice(startI, endI).reduce((a, b) => a + b, 0)
|
||||||
|
cell.innerText = Math.round(remote * 100) / 100
|
||||||
|
startI = endI
|
||||||
|
})
|
||||||
|
|
||||||
|
startI = 0
|
||||||
|
Array.from(this.clockings.querySelector(".clocking.total2").cells).forEach((cell, i) => {
|
||||||
|
let endI = startI + cell.colSpan
|
||||||
|
let total = this.clockingTotals.slice(startI, endI).reduce((a, b) => a + b, 0)
|
||||||
|
cell.innerText = Math.round(total * 100) / 100
|
||||||
|
startI = endI
|
||||||
|
})
|
||||||
|
|
||||||
if (totalClockings === 0) {
|
if (totalClockings === 0) {
|
||||||
console.log("Total clockings = 0")
|
console.log("Total clockings = 0")
|
||||||
return
|
return
|
||||||
|
@ -30,10 +30,10 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr class="clocking" data-type="in_am">
|
<tr class="clocking" data-type="in_am">
|
||||||
<th colspan="2">Check-IN AM</th>
|
<th colspan="2">Check-IN AM</th>
|
||||||
<th rowspan="5" class="total-header">TOTAL</th>
|
<th rowspan="6" class="total-header">TOTAL</th>
|
||||||
<th rowspan="6" class="total-header">% working time</th>
|
<th rowspan="8" class="total-header">% working time</th>
|
||||||
<th rowspan="6" class="total-header">% imputed time</th>
|
<th rowspan="8" class="total-header">% imputed time</th>
|
||||||
<th rowspan="6" class="total-header">time on sagex</th>
|
<th rowspan="8" class="total-header">time on sagex</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="clocking" data-type="out_am">
|
<tr class="clocking" data-type="out_am">
|
||||||
<th colspan="2">Check-OUT AM</th>
|
<th colspan="2">Check-OUT AM</th>
|
||||||
@ -45,11 +45,13 @@
|
|||||||
<th colspan="2">Check-OUT PM</th>
|
<th colspan="2">Check-OUT PM</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="clocking" data-type="remote">
|
<tr class="clocking" data-type="remote">
|
||||||
<th colspan="2">Remote</th>
|
<th colspan="2" rowspan="2">Remote</th>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr class="clocking remote-total"></tr>
|
||||||
<tr class="clocking total">
|
<tr class="clocking total">
|
||||||
<th colspan="2">TOTAL</th>
|
<th colspan="2" rowspan="2">TOTAL</th>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr class="clocking total2"></tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
<tr class="separator"></tr>
|
<tr class="separator"></tr>
|
||||||
<tbody class="times">
|
<tbody class="times">
|
||||||
|
Loading…
x
Reference in New Issue
Block a user