The Code

// Test Data
const events = [
{
    event: "ComicCon",
    city: "New York",
    state: "New York",
    attendance: 240000,
    date: "06/01/2017",
},
{
    event: "ComicCon",
    city: "New York",
    state: "New York",
    attendance: 250000,
    date: "06/01/2018",
},
{
    event: "ComicCon",
    city: "New York",
    state: "New York",
    attendance: 257000,
    date: "06/01/2019",
},
{
    event: "ComicCon",
    city: "San Diego",
    state: "California",
    attendance: 130000,
    date: "06/01/2017",
},
{
    event: "ComicCon",
    city: "San Diego",
    state: "California",
    attendance: 140000,
    date: "06/01/2018",
},
{
    event: "ComicCon",
    city: "San Diego",
    state: "California",
    attendance: 150000,
    date: "06/01/2019",
},
{
    event: "HeroesCon",
    city: "Charlotte",
    state: "North Carolina",
    attendance: 40000,
    date: "06/01/2017",
},
{
    event: "HeroesCon",
    city: "Charlotte",
    state: "North Carolina",
    attendance: 45000,
    date: "06/01/2018",
},
{
    event: "HeroesCon",
    city: "Charlotte",
    state: "North Carolina",
    attendance: 50000,
    date: "06/01/2019",
}];

// Entry point of application, runs when the page loads
function buildDropDown() {

    // get all the events that we know about
    let currentEvents = getEvents();

    // get a list of city names, using map and lambda expression
    let eventCities = currentEvents.map(event => event.city);

    // Class constructor, take array of event cities which has dups and store only unique values as a set
    let uniqueCities = new Set(eventCities);

    // spread operator [...], takes the set and spreads it out into another array with 'All' added at index 0
    let dropdownChoices = ['All', ...uniqueCities];

    // Get template for dropdown before looping over each item
    const dropdownTemplate = document.getElementById('dropdown-item-template');

    const dropdownMenu = document.getElementById('city-dropdown');

    // Clear out what's already in the menu before adding
    dropdownMenu.innerHTML = '';

    // for each of those city names:
    for ( let i=0; i < dropdownChoices.length; i++) {

        let cityName = dropdownChoices[i];

        // make a dropdown item HTML element
        let dropdownItem = dropdownTemplate.content.cloneNode(true);
        dropdownItem.querySelector('a').innerText = cityName;

        // add that element to the dropdown menu
        dropdownMenu.appendChild(dropdownItem);
    }

    displayEvents(currentEvents);

    displayStats(currentEvents);

    // reset table header name
    document.getElementById('stats-location').textContent = 'All';
}

// Grabs the latest events
function getEvents() {
    // get events from local storage
    let eventsJson = localStorage.getItem('rpc-events');

    // initialize stored events if someone hasn't been to the page before
    let storedEvents = events;

    // check for events have been saved before
    if (eventsJson == null) {
        saveEvents(events);
    } else {
        storedEvents = JSON.parse(eventsJson);
    }

    return storedEvents;
}

// Save event added in modal to local storage
function saveEvents(events) {

    let eventsJson = JSON.stringify(events);

    // store in the browser for undetermined amount of time, can be seen and modified by anyone
    localStorage.setItem('rpc-events', eventsJson);
}


function displayEvents(events) {

    // get the table to put the events in
    const eventTable = document.getElementById('eventsTable');

    // clear the table
    eventTable.innerHTML = '';

    // loop through events
    for (let i = 0; i < events.length ; i++) {

        let event = events[i];

        // make a <tr></tr>
        let eventRow = document.createElement('tr');

        // make a <td> for each property
        // put the data into each <td> & append to row
        let eventName = document.createElement('td');
        eventName.innerText = event.event;
        eventRow.appendChild(eventName);

        let eventCity = document.createElement('td');
        eventCity.innerText = event.city;
        eventRow.appendChild(eventCity);

        let eventState = document.createElement('td');
        eventState.innerText = event.state;
        eventRow.appendChild(eventState);

        let eventAttendance = document.createElement('td');
        eventAttendance.innerText = event.attendance.toLocaleString();
        eventRow.appendChild(eventAttendance);

        let eventDate = document.createElement('td');

        let date = new Date(event.date);

        eventDate.innerText = date.toLocaleDateString();
        eventRow.appendChild(eventDate);

        // append the row to the <tbody>
        eventTable.appendChild(eventRow);
    }
}

function calculateStats(events) {

    let sum = 0;
    let min = events[0].attendance;
    let max = 0;

    for(let i = 0; i < events.length; i++) {
        let event = events[i];

        sum += event.attendance;

        // Checks for the min attendance
        if ( event.attendance < min ) {
            min = event.attendance;
        }

        // Checks for the max attendance
        if ( event.attendance > max ) {
            max = event.attendance;
        }
    }

    let avg = sum / events.length;

    // Shorter way to type objects if the properties are the same as the variable values
    let stats = {
        sum,
        avg,
        min,
        max
    }
    return stats;
}

function displayStats(events) {

    let stats = calculateStats(events);
    // calculating and displaying the total attendance
    document.getElementById('total-attendance').innerText = stats.sum.toLocaleString();

    // calculating and displaying the avg attendance
    document.getElementById('avg-attendance').innerText = stats.avg.toLocaleString();

    // calculating and displaying and displaying the max attendance
    document.getElementById('max-attended').innerText = stats.max.toLocaleString();

    // // calculating and displaying and displaying the min attendance
    document.getElementById('min-attended').innerText = stats.min.toLocaleString();
}

function filterByCity(element) {

    // get all the events
    let cityName = element.textContent;

    // revise table header name
    document.getElementById('stats-location').textContent = cityName;

    // get all the events
    let allEvents = getEvents();

    // filter those events to just one city
    let filteredEvents = [];

    for (let i = 0; i < allEvents.length; i++) {
        let event = allEvents[i];

        if ( cityName == event.city || cityName == 'All' ) {
            filteredEvents.push(event);
        }

        // OTHER OPTION TYPE 1: Anonymous function with the filter method can be used to filter the array
        // filteredEvents = allEvents.filter(function(event) {
        //     if (event.city == cityName || cityName == 'All') {
        //         return event;
        //     }
        // })

        // OTHER OPTION TYPE 2: Lambda expression with the filter method can be used to filter the array below is the same as the for loop
        // if (cityName == 'All') {
        //     filteredEvents = allEvents;
        // } else {
        //     filteredEvents = allEvents.filter(event => event.city == cityName)
        // }

        // OTHER OPTION TYPE 3: Ternary statement
        // let filteredEvents = cityName = 'All' ? allEvents : allEvents.filter(e => e.city == cityName);
    }

    // call displayStats with the events for that city
    displayStats(filteredEvents);

    // call displayEvents with the events for that city
    displayEvents(filteredEvents);
}

function saveNewEvent() {

    // Get HTML form element
    let newEventForm = document.getElementById('newEventForm');
    let formData = new FormData(newEventForm);

    // Creates an object from the <input>s
    // value of the property is the value of the input and the property is the name
    // <input name="city" value="kernersville" />
    // let newEvent = { city: 'Kernersville}
    let newEvent = Object.fromEntries(formData.entries());

    // change text to number for attendance
    newEvent.attendance = parseInt(newEvent.attendance);

    // make sure all dates recieved are consistent in the way they're recieved
    newEvent.date = new Date(newEvent.date).toLocaleDateString();

    // Grab the list of events
    let allEvents = getEvents();

    // Add new event to list
    allEvents.push(newEvent);

    // Save updated list of events
    saveEvents(allEvents);

    // Resets the form so it goes back to how it was when page loaded
    newEventForm.reset();

    displayEvents(allEvents);

    // hide the Bootstrap Modal
    let modalElement = document.getElementById('addEventModal');
    let bsModal = bootstrap.Modal.getInstance(modalElement);
    bsModal.hide();

    // display all events
    buildDropDown();
}
JavaScript

TL;DR

Use localStorage.setItem() & localStorage.getItem() to store information in the browser. This information is only accessible on the same browser it was saved in.

Code Explanation

Bash Buddy was created with the following functions:

buildDropDown

getEvents

saveEvents

displayEvents

calculateStats

displayStats

filterByCity

saveNewEvent

What I learned

  • Use map to easily create an array of a spcific property from an array of objects
  • Use Set to provide an array that only contains unique values
  • localStorage.setItem() & localStorage.getItem() allows for storing and then getting information in the browser as a JSON for an undetermined amount of time. This stoarge can be viewed and modified by anyone who has access to the same browserthe information was stored in.

Improvements

  • Ability to delete one / all events
  • Ability to filter by Event Name / Type, Date
  • Set up initial data with an API