diff options
Diffstat (limited to 'app/assets/javascripts/packages')
25 files changed, 247 insertions, 346 deletions
diff --git a/app/assets/javascripts/packages/details/components/additional_metadata.vue b/app/assets/javascripts/packages/details/components/additional_metadata.vue index a3de6dd46c7..76e0976ac05 100644 --- a/app/assets/javascripts/packages/details/components/additional_metadata.vue +++ b/app/assets/javascripts/packages/details/components/additional_metadata.vue @@ -1,7 +1,7 @@ <script> import { GlLink, GlSprintf } from '@gitlab/ui'; import { s__ } from '~/locale'; -import DetailsRow from '~/registry/shared/components/details_row.vue'; +import DetailsRow from '~/vue_shared/components/registry/details_row.vue'; import { generateConanRecipe } from '../utils'; import { PackageType } from '../../shared/constants'; diff --git a/app/assets/javascripts/packages/details/components/app.vue b/app/assets/javascripts/packages/details/components/app.vue index dbb5f7be0a0..af3220840a6 100644 --- a/app/assets/javascripts/packages/details/components/app.vue +++ b/app/assets/javascripts/packages/details/components/app.vue @@ -25,8 +25,9 @@ import { numberToHumanSize } from '~/lib/utils/number_utils'; import timeagoMixin from '~/vue_shared/mixins/timeago'; import FileIcon from '~/vue_shared/components/file_icon.vue'; import { __, s__ } from '~/locale'; -import { PackageType, TrackingActions } from '../../shared/constants'; +import { PackageType, TrackingActions, SHOW_DELETE_SUCCESS_ALERT } from '../../shared/constants'; import { packageTypeToTrackCategory } from '../../shared/utils'; +import { objectToQueryString } from '~/lib/utils/common_utils'; export default { name: 'PackagesApp', @@ -62,17 +63,15 @@ export default { 'packageFiles', 'isLoading', 'canDelete', - 'destroyPath', 'svgPath', 'npmPath', 'npmHelpPath', + 'projectListUrl', + 'groupListUrl', ]), isValidPackage() { return Boolean(this.packageEntity.name); }, - canDeletePackage() { - return this.canDelete && this.destroyPath; - }, filesTableRows() { return this.packageFiles.map(x => ({ name: x.file_name, @@ -100,7 +99,7 @@ export default { }, }, methods: { - ...mapActions(['fetchPackageVersions']), + ...mapActions(['deletePackage', 'fetchPackageVersions']), formatSize(size) { return numberToHumanSize(size); }, @@ -112,6 +111,16 @@ export default { this.fetchPackageVersions(); } }, + async confirmPackageDeletion() { + this.track(TrackingActions.DELETE_PACKAGE); + await this.deletePackage(); + const returnTo = + !this.groupListUrl || document.referrer.includes(this.projectName) + ? this.projectListUrl + : this.groupListUrl; // to avoid security issue url are supplied from backend + const modalQuery = objectToQueryString({ [SHOW_DELETE_SUCCESS_ALERT]: true }); + window.location.replace(`${returnTo}?${modalQuery}`); + }, }, i18n: { deleteModalTitle: s__(`PackageRegistry|Delete Package Version`), @@ -147,12 +156,10 @@ export default { /> <div v-else class="packages-app"> - <div class="detail-page-header d-flex justify-content-between flex-column flex-sm-row"> - <package-title /> - - <div class="mt-sm-2"> + <package-title> + <template #delete-button> <gl-button - v-if="canDeletePackage" + v-if="canDelete" v-gl-modal="'delete-modal'" class="js-delete-button" variant="danger" @@ -161,8 +168,8 @@ export default { > {{ __('Delete') }} </gl-button> - </div> - </div> + </template> + </package-title> <gl-tabs> <gl-tab :title="__('Detail')"> @@ -268,22 +275,22 @@ export default { </template> </gl-sprintf> - <div slot="modal-footer" class="w-100"> - <div class="float-right"> - <gl-button @click="cancelDelete()">{{ __('Cancel') }}</gl-button> - <gl-button - ref="modal-delete-button" - data-method="delete" - :to="destroyPath" - variant="danger" - category="primary" - data-qa-selector="delete_modal_button" - @click="track($options.trackingActions.DELETE_PACKAGE)" - > - {{ __('Delete') }} - </gl-button> + <template #modal-footer> + <div class="gl-w-full"> + <div class="float-right"> + <gl-button @click="cancelDelete">{{ __('Cancel') }}</gl-button> + <gl-button + ref="modal-delete-button" + variant="danger" + category="primary" + data-qa-selector="delete_modal_button" + @click="confirmPackageDeletion" + > + {{ __('Delete') }} + </gl-button> + </div> </div> - </div> + </template> </gl-modal> </div> </template> diff --git a/app/assets/javascripts/packages/details/components/code_instruction.vue b/app/assets/javascripts/packages/details/components/code_instruction.vue deleted file mode 100644 index 0719ddfcd2b..00000000000 --- a/app/assets/javascripts/packages/details/components/code_instruction.vue +++ /dev/null @@ -1,63 +0,0 @@ -<script> -import ClipboardButton from '~/vue_shared/components/clipboard_button.vue'; -import Tracking from '~/tracking'; -import { TrackingLabels } from '../constants'; - -export default { - name: 'CodeInstruction', - components: { - ClipboardButton, - }, - mixins: [ - Tracking.mixin({ - label: TrackingLabels.CODE_INSTRUCTION, - }), - ], - props: { - instruction: { - type: String, - required: true, - }, - copyText: { - type: String, - required: true, - }, - multiline: { - type: Boolean, - required: false, - default: false, - }, - trackingAction: { - type: String, - required: false, - default: '', - }, - }, - methods: { - trackCopy() { - if (this.trackingAction) { - this.track(this.trackingAction); - } - }, - }, -}; -</script> - -<template> - <div v-if="!multiline" class="input-group gl-mb-3"> - <input - :value="instruction" - type="text" - class="form-control monospace js-instruction-input" - readonly - @copy="trackCopy" - /> - <span class="input-group-append js-instruction-button" @click="trackCopy"> - <clipboard-button :text="instruction" :title="copyText" class="input-group-text" /> - </span> - </div> - - <div v-else> - <pre class="js-instruction-pre" @copy="trackCopy">{{ instruction }}</pre> - </div> -</template> diff --git a/app/assets/javascripts/packages/details/components/composer_installation.vue b/app/assets/javascripts/packages/details/components/composer_installation.vue index 1934da149ce..60ad468c293 100644 --- a/app/assets/javascripts/packages/details/components/composer_installation.vue +++ b/app/assets/javascripts/packages/details/components/composer_installation.vue @@ -2,8 +2,8 @@ import { GlLink, GlSprintf } from '@gitlab/ui'; import { mapGetters, mapState } from 'vuex'; import { s__ } from '~/locale'; -import CodeInstruction from './code_instruction.vue'; -import { TrackingActions } from '../constants'; +import CodeInstruction from '~/vue_shared/components/registry/code_instruction.vue'; +import { TrackingActions, TrackingLabels } from '../constants'; export default { name: 'ComposerInstallation', @@ -26,28 +26,30 @@ export default { ), }, trackingActions: { ...TrackingActions }, + TrackingLabels, }; </script> <template> <div> <h3 class="gl-font-lg">{{ __('Installation') }}</h3> - <h4 class="gl-font-base" data-testid="registry-include-title"> - {{ $options.i18n.registryInclude }} - </h4> <code-instruction + :label="$options.i18n.registryInclude" :instruction="composerRegistryInclude" :copy-text="$options.i18n.copyRegistryInclude" :tracking-action="$options.trackingActions.COPY_COMPOSER_REGISTRY_INCLUDE_COMMAND" + :tracking-label="$options.TrackingLabels.CODE_INSTRUCTION" + data-testid="registry-include" /> - <h4 class="gl-font-base" data-testid="package-include-title"> - {{ $options.i18n.packageInclude }} - </h4> + <code-instruction + :label="$options.i18n.packageInclude" :instruction="composerPackageInclude" :copy-text="$options.i18n.copyPackageInclude" :tracking-action="$options.trackingActions.COPY_COMPOSER_PACKAGE_INCLUDE_COMMAND" + :tracking-label="$options.TrackingLabels.CODE_INSTRUCTION" + data-testid="package-include" /> <span data-testid="help-text"> <gl-sprintf :message="$options.i18n.infoLine"> diff --git a/app/assets/javascripts/packages/details/components/conan_installation.vue b/app/assets/javascripts/packages/details/components/conan_installation.vue index cff7d73f1e8..a5df87c9c5b 100644 --- a/app/assets/javascripts/packages/details/components/conan_installation.vue +++ b/app/assets/javascripts/packages/details/components/conan_installation.vue @@ -2,8 +2,8 @@ import { GlLink, GlSprintf } from '@gitlab/ui'; import { mapGetters, mapState } from 'vuex'; import { s__ } from '~/locale'; -import CodeInstruction from './code_instruction.vue'; -import { TrackingActions } from '../constants'; +import CodeInstruction from '~/vue_shared/components/registry/code_instruction.vue'; +import { TrackingActions, TrackingLabels } from '../constants'; export default { name: 'ConanInstallation', @@ -22,30 +22,30 @@ export default { ), }, trackingActions: { ...TrackingActions }, + TrackingLabels, }; </script> <template> <div> <h3 class="gl-font-lg">{{ __('Installation') }}</h3> - <h4 class="gl-font-base"> - {{ s__('PackageRegistry|Conan Command') }} - </h4> <code-instruction + :label="s__('PackageRegistry|Conan Command')" :instruction="conanInstallationCommand" :copy-text="s__('PackageRegistry|Copy Conan Command')" :tracking-action="$options.trackingActions.COPY_CONAN_COMMAND" + :tracking-label="$options.TrackingLabels.CODE_INSTRUCTION" /> <h3 class="gl-font-lg">{{ __('Registry setup') }}</h3> - <h4 class="gl-font-base"> - {{ s__('PackageRegistry|Add Conan Remote') }} - </h4> + <code-instruction + :label="s__('PackageRegistry|Add Conan Remote')" :instruction="conanSetupCommand" :copy-text="s__('PackageRegistry|Copy Conan Setup Command')" :tracking-action="$options.trackingActions.COPY_CONAN_SETUP_COMMAND" + :tracking-label="$options.TrackingLabels.CODE_INSTRUCTION" /> <gl-sprintf :message="$options.i18n.helpText"> <template #link="{ content }"> diff --git a/app/assets/javascripts/packages/details/components/dependency_row.vue b/app/assets/javascripts/packages/details/components/dependency_row.vue index 788673d2881..1a2202b23c8 100644 --- a/app/assets/javascripts/packages/details/components/dependency_row.vue +++ b/app/assets/javascripts/packages/details/components/dependency_row.vue @@ -26,7 +26,7 @@ export default { <div v-if="showVersion" - class="table-section section-50 gl-display-flex justify-content-md-end" + class="table-section section-50 gl-display-flex gl-md-justify-content-end" data-testid="version-pattern" > <span class="gl-text-body">{{ dependency.version_pattern }}</span> diff --git a/app/assets/javascripts/packages/details/components/history_element.vue b/app/assets/javascripts/packages/details/components/history_element.vue deleted file mode 100644 index 8a51c1528cf..00000000000 --- a/app/assets/javascripts/packages/details/components/history_element.vue +++ /dev/null @@ -1,35 +0,0 @@ -<script> -import { GlIcon } from '@gitlab/ui'; -import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue'; - -export default { - name: 'HistoryElement', - components: { - GlIcon, - TimelineEntryItem, - }, - - props: { - icon: { - type: String, - required: true, - }, - }, -}; -</script> - -<template> - <timeline-entry-item class="system-note note-wrapper gl-mb-6!"> - <div class="timeline-icon"> - <gl-icon :name="icon" /> - </div> - <div class="timeline-content"> - <div class="note-header"> - <span> - <slot></slot> - </span> - </div> - <div class="note-body"></div> - </div> - </timeline-entry-item> -</template> diff --git a/app/assets/javascripts/packages/details/components/maven_installation.vue b/app/assets/javascripts/packages/details/components/maven_installation.vue index d6641c886a0..c2f6f76967b 100644 --- a/app/assets/javascripts/packages/details/components/maven_installation.vue +++ b/app/assets/javascripts/packages/details/components/maven_installation.vue @@ -2,8 +2,8 @@ import { GlLink, GlSprintf } from '@gitlab/ui'; import { mapGetters, mapState } from 'vuex'; import { s__ } from '~/locale'; -import CodeInstruction from './code_instruction.vue'; -import { TrackingActions } from '../constants'; +import CodeInstruction from '~/vue_shared/components/registry/code_instruction.vue'; +import { TrackingActions, TrackingLabels } from '../constants'; export default { name: 'MavenInstallation', @@ -28,6 +28,7 @@ export default { ), }, trackingActions: { ...TrackingActions }, + TrackingLabels, }; </script> @@ -35,9 +36,6 @@ export default { <div> <h3 class="gl-font-lg">{{ __('Installation') }}</h3> - <h4 class="gl-font-base"> - {{ s__('PackageRegistry|Maven XML') }} - </h4> <p> <gl-sprintf :message="$options.i18n.xmlText"> <template #code="{ content }"> @@ -45,20 +43,22 @@ export default { </template> </gl-sprintf> </p> + <code-instruction + :label="s__('PackageRegistry|Maven XML')" :instruction="mavenInstallationXml" :copy-text="s__('PackageRegistry|Copy Maven XML')" multiline :tracking-action="$options.trackingActions.COPY_MAVEN_XML" + :tracking-label="$options.TrackingLabels.CODE_INSTRUCTION" /> - <h4 class="gl-font-base"> - {{ s__('PackageRegistry|Maven Command') }} - </h4> <code-instruction + :label="s__('PackageRegistry|Maven Command')" :instruction="mavenInstallationCommand" :copy-text="s__('PackageRegistry|Copy Maven command')" :tracking-action="$options.trackingActions.COPY_MAVEN_COMMAND" + :tracking-label="$options.TrackingLabels.CODE_INSTRUCTION" /> <h3 class="gl-font-lg">{{ __('Registry setup') }}</h3> @@ -74,6 +74,7 @@ export default { :copy-text="s__('PackageRegistry|Copy Maven registry XML')" multiline :tracking-action="$options.trackingActions.COPY_MAVEN_SETUP" + :tracking-label="$options.TrackingLabels.CODE_INSTRUCTION" /> <gl-sprintf :message="$options.i18n.helpText"> <template #link="{ content }"> diff --git a/app/assets/javascripts/packages/details/components/npm_installation.vue b/app/assets/javascripts/packages/details/components/npm_installation.vue index d7ff8428370..37ba279d098 100644 --- a/app/assets/javascripts/packages/details/components/npm_installation.vue +++ b/app/assets/javascripts/packages/details/components/npm_installation.vue @@ -2,8 +2,8 @@ import { GlLink, GlSprintf } from '@gitlab/ui'; import { mapGetters, mapState } from 'vuex'; import { s__ } from '~/locale'; -import CodeInstruction from './code_instruction.vue'; -import { NpmManager, TrackingActions } from '../constants'; +import CodeInstruction from '~/vue_shared/components/registry/code_instruction.vue'; +import { NpmManager, TrackingActions, TrackingLabels } from '../constants'; export default { name: 'NpmInstallation', @@ -34,41 +34,46 @@ export default { ), }, trackingActions: { ...TrackingActions }, + TrackingLabels, }; </script> <template> <div> <h3 class="gl-font-lg">{{ __('Installation') }}</h3> - <h4 class="gl-font-base">{{ s__('PackageRegistry|npm command') }}</h4> <code-instruction + :label="s__('PackageRegistry|npm command')" :instruction="npmCommand" :copy-text="s__('PackageRegistry|Copy npm command')" :tracking-action="$options.trackingActions.COPY_NPM_INSTALL_COMMAND" + :tracking-label="$options.TrackingLabels.CODE_INSTRUCTION" /> - <h4 class="gl-font-base">{{ s__('PackageRegistry|yarn command') }}</h4> <code-instruction + :label="s__('PackageRegistry|yarn command')" :instruction="yarnCommand" :copy-text="s__('PackageRegistry|Copy yarn command')" :tracking-action="$options.trackingActions.COPY_YARN_INSTALL_COMMAND" + :tracking-label="$options.TrackingLabels.CODE_INSTRUCTION" /> <h3 class="gl-font-lg">{{ __('Registry setup') }}</h3> - <h4 class="gl-font-base">{{ s__('PackageRegistry|npm command') }}</h4> <code-instruction + :label="s__('PackageRegistry|npm command')" :instruction="npmSetup" :copy-text="s__('PackageRegistry|Copy npm setup command')" :tracking-action="$options.trackingActions.COPY_NPM_SETUP_COMMAND" + :tracking-label="$options.TrackingLabels.CODE_INSTRUCTION" /> - <h4 class="gl-font-base">{{ s__('PackageRegistry|yarn command') }}</h4> <code-instruction + :label="s__('PackageRegistry|yarn command')" :instruction="yarnSetupCommand" :copy-text="s__('PackageRegistry|Copy yarn setup command')" :tracking-action="$options.trackingActions.COPY_YARN_SETUP_COMMAND" + :tracking-label="$options.TrackingLabels.CODE_INSTRUCTION" /> <gl-sprintf :message="$options.i18n.helpText"> diff --git a/app/assets/javascripts/packages/details/components/nuget_installation.vue b/app/assets/javascripts/packages/details/components/nuget_installation.vue index 150b6e3ab0f..36887703716 100644 --- a/app/assets/javascripts/packages/details/components/nuget_installation.vue +++ b/app/assets/javascripts/packages/details/components/nuget_installation.vue @@ -2,8 +2,8 @@ import { GlLink, GlSprintf } from '@gitlab/ui'; import { mapGetters, mapState } from 'vuex'; import { s__ } from '~/locale'; -import CodeInstruction from './code_instruction.vue'; -import { TrackingActions } from '../constants'; +import CodeInstruction from '~/vue_shared/components/registry/code_instruction.vue'; +import { TrackingActions, TrackingLabels } from '../constants'; export default { name: 'NugetInstallation', @@ -22,29 +22,28 @@ export default { ), }, trackingActions: { ...TrackingActions }, + TrackingLabels, }; </script> <template> <div> <h3 class="gl-font-lg">{{ __('Installation') }}</h3> - <h4 class="gl-font-base"> - {{ s__('PackageRegistry|NuGet Command') }} - </h4> <code-instruction + :label="s__('PackageRegistry|NuGet Command')" :instruction="nugetInstallationCommand" :copy-text="s__('PackageRegistry|Copy NuGet Command')" :tracking-action="$options.trackingActions.COPY_NUGET_INSTALL_COMMAND" + :tracking-label="$options.TrackingLabels.CODE_INSTRUCTION" /> <h3 class="gl-font-lg">{{ __('Registry setup') }}</h3> - <h4 class="gl-font-base"> - {{ s__('PackageRegistry|Add NuGet Source') }} - </h4> <code-instruction + :label="s__('PackageRegistry|Add NuGet Source')" :instruction="nugetSetupCommand" :copy-text="s__('PackageRegistry|Copy NuGet Setup Command')" :tracking-action="$options.trackingActions.COPY_NUGET_SETUP_COMMAND" + :tracking-label="$options.TrackingLabels.CODE_INSTRUCTION" /> <gl-sprintf :message="$options.i18n.helpText"> <template #link="{ content }"> diff --git a/app/assets/javascripts/packages/details/components/package_history.vue b/app/assets/javascripts/packages/details/components/package_history.vue index 96ce106884d..413ab1d15cb 100644 --- a/app/assets/javascripts/packages/details/components/package_history.vue +++ b/app/assets/javascripts/packages/details/components/package_history.vue @@ -2,7 +2,7 @@ import { GlLink, GlSprintf } from '@gitlab/ui'; import { s__ } from '~/locale'; import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; -import HistoryElement from './history_element.vue'; +import HistoryItem from '~/vue_shared/components/registry/history_item.vue'; export default { name: 'PackageHistory', @@ -16,7 +16,7 @@ export default { components: { GlLink, GlSprintf, - HistoryElement, + HistoryItem, TimeAgoTooltip, }, props: { @@ -46,7 +46,7 @@ export default { <div class="issuable-discussion"> <h3 class="gl-font-lg" data-testid="title">{{ __('History') }}</h3> <ul class="timeline main-notes-list notes gl-mb-4" data-testid="timeline"> - <history-element icon="clock" data-testid="created-on"> + <history-item icon="clock" data-testid="created-on"> <gl-sprintf :message="$options.i18n.createdOn"> <template #name> <strong>{{ packageEntity.name }}</strong> @@ -58,8 +58,8 @@ export default { <time-ago-tooltip :time="packageEntity.created_at" /> </template> </gl-sprintf> - </history-element> - <history-element icon="pencil" data-testid="updated-at"> + </history-item> + <history-item icon="pencil" data-testid="updated-at"> <gl-sprintf :message="$options.i18n.updatedAtText"> <template #name> <strong>{{ packageEntity.name }}</strong> @@ -71,9 +71,9 @@ export default { <time-ago-tooltip :time="packageEntity.updated_at" /> </template> </gl-sprintf> - </history-element> + </history-item> <template v-if="packagePipeline"> - <history-element icon="commit" data-testid="commit"> + <history-item icon="commit" data-testid="commit"> <gl-sprintf :message="$options.i18n.commitText"> <template #link> <gl-link :href="packagePipeline.project.commit_url">{{ @@ -84,8 +84,8 @@ export default { <strong>{{ packagePipeline.ref }}</strong> </template> </gl-sprintf> - </history-element> - <history-element icon="pipeline" data-testid="pipeline"> + </history-item> + <history-item icon="pipeline" data-testid="pipeline"> <gl-sprintf :message="$options.i18n.pipelineText"> <template #link> <gl-link :href="packagePipeline.project.pipeline_url" @@ -97,9 +97,9 @@ export default { </template> <template #author>{{ packagePipeline.user.name }}</template> </gl-sprintf> - </history-element> + </history-item> </template> - <history-element icon="package" data-testid="published"> + <history-item icon="package" data-testid="published"> <gl-sprintf :message="$options.i18n.publishText"> <template #project> <strong>{{ projectName }}</strong> @@ -108,7 +108,7 @@ export default { <time-ago-tooltip :time="packageEntity.created_at" /> </template> </gl-sprintf> - </history-element> + </history-item> </ul> </div> </template> diff --git a/app/assets/javascripts/packages/details/components/package_title.vue b/app/assets/javascripts/packages/details/components/package_title.vue index d07883e3e7a..69dd494f11a 100644 --- a/app/assets/javascripts/packages/details/components/package_title.vue +++ b/app/assets/javascripts/packages/details/components/package_title.vue @@ -1,19 +1,21 @@ <script> import { mapState, mapGetters } from 'vuex'; -import { GlAvatar, GlIcon, GlLink, GlSprintf, GlTooltipDirective } from '@gitlab/ui'; +import { GlIcon, GlSprintf, GlTooltipDirective } from '@gitlab/ui'; import PackageTags from '../../shared/components/package_tags.vue'; import { numberToHumanSize } from '~/lib/utils/number_utils'; import timeagoMixin from '~/vue_shared/mixins/timeago'; +import TitleArea from '~/vue_shared/components/registry/title_area.vue'; +import MetadataItem from '~/vue_shared/components/registry/metadata_item.vue'; import { __ } from '~/locale'; export default { name: 'PackageTitle', components: { - GlAvatar, + TitleArea, GlIcon, - GlLink, GlSprintf, PackageTags, + MetadataItem, }, directives: { GlTooltip: GlTooltipDirective, @@ -36,77 +38,49 @@ export default { </script> <template> - <div class="gl-flex-direction-column"> - <div class="gl-display-flex"> - <gl-avatar - v-if="packageIcon" - :src="packageIcon" - shape="rect" - class="gl-align-self-center gl-mr-4" - data-testid="package-icon" - /> - - <div class="gl-display-flex gl-flex-direction-column"> - <h1 class="gl-font-size-h1 gl-mt-3 gl-mb-2"> - {{ packageEntity.name }} - </h1> + <title-area :title="packageEntity.name" :avatar="packageIcon" data-qa-selector="package_title"> + <template #sub-header> + <gl-icon name="eye" class="gl-mr-3" /> + <gl-sprintf :message="$options.i18n.packageInfo"> + <template #version> + {{ packageEntity.version }} + </template> - <div class="gl-display-flex gl-align-items-center gl-text-gray-500"> - <gl-icon name="eye" class="gl-mr-3" /> - <gl-sprintf :message="$options.i18n.packageInfo"> - <template #version> - {{ packageEntity.version }} - </template> + <template #timeAgo> + <span v-gl-tooltip :title="tooltipTitle(packageEntity.created_at)"> + {{ timeFormatted(packageEntity.created_at) }} + </span> + </template> + </gl-sprintf> + </template> - <template #timeAgo> - <span v-gl-tooltip :title="tooltipTitle(packageEntity.created_at)"> - {{ timeFormatted(packageEntity.created_at) }} - </span> - </template> - </gl-sprintf> - </div> - </div> - </div> + <template v-if="packageTypeDisplay" #metadata_type> + <metadata-item data-testid="package-type" icon="package" :text="packageTypeDisplay" /> + </template> - <div class="gl-display-flex gl-flex-wrap gl-align-items-center gl-mb-3"> - <div v-if="packageTypeDisplay" class="gl-display-flex gl-align-items-center gl-mr-5"> - <gl-icon name="package" class="gl-text-gray-500 gl-mr-3" /> - <span data-testid="package-type" class="gl-font-weight-bold">{{ packageTypeDisplay }}</span> - </div> + <template #metadata_size> + <metadata-item data-testid="package-size" icon="disk" :text="totalSize" /> + </template> - <div v-if="hasTagsToDisplay" class="gl-display-flex gl-align-items-center gl-mr-5"> - <package-tags :tag-display-limit="1" :tags="packageEntity.tags" /> - </div> + <template v-if="packagePipeline" #metadata_pipeline> + <metadata-item + data-testid="pipeline-project" + icon="review-list" + :text="packagePipeline.project.name" + :link="packagePipeline.project.web_url" + /> + </template> - <div v-if="packagePipeline" class="gl-display-flex gl-align-items-center gl-mr-5"> - <gl-icon name="review-list" class="gl-text-gray-500 gl-mr-3" /> - <gl-link - data-testid="pipeline-project" - :href="packagePipeline.project.web_url" - class="gl-font-weight-bold text-truncate" - > - {{ packagePipeline.project.name }} - </gl-link> - </div> + <template v-if="packagePipeline" #metadata_ref> + <metadata-item data-testid="package-ref" icon="branch" :text="packagePipeline.ref" /> + </template> - <div - v-if="packagePipeline" - data-testid="package-ref" - class="gl-display-flex gl-align-items-center gl-mr-5" - > - <gl-icon name="branch" class="gl-text-gray-500 gl-mr-3" /> - <span - v-gl-tooltip - class="gl-font-weight-bold text-truncate mw-xs" - :title="packagePipeline.ref" - >{{ packagePipeline.ref }}</span - > - </div> + <template v-if="hasTagsToDisplay" #metadata_tags> + <package-tags :tag-display-limit="2" :tags="packageEntity.tags" hide-label /> + </template> - <div class="gl-display-flex gl-align-items-center gl-mr-5"> - <gl-icon name="disk" class="gl-text-gray-500 gl-mr-3" /> - <span data-testid="package-size" class="gl-font-weight-bold">{{ totalSize }}</span> - </div> - </div> - </div> + <template #right-actions> + <slot name="delete-button"></slot> + </template> + </title-area> </template> diff --git a/app/assets/javascripts/packages/details/components/pypi_installation.vue b/app/assets/javascripts/packages/details/components/pypi_installation.vue index f1c619fd6d3..f87be68be48 100644 --- a/app/assets/javascripts/packages/details/components/pypi_installation.vue +++ b/app/assets/javascripts/packages/details/components/pypi_installation.vue @@ -2,8 +2,8 @@ import { GlLink, GlSprintf } from '@gitlab/ui'; import { mapGetters, mapState } from 'vuex'; import { s__ } from '~/locale'; -import CodeInstruction from './code_instruction.vue'; -import { TrackingActions } from '../constants'; +import CodeInstruction from '~/vue_shared/components/registry/code_instruction.vue'; +import { TrackingActions, TrackingLabels } from '../constants'; export default { name: 'PyPiInstallation', @@ -25,6 +25,7 @@ export default { ), }, trackingActions: { ...TrackingActions }, + TrackingLabels, }; </script> @@ -32,15 +33,13 @@ export default { <div> <h3 class="gl-font-lg">{{ __('Installation') }}</h3> - <h4 class="gl-font-base"> - {{ s__('PackageRegistry|Pip Command') }} - </h4> - <code-instruction + :label="s__('PackageRegistry|Pip Command')" :instruction="pypiPipCommand" :copy-text="s__('PackageRegistry|Copy Pip command')" data-testid="pip-command" :tracking-action="$options.trackingActions.COPY_PIP_INSTALL_COMMAND" + :tracking-label="$options.TrackingLabels.CODE_INSTRUCTION" /> <h3 class="gl-font-lg">{{ __('Registry setup') }}</h3> @@ -58,6 +57,7 @@ export default { data-testid="pypi-setup-content" multiline :tracking-action="$options.trackingActions.COPY_PYPI_SETUP_COMMAND" + :tracking-label="$options.TrackingLabels.CODE_INSTRUCTION" /> <gl-sprintf :message="$options.i18n.helpText"> <template #link="{ content }"> diff --git a/app/assets/javascripts/packages/details/store/actions.js b/app/assets/javascripts/packages/details/store/actions.js index cda80056e19..340f60258a0 100644 --- a/app/assets/javascripts/packages/details/store/actions.js +++ b/app/assets/javascripts/packages/details/store/actions.js @@ -1,9 +1,10 @@ import Api from '~/api'; import { deprecatedCreateFlash as createFlash } from '~/flash'; import { FETCH_PACKAGE_VERSIONS_ERROR } from '../constants'; +import { DELETE_PACKAGE_ERROR_MESSAGE } from '~/packages/shared/constants'; import * as types from './mutation_types'; -export default ({ commit, state }) => { +export const fetchPackageVersions = ({ commit, state }) => { commit(types.SET_LOADING, true); const { project_id, id } = state.packageEntity; @@ -21,3 +22,13 @@ export default ({ commit, state }) => { commit(types.SET_LOADING, false); }); }; + +export const deletePackage = ({ + state: { + packageEntity: { project_id, id }, + }, +}) => { + return Api.deleteProjectPackage(project_id, id).catch(() => { + createFlash(DELETE_PACKAGE_ERROR_MESSAGE); + }); +}; diff --git a/app/assets/javascripts/packages/details/store/getters.js b/app/assets/javascripts/packages/details/store/getters.js index d1814d506ad..ede6d39bde7 100644 --- a/app/assets/javascripts/packages/details/store/getters.js +++ b/app/assets/javascripts/packages/details/store/getters.js @@ -84,10 +84,10 @@ export const npmSetupCommand = ({ packageEntity, npmPath }) => (type = NpmManage const scope = packageEntity.name.substring(0, packageEntity.name.indexOf('/')); if (type === NpmManager.NPM) { - return `echo ${scope}:registry=${npmPath} >> .npmrc`; + return `echo ${scope}:registry=${npmPath}/ >> .npmrc`; } - return `echo \\"${scope}:registry\\" \\"${npmPath}\\" >> .yarnrc`; + return `echo \\"${scope}:registry\\" \\"${npmPath}/\\" >> .yarnrc`; }; export const nugetInstallationCommand = ({ packageEntity }) => diff --git a/app/assets/javascripts/packages/details/store/index.js b/app/assets/javascripts/packages/details/store/index.js index 9687eb98544..15e17bcfaac 100644 --- a/app/assets/javascripts/packages/details/store/index.js +++ b/app/assets/javascripts/packages/details/store/index.js @@ -1,6 +1,6 @@ import Vue from 'vue'; import Vuex from 'vuex'; -import fetchPackageVersions from './actions'; +import * as actions from './actions'; import * as getters from './getters'; import mutations from './mutations'; @@ -8,9 +8,7 @@ Vue.use(Vuex); export default (initialState = {}) => new Vuex.Store({ - actions: { - fetchPackageVersions, - }, + actions, getters, mutations, state: { diff --git a/app/assets/javascripts/packages/list/components/packages_list.vue b/app/assets/javascripts/packages/list/components/packages_list.vue index b26c6087e14..7067f70a923 100644 --- a/app/assets/javascripts/packages/list/components/packages_list.vue +++ b/app/assets/javascripts/packages/list/components/packages_list.vue @@ -82,11 +82,11 @@ export default { </script> <template> - <div class="d-flex flex-column"> + <div class="gl-display-flex gl-flex-direction-column"> <slot v-if="isListEmpty && !isLoading" name="empty-state"></slot> <div v-else-if="isLoading"> - <packages-list-loader :is-group="isGroupPage" /> + <packages-list-loader /> </div> <template v-else> @@ -106,7 +106,7 @@ export default { :per-page="perPage" :total-items="totalItems" align="center" - class="w-100 mt-2" + class="gl-w-full gl-mt-3" /> <gl-modal diff --git a/app/assets/javascripts/packages/list/components/packages_list_app.vue b/app/assets/javascripts/packages/list/components/packages_list_app.vue index ef242ea5f75..6304f723f6a 100644 --- a/app/assets/javascripts/packages/list/components/packages_list_app.vue +++ b/app/assets/javascripts/packages/list/components/packages_list_app.vue @@ -2,11 +2,14 @@ import { mapActions, mapState } from 'vuex'; import { GlEmptyState, GlTab, GlTabs, GlLink, GlSprintf } from '@gitlab/ui'; import { s__, sprintf } from '~/locale'; +import createFlash from '~/flash'; import PackageFilter from './packages_filter.vue'; import PackageList from './packages_list.vue'; import PackageSort from './packages_sort.vue'; -import { PACKAGE_REGISTRY_TABS } from '../constants'; +import { PACKAGE_REGISTRY_TABS, DELETE_PACKAGE_SUCCESS_MESSAGE } from '../constants'; import PackagesComingSoon from '../coming_soon/packages_coming_soon.vue'; +import { SHOW_DELETE_SUCCESS_ALERT } from '~/packages/shared/constants'; +import { historyReplaceState } from '~/lib/utils/common_utils'; export default { components: { @@ -34,6 +37,7 @@ export default { }, mounted() { this.requestPackagesList(); + this.checkDeleteAlert(); }, methods: { ...mapActions(['requestPackagesList', 'requestDeletePackage', 'setSelectedType']), @@ -64,6 +68,16 @@ export default { return s__('PackageRegistry|There are no packages yet'); }, + checkDeleteAlert() { + const urlParams = new URLSearchParams(window.location.search); + const showAlert = urlParams.get(SHOW_DELETE_SUCCESS_ALERT); + if (showAlert) { + // to be refactored to use gl-alert + createFlash({ message: DELETE_PACKAGE_SUCCESS_MESSAGE, type: 'notice' }); + const cleanUrl = window.location.href.split('?')[0]; + historyReplaceState(cleanUrl); + } + }, }, i18n: { widenFilters: s__('PackageRegistry|To widen your search, change or remove the filters above.'), @@ -77,7 +91,9 @@ export default { <template> <gl-tabs @input="tabChanged"> <template #tabs-end> - <div class="d-flex align-self-center ml-md-auto py-1 py-md-0"> + <div + class="gl-display-flex gl-align-self-center gl-py-2 gl-flex-grow-1 gl-justify-content-end" + > <package-filter class="mr-1" @filter="requestPackagesList" /> <package-sort @sort:changed="requestPackagesList" /> </div> diff --git a/app/assets/javascripts/packages/list/constants.js b/app/assets/javascripts/packages/list/constants.js index 510d04965cb..0ff8c86362d 100644 --- a/app/assets/javascripts/packages/list/constants.js +++ b/app/assets/javascripts/packages/list/constants.js @@ -5,7 +5,6 @@ export const FETCH_PACKAGES_LIST_ERROR_MESSAGE = __( 'Something went wrong while fetching the packages list.', ); export const FETCH_PACKAGE_ERROR_MESSAGE = __('Something went wrong while fetching the package.'); -export const DELETE_PACKAGE_ERROR_MESSAGE = __('Something went wrong while deleting the package.'); export const DELETE_PACKAGE_SUCCESS_MESSAGE = __('Package deleted successfully'); export const DEFAULT_PAGE = 1; diff --git a/app/assets/javascripts/packages/list/stores/actions.js b/app/assets/javascripts/packages/list/stores/actions.js index 0ed24aee2c5..bbc11e3cf13 100644 --- a/app/assets/javascripts/packages/list/stores/actions.js +++ b/app/assets/javascripts/packages/list/stores/actions.js @@ -1,10 +1,10 @@ import Api from '~/api'; import axios from '~/lib/utils/axios_utils'; import { deprecatedCreateFlash as createFlash } from '~/flash'; +import { DELETE_PACKAGE_ERROR_MESSAGE } from '~/packages/shared/constants'; import * as types from './mutation_types'; import { FETCH_PACKAGES_LIST_ERROR_MESSAGE, - DELETE_PACKAGE_ERROR_MESSAGE, DELETE_PACKAGE_SUCCESS_MESSAGE, DEFAULT_PAGE, DEFAULT_PAGE_SIZE, diff --git a/app/assets/javascripts/packages/shared/components/package_list_row.vue b/app/assets/javascripts/packages/shared/components/package_list_row.vue index e000279b794..f93bc51d185 100644 --- a/app/assets/javascripts/packages/shared/components/package_list_row.vue +++ b/app/assets/javascripts/packages/shared/components/package_list_row.vue @@ -1,9 +1,10 @@ <script> -import { GlButton, GlIcon, GlLink, GlSprintf, GlTooltipDirective } from '@gitlab/ui'; +import { GlButton, GlIcon, GlLink, GlSprintf, GlTooltipDirective, GlTruncate } from '@gitlab/ui'; import PackageTags from './package_tags.vue'; import PublishMethod from './publish_method.vue'; import { getPackageTypeLabel } from '../utils'; import timeagoMixin from '~/vue_shared/mixins/timeago'; +import ListItem from '~/vue_shared/components/registry/list_item.vue'; export default { name: 'PackageListRow', @@ -12,8 +13,10 @@ export default { GlIcon, GlLink, GlSprintf, + GlTruncate, PackageTags, PublishMethod, + ListItem, }, directives: { GlTooltip: GlTooltipDirective, @@ -59,15 +62,15 @@ export default { </script> <template> - <div class="gl-responsive-table-row" data-qa-selector="packages-row"> - <div class="table-section section-50 d-flex flex-md-column justify-content-between flex-wrap"> - <div class="d-flex align-items-center mr-2"> + <list-item data-qa-selector="package_row"> + <template #left-primary> + <div class="gl-display-flex gl-align-items-center gl-mr-3 gl-min-w-0"> <gl-link :href="packageLink" + class="gl-text-body gl-min-w-0" data-qa-selector="package_link" - class="text-dark font-weight-bold mb-md-1" > - {{ packageEntity.name }} + <gl-truncate :text="packageEntity.name" /> </gl-link> <package-tags @@ -78,41 +81,42 @@ export default { :tag-display-limit="1" /> </div> - - <div class="d-flex text-secondary text-truncate mt-md-2"> + </template> + <template #left-secondary> + <div class="gl-display-flex"> <span>{{ packageEntity.version }}</span> - <div v-if="hasPipeline" class="d-none d-md-inline-block ml-1"> + <div v-if="hasPipeline" class="gl-display-none gl-display-sm-flex gl-ml-2"> <gl-sprintf :message="s__('PackageRegistry|published by %{author}')"> <template #author>{{ packageEntity.pipeline.user.name }}</template> </gl-sprintf> </div> - <div v-if="hasProjectLink" class="d-flex align-items-center"> - <gl-icon name="review-list" class="text-secondary ml-2 mr-1" /> + <div v-if="hasProjectLink" class="gl-display-flex gl-align-items-center"> + <gl-icon name="review-list" class="gl-ml-3 gl-mr-2 gl-min-w-0" /> <gl-link + class="gl-text-body gl-min-w-0" data-testid="packages-row-project" :href="`/${packageEntity.project_path}`" - class="text-secondary" - >{{ packageEntity.projectPathName }}</gl-link > + <gl-truncate :text="packageEntity.projectPathName" /> + </gl-link> </div> <div v-if="showPackageType" class="d-flex align-items-center" data-testid="package-type"> - <gl-icon name="package" class="text-secondary ml-2 mr-1" /> + <gl-icon name="package" class="gl-ml-3 gl-mr-2" /> <span>{{ packageType }}</span> </div> </div> - </div> + </template> - <div - class="table-section d-flex flex-md-column justify-content-between align-items-md-end" - :class="disableDelete ? 'section-50' : 'section-40'" - > + <template #right-primary> <publish-method :package-entity="packageEntity" :is-group="isGroup" /> + </template> - <div class="text-secondary order-0 order-md-1 mt-md-2"> + <template #right-secondary> + <span> <gl-sprintf :message="__('Created %{timestamp}')"> <template #timestamp> <span v-gl-tooltip :title="tooltipTitle(packageEntity.created_at)"> @@ -120,10 +124,10 @@ export default { </span> </template> </gl-sprintf> - </div> - </div> + </span> + </template> - <div v-if="!disableDelete" class="table-section section-10 d-flex justify-content-end"> + <template v-if="!disableDelete" #right-action> <gl-button data-testid="action-delete" icon="remove" @@ -134,6 +138,6 @@ export default { :disabled="!packageEntity._links.delete_api_path" @click="$emit('packageToDelete', packageEntity)" /> - </div> - </div> + </template> + </list-item> </template> diff --git a/app/assets/javascripts/packages/shared/components/package_tags.vue b/app/assets/javascripts/packages/shared/components/package_tags.vue index 391f53c225b..3d7e233c1ba 100644 --- a/app/assets/javascripts/packages/shared/components/package_tags.vue +++ b/app/assets/javascripts/packages/shared/components/package_tags.vue @@ -80,6 +80,7 @@ export default { data-testid="tagBadge" :class="tagBadgeClass(index)" variant="info" + size="sm" >{{ tag.name }}</gl-badge > @@ -89,7 +90,8 @@ export default { data-testid="moreBadge" variant="muted" :title="moreTagsTooltip" - class="gl-display-none d-md-flex gl-ml-2" + size="sm" + class="gl-display-none gl-display-md-flex gl-ml-2" ><gl-sprintf :message="__('+%{tags} more')"> <template #tags> {{ moreTagsDisplay }} @@ -101,7 +103,7 @@ export default { v-if="moreTagsDisplay && hideLabel" data-testid="moreBadge" variant="muted" - class="d-md-none gl-ml-2" + class="gl-display-md-none gl-ml-2" >{{ tagsDisplay }}</gl-badge > </div> diff --git a/app/assets/javascripts/packages/shared/components/packages_list_loader.vue b/app/assets/javascripts/packages/shared/components/packages_list_loader.vue index cd9ef74d467..efd9f8db908 100644 --- a/app/assets/javascripts/packages/shared/components/packages_list_loader.vue +++ b/app/assets/javascripts/packages/shared/components/packages_list_loader.vue @@ -5,40 +5,13 @@ export default { components: { GlSkeletonLoader, }, - props: { - isGroup: { - type: Boolean, - required: false, - default: false, - }, - }, - computed: { - desktopShapes() { - return this.isGroup ? this.$options.shapes.groups : this.$options.shapes.projects; - }, - desktopHeight() { - return this.isGroup ? 38 : 54; - }, - mobileHeight() { - return this.isGroup ? 160 : 170; - }, - }, - shapes: { - groups: [ - { type: 'rect', width: '100', height: '10', x: '0', y: '15' }, - { type: 'rect', width: '100', height: '10', x: '195', y: '15' }, - { type: 'rect', width: '60', height: '10', x: '475', y: '15' }, - { type: 'rect', width: '60', height: '10', x: '675', y: '15' }, - { type: 'rect', width: '100', height: '10', x: '900', y: '15' }, - ], - projects: [ - { type: 'rect', width: '220', height: '10', x: '0', y: '20' }, - { type: 'rect', width: '60', height: '10', x: '305', y: '20' }, - { type: 'rect', width: '60', height: '10', x: '535', y: '20' }, - { type: 'rect', width: '100', height: '10', x: '760', y: '20' }, - { type: 'rect', width: '30', height: '30', x: '970', y: '10', ref: 'button-loader' }, - ], - }, + shapes: [ + { type: 'rect', width: '220', height: '10', x: '0', y: '20' }, + { type: 'rect', width: '60', height: '10', x: '305', y: '20' }, + { type: 'rect', width: '60', height: '10', x: '535', y: '20' }, + { type: 'rect', width: '100', height: '10', x: '760', y: '20' }, + { type: 'rect', width: '30', height: '30', x: '970', y: '10', ref: 'button-loader' }, + ], rowsToRender: { mobile: 5, desktop: 20, @@ -48,34 +21,35 @@ export default { <template> <div> - <div class="d-xs-flex flex-column d-md-none"> + <div class="gl-flex-direction-column gl-display-sm-none" data-testid="mobile-loader"> <gl-skeleton-loader v-for="index in $options.rowsToRender.mobile" :key="index" :width="500" - :height="mobileHeight" + :height="170" preserve-aspect-ratio="xMinYMax meet" > <rect width="500" height="10" x="0" y="15" rx="4" /> <rect width="500" height="10" x="0" y="45" rx="4" /> <rect width="500" height="10" x="0" y="75" rx="4" /> <rect width="500" height="10" x="0" y="105" rx="4" /> - <rect v-if="isGroup" width="500" height="10" x="0" y="135" rx="4" /> - <rect v-else width="30" height="30" x="470" y="135" rx="4" /> + <rect width="500" height="10" x="0" y="135" rx="4" /> </gl-skeleton-loader> </div> - - <div class="d-none d-md-flex flex-column"> + <div + class="gl-display-none gl-display-sm-flex gl-flex-direction-column" + data-testid="desktop-loader" + > <gl-skeleton-loader v-for="index in $options.rowsToRender.desktop" :key="index" :width="1000" - :height="desktopHeight" + :height="54" preserve-aspect-ratio="xMinYMax meet" > <component :is="r.type" - v-for="(r, rIndex) in desktopShapes" + v-for="(r, rIndex) in $options.shapes" :key="rIndex" rx="4" v-bind="r" diff --git a/app/assets/javascripts/packages/shared/components/publish_method.vue b/app/assets/javascripts/packages/shared/components/publish_method.vue index 1e18562a421..d17e23c4032 100644 --- a/app/assets/javascripts/packages/shared/components/publish_method.vue +++ b/app/assets/javascripts/packages/shared/components/publish_method.vue @@ -36,26 +36,28 @@ export default { </script> <template> - <div class="d-flex align-items-center text-secondary order-1 order-md-0 mb-md-1"> + <div class="gl-display-flex gl-align-items-center"> <template v-if="hasPipeline"> - <gl-icon name="git-merge" class="mr-1" /> - <strong ref="pipeline-ref" class="mr-1 text-dark">{{ packageEntity.pipeline.ref }}</strong> + <gl-icon name="git-merge" class="gl-mr-2" /> + <span data-testid="pipeline-ref" class="gl-mr-2">{{ packageEntity.pipeline.ref }}</span> - <gl-icon name="commit" class="mr-1" /> - <gl-link ref="pipeline-sha" :href="linkToCommit" class="mr-1">{{ packageShaShort }}</gl-link> + <gl-icon name="commit" class="gl-mr-2" /> + <gl-link data-testid="pipeline-sha" :href="linkToCommit" class="gl-mr-2">{{ + packageShaShort + }}</gl-link> <clipboard-button :text="packageEntity.pipeline.sha" :title="__('Copy commit SHA')" - css-class="border-0 text-secondary py-0 px-1" + css-class="gl-border-0 gl-py-0 gl-px-2" /> </template> <template v-else> - <gl-icon name="upload" class="mr-1" /> - <strong ref="manual-ref" class="text-dark">{{ - s__('PackageRegistry|Manually Published') - }}</strong> + <gl-icon name="upload" class="gl-mr-2" /> + <span data-testid="manually-published"> + {{ s__('PackageRegistry|Manually Published') }} + </span> </template> </div> </template> diff --git a/app/assets/javascripts/packages/shared/constants.js b/app/assets/javascripts/packages/shared/constants.js index 279c2959fa9..e5131db59bf 100644 --- a/app/assets/javascripts/packages/shared/constants.js +++ b/app/assets/javascripts/packages/shared/constants.js @@ -1,3 +1,5 @@ +import { __ } from '~/locale'; + export const PackageType = { CONAN: 'conan', MAVEN: 'maven', @@ -22,3 +24,6 @@ export const TrackingCategories = { [PackageType.NPM]: 'NpmPackages', [PackageType.CONAN]: 'ConanPackages', }; + +export const SHOW_DELETE_SUCCESS_ALERT = 'showSuccessDeleteAlert'; +export const DELETE_PACKAGE_ERROR_MESSAGE = __('Something went wrong while deleting the package.'); |