diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-09-19 04:45:44 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-09-19 04:45:44 +0300 |
commit | 85dc423f7090da0a52c73eb66faf22ddb20efff9 (patch) | |
tree | 9160f299afd8c80c038f08e1545be119f5e3f1e1 /app/assets/javascripts/import_projects | |
parent | 15c2c8c66dbe422588e5411eee7e68f1fa440bb8 (diff) |
Add latest changes from gitlab-org/gitlab@13-4-stable-ee
Diffstat (limited to 'app/assets/javascripts/import_projects')
10 files changed, 236 insertions, 309 deletions
diff --git a/app/assets/javascripts/import_projects/components/import_projects_table.vue b/app/assets/javascripts/import_projects/components/import_projects_table.vue index 72fdaca7e24..96100e4ac0c 100644 --- a/app/assets/javascripts/import_projects/components/import_projects_table.vue +++ b/app/assets/javascripts/import_projects/components/import_projects_table.vue @@ -1,27 +1,17 @@ <script> -import { throttle } from 'lodash'; import { mapActions, mapState, mapGetters } from 'vuex'; -import { GlButton, GlLoadingIcon } from '@gitlab/ui'; -import { __, sprintf } from '~/locale'; -import PaginationLinks from '~/vue_shared/components/pagination_links.vue'; -import ImportedProjectTableRow from './imported_project_table_row.vue'; +import { GlButton, GlLoadingIcon, GlIntersectionObserver, GlModal } from '@gitlab/ui'; +import { n__, __, sprintf } from '~/locale'; import ProviderRepoTableRow from './provider_repo_table_row.vue'; -import IncompatibleRepoTableRow from './incompatible_repo_table_row.vue'; -import PageQueryParamSync from './page_query_param_sync.vue'; -import { isProjectImportable } from '../utils'; - -const reposFetchThrottleDelay = 1000; export default { name: 'ImportProjectsTable', components: { - ImportedProjectTableRow, ProviderRepoTableRow, - IncompatibleRepoTableRow, - PageQueryParamSync, GlLoadingIcon, GlButton, - PaginationLinks, + GlModal, + GlIntersectionObserver, }, props: { providerTitle: { @@ -41,14 +31,19 @@ export default { }, computed: { - ...mapState(['filter', 'repositories', 'namespaces', 'defaultTargetNamespace', 'pageInfo']), + ...mapState(['filter', 'repositories', 'namespaces', 'defaultTargetNamespace']), ...mapGetters([ 'isLoading', 'isImportingAnyRepo', 'hasImportableRepos', 'hasIncompatibleRepos', + 'importAllCount', ]), + pagePaginationStateKey() { + return `${this.filter}-${this.repositories.length}`; + }, + availableNamespaces() { const serializedNamespaces = this.namespaces.map(({ fullPath }) => ({ id: fullPath, @@ -66,8 +61,12 @@ export default { importAllButtonText() { return this.hasIncompatibleRepos - ? __('Import all compatible repositories') - : __('Import all repositories'); + ? n__( + 'Import %d compatible repository', + 'Import %d compatible repositories', + this.importAllCount, + ) + : n__('Import %d repository', 'Import %d repositories', this.importAllCount); }, emptyStateText() { @@ -83,7 +82,11 @@ export default { mounted() { this.fetchNamespaces(); - this.fetchRepos(); + this.fetchJobs(); + + if (!this.paginatable) { + this.fetchRepos(); + } }, beforeDestroy() { @@ -94,105 +97,95 @@ export default { methods: { ...mapActions([ 'fetchRepos', + 'fetchJobs', 'fetchNamespaces', 'stopJobsPolling', 'clearJobsEtagPoll', 'setFilter', 'importAll', - 'setPage', ]), - - handleFilterInput({ target }) { - this.setFilter(target.value); - }, - - throttledFetchRepos: throttle(function fetch() { - this.fetchRepos(); - }, reposFetchThrottleDelay), - - isProjectImportable, }, }; </script> <template> <div> - <page-query-param-sync :page="pageInfo.page" @popstate="setPage" /> - <p class="light text-nowrap mt-2"> - {{ s__('ImportProjects|Select the projects you want to import') }} + {{ s__('ImportProjects|Select the repositories you want to import') }} </p> <template v-if="hasIncompatibleRepos"> <slot name="incompatible-repos-warning"></slot> </template> + <div class="d-flex justify-content-between align-items-end flex-wrap mb-3"> + <gl-button + variant="success" + :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" + > + {{ + 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> + <input + data-qa-selector="githubish_import_filter_field" + class="form-control" + name="filter" + :placeholder="__('Filter your repositories by name')" + autofocus + size="40" + @keyup.enter="setFilter($event.target.value)" + /> + </form> + </div> + <div v-if="repositories.length" class="table-responsive"> + <table class="table import-table"> + <thead> + <th class="import-jobs-from-col">{{ fromHeaderText }}</th> + <th class="import-jobs-to-col">{{ __('To GitLab') }}</th> + <th class="import-jobs-status-col">{{ __('Status') }}</th> + <th class="import-jobs-cta-col"></th> + </thead> + <tbody> + <template v-for="repo in repositories"> + <provider-repo-table-row + :key="repo.importSource.providerLink" + :repo="repo" + :available-namespaces="availableNamespaces" + /> + </template> + </tbody> + </table> + </div> + <gl-intersection-observer + v-if="paginatable" + :key="pagePaginationStateKey" + @appear="fetchRepos" + /> <gl-loading-icon v-if="isLoading" class="js-loading-button-icon import-projects-loading-icon" size="md" /> - <template v-if="!isLoading"> - <div class="d-flex justify-content-between align-items-end flex-wrap mb-3"> - <gl-button - variant="success" - :loading="isImportingAnyRepo" - :disabled="!hasImportableRepos" - type="button" - @click="importAll" - >{{ importAllButtonText }}</gl-button - > - <slot name="actions"></slot> - <form v-if="filterable" class="gl-ml-auto" novalidate @submit.prevent> - <input - :value="filter" - data-qa-selector="githubish_import_filter_field" - class="form-control" - name="filter" - :placeholder="__('Filter your projects by name')" - autofocus - size="40" - @input="handleFilterInput($event)" - @keyup.enter="throttledFetchRepos" - /> - </form> - </div> - <div v-if="repositories.length" class="table-responsive"> - <table class="table import-table"> - <thead> - <th class="import-jobs-from-col">{{ fromHeaderText }}</th> - <th class="import-jobs-to-col">{{ __('To GitLab') }}</th> - <th class="import-jobs-status-col">{{ __('Status') }}</th> - <th class="import-jobs-cta-col"></th> - </thead> - <tbody> - <template v-for="repo in repositories"> - <incompatible-repo-table-row - v-if="repo.importSource.incompatible" - :key="repo.importSource.id" - :repo="repo" - /> - <provider-repo-table-row - v-else-if="isProjectImportable(repo)" - :key="repo.importSource.id" - :repo="repo" - :available-namespaces="availableNamespaces" - /> - <imported-project-table-row v-else :key="repo.importSource.id" :project="repo" /> - </template> - </tbody> - </table> - </div> - <div v-else class="text-center"> - <strong>{{ emptyStateText }}</strong> - </div> - <pagination-links - v-if="paginatable" - align="center" - class="gl-mt-3" - :page-info="pageInfo" - :prev-page="pageInfo.page - 1" - :next-page="repositories.length && pageInfo.page + 1" - :change="setPage" - /> - </template> + + <div v-if="!isLoading && repositories.length === 0" class="text-center"> + <strong>{{ emptyStateText }}</strong> + </div> </div> </template> diff --git a/app/assets/javascripts/import_projects/components/imported_project_table_row.vue b/app/assets/javascripts/import_projects/components/imported_project_table_row.vue deleted file mode 100644 index 50e735b4478..00000000000 --- a/app/assets/javascripts/import_projects/components/imported_project_table_row.vue +++ /dev/null @@ -1,59 +0,0 @@ -<script> -import { GlIcon } from '@gitlab/ui'; -import ImportStatus from './import_status.vue'; -import { STATUSES } from '../constants'; - -export default { - name: 'ImportedProjectTableRow', - components: { - ImportStatus, - GlIcon, - }, - props: { - project: { - type: Object, - required: true, - }, - }, - - computed: { - displayFullPath() { - return this.project.importedProject.fullPath.replace(/^\//, ''); - }, - - isFinished() { - return this.project.importStatus === STATUSES.FINISHED; - }, - }, -}; -</script> - -<template> - <tr class="import-row"> - <td> - <a - :href="project.importSource.providerLink" - rel="noreferrer noopener" - target="_blank" - data-testid="providerLink" - >{{ project.importSource.fullName }} - <gl-icon v-if="project.importSource.providerLink" name="external-link" /> - </a> - </td> - <td data-testid="fullPath">{{ displayFullPath }}</td> - <td> - <import-status :status="project.importStatus" /> - </td> - <td> - <a - v-if="isFinished" - class="btn btn-default" - data-testid="goToProject" - :href="project.importedProject.fullPath" - rel="noreferrer noopener" - target="_blank" - >{{ __('Go to project') }} - </a> - </td> - </tr> -</template> diff --git a/app/assets/javascripts/import_projects/components/incompatible_repo_table_row.vue b/app/assets/javascripts/import_projects/components/incompatible_repo_table_row.vue deleted file mode 100644 index 3140585ccd7..00000000000 --- a/app/assets/javascripts/import_projects/components/incompatible_repo_table_row.vue +++ /dev/null @@ -1,32 +0,0 @@ -<script> -import { GlIcon, GlBadge } from '@gitlab/ui'; - -export default { - components: { - GlBadge, - GlIcon, - }, - props: { - repo: { - type: Object, - required: true, - }, - }, -}; -</script> - -<template> - <tr class="import-row"> - <td> - <a :href="repo.importSource.providerLink" rel="noreferrer noopener" target="_blank" - >{{ repo.importSource.fullName }} - <gl-icon v-if="repo.importSource.providerLink" name="external-link" /> - </a> - </td> - <td></td> - <td></td> - <td> - <gl-badge variant="danger">{{ __('Incompatible project') }}</gl-badge> - </td> - </tr> -</template> diff --git a/app/assets/javascripts/import_projects/components/page_query_param_sync.vue b/app/assets/javascripts/import_projects/components/page_query_param_sync.vue deleted file mode 100644 index 5ba3d70f5d0..00000000000 --- a/app/assets/javascripts/import_projects/components/page_query_param_sync.vue +++ /dev/null @@ -1,39 +0,0 @@ -<script> -import { queryToObject, setUrlParams, updateHistory } from '~/lib/utils/url_utility'; - -export default { - props: { - page: { - type: Number, - required: true, - }, - }, - - watch: { - page(newPage) { - updateHistory({ - url: setUrlParams({ - page: newPage === 1 ? null : newPage, - }), - }); - }, - }, - - created() { - window.addEventListener('popstate', this.updatePage); - }, - - beforeDestroy() { - window.removeEventListener('popstate', this.updatePage); - }, - - methods: { - updatePage() { - const page = parseInt(queryToObject(window.location.search).page, 10) || 1; - this.$emit('popstate', page); - }, - }, - - render: () => null, -}; -</script> diff --git a/app/assets/javascripts/import_projects/components/provider_repo_table_row.vue b/app/assets/javascripts/import_projects/components/provider_repo_table_row.vue index d8cffc6a7d5..18971313dfe 100644 --- a/app/assets/javascripts/import_projects/components/provider_repo_table_row.vue +++ b/app/assets/javascripts/import_projects/components/provider_repo_table_row.vue @@ -1,9 +1,11 @@ <script> import { mapState, mapGetters, mapActions } from 'vuex'; -import { GlIcon } from '@gitlab/ui'; +import { GlIcon, GlBadge } from '@gitlab/ui'; import Select2Select from '~/vue_shared/components/select2_select.vue'; import { __ } from '~/locale'; import ImportStatus from './import_status.vue'; +import { STATUSES } from '../constants'; +import { isProjectImportable, isIncompatible, getImportStatus } from '../utils'; export default { name: 'ProviderRepoTableRow', @@ -11,6 +13,7 @@ export default { Select2Select, ImportStatus, GlIcon, + GlBadge, }, props: { repo: { @@ -27,6 +30,26 @@ export default { ...mapState(['ciCdOnly']), ...mapGetters(['getImportTarget']), + displayFullPath() { + return this.repo.importedProject.fullPath.replace(/^\//, ''); + }, + + isFinished() { + return this.repo.importedProject?.importStatus === STATUSES.FINISHED; + }, + + isImportNotStarted() { + return isProjectImportable(this.repo); + }, + + isIncompatible() { + return isIncompatible(this.repo); + }, + + importStatus() { + return getImportStatus(this.repo); + }, + importTarget() { return this.getImportTarget(this.repo.importSource.id); }, @@ -85,9 +108,9 @@ export default { <gl-icon v-if="repo.importSource.providerLink" name="external-link" /> </a> </td> - <td class="d-flex flex-wrap flex-lg-nowrap"> - <template v-if="repo.target">{{ repo.target }}</template> - <template v-else> + <td class="d-flex flex-wrap flex-lg-nowrap" data-testid="fullPath"> + <template v-if="repo.importSource.target">{{ repo.importSource.target }}</template> + <template v-else-if="isImportNotStarted"> <select2-select v-model="targetNamespaceSelect" :options="select2Options" /> <span class="px-2 import-slash-divider d-flex justify-content-center align-items-center" >/</span @@ -98,18 +121,31 @@ export default { class="form-control import-project-name-input qa-project-path-field" /> </template> + <template v-else-if="repo.importedProject">{{ displayFullPath }}</template> </td> <td> - <import-status :status="repo.importStatus" /> + <import-status :status="importStatus" /> </td> - <td> + <td data-testid="actions"> + <a + v-if="isFinished" + class="btn btn-default" + :href="repo.importedProject.fullPath" + rel="noreferrer noopener" + target="_blank" + >{{ __('Go to project') }} + </a> <button + v-if="isImportNotStarted" type="button" class="qa-import-button btn btn-default" @click="fetchImport(repo.importSource.id)" > {{ importButtonText }} </button> + <gl-badge v-else-if="isIncompatible" variant="danger">{{ + __('Incompatible project') + }}</gl-badge> </td> </tr> </template> diff --git a/app/assets/javascripts/import_projects/store/actions.js b/app/assets/javascripts/import_projects/store/actions.js index af410f411d8..7b70d290278 100644 --- a/app/assets/javascripts/import_projects/store/actions.js +++ b/app/assets/javascripts/import_projects/store/actions.js @@ -1,11 +1,7 @@ import Visibility from 'visibilityjs'; import * as types from './mutation_types'; import { isProjectImportable } from '../utils'; -import { - convertObjectPropsToCamelCase, - normalizeHeaders, - parseIntPagination, -} from '~/lib/utils/common_utils'; +import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; import Poll from '~/lib/utils/poll'; import { visitUrl, objectToQuery } from '~/lib/utils/url_utility'; import { deprecatedCreateFlash as createFlash } from '~/flash'; @@ -54,12 +50,9 @@ const importAll = ({ state, dispatch }) => { ); }; -const fetchReposFactory = ({ reposPath = isRequired(), hasPagination }) => ({ - state, - dispatch, - commit, -}) => { - dispatch('stopJobsPolling'); +const fetchReposFactory = ({ reposPath = isRequired() }) => ({ state, commit }) => { + const nextPage = state.pageInfo.page + 1; + commit(types.SET_PAGE, nextPage); commit(types.REQUEST_REPOS); const { provider, filter } = state; @@ -68,21 +61,16 @@ const fetchReposFactory = ({ reposPath = isRequired(), hasPagination }) => ({ .get( pathWithParams({ path: reposPath, - filter, - page: hasPagination ? state.pageInfo.page.toString() : '', + filter: filter ?? '', + page: nextPage === 1 ? '' : nextPage.toString(), }), ) - .then(({ data, headers }) => { - const normalizedHeaders = normalizeHeaders(headers); - - if ('X-PAGE' in normalizedHeaders) { - commit(types.SET_PAGE_INFO, parseIntPagination(normalizedHeaders)); - } - + .then(({ data }) => { commit(types.RECEIVE_REPOS_SUCCESS, convertObjectPropsToCamelCase(data, { deep: true })); }) - .then(() => dispatch('fetchJobs')) .catch(e => { + commit(types.SET_PAGE, nextPage - 1); + if (hasRedirectInError(e)) { redirectToUrlInError(e); } else { @@ -136,8 +124,6 @@ const fetchImportFactory = (importPath = isRequired()) => ({ state, commit, gett }; export const fetchJobsFactory = (jobsPath = isRequired()) => ({ state, commit, dispatch }) => { - const { filter } = state; - if (eTagPoll) { stopJobsPolling(); clearJobsEtagPoll(); @@ -145,7 +131,7 @@ export const fetchJobsFactory = (jobsPath = isRequired()) => ({ state, commit, d eTagPoll = new Poll({ resource: { - fetchJobs: () => axios.get(pathWithParams({ path: jobsPath, filter })), + fetchJobs: () => axios.get(pathWithParams({ path: jobsPath, filter: state.filter })), }, method: 'fetchJobs', successCallback: ({ data }) => @@ -157,7 +143,6 @@ export const fetchJobsFactory = (jobsPath = isRequired()) => ({ state, commit, d createFlash(s__('ImportProjects|Update of imported projects with realtime changes failed')); } }, - data: { filter }, }); if (!Visibility.hidden()) { @@ -196,7 +181,7 @@ const setPage = ({ state, commit, dispatch }, page) => { return dispatch('fetchRepos'); }; -export default ({ endpoints = isRequired(), hasPagination }) => ({ +export default ({ endpoints = isRequired() }) => ({ clearJobsEtagPoll, stopJobsPolling, restartJobsPolling, @@ -204,7 +189,7 @@ export default ({ endpoints = isRequired(), hasPagination }) => ({ setImportTarget, importAll, setPage, - fetchRepos: fetchReposFactory({ reposPath: endpoints.reposPath, hasPagination }), + fetchRepos: fetchReposFactory({ reposPath: endpoints.reposPath }), fetchImport: fetchImportFactory(endpoints.importPath), fetchJobs: fetchJobsFactory(endpoints.jobsPath), fetchNamespaces: fetchNamespacesFactory(endpoints.namespacesPath), diff --git a/app/assets/javascripts/import_projects/store/getters.js b/app/assets/javascripts/import_projects/store/getters.js index 7d529c94d7d..b76c52beea2 100644 --- a/app/assets/javascripts/import_projects/store/getters.js +++ b/app/assets/javascripts/import_projects/store/getters.js @@ -1,17 +1,20 @@ import { STATUSES } from '../constants'; +import { isProjectImportable, isIncompatible } from '../utils'; export const isLoading = state => state.isLoadingRepos || state.isLoadingNamespaces; export const isImportingAnyRepo = state => state.repositories.some(repo => - [STATUSES.SCHEDULING, STATUSES.SCHEDULED, STATUSES.STARTED].includes(repo.importStatus), + [STATUSES.SCHEDULING, STATUSES.SCHEDULED, STATUSES.STARTED].includes( + repo.importedProject?.importStatus, + ), ); -export const hasIncompatibleRepos = state => - state.repositories.some(repo => repo.importSource.incompatible); +export const hasIncompatibleRepos = state => state.repositories.some(isIncompatible); -export const hasImportableRepos = state => - state.repositories.some(repo => repo.importStatus === STATUSES.NONE); +export const hasImportableRepos = state => state.repositories.some(isProjectImportable); + +export const importAllCount = state => state.repositories.filter(isProjectImportable).length; export const getImportTarget = state => repoId => { if (state.customImportTargets[repoId]) { diff --git a/app/assets/javascripts/import_projects/store/mutations.js b/app/assets/javascripts/import_projects/store/mutations.js index b3dbef896a6..6999253d4b2 100644 --- a/app/assets/javascripts/import_projects/store/mutations.js +++ b/app/assets/javascripts/import_projects/store/mutations.js @@ -2,46 +2,88 @@ import Vue from 'vue'; import * as types from './mutation_types'; import { STATUSES } from '../constants'; +const makeNewImportedProject = importedProject => ({ + importSource: { + id: importedProject.id, + fullName: importedProject.importSource, + sanitizedName: importedProject.name, + providerLink: importedProject.providerLink, + }, + importedProject, +}); + +const makeNewIncompatibleProject = project => ({ + importSource: { ...project, incompatible: true }, + importedProject: null, +}); + +const processLegacyEntries = ({ newRepositories, existingRepositories, factory }) => { + const newEntries = []; + newRepositories.forEach(project => { + const existingProject = existingRepositories.find(p => p.importSource.id === project.id); + const importedProjectShape = factory(project); + + if (existingProject) { + Object.assign(existingProject, importedProjectShape); + } else { + newEntries.push(importedProjectShape); + } + }); + return newEntries; +}; + export default { [types.SET_FILTER](state, filter) { state.filter = filter; + state.repositories = []; + state.pageInfo.page = 0; }, [types.REQUEST_REPOS](state) { state.isLoadingRepos = true; }, - [types.RECEIVE_REPOS_SUCCESS]( - state, - { importedProjects, providerRepos, incompatibleRepos = [] }, - ) { - // Normalizing structure to support legacy backend format - // See https://gitlab.com/gitlab-org/gitlab/-/issues/27370#note_379034091 for details - + [types.RECEIVE_REPOS_SUCCESS](state, repositories) { state.isLoadingRepos = false; - state.repositories = [ - ...importedProjects.map(({ importSource, providerLink, importStatus, ...project }) => ({ - importSource: { - id: `finished-${project.id}`, - fullName: importSource, - sanitizedName: project.name, - providerLink, - }, - importStatus, - importedProject: project, - })), - ...providerRepos.map(project => ({ - importSource: project, - importStatus: STATUSES.NONE, - importedProject: null, - })), - ...incompatibleRepos.map(project => ({ - importSource: { ...project, incompatible: true }, - importStatus: STATUSES.NONE, - importedProject: null, - })), - ]; + if (!Array.isArray(repositories)) { + // Legacy code path, will be removed when all importers will be switched to new pagination format + // https://gitlab.com/gitlab-org/gitlab/-/issues/27370#note_379034091 + + const newImportedProjects = processLegacyEntries({ + newRepositories: repositories.importedProjects, + existingRepositories: state.repositories, + factory: makeNewImportedProject, + }); + + const incompatibleRepos = repositories.incompatibleRepos ?? []; + const newIncompatibleProjects = processLegacyEntries({ + newRepositories: incompatibleRepos, + existingRepositories: state.repositories, + factory: makeNewIncompatibleProject, + }); + + state.repositories = [ + ...newImportedProjects, + ...state.repositories, + ...repositories.providerRepos.map(project => ({ + importSource: project, + importedProject: null, + })), + ...newIncompatibleProjects, + ]; + + if (incompatibleRepos.length === 0 && repositories.providerRepos.length === 0) { + state.pageInfo.page -= 1; + } + + return; + } + + state.repositories = [...state.repositories, ...repositories]; + if (repositories.length === 0) { + state.pageInfo.page -= 1; + } }, [types.RECEIVE_REPOS_ERROR](state) { @@ -50,31 +92,27 @@ export default { [types.REQUEST_IMPORT](state, { repoId, importTarget }) { const existingRepo = state.repositories.find(r => r.importSource.id === repoId); - existingRepo.importStatus = STATUSES.SCHEDULING; existingRepo.importedProject = { + importStatus: STATUSES.SCHEDULING, fullPath: `/${importTarget.targetNamespace}/${importTarget.newName}`, }; }, [types.RECEIVE_IMPORT_SUCCESS](state, { importedProject, repoId }) { - const { importStatus, ...project } = importedProject; - const existingRepo = state.repositories.find(r => r.importSource.id === repoId); - existingRepo.importStatus = importStatus; - existingRepo.importedProject = project; + existingRepo.importedProject = importedProject; }, [types.RECEIVE_IMPORT_ERROR](state, repoId) { const existingRepo = state.repositories.find(r => r.importSource.id === repoId); - existingRepo.importStatus = STATUSES.NONE; existingRepo.importedProject = null; }, [types.RECEIVE_JOBS_SUCCESS](state, updatedProjects) { updatedProjects.forEach(updatedProject => { const repo = state.repositories.find(p => p.importedProject?.id === updatedProject.id); - if (repo) { - repo.importStatus = updatedProject.importStatus; + if (repo?.importedProject) { + repo.importedProject.importStatus = updatedProject.importStatus; } }); }, @@ -105,10 +143,6 @@ export default { } }, - [types.SET_PAGE_INFO](state, pageInfo) { - state.pageInfo = pageInfo; - }, - [types.SET_PAGE](state, page) { state.pageInfo.page = page; }, diff --git a/app/assets/javascripts/import_projects/store/state.js b/app/assets/javascripts/import_projects/store/state.js index 3318181e4af..ecd93561d52 100644 --- a/app/assets/javascripts/import_projects/store/state.js +++ b/app/assets/javascripts/import_projects/store/state.js @@ -8,6 +8,6 @@ export default () => ({ ciCdOnly: false, filter: '', pageInfo: { - page: 1, + page: 0, }, }); diff --git a/app/assets/javascripts/import_projects/utils.js b/app/assets/javascripts/import_projects/utils.js index c2a2d5a607d..695b12cbcba 100644 --- a/app/assets/javascripts/import_projects/utils.js +++ b/app/assets/javascripts/import_projects/utils.js @@ -1,7 +1,13 @@ import { STATUSES } from './constants'; -// Will be expanded in future -// eslint-disable-next-line import/prefer-default-export +export function isIncompatible(project) { + return project.importSource.incompatible; +} + +export function getImportStatus(project) { + return project.importedProject?.importStatus ?? STATUSES.NONE; +} + export function isProjectImportable(project) { - return project.importStatus === STATUSES.NONE && !project.importSource.incompatible; + return !isIncompatible(project) && getImportStatus(project) === STATUSES.NONE; } |