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
View Saved Locations
Your saved locations are displayed automatically in a grid layout when you visit the page.
Add New Location
Click the “New” button to open the location creation popup.
Enter Location Details
Fill in the location name and address in the popup form.
Save Location
Click “Submit” to save the location to your account.
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 );
});
}
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">×</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.
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">×</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:
Field Type Description idnumber Unique location identifier user_namestring Display name for the location user_addressstring Full address usernamestring Owner’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.