web/satellite: update access grants table

This change updates the access grants table to use the new table component.

see: https://github.com/storj/storj/issues/4994

Change-Id: I6fa8c3feb2023c2dacc7ea5536ba9e16652f8c26
This commit is contained in:
wilfredasomani 2022-08-02 13:22:44 +00:00 committed by Wilfred Asomani
parent 0ec28ca623
commit 26bed35f33
5 changed files with 178 additions and 349 deletions

View File

@ -139,64 +139,77 @@
/>
</div>
<VLoader v-if="areGrantsFetching" width="100px" height="100px" class="grants-loader" />
<div v-if="accessGrantsList.length && !areGrantsFetching" class="access-grants-items2">
<SortAccessGrantsHeader2
:on-header-click-callback="onHeaderSectionClickCallback"
/>
<div class="access-grants-items2__content">
<VList
:data-set="accessGrantsList"
:item-component="itemComponent2"
@openModal="onDeleteClick"
/>
</div>
<div class="access-grants-items2__footer">
<span class="access-grants-items2__footer__total-accesses">
{{ accessGrantsList.length }} Access Grants
<div class="access-grants-items2">
<v-table
v-if="accessGrantsList.length && !areGrantsFetching"
class="access-grants-items2__content"
items-label="access grants"
:limit="accessGrantLimit"
:total-page-count="totalPageCount"
:items="accessGrantsList"
:total-items-count="accessGrantsTotalCount"
:on-page-click-callback="onPageClick"
>
<template #head>
<th class="align-left">Name</th>
<th class="align-left">Date Created</th>
</template>
<template #body>
<AccessGrantsItem2
v-for="(grant, key) in accessGrantsList"
:key="key"
:item-data="grant"
:dropdown-key="key"
:is-dropdown-open="activeDropdown === key"
@openDropdown="openDropdown"
@deleteClick="onDeleteClick"
/>
</template>
</v-table>
<div
v-if="!accessGrantsList.length && !areGrantsFetching"
class="access-grants-items2__empty-state"
>
<span class="access-grants-items2__empty-state__text">
No Results Found
</span>
<VPagination
v-if="totalPageCount > 1"
ref="pagination"
class="access-grants-items2__footer__pagination-area"
:total-page-count="totalPageCount"
:on-page-click-callback="onPageClick"
/>
</div>
</div>
<div
v-if="!accessGrantsList.length && !areGrantsFetching"
class="access-grants-items2__empty-state"
>
<span class="access-grants-items2__empty-state__text">
No Results Found
</span>
</div>
</div>
<div v-if="!isNewAccessGrantFlow">
<VLoader v-if="areGrantsFetching" width="100px" height="100px" class="grants-loader" />
<div v-if="accessGrantsList.length && !areGrantsFetching" class="access-grants-items">
<SortAccessGrantsHeader :on-header-click-callback="onHeaderSectionClickCallback" />
<div class="access-grants-items__content">
<VList
:data-set="accessGrantsList"
:item-component="itemComponent"
:on-item-click="toggleSelection"
/>
</div>
<VPagination
v-if="totalPageCount > 1"
ref="pagination"
class="pagination-area"
<v-table
v-if="accessGrantsList.length && !areGrantsFetching"
class="access-grants-items__content"
items-label="access grants"
:selectable="true"
:limit="accessGrantLimit"
:total-page-count="totalPageCount"
:items="accessGrantsList"
:total-items-count="accessGrantsTotalCount"
:on-page-click-callback="onPageClick"
/>
>
<template #head>
<th class="align-left">Name</th>
<th class="align-left">Date Created</th>
</template>
<template #body>
<AccessGrantsItem
v-for="(grant, key) in accessGrantsList"
:key="key"
:item-data="grant"
@accessGrantClick="toggleSelection"
@selectChange="(_) => toggleSelection(grant)"
/>
</template>
</v-table>
</div>
</div>
<div v-if="!isNewAccessGrantFlow">
<ConfirmDeletePopup
v-if="isDeleteClicked"
@close="onClearSelection"
@reset-pagination="resetPagination"
/>
<EmptyState v-if="!accessGrantsList.length && !areGrantsFetching" />
</div>
@ -204,7 +217,6 @@
<ConfirmDeletePopup2
v-if="isDeleteClicked"
@close="onClearSelection"
@resetPagination="resetPagination"
/>
</div>
<router-view />
@ -219,13 +231,9 @@ import AccessGrantsItem2 from '@/components/accessGrants/AccessGrantsItem2.vue';
import ConfirmDeletePopup from '@/components/accessGrants/ConfirmDeletePopup.vue';
import ConfirmDeletePopup2 from '@/components/accessGrants/ConfirmDeletePopup2.vue';
import EmptyState from '@/components/accessGrants/EmptyState.vue';
import SortAccessGrantsHeader from '@/components/accessGrants/SortingHeader.vue';
import SortAccessGrantsHeader2 from '@/components/accessGrants/SortingHeader2.vue';
import VButton from '@/components/common/VButton.vue';
import VList from '@/components/common/VList.vue';
import VLoader from '@/components/common/VLoader.vue';
import VHeader from '@/components/common/VHeader.vue';
import VPagination from '@/components/common/VPagination.vue';
import AccessGrantsIcon from '@/../static/images/accessGrants/accessGrantsIcon.svg';
import CLIIcon from '@/../static/images/accessGrants/cli.svg';
import S3Icon from '@/../static/images/accessGrants/s3.svg';
@ -235,6 +243,7 @@ import { AccessGrant, AccessGrantsOrderBy } from '@/types/accessGrants';
import { SortDirection } from '@/types/common';
import { AnalyticsHttpApi } from '@/api/analytics';
import { AnalyticsEvent } from '@/utils/constants/analyticsEventNames';
import VTable from "@/components/common/VTable.vue";
const {
FETCH,
@ -244,25 +253,22 @@ const {
SET_SORT_DIRECTION,
SET_SEARCH_QUERY,
} = ACCESS_GRANTS_ACTIONS;
declare interface ResetPagination {
resetPageIndex(): void;
}
// @vue/component
@Component({
components: {
AccessGrantsItem2,
AccessGrantsItem,
AccessGrantsIcon,
CLIIcon,
EmptyState,
S3Icon,
SortAccessGrantsHeader,
SortAccessGrantsHeader2,
VList,
VPagination,
VButton,
ConfirmDeletePopup,
ConfirmDeletePopup2,
VLoader,
VHeader,
VTable,
},
})
export default class AccessGrants extends Vue {
@ -271,11 +277,14 @@ export default class AccessGrants extends Vue {
private readonly analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
public areGrantsFetching = true;
/**
* Indicates if the access modal should be shown and what the defaulted type of access should be defaulted.
*/
private showAccessModal = false;
private modalAccessType = '';
public activeDropdown = -1;
public $refs!: {
pagination: HTMLElement & ResetPagination;
};
public areGrantsFetching = true;
/**
* Indicates if navigation side bar is hidden.
@ -333,18 +342,8 @@ export default class AccessGrants extends Vue {
} catch (error) {
await this.$notify.error(`Unable to fetch Access Grants. ${error.message}`);
}
if (this.totalPageCount > 1) {
this.resetPagination();
}
}
/**
* Resets pagination to default state.
*/
public resetPagination(): void {
if (this.totalPageCount > 1) {
this.$refs.pagination.resetPageIndex();
}
}
/**
* Starts create access grant flow.
*/
@ -352,18 +351,33 @@ export default class AccessGrants extends Vue {
this.analytics.pageVisit(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant).with(RouteConfig.NameStep).path);
this.$router.push(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant).with(RouteConfig.NameStep).path);
}
/**
* Opens AccessGrantItem2 dropdown.
*/
public openDropdown(key: number): void {
if (this.activeDropdown === key) {
this.activeDropdown = -1;
return;
}
this.activeDropdown = key;
}
/**
* Holds on button click login for deleting access grant process.
*/
public onDeleteClick(): void {
public async onDeleteClick(grant: AccessGrant): Promise<void> {
await this.$store.dispatch(TOGGLE_SELECTION, grant);
this.isDeleteClicked = true;
}
/**
* Clears access grants selection.
*/
public async onClearSelection(): Promise<void> {
await this.$store.dispatch(CLEAR_SELECTION);
this.isDeleteClicked = false;
await this.$store.dispatch(CLEAR_SELECTION);
}
/**
* Fetches Access records by name depending on search query.
@ -386,18 +400,21 @@ export default class AccessGrants extends Vue {
public get totalPageCount(): number {
return this.$store.state.accessGrantsModule.page.pageCount;
}
/**
* Returns AccessGrant item component.
* Returns access grants total page count from store.
*/
public get itemComponent(): typeof AccessGrantsItem {
return AccessGrantsItem;
public get accessGrantsTotalCount(): number {
return this.$store.state.accessGrantsModule.page.totalCount;
}
/**
* Returns AccessGrant2 item component.
* Returns access grants limit from store.
*/
public get itemComponent2(): typeof AccessGrantsItem2 {
return AccessGrantsItem2;
public get accessGrantLimit(): number {
return this.$store.state.accessGrantsModule.page.limit;
}
/**
* Returns access grants from store.
*/
@ -568,50 +585,16 @@ export default class AccessGrants extends Vue {
}
.access-grants-items {
position: relative;
&__content {
background-color: #fff;
display: flex;
flex-direction: column;
width: calc(100% - 32px);
justify-content: flex-start;
padding: 16px;
border-radius: 0 0 8px 8px;
margin-top: 20px;
}
}
.access-grants-items2 {
position: relative;
&__content {
background-color: #fff;
display: flex;
flex-direction: column;
justify-content: flex-start;
}
&__footer {
background-color: #fff;
display: flex;
justify-content: space-between;
align-items: center;
height: 80px;
width: 100%;
border: 1px solid #e5e7eb;
border-radius: 0 0 8px 8px;
&__total-accesses {
height: 20px;
padding-left: 20px;
margin-left: 18px;
color: #2c353a;
}
&__pagination-area {
padding-right: 20px;
margin-bottom: 25px;
}
margin-top: 20px;
}
&__empty-state {

View File

@ -2,32 +2,25 @@
// See LICENSE for copying information.
<template>
<div class="grants-item-container">
<div class="grants-item-container__common-info">
<div class="checkbox-container">
<CheckboxIcon class="checkbox-container__image" />
</div>
<div class="name-container" :title="itemData.name">
<p class="name">{{ itemData.name }}</p>
</div>
</div>
<div class="grants-item-container__common-info date-item-container">
<p class="date">{{ itemData.localDate() }}</p>
</div>
</div>
<table-item
:item="{ name: itemData.name, date: itemData.localDate() }"
:selectable="true"
:selected="itemData.isSelected"
:on-click="(_) => $emit('accessGrantClick', itemData)"
@selectChange="(value) => $emit('selectChange', value)"
/>
</template>
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator';
import CheckboxIcon from '@/../static/images/accessGrants/vector.svg';
import { AccessGrant } from '@/types/accessGrants';
import TableItem from "@/components/common/TableItem.vue";
// @vue/component
@Component({
components: {
CheckboxIcon,
TableItem,
},
})
export default class AccessGrantsItem extends Vue {
@ -35,83 +28,3 @@ export default class AccessGrantsItem extends Vue {
private readonly itemData: AccessGrant;
}
</script>
<style scoped lang="scss">
.grants-item-container {
display: flex;
align-items: center;
justify-content: flex-start;
height: 83px;
background-color: #fff;
cursor: pointer;
width: 100%;
&:hover {
background-color: rgb(242 244 247 / 60%);
}
&__common-info {
display: flex;
align-items: center;
justify-content: flex-start;
width: 60%;
}
}
.checkbox-container {
margin-left: 28px;
min-width: 21px;
min-height: 21px;
border-radius: 4px;
border: 1px solid #1b2533;
&__image {
display: none;
}
}
.name-container {
max-width: calc(100% - 131px);
margin-right: 15px;
min-width: 100px;
}
.name {
font-family: 'font_bold', sans-serif;
font-size: 16px;
line-height: 21px;
color: #354049;
margin-left: 17px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.date {
font-family: 'font_regular', sans-serif;
font-size: 16px;
line-height: 21px;
color: #354049;
margin: 0;
}
.grants-item-container.selected {
background-color: rgb(242 244 247 / 60%);
.grants-item-container__common-info {
.checkbox-container {
border: none;
&__image {
display: block;
}
}
}
}
.date-item-container {
width: 40%;
}
</style>

View File

@ -2,176 +2,109 @@
// See LICENSE for copying information.
<template>
<div class="grants-item-container">
<div class="grants-item-container__common-info">
<div class="name-container" :title="itemData.name">
<p class="name">{{ itemData.name }}</p>
<table-item
:item="{ name: itemData.name, date: itemData.localDate() }"
:on-click="onClick"
>
<th slot="options" v-click-outside="closeDropdown" class="grant-item__functional options overflow-visible" @click.stop="openDropdown">
<dots-icon />
<div v-if="isDropdownOpen" class="grant-item__functional__dropdown">
<div class="grant-item__functional__dropdown__item" @click.stop="onDeleteClick">
<delete-icon />
<p class="grant-item__functional__dropdown__item__label">Delete Access</p>
</div>
</div>
</div>
<div class="grants-item-container__common-info date-item-container">
<p class="date">{{ itemData.localDate() }}</p>
</div>
<div class="grants-item-container__common-info menu-item-container">
<p class="ellipses" @click.stop="togglePopupVisibility">...</p>
<div
v-if="popupVisible"
v-click-outside="togglePopupVisibility"
class="popup-menu"
>
<p
class="popup-menu__popup-delete"
@mouseenter="isPopupHovered = true"
@mouseleave="isPopupHovered = false"
@click="toggleSelection"
>
<TrashIconWhite
v-if="isPopupHovered"
/>
<TrashIconBlack
v-if="!isPopupHovered"
/>
Delete Access
</p>
</div>
</div>
</div>
</th>
</table-item>
</template>
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator';
import TrashIconWhite from '@/../static/images/accessGrants/trashIcon.svg';
import TrashIconBlack from '@/../static/images/accessGrants/trashIcon-black.svg';
import { AccessGrant } from '@/types/accessGrants';
import TableItem from "@/components/common/TableItem.vue";
import { ACCESS_GRANTS_ACTIONS } from '@/store/modules/accessGrants';
const {
TOGGLE_SELECTION,
} = ACCESS_GRANTS_ACTIONS;
import DeleteIcon from "../../../static/images/objects/delete.svg";
import DotsIcon from "../../../static/images/objects/dots.svg";
// @vue/component
@Component({
components: {
TrashIconWhite,
TrashIconBlack,
TableItem,
DeleteIcon,
DotsIcon,
},
})
export default class AccessGrantsItem extends Vue {
@Prop({ default: new AccessGrant('', '', new Date(), '') })
private readonly itemData: AccessGrant;
private popupVisible = false;
private isPopupHovered = false;
@Prop({ default: () => () => {} })
public readonly onClick: () => void;
@Prop({ default: false })
public readonly isDropdownOpen: boolean;
@Prop({ default: -1 })
public readonly dropdownKey: number;
public togglePopupVisibility(): void {
this.popupVisible = !this.popupVisible;
/**
* Closes dropdown.
*/
public closeDropdown(): void {
this.$emit('openDropdown', -1);
}
/**
* Toggles access grant selection.
* Opens dropdown.
*/
public async toggleSelection(): Promise<void> {
await this.$store.dispatch(TOGGLE_SELECTION, this.itemData);
this.$emit('altMethod');
this.togglePopupVisibility();
this.isPopupHovered = false;
public openDropdown(): void {
this.$emit('openDropdown', this.dropdownKey);
}
public async onDeleteClick(): Promise<void> {
this.$emit('deleteClick', this.itemData);
this.closeDropdown();
}
}
</script>
<style scoped lang="scss">
@mixin popup-menu-button {
padding: 0 15px;
height: 100%;
line-height: 50px;
text-align: center;
font-family: 'font_regular', sans-serif;
color: #1b2533;
transition: 100ms;
}
.grant-item {
.grants-item-container {
display: flex;
align-items: center;
justify-content: flex-start;
height: 83px;
background-color: #fff;
border: 1px solid #e5e7eb;
border-bottom: 0;
width: 100%;
&__functional {
padding: 0 10px;
position: relative;
cursor: pointer;
&__common-info {
display: flex;
align-items: center;
justify-content: flex-start;
width: 60%;
}
}
&__dropdown {
position: absolute;
top: 25px;
right: 15px;
background: #fff;
box-shadow: 0 20px 34px rgb(10 27 44 / 28%);
border-radius: 6px;
width: 255px;
padding: 10px 0;
z-index: 100;
.name-container {
max-width: calc(100% - 131px);
margin-right: 15px;
min-width: 100px;
}
&__item {
display: flex;
align-items: center;
padding: 20px 25px;
width: calc(100% - 50px);
.name {
font-family: 'font_bold', sans-serif;
font-size: 16px;
line-height: 21px;
color: #354049;
margin-left: 38px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
&__label {
margin: 0 0 0 10px;
}
.date {
font-family: 'font_regular', sans-serif;
font-size: 16px;
line-height: 21px;
color: #354049;
margin: 0;
}
&:hover {
background-color: #f4f5f7;
font-family: 'font_medium', sans-serif;
.ellipses {
margin: 0 auto 20px;
font-size: 30px;
font-weight: 1000;
color: #7c8794;
cursor: pointer;
}
.popup-menu {
width: 160px;
height: 50px;
position: absolute;
right: 40%;
bottom: -65%;
z-index: 1;
background: #fff;
border-radius: 10px;
box-shadow: 0 20px 34px rgb(10 27 44 / 28%);
&__popup-delete {
@include popup-menu-button;
border-radius: 10px;
&:hover {
background-color: #b53737;
cursor: pointer;
color: #fff;
svg ::v-deep path {
fill: #0068dc;
stroke: #0068dc;
}
}
}
}
}
}
.date-item-container {
width: 50%;
}
.menu-item-container {
width: 10%;
position: relative;
}
</style>

View File

@ -135,7 +135,7 @@ export default class ProjectMembersArea extends Vue {
* Returns team members limit from store.
*/
public get projectMemberLimit(): number {
return this.$store.state.projectMembersModule.page.totalCount;
return this.$store.state.projectMembersModule.page.limit;
}
/**

View File

@ -21,7 +21,7 @@ exports[`ProjectMembersArea.vue renders correctly 1`] = `
</div>
<!---->
<!---->
<v-table-stub selectable="true" totalpagecount="1" itemslabel="project members" limit="1" items="[object Object]" totalitemscount="1" onpageclickcallback="[Function]" class="team-area__table"></v-table-stub>
<v-table-stub selectable="true" totalpagecount="1" itemslabel="project members" limit="6" items="[object Object]" totalitemscount="1" onpageclickcallback="[Function]" class="team-area__table"></v-table-stub>
</div>
`;
@ -32,6 +32,6 @@ exports[`ProjectMembersArea.vue team area renders correctly 1`] = `
</div>
<!---->
<!---->
<v-table-stub selectable="true" totalpagecount="1" itemslabel="project members" limit="1" items="[object Object]" totalitemscount="1" onpageclickcallback="[Function]" class="team-area__table"></v-table-stub>
<v-table-stub selectable="true" totalpagecount="1" itemslabel="project members" limit="6" items="[object Object]" totalitemscount="1" onpageclickcallback="[Function]" class="team-area__table"></v-table-stub>
</div>
`;