debian-mirror-gitlab/app/assets/javascripts/access_tokens/components/access_token_table_app.vue

168 lines
5.4 KiB
Vue

<script>
import { GlButton, GlIcon, GlLink, GlPagination, GlTable, GlTooltipDirective } from '@gitlab/ui';
import { helpPagePath } from '~/helpers/help_page_helper';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import { __, sprintf } from '~/locale';
import DomElementListener from '~/vue_shared/components/dom_element_listener.vue';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import UserDate from '~/vue_shared/components/user_date.vue';
import { EVENT_SUCCESS, FIELDS, FORM_SELECTOR, INITIAL_PAGE, PAGE_SIZE } from './constants';
export default {
EVENT_SUCCESS,
FORM_SELECTOR,
PAGE_SIZE,
name: 'AccessTokenTableApp',
components: {
DomElementListener,
GlButton,
GlIcon,
GlLink,
GlPagination,
GlTable,
TimeAgoTooltip,
UserDate,
},
directives: {
GlTooltip: GlTooltipDirective,
},
lastUsedHelpLink: helpPagePath('/user/profile/personal_access_tokens.md', {
anchor: 'view-the-last-time-a-token-was-used',
}),
i18n: {
emptyField: __('Never'),
expired: __('Expired'),
header: __('Active %{accessTokenTypePlural} (%{totalAccessTokens})'),
modalMessage: __(
'Are you sure you want to revoke this %{accessTokenType}? This action cannot be undone.',
),
revokeButton: __('Revoke'),
tokenValidity: __('Token valid until revoked'),
},
inject: [
'accessTokenType',
'accessTokenTypePlural',
'initialActiveAccessTokens',
'noActiveTokensMessage',
'showRole',
],
data() {
return {
activeAccessTokens: this.initialActiveAccessTokens,
currentPage: INITIAL_PAGE,
};
},
computed: {
filteredFields() {
return this.showRole ? FIELDS : FIELDS.filter((field) => field.key !== 'role');
},
header() {
return sprintf(this.$options.i18n.header, {
accessTokenTypePlural: this.accessTokenTypePlural,
totalAccessTokens: this.activeAccessTokens.length,
});
},
modalMessage() {
return sprintf(this.$options.i18n.modalMessage, {
accessTokenType: this.accessTokenType,
});
},
showPagination() {
return this.activeAccessTokens.length > PAGE_SIZE;
},
},
methods: {
onSuccess(event) {
const [{ active_access_tokens: activeAccessTokens }] = event.detail;
this.activeAccessTokens = convertObjectPropsToCamelCase(activeAccessTokens, { deep: true });
this.currentPage = INITIAL_PAGE;
},
sortingChanged(aRow, bRow, key) {
if (['createdAt', 'lastUsedAt', 'expiresAt'].includes(key)) {
// Transform `null` value to the latest possible date
// https://stackoverflow.com/a/11526569/18428169
const maxEpoch = 8640000000000000;
const a = new Date(aRow[key] ?? maxEpoch).getTime();
const b = new Date(bRow[key] ?? maxEpoch).getTime();
return a - b;
}
// For other columns the default sorting works OK
return false;
},
},
};
</script>
<template>
<dom-element-listener :selector="$options.FORM_SELECTOR" @[$options.EVENT_SUCCESS]="onSuccess">
<div>
<hr />
<h5>{{ header }}</h5>
<gl-table
data-testid="active-tokens"
:empty-text="noActiveTokensMessage"
:fields="filteredFields"
:items="activeAccessTokens"
:per-page="$options.PAGE_SIZE"
:current-page="currentPage"
:sort-compare="sortingChanged"
show-empty
>
<template #cell(createdAt)="{ item: { createdAt } }">
<user-date :date="createdAt" />
</template>
<template #head(lastUsedAt)="{ label }">
<span>{{ label }}</span>
<gl-link :href="$options.lastUsedHelpLink"
><gl-icon name="question-o" /><span class="gl-sr-only">{{
s__('AccessTokens|The last time a token was used')
}}</span></gl-link
>
</template>
<template #cell(lastUsedAt)="{ item: { lastUsedAt } }">
<time-ago-tooltip v-if="lastUsedAt" :time="lastUsedAt" />
<template v-else> {{ $options.i18n.emptyField }}</template>
</template>
<template #cell(expiresAt)="{ item: { expiresAt, expired, expiresSoon } }">
<template v-if="expiresAt">
<span v-if="expired" class="text-danger">{{ $options.i18n.expired }}</span>
<time-ago-tooltip v-else :class="{ 'text-warning': expiresSoon }" :time="expiresAt" />
</template>
<span v-else v-gl-tooltip :title="$options.i18n.tokenValidity">{{
$options.i18n.emptyField
}}</span>
</template>
<template #cell(action)="{ item: { revokePath } }">
<gl-button
category="tertiary"
:aria-label="$options.i18n.revokeButton"
:data-confirm="modalMessage"
data-confirm-btn-variant="danger"
data-qa-selector="revoke_button"
data-method="put"
:href="revokePath"
icon="remove"
/>
</template>
</gl-table>
<gl-pagination
v-if="showPagination"
v-model="currentPage"
:per-page="$options.PAGE_SIZE"
:total-items="activeAccessTokens.length"
:prev-text="__('Prev')"
:next-text="__('Next')"
:label-next-page="__('Go to next page')"
:label-prev-page="__('Go to previous page')"
align="center"
/>
</div>
</dom-element-listener>
</template>