minor UI/UX update

Added loading states
TxIds in helpline view
This commit is contained in:
sairaj mote 2020-07-14 15:43:28 +05:30 committed by GitHub
parent e1a39e33d1
commit bb5590c9d4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 680 additions and 93 deletions

View File

@ -11,6 +11,10 @@
scroll-behavior: smooth; scroll-behavior: smooth;
} }
html {
margin-left: calc(100vw - 100%);
}
body { body {
--primary-color: #303F9F; --primary-color: #303F9F;
--text: 17, 17, 17; --text: 17, 17, 17;
@ -132,7 +136,7 @@ input:invalid {
} }
.top-margin { .top-margin {
margin-top: 1em; margin-top: 1.5rem;
} }
.flex { .flex {
@ -870,6 +874,7 @@ form {
margin-top: 4vw; margin-top: 4vw;
margin-bottom: 1rem; margin-bottom: 1rem;
font-weight: 700; font-weight: 700;
font-size: 3rem;
} }
#home_page p { #home_page p {
@ -1223,6 +1228,10 @@ form {
color: #d43125; color: #d43125;
} }
.complaint .processed, .complaint .unprocessed {
margin-bottom: 1.5rem;
}
.complaint button .icon { .complaint button .icon {
padding: 0.2rem; padding: 0.2rem;
margin-right: 0.5rem; margin-right: 0.5rem;
@ -1237,6 +1246,51 @@ form {
gap: 1.5rem; gap: 1.5rem;
} }
#helpline_page sm-select {
margin-bottom: 1.5rem;
}
.complaint-placeholder {
-webkit-animation: pulse infinite 0.6s alternate;
animation: pulse infinite 0.6s alternate;
}
.complaint-placeholder h4, .complaint-placeholder h5 {
border-radius: 0.2rem;
}
.complaint-placeholder h5 {
background: rgba(var(--text), 0.1);
padding: 0.5rem 0.6rem;
}
.complaint-placeholder h4 {
background: rgba(var(--text), 0.2);
padding: 0.8rem 0.8rem;
}
.complaint-placeholder .demo-btn {
padding: 0.8rem 3rem;
}
@-webkit-keyframes pulse {
from {
opacity: 0.4;
}
to {
opacity: 1;
}
}
@keyframes pulse {
from {
opacity: 0.4;
}
to {
opacity: 1;
}
}
@media only screen and (max-width: 640px) { @media only screen and (max-width: 640px) {
sm-select { sm-select {
width: 100%; width: 100%;
@ -1396,19 +1450,20 @@ form {
.complaint .left { .complaint .left {
border-right: 1px solid rgba(var(--text), 0.2); border-right: 1px solid rgba(var(--text), 0.2);
} }
.complaint .left, .complaint .right { .complaint .left {
padding-right: 1.5rem; padding-right: 1.5rem;
} }
.complaint .right { .complaint .right {
display: -webkit-box; display: -webkit-box;
display: -ms-flexbox; display: -ms-flexbox;
display: flex; display: flex;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: center; -webkit-box-pack: center;
-ms-flex-pack: center; -ms-flex-pack: center;
justify-content: center; justify-content: center;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-ms-flex-direction: column;
flex-direction: column;
} }
} }

File diff suppressed because one or more lines are too long

View File

@ -8,6 +8,9 @@
:root{ :root{
scroll-behavior: smooth; scroll-behavior: smooth;
} }
html{
margin-left: calc(100vw - 100%);
}
body{ body{
--primary-color: #303F9F; --primary-color: #303F9F;
--text: 17, 17, 17; --text: 17, 17, 17;
@ -90,7 +93,7 @@ input:invalid{
margin-bottom: 1.5rem; margin-bottom: 1.5rem;
} }
.top-margin{ .top-margin{
margin-top: 1em; margin-top: 1.5rem;
} }
.flex{ .flex{
display: flex; display: flex;
@ -586,6 +589,7 @@ form{
margin-top: 4vw; margin-top: 4vw;
margin-bottom: 1rem; margin-bottom: 1rem;
font-weight: 700; font-weight: 700;
font-size: 3rem;
} }
p{ p{
margin-bottom: 3rem; margin-bottom: 3rem;
@ -852,6 +856,9 @@ form{
.unprocessed{ .unprocessed{
color: #d43125; color: #d43125;
} }
.processed, .unprocessed{
margin-bottom: 1.5rem;
}
button{ button{
.icon{ .icon{
padding: 0.2rem; padding: 0.2rem;
@ -866,6 +873,36 @@ form{
display: grid; display: grid;
gap: 1.5rem; gap: 1.5rem;
} }
#helpline_page{
sm-select{
margin-bottom: 1.5rem;
}
}
.complaint-placeholder{
animation: pulse infinite 0.6s alternate;
h4, h5{
border-radius: 0.2rem;
}
h5{
background: rgba(var(--text), 0.1);
padding: 0.5rem 0.6rem;
}
h4{
background: rgba(var(--text), 0.2);
padding: 0.8rem 0.8rem;
}
.demo-btn{
padding: 0.8rem 3rem;
}
}
@keyframes pulse{
from{
opacity: 0.4;
}
to{
opacity: 1;
}
}
@media only screen and (max-width: 640px){ @media only screen and (max-width: 640px){
sm-select{ sm-select{
width: 100%; width: 100%;
@ -995,13 +1032,13 @@ form{
.left{ .left{
border-right: 1px solid rgba(var(--text), 0.2); border-right: 1px solid rgba(var(--text), 0.2);
} }
.left, .right{ .left{
padding-right: 1.5rem; padding-right: 1.5rem;
} }
.right{ .right{
display: flex; display: flex;
align-items: center;
justify-content: center; justify-content: center;
flex-direction: column;
} }
} }
} }

View File

@ -109,6 +109,9 @@
<button class="primary-btn expand" type="submit" disabled> <button class="primary-btn expand" type="submit" disabled>
transfer transfer
</button> </button>
<svg viewBox="0 0 73 73" class="loader">
<path d="M72.5,36.5c0,19.88-16.12,36-36,36s-36-16.12-36-36s16.12-36,36-36S72.5,16.62,72.5,36.5" />
</svg>
</div> </div>
</div> </div>
<p> <p>
@ -140,6 +143,9 @@
<button class="primary-btn expand" type="submit" disabled> <button class="primary-btn expand" type="submit" disabled>
deposit deposit
</button> </button>
<svg viewBox="0 0 73 73" class="loader">
<path d="M72.5,36.5c0,19.88-16.12,36-36,36s-36-16.12-36-36s16.12-36,36-36S72.5,16.62,72.5,36.5" />
</svg>
</div> </div>
</div> </div>
<p> <p>
@ -166,6 +172,9 @@
<button class="primary-btn expand" type="submit" disabled> <button class="primary-btn expand" type="submit" disabled>
withdraw withdraw
</button> </button>
<svg viewBox="0 0 73 73" class="loader">
<path d="M72.5,36.5c0,19.88-16.12,36-36,36s-36-16.12-36-36s16.12-36,36-36S72.5,16.62,72.5,36.5" />
</svg>
</div> </div>
</div> </div>
<p> <p>
@ -196,6 +205,9 @@
<button class="primary-btn expand" type="submit" disabled> <button class="primary-btn expand" type="submit" disabled>
request request
</button> </button>
<svg viewBox="0 0 73 73" class="loader">
<path d="M72.5,36.5c0,19.88-16.12,36-36,36s-36-16.12-36-36s16.12-36,36-36S72.5,16.62,72.5,36.5" />
</svg>
</div> </div>
</div> </div>
<p> <p>
@ -226,6 +238,9 @@
<button class="primary-btn expand" type="submit" disabled> <button class="primary-btn expand" type="submit" disabled>
pay pay
</button> </button>
<svg viewBox="0 0 73 73" class="loader">
<path d="M72.5,36.5c0,19.88-16.12,36-36,36s-36-16.12-36-36s16.12-36,36-36S72.5,16.62,72.5,36.5" />
</svg>
</div> </div>
</div> </div>
<p> <p>
@ -259,14 +274,14 @@
<line x1="64" y1="64" x2="0" y2="0" /> <line x1="64" y1="64" x2="0" y2="0" />
</svg> </svg>
<h3>Report</h3> <h3>Report</h3>
<button id="report_btn">report</button> <button id="report_btn" class="btn">report</button>
</div> </div>
<h4>Please add any message you want to send us regarding this issue</h4> <h4>Please add any message you want to send us regarding this issue</h4>
<textarea name="complaint" id="complaint_field" rows="6"></textarea> <textarea name="complaint" id="complaint_field" rows="6"></textarea>
</div> </div>
</div> </div>
<header id="main_header" class="flex"> <header id="main_header" class="flex hide-completely">
<svg viewBox="0 0 180 105.7" class="hide-completely"> <svg viewBox="0 0 180 105.7" class="hide-completely">
<path d="M22.5,35.5C27.7,32.6,33.6,31,40,31c19.9,0,36,16.1,36,36c0,16.6-11.2,30.6-26.5,34.7" /> <path d="M22.5,35.5C27.7,32.6,33.6,31,40,31c19.9,0,36,16.1,36,36c0,16.6-11.2,30.6-26.5,34.7" />
<path d="M4,4v63c0,16.2,10.7,30,25.5,34.4" /> <path d="M4,4v63c0,16.2,10.7,30,25.5,34.4" />
@ -296,7 +311,7 @@
</svg> </svg>
</div> </div>
</header> </header>
<nav id="navbar"> <nav id="navbar" class="hide-completely">
<div title="homepage" id="home_page_btn" class="user-option navbar-item active" <div title="homepage" id="home_page_btn" class="user-option navbar-item active"
onclick="showPage(this, 'home_page')"> onclick="showPage(this, 'home_page')">
<svg class="icon" viewBox="0 0 64 64"> <svg class="icon" viewBox="0 0 64 64">
@ -392,9 +407,9 @@
<h5 id="user_type">Cashier</h5> <h5 id="user_type">Cashier</h5>
</div> </div>
</nav> </nav>
<main class=""> <main class="hide-completely">
<section class="page hide-completely" id="home_page"> <section class="page hide-completely" id="home_page">
<h1>What do we offer?</h1> <h1>What DO WE OFFER?</h1>
<p> <p>
Your Rupee balance is digital footprint of actual cash.<br> Deposit, Withdraw or Pay Rupee digitally Your Rupee balance is digital footprint of actual cash.<br> Deposit, Withdraw or Pay Rupee digitally
using a FLO Blockchain address. using a FLO Blockchain address.
@ -670,42 +685,48 @@
<section id="activity_page" class="page"> <section id="activity_page" class="page">
<div class="container-header"> <div class="container-header">
<h2>Activity</h2> <h2>Activity</h2>
<sm-select name="activity type" id="activity_type">
<sm-option value="deposits">Deposits</sm-option>
<sm-option value="withdraws">Withdrawals</sm-option>
<sm-option value="cashierMsg">System notifications</sm-option>
</sm-select>
</div> </div>
<p class="bottom-margin">*If your request isn't completed in 12hrs, use <strong>REPORT</strong> to get <p class="bottom-margin">*If your request isn't completed in 12hrs, use <strong>REPORT</strong> to get
assistance from our helpline.</p> assistance from our helpline.</p>
<div id="deposit_activity_container" class="container activity-container"> <sm-tabs>
<h4>You haven't deposited rupee yet.</h4> <sm-tab slot="tab" active>Deposits</sm-tab>
</div> <sm-panel slot="panel" id="deposit_activity_container" class="container activity-container">
<div id="withdraw_activity_container" class="container activity-container hide-completely"> <h4>You haven't deposited rupee yet.</h4>
<h4>You haven't withdrawn rupee yet.</h4> </sm-panel>
</div> <sm-tab slot="tab">Withdrawals</sm-tab>
<div id="cashier_message_container" class="container activity-container hide-completely"> <sm-panel slot="panel" id="withdraw_activity_container" class="container activity-container">
<h4>No messages from cashier.</h4> <h4>You haven't withdrawn rupee yet.</h4>
</div> </sm-panel>
<sm-tab slot="tab">Pay through cashier</sm-tab>
<sm-panel slot="panel" id="pay_cashier_activity_container" class="container activity-container">
<h4>You haven't paid through cashier yet.</h4>
</sm-panel>
<sm-tab slot="tab">System Notifications</sm-tab>
<sm-panel slot="panel" id="cashier_message_container" class="container activity-container">
<h4>No messages from cashier.</h4>
</sm-panel>
</sm-tabs>
</section> </section>
<section id="helpline_page" class="page hide-completely"> <section id="helpline_page" class="page hide-completely">
<div class="container-header"> <div class="container-header">
<h2>Complaints</h2> <h2>Complaints</h2>
<sm-select name="complaint type" id="complaint_type">
<sm-option value="deposits">Deposits</sm-option>
<sm-option value="withdraws">Withdrawals</sm-option>
<sm-option value="payCashier">Pay through cashier</sm-option>
</sm-select>
</div> </div>
<h5 class="label">Select Cashier</h5> <h5 class="label">Select Cashier</h5>
<sm-select name="cashier" id="select_cashier"></sm-select> <sm-select name="cashier" id="select_cashier"></sm-select>
<div id="deposit_complaints_container" class="complaints-container"></div> <sm-tabs>
<div id="withdraw_complaints_container" class="complaints-container hide-completely"> <sm-tab slot="tab" active>Deposit</sm-tab>
<h4>No withdraw complaints.</h4> <sm-panel slot="panel" id="deposit_complaints_container" class="complaints-container">
</div> <h4>No deposit complaints.</h4>
<div id="pay_cashier_complaints_container" class="complaints-container hide-completely"> </sm-panel>
<h4>No pay through cashier complaints.</h4> <sm-tab slot="tab">Withdraw</sm-tab>
</div> <sm-panel slot="panel" id="withdraw_complaints_container" class="complaints-container hide-completely">
<h4>No withdraw complaints.</h4>
</sm-panel>
<sm-tab slot="tab">Pay through cashier</sm-tab>
<sm-panel slot="panel" id="pay_cashier_complaints_container" class="complaints-container hide-completely">
<h4>No pay through cashier complaints.</h4>
</sm-panel>
</sm-tabs>
</section> </section>
<section id="profile_page" class="page hide-completely"> <section id="profile_page" class="page hide-completely">
<div class="container-header"> <div class="container-header">
@ -866,6 +887,40 @@
</div>`; </div>`;
return card; return card;
}, },
payCashierActivity(vectorClock, receiver, upiTxid, amount) {
let card = document.createElement('div'),
time = parseInt(vectorClock.split('_')[0])
card.classList.add('request')
let timeFrag = time.toString().split(' ');
setAttributes(card, { 'data-vector-clock': vectorClock, 'data-type': 'pay_thru_cashier' })
card.innerHTML = ` <h5 class="time">${formatedTime(time)}</h5>
<h5>UPI Transanction ID</h5>
<div class="copy-row">
<h4 class="copy">${upiTxid}</h4>
<svg class="icon" onclick="copyToClipboard(this.parentNode)" viewBox="0 0 64 64">
<title>Copy</title>
<rect x="16" y="16" width="48" height="48" rx="6" />
<path d="M.5,47.52V6.5a6,6,0,0,1,6-6h41" />
</svg>
</div>
<h5 class="label">Sent to</h5>
<div class="copy-row">
<h4 class="copy">${receiver}</h4>
<svg class="icon" onclick="copyToClipboard(this.parentNode)" viewBox="0 0 64 64">
<title>Copy</title>
<rect x="16" y="16" width="48" height="48" rx="6" />
<path d="M.5,47.52V6.5a6,6,0,0,1,6-6h41" />
</svg>
</div>
<h5>Amount</h5>
<h4 class="withdrawn">₹${amount}</h4>
<div class="flex">
<button class="report secondary-btn">
report
</button>
</div>`;
return card;
},
paymentRequest(time, senderAddress, amount, id) { paymentRequest(time, senderAddress, amount, id) {
let card = document.createElement('div') let card = document.createElement('div')
card.classList.add('request') card.classList.add('request')
@ -931,6 +986,15 @@
composition += ` composition += `
<div class="right"> <div class="right">
<h4 class="processed">Cashier processed the request on ${formatedTime(time)}</h4> <h4 class="processed">Cashier processed the request on ${formatedTime(time)}</h4>
<h5 class="top-margin">Rupee Transaction ID</h5>
<div class="copy-row">
<h4 class="copy">${floTxId}</h4>
<svg class="icon" onclick="copyToClipboard(this.parentNode)" viewBox="0 0 64 64">
<title>Copy</title>
<rect x="16" y="16" width="48" height="48" rx="6" />
<path d="M.5,47.52V6.5a6,6,0,0,1,6-6h41" />
</svg>
</div>
</div>`; </div>`;
} }
else { else {
@ -982,6 +1046,15 @@
composition += ` composition += `
<div class="right"> <div class="right">
<h4 class="processed">Cashier processed the request on ${formatedTime(time)}</h4> <h4 class="processed">Cashier processed the request on ${formatedTime(time)}</h4>
<h5 class="top-margin">Rupee Transaction ID</h5>
<div class="copy-row">
<h4 class="copy">${floTxId}</h4>
<svg class="icon" onclick="copyToClipboard(this.parentNode)" viewBox="0 0 64 64">
<title>Copy</title>
<rect x="16" y="16" width="48" height="48" rx="6" />
<path d="M.5,47.52V6.5a6,6,0,0,1,6-6h41" />
</svg>
</div>
</div>`; </div>`;
} }
else { else {
@ -1002,6 +1075,97 @@
card.innerHTML = composition; card.innerHTML = composition;
return card; return card;
}, },
payCashierComplaint(userData, cashierData, udc) {
let card = document.createElement('div'),
{ amount, customMsg, sender, receiver, vectorClock } = userData,
composition = ``;
card.classList.add('request', 'complaint')
setAttributes(card, { 'data-type': 'withdraw', 'data-udc': JSON.stringify(udc) })
composition = `
<div class="left">
<h5>Sender</h5>
<div class="copy-row">
<h4 class="copy">${sender}</h4>
<svg class="icon" onclick="copyToClipboard(this.parentNode)" viewBox="0 0 64 64">
<title>Copy</title>
<rect x="16" y="16" width="48" height="48" rx="6" />
<path d="M.5,47.52V6.5a6,6,0,0,1,6-6h41" />
</svg>
</div>
<h5>Receiver</h5>
<div class="copy-row">
<h4 class="copy">${receiver}</h4>
<svg class="icon" onclick="copyToClipboard(this.parentNode)" viewBox="0 0 64 64">
<title>Copy</title>
<rect x="16" y="16" width="48" height="48" rx="6" />
<path d="M.5,47.52V6.5a6,6,0,0,1,6-6h41" />
</svg>
</div>`
if (customMsg && customMsg !== '')
composition += ` <h5>Issue description</h5>
<h4>${customMsg}</h4>`
composition += ` <h5>Paid</h5>
<h4>₹${amount}</h4>
<h5>Paid on</h5>
<h4>${formatedTime(vectorClock.split('_')[0])}</h4>
</div>`
if (cashierData.processed) {
let { time, floTxId } = cashierData
composition += `
<div class="right">
<h4 class="processed">Cashier processed the request on ${formatedTime(time)}</h4>
<h5 class="top-margin">Rupee Transaction ID</h5>
<div class="copy-row">
<h4 class="copy">${floTxId}</h4>
<svg class="icon" onclick="copyToClipboard(this.parentNode)" viewBox="0 0 64 64">
<title>Copy</title>
<rect x="16" y="16" width="48" height="48" rx="6" />
<path d="M.5,47.52V6.5a6,6,0,0,1,6-6h41" />
</svg>
</div>
</div>`;
}
else {
composition += `
<div class="right">
<h4 class="unprocessed">Cashier hasn't processed the request</h4>
</div>`;
}
composition += `
<div class="complaint-actions flex">
<button class="mark-resolved">
<svg class="icon" viewBox="0 0 64 64">
<polyline points="0.35 31.82 21.45 52.98 63.65 10.66"/>
</svg>
Mark as Resolved
</button>
</div>`
card.innerHTML = composition;
return card;
},
complaintPlaceholder() {
let card = document.createElement('div');
card.classList.add('request', 'complaint', 'complaint-placeholder')
card.innerHTML = `
<div class="left">
<h5></h5>
<h4></h4>
<h5></h5>
<h4></h4>
<h5></h5>
<h4></h4>
<h5></h5>
<h4></h4>
</div>
<div class="right">
<h4></h4>
</div>
<div class="complaint-actions flex">
<h4 class="demo-btn"></h4>
</div>`
return card;
}
} }
//Checks for internet connection status //Checks for internet connection status
@ -1049,6 +1213,8 @@
if (popup === 'main_loader') { if (popup === 'main_loader') {
loader.classList.add('animate-loader') loader.classList.add('animate-loader')
document.querySelector('main').classList.add('hide-completely') document.querySelector('main').classList.add('hide-completely')
document.querySelector('#main_header').classList.add('hide-completely')
document.querySelector('#navbar').classList.add('hide-completely')
} }
return thisPopup; return thisPopup;
} }
@ -1065,10 +1231,16 @@
clearAllInputs(thisPopup) clearAllInputs(thisPopup)
zIndex--; zIndex--;
thisPopup.parentNode.setAttribute('style', `z-index: ${zIndex}`) thisPopup.parentNode.setAttribute('style', `z-index: ${zIndex}`)
if (thisPopup.querySelector('.action')) {
btnLoading(thisPopup.querySelector('.action'), 'stop')
thisPopup.querySelector("button[type='submit']").disabled = true;
}
}, 400) }, 400)
if (popup === 'main_loader' || popup === 'sign_in_popup') { if (popup === 'main_loader' || popup === 'sign_in_popup') {
loader.classList.remove('animate-loader') loader.classList.remove('animate-loader')
document.querySelector('main').classList.remove('hide-completely') document.querySelector('main').classList.remove('hide-completely')
document.querySelector('#navbar').classList.remove('hide-completely')
document.querySelector('#main_header').classList.remove('hide-completely')
} }
if (popup === 'prompt') { if (popup === 'prompt') {
if (thisPopup.querySelector('input').value == '') if (thisPopup.querySelector('input').value == '')
@ -1085,6 +1257,20 @@
} }
}) })
function btnLoading(btn, option) {
if (typeof btn === 'string')
btn = document.getElementById(btn);
if (option === 'start') {
btn.children[0].classList.add('clip')
btn.children[1].classList.add('animate-loader')
}
else {
btn.children[0].classList.remove('clip')
btn.children[1].classList.remove('animate-loader')
}
}
function setAttributes(el, attrs) { function setAttributes(el, attrs) {
for (var key in attrs) { for (var key in attrs) {
el.setAttribute(key, attrs[key]); el.setAttribute(key, attrs[key]);
@ -1354,15 +1540,8 @@
} }
} }
}) })
document.getElementById('activity_type').addEventListener('change', function (e) {
showActivities(this.value)
})
document.getElementById('complaint_type').addEventListener('change', function (e) {
showComplaints(this.value)
})
document.getElementById('select_cashier').addEventListener('change', async function (e) { document.getElementById('select_cashier').addEventListener('change', async function (e) {
await Promise.all([load_deposit_complaints(this.value), load_withdraw_complaints(this.value)]) await Promise.all([load_deposit_complaints(this.value), load_withdraw_complaints(this.value), load_pay_thru_cashier_complaints(this.value)])
//load_pay_thru_cashier_complaints(this.value)
}) })
document.getElementById('helpline_page').addEventListener('click', async e => { document.getElementById('helpline_page').addEventListener('click', async e => {
@ -1379,38 +1558,6 @@
}) })
let allActivities = document.querySelectorAll('.activity-container')
function showActivities(activity) {
allActivities.forEach(act => {
act.classList.add('hide-completely')
})
if (activity === 'deposits') {
document.getElementById('deposit_activity_container').classList.remove('hide-completely')
}
if (activity === 'withdraws') {
document.getElementById('withdraw_activity_container').classList.remove('hide-completely')
}
if (activity === 'cashierMsg') {
document.getElementById('cashier_message_container').classList.remove('hide-completely')
}
}
let allComplaints = document.querySelectorAll('.complaints-container')
function showComplaints(complaint) {
allComplaints.forEach(comp => {
comp.classList.add('hide-completely')
})
if (complaint === 'deposits') {
document.getElementById('deposit_complaints_container').classList.remove('hide-completely')
}
if (complaint === 'withdraws') {
document.getElementById('withdraw_complaints_container').classList.remove('hide-completely')
}
if (complaint === 'payCashier') {
document.getElementById('pay_cashier_complaints_container').classList.remove('hide-completely')
}
}
let allPages = document.querySelectorAll('.page'), let allPages = document.querySelectorAll('.page'),
allTabs = document.querySelectorAll('.navbar-item'); allTabs = document.querySelectorAll('.navbar-item');
function showPage(btn, page) { function showPage(btn, page) {
@ -1445,6 +1592,8 @@
async function signOut() { async function signOut() {
if (await confirmation('Do you want to sign out?')) { if (await confirmation('Do you want to sign out?')) {
document.querySelector('main').classList.add('hide-completely') document.querySelector('main').classList.add('hide-completely')
document.querySelector('#navbar').classList.add('hide-completely')
document.querySelector('#main_header').classList.add('hide-completely')
floDapps.clearCredentials() floDapps.clearCredentials()
compactIDB.deleteDB().then((message) => { compactIDB.deleteDB().then((message) => {
delete token_app.master_configurations; delete token_app.master_configurations;
@ -1650,6 +1799,293 @@
} }
} }
}) })
// tab-header
const smTabs = document.createElement('template')
smTabs.innerHTML = `
<style>
*{
padding: 0;
margin: 0;
box-sizing: border-box;
}
:host{
display: flex;
}
.tabs{
display: flex;
flex-direction: column;
position: relative;
width: 100%;
}
.tab-header{
position: relative;
overflow: auto hidden;
max-width: 100%;
border-bottom: solid 1px rgba(var(--text), .2);
scrollbar-width: 0;
margin-bottom: 1rem;
}
.tab-header::-webkit-scrollbar-track {
-webkit-box-shadow: none !important;
background-color: transparent !important;
}
.tab-header::-webkit-scrollbar {
height: 0;
background-color: transparent;
}
.indicator{
position: absolute;
left: 0;
bottom: 0;
height: 0.12rem;
background: var(--primary-color);
}
slot[name="tab"]{
display: grid;
grid-auto-flow: column;
gap: 1rem;
grid-auto-columns: max-content;
}
:host([type="tab"]) .indicator{
height: 100%;
border-radius: 0.2rem
}
:host([type="tab"]) .tab-header{
border-bottom: none;
}
:host([type="tab"]) slot[name="tab"]{
border-radius: 0.2rem;
grid-auto-columns: 1fr;
border-bottom: none;
}
.transition{
transition: transform 0.3s cubic-bezier(0.785, 0.135, 0.15, 0.86), width 0.4s;
}
.hide-completely{
display: none;
}
:host([type="tab"]) slot[name="tab"]::slotted(.active){
color: rgba(var(--foreground), 1);
}
slot[name="tab"]::slotted(.active){
color: var(--primary-color);
}
</style>
<div class="tabs">
<div class="tab-header">
<slot name="tab">Nothing to see here</slot>
<div class="indicator"></div>
</div>
<slot name="panel">Nothing to see here</slot>
</div>
`;
customElements.define('sm-tabs', class extends HTMLElement {
constructor() {
super()
this.attachShadow({ mode: 'open' }).append(smTabs.content.cloneNode(true))
this.indicator = this.shadowRoot.querySelector('.indicator');
this.tabSlot = this.shadowRoot.querySelector('slot[name="tab"]');
this.panelSlot = this.shadowRoot.querySelector('slot[name="panel"]');
this.tabHeader = this.shadowRoot.querySelector('.tab-header');
}
connectedCallback() {
//animations
let flyInLeft = [
{
opacity: 0,
transform: 'translateX(-1rem)'
},
{
opacity: 1,
transform: 'none'
}
];
let flyInRight = [
{
opacity: 0,
transform: 'translateX(1rem)'
},
{
opacity: 1,
transform: 'none'
}
]
let flyOutLeft = [
{
opacity: 1,
transform: 'none'
},
{
opacity: 0,
transform: 'translateX(-1rem)'
}
]
let flyOutRight = [
{
opacity: 1,
transform: 'none'
},
{
opacity: 0,
transform: 'translateX(1rem)'
}
]
let animationOptions = {
duration: 300,
fill: 'forwards',
easing: 'ease'
}
this.prevTab
this.shadowRoot.querySelector('slot[name="panel"]').addEventListener('slotchange', () => {
this.shadowRoot.querySelector('slot[name="panel"]').assignedElements().forEach((panel, index) => {
panel.classList.add('hide-completely')
})
})
this.shadowRoot.querySelector('slot[name="tab"]').addEventListener('slotchange', () => {
this.shadowRoot.querySelector('slot[name="tab"]').assignedElements().forEach((panel, index) => {
panel.setAttribute('rank', index + 1)
})
})
this.addEventListener('switchTab', e => {
if (e.target === this.prevTab)
return
if (this.prevTab)
this.prevTab.classList.remove('active')
e.target.classList.add('active')
if (this.prevTab) {
let targetBody = e.target.nextElementSibling,
currentBody = this.prevTab.nextElementSibling;
if (this.prevTab.getAttribute('rank') < e.target.getAttribute('rank')) {
if (currentBody && !targetBody)
currentBody.animate(flyOutLeft, animationOptions).onfinish = () => {
currentBody.classList.add('hide-completely')
}
else if (targetBody && !currentBody) {
targetBody.classList.remove('hide-completely')
targetBody.animate(flyInRight, animationOptions)
}
else if (currentBody && targetBody) {
currentBody.animate(flyOutLeft, animationOptions).onfinish = () => {
currentBody.classList.add('hide-completely')
targetBody.classList.remove('hide-completely')
targetBody.animate(flyInRight, animationOptions)
}
}
} else {
if (currentBody && !targetBody)
currentBody.animate(flyOutRight, animationOptions).onfinish = () => {
currentBody.classList.add('hide-completely')
}
else if (targetBody && !currentBody) {
targetBody.classList.remove('hide-completely')
targetBody.animate(flyInLeft, animationOptions)
}
else if (currentBody && targetBody) {
currentBody.animate(flyOutRight, animationOptions).onfinish = () => {
currentBody.classList.add('hide-completely')
targetBody.classList.remove('hide-completely')
targetBody.animate(flyInLeft, animationOptions)
}
}
}
} else {
e.target.nextElementSibling.classList.remove('hide-completely')
}
e.target.scrollIntoView({ behavior: 'smooth', inline: 'center', block: 'nearest' })
this.indicator.setAttribute('style', `width: ${e.target.getBoundingClientRect().width / 2}px; transform: translateX(${e.target.getBoundingClientRect().left - e.target.parentNode.getBoundingClientRect().left + this.tabHeader.scrollLeft + e.target.getBoundingClientRect().width / 4}px)`)
this.prevTab = e.target;
})
let observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
let activeElement = this.tabSlot.assignedElements().filter(element => {
if (element.classList.contains('active'))
return true
})
if (activeElement.length) {
let tabDimensions = activeElement[0].getBoundingClientRect();
this.indicator.setAttribute('style', `width: ${tabDimensions.width / 2}px; transform: translateX(${tabDimensions.left - activeElement[0].parentNode.getBoundingClientRect().left + this.tabHeader.scrollLeft + tabDimensions.width / 4}px)`)
}
else {
this.tabSlot.assignedElements()[0].classList.add('active')
let tabDimensions = this.tabSlot.assignedElements()[0].getBoundingClientRect();
this.indicator.setAttribute('style', `width: ${tabDimensions.width / 2}px; transform: translateX(${tabDimensions.left - this.tabSlot.assignedElements()[0].parentNode.getBoundingClientRect().left + this.tabHeader.scrollLeft + tabDimensions.width / 4}px)`)
this.prevTab = this.tabSlot.assignedElements()[0];
}
setTimeout(() => {
this.indicator.classList.add('transition')
}, 100);
}
})
},
{ threshold: 1.0 })
observer.observe(this.tabHeader)
}
})
// tab
const smTab = document.createElement('template')
smTab.innerHTML = `
<style>
*{
padding: 0;
margin: 0;
box-sizing: border-box;
}
:host{
display: inline-flex;
z-index: 1;
}
.tab{
user-select: none;
justify-content: center;
cursor: pointer;
-webkit-tap-highlight-color: transparent;
white-space: nowrap;
font-size: 0.9rem;
padding: 0.4rem 0;
font-weight: 600;
letter-spacing: 0.06em;
word-spacing: 0.1em;
text-align: center;
transition: color 0.3s;
text-transform: uppercase;
font-family: var(--font-family);
}
</style>
<div class="tab">
<slot></slot>
</div>
`;
customElements.define('sm-tab', class extends HTMLElement {
constructor() {
super()
this.shadow = this.attachShadow({ mode: 'open' }).append(smTab.content.cloneNode(true))
}
connectedCallback() {
let switchTab = new CustomEvent('switchTab', {
bubbles: true,
composed: true,
})
this.addEventListener('click', () => {
this.dispatchEvent(switchTab)
})
if (this.hasAttribute('active')) {
setTimeout(() => {
this.dispatchEvent(switchTab)
}, 0)
}
}
})
</script> </script>
<script id="init_lib"> <script id="init_lib">
@ -11058,8 +11494,7 @@
for (cashier in token_app.master_configurations.cashiers) for (cashier in token_app.master_configurations.cashiers)
cashierList += `<sm-option value = "${cashier}">${cashier}</sm-option>` cashierList += `<sm-option value = "${cashier}">${cashier}</sm-option>`
cashierSelect.innerHTML = cashierList; cashierSelect.innerHTML = cashierList;
await Promise.all([load_deposit_complaints(cashierSelect.value), load_withdraw_complaints(cashierSelect.value)]) await Promise.all([load_deposit_complaints(cashierSelect.value), load_withdraw_complaints(cashierSelect.value), load_pay_thru_cashier_complaints(cashierSelect.value)])
//load_pay_thru_cashier_complaints(cashierSelect.value)
userType.textContent = 'Helpline' userType.textContent = 'Helpline'
showPage(document.getElementById('helpline_page_btn'), 'helpline_page') showPage(document.getElementById('helpline_page_btn'), 'helpline_page')
} }
@ -11347,6 +11782,7 @@
try { try {
const send_tokens_btn = document.getElementById('send_tokens_btn'); const send_tokens_btn = document.getElementById('send_tokens_btn');
send_tokens_btn.onclick = async function () { send_tokens_btn.onclick = async function () {
btnLoading('send_tokens_btn', 'start')
const token_sender = myFloID; const token_sender = myFloID;
const token_receiver = document.getElementById('token_receiver').value; const token_receiver = document.getElementById('token_receiver').value;
const token_amount = Number(document.getElementById('token_amount').value); const token_amount = Number(document.getElementById('token_amount').value);
@ -11361,6 +11797,7 @@
|| sender_token_balance.balance < token_amount) { || sender_token_balance.balance < token_amount) {
err_msg = `Sender has insufficient ${token_name_radio} balance.`; err_msg = `Sender has insufficient ${token_name_radio} balance.`;
notify(err_msg, 'error'); notify(err_msg, 'error');
hidePopup()
return false; return false;
} }
@ -11368,6 +11805,7 @@
console.log(flo_txid); console.log(flo_txid);
if (typeof flo_txid !== "string") { if (typeof flo_txid !== "string") {
notify(`Transaction unsuccessful.`); notify(`Transaction unsuccessful.`);
hidePopup()
return false; return false;
} else { } else {
notify(`Transaction successful: ${flo_txid}.`, '', 'fixed', true); notify(`Transaction successful: ${flo_txid}.`, '', 'fixed', true);
@ -11375,6 +11813,7 @@
payment_request_status(currentPaymentRequest.requestId, 'APPROVED') payment_request_status(currentPaymentRequest.requestId, 'APPROVED')
showPayRequests() showPayRequests()
} }
hidePopup()
return true; return true;
} }
} }
@ -11513,6 +11952,7 @@
const request_tokens_btn = document.getElementById('request_tokens_btn'); const request_tokens_btn = document.getElementById('request_tokens_btn');
const cashier = randomNoRepeats(Object.keys(token_app.master_configurations.cashiers))(); const cashier = randomNoRepeats(Object.keys(token_app.master_configurations.cashiers))();
request_tokens_btn.onclick = async function () { request_tokens_btn.onclick = async function () {
btnLoading('request_tokens_btn', 'start')
if (typeof myFloID !== "string" || myFloID.length < 1) return; if (typeof myFloID !== "string" || myFloID.length < 1) return;
const token_amount_to_buy = Number(document.getElementById('token_amount_to_buy').value); const token_amount_to_buy = Number(document.getElementById('token_amount_to_buy').value);
const cashier_upi = token_app.master_configurations.cashiers[cashier].upi_id; const cashier_upi = token_app.master_configurations.cashiers[cashier].upi_id;
@ -11528,10 +11968,12 @@
{ receiverID: cashier }).then(result => { { receiverID: cashier }).then(result => {
console.log(result); console.log(result);
notify('Request sent successfully.'); notify('Request sent successfully.');
hidePopup()
}) })
return true; return true;
} }
notify('Request failed.', 'error'); notify('Request failed.', 'error');
hidePopup()
return false; return false;
} }
} catch (error) { } catch (error) {
@ -11544,7 +11986,7 @@
const withdraw_cash_btn = document.getElementById('withdraw_cash_btn') const withdraw_cash_btn = document.getElementById('withdraw_cash_btn')
const cashier = randomNoRepeats(Object.keys(token_app.master_configurations.cashiers))(); const cashier = randomNoRepeats(Object.keys(token_app.master_configurations.cashiers))();
withdraw_cash_btn.onclick = async function () { withdraw_cash_btn.onclick = async function () {
// TODO: Disble the button btnLoading('withdraw_cash_btn', 'start')
if (typeof myFloID !== "string" || myFloID.length < 1) { if (typeof myFloID !== "string" || myFloID.length < 1) {
notify(`Your Flo Id is invalid.`, 'error'); notify(`Your Flo Id is invalid.`, 'error');
return; return;
@ -11563,11 +12005,13 @@
const user_token_balance_object = await ajaxGet(token_balance_url); const user_token_balance_object = await ajaxGet(token_balance_url);
if (typeof user_token_balance_object !== "object" || typeof user_token_balance_object.balance !== "number") { if (typeof user_token_balance_object !== "object" || typeof user_token_balance_object.balance !== "number") {
notify('You do not have rupee balance yet. Please deposit rupee to get balance.', 'error'); notify('You do not have rupee balance yet. Please deposit rupee to get balance.', 'error');
hidePopup()
return; return;
} }
const current_balance = Number(user_token_balance_object.balance); const current_balance = Number(user_token_balance_object.balance);
if (current_balance < withdraw_cash_amount) { if (current_balance < withdraw_cash_amount) {
notify(`You have unsufficient balance to withdraw. You can withdraw only Rs ${user_token_balance_object.balance}`, 'error', 'fixed', true); notify(`You have unsufficient balance to withdraw. You can withdraw only Rs ${user_token_balance_object.balance}`, 'error', 'fixed', true);
hidePopup()
return; return;
} }
@ -11576,6 +12020,7 @@
console.log(flo_txid); console.log(flo_txid);
if (typeof flo_txid !== "string") { if (typeof flo_txid !== "string") {
notify(`Transaction unsuccessfull.`, 'error'); notify(`Transaction unsuccessfull.`, 'error');
hidePopup()
return false; return false;
} }
// TODO: Validate https://ranchimallflo.duckdns.org/api/v1.0/getTransactionDetails/${flo_txid} // TODO: Validate https://ranchimallflo.duckdns.org/api/v1.0/getTransactionDetails/${flo_txid}
@ -11592,6 +12037,7 @@
floCloudAPI.sendGeneralData(request_object, token_app.master_configurations.TYPE_WITHDRAWS, floCloudAPI.sendGeneralData(request_object, token_app.master_configurations.TYPE_WITHDRAWS,
{ receiverID: cashier }); { receiverID: cashier });
notify('Withdraw request sent successfully.'); notify('Withdraw request sent successfully.');
hidePopup()
return true; return true;
} }
notify('Failed to send withdraw request to cashier.', 'error'); notify('Failed to send withdraw request to cashier.', 'error');
@ -11614,6 +12060,7 @@
const cnf_cash_payment_btn = document.getElementById('cnf_cash_payment') const cnf_cash_payment_btn = document.getElementById('cnf_cash_payment')
cnf_cash_payment_btn.onclick = function () { cnf_cash_payment_btn.onclick = function () {
btnLoading('withdraw_cash_btn', 'start')
const paying_amount_div = document.getElementById('recvr_amount_to_pay') const paying_amount_div = document.getElementById('recvr_amount_to_pay')
const recvr_id = document.getElementById('recvr_id').value; const recvr_id = document.getElementById('recvr_id').value;
let amount_to_pay = Number(paying_amount_div.value); let amount_to_pay = Number(paying_amount_div.value);
@ -11632,8 +12079,9 @@
floCloudAPI.sendGeneralData(req_object, token_app.master_configurations.TYPE_PAY_THROUGH_CASHIER, floCloudAPI.sendGeneralData(req_object, token_app.master_configurations.TYPE_PAY_THROUGH_CASHIER,
{ receiverID: cashier, senderIDs: [myFloID] }); { receiverID: cashier, senderIDs: [myFloID] });
notify(`You have sent cash to ${recvr_id} through our cashier service (${cashier_upi}). notify(`You have sent cash to <span class="breakable">${recvr_id}</span> through our cashier service (${cashier_upi}).
Once the cashier receives your payment, he will transfer it to ${recvr_id}.`, '', 'fixed', true); Once the cashier receives your payment, he will transfer it to ${recvr_id}.`, '', 'fixed', true);
hidePopup()
} }
} catch (error) { } catch (error) {
@ -11643,6 +12091,7 @@
const depositActivityContainer = document.getElementById('deposit_activity_container'), const depositActivityContainer = document.getElementById('deposit_activity_container'),
withdrawActivityContainer = document.getElementById('withdraw_activity_container'), withdrawActivityContainer = document.getElementById('withdraw_activity_container'),
payCashierActivityContainer = document.getElementById('pay_cashier_activity_container'),
cashierMessageContainer = document.getElementById('cashier_message_container') cashierMessageContainer = document.getElementById('cashier_message_container')
async function show_user_activities() { async function show_user_activities() {
@ -11654,7 +12103,7 @@
const user_msg = floDapps.getNextGeneralData(token_app.master_configurations.TYPE_MSGES, "0").reverse(); const user_msg = floDapps.getNextGeneralData(token_app.master_configurations.TYPE_MSGES, "0").reverse();
const pay_thru_cashier_msg = floDapps.getNextGeneralData(token_app.master_configurations.TYPE_PAY_THROUGH_CASHIER, "0").reverse(); const pay_thru_cashier_msg = floDapps.getNextGeneralData(token_app.master_configurations.TYPE_PAY_THROUGH_CASHIER, "0").reverse();
console.log(deposit_msg, withdraw_msg, user_msg, pay_thru_cashier_msg) console.log(deposit_msg, withdraw_msg, user_msg)
depositActivityContainer.innerHTML = ``; depositActivityContainer.innerHTML = ``;
for (usr_deposits of deposit_msg) { for (usr_deposits of deposit_msg) {
let { upi_txid, deposit_amount } = usr_deposits.message; let { upi_txid, deposit_amount } = usr_deposits.message;
@ -11671,6 +12120,15 @@
withdrawActivityContainer.append(frag) withdrawActivityContainer.append(frag)
payCashierActivityContainer.innerHTML = ``;
for (cashier_message of pay_thru_cashier_msg) {
console.log(cashier_message)
let { receiver_flo_id, upi_txid, amount } = cashier_message.message;
frag.append(render.payCashierActivity(cashier_message.vectorClock, receiver_flo_id, upi_txid, amount))
}
payCashierActivityContainer.append(frag)
cashierMessageContainer.innerHTML = ``; cashierMessageContainer.innerHTML = ``;
for (msg of user_msg) { for (msg of user_msg) {
frag.append(render.cashierMessage(msg)) frag.append(render.cashierMessage(msg))
@ -11705,7 +12163,7 @@
notify('Your complaint has been registered. Our team will take an action very soon. Thank you.', notify('Your complaint has been registered. Our team will take an action very soon. Thank you.',
'', 'fixed', true); '', 'fixed', true);
hidePopup()
return true; return true;
} catch (e) { } catch (e) {
@ -11806,7 +12264,10 @@
async function load_deposit_complaints(receiver = '', sendersArray = []) { async function load_deposit_complaints(receiver = '', sendersArray = []) {
try { try {
let container = document.getElementById('deposit_complaints_container');
container.innerHTML = ``;
container.append(render.complaintPlaceholder())
await clearCashierData(); await clearCashierData();
await Promise.all([ await Promise.all([
@ -11881,8 +12342,6 @@
} }
} }
let container = document.getElementById('deposit_complaints_container');
container.innerHTML = `` container.innerHTML = ``
if (frag.children.length) if (frag.children.length)
container.append(frag) container.append(frag)
@ -11902,6 +12361,9 @@
async function load_withdraw_complaints(receiver = '', sender = []) { async function load_withdraw_complaints(receiver = '', sender = []) {
try { try {
let container = document.getElementById('withdraw_complaints_container');
container.innerHTML = ``;
container.append(render.complaintPlaceholder())
await clearCashierData(); await clearCashierData();
@ -11970,8 +12432,6 @@
frag.append(render.withdrawComplaint(userData, cashierData, udc)) frag.append(render.withdrawComplaint(userData, cashierData, udc))
} }
let container = document.getElementById('withdraw_complaints_container');
container.innerHTML = `` container.innerHTML = ``
if (frag.children.length) if (frag.children.length)
container.append(frag) container.append(frag)
@ -11992,6 +12452,9 @@
async function load_pay_thru_cashier_complaints(receiver = '', sender = []) { async function load_pay_thru_cashier_complaints(receiver = '', sender = []) {
try { try {
let container = document.getElementById('pay_cashier_complaints_container');
container.innerHTML = ``;
container.append(render.complaintPlaceholder())
await clearCashierData(); await clearCashierData();
@ -12024,7 +12487,7 @@
let this_user_pay_thru_cashier = all_users_pay_thru_cashier.filter(f => f.vectorClock === udc.message.order_vc); let this_user_pay_thru_cashier = all_users_pay_thru_cashier.filter(f => f.vectorClock === udc.message.order_vc);
console.log(this_user_pay_thru_cashier); console.log(this_user_pay_thru_cashier);
let this_user_pay_thru_cashier_upi_txid_list = this_user_withdraws.map(m => m.message.upi_txid); let this_user_pay_thru_cashier_upi_txid_list = this_user_pay_thru_cashier.map(m => m.message.upi_txid);
let all_cashier_processed_pay_thru_cashier = floDapps let all_cashier_processed_pay_thru_cashier = floDapps
.getNextGeneralData(token_app.master_configurations.TYPE_PROCESSED_WITHDRAWS, "0"); .getNextGeneralData(token_app.master_configurations.TYPE_PROCESSED_WITHDRAWS, "0");
@ -12032,8 +12495,40 @@
let this_cashier_processed_user_pay_thru_cashier = all_cashier_processed_pay_thru_cashier let this_cashier_processed_user_pay_thru_cashier = all_cashier_processed_pay_thru_cashier
.filter(f => this_user_pay_thru_cashier_upi_txid_list.includes(f.message.upi_txid)); .filter(f => this_user_pay_thru_cashier_upi_txid_list.includes(f.message.upi_txid));
console.log(this_cashier_processed_user_pay_thru_cashier); console.log(this_cashier_processed_user_pay_thru_cashier);
}
if (this_user_pay_thru_cashier.length) {
let { amount, upi_txid, receiver_flo_id } = this_user_pay_thru_cashier[0].message;
userData = {
amount: amount,
upiTxId: upi_txid,
sender: this_user_pay_thru_cashier[0].sender,
receiver: receiver_flo_id,
vectorClock: this_user_pay_thru_cashier[0].vectorClock,
customMsg: udc.message.custom_msg
}
}
if (this_cashier_processed_user_pay_thru_cashier.length) {
let { datetime, flo_txid } = this_cashier_processed_user_pay_thru_cashier[0].message;
cashierData = {
processed: true,
time: datetime,
floTxId: flo_txid
}
}
else {
cashierData = {
processed: false
}
}
if (this_user_pay_thru_cashier.length)
frag.append(render.payCashierComplaint(userData, cashierData, udc))
}
container.innerHTML = ``
if (frag.children.length)
container.append(frag)
else
container.innerHTML = `<h4>No complaints available.</h4>`
} catch (e) { } catch (e) {
console.error(e); console.error(e);
notify(e); notify(e);