Building a Simple Ticket Tracker with Vanilla JavaScript
ebuka anthony
Posted on August 23, 2023
Learning and creating local storage apps in JavaScript before delving into databases offers several important benefits and insights that lay a solid foundation for understanding databases and more complex data management systems. Here's why it's crucial to start with local storage apps:
Fundamental Concepts
Local storage apps introduce you to core concepts of data storage, retrieval, and manipulation in a simplified environment. This includes understanding data structures, managing data lifecycles, and interacting with data through code.
Synchronous Operations
Local storage operations are synchronous, meaning they happen in a predictable sequence. This helps you understand the flow of data operations and how they impact the user experience.
Isolation from Network and Server Complexity
By working with local storage, you can concentrate solely on client-side code and data manipulation. This isolates you from the complexities of network communication, server setup, and database configuration, which can be overwhelming for beginners.
Data Modeling
Creating local storage apps requires you to think about data modeling – deciding what data to store, how to structure it, and how to retrieve it efficiently. This skill is directly transferable to designing database schemas later on.
Error Handling and Debugging
You'll encounter errors and bugs while working with local storage. Learning how to debug and handle these issues is a valuable skill that directly applies to working with databases.
Applicable Principles
Many principles you learn while working with local storage, such as data validation, security considerations, and handling different data types, directly carry over to database management.
In today's fast-paced world, effective task management is crucial for maintaining productivity and organization. Whether you're managing a small project or tracking customer support requests, having a system to keep track of tickets can greatly simplify your workflow. In this article, we'll guide you through the process of creating a simple ticket tracker using HTML, Bootstrap, and JavaScript.
With that said, let’s get started!
Requirements
Before we begin, make sure you have a basic understanding of HTML, Bootstrap, and JavaScript. We'll also be using Bootstrap to style our application, so having some familiarity with it would be beneficial.
Setting Up the Project
- HTML Structure: Start by creating a new folder for your project and inside it, create an index.html file. Set up the basic HTML structure and include necessary libraries:
We’ll start by creating the HTML markup structure containing the DOM content of the ticket tracker. To keep things simple, we will need just a few HTML elements:
- Link tag for css bootstrap integration for styling ,
- A body onload tag to enable ticket issues to remain in DOM after each page reload
- A section for ticket form data input divs
the following code below outlines the first two points;
At the bottom of the code, you link the javascript , bootstrap styling , and chanceJS for ticket ID Issuance.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<title>Document</title>
</head>
<body onload="fetchIssues()"> <!--This will be populated by JS-->
<script src="main.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
<script src="http://chancejs.com/chance.min.js"></script>
</body>
</html>
Note : You can also use vanilla CSS to style your layouts, we are using bootstrap to ease time constraints in styling out.
Creating the Ticket Tracker Interface
- Form for Ticket Entry: Within the of your index.html file, create a form to input new tickets:
<section class="container">
<h1>Ticktrak</h1>
<section class="jumbotron">
<h3>Add New Tracking Issue:</h3>
<form id="issueInputForm">
<div class="form-group">
<label for="issueSubjInput">Subject</label>
<input type="text" class="form-control" id="issueSubjInput" placeholder="Issue Subject...">
</div>
<div class="form-group">
<label for="issueDescInput">Description</label>
<input type="text" class="form-control" id="issueDescInput" placeholder="Issue Description...">
</div>
<div class="form-group">
<label for="issueSeverityInput">Severity</label>
<select class="form-control" id="issueSeverityInput">
<option value="Low">Low</option>
<option value="Medium">Medium</option>
<option value="High" selected>High</option>
</select>
</div>
<div class="form-group">
<label for="issueAssignedToInput">Assigned To</label>
<input type="text" class="form-control" id="issueAssignedToInput" placeholder="Enter Person Responsible...">
</div>
<button type="submit" class="btn btn-warning">Add</button>
</form>
</section>
<section class="row">
<div class="col-lg-12">
<div id="issuesList">
<!-- empty div to be occupied by JS when event listener is triggered—>
</div>
</div>
Implementing JavaScript Logic:
Our ticket tracker isn’t working right now because we have yet to add some interactivity using JavaScript, which is exactly what we are going to do next.
We will have to add some event listeners to the input button for form submission and also storage of form data in local storage, and also automating listing of open issues in the DOM whenever they are clicked on.
To achieve this, we will tackle the JavaScript code needed to get our form input working section by section so that it’s easy to understand and follow along.
JavaScript for Ticket Handling: Create a main.js file in your project folder. This is where we'll handle the ticket creation and display logic.
-
Then we will write a function called fetchIssues() to facilitate display of stored form in local storage in the DOM. Under it, We create two variables respectively;
- The first variable
issues
; for grabbing data stored as an Object stored in local storage, then parse it from a string into DOM data- The Second Variable
issuesList
; for grabbing data from issues variable and display in the empty html div in the DOM
- The Second Variable
We log the console for emergency debugging and confirming the parsed data contents that was displayed;
- The first variable
function fetchIssues() {
let issues = JSON.parse(localStorage.getItem('issues'))
let issuesList = document.getElementById('issuesList')
console.log(issues)
}
- We then add a method under the variables for enabling displaying the data in html format
function fetchIssues() {
let issues = JSON.parse(localStorage.getItem('issues'))
let issuesList = document.getElementById('issuesList')
console.log(issues)
issuesList.innerHTML = '';
}
- We will run a for loop to get all available input data in each ticket stored as objects in local storage
function fetchIssues() {
let issues = JSON.parse(localStorage.getItem('issues'))
let issuesList = document.getElementById('issuesList')
console.log(issues)
issuesList.innerHTML = '';
for (let i = 0; i < issues.length; i++) { // run loop to get all available data in each ticket stored as objects in local storage
let id = issues[i].id
let subject = issues[i].subject
let description = issues[i].description
let severity = issues[i].severity
let assignedTo = issues[i].assignedTo
let status = issues[i].status
let statusColor = status == "Closed" ? 'label-success' : 'label-info'
}
}
- We then style how our (now empty) form data of issues in html will appear in the DOM
function fetchIssues() {
let issues = JSON.parse(localStorage.getItem('issues'))
let issuesList = document.getElementById('issuesList')
console.log(issues)
issuesList.innerHTML = '';
for (let i = 0; i < issues.length; i++) { // run loop to get all available data in each ticket stored as objects in local storage
let id = issues[i].id
let subject = issues[i].subject
let description = issues[i].description
let severity = issues[i].severity
let assignedTo = issues[i].assignedTo
let status = issues[i].status
let statusColor = status == "Closed" ? 'label-success' : 'label-info'
issuesList.innerHTML += //style your empty form data of issues in html
'<div class="well">' +
'<h6>Issue ID:' + id + '</h6>' +
'<p><span class= "label ' + statusColor + ' ">' + status + '</span></p>' +
'<h3>' + subject + '</h3>' +
'<p>' + description + '</p>' +
'<p><span class="glyphicon glyphicon-time"></span> ' + severity + ' ' + '<span class="glyphicon glyphicon-user"></span>' + assignedTo + '</p>' +
'<a href="#" class="btn btn-warning" onclick="setStatusClosed(\''+id+'\')">Close</a> ' + //pass ticket id to be changed to closed from id through onclick function in button
'<a href="#" class="btn btn-danger" onclick="deleteIssue(\''+id+'\')">Delete</a> ' //pass ticket id to be deleted through onclick function in button
+ '</div>'
}
}
-
We then write another function saveIssues to be triggered by event listener to capture all data from the form of issues and save to local storage’
The first variable issueID contains a function method chance.guid() which is an open source random ID number generator to assign form id with form data to variables to be stored in an object which is then to be stored in local storage
Then we create an object storing form data value with key-value pair of the variables created above the object
function saveIssue(e) {
let issueId = chance.guid()
let issueSubject = document.getElementById('issueSubjInput').value
let issueDesc = document.getElementById('issueDescInput').value
let issueSeverity = document.getElementById('issueSeverityInput').value
let issueAssignedTo = document.getElementById('issueAssignedToInput').value
let issueStatus = 'Open'
let issue = { // object storing form data value
id: issueId,
subject: issueSubject,
description: issueDesc,
severity: issueSeverity,
assignedTo: issueAssignedTo,
status: issueStatus
}
}
- then we have to create our good old conditional statements to first create an empty array to contain the newly created object, then pushing new input data to empty array, then set the value identified by key to value pair, convert array elements to string & save the data in local storage.
OR if data already exists grab the existing issues, parse them & push them to an array, convert back to string & store in local storage
function saveIssue(e) { // function to be triggered by event listener to capture all data from the form of issues and save to local storage
let issueId = chance.guid() //assign form id with form data to variables to be stored in an object which is then to be stored in local storage
let issueSubject = document.getElementById('issueSubjInput').value
let issueDesc = document.getElementById('issueDescInput').value
let issueSeverity = document.getElementById('issueSeverityInput').value
let issueAssignedTo = document.getElementById('issueAssignedToInput').value
let issueStatus = 'Open'
let issue = { // object storing form data value
id: issueId,
subject: issueSubject,
description: issueDesc,
severity: issueSeverity,
assignedTo: issueAssignedTo,
status: issueStatus
}
if(localStorage.getItem('issues')===null) { //if there is no data served up from local storage when user opens for first time
let issues = []// set up an array in this case
issues.push(issue) //push new data to empty array
localStorage.setItem('issues', JSON.stringify(issues)) // set the value identified by key to value pair, convert array elements to string & save the data
} else {
let issues = JSON.parse(localStorage.getItem('issues')) //if it exists grab existing issues, parse them & convert to array
issues.push(issue) //push to array
localStorage.setItem('issues', JSON.stringify(issues)) //convert back to string & store in local storage
}
}
- Under the Conditionals add a document read method to reset form after data is inputed, the adding to local storage to be saved
function saveIssue(e) { // function to be triggered by event listener to capture all data from the form of issues and save to local storage
let issueId = chance.guid() //assign form id with form data to variables to be stored in an object which is then to be stored in local storage
let issueSubject = document.getElementById('issueSubjInput').value
let issueDesc = document.getElementById('issueDescInput').value
let issueSeverity = document.getElementById('issueSeverityInput').value
let issueAssignedTo = document.getElementById('issueAssignedToInput').value
let issueStatus = 'Open'
let issue = { // object storing form data value
id: issueId,
subject: issueSubject,
description: issueDesc,
severity: issueSeverity,
assignedTo: issueAssignedTo,
status: issueStatus
}
if(localStorage.getItem('issues')===null) { //if there is no data served up from local storage when user opens for first time
let issues = []// set up an array in this case
issues.push(issue) //push new data to empty array
localStorage.setItem('issues', JSON.stringify(issues)) // set the value identified by key to value pair, convert array elements to string & save the data
} else {
let issues = JSON.parse(localStorage.getItem('issues')) //if it exists grab existing issues, parse them & convert to array
issues.push(issue) //push to array
localStorage.setItem('issues', JSON.stringify(issues)) //convert back to string & store in local storage
}
document.getElementById('issueInputForm').reset(); //reset form after input data & adding to local storage to be saved
fetchIssues() //display, style new form issues
e.preventDefault() //for the event listener to trigger the saveIssues function only and nothing else
}
- We will then write another function setStatusClosed to grab outform issue , display issue in html in the DOM, then change status & resave in local storage, we will loop through the issues ,then we write a conditional to flip the status if issue id matches id to be passed in, then we “stringify” the issue to convert issue with flipped status back to string & store in local storage
function saveIssue(e) { // function to be triggered by event listener to capture all data from the form of issues and save to local storage
let issueId = chance.guid() //assign form id with form data to variables to be stored in an object which is then to be stored in local storage
let issueSubject = document.getElementById('issueSubjInput').value
let issueDesc = document.getElementById('issueDescInput').value
let issueSeverity = document.getElementById('issueSeverityInput').value
let issueAssignedTo = document.getElementById('issueAssignedToInput').value
let issueStatus = 'Open'
let issue = { // object storing form data value
id: issueId,
subject: issueSubject,
description: issueDesc,
severity: issueSeverity,
assignedTo: issueAssignedTo,
status: issueStatus
}
if(localStorage.getItem('issues')===null) { //if there is no data served up from local storage when user opens for first time
let issues = []// set up an array in this case
issues.push(issue) //push new data to empty array
localStorage.setItem('issues', JSON.stringify(issues)) // set the value identified by key to value pair, convert array elements to string & save the data
} else {
let issues = JSON.parse(localStorage.getItem('issues')) //if it exists grab existing issues, parse them & convert to array
issues.push(issue) //push to array
localStorage.setItem('issues', JSON.stringify(issues)) //convert back to string & store in local storage
}
document.getElementById('issueInputForm').reset(); //reset form after input data & adding to local storage to be saved
fetchIssues() //display, style new form issues
e.preventDefault() //for the event listener to trigger the saveIssues function only and nothing else
function setStatusClosed(id) { //to grab out issue , display issue, change status & resave in local storage
let issues = JSON.parse(localStorage.getItem('issues'))
for(let i=0; i < issues.length; i++) { //loop through issues
if(issues[i].id === id) { //if issue id matches id to be passed in, grab it out to dom, flip status
issues[i].status = "Closed"
}
}
localStorage.setItem('issues', JSON.stringify(issues)) //convert issue with flipped status back to string & store in local storage,
fetchIssues()
}
}
- We then write a function deleteIssue to pull issue out, attend to the form issue, get the data we want, delete & save back in local storage
function saveIssue(e) { // function to be triggered by event listener to capture all data from the form of issues and save to local storage
let issueId = chance.guid() //assign form id with form data to variables to be stored in an object which is then to be stored in local storage
let issueSubject = document.getElementById('issueSubjInput').value
let issueDesc = document.getElementById('issueDescInput').value
let issueSeverity = document.getElementById('issueSeverityInput').value
let issueAssignedTo = document.getElementById('issueAssignedToInput').value
let issueStatus = 'Open'
let issue = { // object storing form data value
id: issueId,
subject: issueSubject,
description: issueDesc,
severity: issueSeverity,
assignedTo: issueAssignedTo,
status: issueStatus
}
if(localStorage.getItem('issues')===null) { //if there is no data served up from local storage when user opens for first time
let issues = []// set up an array in this case
issues.push(issue) //push new data to empty array
localStorage.setItem('issues', JSON.stringify(issues)) // set the value identified by key to value pair, convert array elements to string & save the data
} else {
let issues = JSON.parse(localStorage.getItem('issues')) //if it exists grab existing issues, parse them & convert to array
issues.push(issue) //push to array
localStorage.setItem('issues', JSON.stringify(issues)) //convert back to string & store in local storage
}
document.getElementById('issueInputForm').reset(); //reset form after input data & adding to local storage to be saved
fetchIssues() //display, style new form issues
e.preventDefault() //for the event listener to trigger the saveIssues function only and nothing else
function setStatusClosed(id) { //to grab out issue , display issue, change status & resave in local storage
let issues = JSON.parse(localStorage.getItem('issues'))
for(let i=0; i < issues.length; i++) { //loop through issues
if(issues[i].id === id) { //if issue id matches id to be passed in, grab it out to dom, flip status
issues[i].status = "Closed"
}
}
localStorage.setItem('issues', JSON.stringify(issues)) //convert issue with flipped status back to string & store in local storage,
fetchIssues()
}
function deleteIssue (id) { //pull issue out cut out what we want to delete & save back
let issues = JSON.parse(localStorage.getItem('issues'))
for(let i=0; i < issues.length; i++) {
if(issues[i].id === id) {
issues.splice(i,1) //splicing at current indexin the array , cutting it in half, extract element & rejoin
}
}
localStorage.setItem('issues', JSON.stringify(issues))
fetchIssues()
}
Testing the Ticket Tracker
- Testing: Open your index.html file in a web browser. You should see the ticket entry form and an empty list below it. Fill out the form and click the "Submit Ticket" button. Your entered ticket should appear in the list.
- Congratulations! You've successfully created a simple ticket tracker using HTML, Bootstrap, and JavaScript. This basic implementation can serve as a starting point for further enhancements such as adding features like ticket status, due dates, or even connecting the tracker to a backend for data persistence.
You can find the full source code associated with this tutorial on Github.
Remember that this is just a simple example, and real-world applications might require additional validation, security measures, and more advanced functionality. Happy coding!
Posted on August 23, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.