diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-05-04 18:17:13 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-05-04 18:17:13 +0300 |
commit | 4bdfcf93f224edb9c4daff90d95b0c6c92766ea3 (patch) | |
tree | cedf1f94561571d00033c48846ad3959af64449b /app/assets/javascripts/import_entities | |
parent | fb5d3cceb8d43f8c2dc22a5d8c74327e9397f2e8 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/import_entities')
7 files changed, 229 insertions, 43 deletions
diff --git a/app/assets/javascripts/import_entities/import_projects/components/github_organizations_box.vue b/app/assets/javascripts/import_entities/import_projects/components/github_organizations_box.vue new file mode 100644 index 00000000000..5d5965e33da --- /dev/null +++ b/app/assets/javascripts/import_entities/import_projects/components/github_organizations_box.vue @@ -0,0 +1,73 @@ +<script> +import * as Sentry from '@sentry/browser'; +import { GlCollapsibleListbox } from '@gitlab/ui'; +import { createAlert } from '~/alert'; +import { __, s__ } from '~/locale'; +import axios from '~/lib/utils/axios_utils'; + +export default { + components: { + GlCollapsibleListbox, + }, + inject: ['statusImportGithubGroupPath'], + props: { + value: { + type: String, + required: true, + }, + }, + data() { + return { organizationsLoading: true, organizations: [], organizationFilter: '' }; + }, + computed: { + toggleText() { + return this.value || this.$options.i18n.allOrganizations; + }, + dropdownItems() { + return [ + { text: this.$options.i18n.allOrganizations, value: '' }, + ...this.organizations + .filter((entry) => + entry.name.toLowerCase().includes(this.organizationFilter.toLowerCase()), + ) + .map((entry) => ({ + text: entry.name, + value: entry.name, + })), + ]; + }, + }, + async mounted() { + try { + this.organizationsLoading = true; + const { + data: { provider_groups: organizations }, + } = await axios.get(this.statusImportGithubGroupPath); + this.organizations = organizations; + } catch (e) { + createAlert({ + message: __('Something went wrong on our end.'), + }); + Sentry.captureException(e); + } finally { + this.organizationsLoading = false; + } + }, + i18n: { + allOrganizations: s__('ImportProjects|All organizations'), + }, +}; +</script> +<template> + <gl-collapsible-listbox + :loading="organizationsLoading" + :toggle-text="toggleText" + :header-text="s__('ImportProjects|Organizations')" + :items="dropdownItems" + searchable + role="button" + tabindex="0" + @search="organizationFilter = $event" + @select="$emit('input', $event)" + /> +</template> diff --git a/app/assets/javascripts/import_entities/import_projects/components/github_status_table.vue b/app/assets/javascripts/import_entities/import_projects/components/github_status_table.vue new file mode 100644 index 00000000000..20dcd0356cd --- /dev/null +++ b/app/assets/javascripts/import_entities/import_projects/components/github_status_table.vue @@ -0,0 +1,104 @@ +<script> +import { GlButton, GlSearchBoxByClick, GlTabs, GlTab } from '@gitlab/ui'; +import { mapActions, mapGetters, mapState } from 'vuex'; +import { s__ } from '~/locale'; +import ImportProjectsTable from './import_projects_table.vue'; +import GithubOrganizationsBox from './github_organizations_box.vue'; + +export default { + components: { + ImportProjectsTable, + GithubOrganizationsBox, + GlButton, + GlSearchBoxByClick, + GlTab, + GlTabs, + }, + inheritAttrs: false, + data() { + return { + selectedRelationTypeTabIdx: 0, + }; + }, + computed: { + ...mapState({ + selectedOrganization: (state) => state.filter.organization_login ?? '', + nameFilter: (state) => state.filter.filter ?? '', + }), + ...mapGetters(['isImportingAnyRepo', 'hasImportableRepos']), + isNameFilterDisabled() { + return ( + this.$options.relationTypes[this.selectedRelationTypeTabIdx].showOrganizationFilter && + !this.selectedOrganization + ); + }, + }, + watch: { + selectedRelationTypeTabIdx: { + immediate: true, + handler(newIdx) { + const { backendFilter } = this.$options.relationTypes[newIdx]; + this.setFilter({ ...backendFilter, organization_login: '', filter: '' }); + }, + }, + }, + methods: { + ...mapActions(['setFilter']), + selectOrganization(org) { + this.selectedOrganization = org; + this.setFilter(); + }, + }, + + relationTypes: [ + { title: s__('ImportProjects|Owned'), backendFilter: { relation_type: 'owned' } }, + { title: s__('ImportProjects|Collaborated'), backendFilter: { relation_type: 'collaborated' } }, + { + title: s__('ImportProjects|Organization'), + backendFilter: { relation_type: 'organization' }, + showOrganizationFilter: true, + }, + ], +}; +</script> +<template> + <import-projects-table v-bind="$attrs"> + <template #filter="{ importAllButtonText, showImportAllModal }"> + <gl-tabs v-model="selectedRelationTypeTabIdx" content-class="gl-py-0! gl-mb-3"> + <gl-tab v-for="tab in $options.relationTypes" :key="tab.title" :title="tab.title"> + <div + class="gl-display-flex gl-justify-content-space-between gl-flex-wrap gl-gap-3 gl-p-5 gl-bg-gray-10 gl-border-solid gl-border-0 gl-border-b-gray-100 gl-border-b-1" + > + <form class="gl-display-flex gl-flex-grow-1 gl-mr-3" novalidate @submit.prevent> + <github-organizations-box + v-if="tab.showOrganizationFilter" + class="gl-mr-3" + :value="selectedOrganization" + @input="setFilter({ organization_login: $event })" + /> + <gl-search-box-by-click + data-qa-selector="githubish_import_filter_field" + name="filter" + :disabled="isNameFilterDisabled" + :value="nameFilter" + :placeholder="__('Filter by name')" + autofocus + @submit="setFilter({ filter: $event })" + @clear="setFilter({ filter: '' })" + /> + </form> + <gl-button + variant="confirm" + :loading="isImportingAnyRepo" + :disabled="!hasImportableRepos" + type="button" + @click="showImportAllModal" + > + {{ importAllButtonText }} + </gl-button> + </div> + </gl-tab> + </gl-tabs> + </template> + </import-projects-table> +</template> diff --git a/app/assets/javascripts/import_entities/import_projects/components/import_projects_table.vue b/app/assets/javascripts/import_entities/import_projects/components/import_projects_table.vue index 55a8bad27b9..a867a1695b9 100644 --- a/app/assets/javascripts/import_entities/import_projects/components/import_projects_table.vue +++ b/app/assets/javascripts/import_entities/import_projects/components/import_projects_table.vue @@ -8,6 +8,7 @@ import { } from '@gitlab/ui'; import { mapActions, mapState, mapGetters } from 'vuex'; import { n__, __, sprintf } from '~/locale'; + import ProviderRepoTableRow from './provider_repo_table_row.vue'; import AdvancedSettings from './advanced_settings.vue'; @@ -123,6 +124,10 @@ export default { 'setFilter', 'importAll', ]), + + showImportAllModal() { + this.$refs.importAllModal.show(); + }, }, }; </script> @@ -135,43 +140,30 @@ export default { <template v-if="hasIncompatibleRepos"> <slot name="incompatible-repos-warning"></slot> </template> - <div class="gl-display-flex gl-justify-content-space-between gl-flex-wrap gl-mb-5"> - <gl-button - variant="confirm" - :loading="isImportingAnyRepo" - :disabled="!hasImportableRepos" - type="button" - @click="$refs.importAllModal.show()" - >{{ importAllButtonText }}</gl-button - > - <gl-modal - ref="importAllModal" - modal-id="import-all-modal" - :title="s__('ImportProjects|Import repositories')" - :ok-title="__('Import')" - @ok="importAll({ optionalStages: optionalStagesSelection })" - > - {{ - n__( - 'Are you sure you want to import %d repository?', - 'Are you sure you want to import %d repositories?', - importAllCount, - ) - }} - </gl-modal> - - <slot name="actions"></slot> - <form v-if="filterable" class="gl-ml-auto" novalidate @submit.prevent> - <gl-search-box-by-click - data-qa-selector="githubish_import_filter_field" - name="filter" - :placeholder="__('Filter by name')" - autofocus - @submit="setFilter" - @clear="setFilter('')" - /> - </form> - </div> + <slot name="filter" v-bind="{ showImportAllModal, importAllButtonText }"> + <div class="gl-display-flex gl-justify-content-space-between gl-flex-wrap gl-mb-5"> + <gl-button + variant="confirm" + :loading="isImportingAnyRepo" + :disabled="!hasImportableRepos" + type="button" + @click="showImportAllModal" + >{{ importAllButtonText }}</gl-button + > + + <slot name="actions"></slot> + <form v-if="filterable" class="gl-ml-auto" novalidate @submit.prevent> + <gl-search-box-by-click + data-qa-selector="githubish_import_filter_field" + name="filter" + :placeholder="__('Filter by name')" + autofocus + @submit="setFilter({ filter: $event })" + @clear="setFilter({ filter: '' })" + /> + </form> + </div> + </slot> <advanced-settings v-if="optionalStages && optionalStages.length" v-model="optionalStagesSelection" @@ -179,6 +171,21 @@ export default { :is-initially-expanded="isAdvancedSettingsPanelInitiallyExpanded" class="gl-mb-5" /> + <gl-modal + ref="importAllModal" + modal-id="import-all-modal" + :title="s__('ImportProjects|Import repositories')" + :ok-title="__('Import')" + @ok="importAll({ optionalStages: optionalStagesSelection })" + > + {{ + n__( + 'Are you sure you want to import %d repository?', + 'Are you sure you want to import %d repositories?', + importAllCount, + ) + }} + </gl-modal> <div v-if="repositories.length" class="gl-w-full"> <table class="table gl-table"> <thead> @@ -209,7 +216,7 @@ export default { </table> </div> <gl-intersection-observer - v-if="paginatable && pageInfo.hasNextPage" + v-if="!isLoadingRepos && paginatable && pageInfo.hasNextPage" :key="pagePaginationStateKey" @appear="fetchRepos" /> diff --git a/app/assets/javascripts/import_entities/import_projects/index.js b/app/assets/javascripts/import_entities/import_projects/index.js index f898e23b47a..6ee637b1ce8 100644 --- a/app/assets/javascripts/import_entities/import_projects/index.js +++ b/app/assets/javascripts/import_entities/import_projects/index.js @@ -61,6 +61,7 @@ export default function mountImportProjectsTable({ mountElement, Component = ImportProjectsTable, extraProps = () => ({}), + extraProvide = () => ({}), }) { if (!mountElement) return undefined; @@ -75,6 +76,7 @@ export default function mountImportProjectsTable({ apolloProvider, provide: { detailsPath, + ...extraProvide(mountElement.dataset), }, render(createElement) { // We are using attrs instead of props so root-level component with inheritAttrs diff --git a/app/assets/javascripts/import_entities/import_projects/store/actions.js b/app/assets/javascripts/import_entities/import_projects/store/actions.js index e3c32028b13..4305f8d4db5 100644 --- a/app/assets/javascripts/import_entities/import_projects/store/actions.js +++ b/app/assets/javascripts/import_entities/import_projects/store/actions.js @@ -83,7 +83,7 @@ const fetchReposFactory = ({ reposPath = isRequired() }) => ({ state, commit }) .get( pathWithParams({ path: reposPath, - filter: filter ?? '', + ...(filter ?? {}), ...paginationParams({ state }), }), ) @@ -203,7 +203,7 @@ export const fetchJobsFactory = (jobsPath = isRequired()) => ({ state, commit, d eTagPoll = new Poll({ resource: { - fetchJobs: () => axios.get(pathWithParams({ path: jobsPath, filter: state.filter })), + fetchJobs: () => axios.get(pathWithParams({ path: jobsPath, ...state.filter })), }, method: 'fetchJobs', successCallback: ({ data }) => diff --git a/app/assets/javascripts/import_entities/import_projects/store/mutations.js b/app/assets/javascripts/import_entities/import_projects/store/mutations.js index 734e7b10a77..df529449f90 100644 --- a/app/assets/javascripts/import_entities/import_projects/store/mutations.js +++ b/app/assets/javascripts/import_entities/import_projects/store/mutations.js @@ -23,8 +23,8 @@ const processLegacyEntries = ({ newRepositories, existingRepositories, factory } }; export default { - [types.SET_FILTER](state, filter) { - state.filter = filter; + [types.SET_FILTER](state, newFilter) { + state.filter = { ...state.filter, ...newFilter }; state.repositories = []; state.pageInfo = { page: 0, diff --git a/app/assets/javascripts/import_entities/import_projects/store/state.js b/app/assets/javascripts/import_entities/import_projects/store/state.js index c384848f0a0..62dcefd3339 100644 --- a/app/assets/javascripts/import_entities/import_projects/store/state.js +++ b/app/assets/javascripts/import_entities/import_projects/store/state.js @@ -4,7 +4,7 @@ export default () => ({ customImportTargets: {}, isLoadingRepos: false, ciCdOnly: false, - filter: '', + filter: {}, pageInfo: { page: 0, startCursor: null, |