Cashier side feature and UI update
-- added option to decline top-up requests by cashier with option to specify reason -- separated pending and processed requests
This commit is contained in:
parent
357c7d35f8
commit
a465104276
@ -229,6 +229,11 @@ sm-form {
|
|||||||
|
|
||||||
sm-select {
|
sm-select {
|
||||||
--padding: 0.8rem;
|
--padding: 0.8rem;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
sm-option {
|
||||||
|
font-size: 0.9rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
strip-select {
|
strip-select {
|
||||||
@ -308,6 +313,10 @@ ul {
|
|||||||
grid-auto-flow: column;
|
grid-auto-flow: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.gap-0-3 {
|
||||||
|
gap: 0.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
.gap-0-5 {
|
.gap-0-5 {
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|||||||
2
css/main.min.css
vendored
2
css/main.min.css
vendored
File diff suppressed because one or more lines are too long
@ -210,6 +210,10 @@ sm-form {
|
|||||||
}
|
}
|
||||||
sm-select {
|
sm-select {
|
||||||
--padding: 0.8rem;
|
--padding: 0.8rem;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
sm-option {
|
||||||
|
font-size: 0.9rem;
|
||||||
}
|
}
|
||||||
strip-select {
|
strip-select {
|
||||||
--gap: 0;
|
--gap: 0;
|
||||||
@ -275,6 +279,9 @@ ul {
|
|||||||
grid-auto-flow: column;
|
grid-auto-flow: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.gap-0-3 {
|
||||||
|
gap: 0.3rem;
|
||||||
|
}
|
||||||
.gap-0-5 {
|
.gap-0-5 {
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|||||||
79
index.html
79
index.html
@ -301,10 +301,26 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<section id="cashier" class=" grid gap-1 hide admin-element">
|
<section id="cashier" class=" grid gap-1 hide admin-element">
|
||||||
<h4>Requests</h4>
|
<div class="flex align-center space-between">
|
||||||
<ul id="cashier_request_list" class="observe-empty-state"></ul>
|
<h4>Requests</h4>
|
||||||
<div class="empty-state">
|
<strip-select id="cashier_requests_selector">
|
||||||
<p>No requests to process</p>
|
<strip-option value="pending" selected>Pending</strip-option>
|
||||||
|
<strip-option value="processed">Processed</strip-option>
|
||||||
|
</strip-select>
|
||||||
|
</div>
|
||||||
|
<div id="cashier_requests_wrapper">
|
||||||
|
<div>
|
||||||
|
<ul id="cashier_pending_request_list" class="observe-empty-state"></ul>
|
||||||
|
<div class="empty-state">
|
||||||
|
<p>No requests to process</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="hide">
|
||||||
|
<ul id="cashier_processed_request_list" class="observe-empty-state"></ul>
|
||||||
|
<div class="empty-state">
|
||||||
|
<p>No requests to process</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</section>
|
</section>
|
||||||
@ -350,10 +366,11 @@
|
|||||||
</div>
|
</div>
|
||||||
<div id="wallet_history_wrapper" class="grid gap-1-5">
|
<div id="wallet_history_wrapper" class="grid gap-1-5">
|
||||||
<div class="hide grid gap-1">
|
<div class="hide grid gap-1">
|
||||||
<p>Pending transactions</p>
|
<h4>Pending</h4>
|
||||||
<ul id="pending_wallet_transactions" class="grid gap-0-5"></ul>
|
<ul id="pending_wallet_transactions" class="grid gap-0-5"></ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid gap-1">
|
<div class="grid gap-1">
|
||||||
|
<h4>Processed</h4>
|
||||||
<ul id="wallet_history" class="observe-empty-state grid gap-0-5"></ul>
|
<ul id="wallet_history" class="observe-empty-state grid gap-0-5"></ul>
|
||||||
<div class=" empty-state gap-1 justify-center text-center">
|
<div class=" empty-state gap-1 justify-center text-center">
|
||||||
<p>No transactions</p>
|
<p>No transactions</p>
|
||||||
@ -493,7 +510,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a id="transaction__link" target="_blank" rel="noopener noreferrer">See transaction on blockchain</a>
|
<a id="transaction__link" target="_blank" rel="noopener noreferrer">See transaction on blockchain</a>
|
||||||
<div id="transaction__note" class="hide flex align-center"></div>
|
<p id="transaction__note" class="hide flex flex-direction-column gap-0-5"></p>
|
||||||
</div>
|
</div>
|
||||||
<section id="settings" class="inner-page hide">
|
<section id="settings" class="inner-page hide">
|
||||||
<div class="page__header">
|
<div class="page__header">
|
||||||
@ -834,7 +851,10 @@
|
|||||||
</div>
|
</div>
|
||||||
<sm-copy id="topup_wallet__upi_id" style="font-weight: 700;"></sm-copy>
|
<sm-copy id="topup_wallet__upi_id" style="font-weight: 700;"></sm-copy>
|
||||||
</div>
|
</div>
|
||||||
<p>After sending money, please enter the transaction ID of completed transaction.</p>
|
<p>
|
||||||
|
After sending money, please enter the transaction ID of completed transaction. <br>
|
||||||
|
* <strong>PhonePe</strong> users please enter <strong>UTR ID</strong> instead of transaction ID
|
||||||
|
</p>
|
||||||
<sm-input id="topup_wallet__txid" minlength="12" maxlength="12"
|
<sm-input id="topup_wallet__txid" minlength="12" maxlength="12"
|
||||||
error-text="Please enter UPI transaction ID of money you sent to continue."
|
error-text="Please enter UPI transaction ID of money you sent to continue."
|
||||||
placeholder="UPI transaction ID" autofocus animate required></sm-input>
|
placeholder="UPI transaction ID" autofocus animate required></sm-input>
|
||||||
@ -1039,6 +1059,51 @@
|
|||||||
</div>
|
</div>
|
||||||
</sm-popup>
|
</sm-popup>
|
||||||
|
|
||||||
|
<!-- Cashier popups -->
|
||||||
|
<sm-popup id="confirm_topup_popup">
|
||||||
|
<header slot="header" class="popup__header">
|
||||||
|
<button class="popup__header__close justify-self-start" onclick="hidePopup()">
|
||||||
|
<svg class="icon" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px"
|
||||||
|
fill="#000000">
|
||||||
|
<path d="M0 0h24v24H0V0z" fill="none" />
|
||||||
|
<path
|
||||||
|
d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<h4>Confirm top-up</h4>
|
||||||
|
</header>
|
||||||
|
<div id="confirm_topup_wrapper">
|
||||||
|
<div class="grid gap-1-5">
|
||||||
|
<div class="grid gap-0-5">
|
||||||
|
<p>
|
||||||
|
Check if you have received UPI payment
|
||||||
|
</p>
|
||||||
|
<b id="top_up_amount" style="font-size: 1.5rem;"></b>
|
||||||
|
<sm-copy id="top_up_txid"></sm-copy>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-right gap-0-3">
|
||||||
|
<button class="button" onclick="showChildElement('confirm_topup_wrapper', 1)">Decline</button>
|
||||||
|
<button class="button" onclick="confirmTopUp()">Confirm</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-direction-column gap-1-5 hide" style="height: 24rem;">
|
||||||
|
<div class="grid gap-0-3">
|
||||||
|
<p>Select reason</p>
|
||||||
|
<sm-select id="top_up__reason_selector">
|
||||||
|
<sm-option value="1001">Invalid transaction ID</sm-option>
|
||||||
|
<sm-option value="1002">Amount doesn't match</sm-option>
|
||||||
|
<sm-option value="other">Other</sm-option>
|
||||||
|
</sm-select>
|
||||||
|
</div>
|
||||||
|
<div class="grid gap-0-3 hide">
|
||||||
|
<p>Describe reason</p>
|
||||||
|
<sm-textarea id="top_up__specified_reason" rows="4"></sm-textarea>
|
||||||
|
</div>
|
||||||
|
<button class="button justify-right" style="margin-top: auto;" onclick="declineTopUp()">Decline</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</sm-popup>
|
||||||
|
|
||||||
<!-- templates -->
|
<!-- templates -->
|
||||||
<template id="saved_id_template">
|
<template id="saved_id_template">
|
||||||
<li class="saved-id grid interact" tabindex="0">
|
<li class="saved-id grid interact" tabindex="0">
|
||||||
|
|||||||
@ -687,6 +687,204 @@ customElements.define('sm-input',
|
|||||||
this.clearBtn.removeEventListener('click', this.clear);
|
this.clearBtn.removeEventListener('click', this.clear);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const smTextarea = document.createElement('template')
|
||||||
|
smTextarea.innerHTML = `
|
||||||
|
<style>
|
||||||
|
*,
|
||||||
|
*::before,
|
||||||
|
*::after {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
-webkit-box-sizing: border-box;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
::-moz-focus-inner{
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
.hide{
|
||||||
|
opacity: 0 !important;
|
||||||
|
}
|
||||||
|
:host{
|
||||||
|
display: grid;
|
||||||
|
--danger-color: red;
|
||||||
|
--border-radius: 0.3rem;
|
||||||
|
--background: rgba(var(--text-color,(17,17,17)), 0.06);
|
||||||
|
--padding: initial;
|
||||||
|
--max-height: 8rem;
|
||||||
|
}
|
||||||
|
:host([variant="outlined"]) .textarea {
|
||||||
|
box-shadow: 0 0 0 0.1rem rgba(var(--text-color,(17,17,17)), 0.4) inset;
|
||||||
|
background: rgba(var(--background-color,(255,255,255)), 1);
|
||||||
|
}
|
||||||
|
.textarea{
|
||||||
|
display: grid;
|
||||||
|
position: relative;
|
||||||
|
cursor: text;
|
||||||
|
min-width: 0;
|
||||||
|
text-align: left;
|
||||||
|
overflow: hidden auto;
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
align-items: stretch;
|
||||||
|
max-height: var(--max-height);
|
||||||
|
background: var(--background);
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
padding: var(--padding);
|
||||||
|
}
|
||||||
|
.textarea::after,
|
||||||
|
textarea{
|
||||||
|
padding: 0.7rem 1rem;
|
||||||
|
width: 100%;
|
||||||
|
min-width: 1em;
|
||||||
|
font: inherit;
|
||||||
|
color: inherit;
|
||||||
|
resize: none;
|
||||||
|
grid-area: 2/1;
|
||||||
|
justify-self: stretch;
|
||||||
|
background: none;
|
||||||
|
appearance: none;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
line-height: 1.5;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.textarea::after{
|
||||||
|
content: attr(data-value) ' ';
|
||||||
|
visibility: hidden;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
word-wrap: break-word;
|
||||||
|
hyphens: auto;
|
||||||
|
}
|
||||||
|
.readonly{
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
.textarea:focus-within:not(.readonly){
|
||||||
|
box-shadow: 0 0 0 0.1rem var(--accent-color,teal) inset;
|
||||||
|
}
|
||||||
|
.placeholder{
|
||||||
|
position: absolute;
|
||||||
|
margin: 0.7rem 1rem;
|
||||||
|
opacity: .7;
|
||||||
|
font-weight: inherit;
|
||||||
|
font-size: inherit;
|
||||||
|
line-height: 1.5;
|
||||||
|
pointer-events: none;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
:host([disabled]) .textarea{
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
@media (any-hover: hover){
|
||||||
|
::-webkit-scrollbar{
|
||||||
|
width: 0.5rem;
|
||||||
|
height: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb{
|
||||||
|
background: rgba(var(--text-color,(17,17,17)), 0.3);
|
||||||
|
border-radius: 1rem;
|
||||||
|
&:hover{
|
||||||
|
background: rgba(var(--text-color,(17,17,17)), 0.5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<label class="textarea" part="textarea">
|
||||||
|
<span class="placeholder"></span>
|
||||||
|
<textarea rows="1"></textarea>
|
||||||
|
</label>
|
||||||
|
`;
|
||||||
|
customElements.define('sm-textarea',
|
||||||
|
class extends HTMLElement {
|
||||||
|
constructor() {
|
||||||
|
super()
|
||||||
|
this.attachShadow({
|
||||||
|
mode: 'open'
|
||||||
|
}).append(smTextarea.content.cloneNode(true))
|
||||||
|
|
||||||
|
this.textarea = this.shadowRoot.querySelector('textarea')
|
||||||
|
this.textareaBox = this.shadowRoot.querySelector('.textarea')
|
||||||
|
this.placeholder = this.shadowRoot.querySelector('.placeholder')
|
||||||
|
this.reflectedAttributes = ['disabled', 'required', 'readonly', 'rows', 'minlength', 'maxlength']
|
||||||
|
|
||||||
|
this.reset = this.reset.bind(this)
|
||||||
|
this.focusIn = this.focusIn.bind(this)
|
||||||
|
this.fireEvent = this.fireEvent.bind(this)
|
||||||
|
this.checkInput = this.checkInput.bind(this)
|
||||||
|
}
|
||||||
|
static get observedAttributes() {
|
||||||
|
return ['disabled', 'value', 'placeholder', 'required', 'readonly', 'rows', 'minlength', 'maxlength']
|
||||||
|
}
|
||||||
|
get value() {
|
||||||
|
return this.textarea.value
|
||||||
|
}
|
||||||
|
set value(val) {
|
||||||
|
this.setAttribute('value', val)
|
||||||
|
this.fireEvent()
|
||||||
|
}
|
||||||
|
get disabled() {
|
||||||
|
return this.hasAttribute('disabled')
|
||||||
|
}
|
||||||
|
set disabled(val) {
|
||||||
|
if (val) {
|
||||||
|
this.setAttribute('disabled', '')
|
||||||
|
} else {
|
||||||
|
this.removeAttribute('disabled')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
get isValid() {
|
||||||
|
return this.textarea.checkValidity()
|
||||||
|
}
|
||||||
|
reset() {
|
||||||
|
this.setAttribute('value', '')
|
||||||
|
}
|
||||||
|
focusIn() {
|
||||||
|
this.textarea.focus()
|
||||||
|
}
|
||||||
|
fireEvent() {
|
||||||
|
let event = new Event('input', {
|
||||||
|
bubbles: true,
|
||||||
|
cancelable: true,
|
||||||
|
composed: true
|
||||||
|
});
|
||||||
|
this.dispatchEvent(event);
|
||||||
|
}
|
||||||
|
checkInput() {
|
||||||
|
if (!this.hasAttribute('placeholder') || this.getAttribute('placeholder') === '')
|
||||||
|
return;
|
||||||
|
if (this.textarea.value !== '') {
|
||||||
|
this.placeholder.classList.add('hide')
|
||||||
|
} else {
|
||||||
|
this.placeholder.classList.remove('hide')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
connectedCallback() {
|
||||||
|
this.textarea.addEventListener('input', e => {
|
||||||
|
this.textareaBox.dataset.value = this.textarea.value
|
||||||
|
this.checkInput()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
attributeChangedCallback(name, oldValue, newValue) {
|
||||||
|
if (this.reflectedAttributes.includes(name)) {
|
||||||
|
if (this.hasAttribute(name)) {
|
||||||
|
this.textarea.setAttribute(name, this.getAttribute(name) ? this.getAttribute(name) : '')
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.textContent.removeAttribute(name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (name === 'placeholder') {
|
||||||
|
this.placeholder.textContent = this.getAttribute('placeholder')
|
||||||
|
}
|
||||||
|
else if (name === 'value') {
|
||||||
|
this.textarea.value = newValue;
|
||||||
|
this.textareaBox.dataset.value = newValue
|
||||||
|
this.checkInput()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
const smNotifications = document.createElement('template')
|
const smNotifications = document.createElement('template')
|
||||||
smNotifications.innerHTML = `
|
smNotifications.innerHTML = `
|
||||||
<style>
|
<style>
|
||||||
|
|||||||
@ -164,10 +164,13 @@ User.decideRequest = function (request, note) {
|
|||||||
const Cashier = {};
|
const Cashier = {};
|
||||||
|
|
||||||
Cashier.init = function () {
|
Cashier.init = function () {
|
||||||
delegate(getRef('cashier_request_list'), 'click', '.process-cashier-request', e => {
|
delegate(getRef('cashier_pending_request_list'), 'click', '.process-cashier-request', e => {
|
||||||
const requestID = e.delegateTarget.closest('.cashier-request').id;
|
const requestID = e.delegateTarget.closest('.cashier-request').id;
|
||||||
cashierUI.completeRequest(requestID)
|
cashierUI.completeRequest(requestID)
|
||||||
})
|
})
|
||||||
|
getRef('cashier_requests_selector').addEventListener('change', e => {
|
||||||
|
showChildElement('cashier_requests_wrapper', e.target.value === 'pending' ? 0 : 1)
|
||||||
|
})
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
let promises = [];
|
let promises = [];
|
||||||
//Requests from user to cashier(self) for token-cash exchange
|
//Requests from user to cashier(self) for token-cash exchange
|
||||||
|
|||||||
@ -336,40 +336,29 @@ cashierUI.renderRequests = function (requests, error = null) {
|
|||||||
else if (typeof requests !== "object" || requests === null)
|
else if (typeof requests !== "object" || requests === null)
|
||||||
return;
|
return;
|
||||||
const frag = document.createDocumentFragment();
|
const frag = document.createDocumentFragment();
|
||||||
for (let r in requests) {
|
for (let transactionID in requests) {
|
||||||
const oldCard = document.getElementById(r);
|
const { note, tag } = requests[transactionID];
|
||||||
if (oldCard) oldCard.remove();
|
let status = tag ? 'done' : (note ? 'failed' : "pending");
|
||||||
frag.prepend(render.cashierRequestCard(requests[r]));
|
getRef('cashier_pending_request_list').querySelectorAll(`[data-vc="${transactionID}"]`).forEach(card => card.remove());
|
||||||
|
getRef(status === 'pending' ? 'cashier_pending_request_list' : 'cashier_processed_request_list').prepend(render.cashierRequestCard(requests[transactionID]))
|
||||||
}
|
}
|
||||||
getRef('cashier_request_list').prepend(frag)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cashierUI.completeRequest = function (reqID) {
|
cashierUI.completeRequest = function (reqID) {
|
||||||
let request = Cashier.Requests[reqID];
|
floGlobals.cashierProcessingRequest = Cashier.Requests[reqID];
|
||||||
if (request.message.mode === "cash-to-token")
|
const { message: { mode } } = floGlobals.cashierProcessingRequest;
|
||||||
completeCashToTokenRequest(request);
|
if (mode === "cash-to-token")
|
||||||
else if (request.message.mode === "token-to-cash")
|
completeCashToTokenRequest(floGlobals.cashierProcessingRequest);
|
||||||
completeTokenToCashRequest(request);
|
else if (mode === "token-to-cash")
|
||||||
|
completeTokenToCashRequest(floGlobals.cashierProcessingRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
function completeCashToTokenRequest(request) {
|
function completeCashToTokenRequest(request) {
|
||||||
const { message: { upi_txid, amount }, vectorClock, senderID } = request;
|
const { message: { upi_txid, amount }, vectorClock, senderID } = request;
|
||||||
Cashier.checkIfUpiTxIsValid(upi_txid).then(_ => {
|
Cashier.checkIfUpiTxIsValid(upi_txid).then(_ => {
|
||||||
getConfirmation('Confirm', {
|
getRef('top_up_amount').textContent = formatAmount(amount);
|
||||||
message: `Check if you have received UPI transfer\nTxID: ${upi_txid}\nAmount: ${formatAmount(amount)}`,
|
getRef('top_up_txid').value = upi_txid;
|
||||||
confirmText: 'Confirm'
|
showPopup('confirm_topup_popup');
|
||||||
}).then(confirmed => {
|
|
||||||
if (confirmed) {
|
|
||||||
User.sendToken(senderID, amount, 'for cash-to-token').then(txid => {
|
|
||||||
console.warn(`${amount} cash-to-token for ${senderID}`, txid);
|
|
||||||
Cashier.finishRequest(request, txid).then(result => {
|
|
||||||
console.log(result);
|
|
||||||
console.info('Completed cash-to-token request:', vectorClock);
|
|
||||||
notify("Completed request", 'success');
|
|
||||||
}).catch(error => console.error(error))
|
|
||||||
}).catch(error => console.error(error))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
notify(error, 'error');
|
notify(error, 'error');
|
||||||
if (Array.isArray(error) && error[0] === true && typeof error[1] === 'string')
|
if (Array.isArray(error) && error[0] === true && typeof error[1] === 'string')
|
||||||
@ -380,6 +369,41 @@ function completeCashToTokenRequest(request) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function confirmTopUp() {
|
||||||
|
const { message: { amount }, vectorClock, senderID } = floGlobals.cashierProcessingRequest;
|
||||||
|
User.sendToken(senderID, amount, 'for cash-to-token').then(txid => {
|
||||||
|
console.warn(`${amount} cash-to-token for ${senderID}`, txid);
|
||||||
|
Cashier.finishRequest(floGlobals.cashierProcessingRequest, txid).then(result => {
|
||||||
|
console.log(result);
|
||||||
|
console.info('Completed cash-to-token request:', vectorClock);
|
||||||
|
notify("Completed request", 'success');
|
||||||
|
}).catch(error => console.error(error))
|
||||||
|
}).catch(error => console.error(error))
|
||||||
|
}
|
||||||
|
|
||||||
|
getRef('top_up__reason_selector').addEventListener('change', e => {
|
||||||
|
console.log(e.target.value);
|
||||||
|
if (e.target.value === 'other') {
|
||||||
|
getRef('top_up__specified_reason').parentNode.classList.remove('hide');
|
||||||
|
} else {
|
||||||
|
getRef('top_up__specified_reason').parentNode.classList.add('hide');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
function declineTopUp() {
|
||||||
|
const { vectorClock } = floGlobals.cashierProcessingRequest;
|
||||||
|
let reason = getRef('top_up__reason_selector').value;
|
||||||
|
if (reason === 'other') {
|
||||||
|
reason = getRef('top_up__specified_reason').value
|
||||||
|
}
|
||||||
|
Cashier.rejectRequest(floGlobals.cashierProcessingRequest, reason).then(result => {
|
||||||
|
console.log(result);
|
||||||
|
console.info('Rejected cash-to-token request:', vectorClock);
|
||||||
|
notify('Request top-up request', 'success');
|
||||||
|
hidePopup()
|
||||||
|
}).catch(error => console.error(error))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function completeTokenToCashRequest(request) {
|
function completeTokenToCashRequest(request) {
|
||||||
const { vectorClock, senderID, message: { token_txid, amount, upi_id } } = request
|
const { vectorClock, senderID, message: { token_txid, amount, upi_id } } = request
|
||||||
Cashier.checkIfTokenTxIsValid(token_txid, senderID, amount).then(result => {
|
Cashier.checkIfTokenTxIsValid(token_txid, senderID, amount).then(result => {
|
||||||
@ -427,6 +451,11 @@ function getStatusIcon(status) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const cashierRejectionErrors = {
|
||||||
|
1001: `Your request was reject because of wrong transaction ID. If you have sent money, it'll be returned within 24 hrs.`,
|
||||||
|
1002: `Amount requested and amount sent via UPI doesn't match. your transferred money will be returned within 24hrs.`
|
||||||
|
}
|
||||||
|
|
||||||
const render = {
|
const render = {
|
||||||
savedId(floID, details) {
|
savedId(floID, details) {
|
||||||
const { title } = details;
|
const { title } = details;
|
||||||
@ -469,7 +498,7 @@ const render = {
|
|||||||
:
|
:
|
||||||
`<svg class="icon" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0z" fill="none"></path><path d="M21 18v1c0 1.1-.9 2-2 2H5c-1.11 0-2-.9-2-2V5c0-1.1.89-2 2-2h14c1.1 0 2 .9 2 2v1h-9c-1.11 0-2 .9-2 2v8c0 1.1.89 2 2 2h9zm-9-2h10V8H12v8zm4-2.5c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5z"></path></svg>`;
|
`<svg class="icon" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0z" fill="none"></path><path d="M21 18v1c0 1.1-.9 2-2 2H5c-1.11 0-2-.9-2-2V5c0-1.1.89-2 2-2h14c1.1 0 2 .9 2 2v1h-9c-1.11 0-2 .9-2 2v8c0 1.1.89 2 2 2h9zm-9-2h10V8H12v8zm4-2.5c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5z"></path></svg>`;
|
||||||
if (status)
|
if (status)
|
||||||
clone.querySelector('.cashier-request__status').textContent = status;
|
clone.querySelector('.cashier-request__status').textContent = status.includes(':') ? status.split(':')[0] : status;
|
||||||
else
|
else
|
||||||
clone.querySelector('.cashier-request__status').innerHTML = `<button class="button process-cashier-request">Process</button>`;
|
clone.querySelector('.cashier-request__status').innerHTML = `<button class="button process-cashier-request">Process</button>`;
|
||||||
return clone;
|
return clone;
|
||||||
|
|||||||
@ -72,7 +72,7 @@ const debounce = (callback, wait) => {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let zIndex = 10
|
let zIndex = 100
|
||||||
// function required for popups or modals to appear
|
// function required for popups or modals to appear
|
||||||
function showPopup(popupId, pinned) {
|
function showPopup(popupId, pinned) {
|
||||||
zIndex++
|
zIndex++
|
||||||
@ -135,6 +135,9 @@ document.addEventListener('popupclosed', e => {
|
|||||||
case 'transfer_to_exchange_popup':
|
case 'transfer_to_exchange_popup':
|
||||||
showChildElement('exchange_transfer_process', 0);
|
showChildElement('exchange_transfer_process', 0);
|
||||||
break;
|
break;
|
||||||
|
case 'confirm_topup_popup':
|
||||||
|
showChildElement('confirm_topup_wrapper', 0);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -448,6 +451,7 @@ async function showPage(targetPage, options = {}) {
|
|||||||
}
|
}
|
||||||
} else if (params.type === 'wallet') {
|
} else if (params.type === 'wallet') {
|
||||||
transactionDetails = User.cashierRequests[params.transactionId]
|
transactionDetails = User.cashierRequests[params.transactionId]
|
||||||
|
console.log(transactionDetails)
|
||||||
const { message: { amount, mode, upi_id, upi_txid }, note, tag } = transactionDetails
|
const { message: { amount, mode, upi_id, upi_txid }, note, tag } = transactionDetails
|
||||||
status = tag ? tag : (note ? 'REJECTED' : "PENDING");
|
status = tag ? tag : (note ? 'REJECTED' : "PENDING");
|
||||||
getRef('transaction__type').textContent = mode === 'cash-to-token' ? 'Wallet top-up' : 'Withdraw';
|
getRef('transaction__type').textContent = mode === 'cash-to-token' ? 'Wallet top-up' : 'Withdraw';
|
||||||
@ -459,7 +463,15 @@ async function showPage(targetPage, options = {}) {
|
|||||||
getRef('transaction__note').classList.remove('hide')
|
getRef('transaction__note').classList.remove('hide')
|
||||||
}
|
}
|
||||||
if (mode === 'cash-to-token') {
|
if (mode === 'cash-to-token') {
|
||||||
getRef('transaction__note').textContent = `UPI transaction ID: ${upi_txid}`
|
if (status === 'COMPLETED') {
|
||||||
|
getRef('transaction__note').textContent = `UPI transaction ID: ${upi_txid}`
|
||||||
|
} else if (status === 'REJECTED') {
|
||||||
|
const reason = ['1001', '1002'].includes(note.split(':')[1]) ? cashierRejectionErrors[note.split(':')[1]] : note.split(':')[1]
|
||||||
|
getRef('transaction__note').innerHTML = `
|
||||||
|
<svg class="icon failed" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0z" fill="none"></path><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z"></path></svg>
|
||||||
|
${reason}
|
||||||
|
`
|
||||||
|
}
|
||||||
getRef('transaction__note').classList.remove('hide')
|
getRef('transaction__note').classList.remove('hide')
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
@ -806,7 +818,13 @@ function createState(defaultValue, key, callback) {
|
|||||||
return reactiveState.createState(defaultValue, key, callback)
|
return reactiveState.createState(defaultValue, key, callback)
|
||||||
}
|
}
|
||||||
const smState = document.createElement('template')
|
const smState = document.createElement('template')
|
||||||
smState.innerHTML = ``
|
smState.innerHTML = `
|
||||||
|
<style>
|
||||||
|
font-size: inherit;
|
||||||
|
font-weight: inherit;
|
||||||
|
font-family: inherit;
|
||||||
|
</style>
|
||||||
|
`
|
||||||
class SmState extends HTMLElement {
|
class SmState extends HTMLElement {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user