diff options
Diffstat (limited to 'app/assets/javascripts/registry/explorer/pages/list.vue')
-rw-r--r-- | app/assets/javascripts/registry/explorer/pages/list.vue | 211 |
1 files changed, 209 insertions, 2 deletions
diff --git a/app/assets/javascripts/registry/explorer/pages/list.vue b/app/assets/javascripts/registry/explorer/pages/list.vue index 6d32ba41eae..dc730ac2828 100644 --- a/app/assets/javascripts/registry/explorer/pages/list.vue +++ b/app/assets/javascripts/registry/explorer/pages/list.vue @@ -1,7 +1,214 @@ <script> -export default {}; +import { mapState, mapActions } from 'vuex'; +import { + GlLoadingIcon, + GlEmptyState, + GlPagination, + GlTooltipDirective, + GlButton, + GlIcon, + GlModal, + GlSprintf, + GlLink, +} from '@gitlab/ui'; +import Tracking from '~/tracking'; +import ClipboardButton from '~/vue_shared/components/clipboard_button.vue'; +import ProjectEmptyState from '../components/project_empty_state.vue'; +import GroupEmptyState from '../components/group_empty_state.vue'; + +export default { + name: 'RegistryListApp', + components: { + GlEmptyState, + GlLoadingIcon, + GlPagination, + ProjectEmptyState, + GroupEmptyState, + ClipboardButton, + GlButton, + GlIcon, + GlModal, + GlSprintf, + GlLink, + }, + directives: { + GlTooltip: GlTooltipDirective, + }, + mixins: [Tracking.mixin()], + data() { + return { + itemToDelete: {}, + }; + }, + computed: { + ...mapState(['config', 'isLoading', 'images', 'pagination']), + tracking() { + return { + label: 'registry_repository_delete', + }; + }, + currentPage: { + get() { + return this.pagination.page; + }, + set(page) { + this.requestImagesList({ page }); + }, + }, + }, + methods: { + ...mapActions(['requestImagesList', 'requestDeleteImage']), + deleteImage(item) { + // This event is already tracked in the system and so the name must be kept to aggregate the data + this.track('click_button'); + this.itemToDelete = item; + this.$refs.deleteModal.show(); + }, + handleDeleteRepository() { + this.track('confirm_delete'); + this.requestDeleteImage(this.itemToDelete.destroy_path); + this.itemToDelete = {}; + }, + encodeListItem(item) { + const params = JSON.stringify({ name: item.path, tags_path: item.tags_path }); + return window.btoa(params); + }, + }, +}; </script> <template> - <div></div> + <div class="position-absolute w-100 slide-enter-from-element"> + <gl-empty-state + v-if="config.characterError" + :title="s__('ContainerRegistry|Docker connection error')" + :svg-path="config.containersErrorImage" + > + <template #description> + <p> + <gl-sprintf + :message=" + s__(`ContainerRegistry|We are having trouble connecting to Docker, which could be due to an + issue with your project name or path. + %{docLinkStart}More Information%{docLinkEnd}`) + " + > + <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> + <gl-loading-icon v-if="isLoading" size="md" class="prepend-top-16" /> + + <template v-else> + <div v-if="images.length" ref="imagesList"> + <h4>{{ s__('ContainerRegistry|Container Registry') }}</h4> + <p> + <gl-sprintf + :message=" + s__(`ContainerRegistry|With the Docker Container Registry integrated into GitLab, every + project can have its own space to store its Docker images. + %{docLinkStart}More Information%{docLinkEnd}`) + " + > + <template #docLink="{content}"> + <gl-link :href="config.helpPagePath" target="_blank"> + {{ content }} + </gl-link> + </template> + </gl-sprintf> + </p> + + <div class="d-flex flex-column"> + <div + v-for="(listItem, index) in images" + :key="index" + ref="rowItem" + :class="[ + 'd-flex justify-content-between align-items-center py-2 border-bottom', + { 'border-top': index === 0 }, + ]" + > + <div> + <router-link + ref="detailsLink" + :to="{ name: 'details', params: { id: encodeListItem(listItem) } }" + > + {{ listItem.path }} + </router-link> + <clipboard-button + v-if="listItem.location" + ref="clipboardButton" + :text="listItem.location" + :title="listItem.location" + css-class="btn-default btn-transparent btn-clipboard" + /> + </div> + <div + v-gl-tooltip="{ disabled: listItem.destroy_path }" + class="d-none d-sm-block" + :title=" + s__( + 'ContainerRegistry|Missing or insufficient permission, delete button disabled', + ) + " + > + <gl-button + ref="deleteImageButton" + v-gl-tooltip + :disabled="!listItem.destroy_path" + :title="s__('ContainerRegistry|Remove repository')" + :aria-label="s__('ContainerRegistry|Remove repository')" + class="btn-inverted" + variant="danger" + @click="deleteImage(listItem)" + > + <gl-icon name="remove" /> + </gl-button> + </div> + </div> + </div> + <gl-pagination + v-model="currentPage" + :per-page="pagination.perPage" + :total-items="pagination.total" + align="center" + class="w-100 mt-2" + /> + </div> + <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="handleDeleteRepository" + @cancel="track('cancel_delete')" + > + <template #modal-title>{{ s__('ContainerRegistry|Remove repository') }}</template> + <p> + <gl-sprintf + :message=" s__( + 'ContainerRegistry|You are about to remove repository %{title}. Once you confirm, this repository will be permanently deleted.', + )," + > + <template #title> + <b>{{ itemToDelete.path }}</b> + </template> + </gl-sprintf> + </p> + <template #modal-ok>{{ __('Remove') }}</template> + </gl-modal> + </template> + </div> </template> |