|
|
@@ -123,20 +123,23 @@ |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<div id="contribution-container" @scroll="loadMoreIfNeeded"> |
|
|
|
<p v-if="store.getters.getToken && hasPermission(Privileges.Admin)"> |
|
|
|
Enable contribution consolidation in order to select. |
|
|
|
Click to select a row in order to mark a wallet to receive rewards. |
|
|
|
</p> |
|
|
|
<table class="contribution-table table is-fullwidth"> |
|
|
|
<thead> |
|
|
|
<tr> |
|
|
|
<th>Wallet</th> |
|
|
|
<th>Amount</th> |
|
|
|
<th v-if="!enableConsolidation">Time</th> |
|
|
|
<template v-else> |
|
|
|
<th>Bonus</th> |
|
|
|
<th v-if="store.getters.getToken && hasPermission(Privileges.Admin)">Post</th> |
|
|
|
</template> |
|
|
|
<th v-else>Bonus</th> |
|
|
|
</tr> |
|
|
|
</thead> |
|
|
|
<tbody> |
|
|
|
<tr v-for="(contribution, i) in contributions" v-bind:key="i"> |
|
|
|
<tr v-for="(contribution, i) in contributions" |
|
|
|
:class="{ 'contribution-selected': contribution.selected }" |
|
|
|
v-bind:key="i" @click="selectContribution(contribution)"> |
|
|
|
<td>{{ truncateWallet(contribution.wallet, calculateWalletChars(), undefined) }}</td> |
|
|
|
<td>{{ contribution.amount }}</td> |
|
|
|
<td v-if="!enableConsolidation"> |
|
|
@@ -144,18 +147,29 @@ |
|
|
|
{{ formatDate(contribution.createdAt, true) }} |
|
|
|
</span> |
|
|
|
</td> |
|
|
|
<template v-else> |
|
|
|
<td> |
|
|
|
<span>{{ calculateReward(contribution.amount.div(fund.fundInfo.price)) }}</span> |
|
|
|
</td> |
|
|
|
<td v-if="store.getters.getToken && hasPermission(Privileges.Admin)"> |
|
|
|
<button class="button is-small is-outlined">Send</button> |
|
|
|
</td> |
|
|
|
</template> |
|
|
|
<td v-else> |
|
|
|
<span>{{ calculateReward(contribution.amount.div(fund.fundInfo.price)) }}</span> |
|
|
|
</td> |
|
|
|
</tr> |
|
|
|
</tbody> |
|
|
|
</table> |
|
|
|
</div> |
|
|
|
<div v-if="store.getters.getToken && hasPermission(Privileges.Admin)"> |
|
|
|
<div class="box is-flex is-flex-direction-row is-justify-content-space-between"> |
|
|
|
<div class="my-auto"> |
|
|
|
<label for="distribute-confirm" class="checkbox"> |
|
|
|
<input type="checkbox" id="distribute-confirm" v-model="allowDistribution"> |
|
|
|
Allow Distribution |
|
|
|
</label> |
|
|
|
</div> |
|
|
|
<div> |
|
|
|
<button class="button is-success" |
|
|
|
:disabled="!allowDistribution" @click="distributeFund"> |
|
|
|
Distribute Rewards |
|
|
|
</button> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</section> |
|
|
|
<section class="section is-small px-0" |
|
|
|
v-if="store.getters.getToken && hasPermission(Privileges.Admin)"> |
|
|
@@ -236,6 +250,7 @@ import ErrorDisplay, { SignetError } from '@/components/ErrorDisplay.vue'; |
|
|
|
import { |
|
|
|
contribute, |
|
|
|
deleteRewardFund, |
|
|
|
distributeRewardFund, |
|
|
|
getBalance, |
|
|
|
getContributions, |
|
|
|
getRewardFund, |
|
|
@@ -274,40 +289,6 @@ const amount = computed({ |
|
|
|
}, |
|
|
|
}); |
|
|
|
|
|
|
|
const allowDelete = ref(false); |
|
|
|
const deleteFund = async () => { |
|
|
|
const deleted = await deleteRewardFund(identifier, allowDelete.value); |
|
|
|
if (deleted && deleted.success) { |
|
|
|
await router.push('/'); |
|
|
|
} |
|
|
|
}; |
|
|
|
const delTimeout = ref(undefined as number | undefined); |
|
|
|
|
|
|
|
watch(allowDelete, () => { |
|
|
|
if (delTimeout.value) window.clearTimeout(delTimeout.value); |
|
|
|
delTimeout.value = window.setTimeout(() => { |
|
|
|
allowDelete.value = false; |
|
|
|
delTimeout.value = undefined; |
|
|
|
}, 10000); |
|
|
|
}); |
|
|
|
|
|
|
|
const allowSubmit = ref(false); |
|
|
|
const submitFund = async () => { |
|
|
|
const submitted = await submitRewardFund(identifier, allowSubmit.value); |
|
|
|
if (submitted && submitted.success) { |
|
|
|
console.log('submitted'); // TODO: provide feedback for submission |
|
|
|
} |
|
|
|
}; |
|
|
|
const subTimeout = ref(undefined as number | undefined); |
|
|
|
|
|
|
|
watch(allowSubmit, () => { |
|
|
|
if (subTimeout.value) window.clearTimeout(subTimeout.value); |
|
|
|
subTimeout.value = window.setTimeout(() => { |
|
|
|
allowSubmit.value = false; |
|
|
|
subTimeout.value = undefined; |
|
|
|
}, 10000); |
|
|
|
}); |
|
|
|
|
|
|
|
const enableConsolidation = ref(false); |
|
|
|
const fund = ref( |
|
|
|
{ |
|
|
@@ -354,9 +335,14 @@ if (fund.value.contributions.dates) { |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
interface SelectableContribution extends Contribution { |
|
|
|
selected: boolean; |
|
|
|
} |
|
|
|
|
|
|
|
const processContributions = (contributions: Contribution[]) => contributions.map((c) => ({ |
|
|
|
...c, |
|
|
|
amount: new Decimal(c.amount), |
|
|
|
selected: false, |
|
|
|
})); |
|
|
|
|
|
|
|
const reward = ref(new Decimal(0)); |
|
|
@@ -364,7 +350,7 @@ const maxBonus = ref(0); |
|
|
|
const bonus = ref(undefined as Bonus | undefined); |
|
|
|
const amountHeld = ref(new Decimal(fund.value.total.amountHeld)); |
|
|
|
const amountAvailable = ref(new Decimal(fund.value.fundInfo.amountAvailable)); |
|
|
|
const contributions: Ref<Contribution[]> = ref(processContributions( |
|
|
|
const contributions: Ref<SelectableContribution[]> = ref(processContributions( |
|
|
|
fund.value.contributions.list ?? [], |
|
|
|
)); |
|
|
|
const offset = ref(contributions.value.length); |
|
|
@@ -386,7 +372,7 @@ const calcPctComplete = () => round(amountHeld.value.div(amountAvailable.value) |
|
|
|
.mul(100) |
|
|
|
.toNumber()); |
|
|
|
|
|
|
|
const calculateWalletChars = () => round(width.value / 114, 0); |
|
|
|
const calculateWalletChars = () => round(width.value / 110, 0); |
|
|
|
|
|
|
|
const hasInvalidValues = () => { |
|
|
|
if (!fund.value) throw new Error('Fund was not loaded!'); |
|
|
@@ -427,6 +413,86 @@ const calculateReward = (bought: Decimal) => { |
|
|
|
return reward.value.toLocaleString(); |
|
|
|
}; |
|
|
|
|
|
|
|
const selectedContributions = ref([] as Contribution[]); |
|
|
|
const selectContribution = (contribution: SelectableContribution) => { |
|
|
|
if (!store.getters.getToken || !hasPermission(Privileges.Admin)) return; |
|
|
|
if (enableConsolidation.value) { |
|
|
|
if (!contribution.selected) { |
|
|
|
selectedContributions.value.push(contribution); |
|
|
|
} else { |
|
|
|
const existingContributionIndex = selectedContributions.value.findIndex( |
|
|
|
(c) => c.wallet === contribution.wallet, |
|
|
|
); |
|
|
|
selectedContributions.value.splice(existingContributionIndex, 1); |
|
|
|
} |
|
|
|
const newContribution: SelectableContribution = { |
|
|
|
...contribution, |
|
|
|
selected: !contribution.selected, |
|
|
|
}; |
|
|
|
const toReplaceIndex = contributions.value.findIndex((c) => c.wallet === contribution.wallet); |
|
|
|
if (toReplaceIndex === -1) throw new Error('Could not find contribution to select'); |
|
|
|
contributions.value.splice(toReplaceIndex, 1, newContribution); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
const allowDelete = ref(false); |
|
|
|
const deleteFund = async () => { |
|
|
|
const deleted = await deleteRewardFund(identifier, allowDelete.value); |
|
|
|
if (deleted && deleted.success) { |
|
|
|
await router.push('/'); |
|
|
|
} |
|
|
|
}; |
|
|
|
const delTimeout = ref(undefined as number | undefined); |
|
|
|
|
|
|
|
watch(allowDelete, () => { |
|
|
|
if (delTimeout.value) window.clearTimeout(delTimeout.value); |
|
|
|
delTimeout.value = window.setTimeout(() => { |
|
|
|
allowDelete.value = false; |
|
|
|
delTimeout.value = undefined; |
|
|
|
}, 10000); |
|
|
|
}); |
|
|
|
|
|
|
|
const allowDistribution = ref(false); |
|
|
|
const distributeFund = async () => { |
|
|
|
const distributed = await distributeRewardFund( |
|
|
|
identifier, |
|
|
|
selectedContributions.value.map((c) => ({ |
|
|
|
destination: c.wallet, |
|
|
|
amount: c.amount.toNumber(), |
|
|
|
})), |
|
|
|
allowDistribution.value, |
|
|
|
); |
|
|
|
if (distributed && distributed.success) { |
|
|
|
console.log('distributed'); // TODO: provide feedback for distribution |
|
|
|
} |
|
|
|
}; |
|
|
|
const distTimeout = ref(undefined as number | undefined); |
|
|
|
|
|
|
|
watch(allowDistribution, () => { |
|
|
|
if (distTimeout.value) window.clearTimeout(distTimeout.value); |
|
|
|
distTimeout.value = window.setTimeout(() => { |
|
|
|
allowDistribution.value = false; |
|
|
|
distTimeout.value = undefined; |
|
|
|
}, 10000); |
|
|
|
}); |
|
|
|
|
|
|
|
const allowSubmit = ref(false); |
|
|
|
const submitFund = async () => { |
|
|
|
const submitted = await submitRewardFund(identifier, allowSubmit.value); |
|
|
|
if (submitted && submitted.success) { |
|
|
|
console.log('submitted'); // TODO: provide feedback for submission |
|
|
|
} |
|
|
|
}; |
|
|
|
const subTimeout = ref(undefined as number | undefined); |
|
|
|
|
|
|
|
watch(allowSubmit, () => { |
|
|
|
if (subTimeout.value) window.clearTimeout(subTimeout.value); |
|
|
|
subTimeout.value = window.setTimeout(() => { |
|
|
|
allowSubmit.value = false; |
|
|
|
subTimeout.value = undefined; |
|
|
|
}, 10000); |
|
|
|
}); |
|
|
|
|
|
|
|
const errs: SignetError[] = [ |
|
|
|
{ |
|
|
|
text: 'Amount is required', |
|
|
@@ -553,7 +619,10 @@ watch(data, (newVal) => { |
|
|
|
if (!fund.value) throw new Error('Fund not found'); |
|
|
|
getCurrentBonus(); |
|
|
|
if (status.value === 'OPEN') { |
|
|
|
const v = JSON.parse(newVal.trim()) as Contribution; |
|
|
|
const v = { |
|
|
|
...JSON.parse(newVal.trim()), |
|
|
|
selected: false, |
|
|
|
} as SelectableContribution; |
|
|
|
v.createdAt = luxon.DateTime.now() |
|
|
|
.toISO(); |
|
|
|
const formattedDate = formatDate(v.createdAt); |
|
|
@@ -597,6 +666,9 @@ watch(data, (newVal) => { |
|
|
|
.signet-asset-name |
|
|
|
cursor pointer |
|
|
|
|
|
|
|
tbody tr |
|
|
|
cursor pointer |
|
|
|
|
|
|
|
#contribution-container |
|
|
|
max-height 360px |
|
|
|
overflow-y auto |
|
|
@@ -610,6 +682,9 @@ watch(data, (newVal) => { |
|
|
|
.fund-details |
|
|
|
width 182px |
|
|
|
|
|
|
|
.contribution-selected |
|
|
|
background-color #e0cdbb |
|
|
|
|
|
|
|
#consolidate |
|
|
|
vertical-align middle |
|
|
|
|
|
|
|