diff options
Diffstat (limited to 'app/assets/javascripts/ci/catalog/components')
6 files changed, 170 insertions, 21 deletions
diff --git a/app/assets/javascripts/ci/catalog/components/details/ci_resource_components.vue b/app/assets/javascripts/ci/catalog/components/details/ci_resource_components.vue index 85dfa12c756..fbc7ddf5c91 100644 --- a/app/assets/javascripts/ci/catalog/components/details/ci_resource_components.vue +++ b/app/assets/javascripts/ci/catalog/components/details/ci_resource_components.vue @@ -1,11 +1,13 @@ <script> -import { GlLoadingIcon, GlTableLite } from '@gitlab/ui'; +import { GlButton, GlEmptyState, GlLoadingIcon, GlTableLite } from '@gitlab/ui'; import { createAlert } from '~/alert'; import { __, s__ } from '~/locale'; import getCiCatalogResourceComponents from '../../graphql/queries/get_ci_catalog_resource_components.query.graphql'; export default { components: { + GlButton, + GlEmptyState, GlLoadingIcon, GlTableLite, }, @@ -37,6 +39,9 @@ export default { }, }, computed: { + isMetadataMissing() { + return !this.components || this.components?.length === 0; + }, isLoading() { return this.$apollo.queries.components.loading; }, @@ -70,6 +75,12 @@ export default { }, ], i18n: { + copyText: __('Copy value'), + copyAriaText: __('Copy to clipboard'), + emptyStateTitle: s__('CiCatalogComponent|Component details not available'), + emptyStateDesc: s__( + 'CiCatalogComponent|This tab displays auto-collected information about the components in the repository, but no information was found.', + ), inputTitle: s__('CiCatalogComponent|Inputs'), fetchError: s__("CiCatalogComponent|There was an error fetching this resource's components"), }, @@ -79,6 +90,11 @@ export default { <template> <div> <gl-loading-icon v-if="isLoading" size="lg" /> + <gl-empty-state + v-else-if="isMetadataMissing" + :title="$options.i18n.emptyStateTitle" + :description="$options.i18n.emptyStateDesc" + /> <template v-else> <div v-for="component in components" @@ -88,7 +104,24 @@ export default { > <h3 class="gl-font-size-h2" data-testid="component-name">{{ component.name }}</h3> <p class="gl-mt-5">{{ component.description }}</p> - <pre class="gl-w-85p gl-py-4">{{ generateSnippet(component.path) }}</pre> + <div class="gl-display-flex"> + <pre + class="gl-w-85p gl-py-4 gl-display-flex gl-justify-content-space-between gl-m-0 gl-border-r-none" + ><span>{{ generateSnippet(component.path) }}</span> + </pre> + <div class="gl--flex-center gl-bg-gray-10 gl-border gl-border-l-none"> + <gl-button + class="gl-p-4! gl-mr-3!" + category="tertiary" + icon="copy-to-clipboard" + size="small" + :title="$options.i18n.copyText" + :data-clipboard-text="generateSnippet(component.path)" + data-testid="copy-to-clipboard" + :aria-label="$options.i18n.copyAriaText" + /> + </div> + </div> <div class="gl-mt-5"> <b class="gl-display-block gl-mb-4"> {{ $options.i18n.inputTitle }}</b> <gl-table-lite :items="component.inputs.nodes" :fields="$options.fields"> diff --git a/app/assets/javascripts/ci/catalog/components/details/ci_resource_details.vue b/app/assets/javascripts/ci/catalog/components/details/ci_resource_details.vue index c0feb52c185..026a30988fd 100644 --- a/app/assets/javascripts/ci/catalog/components/details/ci_resource_details.vue +++ b/app/assets/javascripts/ci/catalog/components/details/ci_resource_details.vue @@ -30,12 +30,12 @@ export default { <template> <gl-tabs> - <gl-tab v-if="glFeatures.ciCatalogComponentsTab" :title="$options.i18n.tabs.components" lazy> - <ci-resource-components :resource-id="resourceId" - /></gl-tab> <gl-tab :title="$options.i18n.tabs.readme" lazy> <ci-resource-readme :resource-id="resourceId" /> </gl-tab> + <gl-tab v-if="glFeatures.ciCatalogComponentsTab" :title="$options.i18n.tabs.components" lazy> + <ci-resource-components :resource-id="resourceId" + /></gl-tab> </gl-tabs> </template> <style></style> diff --git a/app/assets/javascripts/ci/catalog/components/details/ci_resource_header.vue b/app/assets/javascripts/ci/catalog/components/details/ci_resource_header.vue index 6673785ffd2..29009c14e1b 100644 --- a/app/assets/javascripts/ci/catalog/components/details/ci_resource_header.vue +++ b/app/assets/javascripts/ci/catalog/components/details/ci_resource_header.vue @@ -2,13 +2,13 @@ import { GlAvatar, GlAvatarLink, GlBadge } from '@gitlab/ui'; import { getIdFromGraphQLId } from '~/graphql_shared/utils'; import { isNumeric } from '~/lib/utils/number_utils'; -import CiBadgeLink from '~/vue_shared/components/ci_badge_link.vue'; +import CiIcon from '~/vue_shared/components/ci_icon.vue'; import CiResourceAbout from './ci_resource_about.vue'; import CiResourceHeaderSkeletonLoader from './ci_resource_header_skeleton_loader.vue'; export default { components: { - CiBadgeLink, + CiIcon, CiResourceAbout, CiResourceHeaderSkeletonLoader, GlAvatar, @@ -102,12 +102,11 @@ export default { {{ versionBadgeText }} </gl-badge> </span> - <ci-badge-link + <ci-icon v-if="hasPipelineStatus" - class="gl-mt-2" :status="pipelineStatus" - size="sm" - show-text + show-status-text + class="gl-mt-2" /> </div> </div> diff --git a/app/assets/javascripts/ci/catalog/components/list/catalog_header.vue b/app/assets/javascripts/ci/catalog/components/list/catalog_header.vue index 487215875c0..db84eaa82c2 100644 --- a/app/assets/javascripts/ci/catalog/components/list/catalog_header.vue +++ b/app/assets/javascripts/ci/catalog/components/list/catalog_header.vue @@ -4,12 +4,22 @@ import { __, s__ } from '~/locale'; import { helpPagePath } from '~/helpers/help_page_helper'; import { CATALOG_FEEDBACK_DISMISSED_KEY } from '../../constants'; +const defaultTitle = __('CI/CD Catalog'); +const defaultDescription = s__( + 'CiCatalog|Discover CI configuration resources for a seamless CI/CD experience.', +); + export default { components: { GlBanner, GlLink, }, - inject: ['pageTitle', 'pageDescription'], + inject: { + pageTitle: { default: defaultTitle }, + pageDescription: { + default: defaultDescription, + }, + }, data() { return { isFeedbackBannerDismissed: localStorage.getItem(CATALOG_FEEDBACK_DISMISSED_KEY) === 'true', @@ -50,7 +60,7 @@ export default { </gl-banner> <h1 class="gl-font-size-h-display">{{ pageTitle }}</h1> <p> - <span>{{ pageDescription }}</span> + <span data-testid="description">{{ pageDescription }}</span> <gl-link :href="$options.learnMorePath" target="_blank">{{ $options.i18n.learnMore }}</gl-link> diff --git a/app/assets/javascripts/ci/catalog/components/list/ci_resources_list_item.vue b/app/assets/javascripts/ci/catalog/components/list/ci_resources_list_item.vue index 63243539575..080955b4322 100644 --- a/app/assets/javascripts/ci/catalog/components/list/ci_resources_list_item.vue +++ b/app/assets/javascripts/ci/catalog/components/list/ci_resources_list_item.vue @@ -48,9 +48,6 @@ export default { starCount() { return this.resource?.starCount || 0; }, - forksCount() { - return this.resource?.forksCount || 0; - }, hasReleasedVersion() { return Boolean(this.latestVersion?.releasedAt); }, @@ -111,14 +108,12 @@ export default { <gl-icon name="star" :size="14" class="gl-mr-1" /> <span class="gl-mr-3">{{ starCount }}</span> </span> - <span class="gl--flex-center" data-testid="stats-forks"> - <gl-icon name="fork" :size="14" class="gl-mr-1" /> - <span>{{ forksCount }}</span> - </span> </span> </div> </div> - <div class="gl-display-flex gl-sm-flex-direction-column gl-justify-content-space-between"> + <div + class="gl-display-flex gl-flex-direction-column gl-md-flex-direction-row gl-justify-content-space-between" + > <span class="gl-display-flex gl-flex-basis-two-thirds gl-font-sm">{{ resource.description }}</span> diff --git a/app/assets/javascripts/ci/catalog/components/pages/ci_resources_page.vue b/app/assets/javascripts/ci/catalog/components/pages/ci_resources_page.vue new file mode 100644 index 00000000000..5e8727a3ed0 --- /dev/null +++ b/app/assets/javascripts/ci/catalog/components/pages/ci_resources_page.vue @@ -0,0 +1,112 @@ +<script> +import { createAlert } from '~/alert'; +import { s__ } from '~/locale'; +import CatalogHeader from '~/ci/catalog/components/list/catalog_header.vue'; +import CatalogListSkeletonLoader from '~/ci/catalog/components/list/catalog_list_skeleton_loader.vue'; +import CiResourcesList from '~/ci/catalog/components/list/ci_resources_list.vue'; +import EmptyState from '~/ci/catalog/components/list/empty_state.vue'; +import { ciCatalogResourcesItemsCount } from '~/ci/catalog/graphql/settings'; +import getCatalogResources from '../../graphql/queries/get_ci_catalog_resources.query.graphql'; + +export default { + components: { + CatalogHeader, + CatalogListSkeletonLoader, + CiResourcesList, + EmptyState, + }, + data() { + return { + catalogResources: [], + currentPage: 1, + totalCount: 0, + pageInfo: {}, + }; + }, + apollo: { + catalogResources: { + query: getCatalogResources, + variables() { + return { + first: ciCatalogResourcesItemsCount, + }; + }, + update(data) { + return data?.ciCatalogResources?.nodes || []; + }, + result({ data }) { + const { pageInfo } = data?.ciCatalogResources || {}; + this.pageInfo = pageInfo; + this.totalCount = data?.ciCatalogResources?.count || 0; + }, + error(e) { + createAlert({ message: e.message || this.$options.i18n.fetchError, variant: 'danger' }); + }, + }, + }, + computed: { + hasResources() { + return this.catalogResources.length > 0; + }, + isLoading() { + return this.$apollo.queries.catalogResources.loading; + }, + }, + methods: { + async handlePrevPage() { + try { + await this.$apollo.queries.catalogResources.fetchMore({ + variables: { + before: this.pageInfo.startCursor, + last: ciCatalogResourcesItemsCount, + first: null, + }, + }); + + this.currentPage -= 1; + } catch (e) { + // Ensure that the current query is properly stoped if an error occurs. + this.$apollo.queries.catalogResources.stop(); + createAlert({ message: e?.message || this.$options.i18n.fetchError, variant: 'danger' }); + } + }, + async handleNextPage() { + try { + await this.$apollo.queries.catalogResources.fetchMore({ + variables: { + after: this.pageInfo.endCursor, + }, + }); + + this.currentPage += 1; + } catch (e) { + // Ensure that the current query is properly stoped if an error occurs. + this.$apollo.queries.catalogResources.stop(); + + createAlert({ message: e?.message || this.$options.i18n.fetchError, variant: 'danger' }); + } + }, + }, + i18n: { + fetchError: s__('CiCatalog|There was an error fetching CI/CD Catalog resources.'), + }, +}; +</script> +<template> + <div> + <catalog-header /> + <catalog-list-skeleton-loader v-if="isLoading" class="gl-w-full gl-mt-3" /> + <empty-state v-else-if="!hasResources" /> + <ci-resources-list + v-else + :current-page="currentPage" + :page-info="pageInfo" + :prev-text="__('Prev')" + :next-text="__('Next')" + :resources="catalogResources" + :total-count="totalCount" + @onPrevPage="handlePrevPage" + @onNextPage="handleNextPage" + /> + </div> +</template> |