Adding deposit and loan user flows
This commit is contained in:
parent
ac50a0b15c
commit
9bafae9387
168
components.js
168
components.js
@ -115,7 +115,7 @@ customElements.define('sm-button',
|
||||
set disabled(value) {
|
||||
if (value) {
|
||||
this.setAttribute('disabled', '')
|
||||
}else {
|
||||
} else {
|
||||
this.removeAttribute('disabled')
|
||||
}
|
||||
}
|
||||
@ -164,80 +164,80 @@ smForm.innerHTML = `
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
<form onsubmit="return false">
|
||||
<form part="form" onsubmit="return false">
|
||||
<slot></slot>
|
||||
</form>
|
||||
`
|
||||
|
||||
customElements.define('sm-form', class extends HTMLElement {
|
||||
constructor() {
|
||||
super()
|
||||
this.attachShadow({
|
||||
mode: 'open'
|
||||
}).append(smForm.content.cloneNode(true))
|
||||
constructor() {
|
||||
super()
|
||||
this.attachShadow({
|
||||
mode: 'open'
|
||||
}).append(smForm.content.cloneNode(true))
|
||||
|
||||
this.form = this.shadowRoot.querySelector('form')
|
||||
this.formElements
|
||||
this.requiredElements
|
||||
this.submitButton
|
||||
this.resetButton
|
||||
this.allRequiredValid = false
|
||||
this.form = this.shadowRoot.querySelector('form')
|
||||
this.formElements
|
||||
this.requiredElements
|
||||
this.submitButton
|
||||
this.resetButton
|
||||
this.allRequiredValid = false
|
||||
|
||||
this.debounce = this.debounce.bind(this)
|
||||
this.handleInput = this.handleInput.bind(this)
|
||||
this.handleKeydown = this.handleKeydown.bind(this)
|
||||
this.reset = this.reset.bind(this)
|
||||
}
|
||||
debounce(callback, wait) {
|
||||
let timeoutId = null;
|
||||
return (...args) => {
|
||||
window.clearTimeout(timeoutId);
|
||||
timeoutId = window.setTimeout(() => {
|
||||
callback.apply(null, args);
|
||||
}, wait);
|
||||
};
|
||||
}
|
||||
handleInput(e) {
|
||||
this.allRequiredValid = this.requiredElements.every(elem => elem.isValid)
|
||||
if (!this.submitButton) return;
|
||||
if (this.allRequiredValid) {
|
||||
this.submitButton.disabled = false;
|
||||
}
|
||||
else {
|
||||
this.submitButton.disabled = true;
|
||||
}
|
||||
}
|
||||
handleKeydown(e) {
|
||||
if (e.key === 'Enter' && e.target.tagName !== 'SM-TEXTAREA' ) {
|
||||
if (this.allRequiredValid) {
|
||||
this.submitButton.click()
|
||||
}
|
||||
else {
|
||||
this.requiredElements.find(elem => !elem.isValid).vibrate()
|
||||
}
|
||||
}
|
||||
}
|
||||
reset() {
|
||||
this.formElements.forEach(elem => elem.reset())
|
||||
}
|
||||
connectedCallback() {
|
||||
const slot = this.shadowRoot.querySelector('slot')
|
||||
slot.addEventListener('slotchange', e => {
|
||||
this.formElements = [...this.querySelectorAll('sm-input, sm-textarea, sm-checkbox, tags-input, file-input, sm-switch, sm-radio')]
|
||||
this.requiredElements = this.formElements.filter(elem => elem.hasAttribute('required'))
|
||||
this.submitButton = e.target.assignedElements().find(elem => elem.getAttribute('variant') === 'primary' || elem.getAttribute('type') === 'submit');
|
||||
this.resetButton = e.target.assignedElements().find(elem => elem.getAttribute('type') === 'reset');
|
||||
if (this.resetButton) {
|
||||
this.resetButton.addEventListener('click', this.reset)
|
||||
}
|
||||
})
|
||||
this.addEventListener('input', this.debounce(this.handleInput, 100))
|
||||
this.addEventListener('keydown', this.debounce(this.handleKeydown, 100))
|
||||
}
|
||||
disconnectedCallback() {
|
||||
this.removeEventListener('input', this.debounce(this.handleInput, 100))
|
||||
this.removeEventListener('keydown', this.debounce(this.handleKeydown, 100))
|
||||
}
|
||||
this.debounce = this.debounce.bind(this)
|
||||
this.handleInput = this.handleInput.bind(this)
|
||||
this.handleKeydown = this.handleKeydown.bind(this)
|
||||
this.reset = this.reset.bind(this)
|
||||
}
|
||||
debounce(callback, wait) {
|
||||
let timeoutId = null;
|
||||
return (...args) => {
|
||||
window.clearTimeout(timeoutId);
|
||||
timeoutId = window.setTimeout(() => {
|
||||
callback.apply(null, args);
|
||||
}, wait);
|
||||
};
|
||||
}
|
||||
handleInput(e) {
|
||||
this.allRequiredValid = this.requiredElements.every(elem => elem.isValid)
|
||||
if (!this.submitButton) return;
|
||||
if (this.allRequiredValid) {
|
||||
this.submitButton.disabled = false;
|
||||
}
|
||||
else {
|
||||
this.submitButton.disabled = true;
|
||||
}
|
||||
}
|
||||
handleKeydown(e) {
|
||||
if (e.key === 'Enter' && e.target.tagName !== 'SM-TEXTAREA') {
|
||||
if (this.allRequiredValid) {
|
||||
this.submitButton.click()
|
||||
}
|
||||
else {
|
||||
this.requiredElements.find(elem => !elem.isValid).vibrate()
|
||||
}
|
||||
}
|
||||
}
|
||||
reset() {
|
||||
this.formElements.forEach(elem => elem.reset())
|
||||
}
|
||||
connectedCallback() {
|
||||
const slot = this.shadowRoot.querySelector('slot')
|
||||
slot.addEventListener('slotchange', e => {
|
||||
this.formElements = [...this.querySelectorAll('sm-input, sm-textarea, sm-checkbox, tags-input, file-input, sm-switch, sm-radio')]
|
||||
this.requiredElements = this.formElements.filter(elem => elem.hasAttribute('required'))
|
||||
this.submitButton = e.target.assignedElements().find(elem => elem.getAttribute('variant') === 'primary' || elem.getAttribute('type') === 'submit');
|
||||
this.resetButton = e.target.assignedElements().find(elem => elem.getAttribute('type') === 'reset');
|
||||
if (this.resetButton) {
|
||||
this.resetButton.addEventListener('click', this.reset)
|
||||
}
|
||||
})
|
||||
this.addEventListener('input', this.debounce(this.handleInput, 100))
|
||||
this.addEventListener('keydown', this.debounce(this.handleKeydown, 100))
|
||||
}
|
||||
disconnectedCallback() {
|
||||
this.removeEventListener('input', this.debounce(this.handleInput, 100))
|
||||
this.removeEventListener('keydown', this.debounce(this.handleKeydown, 100))
|
||||
}
|
||||
})
|
||||
|
||||
const smInput = document.createElement('template')
|
||||
@ -488,7 +488,7 @@ customElements.define('sm-input',
|
||||
this.isRequired = false
|
||||
this.validationFunction
|
||||
this.reflectedAttributes = ['value', 'required', 'disabled', 'type', 'inputmode', 'readonly', 'min', 'max', 'pattern', 'minlength', 'maxlength', 'step']
|
||||
|
||||
|
||||
this.reset = this.reset.bind(this)
|
||||
this.focusIn = this.focusIn.bind(this)
|
||||
this.focusOut = this.focusOut.bind(this)
|
||||
@ -545,7 +545,7 @@ customElements.define('sm-input',
|
||||
}
|
||||
}
|
||||
set customValidation(val) {
|
||||
|
||||
|
||||
this.validationFunction = val
|
||||
}
|
||||
set errorText(val) {
|
||||
@ -578,19 +578,19 @@ customElements.define('sm-input',
|
||||
return (_isValid && _customValid)
|
||||
}
|
||||
}
|
||||
reset(){
|
||||
reset() {
|
||||
this.value = ''
|
||||
}
|
||||
|
||||
focusIn(){
|
||||
focusIn() {
|
||||
this.input.focus()
|
||||
}
|
||||
|
||||
focusOut(){
|
||||
focusOut() {
|
||||
this.input.blur()
|
||||
}
|
||||
|
||||
fireEvent(){
|
||||
fireEvent() {
|
||||
let event = new Event('input', {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
@ -599,7 +599,7 @@ customElements.define('sm-input',
|
||||
this.dispatchEvent(event);
|
||||
}
|
||||
|
||||
checkInput(e){
|
||||
checkInput(e) {
|
||||
if (!this.hasAttribute('readonly')) {
|
||||
if (this.input.value.trim() !== '') {
|
||||
this.clearBtn.classList.remove('hide')
|
||||
@ -643,7 +643,7 @@ customElements.define('sm-input',
|
||||
this.input.addEventListener('input', this.checkInput)
|
||||
this.clearBtn.addEventListener('click', this.reset)
|
||||
}
|
||||
|
||||
|
||||
attributeChangedCallback(name, oldValue, newValue) {
|
||||
if (oldValue !== newValue) {
|
||||
if (this.reflectedAttributes.includes(name)) {
|
||||
@ -680,7 +680,7 @@ customElements.define('sm-input',
|
||||
}
|
||||
else {
|
||||
this.feedbackText.textContent = ''
|
||||
this.setAttribute('aria-required', 'false')
|
||||
this.setAttribute('aria-required', 'false')
|
||||
}
|
||||
}
|
||||
else if (name === 'readonly') {
|
||||
@ -1175,7 +1175,7 @@ customElements.define('sm-popup', class extends HTMLElement {
|
||||
}
|
||||
|
||||
show(options = {}) {
|
||||
const {pinned = false, popupStack = undefined} = options
|
||||
const { pinned = false, popupStack = undefined } = options
|
||||
if (popupStack)
|
||||
this.popupStack = popupStack
|
||||
if (this.popupStack && !this.hasAttribute('open')) {
|
||||
@ -1311,8 +1311,8 @@ customElements.define('sm-popup', class extends HTMLElement {
|
||||
}
|
||||
});
|
||||
resizeObserver.observe(this)
|
||||
|
||||
|
||||
|
||||
|
||||
this.popupHeader.addEventListener('touchstart', (e) => { this.handleTouchStart(e) }, { passive: true })
|
||||
this.popupHeader.addEventListener('touchmove', (e) => { this.handleTouchMove(e) }, { passive: true })
|
||||
this.popupHeader.addEventListener('touchend', (e) => { this.handleTouchEnd(e) }, { passive: true })
|
||||
@ -1487,7 +1487,7 @@ class ThemeToggle extends HTMLElement {
|
||||
document.body.dataset.theme = 'light'
|
||||
this.setAttribute('aria-checked', 'false')
|
||||
}
|
||||
|
||||
|
||||
nightlight() {
|
||||
this.hasTheme = 'dark'
|
||||
document.body.dataset.theme = 'dark'
|
||||
@ -1509,7 +1509,7 @@ class ThemeToggle extends HTMLElement {
|
||||
this.setAttribute('checked', '')
|
||||
}
|
||||
else {
|
||||
this.removeAttribute('checked')
|
||||
this.removeAttribute('checked')
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1549,7 +1549,7 @@ class ThemeToggle extends HTMLElement {
|
||||
this.addEventListener("keydown", this.handleKeyDown);
|
||||
document.addEventListener('themechange', this.handleThemeChange)
|
||||
}
|
||||
|
||||
|
||||
disconnectedCallback() {
|
||||
this.removeEventListener("click", this.toggleState);
|
||||
this.removeEventListener("keydown", this.handleKeyDown);
|
||||
@ -1648,7 +1648,7 @@ customElements.define('sm-copy',
|
||||
this.attachShadow({
|
||||
mode: 'open'
|
||||
}).append(smCopy.content.cloneNode(true))
|
||||
|
||||
|
||||
this.copyContent = this.shadowRoot.querySelector('.copy-content')
|
||||
this.copyButton = this.shadowRoot.querySelector('.copy-button')
|
||||
|
||||
|
||||
143
css/main.css
143
css/main.css
@ -579,6 +579,7 @@ details[open] > summary .icon {
|
||||
}
|
||||
|
||||
.quick-action {
|
||||
color: inherit;
|
||||
display: -webkit-box;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
@ -722,21 +723,34 @@ details[open] > summary .icon {
|
||||
.status-tag:not(:empty) .icon {
|
||||
margin-right: 0.3rem;
|
||||
}
|
||||
.status-tag:not(:empty).success, .status-tag:not(:empty).active {
|
||||
.status-tag:not(:empty).active {
|
||||
color: var(--green);
|
||||
background-color: rgba(0, 230, 118, 0.1);
|
||||
}
|
||||
.status-tag:not(:empty).closed {
|
||||
color: rgba(var(--text-color), 0.7);
|
||||
background-color: rgba(var(--text-color), 0.06);
|
||||
}
|
||||
.status-tag:not(:empty).success {
|
||||
color: var(--green);
|
||||
background-color: rgba(0, 230, 118, 0.1);
|
||||
}
|
||||
.status-tag:not(:empty).success .icon {
|
||||
fill: var(--green);
|
||||
}
|
||||
.status-tag:not(:empty).failed {
|
||||
color: var(--danger-color);
|
||||
background-color: rgba(255, 75, 75, 0.1);
|
||||
}
|
||||
.status-tag:not(:empty).failed .icon {
|
||||
fill: var(--danger-color);
|
||||
}
|
||||
.status-tag:not(:empty).pending {
|
||||
color: var(--yellow);
|
||||
background-color: rgba(255, 252, 75, 0.1);
|
||||
}
|
||||
.status-tag:not(:empty).closed {
|
||||
color: rgba(var(--text-color), 0.7);
|
||||
background-color: rgba(var(--text-color), 0.06);
|
||||
.status-tag:not(:empty).pending .icon {
|
||||
fill: var(--yellow);
|
||||
}
|
||||
|
||||
.activity-card--response {
|
||||
@ -758,14 +772,14 @@ details[open] > summary .icon {
|
||||
fill: var(--green);
|
||||
}
|
||||
|
||||
.pending .icon {
|
||||
fill: var(--yellow);
|
||||
}
|
||||
|
||||
.failed .icon {
|
||||
fill: var(--danger-color);
|
||||
}
|
||||
|
||||
.pending .icon {
|
||||
fill: var(--yellow);
|
||||
}
|
||||
|
||||
#user_section {
|
||||
display: grid;
|
||||
-ms-flex-line-pack: start;
|
||||
@ -950,7 +964,6 @@ details[open] > summary .icon {
|
||||
.account-step {
|
||||
opacity: 0;
|
||||
grid-template-columns: auto minmax(0, 1fr);
|
||||
grid-template-areas: "step-icon step-title" ". step-description";
|
||||
gap: 0.3rem 0.8rem;
|
||||
-webkit-animation: slide-down 0.3s forwards;
|
||||
animation: slide-down 0.3s forwards;
|
||||
@ -971,31 +984,24 @@ details[open] > summary .icon {
|
||||
--height: 1rem;
|
||||
--width: 1rem;
|
||||
}
|
||||
.account-step:not(:last-of-type)::after {
|
||||
.account-step:not(:last-of-type) .step__line {
|
||||
position: relative;
|
||||
content: "";
|
||||
height: 3rem;
|
||||
width: 0.1rem;
|
||||
margin: 0.5rem 0 1rem 0;
|
||||
margin-left: 0.7rem;
|
||||
justify-self: center;
|
||||
background-color: var(--green);
|
||||
}
|
||||
.account-step .step__icon {
|
||||
grid-area: step-icon;
|
||||
}
|
||||
.account-step .icon {
|
||||
height: 1.5rem;
|
||||
width: 1.5rem;
|
||||
}
|
||||
.account-step:not(.loading) .step__title {
|
||||
text-transform: capitalize;
|
||||
grid-area: step-title;
|
||||
font-weight: 500;
|
||||
font-size: 0.9rem;
|
||||
padding: 0.2rem 0;
|
||||
}
|
||||
.account-step .step__description {
|
||||
grid-area: step-description;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
@ -1024,6 +1030,104 @@ details[open] > summary .icon {
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
#deposit,
|
||||
#loan {
|
||||
padding: 1.5rem 0;
|
||||
grid-template-rows: auto 1fr;
|
||||
}
|
||||
#deposit sm-form,
|
||||
#loan sm-form {
|
||||
justify-self: center;
|
||||
margin-top: 3rem;
|
||||
width: min(24rem, 100%);
|
||||
}
|
||||
#deposit sm-form::part(form),
|
||||
#loan sm-form::part(form) {
|
||||
display: -webkit-box;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-box-direction: normal;
|
||||
-ms-flex-direction: column;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#deposit__icon .icon,
|
||||
#loan__icon .icon {
|
||||
height: 1.8rem;
|
||||
width: 1.8rem;
|
||||
fill: var(--accent-color);
|
||||
}
|
||||
|
||||
#get_deposit_amount,
|
||||
#get_loan_amount {
|
||||
--font-size: 1.5rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
#result {
|
||||
place-content: center;
|
||||
}
|
||||
#result section {
|
||||
text-align: center;
|
||||
-webkit-box-align: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
}
|
||||
#result__icon {
|
||||
display: -webkit-box;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
border-radius: 50%;
|
||||
padding: 1rem;
|
||||
margin: -5rem 0 1.5rem 0;
|
||||
-webkit-animation: pop-in 1s forwards cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
animation: pop-in 1s forwards cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
}
|
||||
#result__icon .icon {
|
||||
height: 1.8rem;
|
||||
width: 1.8rem;
|
||||
}
|
||||
#result__description {
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
#result__back_button {
|
||||
margin-top: 3rem;
|
||||
}
|
||||
|
||||
@-webkit-keyframes pop-in {
|
||||
0% {
|
||||
opacity: 0;
|
||||
-webkit-transform: scale(0.4) translateY(6rem);
|
||||
transform: scale(0.4) translateY(6rem);
|
||||
}
|
||||
40% {
|
||||
opacity: 1;
|
||||
-webkit-transform: scale(0.4) translateY(0);
|
||||
transform: scale(0.4) translateY(0);
|
||||
}
|
||||
100% {
|
||||
-webkit-transform: none;
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes pop-in {
|
||||
0% {
|
||||
opacity: 0;
|
||||
-webkit-transform: scale(0.4) translateY(6rem);
|
||||
transform: scale(0.4) translateY(6rem);
|
||||
}
|
||||
40% {
|
||||
opacity: 1;
|
||||
-webkit-transform: scale(0.4) translateY(0);
|
||||
transform: scale(0.4) translateY(0);
|
||||
}
|
||||
100% {
|
||||
-webkit-transform: none;
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 640px) {
|
||||
sm-button {
|
||||
--padding: 1rem;
|
||||
@ -1058,7 +1162,8 @@ details[open] > summary .icon {
|
||||
}
|
||||
|
||||
#transaction_action_button,
|
||||
#account_action_button {
|
||||
#account_action_button,
|
||||
#deposit_button {
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
|
||||
2
css/main.min.css
vendored
2
css/main.min.css
vendored
File diff suppressed because one or more lines are too long
113
css/main.scss
113
css/main.scss
@ -512,6 +512,7 @@ details {
|
||||
margin: 2rem 0;
|
||||
}
|
||||
.quick-action {
|
||||
color: inherit;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
@ -629,22 +630,34 @@ details {
|
||||
.icon {
|
||||
margin-right: 0.3rem;
|
||||
}
|
||||
&.success,
|
||||
&.active {
|
||||
color: var(--green);
|
||||
background-color: rgb(0, 230, 118, 0.1);
|
||||
}
|
||||
&.closed {
|
||||
color: rgba(var(--text-color), 0.7);
|
||||
background-color: rgba(var(--text-color), 0.06);
|
||||
}
|
||||
&.success {
|
||||
color: var(--green);
|
||||
background-color: rgb(0, 230, 118, 0.1);
|
||||
.icon {
|
||||
fill: var(--green);
|
||||
}
|
||||
}
|
||||
&.failed {
|
||||
color: var(--danger-color);
|
||||
background-color: rgb(255, 75, 75, 0.1);
|
||||
.icon {
|
||||
fill: var(--danger-color);
|
||||
}
|
||||
}
|
||||
&.pending {
|
||||
color: var(--yellow);
|
||||
background-color: rgba(255, 252, 75, 0.1);
|
||||
}
|
||||
&.closed {
|
||||
color: rgba(var(--text-color), 0.7);
|
||||
background-color: rgba(var(--text-color), 0.06);
|
||||
.icon {
|
||||
fill: var(--yellow);
|
||||
}
|
||||
}
|
||||
}
|
||||
.activity-card--response {
|
||||
@ -666,16 +679,16 @@ details {
|
||||
fill: var(--green);
|
||||
}
|
||||
}
|
||||
.pending {
|
||||
.icon {
|
||||
fill: var(--yellow);
|
||||
}
|
||||
}
|
||||
.failed {
|
||||
.icon {
|
||||
fill: var(--danger-color);
|
||||
}
|
||||
}
|
||||
.pending {
|
||||
.icon {
|
||||
fill: var(--yellow);
|
||||
}
|
||||
}
|
||||
#user_section {
|
||||
display: grid;
|
||||
align-content: flex-start;
|
||||
@ -810,7 +823,6 @@ details {
|
||||
.account-step {
|
||||
opacity: 0;
|
||||
grid-template-columns: auto minmax(0, 1fr);
|
||||
grid-template-areas: "step-icon step-title" ". step-description";
|
||||
gap: 0.3rem 0.8rem;
|
||||
animation: slide-down 0.3s forwards;
|
||||
&:nth-of-type(2) {
|
||||
@ -826,33 +838,26 @@ details {
|
||||
--height: 1rem;
|
||||
--width: 1rem;
|
||||
}
|
||||
&:not(:last-of-type)::after {
|
||||
&:not(:last-of-type) .step__line {
|
||||
position: relative;
|
||||
content: "";
|
||||
height: 3rem;
|
||||
width: 0.1rem;
|
||||
margin: 0.5rem 0 1rem 0;
|
||||
margin-left: 0.7rem;
|
||||
justify-self: center;
|
||||
background-color: var(--green);
|
||||
}
|
||||
.step__icon {
|
||||
grid-area: step-icon;
|
||||
}
|
||||
.icon {
|
||||
height: 1.5rem;
|
||||
width: 1.5rem;
|
||||
}
|
||||
&:not(.loading) {
|
||||
.step__title {
|
||||
text-transform: capitalize;
|
||||
grid-area: step-title;
|
||||
font-weight: 500;
|
||||
font-size: 0.9rem;
|
||||
padding: 0.2rem 0;
|
||||
}
|
||||
}
|
||||
.step__description {
|
||||
grid-area: step-description;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
}
|
||||
@ -866,6 +871,71 @@ details {
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
#deposit,
|
||||
#loan {
|
||||
padding: 1.5rem 0;
|
||||
grid-template-rows: auto 1fr;
|
||||
sm-form {
|
||||
justify-self: center;
|
||||
margin-top: 3rem;
|
||||
width: min(24rem, 100%);
|
||||
&::part(form) {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
}
|
||||
#deposit__icon,
|
||||
#loan__icon {
|
||||
.icon {
|
||||
height: 1.8rem;
|
||||
width: 1.8rem;
|
||||
fill: var(--accent-color);
|
||||
}
|
||||
}
|
||||
#get_deposit_amount,
|
||||
#get_loan_amount {
|
||||
--font-size: 1.5rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
#result {
|
||||
place-content: center;
|
||||
section {
|
||||
text-align: center;
|
||||
align-items: center;
|
||||
}
|
||||
&__icon {
|
||||
display: flex;
|
||||
border-radius: 50%;
|
||||
padding: 1rem;
|
||||
margin: -5rem 0 1.5rem 0;
|
||||
animation: pop-in 1s forwards cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
.icon {
|
||||
height: 1.8rem;
|
||||
width: 1.8rem;
|
||||
}
|
||||
}
|
||||
&__description {
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
&__back_button {
|
||||
margin-top: 3rem;
|
||||
}
|
||||
}
|
||||
@keyframes pop-in {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: scale(0.4) translateY(6rem);
|
||||
}
|
||||
40% {
|
||||
opacity: 1;
|
||||
transform: scale(0.4) translateY(0);
|
||||
}
|
||||
100% {
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 640px) {
|
||||
sm-button {
|
||||
--padding: 1rem;
|
||||
@ -889,7 +959,8 @@ details {
|
||||
}
|
||||
}
|
||||
#transaction_action_button,
|
||||
#account_action_button {
|
||||
#account_action_button,
|
||||
#deposit_button {
|
||||
margin-top: auto;
|
||||
}
|
||||
#account_process {
|
||||
|
||||
186
index.html
186
index.html
@ -174,7 +174,7 @@
|
||||
Quick actions
|
||||
</h4>
|
||||
<div id="quick_actions_container">
|
||||
<button class="quick-action">
|
||||
<a class="quick-action" href="#/deposit">
|
||||
<div class="quick-action__icon">
|
||||
<svg class="icon" xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24"
|
||||
height="24px" viewBox="0 0 24 24" width="24px" fill="#000000">
|
||||
@ -186,8 +186,8 @@
|
||||
</svg>
|
||||
</div>
|
||||
<span class="quick-action__title">Make deposit</span>
|
||||
</button>
|
||||
<button class="quick-action">
|
||||
</a>
|
||||
<a class="quick-action" href="#/loan">
|
||||
<div class="quick-action__icon">
|
||||
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<path
|
||||
@ -197,7 +197,7 @@
|
||||
</svg>
|
||||
</div>
|
||||
<span class="quick-action__title">Get loan</span>
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
@ -309,6 +309,70 @@
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
<article id="deposit" class="page hide-completely page-layout">
|
||||
<button class="icon-button justify-self-start" onclick="history.back()">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path fill-rule="evenodd"
|
||||
d="M9.707 16.707a1 1 0 01-1.414 0l-6-6a1 1 0 010-1.414l6-6a1 1 0 011.414 1.414L5.414 9H17a1 1 0 110 2H5.414l4.293 4.293a1 1 0 010 1.414z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
</button>
|
||||
<sm-form>
|
||||
<div id="deposit__icon">
|
||||
<svg class="icon" xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px"
|
||||
viewBox="0 0 24 24" width="24px" fill="#000000">
|
||||
<rect fill="none" height="24" width="24" />
|
||||
<g>
|
||||
<path
|
||||
d="M19.83,7.5l-2.27-2.27c0.07-0.42,0.18-0.81,0.32-1.15C17.96,3.9,18,3.71,18,3.5C18,2.67,17.33,2,16.5,2 c-1.64,0-3.09,0.79-4,2l-5,0C4.46,4,2,6.46,2,9.5S4.5,21,4.5,21l5.5,0v-2h2v2l5.5,0l1.68-5.59L22,14.47V7.5H19.83z M13,9H8V7h5V9z M16,11c-0.55,0-1-0.45-1-1c0-0.55,0.45-1,1-1s1,0.45,1,1C17,10.55,16.55,11,16,11z" />
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="grid gap-0-5">
|
||||
<h3 class="h3">Deposit</h3>
|
||||
<p id="deposit_interest_rate"></p>
|
||||
</div>
|
||||
<sm-input id="get_deposit_amount" type="number" min="1" step="0.01"
|
||||
error-text="Amount should be at least ₹1" placeholder="₹Amount" required>
|
||||
</sm-input>
|
||||
<sm-button id="deposit_button" variant="primary" disabled onclick="initDeposit()">Deposit</sm-button>
|
||||
</sm-form>
|
||||
</article>
|
||||
<article id="loan" class="page hide-completely page-layout">
|
||||
<button class="icon-button justify-self-start" onclick="history.back()">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path fill-rule="evenodd"
|
||||
d="M9.707 16.707a1 1 0 01-1.414 0l-6-6a1 1 0 010-1.414l6-6a1 1 0 011.414 1.414L5.414 9H17a1 1 0 110 2H5.414l4.293 4.293a1 1 0 010 1.414z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
</button>
|
||||
<sm-form>
|
||||
<div id="deposit__icon">
|
||||
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<path
|
||||
d="M12.65,8.5H10.42a5.18,5.18,0,0,0-4,1.95L3.75,13.74a5,5,0,0,0-.68,5.31l0,0h0a5.07,5.07,0,0,0,4.5,2.73h7.88A5.08,5.08,0,0,0,20,19.05h0a5.39,5.39,0,0,0,.43-1.2,4.91,4.91,0,0,0-1-4.06l-2.66-3.34A5.17,5.17,0,0,0,12.65,8.5Z" />
|
||||
<path
|
||||
d="M9.71,6.59h3.94A2.94,2.94,0,0,0,16,5.39h0a1.13,1.13,0,0,0-.23-1.57,1.11,1.11,0,0,0-.95-.18h0a1.2,1.2,0,0,1-.92-.18l-.28-.21a3.64,3.64,0,0,0-4.22,0h0a1.18,1.18,0,0,1-1,.16L8.2,3.35a1.13,1.13,0,0,0-1.36.84A1.17,1.17,0,0,0,7,5H7A3.18,3.18,0,0,0,9.71,6.59Z" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="grid gap-0-5">
|
||||
<h3 class="h3">Loan</h3>
|
||||
<p id="loan_interest_rate"></p>
|
||||
</div>
|
||||
<sm-input id="get_loan_amount" type="number" min="1" step="0.01" error-text="Amount should be at least ₹1"
|
||||
placeholder="₹Amount" required>
|
||||
</sm-input>
|
||||
<sm-button id="loan_button" variant="primary" disabled onclick="initLoan()">Request loan</sm-button>
|
||||
</sm-form>
|
||||
</article>
|
||||
<article id="result" class="page hide-completely page-layout">
|
||||
<section class="flex direction-column">
|
||||
<div id="result__icon" class="pending"></div>
|
||||
<h4 id="result__title"></h4>
|
||||
<p id="result__description"></p>
|
||||
<a id="result__back_button" href="" class="button">Done</a>
|
||||
</section>
|
||||
</article>
|
||||
<article id="transaction" class="page hide-completely page-layout">
|
||||
<button class="icon-button justify-self-start" onclick="history.back()">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 20 20" fill="currentColor">
|
||||
@ -328,7 +392,6 @@
|
||||
<time id="transaction_detail__time"></time>
|
||||
<div id="transaction_detail__status"></div>
|
||||
</div>
|
||||
<sm-button id="transaction_action_button" variant="primary"></sm-button>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
@ -391,6 +454,7 @@
|
||||
<div class="account-step grid">
|
||||
<div class="step__icon"></div>
|
||||
<span class="step__title"></span>
|
||||
<div class="step__line"></div>
|
||||
<p class="step__description"></p>
|
||||
</div>
|
||||
</template>
|
||||
@ -632,7 +696,7 @@
|
||||
return `${month} ${year}`;
|
||||
}
|
||||
else
|
||||
return `${finalHours} ${month} ${date} ${year}`;
|
||||
return `${month} ${date} ${year}, ${finalHours}`;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return time;
|
||||
@ -707,6 +771,14 @@
|
||||
getRef('generated_flo_id').value = floID
|
||||
getRef('generated_private_key').value = privKey
|
||||
break;
|
||||
case 'deposit':
|
||||
const { I_s } = bank_app.getRates()
|
||||
getRef('deposit_interest_rate').textContent = `Make a deposit at ${I_s * 100}% rate of interest`
|
||||
break;
|
||||
case 'loan':
|
||||
const { I_b } = bank_app.getRates()
|
||||
getRef('loan_interest_rate').textContent = `Get a loan at ${I_b * 100}% rate of interest`
|
||||
break;
|
||||
case 'transaction':
|
||||
showTransactionDetails(params)
|
||||
break;
|
||||
@ -981,7 +1053,7 @@
|
||||
card.querySelector('.activity-card__icon').innerHTML = icon
|
||||
card.querySelector('.activity-card__type').textContent = type
|
||||
card.querySelector('.activity-card__time').textContent = getFormattedTime(parseInt(openTime))
|
||||
card.querySelector('.activity-card__amount').textContent = amount.toLocaleString(`en-IN`, { style: 'currency', currency: 'INR' })
|
||||
card.querySelector('.activity-card__amount').textContent = netAmt.toLocaleString(`en-IN`, { style: 'currency', currency: 'INR' })
|
||||
card.querySelector('.activity-card__status').classList.add(status)
|
||||
card.querySelector('.activity-card__status').textContent = status
|
||||
return card
|
||||
@ -1068,7 +1140,6 @@
|
||||
function showTransactionDetails(params) {
|
||||
const { requestID } = params
|
||||
const { amount, rtype, timestamp, status, index } = getRequestDetails(requestID)
|
||||
const { status: accountStatus } = bank_app.accounts[index]
|
||||
|
||||
let type = (rtype === 'openDeposit' || rtype === 'closeDeposit') ? 'deposit' : 'loan'
|
||||
const icon = utils.getRelatedIcon(rtype)
|
||||
@ -1079,22 +1150,6 @@
|
||||
getRef('transaction_detail__type').textContent = type
|
||||
getRef('transaction_detail__amount').textContent = amount.toLocaleString(`en-IN`, { style: 'currency', currency: 'INR' })
|
||||
getRef('transaction_detail__time').textContent = getFormattedTime(timestamp)
|
||||
getRef('transaction_action_button').classList.remove('hide-completely')
|
||||
|
||||
if (accountStatus === 'active') {
|
||||
getRef('transaction_action_button').classList.remove('hide-completely')
|
||||
} else {
|
||||
getRef('transaction_action_button').classList.add('hide-completely')
|
||||
}
|
||||
|
||||
switch (rtype) {
|
||||
case 'openDeposit':
|
||||
getRef('transaction_action_button').textContent = 'Withdraw'
|
||||
break
|
||||
case 'openLoan':
|
||||
getRef('transaction_action_button').textContent = 'Repay'
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
function getAccountStatus(index) {
|
||||
@ -1131,19 +1186,19 @@
|
||||
function renderAccountProgress(index) {
|
||||
const { type, status, openTime, closeTime = 0, amount, netAmt } = bank_app.accounts[index]
|
||||
getRef('account_process__steps').innerHTML = ''
|
||||
getRef('account_process__steps').append(render.accountProgressStep('success', `${type}ed`, getFormattedTime(openTime)))
|
||||
getRef('account_process__steps').append(render.accountProgressStep('success', type === "deposit" ? 'Deposited' : 'Got loan', getFormattedTime(openTime)))
|
||||
const { isPending, pendingRequest } = getAccountStatus(index)
|
||||
if (type === 'deposit') {
|
||||
if (isPending) {
|
||||
getRef('account_process__steps').append(render.accountProgressStep('success', 'Sent withdrawal request', getFormattedTime(pendingRequest.split('_')[0])))
|
||||
getRef('account_process__steps').append(render.accountProgressStep('pending', 'Waiting for confirmation', `Once your request is processed, your withdrawn amount will reflect in your balance.<br>meanwhile you can go back and continue using the app.`))
|
||||
getRef('account_process__steps').append(render.accountProgressStep('pending', 'Waiting for withdrawal confirmation', `Once your request is processed, your withdrawn amount will reflect in your balance.<br>meanwhile you can go back and continue using the app.`))
|
||||
} else {
|
||||
getRef('account_process__steps').append(render.accountProgressStep('success', 'Withdrawal complete', getFormattedTime(closeTime)))
|
||||
}
|
||||
} else {
|
||||
if (isPending) {
|
||||
getRef('account_process__steps').append(render.accountProgressStep('success', 'Sent repay request', getFormattedTime(pendingRequest.split('_')[0])))
|
||||
getRef('account_process__steps').append(render.accountProgressStep('pending', 'Waiting for confirmation', `Once your request is processed, your loan will be closed,`))
|
||||
getRef('account_process__steps').append(render.accountProgressStep('pending', 'Waiting for repayment confirmation', `Once your request is processed, your loan will be closed,`))
|
||||
} else {
|
||||
getRef('account_process__steps').append(render.accountProgressStep('success', 'Repayment complete', getFormattedTime(closeTime)))
|
||||
}
|
||||
@ -1223,8 +1278,8 @@
|
||||
accountChart.data.datasets[0].data = [depositTotal, loanTotal]
|
||||
accountChart.update()
|
||||
}
|
||||
getRef('total_deposit').textContent = depositTotal
|
||||
getRef('total_loan').textContent = loanTotal
|
||||
getRef('total_deposit').textContent = depositTotal.toLocaleString(`en-IN`, { style: 'currency', currency: 'INR' })
|
||||
getRef('total_loan').textContent = loanTotal.toLocaleString(`en-IN`, { style: 'currency', currency: 'INR' })
|
||||
}
|
||||
|
||||
document.querySelector('theme-toggle').addEventListener('themechange', e => {
|
||||
@ -1279,6 +1334,79 @@
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
getRef('get_deposit_amount').addEventListener('input', e => {
|
||||
if (e.target.value.trim() !== '') {
|
||||
getRef('deposit_button').textContent = `Deposit ${parseFloat(e.target.value).toLocaleString('en-IN', { currency: 'INR', style: 'currency' })}`
|
||||
} else {
|
||||
getRef('deposit_button').textContent = `Deposit`
|
||||
}
|
||||
})
|
||||
getRef('get_loan_amount').addEventListener('input', e => {
|
||||
if (e.target.value.trim() !== '') {
|
||||
getRef('loan_button').textContent = `Request loan of ${parseFloat(e.target.value).toLocaleString('en-IN', { currency: 'INR', style: 'currency' })}`
|
||||
} else {
|
||||
getRef('loan_button').textContent = `Request loan`
|
||||
}
|
||||
})
|
||||
|
||||
function showActionResult(details = {}) {
|
||||
const { type, amount, status, reason = '' } = details
|
||||
getRef('result__icon').innerHTML = utils.getRelatedIcon(status)
|
||||
if (type === 'deposit') {
|
||||
getRef('result__title').textContent = 'Sent deposit request'
|
||||
getRef('result__description').textContent = 'Waiting for deposit confirmation. Meanwhile you can go back and continue using app.'
|
||||
} else if (type === 'loan') {
|
||||
getRef('result__title').textContent = 'Sent loan request'
|
||||
getRef('result__description').textContent = 'Waiting for loan confirmation. Meanwhile you can go back and continue using app.'
|
||||
}
|
||||
showPage('result')
|
||||
}
|
||||
|
||||
async function initDeposit() {
|
||||
const amount = parseFloat(getRef('get_deposit_amount').value)
|
||||
const confirm = await getConfirmation(`Confirm deposit of ${amount.toLocaleString('en-IN', { currency: 'INR', style: 'currency' })}?`)
|
||||
if (confirm) {
|
||||
bank_app.makeDeposit(amount)
|
||||
.then(() => {
|
||||
showActionResult({
|
||||
type: 'deposit',
|
||||
amount,
|
||||
status: 'pending'
|
||||
})
|
||||
})
|
||||
.catch(err => {
|
||||
showActionResult({
|
||||
type: 'deposit',
|
||||
amount,
|
||||
status: 'failed',
|
||||
reason: err
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
async function initLoan() {
|
||||
const amount = parseFloat(getRef('get_loan_amount').value)
|
||||
const confirm = await getConfirmation(`Confirm loan of ${amount.toLocaleString('en-IN', { currency: 'INR', style: 'currency' })}?`)
|
||||
if (confirm) {
|
||||
bank_app.requestLoan(amount)
|
||||
.then(() => {
|
||||
showActionResult({
|
||||
type: 'loan',
|
||||
amount,
|
||||
status: 'pending'
|
||||
})
|
||||
})
|
||||
.catch(err => {
|
||||
showActionResult({
|
||||
type: 'loan',
|
||||
amount,
|
||||
status: 'failed',
|
||||
reason: err
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<script id="onLoadStartUp">
|
||||
const requestResponsePairs = {}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user