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:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-08-18 00:09:56 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-08-18 00:09:56 +0300
commit9facfe5cb194005894467c06e4ea1a9800034ab4 (patch)
tree5ceaecd5f89725250bbf4119558715fa14576c4c /app/assets/javascripts/import_projects
parent18da92341dac366b7bcfd13f2d3c443ffa315af0 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/import_projects')
-rw-r--r--app/assets/javascripts/import_projects/components/bitbucket_status_table.vue3
-rw-r--r--app/assets/javascripts/import_projects/components/import_projects_table.vue131
-rw-r--r--app/assets/javascripts/import_projects/components/page_query_param_sync.vue39
-rw-r--r--app/assets/javascripts/import_projects/index.js10
-rw-r--r--app/assets/javascripts/import_projects/store/actions.js62
-rw-r--r--app/assets/javascripts/import_projects/store/index.js4
-rw-r--r--app/assets/javascripts/import_projects/store/mutation_types.js4
-rw-r--r--app/assets/javascripts/import_projects/store/mutations.js8
-rw-r--r--app/assets/javascripts/import_projects/store/state.js3
9 files changed, 194 insertions, 70 deletions
diff --git a/app/assets/javascripts/import_projects/components/bitbucket_status_table.vue b/app/assets/javascripts/import_projects/components/bitbucket_status_table.vue
index f673a0e42dc..bc8aa522596 100644
--- a/app/assets/javascripts/import_projects/components/bitbucket_status_table.vue
+++ b/app/assets/javascripts/import_projects/components/bitbucket_status_table.vue
@@ -9,6 +9,7 @@ export default {
GlSprintf,
GlLink,
},
+ inheritAttrs: false,
props: {
providerTitle: {
type: String,
@@ -28,7 +29,7 @@ export default {
};
</script>
<template>
- <import-projects-table :provider-title="providerTitle">
+ <import-projects-table :provider-title="providerTitle" v-bind="$attrs">
<template #actions>
<slot name="actions"></slot>
</template>
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 3ae12363f39..72fdaca7e24 100644
--- a/app/assets/javascripts/import_projects/components/import_projects_table.vue
+++ b/app/assets/javascripts/import_projects/components/import_projects_table.vue
@@ -3,9 +3,11 @@ 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 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;
@@ -16,8 +18,10 @@ export default {
ImportedProjectTableRow,
ProviderRepoTableRow,
IncompatibleRepoTableRow,
+ PageQueryParamSync,
GlLoadingIcon,
GlButton,
+ PaginationLinks,
},
props: {
providerTitle: {
@@ -29,10 +33,15 @@ export default {
required: false,
default: true,
},
+ paginatable: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
},
computed: {
- ...mapState(['filter', 'repositories', 'namespaces', 'defaultTargetNamespace']),
+ ...mapState(['filter', 'repositories', 'namespaces', 'defaultTargetNamespace', 'pageInfo']),
...mapGetters([
'isLoading',
'isImportingAnyRepo',
@@ -90,6 +99,7 @@ export default {
'clearJobsEtagPoll',
'setFilter',
'importAll',
+ 'setPage',
]),
handleFilterInput({ target }) {
@@ -107,69 +117,82 @@ export default {
<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') }}
</p>
<template v-if="hasIncompatibleRepos">
<slot name="incompatible-repos-warning"></slot>
</template>
- <div v-if="!isLoading" 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>
<gl-loading-icon
v-if="isLoading"
class="js-loading-button-icon import-projects-loading-icon"
size="md"
/>
- <div v-else-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>
+ <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>
</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
new file mode 100644
index 00000000000..5ba3d70f5d0
--- /dev/null
+++ b/app/assets/javascripts/import_projects/components/page_query_param_sync.vue
@@ -0,0 +1,39 @@
+<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/index.js b/app/assets/javascripts/import_projects/index.js
index e616acdb310..79fbd58e355 100644
--- a/app/assets/javascripts/import_projects/index.js
+++ b/app/assets/javascripts/import_projects/index.js
@@ -2,6 +2,7 @@ import Vue from 'vue';
import Translate from '../vue_shared/translate';
import ImportProjectsTable from './components/import_projects_table.vue';
import { parseBoolean } from '../lib/utils/common_utils';
+import { queryToObject } from '../lib/utils/url_utility';
import createStore from './store';
Vue.use(Translate);
@@ -16,14 +17,21 @@ export function initStoreFromElement(element) {
jobsPath,
importPath,
namespacesPath,
+ paginatable,
} = element.dataset;
+ const params = queryToObject(document.location.search);
+ const page = parseInt(params.page ?? 1, 10);
+
return createStore({
initialState: {
defaultTargetNamespace: gon.current_username,
ciCdOnly: parseBoolean(ciCdOnly),
canSelectNamespace: parseBoolean(canSelectNamespace),
provider,
+ pageInfo: {
+ page,
+ },
},
endpoints: {
reposPath,
@@ -31,6 +39,7 @@ export function initStoreFromElement(element) {
importPath,
namespacesPath,
},
+ hasPagination: parseBoolean(paginatable),
});
}
@@ -38,6 +47,7 @@ export function initPropsFromElement(element) {
return {
providerTitle: element.dataset.providerTitle,
filterable: parseBoolean(element.dataset.filterable),
+ paginatable: parseBoolean(element.dataset.paginatable),
};
}
diff --git a/app/assets/javascripts/import_projects/store/actions.js b/app/assets/javascripts/import_projects/store/actions.js
index 5765dea51ac..a592bde6373 100644
--- a/app/assets/javascripts/import_projects/store/actions.js
+++ b/app/assets/javascripts/import_projects/store/actions.js
@@ -1,9 +1,13 @@
import Visibility from 'visibilityjs';
import * as types from './mutation_types';
import { isProjectImportable } from '../utils';
-import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
+import {
+ convertObjectPropsToCamelCase,
+ normalizeHeaders,
+ parseIntPagination,
+} from '~/lib/utils/common_utils';
import Poll from '~/lib/utils/poll';
-import { visitUrl } from '~/lib/utils/url_utility';
+import { visitUrl, objectToQuery } from '~/lib/utils/url_utility';
import createFlash from '~/flash';
import { s__, sprintf } from '~/locale';
import axios from '~/lib/utils/axios_utils';
@@ -12,7 +16,13 @@ let eTagPoll;
const hasRedirectInError = e => e?.response?.data?.error?.redirect;
const redirectToUrlInError = e => visitUrl(e.response.data.error.redirect);
-const pathWithFilter = ({ path, filter = '' }) => (filter ? `${path}?filter=${filter}` : path);
+const pathWithParams = ({ path, ...params }) => {
+ const filteredParams = Object.fromEntries(
+ Object.entries(params).filter(([, value]) => value !== ''),
+ );
+ const queryString = objectToQuery(filteredParams);
+ return queryString ? `${path}?${queryString}` : path;
+};
const isRequired = () => {
// eslint-disable-next-line @gitlab/require-i18n-strings
@@ -44,17 +54,33 @@ const importAll = ({ state, dispatch }) => {
);
};
-const fetchReposFactory = (reposPath = isRequired()) => ({ state, dispatch, commit }) => {
+const fetchReposFactory = ({ reposPath = isRequired(), hasPagination }) => ({
+ state,
+ dispatch,
+ commit,
+}) => {
dispatch('stopJobsPolling');
commit(types.REQUEST_REPOS);
const { provider, filter } = state;
return axios
- .get(pathWithFilter({ path: reposPath, filter }))
- .then(({ data }) =>
- commit(types.RECEIVE_REPOS_SUCCESS, convertObjectPropsToCamelCase(data, { deep: true })),
+ .get(
+ pathWithParams({
+ path: reposPath,
+ filter,
+ page: hasPagination ? state.pageInfo.page.toString() : '',
+ }),
)
+ .then(({ data, headers }) => {
+ const normalizedHeaders = normalizeHeaders(headers);
+
+ if ('X-PAGE' in normalizedHeaders) {
+ commit(types.SET_PAGE_INFO, parseIntPagination(normalizedHeaders));
+ }
+
+ commit(types.RECEIVE_REPOS_SUCCESS, convertObjectPropsToCamelCase(data, { deep: true }));
+ })
.then(() => dispatch('fetchJobs'))
.catch(e => {
if (hasRedirectInError(e)) {
@@ -85,12 +111,12 @@ const fetchImportFactory = (importPath = isRequired()) => ({ state, commit, gett
new_name: newName,
target_namespace: targetNamespace,
})
- .then(({ data }) =>
+ .then(({ data }) => {
commit(types.RECEIVE_IMPORT_SUCCESS, {
importedProject: convertObjectPropsToCamelCase(data, { deep: true }),
repoId,
- }),
- )
+ });
+ })
.catch(e => {
const serverErrorMessage = e?.response?.data?.errors;
const flashMessage = serverErrorMessage
@@ -119,7 +145,7 @@ export const fetchJobsFactory = (jobsPath = isRequired()) => ({ state, commit, d
eTagPoll = new Poll({
resource: {
- fetchJobs: () => axios.get(pathWithFilter({ path: jobsPath, filter })),
+ fetchJobs: () => axios.get(pathWithParams({ path: jobsPath, filter })),
},
method: 'fetchJobs',
successCallback: ({ data }) =>
@@ -161,14 +187,24 @@ const fetchNamespacesFactory = (namespacesPath = isRequired()) => ({ commit }) =
});
};
-export default ({ endpoints = isRequired() }) => ({
+const setPage = ({ state, commit, dispatch }, page) => {
+ if (page === state.pageInfo.page) {
+ return null;
+ }
+
+ commit(types.SET_PAGE, page);
+ return dispatch('fetchRepos');
+};
+
+export default ({ endpoints = isRequired(), hasPagination }) => ({
clearJobsEtagPoll,
stopJobsPolling,
restartJobsPolling,
setFilter,
setImportTarget,
importAll,
- fetchRepos: fetchReposFactory(endpoints.reposPath),
+ setPage,
+ fetchRepos: fetchReposFactory({ reposPath: endpoints.reposPath, hasPagination }),
fetchImport: fetchImportFactory(endpoints.importPath),
fetchJobs: fetchJobsFactory(endpoints.jobsPath),
fetchNamespaces: fetchNamespacesFactory(endpoints.namespacesPath),
diff --git a/app/assets/javascripts/import_projects/store/index.js b/app/assets/javascripts/import_projects/store/index.js
index 1419de877fb..7ba12f81eb9 100644
--- a/app/assets/javascripts/import_projects/store/index.js
+++ b/app/assets/javascripts/import_projects/store/index.js
@@ -7,10 +7,10 @@ import mutations from './mutations';
Vue.use(Vuex);
-export default ({ initialState, endpoints }) =>
+export default ({ initialState, endpoints, hasPagination }) =>
new Vuex.Store({
state: { ...state(), ...initialState },
- actions: actionsFactory({ endpoints }),
+ actions: actionsFactory({ endpoints, hasPagination }),
mutations,
getters,
});
diff --git a/app/assets/javascripts/import_projects/store/mutation_types.js b/app/assets/javascripts/import_projects/store/mutation_types.js
index 1bcf27d7ba4..6adf5e59cff 100644
--- a/app/assets/javascripts/import_projects/store/mutation_types.js
+++ b/app/assets/javascripts/import_projects/store/mutation_types.js
@@ -15,3 +15,7 @@ export const RECEIVE_JOBS_SUCCESS = 'RECEIVE_JOBS_SUCCESS';
export const SET_FILTER = 'SET_FILTER';
export const SET_IMPORT_TARGET = 'SET_IMPORT_TARGET';
+
+export const SET_PAGE = 'SET_PAGE';
+
+export const SET_PAGE_INFO = 'SET_PAGE_INFO';
diff --git a/app/assets/javascripts/import_projects/store/mutations.js b/app/assets/javascripts/import_projects/store/mutations.js
index 44fc3da4de5..b3dbef896a6 100644
--- a/app/assets/javascripts/import_projects/store/mutations.js
+++ b/app/assets/javascripts/import_projects/store/mutations.js
@@ -104,4 +104,12 @@ export default {
Vue.set(state.customImportTargets, repoId, importTarget);
}
},
+
+ [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 8432a210955..3318181e4af 100644
--- a/app/assets/javascripts/import_projects/store/state.js
+++ b/app/assets/javascripts/import_projects/store/state.js
@@ -7,4 +7,7 @@ export default () => ({
isLoadingNamespaces: false,
ciCdOnly: false,
filter: '',
+ pageInfo: {
+ page: 1,
+ },
});