Skip to content

Commit

Permalink
Merge pull request #12 from Xanonymous-GitHub/sorted
Browse files Browse the repository at this point in the history
Sorting and checkout page basket editing
  • Loading branch information
wortley authored Feb 15, 2024
2 parents 6b5df78 + b983a17 commit a2bf80c
Show file tree
Hide file tree
Showing 7 changed files with 121 additions and 19 deletions.
7 changes: 4 additions & 3 deletions src/checkout.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
</head>
<body>
<div class="checkout-area d-flex flex-column justify-content-center position-relative m-auto px-2 mt-3">
<div class="d-flex flex-column justify-content-start ml-0 mb-3">
<div class="d-flex flex-column justify-content-start ms-0 mb-3">
<a href="/" class="text-decoration-none">
<button class="btn btn-warning"><i class='bi bi-arrow-left'></i> Continue shopping</button>
</a>
Expand All @@ -30,15 +30,16 @@ <h2 class="text-start px-2">Basket Contents</h2>
<th scope="col">Quantity</th>
<th scope="col">Price</th>
<th scope="col">Total</th>
<th scope="col" style="width: 1px">Remove</th>
</tr>
</thead>
<!-- FIXME: Unexpected <tbody> hidden happened. The following is just a workaround. -->
<div id="basket" class="table-group-divider overflow-y-auto h-100">
<tr><td colspan="4" class="text-center">Nothing here 🥹!</td></tr>
<tr><td colspan="5" class="text-center">Nothing here 🥹!</td></tr>
</div>
<tfoot id="basket-footer">
<tr>
<td colspan="3">Total</td>
<td colspan="4">Total</td>
<td id="basket-total-price">£0.00</td>
</tr>
</tfoot>
Expand Down
4 changes: 4 additions & 0 deletions src/css/shop.css
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,7 @@ h1 {
max-height: 100%;
overflow-y: auto;
}

#sortMethod {
width: 200px;
}
8 changes: 8 additions & 0 deletions src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@
<title>E-Veg Shop</title>
</head>
<body class="d-flex flex-column">
<div class="d-flex flex-row align-items-center justify-content-center py-2">
<h4 class="me-1">Sort by: </h4>
<select class="form-select" id="sortMethod">
<option value="0" selected>Alphabetical</option>
<option value="1">Price (ascending)</option>
<option value="2">Price (descending)</option>
</select>
</div>
<main class="product-list-window">
<div class="productList stupid-grid my-2"></div>
<div id="noSearchResults" class="d-none text-center w-100 my-5 px-5"><h3>No results 🥹</h3></div>
Expand Down
76 changes: 66 additions & 10 deletions src/js/checkout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { isExpirationDateValid, isSecurityCodeValid, isValid } from './creditCar
const creditCardShown = false
const basket: Basket = readBasketCookie()

const EMPTY_BASKET_HTML = `<div id="basket" class="table-group-divider overflow-y-auto h-100"><tr><td colspan="5" class="text-center">Nothing here 🥹!</td></tr></div>`

addEventListener('DOMContentLoaded', init)

function init() {
Expand All @@ -24,7 +26,8 @@ function resetListeners() {
if (result === DialogCloseResult.Yes) {
basket.clear()
Cookies.remove('basket', cookieOptions)
window.location.reload()
updateCheckoutList()
updateTotalPrice()
}
}).then()
})
Expand All @@ -37,8 +40,10 @@ function onCheckoutButtonClicked(e: Event, form: HTMLFormElement) {
e.preventDefault()
e.stopPropagation()

if (!isCheckoutFormValidated(form))
if (!isCheckoutFormValidated(form)) {
form.classList.add('was-validated') // TODO: Does this do anything
return
}

showSweetAlert('Are you sure?', e, (result) => {
if (result === DialogCloseResult.Yes)
Expand All @@ -47,10 +52,7 @@ function onCheckoutButtonClicked(e: Event, form: HTMLFormElement) {
}

function isCheckoutFormValidated(form: HTMLFormElement): boolean {
const isValidated = form.checkValidity()
if (!isValidated)
form.classList.add('was-validated')
return isValidated
return form.checkValidity()
}

function initiateCreditCardFormDataBinding(parentForm: HTMLFormElement) {
Expand Down Expand Up @@ -143,35 +145,89 @@ function updateTotalPrice() {

const totalPricePretty = ${(totalPrice / 100).toFixed(2)}`
totalPriceElement.textContent = totalPricePretty
payByCreditCardButton.textContent = `Pay ${totalPricePretty}`
payByCreditCardButton.textContent = `Pay ${totalPrice > 0 ? totalPricePretty : ''}`
}

function updateCheckoutList() {
const checkoutList = document.querySelector('.checkoutList tbody') as HTMLTableElement
const basketItemRows: Array<HTMLTableRowElement> = []

if (basket.size === 0) {
(document.querySelector('#clearbasket') as HTMLButtonElement).disabled = true
(document.querySelector('#clearbasket') as HTMLButtonElement).disabled = true;
(document.querySelector('#paycreditcard') as HTMLButtonElement).disabled = true
const checkoutListBody = document.querySelector('.checkoutList tbody')
checkoutListBody?.replaceChildren();
checkoutListBody?.insertAdjacentHTML('afterbegin', EMPTY_BASKET_HTML)
return
}

for (const [id, quantity] of basket) {
const rowHeader = document.createElement('th')
const nameCol = document.createElement('td')
const adjustDown = document.createElement('button')
const quantityCol = document.createElement('td')
const adjustUp = document.createElement('button')
const priceCol = document.createElement('td')
const totalCol = document.createElement('td')
const removeCol = document.createElement('td')
const removeButton = document.createElement('button')
const row = document.createElement('tr')

const unitPrice = (products[id].unitPrice / 100)

rowHeader.scope = 'row'
nameCol.textContent = products[id].name
quantityCol.textContent = quantity.toString()

adjustDown.classList.add('btn', 'btn-sm', 'btn-outline-secondary', 'me-2', 'adjustDown')
adjustDown.textContent = '-'
adjustDown.addEventListener('click', (e) => {
const newQuantity = quantity - 1
if (newQuantity <= 0) {
showSweetAlert('Are you sure you want to remove this item?', e, (result) => {
if (result === DialogCloseResult.Yes) {
basket.delete(id)
Cookies.set('basket', JSON.stringify(Object.fromEntries(basket)), cookieOptions)
updateCheckoutList()
updateTotalPrice()
}
}).then()
}
else { basket.set(id, newQuantity) }

Cookies.set('basket', JSON.stringify(Object.fromEntries(basket)), cookieOptions)
updateCheckoutList()
updateTotalPrice()
})
adjustUp.classList.add('btn', 'btn-sm', 'btn-outline-secondary', 'adjustUp', 'ms-2')
adjustUp.textContent = '+'
adjustUp.addEventListener('click', () => {
basket.set(id, basket.get(id)! + 1)
Cookies.set('basket', JSON.stringify(Object.fromEntries(basket)), cookieOptions)
updateCheckoutList()
updateTotalPrice()
})
quantityCol.replaceChildren(adjustDown, `${quantity.toString()}`, adjustUp)

priceCol.textContent = ${unitPrice.toFixed(2)}`
totalCol.textContent = ${(unitPrice * quantity).toFixed(2)}`

row.replaceChildren(nameCol, quantityCol, priceCol, totalCol)
removeButton.classList.add('btn', 'btn-outline-danger', 'text-nowrap', 'm-0', 'p-1')
removeButton.innerHTML = `<i class='bi bi-trash'></i>`
removeCol.style.whiteSpace = 'nowrap'

removeButton.addEventListener('click', (e) => {
showSweetAlert('Are you sure you want to remove this item?', e, (result) => {
if (result === DialogCloseResult.Yes) {
basket.delete(id)
Cookies.set('basket', JSON.stringify(Object.fromEntries(basket)), cookieOptions)
updateCheckoutList()
updateTotalPrice()
}
}).then()
})

removeCol.appendChild(removeButton)
row.replaceChildren(nameCol, quantityCol, priceCol, totalCol, removeCol)
basketItemRows.push(row)
}

Expand Down
9 changes: 5 additions & 4 deletions src/js/productCard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const cardTemplateStr: string = `
</span>
<div class="shop-product-buying m-auto" data-num="{{ ID }}">
<div class="productBasketDiv m-auto">
<button class="addToBasket m-auto d-block btn btn-warning">Add to Basket</button>
<button class="addToBasket m-auto d-block btn btn-warning">Add to basket</button>
<div class="adjustDiv my-2 d-none">
<span class="m-auto d-flex justify-content-center input-group">
<button class="btn adjustDown">-</button>
Expand Down Expand Up @@ -50,7 +50,7 @@ export function createProductCard(
.replaceAll('{{ ID }}', product.id.toString())
.replace('{{ TITLE }}', product.name)
.replace('{{ PRICE }}', ${(product.unitPrice / 100).toFixed(2)}`)
.replace('{{ UNITS }}', `${product.quantity} ${product.unit}`)
.replace('{{ UNITS }}', `${product.quantity} ${product.unit === 'unit' ? (product.quantity === 1 ? 'unit' : 'units') : product.unit}`)

const thisProductCardTemplate = document.createElement('template')
thisProductCardTemplate.insertAdjacentHTML('beforeend', cardHTMLStr)
Expand All @@ -68,11 +68,12 @@ export function createProductCard(
}

const onInputBoxChanged = () => {
if (inputBox.value === '0' || inputBox.value === '') {
if ((Number.parseInt(inputBox.value) || 0) <= 0) {
const addToBasketBtn = thisProductCard.querySelector('.addToBasket') as HTMLButtonElement
const adjustDiv = addToBasketBtn.nextElementSibling as HTMLDivElement
addToBasketBtn.classList.remove('d-none')
adjustDiv.classList.add('d-none')
onSetProductQuantity(product.id, 0)
}
else {
onSetProductQuantity(product.id, Number.parseInt(inputBox.value))
Expand Down Expand Up @@ -118,7 +119,7 @@ export function createProductCard(

swal({
icon: 'success',
title: `${product.name} was added to basket.`,
title: `${product.name} ${product.name.endsWith('s') ? 'were' : 'was'} added to basket.`,
timer: 2000,
}).then()
},
Expand Down
30 changes: 28 additions & 2 deletions src/js/shop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,19 @@ import Cookies from 'js-cookie'
import LazyLoad from 'vanilla-lazyload'
import { products } from './products.ts'
import { createProductCard } from './productCard.ts'
import type { Basket } from './typing'
import { type Basket, SORT_METHOD } from './typing'
import { cookieOptions, readBasketCookie } from './shared'

let searchStr = ''
const basket: Basket = readBasketCookie()
const allProductElements: Array<HTMLDivElement> = []
let allProductElements: Array<HTMLDivElement> = []

addEventListener('DOMContentLoaded', () => init())

function init() {
setupSearchEventListeners()
setupCookieModalEventListeners()
setupSortingEventListeners()

setTimeout(async () => {
await asyncMakeProductCardElements()
Expand Down Expand Up @@ -69,6 +70,15 @@ function setupSearchEventListeners() {
})
}

function setupSortingEventListeners() {
const sortMethodSelect = document.querySelector('#sortMethod') as HTMLSelectElement

sortMethodSelect?.addEventListener('change', () => {
sortAndUpdateProductCards(Number.parseInt(sortMethodSelect.value) as SORT_METHOD)
// TODO: Store sort method in a cookie
})
}

function onAddToBasketClicked(productId: number, requestedQuantity: number) {
const currentQuantity = basket.get(productId) ?? 0
const newQuantity = currentQuantity + requestedQuantity
Expand Down Expand Up @@ -163,3 +173,19 @@ async function asyncMakeProductCardElements() {
return document.createElement('div')
}))
}

async function sortAndUpdateProductCards(sortMethod: SORT_METHOD) {
if (sortMethod === SORT_METHOD.PRICE_ASC)
products.sort((a, b) => a.unitPrice - b.unitPrice)

else if (sortMethod === SORT_METHOD.PRICE_DESC)
products.sort((a, b) => b.unitPrice - a.unitPrice)

else
products.sort((a, b) => a.name.localeCompare(b.name))

allProductElements = []
await asyncMakeProductCardElements()
updateDisplayedProductCards()
onSearchSubmitted()
}
6 changes: 6 additions & 0 deletions src/js/typing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,9 @@ export interface Product {
}

export type Basket = Map<number, number>

export enum SORT_METHOD {
ALPHABETICAL,
PRICE_ASC,
PRICE_DESC,
}

0 comments on commit a2bf80c

Please sign in to comment.