2020-03-13 15:44:24 +05:30
|
|
|
<script>
|
|
|
|
import { mapState, mapActions } from 'vuex';
|
|
|
|
import {
|
|
|
|
GlEmptyState,
|
|
|
|
GlTooltipDirective,
|
|
|
|
GlModal,
|
|
|
|
GlSprintf,
|
|
|
|
GlLink,
|
2020-04-22 19:07:51 +05:30
|
|
|
GlAlert,
|
2020-04-08 14:13:33 +05:30
|
|
|
GlSkeletonLoader,
|
2020-05-24 23:13:21 +05:30
|
|
|
GlSearchBoxByClick,
|
2020-03-13 15:44:24 +05:30
|
|
|
} from '@gitlab/ui';
|
|
|
|
import Tracking from '~/tracking';
|
2020-05-24 23:13:21 +05:30
|
|
|
|
2020-03-13 15:44:24 +05:30
|
|
|
import ProjectEmptyState from '../components/project_empty_state.vue';
|
|
|
|
import GroupEmptyState from '../components/group_empty_state.vue';
|
2020-04-08 14:13:33 +05:30
|
|
|
import ProjectPolicyAlert from '../components/project_policy_alert.vue';
|
2020-04-22 19:07:51 +05:30
|
|
|
import QuickstartDropdown from '../components/quickstart_dropdown.vue';
|
2020-05-24 23:13:21 +05:30
|
|
|
import ImageList from '../components/image_list.vue';
|
|
|
|
|
2020-04-22 19:07:51 +05:30
|
|
|
import {
|
|
|
|
DELETE_IMAGE_SUCCESS_MESSAGE,
|
|
|
|
DELETE_IMAGE_ERROR_MESSAGE,
|
|
|
|
CONTAINER_REGISTRY_TITLE,
|
|
|
|
CONNECTION_ERROR_TITLE,
|
|
|
|
CONNECTION_ERROR_MESSAGE,
|
|
|
|
LIST_INTRO_TEXT,
|
|
|
|
REMOVE_REPOSITORY_MODAL_TEXT,
|
2020-05-24 23:13:21 +05:30
|
|
|
REMOVE_REPOSITORY_LABEL,
|
|
|
|
SEARCH_PLACEHOLDER_TEXT,
|
|
|
|
IMAGE_REPOSITORY_LIST_LABEL,
|
|
|
|
EMPTY_RESULT_TITLE,
|
|
|
|
EMPTY_RESULT_MESSAGE,
|
2020-04-22 19:07:51 +05:30
|
|
|
} from '../constants';
|
2020-03-13 15:44:24 +05:30
|
|
|
|
|
|
|
export default {
|
|
|
|
name: 'RegistryListApp',
|
|
|
|
components: {
|
|
|
|
GlEmptyState,
|
|
|
|
ProjectEmptyState,
|
|
|
|
GroupEmptyState,
|
2020-04-08 14:13:33 +05:30
|
|
|
ProjectPolicyAlert,
|
2020-04-22 19:07:51 +05:30
|
|
|
QuickstartDropdown,
|
2020-05-24 23:13:21 +05:30
|
|
|
ImageList,
|
2020-03-13 15:44:24 +05:30
|
|
|
GlModal,
|
|
|
|
GlSprintf,
|
|
|
|
GlLink,
|
2020-04-22 19:07:51 +05:30
|
|
|
GlAlert,
|
2020-04-08 14:13:33 +05:30
|
|
|
GlSkeletonLoader,
|
2020-05-24 23:13:21 +05:30
|
|
|
GlSearchBoxByClick,
|
2020-03-13 15:44:24 +05:30
|
|
|
},
|
|
|
|
directives: {
|
|
|
|
GlTooltip: GlTooltipDirective,
|
|
|
|
},
|
|
|
|
mixins: [Tracking.mixin()],
|
2020-04-08 14:13:33 +05:30
|
|
|
loader: {
|
|
|
|
repeat: 10,
|
|
|
|
width: 1000,
|
|
|
|
height: 40,
|
|
|
|
},
|
2020-04-22 19:07:51 +05:30
|
|
|
i18n: {
|
2020-05-24 23:13:21 +05:30
|
|
|
CONTAINER_REGISTRY_TITLE,
|
|
|
|
CONNECTION_ERROR_TITLE,
|
|
|
|
CONNECTION_ERROR_MESSAGE,
|
|
|
|
LIST_INTRO_TEXT,
|
|
|
|
REMOVE_REPOSITORY_MODAL_TEXT,
|
|
|
|
REMOVE_REPOSITORY_LABEL,
|
|
|
|
SEARCH_PLACEHOLDER_TEXT,
|
|
|
|
IMAGE_REPOSITORY_LIST_LABEL,
|
|
|
|
EMPTY_RESULT_TITLE,
|
|
|
|
EMPTY_RESULT_MESSAGE,
|
2020-04-22 19:07:51 +05:30
|
|
|
},
|
2020-03-13 15:44:24 +05:30
|
|
|
data() {
|
|
|
|
return {
|
|
|
|
itemToDelete: {},
|
2020-04-22 19:07:51 +05:30
|
|
|
deleteAlertType: null,
|
2020-05-24 23:13:21 +05:30
|
|
|
search: null,
|
|
|
|
isEmpty: false,
|
2020-03-13 15:44:24 +05:30
|
|
|
};
|
|
|
|
},
|
|
|
|
computed: {
|
|
|
|
...mapState(['config', 'isLoading', 'images', 'pagination']),
|
|
|
|
tracking() {
|
|
|
|
return {
|
|
|
|
label: 'registry_repository_delete',
|
|
|
|
};
|
|
|
|
},
|
2020-04-22 19:07:51 +05:30
|
|
|
showQuickStartDropdown() {
|
|
|
|
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;
|
|
|
|
},
|
2020-03-13 15:44:24 +05:30
|
|
|
},
|
2020-05-24 23:13:21 +05:30
|
|
|
mounted() {
|
|
|
|
this.loadImageList(this.$route.name);
|
|
|
|
},
|
2020-03-13 15:44:24 +05:30
|
|
|
methods: {
|
|
|
|
...mapActions(['requestImagesList', 'requestDeleteImage']),
|
2020-05-24 23:13:21 +05:30
|
|
|
loadImageList(fromName) {
|
|
|
|
if (!fromName || !this.images?.length) {
|
|
|
|
return this.requestImagesList().then(() => {
|
|
|
|
this.isEmpty = this.images.length === 0;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
return Promise.resolve();
|
|
|
|
},
|
2020-03-13 15:44:24 +05:30
|
|
|
deleteImage(item) {
|
|
|
|
this.track('click_button');
|
|
|
|
this.itemToDelete = item;
|
|
|
|
this.$refs.deleteModal.show();
|
|
|
|
},
|
2020-04-22 19:07:51 +05:30
|
|
|
handleDeleteImage() {
|
2020-03-13 15:44:24 +05:30
|
|
|
this.track('confirm_delete');
|
2020-04-22 19:07:51 +05:30
|
|
|
return this.requestDeleteImage(this.itemToDelete)
|
|
|
|
.then(() => {
|
|
|
|
this.deleteAlertType = 'success';
|
|
|
|
})
|
|
|
|
.catch(() => {
|
|
|
|
this.deleteAlertType = 'danger';
|
|
|
|
});
|
2020-03-13 15:44:24 +05:30
|
|
|
},
|
2020-04-22 19:07:51 +05:30
|
|
|
dismissDeleteAlert() {
|
|
|
|
this.deleteAlertType = null;
|
|
|
|
this.itemToDelete = {};
|
|
|
|
},
|
2020-03-13 15:44:24 +05:30
|
|
|
},
|
|
|
|
};
|
|
|
|
</script>
|
|
|
|
|
|
|
|
<template>
|
2020-04-08 14:13:33 +05:30
|
|
|
<div class="w-100 slide-enter-from-element">
|
2020-04-22 19:07:51 +05:30
|
|
|
<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>
|
|
|
|
|
|
|
|
<project-policy-alert v-if="!config.isGroupPage" class="mt-2" />
|
2020-04-08 14:13:33 +05:30
|
|
|
|
2020-03-13 15:44:24 +05:30
|
|
|
<gl-empty-state
|
|
|
|
v-if="config.characterError"
|
2020-05-24 23:13:21 +05:30
|
|
|
:title="$options.i18n.CONNECTION_ERROR_TITLE"
|
2020-03-13 15:44:24 +05:30
|
|
|
:svg-path="config.containersErrorImage"
|
|
|
|
>
|
|
|
|
<template #description>
|
|
|
|
<p>
|
2020-05-24 23:13:21 +05:30
|
|
|
<gl-sprintf :message="$options.i18n.CONNECTION_ERROR_MESSAGE">
|
2020-03-13 15:44:24 +05:30
|
|
|
<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>
|
2020-04-08 14:13:33 +05:30
|
|
|
<div>
|
2020-04-22 19:07:51 +05:30
|
|
|
<div class="d-flex justify-content-between align-items-center">
|
2020-05-24 23:13:21 +05:30
|
|
|
<h4>{{ $options.i18n.CONTAINER_REGISTRY_TITLE }}</h4>
|
2020-04-22 19:07:51 +05:30
|
|
|
<quickstart-dropdown v-if="showQuickStartDropdown" class="d-none d-sm-block" />
|
|
|
|
</div>
|
2020-04-08 14:13:33 +05:30
|
|
|
<p>
|
2020-05-24 23:13:21 +05:30
|
|
|
<gl-sprintf :message="$options.i18n.LIST_INTRO_TEXT">
|
2020-04-08 14:13:33 +05:30
|
|
|
<template #docLink="{content}">
|
|
|
|
<gl-link :href="config.helpPagePath" target="_blank">
|
|
|
|
{{ content }}
|
|
|
|
</gl-link>
|
|
|
|
</template>
|
|
|
|
</gl-sprintf>
|
|
|
|
</p>
|
|
|
|
</div>
|
2020-03-13 15:44:24 +05:30
|
|
|
|
2020-04-08 14:13:33 +05:30
|
|
|
<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>
|
2020-05-24 23:13:21 +05:30
|
|
|
<template v-if="!isEmpty">
|
|
|
|
<div class="gl-display-flex gl-p-1" 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 })"
|
|
|
|
/>
|
2020-03-13 15:44:24 +05:30
|
|
|
</div>
|
|
|
|
</div>
|
2020-05-24 23:13:21 +05:30
|
|
|
|
|
|
|
<image-list
|
|
|
|
v-if="images.length"
|
|
|
|
:images="images"
|
|
|
|
:pagination="pagination"
|
|
|
|
@pageChange="requestImagesList({ pagination: { page: $event }, name: search })"
|
|
|
|
@delete="deleteImage"
|
2020-03-13 15:44:24 +05:30
|
|
|
/>
|
2020-04-08 14:13:33 +05:30
|
|
|
|
2020-05-24 23:13:21 +05:30
|
|
|
<gl-empty-state
|
|
|
|
v-else
|
|
|
|
:svg-path="config.noContainersImage"
|
|
|
|
data-testid="emptySearch"
|
|
|
|
:title="$options.i18n.EMPTY_RESULT_TITLE"
|
|
|
|
class="container-message"
|
|
|
|
>
|
|
|
|
<template #description>
|
|
|
|
{{ $options.i18n.EMPTY_RESULT_MESSAGE }}
|
|
|
|
</template>
|
|
|
|
</gl-empty-state>
|
|
|
|
</template>
|
2020-03-13 15:44:24 +05:30
|
|
|
<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"
|
2020-04-22 19:07:51 +05:30
|
|
|
@ok="handleDeleteImage"
|
2020-03-13 15:44:24 +05:30
|
|
|
@cancel="track('cancel_delete')"
|
|
|
|
>
|
2020-05-24 23:13:21 +05:30
|
|
|
<template #modal-title>{{ $options.i18n.REMOVE_REPOSITORY_LABEL }}</template>
|
2020-03-13 15:44:24 +05:30
|
|
|
<p>
|
2020-05-24 23:13:21 +05:30
|
|
|
<gl-sprintf :message="$options.i18n.REMOVE_REPOSITORY_MODAL_TEXT">
|
2020-03-13 15:44:24 +05:30
|
|
|
<template #title>
|
|
|
|
<b>{{ itemToDelete.path }}</b>
|
|
|
|
</template>
|
|
|
|
</gl-sprintf>
|
|
|
|
</p>
|
|
|
|
<template #modal-ok>{{ __('Remove') }}</template>
|
|
|
|
</gl-modal>
|
|
|
|
</template>
|
|
|
|
</div>
|
|
|
|
</template>
|