Added admin features
This commit is contained in:
parent
6ded0d217e
commit
deb5a8bfae
11
css/main.css
11
css/main.css
@ -17,8 +17,8 @@ body {
|
|||||||
body {
|
body {
|
||||||
--accent-color: #365eff;
|
--accent-color: #365eff;
|
||||||
--text-color: 30, 30, 30;
|
--text-color: 30, 30, 30;
|
||||||
--background-color: 240, 240, 240;
|
--background-color: 248, 248, 248;
|
||||||
--foreground-color: 250, 250, 250;
|
--foreground-color: 255, 255, 255;
|
||||||
--danger-color: rgb(255, 75, 75);
|
--danger-color: rgb(255, 75, 75);
|
||||||
--green: #1cad59;
|
--green: #1cad59;
|
||||||
scrollbar-width: thin;
|
scrollbar-width: thin;
|
||||||
@ -950,6 +950,10 @@ h3 {
|
|||||||
transform: scale(1.15);
|
transform: scale(1.15);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#verifiers_section {
|
||||||
|
width: min(100%, 40rem);
|
||||||
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 40rem) {
|
@media screen and (max-width: 40rem) {
|
||||||
theme-toggle {
|
theme-toggle {
|
||||||
order: 2;
|
order: 2;
|
||||||
@ -975,6 +979,9 @@ h3 {
|
|||||||
#view_file_popup {
|
#view_file_popup {
|
||||||
--width: min(56rem, 100%);
|
--width: min(56rem, 100%);
|
||||||
}
|
}
|
||||||
|
#approve_verifier_popup {
|
||||||
|
--width: min(32rem, 100%);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@media (any-hover: hover) {
|
@media (any-hover: hover) {
|
||||||
::-webkit-scrollbar {
|
::-webkit-scrollbar {
|
||||||
|
|||||||
2
css/main.min.css
vendored
2
css/main.min.css
vendored
File diff suppressed because one or more lines are too long
@ -17,8 +17,8 @@ body {
|
|||||||
body {
|
body {
|
||||||
--accent-color: #365eff;
|
--accent-color: #365eff;
|
||||||
--text-color: 30, 30, 30;
|
--text-color: 30, 30, 30;
|
||||||
--background-color: 240, 240, 240;
|
--background-color: 248, 248, 248;
|
||||||
--foreground-color: 250, 250, 250;
|
--foreground-color: 255, 255, 255;
|
||||||
--danger-color: rgb(255, 75, 75);
|
--danger-color: rgb(255, 75, 75);
|
||||||
--green: #1cad59;
|
--green: #1cad59;
|
||||||
scrollbar-width: thin;
|
scrollbar-width: thin;
|
||||||
@ -897,6 +897,11 @@ h3 {
|
|||||||
transform: scale(1.15);
|
transform: scale(1.15);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#verifiers_section {
|
||||||
|
width: min(100%, 40rem);
|
||||||
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 40rem) {
|
@media screen and (max-width: 40rem) {
|
||||||
theme-toggle {
|
theme-toggle {
|
||||||
order: 2;
|
order: 2;
|
||||||
@ -923,6 +928,9 @@ h3 {
|
|||||||
#view_file_popup {
|
#view_file_popup {
|
||||||
--width: min(56rem, 100%);
|
--width: min(56rem, 100%);
|
||||||
}
|
}
|
||||||
|
#approve_verifier_popup {
|
||||||
|
--width: min(32rem, 100%);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@media (any-hover: hover) {
|
@media (any-hover: hover) {
|
||||||
::-webkit-scrollbar {
|
::-webkit-scrollbar {
|
||||||
|
|||||||
347
index.html
347
index.html
@ -57,6 +57,7 @@
|
|||||||
<h3>Profile</h3>
|
<h3>Profile</h3>
|
||||||
</header>
|
</header>
|
||||||
<section class="grid gap-2">
|
<section class="grid gap-2">
|
||||||
|
<div id="agency_name_container" class="grid gap-0-5"> </div>
|
||||||
<div class="grid gap-0-5">
|
<div class="grid gap-0-5">
|
||||||
<h5>My FLO address</h5>
|
<h5>My FLO address</h5>
|
||||||
<sm-copy class="my-flo-address"></sm-copy>
|
<sm-copy class="my-flo-address"></sm-copy>
|
||||||
@ -94,6 +95,51 @@
|
|||||||
</header>
|
</header>
|
||||||
<div id="commit_approvals_popup__content" class="grid gap-1"></div>
|
<div id="commit_approvals_popup__content" class="grid gap-1"></div>
|
||||||
</sm-popup>
|
</sm-popup>
|
||||||
|
<sm-popup id="set_approver_name_popup">
|
||||||
|
<header slot="header" class="popup__header">
|
||||||
|
<button class="popup__header__close">
|
||||||
|
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
||||||
|
<path fill="none" d="M0 0h24v24H0z" />
|
||||||
|
<path
|
||||||
|
d="M12 10.586l4.95-4.95 1.414 1.414-4.95 4.95 4.95 4.95-1.414 1.414-4.95-4.95-4.95 4.95-1.414-1.414 4.95-4.95-4.95-4.95L7.05 5.636z" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<h3>Set your agency name</h3>
|
||||||
|
</header>
|
||||||
|
<sm-form>
|
||||||
|
<sm-input id="approver_name_input" placeholder="Enter your agency name" required></sm-input>
|
||||||
|
<div class="multi-state-button">
|
||||||
|
<button id="set_approver_name__button" class="button button--primary" type="submit"
|
||||||
|
onclick="setApproverName()">Set</button>
|
||||||
|
</div>
|
||||||
|
</sm-form>
|
||||||
|
</sm-popup>
|
||||||
|
<sm-popup id="approve_verifier_popup">
|
||||||
|
<header slot="header" class="popup__header">
|
||||||
|
<button class="popup__header__close">
|
||||||
|
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
||||||
|
<path fill="none" d="M0 0h24v24H0z" />
|
||||||
|
<path
|
||||||
|
d="M12 10.586l4.95-4.95 1.414 1.414-4.95 4.95 4.95 4.95-1.414 1.414-4.95-4.95-4.95 4.95-1.414-1.414 4.95-4.95-4.95-4.95L7.05 5.636z" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<h3>Approve verifiers</h3>
|
||||||
|
</header>
|
||||||
|
<sm-form>
|
||||||
|
<ul id="verifiers_container" class="grid gap-1">
|
||||||
|
<li class="flex align-center gap-1">
|
||||||
|
<sm-input class="w-100" placeholder="FLO/BTC address" error-text="Invalid address" data-address
|
||||||
|
required></sm-input>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<button class="button button--colored button--small margin-right-auto" onclick="addVerifier()">Add
|
||||||
|
verifier</button>
|
||||||
|
<div class="multi-state-button">
|
||||||
|
<button id="approve_verifier_button" class="button button--primary" type="submit"
|
||||||
|
onclick="approveVerifiers()">Approve</button>
|
||||||
|
</div>
|
||||||
|
</sm-form>
|
||||||
|
</sm-popup>
|
||||||
<script src="https://unpkg.com/uhtml@3.0.1/es.js"></script>
|
<script src="https://unpkg.com/uhtml@3.0.1/es.js"></script>
|
||||||
<script src="scripts/components.min.js"></script>
|
<script src="scripts/components.min.js"></script>
|
||||||
<script src="scripts/lib.js"></script>
|
<script src="scripts/lib.js"></script>
|
||||||
@ -407,7 +453,7 @@
|
|||||||
} else
|
} else
|
||||||
elem.textContent = floGlobals.myBtcID
|
elem.textContent = floGlobals.myBtcID
|
||||||
})
|
})
|
||||||
document.querySelectorAll('sm-input[data-flo-id]').forEach(input => input.customValidation = floCrypto.validateAddr)
|
document.querySelectorAll('sm-input[data-address]').forEach(input => input.customValidation = floCrypto.validateAddr)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
router.addRoute('loading', (state) => {
|
router.addRoute('loading', (state) => {
|
||||||
@ -525,6 +571,16 @@
|
|||||||
function initSearch() {
|
function initSearch() {
|
||||||
location.hash = `#/kyc_status?address=${getRef('address_verify').value.trim()}`;
|
location.hash = `#/kyc_status?address=${getRef('address_verify').value.trim()}`;
|
||||||
}
|
}
|
||||||
|
function getUserKycRequests() {
|
||||||
|
const userKycRequests = floDapps.getNextGeneralData('userKycRequests', '0');
|
||||||
|
const filtered = {}
|
||||||
|
for (let key in userKycRequests) {
|
||||||
|
if (floCrypto.isSameAddr(userKycRequests[key].senderID, floDapps.user.id)) {
|
||||||
|
filtered[key] = userKycRequests[key]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filtered
|
||||||
|
}
|
||||||
async function verify(address) {
|
async function verify(address) {
|
||||||
if (address) {
|
if (address) {
|
||||||
if (getRef('address_verify').value.trim() !== address)
|
if (getRef('address_verify').value.trim() !== address)
|
||||||
@ -545,10 +601,10 @@
|
|||||||
await floCloudAPI.requestGeneralData('userKycRequests', {
|
await floCloudAPI.requestGeneralData('userKycRequests', {
|
||||||
senderID: [floAddress, btcAddress]
|
senderID: [floAddress, btcAddress]
|
||||||
})
|
})
|
||||||
const kycRequests = floDapps.getNextGeneralData('userKycRequests', '0');
|
const kycRequests = getUserKycRequests();
|
||||||
const verifiedRequest = Object.values(kycRequests).find(request => {
|
const verifiedRequest = Object.values(kycRequests).find(request => {
|
||||||
const wasSentByAddress = request.senderID === floAddress || request.senderID === btcAddress
|
const wasSentByAddress = request.senderID === floAddress || request.senderID === btcAddress
|
||||||
const isApproved = request.tag === 'approved'
|
const isApproved = request.tag.includes('approved')
|
||||||
return wasSentByAddress && isApproved
|
return wasSentByAddress && isApproved
|
||||||
});
|
});
|
||||||
getRef('verification_result').classList.remove('hidden');
|
getRef('verification_result').classList.remove('hidden');
|
||||||
@ -615,35 +671,21 @@
|
|||||||
return floAddresses[btcAddress]
|
return floAddresses[btcAddress]
|
||||||
}
|
}
|
||||||
const render = {
|
const render = {
|
||||||
approvedKycAddresses() {
|
|
||||||
const approvedKycAddresses = Object.keys(floGlobals.approvedKyc)
|
|
||||||
.filter(address => !floGlobals.approvedKyc[address].revokedBy)
|
|
||||||
.map(address => render.approvedKycAddressCard(address))
|
|
||||||
renderElem(getRef('approved_addresses'), html`${approvedKycAddresses}`)
|
|
||||||
},
|
|
||||||
approvedAggregatorCard(address) {
|
approvedAggregatorCard(address) {
|
||||||
const floID = getFloAddress(address)
|
const floID = getFloAddress(address)
|
||||||
const btcID = getBtcAddress(floID)
|
const btcID = getBtcAddress(floID)
|
||||||
return html`
|
return html`
|
||||||
<li class="revoke-card" data-address=${btcID} data-search-key=${`${floGlobals.approvedKycAggregators[address]}-${btcID}-${floID}`}>
|
<li class="revoke-card">
|
||||||
<label class="flex align-center">
|
<label class="flex align-center">
|
||||||
<input type="checkbox" value=${btcID}/>
|
<input type="checkbox" value=${btcID}/>
|
||||||
<div class="grid gap-0-5">
|
<div class="grid gap-0-3">
|
||||||
<h4>${floGlobals.approvedKycAggregators[address]}</h4>
|
<span class="wrap-around">BTC: ${btcID}</span>
|
||||||
<div class="grid gap-0-3">
|
<span class="wrap-around">FLO: ${floID}</span>
|
||||||
<span class="wrap-around">BTC: ${btcID}</span>
|
|
||||||
<span class="wrap-around">FLO: ${floID}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
</li>
|
</li>
|
||||||
`
|
`
|
||||||
},
|
},
|
||||||
approvedAggregators() {
|
|
||||||
const approvedAggregators = Object.keys(floGlobals.approvedKycAggregators)
|
|
||||||
.map(address => render.approvedAggregatorCard(address))
|
|
||||||
renderElem(getRef('approved_addresses'), html`${approvedAggregators}`)
|
|
||||||
},
|
|
||||||
pendingKycRequest(request) {
|
pendingKycRequest(request) {
|
||||||
const { senderID, time, files } = request;
|
const { senderID, time, files } = request;
|
||||||
const floAddress = getFloAddress(senderID)
|
const floAddress = getFloAddress(senderID)
|
||||||
@ -673,11 +715,11 @@
|
|||||||
</li>`
|
</li>`
|
||||||
},
|
},
|
||||||
approvedKycRequest(request) {
|
approvedKycRequest(request) {
|
||||||
const { senderID, time, files } = request;
|
const { senderID, time, files, tag } = request;
|
||||||
const floAddress = getFloAddress(senderID)
|
const floAddress = getFloAddress(senderID)
|
||||||
const btcAddress = getBtcAddress(floAddress)
|
const btcAddress = getBtcAddress(floAddress)
|
||||||
const fileButtons = files.map(file => {
|
const fileButtons = files.map(file => {
|
||||||
const { message: { docType, docVectorClock }, vectorClock } = file;
|
const { message: { docType, docVectorClock }, vectorClock, tag } = file;
|
||||||
return html`
|
return html`
|
||||||
<div class="multi-state-button">
|
<div class="multi-state-button">
|
||||||
<button class="button button--primary" .dataset=${{ requestVC: vectorClock, docVC: docVectorClock }} onclick=${viewFile}>
|
<button class="button button--primary" .dataset=${{ requestVC: vectorClock, docVC: docVectorClock }} onclick=${viewFile}>
|
||||||
@ -687,7 +729,7 @@
|
|||||||
`
|
`
|
||||||
})
|
})
|
||||||
return html`
|
return html`
|
||||||
<li class="revoke-card" .dataset=${{ address: floAddress, requestVC: vectorClock, docVC: docVectorClock }}>
|
<li class="revoke-card">
|
||||||
<label class="flex align-center">
|
<label class="flex align-center">
|
||||||
<input type="checkbox" value=${floAddress}/>
|
<input type="checkbox" value=${floAddress}/>
|
||||||
<div class="grid gap-0-3">
|
<div class="grid gap-0-3">
|
||||||
@ -735,12 +777,12 @@
|
|||||||
if (!filteredRequests[floAddress]) {
|
if (!filteredRequests[floAddress]) {
|
||||||
filteredRequests[floAddress] = {
|
filteredRequests[floAddress] = {
|
||||||
time,
|
time,
|
||||||
tag: tag === 'approved' ? 'approved' : tag,
|
tag: tag?.includes('approved') ? 'approved' : tag,
|
||||||
files: [],
|
files: [],
|
||||||
note
|
note
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
filteredRequests[floAddress].files.push({ message, vectorClock });
|
filteredRequests[floAddress].files.push({ message, vectorClock, tag });
|
||||||
}
|
}
|
||||||
const result = []
|
const result = []
|
||||||
Object.entries(filteredRequests).forEach(([senderID, request]) => {
|
Object.entries(filteredRequests).forEach(([senderID, request]) => {
|
||||||
@ -749,7 +791,7 @@
|
|||||||
if (status) {
|
if (status) {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 'approved':
|
case 'approved':
|
||||||
if (tag !== 'approved' && !floGlobals.approvalsToBeCommitted.has(floAddress)) return;
|
if (!tag.includes('approved') && !floGlobals.approvalsToBeCommitted.has(floAddress)) return;
|
||||||
break;
|
break;
|
||||||
case 'rejected':
|
case 'rejected':
|
||||||
if (note !== 'rejected') return;
|
if (note !== 'rejected') return;
|
||||||
@ -777,11 +819,11 @@
|
|||||||
userRequests() {
|
userRequests() {
|
||||||
let isVerified = false;
|
let isVerified = false;
|
||||||
let concurrentRequests = 0;
|
let concurrentRequests = 0;
|
||||||
const requests = floDapps.getNextGeneralData('userKycRequests', '0');
|
const requests = getUserKycRequests();
|
||||||
let hasUploaded = {}
|
let hasUploaded = {}
|
||||||
const renderedRequests = Object.keys(requests).reverse().map((key) => {
|
const renderedRequests = Object.keys(requests).map((key) => {
|
||||||
const { time, tag, message: { docType } } = requests[key];
|
const { time, tag, message: { docType } } = requests[key];
|
||||||
if (!isVerified && tag === 'approved')
|
if (!isVerified && tag.includes('approved'))
|
||||||
isVerified = true;
|
isVerified = true;
|
||||||
if (!tag) {
|
if (!tag) {
|
||||||
concurrentRequests++;
|
concurrentRequests++;
|
||||||
@ -801,7 +843,7 @@
|
|||||||
`}
|
`}
|
||||||
</li>
|
</li>
|
||||||
`;
|
`;
|
||||||
});
|
}).reverse();
|
||||||
return { renderedRequests, concurrentRequests, isVerified, hasUploaded };
|
return { renderedRequests, concurrentRequests, isVerified, hasUploaded };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -864,6 +906,9 @@
|
|||||||
onclick=${revokeKycs}>Revoke selected</button>
|
onclick=${revokeKycs}>Revoke selected</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
${filter === "approved" ? html`
|
||||||
|
<p>Select KYC verification to revoke</p>
|
||||||
|
`: ''}
|
||||||
<ul id="kyc_requests_list" class="flex flex-direction-column gap-0-5" onchange=${handleAddressSelection}>
|
<ul id="kyc_requests_list" class="flex flex-direction-column gap-0-5" onchange=${handleAddressSelection}>
|
||||||
${renderedKycRequests?.length ? renderedKycRequests : html`<li class="text-center" style="padding: 3rem;">No requests found</li>`}
|
${renderedKycRequests?.length ? renderedKycRequests : html`<li class="text-center" style="padding: 3rem;">No requests found</li>`}
|
||||||
</ul>
|
</ul>
|
||||||
@ -871,7 +916,31 @@
|
|||||||
<button id="commit_approvals" class="fab button button--primary" data-count="0" onclick=${initApproval}>Commit approvals</button>
|
<button id="commit_approvals" class="fab button button--primary" data-count="0" onclick=${initApproval}>Commit approvals</button>
|
||||||
</article>
|
</article>
|
||||||
`);
|
`);
|
||||||
|
} else if (floGlobals.isAdmin) {
|
||||||
|
const renderedSubAdmins = floGlobals.subAdmins.map(address => render.approvedAggregatorCard(address));
|
||||||
|
renderElem(getRef('app_body'), html`
|
||||||
|
<article id="home_page">
|
||||||
|
${mainHeader}
|
||||||
|
<section id="verifiers_section" class="grid gap-1">
|
||||||
|
<div class="flex align-center space-between gap-1 flex-wrap">
|
||||||
|
<h3>Approved KYC verifiers</h3>
|
||||||
|
<div class="multi-state-button">
|
||||||
|
<button id="revoke_verifier_button" class="hidden button button--colored button--small" onclick=${revokeVerifiers}>Revoke selected</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ul id="approved_verifiers_list" onchange=${handleVerifierSelection}>
|
||||||
|
${renderedSubAdmins}
|
||||||
|
</ul>
|
||||||
|
<div class="multi-state-button margin-right-auto">
|
||||||
|
<button id="add_sub_admin_button" class="button button--small button--colored" onclick=${() => openPopup('approve_verifier_popup')}>
|
||||||
|
Approve KYC verifiers
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</article>
|
||||||
|
`);
|
||||||
} else {
|
} else {
|
||||||
|
// user KYC home page
|
||||||
const issuersList = floGlobals.subAdmins.map((issuer) => {
|
const issuersList = floGlobals.subAdmins.map((issuer) => {
|
||||||
const { pubKey, name } = floGlobals.appObjects.kycDocs.issuers[issuer] || {};
|
const { pubKey, name } = floGlobals.appObjects.kycDocs.issuers[issuer] || {};
|
||||||
if (!name || !pubKey) return html``;
|
if (!name || !pubKey) return html``;
|
||||||
@ -1326,16 +1395,16 @@
|
|||||||
const floData = `KYC|APPROVE_KYC|${chunk.join('+')}`
|
const floData = `KYC|APPROVE_KYC|${chunk.join('+')}`
|
||||||
txPromises.push(floBlockchainAPI.writeData(floGlobals.myFloID, floData, approverPrivateKey, floGlobals.myFloID))
|
txPromises.push(floBlockchainAPI.writeData(floGlobals.myFloID, floData, approverPrivateKey, floGlobals.myFloID))
|
||||||
}
|
}
|
||||||
const cloudPromises = [...floGlobals.approvalsToBeCommitted.values()].flatMap(({ requestVC, docVC }) => {
|
|
||||||
floGlobals.generalData[floCloudAPI.util.filterKey('userKycRequests')][requestVC].tag = 'approved'
|
|
||||||
return [
|
|
||||||
floCloudAPI.tagApplicationData(requestVC, 'approved'),
|
|
||||||
floCloudAPI.tagApplicationData(docVC, 'approved')
|
|
||||||
]
|
|
||||||
})
|
|
||||||
buttonLoader(getRef('submit_kyc'), true)
|
buttonLoader(getRef('submit_kyc'), true)
|
||||||
try {
|
try {
|
||||||
const [txIds, responses] = await Promise.all([...txPromises, ...cloudPromises])
|
const txIds = await Promise.all(txPromises)
|
||||||
|
const cloudPromises = [...floGlobals.approvalsToBeCommitted.values()].flatMap(({ requestVC, docVC }) => {
|
||||||
|
floGlobals.generalData[floCloudAPI.util.filterKey('userKycRequests')][requestVC].tag = `approved|${txIds.join('+')}`
|
||||||
|
return [
|
||||||
|
floCloudAPI.tagApplicationData(requestVC, `approved|${txIds.join('+')}`),
|
||||||
|
floCloudAPI.tagApplicationData(docVC, `approved|${txIds.join('+')}`)
|
||||||
|
]
|
||||||
|
})
|
||||||
console.log(`Approval request submitted. TXIDs: ${txIds.join(', ')}`)
|
console.log(`Approval request submitted. TXIDs: ${txIds.join(', ')}`)
|
||||||
notify('Users approved successfully', 'success');
|
notify('Users approved successfully', 'success');
|
||||||
floGlobals.approvalsToBeCommitted.clear()
|
floGlobals.approvalsToBeCommitted.clear()
|
||||||
@ -1396,10 +1465,158 @@
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
function setApproverName() {
|
||||||
|
console.log('setApproverName')
|
||||||
|
const approverName = getRef('approver_name_input').value.trim()
|
||||||
|
if (approverName === '') {
|
||||||
|
notify('Please enter a name', 'error')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (approverName.length > 48) {
|
||||||
|
notify('Keep the name shorter than 48 characters', 'error')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!floGlobals.appObjects.kycDocs.issuers[floGlobals.myFloID].name) {
|
||||||
|
floGlobals.appObjects.kycDocs.issuers[floGlobals.myFloID].name = approverName
|
||||||
|
buttonLoader(getRef('set_approver_name__button'), true)
|
||||||
|
floCloudAPI.updateObjectData('kycDocs').then(() => {
|
||||||
|
notify('Name updated successfully', 'success')
|
||||||
|
closePopup()
|
||||||
|
renderElem(getRef('agency_name_container'), html`
|
||||||
|
<h5>Agency name</h5>
|
||||||
|
<h3 id="agency_name">${approverName}</h3>
|
||||||
|
<button class="button button--colored justify-self-start button--small"
|
||||||
|
onclick=${() => openPopup('set_approver_name_popup')}>Change</button>
|
||||||
|
`)
|
||||||
|
}).catch(err => {
|
||||||
|
console.error(err)
|
||||||
|
notify('Error updating name', 'error')
|
||||||
|
}).finally(() => {
|
||||||
|
buttonLoader(getRef('set_approver_name__button'), false)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function showProgress() {
|
||||||
|
const duration = 1000 * 60; // Duration of the progress animation in milliseconds
|
||||||
|
const animation = getRef('progress_bar').children[0].animate([{
|
||||||
|
width: '0%'
|
||||||
|
}, {
|
||||||
|
width: '100%'
|
||||||
|
}], {
|
||||||
|
duration,
|
||||||
|
easing: 'ease-out',
|
||||||
|
fill: 'forwards'
|
||||||
|
})
|
||||||
|
return function () {
|
||||||
|
animation.updatePlaybackRate(100)
|
||||||
|
return new Promise(resolve => {
|
||||||
|
animation.onfinish = () => {
|
||||||
|
resolve()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// show warning when leaving the page with pending approvals
|
||||||
|
window.addEventListener('beforeunload', function (e) {
|
||||||
|
if (floGlobals.approvalsToBeCommitted.size === 0) return
|
||||||
|
// Cancel the event
|
||||||
|
e.preventDefault();
|
||||||
|
// Chrome requires returnValue to be set
|
||||||
|
e.returnValue = 'You need to commit the approvals before leaving the page.';
|
||||||
|
}, { capture: true });
|
||||||
async function clearCredentials() {
|
async function clearCredentials() {
|
||||||
await floDapps.clearCredentials();
|
await floDapps.clearCredentials();
|
||||||
location.reload();
|
location.reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// admin functions
|
||||||
|
function addVerifier() {
|
||||||
|
getRef('verifiers_container').append(html.node`
|
||||||
|
<li class="flex align-center gap-1">
|
||||||
|
<sm-input class="w-100" placeholder="FLO/BTC address"
|
||||||
|
error-text="Invalid address" data-address></sm-input>
|
||||||
|
<button class="button icon-only" onclick=${(e) => { e.target.closest('li').remove() }}>
|
||||||
|
<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="M16 9v10H8V9h8m-1.5-6h-5l-1 1H5v2h14V4h-3.5l-1-1zM18 7H6v12c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7z"/></svg>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
`)
|
||||||
|
getRef('verifiers_container').lastElementChild.querySelector('sm-input').customValidation = floCrypto.validateAddr
|
||||||
|
getRef('verifiers_container').lastElementChild.querySelector('sm-input').focusIn()
|
||||||
|
}
|
||||||
|
async function approveVerifiers() {
|
||||||
|
let addresses = [...getRef('verifiers_container')
|
||||||
|
.querySelectorAll('sm-input')]
|
||||||
|
.map(input => input.value.trim())
|
||||||
|
.filter(address => address !== '' && !floGlobals.subAdmins.includes(address));
|
||||||
|
addresses = [...new Set(addresses)]
|
||||||
|
const confirmation = await getConfirmation(`Approve ${addresses.join(', ')} as KYC verifiers?`, {
|
||||||
|
confirmText: 'Approve',
|
||||||
|
});
|
||||||
|
if (!confirmation) return;
|
||||||
|
buttonLoader(getRef('approve_verifier_button'), true)
|
||||||
|
try {
|
||||||
|
const adminPrivateKey = await floDapps.user.private
|
||||||
|
const floData = `KYC|APPROVE_AGGREGATOR|${addresses.join('+')}`
|
||||||
|
if (floData.length > 1040) {
|
||||||
|
return notify('Too many addresses added. Try removing one.', 'error')
|
||||||
|
}
|
||||||
|
const [txId] = await Promise.all([
|
||||||
|
floBlockchainAPI.writeData(floGlobals.myFloID, floData, adminPrivateKey, floGlobals.myFloID),
|
||||||
|
floDapps.manageAppConfig(adminPrivateKey, addresses)
|
||||||
|
])
|
||||||
|
notify(`Verifiers approval request submitted. TXID: ${txId}`, 'success');
|
||||||
|
floGlobals.subAdmins = [...new Set([...floGlobals.subAdmins, ...addresses])]
|
||||||
|
closePopup()
|
||||||
|
renderHome()
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err)
|
||||||
|
notify('Error approving verifiers', 'error');
|
||||||
|
} finally {
|
||||||
|
buttonLoader(getRef('approve_verifier_button'), false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function handleVerifierSelection(e) {
|
||||||
|
const count = [...getRef('approved_verifiers_list').querySelectorAll('input')].reduce((acc, input) => {
|
||||||
|
if (input.checked) acc++
|
||||||
|
return acc
|
||||||
|
}, 0)
|
||||||
|
if (count) {
|
||||||
|
getRef('revoke_verifier_button').classList.remove('hidden')
|
||||||
|
getRef('revoke_verifier_button').dataset.count = count
|
||||||
|
} else {
|
||||||
|
getRef('revoke_verifier_button').classList.add('hidden')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async function revokeVerifiers() {
|
||||||
|
const confirmation = await getConfirmation(`Revoke selected verifiers?`, {
|
||||||
|
confirmText: 'Revoke',
|
||||||
|
});
|
||||||
|
if (!confirmation) return;
|
||||||
|
const addresses = [...getRef('approved_verifiers_list')
|
||||||
|
.querySelectorAll('input:checked')]
|
||||||
|
.map(input => input.value.trim())
|
||||||
|
buttonLoader(getRef('revoke_verifier_button'), true)
|
||||||
|
try {
|
||||||
|
const adminPrivateKey = await floDapps.user.private
|
||||||
|
const floData = `KYC|REVOKE_AGGREGATOR|${addresses.join('+')}`
|
||||||
|
if (floData.length > 1040) {
|
||||||
|
return notify('Too many addresses selected. Try removing one.', 'error')
|
||||||
|
}
|
||||||
|
const [txId] = await Promise.all([
|
||||||
|
floBlockchainAPI.writeData(floGlobals.myFloID, floData, adminPrivateKey, floGlobals.myFloID),
|
||||||
|
floDapps.manageAppConfig(adminPrivateKey, undefined, addresses)
|
||||||
|
])
|
||||||
|
notify(`Verifiers revoke request submitted. TXID: ${txId}`, 'success');
|
||||||
|
floGlobals.subAdmins = floGlobals.subAdmins.filter(address => !addresses.includes(address))
|
||||||
|
closePopup()
|
||||||
|
renderHome()
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err)
|
||||||
|
notify('Error revoking verifiers', 'error');
|
||||||
|
} finally {
|
||||||
|
buttonLoader(getRef('revoke_verifier_button'), false)
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
<script id="onLoadStartUp">
|
<script id="onLoadStartUp">
|
||||||
router.routeTo('loading')
|
router.routeTo('loading')
|
||||||
@ -1455,11 +1672,8 @@
|
|||||||
floGlobals.myFloID = getFloAddress(floDapps.user.id);
|
floGlobals.myFloID = getFloAddress(floDapps.user.id);
|
||||||
floGlobals.myBtcID = getBtcAddress(floGlobals.myFloID)
|
floGlobals.myBtcID = getBtcAddress(floGlobals.myFloID)
|
||||||
floGlobals.isSubAdmin = floGlobals.subAdmins.includes(floGlobals.myFloID)
|
floGlobals.isSubAdmin = floGlobals.subAdmins.includes(floGlobals.myFloID)
|
||||||
|
floGlobals.isAdmin = floGlobals.myFloID === floGlobals.adminID
|
||||||
try {
|
try {
|
||||||
floGlobals.appObjects.kycDocs.issuers["FFS5hFXG7DBtdgzrLwixZLpenAmsCKRddm"] = {
|
|
||||||
pubKey: "023F54703C693BEC80EE4216542E8552AAA6C53649F218F8C99E8A11AFB205D3F9",
|
|
||||||
name: 'Sairaj mote'
|
|
||||||
} //TODO: remove this
|
|
||||||
if (floGlobals.isSubAdmin) {
|
if (floGlobals.isSubAdmin) {
|
||||||
const promises = []
|
const promises = []
|
||||||
// initialize kycDocs app data structure
|
// initialize kycDocs app data structure
|
||||||
@ -1486,6 +1700,24 @@
|
|||||||
notify('Error adding subadmin public key to kycDocs', 'error')
|
notify('Error adding subadmin public key to kycDocs', 'error')
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
if (!floGlobals.appObjects.kycDocs.issuers[floGlobals.myFloID].name) {
|
||||||
|
openPopup('set_approver_name_popup')
|
||||||
|
renderElem(getRef('agency_name_container'), html`
|
||||||
|
<h5>Agency name</h5>
|
||||||
|
<h3 id="agency_name">Name not set</h3>
|
||||||
|
<button class="button button--colored justify-self-start button--small"
|
||||||
|
onclick="openPopup('set_approver_name_popup')">Set</button>
|
||||||
|
`)
|
||||||
|
} else {
|
||||||
|
renderElem(getRef('agency_name_container'), html`
|
||||||
|
<h5>Agency name</h5>
|
||||||
|
<h3 id="agency_name">${floGlobals.appObjects.kycDocs.issuers[floGlobals.myFloID].name}</h3>
|
||||||
|
<button class="button button--colored justify-self-start button--small"
|
||||||
|
onclick=${() => openPopup('set_approver_name_popup')}>Change</button>
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
} else if (floGlobals.isAdmin) {
|
||||||
|
|
||||||
} else[
|
} else[
|
||||||
// fetch user's kyc requests
|
// fetch user's kyc requests
|
||||||
await floCloudAPI.requestGeneralData('userKycRequests', {
|
await floCloudAPI.requestGeneralData('userKycRequests', {
|
||||||
@ -1511,35 +1743,6 @@
|
|||||||
console.error(error);
|
console.error(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function showProgress() {
|
|
||||||
const duration = 1000 * 60; // Duration of the progress animation in milliseconds
|
|
||||||
const animation = getRef('progress_bar').children[0].animate([{
|
|
||||||
width: '0%'
|
|
||||||
}, {
|
|
||||||
width: '100%'
|
|
||||||
}], {
|
|
||||||
duration,
|
|
||||||
easing: 'ease-out',
|
|
||||||
fill: 'forwards'
|
|
||||||
})
|
|
||||||
return function () {
|
|
||||||
animation.updatePlaybackRate(100)
|
|
||||||
return new Promise(resolve => {
|
|
||||||
animation.onfinish = () => {
|
|
||||||
resolve()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// show warning when leaving the page with pending approvals
|
|
||||||
window.addEventListener('beforeunload', function (e) {
|
|
||||||
if (floGlobals.approvalsToBeCommitted.size === 0) return
|
|
||||||
// Cancel the event
|
|
||||||
e.preventDefault();
|
|
||||||
// Chrome requires returnValue to be set
|
|
||||||
e.returnValue = 'You need to commit the approvals before leaving the page.';
|
|
||||||
}, { capture: true });
|
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user