UI/UX and feature update
-- changes user flow for wallet top-up and changed the visual design to reflect that -- top-up now works with random generated string -- removed UPI ID asking step -- implemented required changes to cashier side also
This commit is contained in:
parent
5168fc3517
commit
5ee21d2994
50
css/main.css
50
css/main.css
@ -67,10 +67,6 @@ strong {
|
||||
line-height: 1.7;
|
||||
color: rgba(var(--text-color), 0.9);
|
||||
}
|
||||
p:not(:last-of-type),
|
||||
strong:not(:last-of-type) {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.warning {
|
||||
line-height: normal;
|
||||
@ -257,10 +253,49 @@ strip-option {
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
ul {
|
||||
ul,
|
||||
ol {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
ol {
|
||||
counter-reset: item;
|
||||
}
|
||||
ol li {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
counter-increment: item;
|
||||
}
|
||||
ol li:not(:last-of-type) {
|
||||
padding-bottom: 1.5rem;
|
||||
}
|
||||
ol li:not(:last-of-type)::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 0.1rem;
|
||||
height: calc(100% - 1.5rem);
|
||||
background: var(--accent-color);
|
||||
margin-left: 0.6rem;
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
ol li::before {
|
||||
content: counter(item);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 500;
|
||||
margin-top: 0.15rem;
|
||||
padding: 0.4rem;
|
||||
margin-right: 1rem;
|
||||
aspect-ratio: 1/1;
|
||||
border-radius: 100%;
|
||||
color: rgba(var(--text-color), 0.8);
|
||||
background: rgba(var(--text-color), 0.1);
|
||||
}
|
||||
|
||||
.overflow-ellipsis {
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
@ -872,10 +907,7 @@ ul {
|
||||
background-color: rgba(var(--text-color), 0.03);
|
||||
border-radius: 0.5rem;
|
||||
height: 12rem;
|
||||
width: -webkit-max-content;
|
||||
width: -moz-max-content;
|
||||
width: max-content;
|
||||
margin: 0 auto;
|
||||
justify-self: flex-start;
|
||||
}
|
||||
#topup_wallet__qr_code svg {
|
||||
width: 100%;
|
||||
|
||||
2
css/main.min.css
vendored
2
css/main.min.css
vendored
File diff suppressed because one or more lines are too long
@ -63,10 +63,6 @@ strong {
|
||||
max-width: 65ch;
|
||||
line-height: 1.7;
|
||||
color: rgba(var(--text-color), 0.9);
|
||||
|
||||
&:not(:last-of-type) {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
}
|
||||
.warning {
|
||||
line-height: normal;
|
||||
@ -231,9 +227,47 @@ strip-option {
|
||||
--border-radius: 0.2rem;
|
||||
user-select: none;
|
||||
}
|
||||
ul {
|
||||
ul,
|
||||
ol {
|
||||
list-style: none;
|
||||
}
|
||||
ol {
|
||||
counter-reset: item;
|
||||
li {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
counter-increment: item;
|
||||
&:not(:last-of-type) {
|
||||
padding-bottom: 1.5rem;
|
||||
&::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 0.1rem;
|
||||
height: calc(100% - 1.5rem);
|
||||
background: var(--accent-color);
|
||||
margin-left: 0.6rem;
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
li::before {
|
||||
content: counter(item);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 500;
|
||||
margin-top: 0.15rem;
|
||||
padding: 0.4rem;
|
||||
margin-right: 1rem;
|
||||
aspect-ratio: 1/1;
|
||||
border-radius: 100%;
|
||||
color: rgba(var(--text-color), 0.8);
|
||||
background: rgba(var(--text-color), 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
.overflow-ellipsis {
|
||||
width: 100%;
|
||||
@ -811,8 +845,7 @@ ul {
|
||||
background-color: rgba(var(--text-color), 0.03);
|
||||
border-radius: 0.5rem;
|
||||
height: 12rem;
|
||||
width: max-content;
|
||||
margin: 0 auto;
|
||||
justify-self: flex-start;
|
||||
svg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
104
index.html
104
index.html
@ -326,7 +326,7 @@
|
||||
</div>
|
||||
</section>
|
||||
<section id="wallet" class="inner-page hide">
|
||||
<div class="flex sticky top-0" style="background-color: rgba(var(--foreground-color),1);">
|
||||
<div class="flex sticky top-0" style="background-color: rgba(var(--foreground-color),1); z-index: 1;">
|
||||
<a href="#/home" class="button icon-only margin-right-0-5">
|
||||
<svg class="icon" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px"
|
||||
fill="#000000">
|
||||
@ -813,7 +813,7 @@
|
||||
</g>
|
||||
</svg>
|
||||
</sm-input>
|
||||
<div class="grid gap-1">
|
||||
<!-- <div class="grid gap-1">
|
||||
<div class="grid gap-0-5 hide">
|
||||
<p>
|
||||
<b>Select UPI ID you'll send money from</b>
|
||||
@ -828,42 +828,70 @@
|
||||
</svg>
|
||||
Add UPI ID
|
||||
</button>
|
||||
</div>
|
||||
</div> -->
|
||||
<strong id="low_user_flo_warning" class="hide warning"></strong>
|
||||
<button class="button button--primary cta" onclick="continueWalletTopup()"
|
||||
type="submit">Continue</button>
|
||||
</sm-form>
|
||||
<sm-form id="confirm_topup_form" class="hide">
|
||||
<div class="grid gap-0-5">
|
||||
<h4>Confirm</h4>
|
||||
<p id="topup_wallet__details"></p>
|
||||
</div>
|
||||
<div class="grid gap-1">
|
||||
<details>
|
||||
<summary class="interact">
|
||||
<b style="font-size: 0.9rem;">Show QR code to scan</b>
|
||||
<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="M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6 1.41-1.41z" />
|
||||
</svg>
|
||||
</summary>
|
||||
<div id="topup_wallet__qr_code"></div>
|
||||
</details>
|
||||
<div class="grid gap-0-5">
|
||||
<h4>Transfer money</h4>
|
||||
<ol type="1">
|
||||
<li>
|
||||
<p>
|
||||
or send money to UPI ID below
|
||||
Open your<strong> preferred UPI app </strong>
|
||||
</p>
|
||||
<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. <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"
|
||||
error-text="Please enter UPI transaction ID of money you sent to continue."
|
||||
placeholder="UPI transaction ID" autofocus animate required></sm-input>
|
||||
</li>
|
||||
<li>
|
||||
<div class="grid gap-1">
|
||||
<div class="grid gap-0-5">
|
||||
<p>
|
||||
Enter <strong> UPI ID </strong>below as recipient
|
||||
</p>
|
||||
<sm-copy id="topup_wallet__upi_id" style="font-weight: 700;"></sm-copy>
|
||||
</div>
|
||||
<details>
|
||||
<summary class="interact">
|
||||
<b style="font-size: 0.9rem;">Or get QR code to scan</b>
|
||||
<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="M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6 1.41-1.41z" />
|
||||
</svg>
|
||||
</summary>
|
||||
<div class="grid gap-0-5">
|
||||
<div id="topup_wallet__qr_code"></div>
|
||||
<p>
|
||||
<strong>
|
||||
*If you are paying by scanning the QR code, amount and remark/message will
|
||||
be
|
||||
added automatically.</strong>
|
||||
</p>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<p id="topup_wallet__details"></p>
|
||||
</li>
|
||||
<li>
|
||||
<div class="grid gap-0-5">
|
||||
<p>
|
||||
Enter <strong>code</strong> below in the <strong> message/remark </strong> field
|
||||
</p>
|
||||
<sm-copy id="topup_wallet__code" style="font-weight: 700;"></sm-copy>
|
||||
<p>
|
||||
<strong>*This is very important step. If you skip or enter wrong code, transaction can't
|
||||
be
|
||||
verified.</strong>
|
||||
</p>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
After sending money, press <b>Confirm</b>.
|
||||
</p>
|
||||
</li>
|
||||
</ol>
|
||||
<div class="multi-state-button">
|
||||
<button id="topup_wallet_button" class="button button--primary cta" onclick="depositMoneyToWallet()"
|
||||
type="submit">Confirm</button>
|
||||
@ -947,7 +975,7 @@
|
||||
</div>
|
||||
<div class="multi-state-button">
|
||||
<button id="withdraw_rupee_button" class="button button--primary cta"
|
||||
onclick="withdrawMoneyFromWallet()" type="submit">Transfer</button>
|
||||
onclick="withdrawMoneyFromWallet()" type="submit">Withdraw</button>
|
||||
</div>
|
||||
</sm-form>
|
||||
<div class="grid gap-0-5 hide justify-center text-center">
|
||||
@ -1089,14 +1117,18 @@
|
||||
<b id="top_up_amount" style="font-size: 1.5rem;"></b>
|
||||
|
||||
</div>
|
||||
<div class="grid">
|
||||
<!-- <div class="grid">
|
||||
<div class="label">UPI ID</div>
|
||||
<sm-copy id="top_up_upi_id"></sm-copy>
|
||||
</div>
|
||||
</div> -->
|
||||
<div class="grid">
|
||||
<div class="label">Transaction code</div>
|
||||
<sm-copy id="top_up__code"></sm-copy>
|
||||
</div>
|
||||
<!-- <div class="grid">
|
||||
<div class="label">Transaction ID</div>
|
||||
<sm-copy id="top_up_txid"></sm-copy>
|
||||
</div>
|
||||
</div> -->
|
||||
<div class="flex justify-right gap-0-3">
|
||||
<button class="button" onclick="showChildElement('confirm_topup_wrapper', 1)">Decline</button>
|
||||
<div class="multi-state-button">
|
||||
@ -1108,7 +1140,7 @@
|
||||
<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="1003">Transaction code mismatch</sm-option>
|
||||
<sm-option value="1002">Amount doesn't match</sm-option>
|
||||
<sm-option value="other">Other</sm-option>
|
||||
</sm-select>
|
||||
|
||||
@ -107,15 +107,16 @@ User.findCashier = function () {
|
||||
}
|
||||
}
|
||||
|
||||
User.cashToToken = function (cashier, amount, upiTxID, upiID) {
|
||||
User.cashToToken = function (cashier, amount, txCode, upiID) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!floGlobals.subAdmins.includes(cashier))
|
||||
return reject("Invalid cashier");
|
||||
floCloudAPI.sendGeneralData({
|
||||
mode: "cash-to-token",
|
||||
amount: amount,
|
||||
upi_txid: upiTxID,
|
||||
upiID
|
||||
// upi_txid: upiTxID,
|
||||
upiID,
|
||||
txCode
|
||||
}, TYPE_CASHIER_REQUEST, {
|
||||
receiverID: cashier
|
||||
}).then(result => resolve(result))
|
||||
@ -232,7 +233,6 @@ Cashier.updateUPI = function (upi_id) {
|
||||
.catch(error => reject(error))
|
||||
})
|
||||
}
|
||||
|
||||
Object.defineProperty(Cashier, 'Requests', {
|
||||
get: function () {
|
||||
let fk = floCloudAPI.util.filterKey(TYPE_CASHIER_REQUEST, {
|
||||
|
||||
@ -41,31 +41,34 @@ function continueWalletTopup() {
|
||||
let cashier = User.findCashier();
|
||||
if (!cashier)
|
||||
return notify("No cashier online. Please try again in a while.", 'error');
|
||||
const upiId = getRef('select_topup_upi_id').value;
|
||||
if (!upiId)
|
||||
return notify("Please add the UPI ID which you'll use to send the money", 'error');
|
||||
// const upiId = getRef('select_topup_upi_id').value;
|
||||
const txCode = randomString(6);
|
||||
getRef('topup_wallet__code').value = txCode;
|
||||
// if (!upiId)
|
||||
// return notify("Please add the UPI ID which you'll use to send the money", 'error');
|
||||
let amount = parseFloat(getRef('request_cashier_amount').value.trim());
|
||||
renderElem(getRef('topup_wallet__details'), html`Send <b>${formatAmount(amount)}</b> from your UPI ID <b>${upiId}</b>`);
|
||||
renderElem(getRef('topup_wallet__details'), html`Enter <b>${formatAmount(amount)}</b> as amount`);
|
||||
getRef('topup_wallet__upi_id').value = cashierUPI[cashier];
|
||||
getRef('topup_wallet__qr_code').innerHTML = ''
|
||||
getRef('topup_wallet__qr_code').append(new QRCode({
|
||||
msg: `upi://pay?pn=FLOPay&pa=${cashierUPI[cashier]}&am=${amount}`,
|
||||
msg: `upi://pay?pn=FLOPay&pa=${cashierUPI[cashier]}&am=${amount}&tn=${txCode}`,
|
||||
ecl: 'H'
|
||||
}))
|
||||
showChildElement('topup_wallet_process', 1)
|
||||
getRef('topup_wallet__txid').focusIn();
|
||||
// getRef('topup_wallet__txid').focusIn();
|
||||
}
|
||||
function depositMoneyToWallet() {
|
||||
let cashier = User.findCashier();
|
||||
if (!cashier)
|
||||
return notify("No cashier online. Please try again in a while.", 'error');
|
||||
let amount = parseFloat(getRef('request_cashier_amount').value.trim());
|
||||
let upiTxID = getRef('topup_wallet__txid').value.trim();
|
||||
const upiId = getRef('select_topup_upi_id').value;
|
||||
if (upiTxID === '')
|
||||
return notify("Please enter UPI transaction ID", 'error');
|
||||
// let upiTxID = getRef('topup_wallet__txid').value.trim();
|
||||
const txCode = getRef('topup_wallet__code').value;
|
||||
// const upiId = getRef('select_topup_upi_id').value;
|
||||
// if (upiTxID === '')
|
||||
// return notify("Please enter UPI transaction ID", 'error');
|
||||
buttonLoader('topup_wallet_button', true);
|
||||
User.cashToToken(cashier, amount, upiTxID, upiId).then(result => {
|
||||
User.cashToToken(cashier, amount, txCode/* , upiId */).then(result => {
|
||||
console.log(result);
|
||||
showChildElement('topup_wallet_process', 2);
|
||||
refreshBalance()
|
||||
@ -153,8 +156,8 @@ function saveUpiId() {
|
||||
if (pagesData.lastPage === 'settings') {
|
||||
getRef('saved_upi_ids_list').append(render.savedUpiId(upiId));
|
||||
} else if (pagesData.lastPage === 'home') {
|
||||
getRef('select_topup_upi_id').append(render.savedUpiIdOption(upiId));
|
||||
getRef('select_topup_upi_id').parentNode.classList.remove('hide')
|
||||
// getRef('select_topup_upi_id').append(render.savedUpiIdOption(upiId));
|
||||
// getRef('select_topup_upi_id').parentNode.classList.remove('hide')
|
||||
getRef('select_withdraw_upi_id').append(render.savedUpiIdOption(upiId));
|
||||
getRef('select_withdraw_upi_id').parentNode.classList.remove('hide')
|
||||
}
|
||||
@ -354,20 +357,24 @@ cashierUI.completeRequest = function (reqID) {
|
||||
}
|
||||
|
||||
function completeCashToTokenRequest(request) {
|
||||
const { message: { upi_txid, amount, upiID }, vectorClock, senderID } = request;
|
||||
Cashier.checkIfUpiTxIsValid(upi_txid).then(_ => {
|
||||
const { message: { upi_txid, amount, upiID, txCode }, vectorClock } = request;
|
||||
getRef('top_up_amount').textContent = formatAmount(amount);
|
||||
getRef('top_up_txid').value = upi_txid;
|
||||
getRef('top_up_upi_id').value = upiID;
|
||||
getRef('top_up__code').value = txCode;
|
||||
showPopup('confirm_topup_popup');
|
||||
}).catch(error => {
|
||||
notify(error, 'error');
|
||||
if (Array.isArray(error) && error[0] === true && typeof error[1] === 'string')
|
||||
Cashier.rejectRequest(request, error[1]).then(result => {
|
||||
console.log(result);
|
||||
console.info('Rejected cash-to-token request:', vectorClock);
|
||||
}).catch(error => console.error(error))
|
||||
})
|
||||
// Cashier.checkIfUpiTxIsValid(upi_txid).then(_ => {
|
||||
// getRef('top_up_amount').textContent = formatAmount(amount);
|
||||
// // getRef('top_up_txid').value = upi_txid;
|
||||
// // getRef('top_up_upi_id').value = upiID;
|
||||
// getRef('top_up__code').value = txCode;
|
||||
// showPopup('confirm_topup_popup');
|
||||
// }).catch(error => {
|
||||
// notify(Array.isArray(error) ? error[1]: error, 'error');
|
||||
// if (Array.isArray(error) && error[0] === true && typeof error[1] === 'string')
|
||||
// Cashier.rejectRequest(request, error[1]).then(result => {
|
||||
// console.log(result);
|
||||
// console.info('Rejected cash-to-token request:', vectorClock);
|
||||
// }).catch(error => console.error(error))
|
||||
// })
|
||||
}
|
||||
|
||||
function confirmTopUp(button) {
|
||||
@ -485,7 +492,8 @@ 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.`
|
||||
1002: `Amount requested and amount sent via UPI doesn't match. your transferred money will be returned within 24hrs.`,
|
||||
1003: `Your request was rejected because of wrong or missing remark/message code. If you have sent money, it'll be returned within 24 hrs.`,
|
||||
}
|
||||
|
||||
const render = {
|
||||
|
||||
@ -109,8 +109,8 @@ document.addEventListener('popupopened', async e => {
|
||||
}
|
||||
if (hasSavedIds) {
|
||||
const clone = frag.cloneNode(true)
|
||||
getRef('select_topup_upi_id').append(frag)
|
||||
getRef('select_topup_upi_id').parentNode.classList.remove('hide')
|
||||
// getRef('select_topup_upi_id').append(frag)
|
||||
// getRef('select_topup_upi_id').parentNode.classList.remove('hide')
|
||||
getRef('select_withdraw_upi_id').append(clone)
|
||||
getRef('select_withdraw_upi_id').parentNode.classList.remove('hide')
|
||||
}
|
||||
@ -125,8 +125,8 @@ document.addEventListener('popupclosed', e => {
|
||||
getRef('search_saved_ids_picker').value = ''
|
||||
break;
|
||||
case 'topup_wallet_popup':
|
||||
getRef('select_topup_upi_id').parentNode.classList.add('hide')
|
||||
getRef('select_topup_upi_id').innerHTML = ''
|
||||
// getRef('select_topup_upi_id').parentNode.classList.add('hide')
|
||||
// getRef('select_topup_upi_id').innerHTML = ''
|
||||
showChildElement('topup_wallet_process', 0)
|
||||
break;
|
||||
case 'withdraw_wallet_popup':
|
||||
@ -454,7 +454,7 @@ async function showPage(targetPage, options = {}) {
|
||||
} else if (params.type === 'wallet') {
|
||||
transactionDetails = User.cashierRequests[params.transactionId]
|
||||
console.log(transactionDetails)
|
||||
const { message: { amount, mode, upi_id, upi_txid, token_txid }, note, tag } = transactionDetails
|
||||
const { message: { amount, mode, upi_id, upi_txid, token_txid, txCode }, note, tag } = transactionDetails
|
||||
status = tag ? tag : (note ? 'REJECTED' : "PENDING");
|
||||
getRef('transaction__type').textContent = mode === 'cash-to-token' ? 'Wallet top-up' : 'Withdraw';
|
||||
if (status === 'COMPLETED') {
|
||||
@ -466,9 +466,13 @@ async function showPage(targetPage, options = {}) {
|
||||
}
|
||||
if (mode === 'cash-to-token') {
|
||||
if (status === 'COMPLETED') {
|
||||
getRef('transaction__note').textContent = `UPI transaction ID: ${upi_txid}`
|
||||
if (txCode) {
|
||||
getRef('transaction__note').textContent = `Transaction code: ${txCode}`
|
||||
} else if (upi_txid) {
|
||||
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]
|
||||
const reason = cashierRejectionErrors.hasOwnProperty(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}
|
||||
@ -839,4 +843,16 @@ class SmState extends HTMLElement {
|
||||
reactiveState.unsubscribe(this.getAttribute('sid'), this)
|
||||
}
|
||||
}
|
||||
window.customElements.define('r-s', SmState);
|
||||
window.customElements.define('r-s', SmState);
|
||||
|
||||
|
||||
// generate random string with numbers and capital and small letters
|
||||
function randomString(length) {
|
||||
let result = '';
|
||||
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||
const charactersLength = characters.length;
|
||||
for (var i = 0; i < length; i++) {
|
||||
result += characters.charAt(Math.floor(Math.random() * charactersLength));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user