Former-commit-id: 8d565d0f3bae3df3c66f14186756fc16084004f1
This commit is contained in:
Ben
2022-04-27 20:00:06 +01:00
parent 7cf9dc7354
commit 676ff3b926
12 changed files with 377 additions and 90 deletions

View File

@@ -20,6 +20,7 @@
<script type="module" src="/components/search.mjs"></script>
<script type="module" src="/components/basket.mjs"></script>
<script type="module" src="/components/basket-popout.mjs"></script>
<script type="module" src="/components/immutable-basket-list.mjs"></script>
<script type="module" src="/components/accessability-popout.mjs"></script>
<script type="module" src="/components/notificationbar.mjs"></script>
<script type="module" src="/components/tag.mjs"></script>

View File

@@ -19,6 +19,7 @@
<script type="module" src="/components/navbar.mjs"></script>
<script type="module" src="/components/search.mjs"></script>
<script type="module" src="/components/basket-popout.mjs"></script>
<script type="module" src="/components/immutable-basket-list.mjs"></script>
<script type="module" src="/components/checkout.mjs"></script>
<script type="module" src="/components/accessability-popout.mjs"></script>
<script type="module" src="/components/notificationbar.mjs"></script>

View File

@@ -180,22 +180,7 @@ class BasketPopout extends Component {
{this.state.total} Items
</div>
<div class="popup-content">
${this.state.items
? Object.keys(this.state.items).map((key) => {
const item = this.state.items[key];
return /* html */`
<div class="popup-content-item">
<span class="popup-content-item-quantity">x${item.quantity}</span>
<super-compact-listing-component class="sc-listing"
id="${key.split('~')[0]}"
type="${item.type}"
quantity="${item.quantity}"
modifier="${key.split('~')[1] || ''}">
</super-compact-listing-component>
</div>
`;
}).join('')
: ''}
<immutable-basket-list-component h="400px" class="basket-list"></immutable-basket-list-component>
</div>
<div class="popup-footer">
<span class="popup-footer-total">Subtotal: £${parseFloat(this.state.subtotal).toFixed(2)}</span>
@@ -311,39 +296,12 @@ class BasketPopout extends Component {
}
.popup-content {
display: flex;
flex-direction: column;
flex-wrap: nowrap;
justify-content: left;
width: 100%;
height: 100%;
overflow-y: scroll;
overflow-x: hidden;
justify-content: space-between;
}
.popup-content-item {
background-color: #F5F6F6;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: space-between;
align-items: center;
}
.popup-content-item-quantity {
font-size: 2em;
flex-grow: 1;
}
.sc-listing {
flex-basis: 100%;
flex-grow: 3;
}
.popup-content-item-value {
text-align: right;
flex-grow: 1;
.basket-list {
width: 100%;
}
.popup-footer {

View File

@@ -258,7 +258,7 @@ class Basket extends Component {
button.addEventListener('click', (event) => {
let clickedItem = event.target.parentElement;
let listing = clickedItem.querySelector('.basket-item-listing');
if (!listing) {
while (listing === null) {
clickedItem = clickedItem.parentElement;
listing = clickedItem.querySelector('.basket-item-listing');
}
@@ -330,7 +330,7 @@ class Basket extends Component {
input.addEventListener('change', (event) => {
let clickedItem = event.target.parentElement;
let listing = clickedItem.querySelector('.basket-item-listing');
if (!listing) {
while (listing === null) {
clickedItem = clickedItem.parentElement;
listing = clickedItem.querySelector('.basket-item-listing');
}

View File

@@ -1,5 +1,5 @@
import { GetBasketTotalPrice } from './basket-popout.mjs';
import { RegisterComponent, Component } from './components.mjs';
import { RegisterComponent, Component, SideLoad } from './components.mjs';
class Checkout extends Component {
static __IDENTIFY() { return 'checkout'; }
@@ -17,53 +17,145 @@ class Checkout extends Component {
Render() {
return {
template: /* html */`
<div class="checkout-header">
<span class="checkout-header-title">Checkout</span>
</div>
<div class="checkout">
<div class="checkout-header">
<span class="checkout-header-title">Checkout</span>
<span class="checkout-header-total">Total: £{this.state.total}</span>
</div>
<div class="checkout-delivery-form">
<div class="checkout-form-row">
<input class="checkout-form-row-input" type="text" name="name" placeholder="name"/>
<div class="checkout-body-left">
<div class="checkout-delivery-form">
<div class="checkout-delivery-form-title section-title">Shipping Details</div>
<input class="checkout-form-row-input" type="text" autocomplete="address-line1" name="address" placeholder="Shipping Address"/>
<input class="checkout-form-row-input" type="text" autocomplete="postal-code" name="postcode" placeholder="Postcode"/>
</div>
<div class="checkout-form-row">
<input class="checkout-form-row-input" type="text" name="address" placeholder="address"/>
</div>
<div class="checkout-form-row">
<input class="checkout-form-row-input" type="text" name="postcode" placeholder="postcode"/>
</div>
</div>
<div class="checkout-payment-form">
<div class="checkout-form-row">
<input class="checkout-form-row-input" type="text" name="card_number" placeholder="Card Number"/>
<input class="checkout-form-row-input" type="text" name="cvv" placeholder="CCV"/>
<input class="checkout-form-row-input" type="text" name="exp" placeholder="MM/YY"/>
<div class="checkout-delivery-form-title section-title">Payment Details</div>
<div class="checkout-payment-form">
<div class="payment-row">
<span class="form-item">
<label class="checkout-form-row-label"> Card Number </label>
<input class="checkout-form-row-input" type="text" autocomplete="cc-number" name="cc-number" placeholder="0000 0000 0000 0000"/>
</span>
<span class="form-item">
<label class="checkout-form-row-label"> Cardholder Post Code </label>
<input class="checkout-form-row-input" type="text" autocomplete="postal-code" name="postal-code" placeholder="e.g AB12 CD3"/>
</span>
</div>
<div class="payment-row">
<span class="form-item">
<label class="checkout-form-row-label"> Expiry Date </label>
<input class="checkout-form-row-input" type="text" autocomplete="cc-exp" name="cc-exp" placeholder="MM/YY"/>
</span>
<span class="form-item">
<label class="checkout-form-row-label"> CVV / CSC </label>
<input class="checkout-form-row-input" type="text" autocomplete="cc-csc" name="cc-csc" placeholder="CCV"/>
</span>
</div>
</div>
<div class="checkout-place-order">
<button class="checkout-place-order-button">Buy £${this.state.total}</button>
</div>
</div>
<div class="checkout-body-right">
<div class="checkout-summary-title section-title">Your Order <a href="/basket"><span class="edit-basket">edit basket</span><a> </div>
<div class="checkout-summary">
<immutable-basket-list-component h="300px"></immutable-basket-list-component>
<div class="checkout-summary-total">Subtotal ${this.state.total}</div>
<div class="checkout-summary-total">Shipping (UK Only) ${this.state.total}</div>
<div class="checkout-summary-total">Total ${this.state.total}</div>
<input type="text" class="offer-text" placeholder="LEGO10"/><button class="offer-button">Apply Offer Code</button>
</div>
</div>
</div>
`,
style: `
.checkout {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
}
.checkout-form-row-input {
border: 1px solid #79747E;
box-sizing: border-box;
border-radius: 4px;
}
`,
style: SideLoad('/components/css/checkout.css'),
};
}
OnRender() {
// card number
let lastCardNumber = '';
this.root.querySelector('input[name="cc-number"]').addEventListener('keyup', (e) => {
if (e.target.value !== lastCardNumber) {
// remove non-numeric characters
e.target.value = e.target.value.replace(/[^0-9]/g, '');
// space every 4 digits
e.target.value = e.target.value.replace(/(\d{4})/g, '$1 ');
// not longer than 4 sets of 4 numbers
e.target.value = e.target.value.trim().substring(0, 19);
lastCardNumber = e.target.value;
// perform validation on card number and determine if valid AND card type
// please not this is NOT a checksum
let cardType = 'unknown';
this.root.querySelector('input[name="cc-number"]').classList.remove('visa', 'mastercard', 'amex', 'unknown');
const cardNumber = e.target.value.replace(/\s/g, '');
if (cardNumber.match(/^4[0-9]{12}(?:[0-9]{3})?$/)) {
cardType = 'visa';
} else if (cardNumber.match(/^5[1-5][0-9]{14}$/)) {
cardType = 'mastercard';
}
// update card type
this.root.querySelector('input[name="cc-number"]').classList.add(cardType);
if (e.target.value.length === 19) {
this.root.querySelector('input[name="postal-code"]').focus();
}
}
if (e.keyCode === 13) {
this.root.querySelector('input[name="postal-code"]').focus();
}
});
// postal code
let lastPostalCode = '';
this.root.querySelector('input[name="postal-code"]').addEventListener('keyup', (e) => {
if (e.target.value !== lastPostalCode) {
// make uppercase
e.target.value = e.target.value.toUpperCase();
// not longer than 4 sets of 4 numbers
lastPostalCode = e.target.value;
}
if (e.keyCode === 13) {
this.root.querySelector('input[name="cc-exp"]').focus();
}
});
// expiry date
let lastExpiryDate = '';
this.root.querySelector('input[name="cc-exp"]').addEventListener('keyup', (e) => {
if (e.target.value !== lastExpiryDate) {
// remove non-numeric characters
e.target.value = e.target.value.replace(/[^0-9]/g, '');
// space every 2 digits
e.target.value = e.target.value.replace(/(\d{2})/g, '$1 / ');
// not longer than 2 sets of 2 numbers
e.target.value = e.target.value.trim().substring(0, 7);
lastExpiryDate = e.target.value;
if (e.target.value.length === 7) {
this.root.querySelector('input[name="cc-csc"]').focus();
}
}
if (e.keyCode === 13) {
this.root.querySelector('input[name="cc-csc"]').focus();
}
});
// cvv
let lastCvv = '';
this.root.querySelector('input[name="cc-csc"]').addEventListener('keyup', (e) => {
if (e.target.value !== lastCvv) {
// remove non-numeric characters
e.target.value = e.target.value.replace(/[^0-9]/g, '');
// not longer than 3 numbers
e.target.value = e.target.value.trim().substring(0, 3);
lastCvv = e.target.value;
}
});
}
}

View File

@@ -0,0 +1,113 @@
.checkout-header {
margin-top: 20px;
font-size: 2em;
border-bottom: 1px solid #ccc;
}
.checkout {
display: flex;
flex-direction: row;
align-items: flex-start;
justify-content: space-between;
width: 100%;
}
.section-title {
font-size: 1.3em;
margin-bottom: 25px;
margin-top: 25px;
border-bottom: 1px solid #ccc;
}
.checkout-body-left {
display: flex;
flex-direction: column;
flex-basis: 45%;
order: 0;
}
.checkout-body-right {
display: flex;
flex-direction: column;
flex-basis: 45%;
order: 1;
}
@media (pointer:none), (pointer:coarse), screen and (max-width: 900px) {
.checkout {
flex-direction: column;
}
.checkout-body-left {
order: 1;
flex-basis: 100%;
}
.checkout-body-right {
order: 0;
flex-basis: 100%;
}
}
.checkout-form-row-input, .checkout-form-row-label {
width: 100%;
}
.checkout-form-row-label {
font-size: 0.9em;
color: #888;
text-transform: uppercase;
}
.checkout-form-row-input {
margin-bottom: 10px;
height: 3em;
background-color: #F5F6F6;
border: 1px solid #ccc;
border-radius: 0.4em;
transition: all 250ms ease-in-out;
}
.checkout-form-row-input:focus {
outline: 0;
border: 2px solid transparent;
border-bottom: 2px solid #222;
border-radius: 0;
}
.checkout-payment-form {
display: flex;
flex-direction: column;
margin-bottom: 20px;
}
.payment-row {
display: flex;
flex-direction: row;
justify-content: space-between;
}
.visa {
background: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/260969/ic_card_visa.png) no-repeat;
background-position: 95%;
}
.mastercard {
background: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/260969/ic_card_master_card.png) no-repeat;
background-position: 95%;
}
.form-item {
width: 49%;
display: flex;
flex-direction: column;
align-items: flex-start;
}
.edit-basket {
font-size: 0.7em;
color: #888;
float: right;
text-transform: uppercase;
}

View File

@@ -0,0 +1,116 @@
import { RegisterComponent, Component } from './components.mjs';
import { GetBasketTotalPrice } from './basket-popout.mjs';
import * as LocalStorageListener from '../localstorage-listener.mjs';
class ImmutableBasketList extends Component {
static __IDENTIFY() { return 'immutable-basket-list'; }
constructor() {
super(ImmutableBasketList);
}
async OnLocalBasketUpdate() {
const basket = localStorage.getItem('basket');
if (basket) {
try {
const basketJSON = JSON.parse(basket);
const subtotal = await GetBasketTotalPrice();
this.setState({
...this.getState,
items: basketJSON.items,
total: basketJSON.total,
subtotal,
});
} catch (e) {
console.log(e);
}
} else {
this.setState({
...this.getState,
items: {},
total: 0,
subtotal: 0,
});
}
}
OnMount() {
LocalStorageListener.ListenOnKey('basket', () => {
this.OnLocalBasketUpdate(Object.bind(this));
});
this.setState({
...this.getState,
items: {},
total: 0,
subtotal: 0,
}, false);
this.OnLocalBasketUpdate(Object.bind(this));
}
Render() {
return {
template: /* html */`
<div class="popup-content">
${this.state.items
? Object.keys(this.state.items).map((key) => {
const item = this.state.items[key];
return /* html */`
<div class="popup-content-item">
<span class="popup-content-item-quantity">x${item.quantity}</span>
<super-compact-listing-component class="sc-listing"
id="${key.split('~')[0]}"
type="${item.type}"
quantity="${item.quantity}"
modifier="${key.split('~')[1] || ''}">
</super-compact-listing-component>
</div>
`;
}).join('')
: ''}
</div>
`,
style: `
.popup-content {
display: flex;
flex-direction: column;
flex-wrap: nowrap;
justify-content: left;
height: ${this.state.w || '100%'};
height: ${this.state.h || 'auto'};
overflow-y: scroll;
overflow-x: hidden;
justify-content: space-between;
}
.popup-content-item {
background-color: #F5F6F6;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: space-between;
align-items: center;
}
.popup-content-item-quantity {
font-size: 2em;
flex-grow: 1;
}
.sc-listing {
flex-basis: 100%;
flex-grow: 3;
}
`,
};
}
OnRender() {
}
}
RegisterComponent(ImmutableBasketList);

View File

@@ -6,7 +6,7 @@ import * as StorageListener from '../localstorage-listener.mjs';
// we need to have this remember the state of the logged in user
// so that we can display the correct navbar
let navbarCallback = null;
export function NotifyNavbar(type, user) {
export function NotifyNavbar(type) {
if (navbarCallback && type === 'login') {
navbarCallback.OnLogin();
}
@@ -98,9 +98,11 @@ class NavBar extends Component {
// setup log in button
const loginButton = this.root.querySelector('.account-button');
loginButton.addEventListener('click', () => {
LoginSignup(this);
});
if (loginButton) {
loginButton.addEventListener('click', () => {
LoginSignup(this);
});
}
}
}

View File

@@ -23,6 +23,7 @@
<script type="module" src="/components/navbar.mjs"></script>
<script type="module" src="/components/search.mjs"></script>
<script type="module" src="/components/basket-popout.mjs"></script>
<script type="module" src="/components/immutable-basket-list.mjs"></script>
<script type="module" src="/components/accessability-popout.mjs"></script>
<script type="module" src="/components/notificationbar.mjs"></script>
<script type="module" src="/components/tag.mjs"></script>

View File

@@ -24,6 +24,7 @@
<script type="module" src="/components/search.mjs"></script>
<script type="module" src="/components/basket.mjs"></script>
<script type="module" src="/components/basket-popout.mjs"></script>
<script type="module" src="/components/immutable-basket-list.mjs"></script>
<script type="module" src="/components/accessability-popout.mjs"></script>
<script type="module" src="/components/notificationbar.mjs"></script>
<script type="module" src="/components/storefront.mjs"></script>

View File

@@ -19,6 +19,7 @@
<script type="module" src="/components/navbar.mjs"></script>
<script type="module" src="/components/search.mjs"></script>
<script type="module" src="/components/basket-popout.mjs"></script>
<script type="module" src="/components/immutable-basket-list.mjs"></script>
<script type="module" src="/components/accessability-popout.mjs"></script>
<script type="module" src="/components/notificationbar.mjs"></script>
<script type="module" src="/components/storefront.mjs"></script>

View File

@@ -24,6 +24,7 @@
<script type="module" src="/components/search.mjs"></script>
<script type="module" src="/components/basket.mjs"></script>
<script type="module" src="/components/basket-popout.mjs"></script>
<script type="module" src="/components/immutable-basket-list.mjs"></script>
<script type="module" src="/components/accessability-popout.mjs"></script>
<script type="module" src="/components/notificationbar.mjs"></script>
<script type="module" src="/components/storefront.mjs"></script>