Commit d0965893 authored by Illya Klymov's avatar Illya Klymov

Introduce new import UI for all providers

- update templates
- introduce filterable flag for underlying component
parent 57b914cc
<script>
import { GlAlert, GlSprintf, GlLink } from '@gitlab/ui';
import ImportProjectsTable from './import_projects_table.vue';
export default {
components: {
ImportProjectsTable,
GlAlert,
GlSprintf,
GlLink,
},
props: {
providerTitle: {
type: String,
required: true,
},
},
data() {
return {
isWarningDismissed: false,
};
},
computed: {
currentPage() {
return window.location.href;
},
},
};
</script>
<template>
<import-projects-table provider-title="providerTitle">
<template #actions>
<slot name="actions"></slot>
</template>
<template #incompatible-repos-warning>
<gl-alert
v-if="!isWarningDismissed"
variant="warning"
class="gl-my-2"
@dismiss="isWarningDismissed = true"
>
<gl-sprintf
:message="
__(
'One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git.',
)
"
>
<template #provider>
{{ providerTitle }}
</template>
</gl-sprintf>
<gl-sprintf
:message="
__(
'Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again.',
)
"
>
<template #link="{ content }">
<gl-link
href="https://www.atlassian.com/git/tutorials/migrating-overview"
target="_blank"
>{{ content }}</gl-link
>
</template>
<template #linkToImportFlow>
<gl-link :href="currentPage">{{ __('import flow') }}</gl-link>
</template>
</gl-sprintf>
</gl-alert>
</template>
</import-projects-table>
</template>
...@@ -24,6 +24,11 @@ export default { ...@@ -24,6 +24,11 @@ export default {
type: String, type: String,
required: true, required: true,
}, },
filterable: {
type: Boolean,
required: false,
default: true,
},
}, },
computed: { computed: {
...@@ -114,7 +119,7 @@ export default { ...@@ -114,7 +119,7 @@ export default {
{{ importAllButtonText }} {{ importAllButtonText }}
</gl-button> </gl-button>
<slot name="actions"></slot> <slot name="actions"></slot>
<form class="gl-ml-auto" novalidate @submit.prevent> <form v-if="filterable" class="gl-ml-auto" novalidate @submit.prevent>
<input <input
:value="filter" :value="filter"
data-qa-selector="githubish_import_filter_field" data-qa-selector="githubish_import_filter_field"
......
...@@ -30,6 +30,7 @@ export function initStoreFromElement(element) { ...@@ -30,6 +30,7 @@ export function initStoreFromElement(element) {
export function initPropsFromElement(element) { export function initPropsFromElement(element) {
return { return {
providerTitle: element.dataset.providerTitle, providerTitle: element.dataset.providerTitle,
filterable: parseBoolean(element.dataset.filterable),
}; };
} }
......
...@@ -2,6 +2,7 @@ import Visibility from 'visibilityjs'; ...@@ -2,6 +2,7 @@ import Visibility from 'visibilityjs';
import * as types from './mutation_types'; import * as types from './mutation_types';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import Poll from '~/lib/utils/poll'; import Poll from '~/lib/utils/poll';
import { visitUrl } from '~/lib/utils/url_utility';
import createFlash from '~/flash'; import createFlash from '~/flash';
import { s__, sprintf } from '~/locale'; import { s__, sprintf } from '~/locale';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
...@@ -9,6 +10,9 @@ import { jobsPathWithFilter, reposPathWithFilter } from './getters'; ...@@ -9,6 +10,9 @@ import { jobsPathWithFilter, reposPathWithFilter } from './getters';
let eTagPoll; let eTagPoll;
const hasRedirectInError = e => e?.response?.data?.error?.redirect;
const redirectToUrlInError = e => visitUrl(e.response.data.error.redirect);
export const clearJobsEtagPoll = () => { export const clearJobsEtagPoll = () => {
eTagPoll = null; eTagPoll = null;
}; };
...@@ -33,14 +37,18 @@ export const fetchRepos = ({ state, dispatch, commit }) => { ...@@ -33,14 +37,18 @@ export const fetchRepos = ({ state, dispatch, commit }) => {
commit(types.RECEIVE_REPOS_SUCCESS, convertObjectPropsToCamelCase(data, { deep: true })), commit(types.RECEIVE_REPOS_SUCCESS, convertObjectPropsToCamelCase(data, { deep: true })),
) )
.then(() => dispatch('fetchJobs')) .then(() => dispatch('fetchJobs'))
.catch(() => { .catch(e => {
createFlash( if (hasRedirectInError(e)) {
sprintf(s__('ImportProjects|Requesting your %{provider} repositories failed'), { redirectToUrlInError(e);
provider, } else {
}), createFlash(
); sprintf(s__('ImportProjects|Requesting your %{provider} repositories failed'), {
provider,
commit(types.RECEIVE_REPOS_ERROR); }),
);
commit(types.RECEIVE_REPOS_ERROR);
}
}); });
}; };
...@@ -87,8 +95,13 @@ export const fetchJobs = ({ state, commit, dispatch }) => { ...@@ -87,8 +95,13 @@ export const fetchJobs = ({ state, commit, dispatch }) => {
method: 'fetchJobs', method: 'fetchJobs',
successCallback: ({ data }) => successCallback: ({ data }) =>
commit(types.RECEIVE_JOBS_SUCCESS, convertObjectPropsToCamelCase(data, { deep: true })), commit(types.RECEIVE_JOBS_SUCCESS, convertObjectPropsToCamelCase(data, { deep: true })),
errorCallback: () => errorCallback: e => {
createFlash(s__('ImportProjects|Update of imported projects with realtime changes failed')), if (hasRedirectInError(e)) {
redirectToUrlInError(e);
} else {
createFlash(s__('ImportProjects|Update of imported projects with realtime changes failed'));
}
},
data: { filter }, data: { filter },
}); });
......
import Vue from 'vue';
import { initStoreFromElement, initPropsFromElement } from '~/import_projects';
import BitbucketStatusTable from '~/import_projects/components/bitbucket_status_table.vue';
document.addEventListener('DOMContentLoaded', () => {
const mountElement = document.getElementById('import-projects-mount-element');
if (!mountElement) return undefined;
const store = initStoreFromElement(mountElement);
const props = initPropsFromElement(mountElement);
return new Vue({
el: mountElement,
store,
render(createElement) {
return createElement(BitbucketStatusTable, { props });
},
});
});
<script>
import { GlButton } from '@gitlab/ui';
import BitbucketStatusTable from '~/import_projects/components/bitbucket_status_table.vue';
export default {
components: {
BitbucketStatusTable,
GlButton,
},
props: {
providerTitle: {
type: String,
required: true,
},
reconfigurePath: {
type: String,
required: true,
},
},
};
</script>
<template>
<bitbucket-status-table :provider-title="providerTitle">
<template #actions>
<gl-button variant="info" class="gl-ml-3" data-method="post" :href="reconfigurePath">{{
__('Reconfigure')
}}</gl-button>
</template>
</bitbucket-status-table>
</template>
import Vue from 'vue';
import { initStoreFromElement, initPropsFromElement } from '~/import_projects';
import BitbucketServerStatusTable from './components/bitbucket_server_status_table.vue';
document.addEventListener('DOMContentLoaded', () => {
const mountElement = document.getElementById('import-projects-mount-element');
if (!mountElement) return undefined;
const store = initStoreFromElement(mountElement);
const props = initPropsFromElement(mountElement);
const { reconfigurePath } = mountElement.dataset;
return new Vue({
el: mountElement,
store,
render(createElement) {
return createElement(BitbucketServerStatusTable, { props: { ...props, reconfigurePath } });
},
});
});
import mountImportProjectsTable from '~/import_projects';
document.addEventListener('DOMContentLoaded', () => {
const mountElement = document.getElementById('import-projects-mount-element');
mountImportProjectsTable(mountElement);
});
import mountImportProjectsTable from '~/import_projects';
document.addEventListener('DOMContentLoaded', () => {
const mountElement = document.getElementById('import-projects-mount-element');
mountImportProjectsTable(mountElement);
});
- provider = local_assigns.fetch(:provider) - provider = local_assigns.fetch(:provider)
- extra_data = local_assigns.fetch(:extra_data, {})
- filterable = local_assigns.fetch(:filterable, true)
- provider_title = Gitlab::ImportSources.title(provider) - provider_title = Gitlab::ImportSources.title(provider)
#import-projects-mount-element{ data: { provider: provider, provider_title: provider_title, #import-projects-mount-element{ data: { provider: provider, provider_title: provider_title,
...@@ -6,4 +8,5 @@ ...@@ -6,4 +8,5 @@
ci_cd_only: has_ci_cd_only_params?.to_s, ci_cd_only: has_ci_cd_only_params?.to_s,
repos_path: url_for([:status, :import, provider, format: :json]), repos_path: url_for([:status, :import, provider, format: :json]),
jobs_path: url_for([:realtime_changes, :import, provider, format: :json]), jobs_path: url_for([:realtime_changes, :import, provider, format: :json]),
import_path: url_for([:import, provider, format: :json]) } } import_path: url_for([:import, provider, format: :json]),
filterable: filterable.to_s }.merge(extra_data) }
...@@ -5,90 +5,93 @@ ...@@ -5,90 +5,93 @@
%i.fa.fa-bitbucket %i.fa.fa-bitbucket
= _('Import projects from Bitbucket') = _('Import projects from Bitbucket')
- if @repos.any? - if Feature.enabled?(:new_import_ui)
%p.light = render 'import/githubish_status', provider: 'bitbucket'
= _('Select projects you want to import.') - else
%p - if @repos.any?
- if @incompatible_repos.any? %p.light
= button_tag class: 'btn btn-import btn-success js-import-all' do = _('Select projects you want to import.')
= _('Import all compatible projects') %p
= icon('spinner spin', class: 'loading-icon') - if @incompatible_repos.any?
- else = button_tag class: 'btn btn-import btn-success js-import-all' do
= button_tag class: 'btn btn-import btn-success js-import-all' do = _('Import all compatible projects')
= _('Import all projects') = icon('spinner spin', class: 'loading-icon')
= icon('spinner spin', class: 'loading-icon') - else
= button_tag class: 'btn btn-import btn-success js-import-all' do
= _('Import all projects')
= icon('spinner spin', class: 'loading-icon')
.position-relative.ms-no-clear.d-flex.flex-fill.float-right.append-bottom-10 .position-relative.ms-no-clear.d-flex.flex-fill.float-right.append-bottom-10
= form_tag status_import_bitbucket_path, method: 'get' do = form_tag status_import_bitbucket_path, method: 'get' do
= text_field_tag :filter, @filter, class: 'form-control pr-5', placeholder: _('Filter projects'), size: 40, autofocus: true, 'aria-label': _('Search') = text_field_tag :filter, @filter, class: 'form-control pr-5', placeholder: _('Filter projects'), size: 40, autofocus: true, 'aria-label': _('Search')
.position-absolute.position-top-0.d-flex.align-items-center.text-muted.position-right-0.h-100 .position-absolute.position-top-0.d-flex.align-items-center.text-muted.position-right-0.h-100
.border-left .border-left
%button{ class: 'btn btn-transparent btn-secondary', 'aria-label': _('Search Button'), type: 'submit' } %button{ class: 'btn btn-transparent btn-secondary', 'aria-label': _('Search Button'), type: 'submit' }
%i{ class: 'fa fa-search', 'aria-hidden': true } %i{ class: 'fa fa-search', 'aria-hidden': true }
.table-responsive .table-responsive
%table.table.import-jobs %table.table.import-jobs
%colgroup.import-jobs-from-col %colgroup.import-jobs-from-col
%colgroup.import-jobs-to-col %colgroup.import-jobs-to-col
%colgroup.import-jobs-status-col %colgroup.import-jobs-status-col
%thead %thead
%tr %tr
%th= _('From Bitbucket') %th= _('From Bitbucket')
%th= _('To GitLab') %th= _('To GitLab')
%th= _('Status') %th= _('Status')
%tbody %tbody
- @already_added_projects.each do |project| - @already_added_projects.each do |project|
%tr{ id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}" } %tr{ id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}" }
%td %td
= link_to project.import_source, "https://bitbucket.org/#{project.import_source}", target: '_blank', rel: 'noopener noreferrer' = link_to project.import_source, "https://bitbucket.org/#{project.import_source}", target: '_blank', rel: 'noopener noreferrer'
%td %td
= link_to project.full_path, [project.namespace.becomes(Namespace), project] = link_to project.full_path, [project.namespace.becomes(Namespace), project]
%td.job-status %td.job-status
- case project.import_status - case project.import_status
- when 'finished' - when 'finished'
%span %span
%i.fa.fa-check %i.fa.fa-check
= _('done') = _('done')
- when 'started' - when 'started'
%i.fa.fa-spinner.fa-spin %i.fa.fa-spinner.fa-spin
= _('started') = _('started')
- else - else
= project.human_import_status_name = project.human_import_status_name
- @repos.each do |repo| - @repos.each do |repo|
%tr{ id: "repo_#{repo.owner}___#{repo.slug}" } %tr{ id: "repo_#{repo.owner}___#{repo.slug}" }
%td %td
= link_to repo.full_name, "https://bitbucket.org/#{repo.full_name}", target: '_blank', rel: 'noopener noreferrer' = link_to repo.full_name, "https://bitbucket.org/#{repo.full_name}", target: '_blank', rel: 'noopener noreferrer'
%td.import-target %td.import-target
%fieldset.row %fieldset.row
.input-group .input-group
.project-path.input-group-prepend .project-path.input-group-prepend
- if current_user.can_select_namespace? - if current_user.can_select_namespace?
- selected = params[:namespace_id] || :current_user - selected = params[:namespace_id] || :current_user
- opts = current_user.can_create_group? ? { extra_group: Group.new(name: repo.owner, path: repo.owner) } : {} - opts = current_user.can_create_group? ? { extra_group: Group.new(name: repo.owner, path: repo.owner) } : {}
= select_tag :namespace_id, namespaces_options(selected, opts.merge({ display_path: true })), { class: 'select2 js-select-namespace', tabindex: 1 } = select_tag :namespace_id, namespaces_options(selected, opts.merge({ display_path: true })), { class: 'select2 js-select-namespace', tabindex: 1 }
- else - else
= text_field_tag :path, current_user.namespace_path, class: "input-group-text input-large form-control", tabindex: 1, disabled: true = text_field_tag :path, current_user.namespace_path, class: "input-group-text input-large form-control", tabindex: 1, disabled: true
%span.input-group-prepend %span.input-group-prepend
.input-group-text / .input-group-text /
= text_field_tag :path, sanitize_project_name(repo.slug), class: "input-mini form-control", tabindex: 2, autofocus: true, required: true = text_field_tag :path, sanitize_project_name(repo.slug), class: "input-mini form-control", tabindex: 2, autofocus: true, required: true
%td.import-actions.job-status %td.import-actions.job-status
= button_tag class: 'btn btn-import js-add-to-import' do = button_tag class: 'btn btn-import js-add-to-import' do
= _('Import') = _('Import')
= icon('spinner spin', class: 'loading-icon') = icon('spinner spin', class: 'loading-icon')
- @incompatible_repos.each do |repo| - @incompatible_repos.each do |repo|
%tr{ id: "repo_#{repo.owner}___#{repo.slug}" } %tr{ id: "repo_#{repo.owner}___#{repo.slug}" }
%td %td
= link_to repo.full_name, "https://bitbucket.org/#{repo.full_name}", target: '_blank', rel: 'noopener noreferrer' = link_to repo.full_name, "https://bitbucket.org/#{repo.full_name}", target: '_blank', rel: 'noopener noreferrer'
%td.import-target %td.import-target
%td.import-actions-job-status %td.import-actions-job-status
= label_tag _('Incompatible Project'), nil, class: 'label badge-danger' = label_tag _('Incompatible Project'), nil, class: 'label badge-danger'
- if @incompatible_repos.any? - if @incompatible_repos.any?
%p %p
= _("One or more of your Bitbucket projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git.") = _("One or more of your Bitbucket projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git.")
- link_to_git = link_to(_('Git'), 'https://www.atlassian.com/git/tutorials/migrating-overview') - link_to_git = link_to(_('Git'), 'https://www.atlassian.com/git/tutorials/migrating-overview')
- link_to_import_flow = link_to(_('import flow'), status_import_bitbucket_path) - link_to_import_flow = link_to(_('import flow'), status_import_bitbucket_path)
= _("Please convert them to %{link_to_git}, and go through the %{link_to_import_flow} again.").html_safe % { link_to_git: link_to_git, link_to_import_flow: link_to_import_flow } = _("Please convert them to %{link_to_git}, and go through the %{link_to_import_flow} again.").html_safe % { link_to_git: link_to_git, link_to_import_flow: link_to_import_flow }
.js-importer-status{ data: { jobs_import_path: "#{jobs_import_bitbucket_path}", import_path: "#{import_bitbucket_path}" } } .js-importer-status{ data: { jobs_import_path: "#{jobs_import_bitbucket_path}", import_path: "#{import_bitbucket_path}" } }
...@@ -5,91 +5,94 @@ ...@@ -5,91 +5,94 @@
%i.fa.fa-bitbucket-square %i.fa.fa-bitbucket-square
= _('Import projects from Bitbucket Server') = _('Import projects from Bitbucket Server')
- if @repos.any? - if Feature.enabled?(:new_import_ui)
%p.light = render 'import/githubish_status', provider: 'bitbucket_server', extra_data: { reconfigure_path: configure_import_bitbucket_server_path }
= _('Select projects you want to import.') - else
.btn-group - if @repos.any?
- if @incompatible_repos.any? %p.light
= button_tag class: 'btn btn-import btn-success js-import-all' do = _('Select projects you want to import.')
= _('Import all compatible projects') .btn-group
= icon('spinner spin', class: 'loading-icon') - if @incompatible_repos.any?
- else = button_tag class: 'btn btn-import btn-success js-import-all' do
= button_tag class: 'btn btn-import btn-success js-import-all' do = _('Import all compatible projects')
= _('Import all projects') = icon('spinner spin', class: 'loading-icon')
= icon('spinner spin', class: 'loading-icon') - else
= button_tag class: 'btn btn-import btn-success js-import-all' do
= _('Import all projects')
= icon('spinner spin', class: 'loading-icon')
.btn-group .btn-group
= link_to('Reconfigure', configure_import_bitbucket_server_path, class: 'btn btn-primary', method: :post) = link_to('Reconfigure', configure_import_bitbucket_server_path, class: 'btn btn-primary', method: :post)
.input-btn-group.float-right .input-btn-group.float-right
= form_tag status_import_bitbucket_server_path, :method => 'get' do = form_tag status_import_bitbucket_server_path, :method => 'get' do
= text_field_tag :filter, sanitize(params[:filter]), class: 'form-control append-bottom-10', placeholder: _('Filter your projects by name'), size: 40, autoFocus: true = text_field_tag :filter, sanitize(params[:filter]), class: 'form-control append-bottom-10', placeholder: _('Filter your projects by name'), size: 40, autoFocus: true
.table-responsive.prepend-top-10 .table-responsive.prepend-top-10
%table.table.import-jobs %table.table.import-jobs
%colgroup.import-jobs-from-col %colgroup.import-jobs-from-col
%colgroup.import-jobs-to-col %colgroup.import-jobs-to-col
%colgroup.import-jobs-status-col %colgroup.import-jobs-status-col
%thead %thead
%tr %tr
%th= _('From Bitbucket Server') %th= _('From Bitbucket Server')
%th= _('To GitLab') %th= _('To GitLab')
%th= _('Status') %th= _('Status')
%tbody %tbody
- @already_added_projects.each do |project| - @already_added_projects.each do |project|
%tr{ id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}" } %tr{ id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}" }
%td %td
= link_to project.import_source, project.import_source, target: '_blank', rel: 'noopener noreferrer' = link_to project.import_source, project.import_source, target: '_blank', rel: 'noopener noreferrer'
%td %td
= link_to project.full_path, [project.namespace.becomes(Namespace), project] = link_to project.full_path, [project.namespace.becomes(Namespace), project]
%td.job-status %td.job-status
- case project.import_status - case project.import_status
- when 'finished' - when 'finished'
= icon('check', text: 'Done') = icon('check', text: 'Done')
- when 'started' - when 'started'
= icon('spin', text: 'started') = icon('spin', text: 'started')
- else - else
= project.human_import_status_name = project.human_import_status_name
- @repos.each do |repo| - @repos.each do |repo|
%tr{ id: "repo_#{repo.project_key}___#{repo.slug}", data: { project: repo.project_key, repository: repo.slug } } %tr{ id: "repo_#{repo.project_key}___#{repo.slug}", data: { project: repo.project_key, repository: repo.slug } }
%td %td
= link_to repo.browse_url, repo.browse_url, target: '_blank', rel: 'noopener noreferrer' = link_to repo.browse_url, repo.browse_url, target: '_blank', rel: 'noopener noreferrer'
%td.import-target %td.import-target
%fieldset.row %fieldset.row
.input-group .input-group
.project-path.input-group-prepend .project-path.input-group-prepend
- if current_user.can_select_namespace? - if current_user.can_select_namespace?
- selected = params[:namespace_id] || :extra_group - selected = params[:namespace_id] || :extra_group
- opts = current_user.can_create_group? ? { extra_group: Group.new(name: sanitize_project_name(repo.project_key), path: sanitize_project_name(repo.project_key)) } : {} - opts = current_user.can_create_group? ? { extra_group: Group.new(name: sanitize_project_name(repo.project_key), path: sanitize_project_name(repo.project_key)) } : {}
= select_tag :namespace_id, namespaces_options(selected, opts.merge({ display_path: true })), { class: 'select2 js-select-namespace', tabindex: 1 } = select_tag :namespace_id, namespaces_options(selected, opts.merge({ display_path: true })), { class: 'select2 js-select-namespace', tabindex: 1 }
- else - else
= text_field_tag :path, current_user.namespace_path, class: "input-group-text input-large form-control", tabindex: 1, disabled: true = text_field_tag :path, current_user.namespace_path, class: "input-group-text input-large form-control", tabindex: 1, disabled: true
%span.input-group-prepend %span.input-group-prepend
.input-group-text / .input-group-text /
= text_field_tag :path, sanitize_project_name(repo.slug), class: "input-mini form-control", tabindex: 2, required: true = text_field_tag :path, sanitize_project_name(repo.slug), class: "input-mini form-control", tabindex: 2, required: true
%td.import-actions.job-status %td.import-actions.job-status
= button_tag class: 'btn btn-import js-add-to-import' do = button_tag class: 'btn btn-import js-add-to-import' do
Import Import
= icon('spinner spin', class: 'loading-icon') = icon('spinner spin', class: 'loading-icon')
- @incompatible_repos.each do |repo| - @incompatible_repos.each do |repo|
%tr{ id: "repo_#{repo.project_key}___#{repo.slug}" } %tr{ id: "repo_#{repo.project_key}___#{repo.slug}" }
%td %td
= link_to repo.browse_url, repo.browse_url, target: '_blank', rel: 'noopener noreferrer' = link_to repo.browse_url, repo.browse_url, target: '_blank', rel: 'noopener noreferrer'
%td.import-target %td.import-target
%td.import-actions-job-status %td.import-actions-job-status
= label_tag 'Incompatible Project', nil, class: 'label badge-danger' = label_tag 'Incompatible Project', nil, class: 'label badge-danger'
- if @incompatible_repos.any? - if @incompatible_repos.any?
%p %p
One or more of your Bitbucket Server projects cannot be imported into GitLab One or more of your Bitbucket Server projects cannot be imported into GitLab
directly because they use Subversion or Mercurial for version control, directly because they use Subversion or Mercurial for version control,
rather than Git. Please convert rather than Git. Please convert
= link_to 'them to Git,', 'https://www.atlassian.com/git/tutorials/migrating-overview' = link_to 'them to Git,', 'https://www.atlassian.com/git/tutorials/migrating-overview'
and go through the and go through the
= link_to 'import flow', status_import_bitbucket_server_path = link_to 'import flow', status_import_bitbucket_server_path
again. again.
= paginate_without_count(@collection) = paginate_without_count(@collection)
.js-importer-status{ data: { jobs_import_path: "#{jobs_import_bitbucket_server_path}", import_path: "#{import_bitbucket_server_path}" } } .js-importer-status{ data: { jobs_import_path: "#{jobs_import_bitbucket_server_path}", import_path: "#{import_bitbucket_server_path}" } }
...@@ -4,56 +4,63 @@ ...@@ -4,56 +4,63 @@
%i.fa.fa-bug %i.fa.fa-bug
= _('Import projects from FogBugz') = _('Import projects from FogBugz')
- if @repos.any? - if Feature.enabled?(:new_import_ui)
%p.light
= _('Select projects you want to import.')
%p.light %p.light
- link_to_customize = link_to('customize', new_user_map_import_fogbugz_path) - link_to_customize = link_to('customize', new_user_map_import_fogbugz_path)
= _('Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab.').html_safe % { link_to_customize: link_to_customize } = _('Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab.').html_safe % { link_to_customize: link_to_customize }
%hr %hr
%p = render 'import/githubish_status', provider: 'fogbugz', filterable: false
= button_tag class: 'btn btn-import btn-success js-import-all' do - else
= _('Import all projects') - if @repos.any?
= icon("spinner spin", class: "loading-icon") %p.light
= _('Select projects you want to import.')
%p.light
- link_to_customize = link_to('customize', new_user_map_import_fogbugz_path)
= _('Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab.').html_safe % { link_to_customize: link_to_customize }
%hr
%p
= button_tag class: 'btn btn-import btn-success js-import-all' do
= _('Import all projects')
= icon("spinner spin", class: "loading-icon")
.table-responsive .table-responsive
%table.table.import-jobs %table.table.import-jobs
%colgroup.import-jobs-from-col %colgroup.import-jobs-from-col
%colgroup.import-jobs-to-col %colgroup.import-jobs-to-col
%colgroup.import-jobs-status-col %colgroup.import-jobs-status-col
%thead %thead
%tr %tr
%th= _("From FogBugz") %th= _("From FogBugz")
%th= _("To GitLab") %th= _("To GitLab")
%th= _("Status") %th= _("Status")
%tbody %tbody
- @already_added_projects.each do |project| - @already_added_projects.each do |project|
%tr{ id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}" } %tr{ id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}" }
%td %td
= project.import_source = project.import_source
%td %td
= link_to project.full_path, [project.namespace.becomes(Namespace), project] = link_to project.full_path, [project.namespace.becomes(Namespace), project]
%td.job-status %td.job-status
- case project.import_status - case project.import_status
- when 'finished' - when 'finished'
%span %span
%i.fa.fa-check %i.fa.fa-check
= _("done") = _("done")
- when 'started' - when 'started'
%i.fa.fa-spinner.fa-spin %i.fa.fa-spinner.fa-spin
= _("started") = _("started")
- else - else
= project.human_import_status_name = project.human_import_status_name
- @repos.each do |repo| - @repos.each do |repo|
%tr{ id: "repo_#{repo.id}" } %tr{ id: "repo_#{repo.id}" }
%td %td
= repo.name = repo.name
%td.import-target %td.import-target
#{current_user.username}/#{repo.name} #{current_user.username}/#{repo.name}
%td.import-actions.job-status %td.import-actions.job-status
= button_tag class: "btn btn-import js-add-to-import" do = button_tag class: "btn btn-import js-add-to-import" do
= _("Import") = _("Import")
= icon("spinner spin", class: "loading-icon") = icon("spinner spin", class: "loading-icon")
.js-importer-status{ data: { jobs_import_path: "#{jobs_import_fogbugz_path}", import_path: "#{import_fogbugz_path}" } } .js-importer-status{ data: { jobs_import_path: "#{jobs_import_fogbugz_path}", import_path: "#{import_fogbugz_path}" } }
...@@ -4,52 +4,55 @@ ...@@ -4,52 +4,55 @@
%i.fa.fa-heart %i.fa.fa-heart
= _('Import projects from GitLab.com') = _('Import projects from GitLab.com')
%p.light - if Feature.enabled?(:new_import_ui)
= _('Select projects you want to import.') = render 'import/githubish_status', provider: 'gitlab', filterable: false
%hr - else
%p %p.light
= button_tag class: "btn btn-import btn-success js-import-all" do = _('Select projects you want to import.')
= _('Import all projects') %hr
= icon("spinner spin", class: "loading-icon") %p
= button_tag class: "btn btn-import btn-success js-import-all" do
= _('Import all projects')
= icon("spinner spin", class: "loading-icon")
.table-responsive .table-responsive
%table.table.import-jobs %table.table.import-jobs
%colgroup.import-jobs-from-col %colgroup.import-jobs-from-col
%colgroup.import-jobs-to-col %colgroup.import-jobs-to-col
%colgroup.import-jobs-status-col %colgroup.import-jobs-status-col
%thead %thead
%tr %tr
%th= _('From GitLab.com') %th= _('From GitLab.com')
%th= _('To this GitLab instance') %th= _('To this GitLab instance')
%th= _('Status') %th= _('Status')
%tbody %tbody
- @already_added_projects.each do |project| - @already_added_projects.each do |project|
%tr{ id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}" } %tr{ id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}" }
%td %td
= link_to project.import_source, "https://gitlab.com/#{project.import_source}", target: "_blank" = link_to project.import_source, "https://gitlab.com/#{project.import_source}", target: "_blank"
%td %td
= link_to project.full_path, [project.namespace.becomes(Namespace), project] = link_to project.full_path, [project.namespace.becomes(Namespace), project]
%td.job-status %td.job-status
- case project.import_status - case project.import_status
- when 'finished' - when 'finished'
%span %span
%i.fa.fa-check %i.fa.fa-check
= _('done') = _('done')
- when 'started' - when 'started'
%i.fa.fa-spinner.fa-spin %i.fa.fa-spinner.fa-spin
= _('started') = _('started')
- else - else
= project.human_import_status_name = project.human_import_status_name
- @repos.each do |repo| - @repos.each do |repo|
%tr{ id: "repo_#{repo["id"]}" } %tr{ id: "repo_#{repo["id"]}" }
%td %td
= link_to repo["path_with_namespace"], "https://gitlab.com/#{repo["path_with_namespace"]}", target: "_blank", rel: 'noopener noreferrer' = link_to repo["path_with_namespace"], "https://gitlab.com/#{repo["path_with_namespace"]}", target: "_blank", rel: 'noopener noreferrer'
%td.import-target %td.import-target
= import_project_target(repo['namespace']['path'], repo['name']) = import_project_target(repo['namespace']['path'], repo['name'])
%td.import-actions.job-status %td.import-actions.job-status
= button_tag class: "btn btn-import js-add-to-import" do = button_tag class: "btn btn-import js-add-to-import" do
= _('Import') = _('Import')
= icon("spinner spin", class: "loading-icon") = icon("spinner spin", class: "loading-icon")
.js-importer-status{ data: { jobs_import_path: "#{jobs_import_gitlab_path}", import_path: "#{import_gitlab_path}" } } .js-importer-status{ data: { jobs_import_path: "#{jobs_import_gitlab_path}", import_path: "#{import_gitlab_path}" } }
---
title: Introduce a feature flag for Vue-based UI for all import providers
merge_request: 34220
author:
type: added
...@@ -15313,6 +15313,9 @@ msgstr "" ...@@ -15313,6 +15313,9 @@ msgstr ""
msgid "One or more of you personal access tokens were revoked" msgid "One or more of you personal access tokens were revoked"
msgstr "" msgstr ""
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
msgid "One or more of your Bitbucket projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git." msgid "One or more of your Bitbucket projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr "" msgstr ""
...@@ -16321,6 +16324,9 @@ msgstr "" ...@@ -16321,6 +16324,9 @@ msgstr ""
msgid "Please complete your profile with email address" msgid "Please complete your profile with email address"
msgstr "" msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
msgid "Please convert them to %{link_to_git}, and go through the %{link_to_import_flow} again." msgid "Please convert them to %{link_to_git}, and go through the %{link_to_import_flow} again."
msgstr "" msgstr ""
...@@ -18229,6 +18235,9 @@ msgstr "" ...@@ -18229,6 +18235,9 @@ msgstr ""
msgid "Recipe" msgid "Recipe"
msgstr "" msgstr ""
msgid "Reconfigure"
msgstr ""
msgid "Recover hidden stage" msgid "Recover hidden stage"
msgstr "" msgstr ""
......
import { nextTick } from 'vue';
import { shallowMount } from '@vue/test-utils';
import { GlAlert } from '@gitlab/ui';
import BitbucketStatusTable from '~/import_projects/components/bitbucket_status_table.vue';
import ImportProjectsTable from '~/import_projects/components/import_projects_table.vue';
const ImportProjectsTableStub = {
name: 'ImportProjectsTable',
template:
'<div><slot name="incompatible-repos-warning"></slot><slot name="actions"></slot></div>',
};
describe('BitbucketStatusTable', () => {
let wrapper;
afterEach(() => {
if (wrapper) {
wrapper.destroy();
wrapper = null;
}
});
function createComponent(propsData, importProjectsTableStub = true, slots) {
wrapper = shallowMount(BitbucketStatusTable, {
propsData,
stubs: {
ImportProjectsTable: importProjectsTableStub,
},
slots,
});
}
it('renders import table component', () => {
createComponent({ providerTitle: 'Test' });
expect(wrapper.contains(ImportProjectsTable)).toBe(true);
});
it('passes alert in incompatible-repos-warning slot', () => {
createComponent({ providerTitle: 'Test' }, ImportProjectsTableStub);
expect(wrapper.find(GlAlert).exists()).toBe(true);
});
it('passes actions slot to import project table component', () => {
const actionsSlotContent = 'DEMO';
createComponent({ providerTitle: 'Test' }, ImportProjectsTableStub, {
actions: actionsSlotContent,
});
expect(wrapper.find(ImportProjectsTable).text()).toBe(actionsSlotContent);
});
it('dismisses alert when requested', async () => {
createComponent({ providerTitle: 'Test' }, ImportProjectsTableStub);
wrapper.find(GlAlert).vm.$emit('dismiss');
await nextTick();
expect(wrapper.find(GlAlert).exists()).toBe(false);
});
});
...@@ -16,6 +16,9 @@ jest.mock('~/import_projects/event_hub', () => ({ ...@@ -16,6 +16,9 @@ jest.mock('~/import_projects/event_hub', () => ({
describe('ImportProjectsTable', () => { describe('ImportProjectsTable', () => {
let wrapper; let wrapper;
const findFilterField = () =>
wrapper.find('input[data-qa-selector="githubish_import_filter_field"]');
const providerTitle = 'THE PROVIDER'; const providerTitle = 'THE PROVIDER';
const providerRepo = { id: 10, sanitizedName: 'sanitizedName', fullName: 'fullName' }; const providerRepo = { id: 10, sanitizedName: 'sanitizedName', fullName: 'fullName' };
const importedProject = { const importedProject = {
...@@ -32,7 +35,12 @@ describe('ImportProjectsTable', () => { ...@@ -32,7 +35,12 @@ describe('ImportProjectsTable', () => {
.filter(w => w.props().variant === 'success') .filter(w => w.props().variant === 'success')
.at(0); .at(0);
function createComponent({ state: initialState, getters: customGetters, slots } = {}) { function createComponent({
state: initialState,
getters: customGetters,
slots,
filterable,
} = {}) {
const localVue = createLocalVue(); const localVue = createLocalVue();
localVue.use(Vuex); localVue.use(Vuex);
...@@ -57,6 +65,7 @@ describe('ImportProjectsTable', () => { ...@@ -57,6 +65,7 @@ describe('ImportProjectsTable', () => {
store, store,
propsData: { propsData: {
providerTitle, providerTitle,
filterable,
}, },
slots, slots,
}); });
...@@ -159,9 +168,14 @@ describe('ImportProjectsTable', () => { ...@@ -159,9 +168,14 @@ describe('ImportProjectsTable', () => {
expect(findImportAllButton().props().loading).toBe(true); expect(findImportAllButton().props().loading).toBe(true);
}); });
it('renders filtering input field', () => { it('renders filtering input field by default', () => {
createComponent(); createComponent();
expect(wrapper.contains('input[data-qa-selector="githubish_import_filter_field"]')).toBe(true); expect(findFilterField().exists()).toBe(true);
});
it('does not render filtering input field when filterable is false', () => {
createComponent({ filterable: false });
expect(findFilterField().exists()).toBe(false);
}); });
it.each` it.each`
......
import { shallowMount } from '@vue/test-utils';
import { GlButton } from '@gitlab/ui';
import BitbucketServerStatusTable from '~/pages/import/bitbucket_server/status/components/bitbucket_server_status_table.vue';
import BitbucketStatusTable from '~/import_projects/components/bitbucket_status_table.vue';
const BitbucketStatusTableStub = {
name: 'BitbucketStatusTable',
template: '<div><slot name="actions"></slot></div>',
};
describe('BitbucketServerStatusTable', () => {
let wrapper;
const findReconfigureButton = () =>
wrapper
.findAll(GlButton)
.filter(w => w.props().variant === 'info')
.at(0);
afterEach(() => {
if (wrapper) {
wrapper.destroy();
wrapper = null;
}
});
function createComponent(bitbucketStatusTableStub = true) {
wrapper = shallowMount(BitbucketServerStatusTable, {
propsData: { providerTitle: 'Test', reconfigurePath: '/reconfigure' },
stubs: {
BitbucketStatusTable: bitbucketStatusTableStub,
},
});
}
it('renders bitbucket status table component', () => {
createComponent();
expect(wrapper.contains(BitbucketStatusTable)).toBe(true);
});
it('renders Reconfigure button', async () => {
createComponent(BitbucketStatusTableStub);
expect(findReconfigureButton().attributes().href).toBe('/reconfigure');
expect(findReconfigureButton().text()).toBe('Reconfigure');
});
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment