diff options
Diffstat (limited to 'app/assets/javascripts/packages/details/components')
4 files changed, 181 insertions, 32 deletions
diff --git a/app/assets/javascripts/packages/details/components/app.vue b/app/assets/javascripts/packages/details/components/app.vue index bcfb17a1529..55ffe10a608 100644 --- a/app/assets/javascripts/packages/details/components/app.vue +++ b/app/assets/javascripts/packages/details/components/app.vue @@ -13,7 +13,7 @@ import { import { mapActions, mapState } from 'vuex'; import { objectToQueryString } from '~/lib/utils/common_utils'; import { numberToHumanSize } from '~/lib/utils/number_utils'; -import { s__ } from '~/locale'; +import { s__, __ } from '~/locale'; import Tracking from '~/tracking'; import PackageListRow from '../../shared/components/package_list_row.vue'; import PackagesListLoader from '../../shared/components/packages_list_loader.vue'; @@ -24,7 +24,6 @@ import DependencyRow from './dependency_row.vue'; import InstallationCommands from './installation_commands.vue'; import PackageFiles from './package_files.vue'; import PackageHistory from './package_history.vue'; -import PackageTitle from './package_title.vue'; export default { name: 'PackagesApp', @@ -36,7 +35,9 @@ export default { GlTab, GlTabs, GlSprintf, - PackageTitle, + PackageTitle: () => import('./package_title.vue'), + TerraformTitle: () => + import('~/packages_and_registries/infrastructure_registry/components/details_title.vue'), PackagesListLoader, PackageListRow, DependencyRow, @@ -50,7 +51,18 @@ export default { GlModal: GlModalDirective, }, mixins: [Tracking.mixin()], + inject: { + titleComponent: { + default: 'PackageTitle', + from: 'titleComponent', + }, + }, trackingActions: { ...TrackingActions }, + data() { + return { + fileToDelete: null, + }; + }, computed: { ...mapState([ 'projectName', @@ -86,13 +98,10 @@ export default { }, }, methods: { - ...mapActions(['deletePackage', 'fetchPackageVersions']), + ...mapActions(['deletePackage', 'fetchPackageVersions', 'deletePackageFile']), formatSize(size) { return numberToHumanSize(size); }, - cancelDelete() { - this.$refs.deleteModal.hide(); - }, getPackageVersions() { if (!this.packageEntity.versions) { this.fetchPackageVersions(); @@ -108,12 +117,43 @@ export default { const modalQuery = objectToQueryString({ [SHOW_DELETE_SUCCESS_ALERT]: true }); window.location.replace(`${returnTo}?${modalQuery}`); }, + handleFileDelete(file) { + this.track(TrackingActions.REQUEST_DELETE_PACKAGE_FILE); + this.fileToDelete = { ...file }; + this.$refs.deleteFileModal.show(); + }, + confirmFileDelete() { + this.track(TrackingActions.DELETE_PACKAGE_FILE); + this.deletePackageFile(this.fileToDelete.id); + this.fileToDelete = null; + }, }, i18n: { deleteModalTitle: s__(`PackageRegistry|Delete Package Version`), deleteModalContent: s__( `PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?`, ), + deleteFileModalTitle: s__(`PackageRegistry|Delete Package File`), + deleteFileModalContent: s__( + `PackageRegistry|You are about to delete %{filename}. This is a destructive action that may render your package unusable. Are you sure?`, + ), + }, + modal: { + packageDeletePrimaryAction: { + text: __('Delete'), + attributes: [ + { variant: 'danger' }, + { category: 'primary' }, + { 'data-qa-selector': 'delete_modal_button' }, + ], + }, + fileDeletePrimaryAction: { + text: __('Delete'), + attributes: [{ variant: 'danger' }, { category: 'primary' }], + }, + cancelAction: { + text: __('Cancel'), + }, }, }; </script> @@ -127,7 +167,7 @@ export default { /> <div v-else class="packages-app"> - <package-title> + <component :is="titleComponent"> <template #delete-button> <gl-button v-if="canDelete" @@ -140,7 +180,7 @@ export default { {{ __('Delete') }} </gl-button> </template> - </package-title> + </component> <gl-tabs> <gl-tab :title="__('Detail')"> @@ -159,7 +199,9 @@ export default { <package-files v-if="showFiles" :package-files="packageFiles" + :can-delete="canDelete" @download-file="track($options.trackingActions.PULL_PACKAGE)" + @delete-file="handleFileDelete" /> </gl-tab> @@ -210,7 +252,15 @@ export default { </gl-tab> </gl-tabs> - <gl-modal ref="deleteModal" class="js-delete-modal" modal-id="delete-modal"> + <gl-modal + ref="deleteModal" + class="js-delete-modal" + modal-id="delete-modal" + :action-primary="$options.modal.packageDeletePrimaryAction" + :action-cancel="$options.modal.cancelAction" + @primary="confirmPackageDeletion" + @canceled="track($options.trackingActions.CANCEL_DELETE_PACKAGE)" + > <template #modal-title>{{ $options.i18n.deleteModalTitle }}</template> <gl-sprintf :message="$options.i18n.deleteModalContent"> <template #version> @@ -221,23 +271,22 @@ export default { <strong>{{ packageEntity.name }}</strong> </template> </gl-sprintf> + </gl-modal> - <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> - </template> + <gl-modal + ref="deleteFileModal" + modal-id="delete-file-modal" + :action-primary="$options.modal.fileDeletePrimaryAction" + :action-cancel="$options.modal.cancelAction" + @primary="confirmFileDelete" + @canceled="track($options.trackingActions.CANCEL_DELETE_PACKAGE_FILE)" + > + <template #modal-title>{{ $options.i18n.deleteFileModalTitle }}</template> + <gl-sprintf v-if="fileToDelete" :message="$options.i18n.deleteFileModalContent"> + <template #filename> + <strong>{{ fileToDelete.file_name }}</strong> + </template> + </gl-sprintf> </gl-modal> </div> </template> diff --git a/app/assets/javascripts/packages/details/components/file_sha.vue b/app/assets/javascripts/packages/details/components/file_sha.vue new file mode 100644 index 00000000000..a25839be7e1 --- /dev/null +++ b/app/assets/javascripts/packages/details/components/file_sha.vue @@ -0,0 +1,41 @@ +<script> +import { s__ } from '~/locale'; +import ClipboardButton from '~/vue_shared/components/clipboard_button.vue'; +import DetailsRow from '~/vue_shared/components/registry/details_row.vue'; + +export default { + name: 'FileSha', + components: { + DetailsRow, + ClipboardButton, + }, + props: { + sha: { + type: String, + required: true, + }, + title: { + type: String, + required: true, + }, + }, + i18n: { + copyButtonTitle: s__('PackageRegistry|Copy SHA'), + }, +}; +</script> + +<template> + <details-row dashed> + <div class="gl-px-4"> + {{ title }}: + {{ sha }} + <clipboard-button + :text="sha" + :title="$options.i18n.copyButtonTitle" + category="tertiary" + size="small" + /> + </div> + </details-row> +</template> diff --git a/app/assets/javascripts/packages/details/components/installation_commands.vue b/app/assets/javascripts/packages/details/components/installation_commands.vue index 28978913e6e..ed55d7fe782 100644 --- a/app/assets/javascripts/packages/details/components/installation_commands.vue +++ b/app/assets/javascripts/packages/details/components/installation_commands.vue @@ -1,5 +1,6 @@ <script> -import { PackageType } from '../../shared/constants'; +import TerraformInstallation from '~/packages_and_registries/infrastructure_registry/components/terraform_installation.vue'; +import { PackageType, TERRAFORM_PACKAGE_TYPE } from '../../shared/constants'; import ComposerInstallation from './composer_installation.vue'; import ConanInstallation from './conan_installation.vue'; import MavenInstallation from './maven_installation.vue'; @@ -16,6 +17,7 @@ export default { [PackageType.NUGET]: NugetInstallation, [PackageType.PYPI]: PypiInstallation, [PackageType.COMPOSER]: ComposerInstallation, + [TERRAFORM_PACKAGE_TYPE]: TerraformInstallation, }, props: { packageEntity: { diff --git a/app/assets/javascripts/packages/details/components/package_files.vue b/app/assets/javascripts/packages/details/components/package_files.vue index 103d1f489bd..0563b612d04 100644 --- a/app/assets/javascripts/packages/details/components/package_files.vue +++ b/app/assets/javascripts/packages/details/components/package_files.vue @@ -1,8 +1,9 @@ <script> -import { GlLink, GlTable } from '@gitlab/ui'; +import { GlLink, GlTable, GlDropdownItem, GlDropdown, GlIcon, GlButton } from '@gitlab/ui'; import { last } from 'lodash'; import { numberToHumanSize } from '~/lib/utils/number_utils'; import { __ } from '~/locale'; +import FileSha from '~/packages/details/components/file_sha.vue'; import Tracking from '~/tracking'; import FileIcon from '~/vue_shared/components/file_icon.vue'; import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; @@ -12,8 +13,13 @@ export default { components: { GlLink, GlTable, + GlIcon, + GlDropdown, + GlDropdownItem, + GlButton, FileIcon, TimeAgoTooltip, + FileSha, }, mixins: [Tracking.mixin()], props: { @@ -22,6 +28,11 @@ export default { required: false, default: () => [], }, + canDelete: { + type: Boolean, + default: false, + required: false, + }, }, computed: { filesTableRows() { @@ -39,7 +50,6 @@ export default { { key: 'name', label: __('Name'), - tdClass: 'gl-display-flex gl-align-items-center', }, { key: 'commit', @@ -55,6 +65,13 @@ export default { label: __('Created'), class: 'gl-text-right', }, + { + key: 'actions', + label: '', + hide: !this.canDelete, + class: 'gl-text-right', + tdClass: 'gl-w-4', + }, ].filter((c) => !c.hide); }, }, @@ -62,6 +79,12 @@ export default { formatSize(size) { return numberToHumanSize(size); }, + hasDetails(item) { + return item.file_sha256 || item.file_md5 || item.file_sha1; + }, + }, + i18n: { + deleteFile: __('Delete file'), }, }; </script> @@ -74,10 +97,18 @@ export default { :items="filesTableRows" :tbody-tr-attr="{ 'data-testid': 'file-row' }" > - <template #cell(name)="{ item }"> + <template #cell(name)="{ item, toggleDetails, detailsShowing }"> + <gl-button + v-if="hasDetails(item)" + :icon="detailsShowing ? 'angle-up' : 'angle-down'" + :aria-label="detailsShowing ? __('Collapse') : __('Expand')" + category="tertiary" + size="small" + @click="toggleDetails" + /> <gl-link :href="item.download_path" - class="gl-relative gl-text-gray-500" + class="gl-text-gray-500" data-testid="download-link" @click="$emit('download-file')" > @@ -86,7 +117,7 @@ export default { css-classes="gl-relative file-icon" class="gl-mr-1 gl-relative" /> - <span class="gl-relative">{{ item.file_name }}</span> + <span>{{ item.file_name }}</span> </gl-link> </template> @@ -103,6 +134,32 @@ export default { <template #cell(created)="{ item }"> <time-ago-tooltip :time="item.created_at" /> </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> + </template> + + <template #row-details="{ item }"> + <div + class="gl-display-flex gl-flex-direction-column gl-flex-grow-1 gl-bg-gray-10 gl-rounded-base gl-inset-border-1-gray-100" + > + <file-sha + v-if="item.file_sha256" + data-testid="sha-256" + title="SHA-256" + :sha="item.file_sha256" + /> + <file-sha v-if="item.file_md5" data-testid="md5" title="MD5" :sha="item.file_md5" /> + <file-sha v-if="item.file_sha1" data-testid="sha-1" title="SHA-1" :sha="item.file_sha1" /> + </div> + </template> </gl-table> </div> </template> |