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/list/components')
-rw-r--r--app/assets/javascripts/packages/list/components/packages_filter.vue21
-rw-r--r--app/assets/javascripts/packages/list/components/packages_list.vue129
-rw-r--r--app/assets/javascripts/packages/list/components/packages_list_app.vue111
-rw-r--r--app/assets/javascripts/packages/list/components/packages_sort.vue60
4 files changed, 321 insertions, 0 deletions
diff --git a/app/assets/javascripts/packages/list/components/packages_filter.vue b/app/assets/javascripts/packages/list/components/packages_filter.vue
new file mode 100644
index 00000000000..17398071217
--- /dev/null
+++ b/app/assets/javascripts/packages/list/components/packages_filter.vue
@@ -0,0 +1,21 @@
+<script>
+import { GlSearchBoxByClick } from '@gitlab/ui';
+import { mapActions } from 'vuex';
+
+export default {
+ components: {
+ GlSearchBoxByClick,
+ },
+ methods: {
+ ...mapActions(['setFilter']),
+ },
+};
+</script>
+
+<template>
+ <gl-search-box-by-click
+ :placeholder="s__('PackageRegistry|Filter by name')"
+ @submit="$emit('filter')"
+ @input="setFilter"
+ />
+</template>
diff --git a/app/assets/javascripts/packages/list/components/packages_list.vue b/app/assets/javascripts/packages/list/components/packages_list.vue
new file mode 100644
index 00000000000..b26c6087e14
--- /dev/null
+++ b/app/assets/javascripts/packages/list/components/packages_list.vue
@@ -0,0 +1,129 @@
+<script>
+import { mapState, mapGetters } from 'vuex';
+import { GlPagination, GlModal, GlSprintf } from '@gitlab/ui';
+import Tracking from '~/tracking';
+import { s__ } from '~/locale';
+import { TrackingActions } from '../../shared/constants';
+import { packageTypeToTrackCategory } from '../../shared/utils';
+import PackagesListLoader from '../../shared/components/packages_list_loader.vue';
+import PackagesListRow from '../../shared/components/package_list_row.vue';
+
+export default {
+ components: {
+ GlPagination,
+ GlModal,
+ GlSprintf,
+ PackagesListLoader,
+ PackagesListRow,
+ },
+ mixins: [Tracking.mixin()],
+ data() {
+ return {
+ itemToBeDeleted: null,
+ };
+ },
+ computed: {
+ ...mapState({
+ perPage: state => state.pagination.perPage,
+ totalItems: state => state.pagination.total,
+ page: state => state.pagination.page,
+ isGroupPage: state => state.config.isGroupPage,
+ isLoading: 'isLoading',
+ }),
+ ...mapGetters({ list: 'getList' }),
+ currentPage: {
+ get() {
+ return this.page;
+ },
+ set(value) {
+ this.$emit('page:changed', value);
+ },
+ },
+ isListEmpty() {
+ return !this.list || this.list.length === 0;
+ },
+ modalAction() {
+ return s__('PackageRegistry|Delete package');
+ },
+ deletePackageName() {
+ return this.itemToBeDeleted?.name ?? '';
+ },
+ tracking() {
+ const category = this.itemToBeDeleted
+ ? packageTypeToTrackCategory(this.itemToBeDeleted.package_type)
+ : undefined;
+ return {
+ category,
+ };
+ },
+ },
+ methods: {
+ setItemToBeDeleted(item) {
+ this.itemToBeDeleted = { ...item };
+ this.track(TrackingActions.REQUEST_DELETE_PACKAGE);
+ this.$refs.packageListDeleteModal.show();
+ },
+ deleteItemConfirmation() {
+ this.$emit('package:delete', this.itemToBeDeleted);
+ this.track(TrackingActions.DELETE_PACKAGE);
+ this.itemToBeDeleted = null;
+ },
+ deleteItemCanceled() {
+ this.track(TrackingActions.CANCEL_DELETE_PACKAGE);
+ this.itemToBeDeleted = null;
+ },
+ },
+ i18n: {
+ deleteModalContent: s__(
+ 'PackageRegistry|You are about to delete %{name}, this operation is irreversible, are you sure?',
+ ),
+ },
+};
+</script>
+
+<template>
+ <div class="d-flex flex-column">
+ <slot v-if="isListEmpty && !isLoading" name="empty-state"></slot>
+
+ <div v-else-if="isLoading">
+ <packages-list-loader :is-group="isGroupPage" />
+ </div>
+
+ <template v-else>
+ <div data-qa-selector="packages-table">
+ <packages-list-row
+ v-for="packageEntity in list"
+ :key="packageEntity.id"
+ :package-entity="packageEntity"
+ :package-link="packageEntity._links.web_path"
+ :is-group="isGroupPage"
+ @packageToDelete="setItemToBeDeleted"
+ />
+ </div>
+
+ <gl-pagination
+ v-model="currentPage"
+ :per-page="perPage"
+ :total-items="totalItems"
+ align="center"
+ class="w-100 mt-2"
+ />
+
+ <gl-modal
+ ref="packageListDeleteModal"
+ modal-id="confirm-delete-pacakge"
+ ok-variant="danger"
+ @ok="deleteItemConfirmation"
+ @cancel="deleteItemCanceled"
+ >
+ <template #modal-title>{{ modalAction }}</template>
+ <template #modal-ok>{{ modalAction }}</template>
+ <gl-sprintf :message="$options.i18n.deleteModalContent">
+ <template #name>
+ <strong>{{ deletePackageName }}</strong>
+ </template>
+ </gl-sprintf>
+ </gl-modal>
+ </template>
+ </div>
+</template>
diff --git a/app/assets/javascripts/packages/list/components/packages_list_app.vue b/app/assets/javascripts/packages/list/components/packages_list_app.vue
new file mode 100644
index 00000000000..ef242ea5f75
--- /dev/null
+++ b/app/assets/javascripts/packages/list/components/packages_list_app.vue
@@ -0,0 +1,111 @@
+<script>
+import { mapActions, mapState } from 'vuex';
+import { GlEmptyState, GlTab, GlTabs, GlLink, GlSprintf } from '@gitlab/ui';
+import { s__, sprintf } from '~/locale';
+import PackageFilter from './packages_filter.vue';
+import PackageList from './packages_list.vue';
+import PackageSort from './packages_sort.vue';
+import { PACKAGE_REGISTRY_TABS } from '../constants';
+import PackagesComingSoon from '../coming_soon/packages_coming_soon.vue';
+
+export default {
+ components: {
+ GlEmptyState,
+ GlTab,
+ GlTabs,
+ GlLink,
+ GlSprintf,
+ PackageFilter,
+ PackageList,
+ PackageSort,
+ PackagesComingSoon,
+ },
+ computed: {
+ ...mapState({
+ emptyListIllustration: state => state.config.emptyListIllustration,
+ emptyListHelpUrl: state => state.config.emptyListHelpUrl,
+ comingSoon: state => state.config.comingSoon,
+ filterQuery: state => state.filterQuery,
+ selectedType: state => state.selectedType,
+ }),
+ tabsToRender() {
+ return PACKAGE_REGISTRY_TABS;
+ },
+ },
+ mounted() {
+ this.requestPackagesList();
+ },
+ methods: {
+ ...mapActions(['requestPackagesList', 'requestDeletePackage', 'setSelectedType']),
+ onPageChanged(page) {
+ return this.requestPackagesList({ page });
+ },
+ onPackageDeleteRequest(item) {
+ return this.requestDeletePackage(item);
+ },
+ tabChanged(index) {
+ const selected = PACKAGE_REGISTRY_TABS[index];
+
+ if (selected !== this.selectedType) {
+ this.setSelectedType(selected);
+ this.requestPackagesList();
+ }
+ },
+ emptyStateTitle({ title, type }) {
+ if (this.filterQuery) {
+ return s__('PackageRegistry|Sorry, your filter produced no results');
+ }
+
+ if (type) {
+ return sprintf(s__('PackageRegistry|There are no %{packageType} packages yet'), {
+ packageType: title,
+ });
+ }
+
+ return s__('PackageRegistry|There are no packages yet');
+ },
+ },
+ i18n: {
+ widenFilters: s__('PackageRegistry|To widen your search, change or remove the filters above.'),
+ noResults: s__(
+ 'PackageRegistry|Learn how to %{noPackagesLinkStart}publish and share your packages%{noPackagesLinkEnd} with GitLab.',
+ ),
+ },
+};
+</script>
+
+<template>
+ <gl-tabs @input="tabChanged">
+ <template #tabs-end>
+ <div class="d-flex align-self-center ml-md-auto py-1 py-md-0">
+ <package-filter class="mr-1" @filter="requestPackagesList" />
+ <package-sort @sort:changed="requestPackagesList" />
+ </div>
+ </template>
+
+ <gl-tab v-for="(tab, index) in tabsToRender" :key="index" :title="tab.title">
+ <package-list @page:changed="onPageChanged" @package:delete="onPackageDeleteRequest">
+ <template #empty-state>
+ <gl-empty-state :title="emptyStateTitle(tab)" :svg-path="emptyListIllustration">
+ <template #description>
+ <gl-sprintf v-if="filterQuery" :message="$options.i18n.widenFilters" />
+ <gl-sprintf v-else :message="$options.i18n.noResults">
+ <template #noPackagesLink="{content}">
+ <gl-link :href="emptyListHelpUrl" target="_blank">{{ content }}</gl-link>
+ </template>
+ </gl-sprintf>
+ </template>
+ </gl-empty-state>
+ </template>
+ </package-list>
+ </gl-tab>
+
+ <gl-tab v-if="comingSoon" :title="__('Coming soon')" lazy>
+ <packages-coming-soon
+ :illustration="emptyListIllustration"
+ :project-path="comingSoon.projectPath"
+ :suggested-contributions-path="comingSoon.suggestedContributions"
+ />
+ </gl-tab>
+ </gl-tabs>
+</template>
diff --git a/app/assets/javascripts/packages/list/components/packages_sort.vue b/app/assets/javascripts/packages/list/components/packages_sort.vue
new file mode 100644
index 00000000000..fa8f4f39d54
--- /dev/null
+++ b/app/assets/javascripts/packages/list/components/packages_sort.vue
@@ -0,0 +1,60 @@
+<script>
+import { GlSorting, GlSortingItem } from '@gitlab/ui';
+import { mapState, mapActions } from 'vuex';
+import { ASCENDING_ODER, DESCENDING_ORDER } from '../constants';
+import getTableHeaders from '../utils';
+
+export default {
+ name: 'PackageSort',
+ components: {
+ GlSorting,
+ GlSortingItem,
+ },
+ computed: {
+ ...mapState({
+ isGroupPage: state => state.config.isGroupPage,
+ orderBy: state => state.sorting.orderBy,
+ sort: state => state.sorting.sort,
+ }),
+ sortText() {
+ const field = this.sortableFields.find(s => s.orderBy === this.orderBy);
+ return field ? field.label : '';
+ },
+ sortableFields() {
+ return getTableHeaders(this.isGroupPage);
+ },
+ isSortAscending() {
+ return this.sort === ASCENDING_ODER;
+ },
+ },
+ methods: {
+ ...mapActions(['setSorting']),
+ onDirectionChange() {
+ const sort = this.isSortAscending ? DESCENDING_ORDER : ASCENDING_ODER;
+ this.setSorting({ sort });
+ this.$emit('sort:changed');
+ },
+ onSortItemClick(item) {
+ this.setSorting({ orderBy: item });
+ this.$emit('sort:changed');
+ },
+ },
+};
+</script>
+
+<template>
+ <gl-sorting
+ :text="sortText"
+ :is-ascending="isSortAscending"
+ @sortDirectionChange="onDirectionChange"
+ >
+ <gl-sorting-item
+ v-for="item in sortableFields"
+ ref="packageListSortItem"
+ :key="item.key"
+ @click="onSortItemClick(item.orderBy)"
+ >
+ {{ item.label }}
+ </gl-sorting-item>
+ </gl-sorting>
+</template>