diff options
Diffstat (limited to 'app/assets/javascripts/packages_and_registries/container_registry')
11 files changed, 101 insertions, 149 deletions
diff --git a/app/assets/javascripts/packages_and_registries/container_registry/explorer/components/details_page/details_header.vue b/app/assets/javascripts/packages_and_registries/container_registry/explorer/components/details_page/details_header.vue index d988ad8d8ca..29c181f04fb 100644 --- a/app/assets/javascripts/packages_and_registries/container_registry/explorer/components/details_page/details_header.vue +++ b/app/assets/javascripts/packages_and_registries/container_registry/explorer/components/details_page/details_header.vue @@ -143,6 +143,7 @@ export default { </template> <template #right-actions> <gl-dropdown + v-if="!deleteButtonDisabled" icon="ellipsis_v" text="More actions" :text-sr-only="true" @@ -150,11 +151,7 @@ export default { no-caret right > - <gl-dropdown-item - variant="danger" - :disabled="deleteButtonDisabled" - @click="$emit('delete')" - > + <gl-dropdown-item variant="danger" @click="$emit('delete')"> {{ __('Delete image repository') }} </gl-dropdown-item> </gl-dropdown> diff --git a/app/assets/javascripts/packages_and_registries/container_registry/explorer/components/details_page/empty_state.vue b/app/assets/javascripts/packages_and_registries/container_registry/explorer/components/details_page/empty_state.vue deleted file mode 100644 index a16d95a6b30..00000000000 --- a/app/assets/javascripts/packages_and_registries/container_registry/explorer/components/details_page/empty_state.vue +++ /dev/null @@ -1,44 +0,0 @@ -<script> -import { GlEmptyState } from '@gitlab/ui'; -import { - NO_TAGS_TITLE, - NO_TAGS_MESSAGE, - MISSING_OR_DELETED_IMAGE_TITLE, - MISSING_OR_DELETED_IMAGE_MESSAGE, -} from '../../constants/index'; - -export default { - components: { - GlEmptyState, - }, - props: { - noContainersImage: { - type: String, - required: false, - default: '', - }, - isEmptyImage: { - type: Boolean, - default: false, - required: false, - }, - }, - computed: { - title() { - return this.isEmptyImage ? MISSING_OR_DELETED_IMAGE_TITLE : NO_TAGS_TITLE; - }, - description() { - return this.isEmptyImage ? MISSING_OR_DELETED_IMAGE_MESSAGE : NO_TAGS_MESSAGE; - }, - }, -}; -</script> - -<template> - <gl-empty-state - :title="title" - :svg-path="noContainersImage" - :description="description" - class="gl-mx-auto gl-my-0" - /> -</template> diff --git a/app/assets/javascripts/packages_and_registries/container_registry/explorer/components/details_page/tags_list.vue b/app/assets/javascripts/packages_and_registries/container_registry/explorer/components/details_page/tags_list.vue index 2d32295b537..4fda4058711 100644 --- a/app/assets/javascripts/packages_and_registries/container_registry/explorer/components/details_page/tags_list.vue +++ b/app/assets/javascripts/packages_and_registries/container_registry/explorer/components/details_page/tags_list.vue @@ -1,28 +1,38 @@ <script> +import { GlEmptyState } from '@gitlab/ui'; import createFlash from '~/flash'; import { n__ } from '~/locale'; import { joinPaths } from '~/lib/utils/url_utility'; import RegistryList from '~/packages_and_registries/shared/components/registry_list.vue'; + +import PersistedSearch from '~/packages_and_registries/shared/components/persisted_search.vue'; +import { FILTERED_SEARCH_TERM } from '~/packages_and_registries/shared/constants'; import { REMOVE_TAGS_BUTTON_TITLE, TAGS_LIST_TITLE, GRAPHQL_PAGE_SIZE, FETCH_IMAGES_LIST_ERROR_MESSAGE, + NAME_SORT_FIELD, + NO_TAGS_TITLE, + NO_TAGS_MESSAGE, + NO_TAGS_MATCHING_FILTERS_TITLE, + NO_TAGS_MATCHING_FILTERS_DESCRIPTION, } from '../../constants/index'; import getContainerRepositoryTagsQuery from '../../graphql/queries/get_container_repository_tags.query.graphql'; -import EmptyState from './empty_state.vue'; import TagsListRow from './tags_list_row.vue'; import TagsLoader from './tags_loader.vue'; export default { name: 'TagsList', components: { + GlEmptyState, TagsListRow, - EmptyState, TagsLoader, RegistryList, + PersistedSearch, }, inject: ['config'], + props: { id: { type: [Number, String], @@ -44,6 +54,7 @@ export default { required: false, }, }, + searchConfig: { NAME_SORT_FIELD }, i18n: { REMOVE_TAGS_BUTTON_TITLE, TAGS_LIST_TITLE, @@ -51,6 +62,9 @@ export default { apollo: { containerRepository: { query: getContainerRepositoryTagsQuery, + skip() { + return !this.sort; + }, variables() { return this.queryVariables; }, @@ -62,6 +76,8 @@ export default { data() { return { containerRepository: {}, + filters: {}, + sort: null, }; }, computed: { @@ -78,6 +94,8 @@ export default { return { id: joinPaths(this.config.gidPrefix, `${this.id}`), first: GRAPHQL_PAGE_SIZE, + name: this.filters?.name, + sort: this.sort, }; }, showMultiDeleteButton() { @@ -87,7 +105,16 @@ export default { return this.tags.length === 0; }, isLoading() { - return this.isImageLoading || this.$apollo.queries.containerRepository.loading; + return this.isImageLoading || this.$apollo.queries.containerRepository.loading || !this.sort; + }, + hasFilters() { + return this.filters?.name; + }, + emptyStateTitle() { + return this.hasFilters ? NO_TAGS_MATCHING_FILTERS_TITLE : NO_TAGS_TITLE; + }, + emptyStateDescription() { + return this.hasFilters ? NO_TAGS_MATCHING_FILTERS_DESCRIPTION : NO_TAGS_MESSAGE; }, }, methods: { @@ -114,15 +141,47 @@ export default { }, }); }, + handleSearchUpdate({ sort, filters }) { + this.sort = sort; + + const parsed = { + name: '', + }; + + // This takes in account the fact that we will be adding more filters types + // this is why is an object and not an array or a simple string + this.filters = filters.reduce((acc, filter) => { + if (filter.type === FILTERED_SEARCH_TERM) { + return { + ...acc, + name: `${acc.name} ${filter.value.data}`.trim(), + }; + } + return acc; + }, parsed); + }, }, }; </script> <template> <div> + <persisted-search + class="gl-mb-5" + :sortable-fields="[$options.searchConfig.NAME_SORT_FIELD]" + :default-order="$options.searchConfig.NAME_SORT_FIELD.orderBy" + default-sort="asc" + @update="handleSearchUpdate" + /> <tags-loader v-if="isLoading" /> <template v-else> - <empty-state v-if="hasNoTags" :no-containers-image="config.noContainersImage" /> + <gl-empty-state + v-if="hasNoTags" + :title="emptyStateTitle" + :svg-path="config.noContainersImage" + :description="emptyStateDescription" + class="gl-mx-auto gl-my-0" + /> <template v-else> <registry-list :title="listTitle" diff --git a/app/assets/javascripts/packages_and_registries/container_registry/explorer/components/details_page/tags_list_row.vue b/app/assets/javascripts/packages_and_registries/container_registry/explorer/components/details_page/tags_list_row.vue index 0556fd298aa..15d92ab0ef7 100644 --- a/app/assets/javascripts/packages_and_registries/container_registry/explorer/components/details_page/tags_list_row.vue +++ b/app/assets/javascripts/packages_and_registries/container_registry/explorer/components/details_page/tags_list_row.vue @@ -107,11 +107,8 @@ export default { isInvalidTag() { return !this.tag.digest; }, - isCheckboxDisabled() { - return this.isInvalidTag || this.disabled; - }, isDeleteDisabled() { - return this.isInvalidTag || this.disabled || !this.tag.canDelete; + return this.disabled || !this.tag.canDelete; }, }, }; @@ -122,7 +119,7 @@ export default { <template #left-action> <gl-form-checkbox v-if="tag.canDelete" - :disabled="isCheckboxDisabled" + :disabled="disabled" class="gl-m-0" :checked="selected" @change="$emit('select')" diff --git a/app/assets/javascripts/packages_and_registries/container_registry/explorer/components/registry_breadcrumb.vue b/app/assets/javascripts/packages_and_registries/container_registry/explorer/components/registry_breadcrumb.vue deleted file mode 100644 index e77eda31596..00000000000 --- a/app/assets/javascripts/packages_and_registries/container_registry/explorer/components/registry_breadcrumb.vue +++ /dev/null @@ -1,51 +0,0 @@ -<script> -// We are using gl-breadcrumb only at the last child of the handwritten breadcrumb -// until this gitlab-ui issue is resolved: https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1079 -// -// See the CSS workaround in app/assets/stylesheets/pages/registry.scss when this file is changed. -import { GlBreadcrumb, GlIcon } from '@gitlab/ui'; - -export default { - components: { - GlBreadcrumb, - GlIcon, - }, - computed: { - rootRoute() { - return this.$router.options.routes.find((r) => r.meta.root); - }, - detailsRoute() { - return this.$router.options.routes.find((r) => r.name === 'details'); - }, - isRootRoute() { - return this.$route.name === this.rootRoute.name; - }, - isLoaded() { - return this.isRootRoute || this.$store?.state.imageDetails?.name; - }, - allCrumbs() { - const crumbs = [ - { - text: this.rootRoute.meta.nameGenerator(), - to: this.rootRoute.path, - }, - ]; - if (!this.isRootRoute) { - crumbs.push({ - text: this.detailsRoute.meta.nameGenerator(), - href: this.detailsRoute.meta.path, - }); - } - return crumbs; - }, - }, -}; -</script> - -<template> - <gl-breadcrumb :key="isLoaded" :items="allCrumbs"> - <template #separator> - <gl-icon name="angle-right" :size="8" /> - </template> - </gl-breadcrumb> -</template> diff --git a/app/assets/javascripts/packages_and_registries/container_registry/explorer/constants/common.js b/app/assets/javascripts/packages_and_registries/container_registry/explorer/constants/common.js index f7beec2c935..17adaec7a7d 100644 --- a/app/assets/javascripts/packages_and_registries/container_registry/explorer/constants/common.js +++ b/app/assets/javascripts/packages_and_registries/container_registry/explorer/constants/common.js @@ -2,3 +2,5 @@ import { s__, __ } from '~/locale'; export const ROOT_IMAGE_TEXT = s__('ContainerRegistry|Root image'); export const MORE_ACTIONS_TEXT = __('More actions'); + +export const NAME_SORT_FIELD = { orderBy: 'NAME', label: __('Name') }; diff --git a/app/assets/javascripts/packages_and_registries/container_registry/explorer/constants/details.js b/app/assets/javascripts/packages_and_registries/container_registry/explorer/constants/details.js index 19e1a75fb2f..8b8769a884d 100644 --- a/app/assets/javascripts/packages_and_registries/container_registry/explorer/constants/details.js +++ b/app/assets/javascripts/packages_and_registries/container_registry/explorer/constants/details.js @@ -116,6 +116,13 @@ export const ROOT_IMAGE_TOOLTIP = s__( 'ContainerRegistry|Image repository with no name located at the project URL.', ); +export const NO_TAGS_MATCHING_FILTERS_TITLE = s__( + 'ContainerRegistry|The filter returned no results', +); +export const NO_TAGS_MATCHING_FILTERS_DESCRIPTION = s__( + 'ContainerRegistry|Please try different search criteria', +); + // Parameters export const DEFAULT_PAGE = 1; diff --git a/app/assets/javascripts/packages_and_registries/container_registry/explorer/constants/list.js b/app/assets/javascripts/packages_and_registries/container_registry/explorer/constants/list.js index d21a154d1b8..7fa950ccfd0 100644 --- a/app/assets/javascripts/packages_and_registries/container_registry/explorer/constants/list.js +++ b/app/assets/javascripts/packages_and_registries/container_registry/explorer/constants/list.js @@ -1,4 +1,5 @@ import { s__, __ } from '~/locale'; +import { NAME_SORT_FIELD } from './common'; // Translations strings @@ -49,5 +50,5 @@ export const GRAPHQL_PAGE_SIZE = 10; export const SORT_FIELDS = [ { orderBy: 'UPDATED', label: __('Updated') }, { orderBy: 'CREATED', label: __('Created') }, - { orderBy: 'NAME', label: __('Name') }, + NAME_SORT_FIELD, ]; diff --git a/app/assets/javascripts/packages_and_registries/container_registry/explorer/graphql/queries/get_container_repository_tags.query.graphql b/app/assets/javascripts/packages_and_registries/container_registry/explorer/graphql/queries/get_container_repository_tags.query.graphql index 502382010f9..d753d33a02c 100644 --- a/app/assets/javascripts/packages_and_registries/container_registry/explorer/graphql/queries/get_container_repository_tags.query.graphql +++ b/app/assets/javascripts/packages_and_registries/container_registry/explorer/graphql/queries/get_container_repository_tags.query.graphql @@ -6,11 +6,13 @@ query getContainerRepositoryTags( $last: Int $after: String $before: String + $name: String + $sort: ContainerRepositoryTagSort ) { containerRepository(id: $id) { id tagsCount - tags(after: $after, before: $before, first: $first, last: $last) { + tags(after: $after, before: $before, first: $first, last: $last, name: $name, sort: $sort) { nodes { digest location diff --git a/app/assets/javascripts/packages_and_registries/container_registry/explorer/index.js b/app/assets/javascripts/packages_and_registries/container_registry/explorer/index.js index 246a6768593..ca5bd8d6964 100644 --- a/app/assets/javascripts/packages_and_registries/container_registry/explorer/index.js +++ b/app/assets/javascripts/packages_and_registries/container_registry/explorer/index.js @@ -3,7 +3,8 @@ import Vue from 'vue'; import { parseBoolean } from '~/lib/utils/common_utils'; import PerformancePlugin from '~/performance/vue_performance_plugin'; import Translate from '~/vue_shared/translate'; -import RegistryBreadcrumb from './components/registry_breadcrumb.vue'; +import RegistryBreadcrumb from '~/packages_and_registries/shared/components/registry_breadcrumb.vue'; +import { renderBreadcrumb } from '~/packages_and_registries/shared/utils'; import { apolloProvider } from './graphql/index'; import RegistryExplorer from './pages/index.vue'; import createRouter from './router'; @@ -84,38 +85,8 @@ export default () => { }, }); - const attachBreadcrumb = () => { - const breadCrumbEls = document.querySelectorAll('nav .js-breadcrumbs-list li'); - const breadCrumbEl = breadCrumbEls[breadCrumbEls.length - 1]; - const crumbs = [breadCrumbEl.querySelector('h2')]; - const nestedBreadcrumbEl = document.createElement('div'); - breadCrumbEl.replaceChild(nestedBreadcrumbEl, breadCrumbEl.querySelector('h2')); - return new Vue({ - el: nestedBreadcrumbEl, - router, - apolloProvider, - components: { - RegistryBreadcrumb, - }, - render(createElement) { - // FIXME(@tnir): this is a workaround until the MR gets merged: - // https://gitlab.com/gitlab-org/gitlab/-/merge_requests/48115 - const parentEl = breadCrumbEl.parentElement.parentElement; - if (parentEl) { - parentEl.classList.remove('breadcrumbs-container'); - parentEl.classList.add('gl-display-flex'); - parentEl.classList.add('w-100'); - } - // End of FIXME(@tnir) - return createElement('registry-breadcrumb', { - class: breadCrumbEl.className, - props: { - crumbs, - }, - }); - }, - }); + return { + attachBreadcrumb: renderBreadcrumb(router, apolloProvider, RegistryBreadcrumb), + attachMainComponent, }; - - return { attachBreadcrumb, attachMainComponent }; }; diff --git a/app/assets/javascripts/packages_and_registries/container_registry/explorer/pages/details.vue b/app/assets/javascripts/packages_and_registries/container_registry/explorer/pages/details.vue index bc6e3091f0e..bb687ffdb89 100644 --- a/app/assets/javascripts/packages_and_registries/container_registry/explorer/pages/details.vue +++ b/app/assets/javascripts/packages_and_registries/container_registry/explorer/pages/details.vue @@ -1,5 +1,5 @@ <script> -import { GlResizeObserverDirective } from '@gitlab/ui'; +import { GlResizeObserverDirective, GlEmptyState } from '@gitlab/ui'; import { GlBreakpointInstance } from '@gitlab/ui/dist/utils'; import createFlash from '~/flash'; import axios from '~/lib/utils/axios_utils'; @@ -9,7 +9,6 @@ import DeleteImage from '../components/delete_image.vue'; import DeleteAlert from '../components/details_page/delete_alert.vue'; import DeleteModal from '../components/details_page/delete_modal.vue'; import DetailsHeader from '../components/details_page/details_header.vue'; -import EmptyState from '../components/details_page/empty_state.vue'; import PartialCleanupAlert from '../components/details_page/partial_cleanup_alert.vue'; import StatusAlert from '../components/details_page/status_alert.vue'; import TagsList from '../components/details_page/tags_list.vue'; @@ -26,6 +25,8 @@ import { MISSING_OR_DELETED_IMAGE_BREADCRUMB, ROOT_IMAGE_TEXT, GRAPHQL_PAGE_SIZE, + MISSING_OR_DELETED_IMAGE_TITLE, + MISSING_OR_DELETED_IMAGE_MESSAGE, } from '../constants/index'; import deleteContainerRepositoryTagsMutation from '../graphql/mutations/delete_container_repository_tags.mutation.graphql'; import getContainerRepositoryDetailsQuery from '../graphql/queries/get_container_repository_details.query.graphql'; @@ -34,13 +35,13 @@ import getContainerRepositoryTagsQuery from '../graphql/queries/get_container_re export default { name: 'RegistryDetailsPage', components: { + GlEmptyState, DeleteAlert, PartialCleanupAlert, DetailsHeader, DeleteModal, TagsList, TagsLoader, - EmptyState, StatusAlert, DeleteImage, }, @@ -49,6 +50,10 @@ export default { }, mixins: [Tracking.mixin()], inject: ['breadCrumbState', 'config'], + i18n: { + MISSING_OR_DELETED_IMAGE_TITLE, + MISSING_OR_DELETED_IMAGE_MESSAGE, + }, apollo: { containerRepository: { query: getContainerRepositoryDetailsQuery, @@ -230,6 +235,12 @@ export default { @cancel="track('cancel_delete')" /> </template> - <empty-state v-else is-empty-image :no-containers-image="config.noContainersImage" /> + <gl-empty-state + v-else + :title="$options.i18n.MISSING_OR_DELETED_IMAGE_TITLE" + :description="$options.i18n.MISSING_OR_DELETED_IMAGE_MESSAGE" + :svg-path="config.noContainersImage" + class="gl-mx-auto gl-my-0" + /> </div> </template> |