Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/packages_and_registries')
-rw-r--r--app/assets/javascripts/packages_and_registries/container_registry/explorer/components/list_page/cleanup_status.vue6
-rw-r--r--app/assets/javascripts/packages_and_registries/container_registry/explorer/components/list_page/image_list_row.vue2
-rw-r--r--app/assets/javascripts/packages_and_registries/dependency_proxy/app.vue4
-rw-r--r--app/assets/javascripts/packages_and_registries/dependency_proxy/index.js8
-rw-r--r--app/assets/javascripts/packages_and_registries/infrastructure_registry/list/stores/actions.js5
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/components/details/package_files.vue109
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/components/details/package_history.vue35
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/components/details/package_title.vue52
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/components/details/pypi_installation.vue47
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/constants.js15
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/graphql/mutations/destroy_package_file.mutation.graphql5
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/graphql/mutations/destroy_package_files.mutation.graphql5
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/graphql/queries/get_package_details.query.graphql4
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/pages/details.vue107
-rw-r--r--app/assets/javascripts/packages_and_registries/settings/project/components/container_expiration_policy.vue2
-rw-r--r--app/assets/javascripts/packages_and_registries/settings/project/components/expiration_dropdown.vue30
-rw-r--r--app/assets/javascripts/packages_and_registries/settings/project/components/packages_cleanup_policy.vue2
-rw-r--r--app/assets/javascripts/packages_and_registries/settings/project/components/packages_cleanup_policy_form.vue25
-rw-r--r--app/assets/javascripts/packages_and_registries/settings/project/constants.js2
-rw-r--r--app/assets/javascripts/packages_and_registries/shared/components/settings_block.vue6
-rw-r--r--app/assets/javascripts/packages_and_registries/shared/constants/package_registry.js4
21 files changed, 356 insertions, 119 deletions
diff --git a/app/assets/javascripts/packages_and_registries/container_registry/explorer/components/list_page/cleanup_status.vue b/app/assets/javascripts/packages_and_registries/container_registry/explorer/components/list_page/cleanup_status.vue
index bfa99c01c3f..ce221a274c9 100644
--- a/app/assets/javascripts/packages_and_registries/container_registry/explorer/components/list_page/cleanup_status.vue
+++ b/app/assets/javascripts/packages_and_registries/container_registry/explorer/components/list_page/cleanup_status.vue
@@ -60,7 +60,7 @@ export default {
return this.$options.i18n[`CLEANUP_STATUS_${this.status}`];
},
calculatedTimeTilNextRun() {
- return timeTilRun(this.expirationPolicy?.next_run);
+ return timeTilRun(this.expirationPolicy?.next_run_at);
},
expireIconName() {
return this.failedDelete ? 'expire' : 'clock';
@@ -90,9 +90,9 @@ export default {
{{ statusText }}
</span>
<gl-icon
- v-if="failedDelete"
+ v-if="failedDelete && calculatedTimeTilNextRun"
:id="iconId"
- :size="14"
+ :size="16"
class="gl-text-gray-500"
data-testid="extra-info"
name="information-o"
diff --git a/app/assets/javascripts/packages_and_registries/container_registry/explorer/components/list_page/image_list_row.vue b/app/assets/javascripts/packages_and_registries/container_registry/explorer/components/list_page/image_list_row.vue
index aecc0bf92ea..80bca536b7c 100644
--- a/app/assets/javascripts/packages_and_registries/container_registry/explorer/components/list_page/image_list_row.vue
+++ b/app/assets/javascripts/packages_and_registries/container_registry/explorer/components/list_page/image_list_row.vue
@@ -95,7 +95,7 @@ export default {
if (this.showFullPath) {
return this.item.path;
}
- const projectPath = this.item?.project?.path ?? '';
+ const projectPath = this.item?.project?.path?.toLowerCase() ?? '';
if (this.item.name) {
return joinPaths(projectPath, this.item.name);
}
diff --git a/app/assets/javascripts/packages_and_registries/dependency_proxy/app.vue b/app/assets/javascripts/packages_and_registries/dependency_proxy/app.vue
index 1faff1ff4de..45dc217b9e3 100644
--- a/app/assets/javascripts/packages_and_registries/dependency_proxy/app.vue
+++ b/app/assets/javascripts/packages_and_registries/dependency_proxy/app.vue
@@ -39,7 +39,7 @@ export default {
directives: {
GlModalDirective,
},
- inject: ['groupPath', 'groupId', 'noManifestsIllustration'],
+ inject: ['groupPath', 'groupId', 'noManifestsIllustration', 'canClearCache'],
i18n: {
proxyImagePrefix: s__('DependencyProxy|Dependency Proxy image prefix'),
copyImagePrefixText: s__('DependencyProxy|Copy prefix'),
@@ -114,7 +114,7 @@ export default {
);
},
showDeleteDropdown() {
- return this.group.dependencyProxyManifests?.nodes.length > 0;
+ return this.group.dependencyProxyManifests?.nodes.length > 0 && this.canClearCache;
},
showDependencyProxyImagePrefix() {
return this.group.dependencyProxyImagePrefix?.length > 0;
diff --git a/app/assets/javascripts/packages_and_registries/dependency_proxy/index.js b/app/assets/javascripts/packages_and_registries/dependency_proxy/index.js
index 14789aafdb7..428d6d6cd75 100644
--- a/app/assets/javascripts/packages_and_registries/dependency_proxy/index.js
+++ b/app/assets/javascripts/packages_and_registries/dependency_proxy/index.js
@@ -1,4 +1,5 @@
import Vue from 'vue';
+import { parseBoolean } from '~/lib/utils/common_utils';
import app from '~/packages_and_registries/dependency_proxy/app.vue';
import { apolloProvider } from '~/packages_and_registries/dependency_proxy/graphql';
import Translate from '~/vue_shared/translate';
@@ -10,12 +11,15 @@ export const initDependencyProxyApp = () => {
if (!el) {
return null;
}
- const { ...dataset } = el.dataset;
+ const { groupPath, groupId, noManifestsIllustration, canClearCache } = el.dataset;
return new Vue({
el,
apolloProvider,
provide: {
- ...dataset,
+ groupPath,
+ groupId,
+ noManifestsIllustration,
+ canClearCache: parseBoolean(canClearCache),
},
render(createElement) {
return createElement(app);
diff --git a/app/assets/javascripts/packages_and_registries/infrastructure_registry/list/stores/actions.js b/app/assets/javascripts/packages_and_registries/infrastructure_registry/list/stores/actions.js
index 408d34fbe93..51a38c434cb 100644
--- a/app/assets/javascripts/packages_and_registries/infrastructure_registry/list/stores/actions.js
+++ b/app/assets/javascripts/packages_and_registries/infrastructure_registry/list/stores/actions.js
@@ -26,8 +26,7 @@ export const receivePackagesListSuccess = ({ commit }, { data, headers }) => {
export const requestPackagesList = ({ dispatch, state }, params = {}) => {
dispatch('setLoading', true);
- // eslint-disable-next-line camelcase
- const { page = DEFAULT_PAGE, per_page = DEFAULT_PAGE_SIZE } = params;
+ const { page = DEFAULT_PAGE, per_page: perPage = DEFAULT_PAGE_SIZE } = params;
const { sort, orderBy } = state.sorting;
const type = state.config.forceTerraform
? TERRAFORM_SEARCH_TYPE
@@ -38,7 +37,7 @@ export const requestPackagesList = ({ dispatch, state }, params = {}) => {
const apiMethod = state.config.isGroupPage ? 'groupPackages' : 'projectPackages';
return Api[apiMethod](state.config.resourceId, {
- params: { page, per_page, sort, order_by: orderBy, ...packageFilters },
+ params: { page, per_page: perPage, sort, order_by: orderBy, ...packageFilters },
})
.then(({ data, headers }) => {
dispatch('receivePackagesListSuccess', { data, headers });
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 a049b0eff8d..b872294d2cf 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,14 +1,16 @@
<script>
-import { GlLink, GlTableLite, GlDropdownItem, GlDropdown, GlIcon, GlButton } from '@gitlab/ui';
+import { GlLink, GlTable, GlDropdownItem, GlDropdown, GlButton, GlFormCheckbox } from '@gitlab/ui';
import { last } from 'lodash';
import { numberToHumanSize } from '~/lib/utils/number_utils';
-import { __ } from '~/locale';
+import { __, s__ } from '~/locale';
import FileSha from '~/packages_and_registries/package_registry/components/details/file_sha.vue';
import Tracking from '~/tracking';
import { packageTypeToTrackCategory } from '~/packages_and_registries/package_registry/utils';
import FileIcon from '~/vue_shared/components/file_icon.vue';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import {
+ REQUEST_DELETE_SELECTED_PACKAGE_FILE_TRACKING_ACTION,
+ SELECT_PACKAGE_FILE_TRACKING_ACTION,
TRACKING_LABEL_PACKAGE_ASSET,
TRACKING_ACTION_EXPAND_PACKAGE_ASSET,
} from '~/packages_and_registries/package_registry/constants';
@@ -17,10 +19,10 @@ export default {
name: 'PackageFiles',
components: {
GlLink,
- GlTableLite,
- GlIcon,
+ GlTable,
GlDropdown,
GlDropdownItem,
+ GlFormCheckbox,
GlButton,
FileIcon,
TimeAgoTooltip,
@@ -33,13 +35,29 @@ export default {
required: false,
default: false,
},
+ isLoading: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
packageFiles: {
type: Array,
required: false,
default: () => [],
},
},
+ data() {
+ return {
+ selectedReferences: [],
+ };
+ },
computed: {
+ areFilesSelected() {
+ return this.selectedReferences.length > 0;
+ },
+ areAllFilesSelected() {
+ return this.packageFiles.every(this.isSelected);
+ },
filesTableRows() {
return this.packageFiles.map((pf) => ({
...pf,
@@ -47,6 +65,9 @@ export default {
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
@@ -55,6 +76,12 @@ export default {
filesTableHeaderFields() {
return [
{
+ key: 'checkbox',
+ label: __('Select all'),
+ class: 'gl-w-4',
+ hide: !this.canDelete,
+ },
+ {
key: 'name',
label: __('Name'),
},
@@ -77,7 +104,7 @@ export default {
label: '',
hide: !this.canDelete,
class: 'gl-text-right',
- tdClass: 'gl-w-4',
+ tdClass: 'gl-w-4 gl-pt-3!',
},
].filter((c) => !c.hide);
},
@@ -99,21 +126,71 @@ export default {
this.track(TRACKING_ACTION_EXPAND_PACKAGE_ASSET, { label: TRACKING_LABEL_PACKAGE_ASSET });
}
},
+ updateSelectedReferences(selection) {
+ this.track(SELECT_PACKAGE_FILE_TRACKING_ACTION);
+ this.selectedReferences = selection;
+ },
+ isSelected(packageFile) {
+ return this.selectedReferences.find((reference) => reference.id === packageFile.id);
+ },
+ handleFileDeleteSelected() {
+ this.track(REQUEST_DELETE_SELECTED_PACKAGE_FILE_TRACKING_ACTION);
+ this.$emit('delete-files', this.selectedReferences);
+ },
},
i18n: {
deleteFile: __('Delete file'),
+ deleteSelected: s__('PackageRegistry|Delete selected'),
+ moreActionsText: __('More actions'),
},
};
</script>
<template>
- <div>
- <h3 class="gl-font-lg gl-mt-5">{{ __('Files') }}</h3>
- <gl-table-lite
+ <div class="gl-pt-6">
+ <div class="gl-display-flex gl-align-items-center gl-justify-content-space-between">
+ <h3 class="gl-font-lg gl-mt-5">{{ __('Files') }}</h3>
+ <gl-button
+ v-if="canDelete"
+ :disabled="isLoading || !areFilesSelected"
+ category="secondary"
+ variant="danger"
+ data-testid="delete-selected"
+ @click="handleFileDeleteSelected"
+ >
+ {{ $options.i18n.deleteSelected }}
+ </gl-button>
+ </div>
+ <gl-table
:fields="filesTableHeaderFields"
:items="filesTableRows"
+ show-empty
+ selectable
+ select-mode="multi"
+ selected-variant="primary"
:tbody-tr-attr="{ 'data-testid': 'file-row' }"
+ @row-selected="updateSelectedReferences"
>
+ <template #head(checkbox)="{ selectAllRows, clearSelected }">
+ <gl-form-checkbox
+ v-if="canDelete"
+ data-testid="package-files-checkbox-all"
+ :checked="areAllFilesSelected"
+ :indeterminate="hasSelectedSomeFiles"
+ @change="areAllFilesSelected ? clearSelected() : selectAllRows()"
+ />
+ </template>
+
+ <template #cell(checkbox)="{ rowSelected, selectRow, unselectRow }">
+ <gl-form-checkbox
+ v-if="canDelete"
+ class="gl-mt-1"
+ :checked="rowSelected"
+ data-testid="package-files-checkbox"
+ @change="rowSelected ? unselectRow() : selectRow()"
+ />
+ </template>
+
<template #cell(name)="{ item, toggleDetails, detailsShowing }">
<gl-button
v-if="hasDetails(item)"
@@ -156,11 +233,15 @@ 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)">
+ <gl-dropdown
+ category="tertiary"
+ icon="ellipsis_v"
+ :text-sr-only="true"
+ :text="$options.i18n.moreActionsText"
+ no-caret
+ right
+ >
+ <gl-dropdown-item data-testid="delete-file" @click="$emit('delete-files', [item])">
{{ $options.i18n.deleteFile }}
</gl-dropdown-item>
</gl-dropdown>
@@ -180,6 +261,6 @@ export default {
<file-sha v-if="item.fileSha1" data-testid="sha-1" title="SHA-1" :sha="item.fileSha1" />
</div>
</template>
- </gl-table-lite>
+ </gl-table>
</div>
</template>
diff --git a/app/assets/javascripts/packages_and_registries/package_registry/components/details/package_history.vue b/app/assets/javascripts/packages_and_registries/package_registry/components/details/package_history.vue
index 96b82a20364..a1fc7563de1 100644
--- a/app/assets/javascripts/packages_and_registries/package_registry/components/details/package_history.vue
+++ b/app/assets/javascripts/packages_and_registries/package_registry/components/details/package_history.vue
@@ -5,12 +5,17 @@ import { first } from 'lodash';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import { truncateSha } from '~/lib/utils/text_utility';
import { s__, n__ } from '~/locale';
+import Tracking from '~/tracking';
+import { packageTypeToTrackCategory } from '~/packages_and_registries/package_registry/utils';
import { HISTORY_PIPELINES_LIMIT } from '~/packages_and_registries/shared/constants';
import HistoryItem from '~/vue_shared/components/registry/history_item.vue';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import {
GRAPHQL_PACKAGE_PIPELINES_PAGE_SIZE,
FETCH_PACKAGE_PIPELINES_ERROR_MESSAGE,
+ TRACKING_ACTION_CLICK_PIPELINE_LINK,
+ TRACKING_ACTION_CLICK_COMMIT_LINK,
+ TRACKING_LABEL_PACKAGE_HISTORY,
} from '../../constants';
import getPackagePipelinesQuery from '../../graphql/queries/get_package_pipelines.query.graphql';
import PackageHistoryLoader from './package_history_loader.vue';
@@ -37,6 +42,9 @@ export default {
PackageHistoryLoader,
TimeAgoTooltip,
},
+ mixins: [Tracking.mixin()],
+ TRACKING_ACTION_CLICK_PIPELINE_LINK,
+ TRACKING_ACTION_CLICK_COMMIT_LINK,
props: {
packageEntity: {
type: Object,
@@ -97,6 +105,11 @@ export default {
first: GRAPHQL_PACKAGE_PIPELINES_PAGE_SIZE,
};
},
+ tracking() {
+ return {
+ category: packageTypeToTrackCategory(this.packageType),
+ };
+ },
},
methods: {
truncate(value) {
@@ -105,6 +118,12 @@ export default {
convertToBaseId(value) {
return getIdFromGraphQLId(value);
},
+ trackPipelineClick() {
+ this.track(TRACKING_ACTION_CLICK_PIPELINE_LINK, { label: TRACKING_LABEL_PACKAGE_HISTORY });
+ },
+ trackCommitClick() {
+ this.track(TRACKING_ACTION_CLICK_COMMIT_LINK, { label: TRACKING_LABEL_PACKAGE_HISTORY });
+ },
},
};
</script>
@@ -140,7 +159,9 @@ export default {
<history-item icon="commit" data-testid="first-pipeline-commit">
<gl-sprintf :message="$options.i18n.createdByCommitText">
<template #link>
- <gl-link :href="firstPipeline.commitPath">#{{ truncate(firstPipeline.sha) }}</gl-link>
+ <gl-link :href="firstPipeline.commitPath" @click="trackCommitClick"
+ >#{{ truncate(firstPipeline.sha) }}</gl-link
+ >
</template>
<template #branch>
<strong>{{ firstPipeline.ref }}</strong>
@@ -150,7 +171,9 @@ export default {
<history-item icon="pipeline" data-testid="first-pipeline-pipeline">
<gl-sprintf :message="$options.i18n.createdByPipelineText">
<template #link>
- <gl-link :href="firstPipeline.path">#{{ convertToBaseId(firstPipeline.id) }}</gl-link>
+ <gl-link :href="firstPipeline.path" @click="trackPipelineClick"
+ >#{{ convertToBaseId(firstPipeline.id) }}</gl-link
+ >
</template>
<template #datetime>
<time-ago-tooltip :time="firstPipeline.createdAt" />
@@ -189,13 +212,17 @@ export default {
>
<gl-sprintf :message="$options.i18n.combinedUpdateText">
<template #link>
- <gl-link :href="pipeline.commitPath">#{{ truncate(pipeline.sha) }}</gl-link>
+ <gl-link :href="pipeline.commitPath" @click="trackCommitClick"
+ >#{{ truncate(pipeline.sha) }}</gl-link
+ >
</template>
<template #branch>
<strong>{{ pipeline.ref }}</strong>
</template>
<template #pipeline>
- <gl-link :href="pipeline.path">#{{ convertToBaseId(pipeline.id) }}</gl-link>
+ <gl-link :href="pipeline.path" @click="trackPipelineClick"
+ >#{{ convertToBaseId(pipeline.id) }}</gl-link
+ >
</template>
<template #datetime>
<time-ago-tooltip :time="pipeline.createdAt" />
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 f5946797626..11fd0db3106 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
@@ -23,6 +23,7 @@ export default {
directives: {
GlResizeObserver: GlResizeObserverDirective,
},
+ inject: ['isGroupPage'],
i18n: {
packageInfo: __('v%{version} published %{timeAgo}'),
},
@@ -65,9 +66,6 @@ export default {
this.checkBreakpoints();
},
methods: {
- dynamicSlotName(index) {
- return `metadata-tag${index}`;
- },
checkBreakpoints() {
this.isDesktop = GlBreakpointInstance.isDesktop();
},
@@ -83,21 +81,38 @@ export default {
data-qa-selector="package_title"
>
<template #sub-header>
- <span data-testid="sub-header">
+ <div data-testid="sub-header" class="gl-display-flex gl-gap-3">
<gl-sprintf :message="$options.i18n.packageInfo">
<template #version>
{{ packageEntity.version }}
</template>
<template #timeAgo>
- <time-ago-tooltip
- v-if="packageEntity.createdAt"
- class="gl-ml-2"
- :time="packageEntity.createdAt"
- />
+ <time-ago-tooltip v-if="packageEntity.createdAt" :time="packageEntity.createdAt" />
</template>
</gl-sprintf>
- </span>
+
+ <package-tags
+ v-if="isDesktop && hasTagsToDisplay"
+ :tag-display-limit="2"
+ :tags="packageEntity.tags.nodes"
+ hide-label
+ />
+
+ <!-- we need to duplicate the package tags on mobile to ensure proper styling inside the flex wrap -->
+ <template v-else-if="hasTagsToDisplay">
+ <gl-badge
+ v-for="(tag, index) in packageEntity.tags.nodes"
+ :key="index"
+ class="gl-my-1"
+ data-testid="tag-badge"
+ variant="info"
+ size="sm"
+ >
+ {{ tag.name }}
+ </gl-badge>
+ </template>
+ </div>
</template>
<template v-if="packageTypeDisplay" #metadata-type>
@@ -108,7 +123,7 @@ export default {
<metadata-item data-testid="package-size" icon="disk" :text="totalSize" />
</template>
- <template v-if="packagePipeline" #metadata-pipeline>
+ <template v-if="isGroupPage && packagePipeline" #metadata-pipeline>
<metadata-item
data-testid="pipeline-project"
icon="review-list"
@@ -121,21 +136,6 @@ export default {
<metadata-item data-testid="package-ref" icon="branch" :text="packagePipeline.ref" />
</template>
- <template v-if="isDesktop && hasTagsToDisplay" #metadata-tags>
- <package-tags :tag-display-limit="2" :tags="packageEntity.tags.nodes" hide-label />
- </template>
-
- <!-- we need to duplicate the package tags on mobile to ensure proper styling inside the flex wrap -->
- <template
- v-for="(tag, index) in packageEntity.tags.nodes"
- v-else-if="hasTagsToDisplay"
- #[dynamicSlotName(index)]
- >
- <gl-badge :key="index" class="gl-my-1" data-testid="tag-badge" variant="info" size="sm">
- {{ tag.name }}
- </gl-badge>
- </template>
-
<template #right-actions>
<slot name="delete-button"></slot>
</template>
diff --git a/app/assets/javascripts/packages_and_registries/package_registry/components/details/pypi_installation.vue b/app/assets/javascripts/packages_and_registries/package_registry/components/details/pypi_installation.vue
index a126d30f1ec..dd58f28a262 100644
--- a/app/assets/javascripts/packages_and_registries/package_registry/components/details/pypi_installation.vue
+++ b/app/assets/javascripts/packages_and_registries/package_registry/components/details/pypi_installation.vue
@@ -1,9 +1,10 @@
<script>
-import { GlLink, GlSprintf } from '@gitlab/ui';
+import { GlFormGroup, GlLink, GlSprintf } from '@gitlab/ui';
import { s__ } from '~/locale';
import InstallationTitle from '~/packages_and_registries/package_registry/components/details/installation_title.vue';
import {
+ PERSONAL_ACCESS_TOKEN_HELP_URL,
TRACKING_ACTION_COPY_PIP_INSTALL_COMMAND,
TRACKING_ACTION_COPY_PYPI_SETUP_COMMAND,
TRACKING_LABEL_CODE_INSTRUCTION,
@@ -16,6 +17,7 @@ export default {
components: {
InstallationTitle,
CodeInstruction,
+ GlFormGroup,
GlLink,
GlSprintf,
},
@@ -43,6 +45,7 @@ password = <your personal access token>`;
TRACKING_LABEL_CODE_INSTRUCTION,
},
i18n: {
+ tokenText: s__(`PackageRegistry|You will need a %{linkStart}personal access token%{linkEnd}.`),
setupText: s__(
`PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file.`,
),
@@ -50,7 +53,10 @@ password = <your personal access token>`;
'PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}.',
),
},
- links: { PYPI_HELP_PATH },
+ links: {
+ PERSONAL_ACCESS_TOKEN_HELP_URL,
+ PYPI_HELP_PATH,
+ },
installOptions: [{ value: 'pypi', label: s__('PackageRegistry|Show PyPi commands') }],
};
</script>
@@ -59,14 +65,28 @@ password = <your personal access token>`;
<div>
<installation-title package-type="pypi" :options="$options.installOptions" />
- <code-instruction
- :label="s__('PackageRegistry|Pip Command')"
- :instruction="pypiPipCommand"
- :copy-text="s__('PackageRegistry|Copy Pip command')"
- data-testid="pip-command"
- :tracking-action="$options.tracking.TRACKING_ACTION_COPY_PIP_INSTALL_COMMAND"
- :tracking-label="$options.tracking.TRACKING_LABEL_CODE_INSTRUCTION"
- />
+ <gl-form-group id="installation-pip-command-group">
+ <code-instruction
+ id="installation-pip-command"
+ :label="s__('PackageRegistry|Pip Command')"
+ :instruction="pypiPipCommand"
+ :copy-text="s__('PackageRegistry|Copy Pip command')"
+ data-testid="pip-command"
+ :tracking-action="$options.tracking.TRACKING_ACTION_COPY_PIP_INSTALL_COMMAND"
+ :tracking-label="$options.tracking.TRACKING_LABEL_CODE_INSTRUCTION"
+ />
+ <template #description>
+ <gl-sprintf :message="$options.i18n.tokenText">
+ <template #link="{ content }">
+ <gl-link
+ :href="$options.links.PERSONAL_ACCESS_TOKEN_HELP_URL"
+ data-testid="access-token-link"
+ >{{ content }}</gl-link
+ >
+ </template>
+ </gl-sprintf>
+ </template>
+ </gl-form-group>
<h3 class="gl-font-lg">{{ __('Registry setup') }}</h3>
<p>
@@ -87,7 +107,12 @@ password = <your personal access token>`;
/>
<gl-sprintf :message="$options.i18n.helpText">
<template #link="{ content }">
- <gl-link :href="$options.links.PYPI_HELP_PATH" target="_blank">{{ content }}</gl-link>
+ <gl-link
+ :href="$options.links.PYPI_HELP_PATH"
+ target="_blank"
+ data-testid="pypi-docs-link"
+ >{{ content }}</gl-link
+ >
</template>
</gl-sprintf>
</div>
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 cea053992f8..5b2a347a4ee 100644
--- a/app/assets/javascripts/packages_and_registries/package_registry/constants.js
+++ b/app/assets/javascripts/packages_and_registries/package_registry/constants.js
@@ -7,9 +7,12 @@ export {
CANCEL_DELETE_PACKAGE_TRACKING_ACTION,
PULL_PACKAGE_TRACKING_ACTION,
DELETE_PACKAGE_FILE_TRACKING_ACTION,
+ DELETE_PACKAGE_FILES_TRACKING_ACTION,
REQUEST_DELETE_PACKAGE_FILE_TRACKING_ACTION,
+ REQUEST_DELETE_SELECTED_PACKAGE_FILE_TRACKING_ACTION,
CANCEL_DELETE_PACKAGE_FILE_TRACKING_ACTION,
DOWNLOAD_PACKAGE_ASSET_TRACKING_ACTION,
+ SELECT_PACKAGE_FILE_TRACKING_ACTION,
} from '~/packages_and_registries/shared/constants';
export const PACKAGE_TYPE_CONAN = 'CONAN';
@@ -69,6 +72,11 @@ export const TRACKING_ACTION_DOWNLOAD_PACKAGE_ASSET = 'download_package_asset';
export const TRACKING_ACTION_EXPAND_PACKAGE_ASSET = 'expand_package_asset';
export const TRACKING_ACTION_COPY_PACKAGE_ASSET_SHA = 'copy_package_asset_sha';
+export const TRACKING_ACTION_CLICK_PIPELINE_LINK = 'click_pipeline_link_from_package';
+export const TRACKING_ACTION_CLICK_COMMIT_LINK = 'click_commit_link_from_package';
+
+export const TRACKING_LABEL_PACKAGE_HISTORY = 'package_history';
+
export const SHOW_DELETE_SUCCESS_ALERT = 'showSuccessDeleteAlert';
export const DELETE_PACKAGE_FILE_ERROR_MESSAGE = s__(
'PackageRegistry|Something went wrong while deleting the package file.',
@@ -76,6 +84,12 @@ export const DELETE_PACKAGE_FILE_ERROR_MESSAGE = s__(
export const DELETE_PACKAGE_FILE_SUCCESS_MESSAGE = s__(
'PackageRegistry|Package file deleted successfully',
);
+export const DELETE_PACKAGE_FILES_ERROR_MESSAGE = s__(
+ 'PackageRegistry|Something went wrong while deleting the package assets.',
+);
+export const DELETE_PACKAGE_FILES_SUCCESS_MESSAGE = s__(
+ 'PackageRegistry|Package assets deleted successfully',
+);
export const FETCH_PACKAGE_DETAILS_ERROR_MESSAGE = s__(
'PackageRegistry|Failed to load the package data',
);
@@ -162,5 +176,6 @@ export const CONAN_HELP_PATH = helpPagePath('user/packages/conan_repository/inde
export const NUGET_HELP_PATH = helpPagePath('user/packages/nuget_repository/index');
export const PYPI_HELP_PATH = helpPagePath('user/packages/pypi_repository/index');
export const COMPOSER_HELP_PATH = helpPagePath('user/packages/composer_repository/index');
+export const PERSONAL_ACCESS_TOKEN_HELP_URL = helpPagePath('user/profile/personal_access_tokens');
export const GRAPHQL_PACKAGE_PIPELINES_PAGE_SIZE = 10;
diff --git a/app/assets/javascripts/packages_and_registries/package_registry/graphql/mutations/destroy_package_file.mutation.graphql b/app/assets/javascripts/packages_and_registries/package_registry/graphql/mutations/destroy_package_file.mutation.graphql
deleted file mode 100644
index f016640f57d..00000000000
--- a/app/assets/javascripts/packages_and_registries/package_registry/graphql/mutations/destroy_package_file.mutation.graphql
+++ /dev/null
@@ -1,5 +0,0 @@
-mutation destroyPackageFile($id: PackagesPackageFileID!) {
- destroyPackageFile(input: { id: $id }) {
- errors
- }
-}
diff --git a/app/assets/javascripts/packages_and_registries/package_registry/graphql/mutations/destroy_package_files.mutation.graphql b/app/assets/javascripts/packages_and_registries/package_registry/graphql/mutations/destroy_package_files.mutation.graphql
new file mode 100644
index 00000000000..8f9a3156492
--- /dev/null
+++ b/app/assets/javascripts/packages_and_registries/package_registry/graphql/mutations/destroy_package_files.mutation.graphql
@@ -0,0 +1,5 @@
+mutation destroyPackageFiles($projectPath: ID!, $ids: [PackagesPackageFileID!]!) {
+ destroyPackageFiles(input: { projectPath: $projectPath, ids: $ids }) {
+ errors
+ }
+}
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 5574020c9e4..f3f0d096d10 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
@@ -20,6 +20,7 @@ query getPackageDetails($id: PackagesPackageID!) {
id
path
name
+ fullPath
}
tags(first: 10) {
nodes {
@@ -39,6 +40,9 @@ query getPackageDetails($id: PackagesPackageID!) {
}
}
packageFiles(first: 100) {
+ pageInfo {
+ hasNextPage
+ }
nodes {
id
fileMd5
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 29438fba86b..e83962bb608 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
@@ -34,16 +34,19 @@ import {
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,
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,
DOWNLOAD_PACKAGE_ASSET_TRACKING_ACTION,
} from '~/packages_and_registries/package_registry/constants';
-import destroyPackageFileMutation from '~/packages_and_registries/package_registry/graphql/mutations/destroy_package_file.mutation.graphql';
+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 Tracking from '~/tracking';
@@ -83,7 +86,8 @@ export default {
},
data() {
return {
- fileToDelete: null,
+ filesToDelete: [],
+ mutationLoading: false,
packageEntity: {},
};
},
@@ -114,6 +118,9 @@ export default {
projectName() {
return this.packageEntity.project?.name;
},
+ projectPath() {
+ return this.packageEntity.project?.fullPath;
+ },
packageId() {
return this.$route.params.id;
},
@@ -131,6 +138,9 @@ export default {
isLoading() {
return this.$apollo.queries.packageEntity.loading;
},
+ packageFilesLoading() {
+ return this.isLoading || this.mutationLoading;
+ },
isValidPackage() {
return this.isLoading || Boolean(this.packageEntity.name);
},
@@ -175,12 +185,14 @@ export default {
window.location.replace(`${returnTo}?${modalQuery}`);
},
- async deletePackageFile(id) {
+ async deletePackageFiles(ids) {
+ this.mutationLoading = true;
try {
const { data } = await this.$apollo.mutate({
- mutation: destroyPackageFileMutation,
+ mutation: destroyPackageFilesMutation,
variables: {
- id,
+ projectPath: this.projectPath,
+ ids,
},
awaitRefetchQueries: true,
refetchQueries: [
@@ -190,31 +202,53 @@ export default {
},
],
});
- if (data?.destroyPackageFile?.errors[0]) {
- throw data.destroyPackageFile.errors[0];
+ if (data?.destroyPackageFiles?.errors[0]) {
+ throw data.destroyPackageFiles.errors[0];
}
createFlash({
- message: DELETE_PACKAGE_FILE_SUCCESS_MESSAGE,
+ message:
+ ids.length === 1
+ ? DELETE_PACKAGE_FILE_SUCCESS_MESSAGE
+ : DELETE_PACKAGE_FILES_SUCCESS_MESSAGE,
type: 'success',
});
} catch (error) {
createFlash({
- message: DELETE_PACKAGE_FILE_ERROR_MESSAGE,
+ message:
+ ids.length === 1
+ ? DELETE_PACKAGE_FILE_ERROR_MESSAGE
+ : DELETE_PACKAGE_FILES_ERROR_MESSAGE,
type: 'warning',
captureError: true,
error,
});
}
+ this.mutationLoading = false;
},
- handleFileDelete(file) {
+ handleFileDelete(files) {
this.track(REQUEST_DELETE_PACKAGE_FILE_TRACKING_ACTION);
- this.fileToDelete = { ...file };
- this.$refs.deleteFileModal.show();
+ if (
+ files.length === this.packageFiles.length &&
+ !this.packageEntity.packageFiles?.pageInfo?.hasNextPage
+ ) {
+ this.$refs.deleteModal.show();
+ } else {
+ this.filesToDelete = files;
+ if (files.length === 1) {
+ this.$refs.deleteFileModal.show();
+ } else if (files.length > 1) {
+ this.$refs.deleteFilesModal.show();
+ }
+ }
},
- confirmFileDelete() {
- this.track(DELETE_PACKAGE_FILE_TRACKING_ACTION);
- this.deletePackageFile(this.fileToDelete.id);
- this.fileToDelete = null;
+ confirmFilesDelete() {
+ if (this.filesToDelete.length === 1) {
+ 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 = [];
},
},
i18n: {
@@ -240,6 +274,10 @@ export default {
text: __('Delete'),
attributes: [{ variant: 'danger' }, { category: 'primary' }],
},
+ filesDeletePrimaryAction: {
+ text: s__('PackageRegistry|Permanently delete assets'),
+ attributes: [{ variant: 'danger' }, { category: 'primary' }],
+ },
cancelAction: {
text: __('Cancel'),
},
@@ -287,9 +325,10 @@ export default {
<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-file="handleFileDelete"
+ @delete-files="handleFileDelete"
/>
</gl-tab>
@@ -355,15 +394,43 @@ export default {
:action-primary="$options.modal.fileDeletePrimaryAction"
:action-cancel="$options.modal.cancelAction"
data-testid="delete-file-modal"
- @primary="confirmFileDelete"
+ @primary="confirmFilesDelete"
@canceled="track($options.trackingActions.CANCEL_DELETE_PACKAGE_FILE)"
>
<template #modal-title>{{ $options.i18n.deleteFileModalTitle }}</template>
- <gl-sprintf v-if="fileToDelete" :message="$options.i18n.deleteFileModalContent">
+ <gl-sprintf v-if="filesToDelete.length === 1" :message="$options.i18n.deleteFileModalContent">
<template #filename>
- <strong>{{ fileToDelete.file_name }}</strong>
+ <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/settings/project/components/container_expiration_policy.vue b/app/assets/javascripts/packages_and_registries/settings/project/components/container_expiration_policy.vue
index 90a18d5cf5a..1c44d2bc38b 100644
--- a/app/assets/javascripts/packages_and_registries/settings/project/components/container_expiration_policy.vue
+++ b/app/assets/javascripts/packages_and_registries/settings/project/components/container_expiration_policy.vue
@@ -11,7 +11,7 @@ import {
UNAVAILABLE_ADMIN_FEATURE_TEXT,
} from '~/packages_and_registries/settings/project/constants';
import expirationPolicyQuery from '~/packages_and_registries/settings/project/graphql/queries/get_expiration_policy.query.graphql';
-import SettingsBlock from '~/vue_shared/components/settings/settings_block.vue';
+import SettingsBlock from '~/packages_and_registries/shared/components/settings_block.vue';
import ContainerExpirationPolicyForm from './container_expiration_policy_form.vue';
diff --git a/app/assets/javascripts/packages_and_registries/settings/project/components/expiration_dropdown.vue b/app/assets/javascripts/packages_and_registries/settings/project/components/expiration_dropdown.vue
index 7682754fdcb..f06e3a41bd0 100644
--- a/app/assets/javascripts/packages_and_registries/settings/project/components/expiration_dropdown.vue
+++ b/app/assets/javascripts/packages_and_registries/settings/project/components/expiration_dropdown.vue
@@ -35,22 +35,34 @@ export default {
required: false,
default: '',
},
+ dropdownClass: {
+ type: String,
+ required: false,
+ default: '',
+ },
},
};
</script>
<template>
<gl-form-group :id="`${name}-form-group`" :label-for="name" :label="label">
- <gl-form-select :id="name" :value="value" :disabled="disabled" @input="$emit('input', $event)">
- <option
- v-for="option in formOptions"
- :key="option.key"
- :value="option.key"
- data-testid="option"
+ <div :class="dropdownClass">
+ <gl-form-select
+ :id="name"
+ :value="value"
+ :disabled="disabled"
+ @input="$emit('input', $event)"
>
- {{ option.label }}
- </option>
- </gl-form-select>
+ <option
+ v-for="option in formOptions"
+ :key="option.key"
+ :value="option.key"
+ data-testid="option"
+ >
+ {{ option.label }}
+ </option>
+ </gl-form-select>
+ </div>
<template v-if="description" #description>
<span data-testid="description" class="gl-text-gray-400">
{{ description }}
diff --git a/app/assets/javascripts/packages_and_registries/settings/project/components/packages_cleanup_policy.vue b/app/assets/javascripts/packages_and_registries/settings/project/components/packages_cleanup_policy.vue
index 1170407a349..2f4bc35e5f7 100644
--- a/app/assets/javascripts/packages_and_registries/settings/project/components/packages_cleanup_policy.vue
+++ b/app/assets/javascripts/packages_and_registries/settings/project/components/packages_cleanup_policy.vue
@@ -6,7 +6,7 @@ import {
PACKAGES_CLEANUP_POLICY_DESCRIPTION,
} from '~/packages_and_registries/settings/project/constants';
import packagesCleanupPolicyQuery from '~/packages_and_registries/settings/project/graphql/queries/get_packages_cleanup_policy.query.graphql';
-import SettingsBlock from '~/vue_shared/components/settings/settings_block.vue';
+import SettingsBlock from '~/packages_and_registries/shared/components/settings_block.vue';
import PackagesCleanupPolicyForm from './packages_cleanup_policy_form.vue';
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 b1751d5174a..f1f0b970b15 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
@@ -3,10 +3,10 @@ import { GlButton } from '@gitlab/ui';
import {
UPDATE_SETTINGS_ERROR_MESSAGE,
UPDATE_SETTINGS_SUCCESS_MESSAGE,
- SET_CLEANUP_POLICY_BUTTON,
KEEP_N_DUPLICATED_PACKAGE_FILES_DESCRIPTION,
KEEP_N_DUPLICATED_PACKAGE_FILES_FIELDNAME,
KEEP_N_DUPLICATED_PACKAGE_FILES_LABEL,
+ SET_CLEANUP_POLICY_BUTTON,
} from '~/packages_and_registries/settings/project/constants';
import updatePackagesCleanupPolicyMutation from '~/packages_and_registries/settings/project/graphql/mutations/update_packages_cleanup_policy.mutation.graphql';
import { formOptionsGenerator } from '~/packages_and_registries/settings/project/utils';
@@ -108,18 +108,17 @@ export default {
<template>
<form ref="form-element" @submit.prevent="submit">
- <div class="gl-md-max-w-50p">
- <expiration-dropdown
- v-model="prefilledForm.keepNDuplicatedPackageFiles"
- :disabled="isFieldDisabled"
- :form-options="$options.formOptions.keepNDuplicatedPackageFiles"
- :label="$options.i18n.KEEP_N_DUPLICATED_PACKAGE_FILES_LABEL"
- :description="$options.i18n.KEEP_N_DUPLICATED_PACKAGE_FILES_DESCRIPTION"
- name="keep-n-duplicated-package-files"
- data-testid="keep-n-duplicated-package-files-dropdown"
- @input="onModelChange($event, 'keepNDuplicatedPackageFiles')"
- />
- </div>
+ <expiration-dropdown
+ :value="prefilledForm.keepNDuplicatedPackageFiles"
+ :disabled="isFieldDisabled"
+ :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"
+ name="keep-n-duplicated-package-files"
+ data-testid="keep-n-duplicated-package-files-dropdown"
+ @input="onModelChange($event, 'keepNDuplicatedPackageFiles')"
+ />
<div class="gl-mt-7 gl-display-flex gl-align-items-center">
<gl-button
data-testid="save-button"
diff --git a/app/assets/javascripts/packages_and_registries/settings/project/constants.js b/app/assets/javascripts/packages_and_registries/settings/project/constants.js
index 948520151ce..fcb4a8ee297 100644
--- a/app/assets/javascripts/packages_and_registries/settings/project/constants.js
+++ b/app/assets/javascripts/packages_and_registries/settings/project/constants.js
@@ -4,7 +4,7 @@ export const CONTAINER_CLEANUP_POLICY_TITLE = s__(`ContainerRegistry|Clean up im
export const CONTAINER_CLEANUP_POLICY_DESCRIPTION = s__(
`ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}`,
);
-export const SET_CLEANUP_POLICY_BUTTON = __('Save');
+export const SET_CLEANUP_POLICY_BUTTON = __('Save changes');
export const UNAVAILABLE_FEATURE_TITLE = s__(
`ContainerRegistry|Cleanup policy for tags is disabled`,
);
diff --git a/app/assets/javascripts/packages_and_registries/shared/components/settings_block.vue b/app/assets/javascripts/packages_and_registries/shared/components/settings_block.vue
index 5caf95cd050..0458b914b58 100644
--- a/app/assets/javascripts/packages_and_registries/shared/components/settings_block.vue
+++ b/app/assets/javascripts/packages_and_registries/shared/components/settings_block.vue
@@ -1,7 +1,7 @@
<template>
<section class="settings gl-py-7">
- <div class="gl-lg-display-flex">
- <div class="gl-lg-w-half gl-pr-10">
+ <div class="gl-lg-display-flex gl-gap-6">
+ <div class="gl-lg-w-40p gl-pr-10 gl-flex-shrink-0">
<h4>
<slot name="title"></slot>
</h4>
@@ -9,7 +9,7 @@
<slot name="description"></slot>
</p>
</div>
- <div class="gl-lg-w-half gl-pt-3">
+ <div class="gl-pt-3 gl-flex-grow-1">
<slot></slot>
</div>
</div>
diff --git a/app/assets/javascripts/packages_and_registries/shared/constants/package_registry.js b/app/assets/javascripts/packages_and_registries/shared/constants/package_registry.js
index 5505205cf33..6744e821565 100644
--- a/app/assets/javascripts/packages_and_registries/shared/constants/package_registry.js
+++ b/app/assets/javascripts/packages_and_registries/shared/constants/package_registry.js
@@ -9,7 +9,11 @@ export const REQUEST_DELETE_PACKAGE_TRACKING_ACTION = 'request_delete_package';
export const CANCEL_DELETE_PACKAGE_TRACKING_ACTION = 'cancel_delete_package';
export const PULL_PACKAGE_TRACKING_ACTION = 'pull_package';
export const DELETE_PACKAGE_FILE_TRACKING_ACTION = 'delete_package_file';
+export const DELETE_PACKAGE_FILES_TRACKING_ACTION = 'delete_package_files';
+export const SELECT_PACKAGE_FILE_TRACKING_ACTION = 'select_package_file';
export const REQUEST_DELETE_PACKAGE_FILE_TRACKING_ACTION = 'request_delete_package_file';
+export const REQUEST_DELETE_SELECTED_PACKAGE_FILE_TRACKING_ACTION =
+ 'request_delete_selected_package_file';
export const CANCEL_DELETE_PACKAGE_FILE_TRACKING_ACTION = 'cancel_delete_package_file';
export const DOWNLOAD_PACKAGE_ASSET_TRACKING_ACTION = 'download_package_asset';