From b36d726db9d53ef4f786469c15fc9043d67a1c54 Mon Sep 17 00:00:00 2001 From: Daniel Baird Date: Mon, 17 Jun 2024 12:12:16 +1000 Subject: [PATCH] adds sprint calendar generator --- tools/sprintcalendar/behaviour.js | 150 ++++++++++++++++++++++++++++++ tools/sprintcalendar/index.html | 97 +++++++++++++++++++ tools/sprintcalendar/styles.css | 78 ++++++++++++++++ 3 files changed, 325 insertions(+) create mode 100644 tools/sprintcalendar/behaviour.js create mode 100644 tools/sprintcalendar/index.html create mode 100644 tools/sprintcalendar/styles.css diff --git a/tools/sprintcalendar/behaviour.js b/tools/sprintcalendar/behaviour.js new file mode 100644 index 0000000..f166701 --- /dev/null +++ b/tools/sprintcalendar/behaviour.js @@ -0,0 +1,150 @@ + +// ------------------------------------------------------------- +function stringIncrement(string) { + let chunks = string.split('.') + let lastChunk = chunks[chunks.length - 1] + const lastChunkLen = lastChunk.length + + // try incrementing the last chunk + lastChunk = '' + (parseInt(lastChunk,10) + 1) + + // pad last chunk to its original length + while (lastChunk.length < lastChunkLen) { + lastChunk = '0' + lastChunk + } + + // put chunks back together + chunks[chunks.length - 1] = lastChunk + return chunks.join('.') +} +// ------------------------------------------------------------- +function makeDayCell(date, above, left) { + const content = date.toLocaleString('en-AU', { weekday: 'short', day: 'numeric' }) + const [wDay, mDate] = content.split(' ') + + // work out what special classes to add + let classes = [] + // is the day above our same month? + if (above.getMonth() !== date.getMonth()) { + classes.push('differentAbove') + } + if (left.getMonth() !== date.getMonth()) { + classes.push('differentLeft') + } + if (date.getDay() === 0 || date.getDay() === 6) { + classes.push('weekend') + } + + return [ + '', + '', wDay, '', + '', mDate, '', + '' + ].join('') +} +// ------------------------------------------------------------- +function makeMonthCell(date, length, formats) { + let above = new Date(date) + above.setDate(date.getDate() - length) + + // work out what special classes to add + let classes = [] + // is the day above our same month? + if (above.getMonth() !== date.getMonth()) { + classes.push('differentAbove') + } + + const content = date.toLocaleString('en-AU', {month: 'long'}).substring(0, formats.monthFormat) + return '' + content + '' +} +// ------------------------------------------------------------- +function makeTextCell(text) { + return '' + text + '' +} +// ------------------------------------------------------------- +function updateSummary(firstDay, length, calendarLength, formats) { + let summary = [ + 'Calendar of', + calendarLength, + 'sprints of', + length, + 'days each, starting on', + firstDay.toLocaleDateString('en-AU', { + weekday: "long", + year: "numeric", + month: "long", + day: "numeric" + }) + ].join(' ') + document.querySelector('.summary').textContent = summary +} +// ------------------------------------------------------------- +function makeSprintRow(firstDay, length, name, formats) { + const row = [] + row.push(makeTextCell(name)) + row.push(makeMonthCell(firstDay, length, formats)) + const lastDay = new Date(firstDay) + lastDay.setDate(firstDay.getDate() + length) + + let daysToGo = length + let day = new Date(firstDay) + let left, above + day.setDate(day.getDate() - 1) + while (daysToGo > 0) { + day.setDate(day.getDate() + 1) + + left = new Date(day) + left.setDate(day.getDate() - 1) + + above = new Date(day) + above.setDate(day.getDate() - length) + daysToGo -= 1 + + row.push(makeDayCell(day, above, left)) + } + row.push(makeMonthCell(day, length, formats)) + row.push(makeTextCell(name)) + + return '' + row.join('\n') + '' +} +// ------------------------------------------------------------- +function makeCalendar() { + const tableBody = document.querySelector('table.calendar tbody') + + const firstDay = new Date(document.getElementById('sprintStart').value) + const length = parseInt(document.getElementById('sprintLength').value, 10) + const calendarLength = parseInt(document.getElementById('calendarLength').value, 10) + let name = document.getElementById('sprintName').value + + const formats = { + monthFormat: document.getElementById('monthNameFormat').value + } + + updateSummary(firstDay, length, calendarLength, formats) + + let sprints = [] + + for (let sprint=0; sprint < calendarLength; sprint++) { + sprints.push(makeSprintRow(firstDay, length, name, formats)) + firstDay.setDate(firstDay.getDate() + length) + name = stringIncrement(name) + } + tableBody.innerHTML = sprints.join('\n') +} +// ------------------------------------------------------------- +// ------------------------------------------------------------- +// attach handler to button +const goButton = document.getElementById('makeCalendar') +if (goButton) { + goButton.addEventListener('click', function(e) { + makeCalendar() + e.preventDefault() + return false + }) +} + +document.querySelectorAll('input, select').forEach( + el => el.addEventListener('change', makeCalendar) +) + +makeCalendar() diff --git a/tools/sprintcalendar/index.html b/tools/sprintcalendar/index.html new file mode 100644 index 0000000..11408e4 --- /dev/null +++ b/tools/sprintcalendar/index.html @@ -0,0 +1,97 @@ + + + + + + + Sprint Calendar + + + + + + + + + +
+

Sprint Calendar

+
+ +
+ +
+
+ Sprints + + + + +
+
+ Display + + + +
+ +
+ +

+ + + + + + + +
+ +
+ + + + + + diff --git a/tools/sprintcalendar/styles.css b/tools/sprintcalendar/styles.css new file mode 100644 index 0000000..760de4d --- /dev/null +++ b/tools/sprintcalendar/styles.css @@ -0,0 +1,78 @@ + +html { + font-family: Montserrat, lato, sans-serif; + font-size: 14px; + font-size: calc(10px + 0.5vw); + line-height: 1.5; + background: white; + padding: 0 1rem; + margin: 0; +} + +fieldset { + margin: 0 0 1rem 0; + border: solid 1px #bdf; + border-width: 1px 0 0 1rem; +} +legend { + padding: 0 0.5em; +} +label { + display: block; + padding: 0.25em 0; +} +input, select, button { + font-size: inherit; + font-family: inherit; + padding: 0.2em 0.5em; + border-radius: 0.25rem; + border: 1px solid #ccc; +} +input[type=number] { + text-align: center; + max-width: 4em; +} + +table.calendar { + border-collapse: collapse; +} +table.calendar td, table.calendar th { + text-align: center; + border: 1px solid #aaa; + padding: 0.5rem 0; + min-width: 3.5rem; +} +table.calendar td.weekend, table.calendar th.weekend { + min-width: 1.66rem; +} +.calendar .dayName, .calendar .monthDate { + display: block; + line-height: 1.0; + padding-top: 0.1em; + font-size: 0.8rem; +} +.calendar .weekend .dayName { font-size: 0.5rem; } +.calendar .monthDate { + font-weight: 700; + opacity: 0.5; + font-size: 1.66rem; + opacity: 0.25; +} +.calendar .weekend .monthDate { font-size: 0.9rem; } + +table.calendar td.differentAbove { + border-top-width: 4px; +} +table.calendar td.differentLeft { + border-left-width: 4px; +} +table.calendar td.weekend { + font-size: 60%; + background: #eee; +} + +@media print { + fieldset { + display: none; + } +}