Переглянути джерело

Allow reward distribution

master
Jared 1 рік тому
джерело
коміт
9ccff211ac
3 змінених файлів з 144 додано та 50 видалено
  1. +8
    -0
      src/api/composed.ts
  2. +11
    -0
      src/api/types.ts
  3. +125
    -50
      src/views/FundView.vue

+ 8
- 0
src/api/composed.ts Переглянути файл

@@ -7,6 +7,7 @@ import {
CreateQueueRequest,
CreateQueueResponse,
CreateRewardFundRequest,
DistributeRewardsRequest,
EditQueueRequest,
GetBalanceRequest,
GetBalanceResponse,
@@ -23,6 +24,7 @@ import {
NearlyCompleteFundsRequest,
NearlyCompleteFundsResponse,
QueueMember,
RewardDistributionInfo,
SubmitRewardFundRequest,
SuccessResponse,
} from '@/api/types';
@@ -120,3 +122,9 @@ export const submitRewardFund = (fundID: number, submit: boolean) => controller.
fundID,
submit,
});

export const distributeRewardFund = (rewardFundID: number, payments: RewardDistributionInfo[], distribute: boolean) => controller.post<SuccessResponse, DistributeRewardsRequest>('DistributeRewardFund', {
rewardFundID,
payments,
distribute,
});

+ 11
- 0
src/api/types.ts Переглянути файл

@@ -197,3 +197,14 @@ export interface SubmitRewardFundRequest {
fundID: number;
submit: boolean;
}

export interface RewardDistributionInfo {
destination: string;
amount: number;
}

export interface DistributeRewardsRequest {
rewardFundID: number;
payments: RewardDistributionInfo[];
distribute: boolean;
}

+ 125
- 50
src/views/FundView.vue Переглянути файл

@@ -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



Завантаження…
Відмінити
Зберегти