diff --git a/src/api/composed.ts b/src/api/composed.ts index 700c459..366324e 100644 --- a/src/api/composed.ts +++ b/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('DistributeRewardFund', { + rewardFundID, + payments, + distribute, +}); diff --git a/src/api/types.ts b/src/api/types.ts index 68b38b5..c33d72b 100644 --- a/src/api/types.ts +++ b/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; +} diff --git a/src/views/FundView.vue b/src/views/FundView.vue index b19a3e1..9efa9e2 100644 --- a/src/views/FundView.vue +++ b/src/views/FundView.vue @@ -123,20 +123,23 @@
+

+ Enable contribution consolidation in order to select. + Click to select a row in order to mark a wallet to receive rewards. +

- + - + - +
Wallet Amount TimeBonus
{{ truncateWallet(contribution.wallet, calculateWalletChars(), undefined) }} {{ contribution.amount }} @@ -144,18 +147,29 @@ {{ formatDate(contribution.createdAt, true) }} + {{ calculateReward(contribution.amount.div(fund.fundInfo.price)) }} +
+
+
+
+ +
+
+ +
+
+
@@ -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 = ref(processContributions( +const contributions: Ref = 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