Skip to content

Commit

Permalink
Polishes QR code flow
Browse files Browse the repository at this point in the history
  • Loading branch information
peterpolman committed Sep 18, 2024
1 parent 5dfb353 commit 71cdba5
Show file tree
Hide file tree
Showing 9 changed files with 177 additions and 86 deletions.
36 changes: 27 additions & 9 deletions apps/studio/src/components/BaseFormCollectionMetadataQRCodes.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<b-tabs justified pills content-class="py-3">
<b-tab title="Entries">
<b-tab title="Codes">
<b-form @submit.prevent="onSubmitEntries">
<BaseFormGroup
label="Amount"
Expand All @@ -13,12 +13,16 @@
</BaseFormGroup>
<b-button type="submit" variant="primary" class="w-100">
<b-spinner v-if="isLoading" small />
<template v-else> Create QR Codes </template>
<template v-else> Create Codes </template>
</b-button>
</b-form>
</b-tab>
<b-tab title="Download">
<b-form @submit="onSubmitDownload">
<b-alert v-model="isAlertShown" variant="primary" show class="py-2">
<BaseIcon icon="info-circle" class="me-2" />
You can download <strong>{{ codes.length }}</strong> entries as a zip with QR codes.
</b-alert>
<BaseFormGroup label="Logo Image" tooltip="Positioned in the center of the QR code">
<b-form-file
v-model="imageFile"
Expand Down Expand Up @@ -96,6 +100,7 @@ export default defineComponent({
data() {
return {
isLoading: false,
isAlertShown: true,
codes: [],
index: 0,
amount: 0,
Expand All @@ -109,6 +114,9 @@ export default defineComponent({
computed: {
...mapStores(useEntryStore),
},
async mounted() {
await this.listCodes();
},
methods: {
async onSubmitEntries() {
try {
Expand All @@ -135,21 +143,31 @@ export default defineComponent({
url.pathname = `/v1/qr-codes/r/${uuid}`;
return url.toString();
},
async onSubmitDownload() {
async listCodes() {
try {
if (!this.metadata) throw new Error('Metadata not found');
this.isLoading = true;
const filename = `qr_codes_${new Date().getTime()}`;
const zip = new JSZip();
const archive = zip.folder(filename) as JSZip;
// Get the first 5000 (max) QR codes for this metadata. Increase if needed.
const { request } = useAuthStore();
const data = await request('/qr-codes', {
params: { erc721MetadataId: this.metadata._id, page: 1, limit: 5000 },
});
this.codes = data.results.map((code: TQRCodeEntry) => code.uuid);
} catch (error) {
toast(error.message, 'light', 3000, () => {
return;
});
} finally {
this.isLoading = false;
}
},
async onSubmitDownload() {
try {
if (!this.metadata) throw new Error('Metadata not found');
this.isLoading = true;
const filename = `qr_codes_${new Date().getTime()}`;
const zip = new JSZip();
const archive = zip.folder(filename) as JSZip;
// Iterate over the uuids and create a QR code for each
for (const uuid of this.codes) {
Expand Down
13 changes: 12 additions & 1 deletion apps/studio/src/scss/_tables.scss
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,15 @@
vertical-align: middle;
}
}
}
}

.table > :not(caption) > * > * {
border: 0 !important;
}
.table > td {
vertical-align: middle !important;
}

.table .cursor-pointer {
cursor: pointer;
}
2 changes: 2 additions & 0 deletions apps/studio/src/scss/main.scss
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@
@import '../../../../node_modules/bootstrap/scss/offcanvas';
@import '../../../../node_modules/bootstrap/scss/placeholders';

@import './tables';

// Helpers
@import '../../../../node_modules/bootstrap/scss/helpers';

Expand Down
4 changes: 4 additions & 0 deletions apps/studio/src/stores/Collections.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ export const useCollectionStore = defineStore('collection', {
const data = await this.request(`/erc721`, { method: 'POST', body, params: {} });
this.collections.push(data);
},
async remove(id: string) {
await this.request(`/erc721/${id}`, { method: 'DELETE' });
this.collections = this.collections.filter((collection) => collection._id !== id);
},
async listMetadata(erc721Id: string, params: { page: number; limit: number }) {
const data = await this.request(`/erc721/${erc721Id}/metadata`, { params });
this.metadata[erc721Id] = data;
Expand Down
21 changes: 19 additions & 2 deletions apps/studio/src/views/Studio.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<b-container>
<b-row>
<b-navbar>
<b-navbar-brand href="/">
<b-navbar-brand to="/">
<b-img :src="imgLogo" width="50" class="rounded" />
</b-navbar-brand>
<b-navbar-toggle target="nav-collapse" />
Expand All @@ -12,9 +12,18 @@
<b-nav-item to="/qr-codes">QR Codes</b-nav-item>
</b-navbar-nav>
</b-collapse>
<b-navbar-nav class="me-3">
<b-nav-item href="https://www.twinstory.io" target="_blank">FAQ</b-nav-item>
<b-nav-item href="https://www.twinstory.io" target="_blank">Support</b-nav-item>
</b-navbar-nav>
<b-nav-item-dropdown right no-caret class="d-flex">
<template #button-content>
<BAvatar variant="dark" text="BV" />
<b-avatar
size="40"
variant="dark"
:src="accountStore.account ? accountStore.account.profileImg : undefined"
:text="accountStore.account ? accountStore.account.username.substring(0, 2) : '..'"
/>
</template>
<b-dropdown-item to="/account">Account</b-dropdown-item>
<b-dropdown-item to="/logout">Logout</b-dropdown-item>
Expand All @@ -27,6 +36,8 @@

<script lang="ts">
import { defineComponent } from 'vue';
import { mapStores } from 'pinia';
import { useAccountStore } from '@thxnetwork/studio/stores';
import imgLogo from '@thxnetwork/studio/assets/logo.jpg';
export default defineComponent({
Expand All @@ -36,5 +47,11 @@ export default defineComponent({
imgLogo,
};
},
computed: {
...mapStores(useAccountStore),
},
mounted() {
this.accountStore.get();
},
});
</script>
24 changes: 0 additions & 24 deletions apps/studio/src/views/studio/Collection.vue

This file was deleted.

49 changes: 26 additions & 23 deletions apps/studio/src/views/studio/CollectionCreate.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,30 +9,33 @@
</b-container>
<div class="bg-dark text-white py-5 flex-grow-1">
<b-container>
<b-row>
<b-col md="6">
<BaseFormGroup label="Name" tooltip="This name is used to describe your collection">
<b-form-input v-model="name" />
</BaseFormGroup>
<BaseFormGroup label="Symbol" tooltip="ERC721 collections require a symbol like ABC.">
<b-form-input v-model="description" />
</BaseFormGroup>
</b-col>
<b-col md="6">
<BaseFormGroup label="Description" tooltip="Describe your collection in a couple of words">
<b-form-textarea v-model="description" />
</BaseFormGroup>
<b-form @submit="onSubmit">
<b-button variant="primary" type="submit" class="w-100">
{{ isCreating ? 'Create' : 'Update' }} Collection
</b-button>
</b-form>
</b-col>
</b-row>
<h2>Details</h2>
<b-card class="mb-5">
<b-row>
<b-col md="6">
<BaseFormGroup label="Name" tooltip="This name is used to describe your collection">
<b-form-input v-model="name" />
</BaseFormGroup>
<BaseFormGroup label="Symbol" tooltip="ERC721 collections require a symbol like ABC.">
<b-form-input v-model="description" />
</BaseFormGroup>
</b-col>
<b-col md="6">
<BaseFormGroup label="Description" tooltip="Describe your collection in a couple of words">
<b-form-textarea v-model="description" />
</BaseFormGroup>
<b-form @submit="onSubmit">
<b-button variant="primary" type="submit" class="w-100">
{{ isCreating ? 'Create' : 'Update' }} Collection
</b-button>
</b-form>
</b-col>
</b-row>
</b-card>
<h2 class="my-3 d-flex">
Metadata
<b-button variant="dark" class="ms-auto" @click="isModelCollectionMetadataShown = true">
Create Metadata
Collectibles
<b-button variant="primary" class="ms-auto" @click="isModelCollectionMetadataShown = true">
Create Collectible
<BaseIcon icon="plus" class="ms-1" />
</b-button>
<b-modal
Expand Down
89 changes: 69 additions & 20 deletions apps/studio/src/views/studio/Collections.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,32 @@
</b-container>
<div class="bg-dark text-white py-5 flex-grow-1">
<b-container>
<b-row v-if="isLoading && !collectionStore.collections.length">
<b-col v-for="val in [1, 2, 3]" md="4">
<b-placeholder-card :key="val" no-img />
</b-col>
</b-row>
<b-row v-else>
<b-col v-for="collection of collectionStore.collections" md="4">
<b-link class="text-decoration-none" :to="`/collections/${collection._id}`">
<b-card class="mb-3" :header="collection.name">
{{ collection.description }}
<template #footer>
<b-button class="w-100" variant="primary">
Configure
<BaseIcon icon="chevron-right" class="ms-2" />
</b-button>
<b-card variant="darker">
<b-table :items="collections" responsive="lg" :tbody-tr-class="rowClass" @row-clicked="onClickRow">
<template #head(name)>Name</template>
<template #head(address)>Address</template>
<template #head(actions)></template>

<template #cell(name)="{ item }">
<strong>{{ item.name }}</strong>
</template>
<template #cell(address)="{ item }">
<b-link href="" target="_blank">
<code>
{{ item.address.value }}
</code>
</b-link>
</template>
<template #cell(actions)="{ item }">
<b-dropdown no-caret size="sm" end variant="link">
<template #button-content>
<BaseIcon icon="ellipsis-v text-light" />
</template>
</b-card>
</b-link>
</b-col>
</b-row>
<b-dropdown-item @click="onClickDelete(item.actions.id)"> Remove </b-dropdown-item>
</b-dropdown>
</template>
</b-table>
</b-card>
</b-container>
</div>
</template>
Expand All @@ -48,17 +54,60 @@ export default defineComponent({
},
computed: {
...mapStores(useAuthStore, useCollectionStore),
collections() {
return this.collectionStore.collections.map((collection) => {
return {
name: collection.name,
address: {
value: collection.address,
short: collection.address?.substring(0, 10),
},
actions: {
id: collection._id,
},
};
});
},
},
mounted() {
this.listCollections();
},
methods: {
rowClass(_item, type: string) {
return type === 'row' ? 'cursor-pointer' : '';
},
async listCollections() {
try {
this.isLoading = true;
await this.collectionStore.list();
} catch (error: any) {
toast(error.message, 'danger', 3000, () => alert('bla'));
toast(error.message, 'light', 3000, () => {
return;
});
} finally {
this.isLoading = false;
}
},
async onClickDelete(id: string) {
try {
this.isLoading = true;
await this.collectionStore.remove(id);
} catch (error: any) {
toast(error.message, 'light', 3000, () => {
return;
});
} finally {
this.isLoading = true;
}
},
async onClickRow(data: { actions: { id: string } }) {
try {
this.isLoading = true;
await this.$router.push(`/collections/${data.actions.id}`);
} catch (error: any) {
toast(error.message, 'light', 3000, () => {
return;
});
} finally {
this.isLoading = false;
}
Expand Down
Loading

0 comments on commit 71cdba5

Please sign in to comment.