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/registry')
-rw-r--r--app/assets/javascripts/registry/explorer/components/delete_button.vue56
-rw-r--r--app/assets/javascripts/registry/explorer/components/delete_image.vue75
-rw-r--r--app/assets/javascripts/registry/explorer/components/details_page/delete_alert.vue70
-rw-r--r--app/assets/javascripts/registry/explorer/components/details_page/delete_modal.vue109
-rw-r--r--app/assets/javascripts/registry/explorer/components/details_page/details_header.vue164
-rw-r--r--app/assets/javascripts/registry/explorer/components/details_page/empty_state.vue44
-rw-r--r--app/assets/javascripts/registry/explorer/components/details_page/partial_cleanup_alert.vue38
-rw-r--r--app/assets/javascripts/registry/explorer/components/details_page/status_alert.vue50
-rw-r--r--app/assets/javascripts/registry/explorer/components/details_page/tags_list.vue179
-rw-r--r--app/assets/javascripts/registry/explorer/components/details_page/tags_list_row.vue256
-rw-r--r--app/assets/javascripts/registry/explorer/components/details_page/tags_loader.vue34
-rw-r--r--app/assets/javascripts/registry/explorer/components/list_page/cleanup_status.vue71
-rw-r--r--app/assets/javascripts/registry/explorer/components/list_page/cli_commands.vue71
-rw-r--r--app/assets/javascripts/registry/explorer/components/list_page/group_empty_state.vue35
-rw-r--r--app/assets/javascripts/registry/explorer/components/list_page/image_list.vue54
-rw-r--r--app/assets/javascripts/registry/explorer/components/list_page/image_list_row.vue153
-rw-r--r--app/assets/javascripts/registry/explorer/components/list_page/project_empty_state.vue111
-rw-r--r--app/assets/javascripts/registry/explorer/components/list_page/registry_header.vue110
-rw-r--r--app/assets/javascripts/registry/explorer/components/registry_breadcrumb.vue51
-rw-r--r--app/assets/javascripts/registry/explorer/constants/common.js4
-rw-r--r--app/assets/javascripts/registry/explorer/constants/details.js166
-rw-r--r--app/assets/javascripts/registry/explorer/constants/expiration_policies.js15
-rw-r--r--app/assets/javascripts/registry/explorer/constants/index.js5
-rw-r--r--app/assets/javascripts/registry/explorer/constants/list.js53
-rw-r--r--app/assets/javascripts/registry/explorer/constants/quick_start.js9
-rw-r--r--app/assets/javascripts/registry/explorer/graphql/index.js15
-rw-r--r--app/assets/javascripts/registry/explorer/graphql/mutations/delete_container_repository.mutation.graphql9
-rw-r--r--app/assets/javascripts/registry/explorer/graphql/mutations/delete_container_repository_tags.mutation.graphql5
-rw-r--r--app/assets/javascripts/registry/explorer/graphql/queries/get_container_repositories_details.query.graphql41
-rw-r--r--app/assets/javascripts/registry/explorer/graphql/queries/get_container_repository_details.query.graphql22
-rw-r--r--app/assets/javascripts/registry/explorer/graphql/queries/get_container_repository_tags.query.graphql29
-rw-r--r--app/assets/javascripts/registry/explorer/graphql/queries/get_container_repository_tags_count.query.graphql6
-rw-r--r--app/assets/javascripts/registry/explorer/index.js121
-rw-r--r--app/assets/javascripts/registry/explorer/pages/details.vue233
-rw-r--r--app/assets/javascripts/registry/explorer/pages/index.vue5
-rw-r--r--app/assets/javascripts/registry/explorer/pages/list.vue409
-rw-r--r--app/assets/javascripts/registry/explorer/router.js35
37 files changed, 0 insertions, 2913 deletions
diff --git a/app/assets/javascripts/registry/explorer/components/delete_button.vue b/app/assets/javascripts/registry/explorer/components/delete_button.vue
deleted file mode 100644
index e4a1a1a8266..00000000000
--- a/app/assets/javascripts/registry/explorer/components/delete_button.vue
+++ /dev/null
@@ -1,56 +0,0 @@
-<script>
-import { GlTooltipDirective, GlButton } from '@gitlab/ui';
-
-export default {
- name: 'DeleteButton',
- components: {
- GlButton,
- },
- directives: {
- GlTooltip: GlTooltipDirective,
- },
- props: {
- title: {
- type: String,
- required: true,
- },
- tooltipTitle: {
- type: String,
- required: true,
- },
- disabled: {
- type: Boolean,
- default: false,
- required: false,
- },
- tooltipDisabled: {
- type: Boolean,
- default: false,
- required: false,
- },
- },
- computed: {
- tooltipConfiguration() {
- return {
- disabled: this.tooltipDisabled,
- title: this.tooltipTitle,
- };
- },
- },
-};
-</script>
-
-<template>
- <div v-gl-tooltip="tooltipConfiguration">
- <gl-button
- v-gl-tooltip
- :disabled="disabled"
- :title="title"
- :aria-label="title"
- variant="danger"
- category="secondary"
- icon="remove"
- @click="$emit('delete')"
- />
- </div>
-</template>
diff --git a/app/assets/javascripts/registry/explorer/components/delete_image.vue b/app/assets/javascripts/registry/explorer/components/delete_image.vue
deleted file mode 100644
index a313854f5e4..00000000000
--- a/app/assets/javascripts/registry/explorer/components/delete_image.vue
+++ /dev/null
@@ -1,75 +0,0 @@
-<script>
-import { produce } from 'immer';
-import { GRAPHQL_PAGE_SIZE } from '../constants/index';
-import deleteContainerRepositoryMutation from '../graphql/mutations/delete_container_repository.mutation.graphql';
-import getContainerRepositoryDetailsQuery from '../graphql/queries/get_container_repository_details.query.graphql';
-
-export default {
- props: {
- id: {
- type: String,
- required: false,
- default: null,
- },
- useUpdateFn: {
- type: Boolean,
- required: false,
- default: false,
- },
- },
- methods: {
- updateImageStatus(store, { data: { destroyContainerRepository } }) {
- const variables = {
- id: this.id,
- first: GRAPHQL_PAGE_SIZE,
- };
- const sourceData = store.readQuery({
- query: getContainerRepositoryDetailsQuery,
- variables,
- });
-
- const data = produce(sourceData, (draftState) => {
- draftState.containerRepository.status =
- destroyContainerRepository.containerRepository.status;
- });
-
- store.writeQuery({
- query: getContainerRepositoryDetailsQuery,
- variables,
- data,
- });
- },
- doDelete() {
- this.$emit('start');
- return this.$apollo
- .mutate({
- mutation: deleteContainerRepositoryMutation,
- variables: {
- id: this.id,
- },
- update: this.useUpdateFn ? this.updateImageStatus : undefined,
- })
- .then(({ data }) => {
- if (data?.destroyContainerRepository?.errors[0]) {
- this.$emit('error', data?.destroyContainerRepository?.errors);
- return;
- }
- this.$emit('success');
- })
- .catch((e) => {
- // note: we are adding an array to follow the same format of the error raised above
- this.$emit('error', [e]);
- })
- .finally(() => {
- this.$emit('end');
- });
- },
- },
- render() {
- if (this.$scopedSlots?.default) {
- return this.$scopedSlots.default({ doDelete: this.doDelete });
- }
- return null;
- },
-};
-</script>
diff --git a/app/assets/javascripts/registry/explorer/components/details_page/delete_alert.vue b/app/assets/javascripts/registry/explorer/components/details_page/delete_alert.vue
deleted file mode 100644
index 56d2ff86fb7..00000000000
--- a/app/assets/javascripts/registry/explorer/components/details_page/delete_alert.vue
+++ /dev/null
@@ -1,70 +0,0 @@
-<script>
-import { GlSprintf, GlAlert, GlLink } from '@gitlab/ui';
-
-import { ALERT_MESSAGES, ADMIN_GARBAGE_COLLECTION_TIP } from '../../constants/index';
-
-export default {
- components: {
- GlSprintf,
- GlAlert,
- GlLink,
- },
- model: {
- prop: 'deleteAlertType',
- event: 'change',
- },
- props: {
- deleteAlertType: {
- type: String,
- default: null,
- required: false,
- validator(value) {
- return !value || ALERT_MESSAGES[value] !== undefined;
- },
- },
- garbageCollectionHelpPagePath: { type: String, required: false, default: '' },
- isAdmin: {
- type: Boolean,
- default: false,
- required: false,
- },
- },
- computed: {
- deleteAlertConfig() {
- const config = {
- title: '',
- message: '',
- type: 'success',
- };
- if (this.deleteAlertType) {
- [config.type] = this.deleteAlertType.split('_');
-
- config.message = ALERT_MESSAGES[this.deleteAlertType];
-
- if (this.isAdmin && config.type === 'success') {
- config.title = config.message;
- config.message = ADMIN_GARBAGE_COLLECTION_TIP;
- }
- }
- return config;
- },
- },
-};
-</script>
-
-<template>
- <gl-alert
- v-if="deleteAlertType"
- :variant="deleteAlertConfig.type"
- :title="deleteAlertConfig.title"
- @dismiss="$emit('change', null)"
- >
- <gl-sprintf :message="deleteAlertConfig.message">
- <template #docLink="{ content }">
- <gl-link :href="garbageCollectionHelpPagePath" target="_blank">
- {{ content }}
- </gl-link>
- </template>
- </gl-sprintf>
- </gl-alert>
-</template>
diff --git a/app/assets/javascripts/registry/explorer/components/details_page/delete_modal.vue b/app/assets/javascripts/registry/explorer/components/details_page/delete_modal.vue
deleted file mode 100644
index f857c96c9d1..00000000000
--- a/app/assets/javascripts/registry/explorer/components/details_page/delete_modal.vue
+++ /dev/null
@@ -1,109 +0,0 @@
-<script>
-import { GlModal, GlSprintf, GlFormInput } from '@gitlab/ui';
-import { n__ } from '~/locale';
-import {
- REMOVE_TAG_CONFIRMATION_TEXT,
- REMOVE_TAGS_CONFIRMATION_TEXT,
- DELETE_IMAGE_CONFIRMATION_TITLE,
- DELETE_IMAGE_CONFIRMATION_TEXT,
-} from '../../constants';
-
-export default {
- components: {
- GlModal,
- GlSprintf,
- GlFormInput,
- },
- props: {
- itemsToBeDeleted: {
- type: Array,
- required: false,
- default: () => [],
- },
- deleteImage: {
- type: Boolean,
- default: false,
- required: false,
- },
- },
- data() {
- return {
- projectPath: '',
- };
- },
- computed: {
- imageProjectPath() {
- return this.itemsToBeDeleted[0]?.project?.path;
- },
- modalTitle() {
- if (this.deleteImage) {
- return DELETE_IMAGE_CONFIRMATION_TITLE;
- }
- return n__(
- 'ContainerRegistry|Remove tag',
- 'ContainerRegistry|Remove tags',
- this.itemsToBeDeleted.length,
- );
- },
- modalDescription() {
- if (this.deleteImage) {
- return {
- message: DELETE_IMAGE_CONFIRMATION_TEXT,
- item: this.imageProjectPath,
- };
- }
- if (this.itemsToBeDeleted.length > 1) {
- return {
- message: REMOVE_TAGS_CONFIRMATION_TEXT,
- item: this.itemsToBeDeleted.length,
- };
- }
-
- const [first] = this.itemsToBeDeleted;
- return {
- message: REMOVE_TAG_CONFIRMATION_TEXT,
- item: first?.path,
- };
- },
- disablePrimaryButton() {
- return this.deleteImage && this.projectPath !== this.imageProjectPath;
- },
- },
- methods: {
- show() {
- this.$refs.deleteModal.show();
- },
- },
-};
-</script>
-
-<template>
- <gl-modal
- ref="deleteModal"
- modal-id="delete-tag-modal"
- ok-variant="danger"
- :action-primary="{
- text: __('Delete'),
- attributes: [{ variant: 'danger' }, { disabled: disablePrimaryButton }],
- }"
- :action-cancel="{ text: __('Cancel') }"
- @primary="$emit('confirmDelete')"
- @cancel="$emit('cancelDelete')"
- @change="projectPath = ''"
- >
- <template #modal-title>{{ modalTitle }}</template>
- <p v-if="modalDescription" data-testid="description">
- <gl-sprintf :message="modalDescription.message">
- <template #item>
- <b>{{ modalDescription.item }}</b>
- </template>
- <template #code>
- <code>{{ modalDescription.item }}</code>
- </template>
- </gl-sprintf>
- </p>
- <div v-if="deleteImage">
- <gl-form-input v-model="projectPath" />
- </div>
- </gl-modal>
-</template>
diff --git a/app/assets/javascripts/registry/explorer/components/details_page/details_header.vue b/app/assets/javascripts/registry/explorer/components/details_page/details_header.vue
deleted file mode 100644
index e9e36151fe6..00000000000
--- a/app/assets/javascripts/registry/explorer/components/details_page/details_header.vue
+++ /dev/null
@@ -1,164 +0,0 @@
-<script>
-import { GlIcon, GlTooltipDirective, GlDropdown, GlDropdownItem } from '@gitlab/ui';
-import { sprintf, n__, s__ } from '~/locale';
-import MetadataItem from '~/vue_shared/components/registry/metadata_item.vue';
-import TitleArea from '~/vue_shared/components/registry/title_area.vue';
-import timeagoMixin from '~/vue_shared/mixins/timeago';
-import {
- UPDATED_AT,
- CLEANUP_UNSCHEDULED_TEXT,
- CLEANUP_SCHEDULED_TEXT,
- CLEANUP_ONGOING_TEXT,
- CLEANUP_UNFINISHED_TEXT,
- CLEANUP_DISABLED_TEXT,
- CLEANUP_SCHEDULED_TOOLTIP,
- CLEANUP_ONGOING_TOOLTIP,
- CLEANUP_UNFINISHED_TOOLTIP,
- CLEANUP_DISABLED_TOOLTIP,
- UNFINISHED_STATUS,
- UNSCHEDULED_STATUS,
- SCHEDULED_STATUS,
- ONGOING_STATUS,
- ROOT_IMAGE_TEXT,
- ROOT_IMAGE_TOOLTIP,
-} from '../../constants/index';
-
-import getContainerRepositoryTagsCountQuery from '../../graphql/queries/get_container_repository_tags_count.query.graphql';
-
-export default {
- name: 'DetailsHeader',
- components: { GlIcon, TitleArea, MetadataItem, GlDropdown, GlDropdownItem },
- directives: {
- GlTooltip: GlTooltipDirective,
- },
- mixins: [timeagoMixin],
- props: {
- image: {
- type: Object,
- required: true,
- },
- disabled: {
- type: Boolean,
- default: false,
- required: false,
- },
- },
- data() {
- return {
- containerRepository: {},
- fetchTagsCount: false,
- };
- },
- apollo: {
- containerRepository: {
- query: getContainerRepositoryTagsCountQuery,
- variables() {
- return {
- id: this.image.id,
- };
- },
- },
- },
- computed: {
- imageDetails() {
- return { ...this.image, ...this.containerRepository };
- },
- visibilityIcon() {
- return this.imageDetails?.project?.visibility === 'public' ? 'eye' : 'eye-slash';
- },
- timeAgo() {
- return this.timeFormatted(this.imageDetails.updatedAt);
- },
- updatedText() {
- return sprintf(UPDATED_AT, { time: this.timeAgo });
- },
- tagCountText() {
- if (this.$apollo.queries.containerRepository.loading) {
- return s__('ContainerRegistry|-- tags');
- }
- return n__('%d tag', '%d tags', this.imageDetails.tagsCount);
- },
- cleanupTextAndTooltip() {
- if (!this.imageDetails.project.containerExpirationPolicy?.enabled) {
- return { text: CLEANUP_DISABLED_TEXT, tooltip: CLEANUP_DISABLED_TOOLTIP };
- }
- return {
- [UNSCHEDULED_STATUS]: {
- text: sprintf(CLEANUP_UNSCHEDULED_TEXT, {
- time: this.timeFormatted(this.imageDetails.project.containerExpirationPolicy.nextRunAt),
- }),
- },
- [SCHEDULED_STATUS]: { text: CLEANUP_SCHEDULED_TEXT, tooltip: CLEANUP_SCHEDULED_TOOLTIP },
- [ONGOING_STATUS]: { text: CLEANUP_ONGOING_TEXT, tooltip: CLEANUP_ONGOING_TOOLTIP },
- [UNFINISHED_STATUS]: { text: CLEANUP_UNFINISHED_TEXT, tooltip: CLEANUP_UNFINISHED_TOOLTIP },
- }[this.imageDetails?.expirationPolicyCleanupStatus];
- },
- deleteButtonDisabled() {
- return this.disabled || !this.imageDetails.canDelete;
- },
- rootImageTooltip() {
- return !this.imageDetails.name ? ROOT_IMAGE_TOOLTIP : '';
- },
- imageName() {
- return this.imageDetails.name || ROOT_IMAGE_TEXT;
- },
- },
-};
-</script>
-
-<template>
- <title-area>
- <template #title>
- <span data-testid="title">
- {{ imageName }}
- </span>
- <gl-icon
- v-if="rootImageTooltip"
- v-gl-tooltip="rootImageTooltip"
- class="gl-text-blue-600"
- name="information-o"
- :aria-label="rootImageTooltip"
- />
- </template>
- <template #metadata-tags-count>
- <metadata-item icon="tag" :text="tagCountText" data-testid="tags-count" />
- </template>
-
- <template #metadata-cleanup>
- <metadata-item
- icon="expire"
- :text="cleanupTextAndTooltip.text"
- :text-tooltip="cleanupTextAndTooltip.tooltip"
- size="xl"
- data-testid="cleanup"
- />
- </template>
-
- <template #metadata-updated>
- <metadata-item
- :icon="visibilityIcon"
- :text="updatedText"
- size="xl"
- data-testid="updated-and-visibility"
- />
- </template>
- <template #right-actions>
- <gl-dropdown
- icon="ellipsis_v"
- text="More actions"
- :text-sr-only="true"
- category="tertiary"
- no-caret
- right
- >
- <gl-dropdown-item
- variant="danger"
- :disabled="deleteButtonDisabled"
- @click="$emit('delete')"
- >
- {{ __('Delete image repository') }}
- </gl-dropdown-item>
- </gl-dropdown>
- </template>
- </title-area>
-</template>
diff --git a/app/assets/javascripts/registry/explorer/components/details_page/empty_state.vue b/app/assets/javascripts/registry/explorer/components/details_page/empty_state.vue
deleted file mode 100644
index a16d95a6b30..00000000000
--- a/app/assets/javascripts/registry/explorer/components/details_page/empty_state.vue
+++ /dev/null
@@ -1,44 +0,0 @@
-<script>
-import { GlEmptyState } from '@gitlab/ui';
-import {
- NO_TAGS_TITLE,
- NO_TAGS_MESSAGE,
- MISSING_OR_DELETED_IMAGE_TITLE,
- MISSING_OR_DELETED_IMAGE_MESSAGE,
-} from '../../constants/index';
-
-export default {
- components: {
- GlEmptyState,
- },
- props: {
- noContainersImage: {
- type: String,
- required: false,
- default: '',
- },
- isEmptyImage: {
- type: Boolean,
- default: false,
- required: false,
- },
- },
- computed: {
- title() {
- return this.isEmptyImage ? MISSING_OR_DELETED_IMAGE_TITLE : NO_TAGS_TITLE;
- },
- description() {
- return this.isEmptyImage ? MISSING_OR_DELETED_IMAGE_MESSAGE : NO_TAGS_MESSAGE;
- },
- },
-};
-</script>
-
-<template>
- <gl-empty-state
- :title="title"
- :svg-path="noContainersImage"
- :description="description"
- class="gl-mx-auto gl-my-0"
- />
-</template>
diff --git a/app/assets/javascripts/registry/explorer/components/details_page/partial_cleanup_alert.vue b/app/assets/javascripts/registry/explorer/components/details_page/partial_cleanup_alert.vue
deleted file mode 100644
index 12095655126..00000000000
--- a/app/assets/javascripts/registry/explorer/components/details_page/partial_cleanup_alert.vue
+++ /dev/null
@@ -1,38 +0,0 @@
-<script>
-import { GlSprintf, GlAlert, GlLink } from '@gitlab/ui';
-
-import { DELETE_ALERT_TITLE, DELETE_ALERT_LINK_TEXT } from '../../constants/index';
-
-export default {
- components: {
- GlSprintf,
- GlAlert,
- GlLink,
- },
- props: {
- runCleanupPoliciesHelpPagePath: { type: String, required: false, default: '' },
- cleanupPoliciesHelpPagePath: { type: String, required: false, default: '' },
- },
- i18n: {
- DELETE_ALERT_TITLE,
- DELETE_ALERT_LINK_TEXT,
- },
-};
-</script>
-
-<template>
- <gl-alert variant="warning" :title="$options.i18n.DELETE_ALERT_TITLE" @dismiss="$emit('dismiss')">
- <gl-sprintf :message="$options.i18n.DELETE_ALERT_LINK_TEXT">
- <template #adminLink="{ content }">
- <gl-link data-testid="run-link" :href="runCleanupPoliciesHelpPagePath" target="_blank">{{
- content
- }}</gl-link>
- </template>
- <template #docLink="{ content }">
- <gl-link data-testid="help-link" :href="cleanupPoliciesHelpPagePath" target="_blank">{{
- content
- }}</gl-link>
- </template>
- </gl-sprintf>
- </gl-alert>
-</template>
diff --git a/app/assets/javascripts/registry/explorer/components/details_page/status_alert.vue b/app/assets/javascripts/registry/explorer/components/details_page/status_alert.vue
deleted file mode 100644
index fc1504f6c31..00000000000
--- a/app/assets/javascripts/registry/explorer/components/details_page/status_alert.vue
+++ /dev/null
@@ -1,50 +0,0 @@
-<script>
-import { GlAlert, GlLink, GlSprintf } from '@gitlab/ui';
-import {
- IMAGE_STATUS_MESSAGES,
- IMAGE_STATUS_TITLES,
- IMAGE_STATUS_ALERT_TYPE,
- PACKAGE_DELETE_HELP_PAGE_PATH,
-} from '../../constants';
-
-export default {
- components: {
- GlAlert,
- GlSprintf,
- GlLink,
- },
- props: {
- status: {
- type: String,
- required: true,
- },
- },
- computed: {
- message() {
- return IMAGE_STATUS_MESSAGES[this.status];
- },
- title() {
- return IMAGE_STATUS_TITLES[this.status];
- },
- variant() {
- return IMAGE_STATUS_ALERT_TYPE[this.status];
- },
- },
- links: {
- PACKAGE_DELETE_HELP_PAGE_PATH,
- },
-};
-</script>
-<template>
- <gl-alert :title="title" :variant="variant">
- <span data-testid="message">
- <gl-sprintf :message="message">
- <template #link="{ content }">
- <gl-link :href="$options.links.PACKAGE_DELETE_HELP_PAGE_PATH" target="_blank">{{
- content
- }}</gl-link>
- </template>
- </gl-sprintf>
- </span>
- </gl-alert>
-</template>
diff --git a/app/assets/javascripts/registry/explorer/components/details_page/tags_list.vue b/app/assets/javascripts/registry/explorer/components/details_page/tags_list.vue
deleted file mode 100644
index 3e19a646f53..00000000000
--- a/app/assets/javascripts/registry/explorer/components/details_page/tags_list.vue
+++ /dev/null
@@ -1,179 +0,0 @@
-<script>
-import { GlButton, GlKeysetPagination } from '@gitlab/ui';
-import createFlash from '~/flash';
-import { joinPaths } from '~/lib/utils/url_utility';
-import {
- REMOVE_TAGS_BUTTON_TITLE,
- TAGS_LIST_TITLE,
- GRAPHQL_PAGE_SIZE,
- FETCH_IMAGES_LIST_ERROR_MESSAGE,
-} from '../../constants/index';
-import getContainerRepositoryTagsQuery from '../../graphql/queries/get_container_repository_tags.query.graphql';
-import EmptyState from './empty_state.vue';
-import TagsListRow from './tags_list_row.vue';
-import TagsLoader from './tags_loader.vue';
-
-export default {
- name: 'TagsList',
- components: {
- GlButton,
- GlKeysetPagination,
- TagsListRow,
- EmptyState,
- TagsLoader,
- },
- inject: ['config'],
- props: {
- id: {
- type: [Number, String],
- required: true,
- },
- isMobile: {
- type: Boolean,
- default: true,
- required: false,
- },
- disabled: {
- type: Boolean,
- default: false,
- required: false,
- },
- isImageLoading: {
- type: Boolean,
- default: false,
- required: false,
- },
- },
- i18n: {
- REMOVE_TAGS_BUTTON_TITLE,
- TAGS_LIST_TITLE,
- },
- apollo: {
- containerRepository: {
- query: getContainerRepositoryTagsQuery,
- variables() {
- return this.queryVariables;
- },
- error() {
- createFlash({ message: FETCH_IMAGES_LIST_ERROR_MESSAGE });
- },
- },
- },
- data() {
- return {
- selectedItems: {},
- containerRepository: {},
- };
- },
- computed: {
- tags() {
- return this.containerRepository?.tags?.nodes || [];
- },
- tagsPageInfo() {
- return this.containerRepository?.tags?.pageInfo;
- },
- queryVariables() {
- return {
- id: joinPaths(this.config.gidPrefix, `${this.id}`),
- first: GRAPHQL_PAGE_SIZE,
- };
- },
- hasSelectedItems() {
- return this.tags.some((tag) => this.selectedItems[tag.name]);
- },
- showMultiDeleteButton() {
- return this.tags.some((tag) => tag.canDelete) && !this.isMobile;
- },
- multiDeleteButtonIsDisabled() {
- return !this.hasSelectedItems || this.disabled;
- },
- showPagination() {
- return this.tagsPageInfo.hasPreviousPage || this.tagsPageInfo.hasNextPage;
- },
- hasNoTags() {
- return this.tags.length === 0;
- },
- isLoading() {
- return this.isImageLoading || this.$apollo.queries.containerRepository.loading;
- },
- },
- methods: {
- updateSelectedItems(name) {
- this.$set(this.selectedItems, name, !this.selectedItems[name]);
- },
- mapTagsToBeDleeted(items) {
- return this.tags.filter((tag) => items[tag.name]);
- },
- fetchNextPage() {
- this.$apollo.queries.containerRepository.fetchMore({
- variables: {
- after: this.tagsPageInfo?.endCursor,
- first: GRAPHQL_PAGE_SIZE,
- },
- updateQuery(previousResult, { fetchMoreResult }) {
- return fetchMoreResult;
- },
- });
- },
- fetchPreviousPage() {
- this.$apollo.queries.containerRepository.fetchMore({
- variables: {
- first: null,
- before: this.tagsPageInfo?.startCursor,
- last: GRAPHQL_PAGE_SIZE,
- },
- updateQuery(previousResult, { fetchMoreResult }) {
- return fetchMoreResult;
- },
- });
- },
- },
-};
-</script>
-
-<template>
- <div>
- <tags-loader v-if="isLoading" />
- <template v-else>
- <empty-state v-if="hasNoTags" :no-containers-image="config.noContainersImage" />
- <template v-else>
- <div class="gl-display-flex gl-justify-content-space-between gl-mb-3">
- <h5 data-testid="list-title">
- {{ $options.i18n.TAGS_LIST_TITLE }}
- </h5>
-
- <gl-button
- v-if="showMultiDeleteButton"
- :disabled="multiDeleteButtonIsDisabled"
- category="secondary"
- variant="danger"
- @click="$emit('delete', mapTagsToBeDleeted(selectedItems))"
- >
- {{ $options.i18n.REMOVE_TAGS_BUTTON_TITLE }}
- </gl-button>
- </div>
- <tags-list-row
- v-for="(tag, index) in tags"
- :key="tag.path"
- :tag="tag"
- :first="index === 0"
- :selected="selectedItems[tag.name]"
- :is-mobile="isMobile"
- :disabled="disabled"
- @select="updateSelectedItems(tag.name)"
- @delete="$emit('delete', mapTagsToBeDleeted({ [tag.name]: true }))"
- />
- <div class="gl-display-flex gl-justify-content-center">
- <gl-keyset-pagination
- v-if="showPagination"
- :has-next-page="tagsPageInfo.hasNextPage"
- :has-previous-page="tagsPageInfo.hasPreviousPage"
- class="gl-mt-3"
- @prev="fetchPreviousPage"
- @next="fetchNextPage"
- />
- </div>
- </template>
- </template>
- </div>
-</template>
diff --git a/app/assets/javascripts/registry/explorer/components/details_page/tags_list_row.vue b/app/assets/javascripts/registry/explorer/components/details_page/tags_list_row.vue
deleted file mode 100644
index 0556fd298aa..00000000000
--- a/app/assets/javascripts/registry/explorer/components/details_page/tags_list_row.vue
+++ /dev/null
@@ -1,256 +0,0 @@
-<script>
-import {
- GlFormCheckbox,
- GlTooltipDirective,
- GlSprintf,
- GlIcon,
- GlDropdown,
- GlDropdownItem,
-} from '@gitlab/ui';
-import { formatDate } from '~/lib/utils/datetime_utility';
-import { numberToHumanSize } from '~/lib/utils/number_utils';
-import { n__ } from '~/locale';
-import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
-import DetailsRow from '~/vue_shared/components/registry/details_row.vue';
-import ListItem from '~/vue_shared/components/registry/list_item.vue';
-import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
-import {
- REMOVE_TAG_BUTTON_TITLE,
- DIGEST_LABEL,
- CREATED_AT_LABEL,
- PUBLISHED_DETAILS_ROW_TEXT,
- MANIFEST_DETAILS_ROW_TEST,
- CONFIGURATION_DETAILS_ROW_TEST,
- MISSING_MANIFEST_WARNING_TOOLTIP,
- NOT_AVAILABLE_TEXT,
- NOT_AVAILABLE_SIZE,
- MORE_ACTIONS_TEXT,
-} from '../../constants/index';
-
-export default {
- components: {
- GlSprintf,
- GlFormCheckbox,
- GlIcon,
- GlDropdown,
- GlDropdownItem,
- ListItem,
- ClipboardButton,
- TimeAgoTooltip,
- DetailsRow,
- },
- directives: {
- GlTooltip: GlTooltipDirective,
- },
- props: {
- tag: {
- type: Object,
- required: true,
- },
- isMobile: {
- type: Boolean,
- default: true,
- required: false,
- },
- selected: {
- type: Boolean,
- default: false,
- required: false,
- },
- disabled: {
- type: Boolean,
- default: false,
- required: false,
- },
- },
- i18n: {
- REMOVE_TAG_BUTTON_TITLE,
- DIGEST_LABEL,
- CREATED_AT_LABEL,
- PUBLISHED_DETAILS_ROW_TEXT,
- MANIFEST_DETAILS_ROW_TEST,
- CONFIGURATION_DETAILS_ROW_TEST,
- MISSING_MANIFEST_WARNING_TOOLTIP,
- MORE_ACTIONS_TEXT,
- },
- computed: {
- formattedSize() {
- return this.tag.totalSize
- ? numberToHumanSize(Number(this.tag.totalSize))
- : NOT_AVAILABLE_SIZE;
- },
- layers() {
- return this.tag.layers ? n__('%d layer', '%d layers', this.tag.layers) : '';
- },
- mobileClasses() {
- return this.isMobile ? 'mw-s' : '';
- },
- shortDigest() {
- // remove sha256: from the string, and show only the first 7 char
- return this.tag.digest?.substring(7, 14) ?? NOT_AVAILABLE_TEXT;
- },
- publishedDate() {
- return formatDate(this.tag.createdAt, 'isoDate');
- },
- publishedTime() {
- return formatDate(this.tag.createdAt, 'hh:MM Z');
- },
- formattedRevision() {
- // to be removed when API response is adjusted
- // see https://gitlab.com/gitlab-org/gitlab/-/issues/225324
- // eslint-disable-next-line @gitlab/require-i18n-strings
- return `sha256:${this.tag.revision}`;
- },
- tagLocation() {
- return this.tag.path?.replace(`:${this.tag.name}`, '');
- },
- isInvalidTag() {
- return !this.tag.digest;
- },
- isCheckboxDisabled() {
- return this.isInvalidTag || this.disabled;
- },
- isDeleteDisabled() {
- return this.isInvalidTag || this.disabled || !this.tag.canDelete;
- },
- },
-};
-</script>
-
-<template>
- <list-item v-bind="$attrs" :selected="selected" :disabled="disabled">
- <template #left-action>
- <gl-form-checkbox
- v-if="tag.canDelete"
- :disabled="isCheckboxDisabled"
- class="gl-m-0"
- :checked="selected"
- @change="$emit('select')"
- />
- </template>
- <template #left-primary>
- <div class="gl-display-flex gl-align-items-center">
- <div
- v-gl-tooltip="{ title: tag.name }"
- data-testid="name"
- class="gl-text-overflow-ellipsis gl-overflow-hidden gl-white-space-nowrap"
- :class="mobileClasses"
- >
- {{ tag.name }}
- </div>
-
- <clipboard-button
- v-if="tag.location"
- :title="tag.location"
- :text="tag.location"
- category="tertiary"
- :disabled="disabled"
- />
-
- <gl-icon
- v-if="isInvalidTag"
- v-gl-tooltip="{ title: $options.i18n.MISSING_MANIFEST_WARNING_TOOLTIP }"
- name="warning"
- class="gl-text-orange-500 gl-mb-2 gl-ml-2"
- />
- </div>
- </template>
-
- <template #left-secondary>
- <span data-testid="size">
- {{ formattedSize }}
- <template v-if="formattedSize && layers">&middot;</template>
- {{ layers }}
- </span>
- </template>
- <template #right-primary>
- <span data-testid="time">
- <gl-sprintf :message="$options.i18n.CREATED_AT_LABEL">
- <template #timeInfo>
- <time-ago-tooltip :time="tag.createdAt" />
- </template>
- </gl-sprintf>
- </span>
- </template>
- <template #right-secondary>
- <span data-testid="digest">
- <gl-sprintf :message="$options.i18n.DIGEST_LABEL">
- <template #imageId>{{ shortDigest }}</template>
- </gl-sprintf>
- </span>
- </template>
- <template #right-action>
- <gl-dropdown
- :disabled="isDeleteDisabled"
- icon="ellipsis_v"
- :text="$options.i18n.MORE_ACTIONS_TEXT"
- :text-sr-only="true"
- category="tertiary"
- no-caret
- right
- :class="{ 'gl-opacity-0 gl-pointer-events-none': isDeleteDisabled }"
- data-testid="additional-actions"
- data-qa-selector="more_actions_menu"
- >
- <gl-dropdown-item
- variant="danger"
- data-testid="single-delete-button"
- data-qa-selector="tag_delete_button"
- @click="$emit('delete')"
- >
- {{ $options.i18n.REMOVE_TAG_BUTTON_TITLE }}
- </gl-dropdown-item>
- </gl-dropdown>
- </template>
-
- <template v-if="!isInvalidTag" #details-published>
- <details-row icon="clock" data-testid="published-date-detail">
- <gl-sprintf :message="$options.i18n.PUBLISHED_DETAILS_ROW_TEXT">
- <template #repositoryPath>
- <i>{{ tagLocation }}</i>
- </template>
- <template #time>
- {{ publishedTime }}
- </template>
- <template #date>
- {{ publishedDate }}
- </template>
- </gl-sprintf>
- </details-row>
- </template>
- <template v-if="!isInvalidTag" #details-manifest-digest>
- <details-row icon="log" data-testid="manifest-detail">
- <gl-sprintf :message="$options.i18n.MANIFEST_DETAILS_ROW_TEST">
- <template #digest>
- {{ tag.digest }}
- </template>
- </gl-sprintf>
- <clipboard-button
- v-if="tag.digest"
- :title="tag.digest"
- :text="tag.digest"
- category="tertiary"
- size="small"
- :disabled="disabled"
- />
- </details-row>
- </template>
- <template v-if="!isInvalidTag" #details-configuration-digest>
- <details-row icon="cloud-gear" data-testid="configuration-detail">
- <gl-sprintf :message="$options.i18n.CONFIGURATION_DETAILS_ROW_TEST">
- <template #digest>
- {{ formattedRevision }}
- </template>
- </gl-sprintf>
- <clipboard-button
- v-if="formattedRevision"
- :title="formattedRevision"
- :text="formattedRevision"
- category="tertiary"
- size="small"
- :disabled="disabled"
- />
- </details-row>
- </template>
- </list-item>
-</template>
diff --git a/app/assets/javascripts/registry/explorer/components/details_page/tags_loader.vue b/app/assets/javascripts/registry/explorer/components/details_page/tags_loader.vue
deleted file mode 100644
index b7afa5fba33..00000000000
--- a/app/assets/javascripts/registry/explorer/components/details_page/tags_loader.vue
+++ /dev/null
@@ -1,34 +0,0 @@
-<script>
-import { GlSkeletonLoader } from '@gitlab/ui';
-
-export default {
- components: {
- GlSkeletonLoader,
- },
- loader: {
- repeat: 10,
- width: 1000,
- height: 40,
- },
-};
-</script>
-
-<template>
- <div>
- <gl-skeleton-loader
- v-for="index in $options.loader.repeat"
- :key="index"
- :width="$options.loader.width"
- :height="$options.loader.height"
- preserve-aspect-ratio="xMinYMax meet"
- >
- <rect width="15" x="0" y="12.5" height="15" rx="4" />
- <rect width="250" x="25" y="10" height="20" rx="4" />
- <circle cx="290" cy="20" r="10" />
- <rect width="100" x="315" y="10" height="20" rx="4" />
- <rect width="100" x="500" y="10" height="20" rx="4" />
- <rect width="100" x="630" y="10" height="20" rx="4" />
- <rect x="960" y="0" width="40" height="40" rx="4" />
- </gl-skeleton-loader>
- </div>
-</template>
diff --git a/app/assets/javascripts/registry/explorer/components/list_page/cleanup_status.vue b/app/assets/javascripts/registry/explorer/components/list_page/cleanup_status.vue
deleted file mode 100644
index 1f52e319ad0..00000000000
--- a/app/assets/javascripts/registry/explorer/components/list_page/cleanup_status.vue
+++ /dev/null
@@ -1,71 +0,0 @@
-<script>
-import { GlTooltipDirective, GlIcon } from '@gitlab/ui';
-import {
- CLEANUP_TIMED_OUT_ERROR_MESSAGE,
- CLEANUP_STATUS_SCHEDULED,
- CLEANUP_STATUS_ONGOING,
- CLEANUP_STATUS_UNFINISHED,
- UNFINISHED_STATUS,
- UNSCHEDULED_STATUS,
- SCHEDULED_STATUS,
- ONGOING_STATUS,
-} from '../../constants/index';
-
-export default {
- name: 'CleanupStatus',
- components: {
- GlIcon,
- },
- directives: {
- GlTooltip: GlTooltipDirective,
- },
- props: {
- status: {
- type: String,
- required: true,
- validator(value) {
- return [UNFINISHED_STATUS, UNSCHEDULED_STATUS, SCHEDULED_STATUS, ONGOING_STATUS].includes(
- value,
- );
- },
- },
- },
- i18n: {
- CLEANUP_STATUS_SCHEDULED,
- CLEANUP_STATUS_ONGOING,
- CLEANUP_STATUS_UNFINISHED,
- CLEANUP_TIMED_OUT_ERROR_MESSAGE,
- },
- computed: {
- showStatus() {
- return this.status !== UNSCHEDULED_STATUS;
- },
- failedDelete() {
- return this.status === UNFINISHED_STATUS;
- },
- statusText() {
- return this.$options.i18n[`CLEANUP_STATUS_${this.status}`];
- },
- expireIconClass() {
- return this.failedDelete ? 'gl-text-orange-500' : '';
- },
- },
-};
-</script>
-
-<template>
- <div v-if="showStatus" class="gl-display-inline-flex gl-align-items-center">
- <gl-icon name="expire" data-testid="main-icon" :class="expireIconClass" />
- <span class="gl-mx-2">
- {{ statusText }}
- </span>
- <gl-icon
- v-if="failedDelete"
- v-gl-tooltip="{ title: $options.i18n.CLEANUP_TIMED_OUT_ERROR_MESSAGE }"
- :size="14"
- class="gl-text-black-normal"
- data-testid="extra-info"
- name="information"
- />
- </div>
-</template>
diff --git a/app/assets/javascripts/registry/explorer/components/list_page/cli_commands.vue b/app/assets/javascripts/registry/explorer/components/list_page/cli_commands.vue
deleted file mode 100644
index 07ee3c6083b..00000000000
--- a/app/assets/javascripts/registry/explorer/components/list_page/cli_commands.vue
+++ /dev/null
@@ -1,71 +0,0 @@
-<script>
-import { GlDropdown } from '@gitlab/ui';
-import Tracking from '~/tracking';
-import CodeInstruction from '~/vue_shared/components/registry/code_instruction.vue';
-import {
- QUICK_START,
- LOGIN_COMMAND_LABEL,
- COPY_LOGIN_TITLE,
- BUILD_COMMAND_LABEL,
- COPY_BUILD_TITLE,
- PUSH_COMMAND_LABEL,
- COPY_PUSH_TITLE,
-} from '../../constants/index';
-
-const trackingLabel = 'quickstart_dropdown';
-
-export default {
- components: {
- GlDropdown,
- CodeInstruction,
- },
- mixins: [Tracking.mixin({ label: trackingLabel })],
- inject: ['config', 'dockerBuildCommand', 'dockerPushCommand', 'dockerLoginCommand'],
- trackingLabel,
- i18n: {
- QUICK_START,
- LOGIN_COMMAND_LABEL,
- COPY_LOGIN_TITLE,
- BUILD_COMMAND_LABEL,
- COPY_BUILD_TITLE,
- PUSH_COMMAND_LABEL,
- COPY_PUSH_TITLE,
- },
-};
-</script>
-<template>
- <gl-dropdown
- :text="$options.i18n.QUICK_START"
- variant="info"
- right
- @shown="track('click_dropdown')"
- >
- <!-- This li is used as a container since gl-dropdown produces a root ul, this mimics the functionality exposed by b-dropdown-form -->
- <li role="presentation" class="px-2 py-1">
- <code-instruction
- :label="$options.i18n.LOGIN_COMMAND_LABEL"
- :instruction="dockerLoginCommand"
- :copy-text="$options.i18n.COPY_LOGIN_TITLE"
- tracking-action="click_copy_login"
- :tracking-label="$options.trackingLabel"
- />
-
- <code-instruction
- :label="$options.i18n.BUILD_COMMAND_LABEL"
- :instruction="dockerBuildCommand"
- :copy-text="$options.i18n.COPY_BUILD_TITLE"
- tracking-action="click_copy_build"
- :tracking-label="$options.trackingLabel"
- />
-
- <code-instruction
- class="mb-0"
- :label="$options.i18n.PUSH_COMMAND_LABEL"
- :instruction="dockerPushCommand"
- :copy-text="$options.i18n.COPY_PUSH_TITLE"
- tracking-action="click_copy_push"
- :tracking-label="$options.trackingLabel"
- />
- </li>
- </gl-dropdown>
-</template>
diff --git a/app/assets/javascripts/registry/explorer/components/list_page/group_empty_state.vue b/app/assets/javascripts/registry/explorer/components/list_page/group_empty_state.vue
deleted file mode 100644
index a68c4de5aa6..00000000000
--- a/app/assets/javascripts/registry/explorer/components/list_page/group_empty_state.vue
+++ /dev/null
@@ -1,35 +0,0 @@
-<script>
-import { GlEmptyState, GlSprintf, GlLink } from '@gitlab/ui';
-
-export default {
- name: 'GroupEmptyState',
- components: {
- GlEmptyState,
- GlSprintf,
- GlLink,
- },
- inject: ['config'],
-};
-</script>
-<template>
- <gl-empty-state
- :title="s__('ContainerRegistry|There are no container images available in this group')"
- :svg-path="config.noContainersImage"
- >
- <template #description>
- <p>
- <gl-sprintf
- :message="
- s__(
- `ContainerRegistry|With the Container Registry, every project can have its own space to store its Docker images. Push at least one Docker image in one of this group's projects in order to show up here. %{docLinkStart}More Information%{docLinkEnd}`,
- )
- "
- >
- <template #docLink="{ content }">
- <gl-link :href="config.helpPagePath" target="_blank">{{ content }}</gl-link>
- </template>
- </gl-sprintf>
- </p>
- </template>
- </gl-empty-state>
-</template>
diff --git a/app/assets/javascripts/registry/explorer/components/list_page/image_list.vue b/app/assets/javascripts/registry/explorer/components/list_page/image_list.vue
deleted file mode 100644
index 5bd13322ebb..00000000000
--- a/app/assets/javascripts/registry/explorer/components/list_page/image_list.vue
+++ /dev/null
@@ -1,54 +0,0 @@
-<script>
-import { GlKeysetPagination } from '@gitlab/ui';
-import ImageListRow from './image_list_row.vue';
-
-export default {
- name: 'ImageList',
- components: {
- GlKeysetPagination,
- ImageListRow,
- },
- props: {
- images: {
- type: Array,
- required: true,
- },
- metadataLoading: {
- type: Boolean,
- default: false,
- required: false,
- },
- pageInfo: {
- type: Object,
- required: true,
- },
- },
- computed: {
- showPagination() {
- return this.pageInfo.hasPreviousPage || this.pageInfo.hasNextPage;
- },
- },
-};
-</script>
-
-<template>
- <div class="gl-display-flex gl-flex-direction-column">
- <image-list-row
- v-for="(listItem, index) in images"
- :key="index"
- :item="listItem"
- :metadata-loading="metadataLoading"
- @delete="$emit('delete', $event)"
- />
- <div class="gl-display-flex gl-justify-content-center">
- <gl-keyset-pagination
- v-if="showPagination"
- :has-next-page="pageInfo.hasNextPage"
- :has-previous-page="pageInfo.hasPreviousPage"
- class="gl-mt-3"
- @prev="$emit('prev-page')"
- @next="$emit('next-page')"
- />
- </div>
- </div>
-</template>
diff --git a/app/assets/javascripts/registry/explorer/components/list_page/image_list_row.vue b/app/assets/javascripts/registry/explorer/components/list_page/image_list_row.vue
deleted file mode 100644
index c1ec523574a..00000000000
--- a/app/assets/javascripts/registry/explorer/components/list_page/image_list_row.vue
+++ /dev/null
@@ -1,153 +0,0 @@
-<script>
-import { GlTooltipDirective, GlIcon, GlSprintf, GlSkeletonLoader } from '@gitlab/ui';
-import { getIdFromGraphQLId } from '~/graphql_shared/utils';
-import { n__ } from '~/locale';
-
-import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
-import ListItem from '~/vue_shared/components/registry/list_item.vue';
-import {
- ASYNC_DELETE_IMAGE_ERROR_MESSAGE,
- LIST_DELETE_BUTTON_DISABLED,
- REMOVE_REPOSITORY_LABEL,
- ROW_SCHEDULED_FOR_DELETION,
- CLEANUP_TIMED_OUT_ERROR_MESSAGE,
- IMAGE_DELETE_SCHEDULED_STATUS,
- IMAGE_FAILED_DELETED_STATUS,
- ROOT_IMAGE_TEXT,
-} from '../../constants/index';
-import DeleteButton from '../delete_button.vue';
-import CleanupStatus from './cleanup_status.vue';
-
-export default {
- name: 'ImageListRow',
- components: {
- ClipboardButton,
- DeleteButton,
- GlSprintf,
- GlIcon,
- ListItem,
- GlSkeletonLoader,
- CleanupStatus,
- },
- directives: {
- GlTooltip: GlTooltipDirective,
- },
- props: {
- item: {
- type: Object,
- required: true,
- },
- metadataLoading: {
- type: Boolean,
- default: false,
- required: false,
- },
- },
- i18n: {
- LIST_DELETE_BUTTON_DISABLED,
- REMOVE_REPOSITORY_LABEL,
- ROW_SCHEDULED_FOR_DELETION,
- },
- computed: {
- disabledDelete() {
- return !this.item.canDelete || this.deleting;
- },
- id() {
- return getIdFromGraphQLId(this.item.id);
- },
- deleting() {
- return this.item.status === IMAGE_DELETE_SCHEDULED_STATUS;
- },
- failedDelete() {
- return this.item.status === IMAGE_FAILED_DELETED_STATUS;
- },
- tagsCountText() {
- return n__(
- 'ContainerRegistry|%{count} Tag',
- 'ContainerRegistry|%{count} Tags',
- this.item.tagsCount,
- );
- },
- warningIconText() {
- if (this.failedDelete) {
- return ASYNC_DELETE_IMAGE_ERROR_MESSAGE;
- }
- if (this.item.expirationPolicyStartedAt) {
- return CLEANUP_TIMED_OUT_ERROR_MESSAGE;
- }
- return null;
- },
- imageName() {
- return this.item.name ? this.item.path : `${this.item.path}/ ${ROOT_IMAGE_TEXT}`;
- },
- routerLinkEvent() {
- return this.deleting ? '' : 'click';
- },
- },
-};
-</script>
-
-<template>
- <list-item
- v-gl-tooltip="{
- placement: 'left',
- disabled: !deleting,
- title: $options.i18n.ROW_SCHEDULED_FOR_DELETION,
- }"
- v-bind="$attrs"
- :disabled="deleting"
- >
- <template #left-primary>
- <router-link
- class="gl-text-body gl-font-weight-bold"
- data-testid="details-link"
- data-qa-selector="registry_image_content"
- :event="routerLinkEvent"
- :to="{ name: 'details', params: { id } }"
- >
- {{ imageName }}
- </router-link>
- <clipboard-button
- v-if="item.location"
- :disabled="deleting"
- :text="item.location"
- :title="item.location"
- category="tertiary"
- />
- </template>
- <template #left-secondary>
- <template v-if="!metadataLoading">
- <span class="gl-display-flex gl-align-items-center" data-testid="tags-count">
- <gl-icon name="tag" class="gl-mr-2" />
- <gl-sprintf :message="tagsCountText">
- <template #count>
- {{ item.tagsCount }}
- </template>
- </gl-sprintf>
- </span>
-
- <cleanup-status
- v-if="item.expirationPolicyCleanupStatus"
- class="ml-2"
- :status="item.expirationPolicyCleanupStatus"
- />
- </template>
-
- <div v-else class="gl-w-full">
- <gl-skeleton-loader :width="900" :height="16" preserve-aspect-ratio="xMinYMax meet">
- <circle cx="6" cy="8" r="6" />
- <rect x="16" y="4" width="100" height="8" rx="4" />
- </gl-skeleton-loader>
- </div>
- </template>
- <template #right-action>
- <delete-button
- :title="$options.i18n.REMOVE_REPOSITORY_LABEL"
- :disabled="disabledDelete"
- :tooltip-disabled="item.canDelete"
- :tooltip-title="$options.i18n.LIST_DELETE_BUTTON_DISABLED"
- @delete="$emit('delete', item)"
- />
- </template>
- </list-item>
-</template>
diff --git a/app/assets/javascripts/registry/explorer/components/list_page/project_empty_state.vue b/app/assets/javascripts/registry/explorer/components/list_page/project_empty_state.vue
deleted file mode 100644
index 5aa04419ca0..00000000000
--- a/app/assets/javascripts/registry/explorer/components/list_page/project_empty_state.vue
+++ /dev/null
@@ -1,111 +0,0 @@
-<script>
-import { GlEmptyState, GlSprintf, GlLink, GlFormInputGroup, GlFormInput } from '@gitlab/ui';
-import { s__ } from '~/locale';
-import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
-import {
- COPY_LOGIN_TITLE,
- COPY_BUILD_TITLE,
- COPY_PUSH_TITLE,
- QUICK_START,
-} from '../../constants/index';
-
-export default {
- name: 'ProjectEmptyState',
- components: {
- ClipboardButton,
- GlEmptyState,
- GlSprintf,
- GlLink,
- GlFormInputGroup,
- GlFormInput,
- },
- inject: ['config', 'dockerBuildCommand', 'dockerPushCommand', 'dockerLoginCommand'],
- i18n: {
- quickStart: QUICK_START,
- copyLoginTitle: COPY_LOGIN_TITLE,
- copyBuildTitle: COPY_BUILD_TITLE,
- copyPushTitle: COPY_PUSH_TITLE,
- introText: s__(
- `ContainerRegistry|With the Container Registry, every project can have its own space to store its Docker images. %{docLinkStart}More Information%{docLinkEnd}`,
- ),
- notLoggedInMessage: s__(
- `ContainerRegistry|If you are not already logged in, you need to authenticate to the Container Registry by using your GitLab username and password. If you have %{twofaDocLinkStart}Two-Factor Authentication%{twofaDocLinkEnd} enabled, use a %{personalAccessTokensDocLinkStart}Personal Access Token%{personalAccessTokensDocLinkEnd} instead of a password.`,
- ),
- addImageText: s__(
- 'ContainerRegistry|You can add an image to this registry with the following commands:',
- ),
- },
-};
-</script>
-<template>
- <gl-empty-state
- :title="s__('ContainerRegistry|There are no container images stored for this project')"
- :svg-path="config.noContainersImage"
- >
- <template #description>
- <p>
- <gl-sprintf :message="$options.i18n.introText">
- <template #docLink="{ content }">
- <gl-link :href="config.helpPagePath" target="_blank">{{ content }}</gl-link>
- </template>
- </gl-sprintf>
- </p>
- <h5>{{ $options.i18n.quickStart }}</h5>
- <p>
- <gl-sprintf :message="$options.i18n.notLoggedInMessage">
- <template #twofaDocLink="{ content }">
- <gl-link :href="config.twoFactorAuthHelpLink" target="_blank">{{ content }}</gl-link>
- </template>
- <template #personalAccessTokensDocLink="{ content }">
- <gl-link :href="config.personalAccessTokensHelpLink" target="_blank">{{
- content
- }}</gl-link>
- </template>
- </gl-sprintf>
- </p>
- <gl-form-input-group class="gl-mb-4">
- <gl-form-input
- :value="dockerLoginCommand"
- readonly
- type="text"
- class="gl-font-monospace!"
- />
- <template #append>
- <clipboard-button
- :text="dockerLoginCommand"
- :title="$options.i18n.copyLoginTitle"
- class="gl-m-0!"
- />
- </template>
- </gl-form-input-group>
- <p class="gl-mb-4">
- {{ $options.i18n.addImageText }}
- </p>
- <gl-form-input-group class="gl-mb-4">
- <gl-form-input
- :value="dockerBuildCommand"
- readonly
- type="text"
- class="gl-font-monospace!"
- />
- <template #append>
- <clipboard-button
- :text="dockerBuildCommand"
- :title="$options.i18n.copyBuildTitle"
- class="gl-m-0!"
- />
- </template>
- </gl-form-input-group>
- <gl-form-input-group>
- <gl-form-input :value="dockerPushCommand" readonly type="text" class="gl-font-monospace!" />
- <template #append>
- <clipboard-button
- :text="dockerPushCommand"
- :title="$options.i18n.copyPushTitle"
- class="gl-m-0!"
- />
- </template>
- </gl-form-input-group>
- </template>
- </gl-empty-state>
-</template>
diff --git a/app/assets/javascripts/registry/explorer/components/list_page/registry_header.vue b/app/assets/javascripts/registry/explorer/components/list_page/registry_header.vue
deleted file mode 100644
index 6d2ff9ea7b6..00000000000
--- a/app/assets/javascripts/registry/explorer/components/list_page/registry_header.vue
+++ /dev/null
@@ -1,110 +0,0 @@
-<script>
-import { approximateDuration, calculateRemainingMilliseconds } from '~/lib/utils/datetime_utility';
-import { n__, sprintf } from '~/locale';
-import MetadataItem from '~/vue_shared/components/registry/metadata_item.vue';
-import TitleArea from '~/vue_shared/components/registry/title_area.vue';
-
-import {
- CONTAINER_REGISTRY_TITLE,
- LIST_INTRO_TEXT,
- EXPIRATION_POLICY_WILL_RUN_IN,
- EXPIRATION_POLICY_DISABLED_TEXT,
-} from '../../constants/index';
-
-export default {
- name: 'ListHeader',
- components: {
- TitleArea,
- MetadataItem,
- },
- props: {
- expirationPolicy: {
- type: Object,
- default: () => ({}),
- required: false,
- },
- imagesCount: {
- type: Number,
- default: 0,
- required: false,
- },
- helpPagePath: {
- type: String,
- default: '',
- required: false,
- },
- hideExpirationPolicyData: {
- type: Boolean,
- required: false,
- default: false,
- },
- metadataLoading: {
- type: Boolean,
- required: false,
- default: false,
- },
- },
- loader: {
- repeat: 10,
- width: 1000,
- height: 40,
- },
- i18n: {
- CONTAINER_REGISTRY_TITLE,
- },
- computed: {
- imagesCountText() {
- const pluralisedString = n__(
- 'ContainerRegistry|%{count} Image repository',
- 'ContainerRegistry|%{count} Image repositories',
- this.imagesCount,
- );
- return sprintf(pluralisedString, { count: this.imagesCount });
- },
- timeTillRun() {
- const difference = calculateRemainingMilliseconds(this.expirationPolicy?.next_run_at);
- return approximateDuration(difference / 1000);
- },
- expirationPolicyEnabled() {
- return this.expirationPolicy?.enabled;
- },
- expirationPolicyText() {
- return this.expirationPolicyEnabled
- ? sprintf(EXPIRATION_POLICY_WILL_RUN_IN, { time: this.timeTillRun })
- : EXPIRATION_POLICY_DISABLED_TEXT;
- },
- infoMessages() {
- return [{ text: LIST_INTRO_TEXT, link: this.helpPagePath }];
- },
- },
-};
-</script>
-
-<template>
- <title-area
- :title="$options.i18n.CONTAINER_REGISTRY_TITLE"
- :info-messages="infoMessages"
- :metadata-loading="metadataLoading"
- >
- <template #right-actions>
- <slot name="commands"></slot>
- </template>
- <template #metadata-count>
- <metadata-item
- v-if="imagesCount"
- data-testid="images-count"
- icon="container-image"
- :text="imagesCountText"
- />
- </template>
- <template #metadata-exp-policies>
- <metadata-item
- v-if="!hideExpirationPolicyData"
- data-testid="expiration-policy"
- icon="expire"
- :text="expirationPolicyText"
- size="xl"
- />
- </template>
- </title-area>
-</template>
diff --git a/app/assets/javascripts/registry/explorer/components/registry_breadcrumb.vue b/app/assets/javascripts/registry/explorer/components/registry_breadcrumb.vue
deleted file mode 100644
index e77eda31596..00000000000
--- a/app/assets/javascripts/registry/explorer/components/registry_breadcrumb.vue
+++ /dev/null
@@ -1,51 +0,0 @@
-<script>
-// We are using gl-breadcrumb only at the last child of the handwritten breadcrumb
-// until this gitlab-ui issue is resolved: https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1079
-//
-// See the CSS workaround in app/assets/stylesheets/pages/registry.scss when this file is changed.
-import { GlBreadcrumb, GlIcon } from '@gitlab/ui';
-
-export default {
- components: {
- GlBreadcrumb,
- GlIcon,
- },
- computed: {
- rootRoute() {
- return this.$router.options.routes.find((r) => r.meta.root);
- },
- detailsRoute() {
- return this.$router.options.routes.find((r) => r.name === 'details');
- },
- isRootRoute() {
- return this.$route.name === this.rootRoute.name;
- },
- isLoaded() {
- return this.isRootRoute || this.$store?.state.imageDetails?.name;
- },
- allCrumbs() {
- const crumbs = [
- {
- text: this.rootRoute.meta.nameGenerator(),
- to: this.rootRoute.path,
- },
- ];
- if (!this.isRootRoute) {
- crumbs.push({
- text: this.detailsRoute.meta.nameGenerator(),
- href: this.detailsRoute.meta.path,
- });
- }
- return crumbs;
- },
- },
-};
-</script>
-
-<template>
- <gl-breadcrumb :key="isLoaded" :items="allCrumbs">
- <template #separator>
- <gl-icon name="angle-right" :size="8" />
- </template>
- </gl-breadcrumb>
-</template>
diff --git a/app/assets/javascripts/registry/explorer/constants/common.js b/app/assets/javascripts/registry/explorer/constants/common.js
deleted file mode 100644
index f7beec2c935..00000000000
--- a/app/assets/javascripts/registry/explorer/constants/common.js
+++ /dev/null
@@ -1,4 +0,0 @@
-import { s__, __ } from '~/locale';
-
-export const ROOT_IMAGE_TEXT = s__('ContainerRegistry|Root image');
-export const MORE_ACTIONS_TEXT = __('More actions');
diff --git a/app/assets/javascripts/registry/explorer/constants/details.js b/app/assets/javascripts/registry/explorer/constants/details.js
deleted file mode 100644
index 19e1a75fb2f..00000000000
--- a/app/assets/javascripts/registry/explorer/constants/details.js
+++ /dev/null
@@ -1,166 +0,0 @@
-import { helpPagePath } from '~/helpers/help_page_helper';
-import { s__, __ } from '~/locale';
-
-// Translations strings
-export const DELETE_TAG_ERROR_MESSAGE = s__(
- 'ContainerRegistry|Something went wrong while marking the tag for deletion.',
-);
-export const DELETE_TAG_SUCCESS_MESSAGE = s__(
- 'ContainerRegistry|Tag successfully marked for deletion.',
-);
-export const DELETE_TAGS_ERROR_MESSAGE = s__(
- 'ContainerRegistry|Something went wrong while marking the tags for deletion.',
-);
-export const DELETE_TAGS_SUCCESS_MESSAGE = s__(
- 'ContainerRegistry|Tags successfully marked for deletion.',
-);
-
-export const FETCH_IMAGE_DETAILS_ERROR_MESSAGE = s__(
- 'ContainerRegistry|Something went wrong while fetching the image details.',
-);
-
-export const TAGS_LIST_TITLE = s__('ContainerRegistry|Image tags');
-export const DIGEST_LABEL = s__('ContainerRegistry|Digest: %{imageId}');
-export const CREATED_AT_LABEL = s__('ContainerRegistry|Published %{timeInfo}');
-export const PUBLISHED_DETAILS_ROW_TEXT = s__(
- 'ContainerRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}',
-);
-export const MANIFEST_DETAILS_ROW_TEST = s__('ContainerRegistry|Manifest digest: %{digest}');
-export const CONFIGURATION_DETAILS_ROW_TEST = s__(
- 'ContainerRegistry|Configuration digest: %{digest}',
-);
-
-export const REMOVE_TAG_BUTTON_TITLE = s__('ContainerRegistry|Delete tag');
-export const REMOVE_TAGS_BUTTON_TITLE = s__('ContainerRegistry|Delete selected tags');
-
-export const REMOVE_TAG_CONFIRMATION_TEXT = s__(
- `ContainerRegistry|You are about to remove %{item}. Are you sure?`,
-);
-export const REMOVE_TAGS_CONFIRMATION_TEXT = s__(
- `ContainerRegistry|You are about to remove %{item} tags. Are you sure?`,
-);
-export const NO_TAGS_TITLE = s__('ContainerRegistry|This image has no active tags');
-export const NO_TAGS_MESSAGE = s__(
- `ContainerRegistry|The last tag related to this image was recently removed.
-This empty image and any associated data will be automatically removed as part of the regular Garbage Collection process.
-If you have any questions, contact your administrator.`,
-);
-
-export const MISSING_OR_DELETED_IMAGE_TITLE = s__(
- 'ContainerRegistry|The image repository could not be found.',
-);
-export const MISSING_OR_DELETED_IMAGE_MESSAGE = s__(
- 'ContainerRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page.',
-);
-
-export const MISSING_OR_DELETED_IMAGE_BREADCRUMB = s__(
- 'ContainerRegistry|Image repository not found',
-);
-
-export const ADMIN_GARBAGE_COLLECTION_TIP = s__(
- 'ContainerRegistry|Remember to run %{docLinkStart}garbage collection%{docLinkEnd} to remove the stale data from storage.',
-);
-
-export const MISSING_MANIFEST_WARNING_TOOLTIP = s__(
- 'ContainerRegistry|Invalid tag: missing manifest digest',
-);
-
-export const UPDATED_AT = s__('ContainerRegistry|Last updated %{time}');
-
-export const NOT_AVAILABLE_TEXT = __('N/A');
-export const NOT_AVAILABLE_SIZE = __('0 bytes');
-
-export const CLEANUP_UNSCHEDULED_TEXT = s__('ContainerRegistry|Cleanup will run %{time}');
-export const CLEANUP_SCHEDULED_TEXT = s__('ContainerRegistry|Cleanup pending');
-export const CLEANUP_ONGOING_TEXT = s__('ContainerRegistry|Cleanup in progress');
-export const CLEANUP_UNFINISHED_TEXT = s__('ContainerRegistry|Cleanup incomplete');
-export const CLEANUP_DISABLED_TEXT = s__('ContainerRegistry|Cleanup disabled');
-
-export const CLEANUP_SCHEDULED_TOOLTIP = s__('ContainerRegistry|Cleanup will run soon');
-export const CLEANUP_ONGOING_TOOLTIP = s__('ContainerRegistry|Cleanup is currently removing tags');
-export const CLEANUP_UNFINISHED_TOOLTIP = s__(
- 'ContainerRegistry|Cleanup ran but some tags were not removed',
-);
-export const CLEANUP_DISABLED_TOOLTIP = s__(
- 'ContainerRegistry|Cleanup is disabled for this project',
-);
-
-export const CLEANUP_STATUS_SCHEDULED = s__('ContainerRegistry|Cleanup will run soon');
-export const CLEANUP_STATUS_ONGOING = s__('ContainerRegistry|Cleanup is ongoing');
-export const CLEANUP_STATUS_UNFINISHED = s__('ContainerRegistry|Cleanup timed out');
-
-export const DETAILS_DELETE_IMAGE_ERROR_MESSAGE = s__(
- 'ContainerRegistry|Something went wrong while scheduling the image for deletion.',
-);
-
-export const DELETE_IMAGE_CONFIRMATION_TITLE = s__('ContainerRegistry|Delete image repository?');
-export const DELETE_IMAGE_CONFIRMATION_TEXT = s__(
- 'ContainerRegistry|Deleting the image repository will delete all images and tags inside. This action cannot be undone. Please type the following to confirm: %{code}',
-);
-
-export const SCHEDULED_FOR_DELETION_STATUS_TITLE = s__(
- 'ContainerRegistry|Image repository will be deleted',
-);
-export const SCHEDULED_FOR_DELETION_STATUS_MESSAGE = s__(
- 'ContainerRegistry|This image repository will be deleted. %{linkStart}Learn more.%{linkEnd}',
-);
-
-export const FAILED_DELETION_STATUS_TITLE = s__(
- 'ContainerRegistry|Image repository deletion failed',
-);
-export const FAILED_DELETION_STATUS_MESSAGE = s__(
- 'ContainerRegistry|This image repository has failed to be deleted',
-);
-
-export const ROOT_IMAGE_TOOLTIP = s__(
- 'ContainerRegistry|Image repository with no name located at the project URL.',
-);
-
-// Parameters
-
-export const DEFAULT_PAGE = 1;
-export const DEFAULT_PAGE_SIZE = 10;
-export const GROUP_PAGE_TYPE = 'groups';
-export const ALERT_SUCCESS_TAG = 'success_tag';
-export const ALERT_DANGER_TAG = 'danger_tag';
-export const ALERT_SUCCESS_TAGS = 'success_tags';
-export const ALERT_DANGER_TAGS = 'danger_tags';
-export const ALERT_DANGER_IMAGE = 'danger_image';
-
-export const DELETE_SCHEDULED = 'DELETE_SCHEDULED';
-export const DELETE_FAILED = 'DELETE_FAILED';
-
-export const ALERT_MESSAGES = {
- [ALERT_SUCCESS_TAG]: DELETE_TAG_SUCCESS_MESSAGE,
- [ALERT_DANGER_TAG]: DELETE_TAG_ERROR_MESSAGE,
- [ALERT_SUCCESS_TAGS]: DELETE_TAGS_SUCCESS_MESSAGE,
- [ALERT_DANGER_TAGS]: DELETE_TAGS_ERROR_MESSAGE,
- [ALERT_DANGER_IMAGE]: DETAILS_DELETE_IMAGE_ERROR_MESSAGE,
-};
-
-export const UNFINISHED_STATUS = 'UNFINISHED';
-export const UNSCHEDULED_STATUS = 'UNSCHEDULED';
-export const SCHEDULED_STATUS = 'SCHEDULED';
-export const ONGOING_STATUS = 'ONGOING';
-
-export const IMAGE_STATUS_TITLES = {
- [DELETE_SCHEDULED]: SCHEDULED_FOR_DELETION_STATUS_TITLE,
- [DELETE_FAILED]: FAILED_DELETION_STATUS_TITLE,
-};
-
-export const IMAGE_STATUS_MESSAGES = {
- [DELETE_SCHEDULED]: SCHEDULED_FOR_DELETION_STATUS_MESSAGE,
- [DELETE_FAILED]: FAILED_DELETION_STATUS_MESSAGE,
-};
-
-export const IMAGE_STATUS_ALERT_TYPE = {
- [DELETE_SCHEDULED]: 'info',
- [DELETE_FAILED]: 'warning',
-};
-
-export const PACKAGE_DELETE_HELP_PAGE_PATH = helpPagePath(
- 'user/packages/container_registry/index',
- {
- anchor: 'delete-images',
- },
-);
diff --git a/app/assets/javascripts/registry/explorer/constants/expiration_policies.js b/app/assets/javascripts/registry/explorer/constants/expiration_policies.js
deleted file mode 100644
index 40f9b09a982..00000000000
--- a/app/assets/javascripts/registry/explorer/constants/expiration_policies.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import { s__ } from '~/locale';
-
-export const EXPIRATION_POLICY_WILL_RUN_IN = s__(
- 'ContainerRegistry|Expiration policy will run in %{time}',
-);
-export const EXPIRATION_POLICY_DISABLED_TEXT = s__(
- 'ContainerRegistry|Expiration policy is disabled',
-);
-export const DELETE_ALERT_TITLE = s__('ContainerRegistry|Some tags were not deleted');
-export const DELETE_ALERT_LINK_TEXT = s__(
- 'ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}',
-);
-export const CLEANUP_TIMED_OUT_ERROR_MESSAGE = s__(
- 'ContainerRegistry|Cleanup timed out before it could delete all tags',
-);
diff --git a/app/assets/javascripts/registry/explorer/constants/index.js b/app/assets/javascripts/registry/explorer/constants/index.js
deleted file mode 100644
index 6886356d8e2..00000000000
--- a/app/assets/javascripts/registry/explorer/constants/index.js
+++ /dev/null
@@ -1,5 +0,0 @@
-export * from './common';
-export * from './expiration_policies';
-export * from './quick_start';
-export * from './list';
-export * from './details';
diff --git a/app/assets/javascripts/registry/explorer/constants/list.js b/app/assets/javascripts/registry/explorer/constants/list.js
deleted file mode 100644
index d21a154d1b8..00000000000
--- a/app/assets/javascripts/registry/explorer/constants/list.js
+++ /dev/null
@@ -1,53 +0,0 @@
-import { s__, __ } from '~/locale';
-
-// Translations strings
-
-export const CONTAINER_REGISTRY_TITLE = s__('ContainerRegistry|Container Registry');
-export const CONNECTION_ERROR_TITLE = s__('ContainerRegistry|Docker connection error');
-export const CONNECTION_ERROR_MESSAGE = s__(
- `ContainerRegistry|We are having trouble connecting to the Container Registry. Please try refreshing the page. If this error persists, please review %{docLinkStart}the troubleshooting documentation%{docLinkEnd}.`,
-);
-export const LIST_INTRO_TEXT = s__(
- `ContainerRegistry|With the GitLab Container Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}`,
-);
-export const LIST_DELETE_BUTTON_DISABLED = s__(
- 'ContainerRegistry|Missing or insufficient permission, delete button disabled',
-);
-export const REMOVE_REPOSITORY_LABEL = s__('ContainerRegistry|Remove repository');
-export const REMOVE_REPOSITORY_MODAL_TEXT = s__(
- 'ContainerRegistry|You are about to remove repository %{title}. Once you confirm, this repository will be permanently deleted.',
-);
-export const ROW_SCHEDULED_FOR_DELETION = s__(
- `ContainerRegistry|This image repository is scheduled for deletion`,
-);
-export const FETCH_IMAGES_LIST_ERROR_MESSAGE = s__(
- 'ContainerRegistry|Something went wrong while fetching the repository list.',
-);
-export const FETCH_TAGS_LIST_ERROR_MESSAGE = s__(
- 'ContainerRegistry|Something went wrong while fetching the tags list.',
-);
-export const DELETE_IMAGE_ERROR_MESSAGE = s__(
- 'ContainerRegistry|Something went wrong while scheduling %{title} for deletion. Please try again.',
-);
-export const ASYNC_DELETE_IMAGE_ERROR_MESSAGE = s__(
- `ContainerRegistry|There was an error during the deletion of this image repository, please try again.`,
-);
-export const DELETE_IMAGE_SUCCESS_MESSAGE = s__(
- 'ContainerRegistry|%{title} was successfully scheduled for deletion',
-);
-export const EMPTY_RESULT_TITLE = s__('ContainerRegistry|Sorry, your filter produced no results.');
-export const EMPTY_RESULT_MESSAGE = s__(
- 'ContainerRegistry|To widen your search, change or remove the filters above.',
-);
-
-// Parameters
-
-export const IMAGE_DELETE_SCHEDULED_STATUS = 'DELETE_SCHEDULED';
-export const IMAGE_FAILED_DELETED_STATUS = 'DELETE_FAILED';
-export const GRAPHQL_PAGE_SIZE = 10;
-
-export const SORT_FIELDS = [
- { orderBy: 'UPDATED', label: __('Updated') },
- { orderBy: 'CREATED', label: __('Created') },
- { orderBy: 'NAME', label: __('Name') },
-];
diff --git a/app/assets/javascripts/registry/explorer/constants/quick_start.js b/app/assets/javascripts/registry/explorer/constants/quick_start.js
deleted file mode 100644
index 6a39c07eba2..00000000000
--- a/app/assets/javascripts/registry/explorer/constants/quick_start.js
+++ /dev/null
@@ -1,9 +0,0 @@
-import { s__ } from '~/locale';
-
-export const QUICK_START = s__('ContainerRegistry|CLI Commands');
-export const LOGIN_COMMAND_LABEL = s__('ContainerRegistry|Login');
-export const COPY_LOGIN_TITLE = s__('ContainerRegistry|Copy login command');
-export const BUILD_COMMAND_LABEL = s__('ContainerRegistry|Build an image');
-export const COPY_BUILD_TITLE = s__('ContainerRegistry|Copy build command');
-export const PUSH_COMMAND_LABEL = s__('ContainerRegistry|Push an image');
-export const COPY_PUSH_TITLE = s__('ContainerRegistry|Copy push command');
diff --git a/app/assets/javascripts/registry/explorer/graphql/index.js b/app/assets/javascripts/registry/explorer/graphql/index.js
deleted file mode 100644
index d934bcc7419..00000000000
--- a/app/assets/javascripts/registry/explorer/graphql/index.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import Vue from 'vue';
-import VueApollo from 'vue-apollo';
-import createDefaultClient from '~/lib/graphql';
-
-Vue.use(VueApollo);
-
-export const apolloProvider = new VueApollo({
- defaultClient: createDefaultClient(
- {},
- {
- batchMax: 1,
- assumeImmutableResults: true,
- },
- ),
-});
diff --git a/app/assets/javascripts/registry/explorer/graphql/mutations/delete_container_repository.mutation.graphql b/app/assets/javascripts/registry/explorer/graphql/mutations/delete_container_repository.mutation.graphql
deleted file mode 100644
index 4c88b726ee5..00000000000
--- a/app/assets/javascripts/registry/explorer/graphql/mutations/delete_container_repository.mutation.graphql
+++ /dev/null
@@ -1,9 +0,0 @@
-mutation destroyContainerRepository($id: ContainerRepositoryID!) {
- destroyContainerRepository(input: { id: $id }) {
- containerRepository {
- id
- status
- }
- errors
- }
-}
diff --git a/app/assets/javascripts/registry/explorer/graphql/mutations/delete_container_repository_tags.mutation.graphql b/app/assets/javascripts/registry/explorer/graphql/mutations/delete_container_repository_tags.mutation.graphql
deleted file mode 100644
index a31f2829e13..00000000000
--- a/app/assets/javascripts/registry/explorer/graphql/mutations/delete_container_repository_tags.mutation.graphql
+++ /dev/null
@@ -1,5 +0,0 @@
-mutation destroyContainerRepositoryTags($id: ContainerRepositoryID!, $tagNames: [String!]!) {
- destroyContainerRepositoryTags(input: { id: $id, tagNames: $tagNames }) {
- errors
- }
-}
diff --git a/app/assets/javascripts/registry/explorer/graphql/queries/get_container_repositories_details.query.graphql b/app/assets/javascripts/registry/explorer/graphql/queries/get_container_repositories_details.query.graphql
deleted file mode 100644
index 01cb7fa1cab..00000000000
--- a/app/assets/javascripts/registry/explorer/graphql/queries/get_container_repositories_details.query.graphql
+++ /dev/null
@@ -1,41 +0,0 @@
-query getContainerRepositoriesDetails(
- $fullPath: ID!
- $name: String
- $first: Int
- $last: Int
- $after: String
- $before: String
- $isGroupPage: Boolean!
- $sort: ContainerRepositorySort
-) {
- project(fullPath: $fullPath) @skip(if: $isGroupPage) {
- containerRepositories(
- name: $name
- after: $after
- before: $before
- first: $first
- last: $last
- sort: $sort
- ) {
- nodes {
- id
- tagsCount
- }
- }
- }
- group(fullPath: $fullPath) @include(if: $isGroupPage) {
- containerRepositories(
- name: $name
- after: $after
- before: $before
- first: $first
- last: $last
- sort: $sort
- ) {
- nodes {
- id
- tagsCount
- }
- }
- }
-}
diff --git a/app/assets/javascripts/registry/explorer/graphql/queries/get_container_repository_details.query.graphql b/app/assets/javascripts/registry/explorer/graphql/queries/get_container_repository_details.query.graphql
deleted file mode 100644
index b5a99fd9ac1..00000000000
--- a/app/assets/javascripts/registry/explorer/graphql/queries/get_container_repository_details.query.graphql
+++ /dev/null
@@ -1,22 +0,0 @@
-query getContainerRepositoryDetails($id: ID!) {
- containerRepository(id: $id) {
- id
- name
- path
- status
- location
- canDelete
- createdAt
- updatedAt
- expirationPolicyStartedAt
- expirationPolicyCleanupStatus
- project {
- visibility
- path
- containerExpirationPolicy {
- enabled
- nextRunAt
- }
- }
- }
-}
diff --git a/app/assets/javascripts/registry/explorer/graphql/queries/get_container_repository_tags.query.graphql b/app/assets/javascripts/registry/explorer/graphql/queries/get_container_repository_tags.query.graphql
deleted file mode 100644
index a703c2dd0ac..00000000000
--- a/app/assets/javascripts/registry/explorer/graphql/queries/get_container_repository_tags.query.graphql
+++ /dev/null
@@ -1,29 +0,0 @@
-#import "~/graphql_shared/fragments/pageInfo.fragment.graphql"
-
-query getContainerRepositoryTags(
- $id: ID!
- $first: Int
- $last: Int
- $after: String
- $before: String
-) {
- containerRepository(id: $id) {
- id
- tags(after: $after, before: $before, first: $first, last: $last) {
- nodes {
- digest
- location
- path
- name
- revision
- shortRevision
- createdAt
- totalSize
- canDelete
- }
- pageInfo {
- ...PageInfo
- }
- }
- }
-}
diff --git a/app/assets/javascripts/registry/explorer/graphql/queries/get_container_repository_tags_count.query.graphql b/app/assets/javascripts/registry/explorer/graphql/queries/get_container_repository_tags_count.query.graphql
deleted file mode 100644
index 9092a71edb0..00000000000
--- a/app/assets/javascripts/registry/explorer/graphql/queries/get_container_repository_tags_count.query.graphql
+++ /dev/null
@@ -1,6 +0,0 @@
-query getContainerRepositoryTagsCount($id: ID!) {
- containerRepository(id: $id) {
- id
- tagsCount
- }
-}
diff --git a/app/assets/javascripts/registry/explorer/index.js b/app/assets/javascripts/registry/explorer/index.js
deleted file mode 100644
index 246a6768593..00000000000
--- a/app/assets/javascripts/registry/explorer/index.js
+++ /dev/null
@@ -1,121 +0,0 @@
-import { GlToast } from '@gitlab/ui';
-import Vue from 'vue';
-import { parseBoolean } from '~/lib/utils/common_utils';
-import PerformancePlugin from '~/performance/vue_performance_plugin';
-import Translate from '~/vue_shared/translate';
-import RegistryBreadcrumb from './components/registry_breadcrumb.vue';
-import { apolloProvider } from './graphql/index';
-import RegistryExplorer from './pages/index.vue';
-import createRouter from './router';
-
-Vue.use(Translate);
-Vue.use(GlToast);
-
-Vue.use(PerformancePlugin, {
- components: [
- 'RegistryListPage',
- 'ListHeader',
- 'ImageListRow',
- 'RegistryDetailsPage',
- 'DetailsHeader',
- 'TagsList',
- ],
-});
-
-export default () => {
- const el = document.getElementById('js-container-registry');
-
- if (!el) {
- return null;
- }
-
- const {
- endpoint,
- expirationPolicy,
- isGroupPage,
- isAdmin,
- showCleanupPolicyOnAlert,
- showUnfinishedTagCleanupCallout,
- connectionError,
- invalidPathError,
- ...config
- } = el.dataset;
-
- // This is a mini state to help the breadcrumb have the correct name in the details page
- const breadCrumbState = Vue.observable({
- name: '',
- updateName(value) {
- this.name = value;
- },
- });
-
- const router = createRouter(endpoint, breadCrumbState);
-
- const attachMainComponent = () =>
- new Vue({
- el,
- router,
- apolloProvider,
- components: {
- RegistryExplorer,
- },
- provide() {
- return {
- breadCrumbState,
- config: {
- ...config,
- expirationPolicy: expirationPolicy ? JSON.parse(expirationPolicy) : undefined,
- isGroupPage: parseBoolean(isGroupPage),
- isAdmin: parseBoolean(isAdmin),
- showCleanupPolicyOnAlert: parseBoolean(showCleanupPolicyOnAlert),
- showUnfinishedTagCleanupCallout: parseBoolean(showUnfinishedTagCleanupCallout),
- connectionError: parseBoolean(connectionError),
- invalidPathError: parseBoolean(invalidPathError),
- },
- /* eslint-disable @gitlab/require-i18n-strings */
- dockerBuildCommand: `docker build -t ${config.repositoryUrl} .`,
- dockerPushCommand: `docker push ${config.repositoryUrl}`,
- dockerLoginCommand: `docker login ${config.registryHostUrlWithPort}`,
- /* eslint-enable @gitlab/require-i18n-strings */
- };
- },
- render(createElement) {
- return createElement('registry-explorer');
- },
- });
-
- const attachBreadcrumb = () => {
- const breadCrumbEls = document.querySelectorAll('nav .js-breadcrumbs-list li');
- const breadCrumbEl = breadCrumbEls[breadCrumbEls.length - 1];
- const crumbs = [breadCrumbEl.querySelector('h2')];
- const nestedBreadcrumbEl = document.createElement('div');
- breadCrumbEl.replaceChild(nestedBreadcrumbEl, breadCrumbEl.querySelector('h2'));
- return new Vue({
- el: nestedBreadcrumbEl,
- router,
- apolloProvider,
- components: {
- RegistryBreadcrumb,
- },
- render(createElement) {
- // FIXME(@tnir): this is a workaround until the MR gets merged:
- // https://gitlab.com/gitlab-org/gitlab/-/merge_requests/48115
- const parentEl = breadCrumbEl.parentElement.parentElement;
- if (parentEl) {
- parentEl.classList.remove('breadcrumbs-container');
- parentEl.classList.add('gl-display-flex');
- parentEl.classList.add('w-100');
- }
- // End of FIXME(@tnir)
- return createElement('registry-breadcrumb', {
- class: breadCrumbEl.className,
- props: {
- crumbs,
- },
- });
- },
- });
- };
-
- return { attachBreadcrumb, attachMainComponent };
-};
diff --git a/app/assets/javascripts/registry/explorer/pages/details.vue b/app/assets/javascripts/registry/explorer/pages/details.vue
deleted file mode 100644
index feabc4f770b..00000000000
--- a/app/assets/javascripts/registry/explorer/pages/details.vue
+++ /dev/null
@@ -1,233 +0,0 @@
-<script>
-import { GlResizeObserverDirective } from '@gitlab/ui';
-import { GlBreakpointInstance } from '@gitlab/ui/dist/utils';
-import createFlash from '~/flash';
-import axios from '~/lib/utils/axios_utils';
-import { joinPaths } from '~/lib/utils/url_utility';
-import Tracking from '~/tracking';
-import DeleteImage from '../components/delete_image.vue';
-import DeleteAlert from '../components/details_page/delete_alert.vue';
-import DeleteModal from '../components/details_page/delete_modal.vue';
-import DetailsHeader from '../components/details_page/details_header.vue';
-import EmptyState from '../components/details_page/empty_state.vue';
-import PartialCleanupAlert from '../components/details_page/partial_cleanup_alert.vue';
-import StatusAlert from '../components/details_page/status_alert.vue';
-import TagsList from '../components/details_page/tags_list.vue';
-import TagsLoader from '../components/details_page/tags_loader.vue';
-
-import {
- ALERT_SUCCESS_TAG,
- ALERT_DANGER_TAG,
- ALERT_SUCCESS_TAGS,
- ALERT_DANGER_TAGS,
- ALERT_DANGER_IMAGE,
- FETCH_IMAGES_LIST_ERROR_MESSAGE,
- UNFINISHED_STATUS,
- MISSING_OR_DELETED_IMAGE_BREADCRUMB,
- ROOT_IMAGE_TEXT,
-} from '../constants/index';
-import deleteContainerRepositoryTagsMutation from '../graphql/mutations/delete_container_repository_tags.mutation.graphql';
-import getContainerRepositoryDetailsQuery from '../graphql/queries/get_container_repository_details.query.graphql';
-
-export default {
- name: 'RegistryDetailsPage',
- components: {
- DeleteAlert,
- PartialCleanupAlert,
- DetailsHeader,
- DeleteModal,
- TagsList,
- TagsLoader,
- EmptyState,
- StatusAlert,
- DeleteImage,
- },
- directives: {
- GlResizeObserver: GlResizeObserverDirective,
- },
- mixins: [Tracking.mixin()],
- inject: ['breadCrumbState', 'config'],
- apollo: {
- containerRepository: {
- query: getContainerRepositoryDetailsQuery,
- variables() {
- return this.queryVariables;
- },
- result() {
- this.updateBreadcrumb();
- },
- error() {
- createFlash({ message: FETCH_IMAGES_LIST_ERROR_MESSAGE });
- },
- },
- },
- data() {
- return {
- containerRepository: {},
- itemsToBeDeleted: [],
- isMobile: false,
- mutationLoading: false,
- deleteAlertType: null,
- hidePartialCleanupWarning: false,
- deleteImageAlert: false,
- };
- },
- computed: {
- queryVariables() {
- return {
- id: joinPaths(this.config.gidPrefix, `${this.$route.params.id}`),
- };
- },
- isLoading() {
- return this.$apollo.queries.containerRepository.loading || this.mutationLoading;
- },
- showPartialCleanupWarning() {
- return (
- this.config.showUnfinishedTagCleanupCallout &&
- this.containerRepository?.expirationPolicyCleanupStatus === UNFINISHED_STATUS &&
- !this.hidePartialCleanupWarning
- );
- },
- tracking() {
- return {
- label:
- this.itemsToBeDeleted?.length > 1 ? 'bulk_registry_tag_delete' : 'registry_tag_delete',
- };
- },
- pageActionsAreDisabled() {
- return Boolean(this.containerRepository?.status);
- },
- },
- methods: {
- updateBreadcrumb() {
- const name = this.containerRepository?.id
- ? this.containerRepository?.name || ROOT_IMAGE_TEXT
- : MISSING_OR_DELETED_IMAGE_BREADCRUMB;
- this.breadCrumbState.updateName(name);
- },
- deleteTags(toBeDeleted) {
- this.deleteImageAlert = false;
- this.itemsToBeDeleted = toBeDeleted;
- this.track('click_button');
- this.$refs.deleteModal.show();
- },
- confirmDelete() {
- if (this.deleteImageAlert) {
- this.$refs.deleteImage.doDelete();
- } else {
- this.handleDeleteTag();
- }
- },
- async handleDeleteTag() {
- this.track('confirm_delete');
- const { itemsToBeDeleted } = this;
- this.itemsToBeDeleted = [];
- this.mutationLoading = true;
- try {
- const { data } = await this.$apollo.mutate({
- mutation: deleteContainerRepositoryTagsMutation,
- variables: {
- id: this.queryVariables.id,
- tagNames: itemsToBeDeleted.map((i) => i.name),
- },
- awaitRefetchQueries: true,
- refetchQueries: [
- {
- query: getContainerRepositoryDetailsQuery,
- variables: this.queryVariables,
- },
- ],
- });
-
- if (data?.destroyContainerRepositoryTags?.errors[0]) {
- throw new Error();
- }
- this.deleteAlertType =
- itemsToBeDeleted.length === 0 ? ALERT_SUCCESS_TAG : ALERT_SUCCESS_TAGS;
- } catch (e) {
- this.deleteAlertType = itemsToBeDeleted.length === 0 ? ALERT_DANGER_TAG : ALERT_DANGER_TAGS;
- }
-
- this.mutationLoading = false;
- },
- handleResize() {
- this.isMobile = GlBreakpointInstance.getBreakpointSize() === 'xs';
- },
- dismissPartialCleanupWarning() {
- this.hidePartialCleanupWarning = true;
- axios.post(this.config.userCalloutsPath, {
- feature_name: this.config.userCalloutId,
- });
- },
- deleteImage() {
- this.deleteImageAlert = true;
- this.itemsToBeDeleted = [{ ...this.containerRepository }];
- this.$refs.deleteModal.show();
- },
- deleteImageError() {
- this.deleteAlertType = ALERT_DANGER_IMAGE;
- },
- deleteImageIniit() {
- this.itemsToBeDeleted = [];
- this.mutationLoading = true;
- },
- },
-};
-</script>
-
-<template>
- <div v-gl-resize-observer="handleResize" class="gl-my-3">
- <template v-if="containerRepository">
- <delete-alert
- v-model="deleteAlertType"
- :garbage-collection-help-page-path="config.garbageCollectionHelpPagePath"
- :is-admin="config.isAdmin"
- class="gl-my-2"
- />
-
- <partial-cleanup-alert
- v-if="showPartialCleanupWarning"
- :run-cleanup-policies-help-page-path="config.runCleanupPoliciesHelpPagePath"
- :cleanup-policies-help-page-path="config.expirationPolicyHelpPagePath"
- @dismiss="dismissPartialCleanupWarning"
- />
-
- <status-alert v-if="containerRepository.status" :status="containerRepository.status" />
-
- <details-header
- v-if="!isLoading"
- :image="containerRepository"
- :disabled="pageActionsAreDisabled"
- @delete="deleteImage"
- />
-
- <tags-loader v-if="isLoading" />
- <tags-list
- v-else
- :id="$route.params.id"
- :is-image-loading="isLoading"
- :is-mobile="isMobile"
- :disabled="pageActionsAreDisabled"
- @delete="deleteTags"
- />
-
- <delete-image
- :id="containerRepository.id"
- ref="deleteImage"
- use-update-fn
- @start="deleteImageIniit"
- @error="deleteImageError"
- @end="mutationLoading = false"
- />
-
- <delete-modal
- ref="deleteModal"
- :items-to-be-deleted="itemsToBeDeleted"
- :delete-image="deleteImageAlert"
- @confirmDelete="confirmDelete"
- @cancel="track('cancel_delete')"
- />
- </template>
- <empty-state v-else is-empty-image :no-containers-image="config.noContainersImage" />
- </div>
-</template>
diff --git a/app/assets/javascripts/registry/explorer/pages/index.vue b/app/assets/javascripts/registry/explorer/pages/index.vue
deleted file mode 100644
index dca63e1a569..00000000000
--- a/app/assets/javascripts/registry/explorer/pages/index.vue
+++ /dev/null
@@ -1,5 +0,0 @@
-<template>
- <div>
- <router-view ref="router-view" />
- </div>
-</template>
diff --git a/app/assets/javascripts/registry/explorer/pages/list.vue b/app/assets/javascripts/registry/explorer/pages/list.vue
deleted file mode 100644
index 73b957f42f2..00000000000
--- a/app/assets/javascripts/registry/explorer/pages/list.vue
+++ /dev/null
@@ -1,409 +0,0 @@
-<script>
-import {
- GlEmptyState,
- GlTooltipDirective,
- GlModal,
- GlSprintf,
- GlLink,
- GlAlert,
- GlSkeletonLoader,
-} from '@gitlab/ui';
-import { get } from 'lodash';
-import getContainerRepositoriesQuery from 'shared_queries/container_registry/get_container_repositories.query.graphql';
-import createFlash from '~/flash';
-import CleanupPolicyEnabledAlert from '~/packages_and_registries/shared/components/cleanup_policy_enabled_alert.vue';
-import { FILTERED_SEARCH_TERM } from '~/packages_and_registries/shared/constants';
-import { extractFilterAndSorting } from '~/packages_and_registries/shared/utils';
-import Tracking from '~/tracking';
-import RegistrySearch from '~/vue_shared/components/registry/registry_search.vue';
-import DeleteImage from '../components/delete_image.vue';
-import RegistryHeader from '../components/list_page/registry_header.vue';
-
-import {
- DELETE_IMAGE_SUCCESS_MESSAGE,
- DELETE_IMAGE_ERROR_MESSAGE,
- CONNECTION_ERROR_TITLE,
- CONNECTION_ERROR_MESSAGE,
- REMOVE_REPOSITORY_MODAL_TEXT,
- REMOVE_REPOSITORY_LABEL,
- EMPTY_RESULT_TITLE,
- EMPTY_RESULT_MESSAGE,
- GRAPHQL_PAGE_SIZE,
- FETCH_IMAGES_LIST_ERROR_MESSAGE,
- SORT_FIELDS,
-} from '../constants/index';
-import getContainerRepositoriesDetails from '../graphql/queries/get_container_repositories_details.query.graphql';
-
-export default {
- name: 'RegistryListPage',
- components: {
- GlEmptyState,
- ProjectEmptyState: () =>
- import(
- /* webpackChunkName: 'container_registry_components' */ '../components/list_page/project_empty_state.vue'
- ),
- GroupEmptyState: () =>
- import(
- /* webpackChunkName: 'container_registry_components' */ '../components/list_page/group_empty_state.vue'
- ),
- ImageList: () =>
- import(
- /* webpackChunkName: 'container_registry_components' */ '../components/list_page/image_list.vue'
- ),
- CliCommands: () =>
- import(
- /* webpackChunkName: 'container_registry_components' */ '../components/list_page/cli_commands.vue'
- ),
- GlModal,
- GlSprintf,
- GlLink,
- GlAlert,
- GlSkeletonLoader,
- RegistryHeader,
- DeleteImage,
- RegistrySearch,
- CleanupPolicyEnabledAlert,
- },
- directives: {
- GlTooltip: GlTooltipDirective,
- },
- mixins: [Tracking.mixin()],
- inject: ['config'],
- loader: {
- repeat: 10,
- width: 1000,
- height: 40,
- },
- i18n: {
- CONNECTION_ERROR_TITLE,
- CONNECTION_ERROR_MESSAGE,
- REMOVE_REPOSITORY_MODAL_TEXT,
- REMOVE_REPOSITORY_LABEL,
- EMPTY_RESULT_TITLE,
- EMPTY_RESULT_MESSAGE,
- },
- searchConfig: SORT_FIELDS,
- apollo: {
- baseImages: {
- skip() {
- return !this.fetchBaseQuery;
- },
- query: getContainerRepositoriesQuery,
- variables() {
- return this.queryVariables;
- },
- update(data) {
- return data[this.graphqlResource]?.containerRepositories.nodes;
- },
- result({ data }) {
- this.pageInfo = data[this.graphqlResource]?.containerRepositories?.pageInfo;
- this.containerRepositoriesCount = data[this.graphqlResource]?.containerRepositoriesCount;
- },
- error() {
- createFlash({ message: FETCH_IMAGES_LIST_ERROR_MESSAGE });
- },
- },
- additionalDetails: {
- skip() {
- return !this.fetchAdditionalDetails;
- },
- query: getContainerRepositoriesDetails,
- variables() {
- return this.queryVariables;
- },
- update(data) {
- return data[this.graphqlResource]?.containerRepositories.nodes;
- },
- error() {
- createFlash({ message: FETCH_IMAGES_LIST_ERROR_MESSAGE });
- },
- },
- },
- data() {
- return {
- baseImages: [],
- additionalDetails: [],
- pageInfo: {},
- containerRepositoriesCount: 0,
- itemToDelete: {},
- deleteAlertType: null,
- filter: [],
- sorting: { orderBy: 'UPDATED', sort: 'desc' },
- name: null,
- mutationLoading: false,
- fetchBaseQuery: false,
- fetchAdditionalDetails: false,
- };
- },
- computed: {
- images() {
- if (this.baseImages) {
- return this.baseImages.map((image, index) => ({
- ...image,
- ...get(this.additionalDetails, index, {}),
- }));
- }
- return [];
- },
- graphqlResource() {
- return this.config.isGroupPage ? 'group' : 'project';
- },
- queryVariables() {
- return {
- name: this.name,
- sort: this.sortBy,
- fullPath: this.config.isGroupPage ? this.config.groupPath : this.config.projectPath,
- isGroupPage: this.config.isGroupPage,
- first: GRAPHQL_PAGE_SIZE,
- };
- },
- tracking() {
- return {
- label: 'registry_repository_delete',
- };
- },
- isLoading() {
- return this.$apollo.queries.baseImages.loading || this.mutationLoading;
- },
- showCommands() {
- return Boolean(!this.isLoading && !this.config?.isGroupPage && this.images?.length);
- },
- showDeleteAlert() {
- return this.deleteAlertType && this.itemToDelete?.path;
- },
- showConnectionError() {
- return this.config.connectionError || this.config.invalidPathError;
- },
- deleteImageAlertMessage() {
- return this.deleteAlertType === 'success'
- ? DELETE_IMAGE_SUCCESS_MESSAGE
- : DELETE_IMAGE_ERROR_MESSAGE;
- },
- sortBy() {
- const { orderBy, sort } = this.sorting;
- return `${orderBy}_${sort}`.toUpperCase();
- },
- },
- mounted() {
- const { sorting, filters } = extractFilterAndSorting(this.$route.query);
-
- this.filter = [...filters];
- this.name = filters[0]?.value.data;
- this.sorting = { ...this.sorting, ...sorting };
-
- // If the two graphql calls - which are not batched - resolve togheter we will have a race
- // condition when apollo sets the cache, with this we give the 'base' call an headstart
- this.fetchBaseQuery = true;
- setTimeout(() => {
- this.fetchAdditionalDetails = true;
- }, 200);
- },
- methods: {
- deleteImage(item) {
- this.track('click_button');
- this.itemToDelete = item;
- this.$refs.deleteModal.show();
- },
- dismissDeleteAlert() {
- this.deleteAlertType = null;
- this.itemToDelete = {};
- },
- updateQuery(_, { fetchMoreResult }) {
- return fetchMoreResult;
- },
- async fetchNextPage() {
- if (this.pageInfo?.hasNextPage) {
- const variables = {
- after: this.pageInfo?.endCursor,
- first: GRAPHQL_PAGE_SIZE,
- };
-
- this.$apollo.queries.baseImages.fetchMore({
- variables,
- updateQuery: this.updateQuery,
- });
-
- await this.$nextTick();
-
- this.$apollo.queries.additionalDetails.fetchMore({
- variables,
- updateQuery: this.updateQuery,
- });
- }
- },
- async fetchPreviousPage() {
- if (this.pageInfo?.hasPreviousPage) {
- const variables = {
- first: null,
- before: this.pageInfo?.startCursor,
- last: GRAPHQL_PAGE_SIZE,
- };
- this.$apollo.queries.baseImages.fetchMore({
- variables,
- updateQuery: this.updateQuery,
- });
-
- await this.$nextTick();
-
- this.$apollo.queries.additionalDetails.fetchMore({
- variables,
- updateQuery: this.updateQuery,
- });
- }
- },
- startDelete() {
- this.track('confirm_delete');
- this.mutationLoading = true;
- },
- updateSorting(value) {
- this.sorting = {
- ...this.sorting,
- ...value,
- };
- },
- doFilter() {
- const search = this.filter.find((i) => i.type === FILTERED_SEARCH_TERM);
- this.name = search?.value?.data;
- },
- updateUrlQueryString(query) {
- this.$router.push({ query });
- },
- },
-};
-</script>
-
-<template>
- <div>
- <gl-alert
- v-if="showDeleteAlert"
- :variant="deleteAlertType"
- class="gl-mt-5"
- dismissible
- @dismiss="dismissDeleteAlert"
- >
- <gl-sprintf :message="deleteImageAlertMessage">
- <template #title>
- {{ itemToDelete.path }}
- </template>
- </gl-sprintf>
- </gl-alert>
-
- <cleanup-policy-enabled-alert
- v-if="config.showCleanupPolicyOnAlert"
- :project-path="config.projectPath"
- :cleanup-policies-settings-path="config.cleanupPoliciesSettingsPath"
- />
-
- <gl-empty-state
- v-if="showConnectionError"
- :title="$options.i18n.CONNECTION_ERROR_TITLE"
- :svg-path="config.containersErrorImage"
- >
- <template #description>
- <p>
- <gl-sprintf :message="$options.i18n.CONNECTION_ERROR_MESSAGE">
- <template #docLink="{ content }">
- <gl-link :href="`${config.helpPagePath}#docker-connection-error`" target="_blank">
- {{ content }}
- </gl-link>
- </template>
- </gl-sprintf>
- </p>
- </template>
- </gl-empty-state>
-
- <template v-else>
- <registry-header
- :metadata-loading="isLoading"
- :images-count="containerRepositoriesCount"
- :expiration-policy="config.expirationPolicy"
- :help-page-path="config.helpPagePath"
- :hide-expiration-policy-data="config.isGroupPage"
- >
- <template #commands>
- <cli-commands v-if="showCommands" />
- </template>
- </registry-header>
-
- <registry-search
- :filter="filter"
- :sorting="sorting"
- :tokens="[]"
- :sortable-fields="$options.searchConfig"
- @sorting:changed="updateSorting"
- @filter:changed="filter = $event"
- @filter:submit="doFilter"
- @query:changed="updateUrlQueryString"
- />
-
- <div v-if="isLoading" class="gl-mt-5">
- <gl-skeleton-loader
- v-for="index in $options.loader.repeat"
- :key="index"
- :width="$options.loader.width"
- :height="$options.loader.height"
- preserve-aspect-ratio="xMinYMax meet"
- >
- <rect width="500" x="10" y="10" height="20" rx="4" />
- <circle cx="525" cy="20" r="10" />
- <rect x="960" y="0" width="40" height="40" rx="4" />
- </gl-skeleton-loader>
- </div>
- <template v-else>
- <template v-if="images.length > 0 || name">
- <image-list
- v-if="images.length"
- :images="images"
- :metadata-loading="$apollo.queries.additionalDetails.loading"
- :page-info="pageInfo"
- @delete="deleteImage"
- @prev-page="fetchPreviousPage"
- @next-page="fetchNextPage"
- />
-
- <gl-empty-state
- v-else
- :svg-path="config.noContainersImage"
- data-testid="emptySearch"
- :title="$options.i18n.EMPTY_RESULT_TITLE"
- >
- <template #description>
- {{ $options.i18n.EMPTY_RESULT_MESSAGE }}
- </template>
- </gl-empty-state>
- </template>
- <template v-else>
- <project-empty-state v-if="!config.isGroupPage" />
- <group-empty-state v-else />
- </template>
- </template>
-
- <delete-image
- :id="itemToDelete.id"
- @start="startDelete"
- @error="deleteAlertType = 'danger'"
- @success="deleteAlertType = 'success'"
- @end="mutationLoading = false"
- >
- <template #default="{ doDelete }">
- <gl-modal
- ref="deleteModal"
- modal-id="delete-image-modal"
- :action-primary="{ text: __('Remove'), attributes: { variant: 'danger' } }"
- @primary="doDelete"
- @cancel="track('cancel_delete')"
- >
- <template #modal-title>{{ $options.i18n.REMOVE_REPOSITORY_LABEL }}</template>
- <p>
- <gl-sprintf :message="$options.i18n.REMOVE_REPOSITORY_MODAL_TEXT">
- <template #title>
- <b>{{ itemToDelete.path }}</b>
- </template>
- </gl-sprintf>
- </p>
- </gl-modal>
- </template>
- </delete-image>
- </template>
- </div>
-</template>
diff --git a/app/assets/javascripts/registry/explorer/router.js b/app/assets/javascripts/registry/explorer/router.js
deleted file mode 100644
index a0c4417d549..00000000000
--- a/app/assets/javascripts/registry/explorer/router.js
+++ /dev/null
@@ -1,35 +0,0 @@
-import Vue from 'vue';
-import VueRouter from 'vue-router';
-import { CONTAINER_REGISTRY_TITLE } from './constants/index';
-import Details from './pages/details.vue';
-import List from './pages/list.vue';
-
-Vue.use(VueRouter);
-
-export default function createRouter(base, breadCrumbState) {
- const router = new VueRouter({
- base,
- mode: 'history',
- routes: [
- {
- name: 'list',
- path: '/',
- component: List,
- meta: {
- nameGenerator: () => CONTAINER_REGISTRY_TITLE,
- root: true,
- },
- },
- {
- name: 'details',
- path: '/:id',
- component: Details,
- meta: {
- nameGenerator: () => breadCrumbState.name,
- },
- },
- ],
- });
-
- return router;
-}