easy-planner/index.html
Yann Esposito (Yogsototh) 0433ad434f
improvements
2021-09-06 18:39:45 +02:00

365 lines
10 KiB
HTML

<html>
<head>
<title>Plan</title>
<style>
body { font-family: Monospace;
color: hsl(218,20%, 35%);
background-color: hsl(218,20%, 95%);
font-size: 16px;
}
table { font-size: 16px; border: none; padding: 1em; border-collapse: collapse;}
img {
width:50px;
height:50px;
}
td {
width:130px;
height:30px;
text-align: center;
padding: 5px;
border: solid 1px hsla(218,20%,35%,0.15);
}
th { padding-right: 1em; }
.card { border-radius: 6px;
border: solid 2px;
min-width: 1em;
height: 1em;
padding: 1px 3px 3px ;
text-align: center;
font-weight: bold;
margin: 2px;
display: inline-block;
}
#trash {
padding: 0.3em;
width: 50px;
border-radius: 3px;
margin: 1em;
font-size: 2em;
font-weight: bold;
}
.hl { background-color: hsl(0,90%,80%); color: white; }
</style>
</head>
<body>
<center>
<h1>Plan</h1>
<h3>Team</h3>
<div id="devs"></div>
<h3>releases</h3>
<div id="main"></div>
<div id="trash">&#x1f5d1;</div>
</body>
<script>
// (function () {
function debug(args) {
// uncomment do debug
// console.log(args);
return null;
}
function enabledrag(event) { event.preventDefault(); }
var state = null;
function retrieveState(){
var savestr = localStorage.getItem('plannerstate');
if (savestr) {
state = JSON.parse(savestr);
} else {
state = { people: ["Ag",
"Ambrose",
"GB",
"GE",
"Irina",
"Kiril",
"M",
"Mark",
"Olivier",
"Rob",
"Wanderson",
"Y",
],
fts: [ "SX Session",
"Bug Squashing",
"Secure Client",
"PTO"],
assignments: {}
};
}
debug(state);
}
function saveState() {
localStorage.setItem('plannerstate',JSON.stringify(state));
}
function init(){
retrieveState();
debug(state);
var devcards=document.getElementById("devs");
state.people.forEach(function(p) {
devcards.appendChild( createDev(p) );
})
renderState();
}
function renderState () {
if (state) {
// build main table
var main = document.getElementById("main");
main.innerHTML='';
main.appendChild(genTable());
var cells = document.querySelectorAll("td");
cells.forEach(function(cell) {
cell.ondragover = enabledrag;
cell.ondrop = function(ev){drop(ev,this);};
});
// build trash
var trashel = document.getElementById("trash");
trashel.ondragover = enabledrag;
trashel.ondrop = trash;
trashel.ondragenter = hltrash;
trashel.ondragleave = unhltrash;
var cards = document.querySelectorAll(".card");
cards.forEach(function(card){ card.draggable = true; card.ondragstart = drag; });
// build assignments
for ( cellid in state.assignments ) {
state.assignments[cellid].forEach (function(member) {
var el = document.getElementById( cellid );
if ( el ) {
debug("devcard; member: " + member + ", cellid: " + cellid);
el.appendChild(devcard(member, cellid));
}
})
}
}
}
function genTable () {
var t = document.createElement("table");
t.id = "maintable";
var previous = 2; // nb of previous release to show (2 means, 1 previous to prod, + current in prod)
var nexts = 4; // nb of next releases to show
var nbcolumns = previous + nexts;
var rels = releases( previous, nexts );
rels[0]="FT";
t.appendChild(mkTitleRow(rels));
state.fts.forEach( function(ft) {
t.appendChild(mkRow(ft,nbcolumns,rels));
});
return t;
}
function mkTitleRow( vs ) {
var rows = document.createElement("tr");
vs.forEach (function(v) {
var c = document.createElement("th");
c.textContent = v;
rows.appendChild(c);
});
return rows;
}
function safe (str) {
var safestr = str.replace(/\W/g,'_');
return safestr;
}
function mkid(ft,rel) {
return "cell-" + safe(ft) + "-" + rel;
}
function mkRow( ft, n, rels ) {
var rows = document.createElement("tr");
var ttitle = document.createElement("th");
ttitle.textContent = ft;
rows.appendChild(ttitle)
var arr = new Array(n);
arr.fill("");
i=1;
arr.forEach (function(v) {
var c = document.createElement("td");
c.textContent = v;
c.id = mkid(ft, rels[i]);
rows.appendChild(c);
i++;
});
return rows;
}
function devcard(devname,cellid){
var devdiv = document.createElement('div');
devdiv.classList.add(devname);
devdiv.classList.add('card');
devdiv.textContent = devname;
devdiv.draggable = true;
devdiv.id = "inner-" + devname + "-"+ cellid;
devdiv.style.backgroundColor = genColor(devname);
devdiv.ondragstart = drag;
return devdiv;
}
function hashcode(s){
return s.split("").reduce(
function(acc,l){
acc = acc + 10 * (l.charCodeAt(0) - 'A'.charCodeAt(0));
return (acc % 256);
},
0);
}
function genColor(devname) {
var hue = hashcode(devname);
switch (devname) {
case "GE":
case "GB":
case "M":
case "Y":
hue = 0;
break;
default:
hue = hue % 200 + 56;
}
return "hsl(" + hue + ",90%, 80%)";
}
function createDev(devname){
var res = devcard(devname,'');
res.id = devname;
return res;
};
// release logic
function getWeekNumber(d) {
// Copy date so don't modify original
d = new Date(Date.UTC(d.getFullYear(), d.getMonth(), d.getDate()));
// Set to nearest Thursday: current date + 4 - current day number
// Make Sunday's day number 7
d.setUTCDate(d.getUTCDate() + 4 - (d.getUTCDay()||7));
// Get first day of year
var yearStart = new Date(Date.UTC(d.getUTCFullYear(),0,1));
// Calculate full weeks to nearest Thursday
var weekNo = Math.ceil(( ( (d - yearStart) / 86400000) + 1)/7);
// Return array of year and week number
return [d.getUTCFullYear(), weekNo];
}
// should return a list of next releases
// use the current date
function releases(previous,nexts) {
var curr = new Date; // get current date
var monday = curr.getDate() - curr.getDay() + 1;
var wed = monday + 2;
var nbDays = curr.getDay() - 3;
var afterWed = nbDays > 0;
var w = getWeekNumber(curr);
var year = w[0];
var week = w[1];
var release = "";
var releaseWeek = (week+1)%2;
var releaseNumVersion = Math.floor((week - 16)/2) + 71;
var currentVersion = "v1." + (releaseNumVersion - (1 + previous));
var size=nexts + previous + 1;
var releases = new Array(size)
for (i = 0; i < size; i++) {
releases [i] = "v1." + (releaseNumVersion + (i - 3));
}
return releases;
}
// drag n drop
function drag(ev) {
ev.dataTransfer.setData("text", ev.target.id);
}
function createAssignment(id, name) {
if (! state.assignments[id]) {
state.assignments[id]=[];
}
if ( state.assignments[id].indexOf(name) == -1 ) {
state.assignments[id].push(name);
}
debug("createAssignment");
debug(state.assignments);
}
function moveAssignment(id, name) {
var memberName = name.replace(/^[^-]*-/,'').replace(/-cell-.*$/,'');
removeAssignment(name);
if (! state.assignments[id]) {
state.assignments[id]=[];
}
if ( state.assignments[id].indexOf(memberName) == -1 ) {
state.assignments[id].push(memberName);
}
debug("moveAssignment");
debug(state.assignments);
}
function drop(ev,el) {
ev.preventDefault();
var name = ev.dataTransfer.getData("text");
if ( name.startsWith("inner-") ) {
debug("move: " + name);
document.getElementById(name).remove();
var memberName = name.replace(/^[^-]*-/,'').replace(/-cell-.*$/,'');
var newdevcard = devcard(memberName,el.id);
if (! document.getElementById(newdevcard.id)) {
el.appendChild(newdevcard);
moveAssignment(el.id, name);
}
} else {
debug("create: " + name);
var newdevcard =devcard(name,el.id);
if (! document.getElementById(newdevcard.id)) {
el.appendChild(newdevcard);
createAssignment(el.id, name);
}
}
saveState();
}
function removeAssignment(name) {
var memberName = name.replace(/^[^-]*-/,'').replace(/-cell-.*$/,'');
var oldid = name.replace(/^.*-cell-/,'cell-');
debug("memberName: " + memberName);
debug("OLDID: " + oldid);
var idx = state.assignments[oldid].indexOf(memberName);
if (idx !== -1) {
state.assignments[oldid].splice(idx,1);
}
debug("removeAssignment");
debug(state.assignments);
}
function trash(ev) {
ev.preventDefault();
var name = ev.dataTransfer.getData("text");
if ( name.startsWith("inner-") ) {
debug("TRASH: " + name);
unhltrash(null);
document.getElementById(name).remove();
removeAssignment(name);
}
saveState();
}
function hltrash(ev) {
var trashel = document.getElementById('trash');
trashel.classList.add("hl");
}
function unhltrash(ev) {
var trashel = document.getElementById('trash');
trashel.classList.remove("hl");
}
window.addEventListener("load", init);
// })();
</script>
</html>