diff --git a/src/checkout.html b/src/checkout.html index 8c5212d..ddeb5da 100644 --- a/src/checkout.html +++ b/src/checkout.html @@ -17,7 +17,7 @@
-
+
@@ -30,15 +30,16 @@

Basket Contents

Quantity Price Total + Remove
- Nothing here 🥹! + Nothing here 🥹!
- Total + Total £0.00 diff --git a/src/css/shop.css b/src/css/shop.css index cd9e53b..30f6de2 100644 --- a/src/css/shop.css +++ b/src/css/shop.css @@ -68,3 +68,7 @@ h1 { max-height: 100%; overflow-y: auto; } + +#sortMethod { + width: 200px; +} diff --git a/src/index.html b/src/index.html index e938e8c..76ad30b 100644 --- a/src/index.html +++ b/src/index.html @@ -16,6 +16,14 @@ E-Veg Shop +
+

Sort by:

+ +

No results 🥹

diff --git a/src/js/checkout.ts b/src/js/checkout.ts index 1d958d1..a6f18b9 100644 --- a/src/js/checkout.ts +++ b/src/js/checkout.ts @@ -8,6 +8,8 @@ import { isExpirationDateValid, isSecurityCodeValid, isValid } from './creditCar const creditCardShown = false const basket: Basket = readBasketCookie() +const EMPTY_BASKET_HTML = `
Nothing here 🥹!
` + addEventListener('DOMContentLoaded', init) function init() { @@ -24,7 +26,8 @@ function resetListeners() { if (result === DialogCloseResult.Yes) { basket.clear() Cookies.remove('basket', cookieOptions) - window.location.reload() + updateCheckoutList() + updateTotalPrice() } }).then() }) @@ -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) @@ -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) { @@ -143,7 +145,7 @@ function updateTotalPrice() { const totalPricePretty = `£${(totalPrice / 100).toFixed(2)}` totalPriceElement.textContent = totalPricePretty - payByCreditCardButton.textContent = `Pay ${totalPricePretty}` + payByCreditCardButton.textContent = `Pay ${totalPrice > 0 ? totalPricePretty : ''}` } function updateCheckoutList() { @@ -151,27 +153,81 @@ function updateCheckoutList() { const basketItemRows: Array = [] 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 = `` + 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) } diff --git a/src/js/productCard.ts b/src/js/productCard.ts index faef4ad..4e07c2a 100644 --- a/src/js/productCard.ts +++ b/src/js/productCard.ts @@ -16,7 +16,7 @@ const cardTemplateStr: string = `
- +
@@ -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) @@ -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)) @@ -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() }, diff --git a/src/js/shop.ts b/src/js/shop.ts index 18c78a1..03bee69 100644 --- a/src/js/shop.ts +++ b/src/js/shop.ts @@ -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 = [] +let allProductElements: Array = [] addEventListener('DOMContentLoaded', () => init()) function init() { setupSearchEventListeners() setupCookieModalEventListeners() + setupSortingEventListeners() setTimeout(async () => { await asyncMakeProductCardElements() @@ -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 @@ -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() +} diff --git a/src/js/typing.ts b/src/js/typing.ts index 29ee9af..d20ed4b 100644 --- a/src/js/typing.ts +++ b/src/js/typing.ts @@ -9,3 +9,9 @@ export interface Product { } export type Basket = Map + +export enum SORT_METHOD { + ALPHABETICAL, + PRICE_ASC, + PRICE_DESC, +}