Task management & time tracking script – Javascript
We are going to create customized task management and time tracking script using simple HTML and Javascript. Since we are creating our own script, so we can customize it as much as we need. You can check the demo from here.
Video tutorial
Include libraries
First, we need to include all the libraries that we are going to use. We will be using Bootstrap for design and SweetAlert for displaying pop-up messages.
<!-- to make responsive -->
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- include bootstrap css -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.0/css/bootstrap.min.css" />
<!-- include jquery and bootstrap js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.0/js/bootstrap.min.js"></script>
<!-- include sweetalert for displaying dialog messages -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/sweetalert/2.1.2/sweetalert.min.js"></script>
Add project
Then we are going to create a button that when clicked will display a Bootstrap modal to add a task.
<div class="container" style="margin-top: 50px; margin-bottom: 50px;">
<!-- button to add task -->
<div class="row" style="margin-bottom: 50px;">
<div class="col-md-12">
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#addTaskModal">Add Task</button>
</div>
</div>
</div>
Then we are going to create a Bootstrap modal. It will have a form with project and task name fields. It will also have a dropdown from which the user can select his already created projects.
<!-- modal to add project and task -->
<div class="modal fade" id="addTaskModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Add Task</h5>
<button class="close" type="button" data-dismiss="modal">x</button>
</div>
<div class="modal-body">
<form method="POST" onsubmit="return taskObj.addTask(this);" id="form-task-hour-calculator">
<!-- select project from already created -->
<div class="form-group">
<label>Project</label>
<select name="project" id="add-task-project" class="form-control" required></select>
</div>
<!-- create new project -->
<div class="form-group">
<label>New Project</label>
<input type="text" name="new_project" id="add-project" class="form-control" placeholder="Project Name">
<button type="button" onclick="taskObj.addProject();" class="btn btn-primary" style="margin-top: 10px;">Add Project</button>
</div>
<!-- enter task -->
<div class="form-group">
<label>Task</label>
<input type="text" name="task" class="form-control" placeholder="What are you going to do ?" required />
</div>
</form>
</div>
<!-- form submit button -->
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button type="submit" form="form-task-hour-calculator" class="btn btn-primary">Add Task</button>
</div>
</div>
</div>
</div>
Then we need to create a Javascript function “addProject” inside the “taskObj” object. It will first check if there is any project selected, if not, then it will show an error message. Then it will create an empty array in local storage if not already created. Then it will create a new project object with empty “tasks” array. Finally, it will push the project into an array and update the local storage value, and also it will reload the projects and tasks.
// main object
var taskObj = {
// local storage key
key: "projects",
// add project
addProject: function () {
// check if project is selected
if (document.getElementById("add-project").value == "") {
swal("Please enter project name");
return false;
}
// initialize local storage if not already initialized
var option = "";
if (localStorage.getItem(this.key) == null) {
localStorage.setItem(this.key, "[]");
}
// get stored object from local storage
var data = JSON.parse(localStorage.getItem(this.key));
// project object
var project = {
id: data.length,
name: document.getElementById("add-project").value,
tasks: []
};
// push new project in local storage
data.push(project);
localStorage.setItem(this.key, JSON.stringify(data));
// re-load all projects
this.loadAllProjects();
// show all tasks
this.showAllTasks();
},
};
To load all the projects, we will be using the following 2 functions inside this object:
// get all stored projects
getAllProjects: function() {
if (localStorage.getItem(this.key) == null) {
localStorage.setItem(this.key, "[]");
}
return JSON.parse(localStorage.getItem(this.key))
},
// load all projects in dropdown
loadAllProjects: function () {
var projects = taskObj.getAllProjects();
projects = projects.reverse();
var html = "<option value=''>Select Project</option>";
for (var a = 0; a < projects.length; a++) {
html += "<option value='" + projects[a].id + "'>" + projects[a].name + "</option>";
}
document.getElementById("add-task-project").innerHTML = html;
document.getElementById("form-task-hour-calculator-all-projects").innerHTML = html;
},
We will do the “showAllTasks” function later. Now, when the page loads, we need to fetch all the projects and tasks and display them.
// when page loads
window.addEventListener("load", function () {
// show all projects and tasks
taskObj.loadAllProjects();
taskObj.showAllTasks();
});
At this point, you will be able to create projects and see them in the dropdown. Now we need to add tasks.
Add task
To add a task, we will get the project and task name. Then we will push the newly created task object into the “tasks” array. Finally, we will update the local storage value, hide the Bootstrap modal and reload the tasks.
// add new task
addTask: function (form) {
// get selected project and entered task
var project = form.project.value;
var task = form.task.value;
// add task in project's array
var projects = this.getAllProjects();
for (var a = 0; a < projects.length; a++) {
if (projects[a].id == project) {
var taskObj = {
id: projects[a].tasks.length,
name: task,
status: "Progress", // Progress, Completed
isStarted: false,
logs: [],
started: this.getCurrentTimeInTaskStartEndFormat(),
ended: ""
}
projects[a].tasks.push(taskObj);
break;
}
}
// update local storage
localStorage.setItem(this.key, JSON.stringify(projects));
// hide modal
jQuery("#addTaskModal").modal("hide");
jQuery('.modal-backdrop').remove();
// re-load all tasks
this.showAllTasks();
// prevent form from submitting
return false;
},
You might have see the function “getCurrentTimeInTaskStartEndFormat”. Our task management script will save the date and time in format YYYY-MM-DD HH:mm:ss e.g. 2021-06-15 20:53:15.
// get current datetime in proper format (e.g. 2021-06-15 20:53:15)
getCurrentTimeInTaskStartEndFormat() {
let current_datetime = new Date();
var date = current_datetime.getDate();
date = (date < 10) ? "0" + date : date;
var month = (current_datetime.getMonth() + 1);
month = (month < 10) ? "0" + month : month;
var hours = current_datetime.getHours();
hours = (hours < 10) ? "0" + hours : hours;
var minutes = current_datetime.getMinutes();
minutes = (minutes < 10) ? "0" + minutes : minutes;
var seconds = current_datetime.getSeconds();
seconds = (seconds < 10) ? "0" + seconds : seconds;
let formatted_date = current_datetime.getFullYear() + "-" + month + "-" + date + " " + hours + ":" + minutes + ":" + seconds;
return formatted_date;
},
Show all tasks
To show all tasks, we need to create a table where we will display all tasks created.
<!-- show all tasks -->
<table class="table">
<caption class="text-center">All Tasks</caption>
<tr>
<th>Task</th>
<th>Project</th>
<th>Status</th>
<th>Duration</th>
<th>Date</th>
<th>Action</th>
</tr>
<tbody id="all-tasks"></tbody>
</table>
Now is the time to create a function “showAllTasks” in our “taskObj” object. This will be a long function, so we will explain the working of this function step-by-step:
// show all tasks in table
showAllTasks: function () {
var html = "";
// get all projects
var projects = this.getAllProjects();
for (var a = 0; a < projects.length; a++) {
projects[a].tasks = projects[a].tasks.reverse();
// get tasks in each project
for (var b = 0; b < projects[a].tasks.length; b++) {
html += "<tr>";
html += "<td>" + projects[a].tasks[b].name + "</td>";
html += "<td>" + projects[a].name + "</td>";
if (projects[a].tasks[b].isStarted) {
html += "<td><label class='started'>Started</label></td>";
} else {
if (projects[a].tasks[b].status == "Completed") {
html += "<td><label class='completed'>" + projects[a].tasks[b].status + "</label></td>";
} else {
html += "<td>" + projects[a].tasks[b].status + "</td>";
}
}
// get total duration of each task using it's logs
var duration = 0;
for (var c = 0; c < projects[a].tasks[b].logs.length; c++) {
var log = projects[a].tasks[b].logs[c];
if (log.endTime > 0) {
duration += log.endTime - log.startTime;
}
}
// convert millisecond into hours, minutes and seconds
duration = Math.abs((duration / 1000).toFixed(0));
var hours = Math.floor(duration / 3600) % 24;
hours = (hours < 10) ? "0" + hours : hours;
// var days = Math.floor(diff / 86400);
var minutes = Math.floor(duration / 60) % 60;
minutes = (minutes < 10) ? "0" + minutes : minutes;
var seconds = duration % 60;
seconds = (seconds < 10) ? "0" + seconds : seconds;
// show timer if task is already started
if (projects[a].tasks[b].isStarted) {
var dataStartedObj = {
"duration": duration,
"project": projects[a].id,
"task": projects[a].tasks[b].id
};
html += "<td data-started='" + JSON.stringify(dataStartedObj) + "'>" + hours + ":" + minutes + ":" + seconds + "</td>";
} else {
html += "<td>" + hours + ":" + minutes + ":" + seconds + "</td>";
}
// show task duration if completed
if (projects[a].tasks[b].status == "Completed") {
html += "<td>" + projects[a].tasks[b].started + "<br><span style='margin-left: 30px;'>to</span><br>" + projects[a].tasks[b].ended + "</td>";
} else {
html += "<td>" + projects[a].tasks[b].started + "</td>";
}
// form to change task status
html += "<td>";
html += "<form method='POST' id='form-change-task-status-" + projects[a].id + projects[a].tasks[b].id + "'>";
html += "<input type='hidden' name='project' value='" + projects[a].id + "'>";
html += "<input type='hidden' name='task' value='" + projects[a].tasks[b].id + "'>";
html += "<select class='form-control' name='status' onchange='taskObj.changeTaskStatus(this);' data-form-id='form-change-task-status-" + projects[a].id + projects[a].tasks[b].id + "'>";
html += "<option value=''>Change status</option>";
if (projects[a].tasks[b].isStarted) {
html += "<option value='stop'>Stop</option>";
} else {
html += "<option value='start'>Start</option>";
}
if (projects[a].tasks[b].status == "Progress") {
html += "<option value='complete'>Mark as Completed</option>";
} else {
html += "<option value='progress'>Make in Progress Again</option>";
}
html += "<option value='delete'>Delete</option>";
html += "</select>";
html += "</form>";
html += "</td>";
html += "</tr>";
}
}
document.getElementById("all-tasks").innerHTML = html;
},
The step number corresponds to the highlighted line above.
- First, we are looping through all projects and each project’s tasks.
- Displaying the task name and project name.
- If the task status is “started” then displaying a started message. (we will do this later).
- And if the task status is “completed”, then displaying a message for the completed task.
- Then we will get the total duration of each task.
- The duration will be in milliseconds, so we will convert that into days, hours, minutes, and seconds.
- If the task is started, then we will attach an attribute “data-started” to the <td> tag. This will allow us to continuously update the timer every second.
- And if the task is completed, then we will display the start and end times of the task. So you can know when the task started and when it is finished.
- And finally, we are creating a form from which we can change the status of the task.
Change task status
Now we need to create a function to change the status of the task. Again, this will be a long function, we will explain that step-by-step too.
// change task status
changeTaskStatus: function (self) {
// if task is not selected
if (self.value == "") {
return;
}
// loop through all projects
var formId = self.getAttribute("data-form-id");
var form = document.getElementById(formId);
var projects = this.getAllProjects();
for (var a = 0; a < projects.length; a++) {
// if project matches
if (projects[a].id == form.project.value) {
// loop through all tasks of that project
for (var b = 0; b < projects[a].tasks.length; b++) {
// if task matches
if (projects[a].tasks[b].id == form.task.value) {
// if the status is set to delete
if (self.value == "delete") {
// ask for confirmation
swal({
title: "Are you sure?",
text: "Deleting the task will delete its hours too.",
icon: "warning",
buttons: true,
dangerMode: true,
})
.then((willDelete) => {
if (willDelete) {
// remove task from array
projects[a].tasks.splice(b, 1);
// update local storage
localStorage.setItem(this.key, JSON.stringify(projects));
// re-load all tasks
this.showAllTasks();
} else {
// reset dropdown
self.value = "";
}
});
} else if (self.value == "complete") {
// mark as completed
projects[a].tasks[b].status = "Completed";
// stop the timer
projects[a].tasks[b].isStarted = false;
// log end time
projects[a].tasks[b].ended = this.getCurrentTimeInTaskStartEndFormat();
for (var c = 0; c < projects[a].tasks[b].logs.length; c++) {
if (projects[a].tasks[b].logs[c].endTime == 0) {
projects[a].tasks[b].logs[c].endTime = new Date().getTime();
break;
}
}
} else if (self.value == "progress") {
// mark as in progress
projects[a].tasks[b].status = "Progress";
// stop the timer
projects[a].tasks[b].isStarted = false;
} else if (self.value == "start") {
// ask for confirmation
swal({
title: "Are you sure?",
text: "This will start the timer.",
icon: "warning",
buttons: true,
dangerMode: true,
})
.then((doStart) => {
if (doStart) {
// mark as started
projects[a].tasks[b].isStarted = true;
// add in log
var logObj = {
id: projects[a].tasks[b].logs.length,
startTime: new Date().getTime(),
endTime: 0
};
projects[a].tasks[b].logs.push(logObj);
// update local storage
localStorage.setItem(this.key, JSON.stringify(projects));
// re-load all tasks
this.showAllTasks();
} else {
// reset dropdown
self.value = "";
}
});
} else if (self.value == "stop") {
// ask for confirmation
swal({
title: "Are you sure?",
text: "This will stop the timer.",
icon: "warning",
buttons: true,
dangerMode: true,
})
.then((doStop) => {
if (doStop) {
// mark as stopped
projects[a].tasks[b].isStarted = false;
// update end time in log
for (var c = 0; c < projects[a].tasks[b].logs.length; c++) {
if (projects[a].tasks[b].logs[c].endTime == 0) {
projects[a].tasks[b].logs[c].endTime = new Date().getTime();
break;
}
}
// update local storage
localStorage.setItem(this.key, JSON.stringify(projects));
// re-load tasks
this.showAllTasks();
} else {
// reset dropdown
self.value = "";
}
});
}
break;
}
}
break;
}
}
// delete, start and stop are already handled above
if (self.value == "delete"
|| self.value == "start"
|| self.value == "stop") {
//
} else {
// update local storage and re-load tasks
localStorage.setItem(this.key, JSON.stringify(projects));
this.showAllTasks();
}
},
Comments have been added with each line for an explanation. If you still find it difficult, please feel free to mention it in the comments section below.
At this point, you will be able to start and stop the timer. Upon stopping, you will be able to see the start and end time of the task along with the total duration of the task.
Now, we need to update the timer each second if the task is started. You need to write the following lines in your window “load” event listener:
// call this function each second
setInterval(function () {
// increment 1 second in all running tasks
var dataStarted = document.querySelectorAll("td[data-started]");
for (var i = 0; i < dataStarted.length; i++) {
var dataStartedObj = dataStarted[i].getAttribute("data-started");
var dataStartedObj = JSON.parse(dataStartedObj);
dataStartedObj.duration++;
// convert timestamp into readable format
var hours = Math.floor(dataStartedObj.duration / 3600) % 24;
hours = (hours < 10) ? "0" + hours : hours;
// var days = Math.floor(diff / 86400);
var minutes = Math.floor(dataStartedObj.duration / 60) % 60;
minutes = (minutes < 10) ? "0" + minutes : minutes;
var seconds = dataStartedObj.duration % 60;
seconds = (seconds < 10) ? "0" + seconds : seconds;
dataStarted[i].innerHTML = hours + ":" + minutes + ":" + seconds;
// update log end time
var projects = taskObj.getAllProjects();
for (var a = 0; a < projects.length; a++) {
if (projects[a].id == dataStartedObj.project) {
for (var b = 0; b < projects[a].tasks.length; b++) {
if (projects[a].tasks[b].id == dataStartedObj.task) {
for (var c = 0; c < projects[a].tasks[b].logs.length; c++) {
if (c == projects[a].tasks[b].logs.length - 1) {
projects[a].tasks[b].logs[c].endTime = new Date().getTime();
// update local storage
window.localStorage.setItem(taskObj.key, JSON.stringify(projects));
// update timer
dataStarted[i].setAttribute("data-started", JSON.stringify(dataStartedObj));
break;
}
}
break;
}
}
break;
}
}
}
}, 1000);
For explanation, comments have been added. Feel free to ask if you are having trouble understanding something.
Delete project
You are now able to create projects, now is the time to have the ability to delete them. So create a simple form that displays a list of all created projects in a dropdown:
<!-- form to delete project -->
<form method="POST" onsubmit="return taskObj.deleteProject(this);" style="display: contents;">
<select name="project" class="form-control" style="display: initial; width: 200px; margin-left: 5px; margin-right: 5px;" id="form-task-hour-calculator-all-projects"></select>
<input type="submit" class="btn btn-danger" value="Delete Project">
</form>
Then we need to create a function “deleteProject” in our “taskObj” object that will delete the project from local storage and refresh the projects and tasks UI.
// delete project
deleteProject: function (self) {
// check if any project is selected
if (self.project.value == "") {
swal("Please select a project to delete");
return false;
}
// ask for confirmation
swal({
title: "Are you sure?",
text: "Deleting the project will delete its tasks too.",
icon: "warning",
buttons: true,
dangerMode: true,
})
.then((willDelete) => {
if (willDelete) {
// remove from array and update local storage
var projects = taskObj.getAllProjects();
for (var a = 0; a < projects.length; a++) {
if (projects[a].id == self.project.value) {
projects.splice(a, 1);
localStorage.setItem(taskObj.key, JSON.stringify(projects));
// re-load data
taskObj.loadAllProjects();
taskObj.showAllTasks();
break;
}
}
} else {
// reset project dropdown
self.project.value = "";
}
});
return false;
}
Applying CSS Styles
This may not be the major concern for you, but just to highlight the task when it is started and when it is completed, we are applying some CSS styles to it.
/* style when project is started */
.started {
color: white;
font-weight: bold;
background: green;
padding: 5px;
border-radius: 5px;
}
/* style when project is completed */
.completed {
color: white;
font-weight: bold;
background: greenyellow;
padding: 5px;
border-radius: 5px;
}
Congratulations ! You just created your own task management and time tracking script in Javascript. Feel free to add more features in it as per your needs.
[wpdm_package id=’1219′]