diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-02-18 13:34:06 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-02-18 13:34:06 +0300 |
commit | 859a6fb938bb9ee2a317c46dfa4fcc1af49608f0 (patch) | |
tree | d7f2700abe6b4ffcb2dcfc80631b2d87d0609239 /app/assets/javascripts/import_entities | |
parent | 446d496a6d000c73a304be52587cd9bbc7493136 (diff) |
Add latest changes from gitlab-org/gitlab@13-9-stable-eev13.9.0-rc42
Diffstat (limited to 'app/assets/javascripts/import_entities')
12 files changed, 333 insertions, 181 deletions
diff --git a/app/assets/javascripts/import_entities/import_groups/components/import_table.vue b/app/assets/javascripts/import_entities/import_groups/components/import_table.vue index 80e2e73f420..7c5f48dcafc 100644 --- a/app/assets/javascripts/import_entities/import_groups/components/import_table.vue +++ b/app/assets/javascripts/import_entities/import_groups/components/import_table.vue @@ -1,78 +1,184 @@ <script> -import { GlLoadingIcon } from '@gitlab/ui'; -import bulkImportSourceGroupsQuery from '../graphql/queries/bulk_import_source_groups.query.graphql'; -import availableNamespacesQuery from '../graphql/queries/available_namespaces.query.graphql'; -import setTargetNamespaceMutation from '../graphql/mutations/set_target_namespace.mutation.graphql'; -import setNewNameMutation from '../graphql/mutations/set_new_name.mutation.graphql'; +import { + GlEmptyState, + GlIcon, + GlLink, + GlLoadingIcon, + GlSearchBoxByClick, + GlSprintf, +} from '@gitlab/ui'; +import { s__ } from '~/locale'; +import PaginationLinks from '~/vue_shared/components/pagination_links.vue'; import importGroupMutation from '../graphql/mutations/import_group.mutation.graphql'; +import setNewNameMutation from '../graphql/mutations/set_new_name.mutation.graphql'; +import setTargetNamespaceMutation from '../graphql/mutations/set_target_namespace.mutation.graphql'; +import availableNamespacesQuery from '../graphql/queries/available_namespaces.query.graphql'; +import bulkImportSourceGroupsQuery from '../graphql/queries/bulk_import_source_groups.query.graphql'; import ImportTableRow from './import_table_row.vue'; -const mapApolloMutations = (mutations) => - Object.fromEntries( - Object.entries(mutations).map(([key, mutation]) => [ - key, - function mutate(config) { - return this.$apollo.mutate({ - mutation, - ...config, - }); - }, - ]), - ); - export default { components: { + GlEmptyState, + GlIcon, + GlLink, GlLoadingIcon, + GlSearchBoxByClick, + GlSprintf, ImportTableRow, + PaginationLinks, + }, + + props: { + sourceUrl: { + type: String, + required: true, + }, + }, + + data() { + return { + filter: '', + page: 1, + }; }, apollo: { - bulkImportSourceGroups: bulkImportSourceGroupsQuery, + bulkImportSourceGroups: { + query: bulkImportSourceGroupsQuery, + variables() { + return { page: this.page, filter: this.filter }; + }, + }, availableNamespaces: availableNamespacesQuery, }, + computed: { + hasGroups() { + return this.bulkImportSourceGroups?.nodes?.length > 0; + }, + + hasEmptyFilter() { + return this.filter.length > 0 && !this.hasGroups; + }, + + statusMessage() { + return this.filter.length === 0 + ? s__('BulkImport|Showing %{start}-%{end} of %{total} from %{link}') + : s__( + 'BulkImport|Showing %{start}-%{end} of %{total} matching filter "%{filter}" from %{link}', + ); + }, + + paginationInfo() { + const { page, perPage, total } = this.bulkImportSourceGroups?.pageInfo ?? { + page: 1, + perPage: 0, + total: 0, + }; + const start = (page - 1) * perPage + 1; + const end = start + (this.bulkImportSourceGroups.nodes?.length ?? 0) - 1; + + return { start, end, total }; + }, + }, + + watch: { + filter() { + this.page = 1; + }, + }, + methods: { - ...mapApolloMutations({ - setTargetNamespace: setTargetNamespaceMutation, - setNewName: setNewNameMutation, - importGroup: importGroupMutation, - }), + setPage(page) { + this.page = page; + }, + + updateTargetNamespace(sourceGroupId, targetNamespace) { + this.$apollo.mutate({ + mutation: setTargetNamespaceMutation, + variables: { sourceGroupId, targetNamespace }, + }); + }, + + updateNewName(sourceGroupId, newName) { + this.$apollo.mutate({ + mutation: setNewNameMutation, + variables: { sourceGroupId, newName }, + }); + }, + + importGroup(sourceGroupId) { + this.$apollo.mutate({ + mutation: importGroupMutation, + variables: { sourceGroupId }, + }); + }, }, }; </script> <template> <div> - <gl-loading-icon v-if="$apollo.loading" size="md" class="gl-mt-5" /> - <div v-else-if="bulkImportSourceGroups.length"> - <table class="gl-w-full"> - <thead class="gl-border-solid gl-border-gray-200 gl-border-0 gl-border-b-1"> - <th class="gl-py-4 import-jobs-from-col">{{ s__('BulkImport|From source group') }}</th> - <th class="gl-py-4 import-jobs-to-col">{{ s__('BulkImport|To new group') }}</th> - <th class="gl-py-4 import-jobs-status-col">{{ __('Status') }}</th> - <th class="gl-py-4 import-jobs-cta-col"></th> - </thead> - <tbody> - <template v-for="group in bulkImportSourceGroups"> - <import-table-row - :key="group.id" - :group="group" - :available-namespaces="availableNamespaces" - @update-target-namespace=" - setTargetNamespace({ - variables: { sourceGroupId: group.id, targetNamespace: $event }, - }) - " - @update-new-name=" - setNewName({ - variables: { sourceGroupId: group.id, newName: $event }, - }) - " - @import-group="importGroup({ variables: { sourceGroupId: group.id } })" - /> + <div + class="gl-py-5 gl-border-solid gl-border-gray-200 gl-border-0 gl-border-b-1 gl-display-flex gl-align-items-center" + > + <span> + <gl-sprintf v-if="!$apollo.loading && hasGroups" :message="statusMessage"> + <template #start> + <strong>{{ paginationInfo.start }}</strong> + </template> + <template #end> + <strong>{{ paginationInfo.end }}</strong> </template> - </tbody> - </table> + <template #total> + <strong>{{ n__('%d group', '%d groups', paginationInfo.total) }}</strong> + </template> + <template #filter> + <strong>{{ filter }}</strong> + </template> + <template #link> + <gl-link class="gl-display-inline-block" :href="sourceUrl" target="_blank"> + {{ sourceUrl }} <gl-icon name="external-link" class="vertical-align-middle" /> + </gl-link> + </template> + </gl-sprintf> + </span> + <gl-search-box-by-click class="gl-ml-auto" @submit="filter = $event" @clear="filter = ''" /> </div> + <gl-loading-icon v-if="$apollo.loading" size="md" class="gl-mt-5" /> + <template v-else> + <gl-empty-state v-if="hasEmptyFilter" :title="__('Sorry, your filter produced no results')" /> + <gl-empty-state + v-else-if="!hasGroups" + :title="s__('BulkImport|No groups available for import')" + /> + <div v-else class="gl-display-flex gl-flex-direction-column gl-align-items-center"> + <table class="gl-w-full"> + <thead class="gl-border-solid gl-border-gray-200 gl-border-0 gl-border-b-1"> + <th class="gl-py-4 import-jobs-from-col">{{ s__('BulkImport|From source group') }}</th> + <th class="gl-py-4 import-jobs-to-col">{{ s__('BulkImport|To new group') }}</th> + <th class="gl-py-4 import-jobs-status-col">{{ __('Status') }}</th> + <th class="gl-py-4 import-jobs-cta-col"></th> + </thead> + <tbody> + <template v-for="group in bulkImportSourceGroups.nodes"> + <import-table-row + :key="group.id" + :group="group" + :available-namespaces="availableNamespaces" + @update-target-namespace="updateTargetNamespace(group.id, $event)" + @update-new-name="updateNewName(group.id, $event)" + @import-group="importGroup(group.id)" + /> + </template> + </tbody> + </table> + <pagination-links + :change="setPage" + :page-info="bulkImportSourceGroups.pageInfo" + class="gl-mt-3" + /> + </div> + </template> </div> </template> diff --git a/app/assets/javascripts/import_entities/import_groups/graphql/client_factory.js b/app/assets/javascripts/import_entities/import_groups/graphql/client_factory.js index 8f2d488d661..8110934efc4 100644 --- a/app/assets/javascripts/import_entities/import_groups/graphql/client_factory.js +++ b/app/assets/javascripts/import_entities/import_groups/graphql/client_factory.js @@ -1,15 +1,18 @@ -import axios from '~/lib/utils/axios_utils'; +import createFlash from '~/flash'; import createDefaultClient from '~/lib/graphql'; +import axios from '~/lib/utils/axios_utils'; +import { parseIntPagination, normalizeHeaders } from '~/lib/utils/common_utils'; import { s__ } from '~/locale'; -import createFlash from '~/flash'; import { STATUSES } from '../../constants'; import availableNamespacesQuery from './queries/available_namespaces.query.graphql'; import { SourceGroupsManager } from './services/source_groups_manager'; import { StatusPoller } from './services/status_poller'; export const clientTypenames = { + BulkImportSourceGroupConnection: 'ClientBulkImportSourceGroupConnection', BulkImportSourceGroup: 'ClientBulkImportSourceGroup', AvailableNamespace: 'ClientAvailableNamespace', + BulkImportPageInfo: 'ClientBulkImportPageInfo', }; export function createResolvers({ endpoints }) { @@ -17,22 +20,47 @@ export function createResolvers({ endpoints }) { return { Query: { - async bulkImportSourceGroups(_, __, { client }) { + async bulkImportSourceGroups(_, vars, { client }) { const { data: { availableNamespaces }, } = await client.query({ query: availableNamespacesQuery }); - return axios.get(endpoints.status).then(({ data }) => { - return data.importable_data.map((group) => ({ - __typename: clientTypenames.BulkImportSourceGroup, - ...group, - status: STATUSES.NONE, - import_target: { - new_name: group.full_path, - target_namespace: availableNamespaces[0].full_path, + if (!statusPoller) { + statusPoller = new StatusPoller({ + client, + pollPath: endpoints.jobs, + }); + statusPoller.startPolling(); + } + + return axios + .get(endpoints.status, { + params: { + page: vars.page, + per_page: vars.perPage, + filter: vars.filter, }, - })); - }); + }) + .then(({ headers, data }) => { + const pagination = parseIntPagination(normalizeHeaders(headers)); + + return { + __typename: clientTypenames.BulkImportSourceGroupConnection, + nodes: data.importable_data.map((group) => ({ + __typename: clientTypenames.BulkImportSourceGroup, + ...group, + status: STATUSES.NONE, + import_target: { + new_name: group.full_path, + target_namespace: availableNamespaces[0].full_path, + }, + })), + pageInfo: { + __typename: clientTypenames.BulkImportPageInfo, + ...pagination, + }, + }; + }); }, availableNamespaces: () => @@ -63,7 +91,7 @@ export function createResolvers({ endpoints }) { const group = groupManager.findById(sourceGroupId); groupManager.setImportStatus(group, STATUSES.SCHEDULING); try { - await axios.post(endpoints.createBulkImport, { + const response = await axios.post(endpoints.createBulkImport, { bulk_import: [ { source_type: 'group_entity', @@ -74,10 +102,7 @@ export function createResolvers({ endpoints }) { ], }); groupManager.setImportStatus(group, STATUSES.STARTED); - if (!statusPoller) { - statusPoller = new StatusPoller({ client, interval: 3000 }); - statusPoller.startPolling(); - } + SourceGroupsManager.attachImportId(group, response.data.id); } catch (e) { createFlash({ message: s__('BulkImport|Importing the group failed'), diff --git a/app/assets/javascripts/import_entities/import_groups/graphql/queries/bulk_import_source_groups.query.graphql b/app/assets/javascripts/import_entities/import_groups/graphql/queries/bulk_import_source_groups.query.graphql index 8d52d94925c..28dfefdf8a7 100644 --- a/app/assets/javascripts/import_entities/import_groups/graphql/queries/bulk_import_source_groups.query.graphql +++ b/app/assets/javascripts/import_entities/import_groups/graphql/queries/bulk_import_source_groups.query.graphql @@ -1,7 +1,15 @@ #import "../fragments/bulk_import_source_group_item.fragment.graphql" -query bulkImportSourceGroups { - bulkImportSourceGroups @client { - ...BulkImportSourceGroupItem +query bulkImportSourceGroups($page: Int = 1, $perPage: Int = 20, $filter: String = "") { + bulkImportSourceGroups(page: $page, filter: $filter, perPage: $perPage) @client { + nodes { + ...BulkImportSourceGroupItem + } + pageInfo { + page + perPage + total + totalPages + } } } diff --git a/app/assets/javascripts/import_entities/import_groups/graphql/services/source_groups_manager.js b/app/assets/javascripts/import_entities/import_groups/graphql/services/source_groups_manager.js index 047b04fe7d6..261e30edbbb 100644 --- a/app/assets/javascripts/import_entities/import_groups/graphql/services/source_groups_manager.js +++ b/app/assets/javascripts/import_entities/import_groups/graphql/services/source_groups_manager.js @@ -14,6 +14,12 @@ function generateGroupId(id) { } export class SourceGroupsManager { + static importMap = new Map(); + + static attachImportId(group, importId) { + SourceGroupsManager.importMap.set(importId, group.id); + } + constructor({ client }) { this.client = client; } @@ -36,6 +42,10 @@ export class SourceGroupsManager { this.update(group, fn); } + findByImportId(importId) { + return this.findById(SourceGroupsManager.importMap.get(importId)); + } + setImportStatus(group, status) { this.update(group, (sourceGroup) => { // eslint-disable-next-line no-param-reassign diff --git a/app/assets/javascripts/import_entities/import_groups/graphql/services/status_poller.js b/app/assets/javascripts/import_entities/import_groups/graphql/services/status_poller.js index 41dd25b9150..63cd6b48fc4 100644 --- a/app/assets/javascripts/import_entities/import_groups/graphql/services/status_poller.js +++ b/app/assets/javascripts/import_entities/import_groups/graphql/services/status_poller.js @@ -1,68 +1,47 @@ -import gql from 'graphql-tag'; +import Visibility from 'visibilityjs'; import createFlash from '~/flash'; +import axios from '~/lib/utils/axios_utils'; +import Poll from '~/lib/utils/poll'; import { s__ } from '~/locale'; -import bulkImportSourceGroupsQuery from '../queries/bulk_import_source_groups.query.graphql'; -import { STATUSES } from '../../../constants'; import { SourceGroupsManager } from './source_groups_manager'; -const groupId = (i) => `group${i}`; - -function generateGroupsQuery(groups) { - return gql`{ - ${groups - .map( - (g, idx) => - `${groupId(idx)}: group(fullPath: "${g.import_target.target_namespace}/${ - g.import_target.new_name - }") { id }`, - ) - .join('\n')} - }`; -} - export class StatusPoller { - constructor({ client, interval }) { + constructor({ client, pollPath }) { this.client = client; - this.interval = interval; - this.timeoutId = null; - this.groupManager = new SourceGroupsManager({ client }); - } - startPolling() { - if (this.timeoutId) { - return; - } + this.eTagPoll = new Poll({ + resource: { + fetchJobs: () => axios.get(pollPath), + }, + method: 'fetchJobs', + successCallback: ({ data }) => this.updateImportsStatuses(data), + errorCallback: () => + createFlash({ + message: s__('BulkImport|Update of import statuses with realtime changes failed'), + }), + }); + + Visibility.change(() => { + if (!Visibility.hidden()) { + this.eTagPoll.restart(); + } else { + this.eTagPoll.stop(); + } + }); - this.checkPendingImports(); + this.groupManager = new SourceGroupsManager({ client }); } - stopPolling() { - clearTimeout(this.timeoutId); - this.timeoutId = null; + startPolling() { + this.eTagPoll.makeRequest(); } - async checkPendingImports() { - try { - const { bulkImportSourceGroups } = this.client.readQuery({ - query: bulkImportSourceGroupsQuery, - }); - const groupsInProgress = bulkImportSourceGroups.filter((g) => g.status === STATUSES.STARTED); - if (groupsInProgress.length) { - const { data: results } = await this.client.query({ - query: generateGroupsQuery(groupsInProgress), - fetchPolicy: 'no-cache', - }); - const completedGroups = groupsInProgress.filter((_, idx) => Boolean(results[groupId(idx)])); - completedGroups.forEach((group) => { - this.groupManager.setImportStatus(group, STATUSES.FINISHED); - }); + async updateImportsStatuses(importStatuses) { + importStatuses.forEach(({ id, status_name: statusName }) => { + const group = this.groupManager.findByImportId(id); + if (group.id) { + this.groupManager.setImportStatus(group, statusName); } - } catch (e) { - createFlash({ - message: s__('BulkImport|Update of import statuses with realtime changes failed'), - }); - } finally { - this.timeoutId = setTimeout(() => this.checkPendingImports(), this.interval); - } + }); } } diff --git a/app/assets/javascripts/import_entities/import_groups/index.js b/app/assets/javascripts/import_entities/import_groups/index.js index bf427075564..cd837a840e4 100644 --- a/app/assets/javascripts/import_entities/import_groups/index.js +++ b/app/assets/javascripts/import_entities/import_groups/index.js @@ -1,8 +1,8 @@ import Vue from 'vue'; import VueApollo from 'vue-apollo'; import Translate from '~/vue_shared/translate'; -import { createApolloClient } from './graphql/client_factory'; import ImportTable from './components/import_table.vue'; +import { createApolloClient } from './graphql/client_factory'; Vue.use(Translate); Vue.use(VueApollo); @@ -10,13 +10,20 @@ Vue.use(VueApollo); export function mountImportGroupsApp(mountElement) { if (!mountElement) return undefined; - const { statusPath, availableNamespacesPath, createBulkImportPath } = mountElement.dataset; + const { + statusPath, + availableNamespacesPath, + createBulkImportPath, + jobsPath, + sourceUrl, + } = mountElement.dataset; const apolloProvider = new VueApollo({ defaultClient: createApolloClient({ endpoints: { status: statusPath, availableNamespaces: availableNamespacesPath, createBulkImport: createBulkImportPath, + jobs: jobsPath, }, }), }); @@ -25,7 +32,11 @@ export function mountImportGroupsApp(mountElement) { el: mountElement, apolloProvider, render(createElement) { - return createElement(ImportTable); + return createElement(ImportTable, { + props: { + sourceUrl, + }, + }); }, }); } 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 192d6e056cd..be09052fb7e 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 @@ -1,6 +1,6 @@ <script> +import { GlButton, GlLoadingIcon, GlIntersectionObserver, GlModal, GlFormInput } from '@gitlab/ui'; import { mapActions, mapState, mapGetters } from 'vuex'; -import { GlButton, GlLoadingIcon, GlIntersectionObserver, GlModal } from '@gitlab/ui'; import { n__, __, sprintf } from '~/locale'; import ProviderRepoTableRow from './provider_repo_table_row.vue'; @@ -12,6 +12,7 @@ export default { GlButton, GlModal, GlIntersectionObserver, + GlFormInput, }, props: { providerTitle: { @@ -115,13 +116,13 @@ export default { <template> <div> - <p class="light text-nowrap mt-2"> + <p class="gl-text-gray-900 gl-white-space-nowrap gl-mt-3"> {{ 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"> + <div class="gl-display-flex gl-justify-content-space-between gl-flex-wrap gl-mb-5"> <gl-button variant="success" :loading="isImportingAnyRepo" @@ -148,24 +149,29 @@ export default { <slot name="actions"></slot> <form v-if="filterable" class="gl-ml-auto" novalidate @submit.prevent> - <input + <gl-form-input data-qa-selector="githubish_import_filter_field" - class="form-control" name="filter" :placeholder="__('Filter your repositories by name')" autofocus - size="40" + size="lg" @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> + <div v-if="repositories.length" class="gl-w-full"> + <table> + <thead class="gl-border-0 gl-border-solid gl-border-t-1 gl-border-gray-100"> + <th class="import-jobs-from-col gl-p-4 gl-vertical-align-top gl-border-b-1"> + {{ fromHeaderText }} + </th> + <th class="import-jobs-to-col gl-p-4 gl-vertical-align-top gl-border-b-1"> + {{ __('To GitLab') }} + </th> + <th class="import-jobs-status-col gl-p-4 gl-vertical-align-top gl-border-b-1"> + {{ __('Status') }} + </th> + <th class="import-jobs-cta-col gl-p-4 gl-vertical-align-top gl-border-b-1"></th> </thead> <tbody> <template v-for="repo in repositories"> @@ -183,9 +189,9 @@ export default { :key="pagePaginationStateKey" @appear="fetchRepos" /> - <gl-loading-icon v-if="isLoading" class="import-projects-loading-icon" size="md" /> + <gl-loading-icon v-if="isLoading" class="gl-mt-7" size="md" /> - <div v-if="!isLoading && repositories.length === 0" class="text-center"> + <div v-if="!isLoading && repositories.length === 0" class="gl-text-center"> <strong>{{ emptyStateText }}</strong> </div> </div> diff --git a/app/assets/javascripts/import_entities/import_projects/components/provider_repo_table_row.vue b/app/assets/javascripts/import_entities/import_projects/components/provider_repo_table_row.vue index 983abda57f7..289c83979bb 100644 --- a/app/assets/javascripts/import_entities/import_projects/components/provider_repo_table_row.vue +++ b/app/assets/javascripts/import_entities/import_projects/components/provider_repo_table_row.vue @@ -1,8 +1,8 @@ <script> +import { GlIcon, GlBadge, GlFormInput, GlButton, GlLink } from '@gitlab/ui'; import { mapState, mapGetters, mapActions } from 'vuex'; -import { GlIcon, GlBadge } from '@gitlab/ui'; -import Select2Select from '~/vue_shared/components/select2_select.vue'; import { __ } from '~/locale'; +import Select2Select from '~/vue_shared/components/select2_select.vue'; import ImportStatus from '../../components/import_status.vue'; import { STATUSES } from '../../constants'; import { isProjectImportable, isIncompatible, getImportStatus } from '../utils'; @@ -12,8 +12,11 @@ export default { components: { Select2Select, ImportStatus, + GlFormInput, + GlButton, GlIcon, GlBadge, + GlLink, }, props: { repo: { @@ -61,7 +64,7 @@ export default { select2Options() { return { data: this.availableNamespaces, - containerCssClass: 'import-namespace-select qa-project-namespace-select w-auto', + containerCssClass: 'import-namespace-select qa-project-namespace-select gl-w-auto', }; }, @@ -97,52 +100,56 @@ export default { </script> <template> - <tr class="qa-project-import-row import-row"> - <td> - <a - :href="repo.importSource.providerLink" - rel="noreferrer noopener" - target="_blank" - data-testid="providerLink" + <tr + class="qa-project-import-row gl-h-11 gl-border-0 gl-border-solid gl-border-t-1 gl-border-gray-100 gl-h-11" + > + <td class="gl-p-4"> + <gl-link :href="repo.importSource.providerLink" target="_blank" data-testid="providerLink" >{{ repo.importSource.fullName }} <gl-icon v-if="repo.importSource.providerLink" name="external-link" /> - </a> + </gl-link> </td> - <td class="d-flex flex-wrap flex-lg-nowrap" data-testid="fullPath"> + <td + class="gl-display-flex gl-flex-sm-wrap gl-p-4 gl-pt-5 gl-vertical-align-top" + 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 - > - <input - v-model="newNameInput" - type="text" - class="form-control import-project-name-input qa-project-path-field" - /> + <div class="import-entities-target-select gl-display-flex gl-align-items-stretch gl-w-full"> + <select2-select v-model="targetNamespaceSelect" :options="select2Options" /> + <div + class="import-entities-target-select-separator gl-px-3 gl-display-flex gl-align-items-center gl-border-solid gl-border-0 gl-border-t-1 gl-border-b-1" + > + / + </div> + <gl-form-input + v-model="newNameInput" + class="gl-rounded-top-left-none gl-rounded-bottom-left-none qa-project-path-field" + /> + </div> </template> <template v-else-if="repo.importedProject">{{ displayFullPath }}</template> </td> - <td> + <td class="gl-p-4"> <import-status :status="importStatus" /> </td> <td data-testid="actions"> - <a + <gl-button v-if="isFinished" class="btn btn-default" :href="repo.importedProject.fullPath" rel="noreferrer noopener" target="_blank" >{{ __('Go to project') }} - </a> - <button + </gl-button> + <gl-button v-if="isImportNotStarted" type="button" - class="qa-import-button btn btn-default" + class="qa-import-button" @click="fetchImport(repo.importSource.id)" > {{ importButtonText }} - </button> + </gl-button> <gl-badge v-else-if="isIncompatible" variant="danger">{{ __('Incompatible project') }}</gl-badge> diff --git a/app/assets/javascripts/import_entities/import_projects/index.js b/app/assets/javascripts/import_entities/import_projects/index.js index 7373b628f2b..6b7fe23ed60 100644 --- a/app/assets/javascripts/import_entities/import_projects/index.js +++ b/app/assets/javascripts/import_entities/import_projects/index.js @@ -1,6 +1,6 @@ import Vue from 'vue'; -import Translate from '~/vue_shared/translate'; import { parseBoolean } from '~/lib/utils/common_utils'; +import Translate from '~/vue_shared/translate'; import ImportProjectsTable from './components/import_projects_table.vue'; import createStore from './store'; 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 a8217ff1033..33f8dbb8737 100644 --- a/app/assets/javascripts/import_entities/import_projects/store/actions.js +++ b/app/assets/javascripts/import_entities/import_projects/store/actions.js @@ -1,14 +1,14 @@ import Visibility from 'visibilityjs'; -import * as types from './mutation_types'; -import { isProjectImportable } from '../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'; -import { s__, sprintf } from '~/locale'; import axios from '~/lib/utils/axios_utils'; +import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; import httpStatusCodes from '~/lib/utils/http_status'; +import Poll from '~/lib/utils/poll'; import { capitalizeFirstCharacter } from '~/lib/utils/text_utility'; +import { visitUrl, objectToQuery } from '~/lib/utils/url_utility'; +import { s__, sprintf } from '~/locale'; +import { isProjectImportable } from '../utils'; +import * as types from './mutation_types'; let eTagPoll; diff --git a/app/assets/javascripts/import_entities/import_projects/store/index.js b/app/assets/javascripts/import_entities/import_projects/store/index.js index 7ba12f81eb9..a2880e7d031 100644 --- a/app/assets/javascripts/import_entities/import_projects/store/index.js +++ b/app/assets/javascripts/import_entities/import_projects/store/index.js @@ -1,9 +1,9 @@ import Vue from 'vue'; import Vuex from 'vuex'; -import state from './state'; import actionsFactory from './actions'; import * as getters from './getters'; import mutations from './mutations'; +import state from './state'; Vue.use(Vuex); 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 1a96508bd48..c5e1922597a 100644 --- a/app/assets/javascripts/import_entities/import_projects/store/mutations.js +++ b/app/assets/javascripts/import_entities/import_projects/store/mutations.js @@ -1,6 +1,6 @@ import Vue from 'vue'; -import * as types from './mutation_types'; import { STATUSES } from '../../constants'; +import * as types from './mutation_types'; const makeNewImportedProject = (importedProject) => ({ importSource: { |