See the live version of this project:
- π₯ User interface
- π§ Admin panel
The goal of this project is to showcase my understanding of REST API through an application that allows users to find the perfect weekend excursion. The excursion data is fetched from an API hosted on a self-configured server on AWS and displayed on the main page. Each trip includes a detailed description, as well as pricing for both adults and children. Users can place an order by selecting the number of participants (adults and children), adding excursions to the cart, and then providing their contact information β name, surname, and email address. The submitted order, along with the entered details, is sent to the API, where the administrator can view all received orders. The project also includes an admin panel that allows managing excursions β adding new ones and editing existing entries.
Main features:
- Displaying available excursions to the user by fetching data from the API (
https://16.170.252.49/excursions
) β managing the excursion list. - Allowing users to place an order by adding excursions to the cart and filling out all required, validated fields. The submitted order is then sent to the API (
https://16.170.252.49/orders
) β managing orders. - An admin panel that allows adding and editing excursions.
Β
For security reasons, the API is hosted with a self-signed HTTPS certificate. Before using the app for the first time, you need to manually visit the API link and confirm your browser's security exception:
- Open the link above in your browser
- Click βAdvancedβ β βProceed to 16.170.252.49 (unsafe)β
- Return to the app β it will now be able to fetch data from the API correctly
This only needs to be done once per browser.
Β
Β
Are you interested in RWD and CSS? See my other project Responsive Layout Showcase.
Β
The project uses npm. To install it, type into the terminal: npm install
. To run the project, use the command: npm start
.
If you want to launch the project locally for development purposes:
- Open
http://localhost:8080/index.html
for the user interface - Open
http://localhost:8080/admin.html
for the admin panel
To start the backend, you don't need to run anything locally. The API is already live and running on a server I configured myself on AWS using json-server. It is served over HTTPS with a self-signed certificate using Nginx as a reverse proxy.
API endpoints:
https://16.170.252.49/excursions
https://16.170.252.49/orders
Β
I manually set up an Ubuntu-based EC2 instance on AWS. After configuring the firewall and opening the necessary ports (3000 and 443), I installed Node.js and globally installed json-server. Then I deployed the excursions.json
file and ran the backend using PM2 to keep it alive in the background:
pm2 start json-server --name json-api -- \
--watch /home/ubuntu/excursions_app/excursions.json --port 3000
pm2 save
Β
- Fetching data from API: To load excursions, I used the Fetch API to request data from the backend and display it dynamically on the webpage. The API requests are handled in the ExcursionsAPI class, ensuring a separation of concerns and making the code more modular and easier to maintain.
fetch(this.apiExcursions)
.then(resp => resp.json())
.then(data => { renderExcursions(data); })
.catch(err => console.error(err));
Β
- Handling the cart system: A cart system was implemented to allow users to add excursions to the cart, specify the number of adults and children, and calculate the total price. The data is stored in an array (basket) and dynamically updated in the UI.
basket.push({
"id": excursion.id,
"city": excursion.city,
"priceForAdult": excursion.priceForAdult,
"priceForChild": excursion.priceForChild,
"adults": tripAdults,
"children": tripChildren
});
Β
- Admin Panel for CRUD Operations: The admin panel allows adding, editing, and deleting excursions. Data is sent and updated via POST and PUT methods, and all excursions are reloaded after any change to ensure the UI is updated.
fetch(this.apiExcursions, {
method: 'POST',
body: JSON.stringify(data),
headers: { 'Content-Type': 'application/json' }
});
Issue | Solution | Example Code |
---|---|---|
Fetching and handling API data | Using the Fetch API for asynchronous data fetching | fetch(url).then(resp => resp.json()) |
Adding and updating the cart | Dynamically updating the cart with user input | basket.push({ id, city, priceForAdult, adults, children }) |
Admin panel functionality | Providing CRUD operations with real-time UI updates | fetch(this.apiExcursions, { method: 'POST' }) |
Β
- Client-side form validation: I implemented basic client-side validation for the order form to ensure the user fills out all required fields before submitting. If validation fails, the user is alerted with the specific error.
if (required && value.length === 0) {
alert(`Field ${label} is required!`);
}
Β
- Cloning prototype HTML elements: To dynamically generate excursion list items and summary entries, I used a hidden HTML prototype element (with a --prototype class). This element is cloned, updated with specific data, and then appended to the DOM. This approach simplifies rendering, keeps the UI consistent, and avoids manually creating elements in JavaScript.
const liElCopy = liElement.cloneNode(true);
liElCopy.classList.remove('excursions__item--prototype');
tripTitle.textContent = excursion.city;
ulElement.appendChild(liElCopy);
Β
I would like to improve:
Currently, errors from the API are logged in the console, but it would be better to display user-friendly error messages directly in the UI to guide users in case of issues like server downtime.
.catch(err => alert('There was an error fetching the data. Please try again later.'));
While the current approach of managing the cart system works, it would be beneficial to look into more advanced state management techniques such as Redux or React Context to improve scalability and maintainability of the application.
Β
Write sth nice ;) Find me on:
Β
Thanks to my Mentor - devmentor.pl β for providing me with this task and for code review.