Skip to content

Commit 8c5741a

Browse files
committed
SImplet FE (improvements and fixes)
1 parent 37a7a81 commit 8c5741a

23 files changed

Lines changed: 183 additions & 143 deletions

File tree

frontend/app.vue

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,6 @@
11
<script lang="ts" setup>
2-
import { ClaimType } from '~/lib/values/general.values';
3-
4-
const config = useRuntimeConfig();
5-
const title =
6-
config.public.CLAIM_TYPE === ClaimType.FREE_MINT
7-
? 'NFT Wild West'
8-
: config.public.CLAIM_TYPE === ClaimType.POAP
9-
? 'NFT Event Edition'
10-
: 'NFT Brand Booster';
11-
12-
useHead({ title });
2+
const { claimTypeName } = useScreen();
3+
useHead({ title: claimTypeName });
134
</script>
145

156
<template>

frontend/assets/styles/_naive-ui.css

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,12 @@ body > .n-message-container {
378378
.n-base-selection .n-base-suffix {
379379
@apply right-2;
380380
}
381+
382+
.n-pagination-item {
383+
&--clickable {
384+
@apply text-black font-semibold;
385+
}
386+
}
381387
}
382388

383389
/** Popover */

frontend/components/parts/PreviewUpload.vue

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,19 +27,49 @@
2727
<p class="mt-2 text-xs">Your balance: {{ userStore.balance }} credits</p>
2828
</div>
2929
</div>
30-
<Btn size="large" type="primary" @click="$emit('deploy')"> Add new recipients </Btn>
30+
<Btn size="large" type="primary" @click="deploy()"> Send minting invites </Btn>
3131
</div>
3232
</div>
33+
34+
<n-modal v-model:show="isSpendingWarningOpen" class="max-w-xl" preset="card" size="small" :mask-closable="false">
35+
<div class="text-center">
36+
<NuxtIcon name="icon/alert" class="inline-block text-xl text-pink" filled />
37+
<h4 class="my-4">Email delivery: Test mode</h4>
38+
<span>
39+
You’re using Apillon’s default email sender. It’s great for testing, but emails will not come from your domain
40+
and may land in the <strong>spam folder</strong>.
41+
</span>
42+
</div>
43+
<template #footer>
44+
<div class="flex gap-4 mb-4">
45+
<div class="w-1/2">
46+
<Btn class="px-4" type="secondary" size="large" @click="$emit('deploy')">Use test now, change it later</Btn>
47+
</div>
48+
<div class="w-1/2">
49+
<Btn size="large" href="https://app.apillon.io/dashboard/simplet/list" @click="isLoggedIn = false">
50+
Set up my sender now
51+
</Btn>
52+
</div>
53+
</div>
54+
</template>
55+
</n-modal>
3356
</template>
3457

3558
<script setup lang="ts">
36-
defineEmits(['back', 'deploy']);
59+
const emit = defineEmits(['back', 'deploy']);
3760
const props = defineProps({
61+
isMethodWallet: { type: Boolean, default: false },
3862
numOfNfts: { type: Number, default: 0 },
3963
maxSupply: { type: Number, default: 0 },
4064
});
4165
66+
const authStore = useAuthStore();
4267
const userStore = useUserStore();
68+
const isSpendingWarningOpen = ref(false);
69+
70+
onMounted(() => {
71+
authStore.getConfig();
72+
});
4373
4474
const data = ref<Record<string, string | boolean>[]>([
4575
{ label: 'Total NFTs to distribute this round', value: props.numOfNfts + ' NFTs' },
@@ -52,4 +82,12 @@ const data = ref<Record<string, string | boolean>[]>([
5282
},
5383
{ label: 'Price per NFT', value: mintPrice() + ' credits' },
5484
]);
85+
86+
function deploy() {
87+
if (!props.isMethodWallet && authStore.smtpConfigured === false) {
88+
isSpendingWarningOpen.value = true;
89+
} else {
90+
emit('deploy');
91+
}
92+
}
5593
</script>

frontend/components/parts/Smtp.vue

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,24 @@
22
<div v-if="authStore.smtpConfigured === false" class="fixed right-4 z-50" :style="{ bottom: `${bottom}px` }">
33
<button
44
class="n-button w-9 h-9 rounded-full !bg-bg-darker hover:!bg-bg-dark transition-colors"
5-
@click="showAlert = !showAlert"
5+
@click="authStore.smtpConfiguredAlert = !authStore.smtpConfiguredAlert"
66
>
77
<NuxtIcon name="logo/apillon-icon" filled />
88
</button>
99

1010
<transition name="fade" appear>
11-
<div v-if="showAlert" class="absolute right-12 bottom-12 min-w-60 sm:min-w-[32rem] max-w-lg" :class="$style.bg">
11+
<div
12+
v-if="authStore.smtpConfiguredAlert"
13+
class="absolute right-12 bottom-12 min-w-60 sm:min-w-[32rem] max-w-lg"
14+
:class="$style.bg"
15+
>
1216
<div class="relative bg-bg-light border border-black rounded-lg pl-8 p-2" :class="$style.arrow">
1317
<div class="absolute top-3 left-2">
1418
<NuxtIcon name="icon/alert" class="text-pink text-lg" filled />
1519
</div>
1620
<button
1721
class="n-button absolute flex-cc -top-4 -right-4 w-8 h-8 !bg-white border border-solid border-grey rounded-full"
18-
@click="showAlert = false"
22+
@click="authStore.smtpConfiguredAlert = false"
1923
>
2024
<NuxtIcon name="action/close" class="text-bg-dark text-xs" filled />
2125
</button>
@@ -41,7 +45,6 @@ defineProps({
4145
bottom: { type: Number, default: 16 },
4246
});
4347
const authStore = useAuthStore();
44-
const showAlert = ref(false);
4548
4649
onMounted(() => {
4750
authStore.getConfig();

frontend/components/parts/Table/Airdrop.vue

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -124,14 +124,14 @@ const createColumns = (): DataTableColumns<UserInterface> => {
124124
125125
return [
126126
...cols,
127-
{
128-
key: 'airdrop_status',
129-
title: 'Status',
130-
minWidth: 100,
131-
render(row: UserInterface) {
132-
return AirdropStatus[row.airdrop_status].replaceAll('_', ' ');
133-
},
134-
},
127+
// {
128+
// key: 'airdrop_status',
129+
// title: 'Status',
130+
// minWidth: 100,
131+
// render(row: UserInterface) {
132+
// return AirdropStatus[row.airdrop_status].replaceAll('_', ' ');
133+
// },
134+
// },
135135
{
136136
key: 'actions',
137137
title: '',

frontend/components/parts/Table/Ellipsis.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<template>
22
<div v-if="text" class="flex">
33
<n-ellipsis class="text-grey-dark align-bottom" :line-clamp="1">{{ text }}</n-ellipsis>
4-
<button class="ml-2" @click="copyToClipboard(text)">
4+
<button v-if="copy" class="ml-2" @click="copyToClipboard(text)">
55
<span class="icon-copy text-grey-dark"></span>
66
</button>
77
</div>
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<template>
2+
<div v-if="link" class="flex justify-between text-body">
3+
<div>
4+
<span v-if="prefix" class="mr-1 whitespace-nowrap text-white">{{ prefix }}:</span>
5+
<a :href="link" class="no-underline hover:underline" target="_blank">
6+
<TableEllipsis :text="textOrLink" :copy="false" />
7+
</a>
8+
</div>
9+
10+
<button class="ml-2" @click="copyToClipboard(link)">
11+
<span class="icon-copy text-white-primary" :class="color"></span>
12+
</button>
13+
</div>
14+
</template>
15+
16+
<script lang="ts" setup>
17+
const props = defineProps({
18+
prefix: { type: String, default: '' },
19+
link: { type: String, default: '' },
20+
text: { type: String, default: '' },
21+
color: { type: String, default: '' },
22+
});
23+
24+
const textOrLink = computed(() => {
25+
return props.text || props.link;
26+
});
27+
</script>

frontend/components/parts/Table/PoapReservation.vue

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@
1212

1313
<script lang="ts" setup>
1414
import type { DataTableColumns } from 'naive-ui';
15+
import { transactionLink } from '~/lib/misc/chain';
1516
import { PaginationValues } from '~/lib/values/general.values';
1617
18+
const config = useRuntimeConfig();
1719
const userStore = useUserStore();
1820
const { getUsers } = useUser();
1921
@@ -35,8 +37,14 @@ const columns: DataTableColumns<any> = [
3537
},
3638
{
3739
title: 'Tx hash',
38-
key: 'txHash',
39-
minWidth: 100,
40+
key: 'tx_hash',
41+
minWidth: 150,
42+
render(row: any) {
43+
return h(resolveComponent('TableLink'), {
44+
link: transactionLink(row.tx_hash, config.public.CHAIN_ID),
45+
text: shortHash(row.tx_hash),
46+
});
47+
},
4048
},
4149
{
4250
title: 'Airdrop status',

frontend/components/parts/Table/Users.vue

Lines changed: 0 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313

1414
<script lang="ts" setup>
1515
import { useMessage, type DataTableColumns } from 'naive-ui';
16-
import { createPublicClient, http } from 'viem';
1716
import { dateTimeToDate, dateTimeToDateAndTime } from '~/lib/misc/dates';
1817
import { AirdropStatus, PaginationValues } from '~/lib/values/general.values';
1918
@@ -30,18 +29,11 @@ const props = defineProps({
3029
users: { type: Array<UserInterface>, required: true },
3130
});
3231
const message = useMessage();
33-
const txWait = useTxWait();
3432
const userStore = useUserStore();
3533
const { fetchStatistics } = useUser();
3634
const { handleError } = useErrors();
37-
const { network, ensureCorrectNetwork } = useWalletConnect();
38-
const publicClient = createPublicClient({ chain: network.value, transport: http() });
39-
4035
const loading = ref<number>(-1);
4136
42-
const updateUserStatus = (id: number, status: number) =>
43-
((userStore.users.find(u => u.id === id) || ({} as UserInterface)).airdrop_status = status);
44-
4537
const data = computed(() => {
4638
if (props.search) {
4739
return props.users;
@@ -152,47 +144,6 @@ const createColumns = (): DataTableColumns<UserInterface> => {
152144
};
153145
const columns = createColumns();
154146
155-
/** Admin mint
156-
async function mint(userId: number) {
157-
loading.value = userId;
158-
159-
try {
160-
const { data } = await $api.post<ClaimResponse>('/claim-admin', {
161-
userId,
162-
});
163-
if (data.success) {
164-
await ensureCorrectNetwork();
165-
txWait.hash.value = data.transactionHash;
166-
message.info('NFT minting has started');
167-
updateUserStatus(userId, AirdropStatus.TRANSACTION_CREATED);
168-
169-
const receipt: any = await Promise.race([
170-
txWait.wait(),
171-
publicClient.waitForTransactionReceipt({ hash: data.transactionHash }),
172-
]);
173-
message.success('You successfully claimed NFT');
174-
175-
const logs = receipt?.logs || receipt.data?.logs;
176-
if (logs && logs[0].topics[3]) {
177-
message.success('You successfully minted NFT');
178-
updateUserStatus(userId, AirdropStatus.AIRDROP_COMPLETED);
179-
} else {
180-
message.error('Mint failed, missing NFT ID!');
181-
updateUserStatus(userId, AirdropStatus.AIRDROP_ERROR);
182-
}
183-
} else {
184-
message.error('Failed to claim NFT, please try again later.');
185-
updateUserStatus(userId, AirdropStatus.AIRDROP_ERROR);
186-
}
187-
loading.value = -1;
188-
} catch (e) {
189-
handleError(e);
190-
updateUserStatus(userId, AirdropStatus.AIRDROP_ERROR);
191-
loading.value = -1;
192-
}
193-
}
194-
*/
195-
196147
async function handleDeleteUser(id: number) {
197148
loading.value = id;
198149

frontend/components/parts/airdrop/AirdropModal.vue

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ const availableNFTs = computed(() => maxSupply - userStore.users.length - items.
5454
const isButtonDisabled = computed(() => {
5555
switch (uploadStep.value) {
5656
case Step.TYPE:
57-
return !selectedMethod.value;
57+
return !selectedMethod.value || availableNFTs.value <= 0;
5858
case Step.DATA:
5959
return items.value.length === 0 || availableNFTs.value < 0 || hasEmptyRow() || editingRow.value >= 0;
6060
default:
@@ -171,7 +171,7 @@ async function deploy() {
171171
@close="emit('close')"
172172
>
173173
<div v-if="isStep(Step.TYPE)" class="max-w-lg w-full mx-auto pt-4 lg:pt-0">
174-
<Tag v-if="availableNFTs <= 0" type="error" class="absolute top-2 right-2">
174+
<Tag v-if="availableNFTs <= 0" type="error" class="absolute top-2 left-1/2 -translate-x-1/2">
175175
You have exceeded the maximum number of NFTs available for airdrop.
176176
</Tag>
177177
<h4>Select distribution methods</h4>
@@ -180,6 +180,7 @@ async function deploy() {
180180
v-for="method in methods"
181181
:key="method.value"
182182
v-bind="method"
183+
:class="{ 'pointer-events-none': availableNFTs <= 0 }"
183184
:disabled="availableNFTs <= 0"
184185
:selected="selectedMethod === method.value"
185186
:alert="!authStore.smtpConfigured && method.value === AirdropMethod.EMAIL ? 'Needs setup' : ''"
@@ -203,15 +204,10 @@ async function deploy() {
203204
</div>
204205
<div v-else-if="isStep(Step.DATA)">
205206
<div class="flex justify-between items-center mb-6">
206-
<div v-if="isMethodWallet">
207-
<h3 class="mb-2">List of NFT wallet airdrop</h3>
208-
<span>Please check list before proceed</span>
207+
<div>
208+
<h3 class="mb-2">Your NFT {{ isMethodWallet ? 'Wallet' : 'Email' }} Mint list</h3>
209+
<span>Please check the list before proceeding.</span>
209210
</div>
210-
<div v-else>
211-
<h3 class="mb-2">List of NFT email airdrop</h3>
212-
<span>Please check list before proceed</span>
213-
</div>
214-
215211
<Btn v-if="availableNFTs > 0" type="secondary" @click="uploadStep = Step.UPLOAD"> Upload more </Btn>
216212
</div>
217213

@@ -232,6 +228,7 @@ async function deploy() {
232228
</div>
233229
<PreviewUpload
234230
v-else-if="isStep(Step.REVIEW)"
231+
:is-method-wallet="isMethodWallet"
235232
:num-of-nfts="items.length"
236233
:max-supply="maxSupply"
237234
@back="uploadStep = Step.DATA"

0 commit comments

Comments
 (0)