debian-mirror-gitlab/app/assets/javascripts/registry/explorer/pages/list.vue
2020-10-24 23:57:45 +05:30

251 lines
6.9 KiB
Vue

<script>
import { mapState, mapActions } from 'vuex';
import {
GlEmptyState,
GlTooltipDirective,
GlModal,
GlSprintf,
GlLink,
GlAlert,
GlSkeletonLoader,
GlSearchBoxByClick,
} from '@gitlab/ui';
import Tracking from '~/tracking';
import ProjectEmptyState from '../components/list_page/project_empty_state.vue';
import GroupEmptyState from '../components/list_page/group_empty_state.vue';
import RegistryHeader from '../components/list_page/registry_header.vue';
import ImageList from '../components/list_page/image_list.vue';
import CliCommands from '../components/list_page/cli_commands.vue';
import {
DELETE_IMAGE_SUCCESS_MESSAGE,
DELETE_IMAGE_ERROR_MESSAGE,
CONNECTION_ERROR_TITLE,
CONNECTION_ERROR_MESSAGE,
REMOVE_REPOSITORY_MODAL_TEXT,
REMOVE_REPOSITORY_LABEL,
SEARCH_PLACEHOLDER_TEXT,
IMAGE_REPOSITORY_LIST_LABEL,
EMPTY_RESULT_TITLE,
EMPTY_RESULT_MESSAGE,
} from '../constants/index';
export default {
name: 'RegistryListApp',
components: {
GlEmptyState,
ProjectEmptyState,
GroupEmptyState,
ImageList,
GlModal,
GlSprintf,
GlLink,
GlAlert,
GlSkeletonLoader,
GlSearchBoxByClick,
RegistryHeader,
CliCommands,
},
directives: {
GlTooltip: GlTooltipDirective,
},
mixins: [Tracking.mixin()],
loader: {
repeat: 10,
width: 1000,
height: 40,
},
i18n: {
CONNECTION_ERROR_TITLE,
CONNECTION_ERROR_MESSAGE,
REMOVE_REPOSITORY_MODAL_TEXT,
REMOVE_REPOSITORY_LABEL,
SEARCH_PLACEHOLDER_TEXT,
IMAGE_REPOSITORY_LIST_LABEL,
EMPTY_RESULT_TITLE,
EMPTY_RESULT_MESSAGE,
},
data() {
return {
itemToDelete: {},
deleteAlertType: null,
search: null,
isEmpty: false,
};
},
computed: {
...mapState(['config', 'isLoading', 'images', 'pagination']),
tracking() {
return {
label: 'registry_repository_delete',
};
},
showCommands() {
return Boolean(!this.isLoading && !this.config?.isGroupPage && this.images?.length);
},
showDeleteAlert() {
return this.deleteAlertType && this.itemToDelete?.path;
},
deleteImageAlertMessage() {
return this.deleteAlertType === 'success'
? DELETE_IMAGE_SUCCESS_MESSAGE
: DELETE_IMAGE_ERROR_MESSAGE;
},
},
mounted() {
this.loadImageList(this.$route.name);
},
methods: {
...mapActions(['requestImagesList', 'requestDeleteImage']),
loadImageList(fromName) {
if (!fromName || !this.images?.length) {
return this.requestImagesList().then(() => {
this.isEmpty = this.images.length === 0;
});
}
return Promise.resolve();
},
deleteImage(item) {
this.track('click_button');
this.itemToDelete = item;
this.$refs.deleteModal.show();
},
handleDeleteImage() {
this.track('confirm_delete');
return this.requestDeleteImage(this.itemToDelete)
.then(() => {
this.deleteAlertType = 'success';
})
.catch(() => {
this.deleteAlertType = 'danger';
});
},
dismissDeleteAlert() {
this.deleteAlertType = null;
this.itemToDelete = {};
},
},
};
</script>
<template>
<div>
<gl-alert
v-if="showDeleteAlert"
:variant="deleteAlertType"
class="mt-2"
dismissible
@dismiss="dismissDeleteAlert"
>
<gl-sprintf :message="deleteImageAlertMessage">
<template #title>
{{ itemToDelete.path }}
</template>
</gl-sprintf>
</gl-alert>
<gl-empty-state
v-if="config.characterError"
:title="$options.i18n.CONNECTION_ERROR_TITLE"
:svg-path="config.containersErrorImage"
>
<template #description>
<p>
<gl-sprintf :message="$options.i18n.CONNECTION_ERROR_MESSAGE">
<template #docLink="{content}">
<gl-link :href="`${config.helpPagePath}#docker-connection-error`" target="_blank">
{{ content }}
</gl-link>
</template>
</gl-sprintf>
</p>
</template>
</gl-empty-state>
<template v-else>
<registry-header
:images-count="pagination.total"
:expiration-policy="config.expirationPolicy"
:help-page-path="config.helpPagePath"
:expiration-policy-help-page-path="config.expirationPolicyHelpPagePath"
:hide-expiration-policy-data="config.isGroupPage"
>
<template #commands>
<cli-commands v-if="showCommands" />
</template>
</registry-header>
<div v-if="isLoading" class="mt-2">
<gl-skeleton-loader
v-for="index in $options.loader.repeat"
:key="index"
:width="$options.loader.width"
:height="$options.loader.height"
preserve-aspect-ratio="xMinYMax meet"
>
<rect width="500" x="10" y="10" height="20" rx="4" />
<circle cx="525" cy="20" r="10" />
<rect x="960" y="0" width="40" height="40" rx="4" />
</gl-skeleton-loader>
</div>
<template v-else>
<template v-if="!isEmpty">
<div class="gl-display-flex gl-p-1 gl-mt-3" data-testid="listHeader">
<div class="gl-flex-fill-1">
<h5>{{ $options.i18n.IMAGE_REPOSITORY_LIST_LABEL }}</h5>
</div>
<div>
<gl-search-box-by-click
v-model="search"
:placeholder="$options.i18n.SEARCH_PLACEHOLDER_TEXT"
@submit="requestImagesList({ name: $event })"
/>
</div>
</div>
<image-list
v-if="images.length"
:images="images"
:pagination="pagination"
@pageChange="requestImagesList({ pagination: { page: $event }, name: search })"
@delete="deleteImage"
/>
<gl-empty-state
v-else
:svg-path="config.noContainersImage"
data-testid="emptySearch"
:title="$options.i18n.EMPTY_RESULT_TITLE"
>
<template #description>
{{ $options.i18n.EMPTY_RESULT_MESSAGE }}
</template>
</gl-empty-state>
</template>
<template v-else>
<project-empty-state v-if="!config.isGroupPage" />
<group-empty-state v-else />
</template>
</template>
<gl-modal
ref="deleteModal"
modal-id="delete-image-modal"
ok-variant="danger"
@ok="handleDeleteImage"
@cancel="track('cancel_delete')"
>
<template #modal-title>{{ $options.i18n.REMOVE_REPOSITORY_LABEL }}</template>
<p>
<gl-sprintf :message="$options.i18n.REMOVE_REPOSITORY_MODAL_TEXT">
<template #title>
<b>{{ itemToDelete.path }}</b>
</template>
</gl-sprintf>
</p>
<template #modal-ok>{{ __('Remove') }}</template>
</gl-modal>
</template>
</div>
</template>