Commit dc27c3fe authored by Ezekiel Kigbo's avatar Ezekiel Kigbo

Adds a vue based transfer project form

Wires the vue based transfer project form to the
project settings page.

Updates the namespace json data

Updates the namespace data passed to the transfer
project form.

Remove `remote` option from the form`
parent 8e4fed9e
...@@ -6,9 +6,9 @@ import initFilePickers from '~/file_pickers'; ...@@ -6,9 +6,9 @@ import initFilePickers from '~/file_pickers';
import mountBadgeSettings from '~/pages/shared/mount_badge_settings'; import mountBadgeSettings from '~/pages/shared/mount_badge_settings';
import initProjectDeleteButton from '~/projects/project_delete_button'; import initProjectDeleteButton from '~/projects/project_delete_button';
import initServiceDesk from '~/projects/settings_service_desk'; import initServiceDesk from '~/projects/settings_service_desk';
import initTransferProjectForm from '~/projects/settings/init_transfer_project_form';
import initSearchSettings from '~/search_settings'; import initSearchSettings from '~/search_settings';
import initSettingsPanels from '~/settings_panels'; import initSettingsPanels from '~/settings_panels';
import setupTransferEdit from '~/transfer_edit';
import UserCallout from '~/user_callout'; import UserCallout from '~/user_callout';
import initTopicsTokenSelector from '~/projects/settings/topics'; import initTopicsTokenSelector from '~/projects/settings/topics';
import initProjectPermissionsSettings from '../shared/permissions'; import initProjectPermissionsSettings from '../shared/permissions';
...@@ -26,7 +26,7 @@ initServiceDesk(); ...@@ -26,7 +26,7 @@ initServiceDesk();
initProjectLoadingSpinner(); initProjectLoadingSpinner();
initProjectPermissionsSettings(); initProjectPermissionsSettings();
setupTransferEdit('.js-project-transfer-form', 'select.select2'); initTransferProjectForm();
dirtySubmitFactory(document.querySelectorAll('.js-general-settings-form, .js-mr-settings-form')); dirtySubmitFactory(document.querySelectorAll('.js-general-settings-form, .js-mr-settings-form'));
......
<script>
import { GlFormGroup } from '@gitlab/ui';
import { __ } from '~/locale';
import NamespaceSelect from '~/vue_shared/components/namespace_select.vue';
import ConfirmDanger from '~/vue_shared/components/confirm_danger/confirm_danger.vue';
export default {
name: 'TransferProjectForm',
components: {
GlFormGroup,
NamespaceSelect,
ConfirmDanger,
},
props: {
namespaces: {
type: Object,
required: true,
},
confirmationPhrase: {
type: String,
required: true,
},
confirmButtonText: {
type: String,
required: true,
},
},
data() {
return { selectedNamespace: null };
},
computed: {
hasSelectedNamespace() {
return Boolean(this.selectedNamespace?.humanName);
},
dropdownText() {
return this.selectedNamespace?.humanName || this.$options.i18n.defaultText;
},
},
methods: {
handleSelect(selectedNamespace) {
this.selectedNamespace = selectedNamespace;
this.$emit('selectNamespace', selectedNamespace.id);
},
},
i18n: {
defaultText: __('Select a namespace'),
},
};
</script>
<template>
<div>
<gl-form-group>
<namespace-select
data-testid="transfer-project-namespace"
:full-width="true"
:data="namespaces"
:dropdown-text="dropdownText"
@select="handleSelect"
/>
</gl-form-group>
<confirm-danger
:disabled="!hasSelectedNamespace"
:phrase="confirmationPhrase"
:button-text="confirmButtonText"
@confirm="$emit('confirm')"
/>
</div>
</template>
import Vue from 'vue';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import TransferProjectForm from './components/transfer_project_form.vue';
const prepareNamespaces = (rawNamespaces = '') => {
const data = JSON.parse(rawNamespaces);
return {
group: data?.group.map(convertObjectPropsToCamelCase),
user: data?.user.map(convertObjectPropsToCamelCase),
};
};
export default () => {
const el = document.querySelector('.js-transfer-project-form');
const {
targetFormId = null,
targetHiddenInputId = null,
buttonText: confirmButtonText = '',
phrase: confirmationPhrase = '',
confirmDangerMessage = '',
namespaces = '',
} = el.dataset;
if (!el) {
return false;
}
return new Vue({
el,
provide: {
confirmDangerMessage,
},
render(createElement) {
return createElement(TransferProjectForm, {
props: {
confirmButtonText,
confirmationPhrase,
namespaces: prepareNamespaces(namespaces),
},
on: {
selectNamespace: (id) => {
if (targetHiddenInputId) document.getElementById(targetHiddenInputId).value = id;
},
confirm: () => {
if (targetFormId) document.getElementById(targetFormId)?.submit();
},
},
});
},
});
};
<script>
import { GlDropdown, GlDropdownItem, GlDropdownSectionHeader, GlSearchBoxByType } from '@gitlab/ui';
const filterByName = (data, searchTerm = '') =>
data.filter((d) => d.humanName.toLowerCase().includes(searchTerm));
export default {
name: 'NamespaceSelect',
components: {
GlDropdown,
GlDropdownItem,
GlDropdownSectionHeader,
GlSearchBoxByType,
},
props: {
fullWidth: {
type: Boolean,
required: false,
default: false,
},
dropdownText: {
type: String,
required: true,
},
data: {
type: Object,
required: true,
},
dropdownClasses: {
type: Array,
required: false,
default: () => [],
},
},
data() {
return {
searchTerm: '',
};
},
computed: {
hasUserNamespaces() {
return this.data.user.length;
},
hasGroupNamespaces() {
return this.data.group.length;
},
filteredGroupNamespaces() {
if (!this.hasGroupNamespaces) return [];
return filterByName(this.data.group, this.searchTerm);
},
filteredUserNamespaces() {
if (!this.hasUserNamespaces) return [];
return filterByName(this.data.user, this.searchTerm);
},
},
methods: {
handleSelect(item) {
this.$emit('select', item);
},
},
};
</script>
<template>
<gl-dropdown :text="dropdownText" :block="fullWidth">
<template #header>
<gl-search-box-by-type v-model.trim="searchTerm" />
</template>
<template v-if="hasGroupNamespaces">
<gl-dropdown-section-header>{{ __('Groups') }}</gl-dropdown-section-header>
<gl-dropdown-item
v-for="item in filteredGroupNamespaces"
:key="item.id"
@click="handleSelect(item)"
>{{ item.humanName }}</gl-dropdown-item
>
</template>
<template v-if="hasUserNamespaces">
<gl-dropdown-section-header>{{ __('Users') }}</gl-dropdown-section-header>
<gl-dropdown-item
v-for="item in filteredUserNamespaces"
:key="item.id"
@click="handleSelect(item)"
>{{ item.humanName }}</gl-dropdown-item
>
</template>
</gl-dropdown>
</template>
...@@ -88,6 +88,13 @@ module NamespacesHelper ...@@ -88,6 +88,13 @@ module NamespacesHelper
group.namespace_settings.public_send(method_name, **args) # rubocop:disable GitlabSecurity/PublicSend group.namespace_settings.public_send(method_name, **args) # rubocop:disable GitlabSecurity/PublicSend
end end
def namespaces_as_json(selected = :current_user)
{
group: formatted_namespaces(current_user.manageable_groups_with_routes, 'group'),
user: formatted_namespaces([current_user.namespace], 'user')
}.to_json
end
private private
# Many importers create a temporary Group, so use the real # Many importers create a temporary Group, so use the real
...@@ -119,6 +126,21 @@ module NamespacesHelper ...@@ -119,6 +126,21 @@ module NamespacesHelper
[group_label.camelize, elements] [group_label.camelize, elements]
end end
def formatted_namespaces(namespaces, type)
namespaces.sort_by(&:human_name).map do |n|
{
id: n.id,
display_path: n.full_path,
human_name: n.human_name,
visibility_level: n.visibility_level_value,
visibility: n.visibility,
name: n.name,
show_path: type == 'group' ? group_path(n) : user_path(n),
edit_path: type == 'group' ? edit_group_path(n) : nil
}
end
end
end end
NamespacesHelper.prepend_mod_with('NamespacesHelper') NamespacesHelper.prepend_mod_with('NamespacesHelper')
- return unless can?(current_user, :change_namespace, @project) - return unless can?(current_user, :change_namespace, @project)
- form_id = "transfer-project-form"
- hidden_input_id = "new_namespace_id"
- initial_data = { namespaces: namespaces_as_json, button_text: s_('ProjectSettings|Transfer project'), confirm_danger_message: transfer_project_message(@project), phrase: @project.name, target_form_id: form_id, target_hidden_input_id: hidden_input_id }
.sub-section .sub-section
%h4.danger-title= _('Transfer project') %h4.danger-title= _('Transfer project')
= form_for @project, url: transfer_project_path(@project), method: :put, remote: true, html: { class: 'js-project-transfer-form' } do |f| = form_for @project, url: transfer_project_path(@project), method: :put, html: { class: "js-project-transfer-form", id: form_id } do |f|
.form-group .form-group
- link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: help_page_path('user/project/settings/index', anchor: 'transferring-an-existing-project-into-another-namespace') } - link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: help_page_path('user/project/settings/index', anchor: 'transferring-an-existing-project-into-another-namespace') }
%p= _("Transfer your project into another namespace. %{link_start}Learn more.%{link_end}").html_safe % { link_start: link_start, link_end: '</a>'.html_safe } %p= _("Transfer your project into another namespace. %{link_start}Learn more.%{link_end}").html_safe % { link_start: link_start, link_end: '</a>'.html_safe }
...@@ -11,7 +14,6 @@ ...@@ -11,7 +14,6 @@
%li= _('You can only transfer the project to namespaces you manage.') %li= _('You can only transfer the project to namespaces you manage.')
%li= _('You will need to update your local repositories to point to the new location.') %li= _('You will need to update your local repositories to point to the new location.')
%li= _('Project visibility level will be changed to match namespace rules when transferring to a group.') %li= _('Project visibility level will be changed to match namespace rules when transferring to a group.')
= hidden_field_tag(hidden_input_id)
= label_tag :new_namespace_id, _('Select a new namespace'), class: 'gl-font-weight-bold' = label_tag :new_namespace_id, _('Select a new namespace'), class: 'gl-font-weight-bold'
.form-group .js-transfer-project-form{ data: initial_data }
= select_tag :new_namespace_id, namespaces_options(nil), include_blank: true, class: 'select2'
= f.submit 'Transfer project', class: "gl-button btn btn-danger js-legacy-confirm-danger qa-transfer-button", data: { "confirm-danger-message" => transfer_project_message(@project) }
:plain
location.href = "#{edit_project_path(@project)}";
...@@ -16,10 +16,14 @@ RSpec.describe 'Projects > Settings > User transfers a project', :js do ...@@ -16,10 +16,14 @@ RSpec.describe 'Projects > Settings > User transfers a project', :js do
visit edit_project_path(project) visit edit_project_path(project)
page.within('.js-project-transfer-form') do page.within('.js-project-transfer-form') do
page.find('.select2-container').click # page.find('.select2-container').click
page.find('[data-testid="transfer-project-namespace"]').click
end end
page.find("div[role='option']", text: group.full_name).click # binding.pry
page.within('[data-testid="transfer-project-namespace"]') do
page.find("li button", text: group.full_name).click
end
click_button('Transfer project') click_button('Transfer project')
......
// TODO: remove jquery dep
import $ from 'jquery'; import $ from 'jquery';
import { loadHTMLFixture } from 'helpers/fixtures'; import { loadHTMLFixture } from 'helpers/fixtures';
......
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