{"id":22884241,"url":"https://github.com/minusinfinite/work-day-scheduler","last_synced_at":"2026-05-06T11:36:27.917Z","repository":{"id":178015800,"uuid":"383405706","full_name":"minusInfinite/Work-Day-Scheduler","owner":"minusInfinite","description":"A Simple Daily Planner","archived":false,"fork":false,"pushed_at":"2021-07-10T08:03:41.000Z","size":842,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-06-05T04:10:00.702Z","etag":null,"topics":["bootcamp","bootstrap5","javascript"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/minusInfinite.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-07-06T09:01:40.000Z","updated_at":"2021-07-13T05:26:39.000Z","dependencies_parsed_at":null,"dependency_job_id":"e93a0c3b-167d-4ba3-86af-80ddc57f70c7","html_url":"https://github.com/minusInfinite/Work-Day-Scheduler","commit_stats":null,"previous_names":["minusinfinite/work-day-scheduler"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/minusInfinite/Work-Day-Scheduler","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/minusInfinite%2FWork-Day-Scheduler","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/minusInfinite%2FWork-Day-Scheduler/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/minusInfinite%2FWork-Day-Scheduler/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/minusInfinite%2FWork-Day-Scheduler/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/minusInfinite","download_url":"https://codeload.github.com/minusInfinite/Work-Day-Scheduler/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/minusInfinite%2FWork-Day-Scheduler/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32692338,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-06T08:33:17.875Z","status":"ssl_error","status_checked_at":"2026-05-06T08:33:17.221Z","response_time":117,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bootcamp","bootstrap5","javascript"],"created_at":"2024-12-13T19:17:02.665Z","updated_at":"2026-05-06T11:36:27.901Z","avatar_url":"https://github.com/minusInfinite.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Bootcamp Week 4 Work Day Scheduler\n\n## Project Details\n\n**Live Demo - https://minusinfinite.github.io/Work-Day-Scheduler/**\n\nThis week was a focus on working with third-party libraries and frameworks.\nWith a focus on [Bootstrap](https://getbootstrap.com/), JQuery and Moment.\n\nThe features from JQuery and Moment are quite matured at this point, for this project I used vanilla JS and [Luxon](https://moment.github.io/luxon/#/) as the Date utility.\n\nFor this project, the following User Story was provided.\n\n## User Story\n\n\u003e AS AN employee with a busy schedule\n\u003e\n\u003e I WANT to add important events to a daily planner\n\u003e\n\u003e SO THAT I can manage my time effectively\n\nThe following GIF was given as a demo of expected functionality.\nAs well as an HTML template and CSS Stylesheet\n\n![Application Functional Demo](./assets/md/demo.gif)\n\n## Acceptance Criteria and Development process\n\n\u003e GIVEN I am using a daily planner to create a schedule\n\u003e\n\u003e WHEN I open the planner\n\u003e\n\u003e THEN the current day is displayed at the top of the calendar\n\nTo use a library such as Luxon from the browser it is important to add it to the HTML\n\n```html\n\u003cscript\n    src=\"https://cdn.jsdelivr.net/npm/luxon@1.27.0/build/global/luxon.min.js\"\n    integrity=\"sha256-cJnCTPRTD3OUjTD4Ml0WEMsmTiLl71arKaZ9DEZJk0o=\"\n    crossorigin=\"anonymous\"\n\u003e\u003c/script\u003e\n```\n\nIf your script you can then call the libraries namespace, in this case, `luxon`. It is then possible to create a global `setInterval` to continually update and track DateTime\n\n```javascript\nconst dt = luxon.DateTime\n\nsetInterval(function () {\n    const todayEl = document.querySelector(\"#currentDay\")\n    let dateLocal = dt.local().toLocaleString(dt.DATE_FULL)\n    todayEl.textContent = dateLocal\n}, 1000)\n```\n\nThis is set with a delay of 1 second due to a function needed later in the development.\n\n\u003e WHEN I scroll down\n\u003e\n\u003e THEN I am presented with timeblocks for standard business hours\n\nFrameworks like Bootstrap often end up with a `class` attribute as long as a sane person character limit.\nI found it a requirement to have a way to dynamically store the required page structure without having it HTML itself.\n\nAs such the following was devised as a solution\n\n```javascript\nfunction buildEl(tagName, elText, cssString, elAttr) {\n    let el = document.createElement(tagName)\n    el.className = cssString\n    el.textContent = elText\n    for (let i = 0; i \u003c elAttr.length; i++) {\n        el.setAttribute(\n            elAttr[i].toString().split(\" \")[0],\n            elAttr[i].toString().split(\" \")[1]\n        )\n    }\n    return el\n}\n```\n\nThe function above was made to make a valid DOMString for all required elements.\nIt would likely be possible to extend this function to account for additional child elements but that wasn't needed for this project\n\nWith the above function created the following object was used to store the required tag, class and the array to assign the ids and data-\\* attributes for later\n\n```javascript\nconst elementStrings = {\n    tagName: [\"p\", \"textarea\", \"button\", \"div\"],\n    classes: [\n        \"col-sm-1 hour text-end\",\n        \"col-sm-6 border border-dark border-1\",\n        \"saveBtn col-sm-1 btn rounded-0 fs-3\",\n        \"w-100 d-none my-1 d-sm-block\",\n    ],\n    attributes: [],\n}\n```\n\nThis is the array made for standard work hours, this is used to create unique ids and data-\\* attributes\n\n```javascript\nconst workHours = [9, 10, 11, 12, 13, 14, 15, 16, 17]\n```\n\nThis comes together with the following `for` loop\n\n```javascript\nfor (let i = 0; i \u003c workHours.length; i++) {\n    for (let e = 0; e \u003c elementStrings.tagName.length; e++) {\n        let eName = elementStrings.tagName[e]\n        let text = \"\"\n        let eClasses = elementStrings.classes[e]\n        elementStrings.attributes = []\n        if (eName === \"p\") {\n            text = formatHour(workHours[i])\n        }\n        if (eName === \"textarea\") {\n            text = savedSchedules[workHours[i]]\n        }\n        if (eName === \"button\") {\n            elementStrings.attributes.push(`type ${eName}`)\n        }\n        elementStrings.attributes.push(`id ${eName}-${workHours[i]}`)\n        elementStrings.attributes.push(`data-hour ${workHours[i]}`)\n        timeblockEl.appendChild(\n            buildEl(eName, text, eClasses, elementStrings.attributes)\n        )\n    }\n}\n```\n\nOne minor limitation of Luxon due to its use of the [`Intl`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl) Javascript namespace to find a format DateTime properties having it add the Meridien AM/PM to the timestamp consistently was a hurdle.\n\nTo tackle this shortcoming the following utility was created.\nThis allowed for adding a format option without overthinking where the timeStamp was being provided.\n\n```javascript\nconst formatHour = function (timeStamp) {\n    let hour = \"\"\n    if (timeStamp === 0) {\n        hour = `${12}am`\n    } else if (timeStamp === 12) {\n        hour = `${12}pm`\n    } else if (timeStamp \u003e 12) {\n        hour = timeStamp - 12 + \"pm\"\n    } else {\n        hour = `${timeStamp}am`\n    }\n    return hour\n}\n```\n\nA feature, not in this brief could be to make a prompt for the user to specify their start time and duration of work, this could create more or fewer blocks as needed.\n\n\u003e WHEN I view the timeblocks for that day\n\u003e\n\u003e THEN each timeblock is color coded to indicate whether it is in the past, present, or future\n\nFor this requirement, the following function was created to compare the time to the textarea tags data-\\* attribute.\nIf this matched the current hour it will add or remove the class required. The [`classList`](https://developer.mozilla.org/en-US/docs/Web/API/Element/classList) property and methods made it easy to do without editing any other Layout classes.\n\n```javascript\nfunction updateTimeBlock() {\n    const timeBlocks = document.querySelectorAll(\"textarea[data-hour\")\n    const currentHour = dt.local().hour\n\n    for (let i = 0; i \u003c timeBlocks.length; i++) {\n        //data-* are always string values so a parseInt here confirms it will always be a number\n        let currentBlock = parseInt(timeBlocks[i].getAttribute(\"data-hour\"))\n        if (currentBlock === currentHour) {\n            timeBlocks[i].classList.add(\"present\")\n            timeBlocks[i].classList.remove(\"past\")\n            timeBlocks[i].classList.remove(\"future\")\n        } else if (currentBlock \u003c currentHour) {\n            timeBlocks[i].classList.remove(\"present\")\n            timeBlocks[i].classList.add(\"past\")\n            timeBlocks[i].classList.remove(\"future\")\n        } else if (currentBlock \u003e currentHour) {\n            timeBlocks[i].classList.remove(\"present\")\n            timeBlocks[i].classList.remove(\"past\")\n            timeBlocks[i].classList.add(\"future\")\n        }\n    }\n    confirm.classList.add(\"confirmed\")\n}\n```\n\n\u003e WHEN I click into a timeblock\n\u003e\n\u003e THEN I can enter an event\n\u003e\n\u003e WHEN I click the save button for that timeblock\n\u003e\n\u003e THEN the text for that event is saved in local storage\n\u003e\n\u003e WHEN I refresh the page\n\u003e\n\u003e THEN the saved events persist\n\nThe following snippet provides the functions for tracking our button clicks and saving any entered text value\n\n```javascript\ntimeblockEl.addEventListener(\"click\", function (event) {\n    const element = event.target\n    let timeBlock = \"\"\n    let textarea\n\n    let scheduleEntry = {\n        time: 0,\n        entry: \"\",\n    }\n\n    if (element.localName === \"button\" \u0026\u0026 element.hasAttribute(\"data-hour\")) {\n        timeBlock = element.getAttribute(\"data-hour\")\n        textarea = document.getElementById(`textarea-${timeBlock}`)\n\n        scheduleEntry.time = parseInt(timeBlock)\n        scheduleEntry.entry = textarea.value.trim()\n        localStorage.setItem(scheduleEntry.time, scheduleEntry.entry)\n\n        scheduleObjects()\n        confirm.classList.remove(\"confirmed\")\n    }\n})\n```\n\nThis is quite similar to our requirement from the last Project.\nA difference here is that each textarea is equal to a given key in [`localStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage)\n\nAs this has multiple keys to collect and recall data from the process of recalling them of refresh is a little different.\n\nThe snippet below takes the previously defined array of work hours and either make the same amount of objects.\nThis is called by another variable that is used to call the [Computed property name](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#computed_property_names)\nobject values needed. When the page is built this it was possible to call the keys with bracket notation eg `text = savedSchedules[workHours[i]]`\n\n```javascript\nlet scheduleObjects = function () {\n    let obj = {}\n    for (let i = 0; i \u003c workHours.length; i++) {\n        let savedTask = localStorage.getItem(workHours[i]) || \"\"\n        obj[workHours[i]] = savedTask\n    }\n    return obj\n}\n\nconst savedSchedules = scheduleObjects()\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fminusinfinite%2Fwork-day-scheduler","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fminusinfinite%2Fwork-day-scheduler","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fminusinfinite%2Fwork-day-scheduler/lists"}