diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-06-20 13:43:29 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-06-20 13:43:29 +0300 |
commit | 3b1af5cc7ed2666ff18b718ce5d30fa5a2756674 (patch) | |
tree | 3bc4a40e0ee51ec27eabf917c537033c0c5b14d4 /app/assets/javascripts/packages_and_registries | |
parent | 9bba14be3f2c211bf79e15769cd9b77bc73a13bc (diff) |
Add latest changes from gitlab-org/gitlab@16-1-stable-eev16.1.0-rc42
Diffstat (limited to 'app/assets/javascripts/packages_and_registries')
13 files changed, 302 insertions, 240 deletions
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 7ac803a8ece..3a5992d182a 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 @@ -68,7 +68,7 @@ export const MISSING_MANIFEST_WARNING_TOOLTIP = s__( export const CREATED_AT = s__('ContainerRegistry|Created %{time}'); export const NOT_AVAILABLE_TEXT = __('Not applicable.'); -export const NOT_AVAILABLE_SIZE = __('0 bytes'); +export const NOT_AVAILABLE_SIZE = __('0 B'); export const CLEANUP_UNSCHEDULED_TEXT = s__('ContainerRegistry|Cleanup will run %{time}'); export const CLEANUP_SCHEDULED_TEXT = s__('ContainerRegistry|Cleanup pending'); diff --git a/app/assets/javascripts/packages_and_registries/harbor_registry/constants/details.js b/app/assets/javascripts/packages_and_registries/harbor_registry/constants/details.js index 5b4b85ec31e..ce98be914ae 100644 --- a/app/assets/javascripts/packages_and_registries/harbor_registry/constants/details.js +++ b/app/assets/javascripts/packages_and_registries/harbor_registry/constants/details.js @@ -16,7 +16,7 @@ export const DIGEST_LABEL = s__('HarborRegistry|Digest: %{imageId}'); export const CREATED_AT_LABEL = s__('HarborRegistry|Published %{timeInfo}'); export const NOT_AVAILABLE_TEXT = __('Not applicable.'); -export const NOT_AVAILABLE_SIZE = __('0 bytes'); +export const NOT_AVAILABLE_SIZE = __('0 B'); export const TOKEN_TYPE_TAG_NAME = 'tag_name'; diff --git a/app/assets/javascripts/packages_and_registries/infrastructure_registry/details/components/package_files.vue b/app/assets/javascripts/packages_and_registries/infrastructure_registry/details/components/package_files.vue index e45b88bc6d5..ecd1bfb8ebe 100644 --- a/app/assets/javascripts/packages_and_registries/infrastructure_registry/details/components/package_files.vue +++ b/app/assets/javascripts/packages_and_registries/infrastructure_registry/details/components/package_files.vue @@ -1,5 +1,11 @@ <script> -import { GlLink, GlTable, GlDropdownItem, GlDropdown, GlIcon, GlButton } from '@gitlab/ui'; +import { + GlLink, + GlTable, + GlDisclosureDropdownItem, + GlDisclosureDropdown, + GlButton, +} from '@gitlab/ui'; import { last } from 'lodash'; import { numberToHumanSize } from '~/lib/utils/number_utils'; import { __ } from '~/locale'; @@ -13,9 +19,8 @@ export default { components: { GlLink, GlTable, - GlIcon, - GlDropdown, - GlDropdownItem, + GlDisclosureDropdown, + GlDisclosureDropdownItem, GlButton, FileIcon, TimeAgoTooltip, @@ -136,14 +141,16 @@ export default { </template> <template #cell(actions)="{ item }"> - <gl-dropdown category="tertiary" right> - <template #button-content> - <gl-icon name="ellipsis_v" /> - </template> - <gl-dropdown-item data-testid="delete-file" @click="$emit('delete-file', item)"> - {{ $options.i18n.deleteFile }} - </gl-dropdown-item> - </gl-dropdown> + <gl-disclosure-dropdown category="tertiary" right no-caret icon="ellipsis_v"> + <gl-disclosure-dropdown-item + data-testid="delete-file" + @action="$emit('delete-file', item)" + > + <template #list-item> + {{ $options.i18n.deleteFile }} + </template> + </gl-disclosure-dropdown-item> + </gl-disclosure-dropdown> </template> <template #row-details="{ item }"> diff --git a/app/assets/javascripts/packages_and_registries/infrastructure_registry/list/components/packages_list_app.vue b/app/assets/javascripts/packages_and_registries/infrastructure_registry/list/components/packages_list_app.vue index 6ea1fff9ef0..37fc326f902 100644 --- a/app/assets/javascripts/packages_and_registries/infrastructure_registry/list/components/packages_list_app.vue +++ b/app/assets/javascripts/packages_and_registries/infrastructure_registry/list/components/packages_list_app.vue @@ -81,7 +81,6 @@ export default { const urlParams = new URLSearchParams(window.location.search); const showAlert = urlParams.get(SHOW_DELETE_SUCCESS_ALERT); if (showAlert) { - // to be refactored to use gl-alert createAlert({ message: DELETE_PACKAGE_SUCCESS_MESSAGE, variant: VARIANT_INFO }); const cleanUrl = window.location.href.split('?')[0]; historyReplaceState(cleanUrl); diff --git a/app/assets/javascripts/packages_and_registries/package_registry/components/details/package_files.vue b/app/assets/javascripts/packages_and_registries/package_registry/components/details/package_files.vue index 8eb8654cddd..3157653648b 100644 --- a/app/assets/javascripts/packages_and_registries/package_registry/components/details/package_files.vue +++ b/app/assets/javascripts/packages_and_registries/package_registry/components/details/package_files.vue @@ -1,6 +1,17 @@ <script> -import { GlLink, GlTable, GlDropdownItem, GlDropdown, GlButton, GlFormCheckbox } from '@gitlab/ui'; -import { last } from 'lodash'; +import { + GlAlert, + GlLink, + GlTable, + GlDropdownItem, + GlDropdown, + GlButton, + GlFormCheckbox, + GlLoadingIcon, + GlModal, + GlSprintf, +} from '@gitlab/ui'; +import { createAlert, VARIANT_SUCCESS, VARIANT_WARNING } from '~/alert'; import { numberToHumanSize } from '~/lib/utils/number_utils'; import { __, s__ } from '~/locale'; import FileSha from '~/packages_and_registries/package_registry/components/details/file_sha.vue'; @@ -9,69 +20,117 @@ import { packageTypeToTrackCategory } from '~/packages_and_registries/package_re import FileIcon from '~/vue_shared/components/file_icon.vue'; import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; import { + FETCH_PACKAGE_FILES_ERROR_MESSAGE, + GRAPHQL_PACKAGE_FILES_PAGE_SIZE, REQUEST_DELETE_SELECTED_PACKAGE_FILE_TRACKING_ACTION, SELECT_PACKAGE_FILE_TRACKING_ACTION, + DOWNLOAD_PACKAGE_ASSET_TRACKING_ACTION, + CANCEL_DELETE_PACKAGE_FILE_TRACKING_ACTION, + DELETE_PACKAGE_FILE_TRACKING_ACTION, + REQUEST_DELETE_PACKAGE_FILE_TRACKING_ACTION, TRACKING_LABEL_PACKAGE_ASSET, TRACKING_ACTION_EXPAND_PACKAGE_ASSET, + DELETE_PACKAGE_FILE_ERROR_MESSAGE, + DELETE_PACKAGE_FILE_SUCCESS_MESSAGE, + DELETE_PACKAGE_FILES_ERROR_MESSAGE, + DELETE_PACKAGE_FILES_SUCCESS_MESSAGE, + DELETE_PACKAGE_FILES_TRACKING_ACTION, + DELETE_ALL_PACKAGE_FILES_MODAL_CONTENT, + DELETE_LAST_PACKAGE_FILE_MODAL_CONTENT, } from '~/packages_and_registries/package_registry/constants'; +import getPackageFilesQuery from '~/packages_and_registries/package_registry/graphql/queries/get_package_files.query.graphql'; +import destroyPackageFilesMutation from '~/packages_and_registries/package_registry/graphql/mutations/destroy_package_files.mutation.graphql'; export default { name: 'PackageFiles', components: { + GlAlert, GlLink, GlTable, GlDropdown, GlDropdownItem, GlFormCheckbox, GlButton, + GlLoadingIcon, + GlModal, + GlSprintf, FileIcon, TimeAgoTooltip, FileSha, }, mixins: [Tracking.mixin()], + trackingActions: { + DELETE_PACKAGE_FILE_TRACKING_ACTION, + REQUEST_DELETE_PACKAGE_FILE_TRACKING_ACTION, + CANCEL_DELETE_PACKAGE_FILE_TRACKING_ACTION, + DOWNLOAD_PACKAGE_ASSET_TRACKING_ACTION, + }, props: { canDelete: { type: Boolean, required: false, default: false, }, - isLoading: { - type: Boolean, - required: false, - default: false, + packageId: { + type: String, + required: true, + }, + packageType: { + type: String, + required: true, }, + projectPath: { + type: String, + required: true, + }, + }, + apollo: { packageFiles: { - type: Array, - required: false, - default: () => [], + query: getPackageFilesQuery, + context: { + isSingleRequest: true, + }, + variables() { + return this.queryVariables; + }, + update(data) { + return data.package?.packageFiles ?? {}; + }, + error() { + this.fetchPackageFilesError = true; + }, }, }, data() { return { + fetchPackageFilesError: false, + filesToDelete: [], + packageFiles: {}, + mutationLoading: false, selectedReferences: [], }; }, computed: { + files() { + return this.packageFiles?.nodes ?? []; + }, areFilesSelected() { return this.selectedReferences.length > 0; }, areAllFilesSelected() { - return this.packageFiles.every(this.isSelected); + return this.files.length > 0 && this.files.every(this.isSelected); }, filesTableRows() { - return this.packageFiles.map((pf) => ({ + return this.files.map((pf) => ({ ...pf, size: this.formatSize(pf.size), - pipeline: last(pf.pipelines), })); }, hasSelectedSomeFiles() { return this.areFilesSelected && !this.areAllFilesSelected; }, - showCommitColumn() { - // note that this is always false for now since we do not return - // pipelines associated to files for performance concerns - return this.filesTableRows.some((row) => Boolean(row.pipeline?.id)); + isLoading() { + return this.$apollo.queries.packageFiles.loading || this.mutationLoading; }, filesTableHeaderFields() { return [ @@ -86,11 +145,6 @@ export default { label: __('Name'), }, { - key: 'commit', - label: __('Commit'), - hide: !this.showCommitColumn, - }, - { key: 'size', label: __('Size'), }, @@ -108,11 +162,40 @@ export default { }, ].filter((c) => !c.hide); }, + queryVariables() { + return { + id: this.packageId, + first: GRAPHQL_PACKAGE_FILES_PAGE_SIZE, + }; + }, tracking() { return { category: packageTypeToTrackCategory(this.packageType), }; }, + refetchQueriesData() { + return [ + { + query: getPackageFilesQuery, + variables: this.queryVariables, + }, + ]; + }, + modalAction() { + return this.hasOneItem(this.filesToDelete) + ? this.$options.modal.fileDeletePrimaryAction + : this.$options.modal.filesDeletePrimaryAction; + }, + modalTitle() { + return this.hasOneItem(this.filesToDelete) + ? this.$options.i18n.deleteFileModalTitle + : this.$options.i18n.deleteFilesModalTitle; + }, + modalDescription() { + return this.hasOneItem(this.filesToDelete) + ? this.$options.i18n.deleteFileModalContent + : this.$options.i18n.deleteFilesModalContent; + }, }, methods: { formatSize(size) { @@ -135,13 +218,96 @@ export default { }, handleFileDeleteSelected() { this.track(REQUEST_DELETE_SELECTED_PACKAGE_FILE_TRACKING_ACTION); - this.$emit('delete-files', this.selectedReferences); + this.handleFileDelete(this.selectedReferences); + }, + async deletePackageFiles(ids) { + this.mutationLoading = true; + try { + const { data } = await this.$apollo.mutate({ + mutation: destroyPackageFilesMutation, + variables: { + projectPath: this.projectPath, + ids, + }, + awaitRefetchQueries: true, + refetchQueries: this.refetchQueriesData, + }); + if (data?.destroyPackageFiles?.errors[0]) { + throw data.destroyPackageFiles.errors[0]; + } + createAlert({ + message: this.hasOneItem(ids) + ? DELETE_PACKAGE_FILE_SUCCESS_MESSAGE + : DELETE_PACKAGE_FILES_SUCCESS_MESSAGE, + variant: VARIANT_SUCCESS, + }); + } catch (error) { + createAlert({ + message: this.hasOneItem(ids) + ? DELETE_PACKAGE_FILE_ERROR_MESSAGE + : DELETE_PACKAGE_FILES_ERROR_MESSAGE, + variant: VARIANT_WARNING, + captureError: true, + error, + }); + } finally { + this.mutationLoading = false; + this.filesToDelete = []; + this.selectedReferences = []; + } + }, + handleFileDelete(files) { + this.track(REQUEST_DELETE_PACKAGE_FILE_TRACKING_ACTION); + if (files.length === this.files.length && !this.packageFiles?.pageInfo?.hasNextPage) { + this.$emit( + 'delete-all-files', + this.hasOneItem(files) + ? DELETE_LAST_PACKAGE_FILE_MODAL_CONTENT + : DELETE_ALL_PACKAGE_FILES_MODAL_CONTENT, + ); + } else { + this.filesToDelete = files; + this.$refs.deleteFilesModal.show(); + } + }, + hasOneItem(items) { + return items.length === 1; + }, + confirmFilesDelete() { + if (this.hasOneItem(this.filesToDelete)) { + this.track(DELETE_PACKAGE_FILE_TRACKING_ACTION); + } else { + this.track(DELETE_PACKAGE_FILES_TRACKING_ACTION); + } + this.deletePackageFiles(this.filesToDelete.map((file) => file.id)); }, }, i18n: { - deleteFile: __('Delete asset'), + deleteFile: s__('PackageRegistry|Delete asset'), + deleteFileModalTitle: s__('PackageRegistry|Delete package asset'), + deleteFileModalContent: s__( + 'PackageRegistry|You are about to delete %{filename}. This is a destructive action that may render your package unusable. Are you sure?', + ), + deleteFilesModalTitle: s__('PackageRegistry|Delete %{count} assets'), + deleteFilesModalContent: s__( + 'PackageRegistry|You are about to delete %{count} assets. This operation is irreversible.', + ), deleteSelected: s__('PackageRegistry|Delete selected'), moreActionsText: __('More actions'), + fetchPackageFilesErrorMessage: FETCH_PACKAGE_FILES_ERROR_MESSAGE, + }, + modal: { + fileDeletePrimaryAction: { + text: __('Delete'), + attributes: { variant: 'danger', category: 'primary' }, + }, + filesDeletePrimaryAction: { + text: s__('PackageRegistry|Permanently delete assets'), + attributes: { variant: 'danger', category: 'primary' }, + }, + cancelAction: { + text: __('Cancel'), + }, }, }; </script> @@ -151,7 +317,7 @@ export default { <div class="gl-display-flex gl-align-items-center gl-justify-content-space-between"> <h3 class="gl-font-lg gl-mt-5">{{ __('Assets') }}</h3> <gl-button - v-if="canDelete" + v-if="!fetchPackageFilesError && canDelete" :disabled="isLoading || !areFilesSelected" category="secondary" variant="danger" @@ -161,7 +327,16 @@ export default { {{ $options.i18n.deleteSelected }} </gl-button> </div> + <gl-alert + v-if="fetchPackageFilesError" + variant="danger" + @dismiss="fetchPackageFilesError = false" + > + {{ $options.i18n.fetchPackageFilesErrorMessage }} + </gl-alert> <gl-table + v-else + :busy="isLoading" :fields="filesTableHeaderFields" :items="filesTableRows" show-empty @@ -171,6 +346,9 @@ export default { :tbody-tr-attr="{ 'data-testid': 'file-row' }" @row-selected="updateSelectedReferences" > + <template #table-busy> + <gl-loading-icon size="lg" class="gl-my-5" /> + </template> <template #head(checkbox)="{ selectAllRows, clearSelected }"> <gl-form-checkbox v-if="canDelete" @@ -207,7 +385,7 @@ export default { :href="item.downloadPath" class="gl-text-gray-500" data-testid="download-link" - @click="$emit('download-file')" + @click="track($options.trackingActions.DOWNLOAD_PACKAGE_ASSET_TRACKING_ACTION)" > <file-icon :file-name="item.fileName" @@ -218,16 +396,6 @@ export default { </gl-link> </template> - <template #cell(commit)="{ item }"> - <gl-link - v-if="item.pipeline && item.pipeline" - :href="item.pipeline.commitPath" - class="gl-text-gray-500" - data-testid="commit-link" - >{{ item.pipeline.sha }} - </gl-link> - </template> - <template #cell(created)="{ item }"> <time-ago-tooltip :time="item.createdAt" /> </template> @@ -241,7 +409,7 @@ export default { no-caret right > - <gl-dropdown-item data-testid="delete-file" @click="$emit('delete-files', [item])"> + <gl-dropdown-item data-testid="delete-file" @click="handleFileDelete([item])"> {{ $options.i18n.deleteFile }} </gl-dropdown-item> </gl-dropdown> @@ -262,5 +430,34 @@ export default { </div> </template> </gl-table> + + <gl-modal + ref="deleteFilesModal" + size="sm" + modal-id="delete-files-modal" + :action-primary="modalAction" + :action-cancel="$options.modal.cancelAction" + data-testid="delete-files-modal" + @primary="confirmFilesDelete" + @canceled="track($options.trackingActions.CANCEL_DELETE_PACKAGE_FILE)" + > + <template #modal-title> + <gl-sprintf :message="modalTitle"> + <template #count> + {{ filesToDelete.length }} + </template> + </gl-sprintf> + </template> + + <gl-sprintf :message="modalDescription"> + <template #filename> + <strong>{{ filesToDelete[0].fileName }}</strong> + </template> + + <template #count> + {{ filesToDelete.length }} + </template> + </gl-sprintf> + </gl-modal> </div> </template> diff --git a/app/assets/javascripts/packages_and_registries/package_registry/components/details/package_title.vue b/app/assets/javascripts/packages_and_registries/package_registry/components/details/package_title.vue index cee976656f9..5eabcea9e15 100644 --- a/app/assets/javascripts/packages_and_registries/package_registry/components/details/package_title.vue +++ b/app/assets/javascripts/packages_and_registries/package_registry/components/details/package_title.vue @@ -1,7 +1,6 @@ <script> import { GlSprintf, GlBadge, GlResizeObserverDirective } from '@gitlab/ui'; import { GlBreakpointInstance } from '@gitlab/ui/dist/utils'; -import { numberToHumanSize } from '~/lib/utils/number_utils'; import { __, s__, sprintf } from '~/locale'; import { formatDate } from '~/lib/utils/datetime_utility'; import PackageTags from '~/packages_and_registries/shared/components/package_tags.vue'; @@ -61,13 +60,6 @@ export default { hasTagsToDisplay() { return Boolean(this.packageEntity.tags?.nodes && this.packageEntity.tags?.nodes.length); }, - totalSize() { - return this.packageEntity.packageFiles - ? numberToHumanSize( - this.packageEntity.packageFiles.nodes.reduce((acc, p) => acc + Number(p.size), 0), - ) - : '0'; - }, }, mounted() { this.checkBreakpoints(); @@ -126,10 +118,6 @@ export default { <metadata-item data-testid="package-type" icon="package" :text="packageTypeDisplay" /> </template> - <template #metadata-size> - <metadata-item data-testid="package-size" icon="disk" :text="totalSize" /> - </template> - <template v-if="isGroupPage && packagePipeline" #metadata-pipeline> <metadata-item data-testid="pipeline-project" diff --git a/app/assets/javascripts/packages_and_registries/package_registry/constants.js b/app/assets/javascripts/packages_and_registries/package_registry/constants.js index b4276d69ed6..80712c2991c 100644 --- a/app/assets/javascripts/packages_and_registries/package_registry/constants.js +++ b/app/assets/javascripts/packages_and_registries/package_registry/constants.js @@ -102,6 +102,9 @@ export const FETCH_PACKAGE_PIPELINES_ERROR_MESSAGE = s__( export const FETCH_PACKAGE_METADATA_ERROR_MESSAGE = s__( 'PackageRegistry|Something went wrong while fetching the package metadata.', ); +export const FETCH_PACKAGE_FILES_ERROR_MESSAGE = s__( + 'PackageRegistry|Something went wrong while fetching package assets.', +); export const DELETE_PACKAGES_TRACKING_ACTION = 'delete_packages'; export const REQUEST_DELETE_PACKAGES_TRACKING_ACTION = 'request_delete_packages'; @@ -232,3 +235,4 @@ export const REQUEST_FORWARDING_HELP_PAGE_PATH = helpPagePath( ); export const GRAPHQL_PACKAGE_PIPELINES_PAGE_SIZE = 10; +export const GRAPHQL_PACKAGE_FILES_PAGE_SIZE = 100; diff --git a/app/assets/javascripts/packages_and_registries/package_registry/graphql/index.js b/app/assets/javascripts/packages_and_registries/package_registry/graphql/index.js index 39e5da54509..d05ff5daad4 100644 --- a/app/assets/javascripts/packages_and_registries/package_registry/graphql/index.js +++ b/app/assets/javascripts/packages_and_registries/package_registry/graphql/index.js @@ -21,6 +21,9 @@ export const apolloProvider = new VueApollo({ keyArgs: false, merge: mergeVariables, }, + packageFiles: { + merge: mergeVariables, + }, }, }, }, diff --git a/app/assets/javascripts/packages_and_registries/package_registry/graphql/queries/get_package_details.query.graphql b/app/assets/javascripts/packages_and_registries/package_registry/graphql/queries/get_package_details.query.graphql index 984996b829a..4c71de9ee20 100644 --- a/app/assets/javascripts/packages_and_registries/package_registry/graphql/queries/get_package_details.query.graphql +++ b/app/assets/javascripts/packages_and_registries/package_registry/graphql/queries/get_package_details.query.graphql @@ -46,21 +46,6 @@ query getPackageDetails($id: PackagesPackageID!) { } } } - packageFiles(first: 100) { - pageInfo { - hasNextPage - } - nodes { - id - fileMd5 - fileName - fileSha1 - fileSha256 - size - createdAt - downloadPath - } - } versions { count } diff --git a/app/assets/javascripts/packages_and_registries/package_registry/graphql/queries/get_package_files.query.graphql b/app/assets/javascripts/packages_and_registries/package_registry/graphql/queries/get_package_files.query.graphql new file mode 100644 index 00000000000..e6f292ec1d3 --- /dev/null +++ b/app/assets/javascripts/packages_and_registries/package_registry/graphql/queries/get_package_files.query.graphql @@ -0,0 +1,20 @@ +query getPackageFiles($id: PackagesPackageID!, $first: Int) { + package(id: $id) { + id + packageFiles(first: $first) { + pageInfo { + hasNextPage + } + nodes { + id + fileMd5 + fileName + fileSha1 + fileSha256 + size + createdAt + downloadPath + } + } + } +} diff --git a/app/assets/javascripts/packages_and_registries/package_registry/pages/details.vue b/app/assets/javascripts/packages_and_registries/package_registry/pages/details.vue index 6d4979ac785..d96418571e1 100644 --- a/app/assets/javascripts/packages_and_registries/package_registry/pages/details.vue +++ b/app/assets/javascripts/packages_and_registries/package_registry/pages/details.vue @@ -11,7 +11,7 @@ import { GlTabs, GlSprintf, } from '@gitlab/ui'; -import { createAlert, VARIANT_SUCCESS, VARIANT_WARNING } from '~/alert'; +import { createAlert } from '~/alert'; import { TYPENAME_PACKAGES_PACKAGE } from '~/graphql_shared/constants'; import { convertToGraphQLId } from '~/graphql_shared/utils'; import { numberToHumanSize } from '~/lib/utils/number_utils'; @@ -21,10 +21,8 @@ import { packageTypeToTrackCategory } from '~/packages_and_registries/package_re import AdditionalMetadata from '~/packages_and_registries/package_registry/components/details/additional_metadata.vue'; import DependencyRow from '~/packages_and_registries/package_registry/components/details/dependency_row.vue'; import InstallationCommands from '~/packages_and_registries/package_registry/components/details/installation_commands.vue'; -import PackageFiles from '~/packages_and_registries/package_registry/components/details/package_files.vue'; import PackageHistory from '~/packages_and_registries/package_registry/components/details/package_history.vue'; import PackageTitle from '~/packages_and_registries/package_registry/components/details/package_title.vue'; -import PackageVersionsList from '~/packages_and_registries/package_registry/components/details/package_versions_list.vue'; import DeletePackages from '~/packages_and_registries/package_registry/components/functional/delete_packages.vue'; import { PACKAGE_TYPE_NUGET, @@ -35,27 +33,15 @@ import { DELETE_PACKAGE_TRACKING_ACTION, REQUEST_DELETE_PACKAGE_TRACKING_ACTION, CANCEL_DELETE_PACKAGE_TRACKING_ACTION, - DELETE_PACKAGE_FILE_TRACKING_ACTION, - DELETE_PACKAGE_FILES_TRACKING_ACTION, - REQUEST_DELETE_PACKAGE_FILE_TRACKING_ACTION, REQUEST_FORWARDING_HELP_PAGE_PATH, - CANCEL_DELETE_PACKAGE_FILE_TRACKING_ACTION, SHOW_DELETE_SUCCESS_ALERT, FETCH_PACKAGE_DETAILS_ERROR_MESSAGE, - DELETE_PACKAGE_FILE_ERROR_MESSAGE, - DELETE_PACKAGE_FILE_SUCCESS_MESSAGE, - DELETE_PACKAGE_FILES_ERROR_MESSAGE, - DELETE_PACKAGE_FILES_SUCCESS_MESSAGE, DELETE_PACKAGE_REQUEST_FORWARDING_MODAL_CONTENT, - DOWNLOAD_PACKAGE_ASSET_TRACKING_ACTION, DELETE_MODAL_TITLE, DELETE_MODAL_CONTENT, - DELETE_ALL_PACKAGE_FILES_MODAL_CONTENT, - DELETE_LAST_PACKAGE_FILE_MODAL_CONTENT, GRAPHQL_PAGE_SIZE, } from '~/packages_and_registries/package_registry/constants'; -import destroyPackageFilesMutation from '~/packages_and_registries/package_registry/graphql/mutations/destroy_package_files.mutation.graphql'; import getPackageDetails from '~/packages_and_registries/package_registry/graphql/queries/get_package_details.query.graphql'; import getPackageVersionsQuery from '~/packages_and_registries/package_registry/graphql/queries/get_package_versions.query.graphql'; import Tracking from '~/tracking'; @@ -76,9 +62,13 @@ export default { PackageHistory, AdditionalMetadata, InstallationCommands, - PackageFiles, + PackageFiles: () => + import('~/packages_and_registries/package_registry/components/details/package_files.vue'), DeletePackages, - PackageVersionsList, + PackageVersionsList: () => + import( + '~/packages_and_registries/package_registry/components/details/package_versions_list.vue' + ), }, directives: { GlTooltip: GlTooltipDirective, @@ -90,10 +80,6 @@ export default { DELETE_PACKAGE_TRACKING_ACTION, REQUEST_DELETE_PACKAGE_TRACKING_ACTION, CANCEL_DELETE_PACKAGE_TRACKING_ACTION, - DELETE_PACKAGE_FILE_TRACKING_ACTION, - REQUEST_DELETE_PACKAGE_FILE_TRACKING_ACTION, - CANCEL_DELETE_PACKAGE_FILE_TRACKING_ACTION, - DOWNLOAD_PACKAGE_ASSET_TRACKING_ACTION, }, data() { return { @@ -147,18 +133,12 @@ export default { id: convertToGraphQLId(TYPENAME_PACKAGES_PACKAGE, this.packageId), }; }, - packageFiles() { - return this.packageEntity.packageFiles?.nodes; - }, packageType() { return this.packageEntity.packageType; }, isLoading() { return this.$apollo.queries.packageEntity.loading; }, - packageFilesLoading() { - return this.isLoading || this.mutationLoading; - }, isValidPackage() { return this.isLoading || Boolean(this.packageEntity.name); }, @@ -194,14 +174,6 @@ export default { PACKAGE_TYPE_PYPI, ].includes(this.packageType); }, - refetchQueriesData() { - return [ - { - query: getPackageDetails, - variables: this.queryVariables, - }, - ]; - }, refetchVersionsQueryData() { return [ { @@ -228,71 +200,9 @@ export default { window.location.replace(`${returnTo}?${modalQuery}`); }, - async deletePackageFiles(ids) { - this.mutationLoading = true; - try { - const { data } = await this.$apollo.mutate({ - mutation: destroyPackageFilesMutation, - variables: { - projectPath: this.projectPath, - ids, - }, - awaitRefetchQueries: true, - refetchQueries: this.refetchQueriesData, - }); - if (data?.destroyPackageFiles?.errors[0]) { - throw data.destroyPackageFiles.errors[0]; - } - createAlert({ - message: this.isLastItem(ids) - ? DELETE_PACKAGE_FILE_SUCCESS_MESSAGE - : DELETE_PACKAGE_FILES_SUCCESS_MESSAGE, - variant: VARIANT_SUCCESS, - }); - } catch (error) { - createAlert({ - message: this.isLastItem(ids) - ? DELETE_PACKAGE_FILE_ERROR_MESSAGE - : DELETE_PACKAGE_FILES_ERROR_MESSAGE, - variant: VARIANT_WARNING, - captureError: true, - error, - }); - } - this.mutationLoading = false; - }, - handleFileDelete(files) { - this.track(REQUEST_DELETE_PACKAGE_FILE_TRACKING_ACTION); - if ( - files.length === this.packageFiles.length && - !this.packageEntity.packageFiles?.pageInfo?.hasNextPage - ) { - if (this.isLastItem(files)) { - this.deletePackageModalContent = DELETE_LAST_PACKAGE_FILE_MODAL_CONTENT; - } else { - this.deletePackageModalContent = DELETE_ALL_PACKAGE_FILES_MODAL_CONTENT; - } - this.$refs.deleteModal.show(); - } else { - this.filesToDelete = files; - if (this.isLastItem(files)) { - this.$refs.deleteFileModal.show(); - } else if (files.length > 1) { - this.$refs.deleteFilesModal.show(); - } - } - }, - isLastItem(items) { - return items.length === 1; - }, - confirmFilesDelete() { - if (this.isLastItem(this.filesToDelete)) { - this.track(DELETE_PACKAGE_FILE_TRACKING_ACTION); - } else { - this.track(DELETE_PACKAGE_FILES_TRACKING_ACTION); - } - this.deletePackageFiles(this.filesToDelete.map((file) => file.id)); - this.filesToDelete = []; + handleAllFilesDelete(content) { + this.deletePackageModalContent = content; + this.$refs.deleteModal.show(); }, resetDeleteModalContent() { this.deletePackageModalContent = DELETE_MODAL_CONTENT; @@ -300,10 +210,6 @@ export default { }, i18n: { DELETE_MODAL_TITLE, - deleteFileModalTitle: s__(`PackageRegistry|Delete package asset`), - deleteFileModalContent: s__( - `PackageRegistry|You are about to delete %{filename}. This is a destructive action that may render your package unusable. Are you sure?`, - ), otherVersionsTabTitle: s__('PackageRegistry|Other versions'), }, links: { @@ -358,7 +264,7 @@ export default { <gl-tabs> <gl-tab :title="__('Detail')"> - <div v-if="!isLoading" data-qa-selector="package_information_content"> + <div data-qa-selector="package_information_content"> <package-history :package-entity="packageEntity" :project-name="projectName" /> <installation-commands :package-entity="packageEntity" /> @@ -368,16 +274,16 @@ export default { :package-id="packageEntity.id" :package-type="packageType" /> - </div> - <package-files - v-if="showFiles" - :can-delete="packageEntity.canDestroy" - :is-loading="packageFilesLoading" - :package-files="packageFiles" - @download-file="track($options.trackingActions.DOWNLOAD_PACKAGE_ASSET_TRACKING_ACTION)" - @delete-files="handleFileDelete" - /> + <package-files + v-if="showFiles" + :can-delete="packageEntity.canDestroy" + :package-id="packageEntity.id" + :package-type="packageType" + :project-path="projectPath" + @delete-all-files="handleAllFilesDelete" + /> + </div> </gl-tab> <gl-tab v-if="showDependencies"> @@ -468,51 +374,5 @@ export default { </gl-modal> </template> </delete-packages> - - <gl-modal - ref="deleteFileModal" - size="sm" - modal-id="delete-file-modal" - :action-primary="$options.modal.fileDeletePrimaryAction" - :action-cancel="$options.modal.cancelAction" - data-testid="delete-file-modal" - @primary="confirmFilesDelete" - @canceled="track($options.trackingActions.CANCEL_DELETE_PACKAGE_FILE)" - > - <template #modal-title>{{ $options.i18n.deleteFileModalTitle }}</template> - <gl-sprintf v-if="isLastItem(filesToDelete)" :message="$options.i18n.deleteFileModalContent"> - <template #filename> - <strong>{{ filesToDelete[0].fileName }}</strong> - </template> - </gl-sprintf> - </gl-modal> - - <gl-modal - ref="deleteFilesModal" - size="sm" - modal-id="delete-files-modal" - :action-primary="$options.modal.filesDeletePrimaryAction" - :action-cancel="$options.modal.cancelAction" - data-testid="delete-files-modal" - @primary="confirmFilesDelete" - @canceled="track($options.trackingActions.CANCEL_DELETE_PACKAGE_FILE)" - > - <template #modal-title>{{ - n__( - `PackageRegistry|Delete 1 asset`, - `PackageRegistry|Delete %d assets`, - filesToDelete.length, - ) - }}</template> - <span v-if="filesToDelete.length > 0"> - {{ - n__( - `PackageRegistry|You are about to delete 1 asset. This operation is irreversible.`, - `PackageRegistry|You are about to delete %d assets. This operation is irreversible.`, - filesToDelete.length, - ) - }} - </span> - </gl-modal> </div> </template> diff --git a/app/assets/javascripts/packages_and_registries/package_registry/pages/list.vue b/app/assets/javascripts/packages_and_registries/package_registry/pages/list.vue index 044ce4e6413..14d617a7a3c 100644 --- a/app/assets/javascripts/packages_and_registries/package_registry/pages/list.vue +++ b/app/assets/javascripts/packages_and_registries/package_registry/pages/list.vue @@ -114,7 +114,6 @@ export default { const urlParams = new URLSearchParams(window.location.search); const showAlert = urlParams.get(SHOW_DELETE_SUCCESS_ALERT); if (showAlert) { - // to be refactored to use gl-alert createAlert({ message: DELETE_PACKAGE_SUCCESS_MESSAGE, variant: VARIANT_INFO }); const cleanUrl = window.location.href.split('?')[0]; historyReplaceState(cleanUrl); diff --git a/app/assets/javascripts/packages_and_registries/settings/project/components/packages_cleanup_policy_form.vue b/app/assets/javascripts/packages_and_registries/settings/project/components/packages_cleanup_policy_form.vue index f95ec4336dc..80df8ef81e6 100644 --- a/app/assets/javascripts/packages_and_registries/settings/project/components/packages_cleanup_policy_form.vue +++ b/app/assets/javascripts/packages_and_registries/settings/project/components/packages_cleanup_policy_form.vue @@ -139,7 +139,7 @@ export default { :form-options="$options.formOptions.keepNDuplicatedPackageFiles" :label="$options.i18n.KEEP_N_DUPLICATED_PACKAGE_FILES_LABEL" :description="$options.i18n.KEEP_N_DUPLICATED_PACKAGE_FILES_DESCRIPTION" - dropdown-class="gl-md-max-w-50p gl-sm-pr-5" + dropdown-class="gl-md-max-w-50p" name="keep-n-duplicated-package-files" data-testid="keep-n-duplicated-package-files-dropdown" @input="onModelChange($event, 'keepNDuplicatedPackageFiles')" |