Last active
May 15, 2020 06:26
-
-
Save sharbel93/146df2bd1072eacf34773b2ac1491fb7 to your computer and use it in GitHub Desktop.
Gradr
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8" /> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |
<meta http-equiv="X-UA-Compatible" content="ie=edge" /> | |
<title>Mini App</title> | |
<style> | |
body { | |
margin: 0; | |
padding: 1em; | |
background-color: white; | |
} | |
[data-cart-info] span{ | |
display: inline-block; | |
vertical-align: middle; | |
} | |
.material-icons{ | |
font-size: 150px; | |
} | |
[data-cart-info], | |
[data-credit-card] { | |
transform: scale(0.78); | |
margin-left: -3.4em; | |
} | |
[data-credit-card]{ | |
width: 435px; | |
min-height: 240px; | |
border-radius: 10px; | |
background-color: #5d6874; | |
} | |
[data-card-type]{ | |
display: block; | |
width:120px; | |
height: 60px; | |
} | |
[data-cc-digits]{ | |
margin-top: 2em; | |
} | |
[data-cc-digits] input{ | |
color:white; | |
font-size: 2em; | |
line-height: 2em; | |
border-style: none; | |
background: none; | |
margin-right: 0.5em; | |
} | |
[data-cc-info]{ | |
margin-top: 1em; | |
} | |
[data-cc-info] input{ | |
color:white; | |
font-size: 1.2em; | |
border-style:none; | |
background:none; | |
} | |
[data-cc-info] input:nth-child(2){ | |
padding-right: 10px; | |
float: right; | |
} | |
[data-pay-btn]{ | |
position:fixed; | |
width: 90%; | |
border: 1px solid; | |
bottom: 20px; | |
} | |
[data-cc-info] input:focus, | |
[data-cc-digits] input:focus { | |
outline: none; | |
} | |
.mdc-card__primary-action, | |
.mdc-card__primary-action:hover { | |
cursor: auto; | |
padding: 20px; | |
min-height: inherit; | |
} | |
[data-credit-card] [data-card-type] { | |
transition: width 1.5s; | |
margin-left: calc(100% - 130px); | |
} | |
[data-credit-card].is-visa { | |
background: linear-gradient(135deg, #622774 0%, #c53364 100%); | |
} | |
[data-credit-card].is-mastercard { | |
background: linear-gradient(135deg, #65799b 0%, #5e2563 100%); | |
} | |
.is-visa [data-card-type], | |
.is-mastercard [data-card-type] { | |
width: auto; | |
} | |
input.is-invalid, | |
.is-invalid input { | |
text-decoration: line-through; | |
} | |
::placeholder { | |
color: #fff; | |
} | |
</style> | |
</head> | |
<body> | |
<div data-cart-info> | |
<h4 class="mdc-typography--headline4"> | |
<span class="material-icons">shopping_cart</span> | |
<span data-bill></span> | |
</h4> | |
</div> | |
<div data-credit-card class="mdc-card mdc-card--outlined"> | |
<div class="mdc-card__primary-action"> | |
<img alt="image" src="http://placehold.it/120x60.png?text=Card" data-card-type> | |
<div data-cc-digits> | |
<input type="text" maxlength="4" size="4" placeholder="----"> | |
<input type="text" maxlength="4" size="4" placeholder="----"> | |
<input type="text" maxlength="4" size="4" placeholder="----"> | |
<input type="text" maxlength="4" size="4" placeholder="----"> | |
</div> | |
<div data-cc-info> | |
<input type="text" size="20" placeholder="Name Surname"> | |
<input type="text" size="6" placeholder="MM/YY"> | |
</div> | |
</div> | |
</div> | |
<button class="mdc-button" data-pay-btn>Pay & Checkout Now</button> | |
<script> | |
const supportedCards = { | |
visa, mastercard | |
}; | |
const countries = [ | |
{ | |
code: "US", | |
currency: "USD", | |
country: 'United States' | |
}, | |
{ | |
code: "NG", | |
currency: "NGN", | |
country: 'Nigeria' | |
}, | |
{ | |
code: 'KE', | |
currency: 'KES', | |
country: 'Kenya' | |
}, | |
{ | |
code: 'UG', | |
currency: 'UGX', | |
country: 'Uganda' | |
}, | |
{ | |
code: 'RW', | |
currency: 'RWF', | |
country: 'Rwanda' | |
}, | |
{ | |
code: 'TZ', | |
currency: 'TZS', | |
country: 'Tanzania' | |
}, | |
{ | |
code: 'ZA', | |
currency: 'ZAR', | |
country: 'South Africa' | |
}, | |
{ | |
code: 'CM', | |
currency: 'XAF', | |
country: 'Cameroon' | |
}, | |
{ | |
code: 'GH', | |
currency: 'GHS', | |
country: 'Ghana' | |
} | |
]; | |
const appState = {}; | |
const formatAsMoney = (amount, buyerCountry) => { | |
const _money = countries.find(val => val.country === buyerCountry); | |
if(_money){ | |
return amount.toLocaleString( | |
'en-'+_money.code, | |
{style:"currency", | |
currency: _money.currency}); | |
} else { | |
return amount.toLocaleString('en-US', | |
{ | |
style: "currency", | |
currency: "USD" | |
}); | |
} | |
}; | |
const flagIfInvalid = (field, isValid) => { | |
if(isValid){ | |
field.classList.remove('is-invalid'); | |
}else { | |
field.classList.add('is-invalid'); | |
} | |
}; | |
const expiryDateFormatIsValid = (target) => { | |
return /^\d{2}\/\d{2}$/.test(target.value.trim()); | |
// if(DateTime.ParseExact('MM/YY', target)){ | |
// return true; | |
// } else { | |
// return false; | |
// } | |
}; | |
const detectCardType = ({target}) => { | |
const _visa = target.value.startsWith(4); | |
const _master = target.value.startsWith(5); | |
const card = document.querySelector('[data-credit-card]'); | |
const cardType = document.querySelector('[data-card-type]'); | |
if(_visa){ | |
card.classList.add('is-visa'); | |
card.classList.remove('is-mastercard'); | |
cardType.src = supportedCards.visa; | |
return 'is-visa'; | |
}else if(_master){ | |
card.classList.remove('is-visa'); | |
card.classList.add('is-mastercard'); | |
cardType.src = supportedCards.mastercard; | |
return 'is-mastercard'; | |
} | |
}; | |
const validateCardExpiryDate = ({target}) => { | |
// const value = target; | |
// const validate = expiryDateFormatIsValid(value); | |
// const today = new Date(); | |
// const ds = value.split('/'); | |
// const expiryDate = new Date(Number(ds[1]), (Number(ds[0]) - 1)); | |
// if(validate) { | |
// if(expiryDate > today){ | |
// flagIfInvalid(target, true); | |
// } | |
// return true; | |
if(expiryDateFormatIsValid(target)){ | |
const [month, year] = target.value.split('/'); | |
const expiryDate = new Date(`20${year}/${month}`); | |
const now = new Date(); | |
if(expiryDate > now){ | |
flagIfInvalid(target, true); | |
return true; | |
} else { | |
flagIfInvalid(target, false); | |
return false; | |
} | |
}else { | |
flagIfInvalid(target, false); | |
return false; | |
} | |
} | |
const validateCardHolderName = ({target}) => { | |
const value = target.value; | |
const result = /^[a-zA-Z]{3,}\s[a-zA-Z]{3,}$/.test(value.trim()); | |
if(result){ | |
flagIfInvalid(target,true); | |
return true; | |
}else { | |
flagIfInvalid(target, false); | |
return false; | |
} | |
// return result && flagIfInvalid(target, result); | |
}; | |
const validateWithLuhn = (digits) => { | |
const arr = []; | |
for(var i = digits.length-1; i >=0; i--){ | |
if((digits.length - i) % 2 === 0 ){ | |
let x = digits[i] * 2; | |
if(x > 9){ | |
x -= 9; | |
arr.push(x); | |
} else { | |
arr.push(x); | |
} | |
} else { | |
arr.push(digits[i]); | |
} | |
} | |
const sum = arr.reduce((prev,cur,index) => prev + cur, 0); | |
if(sum%10 === 0){ | |
return true; | |
} else { | |
return false; | |
} | |
}; | |
const validateCardNumber = () => { | |
//document.querySelector("[data-cc-digits]").classList.remove("is-invalid"); | |
const v1 = document.querySelector("[data-cc-digits] input:nth-child(1)").value; | |
const v2 = document.querySelector("[data-cc-digits] input:nth-child(2)").value; | |
const v3 = document.querySelector("[data-cc-digits] input:nth-child(3)").value; | |
const v4 = document.querySelector("[data-cc-digits] input:nth-child(4)").value; | |
const getStr = (v1+v2+v3+v4); | |
let creditStringArray = []; | |
for(let i=0; i < getStr.length; i++){ | |
creditStringArray.push(parseInt(getStr.charAt(i))); | |
} | |
const isValid = validateWithLuhn(creditStringArray); | |
if(!isValid){ | |
document.querySelector("[data-cc-digits]").classList.add("is-invalid"); | |
} else { | |
document.querySelector("[data-cc-digits]").classList.remove("is-invalid"); | |
} | |
return isValid; | |
}; | |
const uiCanInteract = () => { | |
const var1 = document.querySelector('[data-cc-digits] input:nth-child(1)'); | |
var1.addEventListener('blur', detectCardType); | |
var1.focus(); | |
const var2 = document.querySelector('[data-cc-info] input:nth-child(1)'); | |
var2.addEventListener('blur', validateCardHolderName); | |
const var3 = document.querySelector('[data-cc-info] input:nth-child(2)'); | |
var3.addEventListener('blur', validateCardExpiryDate); | |
const var4 = document.querySelector('[data-pay-btn]'); | |
var4.addEventListener('click', validateCardNumber); | |
}; | |
const displayCartTotal = ({results}) => { | |
let [data] = results; | |
let { itemsInCart, buyerCountry } = data; | |
appState.items = itemsInCart; | |
appState.country = buyerCountry; | |
appState.bill = itemsInCart.reduce((a,b) => (a["price"] * a["qty"]) + (b["price"] * b["qty"])); | |
appState.billFormatted = formatAsMoney(appState.bill, appState.country); | |
document.querySelector('[data-bill]').textContent = appState.billFormatted; | |
uiCanInteract(); | |
}; | |
const fetchBill = () => { | |
const api = "https://randomapi.com/api/006b08a801d82d0c9824dcfdfdfa3b3c"; | |
fetch(api) | |
.then(response => response.json()) | |
.then(data => { | |
displayCartTotal(data); | |
}).catch(err => { | |
console.log(err); | |
}); | |
}; | |
const startApp = () => { | |
fetchBill(); | |
} | |
startApp(); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment