summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/timetrack/timetrack.html125
1 files changed, 115 insertions, 10 deletions
diff --git a/modules/timetrack/timetrack.html b/modules/timetrack/timetrack.html
index c3e92dd..8d89455 100644
--- a/modules/timetrack/timetrack.html
+++ b/modules/timetrack/timetrack.html
@@ -6,6 +6,8 @@
<script>
"use strict";
+var lastlist=null;
+
function fetch(method,url,data/*?*/,creds/*?*/,cb){
if(!creds){
cb=data;
@@ -46,6 +48,7 @@ function toinputdate(date) {
pad(date.getSeconds(),2,"0");
}
+var monthnames=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
var weekdays=["Mon","Tue","Wed","Thu","Fri","Sat","Sun"];
function formatdate(date) {
@@ -110,11 +113,9 @@ function refreshlist(list){
var listelem=document.getElementById("eventlist");
listelem.innerHTML="";
var rows=[];
- var i,ev;
- var now=new Date();
+ var i;
for(i=0;i<list.length;i++){
- ev=list[i];
- rows.push([ev.date,tablerowfor(ev)]);
+ rows.push([list[i].date,tablerowfor(list[i])]);
}
if(rows.length==0){
var div=document.createElement("div");
@@ -123,16 +124,107 @@ function refreshlist(list){
listelem.appendChild(div);
return;
}
- rows.sort(function(a,b){return a[0]-b[0];}); //ascending sort on the dates
for(i=0;i<rows.length;i++){
listelem.appendChild(rows[i][1]);
}
}
+function weekstart(date){
+ var d=new Date(date.getFullYear(), date.getMonth(), date.getDate());
+ var wkday=(d.getDay()+6)%7;
+ return new Date(d - wkday*24*3600*1000);
+}
+
+function refreshcalendar(list,npreweeks){
+ if(npreweeks==undefined)npreweeks=2;
+
+ function datekey(d){return d.getFullYear()+"-"+d.getMonth()+"-"+d.getDate();}
+ var hist=new Map(), incheck=new Map();
+ var errors=[];
+ for(var i=0;i<list.length;i++){
+ var key=datekey(list[i].date);
+ if(list[i].type=="in"){
+ if(incheck.has(key)){
+ errors.push("Double check-in at "+formatdate(list[i].date));
+ continue;
+ }
+ incheck.set(key,list[i]);
+ } else if(list[i].type=="out"){
+ if(!incheck.has(key)){
+ errors.push("Loose check-out at "+formatdate(list[i].date));
+ continue;
+ }
+ var thistime=list[i].date-incheck.get(key).date;
+ incheck.delete(key);
+ var yet=hist.has(key)?hist.get(key):0;
+ hist.set(key,yet+thistime);
+ } else {
+ errors.push("Unknown type '"+list[i].type+"'");
+ }
+ }
+
+ var tb=document.getElementById("calendartb");
+ tb.innerHTML="";
+
+ var tr,td;
+ for(var i=0;i<errors.length;i++){
+ tr=document.createElement("tr");
+ td=document.createElement("td");
+ td.appendChild(document.createTextNode(errors[i]));
+ tr.appendChild(td);
+ tb.appendChild(tr);
+ }
+
+ var tr=document.createElement("tr");
+ tr.appendChild(document.createElement("td"));
+ var td;
+ for(var i=0;i<7;i++){
+ td=document.createElement("td");
+ td.appendChild(document.createTextNode(weekdays[i]));
+ tr.appendChild(td);
+ }
+ td=document.createElement("td");
+ var a=document.createElement("a");
+ a.href="javascript:refreshcalendar(lastlist,"+(npreweeks+4)+")";
+ a.appendChild(document.createTextNode("\u2191"));
+ td.appendChild(a);
+ tr.appendChild(td);
+ tb.appendChild(tr);
+
+ var thismonday=weekstart(new Date());
+ for(var wkoff=-npreweeks;wkoff<=1;wkoff++){
+ var monday=new Date(thismonday.getTime() + wkoff*7*24*3600*1000);
+
+ tr=document.createElement("tr");
+ td=document.createElement("td");
+ var label=monday.getDate().toString();
+ if(monday.getDate() <= 7 || wkoff == -npreweeks){
+ label = monthnames[monday.getMonth()].slice(0,3) + " " + label;
+ }
+ td.appendChild(document.createTextNode(label));
+ tr.appendChild(td);
+
+ for(var i=0;i<7;i++){
+ var day=new Date(thismonday.getTime() + (wkoff*7+i)*24*3600*1000);
+ var tm=hist.get(datekey(day))||0;
+ var descr=Math.round(tm/1000/3600*10)/10+"h";
+ var target=(day.getDay()+6)%7 < 5 ? 38/5 - 1 : 0;
+ var diff=tm - target*3600*1000;
+ var surplus=1-Math.exp(-(diff>0?diff:0)/(3*3600*1000));
+ var deficit=1-Math.exp(-(diff<0?-diff:0)/(3*3600*1000));
+ td=document.createElement("td");
+ td.appendChild(document.createTextNode(descr));
+ td.setAttribute("style","background-color:rgb("+(255-255*surplus)+","+(255-255*(deficit+surplus))+","+(255-255*deficit)+")");
+ tr.appendChild(td);
+ }
+ tb.appendChild(tr);
+ }
+}
+
function handleReceivedList(list){
- var i;
- for(i=0;i<list.length;i++)list[i].date=new Date(list[i].date);
+ lastlist=list;
refreshlist(list);
+ refreshcalendar(list);
}
function getlist(){
@@ -148,6 +240,8 @@ function getlist(){
alert("An error occurred!");
return;
}
+ for(var i=0;i<list.length;i++)list[i].date=new Date(list[i].date);
+ list.sort(function(a,b){return a.date-b.date;});
handleReceivedList(list);
});
}
@@ -280,17 +374,24 @@ body{
color:red;
cursor:pointer;
}
-.addeventform{
+#addeventform{
border:1px #ddd solid;
display:inline-block;
padding:5px;
}
-.addeventform > input[type="text"] {
+#addeventform > input[type="text"] {
margin-bottom: 5px;
}
#logoutwrapper{
float:right;
}
+#calendartb td{
+ width:35px;
+}
+#calendartb td:first-child{
+ text-align:right;
+ width:50px;
+}
</style>
</head>
<body>
@@ -300,7 +401,7 @@ body{
<h1>TimeTrack</h1>
<div id="eventlist"></div>
<br><br>
-<div class="addeventform">
+<div id="addeventform">
Sheet: <input type="text" id="addeventsheet" placeholder="Sheet">
<select id="sheetselect" onchange="setSheetFromSelect()"></select> <br>
Text: <input type="text" id="addeventtext" placeholder="Text"> (optional) <br>
@@ -310,5 +411,9 @@ body{
<label for="addeventtypeout" style="margin-left: 20px"><input type="radio" id="addeventtypeout" name="addeventtype"> Out</label> <br style="margin-top: 5px">
<input type="button" onclick="doAddEvent(event)" value="Add" style="margin-top: 5px">
</div>
+<br><br>
+<div id="calendar">
+ <table><tbody id="calendartb"></tbody></table>
+</div>
</body>
</html>