web/satellite: unmock list buckets in vuetify app

This change implements the table on the buckets view of the vuetify app.
Searching and pagination are implemented, but sorting functionality
needs to be added in a separate change - we need to extend the
bucketsStore/backend functionality in order to support sorting.

Resolves https://github.com/storj/storj/issues/6062

Change-Id: I4e92e6facbd7b21f4ec081e41f7e568ddb3cea29
This commit is contained in:
Moby von Briesen 2023-07-24 19:39:47 -04:00
parent 16c5b3c340
commit 7487809476
3 changed files with 133 additions and 51 deletions

View File

@ -11,45 +11,161 @@
hide-details
/>
<v-data-table
<v-data-table-server
v-model="selected"
:sort-by="sortBy"
:headers="headers"
:items="buckets"
:items="page.buckets"
:search="search"
:loading="areBucketsFetching"
:items-length="page.totalCount"
:items-per-page-options="tableSizeOptions(page.totalCount)"
item-value="name"
class="elevation-1"
show-select
@update:itemsPerPage="onUpdateLimit"
@update:page="onUpdatePage"
>
<template #item.name="{ item }">
<div>
<v-btn
class="rounded-lg w-100 pl-1 pr-4 justify-start font-weight-bold"
class="rounded-lg w-100 pl-1 pr-4 justify-start font-weight-bold text-lowercase"
variant="text"
height="40"
color="default"
to="/bucket"
@click="openBucket(item.raw.name)"
>
<img src="../assets/icon-bucket-tonal.svg" alt="Bucket" class="mr-3">
{{ item.raw.name }}
</v-btn>
</div>
</template>
</v-data-table>
<template #item.storage="{ item }">
<span>
{{ item.raw.storage.toFixed(2) + 'GB' }}
</span>
</template>
<template #item.egress="{ item }">
<span>
{{ item.raw.egress.toFixed(2) + 'GB' }}
</span>
</template>
<template #item.objectCount="{ item }">
<span>
{{ item.raw.objectCount.toLocaleString() }}
</span>
</template>
<template #item.segmentCount="{ item }">
<span>
{{ item.raw.segmentCount.toLocaleString() }}
</span>
</template>
<template #item.createdAt="{ item }">
<span>
{{ item.raw.since.toLocaleString() }}
</span>
</template>
</v-data-table-server>
</v-card>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { ref, watch, onMounted, computed } from 'vue';
import { useRouter } from 'vue-router';
import { VCard, VTextField, VBtn } from 'vuetify/components';
import { VDataTable } from 'vuetify/labs/components';
import { VDataTableServer } from 'vuetify/labs/components';
const props = defineProps<{
headers: unknown[],
buckets: unknown[],
}>();
import { BucketPage, BucketCursor } from '@/types/buckets';
import { useBucketsStore } from '@/store/modules/bucketsStore';
import { useNotify } from '@/utils/hooks';
import { AnalyticsErrorEventSource, AnalyticsEvent } from '@/utils/constants/analyticsEventNames';
import { useProjectsStore } from '@/store/modules/projectsStore';
import { DEFAULT_PAGE_LIMIT } from '@/types/pagination';
import { tableSizeOptions } from '@/types/common';
const bucketsStore = useBucketsStore();
const projectsStore = useProjectsStore();
const notify = useNotify();
const router = useRouter();
const FIRST_PAGE = 1;
const areBucketsFetching = ref<boolean>(true);
const search = ref<string>('');
const searchTimer = ref<NodeJS.Timeout>();
const selected = ref([]);
const sortBy = [{ key: 'date', order: 'asc' }];
const headers = [
{
title: 'Name',
align: 'start',
key: 'name',
sortable: false,
},
{ title: 'Storage', key: 'storage', sortable: false },
{ title: 'Egress', key: 'egress', sortable: false },
{ title: 'Objects', key: 'objectCount', sortable: false },
{ title: 'Segments', key: 'segmentCount', sortable: false },
{ title: 'Date Created', key: 'createdAt', sortable: false },
];
/**
* Returns buckets cursor from store.
*/
const cursor = computed((): BucketCursor => {
return bucketsStore.state.cursor;
});
/**
* Returns buckets page from store.
*/
const page = computed((): BucketPage => {
return bucketsStore.state.page;
});
/**
* Fetches bucket using api.
*/
async function fetchBuckets(page = FIRST_PAGE, limit = DEFAULT_PAGE_LIMIT): Promise<void> {
try {
await bucketsStore.getBuckets(page, projectsStore.state.selectedProject.id, limit);
if (areBucketsFetching.value) areBucketsFetching.value = false;
} catch (error) {
notify.error(`Unable to fetch buckets. ${error.message}`, AnalyticsErrorEventSource.BUCKET_TABLE);
}
}
/**
* Handles update table rows limit event.
*/
function onUpdateLimit(limit: number): void {
fetchBuckets(page.value.currentPage, limit);
}
/**
* Handles update table page event.
*/
function onUpdatePage(page: number): void {
fetchBuckets(page, cursor.value.limit);
}
/**
* Handles update table search.
*/
watch(() => search.value, () => {
clearTimeout(searchTimer.value);
searchTimer.value = setTimeout(() => {
bucketsStore.setBucketsSearch(search.value);
fetchBuckets();
}, 500); // 500ms delay for every new call.
});
/**
* Navigates to bucket page.
*/
function openBucket(bucketName: string): void {
router.push(`/projects/${projectsStore.state.selectedProject.id}/buckets/${bucketName}`);
}
onMounted(() => {
fetchBuckets();
});
</script>

View File

@ -58,9 +58,9 @@ const routes: RouteRecordRaw[] = [
component: () => import(/* webpackChunkName: "Buckets" */ '@poc/views/Buckets.vue'),
},
{
path: 'bucket',
path: 'buckets/:bucketName',
name: 'Bucket',
component: () => import(/* webpackChunkName: "Buckets" */ '@poc/views/Bucket.vue'),
component: () => import(/* webpackChunkName: "Bucket" */ '@poc/views/Bucket.vue'),
},
{
path: 'access',

View File

@ -87,7 +87,7 @@
</v-col>
</v-row>
<BucketsDataTable :headers="headers" :buckets="buckets" />
<BucketsDataTable />
</v-container>
</template>
@ -126,38 +126,4 @@ const bucketNameRules = [
return true;
},
];
const headers = [
{
title: 'Name',
align: 'start',
key: 'name',
},
{ title: 'Files', key: 'files' },
{ title: 'Storage', key: 'storage' },
{ title: 'Bandwidth', key: 'bandwidth' },
{ title: 'Date Created', key: 'date' },
];
const buckets = [
{
name: 'Demo',
date: '02 Mar 2023',
files: '4,210',
bandwidth: '481 MB',
storage: '32 GB',
},
{
name: 'Photos',
date: '03 Mar 2023',
files: '92,155',
bandwidth: '2.0 GB',
storage: '5.2 GB',
},
{
name: 'Videos',
date: '04 Mar 2023',
files: '24',
bandwidth: '102 MB',
storage: '1.7 TB',
},
];
</script>