←
JavaScript / Firebase

Skills Tracker: Building an Application to Map IT Competencies

A step-by-step guide to building a lightweight SPA to visually track technical learning progress, integrated with a real-time Firebase database.


The Skills Tracker was born out of a very practical need: to take stock of one’s professional growth by clearly mapping and visualizing current skill levels across various technologies.

In this guide, we explore how I built a straightforward Single Page Application (SPA) to manage and update tech competencies in real time.


Project Architecture

To keep the application responsive and clean, the architecture relies on minimal core building blocks:

  • Frontend: A single HTML file paired with JavaScript for UI interactions and dynamic DOM manipulation.
  • UI Framework: Materialize CSS for the visual layout (sliders, grid system, and toast notifications) alongside Google Material Icons.
  • Database: Firebase Realtime Database for data persistence handled via RESTful HTTP calls.

Step 1: HTML Structure

The interface uses a clean, essential markup skeleton. The <head> imports Materialize CSS, icon stylesheets, and jQuery for quick DOM traversal and AJAX execution.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  
  <!-- Google Material Icons & Materialize CSS -->
  <link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">
  
  <!-- Scripts -->
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
  <title>My Skills Tracker</title>
</head>
<body>
  <header class="container">
    <h1 class="app-title">My Skills</h1>
  </header>

  <!-- Container for dynamically rendered skill sliders -->
  <div id="mainContainer" class="container"></div>

  <div class="container">
    <div class="row">
      <button type="button" class="waves-effect waves-light btn" onclick="postLivelli()" style="float:right">
        <i class="material-icons left">save</i>Update Levels
      </button>
    </div>
  </div>
</body>
</html>

Step 2: Fetching Skills (GET Request)

When the app initializes, a JavaScript function triggers an HTTP GET request to the Firebase Realtime Database REST API. The returned JSON array is iterated over to dynamically build range slider inputs (ranging from 0 to 100).

function getLivelli() {
  $.ajax({
    type: "GET",
    url: "https://<YOUR-FIREBASE-APP>.firebaseio.com/skills.json",
    dataType: "json",
    success: function (result) {
      $('#mainContainer').html('');
      for (var comp in result) {
        $('#mainContainer').append(
          '<div>' +
            '<p class="range-field">' +
              '<label for="' + result[comp].nome + '" class="form-label">' + result[comp].label + '</label>' +
              '<input type="range" class="form-range" id="' + result[comp].nome + '" max="100" min="0" value="' + result[comp].livello + '">' +
            '</p>' +
          '</div>'
        );
      }
    },
    error: function (xhr, status, error) {
      alert("Error fetching skills: " + status + " - " + error);
    }
  });
}

Step 3: Updating Skills (PUT Request)

When sliders are adjusted and the “Update Levels” button is clicked, the app gathers the updated values directly from the DOM inputs and sends a JSON payload via an HTTP PUT request to Firebase.

function postLivelli() {
  var params = [];

  // Gather current slider values from the DOM
  $("p.range-field").each(function () {
    params.push({
      "nome": $(this).find('input').attr('id'),
      "livello": $(this).find('input').val(),
      "label": $(this).find('label').text()
    });
  });

  // Send HTTP PUT request to overwrite state in Firebase
  $.ajax({
    type: "PUT",
    url: "https://<YOUR-FIREBASE-APP>.firebaseio.com/skills.json",
    dataType: "json",
    data: JSON.stringify(params),
    success: function () {
      // Show Materialize Toast notification
      M.toast({ html: 'Changes saved successfully!' });
      getLivelli();
    },
    error: function (xhr, status, error) {
      alert("Error saving skills: " + status + " - " + error);
    }
  });
}

Deployment Process

The app is hosted on Netlify. The deployment workflow is straightforward:

  1. Push the source code to a GitHub repository.
  2. Connect the repository to Netlify and select the primary branch (main).
  3. Automated continuous deployment on every git push.

Roadmap & Future Enhancements

While this prototype serves as a solid MVP, future refactoring iterations will include:

  • Frontend Migration: Rewriting the UI layer using a modern framework like Vue.js or Angular.
  • Full CRUD Functionality: Adding native UI options to create and delete custom skills.
  • Dedicated Backend: Building a lightweight Java Spring Boot microservice to eliminate direct database exposure.
  • Authentication: Integrating Firebase Authentication to secure update endpoints.

Small, focused side projects like this are ideal for testing real-time cloud database integrations. Feel free to reach out if you have feedback or want to chat about web architecture!