This is the client front end for the Bsale Test App, challenge of BSale made by David Montoya
Here are the back and front end repositories
This project consists of building a online store, where we can see the products, search and filter by category. The Frontend single-page application developed with vanilla JavaScript that consumes data from the API - Backend
- When open list all the products availables
- Use the Search bar on the top to search products (case-insensitive add)
- Use the filters by category section to show products by category
- Use the sort by select to list products by name or price
- On mobile version display the categories list by click in the filter icon
- Can add products to a shop cart handled by localStorage
- When click on the logo reset all the products
Start a local server as Live Server with the project folder. You can change the BASE_URI variable in the config.js file.
const URI = "https://bsale-test-api.fly.dev/";
or With the API - Backend repo
const URI = 'http://localhost:8080/';
We create our html page for our single web page application, notice we have a <main id="root"></main>
this will be the element that we going to target to create all our application
and we have to create our <script type="module" src="index.js"></script>
as well
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="styles/index.css" />
<link href='https://unpkg.com/[email protected]/css/boxicons.min.css' rel='stylesheet'>
<title>Bsale-Test</title>
</head>
<body>
<main id="root"></main>
<script type="module" src="index.js"></script>
</body>
</html>
This HOF takes a parent selector and returns an object with a load method that takes a module and a reload method that reloads the module @param parent - The parent element that the module will be loaded into
function DomBuilder(parent) {
const targetParent = document.querySelector(parent);
if (!targetParent) throw new Error("Parent not found");
return {
module: null,
load(module) {
this.module = module;
targetParent.innerHTML = module;
module.addListeners();
},
reload() {
this.load(this.module);
},
};
}
Thanks to a handler fetching custom function that we call fetchAPI
const fetchAPI = async (endpoint, { method, body } = {}) => {
const config = {
method: method || (body ? "POST" : "GET"),
};
const response = await fetch(URI + endpoint, config);
let data;
if (!response.ok) {
try {
data = await response.json();
} catch (error) {
throw new Error(response.statusText);
}
throw new Error(JSON.stringify(data));
}
try {
data = await response.json();
} catch (error) {
data = response.statusText;
}
return data;
};
We will be able to set all our services requests, to get the products and categories
async function getCategories() {
return await fetchAPI("categories");
}
async function getProducts() {
return await fetchAPI("products");
}
async function getProductsByCtgry(ctgryId) {
return await fetchAPI("categories/" + ctgryId + "/products");
}
async function getProductsByQuery(query) {
return await fetchAPI("products/" + query);
}
This is a sort of a Redux strategy, but in a Vanilla Javascript mode that will handle our state variables and store them in our Providers objects
const productsProvider = {
products: [],
status: "pending",
querySearch: "",
fecthProducts,
fetchProductsByCtgry,
fetchProductsByQuery,
};
const ctgriesProvider = {
categories: [],
currentCategory: null,
fetchCategories,
};
const renderHomePage = () => {
return `
${headerBar}
<section class="container-xl">
<div class="options-container">
<p>Results: <strong>${products.length} products found</strong></p>
........
<div class="pagination_wrapper flex">
<button class="btn btn-primary js-btn-next"><i class='bx bxs-chevron-left'></i></button>
<button class="btn btn-primary js-btn-back"><i class='bx bxs-chevron-right'></i></button>
</div>
</section>
`;
};
export const homePage = {
toString() {
return renderHomePage();
},
addListeners() {
// Here we add listeners
},
};
const runApp = async () => {
try {
await productsProvider.fecthProducts();
await ctgriesProvider.fetchCategories();
DomBuilder("#root").load(homePage);
} catch (error) {
console.log(error);
}
};
runApp();