web/satellite/vuetify-poc: improve team page

This change makes minor improvements to the vuetify team page.

Issue: https://github.com/storj/storj/issues/6376

Change-Id: Iedd0e1f47d8075f13b424da65d719057eade14ed
This commit is contained in:
Wilfred Asomani 2023-10-05 13:07:56 +00:00 committed by Storj Robot
parent db3578d9ba
commit ee2b6e66de
4 changed files with 47 additions and 62 deletions

View File

@ -3,27 +3,42 @@
<template> <template>
<v-card min-height="24" :border="0" class="mb-2 elevation-0"> <v-card min-height="24" :border="0" class="mb-2 elevation-0">
<v-row v-if="modelValue.length > 0" class="justify-end align-center ma-0"> <v-row v-if="selectedMembers.length > 0" class="justify-end align-center ma-0">
<p>{{ modelValue.length }} user{{ modelValue.length !== 1 ? 's' : '' }} selected</p> <p>{{ selectedMembers.length }} user{{ selectedMembers.length !== 1 ? 's' : '' }} selected</p>
</v-row> </v-row>
</v-card> </v-card>
<v-card variant="flat" :border="true" rounded="xlg"> <v-card variant="flat" :border="true" rounded="xlg">
<v-text-field <v-row align="center" class="ma-0">
v-model="search" <v-col v-if="selectedMembers.length" class="px-0 mr-0 ml-2 mt-2" cols="auto">
label="Search" <v-btn
prepend-inner-icon="mdi-magnify" class=" text-caption"
single-line prepend-icon="mdi-trash-can-outline"
variant="solo-filled" variant="outlined"
flat color="default"
hide-details @click="showDeleteDialog"
clearable >
density="comfortable" Remove
rounded="lg" </v-btn>
class="mx-2 mt-2" </v-col>
/>
<v-col class="px-0 mx-2 mt-2">
<v-text-field
v-model="search"
label="Search"
prepend-inner-icon="mdi-magnify"
single-line
variant="solo-filled"
flat
hide-details
clearable
density="comfortable"
rounded="lg"
/>
</v-col>
</v-row>
<v-data-table-server <v-data-table-server
v-model="model" v-model="selectedMembers"
:search="search" :search="search"
:headers="headers" :headers="headers"
:items="projectMembers" :items="projectMembers"
@ -79,6 +94,7 @@
</v-list-item-title> </v-list-item-title>
</v-list-item> </v-list-item>
<v-list-item <v-list-item
class="text-error"
density="comfortable" density="comfortable"
link rounded="lg" link rounded="lg"
@click="() => onSingleDelete(item.raw.email)" @click="() => onSingleDelete(item.raw.email)"
@ -99,7 +115,7 @@
<remove-project-member-dialog <remove-project-member-dialog
v-model="isRemoveMembersDialogShown" v-model="isRemoveMembersDialogShown"
:emails="modelValue" :emails="selectedMembers"
@deleted="onPostDelete" @deleted="onPostDelete"
/> />
</template> </template>
@ -109,6 +125,7 @@ import { computed, onMounted, ref, watch } from 'vue';
import { import {
VRow, VRow,
VCard, VCard,
VCol,
VTextField, VTextField,
VChip, VChip,
VIcon, VIcon,
@ -165,21 +182,12 @@ const router = useRouter();
const notify = useNotify(); const notify = useNotify();
const { isLoading, withLoading } = useLoading(); const { isLoading, withLoading } = useLoading();
const emit = defineEmits<{
(event: 'update:modelValue', value: string[]): void,
}>();
const props = defineProps<{
modelValue: string[]
}>();
const model = computed<string[]>({
get: () => props.modelValue,
set: value => emit('update:modelValue', value),
});
const isRemoveMembersDialogShown = ref<boolean>(false); const isRemoveMembersDialogShown = ref<boolean>(false);
const search = ref<string>(''); const search = ref<string>('');
const searchTimer = ref<NodeJS.Timeout>(); const searchTimer = ref<NodeJS.Timeout>();
const sortBy = ref([{ key: 'date', order: 'asc' }]); const sortBy = ref([{ key: 'date', order: 'asc' }]);
const selectedMembers = ref<string[]>([]);
const headers = ref([ const headers = ref([
{ {
title: 'Name', title: 'Name',
@ -252,18 +260,18 @@ async function onUpdatePage(page: number): Promise<void> {
* Handles post delete operations. * Handles post delete operations.
*/ */
async function onPostDelete(): Promise<void> { async function onPostDelete(): Promise<void> {
if (props.modelValue.includes(usersStore.state.user.email)) { if (selectedMembers.value.includes(usersStore.state.user.email)) {
router.push('/projects'); router.push('/projects');
return; return;
} }
search.value = ''; search.value = '';
emit('update:modelValue', []); selectedMembers.value = [];
await onUpdatePage(FIRST_PAGE); await onUpdatePage(FIRST_PAGE);
} }
function onSingleDelete(email: string): void { function onSingleDelete(email: string): void {
emit('update:modelValue', [email]); selectedMembers.value = [email];
isRemoveMembersDialogShown.value = true; isRemoveMembersDialogShown.value = true;
} }
@ -367,6 +375,4 @@ watch(() => search.value, () => {
onMounted(() => { onMounted(() => {
fetch(); fetch();
}); });
defineExpose({ showDeleteDialog });
</script> </script>

View File

@ -139,7 +139,7 @@ async function onAddUsersClick(): Promise<void> {
await withLoading(async () => { await withLoading(async () => {
try { try {
await pmStore.inviteMembers([email.value], props.projectId); await pmStore.inviteMembers([email.value], props.projectId);
notify.notify('Invites sent!'); notify.success('Invites sent!');
email.value = ''; email.value = '';
} catch (error) { } catch (error) {
error.message = `Error adding project members. ${error.message}`; error.message = `Error adding project members. ${error.message}`;

View File

@ -60,13 +60,13 @@
</v-card-item> </v-card-item>
<v-card-item class="px-7 py-0"> <v-card-item class="px-7 py-0">
<v-card class="mb-4 pa-4" color="warning"> <v-alert variant="tonal" class="mb-4 pa-4" color="warning">
<p> <template #text>
<strong>Please note:</strong> any access grants they have created will still provide <strong>Please note:</strong> any access grants they have created will still provide
them with full access. If necessary, please revoke these access grants to ensure them with full access. If necessary, please revoke these access grants to ensure
the security of your data. the security of your data.
</p> </template>
</v-card> </v-alert>
<v-divider /> <v-divider />
</v-card-item> </v-card-item>
@ -80,7 +80,7 @@
</v-col> </v-col>
<v-col> <v-col>
<v-btn color="error" variant="flat" block :loading="isLoading" @click="onDelete"> <v-btn color="error" variant="flat" block :loading="isLoading" @click="onDelete">
Delete Remove
</v-btn> </v-btn>
</v-col> </v-col>
</v-row> </v-row>
@ -92,6 +92,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue'; import { computed } from 'vue';
import { import {
VAlert,
VDialog, VDialog,
VCard, VCard,
VCardItem, VCardItem,

View File

@ -17,20 +17,10 @@
</svg> </svg>
Add Members Add Members
</v-btn> </v-btn>
<v-btn
v-if="selectedMembers.length"
prepend-icon="mdi-trash-can-outline"
variant="outlined"
color="default"
class="ml-2 text-caption"
@click="showDeleteDialog"
>
Remove
</v-btn>
</v-row> </v-row>
</v-col> </v-col>
<TeamTableComponent ref="tableComponent" v-model="selectedMembers" /> <TeamTableComponent />
</v-container> </v-container>
<add-team-member-dialog v-model="isAddMemberDialogShown" :project-id="selectedProjectID" /> <add-team-member-dialog v-model="isAddMemberDialogShown" :project-id="selectedProjectID" />
@ -47,22 +37,10 @@ import PageSubtitleComponent from '@poc/components/PageSubtitleComponent.vue';
import TeamTableComponent from '@poc/components/TeamTableComponent.vue'; import TeamTableComponent from '@poc/components/TeamTableComponent.vue';
import AddTeamMemberDialog from '@poc/components/dialogs/AddTeamMemberDialog.vue'; import AddTeamMemberDialog from '@poc/components/dialogs/AddTeamMemberDialog.vue';
interface DeleteDialog {
showDeleteDialog(): void;
}
const projectsStore = useProjectsStore(); const projectsStore = useProjectsStore();
const selectedMembers = ref<string[]>([]);
const tableComponent = ref<InstanceType<typeof TeamTableComponent> & DeleteDialog>();
const isAddMemberDialogShown = ref<boolean>(false); const isAddMemberDialogShown = ref<boolean>(false);
const selectedProjectID = computed((): string => projectsStore.state.selectedProject.id); const selectedProjectID = computed((): string => projectsStore.state.selectedProject.id);
/**
* Makes delete project members dialog visible.
*/
function showDeleteDialog(): void {
tableComponent.value?.showDeleteDialog();
}
</script> </script>