Skip to content

Commit

Permalink
Merge pull request #85 from akhilome/gh-pages
Browse files Browse the repository at this point in the history
Consume API endpoints from frontend
  • Loading branch information
akhilome committed Oct 18, 2018
2 parents efd7fce + f9e4d3f commit 39ce405
Show file tree
Hide file tree
Showing 25 changed files with 662 additions and 180 deletions.
10 changes: 10 additions & 0 deletions ui/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module.exports = {
'extends': 'airbnb-base',
'env': {
'browser': true,
},
'rules': {
'no-undef': 'off',
'no-console': 'off'
}
};
4 changes: 4 additions & 0 deletions ui/add-food.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<script src="assets/js/jwt-decode.min.js"></script>
<script src="assets/js/auth.js"></script>
<script src="assets/js/authAdmin.js"></script>
<link href="https://fonts.googleapis.com/css?family=Comfortaa|Open+Sans" rel="stylesheet">
<link rel="stylesheet" href="assets/main.css">
<title>Add Food To Menu</title>
Expand Down Expand Up @@ -45,5 +48,6 @@ <h2>Add New Food Item</h2>
</footer>

<script src="assets/main.js"></script>
<script src="assets/js/addFood.js"></script>
</body>
</html>
3 changes: 3 additions & 0 deletions ui/admin-menu.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<script src="assets/js/jwt-decode.min.js"></script>
<script src="assets/js/auth.js"></script>
<script src="assets/js/authAdmin.js"></script>
<link href="https://fonts.googleapis.com/css?family=Comfortaa|Open+Sans" rel="stylesheet">
<link rel="stylesheet" href="assets/main.css">
<title>Food Menu</title>
Expand Down
32 changes: 32 additions & 0 deletions ui/assets/js/addFood.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
const foodForm = document.querySelector('form');

function addNewFood(e) {
e.preventDefault();
flashMessage('Adding food to menu ...');
const formData = {
foodName: foodForm['food-item'].value,
foodImage: foodForm['food-image'].value,
price: foodForm['food-price'].value,
};

fetch('https://kiakiafood.herokuapp.com/api/v1/menu', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-auth': token,
},
body: JSON.stringify(formData),
}).then(data => data.json())
.then((response) => {
if (response.status !== 'success') return flashMessage(response.message, 'error');

window.location = 'admin-menu.html';
return flashMessage('Food added!', 'success');
})
.catch((error) => {
console.error(error);
flashMessage('Something went wrong while new food item', 'error');
});
}

foodForm.addEventListener('submit', addNewFood);
9 changes: 9 additions & 0 deletions ui/assets/js/admin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Admins should always default to the admin-menu page
try {
const { userStatus } = (jwt_decode(localStorage.getItem('kiakiafoodToken')));
if (userStatus === 'admin') {
window.location = 'admin-menu.html';
}
} catch (error) {
console.error(error);
}
67 changes: 67 additions & 0 deletions ui/assets/js/allOrders.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
function adminOrderCardBluePrint(orderId, customerName, foodItems = [], date, status) {
const formattedNames = foodItems
.map(foodName => `<p>${foodName}</p>`).join('');

let actionButtons;

switch (status) {
case 'processing':
actionButtons = '<button class="small done">mark done</button>';
break;
case 'cancelled':
actionButtons = '<button class="small disabled">handled</button>';
break;
case 'complete':
actionButtons = '<button class="small disabled">handled</button>';
break;
default:
actionButtons = '<button class="small accept">accept</button><button class="small reject">reject</button>';
}

return `
<div data-orderid="${orderId}" class="order-card">
<div class="order-card_food-items">
<h3>Food Item(s)</h3>
<div class="order-card_food-items_names">${formattedNames}</div>
</div>
<div class="order-card_date">
<p><strong>date:</strong></p>
<p>${date.split('T')[0].split('-').reverse().join('/')}</p>
</div>
<div class="order-card_price">
<p><strong>customer:</strong></p>
<p>${customerName}</p>
</div>
<div class="order-card_admin-actions">${actionButtons}</div>
</div>
`;
}

const url = 'https://kiakiafood.herokuapp.com/api/v1/orders';

fetch(url, {
headers: { 'x-auth': token },
}).then(data => data.json())
.then((response) => {
const { orders } = response;
if (!orders.length) {
document.querySelector('.admin-orders').innerHTML = '<p>No orders have been made yet!</p>';
} else {
const allOrders = orders
.map(order => adminOrderCardBluePrint(
order.id,
order.author,
order.items,
order.date,
order.status,
))
.join('');

document.querySelector('.admin-orders').innerHTML = allOrders;
}
})
.catch((error) => {
console.error(error);
flashMessage('An error occured while fetching the orders', 'error');
document.querySelector('.admin-orders').innerHTML = '<div></div>';
});
10 changes: 10 additions & 0 deletions ui/assets/js/auth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const token = localStorage.getItem('kiakiafoodToken');
if (!token) {
window.location = 'sign-in.html';
}

try {
jwt_decode(token);
} catch (error) {
window.location = 'sign-in.html';
}
5 changes: 5 additions & 0 deletions ui/assets/js/authAdmin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const decoded = jwt_decode(token);

if (decoded.userStatus !== 'admin') {
window.location = 'index.html';
}
37 changes: 24 additions & 13 deletions ui/assets/js/cart.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,36 @@
// Get all buttons
const buyButtons = document.querySelectorAll('.food-details__action > button');
function addToCart(e) {
if (!e.target.matches('button')) return; // event delegation
const button = e.target;

function addToCart() {
if(!localStorage.getItem('cart')) localStorage.setItem('cart', '{}');
const cart = JSON.parse(localStorage.getItem('cart'));
const foodDetails = this.parentNode.previousElementSibling;
// Prevent unauthenticated customer from adding to cart
try {
const token = localStorage.getItem('kiakiafoodToken');
jwt_decode(token);
if (!token) throw new Error();
} catch (error) {
window.location = 'sign-in.html';
return;
}


const cart = JSON.parse(localStorage.getItem('cart')) || {};

const foodDetails = button.parentNode.previousElementSibling;
const foodImage = foodDetails.parentNode.previousElementSibling.src;
const foodId = foodDetails.dataset.foodid
const foodId = foodDetails.dataset.foodid;
const foodName = foodDetails.querySelector('h4').innerText;
const foodPrice = foodDetails.querySelector('p').innerText;
if(!cart.hasOwnProperty(foodId)) {
cart[foodId] = { foodName, foodPrice, foodImage }

if (!Object.keys(cart).includes(foodId)) {
cart[foodId] = { foodName, foodPrice, foodImage };
} else {
return flashMessage('Item already in cart', 'error');
flashMessage('Item already in cart', 'error');
return;
}

localStorage.setItem('cart', JSON.stringify(cart));
flashMessage('Item added to cart', 'success');
}


// Event Listeners
buyButtons.forEach(button => button.addEventListener('click', addToCart));
document.querySelector('section.food-menu').addEventListener('click', addToCart);
80 changes: 70 additions & 10 deletions ui/assets/js/checkout.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,37 @@ function checkoutCardBlueprint(id, foodName, foodPrice) {
`;
}

// compute order price
const orderPrice = () => {
const cartItems = JSON.parse(localStorage.getItem('cart'));
// if (!Object.keys(cartItems).length) return;

return Object.keys(cartItems)
.map(foodId => cartItems[foodId].foodPrice)
.map(price => Number(price.split(',').join('').substr(1)))
.reduce((accum, current) => accum + current)
.toLocaleString();
};

// Populate Cart Items on Page Load
(() => {
const cartItems = JSON.parse(localStorage.getItem('cart'));
if(!Object.keys(cartItems).length) {
const cartItems = JSON.parse(localStorage.getItem('cart')) || {};
if (!Object.keys(cartItems).length) {
document.querySelector('#checkout').remove();
return document.querySelector('section.checkout').innerHTML = '<div>No items in cart!</div>';
document.querySelector('.order-price').remove();
document.querySelector('section.checkout').innerHTML = '<div>No items in cart!</div>';
return;
}

const formatted = [];
for (const food in cartItems) {
formatted.push(checkoutCardBlueprint(food, cartItems[food].foodName, cartItems[food].foodPrice));
}
const formatted = Object.keys(cartItems)
.map(food => checkoutCardBlueprint(food, cartItems[food].foodName, cartItems[food].foodPrice));

document.querySelector('section.checkout').innerHTML = formatted.join('');
document.querySelector('#order-price').textContent = orderPrice();
})();

//
const removeButtons = document.querySelectorAll('.food-card-checkout > button')
// Removing items from cart
const removeButtons = document.querySelectorAll('.food-card-checkout > button');

function removeFromCart() {
const cart = JSON.parse(localStorage.getItem('cart'));
Expand All @@ -35,7 +49,53 @@ function removeFromCart() {
delete cart[foodId];

localStorage.setItem('cart', JSON.stringify(cart));

if (!Object.keys(cart).length) {
document.querySelector('.order-price').remove();
document.querySelector('#checkout').remove();
document.querySelector('section.checkout').innerHTML = '<div>No items in cart!</div>';
return;
}

foodCard.remove();
document.querySelector('#order-price').textContent = orderPrice();
}

removeButtons.forEach(button => button.addEventListener('click', removeFromCart));

// Initiate actual checkout
const wrapper = document.querySelector('.wrapper');

function buyFoodItems(e) {
if (!e.target.matches('#checkout')) return;

const foodIds = Object.keys(JSON.parse(localStorage.getItem('cart'))).map(Number);
flashMessage('placing your order ...');

const url = 'https://kiakiafood.herokuapp.com/api/v1/orders';
const token = localStorage.getItem('kiakiafoodToken');

fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-auth': token,
},
body: JSON.stringify({ foodIds }),
})
.then(data => data.json())
.then((response) => {
if (response.status !== 'success') return flashMessage(response.message, 'error');
localStorage.removeItem('cart');
setTimeout(() => {
window.location = 'order-history.html';
}, 2000);
return flashMessage('Order placed!', 'success');
})
.catch((error) => {
console.error(error);
flashMessage('Something went wrong while placing your order', 'error');
});
}

removeButtons.forEach(button => button.addEventListener('click', removeFromCart));
wrapper.addEventListener('click', buyFoodItems);
66 changes: 66 additions & 0 deletions ui/assets/js/customerOrderHistory.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
function orderCardBluePrint(foodNames = [], orderPrice, date, status) {
const formattedNames = foodNames
.map(foodName => `<p>${foodName}</p>`).join('');

let orderStatus;

switch (status) {
case 'processing':
orderStatus = 'progress';
break;
case 'cancelled':
orderStatus = 'rejected';
break;
case 'complete':
orderStatus = 'completed';
break;
default:
orderStatus = 'new';
}

return `
<div class="order-card">
<div class="order-card_food-items">
<h3>Food Item(s)</h3>
<div class="order-card_food-items_names">${formattedNames}</div>
</div>
<div class="order-card_price">
<p><strong>price:</strong></p>
<p>₦${orderPrice.toLocaleString()}</p>
</div>
<div class="order-card_date">
<p><strong>date:</strong></p>
<p>${date.split('T')[0].split('-').reverse().join('/')}</p>
</div>
<div class="order-status-${orderStatus}"></div>
</div>
`;
}

let decodedUserId;
try {
decodedUserId = (jwt_decode(token)).userId;
} catch (error) {
console.error(error);
}

const url = `https://kiakiafood.herokuapp.com/api/v1/users/${decodedUserId}/orders`;

fetch(url, {
headers: { 'x-auth': token },
}).then(data => data.json())
.then((response) => {
if (!response.orders.length) {
document.querySelector('.order-history').innerHTML = '<p>You have not made any orders yet!</p>';
} else {
const allUserOrders = response.orders
.map(order => orderCardBluePrint(order.items, order.price, order.date, order.status))
.join('');
document.querySelector('.order-history').innerHTML = allUserOrders;
}
})
.catch((error) => {
console.error(error);
flashMessage('An error occured while fetching your order history', 'error');
document.querySelector('.order-history').innerHTML = '<div></div>';
});
2 changes: 2 additions & 0 deletions ui/assets/js/jwt-decode.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 39ce405

Please sign in to comment.