added clockings edit + auto calculations
This commit is contained in:
@ -0,0 +1,7 @@
|
||||
function req(url, options) {
|
||||
let headers = options.headers || {}
|
||||
let csrftoken = document.querySelector("input[name='csrfmiddlewaretoken']").value
|
||||
headers["X-CSRFToken"] = csrftoken
|
||||
options.headers = headers
|
||||
return fetch(url, options)
|
||||
}
|
@ -5,13 +5,9 @@ window.addEventListener("load", () => {
|
||||
selector.addEventListener("change", () => {
|
||||
let fd = new FormData()
|
||||
fd.set("parent_id", selector.value)
|
||||
let csrftoken = document.querySelector("input[name='csrfmiddlewaretoken']").value
|
||||
fetch(`${id}/set_parent`, {
|
||||
req(`${id}/set_parent`, {
|
||||
method: "POST",
|
||||
body: fd,
|
||||
headers: {
|
||||
"X-CSRFToken": csrftoken
|
||||
}
|
||||
body: fd
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -41,6 +41,13 @@
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&.total {
|
||||
text-align: right;
|
||||
td {
|
||||
padding: 0.4em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -97,6 +104,10 @@
|
||||
|
||||
&:first-child {
|
||||
text-align: left;
|
||||
|
||||
&:hover, &:hover ~ td {
|
||||
background-color: rgba(255, 0, 0, 0.2);
|
||||
}
|
||||
}
|
||||
&:nth-child(2) {
|
||||
text-align: center;
|
||||
|
@ -1,6 +1,5 @@
|
||||
let table
|
||||
let prevMonthBtn, nextMonthBtn, month
|
||||
let curMonth = new Date().getMonth()
|
||||
|
||||
const DAYS_SHORT = [
|
||||
"Mon",
|
||||
@ -58,6 +57,11 @@ function formatDate(date) {
|
||||
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")
|
||||
@ -84,6 +88,9 @@ class Table {
|
||||
this.startDate = today
|
||||
this.endDate = today
|
||||
|
||||
this.totalProjects = 0
|
||||
this.clockingTotals = []
|
||||
|
||||
this.update()
|
||||
}
|
||||
|
||||
@ -133,7 +140,7 @@ class Table {
|
||||
addClockings(date) {
|
||||
let dates = this.clockings.querySelector(".dates")
|
||||
let dateCell = document.createElement("th")
|
||||
let weekDay = DAYS_SHORT[(date.getDay() + 1) % 7]
|
||||
let weekDay = DAYS_SHORT[(date.getDay() + 6) % 7]
|
||||
let monthDay = date.getDate().toString().padStart(2, "0")
|
||||
dateCell.innerText = `${weekDay} ${monthDay}`
|
||||
dates.appendChild(dateCell)
|
||||
@ -146,7 +153,12 @@ class Table {
|
||||
return
|
||||
}
|
||||
|
||||
cell.replaceWith(this.timeInputTemplate.cloneNode(true))
|
||||
let inputCell = this.timeInputTemplate.cloneNode(true)
|
||||
let input = inputCell.querySelector("input")
|
||||
input.addEventListener("input", () => {
|
||||
this.setClocking(date, clocking.dataset.type, input.value)
|
||||
})
|
||||
cell.replaceWith(inputCell)
|
||||
})
|
||||
}
|
||||
|
||||
@ -184,13 +196,30 @@ class Table {
|
||||
row.dataset.id = id
|
||||
row.insertCell(-1).innerText = name
|
||||
row.insertCell(-1).innerText = sagexNum
|
||||
let total = 0
|
||||
times.forEach(time => {
|
||||
if (time) {
|
||||
total += time
|
||||
}
|
||||
let t = time.toString().split(".")
|
||||
if (t.length > 1 ){
|
||||
if (t.length > 1) {
|
||||
t[1] = t[1].slice(0, 2)
|
||||
}
|
||||
row.insertCell(-1).innerText = t.join(".")
|
||||
})
|
||||
let totalStr = total.toString().split(".")
|
||||
if (totalStr.length > 1) {
|
||||
totalStr[1] = totalStr[1].slice(0, 2)
|
||||
}
|
||||
row.insertCell(-1).innerText = totalStr.join(".")
|
||||
|
||||
if (isParent) {
|
||||
row.dataset.total = total
|
||||
this.totalProjects += total
|
||||
row.insertCell(-1)
|
||||
row.insertCell(-1)
|
||||
row.insertCell(-1)
|
||||
}
|
||||
}
|
||||
|
||||
addMonth(anchorDate) {
|
||||
@ -215,6 +244,8 @@ class Table {
|
||||
this.addWeek(monday, len)
|
||||
monday = nextMonday
|
||||
}
|
||||
|
||||
this.clockings.querySelector(".clocking.total").insertCell(-1)
|
||||
this.endDate = new Date(monday.valueOf() - monday.getDate() * DAY_MS)
|
||||
}
|
||||
|
||||
@ -233,29 +264,114 @@ class Table {
|
||||
}
|
||||
|
||||
displayData(data) {
|
||||
this.displayClockings(data.clockings)
|
||||
this.totalProjects = 0
|
||||
data.parents.forEach(parent => {
|
||||
this.addProject(
|
||||
parent.id,
|
||||
parent.name,
|
||||
parent.project_num,
|
||||
Array(this.nDays).fill(""),
|
||||
true
|
||||
)
|
||||
let parentDurations = Array(this.nDays).fill(0)
|
||||
|
||||
parent.projects.forEach(project => {
|
||||
let projects = parent.projects.map(project => {
|
||||
let durations = Array(this.nDays).fill("")
|
||||
project.tasks.forEach(task => {
|
||||
let date = new Date(task.date)
|
||||
let i = Math.floor((date.valueOf() - this.startDate.valueOf()) / DAY_MS)
|
||||
durations[i] = task.duration / 60
|
||||
let hours = task.duration / 60
|
||||
durations[i] = hours
|
||||
parentDurations[i] += hours
|
||||
})
|
||||
this.addProject(
|
||||
project.id,
|
||||
project.name,
|
||||
"",
|
||||
durations
|
||||
)
|
||||
return {
|
||||
id: project.id,
|
||||
name: project.name,
|
||||
durations: durations
|
||||
}
|
||||
})
|
||||
|
||||
this.addProject(
|
||||
parent.id,
|
||||
parent.name,
|
||||
parent.project_num,
|
||||
parentDurations.map(v => v === 0 ? "" : v),
|
||||
true
|
||||
)
|
||||
|
||||
projects.forEach(project => {
|
||||
this.addProject(project.id, project.name, "", project.durations)
|
||||
})
|
||||
})
|
||||
this.updateTotals()
|
||||
}
|
||||
|
||||
displayClockings(clockings) {
|
||||
this.clockingTotals = Array(this.nDays).fill(0)
|
||||
clockings.forEach(clocking => {
|
||||
let date = new Date(clocking.date)
|
||||
if (date < this.startDate || date > this.endDate) {
|
||||
return
|
||||
}
|
||||
let i = Math.floor((date - this.startDate) / DAY_MS)
|
||||
this.clockings.querySelectorAll("tr.clocking[data-type]").forEach(row => {
|
||||
let type = row.dataset.type
|
||||
let cell = row.cells[i + 1]
|
||||
if (clocking[type]) {
|
||||
cell.querySelector("input").value = clocking[type]
|
||||
}
|
||||
})
|
||||
let totalRow = this.clockings.querySelector(".clocking.total")
|
||||
let totalCell = totalRow.cells[i + 1]
|
||||
let hours = +clocking.total / 60
|
||||
this.clockingTotals[i] = hours
|
||||
totalCell.innerText = hours === 0 ? "" : Math.round(hours * 100) / 100
|
||||
})
|
||||
}
|
||||
|
||||
setClocking(date, type, time) {
|
||||
this.post(`/clockings/${formatDate(date)}/`, {
|
||||
[type]: time
|
||||
}).then(res => {
|
||||
let row = this.clockings.querySelector(".clocking.total")
|
||||
let i = Math.floor((date - this.startDate) / DAY_MS)
|
||||
let cell = row.cells[i + 1]
|
||||
let minutes = +res.clocking.total
|
||||
let hours = minutes / 60
|
||||
this.clockingTotals[i] = hours
|
||||
cell.innerText = hours === 0 ? "" : Math.round(hours * 100) / 100
|
||||
this.updateTotals()
|
||||
})
|
||||
}
|
||||
|
||||
updateTotals() {
|
||||
let totalClockings = this.clockingTotals.reduce((a, b) => a + b, 0)
|
||||
let totalProjects = this.totalProjects
|
||||
this.clockings.querySelector(".clocking.total").cells[this.nDays + 1].innerText = Math.round(totalClockings * 100) / 100
|
||||
|
||||
if (totalClockings === 0) {
|
||||
console.log("Total clockings = 0")
|
||||
return
|
||||
}
|
||||
if (totalProjects === 0) {
|
||||
console.log("Total projects = 0")
|
||||
return
|
||||
}
|
||||
this.times.querySelectorAll(".project.parent").forEach(parent => {
|
||||
let total = +parent.dataset.total
|
||||
let workingTimeRatio = total / totalClockings
|
||||
let imputedTimeRatio = total / totalProjects
|
||||
parent.cells[this.nDays + 3].innerText = formatPercentage(workingTimeRatio)
|
||||
parent.cells[this.nDays + 4].innerText = formatPercentage(imputedTimeRatio)
|
||||
let sagexTime = imputedTimeRatio * totalClockings
|
||||
parent.cells[this.nDays + 5].innerText = Math.round(sagexTime * 100) / 100
|
||||
})
|
||||
}
|
||||
|
||||
post(endpoint, data) {
|
||||
let fd = new FormData()
|
||||
Object.entries(data).forEach(([key, value]) => {
|
||||
fd.set(key, value)
|
||||
})
|
||||
return req(endpoint, {
|
||||
method: "POST",
|
||||
body: fd
|
||||
}).then(res => {
|
||||
return res.json()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user