Skip to main content

Favorite Locations

The Favorite Locations feature allows users to save, edit, and manage their frequently visited places for quick access and route planning.

Overview

This feature provides a complete CRUD (Create, Read, Update, Delete) interface for managing saved locations, with user-specific filtering and an intuitive grid-based UI.

How to Use

1

View Saved Locations

Your saved locations are displayed automatically in a grid layout when you visit the page.
2

Add New Location

Click the “New” button to open the location creation popup.
3

Enter Location Details

Fill in the location name and address in the popup form.
4

Save Location

Click “Submit” to save the location to your account.
5

Manage Existing Locations

Click on any saved location to view details, edit, or delete it.

API Integration

The feature uses RESTful API endpoints for all operations:

Base Configuration

import { pythonURI, javaURI, fetchOptions, login } from '../../assets/js/api/config.js';

Read Locations (GET)

async function readScores() {
  try {
    const scoresResponse = await fetch(`${pythonURI}/api/saved_locations`, fetchOptions);
    if (!scoresResponse.ok) throw new Error('Failed to fetch locations');
    const scores = await scoresResponse.json();
    return (scores);
  } catch (error) {
    console.error('Error fetching locations:', error);
    alert('Error fetching locations: ' + error.message);
    return null;
  }
}

Create Location (POST)

async function createScores(inputName, inputAddress) {
  const locationData = {
    address: inputAddress,
    name: inputName
  };

  try {
    const response = await fetch(`${pythonURI}/api/saved_locations`, {
      ...fetchOptions,
      method: 'POST',
      body: JSON.stringify(locationData),
    });

    if (!response.ok) {
      throw new Error(`Failed to submit location: ${response.statusText}`);
    }

    const result = await response.json();
    return (result);
  } catch (error) {
    console.error('Error submitting location:', error);
    alert('Error submitting location: ' + error.message);
    return null;
  }
}

Update Location (PUT)

async function updateScores(inputId, inputAddress, inputName) {
  const scoreData = {
    id: inputId,
    address: inputAddress,
    name: inputName
  }

  try {
    const response = await fetch(`${pythonURI}/api/saved_locations`, {
      ...fetchOptions,
      method: 'PUT',
      body: JSON.stringify(scoreData),
    });

    if (!response.ok) {
      throw new Error(`Failed to update location: ${response.statusText}`);
    }

    const result = await response.json();
    return (result);
  } catch (error) {
    console.error('Error updating location:', error);
    alert('Error updating location: ' + error.message);
  }
}

Delete Location (DELETE)

async function deleteScores(inputId) {
  const scoreData = {
    id: inputId
  }

  try {
    const response = await fetch(`${pythonURI}/api/saved_locations`, {
      ...fetchOptions,
      method: 'DELETE',
      body: JSON.stringify(scoreData),
    });

    if (!response.ok) {
      throw new Error(`Failed to delete location: ${response.statusText}`);
    }

    const result = await response.json();
    return (result);
  } catch (error) {
    console.error('Error deleting location:', error);
    alert('Error deleting location: ' + error.message);
    return null;
  }
}

User Filtering

Locations are automatically filtered to show only those belonging to the current user:
async function filterForUsername(scores) {
  const currentUserResponse = await fetch(`${pythonURI}/api/user`, fetchOptions);
  if (!currentUserResponse.ok) throw new Error('Failed to fetch current user');
  const currentUser = await currentUserResponse.json();
  let userName = currentUser.name;

  return (scores.filter((entry) => String(entry.username) === String(userName)));
}
The system automatically fetches the current user’s information and filters locations accordingly, ensuring users only see their own saved places.

UI Components

Grid Layout

Locations displayed in an organized grid for easy browsing

Modal Popups

Clean popup interfaces for adding and editing locations

Inline Editing

Edit location details directly in the popup

Quick Actions

Edit and delete buttons for easy management

Grid Population

async function populateScores() {
  const grid = document.getElementById('locations-grid');
  grid.innerHTML = ''; // Clear existing content

  let scores = await filterForUsername(await readScores());

  scores.forEach(location => {
    const square = document.createElement('button');
    square.className = 'location-square';
    square.textContent = location.user_name;
    square.addEventListener('click', () => showPopup(location));
    grid.appendChild(square);
  });
}

New Location Popup

Adding a new location opens an interactive popup:
function showNewLocationPopup() {
  const popup = document.createElement('div');
  popup.className = 'popup';

  popup.innerHTML = `
    <div class="popup-content">
      <button class="popup-close">&times;</button>
      <h2>Add New Location</h2>
      <input type="text" id="new-location-name" placeholder="Enter name" />
      <input type="text" id="new-location-address" placeholder="Enter address" />
      <div>
        <button class="popup-cancel">Cancel</button>
        <button class="popup-submit">Submit</button>
      </div>
    </div>
  `;

  popup.querySelector('.popup-submit').addEventListener('click', async () => {
    const name = document.getElementById('new-location-name').value.trim();
    const address = document.getElementById('new-location-address').value.trim();

    if (name && address) {
      await createScores(name, address);
      document.body.removeChild(popup);
      populateScores(); // Refresh the grid
    } else {
      alert('Please fill in both fields.');
    }
  });

  document.body.appendChild(popup);
}
Input validation ensures both name and address fields are filled before allowing submission.

Location Details Popup

Clicking a location opens a detailed view:
function showPopup(location) {
  const popup = document.createElement('div');
  popup.className = 'popup';

  popup.innerHTML = `
    <div class="popup-content">
      <button class="popup-close">&times;</button>
      <p><strong>Name:</strong> <span id="location-name">${location.user_name}</span></p>
      <p><strong>Address:</strong> <span id="location-address">${location.user_address}</span></p>
      <div>
        <button class="popup-delete">Delete</button>
        <button class="popup-edit">Edit</button>
      </div>
    </div>
  `;

  document.body.appendChild(popup);
}

Editing Locations

The edit functionality provides inline editing:
const editButton = popup.querySelector('.popup-edit');
editButton.addEventListener('click', () => {
  const nameSpan = document.getElementById('location-name');
  const addressSpan = document.getElementById('location-address');

  if (editButton.textContent === 'Edit') {
    // Change to editable fields
    nameSpan.innerHTML = `<input type="text" id="edit-location-name" value="${location.user_name}" />`;
    addressSpan.innerHTML = `<input type="text" id="edit-location-address" value="${location.user_address}" />`;
    editButton.textContent = 'Submit';
  } else {
    // Submit the updated data
    const updatedName = document.getElementById('edit-location-name').value.trim();
    const updatedAddress = document.getElementById('edit-location-address').value.trim();

    if (updatedName && updatedAddress) {
      updateScores(location.id, updatedAddress, updatedName).then(() => {
        document.body.removeChild(popup);
        populateScores();
      }).catch(error => {
        alert('Error updating location: ' + error.message);
      });
    } else {
      alert('Please fill in both fields.');
    }
  }
});
The Edit button dynamically switches between “Edit” and “Submit” modes:
  • Edit mode: Displays input fields with current values
  • Submit mode: Validates and sends update request to API

Deleting Locations

popup.querySelector('.popup-delete').addEventListener('click', async () => {
  await deleteScores(location.id);
  document.body.removeChild(popup);
  populateScores(); // Refresh the grid
});

Data Structure

Location objects contain:
FieldTypeDescription
idnumberUnique location identifier
user_namestringDisplay name for the location
user_addressstringFull address
usernamestringOwner’s username

Automatic Initialization

Locations are automatically loaded when the page loads:
document.addEventListener('DOMContentLoaded', populateScores);

HTML Structure

<div id="locations-grid"></div>
<button id="new-location-button" class="new-location-button">New</button>
All operations require valid user authentication. Ensure you’re logged in before managing locations.

Build docs developers (and LLMs) love