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';
import mountBadgeSettings from '~/pages/shared/mount_badge_settings';
import initProjectDeleteButton from '~/projects/project_delete_button';
import initServiceDesk from '~/projects/settings_service_desk';
import initTransferProjectForm from '~/projects/settings/init_transfer_project_form';
import initSearchSettings from '~/search_settings';
import initSettingsPanels from '~/settings_panels';
import setupTransferEdit from '~/transfer_edit';
import UserCallout from '~/user_callout';
import initTopicsTokenSelector from '~/projects/settings/topics';
import initProjectPermissionsSettings from '../shared/permissions';
......@@ -26,7 +26,7 @@ initServiceDesk();
initProjectLoadingSpinner();
initProjectPermissionsSettings();
setupTransferEdit('.js-project-transfer-form', 'select.select2');
initTransferProjectForm();
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
group.namespace_settings.public_send(method_name, **args) # rubocop:disable GitlabSecurity/PublicSend
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
# Many importers create a temporary Group, so use the real
......@@ -119,6 +126,21 @@ module NamespacesHelper
[group_label.camelize, elements]
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
NamespacesHelper.prepend_mod_with('NamespacesHelper')
- 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
%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
- 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 }
......@@ -11,7 +14,6 @@
%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= _('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'
.form-group
= 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) }
.js-transfer-project-form{ data: initial_data }
:plain
location.href = "#{edit_project_path(@project)}";
......@@ -16,10 +16,14 @@ RSpec.describe 'Projects > Settings > User transfers a project', :js do
visit edit_project_path(project)
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
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')
......
// TODO: remove jquery dep
import $ from 'jquery';
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