Browse Source

Componentized the queue editor, among other things

wip/alt-interface
Jared 1 year ago
parent
commit
2599da4a1e
6 changed files with 344 additions and 201 deletions
  1. +52
    -6
      src/App.vue
  2. +43
    -41
      src/api/types.ts
  3. +105
    -0
      src/components/EditQueue.vue
  4. +27
    -0
      src/components/ErrorDisplay.vue
  5. +63
    -116
      src/views/AddFundView.vue
  6. +54
    -38
      src/views/FundView.vue

+ 52
- 6
src/App.vue View File

@@ -2,8 +2,10 @@
<nav class="navbar has-background-grey-dark" role="navigation" aria-label="main navigation"> <nav class="navbar has-background-grey-dark" role="navigation" aria-label="main navigation">
<div class="navbar-brand"> <div class="navbar-brand">
<RouterLink to="/" class="navbar-item"> <RouterLink to="/" class="navbar-item">
<span class="signet-logo title is-3-desktop is-4-mobile has-text-white-ter">
Beignet
<span class="signet-logo title is-3-desktop is-4-mobile">
<template v-for="(element, i) in logoElements" v-bind:key="i">
<span :style="`color: #${element.color}`">{{ element.letter }}</span>
</template>
</span> </span>
</RouterLink> </RouterLink>
</div> </div>
@@ -32,10 +34,13 @@
<div id="content"> <div id="content">
<RouterView v-slot="{ Component }"> <RouterView v-slot="{ Component }">
<Suspense> <Suspense>
<Component :is="Component" />
<Component :is="Component"/>


<template #fallback> <template #fallback>
<span style="font-size: 4em; color: saddlebrown">Loading</span>
<div class="is-flex is-flex-direction-row is-justify-content-center"
style="height: 90vh">
<span style="font-size: 1.25em; color: greenyellow; margin: auto 0">Loading...</span>
</div>
</template> </template>
</Suspense> </Suspense>
</RouterView> </RouterView>
@@ -43,7 +48,8 @@


<footer> <footer>
<div> <div>
Proudly made in Michigan <div class="michigan-icon"></div>
Proudly made in Michigan
<div class="michigan-icon"></div>
</div> </div>
</footer> </footer>
</template> </template>
@@ -58,7 +64,11 @@ import {
import jwtDecode from 'jwt-decode'; import jwtDecode from 'jwt-decode';
import { Claims } from '@/api/types'; import { Claims } from '@/api/types';


const userData = ref({ username: '', privileges: -1, exp: -1 } as Claims);
const userData = ref({
username: '',
privileges: -1,
exp: -1,
} as Claims);


const state = useSessionStorage('jwt', { token: '' }); const state = useSessionStorage('jwt', { token: '' });
if (state.value.token) { if (state.value.token) {
@@ -67,6 +77,42 @@ if (state.value.token) {
} }


const hasToken = computed(() => !!store.getters.getToken); const hasToken = computed(() => !!store.getters.getToken);

interface LogoElement {
letter: string;
color: string;
}

const logoElements: LogoElement[] = [
{
color: '9fe82c',
letter: 'B',
},
{
color: '8ee045',
letter: 'e',
},
{
color: '7dd95c',
letter: 'i',
},
{
color: '6dd373',
letter: 'g',
},
{
color: '5dcb8a',
letter: 'n',
},
{
color: '4cc4a2',
letter: 'e',
},
{
color: '3dbeb8',
letter: 't',
},
];
</script> </script>


<style lang="stylus"> <style lang="stylus">


+ 43
- 41
src/api/types.ts View File

@@ -6,41 +6,30 @@ export enum Privileges {
Admin Admin
} }


export interface Tag {
createdAt: string;
deletedAt: string;
ID: number;
updatedAt: string;
description: string;
active: boolean;
contribution: number;
}

export interface Contribution { export interface Contribution {
createdAt: string; createdAt: string;
amount: number; amount: number;
rewardFundID: number; rewardFundID: number;
tags: Tag[];
transactionID: string; transactionID: string;
wallet: string; wallet: string;
} }


interface Contributions { interface Contributions {
list: Contribution[]
dates: string[]
total: number
list: Contribution[];
dates: string[];
total: number;
} }


export interface RewardFund { export interface RewardFund {
id: number
asset: string
wallet: string
memo: string
amountGoal: number
minContribution: number
contributions: Contribution[] | null
title: string
description: string
id: number;
asset: string;
wallet: string;
memo: string;
amountGoal: number;
minContribution: number;
contributions: Contribution[] | null;
title: string;
description: string;
} }


export interface Queue { export interface Queue {
@@ -57,16 +46,16 @@ export interface CreateQueueResponse {
} }


export interface GetQueuesResponse { export interface GetQueuesResponse {
queues: Queue[]
queues: Queue[];
} }


export interface SuccessResponse { export interface SuccessResponse {
success: boolean
success: boolean;
} }


export interface GetRewardFundRequest { export interface GetRewardFundRequest {
id: number
consolidateContributions: boolean
id: number;
consolidateContributions: boolean;
} }


export interface Bonus { export interface Bonus {
@@ -74,6 +63,19 @@ export interface Bonus {
percent?: number; percent?: number;
} }


export interface CreateRewardFundRequest {
asset: string;
fundWallet: string;
sellingWallet: string;
issuerWallet: string;
memo: string;
minContribution: number;
title: string;
description: string;
queueID?: number | null;
bonuses: Bonus[];
}

export interface FundInfo { export interface FundInfo {
id: number; id: number;
asset: string; asset: string;
@@ -96,9 +98,9 @@ interface Total {
} }


export interface GetRewardFundResponse { export interface GetRewardFundResponse {
fundInfo: FundInfo
contributions: Contributions
total: Total
fundInfo: FundInfo;
contributions: Contributions;
total: Total;
} }


export interface GetBalanceRequest { export interface GetBalanceRequest {
@@ -110,9 +112,9 @@ export interface GetBalanceResponse {
} }


export interface ContributeRequest { export interface ContributeRequest {
privateKey: string
amount: number
rewardFund: number
privateKey: string;
amount: number;
rewardFund: number;
} }


export interface AuthenticationRequest { export interface AuthenticationRequest {
@@ -122,7 +124,7 @@ export interface AuthenticationRequest {


export interface LoginResponse { export interface LoginResponse {
token: string | null; token: string | null;
} // TODO: change shape of fund creation request
}


export interface GetQueueMembersRequest { export interface GetQueueMembersRequest {
id: number; id: number;
@@ -137,21 +139,21 @@ export interface GetRewardFundsRequest {
} }


export interface GetRewardFundsResponse { export interface GetRewardFundsResponse {
rewardFunds: FundInfo[]
total: number
rewardFunds: FundInfo[];
total: number;
} }


export interface Claims { export interface Claims {
username: string
username: string;
privileges: Privileges; privileges: Privileges;
exp: number; exp: number;
} }


export interface GetContributionsRequest { export interface GetContributionsRequest {
id: number
offset: number
forDate: string | undefined
consolidateContributions: boolean
id: number;
offset: number;
forDate: string | undefined;
consolidateContributions: boolean;
} }


export type GetContributionsResponse = Contributions; export type GetContributionsResponse = Contributions;


+ 105
- 0
src/components/EditQueue.vue View File

@@ -0,0 +1,105 @@
<template>
<div class="is-flex is-flex-direction-row is-justify-content-space-between">
<div class="select mr-1">
<select
ref="queueOptions"
v-model="queueSelection"
aria-label="Queue Selection"
@change="changedSelection"
>
<option :value="-2">None</option>
<option :value="-1">New Queue</option>
<option v-for="(queue, i) in queues" v-bind:key="i" :value="queue.id">
{{ queue.name }}
</option>
</select>
</div>
<div v-if="queueSelection === -1" class="is-flex is-flex-direction-row is-flex-grow-1 ml-1">
<input
v-model="queueName"
aria-label="Queue Name"
class="input mr-1"
placeholder="Queue Name"
type="text"
@blur="createdQueue"
>
</div>
<div v-else-if="queueSelection >= 0" class="is-flex-grow-1 ml-1">
<Draggable
v-model="queueMembers"
group="people"
item-key="id"
@end="drag=false"
@start="drag=true">
>
<template #item="{ queue }">
{{ queue.title }}
</template>
<div>{{ queue.title }}</div>
</Draggable>
</div>
</div>
</template>

<script lang="ts" setup>
import {
ref,
watch,
} from 'vue';
import {
GetQueueMembersRequest,
GetQueueMembersResponse,
GetQueuesResponse,
Queue,
RewardFund,
} from '@/api/types';
import Draggable from 'vuedraggable';
import SignetRequestController from '@/api/requests';
import store from '@/store';

const controller = new SignetRequestController(store.getters.getToken);

// eslint-disable-next-line no-undef
const emits = defineEmits(['selected', 'created']);

const queueSelection = ref(undefined as number | undefined);
const queueName = ref(undefined as string | undefined);
const queueMembers = ref(undefined as RewardFund[] | undefined);
const drag = ref(false);

const queues = ref([] as Queue[]);
const fetchQueues = async () => {
const v = await controller.post<GetQueuesResponse, null>('GetQueues', null);
if (v) {
queues.value = v.queues;
}
};

await fetchQueues();

const changedSelection = () => {
emits('selected', queueSelection.value);
};

const createdQueue = () => {
emits('created', queueName.value);
};

const populateQueueMembers = async (id: number) => {
const resp = await controller.post<GetQueueMembersResponse, GetQueueMembersRequest>('GetQueueMembers', { id });
queueMembers.value = resp?.members;
};

watch(queueSelection, async (newValue) => {
if (newValue !== undefined && newValue >= 0) {
await populateQueueMembers(newValue);
}
});

// TODO: send new order of queued items
</script>

<style lang="stylus" scoped>
input::placeholder, textarea::placeholder
color #7d7d7d
</style>

+ 27
- 0
src/components/ErrorDisplay.vue View File

@@ -0,0 +1,27 @@
<template>
<article class="message is-danger">
<div class="message-header">
<p>Errors</p>
</div>
<div class="message-body">
<ol class="ml-2">
<template v-for="(err, i) in props.errors" v-bind:key="i">
<li v-show="err.condition">
{{ err.text }}
</li>
</template>
</ol>
</div>
</article>
</template>

<script setup lang="ts">
export interface SignetError {
text: string;
condition: boolean;
}

// eslint-disable-next-line no-undef
const props = defineProps<{ 'errors': SignetError[] }>();

</script>

+ 63
- 116
src/views/AddFundView.vue View File

@@ -1,100 +1,64 @@
<template> <template>
<div class="container is-max-desktop">
<section class="section is-small">
<div class="title is-4 has-text-white-ter has-text-centered">Add Fund</div>
<section class="section px-0 py-4">
<div class="title is-5 has-text-white-ter">Post</div>
<div class="control my-2">
<input class="input is-normal has-background-white has-text-black" type="text"
placeholder="Title" aria-label="Title" v-model="title">
</div>
<div class="control my-2">
<div class="container is-max-desktop">
<section class="section is-small">
<div class="title is-4 has-text-white-ter has-text-centered">Add Fund</div>
<section class="section px-0 py-4">
<div class="title is-5 has-text-white-ter">Post</div>
<div class="control my-2">
<input class="input is-normal has-background-white has-text-black" type="text"
placeholder="Title" aria-label="Title" v-model="title">
</div>
<div class="control my-2">
<textarea class="textarea is-normal has-background-white has-text-black" <textarea class="textarea is-normal has-background-white has-text-black"
placeholder="Description" aria-label="Description" v-model="description"> placeholder="Description" aria-label="Description" v-model="description">
</textarea> </textarea>
</div>
</section>
<section class="section px-0 py-4">
<div class="title is-5 has-text-white-ter">Wallet</div>
<div class="control my-2">
<input class="input is-normal has-background-white has-text-black" type="text"
placeholder="Fund Wallet" aria-label="Fund Wallet" v-model="fundWallet">
</div>
<div class="control my-2">
<input class="input is-normal has-background-white has-text-black" type="text"
placeholder="Selling Wallet" aria-label="Selling Wallet" v-model="sellWallet">
</div>
<div class="control my-2">
<input class="input is-normal has-background-white has-text-black" type="text"
placeholder="Issuer Wallet" aria-label="Issuer Wallet" v-model="issuerWallet">
</div>
</section>
<section class="section px-0 py-4">
<div class="title is-5 has-text-white-ter">Fund</div>
<div class="control my-2 is-flex is-justify-content-space-between">
<input class="input is-normal mr-1 has-background-white has-text-black" type="text"
placeholder="Asset Code" aria-label="Asset" v-model="asset">
<input class="input is-normal mx-1 has-background-white has-text-black" type="text"
placeholder="Memo" aria-label="Memo" v-model="memo">
<input class="input is-normal ml-1 has-background-white has-text-black" type="number"
placeholder="Min Contribution" aria-label="Min Contribution"
v-model="minContribution">
</div>
</section>
<section class="section px-0 py-4">
<div class="title is-5 has-text-white-ter">Bonus Structure</div>
<FundTierInput @save="saveBonuses" />
</section>
<section class="section px-0 py-4">
<div class="title is-5 has-text-white-ter">Queue</div>
<div class="is-flex is-flex-direction-row is-justify-content-space-between">
<div class="select mr-1">
<select
v-model="queueSelection"
aria-label="Queue Selection"
ref="queueOptions"
>
<option :value="-2">None</option>
<option :value="-1">New Queue</option>
<option :value="queue.id" v-for="(queue, i) in queues" v-bind:key="i">
{{ queue.name }}
</option>
</select>
</div> </div>
<div class="is-flex is-flex-direction-row is-flex-grow-1 ml-1" v-if="queueSelection === -1">
<input
class="input mr-1"
type="text"
placeholder="Queue Name"
v-model="queueName"
aria-label="Queue Name"
>
</section>
<section class="section px-0 py-4">
<div class="title is-5 has-text-white-ter">Wallet</div>
<div class="control my-2">
<input class="input is-normal has-background-white has-text-black" type="text"
placeholder="Fund Wallet" aria-label="Fund Wallet" v-model="fundWallet">
</div>
<div class="control my-2">
<input class="input is-normal has-background-white has-text-black" type="text"
placeholder="Selling Wallet" aria-label="Selling Wallet" v-model="sellWallet">
</div>
<div class="control my-2">
<input class="input is-normal has-background-white has-text-black" type="text"
placeholder="Issuer Wallet" aria-label="Issuer Wallet" v-model="issuerWallet">
</div> </div>
<div class="is-flex-grow-1 ml-1" v-else-if="queueSelection >= 0">
<Draggable
v-model="queueMembers"
group="people"
@start="drag=true"
@end="drag=false"
item-key="id">
>
<template #item="{ queue }">
{{ queue.title }}
</template>
<div>{{ queue.title }}</div>
</Draggable>
</section>
<section class="section px-0 py-4">
<div class="title is-5 has-text-white-ter">Fund</div>
<div class="control my-2 is-flex is-justify-content-space-between">
<input class="input is-normal mr-1 has-background-white has-text-black" type="text"
placeholder="Asset Code" aria-label="Asset" v-model="asset">
<input class="input is-normal mx-1 has-background-white has-text-black" type="text"
placeholder="Memo" aria-label="Memo" v-model="memo">
<input class="input is-normal ml-1 has-background-white has-text-black" type="number"
placeholder="Min Contribution" aria-label="Min Contribution"
v-model="minContribution">
</div> </div>
</div>
</section>
<section class="section px-0 py-4">
<div class="title is-5 has-text-white-ter">Bonus Structure</div>
<FundTierInput @save="saveBonuses"/>
</section>
<section class="section px-0 py-4">
<div class="title is-5 has-text-white-ter">Queue</div>
<EditQueue @created="setQueueName" @selected="setQueueSelection"/>
</section>
</section> </section>
</section>
<div class="buttons is-flex is-justify-content-end mt-5">
<button
class="button is-success"
:class="requesting ? 'is-loading' : ''"
@click="submit"
>Submit</button>
<div class="buttons is-flex is-justify-content-end mt-5">
<button
class="button is-success"
:class="requesting ? 'is-loading' : ''"
@click="submit"
>Submit
</button>
</div>
</div> </div>
</div>
</template> </template>


<script setup lang="ts"> <script setup lang="ts">
@@ -104,23 +68,15 @@ import {
Bonus, Bonus,
CreateQueueRequest, CreateQueueRequest,
CreateQueueResponse, CreateQueueResponse,
FundInfo,
GetQueueMembersRequest,
GetQueueMembersResponse,
GetQueuesResponse,
Queue,
RewardFund,
CreateRewardFundRequest,
SuccessResponse, SuccessResponse,
} from '@/api/types'; } from '@/api/types';
import {
ref,
watch,
} from 'vue';
import { ref } from 'vue';
import store from '@/store'; import store from '@/store';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import FundTierInput from '@/components/FundTierInput.vue'; import FundTierInput from '@/components/FundTierInput.vue';
import { sanitize } from '@/lib/helpers'; import { sanitize } from '@/lib/helpers';
import Draggable from 'vuedraggable';
import EditQueue from '@/components/EditQueue.vue';


const router = useRouter(); const router = useRouter();


@@ -138,20 +94,17 @@ const asset = ref('');
const memo = ref(''); const memo = ref('');
const minContribution = ref(undefined as number | undefined); const minContribution = ref(undefined as number | undefined);


const queueSelection = ref(undefined as number | undefined);
const queueName = ref(undefined as string | undefined); const queueName = ref(undefined as string | undefined);
const queueMembers = ref(undefined as RewardFund[] | undefined);
const drag = ref(false);
const queueSelection = ref(undefined as number | undefined);
// TODO: figure out why the above vars are not reactive


const queues = ref([] as Queue[]);
const fetchQueues = async () => {
const v = await controller.post<GetQueuesResponse, null>('GetQueues', null);
if (v) {
queues.value = v.queues;
}
const setQueueName = (name: string) => {
queueName.value = name;
}; };


await fetchQueues();
const setQueueSelection = (val: number) => {
queueSelection.value = val;
};


const bonuses = ref([] as Bonus[]); const bonuses = ref([] as Bonus[]);
const saveBonuses = (evt: Bonus[]) => { const saveBonuses = (evt: Bonus[]) => {
@@ -180,7 +133,7 @@ const submit = async () => {
} else { } else {
forQueue.value = queueSelection.value; forQueue.value = queueSelection.value;
} }
const resp = await controller.post<SuccessResponse, Partial<FundInfo>>('CreateRewardFund', {
const resp = await controller.post<SuccessResponse, CreateRewardFundRequest>('CreateRewardFund', {
asset: asset.value, asset: asset.value,
fundWallet: sanitize(fundWallet.value), fundWallet: sanitize(fundWallet.value),
sellingWallet: sanitize(sellWallet.value), sellingWallet: sanitize(sellWallet.value),
@@ -201,12 +154,6 @@ const submit = async () => {
} }
}; };


watch(queueSelection, async (newValue) => {
if (newValue !== undefined && newValue >= 0) {
const resp = await controller.post<GetQueueMembersResponse, GetQueueMembersRequest>('GetQueueMembers', { id: newValue });
queueMembers.value = resp?.members;
}
});
</script> </script>


<style scoped lang="stylus"> <style scoped lang="stylus">


+ 54
- 38
src/views/FundView.vue View File

@@ -82,30 +82,7 @@
<div class="title is-size-4 has-text-white-ter has-text-centered"> <div class="title is-size-4 has-text-white-ter has-text-centered">
Contribute Contribute
</div> </div>
<article class="message is-danger" v-if="invalidContributionForm">
<div class="message-header">
<p>Errors</p>
</div>
<div class="message-body">
<ol class="ml-2">
<li v-show="amt === 0 && !fund.fundInfo.minContribution">
Amount must be greater than 0
</li>
<li v-show="amt < fund.fundInfo.minContribution">
Amount is less than the minimum contribution
</li>
<li v-show="amt > amountAvailable ">
Not enough {{ fund.fundInfo.asset }} for sale in ICO
</li>
<li v-show="amt > acctBalance">
Not enough XLM to send ({{ amt.toLocaleString() }})
</li>
<li v-show="unknownAcct">
Could not find Stellar wallet
</li>
</ol>
</div>
</article>
<ErrorDisplay :errors="errs" v-if="invalidContributionForm"/>
<div class="control my-2"> <div class="control my-2">
<input class="input is-normal has-background-white has-text-black" type="text" <input class="input is-normal has-background-white has-text-black" type="text"
placeholder="Private Key" aria-label="Wallet" v-model="pk" @blur="queryAccount"> placeholder="Private Key" aria-label="Wallet" v-model="pk" @blur="queryAccount">
@@ -127,7 +104,8 @@
:class="loading.contribution ? 'is-loading' : ''" :class="loading.contribution ? 'is-loading' : ''"
:disabled="invalidContributionForm" :disabled="invalidContributionForm"
@click="makeContribution" @click="makeContribution"
>Submit</button>
>Submit
</button>
</div> </div>
</section> </section>
<section class="section is-small" v-if="contributions.length > 0"> <section class="section is-small" v-if="contributions.length > 0">
@@ -172,8 +150,8 @@
<td>{{ truncateWallet(contribution.wallet, 6, undefined) }}</td> <td>{{ truncateWallet(contribution.wallet, 6, undefined) }}</td>
<td>{{ contribution.amount }}</td> <td>{{ contribution.amount }}</td>
<td v-if="!enableConsolidation"> <td v-if="!enableConsolidation">
<span class="transaction-date" :title="formatDate(contribution.CreatedAt, true)">
{{ formatDate(contribution.CreatedAt, true) }}
<span class="transaction-date" :title="formatDate(contribution.createdAt, true)">
{{ formatDate(contribution.createdAt, true) }}
</span> </span>
</td> </td>
<td v-else> <td v-else>
@@ -243,6 +221,7 @@ import {
} from '@/lib/helpers'; } from '@/lib/helpers';
import * as luxon from 'luxon'; import * as luxon from 'luxon';
import hasPermission from '@/lib/auth'; import hasPermission from '@/lib/auth';
import ErrorDisplay, { SignetError } from '@/components/ErrorDisplay.vue';


const controller = new SignetRequestController(store.getters.getToken); const controller = new SignetRequestController(store.getters.getToken);


@@ -253,7 +232,8 @@ const { id } = route.params;
const identifier = parseInt(id as string, 10); const identifier = parseInt(id as string, 10);


const formatDate = (time: string, includeTime = false) => { const formatDate = (time: string, includeTime = false) => {
const s = luxon.DateTime.fromISO(time).toUTC();
const s = luxon.DateTime.fromISO(time)
.toUTC();
const date = s.toFormat('yyyy-LLL-dd'); const date = s.toFormat('yyyy-LLL-dd');
return includeTime ? `${date} ${s.toFormat('HH:mm')} (UTC)` : date; return includeTime ? `${date} ${s.toFormat('HH:mm')} (UTC)` : date;
}; };
@@ -283,11 +263,18 @@ const enableConsolidation = ref(false);
const fund = ref( const fund = ref(
{ {
fundInfo: {} as FundInfo, fundInfo: {} as FundInfo,
contributions: { list: [], dates: [] as string[], total: 0 },
contributions: {
list: [],
dates: [] as string[],
total: 0,
},
total: { amountHeld: 0 }, total: { amountHeld: 0 },
} as GetRewardFundResponse | null, } as GetRewardFundResponse | null,
); );
const fundDetails = ref([{ title: '', val: '' }]);
const fundDetails = ref([{
title: '',
val: '',
}]);


fund.value = await controller.post<GetRewardFundResponse, GetRewardFundRequest>('GetRewardFund', { fund.value = await controller.post<GetRewardFundResponse, GetRewardFundRequest>('GetRewardFund', {
id: identifier, id: identifier,
@@ -341,10 +328,10 @@ const hasInvalidValues = () => {
if (!fund.value) throw new Error('Fund was not loaded!'); if (!fund.value) throw new Error('Fund was not loaded!');
return [pk, amt].every((v) => v.value !== undefined && v.value !== '') return [pk, amt].every((v) => v.value !== undefined && v.value !== '')
&& (amt.value === 0 && (amt.value === 0
|| amt.value! > amountAvailable.value
|| amt.value! < fund.value.fundInfo.minContribution
|| (acctBalance.value && amt.value! > acctBalance.value)
|| unknownAcct.value);
|| amt.value! > amountAvailable.value
|| amt.value! < fund.value.fundInfo.minContribution
|| (acctBalance.value && amt.value! > acctBalance.value)
|| unknownAcct.value);
}; };


const invalidContributionForm = computed(() => hasInvalidValues()); const invalidContributionForm = computed(() => hasInvalidValues());
@@ -376,6 +363,33 @@ const calculateReward = (bought: number) => {


const fixNewlines = (s: string) => s.replace('\n', '<br/>'); const fixNewlines = (s: string) => s.replace('\n', '<br/>');


const errs: SignetError[] = [
{
text: 'Amount is required',
condition: amt.value === undefined,
},
{
text: 'Amount must be greater than 0',
condition: amt.value && amt.value === 0 && !fund.value.fundInfo.minContribution,
},
{
text: 'Amount is less than the minimum contribution',
condition: amt.value && amt.value < fund.value.fundInfo.minContribution,
},
{
text: `Not enough ${fund.value.fundInfo.asset} for sale in ICO`,
condition: amt.value && amt.value > amountAvailable.value,
},
{
text: `Not enough XLM to send (${amt.value?.toLocaleString()})`,
condition: amt.value && acctBalance.value && amt.value > acctBalance.value,
},
{
text: 'Could not find Stellar wallet',
condition: unknownAcct,
},
];

document.title = `Beignet - ${fund.value.fundInfo.title}`; document.title = `Beignet - ${fund.value.fundInfo.title}`;


watch(selectedDate, async (newVal) => { watch(selectedDate, async (newVal) => {
@@ -427,7 +441,7 @@ const {
status, status,
data, data,
} = useWebSocket( } = useWebSocket(
'ws://127.0.0.1:7300/ContributorStream', // TODO: change url
'ws://127.0.0.1:7300/ContributorStream',
{ {
immediate: true, immediate: true,
autoReconnect: true, autoReconnect: true,
@@ -497,13 +511,15 @@ watch(data, (newVal) => {
getCurrentBonus(); getCurrentBonus();
if (status.value === 'OPEN') { if (status.value === 'OPEN') {
const v = JSON.parse(newVal.trim()) as Contribution; const v = JSON.parse(newVal.trim()) as Contribution;
v.CreatedAt = luxon.DateTime.now().toISO();
const formattedDate = formatDate(v.CreatedAt);
v.createdAt = luxon.DateTime.now()
.toISO();
const formattedDate = formatDate(v.createdAt);
if (!selectableDates.value.includes(formattedDate)) { if (!selectableDates.value.includes(formattedDate)) {
selectableDates.value.push(formattedDate); selectableDates.value.push(formattedDate);
} }
if (enableConsolidation.value && contributions.value if (enableConsolidation.value && contributions.value
&& contributions.value.map((c: Contribution) => c.wallet).includes(v.wallet)) {
&& contributions.value.map((c: Contribution) => c.wallet)
.includes(v.wallet)) {
const hasContribution = contributions.value.find((c: Contribution) => c.wallet === v.wallet); const hasContribution = contributions.value.find((c: Contribution) => c.wallet === v.wallet);
if (!hasContribution) throw new Error('Something went wrong'); if (!hasContribution) throw new Error('Something went wrong');
hasContribution.amount += v.amount; hasContribution.amount += v.amount;


Loading…
Cancel
Save